##// END OF EJS Templates
commits/summary: unify fetching remote attribute in summary and commits page to properly and in the same way show the data.
super-admin -
r4750:8532c1ca default
parent child Browse files
Show More
@@ -1,816 +1,821 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import time
21 import time
22 import logging
22 import logging
23 import operator
23 import operator
24
24
25 from pyramid import compat
25 from pyramid import compat
26 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPBadRequest
26 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPBadRequest
27
27
28 from rhodecode.lib import helpers as h, diffs, rc_cache
28 from rhodecode.lib import helpers as h, diffs, rc_cache
29 from rhodecode.lib.utils2 import (
29 from rhodecode.lib.utils2 import (
30 StrictAttributeDict, str2bool, safe_int, datetime_to_time, safe_unicode)
30 StrictAttributeDict, str2bool, safe_int, datetime_to_time, safe_unicode)
31 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
31 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
32 from rhodecode.lib.vcs.backends.base import EmptyCommit
32 from rhodecode.lib.vcs.backends.base import EmptyCommit
33 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
33 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
34 from rhodecode.model import repo
34 from rhodecode.model import repo
35 from rhodecode.model import repo_group
35 from rhodecode.model import repo_group
36 from rhodecode.model import user_group
36 from rhodecode.model import user_group
37 from rhodecode.model import user
37 from rhodecode.model import user
38 from rhodecode.model.db import User
38 from rhodecode.model.db import User
39 from rhodecode.model.scm import ScmModel
39 from rhodecode.model.scm import ScmModel
40 from rhodecode.model.settings import VcsSettingsModel, IssueTrackerSettingsModel
40 from rhodecode.model.settings import VcsSettingsModel, IssueTrackerSettingsModel
41 from rhodecode.model.repo import ReadmeFinder
41 from rhodecode.model.repo import ReadmeFinder
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 ADMIN_PREFIX = '/_admin'
46 ADMIN_PREFIX = '/_admin'
47 STATIC_FILE_PREFIX = '/_static'
47 STATIC_FILE_PREFIX = '/_static'
48
48
49 URL_NAME_REQUIREMENTS = {
49 URL_NAME_REQUIREMENTS = {
50 # group name can have a slash in them, but they must not end with a slash
50 # group name can have a slash in them, but they must not end with a slash
51 'group_name': r'.*?[^/]',
51 'group_name': r'.*?[^/]',
52 'repo_group_name': r'.*?[^/]',
52 'repo_group_name': r'.*?[^/]',
53 # repo names can have a slash in them, but they must not end with a slash
53 # repo names can have a slash in them, but they must not end with a slash
54 'repo_name': r'.*?[^/]',
54 'repo_name': r'.*?[^/]',
55 # file path eats up everything at the end
55 # file path eats up everything at the end
56 'f_path': r'.*',
56 'f_path': r'.*',
57 # reference types
57 # reference types
58 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
58 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
59 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
59 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
60 }
60 }
61
61
62
62
63 def add_route_with_slash(config,name, pattern, **kw):
63 def add_route_with_slash(config,name, pattern, **kw):
64 config.add_route(name, pattern, **kw)
64 config.add_route(name, pattern, **kw)
65 if not pattern.endswith('/'):
65 if not pattern.endswith('/'):
66 config.add_route(name + '_slash', pattern + '/', **kw)
66 config.add_route(name + '_slash', pattern + '/', **kw)
67
67
68
68
69 def add_route_requirements(route_path, requirements=None):
69 def add_route_requirements(route_path, requirements=None):
70 """
70 """
71 Adds regex requirements to pyramid routes using a mapping dict
71 Adds regex requirements to pyramid routes using a mapping dict
72 e.g::
72 e.g::
73 add_route_requirements('{repo_name}/settings')
73 add_route_requirements('{repo_name}/settings')
74 """
74 """
75 requirements = requirements or URL_NAME_REQUIREMENTS
75 requirements = requirements or URL_NAME_REQUIREMENTS
76 for key, regex in requirements.items():
76 for key, regex in requirements.items():
77 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
77 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
78 return route_path
78 return route_path
79
79
80
80
81 def get_format_ref_id(repo):
81 def get_format_ref_id(repo):
82 """Returns a `repo` specific reference formatter function"""
82 """Returns a `repo` specific reference formatter function"""
83 if h.is_svn(repo):
83 if h.is_svn(repo):
84 return _format_ref_id_svn
84 return _format_ref_id_svn
85 else:
85 else:
86 return _format_ref_id
86 return _format_ref_id
87
87
88
88
89 def _format_ref_id(name, raw_id):
89 def _format_ref_id(name, raw_id):
90 """Default formatting of a given reference `name`"""
90 """Default formatting of a given reference `name`"""
91 return name
91 return name
92
92
93
93
94 def _format_ref_id_svn(name, raw_id):
94 def _format_ref_id_svn(name, raw_id):
95 """Special way of formatting a reference for Subversion including path"""
95 """Special way of formatting a reference for Subversion including path"""
96 return '%s@%s' % (name, raw_id)
96 return '%s@%s' % (name, raw_id)
97
97
98
98
99 class TemplateArgs(StrictAttributeDict):
99 class TemplateArgs(StrictAttributeDict):
100 pass
100 pass
101
101
102
102
103 class BaseAppView(object):
103 class BaseAppView(object):
104
104
105 def __init__(self, context, request):
105 def __init__(self, context, request):
106 self.request = request
106 self.request = request
107 self.context = context
107 self.context = context
108 self.session = request.session
108 self.session = request.session
109 if not hasattr(request, 'user'):
109 if not hasattr(request, 'user'):
110 # NOTE(marcink): edge case, we ended up in matched route
110 # NOTE(marcink): edge case, we ended up in matched route
111 # but probably of web-app context, e.g API CALL/VCS CALL
111 # but probably of web-app context, e.g API CALL/VCS CALL
112 if hasattr(request, 'vcs_call') or hasattr(request, 'rpc_method'):
112 if hasattr(request, 'vcs_call') or hasattr(request, 'rpc_method'):
113 log.warning('Unable to process request `%s` in this scope', request)
113 log.warning('Unable to process request `%s` in this scope', request)
114 raise HTTPBadRequest()
114 raise HTTPBadRequest()
115
115
116 self._rhodecode_user = request.user # auth user
116 self._rhodecode_user = request.user # auth user
117 self._rhodecode_db_user = self._rhodecode_user.get_instance()
117 self._rhodecode_db_user = self._rhodecode_user.get_instance()
118 self._maybe_needs_password_change(
118 self._maybe_needs_password_change(
119 request.matched_route.name, self._rhodecode_db_user)
119 request.matched_route.name, self._rhodecode_db_user)
120
120
121 def _maybe_needs_password_change(self, view_name, user_obj):
121 def _maybe_needs_password_change(self, view_name, user_obj):
122
122
123 dont_check_views = [
123 dont_check_views = [
124 'channelstream_connect'
124 'channelstream_connect'
125 ]
125 ]
126 if view_name in dont_check_views:
126 if view_name in dont_check_views:
127 return
127 return
128
128
129 log.debug('Checking if user %s needs password change on view %s',
129 log.debug('Checking if user %s needs password change on view %s',
130 user_obj, view_name)
130 user_obj, view_name)
131
131
132 skip_user_views = [
132 skip_user_views = [
133 'logout', 'login',
133 'logout', 'login',
134 'my_account_password', 'my_account_password_update'
134 'my_account_password', 'my_account_password_update'
135 ]
135 ]
136
136
137 if not user_obj:
137 if not user_obj:
138 return
138 return
139
139
140 if user_obj.username == User.DEFAULT_USER:
140 if user_obj.username == User.DEFAULT_USER:
141 return
141 return
142
142
143 now = time.time()
143 now = time.time()
144 should_change = user_obj.user_data.get('force_password_change')
144 should_change = user_obj.user_data.get('force_password_change')
145 change_after = safe_int(should_change) or 0
145 change_after = safe_int(should_change) or 0
146 if should_change and now > change_after:
146 if should_change and now > change_after:
147 log.debug('User %s requires password change', user_obj)
147 log.debug('User %s requires password change', user_obj)
148 h.flash('You are required to change your password', 'warning',
148 h.flash('You are required to change your password', 'warning',
149 ignore_duplicate=True)
149 ignore_duplicate=True)
150
150
151 if view_name not in skip_user_views:
151 if view_name not in skip_user_views:
152 raise HTTPFound(
152 raise HTTPFound(
153 self.request.route_path('my_account_password'))
153 self.request.route_path('my_account_password'))
154
154
155 def _log_creation_exception(self, e, repo_name):
155 def _log_creation_exception(self, e, repo_name):
156 _ = self.request.translate
156 _ = self.request.translate
157 reason = None
157 reason = None
158 if len(e.args) == 2:
158 if len(e.args) == 2:
159 reason = e.args[1]
159 reason = e.args[1]
160
160
161 if reason == 'INVALID_CERTIFICATE':
161 if reason == 'INVALID_CERTIFICATE':
162 log.exception(
162 log.exception(
163 'Exception creating a repository: invalid certificate')
163 'Exception creating a repository: invalid certificate')
164 msg = (_('Error creating repository %s: invalid certificate')
164 msg = (_('Error creating repository %s: invalid certificate')
165 % repo_name)
165 % repo_name)
166 else:
166 else:
167 log.exception("Exception creating a repository")
167 log.exception("Exception creating a repository")
168 msg = (_('Error creating repository %s')
168 msg = (_('Error creating repository %s')
169 % repo_name)
169 % repo_name)
170 return msg
170 return msg
171
171
172 def _get_local_tmpl_context(self, include_app_defaults=True):
172 def _get_local_tmpl_context(self, include_app_defaults=True):
173 c = TemplateArgs()
173 c = TemplateArgs()
174 c.auth_user = self.request.user
174 c.auth_user = self.request.user
175 # TODO(marcink): migrate the usage of c.rhodecode_user to c.auth_user
175 # TODO(marcink): migrate the usage of c.rhodecode_user to c.auth_user
176 c.rhodecode_user = self.request.user
176 c.rhodecode_user = self.request.user
177
177
178 if include_app_defaults:
178 if include_app_defaults:
179 from rhodecode.lib.base import attach_context_attributes
179 from rhodecode.lib.base import attach_context_attributes
180 attach_context_attributes(c, self.request, self.request.user.user_id)
180 attach_context_attributes(c, self.request, self.request.user.user_id)
181
181
182 c.is_super_admin = c.auth_user.is_admin
182 c.is_super_admin = c.auth_user.is_admin
183
183
184 c.can_create_repo = c.is_super_admin
184 c.can_create_repo = c.is_super_admin
185 c.can_create_repo_group = c.is_super_admin
185 c.can_create_repo_group = c.is_super_admin
186 c.can_create_user_group = c.is_super_admin
186 c.can_create_user_group = c.is_super_admin
187
187
188 c.is_delegated_admin = False
188 c.is_delegated_admin = False
189
189
190 if not c.auth_user.is_default and not c.is_super_admin:
190 if not c.auth_user.is_default and not c.is_super_admin:
191 c.can_create_repo = h.HasPermissionAny('hg.create.repository')(
191 c.can_create_repo = h.HasPermissionAny('hg.create.repository')(
192 user=self.request.user)
192 user=self.request.user)
193 repositories = c.auth_user.repositories_admin or c.can_create_repo
193 repositories = c.auth_user.repositories_admin or c.can_create_repo
194
194
195 c.can_create_repo_group = h.HasPermissionAny('hg.repogroup.create.true')(
195 c.can_create_repo_group = h.HasPermissionAny('hg.repogroup.create.true')(
196 user=self.request.user)
196 user=self.request.user)
197 repository_groups = c.auth_user.repository_groups_admin or c.can_create_repo_group
197 repository_groups = c.auth_user.repository_groups_admin or c.can_create_repo_group
198
198
199 c.can_create_user_group = h.HasPermissionAny('hg.usergroup.create.true')(
199 c.can_create_user_group = h.HasPermissionAny('hg.usergroup.create.true')(
200 user=self.request.user)
200 user=self.request.user)
201 user_groups = c.auth_user.user_groups_admin or c.can_create_user_group
201 user_groups = c.auth_user.user_groups_admin or c.can_create_user_group
202 # delegated admin can create, or manage some objects
202 # delegated admin can create, or manage some objects
203 c.is_delegated_admin = repositories or repository_groups or user_groups
203 c.is_delegated_admin = repositories or repository_groups or user_groups
204 return c
204 return c
205
205
206 def _get_template_context(self, tmpl_args, **kwargs):
206 def _get_template_context(self, tmpl_args, **kwargs):
207
207
208 local_tmpl_args = {
208 local_tmpl_args = {
209 'defaults': {},
209 'defaults': {},
210 'errors': {},
210 'errors': {},
211 'c': tmpl_args
211 'c': tmpl_args
212 }
212 }
213 local_tmpl_args.update(kwargs)
213 local_tmpl_args.update(kwargs)
214 return local_tmpl_args
214 return local_tmpl_args
215
215
216 def load_default_context(self):
216 def load_default_context(self):
217 """
217 """
218 example:
218 example:
219
219
220 def load_default_context(self):
220 def load_default_context(self):
221 c = self._get_local_tmpl_context()
221 c = self._get_local_tmpl_context()
222 c.custom_var = 'foobar'
222 c.custom_var = 'foobar'
223
223
224 return c
224 return c
225 """
225 """
226 raise NotImplementedError('Needs implementation in view class')
226 raise NotImplementedError('Needs implementation in view class')
227
227
228
228
229 class RepoAppView(BaseAppView):
229 class RepoAppView(BaseAppView):
230
230
231 def __init__(self, context, request):
231 def __init__(self, context, request):
232 super(RepoAppView, self).__init__(context, request)
232 super(RepoAppView, self).__init__(context, request)
233 self.db_repo = request.db_repo
233 self.db_repo = request.db_repo
234 self.db_repo_name = self.db_repo.repo_name
234 self.db_repo_name = self.db_repo.repo_name
235 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
235 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
236 self.db_repo_artifacts = ScmModel().get_artifacts(self.db_repo)
236 self.db_repo_artifacts = ScmModel().get_artifacts(self.db_repo)
237 self.db_repo_patterns = IssueTrackerSettingsModel(repo=self.db_repo)
237 self.db_repo_patterns = IssueTrackerSettingsModel(repo=self.db_repo)
238
238
239 def _handle_missing_requirements(self, error):
239 def _handle_missing_requirements(self, error):
240 log.error(
240 log.error(
241 'Requirements are missing for repository %s: %s',
241 'Requirements are missing for repository %s: %s',
242 self.db_repo_name, safe_unicode(error))
242 self.db_repo_name, safe_unicode(error))
243
243
244 def _get_local_tmpl_context(self, include_app_defaults=True):
244 def _get_local_tmpl_context(self, include_app_defaults=True):
245 _ = self.request.translate
245 _ = self.request.translate
246 c = super(RepoAppView, self)._get_local_tmpl_context(
246 c = super(RepoAppView, self)._get_local_tmpl_context(
247 include_app_defaults=include_app_defaults)
247 include_app_defaults=include_app_defaults)
248
248
249 # register common vars for this type of view
249 # register common vars for this type of view
250 c.rhodecode_db_repo = self.db_repo
250 c.rhodecode_db_repo = self.db_repo
251 c.repo_name = self.db_repo_name
251 c.repo_name = self.db_repo_name
252 c.repository_pull_requests = self.db_repo_pull_requests
252 c.repository_pull_requests = self.db_repo_pull_requests
253 c.repository_artifacts = self.db_repo_artifacts
253 c.repository_artifacts = self.db_repo_artifacts
254 c.repository_is_user_following = ScmModel().is_following_repo(
254 c.repository_is_user_following = ScmModel().is_following_repo(
255 self.db_repo_name, self._rhodecode_user.user_id)
255 self.db_repo_name, self._rhodecode_user.user_id)
256 self.path_filter = PathFilter(None)
256 self.path_filter = PathFilter(None)
257
257
258 c.repository_requirements_missing = {}
258 c.repository_requirements_missing = {}
259 try:
259 try:
260 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
260 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
261 # NOTE(marcink):
261 # NOTE(marcink):
262 # comparison to None since if it's an object __bool__ is expensive to
262 # comparison to None since if it's an object __bool__ is expensive to
263 # calculate
263 # calculate
264 if self.rhodecode_vcs_repo is not None:
264 if self.rhodecode_vcs_repo is not None:
265 path_perms = self.rhodecode_vcs_repo.get_path_permissions(
265 path_perms = self.rhodecode_vcs_repo.get_path_permissions(
266 c.auth_user.username)
266 c.auth_user.username)
267 self.path_filter = PathFilter(path_perms)
267 self.path_filter = PathFilter(path_perms)
268 except RepositoryRequirementError as e:
268 except RepositoryRequirementError as e:
269 c.repository_requirements_missing = {'error': str(e)}
269 c.repository_requirements_missing = {'error': str(e)}
270 self._handle_missing_requirements(e)
270 self._handle_missing_requirements(e)
271 self.rhodecode_vcs_repo = None
271 self.rhodecode_vcs_repo = None
272
272
273 c.path_filter = self.path_filter # used by atom_feed_entry.mako
273 c.path_filter = self.path_filter # used by atom_feed_entry.mako
274
274
275 if self.rhodecode_vcs_repo is None:
275 if self.rhodecode_vcs_repo is None:
276 # unable to fetch this repo as vcs instance, report back to user
276 # unable to fetch this repo as vcs instance, report back to user
277 h.flash(_(
277 h.flash(_(
278 "The repository `%(repo_name)s` cannot be loaded in filesystem. "
278 "The repository `%(repo_name)s` cannot be loaded in filesystem. "
279 "Please check if it exist, or is not damaged.") %
279 "Please check if it exist, or is not damaged.") %
280 {'repo_name': c.repo_name},
280 {'repo_name': c.repo_name},
281 category='error', ignore_duplicate=True)
281 category='error', ignore_duplicate=True)
282 if c.repository_requirements_missing:
282 if c.repository_requirements_missing:
283 route = self.request.matched_route.name
283 route = self.request.matched_route.name
284 if route.startswith(('edit_repo', 'repo_summary')):
284 if route.startswith(('edit_repo', 'repo_summary')):
285 # allow summary and edit repo on missing requirements
285 # allow summary and edit repo on missing requirements
286 return c
286 return c
287
287
288 raise HTTPFound(
288 raise HTTPFound(
289 h.route_path('repo_summary', repo_name=self.db_repo_name))
289 h.route_path('repo_summary', repo_name=self.db_repo_name))
290
290
291 else: # redirect if we don't show missing requirements
291 else: # redirect if we don't show missing requirements
292 raise HTTPFound(h.route_path('home'))
292 raise HTTPFound(h.route_path('home'))
293
293
294 c.has_origin_repo_read_perm = False
294 c.has_origin_repo_read_perm = False
295 if self.db_repo.fork:
295 if self.db_repo.fork:
296 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
296 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
297 'repository.write', 'repository.read', 'repository.admin')(
297 'repository.write', 'repository.read', 'repository.admin')(
298 self.db_repo.fork.repo_name, 'summary fork link')
298 self.db_repo.fork.repo_name, 'summary fork link')
299
299
300 return c
300 return c
301
301
302 def _get_f_path_unchecked(self, matchdict, default=None):
302 def _get_f_path_unchecked(self, matchdict, default=None):
303 """
303 """
304 Should only be used by redirects, everything else should call _get_f_path
304 Should only be used by redirects, everything else should call _get_f_path
305 """
305 """
306 f_path = matchdict.get('f_path')
306 f_path = matchdict.get('f_path')
307 if f_path:
307 if f_path:
308 # fix for multiple initial slashes that causes errors for GIT
308 # fix for multiple initial slashes that causes errors for GIT
309 return f_path.lstrip('/')
309 return f_path.lstrip('/')
310
310
311 return default
311 return default
312
312
313 def _get_f_path(self, matchdict, default=None):
313 def _get_f_path(self, matchdict, default=None):
314 f_path_match = self._get_f_path_unchecked(matchdict, default)
314 f_path_match = self._get_f_path_unchecked(matchdict, default)
315 return self.path_filter.assert_path_permissions(f_path_match)
315 return self.path_filter.assert_path_permissions(f_path_match)
316
316
317 def _get_general_setting(self, target_repo, settings_key, default=False):
317 def _get_general_setting(self, target_repo, settings_key, default=False):
318 settings_model = VcsSettingsModel(repo=target_repo)
318 settings_model = VcsSettingsModel(repo=target_repo)
319 settings = settings_model.get_general_settings()
319 settings = settings_model.get_general_settings()
320 return settings.get(settings_key, default)
320 return settings.get(settings_key, default)
321
321
322 def _get_repo_setting(self, target_repo, settings_key, default=False):
322 def _get_repo_setting(self, target_repo, settings_key, default=False):
323 settings_model = VcsSettingsModel(repo=target_repo)
323 settings_model = VcsSettingsModel(repo=target_repo)
324 settings = settings_model.get_repo_settings_inherited()
324 settings = settings_model.get_repo_settings_inherited()
325 return settings.get(settings_key, default)
325 return settings.get(settings_key, default)
326
326
327 def _get_readme_data(self, db_repo, renderer_type, commit_id=None, path='/'):
327 def _get_readme_data(self, db_repo, renderer_type, commit_id=None, path='/'):
328 log.debug('Looking for README file at path %s', path)
328 log.debug('Looking for README file at path %s', path)
329 if commit_id:
329 if commit_id:
330 landing_commit_id = commit_id
330 landing_commit_id = commit_id
331 else:
331 else:
332 landing_commit = db_repo.get_landing_commit()
332 landing_commit = db_repo.get_landing_commit()
333 if isinstance(landing_commit, EmptyCommit):
333 if isinstance(landing_commit, EmptyCommit):
334 return None, None
334 return None, None
335 landing_commit_id = landing_commit.raw_id
335 landing_commit_id = landing_commit.raw_id
336
336
337 cache_namespace_uid = 'cache_repo.{}'.format(db_repo.repo_id)
337 cache_namespace_uid = 'cache_repo.{}'.format(db_repo.repo_id)
338 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
338 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
339 start = time.time()
339 start = time.time()
340
340
341 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
341 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
342 def generate_repo_readme(repo_id, _commit_id, _repo_name, _readme_search_path, _renderer_type):
342 def generate_repo_readme(repo_id, _commit_id, _repo_name, _readme_search_path, _renderer_type):
343 readme_data = None
343 readme_data = None
344 readme_filename = None
344 readme_filename = None
345
345
346 commit = db_repo.get_commit(_commit_id)
346 commit = db_repo.get_commit(_commit_id)
347 log.debug("Searching for a README file at commit %s.", _commit_id)
347 log.debug("Searching for a README file at commit %s.", _commit_id)
348 readme_node = ReadmeFinder(_renderer_type).search(commit, path=_readme_search_path)
348 readme_node = ReadmeFinder(_renderer_type).search(commit, path=_readme_search_path)
349
349
350 if readme_node:
350 if readme_node:
351 log.debug('Found README node: %s', readme_node)
351 log.debug('Found README node: %s', readme_node)
352 relative_urls = {
352 relative_urls = {
353 'raw': h.route_path(
353 'raw': h.route_path(
354 'repo_file_raw', repo_name=_repo_name,
354 'repo_file_raw', repo_name=_repo_name,
355 commit_id=commit.raw_id, f_path=readme_node.path),
355 commit_id=commit.raw_id, f_path=readme_node.path),
356 'standard': h.route_path(
356 'standard': h.route_path(
357 'repo_files', repo_name=_repo_name,
357 'repo_files', repo_name=_repo_name,
358 commit_id=commit.raw_id, f_path=readme_node.path),
358 commit_id=commit.raw_id, f_path=readme_node.path),
359 }
359 }
360 readme_data = self._render_readme_or_none(commit, readme_node, relative_urls)
360 readme_data = self._render_readme_or_none(commit, readme_node, relative_urls)
361 readme_filename = readme_node.unicode_path
361 readme_filename = readme_node.unicode_path
362
362
363 return readme_data, readme_filename
363 return readme_data, readme_filename
364
364
365 readme_data, readme_filename = generate_repo_readme(
365 readme_data, readme_filename = generate_repo_readme(
366 db_repo.repo_id, landing_commit_id, db_repo.repo_name, path, renderer_type,)
366 db_repo.repo_id, landing_commit_id, db_repo.repo_name, path, renderer_type,)
367 compute_time = time.time() - start
367 compute_time = time.time() - start
368 log.debug('Repo README for path %s generated and computed in %.4fs',
368 log.debug('Repo README for path %s generated and computed in %.4fs',
369 path, compute_time)
369 path, compute_time)
370 return readme_data, readme_filename
370 return readme_data, readme_filename
371
371
372 def _render_readme_or_none(self, commit, readme_node, relative_urls):
372 def _render_readme_or_none(self, commit, readme_node, relative_urls):
373 log.debug('Found README file `%s` rendering...', readme_node.path)
373 log.debug('Found README file `%s` rendering...', readme_node.path)
374 renderer = MarkupRenderer()
374 renderer = MarkupRenderer()
375 try:
375 try:
376 html_source = renderer.render(
376 html_source = renderer.render(
377 readme_node.content, filename=readme_node.path)
377 readme_node.content, filename=readme_node.path)
378 if relative_urls:
378 if relative_urls:
379 return relative_links(html_source, relative_urls)
379 return relative_links(html_source, relative_urls)
380 return html_source
380 return html_source
381 except Exception:
381 except Exception:
382 log.exception(
382 log.exception(
383 "Exception while trying to render the README")
383 "Exception while trying to render the README")
384
384
385 def get_recache_flag(self):
385 def get_recache_flag(self):
386 for flag_name in ['force_recache', 'force-recache', 'no-cache']:
386 for flag_name in ['force_recache', 'force-recache', 'no-cache']:
387 flag_val = self.request.GET.get(flag_name)
387 flag_val = self.request.GET.get(flag_name)
388 if str2bool(flag_val):
388 if str2bool(flag_val):
389 return True
389 return True
390 return False
390 return False
391
391
392 def get_commit_preload_attrs(cls):
393 pre_load = ['author', 'branch', 'date', 'message', 'parents',
394 'obsolete', 'phase', 'hidden']
395 return pre_load
396
392
397
393 class PathFilter(object):
398 class PathFilter(object):
394
399
395 # Expects and instance of BasePathPermissionChecker or None
400 # Expects and instance of BasePathPermissionChecker or None
396 def __init__(self, permission_checker):
401 def __init__(self, permission_checker):
397 self.permission_checker = permission_checker
402 self.permission_checker = permission_checker
398
403
399 def assert_path_permissions(self, path):
404 def assert_path_permissions(self, path):
400 if self.path_access_allowed(path):
405 if self.path_access_allowed(path):
401 return path
406 return path
402 raise HTTPForbidden()
407 raise HTTPForbidden()
403
408
404 def path_access_allowed(self, path):
409 def path_access_allowed(self, path):
405 log.debug('Checking ACL permissions for PathFilter for `%s`', path)
410 log.debug('Checking ACL permissions for PathFilter for `%s`', path)
406 if self.permission_checker:
411 if self.permission_checker:
407 has_access = path and self.permission_checker.has_access(path)
412 has_access = path and self.permission_checker.has_access(path)
408 log.debug('ACL Permissions checker enabled, ACL Check has_access: %s', has_access)
413 log.debug('ACL Permissions checker enabled, ACL Check has_access: %s', has_access)
409 return has_access
414 return has_access
410
415
411 log.debug('ACL permissions checker not enabled, skipping...')
416 log.debug('ACL permissions checker not enabled, skipping...')
412 return True
417 return True
413
418
414 def filter_patchset(self, patchset):
419 def filter_patchset(self, patchset):
415 if not self.permission_checker or not patchset:
420 if not self.permission_checker or not patchset:
416 return patchset, False
421 return patchset, False
417 had_filtered = False
422 had_filtered = False
418 filtered_patchset = []
423 filtered_patchset = []
419 for patch in patchset:
424 for patch in patchset:
420 filename = patch.get('filename', None)
425 filename = patch.get('filename', None)
421 if not filename or self.permission_checker.has_access(filename):
426 if not filename or self.permission_checker.has_access(filename):
422 filtered_patchset.append(patch)
427 filtered_patchset.append(patch)
423 else:
428 else:
424 had_filtered = True
429 had_filtered = True
425 if had_filtered:
430 if had_filtered:
426 if isinstance(patchset, diffs.LimitedDiffContainer):
431 if isinstance(patchset, diffs.LimitedDiffContainer):
427 filtered_patchset = diffs.LimitedDiffContainer(patchset.diff_limit, patchset.cur_diff_size, filtered_patchset)
432 filtered_patchset = diffs.LimitedDiffContainer(patchset.diff_limit, patchset.cur_diff_size, filtered_patchset)
428 return filtered_patchset, True
433 return filtered_patchset, True
429 else:
434 else:
430 return patchset, False
435 return patchset, False
431
436
432 def render_patchset_filtered(self, diffset, patchset, source_ref=None, target_ref=None):
437 def render_patchset_filtered(self, diffset, patchset, source_ref=None, target_ref=None):
433 filtered_patchset, has_hidden_changes = self.filter_patchset(patchset)
438 filtered_patchset, has_hidden_changes = self.filter_patchset(patchset)
434 result = diffset.render_patchset(
439 result = diffset.render_patchset(
435 filtered_patchset, source_ref=source_ref, target_ref=target_ref)
440 filtered_patchset, source_ref=source_ref, target_ref=target_ref)
436 result.has_hidden_changes = has_hidden_changes
441 result.has_hidden_changes = has_hidden_changes
437 return result
442 return result
438
443
439 def get_raw_patch(self, diff_processor):
444 def get_raw_patch(self, diff_processor):
440 if self.permission_checker is None:
445 if self.permission_checker is None:
441 return diff_processor.as_raw()
446 return diff_processor.as_raw()
442 elif self.permission_checker.has_full_access:
447 elif self.permission_checker.has_full_access:
443 return diff_processor.as_raw()
448 return diff_processor.as_raw()
444 else:
449 else:
445 return '# Repository has user-specific filters, raw patch generation is disabled.'
450 return '# Repository has user-specific filters, raw patch generation is disabled.'
446
451
447 @property
452 @property
448 def is_enabled(self):
453 def is_enabled(self):
449 return self.permission_checker is not None
454 return self.permission_checker is not None
450
455
451
456
452 class RepoGroupAppView(BaseAppView):
457 class RepoGroupAppView(BaseAppView):
453 def __init__(self, context, request):
458 def __init__(self, context, request):
454 super(RepoGroupAppView, self).__init__(context, request)
459 super(RepoGroupAppView, self).__init__(context, request)
455 self.db_repo_group = request.db_repo_group
460 self.db_repo_group = request.db_repo_group
456 self.db_repo_group_name = self.db_repo_group.group_name
461 self.db_repo_group_name = self.db_repo_group.group_name
457
462
458 def _get_local_tmpl_context(self, include_app_defaults=True):
463 def _get_local_tmpl_context(self, include_app_defaults=True):
459 _ = self.request.translate
464 _ = self.request.translate
460 c = super(RepoGroupAppView, self)._get_local_tmpl_context(
465 c = super(RepoGroupAppView, self)._get_local_tmpl_context(
461 include_app_defaults=include_app_defaults)
466 include_app_defaults=include_app_defaults)
462 c.repo_group = self.db_repo_group
467 c.repo_group = self.db_repo_group
463 return c
468 return c
464
469
465 def _revoke_perms_on_yourself(self, form_result):
470 def _revoke_perms_on_yourself(self, form_result):
466 _updates = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
471 _updates = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
467 form_result['perm_updates'])
472 form_result['perm_updates'])
468 _additions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
473 _additions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
469 form_result['perm_additions'])
474 form_result['perm_additions'])
470 _deletions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
475 _deletions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
471 form_result['perm_deletions'])
476 form_result['perm_deletions'])
472 admin_perm = 'group.admin'
477 admin_perm = 'group.admin'
473 if _updates and _updates[0][1] != admin_perm or \
478 if _updates and _updates[0][1] != admin_perm or \
474 _additions and _additions[0][1] != admin_perm or \
479 _additions and _additions[0][1] != admin_perm or \
475 _deletions and _deletions[0][1] != admin_perm:
480 _deletions and _deletions[0][1] != admin_perm:
476 return True
481 return True
477 return False
482 return False
478
483
479
484
480 class UserGroupAppView(BaseAppView):
485 class UserGroupAppView(BaseAppView):
481 def __init__(self, context, request):
486 def __init__(self, context, request):
482 super(UserGroupAppView, self).__init__(context, request)
487 super(UserGroupAppView, self).__init__(context, request)
483 self.db_user_group = request.db_user_group
488 self.db_user_group = request.db_user_group
484 self.db_user_group_name = self.db_user_group.users_group_name
489 self.db_user_group_name = self.db_user_group.users_group_name
485
490
486
491
487 class UserAppView(BaseAppView):
492 class UserAppView(BaseAppView):
488 def __init__(self, context, request):
493 def __init__(self, context, request):
489 super(UserAppView, self).__init__(context, request)
494 super(UserAppView, self).__init__(context, request)
490 self.db_user = request.db_user
495 self.db_user = request.db_user
491 self.db_user_id = self.db_user.user_id
496 self.db_user_id = self.db_user.user_id
492
497
493 _ = self.request.translate
498 _ = self.request.translate
494 if not request.db_user_supports_default:
499 if not request.db_user_supports_default:
495 if self.db_user.username == User.DEFAULT_USER:
500 if self.db_user.username == User.DEFAULT_USER:
496 h.flash(_("Editing user `{}` is disabled.".format(
501 h.flash(_("Editing user `{}` is disabled.".format(
497 User.DEFAULT_USER)), category='warning')
502 User.DEFAULT_USER)), category='warning')
498 raise HTTPFound(h.route_path('users'))
503 raise HTTPFound(h.route_path('users'))
499
504
500
505
501 class DataGridAppView(object):
506 class DataGridAppView(object):
502 """
507 """
503 Common class to have re-usable grid rendering components
508 Common class to have re-usable grid rendering components
504 """
509 """
505
510
506 def _extract_ordering(self, request, column_map=None):
511 def _extract_ordering(self, request, column_map=None):
507 column_map = column_map or {}
512 column_map = column_map or {}
508 column_index = safe_int(request.GET.get('order[0][column]'))
513 column_index = safe_int(request.GET.get('order[0][column]'))
509 order_dir = request.GET.get(
514 order_dir = request.GET.get(
510 'order[0][dir]', 'desc')
515 'order[0][dir]', 'desc')
511 order_by = request.GET.get(
516 order_by = request.GET.get(
512 'columns[%s][data][sort]' % column_index, 'name_raw')
517 'columns[%s][data][sort]' % column_index, 'name_raw')
513
518
514 # translate datatable to DB columns
519 # translate datatable to DB columns
515 order_by = column_map.get(order_by) or order_by
520 order_by = column_map.get(order_by) or order_by
516
521
517 search_q = request.GET.get('search[value]')
522 search_q = request.GET.get('search[value]')
518 return search_q, order_by, order_dir
523 return search_q, order_by, order_dir
519
524
520 def _extract_chunk(self, request):
525 def _extract_chunk(self, request):
521 start = safe_int(request.GET.get('start'), 0)
526 start = safe_int(request.GET.get('start'), 0)
522 length = safe_int(request.GET.get('length'), 25)
527 length = safe_int(request.GET.get('length'), 25)
523 draw = safe_int(request.GET.get('draw'))
528 draw = safe_int(request.GET.get('draw'))
524 return draw, start, length
529 return draw, start, length
525
530
526 def _get_order_col(self, order_by, model):
531 def _get_order_col(self, order_by, model):
527 if isinstance(order_by, compat.string_types):
532 if isinstance(order_by, compat.string_types):
528 try:
533 try:
529 return operator.attrgetter(order_by)(model)
534 return operator.attrgetter(order_by)(model)
530 except AttributeError:
535 except AttributeError:
531 return None
536 return None
532 else:
537 else:
533 return order_by
538 return order_by
534
539
535
540
536 class BaseReferencesView(RepoAppView):
541 class BaseReferencesView(RepoAppView):
537 """
542 """
538 Base for reference view for branches, tags and bookmarks.
543 Base for reference view for branches, tags and bookmarks.
539 """
544 """
540 def load_default_context(self):
545 def load_default_context(self):
541 c = self._get_local_tmpl_context()
546 c = self._get_local_tmpl_context()
542 return c
547 return c
543
548
544 def load_refs_context(self, ref_items, partials_template):
549 def load_refs_context(self, ref_items, partials_template):
545 _render = self.request.get_partial_renderer(partials_template)
550 _render = self.request.get_partial_renderer(partials_template)
546 pre_load = ["author", "date", "message", "parents"]
551 pre_load = ["author", "date", "message", "parents"]
547
552
548 is_svn = h.is_svn(self.rhodecode_vcs_repo)
553 is_svn = h.is_svn(self.rhodecode_vcs_repo)
549 is_hg = h.is_hg(self.rhodecode_vcs_repo)
554 is_hg = h.is_hg(self.rhodecode_vcs_repo)
550
555
551 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
556 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
552
557
553 closed_refs = {}
558 closed_refs = {}
554 if is_hg:
559 if is_hg:
555 closed_refs = self.rhodecode_vcs_repo.branches_closed
560 closed_refs = self.rhodecode_vcs_repo.branches_closed
556
561
557 data = []
562 data = []
558 for ref_name, commit_id in ref_items:
563 for ref_name, commit_id in ref_items:
559 commit = self.rhodecode_vcs_repo.get_commit(
564 commit = self.rhodecode_vcs_repo.get_commit(
560 commit_id=commit_id, pre_load=pre_load)
565 commit_id=commit_id, pre_load=pre_load)
561 closed = ref_name in closed_refs
566 closed = ref_name in closed_refs
562
567
563 # TODO: johbo: Unify generation of reference links
568 # TODO: johbo: Unify generation of reference links
564 use_commit_id = '/' in ref_name or is_svn
569 use_commit_id = '/' in ref_name or is_svn
565
570
566 if use_commit_id:
571 if use_commit_id:
567 files_url = h.route_path(
572 files_url = h.route_path(
568 'repo_files',
573 'repo_files',
569 repo_name=self.db_repo_name,
574 repo_name=self.db_repo_name,
570 f_path=ref_name if is_svn else '',
575 f_path=ref_name if is_svn else '',
571 commit_id=commit_id,
576 commit_id=commit_id,
572 _query=dict(at=ref_name)
577 _query=dict(at=ref_name)
573 )
578 )
574
579
575 else:
580 else:
576 files_url = h.route_path(
581 files_url = h.route_path(
577 'repo_files',
582 'repo_files',
578 repo_name=self.db_repo_name,
583 repo_name=self.db_repo_name,
579 f_path=ref_name if is_svn else '',
584 f_path=ref_name if is_svn else '',
580 commit_id=ref_name,
585 commit_id=ref_name,
581 _query=dict(at=ref_name)
586 _query=dict(at=ref_name)
582 )
587 )
583
588
584 data.append({
589 data.append({
585 "name": _render('name', ref_name, files_url, closed),
590 "name": _render('name', ref_name, files_url, closed),
586 "name_raw": ref_name,
591 "name_raw": ref_name,
587 "date": _render('date', commit.date),
592 "date": _render('date', commit.date),
588 "date_raw": datetime_to_time(commit.date),
593 "date_raw": datetime_to_time(commit.date),
589 "author": _render('author', commit.author),
594 "author": _render('author', commit.author),
590 "commit": _render(
595 "commit": _render(
591 'commit', commit.message, commit.raw_id, commit.idx),
596 'commit', commit.message, commit.raw_id, commit.idx),
592 "commit_raw": commit.idx,
597 "commit_raw": commit.idx,
593 "compare": _render(
598 "compare": _render(
594 'compare', format_ref_id(ref_name, commit.raw_id)),
599 'compare', format_ref_id(ref_name, commit.raw_id)),
595 })
600 })
596
601
597 return data
602 return data
598
603
599
604
600 class RepoRoutePredicate(object):
605 class RepoRoutePredicate(object):
601 def __init__(self, val, config):
606 def __init__(self, val, config):
602 self.val = val
607 self.val = val
603
608
604 def text(self):
609 def text(self):
605 return 'repo_route = %s' % self.val
610 return 'repo_route = %s' % self.val
606
611
607 phash = text
612 phash = text
608
613
609 def __call__(self, info, request):
614 def __call__(self, info, request):
610 if hasattr(request, 'vcs_call'):
615 if hasattr(request, 'vcs_call'):
611 # skip vcs calls
616 # skip vcs calls
612 return
617 return
613
618
614 repo_name = info['match']['repo_name']
619 repo_name = info['match']['repo_name']
615 repo_model = repo.RepoModel()
620 repo_model = repo.RepoModel()
616
621
617 by_name_match = repo_model.get_by_repo_name(repo_name, cache=False)
622 by_name_match = repo_model.get_by_repo_name(repo_name, cache=False)
618
623
619 def redirect_if_creating(route_info, db_repo):
624 def redirect_if_creating(route_info, db_repo):
620 skip_views = ['edit_repo_advanced_delete']
625 skip_views = ['edit_repo_advanced_delete']
621 route = route_info['route']
626 route = route_info['route']
622 # we should skip delete view so we can actually "remove" repositories
627 # we should skip delete view so we can actually "remove" repositories
623 # if they get stuck in creating state.
628 # if they get stuck in creating state.
624 if route.name in skip_views:
629 if route.name in skip_views:
625 return
630 return
626
631
627 if db_repo.repo_state in [repo.Repository.STATE_PENDING]:
632 if db_repo.repo_state in [repo.Repository.STATE_PENDING]:
628 repo_creating_url = request.route_path(
633 repo_creating_url = request.route_path(
629 'repo_creating', repo_name=db_repo.repo_name)
634 'repo_creating', repo_name=db_repo.repo_name)
630 raise HTTPFound(repo_creating_url)
635 raise HTTPFound(repo_creating_url)
631
636
632 if by_name_match:
637 if by_name_match:
633 # register this as request object we can re-use later
638 # register this as request object we can re-use later
634 request.db_repo = by_name_match
639 request.db_repo = by_name_match
635 redirect_if_creating(info, by_name_match)
640 redirect_if_creating(info, by_name_match)
636 return True
641 return True
637
642
638 by_id_match = repo_model.get_repo_by_id(repo_name)
643 by_id_match = repo_model.get_repo_by_id(repo_name)
639 if by_id_match:
644 if by_id_match:
640 request.db_repo = by_id_match
645 request.db_repo = by_id_match
641 redirect_if_creating(info, by_id_match)
646 redirect_if_creating(info, by_id_match)
642 return True
647 return True
643
648
644 return False
649 return False
645
650
646
651
647 class RepoForbidArchivedRoutePredicate(object):
652 class RepoForbidArchivedRoutePredicate(object):
648 def __init__(self, val, config):
653 def __init__(self, val, config):
649 self.val = val
654 self.val = val
650
655
651 def text(self):
656 def text(self):
652 return 'repo_forbid_archived = %s' % self.val
657 return 'repo_forbid_archived = %s' % self.val
653
658
654 phash = text
659 phash = text
655
660
656 def __call__(self, info, request):
661 def __call__(self, info, request):
657 _ = request.translate
662 _ = request.translate
658 rhodecode_db_repo = request.db_repo
663 rhodecode_db_repo = request.db_repo
659
664
660 log.debug(
665 log.debug(
661 '%s checking if archived flag for repo for %s',
666 '%s checking if archived flag for repo for %s',
662 self.__class__.__name__, rhodecode_db_repo.repo_name)
667 self.__class__.__name__, rhodecode_db_repo.repo_name)
663
668
664 if rhodecode_db_repo.archived:
669 if rhodecode_db_repo.archived:
665 log.warning('Current view is not supported for archived repo:%s',
670 log.warning('Current view is not supported for archived repo:%s',
666 rhodecode_db_repo.repo_name)
671 rhodecode_db_repo.repo_name)
667
672
668 h.flash(
673 h.flash(
669 h.literal(_('Action not supported for archived repository.')),
674 h.literal(_('Action not supported for archived repository.')),
670 category='warning')
675 category='warning')
671 summary_url = request.route_path(
676 summary_url = request.route_path(
672 'repo_summary', repo_name=rhodecode_db_repo.repo_name)
677 'repo_summary', repo_name=rhodecode_db_repo.repo_name)
673 raise HTTPFound(summary_url)
678 raise HTTPFound(summary_url)
674 return True
679 return True
675
680
676
681
677 class RepoTypeRoutePredicate(object):
682 class RepoTypeRoutePredicate(object):
678 def __init__(self, val, config):
683 def __init__(self, val, config):
679 self.val = val or ['hg', 'git', 'svn']
684 self.val = val or ['hg', 'git', 'svn']
680
685
681 def text(self):
686 def text(self):
682 return 'repo_accepted_type = %s' % self.val
687 return 'repo_accepted_type = %s' % self.val
683
688
684 phash = text
689 phash = text
685
690
686 def __call__(self, info, request):
691 def __call__(self, info, request):
687 if hasattr(request, 'vcs_call'):
692 if hasattr(request, 'vcs_call'):
688 # skip vcs calls
693 # skip vcs calls
689 return
694 return
690
695
691 rhodecode_db_repo = request.db_repo
696 rhodecode_db_repo = request.db_repo
692
697
693 log.debug(
698 log.debug(
694 '%s checking repo type for %s in %s',
699 '%s checking repo type for %s in %s',
695 self.__class__.__name__, rhodecode_db_repo.repo_type, self.val)
700 self.__class__.__name__, rhodecode_db_repo.repo_type, self.val)
696
701
697 if rhodecode_db_repo.repo_type in self.val:
702 if rhodecode_db_repo.repo_type in self.val:
698 return True
703 return True
699 else:
704 else:
700 log.warning('Current view is not supported for repo type:%s',
705 log.warning('Current view is not supported for repo type:%s',
701 rhodecode_db_repo.repo_type)
706 rhodecode_db_repo.repo_type)
702 return False
707 return False
703
708
704
709
705 class RepoGroupRoutePredicate(object):
710 class RepoGroupRoutePredicate(object):
706 def __init__(self, val, config):
711 def __init__(self, val, config):
707 self.val = val
712 self.val = val
708
713
709 def text(self):
714 def text(self):
710 return 'repo_group_route = %s' % self.val
715 return 'repo_group_route = %s' % self.val
711
716
712 phash = text
717 phash = text
713
718
714 def __call__(self, info, request):
719 def __call__(self, info, request):
715 if hasattr(request, 'vcs_call'):
720 if hasattr(request, 'vcs_call'):
716 # skip vcs calls
721 # skip vcs calls
717 return
722 return
718
723
719 repo_group_name = info['match']['repo_group_name']
724 repo_group_name = info['match']['repo_group_name']
720 repo_group_model = repo_group.RepoGroupModel()
725 repo_group_model = repo_group.RepoGroupModel()
721 by_name_match = repo_group_model.get_by_group_name(repo_group_name, cache=False)
726 by_name_match = repo_group_model.get_by_group_name(repo_group_name, cache=False)
722
727
723 if by_name_match:
728 if by_name_match:
724 # register this as request object we can re-use later
729 # register this as request object we can re-use later
725 request.db_repo_group = by_name_match
730 request.db_repo_group = by_name_match
726 return True
731 return True
727
732
728 return False
733 return False
729
734
730
735
731 class UserGroupRoutePredicate(object):
736 class UserGroupRoutePredicate(object):
732 def __init__(self, val, config):
737 def __init__(self, val, config):
733 self.val = val
738 self.val = val
734
739
735 def text(self):
740 def text(self):
736 return 'user_group_route = %s' % self.val
741 return 'user_group_route = %s' % self.val
737
742
738 phash = text
743 phash = text
739
744
740 def __call__(self, info, request):
745 def __call__(self, info, request):
741 if hasattr(request, 'vcs_call'):
746 if hasattr(request, 'vcs_call'):
742 # skip vcs calls
747 # skip vcs calls
743 return
748 return
744
749
745 user_group_id = info['match']['user_group_id']
750 user_group_id = info['match']['user_group_id']
746 user_group_model = user_group.UserGroup()
751 user_group_model = user_group.UserGroup()
747 by_id_match = user_group_model.get(user_group_id, cache=False)
752 by_id_match = user_group_model.get(user_group_id, cache=False)
748
753
749 if by_id_match:
754 if by_id_match:
750 # register this as request object we can re-use later
755 # register this as request object we can re-use later
751 request.db_user_group = by_id_match
756 request.db_user_group = by_id_match
752 return True
757 return True
753
758
754 return False
759 return False
755
760
756
761
757 class UserRoutePredicateBase(object):
762 class UserRoutePredicateBase(object):
758 supports_default = None
763 supports_default = None
759
764
760 def __init__(self, val, config):
765 def __init__(self, val, config):
761 self.val = val
766 self.val = val
762
767
763 def text(self):
768 def text(self):
764 raise NotImplementedError()
769 raise NotImplementedError()
765
770
766 def __call__(self, info, request):
771 def __call__(self, info, request):
767 if hasattr(request, 'vcs_call'):
772 if hasattr(request, 'vcs_call'):
768 # skip vcs calls
773 # skip vcs calls
769 return
774 return
770
775
771 user_id = info['match']['user_id']
776 user_id = info['match']['user_id']
772 user_model = user.User()
777 user_model = user.User()
773 by_id_match = user_model.get(user_id, cache=False)
778 by_id_match = user_model.get(user_id, cache=False)
774
779
775 if by_id_match:
780 if by_id_match:
776 # register this as request object we can re-use later
781 # register this as request object we can re-use later
777 request.db_user = by_id_match
782 request.db_user = by_id_match
778 request.db_user_supports_default = self.supports_default
783 request.db_user_supports_default = self.supports_default
779 return True
784 return True
780
785
781 return False
786 return False
782
787
783
788
784 class UserRoutePredicate(UserRoutePredicateBase):
789 class UserRoutePredicate(UserRoutePredicateBase):
785 supports_default = False
790 supports_default = False
786
791
787 def text(self):
792 def text(self):
788 return 'user_route = %s' % self.val
793 return 'user_route = %s' % self.val
789
794
790 phash = text
795 phash = text
791
796
792
797
793 class UserRouteWithDefaultPredicate(UserRoutePredicateBase):
798 class UserRouteWithDefaultPredicate(UserRoutePredicateBase):
794 supports_default = True
799 supports_default = True
795
800
796 def text(self):
801 def text(self):
797 return 'user_with_default_route = %s' % self.val
802 return 'user_with_default_route = %s' % self.val
798
803
799 phash = text
804 phash = text
800
805
801
806
802 def includeme(config):
807 def includeme(config):
803 config.add_route_predicate(
808 config.add_route_predicate(
804 'repo_route', RepoRoutePredicate)
809 'repo_route', RepoRoutePredicate)
805 config.add_route_predicate(
810 config.add_route_predicate(
806 'repo_accepted_types', RepoTypeRoutePredicate)
811 'repo_accepted_types', RepoTypeRoutePredicate)
807 config.add_route_predicate(
812 config.add_route_predicate(
808 'repo_forbid_when_archived', RepoForbidArchivedRoutePredicate)
813 'repo_forbid_when_archived', RepoForbidArchivedRoutePredicate)
809 config.add_route_predicate(
814 config.add_route_predicate(
810 'repo_group_route', RepoGroupRoutePredicate)
815 'repo_group_route', RepoGroupRoutePredicate)
811 config.add_route_predicate(
816 config.add_route_predicate(
812 'user_group_route', UserGroupRoutePredicate)
817 'user_group_route', UserGroupRoutePredicate)
813 config.add_route_predicate(
818 config.add_route_predicate(
814 'user_route_with_default', UserRouteWithDefaultPredicate)
819 'user_route_with_default', UserRouteWithDefaultPredicate)
815 config.add_route_predicate(
820 config.add_route_predicate(
816 'user_route', UserRoutePredicate)
821 'user_route', UserRoutePredicate)
@@ -1,358 +1,355 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import logging
22 import logging
23
23
24 from pyramid.httpexceptions import HTTPNotFound, HTTPFound
24 from pyramid.httpexceptions import HTTPNotFound, HTTPFound
25
25
26 from pyramid.renderers import render
26 from pyramid.renderers import render
27 from pyramid.response import Response
27 from pyramid.response import Response
28
28
29 from rhodecode.apps._base import RepoAppView
29 from rhodecode.apps._base import RepoAppView
30 import rhodecode.lib.helpers as h
30 import rhodecode.lib.helpers as h
31 from rhodecode.lib.auth import (
31 from rhodecode.lib.auth import (
32 LoginRequired, HasRepoPermissionAnyDecorator)
32 LoginRequired, HasRepoPermissionAnyDecorator)
33
33
34 from rhodecode.lib.ext_json import json
34 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.graphmod import _colored, _dagwalker
35 from rhodecode.lib.graphmod import _colored, _dagwalker
36 from rhodecode.lib.helpers import RepoPage
36 from rhodecode.lib.helpers import RepoPage
37 from rhodecode.lib.utils2 import safe_int, safe_str, str2bool, safe_unicode
37 from rhodecode.lib.utils2 import safe_int, safe_str, str2bool, safe_unicode
38 from rhodecode.lib.vcs.exceptions import (
38 from rhodecode.lib.vcs.exceptions import (
39 RepositoryError, CommitDoesNotExistError,
39 RepositoryError, CommitDoesNotExistError,
40 CommitError, NodeDoesNotExistError, EmptyRepositoryError)
40 CommitError, NodeDoesNotExistError, EmptyRepositoryError)
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44 DEFAULT_CHANGELOG_SIZE = 20
44 DEFAULT_CHANGELOG_SIZE = 20
45
45
46
46
47 class RepoChangelogView(RepoAppView):
47 class RepoChangelogView(RepoAppView):
48
48
49 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
49 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
50 """
50 """
51 This is a safe way to get commit. If an error occurs it redirects to
51 This is a safe way to get commit. If an error occurs it redirects to
52 tip with proper message
52 tip with proper message
53
53
54 :param commit_id: id of commit to fetch
54 :param commit_id: id of commit to fetch
55 :param redirect_after: toggle redirection
55 :param redirect_after: toggle redirection
56 """
56 """
57 _ = self.request.translate
57 _ = self.request.translate
58
58
59 try:
59 try:
60 return self.rhodecode_vcs_repo.get_commit(commit_id)
60 return self.rhodecode_vcs_repo.get_commit(commit_id)
61 except EmptyRepositoryError:
61 except EmptyRepositoryError:
62 if not redirect_after:
62 if not redirect_after:
63 return None
63 return None
64
64
65 h.flash(h.literal(
65 h.flash(h.literal(
66 _('There are no commits yet')), category='warning')
66 _('There are no commits yet')), category='warning')
67 raise HTTPFound(
67 raise HTTPFound(
68 h.route_path('repo_summary', repo_name=self.db_repo_name))
68 h.route_path('repo_summary', repo_name=self.db_repo_name))
69
69
70 except (CommitDoesNotExistError, LookupError):
70 except (CommitDoesNotExistError, LookupError):
71 msg = _('No such commit exists for this repository')
71 msg = _('No such commit exists for this repository')
72 h.flash(msg, category='error')
72 h.flash(msg, category='error')
73 raise HTTPNotFound()
73 raise HTTPNotFound()
74 except RepositoryError as e:
74 except RepositoryError as e:
75 h.flash(h.escape(safe_str(e)), category='error')
75 h.flash(h.escape(safe_str(e)), category='error')
76 raise HTTPNotFound()
76 raise HTTPNotFound()
77
77
78 def _graph(self, repo, commits, prev_data=None, next_data=None):
78 def _graph(self, repo, commits, prev_data=None, next_data=None):
79 """
79 """
80 Generates a DAG graph for repo
80 Generates a DAG graph for repo
81
81
82 :param repo: repo instance
82 :param repo: repo instance
83 :param commits: list of commits
83 :param commits: list of commits
84 """
84 """
85 if not commits:
85 if not commits:
86 return json.dumps([]), json.dumps([])
86 return json.dumps([]), json.dumps([])
87
87
88 def serialize(commit, parents=True):
88 def serialize(commit, parents=True):
89 data = dict(
89 data = dict(
90 raw_id=commit.raw_id,
90 raw_id=commit.raw_id,
91 idx=commit.idx,
91 idx=commit.idx,
92 branch=None,
92 branch=None,
93 )
93 )
94 if parents:
94 if parents:
95 data['parents'] = [
95 data['parents'] = [
96 serialize(x, parents=False) for x in commit.parents]
96 serialize(x, parents=False) for x in commit.parents]
97 return data
97 return data
98
98
99 prev_data = prev_data or []
99 prev_data = prev_data or []
100 next_data = next_data or []
100 next_data = next_data or []
101
101
102 current = [serialize(x) for x in commits]
102 current = [serialize(x) for x in commits]
103 commits = prev_data + current + next_data
103 commits = prev_data + current + next_data
104
104
105 dag = _dagwalker(repo, commits)
105 dag = _dagwalker(repo, commits)
106
106
107 data = [[commit_id, vtx, edges, branch]
107 data = [[commit_id, vtx, edges, branch]
108 for commit_id, vtx, edges, branch in _colored(dag)]
108 for commit_id, vtx, edges, branch in _colored(dag)]
109 return json.dumps(data), json.dumps(current)
109 return json.dumps(data), json.dumps(current)
110
110
111 def _check_if_valid_branch(self, branch_name, repo_name, f_path):
111 def _check_if_valid_branch(self, branch_name, repo_name, f_path):
112 if branch_name not in self.rhodecode_vcs_repo.branches_all:
112 if branch_name not in self.rhodecode_vcs_repo.branches_all:
113 h.flash(u'Branch {} is not found.'.format(h.escape(safe_unicode(branch_name))),
113 h.flash(u'Branch {} is not found.'.format(h.escape(safe_unicode(branch_name))),
114 category='warning')
114 category='warning')
115 redirect_url = h.route_path(
115 redirect_url = h.route_path(
116 'repo_commits_file', repo_name=repo_name,
116 'repo_commits_file', repo_name=repo_name,
117 commit_id=branch_name, f_path=f_path or '')
117 commit_id=branch_name, f_path=f_path or '')
118 raise HTTPFound(redirect_url)
118 raise HTTPFound(redirect_url)
119
119
120 def _load_changelog_data(
120 def _load_changelog_data(
121 self, c, collection, page, chunk_size, branch_name=None,
121 self, c, collection, page, chunk_size, branch_name=None,
122 dynamic=False, f_path=None, commit_id=None):
122 dynamic=False, f_path=None, commit_id=None):
123
123
124 def url_generator(page_num):
124 def url_generator(page_num):
125 query_params = {
125 query_params = {
126 'page': page_num
126 'page': page_num
127 }
127 }
128
128
129 if branch_name:
129 if branch_name:
130 query_params.update({
130 query_params.update({
131 'branch': branch_name
131 'branch': branch_name
132 })
132 })
133
133
134 if f_path:
134 if f_path:
135 # changelog for file
135 # changelog for file
136 return h.route_path(
136 return h.route_path(
137 'repo_commits_file',
137 'repo_commits_file',
138 repo_name=c.rhodecode_db_repo.repo_name,
138 repo_name=c.rhodecode_db_repo.repo_name,
139 commit_id=commit_id, f_path=f_path,
139 commit_id=commit_id, f_path=f_path,
140 _query=query_params)
140 _query=query_params)
141 else:
141 else:
142 return h.route_path(
142 return h.route_path(
143 'repo_commits',
143 'repo_commits',
144 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
144 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
145
145
146 c.total_cs = len(collection)
146 c.total_cs = len(collection)
147 c.showing_commits = min(chunk_size, c.total_cs)
147 c.showing_commits = min(chunk_size, c.total_cs)
148 c.pagination = RepoPage(collection, page=page, item_count=c.total_cs,
148 c.pagination = RepoPage(collection, page=page, item_count=c.total_cs,
149 items_per_page=chunk_size, url_maker=url_generator)
149 items_per_page=chunk_size, url_maker=url_generator)
150
150
151 c.next_page = c.pagination.next_page
151 c.next_page = c.pagination.next_page
152 c.prev_page = c.pagination.previous_page
152 c.prev_page = c.pagination.previous_page
153
153
154 if dynamic:
154 if dynamic:
155 if self.request.GET.get('chunk') != 'next':
155 if self.request.GET.get('chunk') != 'next':
156 c.next_page = None
156 c.next_page = None
157 if self.request.GET.get('chunk') != 'prev':
157 if self.request.GET.get('chunk') != 'prev':
158 c.prev_page = None
158 c.prev_page = None
159
159
160 page_commit_ids = [x.raw_id for x in c.pagination]
160 page_commit_ids = [x.raw_id for x in c.pagination]
161 c.comments = c.rhodecode_db_repo.get_comments(page_commit_ids)
161 c.comments = c.rhodecode_db_repo.get_comments(page_commit_ids)
162 c.statuses = c.rhodecode_db_repo.statuses(page_commit_ids)
162 c.statuses = c.rhodecode_db_repo.statuses(page_commit_ids)
163
163
164 def load_default_context(self):
164 def load_default_context(self):
165 c = self._get_local_tmpl_context(include_app_defaults=True)
165 c = self._get_local_tmpl_context(include_app_defaults=True)
166
166
167 c.rhodecode_repo = self.rhodecode_vcs_repo
167 c.rhodecode_repo = self.rhodecode_vcs_repo
168
168
169 return c
169 return c
170
170
171 def _get_preload_attrs(self):
172 pre_load = ['author', 'branch', 'date', 'message', 'parents',
173 'obsolete', 'phase', 'hidden']
174 return pre_load
175
176 @LoginRequired()
171 @LoginRequired()
177 @HasRepoPermissionAnyDecorator(
172 @HasRepoPermissionAnyDecorator(
178 'repository.read', 'repository.write', 'repository.admin')
173 'repository.read', 'repository.write', 'repository.admin')
179 def repo_changelog(self):
174 def repo_changelog(self):
180 c = self.load_default_context()
175 c = self.load_default_context()
181
176
182 commit_id = self.request.matchdict.get('commit_id')
177 commit_id = self.request.matchdict.get('commit_id')
183 f_path = self._get_f_path(self.request.matchdict)
178 f_path = self._get_f_path(self.request.matchdict)
184 show_hidden = str2bool(self.request.GET.get('evolve'))
179 show_hidden = str2bool(self.request.GET.get('evolve'))
185
180
186 chunk_size = 20
181 chunk_size = 20
187
182
188 c.branch_name = branch_name = self.request.GET.get('branch') or ''
183 c.branch_name = branch_name = self.request.GET.get('branch') or ''
189 c.book_name = book_name = self.request.GET.get('bookmark') or ''
184 c.book_name = book_name = self.request.GET.get('bookmark') or ''
190 c.f_path = f_path
185 c.f_path = f_path
191 c.commit_id = commit_id
186 c.commit_id = commit_id
192 c.show_hidden = show_hidden
187 c.show_hidden = show_hidden
193
188
194 hist_limit = safe_int(self.request.GET.get('limit')) or None
189 hist_limit = safe_int(self.request.GET.get('limit')) or None
195
190
196 p = safe_int(self.request.GET.get('page', 1), 1)
191 p = safe_int(self.request.GET.get('page', 1), 1)
197
192
198 c.selected_name = branch_name or book_name
193 c.selected_name = branch_name or book_name
199 if not commit_id and branch_name:
194 if not commit_id and branch_name:
200 self._check_if_valid_branch(branch_name, self.db_repo_name, f_path)
195 self._check_if_valid_branch(branch_name, self.db_repo_name, f_path)
201
196
202 c.changelog_for_path = f_path
197 c.changelog_for_path = f_path
203 pre_load = self._get_preload_attrs()
198 pre_load = self.get_commit_preload_attrs()
204
199
205 partial_xhr = self.request.environ.get('HTTP_X_PARTIAL_XHR')
200 partial_xhr = self.request.environ.get('HTTP_X_PARTIAL_XHR')
206
201
207 try:
202 try:
208 if f_path:
203 if f_path:
209 log.debug('generating changelog for path %s', f_path)
204 log.debug('generating changelog for path %s', f_path)
210 # get the history for the file !
205 # get the history for the file !
211 base_commit = self.rhodecode_vcs_repo.get_commit(commit_id)
206 base_commit = self.rhodecode_vcs_repo.get_commit(commit_id)
212
207
213 try:
208 try:
214 collection = base_commit.get_path_history(
209 collection = base_commit.get_path_history(
215 f_path, limit=hist_limit, pre_load=pre_load)
210 f_path, limit=hist_limit, pre_load=pre_load)
216 if collection and partial_xhr:
211 if collection and partial_xhr:
217 # for ajax call we remove first one since we're looking
212 # for ajax call we remove first one since we're looking
218 # at it right now in the context of a file commit
213 # at it right now in the context of a file commit
219 collection.pop(0)
214 collection.pop(0)
220 except (NodeDoesNotExistError, CommitError):
215 except (NodeDoesNotExistError, CommitError):
221 # this node is not present at tip!
216 # this node is not present at tip!
222 try:
217 try:
223 commit = self._get_commit_or_redirect(commit_id)
218 commit = self._get_commit_or_redirect(commit_id)
224 collection = commit.get_path_history(f_path)
219 collection = commit.get_path_history(f_path)
225 except RepositoryError as e:
220 except RepositoryError as e:
226 h.flash(safe_str(e), category='warning')
221 h.flash(safe_str(e), category='warning')
227 redirect_url = h.route_path(
222 redirect_url = h.route_path(
228 'repo_commits', repo_name=self.db_repo_name)
223 'repo_commits', repo_name=self.db_repo_name)
229 raise HTTPFound(redirect_url)
224 raise HTTPFound(redirect_url)
230 collection = list(reversed(collection))
225 collection = list(reversed(collection))
231 else:
226 else:
232 collection = self.rhodecode_vcs_repo.get_commits(
227 collection = self.rhodecode_vcs_repo.get_commits(
233 branch_name=branch_name, show_hidden=show_hidden,
228 branch_name=branch_name, show_hidden=show_hidden,
234 pre_load=pre_load, translate_tags=False)
229 pre_load=pre_load, translate_tags=False)
235
230
236 self._load_changelog_data(
231 self._load_changelog_data(
237 c, collection, p, chunk_size, c.branch_name,
232 c, collection, p, chunk_size, c.branch_name,
238 f_path=f_path, commit_id=commit_id)
233 f_path=f_path, commit_id=commit_id)
239
234
240 except EmptyRepositoryError as e:
235 except EmptyRepositoryError as e:
241 h.flash(h.escape(safe_str(e)), category='warning')
236 h.flash(h.escape(safe_str(e)), category='warning')
242 raise HTTPFound(
237 raise HTTPFound(
243 h.route_path('repo_summary', repo_name=self.db_repo_name))
238 h.route_path('repo_summary', repo_name=self.db_repo_name))
244 except HTTPFound:
239 except HTTPFound:
245 raise
240 raise
246 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
241 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
247 log.exception(safe_str(e))
242 log.exception(safe_str(e))
248 h.flash(h.escape(safe_str(e)), category='error')
243 h.flash(h.escape(safe_str(e)), category='error')
249
244
250 if commit_id:
245 if commit_id:
251 # from single commit page, we redirect to main commits
246 # from single commit page, we redirect to main commits
252 raise HTTPFound(
247 raise HTTPFound(
253 h.route_path('repo_commits', repo_name=self.db_repo_name))
248 h.route_path('repo_commits', repo_name=self.db_repo_name))
254 else:
249 else:
255 # otherwise we redirect to summary
250 # otherwise we redirect to summary
256 raise HTTPFound(
251 raise HTTPFound(
257 h.route_path('repo_summary', repo_name=self.db_repo_name))
252 h.route_path('repo_summary', repo_name=self.db_repo_name))
258
253
254
255
259 if partial_xhr or self.request.environ.get('HTTP_X_PJAX'):
256 if partial_xhr or self.request.environ.get('HTTP_X_PJAX'):
260 # case when loading dynamic file history in file view
257 # case when loading dynamic file history in file view
261 # loading from ajax, we don't want the first result, it's popped
258 # loading from ajax, we don't want the first result, it's popped
262 # in the code above
259 # in the code above
263 html = render(
260 html = render(
264 'rhodecode:templates/commits/changelog_file_history.mako',
261 'rhodecode:templates/commits/changelog_file_history.mako',
265 self._get_template_context(c), self.request)
262 self._get_template_context(c), self.request)
266 return Response(html)
263 return Response(html)
267
264
268 commit_ids = []
265 commit_ids = []
269 if not f_path:
266 if not f_path:
270 # only load graph data when not in file history mode
267 # only load graph data when not in file history mode
271 commit_ids = c.pagination
268 commit_ids = c.pagination
272
269
273 c.graph_data, c.graph_commits = self._graph(
270 c.graph_data, c.graph_commits = self._graph(
274 self.rhodecode_vcs_repo, commit_ids)
271 self.rhodecode_vcs_repo, commit_ids)
275
272
276 return self._get_template_context(c)
273 return self._get_template_context(c)
277
274
278 @LoginRequired()
275 @LoginRequired()
279 @HasRepoPermissionAnyDecorator(
276 @HasRepoPermissionAnyDecorator(
280 'repository.read', 'repository.write', 'repository.admin')
277 'repository.read', 'repository.write', 'repository.admin')
281 def repo_commits_elements(self):
278 def repo_commits_elements(self):
282 c = self.load_default_context()
279 c = self.load_default_context()
283 commit_id = self.request.matchdict.get('commit_id')
280 commit_id = self.request.matchdict.get('commit_id')
284 f_path = self._get_f_path(self.request.matchdict)
281 f_path = self._get_f_path(self.request.matchdict)
285 show_hidden = str2bool(self.request.GET.get('evolve'))
282 show_hidden = str2bool(self.request.GET.get('evolve'))
286
283
287 chunk_size = 20
284 chunk_size = 20
288 hist_limit = safe_int(self.request.GET.get('limit')) or None
285 hist_limit = safe_int(self.request.GET.get('limit')) or None
289
286
290 def wrap_for_error(err):
287 def wrap_for_error(err):
291 html = '<tr>' \
288 html = '<tr>' \
292 '<td colspan="9" class="alert alert-error">ERROR: {}</td>' \
289 '<td colspan="9" class="alert alert-error">ERROR: {}</td>' \
293 '</tr>'.format(err)
290 '</tr>'.format(err)
294 return Response(html)
291 return Response(html)
295
292
296 c.branch_name = branch_name = self.request.GET.get('branch') or ''
293 c.branch_name = branch_name = self.request.GET.get('branch') or ''
297 c.book_name = book_name = self.request.GET.get('bookmark') or ''
294 c.book_name = book_name = self.request.GET.get('bookmark') or ''
298 c.f_path = f_path
295 c.f_path = f_path
299 c.commit_id = commit_id
296 c.commit_id = commit_id
300 c.show_hidden = show_hidden
297 c.show_hidden = show_hidden
301
298
302 c.selected_name = branch_name or book_name
299 c.selected_name = branch_name or book_name
303 if branch_name and branch_name not in self.rhodecode_vcs_repo.branches_all:
300 if branch_name and branch_name not in self.rhodecode_vcs_repo.branches_all:
304 return wrap_for_error(
301 return wrap_for_error(
305 safe_str('Branch: {} is not valid'.format(branch_name)))
302 safe_str('Branch: {} is not valid'.format(branch_name)))
306
303
307 pre_load = self._get_preload_attrs()
304 pre_load = self.get_commit_preload_attrs()
308
305
309 if f_path:
306 if f_path:
310 try:
307 try:
311 base_commit = self.rhodecode_vcs_repo.get_commit(commit_id)
308 base_commit = self.rhodecode_vcs_repo.get_commit(commit_id)
312 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
309 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
313 log.exception(safe_str(e))
310 log.exception(safe_str(e))
314 raise HTTPFound(
311 raise HTTPFound(
315 h.route_path('repo_commits', repo_name=self.db_repo_name))
312 h.route_path('repo_commits', repo_name=self.db_repo_name))
316
313
317 collection = base_commit.get_path_history(
314 collection = base_commit.get_path_history(
318 f_path, limit=hist_limit, pre_load=pre_load)
315 f_path, limit=hist_limit, pre_load=pre_load)
319 collection = list(reversed(collection))
316 collection = list(reversed(collection))
320 else:
317 else:
321 collection = self.rhodecode_vcs_repo.get_commits(
318 collection = self.rhodecode_vcs_repo.get_commits(
322 branch_name=branch_name, show_hidden=show_hidden, pre_load=pre_load,
319 branch_name=branch_name, show_hidden=show_hidden, pre_load=pre_load,
323 translate_tags=False)
320 translate_tags=False)
324
321
325 p = safe_int(self.request.GET.get('page', 1), 1)
322 p = safe_int(self.request.GET.get('page', 1), 1)
326 try:
323 try:
327 self._load_changelog_data(
324 self._load_changelog_data(
328 c, collection, p, chunk_size, dynamic=True,
325 c, collection, p, chunk_size, dynamic=True,
329 f_path=f_path, commit_id=commit_id)
326 f_path=f_path, commit_id=commit_id)
330 except EmptyRepositoryError as e:
327 except EmptyRepositoryError as e:
331 return wrap_for_error(safe_str(e))
328 return wrap_for_error(safe_str(e))
332 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
329 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
333 log.exception('Failed to fetch commits')
330 log.exception('Failed to fetch commits')
334 return wrap_for_error(safe_str(e))
331 return wrap_for_error(safe_str(e))
335
332
336 prev_data = None
333 prev_data = None
337 next_data = None
334 next_data = None
338
335
339 try:
336 try:
340 prev_graph = json.loads(self.request.POST.get('graph') or '{}')
337 prev_graph = json.loads(self.request.POST.get('graph') or '{}')
341 except json.JSONDecodeError:
338 except json.JSONDecodeError:
342 prev_graph = {}
339 prev_graph = {}
343
340
344 if self.request.GET.get('chunk') == 'prev':
341 if self.request.GET.get('chunk') == 'prev':
345 next_data = prev_graph
342 next_data = prev_graph
346 elif self.request.GET.get('chunk') == 'next':
343 elif self.request.GET.get('chunk') == 'next':
347 prev_data = prev_graph
344 prev_data = prev_graph
348
345
349 commit_ids = []
346 commit_ids = []
350 if not f_path:
347 if not f_path:
351 # only load graph data when not in file history mode
348 # only load graph data when not in file history mode
352 commit_ids = c.pagination
349 commit_ids = c.pagination
353
350
354 c.graph_data, c.graph_commits = self._graph(
351 c.graph_data, c.graph_commits = self._graph(
355 self.rhodecode_vcs_repo, commit_ids,
352 self.rhodecode_vcs_repo, commit_ids,
356 prev_data=prev_data, next_data=next_data)
353 prev_data=prev_data, next_data=next_data)
357
354
358 return self._get_template_context(c)
355 return self._get_template_context(c)
@@ -1,289 +1,290 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2020 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import string
22 import string
23 import time
23 import time
24
24
25 import rhodecode
25 import rhodecode
26
26
27
27
28
28
29 from rhodecode.lib.view_utils import get_format_ref_id
29 from rhodecode.lib.view_utils import get_format_ref_id
30 from rhodecode.apps._base import RepoAppView
30 from rhodecode.apps._base import RepoAppView
31 from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP)
31 from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP)
32 from rhodecode.lib import helpers as h, rc_cache
32 from rhodecode.lib import helpers as h, rc_cache
33 from rhodecode.lib.utils2 import safe_str, safe_int
33 from rhodecode.lib.utils2 import safe_str, safe_int
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.vcs.backends.base import EmptyCommit
36 from rhodecode.lib.vcs.backends.base import EmptyCommit
37 from rhodecode.lib.vcs.exceptions import (
37 from rhodecode.lib.vcs.exceptions import (
38 CommitError, EmptyRepositoryError, CommitDoesNotExistError)
38 CommitError, EmptyRepositoryError, CommitDoesNotExistError)
39 from rhodecode.model.db import Statistics, CacheKey, User
39 from rhodecode.model.db import Statistics, CacheKey, User
40 from rhodecode.model.meta import Session
40 from rhodecode.model.meta import Session
41 from rhodecode.model.scm import ScmModel
41 from rhodecode.model.scm import ScmModel
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 class RepoSummaryView(RepoAppView):
46 class RepoSummaryView(RepoAppView):
47
47
48 def load_default_context(self):
48 def load_default_context(self):
49 c = self._get_local_tmpl_context(include_app_defaults=True)
49 c = self._get_local_tmpl_context(include_app_defaults=True)
50 c.rhodecode_repo = None
50 c.rhodecode_repo = None
51 if not c.repository_requirements_missing:
51 if not c.repository_requirements_missing:
52 c.rhodecode_repo = self.rhodecode_vcs_repo
52 c.rhodecode_repo = self.rhodecode_vcs_repo
53 return c
53 return c
54
54
55 def _load_commits_context(self, c):
55 def _load_commits_context(self, c):
56 p = safe_int(self.request.GET.get('page'), 1)
56 p = safe_int(self.request.GET.get('page'), 1)
57 size = safe_int(self.request.GET.get('size'), 10)
57 size = safe_int(self.request.GET.get('size'), 10)
58
58
59 def url_generator(page_num):
59 def url_generator(page_num):
60 query_params = {
60 query_params = {
61 'page': page_num,
61 'page': page_num,
62 'size': size
62 'size': size
63 }
63 }
64 return h.route_path(
64 return h.route_path(
65 'repo_summary_commits',
65 'repo_summary_commits',
66 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
66 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
67
67
68 pre_load = ['author', 'branch', 'date', 'message']
68 pre_load = self.get_commit_preload_attrs()
69
69 try:
70 try:
70 collection = self.rhodecode_vcs_repo.get_commits(
71 collection = self.rhodecode_vcs_repo.get_commits(
71 pre_load=pre_load, translate_tags=False)
72 pre_load=pre_load, translate_tags=False)
72 except EmptyRepositoryError:
73 except EmptyRepositoryError:
73 collection = self.rhodecode_vcs_repo
74 collection = self.rhodecode_vcs_repo
74
75
75 c.repo_commits = h.RepoPage(
76 c.repo_commits = h.RepoPage(
76 collection, page=p, items_per_page=size, url_maker=url_generator)
77 collection, page=p, items_per_page=size, url_maker=url_generator)
77 page_ids = [x.raw_id for x in c.repo_commits]
78 page_ids = [x.raw_id for x in c.repo_commits]
78 c.comments = self.db_repo.get_comments(page_ids)
79 c.comments = self.db_repo.get_comments(page_ids)
79 c.statuses = self.db_repo.statuses(page_ids)
80 c.statuses = self.db_repo.statuses(page_ids)
80
81
81 def _prepare_and_set_clone_url(self, c):
82 def _prepare_and_set_clone_url(self, c):
82 username = ''
83 username = ''
83 if self._rhodecode_user.username != User.DEFAULT_USER:
84 if self._rhodecode_user.username != User.DEFAULT_USER:
84 username = safe_str(self._rhodecode_user.username)
85 username = safe_str(self._rhodecode_user.username)
85
86
86 _def_clone_uri = c.clone_uri_tmpl
87 _def_clone_uri = c.clone_uri_tmpl
87 _def_clone_uri_id = c.clone_uri_id_tmpl
88 _def_clone_uri_id = c.clone_uri_id_tmpl
88 _def_clone_uri_ssh = c.clone_uri_ssh_tmpl
89 _def_clone_uri_ssh = c.clone_uri_ssh_tmpl
89
90
90 c.clone_repo_url = self.db_repo.clone_url(
91 c.clone_repo_url = self.db_repo.clone_url(
91 user=username, uri_tmpl=_def_clone_uri)
92 user=username, uri_tmpl=_def_clone_uri)
92 c.clone_repo_url_id = self.db_repo.clone_url(
93 c.clone_repo_url_id = self.db_repo.clone_url(
93 user=username, uri_tmpl=_def_clone_uri_id)
94 user=username, uri_tmpl=_def_clone_uri_id)
94 c.clone_repo_url_ssh = self.db_repo.clone_url(
95 c.clone_repo_url_ssh = self.db_repo.clone_url(
95 uri_tmpl=_def_clone_uri_ssh, ssh=True)
96 uri_tmpl=_def_clone_uri_ssh, ssh=True)
96
97
97 @LoginRequired()
98 @LoginRequired()
98 @HasRepoPermissionAnyDecorator(
99 @HasRepoPermissionAnyDecorator(
99 'repository.read', 'repository.write', 'repository.admin')
100 'repository.read', 'repository.write', 'repository.admin')
100 def summary_commits(self):
101 def summary_commits(self):
101 c = self.load_default_context()
102 c = self.load_default_context()
102 self._prepare_and_set_clone_url(c)
103 self._prepare_and_set_clone_url(c)
103 self._load_commits_context(c)
104 self._load_commits_context(c)
104 return self._get_template_context(c)
105 return self._get_template_context(c)
105
106
106 @LoginRequired()
107 @LoginRequired()
107 @HasRepoPermissionAnyDecorator(
108 @HasRepoPermissionAnyDecorator(
108 'repository.read', 'repository.write', 'repository.admin')
109 'repository.read', 'repository.write', 'repository.admin')
109 def summary(self):
110 def summary(self):
110 c = self.load_default_context()
111 c = self.load_default_context()
111
112
112 # Prepare the clone URL
113 # Prepare the clone URL
113 self._prepare_and_set_clone_url(c)
114 self._prepare_and_set_clone_url(c)
114
115
115 # If enabled, get statistics data
116 # If enabled, get statistics data
116 c.show_stats = bool(self.db_repo.enable_statistics)
117 c.show_stats = bool(self.db_repo.enable_statistics)
117
118
118 stats = Session().query(Statistics) \
119 stats = Session().query(Statistics) \
119 .filter(Statistics.repository == self.db_repo) \
120 .filter(Statistics.repository == self.db_repo) \
120 .scalar()
121 .scalar()
121
122
122 c.stats_percentage = 0
123 c.stats_percentage = 0
123
124
124 if stats and stats.languages:
125 if stats and stats.languages:
125 c.no_data = False is self.db_repo.enable_statistics
126 c.no_data = False is self.db_repo.enable_statistics
126 lang_stats_d = json.loads(stats.languages)
127 lang_stats_d = json.loads(stats.languages)
127
128
128 # Sort first by decreasing count and second by the file extension,
129 # Sort first by decreasing count and second by the file extension,
129 # so we have a consistent output.
130 # so we have a consistent output.
130 lang_stats_items = sorted(lang_stats_d.iteritems(),
131 lang_stats_items = sorted(lang_stats_d.iteritems(),
131 key=lambda k: (-k[1], k[0]))[:10]
132 key=lambda k: (-k[1], k[0]))[:10]
132 lang_stats = [(x, {"count": y,
133 lang_stats = [(x, {"count": y,
133 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
134 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
134 for x, y in lang_stats_items]
135 for x, y in lang_stats_items]
135
136
136 c.trending_languages = json.dumps(lang_stats)
137 c.trending_languages = json.dumps(lang_stats)
137 else:
138 else:
138 c.no_data = True
139 c.no_data = True
139 c.trending_languages = json.dumps({})
140 c.trending_languages = json.dumps({})
140
141
141 scm_model = ScmModel()
142 scm_model = ScmModel()
142 c.enable_downloads = self.db_repo.enable_downloads
143 c.enable_downloads = self.db_repo.enable_downloads
143 c.repository_followers = scm_model.get_followers(self.db_repo)
144 c.repository_followers = scm_model.get_followers(self.db_repo)
144 c.repository_forks = scm_model.get_forks(self.db_repo)
145 c.repository_forks = scm_model.get_forks(self.db_repo)
145
146
146 # first interaction with the VCS instance after here...
147 # first interaction with the VCS instance after here...
147 if c.repository_requirements_missing:
148 if c.repository_requirements_missing:
148 self.request.override_renderer = \
149 self.request.override_renderer = \
149 'rhodecode:templates/summary/missing_requirements.mako'
150 'rhodecode:templates/summary/missing_requirements.mako'
150 return self._get_template_context(c)
151 return self._get_template_context(c)
151
152
152 c.readme_data, c.readme_file = \
153 c.readme_data, c.readme_file = \
153 self._get_readme_data(self.db_repo, c.visual.default_renderer)
154 self._get_readme_data(self.db_repo, c.visual.default_renderer)
154
155
155 # loads the summary commits template context
156 # loads the summary commits template context
156 self._load_commits_context(c)
157 self._load_commits_context(c)
157
158
158 return self._get_template_context(c)
159 return self._get_template_context(c)
159
160
160 @LoginRequired()
161 @LoginRequired()
161 @HasRepoPermissionAnyDecorator(
162 @HasRepoPermissionAnyDecorator(
162 'repository.read', 'repository.write', 'repository.admin')
163 'repository.read', 'repository.write', 'repository.admin')
163 def repo_stats(self):
164 def repo_stats(self):
164 show_stats = bool(self.db_repo.enable_statistics)
165 show_stats = bool(self.db_repo.enable_statistics)
165 repo_id = self.db_repo.repo_id
166 repo_id = self.db_repo.repo_id
166
167
167 landing_commit = self.db_repo.get_landing_commit()
168 landing_commit = self.db_repo.get_landing_commit()
168 if isinstance(landing_commit, EmptyCommit):
169 if isinstance(landing_commit, EmptyCommit):
169 return {'size': 0, 'code_stats': {}}
170 return {'size': 0, 'code_stats': {}}
170
171
171 cache_seconds = safe_int(rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
172 cache_seconds = safe_int(rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
172 cache_on = cache_seconds > 0
173 cache_on = cache_seconds > 0
173
174
174 log.debug(
175 log.debug(
175 'Computing REPO STATS for repo_id %s commit_id `%s` '
176 'Computing REPO STATS for repo_id %s commit_id `%s` '
176 'with caching: %s[TTL: %ss]' % (
177 'with caching: %s[TTL: %ss]' % (
177 repo_id, landing_commit, cache_on, cache_seconds or 0))
178 repo_id, landing_commit, cache_on, cache_seconds or 0))
178
179
179 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
180 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
180 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
181 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
181
182
182 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
183 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
183 condition=cache_on)
184 condition=cache_on)
184 def compute_stats(repo_id, commit_id, _show_stats):
185 def compute_stats(repo_id, commit_id, _show_stats):
185 code_stats = {}
186 code_stats = {}
186 size = 0
187 size = 0
187 try:
188 try:
188 commit = self.db_repo.get_commit(commit_id)
189 commit = self.db_repo.get_commit(commit_id)
189
190
190 for node in commit.get_filenodes_generator():
191 for node in commit.get_filenodes_generator():
191 size += node.size
192 size += node.size
192 if not _show_stats:
193 if not _show_stats:
193 continue
194 continue
194 ext = string.lower(node.extension)
195 ext = string.lower(node.extension)
195 ext_info = LANGUAGES_EXTENSIONS_MAP.get(ext)
196 ext_info = LANGUAGES_EXTENSIONS_MAP.get(ext)
196 if ext_info:
197 if ext_info:
197 if ext in code_stats:
198 if ext in code_stats:
198 code_stats[ext]['count'] += 1
199 code_stats[ext]['count'] += 1
199 else:
200 else:
200 code_stats[ext] = {"count": 1, "desc": ext_info}
201 code_stats[ext] = {"count": 1, "desc": ext_info}
201 except (EmptyRepositoryError, CommitDoesNotExistError):
202 except (EmptyRepositoryError, CommitDoesNotExistError):
202 pass
203 pass
203 return {'size': h.format_byte_size_binary(size),
204 return {'size': h.format_byte_size_binary(size),
204 'code_stats': code_stats}
205 'code_stats': code_stats}
205
206
206 stats = compute_stats(self.db_repo.repo_id, landing_commit.raw_id, show_stats)
207 stats = compute_stats(self.db_repo.repo_id, landing_commit.raw_id, show_stats)
207 return stats
208 return stats
208
209
209 @LoginRequired()
210 @LoginRequired()
210 @HasRepoPermissionAnyDecorator(
211 @HasRepoPermissionAnyDecorator(
211 'repository.read', 'repository.write', 'repository.admin')
212 'repository.read', 'repository.write', 'repository.admin')
212 def repo_refs_data(self):
213 def repo_refs_data(self):
213 _ = self.request.translate
214 _ = self.request.translate
214 self.load_default_context()
215 self.load_default_context()
215
216
216 repo = self.rhodecode_vcs_repo
217 repo = self.rhodecode_vcs_repo
217 refs_to_create = [
218 refs_to_create = [
218 (_("Branch"), repo.branches, 'branch'),
219 (_("Branch"), repo.branches, 'branch'),
219 (_("Tag"), repo.tags, 'tag'),
220 (_("Tag"), repo.tags, 'tag'),
220 (_("Bookmark"), repo.bookmarks, 'book'),
221 (_("Bookmark"), repo.bookmarks, 'book'),
221 ]
222 ]
222 res = self._create_reference_data(repo, self.db_repo_name, refs_to_create)
223 res = self._create_reference_data(repo, self.db_repo_name, refs_to_create)
223 data = {
224 data = {
224 'more': False,
225 'more': False,
225 'results': res
226 'results': res
226 }
227 }
227 return data
228 return data
228
229
229 @LoginRequired()
230 @LoginRequired()
230 @HasRepoPermissionAnyDecorator(
231 @HasRepoPermissionAnyDecorator(
231 'repository.read', 'repository.write', 'repository.admin')
232 'repository.read', 'repository.write', 'repository.admin')
232 def repo_refs_changelog_data(self):
233 def repo_refs_changelog_data(self):
233 _ = self.request.translate
234 _ = self.request.translate
234 self.load_default_context()
235 self.load_default_context()
235
236
236 repo = self.rhodecode_vcs_repo
237 repo = self.rhodecode_vcs_repo
237
238
238 refs_to_create = [
239 refs_to_create = [
239 (_("Branches"), repo.branches, 'branch'),
240 (_("Branches"), repo.branches, 'branch'),
240 (_("Closed branches"), repo.branches_closed, 'branch_closed'),
241 (_("Closed branches"), repo.branches_closed, 'branch_closed'),
241 # TODO: enable when vcs can handle bookmarks filters
242 # TODO: enable when vcs can handle bookmarks filters
242 # (_("Bookmarks"), repo.bookmarks, "book"),
243 # (_("Bookmarks"), repo.bookmarks, "book"),
243 ]
244 ]
244 res = self._create_reference_data(
245 res = self._create_reference_data(
245 repo, self.db_repo_name, refs_to_create)
246 repo, self.db_repo_name, refs_to_create)
246 data = {
247 data = {
247 'more': False,
248 'more': False,
248 'results': res
249 'results': res
249 }
250 }
250 return data
251 return data
251
252
252 def _create_reference_data(self, repo, full_repo_name, refs_to_create):
253 def _create_reference_data(self, repo, full_repo_name, refs_to_create):
253 format_ref_id = get_format_ref_id(repo)
254 format_ref_id = get_format_ref_id(repo)
254
255
255 result = []
256 result = []
256 for title, refs, ref_type in refs_to_create:
257 for title, refs, ref_type in refs_to_create:
257 if refs:
258 if refs:
258 result.append({
259 result.append({
259 'text': title,
260 'text': title,
260 'children': self._create_reference_items(
261 'children': self._create_reference_items(
261 repo, full_repo_name, refs, ref_type,
262 repo, full_repo_name, refs, ref_type,
262 format_ref_id),
263 format_ref_id),
263 })
264 })
264 return result
265 return result
265
266
266 def _create_reference_items(self, repo, full_repo_name, refs, ref_type, format_ref_id):
267 def _create_reference_items(self, repo, full_repo_name, refs, ref_type, format_ref_id):
267 result = []
268 result = []
268 is_svn = h.is_svn(repo)
269 is_svn = h.is_svn(repo)
269 for ref_name, raw_id in refs.iteritems():
270 for ref_name, raw_id in refs.iteritems():
270 files_url = self._create_files_url(
271 files_url = self._create_files_url(
271 repo, full_repo_name, ref_name, raw_id, is_svn)
272 repo, full_repo_name, ref_name, raw_id, is_svn)
272 result.append({
273 result.append({
273 'text': ref_name,
274 'text': ref_name,
274 'id': format_ref_id(ref_name, raw_id),
275 'id': format_ref_id(ref_name, raw_id),
275 'raw_id': raw_id,
276 'raw_id': raw_id,
276 'type': ref_type,
277 'type': ref_type,
277 'files_url': files_url,
278 'files_url': files_url,
278 'idx': 0,
279 'idx': 0,
279 })
280 })
280 return result
281 return result
281
282
282 def _create_files_url(self, repo, full_repo_name, ref_name, raw_id, is_svn):
283 def _create_files_url(self, repo, full_repo_name, ref_name, raw_id, is_svn):
283 use_commit_id = '/' in ref_name or is_svn
284 use_commit_id = '/' in ref_name or is_svn
284 return h.route_path(
285 return h.route_path(
285 'repo_files',
286 'repo_files',
286 repo_name=full_repo_name,
287 repo_name=full_repo_name,
287 f_path=ref_name if is_svn else '',
288 f_path=ref_name if is_svn else '',
288 commit_id=raw_id if use_commit_id else ref_name,
289 commit_id=raw_id if use_commit_id else ref_name,
289 _query=dict(at=ref_name))
290 _query=dict(at=ref_name))
General Comments 0
You need to be logged in to leave comments. Login now