##// END OF EJS Templates
repo-summary: re-implemented summary view as pyramid....
marcink -
r1785:1cce4ff2 default
parent child Browse files
Show More
@@ -0,0 +1,368 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22 import string
23
24 from pyramid.view import view_config
25
26 from beaker.cache import cache_region
27
28
29 from rhodecode.controllers import utils
30
31 from rhodecode.apps._base import RepoAppView
32 from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP)
33 from rhodecode.lib import caches, helpers as h
34 from rhodecode.lib.helpers import RepoPage
35 from rhodecode.lib.utils2 import safe_str, safe_int
36 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
37 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
38 from rhodecode.lib.ext_json import json
39 from rhodecode.lib.vcs.backends.base import EmptyCommit
40 from rhodecode.lib.vcs.exceptions import CommitError, EmptyRepositoryError
41 from rhodecode.model.db import Statistics, CacheKey, User
42 from rhodecode.model.meta import Session
43 from rhodecode.model.repo import ReadmeFinder
44 from rhodecode.model.scm import ScmModel
45
46 log = logging.getLogger(__name__)
47
48
49 class RepoSummaryView(RepoAppView):
50
51 def load_default_context(self):
52 c = self._get_local_tmpl_context(include_app_defaults=True)
53
54 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
55 c.repo_info = self.db_repo
56 c.rhodecode_repo = None
57 if not c.repository_requirements_missing:
58 c.rhodecode_repo = self.rhodecode_vcs_repo
59
60 self._register_global_c(c)
61 return c
62
63 def _get_readme_data(self, db_repo, default_renderer):
64 repo_name = db_repo.repo_name
65 log.debug('Looking for README file')
66
67 @cache_region('long_term')
68 def _generate_readme(cache_key):
69 readme_data = None
70 readme_node = None
71 readme_filename = None
72 commit = self._get_landing_commit_or_none(db_repo)
73 if commit:
74 log.debug("Searching for a README file.")
75 readme_node = ReadmeFinder(default_renderer).search(commit)
76 if readme_node:
77 relative_url = h.url('files_raw_home',
78 repo_name=repo_name,
79 revision=commit.raw_id,
80 f_path=readme_node.path)
81 readme_data = self._render_readme_or_none(
82 commit, readme_node, relative_url)
83 readme_filename = readme_node.path
84 return readme_data, readme_filename
85
86 invalidator_context = CacheKey.repo_context_cache(
87 _generate_readme, repo_name, CacheKey.CACHE_TYPE_README)
88
89 with invalidator_context as context:
90 context.invalidate()
91 computed = context.compute()
92
93 return computed
94
95 def _get_landing_commit_or_none(self, db_repo):
96 log.debug("Getting the landing commit.")
97 try:
98 commit = db_repo.get_landing_commit()
99 if not isinstance(commit, EmptyCommit):
100 return commit
101 else:
102 log.debug("Repository is empty, no README to render.")
103 except CommitError:
104 log.exception(
105 "Problem getting commit when trying to render the README.")
106
107 def _render_readme_or_none(self, commit, readme_node, relative_url):
108 log.debug(
109 'Found README file `%s` rendering...', readme_node.path)
110 renderer = MarkupRenderer()
111 try:
112 html_source = renderer.render(
113 readme_node.content, filename=readme_node.path)
114 if relative_url:
115 return relative_links(html_source, relative_url)
116 return html_source
117 except Exception:
118 log.exception(
119 "Exception while trying to render the README")
120
121 def _load_commits_context(self, c):
122 p = safe_int(self.request.GET.get('page'), 1)
123 size = safe_int(self.request.GET.get('size'), 10)
124
125 def url_generator(**kw):
126 query_params = {
127 'size': size
128 }
129 query_params.update(kw)
130 return h.route_path(
131 'repo_summary_commits',
132 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
133
134 pre_load = ['author', 'branch', 'date', 'message']
135 try:
136 collection = self.rhodecode_vcs_repo.get_commits(pre_load=pre_load)
137 except EmptyRepositoryError:
138 collection = self.rhodecode_vcs_repo
139
140 c.repo_commits = RepoPage(
141 collection, page=p, items_per_page=size, url=url_generator)
142 page_ids = [x.raw_id for x in c.repo_commits]
143 c.comments = self.db_repo.get_comments(page_ids)
144 c.statuses = self.db_repo.statuses(page_ids)
145
146 @LoginRequired()
147 @HasRepoPermissionAnyDecorator(
148 'repository.read', 'repository.write', 'repository.admin')
149 @view_config(
150 route_name='repo_summary_commits', request_method='GET',
151 renderer='rhodecode:templates/summary/summary_commits.mako')
152 def summary_commits(self):
153 c = self.load_default_context()
154 self._load_commits_context(c)
155 return self._get_template_context(c)
156
157 @LoginRequired()
158 @HasRepoPermissionAnyDecorator(
159 'repository.read', 'repository.write', 'repository.admin')
160 @view_config(
161 route_name='repo_summary', request_method='GET',
162 renderer='rhodecode:templates/summary/summary.mako')
163 @view_config(
164 route_name='repo_summary_slash', request_method='GET',
165 renderer='rhodecode:templates/summary/summary.mako')
166 def summary(self):
167 c = self.load_default_context()
168
169 # Prepare the clone URL
170 username = ''
171 if self._rhodecode_user.username != User.DEFAULT_USER:
172 username = safe_str(self._rhodecode_user.username)
173
174 _def_clone_uri = _def_clone_uri_by_id = c.clone_uri_tmpl
175 if '{repo}' in _def_clone_uri:
176 _def_clone_uri_by_id = _def_clone_uri.replace(
177 '{repo}', '_{repoid}')
178 elif '{repoid}' in _def_clone_uri:
179 _def_clone_uri_by_id = _def_clone_uri.replace(
180 '_{repoid}', '{repo}')
181
182 c.clone_repo_url = self.db_repo.clone_url(
183 user=username, uri_tmpl=_def_clone_uri)
184 c.clone_repo_url_id = self.db_repo.clone_url(
185 user=username, uri_tmpl=_def_clone_uri_by_id)
186
187 # If enabled, get statistics data
188
189 c.show_stats = bool(self.db_repo.enable_statistics)
190
191 stats = Session().query(Statistics) \
192 .filter(Statistics.repository == self.db_repo) \
193 .scalar()
194
195 c.stats_percentage = 0
196
197 if stats and stats.languages:
198 c.no_data = False is self.db_repo.enable_statistics
199 lang_stats_d = json.loads(stats.languages)
200
201 # Sort first by decreasing count and second by the file extension,
202 # so we have a consistent output.
203 lang_stats_items = sorted(lang_stats_d.iteritems(),
204 key=lambda k: (-k[1], k[0]))[:10]
205 lang_stats = [(x, {"count": y,
206 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
207 for x, y in lang_stats_items]
208
209 c.trending_languages = json.dumps(lang_stats)
210 else:
211 c.no_data = True
212 c.trending_languages = json.dumps({})
213
214 scm_model = ScmModel()
215 c.enable_downloads = self.db_repo.enable_downloads
216 c.repository_followers = scm_model.get_followers(self.db_repo)
217 c.repository_forks = scm_model.get_forks(self.db_repo)
218 c.repository_is_user_following = scm_model.is_following_repo(
219 self.db_repo_name, self._rhodecode_user.user_id)
220
221 # first interaction with the VCS instance after here...
222 if c.repository_requirements_missing:
223 self.request.override_renderer = \
224 'rhodecode:templates/summary/missing_requirements.mako'
225 return self._get_template_context(c)
226
227 c.readme_data, c.readme_file = \
228 self._get_readme_data(self.db_repo, c.visual.default_renderer)
229
230 # loads the summary commits template context
231 self._load_commits_context(c)
232
233 return self._get_template_context(c)
234
235 def get_request_commit_id(self):
236 return self.request.matchdict['commit_id']
237
238 @LoginRequired()
239 @HasRepoPermissionAnyDecorator(
240 'repository.read', 'repository.write', 'repository.admin')
241 @view_config(
242 route_name='repo_stats', request_method='GET',
243 renderer='json_ext')
244 def repo_stats(self):
245 commit_id = self.get_request_commit_id()
246
247 _namespace = caches.get_repo_namespace_key(
248 caches.SUMMARY_STATS, self.db_repo_name)
249 show_stats = bool(self.db_repo.enable_statistics)
250 cache_manager = caches.get_cache_manager(
251 'repo_cache_long', _namespace)
252 _cache_key = caches.compute_key_from_params(
253 self.db_repo_name, commit_id, show_stats)
254
255 def compute_stats():
256 code_stats = {}
257 size = 0
258 try:
259 scm_instance = self.db_repo.scm_instance()
260 commit = scm_instance.get_commit(commit_id)
261
262 for node in commit.get_filenodes_generator():
263 size += node.size
264 if not show_stats:
265 continue
266 ext = string.lower(node.extension)
267 ext_info = LANGUAGES_EXTENSIONS_MAP.get(ext)
268 if ext_info:
269 if ext in code_stats:
270 code_stats[ext]['count'] += 1
271 else:
272 code_stats[ext] = {"count": 1, "desc": ext_info}
273 except EmptyRepositoryError:
274 pass
275 return {'size': h.format_byte_size_binary(size),
276 'code_stats': code_stats}
277
278 stats = cache_manager.get(_cache_key, createfunc=compute_stats)
279 return stats
280
281 @LoginRequired()
282 @HasRepoPermissionAnyDecorator(
283 'repository.read', 'repository.write', 'repository.admin')
284 @view_config(
285 route_name='repo_refs_data', request_method='GET',
286 renderer='json_ext')
287 def repo_refs_data(self):
288 _ = self.request.translate
289 self.load_default_context()
290
291 repo = self.rhodecode_vcs_repo
292 refs_to_create = [
293 (_("Branch"), repo.branches, 'branch'),
294 (_("Tag"), repo.tags, 'tag'),
295 (_("Bookmark"), repo.bookmarks, 'book'),
296 ]
297 res = self._create_reference_data(
298 repo, self.db_repo_name, refs_to_create)
299 data = {
300 'more': False,
301 'results': res
302 }
303 return data
304
305 @LoginRequired()
306 @HasRepoPermissionAnyDecorator(
307 'repository.read', 'repository.write', 'repository.admin')
308 @view_config(
309 route_name='repo_refs_changelog_data', request_method='GET',
310 renderer='json_ext')
311 def repo_refs_changelog_data(self):
312 _ = self.request.translate
313 self.load_default_context()
314
315 repo = self.rhodecode_vcs_repo
316
317 refs_to_create = [
318 (_("Branches"), repo.branches, 'branch'),
319 (_("Closed branches"), repo.branches_closed, 'branch_closed'),
320 # TODO: enable when vcs can handle bookmarks filters
321 # (_("Bookmarks"), repo.bookmarks, "book"),
322 ]
323 res = self._create_reference_data(
324 repo, self.db_repo_name, refs_to_create)
325 data = {
326 'more': False,
327 'results': res
328 }
329 return data
330
331 def _create_reference_data(self, repo, full_repo_name, refs_to_create):
332 format_ref_id = utils.get_format_ref_id(repo)
333
334 result = []
335 for title, refs, ref_type in refs_to_create:
336 if refs:
337 result.append({
338 'text': title,
339 'children': self._create_reference_items(
340 repo, full_repo_name, refs, ref_type,
341 format_ref_id),
342 })
343 return result
344
345 def _create_reference_items(self, repo, full_repo_name, refs, ref_type,
346 format_ref_id):
347 result = []
348 is_svn = h.is_svn(repo)
349 for ref_name, raw_id in refs.iteritems():
350 files_url = self._create_files_url(
351 repo, full_repo_name, ref_name, raw_id, is_svn)
352 result.append({
353 'text': ref_name,
354 'id': format_ref_id(ref_name, raw_id),
355 'raw_id': raw_id,
356 'type': ref_type,
357 'files_url': files_url,
358 })
359 return result
360
361 def _create_files_url(self, repo, full_repo_name, ref_name, raw_id, is_svn):
362 use_commit_id = '/' in ref_name or is_svn
363 return h.url(
364 'files_home',
365 repo_name=full_repo_name,
366 f_path=ref_name if is_svn else '',
367 revision=raw_id if use_commit_id else ref_name,
368 at=ref_name)
@@ -0,0 +1,136 b''
1 ## -*- coding: utf-8 -*-
2 <%namespace name="base" file="/base/base.mako"/>
3 %if c.repo_commits:
4 <table class="rctable repo_summary table_disp">
5 <tr>
6
7 <th class="status" colspan="2"></th>
8 <th>${_('Commit')}</th>
9 <th>${_('Commit message')}</th>
10 <th>${_('Age')}</th>
11 <th>${_('Author')}</th>
12 <th>${_('Refs')}</th>
13 </tr>
14 %for cnt,cs in enumerate(c.repo_commits):
15 <tr class="parity${cnt%2}">
16
17 <td class="td-status">
18 %if c.statuses.get(cs.raw_id):
19 <div class="changeset-status-ico shortlog">
20 %if c.statuses.get(cs.raw_id)[2]:
21 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (c.statuses.get(cs.raw_id)[0], c.statuses.get(cs.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}">
22 <div class="${'flag_status %s' % c.statuses.get(cs.raw_id)[0]}"></div>
23 </a>
24 %else:
25 <a class="tooltip" title="${_('Commit status: %s') % h.commit_status_lbl(c.statuses.get(cs.raw_id)[0])}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
26 <div class="${'flag_status %s' % c.statuses.get(cs.raw_id)[0]}"></div>
27 </a>
28 %endif
29 </div>
30 %else:
31 <div class="tooltip flag_status not_reviewed" title="${_('Commit status: Not Reviewed')}"></div>
32 %endif
33 </td>
34 <td class="td-comments">
35 %if c.comments.get(cs.raw_id,[]):
36 <a title="${_('Commit has comments')}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
37 <i class="icon-comment"></i> ${len(c.comments[cs.raw_id])}
38 </a>
39 %endif
40 </td>
41 <td class="td-commit">
42 <pre><a href="${h.url('changeset_home', repo_name=c.repo_name, revision=cs.raw_id)}">${h.show_id(cs)}</a></pre>
43 </td>
44
45 <td class="td-description mid">
46 <div class="log-container truncate-wrap">
47 <div class="message truncate" id="c-${cs.raw_id}">${h.urlify_commit_message(cs.message, c.repo_name)}</div>
48 </div>
49 </td>
50
51 <td class="td-time">
52 ${h.age_component(cs.date)}
53 </td>
54 <td class="td-user author">
55 ${base.gravatar_with_user(cs.author)}
56 </td>
57
58 <td class="td-tags">
59 <div class="autoexpand">
60 %if h.is_hg(c.rhodecode_repo):
61 %for book in cs.bookmarks:
62 <span class="booktag tag" title="${_('Bookmark %s') % book}">
63 <a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
64 </span>
65 %endfor
66 %endif
67 ## tags
68 %for tag in cs.tags:
69 <span class="tagtag tag" title="${_('Tag %s') % tag}">
70 <a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
71 </span>
72 %endfor
73
74 ## branch
75 %if cs.branch:
76 <span class="branchtag tag" title="${_('Branch %s') % cs.branch}">
77 <a href="${h.url('changelog_home',repo_name=c.repo_name,branch=cs.branch)}"><i class="icon-code-fork"></i>${h.shorter(cs.branch)}</a>
78 </span>
79 %endif
80 </div>
81 </td>
82 </tr>
83 %endfor
84
85 </table>
86
87 <script type="text/javascript">
88 $(document).pjax('#shortlog_data .pager_link','#shortlog_data', {timeout: 2000, scrollTo: false });
89 $(document).on('pjax:success', function(){ timeagoActivate(); });
90 </script>
91
92 <div class="pagination-wh pagination-left">
93 ${c.repo_commits.pager('$link_previous ~2~ $link_next')}
94 </div>
95 %else:
96
97 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
98 <div class="quick_start">
99 <div class="fieldset">
100 <div class="left-label">${_('Add or upload files directly via RhodeCode:')}</div>
101 <div class="right-content">
102 <div id="add_node_id" class="add_node">
103 <a href="${h.url('files_add_home',repo_name=c.repo_name,revision=0,f_path='', anchor='edit')}" class="btn btn-default">${_('Add New File')}</a>
104 </div>
105 </div>
106 %endif
107 </div>
108
109 %if not h.is_svn(c.rhodecode_repo):
110 <div class="fieldset">
111 <div class="left-label">${_('Push new repo:')}</div>
112 <div class="right-content">
113 <pre>
114 ${c.rhodecode_repo.alias} clone ${c.clone_repo_url}
115 ${c.rhodecode_repo.alias} add README # add first file
116 ${c.rhodecode_repo.alias} commit -m "Initial" # commit with message
117 ${c.rhodecode_repo.alias} push ${'origin master' if h.is_git(c.rhodecode_repo) else ''} # push changes back
118 </pre>
119 </div>
120 </div>
121 <div class="fieldset">
122 <div class="left-label">${_('Existing repository?')}</div>
123 <div class="right-content">
124 <pre>
125 %if h.is_git(c.rhodecode_repo):
126 git remote add origin ${c.clone_repo_url}
127 git push -u origin master
128 %else:
129 hg push ${c.clone_repo_url}
130 %endif
131 </pre>
132 </div>
133 </div>
134 %endif
135 </div>
136 %endif
@@ -105,9 +105,14 b' class BaseAppView(object):'
105 raise HTTPFound(
105 raise HTTPFound(
106 self.request.route_path('my_account_password'))
106 self.request.route_path('my_account_password'))
107
107
108 def _get_local_tmpl_context(self):
108 def _get_local_tmpl_context(self, include_app_defaults=False):
109 c = TemplateArgs()
109 c = TemplateArgs()
110 c.auth_user = self.request.user
110 c.auth_user = self.request.user
111 if include_app_defaults:
112 # NOTE(marcink): after full pyramid migration include_app_defaults
113 # should be turned on by default
114 from rhodecode.lib.base import attach_context_attributes
115 attach_context_attributes(c, self.request, self.request.user.user_id)
111 return c
116 return c
112
117
113 def _register_global_c(self, tmpl_args):
118 def _register_global_c(self, tmpl_args):
@@ -154,8 +159,10 b' class RepoAppView(BaseAppView):'
154 'Requirements are missing for repository %s: %s',
159 'Requirements are missing for repository %s: %s',
155 self.db_repo_name, error.message)
160 self.db_repo_name, error.message)
156
161
157 def _get_local_tmpl_context(self):
162 def _get_local_tmpl_context(self, include_app_defaults=False):
158 c = super(RepoAppView, self)._get_local_tmpl_context()
163 c = super(RepoAppView, self)._get_local_tmpl_context(
164 include_app_defaults=include_app_defaults)
165
159 # register common vars for this type of view
166 # register common vars for this type of view
160 c.rhodecode_db_repo = self.db_repo
167 c.rhodecode_db_repo = self.db_repo
161 c.repo_name = self.db_repo_name
168 c.repo_name = self.db_repo_name
@@ -309,7 +316,7 b' class RepoTypeRoutePredicate(object):'
309 # _('Action not supported for %s.' % rhodecode_repo.alias)),
316 # _('Action not supported for %s.' % rhodecode_repo.alias)),
310 # category='warning')
317 # category='warning')
311 # return redirect(
318 # return redirect(
312 # url('summary_home', repo_name=cls.rhodecode_db_repo.repo_name))
319 # route_path('repo_summary', repo_name=cls.rhodecode_db_repo.repo_name))
313
320
314 return False
321 return False
315
322
@@ -123,7 +123,7 b' class HomeView(BaseAppView):'
123 'text': obj['name'],
123 'text': obj['name'],
124 'type': 'repo',
124 'type': 'repo',
125 'obj': obj['dbrepo'],
125 'obj': obj['dbrepo'],
126 'url': h.url('summary_home', repo_name=obj['name'])
126 'url': h.route_path('repo_summary', repo_name=obj['name'])
127 }
127 }
128 for obj in repo_iter]
128 for obj in repo_iter]
129
129
@@ -17,18 +17,33 b''
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 from rhodecode.apps._base import add_route_with_slash
20
21
21
22
22 def includeme(config):
23 def includeme(config):
23
24
24 # Summary
25 # Summary
25 config.add_route(
26 # NOTE(marcink): one additional route is defined in very bottom, catch
26 name='repo_summary',
27 # all pattern
27 pattern='/{repo_name:.*?[^/]}', repo_route=True)
28
29 config.add_route(
28 config.add_route(
30 name='repo_summary_explicit',
29 name='repo_summary_explicit',
31 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
30 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
31 config.add_route(
32 name='repo_summary_commits',
33 pattern='/{repo_name:.*?[^/]}/summary-commits', repo_route=True)
34
35 # refs data
36 config.add_route(
37 name='repo_refs_data',
38 pattern='/{repo_name:.*?[^/]}/refs-data', repo_route=True)
39
40 config.add_route(
41 name='repo_refs_changelog_data',
42 pattern='/{repo_name:.*?[^/]}/refs-data-changelog', repo_route=True)
43
44 config.add_route(
45 name='repo_stats',
46 pattern='/{repo_name:.*?[^/]}/repo_stats/{commit_id}', repo_route=True)
32
47
33 # Tags
48 # Tags
34 config.add_route(
49 config.add_route(
@@ -40,7 +55,6 b' def includeme(config):'
40 name='branches_home',
55 name='branches_home',
41 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
56 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
42
57
43 # Bookmarks
44 config.add_route(
58 config.add_route(
45 name='bookmarks_home',
59 name='bookmarks_home',
46 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
60 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
@@ -125,9 +139,10 b' def includeme(config):'
125 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
139 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
126
140
127 # NOTE(marcink): needs to be at the end for catch-all
141 # NOTE(marcink): needs to be at the end for catch-all
128 # config.add_route(
142 add_route_with_slash(
129 # name='repo_summary',
143 config,
130 # pattern='/{repo_name:.*?[^/]}', repo_route=True)
144 name='repo_summary',
145 pattern='/{repo_name:.*?[^/]}', repo_route=True)
131
146
132 # Scan module for configuration decorators.
147 # Scan module for configuration decorators.
133 config.scan()
148 config.scan()
@@ -23,16 +23,16 b' import re'
23 import mock
23 import mock
24 import pytest
24 import pytest
25
25
26 from rhodecode.controllers import summary
26 from rhodecode.apps.repository.views.repo_summary import RepoSummaryView
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.compat import OrderedDict
28 from rhodecode.lib.compat import OrderedDict
29 from rhodecode.lib.utils2 import AttributeDict
29 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
30 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
30 from rhodecode.model.db import Repository
31 from rhodecode.model.db import Repository
31 from rhodecode.model.meta import Session
32 from rhodecode.model.meta import Session
32 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.scm import ScmModel
34 from rhodecode.model.scm import ScmModel
34 from rhodecode.tests import (
35 from rhodecode.tests import assert_session_flash
35 TestController, url, HG_REPO, assert_session_flash)
36 from rhodecode.tests.fixture import Fixture
36 from rhodecode.tests.fixture import Fixture
37 from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
37 from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
38
38
@@ -40,14 +40,31 b' from rhodecode.tests.utils import Assert'
40 fixture = Fixture()
40 fixture = Fixture()
41
41
42
42
43 class TestSummaryController(TestController):
43 def route_path(name, params=None, **kwargs):
44 def test_index(self, backend, http_host_only_stub):
44 import urllib
45 self.log_user()
45
46 base_url = {
47 'repo_summary': '/{repo_name}',
48 'repo_stats': '/{repo_name}/repo_stats/{commit_id}',
49 'repo_refs_data': '/{repo_name}/refs-data',
50 'repo_refs_changelog_data': '/{repo_name}/refs-data-changelog'
51
52 }[name].format(**kwargs)
53
54 if params:
55 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
56 return base_url
57
58
59 @pytest.mark.usefixtures('app')
60 class TestSummaryView(object):
61 def test_index(self, autologin_user, backend, http_host_only_stub):
46 repo_id = backend.repo.repo_id
62 repo_id = backend.repo.repo_id
47 repo_name = backend.repo_name
63 repo_name = backend.repo_name
48 with mock.patch('rhodecode.lib.helpers.is_svn_without_proxy',
64 with mock.patch('rhodecode.lib.helpers.is_svn_without_proxy',
49 return_value=False):
65 return_value=False):
50 response = self.app.get(url('summary_home', repo_name=repo_name))
66 response = self.app.get(
67 route_path('repo_summary', repo_name=repo_name))
51
68
52 # repo type
69 # repo type
53 response.mustcontain(
70 response.mustcontain(
@@ -66,11 +83,11 b' class TestSummaryController(TestControll'
66 'id="clone_url_id" readonly="readonly"'
83 'id="clone_url_id" readonly="readonly"'
67 ' value="http://test_admin@%s/_%s"' % (http_host_only_stub, repo_id, ))
84 ' value="http://test_admin@%s/_%s"' % (http_host_only_stub, repo_id, ))
68
85
69 def test_index_svn_without_proxy(self, backend_svn, http_host_only_stub):
86 def test_index_svn_without_proxy(
70 self.log_user()
87 self, autologin_user, backend_svn, http_host_only_stub):
71 repo_id = backend_svn.repo.repo_id
88 repo_id = backend_svn.repo.repo_id
72 repo_name = backend_svn.repo_name
89 repo_name = backend_svn.repo_name
73 response = self.app.get(url('summary_home', repo_name=repo_name))
90 response = self.app.get(route_path('repo_summary', repo_name=repo_name))
74 # clone url...
91 # clone url...
75 response.mustcontain(
92 response.mustcontain(
76 'id="clone_url" disabled'
93 'id="clone_url" disabled'
@@ -79,14 +96,15 b' class TestSummaryController(TestControll'
79 'id="clone_url_id" disabled'
96 'id="clone_url_id" disabled'
80 ' value="http://test_admin@%s/_%s"' % (http_host_only_stub, repo_id, ))
97 ' value="http://test_admin@%s/_%s"' % (http_host_only_stub, repo_id, ))
81
98
82 def test_index_with_trailing_slash(self, autologin_user, backend,
99 def test_index_with_trailing_slash(
83 http_host_only_stub):
100 self, autologin_user, backend, http_host_only_stub):
101
84 repo_id = backend.repo.repo_id
102 repo_id = backend.repo.repo_id
85 repo_name = backend.repo_name
103 repo_name = backend.repo_name
86 with mock.patch('rhodecode.lib.helpers.is_svn_without_proxy',
104 with mock.patch('rhodecode.lib.helpers.is_svn_without_proxy',
87 return_value=False):
105 return_value=False):
88 response = self.app.get(
106 response = self.app.get(
89 url('summary_home', repo_name=repo_name) + '/',
107 route_path('repo_summary', repo_name=repo_name) + '/',
90 status=200)
108 status=200)
91
109
92 # clone url...
110 # clone url...
@@ -97,11 +115,10 b' class TestSummaryController(TestControll'
97 'id="clone_url_id" readonly="readonly"'
115 'id="clone_url_id" readonly="readonly"'
98 ' value="http://test_admin@%s/_%s"' % (http_host_only_stub, repo_id, ))
116 ' value="http://test_admin@%s/_%s"' % (http_host_only_stub, repo_id, ))
99
117
100 def test_index_by_id(self, backend):
118 def test_index_by_id(self, autologin_user, backend):
101 self.log_user()
102 repo_id = backend.repo.repo_id
119 repo_id = backend.repo.repo_id
103 response = self.app.get(url(
120 response = self.app.get(
104 'summary_home', repo_name='_%s' % (repo_id,)))
121 route_path('repo_summary', repo_name='_%s' % (repo_id,)))
105
122
106 # repo type
123 # repo type
107 response.mustcontain(
124 response.mustcontain(
@@ -112,10 +129,9 b' class TestSummaryController(TestControll'
112 """<i class="icon-unlock-alt">"""
129 """<i class="icon-unlock-alt">"""
113 )
130 )
114
131
115 def test_index_by_repo_having_id_path_in_name_hg(self):
132 def test_index_by_repo_having_id_path_in_name_hg(self, autologin_user):
116 self.log_user()
117 fixture.create_repo(name='repo_1')
133 fixture.create_repo(name='repo_1')
118 response = self.app.get(url('summary_home', repo_name='repo_1'))
134 response = self.app.get(route_path('repo_summary', repo_name='repo_1'))
119
135
120 try:
136 try:
121 response.mustcontain("repo_1")
137 response.mustcontain("repo_1")
@@ -123,10 +139,10 b' class TestSummaryController(TestControll'
123 RepoModel().delete(Repository.get_by_repo_name('repo_1'))
139 RepoModel().delete(Repository.get_by_repo_name('repo_1'))
124 Session().commit()
140 Session().commit()
125
141
126 def test_index_with_anonymous_access_disabled(self):
142 def test_index_with_anonymous_access_disabled(
127 with fixture.anon_access(False):
143 self, backend, disable_anonymous_user):
128 response = self.app.get(url('summary_home', repo_name=HG_REPO),
144 response = self.app.get(
129 status=302)
145 route_path('repo_summary', repo_name=backend.repo_name), status=302)
130 assert 'login' in response.location
146 assert 'login' in response.location
131
147
132 def _enable_stats(self, repo):
148 def _enable_stats(self, repo):
@@ -174,17 +190,15 b' class TestSummaryController(TestControll'
174 },
190 },
175 }
191 }
176
192
177 def test_repo_stats(self, backend, xhr_header):
193 def test_repo_stats(self, autologin_user, backend, xhr_header):
178 self.log_user()
179 response = self.app.get(
194 response = self.app.get(
180 url('repo_stats',
195 route_path(
181 repo_name=backend.repo_name, commit_id='tip'),
196 'repo_stats', repo_name=backend.repo_name, commit_id='tip'),
182 extra_environ=xhr_header,
197 extra_environ=xhr_header,
183 status=200)
198 status=200)
184 assert re.match(r'6[\d\.]+ KiB', response.json['size'])
199 assert re.match(r'6[\d\.]+ KiB', response.json['size'])
185
200
186 def test_repo_stats_code_stats_enabled(self, backend, xhr_header):
201 def test_repo_stats_code_stats_enabled(self, autologin_user, backend, xhr_header):
187 self.log_user()
188 repo_name = backend.repo_name
202 repo_name = backend.repo_name
189
203
190 # codes stats
204 # codes stats
@@ -192,8 +206,8 b' class TestSummaryController(TestControll'
192 ScmModel().mark_for_invalidation(repo_name)
206 ScmModel().mark_for_invalidation(repo_name)
193
207
194 response = self.app.get(
208 response = self.app.get(
195 url('repo_stats',
209 route_path(
196 repo_name=backend.repo_name, commit_id='tip'),
210 'repo_stats', repo_name=backend.repo_name, commit_id='tip'),
197 extra_environ=xhr_header,
211 extra_environ=xhr_header,
198 status=200)
212 status=200)
199
213
@@ -204,7 +218,7 b' class TestSummaryController(TestControll'
204
218
205 def test_repo_refs_data(self, backend):
219 def test_repo_refs_data(self, backend):
206 response = self.app.get(
220 response = self.app.get(
207 url('repo_refs_data', repo_name=backend.repo_name),
221 route_path('repo_refs_data', repo_name=backend.repo_name),
208 status=200)
222 status=200)
209
223
210 # Ensure that there is the correct amount of items in the result
224 # Ensure that there is the correct amount of items in the result
@@ -221,72 +235,68 b' class TestSummaryController(TestControll'
221 Repository, 'scm_instance', side_effect=RepositoryRequirementError)
235 Repository, 'scm_instance', side_effect=RepositoryRequirementError)
222
236
223 with scm_patcher:
237 with scm_patcher:
224 response = self.app.get(url('summary_home', repo_name=repo_name))
238 response = self.app.get(route_path('repo_summary', repo_name=repo_name))
225 assert_response = AssertResponse(response)
239 assert_response = AssertResponse(response)
226 assert_response.element_contains(
240 assert_response.element_contains(
227 '.main .alert-warning strong', 'Missing requirements')
241 '.main .alert-warning strong', 'Missing requirements')
228 assert_response.element_contains(
242 assert_response.element_contains(
229 '.main .alert-warning',
243 '.main .alert-warning',
230 'These commits cannot be displayed, because this repository'
244 'Commits cannot be displayed, because this repository '
231 ' uses the Mercurial largefiles extension, which was not enabled.')
245 'uses one or more extensions, which was not enabled.')
232
246
233 def test_missing_requirements_page_does_not_contains_switch_to(
247 def test_missing_requirements_page_does_not_contains_switch_to(
234 self, backend):
248 self, autologin_user, backend):
235 self.log_user()
236 repo_name = backend.repo_name
249 repo_name = backend.repo_name
237 scm_patcher = mock.patch.object(
250 scm_patcher = mock.patch.object(
238 Repository, 'scm_instance', side_effect=RepositoryRequirementError)
251 Repository, 'scm_instance', side_effect=RepositoryRequirementError)
239
252
240 with scm_patcher:
253 with scm_patcher:
241 response = self.app.get(url('summary_home', repo_name=repo_name))
254 response = self.app.get(route_path('repo_summary', repo_name=repo_name))
242 response.mustcontain(no='Switch To')
255 response.mustcontain(no='Switch To')
243
256
244
257
245 @pytest.mark.usefixtures('pylonsapp')
258 @pytest.mark.usefixtures('app')
246 class TestSwitcherReferenceData:
259 class TestRepoLocation(object):
247
260
248 def test_creates_reference_urls_based_on_name(self):
261 @pytest.mark.parametrize("suffix", [u'', u'ąęł'], ids=['', 'non-ascii'])
249 references = {
262 def test_manual_delete(self, autologin_user, backend, suffix, csrf_token):
250 'name': 'commit_id',
263 repo = backend.create_repo(name_suffix=suffix)
251 }
264 repo_name = repo.repo_name
252 controller = summary.SummaryController()
265
253 is_svn = False
266 # delete from file system
254 result = controller._switcher_reference_data(
267 RepoModel()._delete_filesystem_repo(repo)
255 'repo_name', references, is_svn)
256 expected_url = h.url(
257 'files_home', repo_name='repo_name', revision='name',
258 at='name')
259 assert result[0]['files_url'] == expected_url
260
268
261 def test_urls_contain_commit_id_if_slash_in_name(self):
269 # test if the repo is still in the database
262 references = {
270 new_repo = RepoModel().get_by_repo_name(repo_name)
263 'name/with/slash': 'commit_id',
271 assert new_repo.repo_name == repo_name
264 }
265 controller = summary.SummaryController()
266 is_svn = False
267 result = controller._switcher_reference_data(
268 'repo_name', references, is_svn)
269 expected_url = h.url(
270 'files_home', repo_name='repo_name', revision='commit_id',
271 at='name/with/slash')
272 assert result[0]['files_url'] == expected_url
273
272
274 def test_adds_reference_to_path_for_svn(self):
273 # check if repo is not in the filesystem
275 references = {
274 assert not repo_on_filesystem(repo_name)
276 'name/with/slash': 'commit_id',
275 self.assert_repo_not_found_redirect(repo_name)
277 }
276
278 controller = summary.SummaryController()
277 def assert_repo_not_found_redirect(self, repo_name):
279 is_svn = True
278 # run the check page that triggers the other flash message
280 result = controller._switcher_reference_data(
279 response = self.app.get(h.url('repo_check_home', repo_name=repo_name))
281 'repo_name', references, is_svn)
280 assert_session_flash(
282 expected_url = h.url(
281 response, 'The repository at %s cannot be located.' % repo_name)
283 'files_home', repo_name='repo_name', f_path='name/with/slash',
284 revision='commit_id', at='name/with/slash')
285 assert result[0]['files_url'] == expected_url
286
282
287
283
288 @pytest.mark.usefixtures('pylonsapp')
284 @pytest.fixture()
289 class TestCreateReferenceData:
285 def summary_view(context_stub, request_stub, user_util):
286 """
287 Bootstrap view to test the view functions
288 """
289 request_stub.matched_route = AttributeDict(name='test_view')
290
291 request_stub.user = user_util.create_user().AuthUser
292 request_stub.db_repo = user_util.create_repo()
293
294 view = RepoSummaryView(context=context_stub, request=request_stub)
295 return view
296
297
298 @pytest.mark.usefixtures('app')
299 class TestCreateReferenceData(object):
290
300
291 @pytest.fixture
301 @pytest.fixture
292 def example_refs(self):
302 def example_refs(self):
@@ -297,14 +307,13 b' class TestCreateReferenceData:'
297 ]
307 ]
298 return example_refs
308 return example_refs
299
309
300 def test_generates_refs_based_on_commit_ids(self, example_refs):
310 def test_generates_refs_based_on_commit_ids(self, example_refs, summary_view):
301 repo = mock.Mock()
311 repo = mock.Mock()
302 repo.name = 'test-repo'
312 repo.name = 'test-repo'
303 repo.alias = 'git'
313 repo.alias = 'git'
304 full_repo_name = 'pytest-repo-group/' + repo.name
314 full_repo_name = 'pytest-repo-group/' + repo.name
305 controller = summary.SummaryController()
306
315
307 result = controller._create_reference_data(
316 result = summary_view._create_reference_data(
308 repo, full_repo_name, example_refs)
317 repo, full_repo_name, example_refs)
309
318
310 expected_files_url = '/{}/files/'.format(full_repo_name)
319 expected_files_url = '/{}/files/'.format(full_repo_name)
@@ -333,13 +342,13 b' class TestCreateReferenceData:'
333 }]
342 }]
334 assert result == expected_result
343 assert result == expected_result
335
344
336 def test_generates_refs_with_path_for_svn(self, example_refs):
345 def test_generates_refs_with_path_for_svn(self, example_refs, summary_view):
337 repo = mock.Mock()
346 repo = mock.Mock()
338 repo.name = 'test-repo'
347 repo.name = 'test-repo'
339 repo.alias = 'svn'
348 repo.alias = 'svn'
340 full_repo_name = 'pytest-repo-group/' + repo.name
349 full_repo_name = 'pytest-repo-group/' + repo.name
341 controller = summary.SummaryController()
350
342 result = controller._create_reference_data(
351 result = summary_view._create_reference_data(
343 repo, full_repo_name, example_refs)
352 repo, full_repo_name, example_refs)
344
353
345 expected_files_url = '/{}/files/'.format(full_repo_name)
354 expected_files_url = '/{}/files/'.format(full_repo_name)
@@ -373,35 +382,9 b' class TestCreateReferenceData:'
373 assert result == expected_result
382 assert result == expected_result
374
383
375
384
376 @pytest.mark.usefixtures("app")
385 class TestCreateFilesUrl(object):
377 class TestRepoLocation:
378
379 @pytest.mark.parametrize("suffix", [u'', u'ąęł'], ids=['', 'non-ascii'])
380 def test_manual_delete(self, autologin_user, backend, suffix, csrf_token):
381 repo = backend.create_repo(name_suffix=suffix)
382 repo_name = repo.repo_name
383
384 # delete from file system
385 RepoModel()._delete_filesystem_repo(repo)
386
387 # test if the repo is still in the database
388 new_repo = RepoModel().get_by_repo_name(repo_name)
389 assert new_repo.repo_name == repo_name
390
386
391 # check if repo is not in the filesystem
387 def test_creates_non_svn_url(self, summary_view):
392 assert not repo_on_filesystem(repo_name)
393 self.assert_repo_not_found_redirect(repo_name)
394
395 def assert_repo_not_found_redirect(self, repo_name):
396 # run the check page that triggers the other flash message
397 response = self.app.get(url('repo_check_home', repo_name=repo_name))
398 assert_session_flash(
399 response, 'The repository at %s cannot be located.' % repo_name)
400
401
402 class TestCreateFilesUrl(object):
403 def test_creates_non_svn_url(self):
404 controller = summary.SummaryController()
405 repo = mock.Mock()
388 repo = mock.Mock()
406 repo.name = 'abcde'
389 repo.name = 'abcde'
407 full_repo_name = 'test-repo-group/' + repo.name
390 full_repo_name = 'test-repo-group/' + repo.name
@@ -409,16 +392,15 b' class TestCreateFilesUrl(object):'
409 raw_id = 'deadbeef0123456789'
392 raw_id = 'deadbeef0123456789'
410 is_svn = False
393 is_svn = False
411
394
412 with mock.patch.object(summary.h, 'url') as url_mock:
395 with mock.patch('rhodecode.lib.helpers.url') as url_mock:
413 result = controller._create_files_url(
396 result = summary_view._create_files_url(
414 repo, full_repo_name, ref_name, raw_id, is_svn)
397 repo, full_repo_name, ref_name, raw_id, is_svn)
415 url_mock.assert_called_once_with(
398 url_mock.assert_called_once_with(
416 'files_home', repo_name=full_repo_name, f_path='',
399 'files_home', repo_name=full_repo_name, f_path='',
417 revision=ref_name, at=ref_name)
400 revision=ref_name, at=ref_name)
418 assert result == url_mock.return_value
401 assert result == url_mock.return_value
419
402
420 def test_creates_svn_url(self):
403 def test_creates_svn_url(self, summary_view):
421 controller = summary.SummaryController()
422 repo = mock.Mock()
404 repo = mock.Mock()
423 repo.name = 'abcde'
405 repo.name = 'abcde'
424 full_repo_name = 'test-repo-group/' + repo.name
406 full_repo_name = 'test-repo-group/' + repo.name
@@ -426,16 +408,15 b' class TestCreateFilesUrl(object):'
426 raw_id = 'deadbeef0123456789'
408 raw_id = 'deadbeef0123456789'
427 is_svn = True
409 is_svn = True
428
410
429 with mock.patch.object(summary.h, 'url') as url_mock:
411 with mock.patch('rhodecode.lib.helpers.url') as url_mock:
430 result = controller._create_files_url(
412 result = summary_view._create_files_url(
431 repo, full_repo_name, ref_name, raw_id, is_svn)
413 repo, full_repo_name, ref_name, raw_id, is_svn)
432 url_mock.assert_called_once_with(
414 url_mock.assert_called_once_with(
433 'files_home', repo_name=full_repo_name, f_path=ref_name,
415 'files_home', repo_name=full_repo_name, f_path=ref_name,
434 revision=raw_id, at=ref_name)
416 revision=raw_id, at=ref_name)
435 assert result == url_mock.return_value
417 assert result == url_mock.return_value
436
418
437 def test_name_has_slashes(self):
419 def test_name_has_slashes(self, summary_view):
438 controller = summary.SummaryController()
439 repo = mock.Mock()
420 repo = mock.Mock()
440 repo.name = 'abcde'
421 repo.name = 'abcde'
441 full_repo_name = 'test-repo-group/' + repo.name
422 full_repo_name = 'test-repo-group/' + repo.name
@@ -443,8 +424,8 b' class TestCreateFilesUrl(object):'
443 raw_id = 'deadbeef0123456789'
424 raw_id = 'deadbeef0123456789'
444 is_svn = False
425 is_svn = False
445
426
446 with mock.patch.object(summary.h, 'url') as url_mock:
427 with mock.patch('rhodecode.lib.helpers.url') as url_mock:
447 result = controller._create_files_url(
428 result = summary_view._create_files_url(
448 repo, full_repo_name, ref_name, raw_id, is_svn)
429 repo, full_repo_name, ref_name, raw_id, is_svn)
449 url_mock.assert_called_once_with(
430 url_mock.assert_called_once_with(
450 'files_home', repo_name=full_repo_name, f_path='', revision=raw_id,
431 'files_home', repo_name=full_repo_name, f_path='', revision=raw_id,
@@ -463,42 +444,39 b' class TestReferenceItems(object):'
463 def _format_function(name, id_):
444 def _format_function(name, id_):
464 return 'format_function_{}_{}'.format(name, id_)
445 return 'format_function_{}_{}'.format(name, id_)
465
446
466 def test_creates_required_amount_of_items(self):
447 def test_creates_required_amount_of_items(self, summary_view):
467 amount = 100
448 amount = 100
468 refs = {
449 refs = {
469 'ref{}'.format(i): '{0:040d}'.format(i)
450 'ref{}'.format(i): '{0:040d}'.format(i)
470 for i in range(amount)
451 for i in range(amount)
471 }
452 }
472
453
473 controller = summary.SummaryController()
454 url_patcher = mock.patch.object(summary_view, '_create_files_url')
474
455 svn_patcher = mock.patch('rhodecode.lib.helpers.is_svn',
475 url_patcher = mock.patch.object(
456 return_value=False)
476 controller, '_create_files_url')
477 svn_patcher = mock.patch.object(
478 summary.h, 'is_svn', return_value=False)
479
457
480 with url_patcher as url_mock, svn_patcher:
458 with url_patcher as url_mock, svn_patcher:
481 result = controller._create_reference_items(
459 result = summary_view._create_reference_items(
482 self.repo, self.repo_full_name, refs, self.ref_type,
460 self.repo, self.repo_full_name, refs, self.ref_type,
483 self._format_function)
461 self._format_function)
484 assert len(result) == amount
462 assert len(result) == amount
485 assert url_mock.call_count == amount
463 assert url_mock.call_count == amount
486
464
487 def test_single_item_details(self):
465 def test_single_item_details(self, summary_view):
488 ref_name = 'ref1'
466 ref_name = 'ref1'
489 ref_id = 'deadbeef'
467 ref_id = 'deadbeef'
490 refs = {
468 refs = {
491 ref_name: ref_id
469 ref_name: ref_id
492 }
470 }
493
471
494 controller = summary.SummaryController()
472 svn_patcher = mock.patch('rhodecode.lib.helpers.is_svn',
473 return_value=False)
474
495 url_patcher = mock.patch.object(
475 url_patcher = mock.patch.object(
496 controller, '_create_files_url', return_value=self.fake_url)
476 summary_view, '_create_files_url', return_value=self.fake_url)
497 svn_patcher = mock.patch.object(
498 summary.h, 'is_svn', return_value=False)
499
477
500 with url_patcher as url_mock, svn_patcher:
478 with url_patcher as url_mock, svn_patcher:
501 result = controller._create_reference_items(
479 result = summary_view._create_reference_items(
502 self.repo, self.repo_full_name, refs, self.ref_type,
480 self.repo, self.repo_full_name, refs, self.ref_type,
503 self._format_function)
481 self._format_function)
504
482
@@ -39,11 +39,15 b' from routes.middleware import RoutesMidd'
39 import routes.util
39 import routes.util
40
40
41 import rhodecode
41 import rhodecode
42
42 from rhodecode.model import meta
43 from rhodecode.model import meta
43 from rhodecode.config import patches
44 from rhodecode.config import patches
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
45 from rhodecode.config.routing import STATIC_FILE_PREFIX
45 from rhodecode.config.environment import (
46 from rhodecode.config.environment import (
46 load_environment, load_pyramid_environment)
47 load_environment, load_pyramid_environment)
48
49 from rhodecode.lib.vcs import VCSCommunicationError
50 from rhodecode.lib.exceptions import VCSServerUnavailable
47 from rhodecode.lib.middleware import csrf
51 from rhodecode.lib.middleware import csrf
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
52 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.error_handling import (
53 from rhodecode.lib.middleware.error_handling import (
@@ -51,7 +55,7 b' from rhodecode.lib.middleware.error_hand'
51 from rhodecode.lib.middleware.https_fixup import HttpsFixup
55 from rhodecode.lib.middleware.https_fixup import HttpsFixup
52 from rhodecode.lib.middleware.vcs import VCSMiddleware
56 from rhodecode.lib.middleware.vcs import VCSMiddleware
53 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
57 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
54 from rhodecode.lib.utils2 import aslist as rhodecode_aslist
58 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
55 from rhodecode.subscribers import (
59 from rhodecode.subscribers import (
56 scan_repositories_if_enabled, write_js_routes_if_enabled,
60 scan_repositories_if_enabled, write_js_routes_if_enabled,
57 write_metadata_if_needed)
61 write_metadata_if_needed)
@@ -221,7 +225,6 b' def add_pylons_compat_data(registry, glo'
221
225
222 def error_handler(exception, request):
226 def error_handler(exception, request):
223 import rhodecode
227 import rhodecode
224 from rhodecode.lib.utils2 import AttributeDict
225 from rhodecode.lib import helpers
228 from rhodecode.lib import helpers
226
229
227 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
230 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
@@ -230,6 +233,8 b' def error_handler(exception, request):'
230 # prefer original exception for the response since it may have headers set
233 # prefer original exception for the response since it may have headers set
231 if isinstance(exception, HTTPException):
234 if isinstance(exception, HTTPException):
232 base_response = exception
235 base_response = exception
236 elif isinstance(exception, VCSCommunicationError):
237 base_response = VCSServerUnavailable()
233
238
234 def is_http_error(response):
239 def is_http_error(response):
235 # error which should have traceback
240 # error which should have traceback
@@ -257,6 +262,7 b' def error_handler(exception, request):'
257 if hasattr(base_response, 'causes'):
262 if hasattr(base_response, 'causes'):
258 c.causes = base_response.causes
263 c.causes = base_response.causes
259 c.messages = helpers.flash.pop_messages()
264 c.messages = helpers.flash.pop_messages()
265
260 response = render_to_response(
266 response = render_to_response(
261 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
267 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
262 response=base_response)
268 response=base_response)
@@ -404,7 +410,6 b' def wrap_app_in_wsgi_middlewares(pyramid'
404 pool = meta.Base.metadata.bind.engine.pool
410 pool = meta.Base.metadata.bind.engine.pool
405 log.debug('sa pool status: %s', pool.status())
411 log.debug('sa pool status: %s', pool.status())
406
412
407
408 return pyramid_app_with_cleanup
413 return pyramid_app_with_cleanup
409
414
410
415
@@ -117,7 +117,8 b' class JSRoutesMapper(Mapper):'
117
117
118 def make_map(config):
118 def make_map(config):
119 """Create, configure and return the routes Mapper"""
119 """Create, configure and return the routes Mapper"""
120 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
120 rmap = JSRoutesMapper(
121 directory=config['pylons.paths']['controllers'],
121 always_scan=config['debug'])
122 always_scan=config['debug'])
122 rmap.minimization = False
123 rmap.minimization = False
123 rmap.explicit = False
124 rmap.explicit = False
@@ -609,18 +610,6 b' def make_map(config):'
609 controller='admin/repos', action='repo_check',
610 controller='admin/repos', action='repo_check',
610 requirements=URL_NAME_REQUIREMENTS)
611 requirements=URL_NAME_REQUIREMENTS)
611
612
612 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
613 controller='summary', action='repo_stats',
614 conditions={'function': check_repo},
615 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
616
617 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
618 controller='summary', action='repo_refs_data',
619 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
620 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
621 controller='summary', action='repo_refs_changelog_data',
622 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
623
624 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
613 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
625 controller='changeset', revision='tip',
614 controller='changeset', revision='tip',
626 conditions={'function': check_repo},
615 conditions={'function': check_repo},
@@ -834,19 +823,10 b' def make_map(config):'
834 conditions={'function': check_repo, 'method': ['DELETE']},
823 conditions={'function': check_repo, 'method': ['DELETE']},
835 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
824 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
836
825
837 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
838 controller='summary', conditions={'function': check_repo},
839 requirements=URL_NAME_REQUIREMENTS)
840
841 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
826 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
842 controller='changelog', conditions={'function': check_repo},
827 controller='changelog', conditions={'function': check_repo},
843 requirements=URL_NAME_REQUIREMENTS)
828 requirements=URL_NAME_REQUIREMENTS)
844
829
845 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
846 controller='changelog', action='changelog_summary',
847 conditions={'function': check_repo},
848 requirements=URL_NAME_REQUIREMENTS)
849
850 rmap.connect('changelog_file_home',
830 rmap.connect('changelog_file_home',
851 '/{repo_name}/changelog/{revision}/{f_path}',
831 '/{repo_name}/changelog/{revision}/{f_path}',
852 controller='changelog', f_path=None,
832 controller='changelog', f_path=None,
@@ -999,19 +979,4 b' def make_map(config):'
999 conditions={'function': check_repo},
979 conditions={'function': check_repo},
1000 requirements=URL_NAME_REQUIREMENTS)
980 requirements=URL_NAME_REQUIREMENTS)
1001
981
1002 # catch all, at the end
1003 _connect_with_slash(
1004 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1005 controller='summary', action='index',
1006 conditions={'function': check_repo},
1007 requirements=URL_NAME_REQUIREMENTS)
1008
1009 return rmap
982 return rmap
1010
1011
1012 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1013 """
1014 Connect a route with an optional trailing slash in `path`.
1015 """
1016 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1017 mapper.connect(name, path, *args, **kwargs)
@@ -274,9 +274,9 b' class ReposController(BaseRepoController'
274 h.flash(_('Created repository %s from %s')
274 h.flash(_('Created repository %s from %s')
275 % (repo.repo_name, clone_uri), category='success')
275 % (repo.repo_name, clone_uri), category='success')
276 else:
276 else:
277 repo_url = h.link_to(repo.repo_name,
277 repo_url = h.link_to(
278 h.url('summary_home',
278 repo.repo_name,
279 repo_name=repo.repo_name))
279 h.route_path('repo_summary',repo_name=repo.repo_name))
280 fork = repo.fork
280 fork = repo.fork
281 if fork:
281 if fork:
282 fork_name = fork.repo_name
282 fork_name = fork.repo_name
@@ -366,7 +366,7 b' class ReposController(BaseRepoController'
366 log.exception("Exception during unlocking")
366 log.exception("Exception during unlocking")
367 h.flash(_('An error occurred during unlocking'),
367 h.flash(_('An error occurred during unlocking'),
368 category='error')
368 category='error')
369 return redirect(url('summary_home', repo_name=repo_name))
369 return redirect(h.route_path('repo_summary', repo_name=repo_name))
370
370
371 @HasRepoPermissionAllDecorator('repository.admin')
371 @HasRepoPermissionAllDecorator('repository.admin')
372 @auth.CSRFRequired()
372 @auth.CSRFRequired()
@@ -46,27 +46,6 b' log = logging.getLogger(__name__)'
46 DEFAULT_CHANGELOG_SIZE = 20
46 DEFAULT_CHANGELOG_SIZE = 20
47
47
48
48
49 def _load_changelog_summary():
50 p = safe_int(request.GET.get('page'), 1)
51 size = safe_int(request.GET.get('size'), 10)
52
53 def url_generator(**kw):
54 return url('summary_home',
55 repo_name=c.rhodecode_db_repo.repo_name, size=size, **kw)
56
57 pre_load = ['author', 'branch', 'date', 'message']
58 try:
59 collection = c.rhodecode_repo.get_commits(pre_load=pre_load)
60 except EmptyRepositoryError:
61 collection = c.rhodecode_repo
62
63 c.repo_commits = RepoPage(
64 collection, page=p, items_per_page=size, url=url_generator)
65 page_ids = [x.raw_id for x in c.repo_commits]
66 c.comments = c.rhodecode_db_repo.get_comments(page_ids)
67 c.statuses = c.rhodecode_db_repo.statuses(page_ids)
68
69
70 class ChangelogController(BaseRepoController):
49 class ChangelogController(BaseRepoController):
71
50
72 def __before__(self):
51 def __before__(self):
@@ -211,7 +190,7 b' class ChangelogController(BaseRepoContro'
211
190
212 except EmptyRepositoryError as e:
191 except EmptyRepositoryError as e:
213 h.flash(safe_str(e), category='warning')
192 h.flash(safe_str(e), category='warning')
214 return redirect(url('summary_home', repo_name=repo_name))
193 return redirect(h.route_path('repo_summary', repo_name=repo_name))
215 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
194 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
216 msg = safe_str(e)
195 msg = safe_str(e)
217 log.exception(msg)
196 log.exception(msg)
@@ -279,12 +258,3 b' class ChangelogController(BaseRepoContro'
279 c.rhodecode_repo, c.pagination,
258 c.rhodecode_repo, c.pagination,
280 prev_data=prev_data, next_data=next_data)
259 prev_data=prev_data, next_data=next_data)
281 return render('changelog/changelog_elements.mako')
260 return render('changelog/changelog_elements.mako')
282
283 @LoginRequired()
284 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
285 'repository.admin')
286 def changelog_summary(self, repo_name):
287 if request.environ.get('HTTP_X_PJAX'):
288 _load_changelog_summary()
289 return render('changelog/changelog_summary_data.mako')
290 raise HTTPNotFound()
@@ -63,14 +63,14 b' class CompareController(BaseRepoControll'
63 return repo.scm_instance().EMPTY_COMMIT
63 return repo.scm_instance().EMPTY_COMMIT
64 h.flash(h.literal(_('There are no commits yet')),
64 h.flash(h.literal(_('There are no commits yet')),
65 category='warning')
65 category='warning')
66 redirect(url('summary_home', repo_name=repo.repo_name))
66 redirect(h.route_path('repo_summary', repo_name=repo.repo_name))
67
67
68 except RepositoryError as e:
68 except RepositoryError as e:
69 msg = safe_str(e)
69 msg = safe_str(e)
70 log.exception(msg)
70 log.exception(msg)
71 h.flash(msg, category='warning')
71 h.flash(msg, category='warning')
72 if not partial:
72 if not partial:
73 redirect(h.url('summary_home', repo_name=repo.repo_name))
73 redirect(h.route_path('repo_summary', repo_name=repo.repo_name))
74 raise HTTPBadRequest()
74 raise HTTPBadRequest()
75
75
76 @LoginRequired()
76 @LoginRequired()
@@ -113,7 +113,7 b' class FeedController(BaseRepoController)'
113 def _generate_feed(cache_key):
113 def _generate_feed(cache_key):
114 feed = Atom1Feed(
114 feed = Atom1Feed(
115 title=self.title % repo_name,
115 title=self.title % repo_name,
116 link=url('summary_home', repo_name=repo_name, qualified=True),
116 link=h.route_url('repo_summary', repo_name=repo_name),
117 description=self.description % repo_name,
117 description=self.description % repo_name,
118 language=self.language,
118 language=self.language,
119 ttl=self.ttl
119 ttl=self.ttl
@@ -150,8 +150,7 b' class FeedController(BaseRepoController)'
150 def _generate_feed(cache_key):
150 def _generate_feed(cache_key):
151 feed = Rss201rev2Feed(
151 feed = Rss201rev2Feed(
152 title=self.title % repo_name,
152 title=self.title % repo_name,
153 link=url('summary_home', repo_name=repo_name,
153 link=h.route_url('repo_summary', repo_name=repo_name),
154 qualified=True),
155 description=self.description % repo_name,
154 description=self.description % repo_name,
156 language=self.language,
155 language=self.language,
157 ttl=self.ttl
156 ttl=self.ttl
@@ -101,7 +101,7 b' class FilesController(BaseRepoController'
101 add_new = ""
101 add_new = ""
102 h.flash(h.literal(
102 h.flash(h.literal(
103 _('There are no files yet. %s') % add_new), category='warning')
103 _('There are no files yet. %s') % add_new), category='warning')
104 redirect(h.url('summary_home', repo_name=repo_name))
104 redirect(h.route_path('repo_summary', repo_name=repo_name))
105 except (CommitDoesNotExistError, LookupError):
105 except (CommitDoesNotExistError, LookupError):
106 msg = _('No such commit exists for this repository')
106 msg = _('No such commit exists for this repository')
107 h.flash(msg, category='error')
107 h.flash(msg, category='error')
@@ -669,14 +669,14 b' class FilesController(BaseRepoController'
669
669
670 # If there's no commit, redirect to repo summary
670 # If there's no commit, redirect to repo summary
671 if type(c.commit) is EmptyCommit:
671 if type(c.commit) is EmptyCommit:
672 redirect_url = "summary_home"
672 redirect_url = h.route_path('repo_summary', repo_name=c.repo_name)
673 else:
673 else:
674 redirect_url = "changeset_home"
674 redirect_url = url("changeset_home", repo_name=c.repo_name,
675 revision='tip')
675
676
676 if not filename:
677 if not filename:
677 h.flash(_('No filename'), category='warning')
678 h.flash(_('No filename'), category='warning')
678 return redirect(url(redirect_url, repo_name=c.repo_name,
679 return redirect(redirect_url)
679 revision='tip'))
680
680
681 # extract the location from filename,
681 # extract the location from filename,
682 # allows using foo/bar.txt syntax to create subdirectories
682 # allows using foo/bar.txt syntax to create subdirectories
@@ -85,7 +85,7 b' class PullrequestsController(BaseRepoCon'
85 except EmptyRepositoryError:
85 except EmptyRepositoryError:
86 h.flash(h.literal(_('There are no commits yet')),
86 h.flash(h.literal(_('There are no commits yet')),
87 category='warning')
87 category='warning')
88 redirect(url('summary_home', repo_name=source_repo.repo_name))
88 redirect(h.route_path('repo_summary', repo_name=source_repo.repo_name))
89
89
90 commit_id = request.GET.get('commit')
90 commit_id = request.GET.get('commit')
91 branch_ref = request.GET.get('branch')
91 branch_ref = request.GET.get('branch')
@@ -161,8 +161,9 b' class ActionParser(object):'
161 return action_map
161 return action_map
162
162
163 def get_fork_name(self):
163 def get_fork_name(self):
164 from rhodecode.lib import helpers as h
164 repo_name = self.action_params
165 repo_name = self.action_params
165 _url = url('summary_home', repo_name=repo_name)
166 _url = h.route_path('repo_summary', repo_name=repo_name)
166 return _('fork name %s') % link_to(self.action_params, _url)
167 return _('fork name %s') % link_to(self.action_params, _url)
167
168
168 def get_user_name(self):
169 def get_user_name(self):
@@ -1342,7 +1342,7 b' class HasAcceptedRepoType(object):'
1342 _('Action not supported for %s.' % rhodecode_repo.alias)),
1342 _('Action not supported for %s.' % rhodecode_repo.alias)),
1343 category='warning')
1343 category='warning')
1344 return redirect(
1344 return redirect(
1345 url('summary_home', repo_name=cls.rhodecode_db_repo.repo_name))
1345 h.route_path('repo_summary', repo_name=cls.rhodecode_db_repo.repo_name))
1346
1346
1347
1347
1348 class PermsDecorator(object):
1348 class PermsDecorator(object):
@@ -580,7 +580,7 b' class BaseRepoController(BaseController)'
580 'Requirements are missing for repository %s: %s',
580 'Requirements are missing for repository %s: %s',
581 c.repo_name, error.message)
581 c.repo_name, error.message)
582
582
583 summary_url = url('summary_home', repo_name=c.repo_name)
583 summary_url = h.route_path('repo_summary', repo_name=c.repo_name)
584 statistics_url = url('edit_repo_statistics', repo_name=c.repo_name)
584 statistics_url = url('edit_repo_statistics', repo_name=c.repo_name)
585 settings_update_url = url('repo', repo_name=c.repo_name)
585 settings_update_url = url('repo', repo_name=c.repo_name)
586 path = request.path
586 path = request.path
@@ -1538,7 +1538,7 b' def breadcrumb_repo_link(repo):'
1538 link_to(group.name, route_path('repo_group_home', repo_group_name=group.group_name))
1538 link_to(group.name, route_path('repo_group_home', repo_group_name=group.group_name))
1539 for group in repo.groups_with_parents
1539 for group in repo.groups_with_parents
1540 ] + [
1540 ] + [
1541 link_to(repo.just_name, url('summary_home', repo_name=repo.repo_name))
1541 link_to(repo.just_name, route_path('repo_summary', repo_name=repo.repo_name))
1542 ]
1542 ]
1543
1543
1544 return literal(' &raquo; '.join(path))
1544 return literal(' &raquo; '.join(path))
@@ -268,8 +268,7 b' class CommentsModel(BaseModel):'
268
268
269 target_repo_url = h.link_to(
269 target_repo_url = h.link_to(
270 repo.repo_name,
270 repo.repo_name,
271 h.url('summary_home',
271 h.route_url('repo_summary', repo_name=repo.repo_name))
272 repo_name=repo.repo_name, qualified=True))
273
272
274 # commit specifics
273 # commit specifics
275 kwargs.update({
274 kwargs.update({
@@ -300,13 +299,11 b' class CommentsModel(BaseModel):'
300 qualified=True,)
299 qualified=True,)
301
300
302 # set some variables for email notification
301 # set some variables for email notification
303 pr_target_repo_url = h.url(
302 pr_target_repo_url = h.route_url(
304 'summary_home', repo_name=pr_target_repo.repo_name,
303 'repo_summary', repo_name=pr_target_repo.repo_name)
305 qualified=True)
306
304
307 pr_source_repo_url = h.url(
305 pr_source_repo_url = h.route_url(
308 'summary_home', repo_name=pr_source_repo.repo_name,
306 'repo_summary', repo_name=pr_source_repo.repo_name)
309 qualified=True)
310
307
311 # pull request specifics
308 # pull request specifics
312 kwargs.update({
309 kwargs.update({
@@ -1765,6 +1765,7 b' class Repository(Base, BaseModel):'
1765 # TODO: mikhail: Here there is an anti-pattern, we probably need to
1765 # TODO: mikhail: Here there is an anti-pattern, we probably need to
1766 # move this methods on models level.
1766 # move this methods on models level.
1767 from rhodecode.model.settings import SettingsModel
1767 from rhodecode.model.settings import SettingsModel
1768 from rhodecode.model.repo import RepoModel
1768
1769
1769 repo = self
1770 repo = self
1770 _user_id, _time, _reason = self.locked
1771 _user_id, _time, _reason = self.locked
@@ -1774,7 +1775,7 b' class Repository(Base, BaseModel):'
1774 'repo_name': repo.repo_name,
1775 'repo_name': repo.repo_name,
1775 'repo_type': repo.repo_type,
1776 'repo_type': repo.repo_type,
1776 'clone_uri': repo.clone_uri or '',
1777 'clone_uri': repo.clone_uri or '',
1777 'url': repo.home_url(),
1778 'url': RepoModel().get_url(self),
1778 'private': repo.private,
1779 'private': repo.private,
1779 'created_on': repo.created_on,
1780 'created_on': repo.created_on,
1780 'description': repo.description,
1781 'description': repo.description,
@@ -1935,10 +1936,6 b' class Repository(Base, BaseModel):'
1935 repo_name=self.repo_name,
1936 repo_name=self.repo_name,
1936 repo_id=self.repo_id, **override)
1937 repo_id=self.repo_id, **override)
1937
1938
1938 def home_url(self):
1939 request = get_current_request()
1940 return request.route_url('repo_summary', repo_name=self.repo_name)
1941
1942 def set_state(self, state):
1939 def set_state(self, state):
1943 self.repo_state = state
1940 self.repo_state = state
1944 Session().add(self)
1941 Session().add(self)
@@ -999,15 +999,11 b' class PullRequestModel(BaseModel):'
999 qualified=True,)
999 qualified=True,)
1000
1000
1001 # set some variables for email notification
1001 # set some variables for email notification
1002 pr_target_repo_url = h.url(
1002 pr_target_repo_url = h.route_url(
1003 'summary_home',
1003 'repo_summary', repo_name=pr_target_repo.repo_name)
1004 repo_name=pr_target_repo.repo_name,
1005 qualified=True)
1006
1004
1007 pr_source_repo_url = h.url(
1005 pr_source_repo_url = h.route_url(
1008 'summary_home',
1006 'repo_summary', repo_name=pr_source_repo.repo_name)
1009 repo_name=pr_source_repo.repo_name,
1010 qualified=True)
1011
1007
1012 # pull request specifics
1008 # pull request specifics
1013 pull_request_commits = [
1009 pull_request_commits = [
@@ -30,6 +30,7 b' import time'
30 import traceback
30 import traceback
31 from datetime import datetime, timedelta
31 from datetime import datetime, timedelta
32
32
33 from pyramid.threadlocal import get_current_request
33 from zope.cachedescriptors.property import Lazy as LazyProperty
34 from zope.cachedescriptors.property import Lazy as LazyProperty
34
35
35 from rhodecode import events
36 from rhodecode import events
@@ -154,9 +155,10 b' class RepoModel(BaseModel):'
154 repos = Repository.query().filter(Repository.group == root).all()
155 repos = Repository.query().filter(Repository.group == root).all()
155 return repos
156 return repos
156
157
157 def get_url(self, repo):
158 def get_url(self, repo, request=None):
158 return h.url('summary_home', repo_name=safe_str(repo.repo_name),
159 if not request:
159 qualified=True)
160 request = get_current_request()
161 return request.route_url('repo_summary', repo_name=safe_str(repo.repo_name))
160
162
161 @classmethod
163 @classmethod
162 def update_repoinfo(cls, repositories=None):
164 def update_repoinfo(cls, repositories=None):
@@ -215,6 +215,7 b''
215 float: left;
215 float: left;
216 display: block;
216 display: block;
217 position: relative;
217 position: relative;
218 width: 100%;
218
219
219 // adds some space to make copy and paste easier
220 // adds some space to make copy and paste easier
220 .left-label,
221 .left-label,
@@ -62,7 +62,7 b' function setRCMouseBindings(repoName, re'
62 // nav in repo context
62 // nav in repo context
63 Mousetrap.bind(['g s'], function(e) {
63 Mousetrap.bind(['g s'], function(e) {
64 window.location = pyroutes.url(
64 window.location = pyroutes.url(
65 'summary_home', {'repo_name': repoName});
65 'repo_summary', {'repo_name': repoName});
66 });
66 });
67 Mousetrap.bind(['g c'], function(e) {
67 Mousetrap.bind(['g c'], function(e) {
68 window.location = pyroutes.url(
68 window.location = pyroutes.url(
@@ -18,9 +18,6 b' function registerRCRoutes() {'
18 pyroutes.register('gists', '/_admin/gists', []);
18 pyroutes.register('gists', '/_admin/gists', []);
19 pyroutes.register('new_gist', '/_admin/gists/new', []);
19 pyroutes.register('new_gist', '/_admin/gists/new', []);
20 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
20 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
21 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
22 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
23 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
24 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
21 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
25 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
22 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
26 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
23 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
@@ -46,8 +43,6 b' function registerRCRoutes() {'
46 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
43 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
47 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
44 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
48 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
45 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
49 pyroutes.register('summary_home_slash', '/%(repo_name)s/', ['repo_name']);
50 pyroutes.register('summary_home', '/%(repo_name)s', ['repo_name']);
51 pyroutes.register('favicon', '/favicon.ico', []);
46 pyroutes.register('favicon', '/favicon.ico', []);
52 pyroutes.register('robots', '/robots.txt', []);
47 pyroutes.register('robots', '/robots.txt', []);
53 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
48 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
@@ -99,8 +94,11 b' function registerRCRoutes() {'
99 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
94 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
100 pyroutes.register('repo_list_data', '/_repos', []);
95 pyroutes.register('repo_list_data', '/_repos', []);
101 pyroutes.register('goto_switcher_data', '/_goto_data', []);
96 pyroutes.register('goto_switcher_data', '/_goto_data', []);
102 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
103 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
97 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
98 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
99 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
100 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
101 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
104 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
102 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
105 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
103 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
106 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
104 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
@@ -122,6 +120,8 b' function registerRCRoutes() {'
122 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
120 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
123 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
121 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
124 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
122 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
123 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
124 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
125 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
125 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
126 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
126 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
127 pyroutes.register('search', '/_admin/search', []);
127 pyroutes.register('search', '/_admin/search', []);
@@ -45,7 +45,7 b''
45 </td>
45 </td>
46 <td class="td-componentname">
46 <td class="td-componentname">
47 %if l.repository is not None:
47 %if l.repository is not None:
48 ${h.link_to(l.repository.repo_name,h.url('summary_home',repo_name=l.repository.repo_name))}
48 ${h.link_to(l.repository.repo_name, h.route_path('repo_summary',repo_name=l.repository.repo_name))}
49 %else:
49 %else:
50 ${l.repository_name}
50 ${l.repository_name}
51 %endif
51 %endif
@@ -160,7 +160,7 b''
160 </td>
160 </td>
161 <td class="td-scope">
161 <td class="td-scope">
162 %if integration.repo:
162 %if integration.repo:
163 <a href="${h.url('summary_home', repo_name=integration.repo.repo_name)}">
163 <a href="${h.route_path('repo_summary', repo_name=integration.repo.repo_name)}">
164 ${_('repo')}:${integration.repo.repo_name}
164 ${_('repo')}:${integration.repo.repo_name}
165 </a>
165 </a>
166 %elif integration.repo_group:
166 %elif integration.repo_group:
@@ -48,12 +48,12 b''
48 if (jsonResponse === undefined) {
48 if (jsonResponse === undefined) {
49 setTimeout(function () {
49 setTimeout(function () {
50 // we might have a backend problem, try dashboard again
50 // we might have a backend problem, try dashboard again
51 window.location = "${h.url('summary_home', repo_name = c.repo)}";
51 window.location = "${h.route_path('repo_summary', repo_name = c.repo)}";
52 }, 3000);
52 }, 3000);
53 } else {
53 } else {
54 if (skipCheck || jsonResponse.result === true) {
54 if (skipCheck || jsonResponse.result === true) {
55 // success, means go to dashboard
55 // success, means go to dashboard
56 window.location = "${h.url('summary_home', repo_name = c.repo)}";
56 window.location = "${h.route_path('repo_summary', repo_name = c.repo)}";
57 } else {
57 } else {
58 // Schedule the next request when the current one's complete
58 // Schedule the next request when the current one's complete
59 setTimeout(worker, 1000);
59 setTimeout(worker, 1000);
@@ -27,7 +27,7 b''
27 ${h.secure_form(h.route_path('edit_repo_advanced_fork', repo_name=c.repo_info.repo_name), method='POST')}
27 ${h.secure_form(h.route_path('edit_repo_advanced_fork', repo_name=c.repo_info.repo_name), method='POST')}
28
28
29 % if c.repo_info.fork:
29 % if c.repo_info.fork:
30 <div class="panel-body-title-text">${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.repo_info.fork.repo_name, h.url('summary_home', repo_name=c.repo_info.fork.repo_name))})}
30 <div class="panel-body-title-text">${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.repo_info.fork.repo_name, h.route_path('repo_summary', repo_name=c.repo_info.fork.repo_name))})}
31 | <button class="btn btn-link btn-danger" type="submit">Remove fork reference</button></div>
31 | <button class="btn btn-link btn-danger" type="submit">Remove fork reference</button></div>
32 % endif
32 % endif
33
33
@@ -52,6 +52,6 b''
52 <script>
52 <script>
53 $('#check_for_update').click(function(e){
53 $('#check_for_update').click(function(e){
54 $('#update_notice').show();
54 $('#update_notice').show();
55 $('#update_notice').load("${h.route_path('admin_settings_system_update',version=c.rhodecode_version, platform=c.platform)}");
55 $('#update_notice').load("${h.route_path('admin_settings_system_update')}");
56 })
56 })
57 </script>
57 </script>
@@ -186,7 +186,7 b''
186 %if repo_instance.fork:
186 %if repo_instance.fork:
187 <p>
187 <p>
188 <i class="icon-code-fork"></i> ${_('Fork of')}
188 <i class="icon-code-fork"></i> ${_('Fork of')}
189 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
189 <a href="${h.route_path('repo_summary',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
190 </p>
190 </p>
191 %endif
191 %endif
192
192
@@ -225,7 +225,7 b''
225 <div id="context-bar">
225 <div id="context-bar">
226 <div class="wrapper">
226 <div class="wrapper">
227 <ul id="context-pages" class="horizontal-list navigation">
227 <ul id="context-pages" class="horizontal-list navigation">
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
231 <li class="${is_active('compare')}">
231 <li class="${is_active('compare')}">
@@ -124,7 +124,7 b''
124 <tr class="perm_row ${'%s_%s' % (section, section_perm.split('.')[-1])}">
124 <tr class="perm_row ${'%s_%s' % (section, section_perm.split('.')[-1])}">
125 <td class="td-componentname">
125 <td class="td-componentname">
126 %if section == 'repositories':
126 %if section == 'repositories':
127 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
127 <a href="${h.route_path('repo_summary',repo_name=k)}">${k}</a>
128 %elif section == 'repositories_groups':
128 %elif section == 'repositories_groups':
129 <a href="${h.route_path('repo_group_home', repo_group_name=k)}">${k}</a>
129 <a href="${h.route_path('repo_group_home', repo_group_name=k)}">${k}</a>
130 %elif section == 'user_groups':
130 %elif section == 'user_groups':
@@ -9,7 +9,7 b''
9 <div class="menu_items_container hidden">
9 <div class="menu_items_container hidden">
10 <ul class="menu_items">
10 <ul class="menu_items">
11 <li>
11 <li>
12 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=repo_name)}">
12 <a title="${_('Summary')}" href="${h.route_path('repo_summary',repo_name=repo_name)}">
13 <span>${_('Summary')}</span>
13 <span>${_('Summary')}</span>
14 </a>
14 </a>
15 </li>
15 </li>
@@ -42,7 +42,7 b''
42 %>
42 %>
43 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
43 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
44 ##NAME
44 ##NAME
45 <a href="${h.route_path('edit_repo',repo_name=name) if admin else h.url('summary_home',repo_name=name)}">
45 <a href="${h.route_path('edit_repo',repo_name=name) if admin else h.route_path('repo_summary',repo_name=name)}">
46
46
47 ##TYPE OF REPO
47 ##TYPE OF REPO
48 %if h.is_hg(rtype):
48 %if h.is_hg(rtype):
@@ -64,7 +64,7 b''
64 ${get_name(name)}
64 ${get_name(name)}
65 </a>
65 </a>
66 %if fork_of:
66 %if fork_of:
67 <a href="${h.url('summary_home',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
67 <a href="${h.route_path('repo_summary',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
68 %endif
68 %endif
69 %if rstate == 'repo_state_pending':
69 %if rstate == 'repo_state_pending':
70 <i class="icon-cogs" title="${_('Repository creating in progress...')}"></i>
70 <i class="icon-cogs" title="${_('Repository creating in progress...')}"></i>
@@ -282,7 +282,7 b''
282
282
283 <%def name="pullrequest_target_repo(repo_name)">
283 <%def name="pullrequest_target_repo(repo_name)">
284 <div class="truncate">
284 <div class="truncate">
285 ${h.link_to(repo_name,h.url('summary_home',repo_name=repo_name))}
285 ${h.link_to(repo_name,h.route_path('repo_summary',repo_name=repo_name))}
286 </div>
286 </div>
287 </%def>
287 </%def>
288 <%def name="pullrequest_status(status)">
288 <%def name="pullrequest_status(status)">
@@ -16,7 +16,7 b''
16 ${base.gravatar_with_user(f.user.email, 16)}
16 ${base.gravatar_with_user(f.user.email, 16)}
17 </td>
17 </td>
18 <td class="td-componentname">
18 <td class="td-componentname">
19 ${h.link_to(f.repo_name,h.url('summary_home',repo_name=f.repo_name))}
19 ${h.link_to(f.repo_name,h.route_path('repo_summary',repo_name=f.repo_name))}
20 </td>
20 </td>
21 <td class="td-description">
21 <td class="td-description">
22 <div class="truncate">${f.description}</div>
22 <div class="truncate">${f.description}</div>
@@ -20,7 +20,7 b''
20 <span class="journal_repo_name">
20 <span class="journal_repo_name">
21 %if entry.repository is not None:
21 %if entry.repository is not None:
22 ${h.link_to(entry.repository.repo_name,
22 ${h.link_to(entry.repository.repo_name,
23 h.url('summary_home',repo_name=entry.repository.repo_name))}
23 h.route_path('repo_summary',repo_name=entry.repository.repo_name))}
24 %else:
24 %else:
25 ${entry.repository_name}
25 ${entry.repository_name}
26 %endif
26 %endif
@@ -81,7 +81,7 b''
81 %endif
81 %endif
82 </span>
82 </span>
83 <span class="clone-url">
83 <span class="clone-url">
84 <a href="${h.url('summary_home', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.clone_url()}</a>
84 <a href="${h.route_path('repo_summary', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.clone_url()}</a>
85 </span>
85 </span>
86 <br/>
86 <br/>
87 % if c.ancestor_commit:
87 % if c.ancestor_commit:
@@ -113,7 +113,7 b''
113 %endif
113 %endif
114 </span>
114 </span>
115 <span class="clone-url">
115 <span class="clone-url">
116 <a href="${h.url('summary_home', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.clone_url()}</a>
116 <a href="${h.route_path('repo_summary', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.clone_url()}</a>
117 </span>
117 </span>
118 </div>
118 </div>
119 </div>
119 </div>
@@ -27,7 +27,7 b''
27 %elif h.get_repo_type_by_name(entry.get('repository')) == 'svn':
27 %elif h.get_repo_type_by_name(entry.get('repository')) == 'svn':
28 <i class="icon-svn"></i>
28 <i class="icon-svn"></i>
29 %endif
29 %endif
30 ${h.link_to(entry['repository'], h.url('summary_home',repo_name=entry['repository']))}
30 ${h.link_to(entry['repository'], h.route_path('repo_summary',repo_name=entry['repository']))}
31 </td>
31 </td>
32 <td class="td-commit">
32 <td class="td-commit">
33 ${h.link_to(h._shorten_commit_id(entry['commit_id']),
33 ${h.link_to(h._shorten_commit_id(entry['commit_id']),
@@ -49,7 +49,7 b' for line_number in matching_lines:'
49 %elif h.get_repo_type_by_name(entry.get('repository')) == 'svn':
49 %elif h.get_repo_type_by_name(entry.get('repository')) == 'svn':
50 <i class="icon-svn"></i>
50 <i class="icon-svn"></i>
51 %endif
51 %endif
52 ${h.link_to(entry['repository'], h.url('summary_home',repo_name=entry['repository']))}
52 ${h.link_to(entry['repository'], h.route_path('repo_summary',repo_name=entry['repository']))}
53 </h2>
53 </h2>
54 <div class="stats">
54 <div class="stats">
55 ${h.link_to(h.literal(entry['f_path']), h.url('files_home',repo_name=entry['repository'],revision=entry.get('commit_id', 'tip'),f_path=entry['f_path']))}
55 ${h.link_to(h.literal(entry['f_path']), h.url('files_home',repo_name=entry['repository'],revision=entry.get('commit_id', 'tip'),f_path=entry['f_path']))}
@@ -16,7 +16,7 b''
16 %elif h.get_repo_type_by_name(entry.get('repository')) == 'svn':
16 %elif h.get_repo_type_by_name(entry.get('repository')) == 'svn':
17 <i class="icon-svn"></i>
17 <i class="icon-svn"></i>
18 %endif
18 %endif
19 ${h.link_to(entry['repository'], h.url('summary_home',repo_name=entry['repository']))}
19 ${h.link_to(entry['repository'], h.route_path('repo_summary',repo_name=entry['repository']))}
20 </td>
20 </td>
21 <td class="td-componentname">
21 <td class="td-componentname">
22 ${h.link_to(h.literal(entry['f_path']),
22 ${h.link_to(h.literal(entry['f_path']),
@@ -1,28 +1,28 b''
1 <%def name="refs_counters(branches, closed_branches, tags, bookmarks)">
1 <%def name="refs_counters(branches, closed_branches, tags, bookmarks)">
2 <span class="branchtag tag">
2 <span class="branchtag tag">
3 <a href="${h.route_path('branches_home',repo_name=c.repo_name)}" class="childs">
3 <a href="${h.route_path('branches_home',repo_name=c.repo_name)}" class="childs">
4 <i class="icon-branch"></i>${ungettext(
4 <i class="icon-branch"></i>${_ungettext(
5 '%(num)s Branch','%(num)s Branches', len(branches)) % {'num': len(branches)}}</a>
5 '%(num)s Branch','%(num)s Branches', len(branches)) % {'num': len(branches)}}</a>
6 </span>
6 </span>
7
7
8 %if closed_branches:
8 %if closed_branches:
9 <span class="branchtag tag">
9 <span class="branchtag tag">
10 <a href="${h.route_path('branches_home',repo_name=c.repo_name)}" class="childs">
10 <a href="${h.route_path('branches_home',repo_name=c.repo_name)}" class="childs">
11 <i class="icon-branch"></i>${ungettext(
11 <i class="icon-branch"></i>${_ungettext(
12 '%(num)s Closed Branch', '%(num)s Closed Branches', len(closed_branches)) % {'num': len(closed_branches)}}</a>
12 '%(num)s Closed Branch', '%(num)s Closed Branches', len(closed_branches)) % {'num': len(closed_branches)}}</a>
13 </span>
13 </span>
14 %endif
14 %endif
15
15
16 <span class="tagtag tag">
16 <span class="tagtag tag">
17 <a href="${h.route_path('tags_home',repo_name=c.repo_name)}" class="childs">
17 <a href="${h.route_path('tags_home',repo_name=c.repo_name)}" class="childs">
18 <i class="icon-tag"></i>${ungettext(
18 <i class="icon-tag"></i>${_ungettext(
19 '%(num)s Tag', '%(num)s Tags', len(tags)) % {'num': len(tags)}}</a>
19 '%(num)s Tag', '%(num)s Tags', len(tags)) % {'num': len(tags)}}</a>
20 </span>
20 </span>
21
21
22 %if bookmarks:
22 %if bookmarks:
23 <span class="booktag tag">
23 <span class="booktag tag">
24 <a href="${h.route_path('bookmarks_home',repo_name=c.repo_name)}" class="childs">
24 <a href="${h.route_path('bookmarks_home',repo_name=c.repo_name)}" class="childs">
25 <i class="icon-bookmark"></i>${ungettext(
25 <i class="icon-bookmark"></i>${_ungettext(
26 '%(num)s Bookmark', '%(num)s Bookmarks', len(bookmarks)) % {'num': len(bookmarks)}}</a>
26 '%(num)s Bookmark', '%(num)s Bookmarks', len(bookmarks)) % {'num': len(bookmarks)}}</a>
27 </span>
27 </span>
28 %endif
28 %endif
@@ -92,15 +92,15 b''
92
92
93 ## commits
93 ## commits
94 % if commit_rev == -1:
94 % if commit_rev == -1:
95 ${ungettext('%(num)s Commit', '%(num)s Commits', 0) % {'num': 0}},
95 ${_ungettext('%(num)s Commit', '%(num)s Commits', 0) % {'num': 0}},
96 % else:
96 % else:
97 <a href="${h.url('changelog_home', repo_name=c.repo_name)}">
97 <a href="${h.url('changelog_home', repo_name=c.repo_name)}">
98 ${ungettext('%(num)s Commit', '%(num)s Commits', commit_rev) % {'num': commit_rev}}</a>,
98 ${_ungettext('%(num)s Commit', '%(num)s Commits', commit_rev) % {'num': commit_rev}}</a>,
99 % endif
99 % endif
100
100
101 ## forks
101 ## forks
102 <a title="${_('Number of Repository Forks')}" href="${h.url('repo_forks_home', repo_name=c.repo_name)}">
102 <a title="${_('Number of Repository Forks')}" href="${h.url('repo_forks_home', repo_name=c.repo_name)}">
103 ${c.repository_forks} ${ungettext('Fork', 'Forks', c.repository_forks)}</a>,
103 ${c.repository_forks} ${_ungettext('Fork', 'Forks', c.repository_forks)}</a>,
104
104
105 ## repo size
105 ## repo size
106 % if commit_rev == -1:
106 % if commit_rev == -1:
@@ -14,8 +14,14 b''
14
14
15 <div class="alert alert-dismissable alert-warning">
15 <div class="alert alert-dismissable alert-warning">
16 <strong>Missing requirements</strong>
16 <strong>Missing requirements</strong>
17 These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.
17 Commits cannot be displayed, because this repository uses one or more extensions, which was not enabled. <br/>
18 Please <a href="${h.url('repo_vcs_settings', repo_name=c.repo_name)}">enable this extension in settings</a>, or contact the repository owner for help.
18 Please <a href="${h.url('repo_vcs_settings', repo_name=c.repo_name)}">enable extension in settings</a>, or contact the repository owner for help.
19 Missing extensions could be:
20 <pre>
21
22 - Mercurial largefiles
23 - Git LFS
24 </pre>
19 </div>
25 </div>
20
26
21 </%def>
27 </%def>
@@ -36,7 +36,7 b''
36 %endif
36 %endif
37 <div class="table">
37 <div class="table">
38 <div id="shortlog_data">
38 <div id="shortlog_data">
39 <%include file='../changelog/changelog_summary_data.mako'/>
39 <%include file='summary_commits.mako'/>
40 </div>
40 </div>
41 </div>
41 </div>
42 </div>
42 </div>
@@ -110,8 +110,7 b''
110
110
111 var callback = function (data) {
111 var callback = function (data) {
112 % if c.show_stats:
112 % if c.show_stats:
113 showRepoStats(
113 showRepoStats('lang_stats', data);
114 'lang_stats', data);
115 % endif
114 % endif
116 };
115 };
117
116
@@ -56,7 +56,7 b' def scm_extras(user_regular, repo_stub):'
56 RepoPreCreateEvent, RepoCreateEvent,
56 RepoPreCreateEvent, RepoCreateEvent,
57 RepoPreDeleteEvent, RepoDeleteEvent,
57 RepoPreDeleteEvent, RepoDeleteEvent,
58 ])
58 ])
59 def test_repo_events_serialized(repo_stub, EventClass):
59 def test_repo_events_serialized(config_stub, repo_stub, EventClass):
60 event = EventClass(repo_stub)
60 event = EventClass(repo_stub)
61 data = event.as_dict()
61 data = event.as_dict()
62 assert data['name'] == EventClass.name
62 assert data['name'] == EventClass.name
@@ -67,7 +67,7 b' def test_repo_events_serialized(repo_stu'
67 @pytest.mark.parametrize('EventClass', [
67 @pytest.mark.parametrize('EventClass', [
68 RepoPrePullEvent, RepoPullEvent, RepoPrePushEvent
68 RepoPrePullEvent, RepoPullEvent, RepoPrePushEvent
69 ])
69 ])
70 def test_vcs_repo_events_serialize(repo_stub, scm_extras, EventClass):
70 def test_vcs_repo_events_serialize(config_stub, repo_stub, scm_extras, EventClass):
71 event = EventClass(repo_name=repo_stub.repo_name, extras=scm_extras)
71 event = EventClass(repo_name=repo_stub.repo_name, extras=scm_extras)
72 data = event.as_dict()
72 data = event.as_dict()
73 assert data['name'] == EventClass.name
73 assert data['name'] == EventClass.name
@@ -76,7 +76,7 b' def test_vcs_repo_events_serialize(repo_'
76
76
77
77
78 @pytest.mark.parametrize('EventClass', [RepoPushEvent])
78 @pytest.mark.parametrize('EventClass', [RepoPushEvent])
79 def test_vcs_repo_push_event_serialize(repo_stub, scm_extras, EventClass):
79 def test_vcs_repo_push_event_serialize(config_stub, repo_stub, scm_extras, EventClass):
80 event = EventClass(repo_name=repo_stub.repo_name,
80 event = EventClass(repo_name=repo_stub.repo_name,
81 pushed_commit_ids=scm_extras['commit_ids'],
81 pushed_commit_ids=scm_extras['commit_ids'],
82 extras=scm_extras)
82 extras=scm_extras)
@@ -24,10 +24,10 b' import mock'
24 import pytest
24 import pytest
25
25
26 from rhodecode.lib import auth
26 from rhodecode.lib import auth
27 from rhodecode.lib.utils2 import safe_str, str2bool, safe_unicode
27 from rhodecode.lib.utils2 import safe_str, str2bool
28 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
28 from rhodecode.lib import helpers as h
29 from rhodecode.model.db import Repository, RepoGroup, UserRepoToPerm, User,\
29 from rhodecode.model.db import (
30 Permission
30 Repository, RepoGroup, UserRepoToPerm, User, Permission)
31 from rhodecode.model.meta import Session
31 from rhodecode.model.meta import Session
32 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.repo_group import RepoGroupModel
33 from rhodecode.model.repo_group import RepoGroupModel
@@ -475,7 +475,7 b' class TestAdminRepos(object):'
475 assert new_repo.description == description
475 assert new_repo.description == description
476
476
477 # test if the repository is visible in the list ?
477 # test if the repository is visible in the list ?
478 response = self.app.get(url('summary_home', repo_name=repo_name))
478 response = self.app.get(h.route_path('repo_summary', repo_name=repo_name))
479 response.mustcontain(repo_name)
479 response.mustcontain(repo_name)
480 response.mustcontain(backend.alias)
480 response.mustcontain(backend.alias)
481
481
@@ -796,11 +796,12 b' class TestChangingFiles:'
796
796
797 # Not allowed, redirect to the summary
797 # Not allowed, redirect to the summary
798 redirected = response.follow()
798 redirected = response.follow()
799 summary_url = url('summary_home', repo_name=repo.repo_name)
799 summary_url = h.route_path('repo_summary', repo_name=repo.repo_name)
800
800
801 # As there are no commits, displays the summary page with the error of
801 # As there are no commits, displays the summary page with the error of
802 # creating a file with no filename
802 # creating a file with no filename
803 assert redirected.req.path == summary_url
803
804 assert redirected.request.path == summary_url
804
805
805 @pytest.mark.parametrize("location, filename", [
806 @pytest.mark.parametrize("location, filename", [
806 ('/abs', 'foo'),
807 ('/abs', 'foo'),
@@ -22,6 +22,7 b' import pytest'
22
22
23 from rhodecode.tests import *
23 from rhodecode.tests import *
24 from rhodecode.tests.fixture import Fixture
24 from rhodecode.tests.fixture import Fixture
25 from rhodecode.lib import helpers as h
25
26
26 from rhodecode.model.db import Repository
27 from rhodecode.model.db import Repository
27 from rhodecode.model.repo import RepoModel
28 from rhodecode.model.repo import RepoModel
@@ -147,7 +148,7 b' class _BaseTest(TestController):'
147 assert fork_repo.fork.repo_name == repo_name
148 assert fork_repo.fork.repo_name == repo_name
148
149
149 # test if the repository is visible in the list ?
150 # test if the repository is visible in the list ?
150 response = self.app.get(url('summary_home', repo_name=fork_name_full))
151 response = self.app.get(h.route_path('repo_summary', repo_name=fork_name_full))
151 response.mustcontain(fork_name_full)
152 response.mustcontain(fork_name_full)
152 response.mustcontain(self.REPO_TYPE)
153 response.mustcontain(self.REPO_TYPE)
153
154
@@ -193,7 +194,7 b' class _BaseTest(TestController):'
193 assert fork_repo.fork.repo_name == repo_name
194 assert fork_repo.fork.repo_name == repo_name
194
195
195 # test if the repository is visible in the list ?
196 # test if the repository is visible in the list ?
196 response = self.app.get(url('summary_home', repo_name=fork_name))
197 response = self.app.get(h.route_path('repo_summary', repo_name=fork_name))
197 response.mustcontain(fork_name)
198 response.mustcontain(fork_name)
198 response.mustcontain(self.REPO_TYPE)
199 response.mustcontain(self.REPO_TYPE)
199 response.mustcontain('Fork of')
200 response.mustcontain('Fork of')
@@ -254,7 +255,7 b' class TestHG(_BaseTest):'
254
255
255 @pytest.mark.usefixtures('app', 'autologin_user')
256 @pytest.mark.usefixtures('app', 'autologin_user')
256 @pytest.mark.skip_backends('git','hg')
257 @pytest.mark.skip_backends('git','hg')
257 class TestSVNFork:
258 class TestSVNFork(object):
258
259
259 def test_fork_redirects(self, backend):
260 def test_fork_redirects(self, backend):
260 denied_actions = ['fork','fork_create']
261 denied_actions = ['fork','fork_create']
@@ -266,7 +267,7 b' class TestSVNFork:'
266
267
267 # Not allowed, redirect to the summary
268 # Not allowed, redirect to the summary
268 redirected = response.follow()
269 redirected = response.follow()
269 summary_url = url('summary_home', repo_name=backend.repo_name)
270 summary_url = h.route_path('repo_summary', repo_name=backend.repo_name)
270
271
271 # URL adds leading slash and path doesn't have it
272 # URL adds leading slash and path doesn't have it
272 assert redirected.req.path == summary_url
273 assert redirected.request.path == summary_url
@@ -29,6 +29,7 b' from rhodecode.tests import ('
29 no_newline_id_generator)
29 no_newline_id_generator)
30 from rhodecode.tests.fixture import Fixture
30 from rhodecode.tests.fixture import Fixture
31 from rhodecode.lib.auth import check_password
31 from rhodecode.lib.auth import check_password
32 from rhodecode.lib import helpers as h
32 from rhodecode.model.auth_token import AuthTokenModel
33 from rhodecode.model.auth_token import AuthTokenModel
33 from rhodecode.model import validators
34 from rhodecode.model import validators
34 from rhodecode.model.db import User, Notification, UserApiKeys
35 from rhodecode.model.db import User, Notification, UserApiKeys
@@ -105,8 +106,9 b' class TestLoginController(object):'
105 with fixture.anon_access(False):
106 with fixture.anon_access(False):
106 kwargs = {'branch': 'stable'}
107 kwargs = {'branch': 'stable'}
107 response = self.app.get(
108 response = self.app.get(
108 url('summary_home', repo_name=HG_REPO, **kwargs))
109 h.route_path('repo_summary', repo_name=HG_REPO, _query=kwargs))
109 assert response.status == '302 Found'
110 assert response.status == '302 Found'
111
110 response_query = urlparse.parse_qsl(response.location)
112 response_query = urlparse.parse_qsl(response.location)
111 assert 'branch=stable' in response_query[0][1]
113 assert 'branch=stable' in response_query[0][1]
112
114
@@ -24,13 +24,13 b' from webob.exc import HTTPNotFound'
24
24
25 import rhodecode
25 import rhodecode
26 from rhodecode.lib.vcs.nodes import FileNode
26 from rhodecode.lib.vcs.nodes import FileNode
27 from rhodecode.lib import helpers as h
27 from rhodecode.model.changeset_status import ChangesetStatusModel
28 from rhodecode.model.changeset_status import ChangesetStatusModel
28 from rhodecode.model.db import (
29 from rhodecode.model.db import (
29 PullRequest, ChangesetStatus, UserLog, Notification)
30 PullRequest, ChangesetStatus, UserLog, Notification)
30 from rhodecode.model.meta import Session
31 from rhodecode.model.meta import Session
31 from rhodecode.model.pull_request import PullRequestModel
32 from rhodecode.model.pull_request import PullRequestModel
32 from rhodecode.model.user import UserModel
33 from rhodecode.model.user import UserModel
33 from rhodecode.model.repo import RepoModel
34 from rhodecode.tests import (
34 from rhodecode.tests import (
35 assert_session_flash, url, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN)
35 assert_session_flash, url, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN)
36 from rhodecode.tests.utils import AssertResponse
36 from rhodecode.tests.utils import AssertResponse
@@ -38,7 +38,7 b' from rhodecode.tests.utils import Assert'
38
38
39 @pytest.mark.usefixtures('app', 'autologin_user')
39 @pytest.mark.usefixtures('app', 'autologin_user')
40 @pytest.mark.backends("git", "hg")
40 @pytest.mark.backends("git", "hg")
41 class TestPullrequestsController:
41 class TestPullrequestsController(object):
42
42
43 def test_index(self, backend):
43 def test_index(self, backend):
44 self.app.get(url(
44 self.app.get(url(
@@ -47,7 +47,7 b' class TestPullrequestsController:'
47
47
48 def test_option_menu_create_pull_request_exists(self, backend):
48 def test_option_menu_create_pull_request_exists(self, backend):
49 repo_name = backend.repo_name
49 repo_name = backend.repo_name
50 response = self.app.get(url('summary_home', repo_name=repo_name))
50 response = self.app.get(h.route_path('repo_summary', repo_name=repo_name))
51
51
52 create_pr_link = '<a href="%s">Create Pull Request</a>' % url(
52 create_pr_link = '<a href="%s">Create Pull Request</a>' % url(
53 'pullrequest', repo_name=repo_name)
53 'pullrequest', repo_name=repo_name)
@@ -1074,10 +1074,10 b' def test_redirects_to_repo_summary_for_s'
1074
1074
1075 # Not allowed, redirect to the summary
1075 # Not allowed, redirect to the summary
1076 redirected = response.follow()
1076 redirected = response.follow()
1077 summary_url = url('summary_home', repo_name=backend_svn.repo_name)
1077 summary_url = h.route_path('repo_summary', repo_name=backend_svn.repo_name)
1078
1078
1079 # URL adds leading slash and path doesn't have it
1079 # URL adds leading slash and path doesn't have it
1080 assert redirected.req.path == summary_url
1080 assert redirected.request.path == summary_url
1081
1081
1082
1082
1083 def test_delete_comment_returns_404_if_comment_does_not_exist(pylonsapp):
1083 def test_delete_comment_returns_404_if_comment_does_not_exist(pylonsapp):
@@ -20,10 +20,10 b''
20
20
21 import pytest
21 import pytest
22 from mock import Mock, patch
22 from mock import Mock, patch
23 from pylons import url
24
23
25 from rhodecode.lib import base
24 from rhodecode.lib import base
26 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
25 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
26 from rhodecode.lib import helpers as h
27 from rhodecode.model import db
27 from rhodecode.model import db
28
28
29
29
@@ -165,7 +165,7 b' class TestBaseRepoControllerHandleMissin'
165 context_mock.repo_name = repo_name
165 context_mock.repo_name = repo_name
166 controller._handle_missing_requirements(error)
166 controller._handle_missing_requirements(error)
167
167
168 expected_url = url('summary_home', repo_name=repo_name)
168 expected_url = h.route_path('repo_summary', repo_name=repo_name)
169 if should_redirect:
169 if should_redirect:
170 redirect_mock.assert_called_once_with(expected_url)
170 redirect_mock.assert_called_once_with(expected_url)
171 else:
171 else:
@@ -1683,6 +1683,15 b' def request_stub():'
1683
1683
1684
1684
1685 @pytest.fixture
1685 @pytest.fixture
1686 def context_stub():
1687 """
1688 Stub context object.
1689 """
1690 context = pyramid.testing.DummyResource()
1691 return context
1692
1693
1694 @pytest.fixture
1686 def config_stub(request, request_stub):
1695 def config_stub(request, request_stub):
1687 """
1696 """
1688 Set up pyramid.testing and return the Configurator.
1697 Set up pyramid.testing and return the Configurator.
@@ -123,6 +123,7 b' def set_anonymous_access(enabled):'
123 user.active = enabled
123 user.active = enabled
124 Session().add(user)
124 Session().add(user)
125 Session().commit()
125 Session().commit()
126 time.sleep(1.5) # must sleep for cache (1s to expire)
126 log.info('anonymous access is now: %s', enabled)
127 log.info('anonymous access is now: %s', enabled)
127 assert enabled == User.get_default_user().active, (
128 assert enabled == User.get_default_user().active, (
128 'Cannot set anonymous access')
129 'Cannot set anonymous access')
@@ -1,315 +0,0 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 """
22 Summary controller for RhodeCode Enterprise
23 """
24
25 import logging
26 from string import lower
27
28 from pylons import tmpl_context as c, request
29 from pylons.i18n.translation import _
30 from beaker.cache import cache_region
31
32 from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP)
33 from rhodecode.controllers import utils
34 from rhodecode.controllers.changelog import _load_changelog_summary
35 from rhodecode.lib import caches, helpers as h
36 from rhodecode.lib.utils import jsonify
37 from rhodecode.lib.utils2 import safe_str
38 from rhodecode.lib.auth import (
39 LoginRequired, HasRepoPermissionAnyDecorator, XHRRequired)
40 from rhodecode.lib.base import BaseRepoController, render
41 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
42 from rhodecode.lib.ext_json import json
43 from rhodecode.lib.vcs.backends.base import EmptyCommit
44 from rhodecode.lib.vcs.exceptions import CommitError, EmptyRepositoryError
45 from rhodecode.model.db import Statistics, CacheKey, User
46 from rhodecode.model.repo import ReadmeFinder
47
48
49 log = logging.getLogger(__name__)
50
51
52 class SummaryController(BaseRepoController):
53
54 def __before__(self):
55 super(SummaryController, self).__before__()
56
57 def __get_readme_data(self, db_repo):
58 repo_name = db_repo.repo_name
59 log.debug('Looking for README file')
60 default_renderer = c.visual.default_renderer
61
62 @cache_region('long_term')
63 def _generate_readme(cache_key):
64 readme_data = None
65 readme_node = None
66 readme_filename = None
67 commit = self._get_landing_commit_or_none(db_repo)
68 if commit:
69 log.debug("Searching for a README file.")
70 readme_node = ReadmeFinder(default_renderer).search(commit)
71 if readme_node:
72 relative_url = h.url('files_raw_home',
73 repo_name=repo_name,
74 revision=commit.raw_id,
75 f_path=readme_node.path)
76 readme_data = self._render_readme_or_none(
77 commit, readme_node, relative_url)
78 readme_filename = readme_node.path
79 return readme_data, readme_filename
80
81 invalidator_context = CacheKey.repo_context_cache(
82 _generate_readme, repo_name, CacheKey.CACHE_TYPE_README)
83
84 with invalidator_context as context:
85 context.invalidate()
86 computed = context.compute()
87
88 return computed
89
90 def _get_landing_commit_or_none(self, db_repo):
91 log.debug("Getting the landing commit.")
92 try:
93 commit = db_repo.get_landing_commit()
94 if not isinstance(commit, EmptyCommit):
95 return commit
96 else:
97 log.debug("Repository is empty, no README to render.")
98 except CommitError:
99 log.exception(
100 "Problem getting commit when trying to render the README.")
101
102 def _render_readme_or_none(self, commit, readme_node, relative_url):
103 log.debug(
104 'Found README file `%s` rendering...', readme_node.path)
105 renderer = MarkupRenderer()
106 try:
107 html_source = renderer.render(
108 readme_node.content, filename=readme_node.path)
109 if relative_url:
110 return relative_links(html_source, relative_url)
111 return html_source
112 except Exception:
113 log.exception(
114 "Exception while trying to render the README")
115
116 @LoginRequired()
117 @HasRepoPermissionAnyDecorator(
118 'repository.read', 'repository.write', 'repository.admin')
119 def index(self, repo_name):
120
121 # Prepare the clone URL
122
123 username = ''
124 if c.rhodecode_user.username != User.DEFAULT_USER:
125 username = safe_str(c.rhodecode_user.username)
126
127 _def_clone_uri = _def_clone_uri_by_id = c.clone_uri_tmpl
128 if '{repo}' in _def_clone_uri:
129 _def_clone_uri_by_id = _def_clone_uri.replace(
130 '{repo}', '_{repoid}')
131 elif '{repoid}' in _def_clone_uri:
132 _def_clone_uri_by_id = _def_clone_uri.replace(
133 '_{repoid}', '{repo}')
134
135 c.clone_repo_url = c.rhodecode_db_repo.clone_url(
136 user=username, uri_tmpl=_def_clone_uri)
137 c.clone_repo_url_id = c.rhodecode_db_repo.clone_url(
138 user=username, uri_tmpl=_def_clone_uri_by_id)
139
140 # If enabled, get statistics data
141
142 c.show_stats = bool(c.rhodecode_db_repo.enable_statistics)
143
144 stats = self.sa.query(Statistics)\
145 .filter(Statistics.repository == c.rhodecode_db_repo)\
146 .scalar()
147
148 c.stats_percentage = 0
149
150 if stats and stats.languages:
151 c.no_data = False is c.rhodecode_db_repo.enable_statistics
152 lang_stats_d = json.loads(stats.languages)
153
154 # Sort first by decreasing count and second by the file extension,
155 # so we have a consistent output.
156 lang_stats_items = sorted(lang_stats_d.iteritems(),
157 key=lambda k: (-k[1], k[0]))[:10]
158 lang_stats = [(x, {"count": y,
159 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
160 for x, y in lang_stats_items]
161
162 c.trending_languages = json.dumps(lang_stats)
163 else:
164 c.no_data = True
165 c.trending_languages = json.dumps({})
166
167 c.enable_downloads = c.rhodecode_db_repo.enable_downloads
168 c.repository_followers = self.scm_model.get_followers(
169 c.rhodecode_db_repo)
170 c.repository_forks = self.scm_model.get_forks(c.rhodecode_db_repo)
171 c.repository_is_user_following = self.scm_model.is_following_repo(
172 c.repo_name, c.rhodecode_user.user_id)
173
174 if c.repository_requirements_missing:
175 return render('summary/missing_requirements.mako')
176
177 c.readme_data, c.readme_file = \
178 self.__get_readme_data(c.rhodecode_db_repo)
179
180 _load_changelog_summary()
181
182 if request.is_xhr:
183 return render('changelog/changelog_summary_data.mako')
184
185 return render('summary/summary.mako')
186
187 @LoginRequired()
188 @XHRRequired()
189 @HasRepoPermissionAnyDecorator(
190 'repository.read', 'repository.write', 'repository.admin')
191 @jsonify
192 def repo_stats(self, repo_name, commit_id):
193 _namespace = caches.get_repo_namespace_key(
194 caches.SUMMARY_STATS, repo_name)
195 show_stats = bool(c.rhodecode_db_repo.enable_statistics)
196 cache_manager = caches.get_cache_manager('repo_cache_long', _namespace)
197 _cache_key = caches.compute_key_from_params(
198 repo_name, commit_id, show_stats)
199
200 def compute_stats():
201 code_stats = {}
202 size = 0
203 try:
204 scm_instance = c.rhodecode_db_repo.scm_instance()
205 commit = scm_instance.get_commit(commit_id)
206
207 for node in commit.get_filenodes_generator():
208 size += node.size
209 if not show_stats:
210 continue
211 ext = lower(node.extension)
212 ext_info = LANGUAGES_EXTENSIONS_MAP.get(ext)
213 if ext_info:
214 if ext in code_stats:
215 code_stats[ext]['count'] += 1
216 else:
217 code_stats[ext] = {"count": 1, "desc": ext_info}
218 except EmptyRepositoryError:
219 pass
220 return {'size': h.format_byte_size_binary(size),
221 'code_stats': code_stats}
222
223 stats = cache_manager.get(_cache_key, createfunc=compute_stats)
224 return stats
225
226 def _switcher_reference_data(self, repo_name, references, is_svn):
227 """Prepare reference data for given `references`"""
228 items = []
229 for name, commit_id in references.items():
230 use_commit_id = '/' in name or is_svn
231 items.append({
232 'name': name,
233 'commit_id': commit_id,
234 'files_url': h.url(
235 'files_home',
236 repo_name=repo_name,
237 f_path=name if is_svn else '',
238 revision=commit_id if use_commit_id else name,
239 at=name)
240 })
241 return items
242
243 @LoginRequired()
244 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
245 'repository.admin')
246 @jsonify
247 def repo_refs_data(self, repo_name):
248 repo = c.rhodecode_repo
249 refs_to_create = [
250 (_("Branch"), repo.branches, 'branch'),
251 (_("Tag"), repo.tags, 'tag'),
252 (_("Bookmark"), repo.bookmarks, 'book'),
253 ]
254 res = self._create_reference_data(repo, repo_name, refs_to_create)
255 data = {
256 'more': False,
257 'results': res
258 }
259 return data
260
261 @jsonify
262 def repo_refs_changelog_data(self, repo_name):
263 repo = c.rhodecode_repo
264
265 refs_to_create = [
266 (_("Branches"), repo.branches, 'branch'),
267 (_("Closed branches"), repo.branches_closed, 'branch_closed'),
268 # TODO: enable when vcs can handle bookmarks filters
269 # (_("Bookmarks"), repo.bookmarks, "book"),
270 ]
271 res = self._create_reference_data(repo, repo_name, refs_to_create)
272 data = {
273 'more': False,
274 'results': res
275 }
276 return data
277
278 def _create_reference_data(self, repo, full_repo_name, refs_to_create):
279 format_ref_id = utils.get_format_ref_id(repo)
280
281 result = []
282 for title, refs, ref_type in refs_to_create:
283 if refs:
284 result.append({
285 'text': title,
286 'children': self._create_reference_items(
287 repo, full_repo_name, refs, ref_type, format_ref_id),
288 })
289 return result
290
291 def _create_reference_items(self, repo, full_repo_name, refs, ref_type,
292 format_ref_id):
293 result = []
294 is_svn = h.is_svn(repo)
295 for ref_name, raw_id in refs.iteritems():
296 files_url = self._create_files_url(
297 repo, full_repo_name, ref_name, raw_id, is_svn)
298 result.append({
299 'text': ref_name,
300 'id': format_ref_id(ref_name, raw_id),
301 'raw_id': raw_id,
302 'type': ref_type,
303 'files_url': files_url,
304 })
305 return result
306
307 def _create_files_url(self, repo, full_repo_name, ref_name, raw_id,
308 is_svn):
309 use_commit_id = '/' in ref_name or is_svn
310 return h.url(
311 'files_home',
312 repo_name=full_repo_name,
313 f_path=ref_name if is_svn else '',
314 revision=raw_id if use_commit_id else ref_name,
315 at=ref_name)
@@ -1,136 +0,0 b''
1 ## -*- coding: utf-8 -*-
2 <%namespace name="base" file="/base/base.mako"/>
3 %if c.repo_commits:
4 <table class="rctable repo_summary table_disp">
5 <tr>
6
7 <th class="status" colspan="2"></th>
8 <th>${_('Commit')}</th>
9 <th>${_('Commit message')}</th>
10 <th>${_('Age')}</th>
11 <th>${_('Author')}</th>
12 <th>${_('Refs')}</th>
13 </tr>
14 %for cnt,cs in enumerate(c.repo_commits):
15 <tr class="parity${cnt%2}">
16
17 <td class="td-status">
18 %if c.statuses.get(cs.raw_id):
19 <div class="changeset-status-ico shortlog">
20 %if c.statuses.get(cs.raw_id)[2]:
21 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (c.statuses.get(cs.raw_id)[0], c.statuses.get(cs.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}">
22 <div class="${'flag_status %s' % c.statuses.get(cs.raw_id)[0]}"></div>
23 </a>
24 %else:
25 <a class="tooltip" title="${_('Commit status: %s') % h.commit_status_lbl(c.statuses.get(cs.raw_id)[0])}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
26 <div class="${'flag_status %s' % c.statuses.get(cs.raw_id)[0]}"></div>
27 </a>
28 %endif
29 </div>
30 %else:
31 <div class="tooltip flag_status not_reviewed" title="${_('Commit status: Not Reviewed')}"></div>
32 %endif
33 </td>
34 <td class="td-comments">
35 %if c.comments.get(cs.raw_id,[]):
36 <a title="${_('Commit has comments')}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
37 <i class="icon-comment"></i> ${len(c.comments[cs.raw_id])}
38 </a>
39 %endif
40 </td>
41 <td class="td-commit">
42 <pre><a href="${h.url('changeset_home', repo_name=c.repo_name, revision=cs.raw_id)}">${h.show_id(cs)}</a></pre>
43 </td>
44
45 <td class="td-description mid">
46 <div class="log-container truncate-wrap">
47 <div class="message truncate" id="c-${cs.raw_id}">${h.urlify_commit_message(cs.message, c.repo_name)}</div>
48 </div>
49 </td>
50
51 <td class="td-time">
52 ${h.age_component(cs.date)}
53 </td>
54 <td class="td-user author">
55 ${base.gravatar_with_user(cs.author)}
56 </td>
57
58 <td class="td-tags">
59 <div class="autoexpand">
60 %if h.is_hg(c.rhodecode_repo):
61 %for book in cs.bookmarks:
62 <span class="booktag tag" title="${_('Bookmark %s') % book}">
63 <a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
64 </span>
65 %endfor
66 %endif
67 ## tags
68 %for tag in cs.tags:
69 <span class="tagtag tag" title="${_('Tag %s') % tag}">
70 <a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
71 </span>
72 %endfor
73
74 ## branch
75 %if cs.branch:
76 <span class="branchtag tag" title="${_('Branch %s') % cs.branch}">
77 <a href="${h.url('changelog_home',repo_name=c.repo_name,branch=cs.branch)}"><i class="icon-code-fork"></i>${h.shorter(cs.branch)}</a>
78 </span>
79 %endif
80 </div>
81 </td>
82 </tr>
83 %endfor
84
85 </table>
86
87 <script type="text/javascript">
88 $(document).pjax('#shortlog_data .pager_link','#shortlog_data', {timeout: 2000, scrollTo: false });
89 $(document).on('pjax:success', function(){ timeagoActivate(); });
90 </script>
91
92 <div class="pagination-wh pagination-left">
93 ${c.repo_commits.pager('$link_previous ~2~ $link_next')}
94 </div>
95 %else:
96
97 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
98 <div class="quick_start">
99 <div class="fieldset">
100 <div class="left-label">${_('Add or upload files directly via RhodeCode:')}</div>
101 <div class="right-content">
102 <div id="add_node_id" class="add_node">
103 <a href="${h.url('files_add_home',repo_name=c.repo_name,revision=0,f_path='', anchor='edit')}" class="btn btn-default">${_('Add New File')}</a>
104 </div>
105 </div>
106 %endif
107 </div>
108
109 %if not h.is_svn(c.rhodecode_repo):
110 <div class="fieldset">
111 <div class="left-label">${_('Push new repo:')}</div>
112 <div class="right-content">
113 <pre>
114 ${c.rhodecode_repo.alias} clone ${c.clone_repo_url}
115 ${c.rhodecode_repo.alias} add README # add first file
116 ${c.rhodecode_repo.alias} commit -m "Initial" # commit with message
117 ${c.rhodecode_repo.alias} push ${'origin master' if h.is_git(c.rhodecode_repo) else ''} # push changes back
118 </pre>
119 </div>
120 </div>
121 <div class="fieldset">
122 <div class="left-label">${_('Existing repository?')}</div>
123 <div class="right-content">
124 <pre>
125 %if h.is_git(c.rhodecode_repo):
126 git remote add origin ${c.clone_repo_url}
127 git push -u origin master
128 %else:
129 hg push ${c.clone_repo_url}
130 %endif
131 </pre>
132 </div>
133 </div>
134 %endif
135 </div>
136 %endif
General Comments 0
You need to be logged in to leave comments. Login now