##// END OF EJS Templates
ssh: don't show the SSH clone url if ssh is disabled
marcink -
r2498:5fe36cd7 default
parent child Browse files
Show More
@@ -1,375 +1,372 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2018 RhodeCode GmbH
3 # Copyright (C) 2011-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import string
22 import string
23
23
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26
26
27 from rhodecode.controllers import utils
27 from rhodecode.controllers import utils
28 from rhodecode.apps._base import RepoAppView
28 from rhodecode.apps._base import RepoAppView
29 from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP)
29 from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP)
30 from rhodecode.lib import caches, helpers as h
30 from rhodecode.lib import caches, helpers as h
31 from rhodecode.lib.helpers import RepoPage
31 from rhodecode.lib.helpers import RepoPage
32 from rhodecode.lib.utils2 import safe_str, safe_int
32 from rhodecode.lib.utils2 import safe_str, safe_int
33 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
33 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
34 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
35 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.vcs.backends.base import EmptyCommit
36 from rhodecode.lib.vcs.backends.base import EmptyCommit
37 from rhodecode.lib.vcs.exceptions import CommitError, EmptyRepositoryError, \
37 from rhodecode.lib.vcs.exceptions import CommitError, EmptyRepositoryError, \
38 CommitDoesNotExistError
38 CommitDoesNotExistError
39 from rhodecode.model.db import Statistics, CacheKey, User
39 from rhodecode.model.db import Statistics, CacheKey, User
40 from rhodecode.model.meta import Session
40 from rhodecode.model.meta import Session
41 from rhodecode.model.repo import ReadmeFinder
41 from rhodecode.model.repo import ReadmeFinder
42 from rhodecode.model.scm import ScmModel
42 from rhodecode.model.scm import ScmModel
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class RepoSummaryView(RepoAppView):
47 class RepoSummaryView(RepoAppView):
48
48
49 def load_default_context(self):
49 def load_default_context(self):
50 c = self._get_local_tmpl_context(include_app_defaults=True)
50 c = self._get_local_tmpl_context(include_app_defaults=True)
51
52 c.rhodecode_repo = None
51 c.rhodecode_repo = None
53 if not c.repository_requirements_missing:
52 if not c.repository_requirements_missing:
54 c.rhodecode_repo = self.rhodecode_vcs_repo
53 c.rhodecode_repo = self.rhodecode_vcs_repo
55
56
57 return c
54 return c
58
55
59 def _get_readme_data(self, db_repo, default_renderer):
56 def _get_readme_data(self, db_repo, default_renderer):
60 repo_name = db_repo.repo_name
57 repo_name = db_repo.repo_name
61 log.debug('Looking for README file')
58 log.debug('Looking for README file')
62
59
63 @cache_region('long_term')
60 @cache_region('long_term')
64 def _generate_readme(cache_key):
61 def _generate_readme(cache_key):
65 readme_data = None
62 readme_data = None
66 readme_node = None
63 readme_node = None
67 readme_filename = None
64 readme_filename = None
68 commit = self._get_landing_commit_or_none(db_repo)
65 commit = self._get_landing_commit_or_none(db_repo)
69 if commit:
66 if commit:
70 log.debug("Searching for a README file.")
67 log.debug("Searching for a README file.")
71 readme_node = ReadmeFinder(default_renderer).search(commit)
68 readme_node = ReadmeFinder(default_renderer).search(commit)
72 if readme_node:
69 if readme_node:
73 relative_urls = {
70 relative_urls = {
74 'raw': h.route_path(
71 'raw': h.route_path(
75 'repo_file_raw', repo_name=repo_name,
72 'repo_file_raw', repo_name=repo_name,
76 commit_id=commit.raw_id, f_path=readme_node.path),
73 commit_id=commit.raw_id, f_path=readme_node.path),
77 'standard': h.route_path(
74 'standard': h.route_path(
78 'repo_files', repo_name=repo_name,
75 'repo_files', repo_name=repo_name,
79 commit_id=commit.raw_id, f_path=readme_node.path),
76 commit_id=commit.raw_id, f_path=readme_node.path),
80 }
77 }
81 readme_data = self._render_readme_or_none(
78 readme_data = self._render_readme_or_none(
82 commit, readme_node, relative_urls)
79 commit, readme_node, relative_urls)
83 readme_filename = readme_node.path
80 readme_filename = readme_node.path
84 return readme_data, readme_filename
81 return readme_data, readme_filename
85
82
86 invalidator_context = CacheKey.repo_context_cache(
83 invalidator_context = CacheKey.repo_context_cache(
87 _generate_readme, repo_name, CacheKey.CACHE_TYPE_README)
84 _generate_readme, repo_name, CacheKey.CACHE_TYPE_README)
88
85
89 with invalidator_context as context:
86 with invalidator_context as context:
90 context.invalidate()
87 context.invalidate()
91 computed = context.compute()
88 computed = context.compute()
92
89
93 return computed
90 return computed
94
91
95 def _get_landing_commit_or_none(self, db_repo):
92 def _get_landing_commit_or_none(self, db_repo):
96 log.debug("Getting the landing commit.")
93 log.debug("Getting the landing commit.")
97 try:
94 try:
98 commit = db_repo.get_landing_commit()
95 commit = db_repo.get_landing_commit()
99 if not isinstance(commit, EmptyCommit):
96 if not isinstance(commit, EmptyCommit):
100 return commit
97 return commit
101 else:
98 else:
102 log.debug("Repository is empty, no README to render.")
99 log.debug("Repository is empty, no README to render.")
103 except CommitError:
100 except CommitError:
104 log.exception(
101 log.exception(
105 "Problem getting commit when trying to render the README.")
102 "Problem getting commit when trying to render the README.")
106
103
107 def _render_readme_or_none(self, commit, readme_node, relative_urls):
104 def _render_readme_or_none(self, commit, readme_node, relative_urls):
108 log.debug(
105 log.debug(
109 'Found README file `%s` rendering...', readme_node.path)
106 'Found README file `%s` rendering...', readme_node.path)
110 renderer = MarkupRenderer()
107 renderer = MarkupRenderer()
111 try:
108 try:
112 html_source = renderer.render(
109 html_source = renderer.render(
113 readme_node.content, filename=readme_node.path)
110 readme_node.content, filename=readme_node.path)
114 if relative_urls:
111 if relative_urls:
115 return relative_links(html_source, relative_urls)
112 return relative_links(html_source, relative_urls)
116 return html_source
113 return html_source
117 except Exception:
114 except Exception:
118 log.exception(
115 log.exception(
119 "Exception while trying to render the README")
116 "Exception while trying to render the README")
120
117
121 def _load_commits_context(self, c):
118 def _load_commits_context(self, c):
122 p = safe_int(self.request.GET.get('page'), 1)
119 p = safe_int(self.request.GET.get('page'), 1)
123 size = safe_int(self.request.GET.get('size'), 10)
120 size = safe_int(self.request.GET.get('size'), 10)
124
121
125 def url_generator(**kw):
122 def url_generator(**kw):
126 query_params = {
123 query_params = {
127 'size': size
124 'size': size
128 }
125 }
129 query_params.update(kw)
126 query_params.update(kw)
130 return h.route_path(
127 return h.route_path(
131 'repo_summary_commits',
128 'repo_summary_commits',
132 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
129 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
133
130
134 pre_load = ['author', 'branch', 'date', 'message']
131 pre_load = ['author', 'branch', 'date', 'message']
135 try:
132 try:
136 collection = self.rhodecode_vcs_repo.get_commits(pre_load=pre_load)
133 collection = self.rhodecode_vcs_repo.get_commits(pre_load=pre_load)
137 except EmptyRepositoryError:
134 except EmptyRepositoryError:
138 collection = self.rhodecode_vcs_repo
135 collection = self.rhodecode_vcs_repo
139
136
140 c.repo_commits = RepoPage(
137 c.repo_commits = RepoPage(
141 collection, page=p, items_per_page=size, url=url_generator)
138 collection, page=p, items_per_page=size, url=url_generator)
142 page_ids = [x.raw_id for x in c.repo_commits]
139 page_ids = [x.raw_id for x in c.repo_commits]
143 c.comments = self.db_repo.get_comments(page_ids)
140 c.comments = self.db_repo.get_comments(page_ids)
144 c.statuses = self.db_repo.statuses(page_ids)
141 c.statuses = self.db_repo.statuses(page_ids)
145
142
146 @LoginRequired()
143 @LoginRequired()
147 @HasRepoPermissionAnyDecorator(
144 @HasRepoPermissionAnyDecorator(
148 'repository.read', 'repository.write', 'repository.admin')
145 'repository.read', 'repository.write', 'repository.admin')
149 @view_config(
146 @view_config(
150 route_name='repo_summary_commits', request_method='GET',
147 route_name='repo_summary_commits', request_method='GET',
151 renderer='rhodecode:templates/summary/summary_commits.mako')
148 renderer='rhodecode:templates/summary/summary_commits.mako')
152 def summary_commits(self):
149 def summary_commits(self):
153 c = self.load_default_context()
150 c = self.load_default_context()
154 self._load_commits_context(c)
151 self._load_commits_context(c)
155 return self._get_template_context(c)
152 return self._get_template_context(c)
156
153
157 @LoginRequired()
154 @LoginRequired()
158 @HasRepoPermissionAnyDecorator(
155 @HasRepoPermissionAnyDecorator(
159 'repository.read', 'repository.write', 'repository.admin')
156 'repository.read', 'repository.write', 'repository.admin')
160 @view_config(
157 @view_config(
161 route_name='repo_summary', request_method='GET',
158 route_name='repo_summary', request_method='GET',
162 renderer='rhodecode:templates/summary/summary.mako')
159 renderer='rhodecode:templates/summary/summary.mako')
163 @view_config(
160 @view_config(
164 route_name='repo_summary_slash', request_method='GET',
161 route_name='repo_summary_slash', request_method='GET',
165 renderer='rhodecode:templates/summary/summary.mako')
162 renderer='rhodecode:templates/summary/summary.mako')
166 @view_config(
163 @view_config(
167 route_name='repo_summary_explicit', request_method='GET',
164 route_name='repo_summary_explicit', request_method='GET',
168 renderer='rhodecode:templates/summary/summary.mako')
165 renderer='rhodecode:templates/summary/summary.mako')
169 def summary(self):
166 def summary(self):
170 c = self.load_default_context()
167 c = self.load_default_context()
171
168
172 # Prepare the clone URL
169 # Prepare the clone URL
173 username = ''
170 username = ''
174 if self._rhodecode_user.username != User.DEFAULT_USER:
171 if self._rhodecode_user.username != User.DEFAULT_USER:
175 username = safe_str(self._rhodecode_user.username)
172 username = safe_str(self._rhodecode_user.username)
176
173
177 _def_clone_uri = _def_clone_uri_id = c.clone_uri_tmpl
174 _def_clone_uri = _def_clone_uri_id = c.clone_uri_tmpl
178 _def_clone_uri_ssh = c.clone_uri_ssh_tmpl
175 _def_clone_uri_ssh = c.clone_uri_ssh_tmpl
179
176
180 if '{repo}' in _def_clone_uri:
177 if '{repo}' in _def_clone_uri:
181 _def_clone_uri_id = _def_clone_uri.replace(
178 _def_clone_uri_id = _def_clone_uri.replace(
182 '{repo}', '_{repoid}')
179 '{repo}', '_{repoid}')
183 elif '{repoid}' in _def_clone_uri:
180 elif '{repoid}' in _def_clone_uri:
184 _def_clone_uri_id = _def_clone_uri.replace(
181 _def_clone_uri_id = _def_clone_uri.replace(
185 '_{repoid}', '{repo}')
182 '_{repoid}', '{repo}')
186
183
187 c.clone_repo_url = self.db_repo.clone_url(
184 c.clone_repo_url = self.db_repo.clone_url(
188 user=username, uri_tmpl=_def_clone_uri)
185 user=username, uri_tmpl=_def_clone_uri)
189 c.clone_repo_url_id = self.db_repo.clone_url(
186 c.clone_repo_url_id = self.db_repo.clone_url(
190 user=username, uri_tmpl=_def_clone_uri_id)
187 user=username, uri_tmpl=_def_clone_uri_id)
191 c.clone_repo_url_ssh = self.db_repo.clone_url(
188 c.clone_repo_url_ssh = self.db_repo.clone_url(
192 uri_tmpl=_def_clone_uri_ssh, ssh=True)
189 uri_tmpl=_def_clone_uri_ssh, ssh=True)
193
190
194 # If enabled, get statistics data
191 # If enabled, get statistics data
195
192
196 c.show_stats = bool(self.db_repo.enable_statistics)
193 c.show_stats = bool(self.db_repo.enable_statistics)
197
194
198 stats = Session().query(Statistics) \
195 stats = Session().query(Statistics) \
199 .filter(Statistics.repository == self.db_repo) \
196 .filter(Statistics.repository == self.db_repo) \
200 .scalar()
197 .scalar()
201
198
202 c.stats_percentage = 0
199 c.stats_percentage = 0
203
200
204 if stats and stats.languages:
201 if stats and stats.languages:
205 c.no_data = False is self.db_repo.enable_statistics
202 c.no_data = False is self.db_repo.enable_statistics
206 lang_stats_d = json.loads(stats.languages)
203 lang_stats_d = json.loads(stats.languages)
207
204
208 # Sort first by decreasing count and second by the file extension,
205 # Sort first by decreasing count and second by the file extension,
209 # so we have a consistent output.
206 # so we have a consistent output.
210 lang_stats_items = sorted(lang_stats_d.iteritems(),
207 lang_stats_items = sorted(lang_stats_d.iteritems(),
211 key=lambda k: (-k[1], k[0]))[:10]
208 key=lambda k: (-k[1], k[0]))[:10]
212 lang_stats = [(x, {"count": y,
209 lang_stats = [(x, {"count": y,
213 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
210 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
214 for x, y in lang_stats_items]
211 for x, y in lang_stats_items]
215
212
216 c.trending_languages = json.dumps(lang_stats)
213 c.trending_languages = json.dumps(lang_stats)
217 else:
214 else:
218 c.no_data = True
215 c.no_data = True
219 c.trending_languages = json.dumps({})
216 c.trending_languages = json.dumps({})
220
217
221 scm_model = ScmModel()
218 scm_model = ScmModel()
222 c.enable_downloads = self.db_repo.enable_downloads
219 c.enable_downloads = self.db_repo.enable_downloads
223 c.repository_followers = scm_model.get_followers(self.db_repo)
220 c.repository_followers = scm_model.get_followers(self.db_repo)
224 c.repository_forks = scm_model.get_forks(self.db_repo)
221 c.repository_forks = scm_model.get_forks(self.db_repo)
225 c.repository_is_user_following = scm_model.is_following_repo(
222 c.repository_is_user_following = scm_model.is_following_repo(
226 self.db_repo_name, self._rhodecode_user.user_id)
223 self.db_repo_name, self._rhodecode_user.user_id)
227
224
228 # first interaction with the VCS instance after here...
225 # first interaction with the VCS instance after here...
229 if c.repository_requirements_missing:
226 if c.repository_requirements_missing:
230 self.request.override_renderer = \
227 self.request.override_renderer = \
231 'rhodecode:templates/summary/missing_requirements.mako'
228 'rhodecode:templates/summary/missing_requirements.mako'
232 return self._get_template_context(c)
229 return self._get_template_context(c)
233
230
234 c.readme_data, c.readme_file = \
231 c.readme_data, c.readme_file = \
235 self._get_readme_data(self.db_repo, c.visual.default_renderer)
232 self._get_readme_data(self.db_repo, c.visual.default_renderer)
236
233
237 # loads the summary commits template context
234 # loads the summary commits template context
238 self._load_commits_context(c)
235 self._load_commits_context(c)
239
236
240 return self._get_template_context(c)
237 return self._get_template_context(c)
241
238
242 def get_request_commit_id(self):
239 def get_request_commit_id(self):
243 return self.request.matchdict['commit_id']
240 return self.request.matchdict['commit_id']
244
241
245 @LoginRequired()
242 @LoginRequired()
246 @HasRepoPermissionAnyDecorator(
243 @HasRepoPermissionAnyDecorator(
247 'repository.read', 'repository.write', 'repository.admin')
244 'repository.read', 'repository.write', 'repository.admin')
248 @view_config(
245 @view_config(
249 route_name='repo_stats', request_method='GET',
246 route_name='repo_stats', request_method='GET',
250 renderer='json_ext')
247 renderer='json_ext')
251 def repo_stats(self):
248 def repo_stats(self):
252 commit_id = self.get_request_commit_id()
249 commit_id = self.get_request_commit_id()
253
250
254 _namespace = caches.get_repo_namespace_key(
251 _namespace = caches.get_repo_namespace_key(
255 caches.SUMMARY_STATS, self.db_repo_name)
252 caches.SUMMARY_STATS, self.db_repo_name)
256 show_stats = bool(self.db_repo.enable_statistics)
253 show_stats = bool(self.db_repo.enable_statistics)
257 cache_manager = caches.get_cache_manager(
254 cache_manager = caches.get_cache_manager(
258 'repo_cache_long', _namespace)
255 'repo_cache_long', _namespace)
259 _cache_key = caches.compute_key_from_params(
256 _cache_key = caches.compute_key_from_params(
260 self.db_repo_name, commit_id, show_stats)
257 self.db_repo_name, commit_id, show_stats)
261
258
262 def compute_stats():
259 def compute_stats():
263 code_stats = {}
260 code_stats = {}
264 size = 0
261 size = 0
265 try:
262 try:
266 scm_instance = self.db_repo.scm_instance()
263 scm_instance = self.db_repo.scm_instance()
267 commit = scm_instance.get_commit(commit_id)
264 commit = scm_instance.get_commit(commit_id)
268
265
269 for node in commit.get_filenodes_generator():
266 for node in commit.get_filenodes_generator():
270 size += node.size
267 size += node.size
271 if not show_stats:
268 if not show_stats:
272 continue
269 continue
273 ext = string.lower(node.extension)
270 ext = string.lower(node.extension)
274 ext_info = LANGUAGES_EXTENSIONS_MAP.get(ext)
271 ext_info = LANGUAGES_EXTENSIONS_MAP.get(ext)
275 if ext_info:
272 if ext_info:
276 if ext in code_stats:
273 if ext in code_stats:
277 code_stats[ext]['count'] += 1
274 code_stats[ext]['count'] += 1
278 else:
275 else:
279 code_stats[ext] = {"count": 1, "desc": ext_info}
276 code_stats[ext] = {"count": 1, "desc": ext_info}
280 except (EmptyRepositoryError, CommitDoesNotExistError):
277 except (EmptyRepositoryError, CommitDoesNotExistError):
281 pass
278 pass
282 return {'size': h.format_byte_size_binary(size),
279 return {'size': h.format_byte_size_binary(size),
283 'code_stats': code_stats}
280 'code_stats': code_stats}
284
281
285 stats = cache_manager.get(_cache_key, createfunc=compute_stats)
282 stats = cache_manager.get(_cache_key, createfunc=compute_stats)
286 return stats
283 return stats
287
284
288 @LoginRequired()
285 @LoginRequired()
289 @HasRepoPermissionAnyDecorator(
286 @HasRepoPermissionAnyDecorator(
290 'repository.read', 'repository.write', 'repository.admin')
287 'repository.read', 'repository.write', 'repository.admin')
291 @view_config(
288 @view_config(
292 route_name='repo_refs_data', request_method='GET',
289 route_name='repo_refs_data', request_method='GET',
293 renderer='json_ext')
290 renderer='json_ext')
294 def repo_refs_data(self):
291 def repo_refs_data(self):
295 _ = self.request.translate
292 _ = self.request.translate
296 self.load_default_context()
293 self.load_default_context()
297
294
298 repo = self.rhodecode_vcs_repo
295 repo = self.rhodecode_vcs_repo
299 refs_to_create = [
296 refs_to_create = [
300 (_("Branch"), repo.branches, 'branch'),
297 (_("Branch"), repo.branches, 'branch'),
301 (_("Tag"), repo.tags, 'tag'),
298 (_("Tag"), repo.tags, 'tag'),
302 (_("Bookmark"), repo.bookmarks, 'book'),
299 (_("Bookmark"), repo.bookmarks, 'book'),
303 ]
300 ]
304 res = self._create_reference_data(
301 res = self._create_reference_data(
305 repo, self.db_repo_name, refs_to_create)
302 repo, self.db_repo_name, refs_to_create)
306 data = {
303 data = {
307 'more': False,
304 'more': False,
308 'results': res
305 'results': res
309 }
306 }
310 return data
307 return data
311
308
312 @LoginRequired()
309 @LoginRequired()
313 @HasRepoPermissionAnyDecorator(
310 @HasRepoPermissionAnyDecorator(
314 'repository.read', 'repository.write', 'repository.admin')
311 'repository.read', 'repository.write', 'repository.admin')
315 @view_config(
312 @view_config(
316 route_name='repo_refs_changelog_data', request_method='GET',
313 route_name='repo_refs_changelog_data', request_method='GET',
317 renderer='json_ext')
314 renderer='json_ext')
318 def repo_refs_changelog_data(self):
315 def repo_refs_changelog_data(self):
319 _ = self.request.translate
316 _ = self.request.translate
320 self.load_default_context()
317 self.load_default_context()
321
318
322 repo = self.rhodecode_vcs_repo
319 repo = self.rhodecode_vcs_repo
323
320
324 refs_to_create = [
321 refs_to_create = [
325 (_("Branches"), repo.branches, 'branch'),
322 (_("Branches"), repo.branches, 'branch'),
326 (_("Closed branches"), repo.branches_closed, 'branch_closed'),
323 (_("Closed branches"), repo.branches_closed, 'branch_closed'),
327 # TODO: enable when vcs can handle bookmarks filters
324 # TODO: enable when vcs can handle bookmarks filters
328 # (_("Bookmarks"), repo.bookmarks, "book"),
325 # (_("Bookmarks"), repo.bookmarks, "book"),
329 ]
326 ]
330 res = self._create_reference_data(
327 res = self._create_reference_data(
331 repo, self.db_repo_name, refs_to_create)
328 repo, self.db_repo_name, refs_to_create)
332 data = {
329 data = {
333 'more': False,
330 'more': False,
334 'results': res
331 'results': res
335 }
332 }
336 return data
333 return data
337
334
338 def _create_reference_data(self, repo, full_repo_name, refs_to_create):
335 def _create_reference_data(self, repo, full_repo_name, refs_to_create):
339 format_ref_id = utils.get_format_ref_id(repo)
336 format_ref_id = utils.get_format_ref_id(repo)
340
337
341 result = []
338 result = []
342 for title, refs, ref_type in refs_to_create:
339 for title, refs, ref_type in refs_to_create:
343 if refs:
340 if refs:
344 result.append({
341 result.append({
345 'text': title,
342 'text': title,
346 'children': self._create_reference_items(
343 'children': self._create_reference_items(
347 repo, full_repo_name, refs, ref_type,
344 repo, full_repo_name, refs, ref_type,
348 format_ref_id),
345 format_ref_id),
349 })
346 })
350 return result
347 return result
351
348
352 def _create_reference_items(self, repo, full_repo_name, refs, ref_type,
349 def _create_reference_items(self, repo, full_repo_name, refs, ref_type,
353 format_ref_id):
350 format_ref_id):
354 result = []
351 result = []
355 is_svn = h.is_svn(repo)
352 is_svn = h.is_svn(repo)
356 for ref_name, raw_id in refs.iteritems():
353 for ref_name, raw_id in refs.iteritems():
357 files_url = self._create_files_url(
354 files_url = self._create_files_url(
358 repo, full_repo_name, ref_name, raw_id, is_svn)
355 repo, full_repo_name, ref_name, raw_id, is_svn)
359 result.append({
356 result.append({
360 'text': ref_name,
357 'text': ref_name,
361 'id': format_ref_id(ref_name, raw_id),
358 'id': format_ref_id(ref_name, raw_id),
362 'raw_id': raw_id,
359 'raw_id': raw_id,
363 'type': ref_type,
360 'type': ref_type,
364 'files_url': files_url,
361 'files_url': files_url,
365 })
362 })
366 return result
363 return result
367
364
368 def _create_files_url(self, repo, full_repo_name, ref_name, raw_id, is_svn):
365 def _create_files_url(self, repo, full_repo_name, ref_name, raw_id, is_svn):
369 use_commit_id = '/' in ref_name or is_svn
366 use_commit_id = '/' in ref_name or is_svn
370 return h.route_path(
367 return h.route_path(
371 'repo_files',
368 'repo_files',
372 repo_name=full_repo_name,
369 repo_name=full_repo_name,
373 f_path=ref_name if is_svn else '',
370 f_path=ref_name if is_svn else '',
374 commit_id=raw_id if use_commit_id else ref_name,
371 commit_id=raw_id if use_commit_id else ref_name,
375 _query=dict(at=ref_name))
372 _query=dict(at=ref_name))
@@ -1,543 +1,546 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 The base Controller API
22 The base Controller API
23 Provides the BaseController class for subclassing. And usage in different
23 Provides the BaseController class for subclassing. And usage in different
24 controllers
24 controllers
25 """
25 """
26
26
27 import logging
27 import logging
28 import socket
28 import socket
29
29
30 import markupsafe
30 import markupsafe
31 import ipaddress
31 import ipaddress
32
32
33 from paste.auth.basic import AuthBasicAuthenticator
33 from paste.auth.basic import AuthBasicAuthenticator
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
36
36
37 import rhodecode
37 import rhodecode
38 from rhodecode.authentication.base import VCS_TYPE
38 from rhodecode.authentication.base import VCS_TYPE
39 from rhodecode.lib import auth, utils2
39 from rhodecode.lib import auth, utils2
40 from rhodecode.lib import helpers as h
40 from rhodecode.lib import helpers as h
41 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
41 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
42 from rhodecode.lib.exceptions import UserCreationError
42 from rhodecode.lib.exceptions import UserCreationError
43 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
43 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
44 from rhodecode.lib.utils2 import (
44 from rhodecode.lib.utils2 import (
45 str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist, safe_str)
45 str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist, safe_str)
46 from rhodecode.model.db import Repository, User, ChangesetComment
46 from rhodecode.model.db import Repository, User, ChangesetComment
47 from rhodecode.model.notification import NotificationModel
47 from rhodecode.model.notification import NotificationModel
48 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
48 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52
52
53 def _filter_proxy(ip):
53 def _filter_proxy(ip):
54 """
54 """
55 Passed in IP addresses in HEADERS can be in a special format of multiple
55 Passed in IP addresses in HEADERS can be in a special format of multiple
56 ips. Those comma separated IPs are passed from various proxies in the
56 ips. Those comma separated IPs are passed from various proxies in the
57 chain of request processing. The left-most being the original client.
57 chain of request processing. The left-most being the original client.
58 We only care about the first IP which came from the org. client.
58 We only care about the first IP which came from the org. client.
59
59
60 :param ip: ip string from headers
60 :param ip: ip string from headers
61 """
61 """
62 if ',' in ip:
62 if ',' in ip:
63 _ips = ip.split(',')
63 _ips = ip.split(',')
64 _first_ip = _ips[0].strip()
64 _first_ip = _ips[0].strip()
65 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
65 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
66 return _first_ip
66 return _first_ip
67 return ip
67 return ip
68
68
69
69
70 def _filter_port(ip):
70 def _filter_port(ip):
71 """
71 """
72 Removes a port from ip, there are 4 main cases to handle here.
72 Removes a port from ip, there are 4 main cases to handle here.
73 - ipv4 eg. 127.0.0.1
73 - ipv4 eg. 127.0.0.1
74 - ipv6 eg. ::1
74 - ipv6 eg. ::1
75 - ipv4+port eg. 127.0.0.1:8080
75 - ipv4+port eg. 127.0.0.1:8080
76 - ipv6+port eg. [::1]:8080
76 - ipv6+port eg. [::1]:8080
77
77
78 :param ip:
78 :param ip:
79 """
79 """
80 def is_ipv6(ip_addr):
80 def is_ipv6(ip_addr):
81 if hasattr(socket, 'inet_pton'):
81 if hasattr(socket, 'inet_pton'):
82 try:
82 try:
83 socket.inet_pton(socket.AF_INET6, ip_addr)
83 socket.inet_pton(socket.AF_INET6, ip_addr)
84 except socket.error:
84 except socket.error:
85 return False
85 return False
86 else:
86 else:
87 # fallback to ipaddress
87 # fallback to ipaddress
88 try:
88 try:
89 ipaddress.IPv6Address(safe_unicode(ip_addr))
89 ipaddress.IPv6Address(safe_unicode(ip_addr))
90 except Exception:
90 except Exception:
91 return False
91 return False
92 return True
92 return True
93
93
94 if ':' not in ip: # must be ipv4 pure ip
94 if ':' not in ip: # must be ipv4 pure ip
95 return ip
95 return ip
96
96
97 if '[' in ip and ']' in ip: # ipv6 with port
97 if '[' in ip and ']' in ip: # ipv6 with port
98 return ip.split(']')[0][1:].lower()
98 return ip.split(']')[0][1:].lower()
99
99
100 # must be ipv6 or ipv4 with port
100 # must be ipv6 or ipv4 with port
101 if is_ipv6(ip):
101 if is_ipv6(ip):
102 return ip
102 return ip
103 else:
103 else:
104 ip, _port = ip.split(':')[:2] # means ipv4+port
104 ip, _port = ip.split(':')[:2] # means ipv4+port
105 return ip
105 return ip
106
106
107
107
108 def get_ip_addr(environ):
108 def get_ip_addr(environ):
109 proxy_key = 'HTTP_X_REAL_IP'
109 proxy_key = 'HTTP_X_REAL_IP'
110 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
110 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
111 def_key = 'REMOTE_ADDR'
111 def_key = 'REMOTE_ADDR'
112 _filters = lambda x: _filter_port(_filter_proxy(x))
112 _filters = lambda x: _filter_port(_filter_proxy(x))
113
113
114 ip = environ.get(proxy_key)
114 ip = environ.get(proxy_key)
115 if ip:
115 if ip:
116 return _filters(ip)
116 return _filters(ip)
117
117
118 ip = environ.get(proxy_key2)
118 ip = environ.get(proxy_key2)
119 if ip:
119 if ip:
120 return _filters(ip)
120 return _filters(ip)
121
121
122 ip = environ.get(def_key, '0.0.0.0')
122 ip = environ.get(def_key, '0.0.0.0')
123 return _filters(ip)
123 return _filters(ip)
124
124
125
125
126 def get_server_ip_addr(environ, log_errors=True):
126 def get_server_ip_addr(environ, log_errors=True):
127 hostname = environ.get('SERVER_NAME')
127 hostname = environ.get('SERVER_NAME')
128 try:
128 try:
129 return socket.gethostbyname(hostname)
129 return socket.gethostbyname(hostname)
130 except Exception as e:
130 except Exception as e:
131 if log_errors:
131 if log_errors:
132 # in some cases this lookup is not possible, and we don't want to
132 # in some cases this lookup is not possible, and we don't want to
133 # make it an exception in logs
133 # make it an exception in logs
134 log.exception('Could not retrieve server ip address: %s', e)
134 log.exception('Could not retrieve server ip address: %s', e)
135 return hostname
135 return hostname
136
136
137
137
138 def get_server_port(environ):
138 def get_server_port(environ):
139 return environ.get('SERVER_PORT')
139 return environ.get('SERVER_PORT')
140
140
141
141
142 def get_access_path(environ):
142 def get_access_path(environ):
143 path = environ.get('PATH_INFO')
143 path = environ.get('PATH_INFO')
144 org_req = environ.get('pylons.original_request')
144 org_req = environ.get('pylons.original_request')
145 if org_req:
145 if org_req:
146 path = org_req.environ.get('PATH_INFO')
146 path = org_req.environ.get('PATH_INFO')
147 return path
147 return path
148
148
149
149
150 def get_user_agent(environ):
150 def get_user_agent(environ):
151 return environ.get('HTTP_USER_AGENT')
151 return environ.get('HTTP_USER_AGENT')
152
152
153
153
154 def vcs_operation_context(
154 def vcs_operation_context(
155 environ, repo_name, username, action, scm, check_locking=True,
155 environ, repo_name, username, action, scm, check_locking=True,
156 is_shadow_repo=False):
156 is_shadow_repo=False):
157 """
157 """
158 Generate the context for a vcs operation, e.g. push or pull.
158 Generate the context for a vcs operation, e.g. push or pull.
159
159
160 This context is passed over the layers so that hooks triggered by the
160 This context is passed over the layers so that hooks triggered by the
161 vcs operation know details like the user, the user's IP address etc.
161 vcs operation know details like the user, the user's IP address etc.
162
162
163 :param check_locking: Allows to switch of the computation of the locking
163 :param check_locking: Allows to switch of the computation of the locking
164 data. This serves mainly the need of the simplevcs middleware to be
164 data. This serves mainly the need of the simplevcs middleware to be
165 able to disable this for certain operations.
165 able to disable this for certain operations.
166
166
167 """
167 """
168 # Tri-state value: False: unlock, None: nothing, True: lock
168 # Tri-state value: False: unlock, None: nothing, True: lock
169 make_lock = None
169 make_lock = None
170 locked_by = [None, None, None]
170 locked_by = [None, None, None]
171 is_anonymous = username == User.DEFAULT_USER
171 is_anonymous = username == User.DEFAULT_USER
172 user = User.get_by_username(username)
172 user = User.get_by_username(username)
173 if not is_anonymous and check_locking:
173 if not is_anonymous and check_locking:
174 log.debug('Checking locking on repository "%s"', repo_name)
174 log.debug('Checking locking on repository "%s"', repo_name)
175 repo = Repository.get_by_repo_name(repo_name)
175 repo = Repository.get_by_repo_name(repo_name)
176 make_lock, __, locked_by = repo.get_locking_state(
176 make_lock, __, locked_by = repo.get_locking_state(
177 action, user.user_id)
177 action, user.user_id)
178 user_id = user.user_id
178 user_id = user.user_id
179 settings_model = VcsSettingsModel(repo=repo_name)
179 settings_model = VcsSettingsModel(repo=repo_name)
180 ui_settings = settings_model.get_ui_settings()
180 ui_settings = settings_model.get_ui_settings()
181
181
182 extras = {
182 extras = {
183 'ip': get_ip_addr(environ),
183 'ip': get_ip_addr(environ),
184 'username': username,
184 'username': username,
185 'user_id': user_id,
185 'user_id': user_id,
186 'action': action,
186 'action': action,
187 'repository': repo_name,
187 'repository': repo_name,
188 'scm': scm,
188 'scm': scm,
189 'config': rhodecode.CONFIG['__file__'],
189 'config': rhodecode.CONFIG['__file__'],
190 'make_lock': make_lock,
190 'make_lock': make_lock,
191 'locked_by': locked_by,
191 'locked_by': locked_by,
192 'server_url': utils2.get_server_url(environ),
192 'server_url': utils2.get_server_url(environ),
193 'user_agent': get_user_agent(environ),
193 'user_agent': get_user_agent(environ),
194 'hooks': get_enabled_hook_classes(ui_settings),
194 'hooks': get_enabled_hook_classes(ui_settings),
195 'is_shadow_repo': is_shadow_repo,
195 'is_shadow_repo': is_shadow_repo,
196 }
196 }
197 return extras
197 return extras
198
198
199
199
200 class BasicAuth(AuthBasicAuthenticator):
200 class BasicAuth(AuthBasicAuthenticator):
201
201
202 def __init__(self, realm, authfunc, registry, auth_http_code=None,
202 def __init__(self, realm, authfunc, registry, auth_http_code=None,
203 initial_call_detection=False, acl_repo_name=None):
203 initial_call_detection=False, acl_repo_name=None):
204 self.realm = realm
204 self.realm = realm
205 self.initial_call = initial_call_detection
205 self.initial_call = initial_call_detection
206 self.authfunc = authfunc
206 self.authfunc = authfunc
207 self.registry = registry
207 self.registry = registry
208 self.acl_repo_name = acl_repo_name
208 self.acl_repo_name = acl_repo_name
209 self._rc_auth_http_code = auth_http_code
209 self._rc_auth_http_code = auth_http_code
210
210
211 def _get_response_from_code(self, http_code):
211 def _get_response_from_code(self, http_code):
212 try:
212 try:
213 return get_exception(safe_int(http_code))
213 return get_exception(safe_int(http_code))
214 except Exception:
214 except Exception:
215 log.exception('Failed to fetch response for code %s' % http_code)
215 log.exception('Failed to fetch response for code %s' % http_code)
216 return HTTPForbidden
216 return HTTPForbidden
217
217
218 def get_rc_realm(self):
218 def get_rc_realm(self):
219 return safe_str(self.registry.rhodecode_settings.get('rhodecode_realm'))
219 return safe_str(self.registry.rhodecode_settings.get('rhodecode_realm'))
220
220
221 def build_authentication(self):
221 def build_authentication(self):
222 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
222 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
223 if self._rc_auth_http_code and not self.initial_call:
223 if self._rc_auth_http_code and not self.initial_call:
224 # return alternative HTTP code if alternative http return code
224 # return alternative HTTP code if alternative http return code
225 # is specified in RhodeCode config, but ONLY if it's not the
225 # is specified in RhodeCode config, but ONLY if it's not the
226 # FIRST call
226 # FIRST call
227 custom_response_klass = self._get_response_from_code(
227 custom_response_klass = self._get_response_from_code(
228 self._rc_auth_http_code)
228 self._rc_auth_http_code)
229 return custom_response_klass(headers=head)
229 return custom_response_klass(headers=head)
230 return HTTPUnauthorized(headers=head)
230 return HTTPUnauthorized(headers=head)
231
231
232 def authenticate(self, environ):
232 def authenticate(self, environ):
233 authorization = AUTHORIZATION(environ)
233 authorization = AUTHORIZATION(environ)
234 if not authorization:
234 if not authorization:
235 return self.build_authentication()
235 return self.build_authentication()
236 (authmeth, auth) = authorization.split(' ', 1)
236 (authmeth, auth) = authorization.split(' ', 1)
237 if 'basic' != authmeth.lower():
237 if 'basic' != authmeth.lower():
238 return self.build_authentication()
238 return self.build_authentication()
239 auth = auth.strip().decode('base64')
239 auth = auth.strip().decode('base64')
240 _parts = auth.split(':', 1)
240 _parts = auth.split(':', 1)
241 if len(_parts) == 2:
241 if len(_parts) == 2:
242 username, password = _parts
242 username, password = _parts
243 auth_data = self.authfunc(
243 auth_data = self.authfunc(
244 username, password, environ, VCS_TYPE,
244 username, password, environ, VCS_TYPE,
245 registry=self.registry, acl_repo_name=self.acl_repo_name)
245 registry=self.registry, acl_repo_name=self.acl_repo_name)
246 if auth_data:
246 if auth_data:
247 return {'username': username, 'auth_data': auth_data}
247 return {'username': username, 'auth_data': auth_data}
248 if username and password:
248 if username and password:
249 # we mark that we actually executed authentication once, at
249 # we mark that we actually executed authentication once, at
250 # that point we can use the alternative auth code
250 # that point we can use the alternative auth code
251 self.initial_call = False
251 self.initial_call = False
252
252
253 return self.build_authentication()
253 return self.build_authentication()
254
254
255 __call__ = authenticate
255 __call__ = authenticate
256
256
257
257
258 def calculate_version_hash(config):
258 def calculate_version_hash(config):
259 return md5(
259 return md5(
260 config.get('beaker.session.secret', '') +
260 config.get('beaker.session.secret', '') +
261 rhodecode.__version__)[:8]
261 rhodecode.__version__)[:8]
262
262
263
263
264 def get_current_lang(request):
264 def get_current_lang(request):
265 # NOTE(marcink): remove after pyramid move
265 # NOTE(marcink): remove after pyramid move
266 try:
266 try:
267 return translation.get_lang()[0]
267 return translation.get_lang()[0]
268 except:
268 except:
269 pass
269 pass
270
270
271 return getattr(request, '_LOCALE_', request.locale_name)
271 return getattr(request, '_LOCALE_', request.locale_name)
272
272
273
273
274 def attach_context_attributes(context, request, user_id):
274 def attach_context_attributes(context, request, user_id):
275 """
275 """
276 Attach variables into template context called `c`.
276 Attach variables into template context called `c`.
277 """
277 """
278 config = request.registry.settings
278 config = request.registry.settings
279
279
280
280
281 rc_config = SettingsModel().get_all_settings(cache=True)
281 rc_config = SettingsModel().get_all_settings(cache=True)
282
282
283 context.rhodecode_version = rhodecode.__version__
283 context.rhodecode_version = rhodecode.__version__
284 context.rhodecode_edition = config.get('rhodecode.edition')
284 context.rhodecode_edition = config.get('rhodecode.edition')
285 # unique secret + version does not leak the version but keep consistency
285 # unique secret + version does not leak the version but keep consistency
286 context.rhodecode_version_hash = calculate_version_hash(config)
286 context.rhodecode_version_hash = calculate_version_hash(config)
287
287
288 # Default language set for the incoming request
288 # Default language set for the incoming request
289 context.language = get_current_lang(request)
289 context.language = get_current_lang(request)
290
290
291 # Visual options
291 # Visual options
292 context.visual = AttributeDict({})
292 context.visual = AttributeDict({})
293
293
294 # DB stored Visual Items
294 # DB stored Visual Items
295 context.visual.show_public_icon = str2bool(
295 context.visual.show_public_icon = str2bool(
296 rc_config.get('rhodecode_show_public_icon'))
296 rc_config.get('rhodecode_show_public_icon'))
297 context.visual.show_private_icon = str2bool(
297 context.visual.show_private_icon = str2bool(
298 rc_config.get('rhodecode_show_private_icon'))
298 rc_config.get('rhodecode_show_private_icon'))
299 context.visual.stylify_metatags = str2bool(
299 context.visual.stylify_metatags = str2bool(
300 rc_config.get('rhodecode_stylify_metatags'))
300 rc_config.get('rhodecode_stylify_metatags'))
301 context.visual.dashboard_items = safe_int(
301 context.visual.dashboard_items = safe_int(
302 rc_config.get('rhodecode_dashboard_items', 100))
302 rc_config.get('rhodecode_dashboard_items', 100))
303 context.visual.admin_grid_items = safe_int(
303 context.visual.admin_grid_items = safe_int(
304 rc_config.get('rhodecode_admin_grid_items', 100))
304 rc_config.get('rhodecode_admin_grid_items', 100))
305 context.visual.repository_fields = str2bool(
305 context.visual.repository_fields = str2bool(
306 rc_config.get('rhodecode_repository_fields'))
306 rc_config.get('rhodecode_repository_fields'))
307 context.visual.show_version = str2bool(
307 context.visual.show_version = str2bool(
308 rc_config.get('rhodecode_show_version'))
308 rc_config.get('rhodecode_show_version'))
309 context.visual.use_gravatar = str2bool(
309 context.visual.use_gravatar = str2bool(
310 rc_config.get('rhodecode_use_gravatar'))
310 rc_config.get('rhodecode_use_gravatar'))
311 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
311 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
312 context.visual.default_renderer = rc_config.get(
312 context.visual.default_renderer = rc_config.get(
313 'rhodecode_markup_renderer', 'rst')
313 'rhodecode_markup_renderer', 'rst')
314 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
314 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
315 context.visual.rhodecode_support_url = \
315 context.visual.rhodecode_support_url = \
316 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
316 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
317
317
318 context.visual.affected_files_cut_off = 60
318 context.visual.affected_files_cut_off = 60
319
319
320 context.pre_code = rc_config.get('rhodecode_pre_code')
320 context.pre_code = rc_config.get('rhodecode_pre_code')
321 context.post_code = rc_config.get('rhodecode_post_code')
321 context.post_code = rc_config.get('rhodecode_post_code')
322 context.rhodecode_name = rc_config.get('rhodecode_title')
322 context.rhodecode_name = rc_config.get('rhodecode_title')
323 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
323 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
324 # if we have specified default_encoding in the request, it has more
324 # if we have specified default_encoding in the request, it has more
325 # priority
325 # priority
326 if request.GET.get('default_encoding'):
326 if request.GET.get('default_encoding'):
327 context.default_encodings.insert(0, request.GET.get('default_encoding'))
327 context.default_encodings.insert(0, request.GET.get('default_encoding'))
328 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
328 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
329 context.clone_uri_ssh_tmpl = rc_config.get('rhodecode_clone_uri_ssh_tmpl')
329 context.clone_uri_ssh_tmpl = rc_config.get('rhodecode_clone_uri_ssh_tmpl')
330
330
331 # INI stored
331 # INI stored
332 context.labs_active = str2bool(
332 context.labs_active = str2bool(
333 config.get('labs_settings_active', 'false'))
333 config.get('labs_settings_active', 'false'))
334 context.ssh_enabled = str2bool(
335 config.get('ssh.generate_authorized_keyfile', 'false'))
336
334 context.visual.allow_repo_location_change = str2bool(
337 context.visual.allow_repo_location_change = str2bool(
335 config.get('allow_repo_location_change', True))
338 config.get('allow_repo_location_change', True))
336 context.visual.allow_custom_hooks_settings = str2bool(
339 context.visual.allow_custom_hooks_settings = str2bool(
337 config.get('allow_custom_hooks_settings', True))
340 config.get('allow_custom_hooks_settings', True))
338 context.debug_style = str2bool(config.get('debug_style', False))
341 context.debug_style = str2bool(config.get('debug_style', False))
339
342
340 context.rhodecode_instanceid = config.get('instance_id')
343 context.rhodecode_instanceid = config.get('instance_id')
341
344
342 context.visual.cut_off_limit_diff = safe_int(
345 context.visual.cut_off_limit_diff = safe_int(
343 config.get('cut_off_limit_diff'))
346 config.get('cut_off_limit_diff'))
344 context.visual.cut_off_limit_file = safe_int(
347 context.visual.cut_off_limit_file = safe_int(
345 config.get('cut_off_limit_file'))
348 config.get('cut_off_limit_file'))
346
349
347 # AppEnlight
350 # AppEnlight
348 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
351 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
349 context.appenlight_api_public_key = config.get(
352 context.appenlight_api_public_key = config.get(
350 'appenlight.api_public_key', '')
353 'appenlight.api_public_key', '')
351 context.appenlight_server_url = config.get('appenlight.server_url', '')
354 context.appenlight_server_url = config.get('appenlight.server_url', '')
352
355
353 # JS template context
356 # JS template context
354 context.template_context = {
357 context.template_context = {
355 'repo_name': None,
358 'repo_name': None,
356 'repo_type': None,
359 'repo_type': None,
357 'repo_landing_commit': None,
360 'repo_landing_commit': None,
358 'rhodecode_user': {
361 'rhodecode_user': {
359 'username': None,
362 'username': None,
360 'email': None,
363 'email': None,
361 'notification_status': False
364 'notification_status': False
362 },
365 },
363 'visual': {
366 'visual': {
364 'default_renderer': None
367 'default_renderer': None
365 },
368 },
366 'commit_data': {
369 'commit_data': {
367 'commit_id': None
370 'commit_id': None
368 },
371 },
369 'pull_request_data': {'pull_request_id': None},
372 'pull_request_data': {'pull_request_id': None},
370 'timeago': {
373 'timeago': {
371 'refresh_time': 120 * 1000,
374 'refresh_time': 120 * 1000,
372 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
375 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
373 },
376 },
374 'pyramid_dispatch': {
377 'pyramid_dispatch': {
375
378
376 },
379 },
377 'extra': {'plugins': {}}
380 'extra': {'plugins': {}}
378 }
381 }
379 # END CONFIG VARS
382 # END CONFIG VARS
380
383
381 diffmode = 'sideside'
384 diffmode = 'sideside'
382 if request.GET.get('diffmode'):
385 if request.GET.get('diffmode'):
383 if request.GET['diffmode'] == 'unified':
386 if request.GET['diffmode'] == 'unified':
384 diffmode = 'unified'
387 diffmode = 'unified'
385 elif request.session.get('diffmode'):
388 elif request.session.get('diffmode'):
386 diffmode = request.session['diffmode']
389 diffmode = request.session['diffmode']
387
390
388 context.diffmode = diffmode
391 context.diffmode = diffmode
389
392
390 if request.session.get('diffmode') != diffmode:
393 if request.session.get('diffmode') != diffmode:
391 request.session['diffmode'] = diffmode
394 request.session['diffmode'] = diffmode
392
395
393 context.csrf_token = auth.get_csrf_token(session=request.session)
396 context.csrf_token = auth.get_csrf_token(session=request.session)
394 context.backends = rhodecode.BACKENDS.keys()
397 context.backends = rhodecode.BACKENDS.keys()
395 context.backends.sort()
398 context.backends.sort()
396 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(user_id)
399 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(user_id)
397
400
398 # web case
401 # web case
399 if hasattr(request, 'user'):
402 if hasattr(request, 'user'):
400 context.auth_user = request.user
403 context.auth_user = request.user
401 context.rhodecode_user = request.user
404 context.rhodecode_user = request.user
402
405
403 # api case
406 # api case
404 if hasattr(request, 'rpc_user'):
407 if hasattr(request, 'rpc_user'):
405 context.auth_user = request.rpc_user
408 context.auth_user = request.rpc_user
406 context.rhodecode_user = request.rpc_user
409 context.rhodecode_user = request.rpc_user
407
410
408 # attach the whole call context to the request
411 # attach the whole call context to the request
409 request.call_context = context
412 request.call_context = context
410
413
411
414
412 def get_auth_user(request):
415 def get_auth_user(request):
413 environ = request.environ
416 environ = request.environ
414 session = request.session
417 session = request.session
415
418
416 ip_addr = get_ip_addr(environ)
419 ip_addr = get_ip_addr(environ)
417 # make sure that we update permissions each time we call controller
420 # make sure that we update permissions each time we call controller
418 _auth_token = (request.GET.get('auth_token', '') or
421 _auth_token = (request.GET.get('auth_token', '') or
419 request.GET.get('api_key', ''))
422 request.GET.get('api_key', ''))
420
423
421 if _auth_token:
424 if _auth_token:
422 # when using API_KEY we assume user exists, and
425 # when using API_KEY we assume user exists, and
423 # doesn't need auth based on cookies.
426 # doesn't need auth based on cookies.
424 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
427 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
425 authenticated = False
428 authenticated = False
426 else:
429 else:
427 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
430 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
428 try:
431 try:
429 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
432 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
430 ip_addr=ip_addr)
433 ip_addr=ip_addr)
431 except UserCreationError as e:
434 except UserCreationError as e:
432 h.flash(e, 'error')
435 h.flash(e, 'error')
433 # container auth or other auth functions that create users
436 # container auth or other auth functions that create users
434 # on the fly can throw this exception signaling that there's
437 # on the fly can throw this exception signaling that there's
435 # issue with user creation, explanation should be provided
438 # issue with user creation, explanation should be provided
436 # in Exception itself. We then create a simple blank
439 # in Exception itself. We then create a simple blank
437 # AuthUser
440 # AuthUser
438 auth_user = AuthUser(ip_addr=ip_addr)
441 auth_user = AuthUser(ip_addr=ip_addr)
439
442
440 # in case someone changes a password for user it triggers session
443 # in case someone changes a password for user it triggers session
441 # flush and forces a re-login
444 # flush and forces a re-login
442 if password_changed(auth_user, session):
445 if password_changed(auth_user, session):
443 session.invalidate()
446 session.invalidate()
444 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
447 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
445 auth_user = AuthUser(ip_addr=ip_addr)
448 auth_user = AuthUser(ip_addr=ip_addr)
446
449
447 authenticated = cookie_store.get('is_authenticated')
450 authenticated = cookie_store.get('is_authenticated')
448
451
449 if not auth_user.is_authenticated and auth_user.is_user_object:
452 if not auth_user.is_authenticated and auth_user.is_user_object:
450 # user is not authenticated and not empty
453 # user is not authenticated and not empty
451 auth_user.set_authenticated(authenticated)
454 auth_user.set_authenticated(authenticated)
452
455
453 return auth_user
456 return auth_user
454
457
455
458
456 def h_filter(s):
459 def h_filter(s):
457 """
460 """
458 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
461 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
459 we wrap this with additional functionality that converts None to empty
462 we wrap this with additional functionality that converts None to empty
460 strings
463 strings
461 """
464 """
462 if s is None:
465 if s is None:
463 return markupsafe.Markup()
466 return markupsafe.Markup()
464 return markupsafe.escape(s)
467 return markupsafe.escape(s)
465
468
466
469
467 def add_events_routes(config):
470 def add_events_routes(config):
468 """
471 """
469 Adds routing that can be used in events. Because some events are triggered
472 Adds routing that can be used in events. Because some events are triggered
470 outside of pyramid context, we need to bootstrap request with some
473 outside of pyramid context, we need to bootstrap request with some
471 routing registered
474 routing registered
472 """
475 """
473
476
474 from rhodecode.apps._base import ADMIN_PREFIX
477 from rhodecode.apps._base import ADMIN_PREFIX
475
478
476 config.add_route(name='home', pattern='/')
479 config.add_route(name='home', pattern='/')
477
480
478 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
481 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
479 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
482 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
480 config.add_route(name='repo_summary', pattern='/{repo_name}')
483 config.add_route(name='repo_summary', pattern='/{repo_name}')
481 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
484 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
482 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
485 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
483
486
484 config.add_route(name='pullrequest_show',
487 config.add_route(name='pullrequest_show',
485 pattern='/{repo_name}/pull-request/{pull_request_id}')
488 pattern='/{repo_name}/pull-request/{pull_request_id}')
486 config.add_route(name='pull_requests_global',
489 config.add_route(name='pull_requests_global',
487 pattern='/pull-request/{pull_request_id}')
490 pattern='/pull-request/{pull_request_id}')
488 config.add_route(name='repo_commit',
491 config.add_route(name='repo_commit',
489 pattern='/{repo_name}/changeset/{commit_id}')
492 pattern='/{repo_name}/changeset/{commit_id}')
490
493
491 config.add_route(name='repo_files',
494 config.add_route(name='repo_files',
492 pattern='/{repo_name}/files/{commit_id}/{f_path}')
495 pattern='/{repo_name}/files/{commit_id}/{f_path}')
493
496
494
497
495 def bootstrap_config(request):
498 def bootstrap_config(request):
496 import pyramid.testing
499 import pyramid.testing
497 registry = pyramid.testing.Registry('RcTestRegistry')
500 registry = pyramid.testing.Registry('RcTestRegistry')
498
501
499 config = pyramid.testing.setUp(registry=registry, request=request)
502 config = pyramid.testing.setUp(registry=registry, request=request)
500
503
501 # allow pyramid lookup in testing
504 # allow pyramid lookup in testing
502 config.include('pyramid_mako')
505 config.include('pyramid_mako')
503 config.include('pyramid_beaker')
506 config.include('pyramid_beaker')
504 config.include('rhodecode.lib.caches')
507 config.include('rhodecode.lib.caches')
505
508
506 add_events_routes(config)
509 add_events_routes(config)
507
510
508 return config
511 return config
509
512
510
513
511 def bootstrap_request(**kwargs):
514 def bootstrap_request(**kwargs):
512 import pyramid.testing
515 import pyramid.testing
513
516
514 class TestRequest(pyramid.testing.DummyRequest):
517 class TestRequest(pyramid.testing.DummyRequest):
515 application_url = kwargs.pop('application_url', 'http://example.com')
518 application_url = kwargs.pop('application_url', 'http://example.com')
516 host = kwargs.pop('host', 'example.com:80')
519 host = kwargs.pop('host', 'example.com:80')
517 domain = kwargs.pop('domain', 'example.com')
520 domain = kwargs.pop('domain', 'example.com')
518
521
519 def translate(self, msg):
522 def translate(self, msg):
520 return msg
523 return msg
521
524
522 def plularize(self, singular, plural, n):
525 def plularize(self, singular, plural, n):
523 return singular
526 return singular
524
527
525 def get_partial_renderer(self, tmpl_name):
528 def get_partial_renderer(self, tmpl_name):
526
529
527 from rhodecode.lib.partial_renderer import get_partial_renderer
530 from rhodecode.lib.partial_renderer import get_partial_renderer
528 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
531 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
529
532
530 _call_context = {}
533 _call_context = {}
531 @property
534 @property
532 def call_context(self):
535 def call_context(self):
533 return self._call_context
536 return self._call_context
534
537
535 class TestDummySession(pyramid.testing.DummySession):
538 class TestDummySession(pyramid.testing.DummySession):
536 def save(*arg, **kw):
539 def save(*arg, **kw):
537 pass
540 pass
538
541
539 request = TestRequest(**kwargs)
542 request = TestRequest(**kwargs)
540 request.session = TestDummySession()
543 request.session = TestDummySession()
541
544
542 return request
545 return request
543
546
@@ -1,214 +1,216 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
29 </%def>
29 </%def>
30
30
31 <%def name="summary_detail(breadcrumbs_links, show_downloads=True)">
31 <%def name="summary_detail(breadcrumbs_links, show_downloads=True)">
32 <% summary = lambda n:{False:'summary-short'}.get(n) %>
32 <% summary = lambda n:{False:'summary-short'}.get(n) %>
33
33
34 <div id="summary-menu-stats" class="summary-detail">
34 <div id="summary-menu-stats" class="summary-detail">
35 <div class="summary-detail-header">
35 <div class="summary-detail-header">
36 <div class="breadcrumbs files_location">
36 <div class="breadcrumbs files_location">
37 <h4>
37 <h4>
38 ${breadcrumbs_links}
38 ${breadcrumbs_links}
39 </h4>
39 </h4>
40 </div>
40 </div>
41 <div id="summary_details_expand" class="btn-collapse" data-toggle="summary-details">
41 <div id="summary_details_expand" class="btn-collapse" data-toggle="summary-details">
42 ${_('Show More')}
42 ${_('Show More')}
43 </div>
43 </div>
44 </div>
44 </div>
45
45
46 <div class="fieldset">
46 <div class="fieldset">
47
47
48 <div class="left-clone">
48 <div class="left-clone">
49 <select id="clone_option" name="clone_option">
49 <select id="clone_option" name="clone_option">
50 <option value="http" selected="selected">HTTP</option>
50 <option value="http" selected="selected">HTTP</option>
51 <option value="http_id">HTTP UID</option>
51 <option value="http_id">HTTP UID</option>
52 % if c.ssh_enabled:
52 <option value="ssh">SSH</option>
53 <option value="ssh">SSH</option>
54 % endif
53 </select>
55 </select>
54 </div>
56 </div>
55 <div class="right-clone">
57 <div class="right-clone">
56 <%
58 <%
57 maybe_disabled = ''
59 maybe_disabled = ''
58 if h.is_svn_without_proxy(c.rhodecode_db_repo):
60 if h.is_svn_without_proxy(c.rhodecode_db_repo):
59 maybe_disabled = 'disabled'
61 maybe_disabled = 'disabled'
60 %>
62 %>
61
63
62 <span id="clone_option_http">
64 <span id="clone_option_http">
63 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url}"/>
65 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url}"/>
64 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url}" title="${_('Copy the clone url')}"></i>
66 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url}" title="${_('Copy the clone url')}"></i>
65 </span>
67 </span>
66
68
67 <span style="display: none;" id="clone_option_http_id">
69 <span style="display: none;" id="clone_option_http_id">
68 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url_id}"/>
70 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url_id}"/>
69 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url_id}" title="${_('Copy the clone by id url')}"></i>
71 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url_id}" title="${_('Copy the clone by id url')}"></i>
70 </span>
72 </span>
71
73
72 <span style="display: none;" id="clone_option_ssh">
74 <span style="display: none;" id="clone_option_ssh">
73 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url_ssh}"/>
75 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url_ssh}"/>
74 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url_ssh}" title="${_('Copy the clone by ssh url')}"></i>
76 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url_ssh}" title="${_('Copy the clone by ssh url')}"></i>
75 </span>
77 </span>
76
78
77 % if maybe_disabled:
79 % if maybe_disabled:
78 <p class="help-block">${_('SVN Protocol is disabled. To enable it, see the')} <a href="${h.route_url('enterprise_svn_setup')}" target="_blank">${_('documentation here')}</a>.</p>
80 <p class="help-block">${_('SVN Protocol is disabled. To enable it, see the')} <a href="${h.route_url('enterprise_svn_setup')}" target="_blank">${_('documentation here')}</a>.</p>
79 % endif
81 % endif
80
82
81 </div>
83 </div>
82 </div>
84 </div>
83
85
84 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
86 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
85 <div class="left-label">
87 <div class="left-label">
86 ${_('Description')}:
88 ${_('Description')}:
87 </div>
89 </div>
88 <div class="right-content">
90 <div class="right-content">
89 <div class="input ${summary(c.show_stats)}">
91 <div class="input ${summary(c.show_stats)}">
90 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
92 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
91 ${dt.repo_desc(c.rhodecode_db_repo.description_safe, c.visual.stylify_metatags)}
93 ${dt.repo_desc(c.rhodecode_db_repo.description_safe, c.visual.stylify_metatags)}
92 </div>
94 </div>
93 </div>
95 </div>
94 </div>
96 </div>
95
97
96 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
98 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
97 <div class="left-label">
99 <div class="left-label">
98 ${_('Information')}:
100 ${_('Information')}:
99 </div>
101 </div>
100 <div class="right-content">
102 <div class="right-content">
101
103
102 <div class="repo-size">
104 <div class="repo-size">
103 <% commit_rev = c.rhodecode_db_repo.changeset_cache.get('revision') %>
105 <% commit_rev = c.rhodecode_db_repo.changeset_cache.get('revision') %>
104
106
105 ## commits
107 ## commits
106 % if commit_rev == -1:
108 % if commit_rev == -1:
107 ${_ungettext('%(num)s Commit', '%(num)s Commits', 0) % {'num': 0}},
109 ${_ungettext('%(num)s Commit', '%(num)s Commits', 0) % {'num': 0}},
108 % else:
110 % else:
109 <a href="${h.route_path('repo_changelog', repo_name=c.repo_name)}">
111 <a href="${h.route_path('repo_changelog', repo_name=c.repo_name)}">
110 ${_ungettext('%(num)s Commit', '%(num)s Commits', commit_rev) % {'num': commit_rev}}</a>,
112 ${_ungettext('%(num)s Commit', '%(num)s Commits', commit_rev) % {'num': commit_rev}}</a>,
111 % endif
113 % endif
112
114
113 ## forks
115 ## forks
114 <a title="${_('Number of Repository Forks')}" href="${h.route_path('repo_forks_show_all', repo_name=c.repo_name)}">
116 <a title="${_('Number of Repository Forks')}" href="${h.route_path('repo_forks_show_all', repo_name=c.repo_name)}">
115 ${c.repository_forks} ${_ungettext('Fork', 'Forks', c.repository_forks)}</a>,
117 ${c.repository_forks} ${_ungettext('Fork', 'Forks', c.repository_forks)}</a>,
116
118
117 ## repo size
119 ## repo size
118 % if commit_rev == -1:
120 % if commit_rev == -1:
119 <span class="stats-bullet">0 B</span>
121 <span class="stats-bullet">0 B</span>
120 % else:
122 % else:
121 <span class="stats-bullet" id="repo_size_container">
123 <span class="stats-bullet" id="repo_size_container">
122 ${_('Calculating Repository Size...')}
124 ${_('Calculating Repository Size...')}
123 </span>
125 </span>
124 % endif
126 % endif
125 </div>
127 </div>
126
128
127 <div class="commit-info">
129 <div class="commit-info">
128 <div class="tags">
130 <div class="tags">
129 % if c.rhodecode_repo:
131 % if c.rhodecode_repo:
130 ${refs_counters(
132 ${refs_counters(
131 c.rhodecode_repo.branches,
133 c.rhodecode_repo.branches,
132 c.rhodecode_repo.branches_closed,
134 c.rhodecode_repo.branches_closed,
133 c.rhodecode_repo.tags,
135 c.rhodecode_repo.tags,
134 c.rhodecode_repo.bookmarks)}
136 c.rhodecode_repo.bookmarks)}
135 % else:
137 % else:
136 ## missing requirements can make c.rhodecode_repo None
138 ## missing requirements can make c.rhodecode_repo None
137 ${refs_counters([], [], [], [])}
139 ${refs_counters([], [], [], [])}
138 % endif
140 % endif
139 </div>
141 </div>
140 </div>
142 </div>
141
143
142 </div>
144 </div>
143 </div>
145 </div>
144
146
145 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
147 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
146 <div class="left-label">
148 <div class="left-label">
147 ${_('Statistics')}:
149 ${_('Statistics')}:
148 </div>
150 </div>
149 <div class="right-content">
151 <div class="right-content">
150 <div class="input ${summary(c.show_stats)} statistics">
152 <div class="input ${summary(c.show_stats)} statistics">
151 % if c.show_stats:
153 % if c.show_stats:
152 <div id="lang_stats" class="enabled">
154 <div id="lang_stats" class="enabled">
153 ${_('Calculating Code Statistics...')}
155 ${_('Calculating Code Statistics...')}
154 </div>
156 </div>
155 % else:
157 % else:
156 <span class="disabled">
158 <span class="disabled">
157 ${_('Statistics are disabled for this repository')}
159 ${_('Statistics are disabled for this repository')}
158 </span>
160 </span>
159 % if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
161 % if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
160 , ${h.link_to(_('enable statistics'),h.route_path('edit_repo',repo_name=c.repo_name, _anchor='repo_enable_statistics'))}
162 , ${h.link_to(_('enable statistics'),h.route_path('edit_repo',repo_name=c.repo_name, _anchor='repo_enable_statistics'))}
161 % endif
163 % endif
162 % endif
164 % endif
163 </div>
165 </div>
164
166
165 </div>
167 </div>
166 </div>
168 </div>
167
169
168 % if show_downloads:
170 % if show_downloads:
169 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
171 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
170 <div class="left-label">
172 <div class="left-label">
171 ${_('Downloads')}:
173 ${_('Downloads')}:
172 </div>
174 </div>
173 <div class="right-content">
175 <div class="right-content">
174 <div class="input ${summary(c.show_stats)} downloads">
176 <div class="input ${summary(c.show_stats)} downloads">
175 % if c.rhodecode_repo and len(c.rhodecode_repo.revisions) == 0:
177 % if c.rhodecode_repo and len(c.rhodecode_repo.revisions) == 0:
176 <span class="disabled">
178 <span class="disabled">
177 ${_('There are no downloads yet')}
179 ${_('There are no downloads yet')}
178 </span>
180 </span>
179 % elif not c.enable_downloads:
181 % elif not c.enable_downloads:
180 <span class="disabled">
182 <span class="disabled">
181 ${_('Downloads are disabled for this repository')}
183 ${_('Downloads are disabled for this repository')}
182 </span>
184 </span>
183 % if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
185 % if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
184 , ${h.link_to(_('enable downloads'),h.route_path('edit_repo',repo_name=c.repo_name, _anchor='repo_enable_downloads'))}
186 , ${h.link_to(_('enable downloads'),h.route_path('edit_repo',repo_name=c.repo_name, _anchor='repo_enable_downloads'))}
185 % endif
187 % endif
186 % else:
188 % else:
187 <span class="enabled">
189 <span class="enabled">
188 <a id="archive_link" class="btn btn-small" href="${h.route_path('repo_archivefile',repo_name=c.rhodecode_db_repo.repo_name,fname='tip.zip')}">
190 <a id="archive_link" class="btn btn-small" href="${h.route_path('repo_archivefile',repo_name=c.rhodecode_db_repo.repo_name,fname='tip.zip')}">
189 <i class="icon-archive"></i> tip.zip
191 <i class="icon-archive"></i> tip.zip
190 ## replaced by some JS on select
192 ## replaced by some JS on select
191 </a>
193 </a>
192 </span>
194 </span>
193 ${h.hidden('download_options')}
195 ${h.hidden('download_options')}
194 % endif
196 % endif
195 </div>
197 </div>
196 </div>
198 </div>
197 </div>
199 </div>
198 % endif
200 % endif
199
201
200 </div><!--end summary-detail-->
202 </div><!--end summary-detail-->
201 </%def>
203 </%def>
202
204
203 <%def name="summary_stats(gravatar_function)">
205 <%def name="summary_stats(gravatar_function)">
204 <div class="sidebar-right">
206 <div class="sidebar-right">
205 <div class="summary-detail-header">
207 <div class="summary-detail-header">
206 <h4 class="item">
208 <h4 class="item">
207 ${_('Owner')}
209 ${_('Owner')}
208 </h4>
210 </h4>
209 </div>
211 </div>
210 <div class="sidebar-right-content">
212 <div class="sidebar-right-content">
211 ${gravatar_function(c.rhodecode_db_repo.user.email, 16)}
213 ${gravatar_function(c.rhodecode_db_repo.user.email, 16)}
212 </div>
214 </div>
213 </div><!--end sidebar-right-->
215 </div><!--end sidebar-right-->
214 </%def>
216 </%def>
General Comments 0
You need to be logged in to leave comments. Login now