##// END OF EJS Templates
repo-summary: protect against wrong commits in repo summary.
marcink -
r2148:de03feb2 default
parent child Browse files
Show More
@@ -1,370 +1,371 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import string
23 23
24 24 from pyramid.view import view_config
25 25 from beaker.cache import cache_region
26 26
27 27 from rhodecode.controllers import utils
28 28 from rhodecode.apps._base import RepoAppView
29 29 from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP)
30 30 from rhodecode.lib import caches, helpers as h
31 31 from rhodecode.lib.helpers import RepoPage
32 32 from rhodecode.lib.utils2 import safe_str, safe_int
33 33 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 34 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
35 35 from rhodecode.lib.ext_json import json
36 36 from rhodecode.lib.vcs.backends.base import EmptyCommit
37 from rhodecode.lib.vcs.exceptions import CommitError, EmptyRepositoryError
37 from rhodecode.lib.vcs.exceptions import CommitError, EmptyRepositoryError, \
38 CommitDoesNotExistError
38 39 from rhodecode.model.db import Statistics, CacheKey, User
39 40 from rhodecode.model.meta import Session
40 41 from rhodecode.model.repo import ReadmeFinder
41 42 from rhodecode.model.scm import ScmModel
42 43
43 44 log = logging.getLogger(__name__)
44 45
45 46
46 47 class RepoSummaryView(RepoAppView):
47 48
48 49 def load_default_context(self):
49 50 c = self._get_local_tmpl_context(include_app_defaults=True)
50 51
51 52 c.rhodecode_repo = None
52 53 if not c.repository_requirements_missing:
53 54 c.rhodecode_repo = self.rhodecode_vcs_repo
54 55
55 56 self._register_global_c(c)
56 57 return c
57 58
58 59 def _get_readme_data(self, db_repo, default_renderer):
59 60 repo_name = db_repo.repo_name
60 61 log.debug('Looking for README file')
61 62
62 63 @cache_region('long_term')
63 64 def _generate_readme(cache_key):
64 65 readme_data = None
65 66 readme_node = None
66 67 readme_filename = None
67 68 commit = self._get_landing_commit_or_none(db_repo)
68 69 if commit:
69 70 log.debug("Searching for a README file.")
70 71 readme_node = ReadmeFinder(default_renderer).search(commit)
71 72 if readme_node:
72 73 relative_urls = {
73 74 'raw': h.route_path(
74 75 'repo_file_raw', repo_name=repo_name,
75 76 commit_id=commit.raw_id, f_path=readme_node.path),
76 77 'standard': h.route_path(
77 78 'repo_files', repo_name=repo_name,
78 79 commit_id=commit.raw_id, f_path=readme_node.path),
79 80 }
80 81 readme_data = self._render_readme_or_none(
81 82 commit, readme_node, relative_urls)
82 83 readme_filename = readme_node.path
83 84 return readme_data, readme_filename
84 85
85 86 invalidator_context = CacheKey.repo_context_cache(
86 87 _generate_readme, repo_name, CacheKey.CACHE_TYPE_README)
87 88
88 89 with invalidator_context as context:
89 90 context.invalidate()
90 91 computed = context.compute()
91 92
92 93 return computed
93 94
94 95 def _get_landing_commit_or_none(self, db_repo):
95 96 log.debug("Getting the landing commit.")
96 97 try:
97 98 commit = db_repo.get_landing_commit()
98 99 if not isinstance(commit, EmptyCommit):
99 100 return commit
100 101 else:
101 102 log.debug("Repository is empty, no README to render.")
102 103 except CommitError:
103 104 log.exception(
104 105 "Problem getting commit when trying to render the README.")
105 106
106 107 def _render_readme_or_none(self, commit, readme_node, relative_urls):
107 108 log.debug(
108 109 'Found README file `%s` rendering...', readme_node.path)
109 110 renderer = MarkupRenderer()
110 111 try:
111 112 html_source = renderer.render(
112 113 readme_node.content, filename=readme_node.path)
113 114 if relative_urls:
114 115 return relative_links(html_source, relative_urls)
115 116 return html_source
116 117 except Exception:
117 118 log.exception(
118 119 "Exception while trying to render the README")
119 120
120 121 def _load_commits_context(self, c):
121 122 p = safe_int(self.request.GET.get('page'), 1)
122 123 size = safe_int(self.request.GET.get('size'), 10)
123 124
124 125 def url_generator(**kw):
125 126 query_params = {
126 127 'size': size
127 128 }
128 129 query_params.update(kw)
129 130 return h.route_path(
130 131 'repo_summary_commits',
131 132 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
132 133
133 134 pre_load = ['author', 'branch', 'date', 'message']
134 135 try:
135 136 collection = self.rhodecode_vcs_repo.get_commits(pre_load=pre_load)
136 137 except EmptyRepositoryError:
137 138 collection = self.rhodecode_vcs_repo
138 139
139 140 c.repo_commits = RepoPage(
140 141 collection, page=p, items_per_page=size, url=url_generator)
141 142 page_ids = [x.raw_id for x in c.repo_commits]
142 143 c.comments = self.db_repo.get_comments(page_ids)
143 144 c.statuses = self.db_repo.statuses(page_ids)
144 145
145 146 @LoginRequired()
146 147 @HasRepoPermissionAnyDecorator(
147 148 'repository.read', 'repository.write', 'repository.admin')
148 149 @view_config(
149 150 route_name='repo_summary_commits', request_method='GET',
150 151 renderer='rhodecode:templates/summary/summary_commits.mako')
151 152 def summary_commits(self):
152 153 c = self.load_default_context()
153 154 self._load_commits_context(c)
154 155 return self._get_template_context(c)
155 156
156 157 @LoginRequired()
157 158 @HasRepoPermissionAnyDecorator(
158 159 'repository.read', 'repository.write', 'repository.admin')
159 160 @view_config(
160 161 route_name='repo_summary', request_method='GET',
161 162 renderer='rhodecode:templates/summary/summary.mako')
162 163 @view_config(
163 164 route_name='repo_summary_slash', request_method='GET',
164 165 renderer='rhodecode:templates/summary/summary.mako')
165 166 @view_config(
166 167 route_name='repo_summary_explicit', request_method='GET',
167 168 renderer='rhodecode:templates/summary/summary.mako')
168 169 def summary(self):
169 170 c = self.load_default_context()
170 171
171 172 # Prepare the clone URL
172 173 username = ''
173 174 if self._rhodecode_user.username != User.DEFAULT_USER:
174 175 username = safe_str(self._rhodecode_user.username)
175 176
176 177 _def_clone_uri = _def_clone_uri_by_id = c.clone_uri_tmpl
177 178 if '{repo}' in _def_clone_uri:
178 179 _def_clone_uri_by_id = _def_clone_uri.replace(
179 180 '{repo}', '_{repoid}')
180 181 elif '{repoid}' in _def_clone_uri:
181 182 _def_clone_uri_by_id = _def_clone_uri.replace(
182 183 '_{repoid}', '{repo}')
183 184
184 185 c.clone_repo_url = self.db_repo.clone_url(
185 186 user=username, uri_tmpl=_def_clone_uri)
186 187 c.clone_repo_url_id = self.db_repo.clone_url(
187 188 user=username, uri_tmpl=_def_clone_uri_by_id)
188 189
189 190 # If enabled, get statistics data
190 191
191 192 c.show_stats = bool(self.db_repo.enable_statistics)
192 193
193 194 stats = Session().query(Statistics) \
194 195 .filter(Statistics.repository == self.db_repo) \
195 196 .scalar()
196 197
197 198 c.stats_percentage = 0
198 199
199 200 if stats and stats.languages:
200 201 c.no_data = False is self.db_repo.enable_statistics
201 202 lang_stats_d = json.loads(stats.languages)
202 203
203 204 # Sort first by decreasing count and second by the file extension,
204 205 # so we have a consistent output.
205 206 lang_stats_items = sorted(lang_stats_d.iteritems(),
206 207 key=lambda k: (-k[1], k[0]))[:10]
207 208 lang_stats = [(x, {"count": y,
208 209 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
209 210 for x, y in lang_stats_items]
210 211
211 212 c.trending_languages = json.dumps(lang_stats)
212 213 else:
213 214 c.no_data = True
214 215 c.trending_languages = json.dumps({})
215 216
216 217 scm_model = ScmModel()
217 218 c.enable_downloads = self.db_repo.enable_downloads
218 219 c.repository_followers = scm_model.get_followers(self.db_repo)
219 220 c.repository_forks = scm_model.get_forks(self.db_repo)
220 221 c.repository_is_user_following = scm_model.is_following_repo(
221 222 self.db_repo_name, self._rhodecode_user.user_id)
222 223
223 224 # first interaction with the VCS instance after here...
224 225 if c.repository_requirements_missing:
225 226 self.request.override_renderer = \
226 227 'rhodecode:templates/summary/missing_requirements.mako'
227 228 return self._get_template_context(c)
228 229
229 230 c.readme_data, c.readme_file = \
230 231 self._get_readme_data(self.db_repo, c.visual.default_renderer)
231 232
232 233 # loads the summary commits template context
233 234 self._load_commits_context(c)
234 235
235 236 return self._get_template_context(c)
236 237
237 238 def get_request_commit_id(self):
238 239 return self.request.matchdict['commit_id']
239 240
240 241 @LoginRequired()
241 242 @HasRepoPermissionAnyDecorator(
242 243 'repository.read', 'repository.write', 'repository.admin')
243 244 @view_config(
244 245 route_name='repo_stats', request_method='GET',
245 246 renderer='json_ext')
246 247 def repo_stats(self):
247 248 commit_id = self.get_request_commit_id()
248 249
249 250 _namespace = caches.get_repo_namespace_key(
250 251 caches.SUMMARY_STATS, self.db_repo_name)
251 252 show_stats = bool(self.db_repo.enable_statistics)
252 253 cache_manager = caches.get_cache_manager(
253 254 'repo_cache_long', _namespace)
254 255 _cache_key = caches.compute_key_from_params(
255 256 self.db_repo_name, commit_id, show_stats)
256 257
257 258 def compute_stats():
258 259 code_stats = {}
259 260 size = 0
260 261 try:
261 262 scm_instance = self.db_repo.scm_instance()
262 263 commit = scm_instance.get_commit(commit_id)
263 264
264 265 for node in commit.get_filenodes_generator():
265 266 size += node.size
266 267 if not show_stats:
267 268 continue
268 269 ext = string.lower(node.extension)
269 270 ext_info = LANGUAGES_EXTENSIONS_MAP.get(ext)
270 271 if ext_info:
271 272 if ext in code_stats:
272 273 code_stats[ext]['count'] += 1
273 274 else:
274 275 code_stats[ext] = {"count": 1, "desc": ext_info}
275 except EmptyRepositoryError:
276 except (EmptyRepositoryError, CommitDoesNotExistError):
276 277 pass
277 278 return {'size': h.format_byte_size_binary(size),
278 279 'code_stats': code_stats}
279 280
280 281 stats = cache_manager.get(_cache_key, createfunc=compute_stats)
281 282 return stats
282 283
283 284 @LoginRequired()
284 285 @HasRepoPermissionAnyDecorator(
285 286 'repository.read', 'repository.write', 'repository.admin')
286 287 @view_config(
287 288 route_name='repo_refs_data', request_method='GET',
288 289 renderer='json_ext')
289 290 def repo_refs_data(self):
290 291 _ = self.request.translate
291 292 self.load_default_context()
292 293
293 294 repo = self.rhodecode_vcs_repo
294 295 refs_to_create = [
295 296 (_("Branch"), repo.branches, 'branch'),
296 297 (_("Tag"), repo.tags, 'tag'),
297 298 (_("Bookmark"), repo.bookmarks, 'book'),
298 299 ]
299 300 res = self._create_reference_data(
300 301 repo, self.db_repo_name, refs_to_create)
301 302 data = {
302 303 'more': False,
303 304 'results': res
304 305 }
305 306 return data
306 307
307 308 @LoginRequired()
308 309 @HasRepoPermissionAnyDecorator(
309 310 'repository.read', 'repository.write', 'repository.admin')
310 311 @view_config(
311 312 route_name='repo_refs_changelog_data', request_method='GET',
312 313 renderer='json_ext')
313 314 def repo_refs_changelog_data(self):
314 315 _ = self.request.translate
315 316 self.load_default_context()
316 317
317 318 repo = self.rhodecode_vcs_repo
318 319
319 320 refs_to_create = [
320 321 (_("Branches"), repo.branches, 'branch'),
321 322 (_("Closed branches"), repo.branches_closed, 'branch_closed'),
322 323 # TODO: enable when vcs can handle bookmarks filters
323 324 # (_("Bookmarks"), repo.bookmarks, "book"),
324 325 ]
325 326 res = self._create_reference_data(
326 327 repo, self.db_repo_name, refs_to_create)
327 328 data = {
328 329 'more': False,
329 330 'results': res
330 331 }
331 332 return data
332 333
333 334 def _create_reference_data(self, repo, full_repo_name, refs_to_create):
334 335 format_ref_id = utils.get_format_ref_id(repo)
335 336
336 337 result = []
337 338 for title, refs, ref_type in refs_to_create:
338 339 if refs:
339 340 result.append({
340 341 'text': title,
341 342 'children': self._create_reference_items(
342 343 repo, full_repo_name, refs, ref_type,
343 344 format_ref_id),
344 345 })
345 346 return result
346 347
347 348 def _create_reference_items(self, repo, full_repo_name, refs, ref_type,
348 349 format_ref_id):
349 350 result = []
350 351 is_svn = h.is_svn(repo)
351 352 for ref_name, raw_id in refs.iteritems():
352 353 files_url = self._create_files_url(
353 354 repo, full_repo_name, ref_name, raw_id, is_svn)
354 355 result.append({
355 356 'text': ref_name,
356 357 'id': format_ref_id(ref_name, raw_id),
357 358 'raw_id': raw_id,
358 359 'type': ref_type,
359 360 'files_url': files_url,
360 361 })
361 362 return result
362 363
363 364 def _create_files_url(self, repo, full_repo_name, ref_name, raw_id, is_svn):
364 365 use_commit_id = '/' in ref_name or is_svn
365 366 return h.route_path(
366 367 'repo_files',
367 368 repo_name=full_repo_name,
368 369 f_path=ref_name if is_svn else '',
369 370 commit_id=raw_id if use_commit_id else ref_name,
370 371 _query=dict(at=ref_name))
General Comments 0
You need to be logged in to leave comments. Login now