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 |
# |
|
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. |
|
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. |
|
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( |
|
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( |
|
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( |
|
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( |
|
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 |
|
|
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( |
|
120 | response = self.app.get( | |
104 |
'summary |
|
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( |
|
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,11 +139,11 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( |
|
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 |
|
|
146 | assert 'login' in response.location | |
131 |
|
147 | |||
132 | def _enable_stats(self, repo): |
|
148 | def _enable_stats(self, repo): | |
133 | r = Repository.get_by_repo_name(repo) |
|
149 | r = Repository.get_by_repo_name(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 |
|
|
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 |
|
|
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 |
|
|
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( |
|
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 |
' |
|
244 | 'Commits cannot be displayed, because this repository ' | |
231 |
' |
|
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( |
|
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(' |
|
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. |
|
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 = |
|
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 = |
|
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 |
|
395 | with mock.patch('rhodecode.lib.helpers.url') as url_mock: | |
413 |
result = |
|
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 |
|
411 | with mock.patch('rhodecode.lib.helpers.url') as url_mock: | |
430 |
result = |
|
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 |
|
427 | with mock.patch('rhodecode.lib.helpers.url') as url_mock: | |
447 |
result = |
|
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 = |
|
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 |
|
|
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 = |
|
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,8 +117,9 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 | always_scan=config['debug']) |
|
121 | directory=config['pylons.paths']['controllers'], | |
|
122 | always_scan=config['debug']) | |||
122 | rmap.minimization = False |
|
123 | rmap.minimization = False | |
123 | rmap.explicit = False |
|
124 | rmap.explicit = False | |
124 |
|
125 | |||
@@ -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( |
|
277 | repo_url = h.link_to( | |
278 | h.url('summary_home', |
|
278 | repo.repo_name, | |
279 |
|
|
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( |
|
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( |
|
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( |
|
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. |
|
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 |
|
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 |
|
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. |
|
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 = |
|
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( |
|
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( |
|
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 = |
|
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 |
|
|
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 = |
|
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, |
|
1541 | link_to(repo.just_name, route_path('repo_summary', repo_name=repo.repo_name)) | |
1542 | ] |
|
1542 | ] | |
1543 |
|
1543 | |||
1544 | return literal(' » '.join(path)) |
|
1544 | return literal(' » '.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 |
|
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 |
|
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 |
|
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': |
|
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 |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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' |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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 |
|
|
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 |
|
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=' |
|
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 |
|
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( |
|
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 = |
|
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( |
|
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( |
|
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 = |
|
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 |
|
|
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( |
|
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 = |
|
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 = |
|
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