##// END OF EJS Templates
users: use new session store for temporary settings for users....
marcink -
r3088:fbcbf993 default
parent child Browse files
Show More
@@ -1,73 +1,77 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-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 from rhodecode.config import routing_links
20 from rhodecode.config import routing_links
21
21
22
22
23 class VCSCallPredicate(object):
23 class VCSCallPredicate(object):
24 def __init__(self, val, config):
24 def __init__(self, val, config):
25 self.val = val
25 self.val = val
26
26
27 def text(self):
27 def text(self):
28 return 'vcs_call route = %s' % self.val
28 return 'vcs_call route = %s' % self.val
29
29
30 phash = text
30 phash = text
31
31
32 def __call__(self, info, request):
32 def __call__(self, info, request):
33 if hasattr(request, 'vcs_call'):
33 if hasattr(request, 'vcs_call'):
34 # skip vcs calls
34 # skip vcs calls
35 return False
35 return False
36
36
37 return True
37 return True
38
38
39
39
40 def includeme(config):
40 def includeme(config):
41
41
42 config.add_route(
42 config.add_route(
43 name='home',
43 name='home',
44 pattern='/')
44 pattern='/')
45
45
46 config.add_route(
46 config.add_route(
47 name='user_autocomplete_data',
47 name='user_autocomplete_data',
48 pattern='/_users')
48 pattern='/_users')
49
49
50 config.add_route(
50 config.add_route(
51 name='user_group_autocomplete_data',
51 name='user_group_autocomplete_data',
52 pattern='/_user_groups')
52 pattern='/_user_groups')
53
53
54 config.add_route(
54 config.add_route(
55 name='repo_list_data',
55 name='repo_list_data',
56 pattern='/_repos')
56 pattern='/_repos')
57
57
58 config.add_route(
58 config.add_route(
59 name='goto_switcher_data',
59 name='goto_switcher_data',
60 pattern='/_goto_data')
60 pattern='/_goto_data')
61
61
62 config.add_route(
62 config.add_route(
63 name='markup_preview',
63 name='markup_preview',
64 pattern='/_markup_preview')
64 pattern='/_markup_preview')
65
65
66 config.add_route(
67 name='store_user_session_value',
68 pattern='/_store_session_attr')
69
66 # register our static links via redirection mechanism
70 # register our static links via redirection mechanism
67 routing_links.connect_redirection_links(config)
71 routing_links.connect_redirection_links(config)
68
72
69 # Scan module for configuration decorators.
73 # Scan module for configuration decorators.
70 config.scan('.views', ignore='.tests')
74 config.scan('.views', ignore='.tests')
71
75
72 config.add_route_predicate(
76 config.add_route_predicate(
73 'skip_vcs_call', VCSCallPredicate)
77 'skip_vcs_call', VCSCallPredicate)
@@ -1,446 +1,461 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-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 re
21 import re
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26
26
27 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.lib import helpers as h
28 from rhodecode.lib import helpers as h
29 from rhodecode.lib.auth import (
29 from rhodecode.lib.auth import (
30 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator,
30 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator,
31 CSRFRequired)
31 CSRFRequired)
32 from rhodecode.lib.index import searcher_from_config
32 from rhodecode.lib.index import searcher_from_config
33 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
33 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
34 from rhodecode.lib.ext_json import json
34 from rhodecode.lib.ext_json import json
35 from rhodecode.model.db import (
35 from rhodecode.model.db import (
36 func, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup)
36 func, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup)
37 from rhodecode.model.repo import RepoModel
37 from rhodecode.model.repo import RepoModel
38 from rhodecode.model.repo_group import RepoGroupModel
38 from rhodecode.model.repo_group import RepoGroupModel
39 from rhodecode.model.scm import RepoGroupList, RepoList
39 from rhodecode.model.scm import RepoGroupList, RepoList
40 from rhodecode.model.user import UserModel
40 from rhodecode.model.user import UserModel
41 from rhodecode.model.user_group import UserGroupModel
41 from rhodecode.model.user_group import UserGroupModel
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 class HomeView(BaseAppView):
46 class HomeView(BaseAppView):
47
47
48 def load_default_context(self):
48 def load_default_context(self):
49 c = self._get_local_tmpl_context()
49 c = self._get_local_tmpl_context()
50 c.user = c.auth_user.get_instance()
50 c.user = c.auth_user.get_instance()
51
51
52 return c
52 return c
53
53
54 @LoginRequired()
54 @LoginRequired()
55 @view_config(
55 @view_config(
56 route_name='user_autocomplete_data', request_method='GET',
56 route_name='user_autocomplete_data', request_method='GET',
57 renderer='json_ext', xhr=True)
57 renderer='json_ext', xhr=True)
58 def user_autocomplete_data(self):
58 def user_autocomplete_data(self):
59 self.load_default_context()
59 self.load_default_context()
60 query = self.request.GET.get('query')
60 query = self.request.GET.get('query')
61 active = str2bool(self.request.GET.get('active') or True)
61 active = str2bool(self.request.GET.get('active') or True)
62 include_groups = str2bool(self.request.GET.get('user_groups'))
62 include_groups = str2bool(self.request.GET.get('user_groups'))
63 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
63 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
64 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
64 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
65
65
66 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
66 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
67 query, active, include_groups)
67 query, active, include_groups)
68
68
69 _users = UserModel().get_users(
69 _users = UserModel().get_users(
70 name_contains=query, only_active=active)
70 name_contains=query, only_active=active)
71
71
72 def maybe_skip_default_user(usr):
72 def maybe_skip_default_user(usr):
73 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
73 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
74 return False
74 return False
75 return True
75 return True
76 _users = filter(maybe_skip_default_user, _users)
76 _users = filter(maybe_skip_default_user, _users)
77
77
78 if include_groups:
78 if include_groups:
79 # extend with user groups
79 # extend with user groups
80 _user_groups = UserGroupModel().get_user_groups(
80 _user_groups = UserGroupModel().get_user_groups(
81 name_contains=query, only_active=active,
81 name_contains=query, only_active=active,
82 expand_groups=expand_groups)
82 expand_groups=expand_groups)
83 _users = _users + _user_groups
83 _users = _users + _user_groups
84
84
85 return {'suggestions': _users}
85 return {'suggestions': _users}
86
86
87 @LoginRequired()
87 @LoginRequired()
88 @NotAnonymous()
88 @NotAnonymous()
89 @view_config(
89 @view_config(
90 route_name='user_group_autocomplete_data', request_method='GET',
90 route_name='user_group_autocomplete_data', request_method='GET',
91 renderer='json_ext', xhr=True)
91 renderer='json_ext', xhr=True)
92 def user_group_autocomplete_data(self):
92 def user_group_autocomplete_data(self):
93 self.load_default_context()
93 self.load_default_context()
94 query = self.request.GET.get('query')
94 query = self.request.GET.get('query')
95 active = str2bool(self.request.GET.get('active') or True)
95 active = str2bool(self.request.GET.get('active') or True)
96 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
96 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
97
97
98 log.debug('generating user group list, query:%s, active:%s',
98 log.debug('generating user group list, query:%s, active:%s',
99 query, active)
99 query, active)
100
100
101 _user_groups = UserGroupModel().get_user_groups(
101 _user_groups = UserGroupModel().get_user_groups(
102 name_contains=query, only_active=active,
102 name_contains=query, only_active=active,
103 expand_groups=expand_groups)
103 expand_groups=expand_groups)
104 _user_groups = _user_groups
104 _user_groups = _user_groups
105
105
106 return {'suggestions': _user_groups}
106 return {'suggestions': _user_groups}
107
107
108 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
108 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
109 org_query = name_contains
109 org_query = name_contains
110 allowed_ids = self._rhodecode_user.repo_acl_ids(
110 allowed_ids = self._rhodecode_user.repo_acl_ids(
111 ['repository.read', 'repository.write', 'repository.admin'],
111 ['repository.read', 'repository.write', 'repository.admin'],
112 cache=False, name_filter=name_contains) or [-1]
112 cache=False, name_filter=name_contains) or [-1]
113
113
114 query = Repository.query()\
114 query = Repository.query()\
115 .order_by(func.length(Repository.repo_name))\
115 .order_by(func.length(Repository.repo_name))\
116 .order_by(Repository.repo_name)\
116 .order_by(Repository.repo_name)\
117 .filter(or_(
117 .filter(or_(
118 # generate multiple IN to fix limitation problems
118 # generate multiple IN to fix limitation problems
119 *in_filter_generator(Repository.repo_id, allowed_ids)
119 *in_filter_generator(Repository.repo_id, allowed_ids)
120 ))
120 ))
121
121
122 if repo_type:
122 if repo_type:
123 query = query.filter(Repository.repo_type == repo_type)
123 query = query.filter(Repository.repo_type == repo_type)
124
124
125 if name_contains:
125 if name_contains:
126 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
126 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
127 query = query.filter(
127 query = query.filter(
128 Repository.repo_name.ilike(ilike_expression))
128 Repository.repo_name.ilike(ilike_expression))
129 query = query.limit(limit)
129 query = query.limit(limit)
130
130
131 acl_iter = query
131 acl_iter = query
132
132
133 return [
133 return [
134 {
134 {
135 'id': obj.repo_name,
135 'id': obj.repo_name,
136 'value': org_query,
136 'value': org_query,
137 'value_display': obj.repo_name,
137 'value_display': obj.repo_name,
138 'text': obj.repo_name,
138 'text': obj.repo_name,
139 'type': 'repo',
139 'type': 'repo',
140 'repo_id': obj.repo_id,
140 'repo_id': obj.repo_id,
141 'repo_type': obj.repo_type,
141 'repo_type': obj.repo_type,
142 'private': obj.private,
142 'private': obj.private,
143 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
143 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
144 }
144 }
145 for obj in acl_iter]
145 for obj in acl_iter]
146
146
147 def _get_repo_group_list(self, name_contains=None, limit=20):
147 def _get_repo_group_list(self, name_contains=None, limit=20):
148 org_query = name_contains
148 org_query = name_contains
149 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
149 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
150 ['group.read', 'group.write', 'group.admin'],
150 ['group.read', 'group.write', 'group.admin'],
151 cache=False, name_filter=name_contains) or [-1]
151 cache=False, name_filter=name_contains) or [-1]
152
152
153 query = RepoGroup.query()\
153 query = RepoGroup.query()\
154 .order_by(func.length(RepoGroup.group_name))\
154 .order_by(func.length(RepoGroup.group_name))\
155 .order_by(RepoGroup.group_name) \
155 .order_by(RepoGroup.group_name) \
156 .filter(or_(
156 .filter(or_(
157 # generate multiple IN to fix limitation problems
157 # generate multiple IN to fix limitation problems
158 *in_filter_generator(RepoGroup.group_id, allowed_ids)
158 *in_filter_generator(RepoGroup.group_id, allowed_ids)
159 ))
159 ))
160
160
161 if name_contains:
161 if name_contains:
162 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
162 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
163 query = query.filter(
163 query = query.filter(
164 RepoGroup.group_name.ilike(ilike_expression))
164 RepoGroup.group_name.ilike(ilike_expression))
165 query = query.limit(limit)
165 query = query.limit(limit)
166
166
167 acl_iter = query
167 acl_iter = query
168
168
169 return [
169 return [
170 {
170 {
171 'id': obj.group_name,
171 'id': obj.group_name,
172 'value': org_query,
172 'value': org_query,
173 'value_display': obj.group_name,
173 'value_display': obj.group_name,
174 'type': 'repo_group',
174 'type': 'repo_group',
175 'url': h.route_path(
175 'url': h.route_path(
176 'repo_group_home', repo_group_name=obj.group_name)
176 'repo_group_home', repo_group_name=obj.group_name)
177 }
177 }
178 for obj in acl_iter]
178 for obj in acl_iter]
179
179
180 def _get_user_list(self, name_contains=None, limit=20):
180 def _get_user_list(self, name_contains=None, limit=20):
181 org_query = name_contains
181 org_query = name_contains
182 if not name_contains:
182 if not name_contains:
183 return []
183 return []
184
184
185 name_contains = re.compile('(?:user:)(.+)').findall(name_contains)
185 name_contains = re.compile('(?:user:)(.+)').findall(name_contains)
186 if len(name_contains) != 1:
186 if len(name_contains) != 1:
187 return []
187 return []
188 name_contains = name_contains[0]
188 name_contains = name_contains[0]
189
189
190 query = User.query()\
190 query = User.query()\
191 .order_by(func.length(User.username))\
191 .order_by(func.length(User.username))\
192 .order_by(User.username) \
192 .order_by(User.username) \
193 .filter(User.username != User.DEFAULT_USER)
193 .filter(User.username != User.DEFAULT_USER)
194
194
195 if name_contains:
195 if name_contains:
196 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
196 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
197 query = query.filter(
197 query = query.filter(
198 User.username.ilike(ilike_expression))
198 User.username.ilike(ilike_expression))
199 query = query.limit(limit)
199 query = query.limit(limit)
200
200
201 acl_iter = query
201 acl_iter = query
202
202
203 return [
203 return [
204 {
204 {
205 'id': obj.user_id,
205 'id': obj.user_id,
206 'value': org_query,
206 'value': org_query,
207 'value_display': obj.username,
207 'value_display': obj.username,
208 'type': 'user',
208 'type': 'user',
209 'icon_link': h.gravatar_url(obj.email, 30),
209 'icon_link': h.gravatar_url(obj.email, 30),
210 'url': h.route_path(
210 'url': h.route_path(
211 'user_profile', username=obj.username)
211 'user_profile', username=obj.username)
212 }
212 }
213 for obj in acl_iter]
213 for obj in acl_iter]
214
214
215 def _get_user_groups_list(self, name_contains=None, limit=20):
215 def _get_user_groups_list(self, name_contains=None, limit=20):
216 org_query = name_contains
216 org_query = name_contains
217 if not name_contains:
217 if not name_contains:
218 return []
218 return []
219
219
220 name_contains = re.compile('(?:user_group:)(.+)').findall(name_contains)
220 name_contains = re.compile('(?:user_group:)(.+)').findall(name_contains)
221 if len(name_contains) != 1:
221 if len(name_contains) != 1:
222 return []
222 return []
223 name_contains = name_contains[0]
223 name_contains = name_contains[0]
224
224
225 query = UserGroup.query()\
225 query = UserGroup.query()\
226 .order_by(func.length(UserGroup.users_group_name))\
226 .order_by(func.length(UserGroup.users_group_name))\
227 .order_by(UserGroup.users_group_name)
227 .order_by(UserGroup.users_group_name)
228
228
229 if name_contains:
229 if name_contains:
230 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
230 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
231 query = query.filter(
231 query = query.filter(
232 UserGroup.users_group_name.ilike(ilike_expression))
232 UserGroup.users_group_name.ilike(ilike_expression))
233 query = query.limit(limit)
233 query = query.limit(limit)
234
234
235 acl_iter = query
235 acl_iter = query
236
236
237 return [
237 return [
238 {
238 {
239 'id': obj.users_group_id,
239 'id': obj.users_group_id,
240 'value': org_query,
240 'value': org_query,
241 'value_display': obj.users_group_name,
241 'value_display': obj.users_group_name,
242 'type': 'user_group',
242 'type': 'user_group',
243 'url': h.route_path(
243 'url': h.route_path(
244 'user_group_profile', user_group_name=obj.users_group_name)
244 'user_group_profile', user_group_name=obj.users_group_name)
245 }
245 }
246 for obj in acl_iter]
246 for obj in acl_iter]
247
247
248 def _get_hash_commit_list(self, auth_user, query):
248 def _get_hash_commit_list(self, auth_user, query):
249 org_query = query
249 org_query = query
250 if not query or len(query) < 3:
250 if not query or len(query) < 3:
251 return []
251 return []
252
252
253 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
253 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
254
254
255 if len(commit_hashes) != 1:
255 if len(commit_hashes) != 1:
256 return []
256 return []
257 commit_hash = commit_hashes[0]
257 commit_hash = commit_hashes[0]
258
258
259 searcher = searcher_from_config(self.request.registry.settings)
259 searcher = searcher_from_config(self.request.registry.settings)
260 result = searcher.search(
260 result = searcher.search(
261 'commit_id:%s*' % commit_hash, 'commit', auth_user,
261 'commit_id:%s*' % commit_hash, 'commit', auth_user,
262 raise_on_exc=False)
262 raise_on_exc=False)
263
263
264 return [
264 return [
265 {
265 {
266 'id': entry['commit_id'],
266 'id': entry['commit_id'],
267 'value': org_query,
267 'value': org_query,
268 'value_display': 'repo `{}` commit: {}'.format(
268 'value_display': 'repo `{}` commit: {}'.format(
269 entry['repository'], entry['commit_id']),
269 entry['repository'], entry['commit_id']),
270 'type': 'commit',
270 'type': 'commit',
271 'repo': entry['repository'],
271 'repo': entry['repository'],
272 'url': h.route_path(
272 'url': h.route_path(
273 'repo_commit',
273 'repo_commit',
274 repo_name=entry['repository'], commit_id=entry['commit_id'])
274 repo_name=entry['repository'], commit_id=entry['commit_id'])
275 }
275 }
276 for entry in result['results']]
276 for entry in result['results']]
277
277
278 @LoginRequired()
278 @LoginRequired()
279 @view_config(
279 @view_config(
280 route_name='repo_list_data', request_method='GET',
280 route_name='repo_list_data', request_method='GET',
281 renderer='json_ext', xhr=True)
281 renderer='json_ext', xhr=True)
282 def repo_list_data(self):
282 def repo_list_data(self):
283 _ = self.request.translate
283 _ = self.request.translate
284 self.load_default_context()
284 self.load_default_context()
285
285
286 query = self.request.GET.get('query')
286 query = self.request.GET.get('query')
287 repo_type = self.request.GET.get('repo_type')
287 repo_type = self.request.GET.get('repo_type')
288 log.debug('generating repo list, query:%s, repo_type:%s',
288 log.debug('generating repo list, query:%s, repo_type:%s',
289 query, repo_type)
289 query, repo_type)
290
290
291 res = []
291 res = []
292 repos = self._get_repo_list(query, repo_type=repo_type)
292 repos = self._get_repo_list(query, repo_type=repo_type)
293 if repos:
293 if repos:
294 res.append({
294 res.append({
295 'text': _('Repositories'),
295 'text': _('Repositories'),
296 'children': repos
296 'children': repos
297 })
297 })
298
298
299 data = {
299 data = {
300 'more': False,
300 'more': False,
301 'results': res
301 'results': res
302 }
302 }
303 return data
303 return data
304
304
305 @LoginRequired()
305 @LoginRequired()
306 @view_config(
306 @view_config(
307 route_name='goto_switcher_data', request_method='GET',
307 route_name='goto_switcher_data', request_method='GET',
308 renderer='json_ext', xhr=True)
308 renderer='json_ext', xhr=True)
309 def goto_switcher_data(self):
309 def goto_switcher_data(self):
310 c = self.load_default_context()
310 c = self.load_default_context()
311
311
312 _ = self.request.translate
312 _ = self.request.translate
313
313
314 query = self.request.GET.get('query')
314 query = self.request.GET.get('query')
315 log.debug('generating main filter data, query %s', query)
315 log.debug('generating main filter data, query %s', query)
316
316
317 default_search_val = u'Full text search for: `{}`'.format(query)
317 default_search_val = u'Full text search for: `{}`'.format(query)
318 res = []
318 res = []
319 if not query:
319 if not query:
320 return {'suggestions': res}
320 return {'suggestions': res}
321
321
322 res.append({
322 res.append({
323 'id': -1,
323 'id': -1,
324 'value': query,
324 'value': query,
325 'value_display': default_search_val,
325 'value_display': default_search_val,
326 'type': 'search',
326 'type': 'search',
327 'url': h.route_path(
327 'url': h.route_path(
328 'search', _query={'q': query})
328 'search', _query={'q': query})
329 })
329 })
330 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
330 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
331 if repo_group_id:
331 if repo_group_id:
332 repo_group = RepoGroup.get(repo_group_id)
332 repo_group = RepoGroup.get(repo_group_id)
333 composed_hint = '{}/{}'.format(repo_group.group_name, query)
333 composed_hint = '{}/{}'.format(repo_group.group_name, query)
334 show_hint = not query.startswith(repo_group.group_name)
334 show_hint = not query.startswith(repo_group.group_name)
335 if repo_group and show_hint:
335 if repo_group and show_hint:
336 hint = u'Group search: `{}`'.format(composed_hint)
336 hint = u'Group search: `{}`'.format(composed_hint)
337 res.append({
337 res.append({
338 'id': -1,
338 'id': -1,
339 'value': composed_hint,
339 'value': composed_hint,
340 'value_display': hint,
340 'value_display': hint,
341 'type': 'hint',
341 'type': 'hint',
342 'url': ""
342 'url': ""
343 })
343 })
344
344
345 repo_groups = self._get_repo_group_list(query)
345 repo_groups = self._get_repo_group_list(query)
346 for serialized_repo_group in repo_groups:
346 for serialized_repo_group in repo_groups:
347 res.append(serialized_repo_group)
347 res.append(serialized_repo_group)
348
348
349 repos = self._get_repo_list(query)
349 repos = self._get_repo_list(query)
350 for serialized_repo in repos:
350 for serialized_repo in repos:
351 res.append(serialized_repo)
351 res.append(serialized_repo)
352
352
353 # TODO(marcink): permissions for that ?
353 # TODO(marcink): permissions for that ?
354 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
354 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
355 if allowed_user_search:
355 if allowed_user_search:
356 users = self._get_user_list(query)
356 users = self._get_user_list(query)
357 for serialized_user in users:
357 for serialized_user in users:
358 res.append(serialized_user)
358 res.append(serialized_user)
359
359
360 user_groups = self._get_user_groups_list(query)
360 user_groups = self._get_user_groups_list(query)
361 for serialized_user_group in user_groups:
361 for serialized_user_group in user_groups:
362 res.append(serialized_user_group)
362 res.append(serialized_user_group)
363
363
364 commits = self._get_hash_commit_list(c.auth_user, query)
364 commits = self._get_hash_commit_list(c.auth_user, query)
365 if commits:
365 if commits:
366 unique_repos = collections.OrderedDict()
366 unique_repos = collections.OrderedDict()
367 for commit in commits:
367 for commit in commits:
368 repo_name = commit['repo']
368 repo_name = commit['repo']
369 unique_repos.setdefault(repo_name, []).append(commit)
369 unique_repos.setdefault(repo_name, []).append(commit)
370
370
371 for repo, commits in unique_repos.items():
371 for repo, commits in unique_repos.items():
372 for commit in commits:
372 for commit in commits:
373 res.append(commit)
373 res.append(commit)
374
374
375 return {'suggestions': res}
375 return {'suggestions': res}
376
376
377 def _get_groups_and_repos(self, repo_group_id=None):
377 def _get_groups_and_repos(self, repo_group_id=None):
378 # repo groups groups
378 # repo groups groups
379 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
379 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
380 _perms = ['group.read', 'group.write', 'group.admin']
380 _perms = ['group.read', 'group.write', 'group.admin']
381 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
381 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
382 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
382 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
383 repo_group_list=repo_group_list_acl, admin=False)
383 repo_group_list=repo_group_list_acl, admin=False)
384
384
385 # repositories
385 # repositories
386 repo_list = Repository.get_all_repos(group_id=repo_group_id)
386 repo_list = Repository.get_all_repos(group_id=repo_group_id)
387 _perms = ['repository.read', 'repository.write', 'repository.admin']
387 _perms = ['repository.read', 'repository.write', 'repository.admin']
388 repo_list_acl = RepoList(repo_list, perm_set=_perms)
388 repo_list_acl = RepoList(repo_list, perm_set=_perms)
389 repo_data = RepoModel().get_repos_as_dict(
389 repo_data = RepoModel().get_repos_as_dict(
390 repo_list=repo_list_acl, admin=False)
390 repo_list=repo_list_acl, admin=False)
391
391
392 return repo_data, repo_group_data
392 return repo_data, repo_group_data
393
393
394 @LoginRequired()
394 @LoginRequired()
395 @view_config(
395 @view_config(
396 route_name='home', request_method='GET',
396 route_name='home', request_method='GET',
397 renderer='rhodecode:templates/index.mako')
397 renderer='rhodecode:templates/index.mako')
398 def main_page(self):
398 def main_page(self):
399 c = self.load_default_context()
399 c = self.load_default_context()
400 c.repo_group = None
400 c.repo_group = None
401
401
402 repo_data, repo_group_data = self._get_groups_and_repos()
402 repo_data, repo_group_data = self._get_groups_and_repos()
403 # json used to render the grids
403 # json used to render the grids
404 c.repos_data = json.dumps(repo_data)
404 c.repos_data = json.dumps(repo_data)
405 c.repo_groups_data = json.dumps(repo_group_data)
405 c.repo_groups_data = json.dumps(repo_group_data)
406
406
407 return self._get_template_context(c)
407 return self._get_template_context(c)
408
408
409 @LoginRequired()
409 @LoginRequired()
410 @HasRepoGroupPermissionAnyDecorator(
410 @HasRepoGroupPermissionAnyDecorator(
411 'group.read', 'group.write', 'group.admin')
411 'group.read', 'group.write', 'group.admin')
412 @view_config(
412 @view_config(
413 route_name='repo_group_home', request_method='GET',
413 route_name='repo_group_home', request_method='GET',
414 renderer='rhodecode:templates/index_repo_group.mako')
414 renderer='rhodecode:templates/index_repo_group.mako')
415 @view_config(
415 @view_config(
416 route_name='repo_group_home_slash', request_method='GET',
416 route_name='repo_group_home_slash', request_method='GET',
417 renderer='rhodecode:templates/index_repo_group.mako')
417 renderer='rhodecode:templates/index_repo_group.mako')
418 def repo_group_main_page(self):
418 def repo_group_main_page(self):
419 c = self.load_default_context()
419 c = self.load_default_context()
420 c.repo_group = self.request.db_repo_group
420 c.repo_group = self.request.db_repo_group
421 repo_data, repo_group_data = self._get_groups_and_repos(
421 repo_data, repo_group_data = self._get_groups_and_repos(
422 c.repo_group.group_id)
422 c.repo_group.group_id)
423
423
424 # json used to render the grids
424 # json used to render the grids
425 c.repos_data = json.dumps(repo_data)
425 c.repos_data = json.dumps(repo_data)
426 c.repo_groups_data = json.dumps(repo_group_data)
426 c.repo_groups_data = json.dumps(repo_group_data)
427
427
428 return self._get_template_context(c)
428 return self._get_template_context(c)
429
429
430 @LoginRequired()
430 @LoginRequired()
431 @CSRFRequired()
431 @CSRFRequired()
432 @view_config(
432 @view_config(
433 route_name='markup_preview', request_method='POST',
433 route_name='markup_preview', request_method='POST',
434 renderer='string', xhr=True)
434 renderer='string', xhr=True)
435 def markup_preview(self):
435 def markup_preview(self):
436 # Technically a CSRF token is not needed as no state changes with this
436 # Technically a CSRF token is not needed as no state changes with this
437 # call. However, as this is a POST is better to have it, so automated
437 # call. However, as this is a POST is better to have it, so automated
438 # tools don't flag it as potential CSRF.
438 # tools don't flag it as potential CSRF.
439 # Post is required because the payload could be bigger than the maximum
439 # Post is required because the payload could be bigger than the maximum
440 # allowed by GET.
440 # allowed by GET.
441
441
442 text = self.request.POST.get('text')
442 text = self.request.POST.get('text')
443 renderer = self.request.POST.get('renderer') or 'rst'
443 renderer = self.request.POST.get('renderer') or 'rst'
444 if text:
444 if text:
445 return h.render(text, renderer=renderer, mentions=True)
445 return h.render(text, renderer=renderer, mentions=True)
446 return ''
446 return ''
447
448 @LoginRequired()
449 @CSRFRequired()
450 @view_config(
451 route_name='store_user_session_value', request_method='POST',
452 renderer='string', xhr=True)
453 def store_user_session_attr(self):
454 key = self.request.POST.get('key')
455 val = self.request.POST.get('val')
456
457 existing_value = self.request.session.get(key)
458 if existing_value != val:
459 self.request.session[key] = val
460
461 return 'stored:{}'.format(key)
@@ -1,550 +1,561 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, sha1, aslist, safe_str)
45 str2bool, safe_unicode, AttributeDict, safe_int, sha1, 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, check_branch_perms=False, detect_force_push=False):
156 is_shadow_repo=False, check_branch_perms=False, detect_force_push=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 # NOTE(marcink): This should be also in sync with
182 # NOTE(marcink): This should be also in sync with
183 # rhodecode/apps/ssh_support/lib/backends/base.py:update_enviroment scm_data
183 # rhodecode/apps/ssh_support/lib/backends/base.py:update_enviroment scm_data
184 scm_data = {
184 scm_data = {
185 'ip': get_ip_addr(environ),
185 'ip': get_ip_addr(environ),
186 'username': username,
186 'username': username,
187 'user_id': user_id,
187 'user_id': user_id,
188 'action': action,
188 'action': action,
189 'repository': repo_name,
189 'repository': repo_name,
190 'scm': scm,
190 'scm': scm,
191 'config': rhodecode.CONFIG['__file__'],
191 'config': rhodecode.CONFIG['__file__'],
192 'make_lock': make_lock,
192 'make_lock': make_lock,
193 'locked_by': locked_by,
193 'locked_by': locked_by,
194 'server_url': utils2.get_server_url(environ),
194 'server_url': utils2.get_server_url(environ),
195 'user_agent': get_user_agent(environ),
195 'user_agent': get_user_agent(environ),
196 'hooks': get_enabled_hook_classes(ui_settings),
196 'hooks': get_enabled_hook_classes(ui_settings),
197 'is_shadow_repo': is_shadow_repo,
197 'is_shadow_repo': is_shadow_repo,
198 'detect_force_push': detect_force_push,
198 'detect_force_push': detect_force_push,
199 'check_branch_perms': check_branch_perms,
199 'check_branch_perms': check_branch_perms,
200 }
200 }
201 return scm_data
201 return scm_data
202
202
203
203
204 class BasicAuth(AuthBasicAuthenticator):
204 class BasicAuth(AuthBasicAuthenticator):
205
205
206 def __init__(self, realm, authfunc, registry, auth_http_code=None,
206 def __init__(self, realm, authfunc, registry, auth_http_code=None,
207 initial_call_detection=False, acl_repo_name=None):
207 initial_call_detection=False, acl_repo_name=None):
208 self.realm = realm
208 self.realm = realm
209 self.initial_call = initial_call_detection
209 self.initial_call = initial_call_detection
210 self.authfunc = authfunc
210 self.authfunc = authfunc
211 self.registry = registry
211 self.registry = registry
212 self.acl_repo_name = acl_repo_name
212 self.acl_repo_name = acl_repo_name
213 self._rc_auth_http_code = auth_http_code
213 self._rc_auth_http_code = auth_http_code
214
214
215 def _get_response_from_code(self, http_code):
215 def _get_response_from_code(self, http_code):
216 try:
216 try:
217 return get_exception(safe_int(http_code))
217 return get_exception(safe_int(http_code))
218 except Exception:
218 except Exception:
219 log.exception('Failed to fetch response for code %s', http_code)
219 log.exception('Failed to fetch response for code %s', http_code)
220 return HTTPForbidden
220 return HTTPForbidden
221
221
222 def get_rc_realm(self):
222 def get_rc_realm(self):
223 return safe_str(self.registry.rhodecode_settings.get('rhodecode_realm'))
223 return safe_str(self.registry.rhodecode_settings.get('rhodecode_realm'))
224
224
225 def build_authentication(self):
225 def build_authentication(self):
226 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
226 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
227 if self._rc_auth_http_code and not self.initial_call:
227 if self._rc_auth_http_code and not self.initial_call:
228 # return alternative HTTP code if alternative http return code
228 # return alternative HTTP code if alternative http return code
229 # is specified in RhodeCode config, but ONLY if it's not the
229 # is specified in RhodeCode config, but ONLY if it's not the
230 # FIRST call
230 # FIRST call
231 custom_response_klass = self._get_response_from_code(
231 custom_response_klass = self._get_response_from_code(
232 self._rc_auth_http_code)
232 self._rc_auth_http_code)
233 return custom_response_klass(headers=head)
233 return custom_response_klass(headers=head)
234 return HTTPUnauthorized(headers=head)
234 return HTTPUnauthorized(headers=head)
235
235
236 def authenticate(self, environ):
236 def authenticate(self, environ):
237 authorization = AUTHORIZATION(environ)
237 authorization = AUTHORIZATION(environ)
238 if not authorization:
238 if not authorization:
239 return self.build_authentication()
239 return self.build_authentication()
240 (authmeth, auth) = authorization.split(' ', 1)
240 (authmeth, auth) = authorization.split(' ', 1)
241 if 'basic' != authmeth.lower():
241 if 'basic' != authmeth.lower():
242 return self.build_authentication()
242 return self.build_authentication()
243 auth = auth.strip().decode('base64')
243 auth = auth.strip().decode('base64')
244 _parts = auth.split(':', 1)
244 _parts = auth.split(':', 1)
245 if len(_parts) == 2:
245 if len(_parts) == 2:
246 username, password = _parts
246 username, password = _parts
247 auth_data = self.authfunc(
247 auth_data = self.authfunc(
248 username, password, environ, VCS_TYPE,
248 username, password, environ, VCS_TYPE,
249 registry=self.registry, acl_repo_name=self.acl_repo_name)
249 registry=self.registry, acl_repo_name=self.acl_repo_name)
250 if auth_data:
250 if auth_data:
251 return {'username': username, 'auth_data': auth_data}
251 return {'username': username, 'auth_data': auth_data}
252 if username and password:
252 if username and password:
253 # we mark that we actually executed authentication once, at
253 # we mark that we actually executed authentication once, at
254 # that point we can use the alternative auth code
254 # that point we can use the alternative auth code
255 self.initial_call = False
255 self.initial_call = False
256
256
257 return self.build_authentication()
257 return self.build_authentication()
258
258
259 __call__ = authenticate
259 __call__ = authenticate
260
260
261
261
262 def calculate_version_hash(config):
262 def calculate_version_hash(config):
263 return sha1(
263 return sha1(
264 config.get('beaker.session.secret', '') +
264 config.get('beaker.session.secret', '') +
265 rhodecode.__version__)[:8]
265 rhodecode.__version__)[:8]
266
266
267
267
268 def get_current_lang(request):
268 def get_current_lang(request):
269 # NOTE(marcink): remove after pyramid move
269 # NOTE(marcink): remove after pyramid move
270 try:
270 try:
271 return translation.get_lang()[0]
271 return translation.get_lang()[0]
272 except:
272 except:
273 pass
273 pass
274
274
275 return getattr(request, '_LOCALE_', request.locale_name)
275 return getattr(request, '_LOCALE_', request.locale_name)
276
276
277
277
278 def attach_context_attributes(context, request, user_id):
278 def attach_context_attributes(context, request, user_id):
279 """
279 """
280 Attach variables into template context called `c`.
280 Attach variables into template context called `c`.
281 """
281 """
282 config = request.registry.settings
282 config = request.registry.settings
283
283
284
284
285 rc_config = SettingsModel().get_all_settings(cache=True)
285 rc_config = SettingsModel().get_all_settings(cache=True)
286
286
287 context.rhodecode_version = rhodecode.__version__
287 context.rhodecode_version = rhodecode.__version__
288 context.rhodecode_edition = config.get('rhodecode.edition')
288 context.rhodecode_edition = config.get('rhodecode.edition')
289 # unique secret + version does not leak the version but keep consistency
289 # unique secret + version does not leak the version but keep consistency
290 context.rhodecode_version_hash = calculate_version_hash(config)
290 context.rhodecode_version_hash = calculate_version_hash(config)
291
291
292 # Default language set for the incoming request
292 # Default language set for the incoming request
293 context.language = get_current_lang(request)
293 context.language = get_current_lang(request)
294
294
295 # Visual options
295 # Visual options
296 context.visual = AttributeDict({})
296 context.visual = AttributeDict({})
297
297
298 # DB stored Visual Items
298 # DB stored Visual Items
299 context.visual.show_public_icon = str2bool(
299 context.visual.show_public_icon = str2bool(
300 rc_config.get('rhodecode_show_public_icon'))
300 rc_config.get('rhodecode_show_public_icon'))
301 context.visual.show_private_icon = str2bool(
301 context.visual.show_private_icon = str2bool(
302 rc_config.get('rhodecode_show_private_icon'))
302 rc_config.get('rhodecode_show_private_icon'))
303 context.visual.stylify_metatags = str2bool(
303 context.visual.stylify_metatags = str2bool(
304 rc_config.get('rhodecode_stylify_metatags'))
304 rc_config.get('rhodecode_stylify_metatags'))
305 context.visual.dashboard_items = safe_int(
305 context.visual.dashboard_items = safe_int(
306 rc_config.get('rhodecode_dashboard_items', 100))
306 rc_config.get('rhodecode_dashboard_items', 100))
307 context.visual.admin_grid_items = safe_int(
307 context.visual.admin_grid_items = safe_int(
308 rc_config.get('rhodecode_admin_grid_items', 100))
308 rc_config.get('rhodecode_admin_grid_items', 100))
309 context.visual.repository_fields = str2bool(
309 context.visual.repository_fields = str2bool(
310 rc_config.get('rhodecode_repository_fields'))
310 rc_config.get('rhodecode_repository_fields'))
311 context.visual.show_version = str2bool(
311 context.visual.show_version = str2bool(
312 rc_config.get('rhodecode_show_version'))
312 rc_config.get('rhodecode_show_version'))
313 context.visual.use_gravatar = str2bool(
313 context.visual.use_gravatar = str2bool(
314 rc_config.get('rhodecode_use_gravatar'))
314 rc_config.get('rhodecode_use_gravatar'))
315 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
315 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
316 context.visual.default_renderer = rc_config.get(
316 context.visual.default_renderer = rc_config.get(
317 'rhodecode_markup_renderer', 'rst')
317 'rhodecode_markup_renderer', 'rst')
318 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
318 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
319 context.visual.rhodecode_support_url = \
319 context.visual.rhodecode_support_url = \
320 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
320 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
321
321
322 context.visual.affected_files_cut_off = 60
322 context.visual.affected_files_cut_off = 60
323
323
324 context.pre_code = rc_config.get('rhodecode_pre_code')
324 context.pre_code = rc_config.get('rhodecode_pre_code')
325 context.post_code = rc_config.get('rhodecode_post_code')
325 context.post_code = rc_config.get('rhodecode_post_code')
326 context.rhodecode_name = rc_config.get('rhodecode_title')
326 context.rhodecode_name = rc_config.get('rhodecode_title')
327 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
327 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
328 # if we have specified default_encoding in the request, it has more
328 # if we have specified default_encoding in the request, it has more
329 # priority
329 # priority
330 if request.GET.get('default_encoding'):
330 if request.GET.get('default_encoding'):
331 context.default_encodings.insert(0, request.GET.get('default_encoding'))
331 context.default_encodings.insert(0, request.GET.get('default_encoding'))
332 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
332 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
333 context.clone_uri_ssh_tmpl = rc_config.get('rhodecode_clone_uri_ssh_tmpl')
333 context.clone_uri_ssh_tmpl = rc_config.get('rhodecode_clone_uri_ssh_tmpl')
334
334
335 # INI stored
335 # INI stored
336 context.labs_active = str2bool(
336 context.labs_active = str2bool(
337 config.get('labs_settings_active', 'false'))
337 config.get('labs_settings_active', 'false'))
338 context.ssh_enabled = str2bool(
338 context.ssh_enabled = str2bool(
339 config.get('ssh.generate_authorized_keyfile', 'false'))
339 config.get('ssh.generate_authorized_keyfile', 'false'))
340
340
341 context.visual.allow_repo_location_change = str2bool(
341 context.visual.allow_repo_location_change = str2bool(
342 config.get('allow_repo_location_change', True))
342 config.get('allow_repo_location_change', True))
343 context.visual.allow_custom_hooks_settings = str2bool(
343 context.visual.allow_custom_hooks_settings = str2bool(
344 config.get('allow_custom_hooks_settings', True))
344 config.get('allow_custom_hooks_settings', True))
345 context.debug_style = str2bool(config.get('debug_style', False))
345 context.debug_style = str2bool(config.get('debug_style', False))
346
346
347 context.rhodecode_instanceid = config.get('instance_id')
347 context.rhodecode_instanceid = config.get('instance_id')
348
348
349 context.visual.cut_off_limit_diff = safe_int(
349 context.visual.cut_off_limit_diff = safe_int(
350 config.get('cut_off_limit_diff'))
350 config.get('cut_off_limit_diff'))
351 context.visual.cut_off_limit_file = safe_int(
351 context.visual.cut_off_limit_file = safe_int(
352 config.get('cut_off_limit_file'))
352 config.get('cut_off_limit_file'))
353
353
354 # AppEnlight
354 # AppEnlight
355 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
355 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
356 context.appenlight_api_public_key = config.get(
356 context.appenlight_api_public_key = config.get(
357 'appenlight.api_public_key', '')
357 'appenlight.api_public_key', '')
358 context.appenlight_server_url = config.get('appenlight.server_url', '')
358 context.appenlight_server_url = config.get('appenlight.server_url', '')
359
359
360 diffmode = {
361 "unified": "unified",
362 "sideside": "sideside"
363 }.get(request.GET.get('diffmode'))
364
365 if diffmode and diffmode != request.session.get('rc_user_session_attr.diffmode'):
366 request.session['rc_user_session_attr.diffmode'] = diffmode
367
368 # session settings per user
369 session_attrs = {
370 # defaults
371 "clone_url_format": "http",
372 "diffmode": "sideside"
373 }
374 for k, v in request.session.items():
375 pref = 'rc_user_session_attr.'
376 if k and k.startswith(pref):
377 k = k[len(pref):]
378 session_attrs[k] = v
379
380 context.user_session_attrs = session_attrs
381
360 # JS template context
382 # JS template context
361 context.template_context = {
383 context.template_context = {
362 'repo_name': None,
384 'repo_name': None,
363 'repo_type': None,
385 'repo_type': None,
364 'repo_landing_commit': None,
386 'repo_landing_commit': None,
365 'rhodecode_user': {
387 'rhodecode_user': {
366 'username': None,
388 'username': None,
367 'email': None,
389 'email': None,
368 'notification_status': False
390 'notification_status': False
369 },
391 },
392 'session_attrs': session_attrs,
370 'visual': {
393 'visual': {
371 'default_renderer': None
394 'default_renderer': None
372 },
395 },
373 'commit_data': {
396 'commit_data': {
374 'commit_id': None
397 'commit_id': None
375 },
398 },
376 'pull_request_data': {'pull_request_id': None},
399 'pull_request_data': {'pull_request_id': None},
377 'timeago': {
400 'timeago': {
378 'refresh_time': 120 * 1000,
401 'refresh_time': 120 * 1000,
379 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
402 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
380 },
403 },
381 'pyramid_dispatch': {
404 'pyramid_dispatch': {
382
405
383 },
406 },
384 'extra': {'plugins': {}}
407 'extra': {'plugins': {}}
385 }
408 }
386 # END CONFIG VARS
409 # END CONFIG VARS
387
410
388 diffmode = 'sideside'
389 if request.GET.get('diffmode'):
390 if request.GET['diffmode'] == 'unified':
391 diffmode = 'unified'
392 elif request.session.get('diffmode'):
393 diffmode = request.session['diffmode']
394
395 context.diffmode = diffmode
396
397 if request.session.get('diffmode') != diffmode:
398 request.session['diffmode'] = diffmode
399
400 context.csrf_token = auth.get_csrf_token(session=request.session)
411 context.csrf_token = auth.get_csrf_token(session=request.session)
401 context.backends = rhodecode.BACKENDS.keys()
412 context.backends = rhodecode.BACKENDS.keys()
402 context.backends.sort()
413 context.backends.sort()
403 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(user_id)
414 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(user_id)
404
415
405 # web case
416 # web case
406 if hasattr(request, 'user'):
417 if hasattr(request, 'user'):
407 context.auth_user = request.user
418 context.auth_user = request.user
408 context.rhodecode_user = request.user
419 context.rhodecode_user = request.user
409
420
410 # api case
421 # api case
411 if hasattr(request, 'rpc_user'):
422 if hasattr(request, 'rpc_user'):
412 context.auth_user = request.rpc_user
423 context.auth_user = request.rpc_user
413 context.rhodecode_user = request.rpc_user
424 context.rhodecode_user = request.rpc_user
414
425
415 # attach the whole call context to the request
426 # attach the whole call context to the request
416 request.call_context = context
427 request.call_context = context
417
428
418
429
419 def get_auth_user(request):
430 def get_auth_user(request):
420 environ = request.environ
431 environ = request.environ
421 session = request.session
432 session = request.session
422
433
423 ip_addr = get_ip_addr(environ)
434 ip_addr = get_ip_addr(environ)
424 # make sure that we update permissions each time we call controller
435 # make sure that we update permissions each time we call controller
425 _auth_token = (request.GET.get('auth_token', '') or
436 _auth_token = (request.GET.get('auth_token', '') or
426 request.GET.get('api_key', ''))
437 request.GET.get('api_key', ''))
427
438
428 if _auth_token:
439 if _auth_token:
429 # when using API_KEY we assume user exists, and
440 # when using API_KEY we assume user exists, and
430 # doesn't need auth based on cookies.
441 # doesn't need auth based on cookies.
431 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
442 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
432 authenticated = False
443 authenticated = False
433 else:
444 else:
434 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
445 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
435 try:
446 try:
436 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
447 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
437 ip_addr=ip_addr)
448 ip_addr=ip_addr)
438 except UserCreationError as e:
449 except UserCreationError as e:
439 h.flash(e, 'error')
450 h.flash(e, 'error')
440 # container auth or other auth functions that create users
451 # container auth or other auth functions that create users
441 # on the fly can throw this exception signaling that there's
452 # on the fly can throw this exception signaling that there's
442 # issue with user creation, explanation should be provided
453 # issue with user creation, explanation should be provided
443 # in Exception itself. We then create a simple blank
454 # in Exception itself. We then create a simple blank
444 # AuthUser
455 # AuthUser
445 auth_user = AuthUser(ip_addr=ip_addr)
456 auth_user = AuthUser(ip_addr=ip_addr)
446
457
447 # in case someone changes a password for user it triggers session
458 # in case someone changes a password for user it triggers session
448 # flush and forces a re-login
459 # flush and forces a re-login
449 if password_changed(auth_user, session):
460 if password_changed(auth_user, session):
450 session.invalidate()
461 session.invalidate()
451 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
462 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
452 auth_user = AuthUser(ip_addr=ip_addr)
463 auth_user = AuthUser(ip_addr=ip_addr)
453
464
454 authenticated = cookie_store.get('is_authenticated')
465 authenticated = cookie_store.get('is_authenticated')
455
466
456 if not auth_user.is_authenticated and auth_user.is_user_object:
467 if not auth_user.is_authenticated and auth_user.is_user_object:
457 # user is not authenticated and not empty
468 # user is not authenticated and not empty
458 auth_user.set_authenticated(authenticated)
469 auth_user.set_authenticated(authenticated)
459
470
460 return auth_user
471 return auth_user
461
472
462
473
463 def h_filter(s):
474 def h_filter(s):
464 """
475 """
465 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
476 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
466 we wrap this with additional functionality that converts None to empty
477 we wrap this with additional functionality that converts None to empty
467 strings
478 strings
468 """
479 """
469 if s is None:
480 if s is None:
470 return markupsafe.Markup()
481 return markupsafe.Markup()
471 return markupsafe.escape(s)
482 return markupsafe.escape(s)
472
483
473
484
474 def add_events_routes(config):
485 def add_events_routes(config):
475 """
486 """
476 Adds routing that can be used in events. Because some events are triggered
487 Adds routing that can be used in events. Because some events are triggered
477 outside of pyramid context, we need to bootstrap request with some
488 outside of pyramid context, we need to bootstrap request with some
478 routing registered
489 routing registered
479 """
490 """
480
491
481 from rhodecode.apps._base import ADMIN_PREFIX
492 from rhodecode.apps._base import ADMIN_PREFIX
482
493
483 config.add_route(name='home', pattern='/')
494 config.add_route(name='home', pattern='/')
484
495
485 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
496 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
486 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
497 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
487 config.add_route(name='repo_summary', pattern='/{repo_name}')
498 config.add_route(name='repo_summary', pattern='/{repo_name}')
488 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
499 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
489 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
500 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
490
501
491 config.add_route(name='pullrequest_show',
502 config.add_route(name='pullrequest_show',
492 pattern='/{repo_name}/pull-request/{pull_request_id}')
503 pattern='/{repo_name}/pull-request/{pull_request_id}')
493 config.add_route(name='pull_requests_global',
504 config.add_route(name='pull_requests_global',
494 pattern='/pull-request/{pull_request_id}')
505 pattern='/pull-request/{pull_request_id}')
495 config.add_route(name='repo_commit',
506 config.add_route(name='repo_commit',
496 pattern='/{repo_name}/changeset/{commit_id}')
507 pattern='/{repo_name}/changeset/{commit_id}')
497
508
498 config.add_route(name='repo_files',
509 config.add_route(name='repo_files',
499 pattern='/{repo_name}/files/{commit_id}/{f_path}')
510 pattern='/{repo_name}/files/{commit_id}/{f_path}')
500
511
501
512
502 def bootstrap_config(request):
513 def bootstrap_config(request):
503 import pyramid.testing
514 import pyramid.testing
504 registry = pyramid.testing.Registry('RcTestRegistry')
515 registry = pyramid.testing.Registry('RcTestRegistry')
505
516
506 config = pyramid.testing.setUp(registry=registry, request=request)
517 config = pyramid.testing.setUp(registry=registry, request=request)
507
518
508 # allow pyramid lookup in testing
519 # allow pyramid lookup in testing
509 config.include('pyramid_mako')
520 config.include('pyramid_mako')
510 config.include('pyramid_beaker')
521 config.include('pyramid_beaker')
511 config.include('rhodecode.lib.rc_cache')
522 config.include('rhodecode.lib.rc_cache')
512
523
513 add_events_routes(config)
524 add_events_routes(config)
514
525
515 return config
526 return config
516
527
517
528
518 def bootstrap_request(**kwargs):
529 def bootstrap_request(**kwargs):
519 import pyramid.testing
530 import pyramid.testing
520
531
521 class TestRequest(pyramid.testing.DummyRequest):
532 class TestRequest(pyramid.testing.DummyRequest):
522 application_url = kwargs.pop('application_url', 'http://example.com')
533 application_url = kwargs.pop('application_url', 'http://example.com')
523 host = kwargs.pop('host', 'example.com:80')
534 host = kwargs.pop('host', 'example.com:80')
524 domain = kwargs.pop('domain', 'example.com')
535 domain = kwargs.pop('domain', 'example.com')
525
536
526 def translate(self, msg):
537 def translate(self, msg):
527 return msg
538 return msg
528
539
529 def plularize(self, singular, plural, n):
540 def plularize(self, singular, plural, n):
530 return singular
541 return singular
531
542
532 def get_partial_renderer(self, tmpl_name):
543 def get_partial_renderer(self, tmpl_name):
533
544
534 from rhodecode.lib.partial_renderer import get_partial_renderer
545 from rhodecode.lib.partial_renderer import get_partial_renderer
535 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
546 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
536
547
537 _call_context = {}
548 _call_context = {}
538 @property
549 @property
539 def call_context(self):
550 def call_context(self):
540 return self._call_context
551 return self._call_context
541
552
542 class TestDummySession(pyramid.testing.DummySession):
553 class TestDummySession(pyramid.testing.DummySession):
543 def save(*arg, **kw):
554 def save(*arg, **kw):
544 pass
555 pass
545
556
546 request = TestRequest(**kwargs)
557 request = TestRequest(**kwargs)
547 request.session = TestDummySession()
558 request.session = TestDummySession()
548
559
549 return request
560 return request
550
561
@@ -1,334 +1,335 b''
1
1
2 /******************************************************************************
2 /******************************************************************************
3 * *
3 * *
4 * DO NOT CHANGE THIS FILE MANUALLY *
4 * DO NOT CHANGE THIS FILE MANUALLY *
5 * *
5 * *
6 * *
6 * *
7 * This file is automatically generated when the app starts up with *
7 * This file is automatically generated when the app starts up with *
8 * generate_js_files = true *
8 * generate_js_files = true *
9 * *
9 * *
10 * To add a route here pass jsroute=True to the route definition in the app *
10 * To add a route here pass jsroute=True to the route definition in the app *
11 * *
11 * *
12 ******************************************************************************/
12 ******************************************************************************/
13 function registerRCRoutes() {
13 function registerRCRoutes() {
14 // routes registration
14 // routes registration
15 pyroutes.register('favicon', '/favicon.ico', []);
15 pyroutes.register('favicon', '/favicon.ico', []);
16 pyroutes.register('robots', '/robots.txt', []);
16 pyroutes.register('robots', '/robots.txt', []);
17 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
17 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
18 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
18 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
19 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
19 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
27 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
27 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
33 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
33 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
34 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
34 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
35 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
35 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
36 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
36 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
37 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
37 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
38 pyroutes.register('admin_home', '/_admin', []);
38 pyroutes.register('admin_home', '/_admin', []);
39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
40 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
40 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
41 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
41 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
42 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
42 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
43 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
43 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
44 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
44 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
45 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
45 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
46 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
46 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
47 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
47 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
48 pyroutes.register('admin_settings_exception_tracker', '/_admin/settings/exceptions', []);
48 pyroutes.register('admin_settings_exception_tracker', '/_admin/settings/exceptions', []);
49 pyroutes.register('admin_settings_exception_tracker_delete_all', '/_admin/settings/exceptions/delete', []);
49 pyroutes.register('admin_settings_exception_tracker_delete_all', '/_admin/settings/exceptions/delete', []);
50 pyroutes.register('admin_settings_exception_tracker_show', '/_admin/settings/exceptions/%(exception_id)s', ['exception_id']);
50 pyroutes.register('admin_settings_exception_tracker_show', '/_admin/settings/exceptions/%(exception_id)s', ['exception_id']);
51 pyroutes.register('admin_settings_exception_tracker_delete', '/_admin/settings/exceptions/%(exception_id)s/delete', ['exception_id']);
51 pyroutes.register('admin_settings_exception_tracker_delete', '/_admin/settings/exceptions/%(exception_id)s/delete', ['exception_id']);
52 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
52 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
53 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
53 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
54 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
54 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
55 pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []);
55 pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []);
56 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
56 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
57 pyroutes.register('admin_settings_process_management_master_signal', '/_admin/settings/process_management/master_signal', []);
57 pyroutes.register('admin_settings_process_management_master_signal', '/_admin/settings/process_management/master_signal', []);
58 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
58 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
59 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
59 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
60 pyroutes.register('admin_settings', '/_admin/settings', []);
60 pyroutes.register('admin_settings', '/_admin/settings', []);
61 pyroutes.register('admin_settings_update', '/_admin/settings/update', []);
61 pyroutes.register('admin_settings_update', '/_admin/settings/update', []);
62 pyroutes.register('admin_settings_global', '/_admin/settings/global', []);
62 pyroutes.register('admin_settings_global', '/_admin/settings/global', []);
63 pyroutes.register('admin_settings_global_update', '/_admin/settings/global/update', []);
63 pyroutes.register('admin_settings_global_update', '/_admin/settings/global/update', []);
64 pyroutes.register('admin_settings_vcs', '/_admin/settings/vcs', []);
64 pyroutes.register('admin_settings_vcs', '/_admin/settings/vcs', []);
65 pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []);
65 pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []);
66 pyroutes.register('admin_settings_vcs_svn_pattern_delete', '/_admin/settings/vcs/svn_pattern_delete', []);
66 pyroutes.register('admin_settings_vcs_svn_pattern_delete', '/_admin/settings/vcs/svn_pattern_delete', []);
67 pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []);
67 pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []);
68 pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []);
68 pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []);
69 pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []);
69 pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []);
70 pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []);
70 pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []);
71 pyroutes.register('admin_settings_issuetracker', '/_admin/settings/issue-tracker', []);
71 pyroutes.register('admin_settings_issuetracker', '/_admin/settings/issue-tracker', []);
72 pyroutes.register('admin_settings_issuetracker_update', '/_admin/settings/issue-tracker/update', []);
72 pyroutes.register('admin_settings_issuetracker_update', '/_admin/settings/issue-tracker/update', []);
73 pyroutes.register('admin_settings_issuetracker_test', '/_admin/settings/issue-tracker/test', []);
73 pyroutes.register('admin_settings_issuetracker_test', '/_admin/settings/issue-tracker/test', []);
74 pyroutes.register('admin_settings_issuetracker_delete', '/_admin/settings/issue-tracker/delete', []);
74 pyroutes.register('admin_settings_issuetracker_delete', '/_admin/settings/issue-tracker/delete', []);
75 pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
75 pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
76 pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
76 pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
77 pyroutes.register('admin_settings_hooks', '/_admin/settings/hooks', []);
77 pyroutes.register('admin_settings_hooks', '/_admin/settings/hooks', []);
78 pyroutes.register('admin_settings_hooks_update', '/_admin/settings/hooks/update', []);
78 pyroutes.register('admin_settings_hooks_update', '/_admin/settings/hooks/update', []);
79 pyroutes.register('admin_settings_hooks_delete', '/_admin/settings/hooks/delete', []);
79 pyroutes.register('admin_settings_hooks_delete', '/_admin/settings/hooks/delete', []);
80 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
80 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
81 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
81 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
82 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
82 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
83 pyroutes.register('admin_settings_automation', '/_admin/_admin/settings/automation', []);
83 pyroutes.register('admin_settings_automation', '/_admin/_admin/settings/automation', []);
84 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
84 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
85 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
85 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
86 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
86 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
87 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
87 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
88 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
88 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
89 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
89 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
90 pyroutes.register('admin_permissions_branch', '/_admin/permissions/branch', []);
90 pyroutes.register('admin_permissions_branch', '/_admin/permissions/branch', []);
91 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
91 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
92 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
92 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
93 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
93 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
94 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
94 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
95 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
95 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
96 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
96 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
97 pyroutes.register('users', '/_admin/users', []);
97 pyroutes.register('users', '/_admin/users', []);
98 pyroutes.register('users_data', '/_admin/users_data', []);
98 pyroutes.register('users_data', '/_admin/users_data', []);
99 pyroutes.register('users_create', '/_admin/users/create', []);
99 pyroutes.register('users_create', '/_admin/users/create', []);
100 pyroutes.register('users_new', '/_admin/users/new', []);
100 pyroutes.register('users_new', '/_admin/users/new', []);
101 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
101 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
102 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
102 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
103 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
103 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
104 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
104 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
105 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
105 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
106 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
106 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
107 pyroutes.register('user_force_password_reset', '/_admin/users/%(user_id)s/password_reset', ['user_id']);
107 pyroutes.register('user_force_password_reset', '/_admin/users/%(user_id)s/password_reset', ['user_id']);
108 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
108 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
109 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
109 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
110 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
110 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
111 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
111 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
112 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
112 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
113 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
113 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
114 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
114 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
115 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
115 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
116 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
116 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
117 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
117 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
118 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
118 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
119 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
119 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
120 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
120 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
121 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
121 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
122 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
122 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
123 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
123 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
124 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
124 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
125 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
125 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
126 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
126 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
127 pyroutes.register('edit_user_caches', '/_admin/users/%(user_id)s/edit/caches', ['user_id']);
127 pyroutes.register('edit_user_caches', '/_admin/users/%(user_id)s/edit/caches', ['user_id']);
128 pyroutes.register('edit_user_caches_update', '/_admin/users/%(user_id)s/edit/caches/update', ['user_id']);
128 pyroutes.register('edit_user_caches_update', '/_admin/users/%(user_id)s/edit/caches/update', ['user_id']);
129 pyroutes.register('user_groups', '/_admin/user_groups', []);
129 pyroutes.register('user_groups', '/_admin/user_groups', []);
130 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
130 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
131 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
131 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
132 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
132 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
133 pyroutes.register('repos', '/_admin/repos', []);
133 pyroutes.register('repos', '/_admin/repos', []);
134 pyroutes.register('repo_new', '/_admin/repos/new', []);
134 pyroutes.register('repo_new', '/_admin/repos/new', []);
135 pyroutes.register('repo_create', '/_admin/repos/create', []);
135 pyroutes.register('repo_create', '/_admin/repos/create', []);
136 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
136 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
137 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
137 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
138 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
138 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
139 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
139 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
140 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
140 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
141 pyroutes.register('channelstream_proxy', '/_channelstream', []);
141 pyroutes.register('channelstream_proxy', '/_channelstream', []);
142 pyroutes.register('login', '/_admin/login', []);
142 pyroutes.register('login', '/_admin/login', []);
143 pyroutes.register('logout', '/_admin/logout', []);
143 pyroutes.register('logout', '/_admin/logout', []);
144 pyroutes.register('register', '/_admin/register', []);
144 pyroutes.register('register', '/_admin/register', []);
145 pyroutes.register('reset_password', '/_admin/password_reset', []);
145 pyroutes.register('reset_password', '/_admin/password_reset', []);
146 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
146 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
147 pyroutes.register('home', '/', []);
147 pyroutes.register('home', '/', []);
148 pyroutes.register('user_autocomplete_data', '/_users', []);
148 pyroutes.register('user_autocomplete_data', '/_users', []);
149 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
149 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
150 pyroutes.register('repo_list_data', '/_repos', []);
150 pyroutes.register('repo_list_data', '/_repos', []);
151 pyroutes.register('goto_switcher_data', '/_goto_data', []);
151 pyroutes.register('goto_switcher_data', '/_goto_data', []);
152 pyroutes.register('markup_preview', '/_markup_preview', []);
152 pyroutes.register('markup_preview', '/_markup_preview', []);
153 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
153 pyroutes.register('journal', '/_admin/journal', []);
154 pyroutes.register('journal', '/_admin/journal', []);
154 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
155 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
155 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
156 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
156 pyroutes.register('journal_public', '/_admin/public_journal', []);
157 pyroutes.register('journal_public', '/_admin/public_journal', []);
157 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
158 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
158 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
159 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
159 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
160 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
160 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
161 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
161 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
162 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
162 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
163 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
163 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
164 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
164 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
165 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
165 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
166 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
166 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
167 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
167 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
168 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
168 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
169 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
169 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
170 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
170 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
171 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
171 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
172 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
172 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
173 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
173 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
174 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
174 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
175 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
175 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
176 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
176 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
177 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
177 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
178 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
178 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
179 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
179 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
180 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
180 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
181 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
181 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
182 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
182 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
183 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
183 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
184 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
184 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
185 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
185 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
186 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
186 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
187 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
187 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
188 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
188 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
189 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
189 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
190 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
190 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
191 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
191 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
192 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
192 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
193 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
193 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
194 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
194 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
195 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
195 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
196 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
196 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
197 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
197 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
198 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
198 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
199 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
199 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
200 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
200 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
201 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
201 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
202 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
202 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
203 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
203 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
204 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
204 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
205 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
205 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
206 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
206 pyroutes.register('repo_changelog_elements_file', '/%(repo_name)s/changelog_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
207 pyroutes.register('repo_changelog_elements_file', '/%(repo_name)s/changelog_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
207 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
208 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
208 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
209 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
209 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
210 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
210 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
211 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
211 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
212 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
212 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
213 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
213 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
214 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
214 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
215 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
215 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
216 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
216 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
217 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
217 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
218 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
218 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
219 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
219 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
220 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
220 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
221 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
221 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
222 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
222 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
223 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
223 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
224 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
224 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
225 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
225 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
226 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
226 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
227 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
227 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
228 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
228 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
229 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
229 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
230 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
230 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
231 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
231 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
232 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
232 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
233 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
233 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
234 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
234 pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
235 pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
235 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
236 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
236 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
237 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
237 pyroutes.register('edit_repo_perms_branch', '/%(repo_name)s/settings/branch_permissions', ['repo_name']);
238 pyroutes.register('edit_repo_perms_branch', '/%(repo_name)s/settings/branch_permissions', ['repo_name']);
238 pyroutes.register('edit_repo_perms_branch_delete', '/%(repo_name)s/settings/branch_permissions/%(rule_id)s/delete', ['repo_name', 'rule_id']);
239 pyroutes.register('edit_repo_perms_branch_delete', '/%(repo_name)s/settings/branch_permissions/%(rule_id)s/delete', ['repo_name', 'rule_id']);
239 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
240 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
240 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
241 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
241 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
242 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
242 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
243 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
243 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
244 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
244 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
245 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
245 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
246 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
246 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
247 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
247 pyroutes.register('edit_repo_remote_push', '/%(repo_name)s/settings/remote/push', ['repo_name']);
248 pyroutes.register('edit_repo_remote_push', '/%(repo_name)s/settings/remote/push', ['repo_name']);
248 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
249 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
249 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
250 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
250 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
251 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
251 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
252 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
252 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
253 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
253 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
254 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
254 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
255 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
255 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
256 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
256 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
257 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
257 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
258 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
258 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
259 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
259 pyroutes.register('repo_automation', '/%(repo_name)s/settings/automation', ['repo_name']);
260 pyroutes.register('repo_automation', '/%(repo_name)s/settings/automation', ['repo_name']);
260 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
261 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
261 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
262 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
262 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
263 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
263 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
264 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
264 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
265 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
265 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
266 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
266 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
267 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
267 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
268 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
268 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
269 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
269 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
270 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
270 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
271 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
271 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
272 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
272 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
273 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
273 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
274 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
274 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
275 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
275 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
276 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
276 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
277 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
277 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
278 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
278 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
279 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
279 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
280 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
280 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
281 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
281 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
282 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
282 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
283 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
283 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
284 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
284 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
285 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
285 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
286 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
286 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
287 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
287 pyroutes.register('search', '/_admin/search', []);
288 pyroutes.register('search', '/_admin/search', []);
288 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
289 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
289 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
290 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
290 pyroutes.register('user_group_profile', '/_profile_user_group/%(user_group_name)s', ['user_group_name']);
291 pyroutes.register('user_group_profile', '/_profile_user_group/%(user_group_name)s', ['user_group_name']);
291 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
292 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
292 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
293 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
293 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
294 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
294 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
295 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
295 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
296 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
296 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
297 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
297 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
298 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
298 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
299 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
299 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
300 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
300 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
301 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
301 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
302 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
302 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
303 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
303 pyroutes.register('my_account_user_group_membership', '/_admin/my_account/user_group_membership', []);
304 pyroutes.register('my_account_user_group_membership', '/_admin/my_account/user_group_membership', []);
304 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
305 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
305 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
306 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
306 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
307 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
307 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
308 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
308 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
309 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
309 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
310 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
310 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
311 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
311 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
312 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
312 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
313 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
313 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
314 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
314 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
315 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
315 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
316 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
316 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
317 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
317 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
318 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
318 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
319 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
319 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
320 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
320 pyroutes.register('gists_show', '/_admin/gists', []);
321 pyroutes.register('gists_show', '/_admin/gists', []);
321 pyroutes.register('gists_new', '/_admin/gists/new', []);
322 pyroutes.register('gists_new', '/_admin/gists/new', []);
322 pyroutes.register('gists_create', '/_admin/gists/create', []);
323 pyroutes.register('gists_create', '/_admin/gists/create', []);
323 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
324 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
324 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
325 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
325 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
326 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
326 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
327 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
327 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
328 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
328 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
329 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
329 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
330 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
330 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
331 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
331 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
332 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
332 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
333 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
333 pyroutes.register('apiv2', '/_admin/api', []);
334 pyroutes.register('apiv2', '/_admin/api', []);
334 }
335 }
@@ -1,555 +1,572 b''
1 // # Copyright (C) 2010-2018 RhodeCode GmbH
1 // # Copyright (C) 2010-2018 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 /**
19 /**
20 RhodeCode JS Files
20 RhodeCode JS Files
21 **/
21 **/
22
22
23 if (typeof console == "undefined" || typeof console.log == "undefined"){
23 if (typeof console == "undefined" || typeof console.log == "undefined"){
24 console = { log: function() {} }
24 console = { log: function() {} }
25 }
25 }
26
26
27 // TODO: move the following function to submodules
27 // TODO: move the following function to submodules
28
28
29 /**
29 /**
30 * show more
30 * show more
31 */
31 */
32 var show_more_event = function(){
32 var show_more_event = function(){
33 $('table .show_more').click(function(e) {
33 $('table .show_more').click(function(e) {
34 var cid = e.target.id.substring(1);
34 var cid = e.target.id.substring(1);
35 var button = $(this);
35 var button = $(this);
36 if (button.hasClass('open')) {
36 if (button.hasClass('open')) {
37 $('#'+cid).hide();
37 $('#'+cid).hide();
38 button.removeClass('open');
38 button.removeClass('open');
39 } else {
39 } else {
40 $('#'+cid).show();
40 $('#'+cid).show();
41 button.addClass('open one');
41 button.addClass('open one');
42 }
42 }
43 });
43 });
44 };
44 };
45
45
46 var compare_radio_buttons = function(repo_name, compare_ref_type){
46 var compare_radio_buttons = function(repo_name, compare_ref_type){
47 $('#compare_action').on('click', function(e){
47 $('#compare_action').on('click', function(e){
48 e.preventDefault();
48 e.preventDefault();
49
49
50 var source = $('input[name=compare_source]:checked').val();
50 var source = $('input[name=compare_source]:checked').val();
51 var target = $('input[name=compare_target]:checked').val();
51 var target = $('input[name=compare_target]:checked').val();
52 if(source && target){
52 if(source && target){
53 var url_data = {
53 var url_data = {
54 repo_name: repo_name,
54 repo_name: repo_name,
55 source_ref: source,
55 source_ref: source,
56 source_ref_type: compare_ref_type,
56 source_ref_type: compare_ref_type,
57 target_ref: target,
57 target_ref: target,
58 target_ref_type: compare_ref_type,
58 target_ref_type: compare_ref_type,
59 merge: 1
59 merge: 1
60 };
60 };
61 window.location = pyroutes.url('repo_compare', url_data);
61 window.location = pyroutes.url('repo_compare', url_data);
62 }
62 }
63 });
63 });
64 $('.compare-radio-button').on('click', function(e){
64 $('.compare-radio-button').on('click', function(e){
65 var source = $('input[name=compare_source]:checked').val();
65 var source = $('input[name=compare_source]:checked').val();
66 var target = $('input[name=compare_target]:checked').val();
66 var target = $('input[name=compare_target]:checked').val();
67 if(source && target){
67 if(source && target){
68 $('#compare_action').removeAttr("disabled");
68 $('#compare_action').removeAttr("disabled");
69 $('#compare_action').removeClass("disabled");
69 $('#compare_action').removeClass("disabled");
70 }
70 }
71 })
71 })
72 };
72 };
73
73
74 var showRepoSize = function(target, repo_name, commit_id, callback) {
74 var showRepoSize = function(target, repo_name, commit_id, callback) {
75 var container = $('#' + target);
75 var container = $('#' + target);
76 var url = pyroutes.url('repo_stats',
76 var url = pyroutes.url('repo_stats',
77 {"repo_name": repo_name, "commit_id": commit_id});
77 {"repo_name": repo_name, "commit_id": commit_id});
78
78
79 if (!container.hasClass('loaded')) {
79 if (!container.hasClass('loaded')) {
80 $.ajax({url: url})
80 $.ajax({url: url})
81 .complete(function (data) {
81 .complete(function (data) {
82 var responseJSON = data.responseJSON;
82 var responseJSON = data.responseJSON;
83 container.addClass('loaded');
83 container.addClass('loaded');
84 container.html(responseJSON.size);
84 container.html(responseJSON.size);
85 callback(responseJSON.code_stats)
85 callback(responseJSON.code_stats)
86 })
86 })
87 .fail(function (data) {
87 .fail(function (data) {
88 console.log('failed to load repo stats');
88 console.log('failed to load repo stats');
89 });
89 });
90 }
90 }
91
91
92 };
92 };
93
93
94 var showRepoStats = function(target, data){
94 var showRepoStats = function(target, data){
95 var container = $('#' + target);
95 var container = $('#' + target);
96
96
97 if (container.hasClass('loaded')) {
97 if (container.hasClass('loaded')) {
98 return
98 return
99 }
99 }
100
100
101 var total = 0;
101 var total = 0;
102 var no_data = true;
102 var no_data = true;
103 var tbl = document.createElement('table');
103 var tbl = document.createElement('table');
104 tbl.setAttribute('class', 'trending_language_tbl');
104 tbl.setAttribute('class', 'trending_language_tbl');
105
105
106 $.each(data, function(key, val){
106 $.each(data, function(key, val){
107 total += val.count;
107 total += val.count;
108 });
108 });
109
109
110 var sortedStats = [];
110 var sortedStats = [];
111 for (var obj in data){
111 for (var obj in data){
112 sortedStats.push([obj, data[obj]])
112 sortedStats.push([obj, data[obj]])
113 }
113 }
114 var sortedData = sortedStats.sort(function (a, b) {
114 var sortedData = sortedStats.sort(function (a, b) {
115 return b[1].count - a[1].count
115 return b[1].count - a[1].count
116 });
116 });
117 var cnt = 0;
117 var cnt = 0;
118 $.each(sortedData, function(idx, val){
118 $.each(sortedData, function(idx, val){
119 cnt += 1;
119 cnt += 1;
120 no_data = false;
120 no_data = false;
121
121
122 var hide = cnt > 2;
122 var hide = cnt > 2;
123 var tr = document.createElement('tr');
123 var tr = document.createElement('tr');
124 if (hide) {
124 if (hide) {
125 tr.setAttribute('style', 'display:none');
125 tr.setAttribute('style', 'display:none');
126 tr.setAttribute('class', 'stats_hidden');
126 tr.setAttribute('class', 'stats_hidden');
127 }
127 }
128
128
129 var key = val[0];
129 var key = val[0];
130 var obj = {"desc": val[1].desc, "count": val[1].count};
130 var obj = {"desc": val[1].desc, "count": val[1].count};
131
131
132 var percentage = Math.round((obj.count / total * 100), 2);
132 var percentage = Math.round((obj.count / total * 100), 2);
133
133
134 var td1 = document.createElement('td');
134 var td1 = document.createElement('td');
135 td1.width = 300;
135 td1.width = 300;
136 var trending_language_label = document.createElement('div');
136 var trending_language_label = document.createElement('div');
137 trending_language_label.innerHTML = obj.desc + " (.{0})".format(key);
137 trending_language_label.innerHTML = obj.desc + " (.{0})".format(key);
138 td1.appendChild(trending_language_label);
138 td1.appendChild(trending_language_label);
139
139
140 var td2 = document.createElement('td');
140 var td2 = document.createElement('td');
141 var trending_language = document.createElement('div');
141 var trending_language = document.createElement('div');
142 var nr_files = obj.count +" "+ _ngettext('file', 'files', obj.count);
142 var nr_files = obj.count +" "+ _ngettext('file', 'files', obj.count);
143
143
144 trending_language.title = key + " " + nr_files;
144 trending_language.title = key + " " + nr_files;
145
145
146 trending_language.innerHTML = "<span>" + percentage + "% " + nr_files
146 trending_language.innerHTML = "<span>" + percentage + "% " + nr_files
147 + "</span><b>" + percentage + "% " + nr_files + "</b>";
147 + "</span><b>" + percentage + "% " + nr_files + "</b>";
148
148
149 trending_language.setAttribute("class", 'trending_language');
149 trending_language.setAttribute("class", 'trending_language');
150 $('b', trending_language)[0].style.width = percentage + "%";
150 $('b', trending_language)[0].style.width = percentage + "%";
151 td2.appendChild(trending_language);
151 td2.appendChild(trending_language);
152
152
153 tr.appendChild(td1);
153 tr.appendChild(td1);
154 tr.appendChild(td2);
154 tr.appendChild(td2);
155 tbl.appendChild(tr);
155 tbl.appendChild(tr);
156 if (cnt == 3) {
156 if (cnt == 3) {
157 var show_more = document.createElement('tr');
157 var show_more = document.createElement('tr');
158 var td = document.createElement('td');
158 var td = document.createElement('td');
159 lnk = document.createElement('a');
159 lnk = document.createElement('a');
160
160
161 lnk.href = '#';
161 lnk.href = '#';
162 lnk.innerHTML = _gettext('Show more');
162 lnk.innerHTML = _gettext('Show more');
163 lnk.id = 'code_stats_show_more';
163 lnk.id = 'code_stats_show_more';
164 td.appendChild(lnk);
164 td.appendChild(lnk);
165
165
166 show_more.appendChild(td);
166 show_more.appendChild(td);
167 show_more.appendChild(document.createElement('td'));
167 show_more.appendChild(document.createElement('td'));
168 tbl.appendChild(show_more);
168 tbl.appendChild(show_more);
169 }
169 }
170 });
170 });
171
171
172 $(container).html(tbl);
172 $(container).html(tbl);
173 $(container).addClass('loaded');
173 $(container).addClass('loaded');
174
174
175 $('#code_stats_show_more').on('click', function (e) {
175 $('#code_stats_show_more').on('click', function (e) {
176 e.preventDefault();
176 e.preventDefault();
177 $('.stats_hidden').each(function (idx) {
177 $('.stats_hidden').each(function (idx) {
178 $(this).css("display", "");
178 $(this).css("display", "");
179 });
179 });
180 $('#code_stats_show_more').hide();
180 $('#code_stats_show_more').hide();
181 });
181 });
182
182
183 };
183 };
184
184
185 // returns a node from given html;
185 // returns a node from given html;
186 var fromHTML = function(html){
186 var fromHTML = function(html){
187 var _html = document.createElement('element');
187 var _html = document.createElement('element');
188 _html.innerHTML = html;
188 _html.innerHTML = html;
189 return _html;
189 return _html;
190 };
190 };
191
191
192 // Toggle Collapsable Content
192 // Toggle Collapsable Content
193 function collapsableContent() {
193 function collapsableContent() {
194
194
195 $('.collapsable-content').not('.no-hide').hide();
195 $('.collapsable-content').not('.no-hide').hide();
196
196
197 $('.btn-collapse').unbind(); //in case we've been here before
197 $('.btn-collapse').unbind(); //in case we've been here before
198 $('.btn-collapse').click(function() {
198 $('.btn-collapse').click(function() {
199 var button = $(this);
199 var button = $(this);
200 var togglename = $(this).data("toggle");
200 var togglename = $(this).data("toggle");
201 $('.collapsable-content[data-toggle='+togglename+']').toggle();
201 $('.collapsable-content[data-toggle='+togglename+']').toggle();
202 if ($(this).html()=="Show Less")
202 if ($(this).html()=="Show Less")
203 $(this).html("Show More");
203 $(this).html("Show More");
204 else
204 else
205 $(this).html("Show Less");
205 $(this).html("Show Less");
206 });
206 });
207 };
207 };
208
208
209 var timeagoActivate = function() {
209 var timeagoActivate = function() {
210 $("time.timeago").timeago();
210 $("time.timeago").timeago();
211 };
211 };
212
212
213
213
214 var clipboardActivate = function() {
214 var clipboardActivate = function() {
215 /*
215 /*
216 *
216 *
217 * <i class="tooltip icon-plus clipboard-action" data-clipboard-text="${commit.raw_id}" title="${_('Copy the full commit id')}"></i>
217 * <i class="tooltip icon-plus clipboard-action" data-clipboard-text="${commit.raw_id}" title="${_('Copy the full commit id')}"></i>
218 * */
218 * */
219 var clipboard = new ClipboardJS('.clipboard-action');
219 var clipboard = new ClipboardJS('.clipboard-action');
220
220
221 clipboard.on('success', function(e) {
221 clipboard.on('success', function(e) {
222 var callback = function () {
222 var callback = function () {
223 $(e.trigger).animate({'opacity': 1.00}, 200)
223 $(e.trigger).animate({'opacity': 1.00}, 200)
224 };
224 };
225 $(e.trigger).animate({'opacity': 0.15}, 200, callback);
225 $(e.trigger).animate({'opacity': 0.15}, 200, callback);
226 e.clearSelection();
226 e.clearSelection();
227 });
227 });
228 };
228 };
229
229
230
230
231 // Formatting values in a Select2 dropdown of commit references
231 // Formatting values in a Select2 dropdown of commit references
232 var formatSelect2SelectionRefs = function(commit_ref){
232 var formatSelect2SelectionRefs = function(commit_ref){
233 var tmpl = '';
233 var tmpl = '';
234 if (!commit_ref.text || commit_ref.type === 'sha'){
234 if (!commit_ref.text || commit_ref.type === 'sha'){
235 return commit_ref.text;
235 return commit_ref.text;
236 }
236 }
237 if (commit_ref.type === 'branch'){
237 if (commit_ref.type === 'branch'){
238 tmpl = tmpl.concat('<i class="icon-branch"></i> ');
238 tmpl = tmpl.concat('<i class="icon-branch"></i> ');
239 } else if (commit_ref.type === 'tag'){
239 } else if (commit_ref.type === 'tag'){
240 tmpl = tmpl.concat('<i class="icon-tag"></i> ');
240 tmpl = tmpl.concat('<i class="icon-tag"></i> ');
241 } else if (commit_ref.type === 'book'){
241 } else if (commit_ref.type === 'book'){
242 tmpl = tmpl.concat('<i class="icon-bookmark"></i> ');
242 tmpl = tmpl.concat('<i class="icon-bookmark"></i> ');
243 }
243 }
244 return tmpl.concat(escapeHtml(commit_ref.text));
244 return tmpl.concat(escapeHtml(commit_ref.text));
245 };
245 };
246
246
247 // takes a given html element and scrolls it down offset pixels
247 // takes a given html element and scrolls it down offset pixels
248 function offsetScroll(element, offset) {
248 function offsetScroll(element, offset) {
249 setTimeout(function() {
249 setTimeout(function() {
250 var location = element.offset().top;
250 var location = element.offset().top;
251 // some browsers use body, some use html
251 // some browsers use body, some use html
252 $('html, body').animate({ scrollTop: (location - offset) });
252 $('html, body').animate({ scrollTop: (location - offset) });
253 }, 100);
253 }, 100);
254 }
254 }
255
255
256 // scroll an element `percent`% from the top of page in `time` ms
256 // scroll an element `percent`% from the top of page in `time` ms
257 function scrollToElement(element, percent, time) {
257 function scrollToElement(element, percent, time) {
258 percent = (percent === undefined ? 25 : percent);
258 percent = (percent === undefined ? 25 : percent);
259 time = (time === undefined ? 100 : time);
259 time = (time === undefined ? 100 : time);
260
260
261 var $element = $(element);
261 var $element = $(element);
262 if ($element.length == 0) {
262 if ($element.length == 0) {
263 throw('Cannot scroll to {0}'.format(element))
263 throw('Cannot scroll to {0}'.format(element))
264 }
264 }
265 var elOffset = $element.offset().top;
265 var elOffset = $element.offset().top;
266 var elHeight = $element.height();
266 var elHeight = $element.height();
267 var windowHeight = $(window).height();
267 var windowHeight = $(window).height();
268 var offset = elOffset;
268 var offset = elOffset;
269 if (elHeight < windowHeight) {
269 if (elHeight < windowHeight) {
270 offset = elOffset - ((windowHeight / (100 / percent)) - (elHeight / 2));
270 offset = elOffset - ((windowHeight / (100 / percent)) - (elHeight / 2));
271 }
271 }
272 setTimeout(function() {
272 setTimeout(function() {
273 $('html, body').animate({ scrollTop: offset});
273 $('html, body').animate({ scrollTop: offset});
274 }, time);
274 }, time);
275 }
275 }
276
276
277 /**
277 /**
278 * global hooks after DOM is loaded
278 * global hooks after DOM is loaded
279 */
279 */
280 $(document).ready(function() {
280 $(document).ready(function() {
281 firefoxAnchorFix();
281 firefoxAnchorFix();
282
282
283 $('.navigation a.menulink').on('click', function(e){
283 $('.navigation a.menulink').on('click', function(e){
284 var menuitem = $(this).parent('li');
284 var menuitem = $(this).parent('li');
285 if (menuitem.hasClass('open')) {
285 if (menuitem.hasClass('open')) {
286 menuitem.removeClass('open');
286 menuitem.removeClass('open');
287 } else {
287 } else {
288 menuitem.addClass('open');
288 menuitem.addClass('open');
289 $(document).on('click', function(event) {
289 $(document).on('click', function(event) {
290 if (!$(event.target).closest(menuitem).length) {
290 if (!$(event.target).closest(menuitem).length) {
291 menuitem.removeClass('open');
291 menuitem.removeClass('open');
292 }
292 }
293 });
293 });
294 }
294 }
295 });
295 });
296
296
297 $('body').on('click', '.cb-lineno a', function(event) {
297 $('body').on('click', '.cb-lineno a', function(event) {
298
298
299 function sortNumber(a,b) {
299 function sortNumber(a,b) {
300 return a - b;
300 return a - b;
301 }
301 }
302
302
303 var lineNo = $(this).data('lineNo');
303 var lineNo = $(this).data('lineNo');
304 if (lineNo) {
304 if (lineNo) {
305
305
306 // on shift, we do a range selection, if we got previous line
306 // on shift, we do a range selection, if we got previous line
307 var prevLine = $('.cb-line-selected a').data('lineNo');
307 var prevLine = $('.cb-line-selected a').data('lineNo');
308 if (event.shiftKey && prevLine !== undefined) {
308 if (event.shiftKey && prevLine !== undefined) {
309 var prevLine = parseInt(prevLine);
309 var prevLine = parseInt(prevLine);
310 var nextLine = parseInt(lineNo);
310 var nextLine = parseInt(lineNo);
311 var pos = [prevLine, nextLine].sort(sortNumber);
311 var pos = [prevLine, nextLine].sort(sortNumber);
312 var anchor = '#L{0}-{1}'.format(pos[0], pos[1]);
312 var anchor = '#L{0}-{1}'.format(pos[0], pos[1]);
313
313
314 } else {
314 } else {
315
315
316 var nextLine = parseInt(lineNo);
316 var nextLine = parseInt(lineNo);
317 var pos = [nextLine, nextLine];
317 var pos = [nextLine, nextLine];
318 var anchor = '#L{0}'.format(pos[0]);
318 var anchor = '#L{0}'.format(pos[0]);
319
319
320 }
320 }
321 // highlight
321 // highlight
322 var range = [];
322 var range = [];
323 for (var i = pos[0]; i <= pos[1]; i++) {
323 for (var i = pos[0]; i <= pos[1]; i++) {
324 range.push(i);
324 range.push(i);
325 }
325 }
326 // clear selection
326 // clear selection
327 $('.cb-line-selected').removeClass('cb-line-selected');
327 $('.cb-line-selected').removeClass('cb-line-selected');
328
328
329 $.each(range, function (i, lineNo) {
329 $.each(range, function (i, lineNo) {
330 var line_td = $('td.cb-lineno#L' + lineNo);
330 var line_td = $('td.cb-lineno#L' + lineNo);
331 if (line_td.length) {
331 if (line_td.length) {
332 line_td.addClass('cb-line-selected'); // line number td
332 line_td.addClass('cb-line-selected'); // line number td
333 line_td.prev().addClass('cb-line-selected'); // line data
333 line_td.prev().addClass('cb-line-selected'); // line data
334 line_td.next().addClass('cb-line-selected'); // line content
334 line_td.next().addClass('cb-line-selected'); // line content
335 }
335 }
336 });
336 });
337
337
338
338
339 } else {
339 } else {
340 if ($(this).attr('name') !== undefined) {
340 if ($(this).attr('name') !== undefined) {
341 // clear selection
341 // clear selection
342 $('td.cb-line-selected').removeClass('cb-line-selected');
342 $('td.cb-line-selected').removeClass('cb-line-selected');
343 var aEl = $(this).closest('td');
343 var aEl = $(this).closest('td');
344 aEl.addClass('cb-line-selected');
344 aEl.addClass('cb-line-selected');
345 aEl.next('td').addClass('cb-line-selected');
345 aEl.next('td').addClass('cb-line-selected');
346 }
346 }
347 }
347 }
348
348
349 // Replace URL without jumping to it if browser supports.
349 // Replace URL without jumping to it if browser supports.
350 // Default otherwise
350 // Default otherwise
351 if (history.pushState && anchor !== undefined) {
351 if (history.pushState && anchor !== undefined) {
352 var new_location = location.href.rstrip('#');
352 var new_location = location.href.rstrip('#');
353 if (location.hash) {
353 if (location.hash) {
354 // location without hash
354 // location without hash
355 new_location = new_location.replace(location.hash, "");
355 new_location = new_location.replace(location.hash, "");
356 }
356 }
357
357
358 // Make new anchor url
358 // Make new anchor url
359 new_location = new_location + anchor;
359 new_location = new_location + anchor;
360 history.pushState(true, document.title, new_location);
360 history.pushState(true, document.title, new_location);
361
361
362 return false;
362 return false;
363 }
363 }
364
364
365 });
365 });
366
366
367
367
368 $('.collapse_file').on('click', function(e) {
368 $('.collapse_file').on('click', function(e) {
369 e.stopPropagation();
369 e.stopPropagation();
370 if ($(e.target).is('a')) { return; }
370 if ($(e.target).is('a')) { return; }
371 var node = $(e.delegateTarget).first();
371 var node = $(e.delegateTarget).first();
372 var icon = $($(node.children().first()).children().first());
372 var icon = $($(node.children().first()).children().first());
373 var id = node.attr('fid');
373 var id = node.attr('fid');
374 var target = $('#'+id);
374 var target = $('#'+id);
375 var tr = $('#tr_'+id);
375 var tr = $('#tr_'+id);
376 var diff = $('#diff_'+id);
376 var diff = $('#diff_'+id);
377 if(node.hasClass('expand_file')){
377 if(node.hasClass('expand_file')){
378 node.removeClass('expand_file');
378 node.removeClass('expand_file');
379 icon.removeClass('expand_file_icon');
379 icon.removeClass('expand_file_icon');
380 node.addClass('collapse_file');
380 node.addClass('collapse_file');
381 icon.addClass('collapse_file_icon');
381 icon.addClass('collapse_file_icon');
382 diff.show();
382 diff.show();
383 tr.show();
383 tr.show();
384 target.show();
384 target.show();
385 } else {
385 } else {
386 node.removeClass('collapse_file');
386 node.removeClass('collapse_file');
387 icon.removeClass('collapse_file_icon');
387 icon.removeClass('collapse_file_icon');
388 node.addClass('expand_file');
388 node.addClass('expand_file');
389 icon.addClass('expand_file_icon');
389 icon.addClass('expand_file_icon');
390 diff.hide();
390 diff.hide();
391 tr.hide();
391 tr.hide();
392 target.hide();
392 target.hide();
393 }
393 }
394 });
394 });
395
395
396 $('#expand_all_files').click(function() {
396 $('#expand_all_files').click(function() {
397 $('.expand_file').each(function() {
397 $('.expand_file').each(function() {
398 var node = $(this);
398 var node = $(this);
399 var icon = $($(node.children().first()).children().first());
399 var icon = $($(node.children().first()).children().first());
400 var id = $(this).attr('fid');
400 var id = $(this).attr('fid');
401 var target = $('#'+id);
401 var target = $('#'+id);
402 var tr = $('#tr_'+id);
402 var tr = $('#tr_'+id);
403 var diff = $('#diff_'+id);
403 var diff = $('#diff_'+id);
404 node.removeClass('expand_file');
404 node.removeClass('expand_file');
405 icon.removeClass('expand_file_icon');
405 icon.removeClass('expand_file_icon');
406 node.addClass('collapse_file');
406 node.addClass('collapse_file');
407 icon.addClass('collapse_file_icon');
407 icon.addClass('collapse_file_icon');
408 diff.show();
408 diff.show();
409 tr.show();
409 tr.show();
410 target.show();
410 target.show();
411 });
411 });
412 });
412 });
413
413
414 $('#collapse_all_files').click(function() {
414 $('#collapse_all_files').click(function() {
415 $('.collapse_file').each(function() {
415 $('.collapse_file').each(function() {
416 var node = $(this);
416 var node = $(this);
417 var icon = $($(node.children().first()).children().first());
417 var icon = $($(node.children().first()).children().first());
418 var id = $(this).attr('fid');
418 var id = $(this).attr('fid');
419 var target = $('#'+id);
419 var target = $('#'+id);
420 var tr = $('#tr_'+id);
420 var tr = $('#tr_'+id);
421 var diff = $('#diff_'+id);
421 var diff = $('#diff_'+id);
422 node.removeClass('collapse_file');
422 node.removeClass('collapse_file');
423 icon.removeClass('collapse_file_icon');
423 icon.removeClass('collapse_file_icon');
424 node.addClass('expand_file');
424 node.addClass('expand_file');
425 icon.addClass('expand_file_icon');
425 icon.addClass('expand_file_icon');
426 diff.hide();
426 diff.hide();
427 tr.hide();
427 tr.hide();
428 target.hide();
428 target.hide();
429 });
429 });
430 });
430 });
431
431
432 // Mouse over behavior for comments and line selection
432 // Mouse over behavior for comments and line selection
433
433
434 // Select the line that comes from the url anchor
434 // Select the line that comes from the url anchor
435 // At the time of development, Chrome didn't seem to support jquery's :target
435 // At the time of development, Chrome didn't seem to support jquery's :target
436 // element, so I had to scroll manually
436 // element, so I had to scroll manually
437
437
438 if (location.hash) {
438 if (location.hash) {
439 var result = splitDelimitedHash(location.hash);
439 var result = splitDelimitedHash(location.hash);
440 var loc = result.loc;
440 var loc = result.loc;
441 if (loc.length > 1) {
441 if (loc.length > 1) {
442
442
443 var highlightable_line_tds = [];
443 var highlightable_line_tds = [];
444
444
445 // source code line format
445 // source code line format
446 var page_highlights = loc.substring(
446 var page_highlights = loc.substring(
447 loc.indexOf('#') + 1).split('L');
447 loc.indexOf('#') + 1).split('L');
448
448
449 if (page_highlights.length > 1) {
449 if (page_highlights.length > 1) {
450 var highlight_ranges = page_highlights[1].split(",");
450 var highlight_ranges = page_highlights[1].split(",");
451 var h_lines = [];
451 var h_lines = [];
452 for (var pos in highlight_ranges) {
452 for (var pos in highlight_ranges) {
453 var _range = highlight_ranges[pos].split('-');
453 var _range = highlight_ranges[pos].split('-');
454 if (_range.length === 2) {
454 if (_range.length === 2) {
455 var start = parseInt(_range[0]);
455 var start = parseInt(_range[0]);
456 var end = parseInt(_range[1]);
456 var end = parseInt(_range[1]);
457 if (start < end) {
457 if (start < end) {
458 for (var i = start; i <= end; i++) {
458 for (var i = start; i <= end; i++) {
459 h_lines.push(i);
459 h_lines.push(i);
460 }
460 }
461 }
461 }
462 }
462 }
463 else {
463 else {
464 h_lines.push(parseInt(highlight_ranges[pos]));
464 h_lines.push(parseInt(highlight_ranges[pos]));
465 }
465 }
466 }
466 }
467 for (pos in h_lines) {
467 for (pos in h_lines) {
468 var line_td = $('td.cb-lineno#L' + h_lines[pos]);
468 var line_td = $('td.cb-lineno#L' + h_lines[pos]);
469 if (line_td.length) {
469 if (line_td.length) {
470 highlightable_line_tds.push(line_td);
470 highlightable_line_tds.push(line_td);
471 }
471 }
472 }
472 }
473 }
473 }
474
474
475 // now check a direct id reference (diff page)
475 // now check a direct id reference (diff page)
476 if ($(loc).length && $(loc).hasClass('cb-lineno')) {
476 if ($(loc).length && $(loc).hasClass('cb-lineno')) {
477 highlightable_line_tds.push($(loc));
477 highlightable_line_tds.push($(loc));
478 }
478 }
479 $.each(highlightable_line_tds, function (i, $td) {
479 $.each(highlightable_line_tds, function (i, $td) {
480 $td.addClass('cb-line-selected'); // line number td
480 $td.addClass('cb-line-selected'); // line number td
481 $td.prev().addClass('cb-line-selected'); // line data
481 $td.prev().addClass('cb-line-selected'); // line data
482 $td.next().addClass('cb-line-selected'); // line content
482 $td.next().addClass('cb-line-selected'); // line content
483 });
483 });
484
484
485 if (highlightable_line_tds.length) {
485 if (highlightable_line_tds.length) {
486 var $first_line_td = highlightable_line_tds[0];
486 var $first_line_td = highlightable_line_tds[0];
487 scrollToElement($first_line_td);
487 scrollToElement($first_line_td);
488 $.Topic('/ui/plugins/code/anchor_focus').prepareOrPublish({
488 $.Topic('/ui/plugins/code/anchor_focus').prepareOrPublish({
489 td: $first_line_td,
489 td: $first_line_td,
490 remainder: result.remainder
490 remainder: result.remainder
491 });
491 });
492 }
492 }
493 }
493 }
494 }
494 }
495 collapsableContent();
495 collapsableContent();
496 });
496 });
497
497
498 var feedLifetimeOptions = function(query, initialData){
498 var feedLifetimeOptions = function(query, initialData){
499 var data = {results: []};
499 var data = {results: []};
500 var isQuery = typeof query.term !== 'undefined';
500 var isQuery = typeof query.term !== 'undefined';
501
501
502 var section = _gettext('Lifetime');
502 var section = _gettext('Lifetime');
503 var children = [];
503 var children = [];
504
504
505 //filter results
505 //filter results
506 $.each(initialData.results, function(idx, value) {
506 $.each(initialData.results, function(idx, value) {
507
507
508 if (!isQuery || query.term.length === 0 || value.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
508 if (!isQuery || query.term.length === 0 || value.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
509 children.push({
509 children.push({
510 'id': this.id,
510 'id': this.id,
511 'text': this.text
511 'text': this.text
512 })
512 })
513 }
513 }
514
514
515 });
515 });
516 data.results.push({
516 data.results.push({
517 'text': section,
517 'text': section,
518 'children': children
518 'children': children
519 });
519 });
520
520
521 if (isQuery) {
521 if (isQuery) {
522
522
523 var now = moment.utc();
523 var now = moment.utc();
524
524
525 var parseQuery = function(entry, now){
525 var parseQuery = function(entry, now){
526 var fmt = 'DD/MM/YYYY H:mm';
526 var fmt = 'DD/MM/YYYY H:mm';
527 var parsed = moment.utc(entry, fmt);
527 var parsed = moment.utc(entry, fmt);
528 var diffInMin = parsed.diff(now, 'minutes');
528 var diffInMin = parsed.diff(now, 'minutes');
529
529
530 if (diffInMin > 0){
530 if (diffInMin > 0){
531 return {
531 return {
532 id: diffInMin,
532 id: diffInMin,
533 text: parsed.format(fmt)
533 text: parsed.format(fmt)
534 }
534 }
535 } else {
535 } else {
536 return {
536 return {
537 id: undefined,
537 id: undefined,
538 text: parsed.format('DD/MM/YYYY') + ' ' + _gettext('date not in future')
538 text: parsed.format('DD/MM/YYYY') + ' ' + _gettext('date not in future')
539 }
539 }
540 }
540 }
541
541
542
542
543 };
543 };
544
544
545 data.results.push({
545 data.results.push({
546 'text': _gettext('Specified expiration date'),
546 'text': _gettext('Specified expiration date'),
547 'children': [{
547 'children': [{
548 'id': parseQuery(query.term, now).id,
548 'id': parseQuery(query.term, now).id,
549 'text': parseQuery(query.term, now).text
549 'text': parseQuery(query.term, now).text
550 }]
550 }]
551 });
551 });
552 }
552 }
553
553
554 query.callback(data);
554 query.callback(data);
555 };
555 };
556
557
558 var storeUserSessionAttr = function (key, val) {
559
560 var postData = {
561 'key': key,
562 'val': val,
563 'csrf_token': CSRF_TOKEN
564 };
565
566 var success = function(o) {
567 return true
568 };
569
570 ajaxPOST(pyroutes.url('store_user_session_value'), postData, success);
571 return false;
572 };
@@ -1,767 +1,767 b''
1 <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/>
1 <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/>
2
2
3 <%def name="diff_line_anchor(filename, line, type)"><%
3 <%def name="diff_line_anchor(filename, line, type)"><%
4 return '%s_%s_%i' % (h.safeid(filename), type, line)
4 return '%s_%s_%i' % (h.safeid(filename), type, line)
5 %></%def>
5 %></%def>
6
6
7 <%def name="action_class(action)">
7 <%def name="action_class(action)">
8 <%
8 <%
9 return {
9 return {
10 '-': 'cb-deletion',
10 '-': 'cb-deletion',
11 '+': 'cb-addition',
11 '+': 'cb-addition',
12 ' ': 'cb-context',
12 ' ': 'cb-context',
13 }.get(action, 'cb-empty')
13 }.get(action, 'cb-empty')
14 %>
14 %>
15 </%def>
15 </%def>
16
16
17 <%def name="op_class(op_id)">
17 <%def name="op_class(op_id)">
18 <%
18 <%
19 return {
19 return {
20 DEL_FILENODE: 'deletion', # file deleted
20 DEL_FILENODE: 'deletion', # file deleted
21 BIN_FILENODE: 'warning' # binary diff hidden
21 BIN_FILENODE: 'warning' # binary diff hidden
22 }.get(op_id, 'addition')
22 }.get(op_id, 'addition')
23 %>
23 %>
24 </%def>
24 </%def>
25
25
26
26
27
27
28 <%def name="render_diffset(diffset, commit=None,
28 <%def name="render_diffset(diffset, commit=None,
29
29
30 # collapse all file diff entries when there are more than this amount of files in the diff
30 # collapse all file diff entries when there are more than this amount of files in the diff
31 collapse_when_files_over=20,
31 collapse_when_files_over=20,
32
32
33 # collapse lines in the diff when more than this amount of lines changed in the file diff
33 # collapse lines in the diff when more than this amount of lines changed in the file diff
34 lines_changed_limit=500,
34 lines_changed_limit=500,
35
35
36 # add a ruler at to the output
36 # add a ruler at to the output
37 ruler_at_chars=0,
37 ruler_at_chars=0,
38
38
39 # show inline comments
39 # show inline comments
40 use_comments=False,
40 use_comments=False,
41
41
42 # disable new comments
42 # disable new comments
43 disable_new_comments=False,
43 disable_new_comments=False,
44
44
45 # special file-comments that were deleted in previous versions
45 # special file-comments that were deleted in previous versions
46 # it's used for showing outdated comments for deleted files in a PR
46 # it's used for showing outdated comments for deleted files in a PR
47 deleted_files_comments=None,
47 deleted_files_comments=None,
48
48
49 # for cache purpose
49 # for cache purpose
50 inline_comments=None
50 inline_comments=None
51
51
52 )">
52 )">
53 %if use_comments:
53 %if use_comments:
54 <div id="cb-comments-inline-container-template" class="js-template">
54 <div id="cb-comments-inline-container-template" class="js-template">
55 ${inline_comments_container([], inline_comments)}
55 ${inline_comments_container([], inline_comments)}
56 </div>
56 </div>
57 <div class="js-template" id="cb-comment-inline-form-template">
57 <div class="js-template" id="cb-comment-inline-form-template">
58 <div class="comment-inline-form ac">
58 <div class="comment-inline-form ac">
59
59
60 %if c.rhodecode_user.username != h.DEFAULT_USER:
60 %if c.rhodecode_user.username != h.DEFAULT_USER:
61 ## render template for inline comments
61 ## render template for inline comments
62 ${commentblock.comment_form(form_type='inline')}
62 ${commentblock.comment_form(form_type='inline')}
63 %else:
63 %else:
64 ${h.form('', class_='inline-form comment-form-login', method='get')}
64 ${h.form('', class_='inline-form comment-form-login', method='get')}
65 <div class="pull-left">
65 <div class="pull-left">
66 <div class="comment-help pull-right">
66 <div class="comment-help pull-right">
67 ${_('You need to be logged in to leave comments.')} <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
67 ${_('You need to be logged in to leave comments.')} <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
68 </div>
68 </div>
69 </div>
69 </div>
70 <div class="comment-button pull-right">
70 <div class="comment-button pull-right">
71 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
71 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
72 ${_('Cancel')}
72 ${_('Cancel')}
73 </button>
73 </button>
74 </div>
74 </div>
75 <div class="clearfix"></div>
75 <div class="clearfix"></div>
76 ${h.end_form()}
76 ${h.end_form()}
77 %endif
77 %endif
78 </div>
78 </div>
79 </div>
79 </div>
80
80
81 %endif
81 %endif
82 <%
82 <%
83 collapse_all = len(diffset.files) > collapse_when_files_over
83 collapse_all = len(diffset.files) > collapse_when_files_over
84 %>
84 %>
85
85
86 %if c.diffmode == 'sideside':
86 %if c.user_session_attrs["diffmode"] == 'sideside':
87 <style>
87 <style>
88 .wrapper {
88 .wrapper {
89 max-width: 1600px !important;
89 max-width: 1600px !important;
90 }
90 }
91 </style>
91 </style>
92 %endif
92 %endif
93
93
94 %if ruler_at_chars:
94 %if ruler_at_chars:
95 <style>
95 <style>
96 .diff table.cb .cb-content:after {
96 .diff table.cb .cb-content:after {
97 content: "";
97 content: "";
98 border-left: 1px solid blue;
98 border-left: 1px solid blue;
99 position: absolute;
99 position: absolute;
100 top: 0;
100 top: 0;
101 height: 18px;
101 height: 18px;
102 opacity: .2;
102 opacity: .2;
103 z-index: 10;
103 z-index: 10;
104 //## +5 to account for diff action (+/-)
104 //## +5 to account for diff action (+/-)
105 left: ${ruler_at_chars + 5}ch;
105 left: ${ruler_at_chars + 5}ch;
106 </style>
106 </style>
107 %endif
107 %endif
108
108
109 <div class="diffset ${disable_new_comments and 'diffset-comments-disabled'}">
109 <div class="diffset ${disable_new_comments and 'diffset-comments-disabled'}">
110 <div class="diffset-heading ${diffset.limited_diff and 'diffset-heading-warning' or ''}">
110 <div class="diffset-heading ${diffset.limited_diff and 'diffset-heading-warning' or ''}">
111 %if commit:
111 %if commit:
112 <div class="pull-right">
112 <div class="pull-right">
113 <a class="btn tooltip" title="${h.tooltip(_('Browse Files at revision {}').format(commit.raw_id))}" href="${h.route_path('repo_files',repo_name=diffset.repo_name, commit_id=commit.raw_id, f_path='')}">
113 <a class="btn tooltip" title="${h.tooltip(_('Browse Files at revision {}').format(commit.raw_id))}" href="${h.route_path('repo_files',repo_name=diffset.repo_name, commit_id=commit.raw_id, f_path='')}">
114 ${_('Browse Files')}
114 ${_('Browse Files')}
115 </a>
115 </a>
116 </div>
116 </div>
117 %endif
117 %endif
118 <h2 class="clearinner">
118 <h2 class="clearinner">
119 %if commit:
119 %if commit:
120 <a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=commit.raw_id)}">${'r%s:%s' % (commit.revision,h.short_id(commit.raw_id))}</a> -
120 <a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=commit.raw_id)}">${'r%s:%s' % (commit.revision,h.short_id(commit.raw_id))}</a> -
121 ${h.age_component(commit.date)} -
121 ${h.age_component(commit.date)} -
122 %endif
122 %endif
123
123
124 %if diffset.limited_diff:
124 %if diffset.limited_diff:
125 ${_('The requested commit is too big and content was truncated.')}
125 ${_('The requested commit is too big and content was truncated.')}
126
126
127 ${_ungettext('%(num)s file changed.', '%(num)s files changed.', diffset.changed_files) % {'num': diffset.changed_files}}
127 ${_ungettext('%(num)s file changed.', '%(num)s files changed.', diffset.changed_files) % {'num': diffset.changed_files}}
128 <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
128 <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
129 %else:
129 %else:
130 ${_ungettext('%(num)s file changed: %(linesadd)s inserted, ''%(linesdel)s deleted',
130 ${_ungettext('%(num)s file changed: %(linesadd)s inserted, ''%(linesdel)s deleted',
131 '%(num)s files changed: %(linesadd)s inserted, %(linesdel)s deleted', diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}}
131 '%(num)s files changed: %(linesadd)s inserted, %(linesdel)s deleted', diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}}
132 %endif
132 %endif
133
133
134 </h2>
134 </h2>
135 </div>
135 </div>
136
136
137 %if diffset.has_hidden_changes:
137 %if diffset.has_hidden_changes:
138 <p class="empty_data">${_('Some changes may be hidden')}</p>
138 <p class="empty_data">${_('Some changes may be hidden')}</p>
139 %elif not diffset.files:
139 %elif not diffset.files:
140 <p class="empty_data">${_('No files')}</p>
140 <p class="empty_data">${_('No files')}</p>
141 %endif
141 %endif
142
142
143 <div class="filediffs">
143 <div class="filediffs">
144 ## initial value could be marked as False later on
144 ## initial value could be marked as False later on
145 <% over_lines_changed_limit = False %>
145 <% over_lines_changed_limit = False %>
146 %for i, filediff in enumerate(diffset.files):
146 %for i, filediff in enumerate(diffset.files):
147
147
148 <%
148 <%
149 lines_changed = filediff.patch['stats']['added'] + filediff.patch['stats']['deleted']
149 lines_changed = filediff.patch['stats']['added'] + filediff.patch['stats']['deleted']
150 over_lines_changed_limit = lines_changed > lines_changed_limit
150 over_lines_changed_limit = lines_changed > lines_changed_limit
151 %>
151 %>
152
152
153 <input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state" id="filediff-collapse-${id(filediff)}" type="checkbox">
153 <input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state" id="filediff-collapse-${id(filediff)}" type="checkbox">
154 <div
154 <div
155 class="filediff"
155 class="filediff"
156 data-f-path="${filediff.patch['filename']}"
156 data-f-path="${filediff.patch['filename']}"
157 id="a_${h.FID('', filediff.patch['filename'])}"
157 id="a_${h.FID('', filediff.patch['filename'])}"
158 >
158 >
159
159
160 <label for="filediff-collapse-${id(filediff)}" class="filediff-heading">
160 <label for="filediff-collapse-${id(filediff)}" class="filediff-heading">
161 <div class="filediff-collapse-indicator"></div>
161 <div class="filediff-collapse-indicator"></div>
162 ${diff_ops(filediff)}
162 ${diff_ops(filediff)}
163 </label>
163 </label>
164 ${diff_menu(filediff, use_comments=use_comments)}
164 ${diff_menu(filediff, use_comments=use_comments)}
165 <table class="cb cb-diff-${c.diffmode} code-highlight ${(over_lines_changed_limit and 'cb-collapsed' or '')}">
165 <table class="cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${(over_lines_changed_limit and 'cb-collapsed' or '')}">
166
166
167 ## new/deleted/empty content case
167 ## new/deleted/empty content case
168 % if not filediff.hunks:
168 % if not filediff.hunks:
169 ## Comment container, on "fakes" hunk that contains all data to render comments
169 ## Comment container, on "fakes" hunk that contains all data to render comments
170 ${render_hunk_lines(c.diffmode, filediff.hunk_ops, use_comments=use_comments, inline_comments=inline_comments)}
170 ${render_hunk_lines(c.user_session_attrs["diffmode"], filediff.hunk_ops, use_comments=use_comments, inline_comments=inline_comments)}
171 % endif
171 % endif
172
172
173 %if filediff.limited_diff:
173 %if filediff.limited_diff:
174 <tr class="cb-warning cb-collapser">
174 <tr class="cb-warning cb-collapser">
175 <td class="cb-text" ${(c.diffmode == 'unified' and 'colspan=4' or 'colspan=6')}>
175 <td class="cb-text" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=6')}>
176 ${_('The requested commit is too big and content was truncated.')} <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
176 ${_('The requested commit is too big and content was truncated.')} <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
177 </td>
177 </td>
178 </tr>
178 </tr>
179 %else:
179 %else:
180 %if over_lines_changed_limit:
180 %if over_lines_changed_limit:
181 <tr class="cb-warning cb-collapser">
181 <tr class="cb-warning cb-collapser">
182 <td class="cb-text" ${(c.diffmode == 'unified' and 'colspan=4' or 'colspan=6')}>
182 <td class="cb-text" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=6')}>
183 ${_('This diff has been collapsed as it changes many lines, (%i lines changed)' % lines_changed)}
183 ${_('This diff has been collapsed as it changes many lines, (%i lines changed)' % lines_changed)}
184 <a href="#" class="cb-expand"
184 <a href="#" class="cb-expand"
185 onclick="$(this).closest('table').removeClass('cb-collapsed'); return false;">${_('Show them')}
185 onclick="$(this).closest('table').removeClass('cb-collapsed'); return false;">${_('Show them')}
186 </a>
186 </a>
187 <a href="#" class="cb-collapse"
187 <a href="#" class="cb-collapse"
188 onclick="$(this).closest('table').addClass('cb-collapsed'); return false;">${_('Hide them')}
188 onclick="$(this).closest('table').addClass('cb-collapsed'); return false;">${_('Hide them')}
189 </a>
189 </a>
190 </td>
190 </td>
191 </tr>
191 </tr>
192 %endif
192 %endif
193 %endif
193 %endif
194
194
195 % for hunk in filediff.hunks:
195 % for hunk in filediff.hunks:
196 <tr class="cb-hunk">
196 <tr class="cb-hunk">
197 <td ${(c.diffmode == 'unified' and 'colspan=3' or '')}>
197 <td ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=3' or '')}>
198 ## TODO: dan: add ajax loading of more context here
198 ## TODO: dan: add ajax loading of more context here
199 ## <a href="#">
199 ## <a href="#">
200 <i class="icon-more"></i>
200 <i class="icon-more"></i>
201 ## </a>
201 ## </a>
202 </td>
202 </td>
203 <td ${(c.diffmode == 'sideside' and 'colspan=5' or '')}>
203 <td ${(c.user_session_attrs["diffmode"] == 'sideside' and 'colspan=5' or '')}>
204 @@
204 @@
205 -${hunk.source_start},${hunk.source_length}
205 -${hunk.source_start},${hunk.source_length}
206 +${hunk.target_start},${hunk.target_length}
206 +${hunk.target_start},${hunk.target_length}
207 ${hunk.section_header}
207 ${hunk.section_header}
208 </td>
208 </td>
209 </tr>
209 </tr>
210 ${render_hunk_lines(c.diffmode, hunk, use_comments=use_comments, inline_comments=inline_comments)}
210 ${render_hunk_lines(c.user_session_attrs["diffmode"], hunk, use_comments=use_comments, inline_comments=inline_comments)}
211 % endfor
211 % endfor
212
212
213 <% unmatched_comments = (inline_comments or {}).get(filediff.patch['filename'], {}) %>
213 <% unmatched_comments = (inline_comments or {}).get(filediff.patch['filename'], {}) %>
214
214
215 ## outdated comments that do not fit into currently displayed lines
215 ## outdated comments that do not fit into currently displayed lines
216 % for lineno, comments in unmatched_comments.items():
216 % for lineno, comments in unmatched_comments.items():
217
217
218 %if c.diffmode == 'unified':
218 %if c.user_session_attrs["diffmode"] == 'unified':
219 % if loop.index == 0:
219 % if loop.index == 0:
220 <tr class="cb-hunk">
220 <tr class="cb-hunk">
221 <td colspan="3"></td>
221 <td colspan="3"></td>
222 <td>
222 <td>
223 <div>
223 <div>
224 ${_('Unmatched inline comments below')}
224 ${_('Unmatched inline comments below')}
225 </div>
225 </div>
226 </td>
226 </td>
227 </tr>
227 </tr>
228 % endif
228 % endif
229 <tr class="cb-line">
229 <tr class="cb-line">
230 <td class="cb-data cb-context"></td>
230 <td class="cb-data cb-context"></td>
231 <td class="cb-lineno cb-context"></td>
231 <td class="cb-lineno cb-context"></td>
232 <td class="cb-lineno cb-context"></td>
232 <td class="cb-lineno cb-context"></td>
233 <td class="cb-content cb-context">
233 <td class="cb-content cb-context">
234 ${inline_comments_container(comments, inline_comments)}
234 ${inline_comments_container(comments, inline_comments)}
235 </td>
235 </td>
236 </tr>
236 </tr>
237 %elif c.diffmode == 'sideside':
237 %elif c.user_session_attrs["diffmode"] == 'sideside':
238 % if loop.index == 0:
238 % if loop.index == 0:
239 <tr class="cb-comment-info">
239 <tr class="cb-comment-info">
240 <td colspan="2"></td>
240 <td colspan="2"></td>
241 <td class="cb-line">
241 <td class="cb-line">
242 <div>
242 <div>
243 ${_('Unmatched inline comments below')}
243 ${_('Unmatched inline comments below')}
244 </div>
244 </div>
245 </td>
245 </td>
246 <td colspan="2"></td>
246 <td colspan="2"></td>
247 <td class="cb-line">
247 <td class="cb-line">
248 <div>
248 <div>
249 ${_('Unmatched comments below')}
249 ${_('Unmatched comments below')}
250 </div>
250 </div>
251 </td>
251 </td>
252 </tr>
252 </tr>
253 % endif
253 % endif
254 <tr class="cb-line">
254 <tr class="cb-line">
255 <td class="cb-data cb-context"></td>
255 <td class="cb-data cb-context"></td>
256 <td class="cb-lineno cb-context"></td>
256 <td class="cb-lineno cb-context"></td>
257 <td class="cb-content cb-context">
257 <td class="cb-content cb-context">
258 % if lineno.startswith('o'):
258 % if lineno.startswith('o'):
259 ${inline_comments_container(comments, inline_comments)}
259 ${inline_comments_container(comments, inline_comments)}
260 % endif
260 % endif
261 </td>
261 </td>
262
262
263 <td class="cb-data cb-context"></td>
263 <td class="cb-data cb-context"></td>
264 <td class="cb-lineno cb-context"></td>
264 <td class="cb-lineno cb-context"></td>
265 <td class="cb-content cb-context">
265 <td class="cb-content cb-context">
266 % if lineno.startswith('n'):
266 % if lineno.startswith('n'):
267 ${inline_comments_container(comments, inline_comments)}
267 ${inline_comments_container(comments, inline_comments)}
268 % endif
268 % endif
269 </td>
269 </td>
270 </tr>
270 </tr>
271 %endif
271 %endif
272
272
273 % endfor
273 % endfor
274
274
275 </table>
275 </table>
276 </div>
276 </div>
277 %endfor
277 %endfor
278
278
279 ## outdated comments that are made for a file that has been deleted
279 ## outdated comments that are made for a file that has been deleted
280 % for filename, comments_dict in (deleted_files_comments or {}).items():
280 % for filename, comments_dict in (deleted_files_comments or {}).items():
281 <%
281 <%
282 display_state = 'display: none'
282 display_state = 'display: none'
283 open_comments_in_file = [x for x in comments_dict['comments'] if x.outdated is False]
283 open_comments_in_file = [x for x in comments_dict['comments'] if x.outdated is False]
284 if open_comments_in_file:
284 if open_comments_in_file:
285 display_state = ''
285 display_state = ''
286 %>
286 %>
287 <div class="filediffs filediff-outdated" style="${display_state}">
287 <div class="filediffs filediff-outdated" style="${display_state}">
288 <input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state" id="filediff-collapse-${id(filename)}" type="checkbox">
288 <input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state" id="filediff-collapse-${id(filename)}" type="checkbox">
289 <div class="filediff" data-f-path="${filename}" id="a_${h.FID('', filename)}">
289 <div class="filediff" data-f-path="${filename}" id="a_${h.FID('', filename)}">
290 <label for="filediff-collapse-${id(filename)}" class="filediff-heading">
290 <label for="filediff-collapse-${id(filename)}" class="filediff-heading">
291 <div class="filediff-collapse-indicator"></div>
291 <div class="filediff-collapse-indicator"></div>
292 <span class="pill">
292 <span class="pill">
293 ## file was deleted
293 ## file was deleted
294 <strong>${filename}</strong>
294 <strong>${filename}</strong>
295 </span>
295 </span>
296 <span class="pill-group" style="float: left">
296 <span class="pill-group" style="float: left">
297 ## file op, doesn't need translation
297 ## file op, doesn't need translation
298 <span class="pill" op="removed">removed in this version</span>
298 <span class="pill" op="removed">removed in this version</span>
299 </span>
299 </span>
300 <a class="pill filediff-anchor" href="#a_${h.FID('', filename)}">ΒΆ</a>
300 <a class="pill filediff-anchor" href="#a_${h.FID('', filename)}">ΒΆ</a>
301 <span class="pill-group" style="float: right">
301 <span class="pill-group" style="float: right">
302 <span class="pill" op="deleted">-${comments_dict['stats']}</span>
302 <span class="pill" op="deleted">-${comments_dict['stats']}</span>
303 </span>
303 </span>
304 </label>
304 </label>
305
305
306 <table class="cb cb-diff-${c.diffmode} code-highlight ${over_lines_changed_limit and 'cb-collapsed' or ''}">
306 <table class="cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${over_lines_changed_limit and 'cb-collapsed' or ''}">
307 <tr>
307 <tr>
308 % if c.diffmode == 'unified':
308 % if c.user_session_attrs["diffmode"] == 'unified':
309 <td></td>
309 <td></td>
310 %endif
310 %endif
311
311
312 <td></td>
312 <td></td>
313 <td class="cb-text cb-${op_class(BIN_FILENODE)}" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=5'}>
313 <td class="cb-text cb-${op_class(BIN_FILENODE)}" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=5')}>
314 ${_('File was deleted in this version. There are still outdated/unresolved comments attached to it.')}
314 ${_('File was deleted in this version. There are still outdated/unresolved comments attached to it.')}
315 </td>
315 </td>
316 </tr>
316 </tr>
317 %if c.diffmode == 'unified':
317 %if c.user_session_attrs["diffmode"] == 'unified':
318 <tr class="cb-line">
318 <tr class="cb-line">
319 <td class="cb-data cb-context"></td>
319 <td class="cb-data cb-context"></td>
320 <td class="cb-lineno cb-context"></td>
320 <td class="cb-lineno cb-context"></td>
321 <td class="cb-lineno cb-context"></td>
321 <td class="cb-lineno cb-context"></td>
322 <td class="cb-content cb-context">
322 <td class="cb-content cb-context">
323 ${inline_comments_container(comments_dict['comments'], inline_comments)}
323 ${inline_comments_container(comments_dict['comments'], inline_comments)}
324 </td>
324 </td>
325 </tr>
325 </tr>
326 %elif c.diffmode == 'sideside':
326 %elif c.user_session_attrs["diffmode"] == 'sideside':
327 <tr class="cb-line">
327 <tr class="cb-line">
328 <td class="cb-data cb-context"></td>
328 <td class="cb-data cb-context"></td>
329 <td class="cb-lineno cb-context"></td>
329 <td class="cb-lineno cb-context"></td>
330 <td class="cb-content cb-context"></td>
330 <td class="cb-content cb-context"></td>
331
331
332 <td class="cb-data cb-context"></td>
332 <td class="cb-data cb-context"></td>
333 <td class="cb-lineno cb-context"></td>
333 <td class="cb-lineno cb-context"></td>
334 <td class="cb-content cb-context">
334 <td class="cb-content cb-context">
335 ${inline_comments_container(comments_dict['comments'], inline_comments)}
335 ${inline_comments_container(comments_dict['comments'], inline_comments)}
336 </td>
336 </td>
337 </tr>
337 </tr>
338 %endif
338 %endif
339 </table>
339 </table>
340 </div>
340 </div>
341 </div>
341 </div>
342 % endfor
342 % endfor
343
343
344 </div>
344 </div>
345 </div>
345 </div>
346 </%def>
346 </%def>
347
347
348 <%def name="diff_ops(filediff)">
348 <%def name="diff_ops(filediff)">
349 <%
349 <%
350 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
350 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
351 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
351 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
352 %>
352 %>
353 <span class="pill">
353 <span class="pill">
354 %if filediff.source_file_path and filediff.target_file_path:
354 %if filediff.source_file_path and filediff.target_file_path:
355 %if filediff.source_file_path != filediff.target_file_path:
355 %if filediff.source_file_path != filediff.target_file_path:
356 ## file was renamed, or copied
356 ## file was renamed, or copied
357 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
357 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
358 <strong>${filediff.target_file_path}</strong> β¬… <del>${filediff.source_file_path}</del>
358 <strong>${filediff.target_file_path}</strong> β¬… <del>${filediff.source_file_path}</del>
359 <% final_path = filediff.target_file_path %>
359 <% final_path = filediff.target_file_path %>
360 %elif COPIED_FILENODE in filediff.patch['stats']['ops']:
360 %elif COPIED_FILENODE in filediff.patch['stats']['ops']:
361 <strong>${filediff.target_file_path}</strong> β¬… ${filediff.source_file_path}
361 <strong>${filediff.target_file_path}</strong> β¬… ${filediff.source_file_path}
362 <% final_path = filediff.target_file_path %>
362 <% final_path = filediff.target_file_path %>
363 %endif
363 %endif
364 %else:
364 %else:
365 ## file was modified
365 ## file was modified
366 <strong>${filediff.source_file_path}</strong>
366 <strong>${filediff.source_file_path}</strong>
367 <% final_path = filediff.source_file_path %>
367 <% final_path = filediff.source_file_path %>
368 %endif
368 %endif
369 %else:
369 %else:
370 %if filediff.source_file_path:
370 %if filediff.source_file_path:
371 ## file was deleted
371 ## file was deleted
372 <strong>${filediff.source_file_path}</strong>
372 <strong>${filediff.source_file_path}</strong>
373 <% final_path = filediff.source_file_path %>
373 <% final_path = filediff.source_file_path %>
374 %else:
374 %else:
375 ## file was added
375 ## file was added
376 <strong>${filediff.target_file_path}</strong>
376 <strong>${filediff.target_file_path}</strong>
377 <% final_path = filediff.target_file_path %>
377 <% final_path = filediff.target_file_path %>
378 %endif
378 %endif
379 %endif
379 %endif
380 <i style="color: #aaa" class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${final_path}" title="${_('Copy the full path')}" onclick="return false;"></i>
380 <i style="color: #aaa" class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${final_path}" title="${_('Copy the full path')}" onclick="return false;"></i>
381 </span>
381 </span>
382 <span class="pill-group" style="float: left">
382 <span class="pill-group" style="float: left">
383 %if filediff.limited_diff:
383 %if filediff.limited_diff:
384 <span class="pill tooltip" op="limited" title="The stats for this diff are not complete">limited diff</span>
384 <span class="pill tooltip" op="limited" title="The stats for this diff are not complete">limited diff</span>
385 %endif
385 %endif
386
386
387 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
387 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
388 <span class="pill" op="renamed">renamed</span>
388 <span class="pill" op="renamed">renamed</span>
389 %endif
389 %endif
390
390
391 %if COPIED_FILENODE in filediff.patch['stats']['ops']:
391 %if COPIED_FILENODE in filediff.patch['stats']['ops']:
392 <span class="pill" op="copied">copied</span>
392 <span class="pill" op="copied">copied</span>
393 %endif
393 %endif
394
394
395 %if NEW_FILENODE in filediff.patch['stats']['ops']:
395 %if NEW_FILENODE in filediff.patch['stats']['ops']:
396 <span class="pill" op="created">created</span>
396 <span class="pill" op="created">created</span>
397 %if filediff['target_mode'].startswith('120'):
397 %if filediff['target_mode'].startswith('120'):
398 <span class="pill" op="symlink">symlink</span>
398 <span class="pill" op="symlink">symlink</span>
399 %else:
399 %else:
400 <span class="pill" op="mode">${nice_mode(filediff['target_mode'])}</span>
400 <span class="pill" op="mode">${nice_mode(filediff['target_mode'])}</span>
401 %endif
401 %endif
402 %endif
402 %endif
403
403
404 %if DEL_FILENODE in filediff.patch['stats']['ops']:
404 %if DEL_FILENODE in filediff.patch['stats']['ops']:
405 <span class="pill" op="removed">removed</span>
405 <span class="pill" op="removed">removed</span>
406 %endif
406 %endif
407
407
408 %if CHMOD_FILENODE in filediff.patch['stats']['ops']:
408 %if CHMOD_FILENODE in filediff.patch['stats']['ops']:
409 <span class="pill" op="mode">
409 <span class="pill" op="mode">
410 ${nice_mode(filediff['source_mode'])} ➑ ${nice_mode(filediff['target_mode'])}
410 ${nice_mode(filediff['source_mode'])} ➑ ${nice_mode(filediff['target_mode'])}
411 </span>
411 </span>
412 %endif
412 %endif
413 </span>
413 </span>
414
414
415 <a class="pill filediff-anchor" href="#a_${h.FID('', filediff.patch['filename'])}">ΒΆ</a>
415 <a class="pill filediff-anchor" href="#a_${h.FID('', filediff.patch['filename'])}">ΒΆ</a>
416
416
417 <span class="pill-group" style="float: right">
417 <span class="pill-group" style="float: right">
418 %if BIN_FILENODE in filediff.patch['stats']['ops']:
418 %if BIN_FILENODE in filediff.patch['stats']['ops']:
419 <span class="pill" op="binary">binary</span>
419 <span class="pill" op="binary">binary</span>
420 %if MOD_FILENODE in filediff.patch['stats']['ops']:
420 %if MOD_FILENODE in filediff.patch['stats']['ops']:
421 <span class="pill" op="modified">modified</span>
421 <span class="pill" op="modified">modified</span>
422 %endif
422 %endif
423 %endif
423 %endif
424 %if filediff.patch['stats']['added']:
424 %if filediff.patch['stats']['added']:
425 <span class="pill" op="added">+${filediff.patch['stats']['added']}</span>
425 <span class="pill" op="added">+${filediff.patch['stats']['added']}</span>
426 %endif
426 %endif
427 %if filediff.patch['stats']['deleted']:
427 %if filediff.patch['stats']['deleted']:
428 <span class="pill" op="deleted">-${filediff.patch['stats']['deleted']}</span>
428 <span class="pill" op="deleted">-${filediff.patch['stats']['deleted']}</span>
429 %endif
429 %endif
430 </span>
430 </span>
431
431
432 </%def>
432 </%def>
433
433
434 <%def name="nice_mode(filemode)">
434 <%def name="nice_mode(filemode)">
435 ${filemode.startswith('100') and filemode[3:] or filemode}
435 ${filemode.startswith('100') and filemode[3:] or filemode}
436 </%def>
436 </%def>
437
437
438 <%def name="diff_menu(filediff, use_comments=False)">
438 <%def name="diff_menu(filediff, use_comments=False)">
439 <div class="filediff-menu">
439 <div class="filediff-menu">
440 %if filediff.diffset.source_ref:
440 %if filediff.diffset.source_ref:
441 %if filediff.operation in ['D', 'M']:
441 %if filediff.operation in ['D', 'M']:
442 <a
442 <a
443 class="tooltip"
443 class="tooltip"
444 href="${h.route_path('repo_files',repo_name=filediff.diffset.repo_name,commit_id=filediff.diffset.source_ref,f_path=filediff.source_file_path)}"
444 href="${h.route_path('repo_files',repo_name=filediff.diffset.repo_name,commit_id=filediff.diffset.source_ref,f_path=filediff.source_file_path)}"
445 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
445 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
446 >
446 >
447 ${_('Show file before')}
447 ${_('Show file before')}
448 </a> |
448 </a> |
449 %else:
449 %else:
450 <span
450 <span
451 class="tooltip"
451 class="tooltip"
452 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
452 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
453 >
453 >
454 ${_('Show file before')}
454 ${_('Show file before')}
455 </span> |
455 </span> |
456 %endif
456 %endif
457 %if filediff.operation in ['A', 'M']:
457 %if filediff.operation in ['A', 'M']:
458 <a
458 <a
459 class="tooltip"
459 class="tooltip"
460 href="${h.route_path('repo_files',repo_name=filediff.diffset.source_repo_name,commit_id=filediff.diffset.target_ref,f_path=filediff.target_file_path)}"
460 href="${h.route_path('repo_files',repo_name=filediff.diffset.source_repo_name,commit_id=filediff.diffset.target_ref,f_path=filediff.target_file_path)}"
461 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
461 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
462 >
462 >
463 ${_('Show file after')}
463 ${_('Show file after')}
464 </a> |
464 </a> |
465 %else:
465 %else:
466 <span
466 <span
467 class="tooltip"
467 class="tooltip"
468 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
468 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
469 >
469 >
470 ${_('Show file after')}
470 ${_('Show file after')}
471 </span> |
471 </span> |
472 %endif
472 %endif
473 <a
473 <a
474 class="tooltip"
474 class="tooltip"
475 title="${h.tooltip(_('Raw diff'))}"
475 title="${h.tooltip(_('Raw diff'))}"
476 href="${h.route_path('repo_files_diff',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path, _query=dict(diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='raw'))}"
476 href="${h.route_path('repo_files_diff',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path, _query=dict(diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='raw'))}"
477 >
477 >
478 ${_('Raw diff')}
478 ${_('Raw diff')}
479 </a> |
479 </a> |
480 <a
480 <a
481 class="tooltip"
481 class="tooltip"
482 title="${h.tooltip(_('Download diff'))}"
482 title="${h.tooltip(_('Download diff'))}"
483 href="${h.route_path('repo_files_diff',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path, _query=dict(diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='download'))}"
483 href="${h.route_path('repo_files_diff',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path, _query=dict(diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='download'))}"
484 >
484 >
485 ${_('Download diff')}
485 ${_('Download diff')}
486 </a>
486 </a>
487 % if use_comments:
487 % if use_comments:
488 |
488 |
489 % endif
489 % endif
490
490
491 ## TODO: dan: refactor ignorews_url and context_url into the diff renderer same as diffmode=unified/sideside. Also use ajax to load more context (by clicking hunks)
491 ## TODO: dan: refactor ignorews_url and context_url into the diff renderer same as diffmode=unified/sideside. Also use ajax to load more context (by clicking hunks)
492 %if hasattr(c, 'ignorews_url'):
492 %if hasattr(c, 'ignorews_url'):
493 ${c.ignorews_url(request, h.FID('', filediff.patch['filename']))}
493 ${c.ignorews_url(request, h.FID('', filediff.patch['filename']))}
494 %endif
494 %endif
495 %if hasattr(c, 'context_url'):
495 %if hasattr(c, 'context_url'):
496 ${c.context_url(request, h.FID('', filediff.patch['filename']))}
496 ${c.context_url(request, h.FID('', filediff.patch['filename']))}
497 %endif
497 %endif
498
498
499 %if use_comments:
499 %if use_comments:
500 <a href="#" onclick="return Rhodecode.comments.toggleComments(this);">
500 <a href="#" onclick="return Rhodecode.comments.toggleComments(this);">
501 <span class="show-comment-button">${_('Show comments')}</span><span class="hide-comment-button">${_('Hide comments')}</span>
501 <span class="show-comment-button">${_('Show comments')}</span><span class="hide-comment-button">${_('Hide comments')}</span>
502 </a>
502 </a>
503 %endif
503 %endif
504 %endif
504 %endif
505 </div>
505 </div>
506 </%def>
506 </%def>
507
507
508
508
509 <%def name="inline_comments_container(comments, inline_comments)">
509 <%def name="inline_comments_container(comments, inline_comments)">
510 <div class="inline-comments">
510 <div class="inline-comments">
511 %for comment in comments:
511 %for comment in comments:
512 ${commentblock.comment_block(comment, inline=True)}
512 ${commentblock.comment_block(comment, inline=True)}
513 %endfor
513 %endfor
514 % if comments and comments[-1].outdated:
514 % if comments and comments[-1].outdated:
515 <span class="btn btn-secondary cb-comment-add-button comment-outdated}"
515 <span class="btn btn-secondary cb-comment-add-button comment-outdated}"
516 style="display: none;}">
516 style="display: none;}">
517 ${_('Add another comment')}
517 ${_('Add another comment')}
518 </span>
518 </span>
519 % else:
519 % else:
520 <span onclick="return Rhodecode.comments.createComment(this)"
520 <span onclick="return Rhodecode.comments.createComment(this)"
521 class="btn btn-secondary cb-comment-add-button">
521 class="btn btn-secondary cb-comment-add-button">
522 ${_('Add another comment')}
522 ${_('Add another comment')}
523 </span>
523 </span>
524 % endif
524 % endif
525
525
526 </div>
526 </div>
527 </%def>
527 </%def>
528
528
529 <%!
529 <%!
530 def get_comments_for(diff_type, comments, filename, line_version, line_number):
530 def get_comments_for(diff_type, comments, filename, line_version, line_number):
531 if hasattr(filename, 'unicode_path'):
531 if hasattr(filename, 'unicode_path'):
532 filename = filename.unicode_path
532 filename = filename.unicode_path
533
533
534 if not isinstance(filename, basestring):
534 if not isinstance(filename, basestring):
535 return None
535 return None
536
536
537 line_key = '{}{}'.format(line_version, line_number) ## e.g o37, n12
537 line_key = '{}{}'.format(line_version, line_number) ## e.g o37, n12
538
538
539 if comments and filename in comments:
539 if comments and filename in comments:
540 file_comments = comments[filename]
540 file_comments = comments[filename]
541 if line_key in file_comments:
541 if line_key in file_comments:
542 data = file_comments.pop(line_key)
542 data = file_comments.pop(line_key)
543 return data
543 return data
544 %>
544 %>
545
545
546 <%def name="render_hunk_lines_sideside(hunk, use_comments=False, inline_comments=None)">
546 <%def name="render_hunk_lines_sideside(hunk, use_comments=False, inline_comments=None)">
547
547
548 %for i, line in enumerate(hunk.sideside):
548 %for i, line in enumerate(hunk.sideside):
549 <%
549 <%
550 old_line_anchor, new_line_anchor = None, None
550 old_line_anchor, new_line_anchor = None, None
551 if line.original.lineno:
551 if line.original.lineno:
552 old_line_anchor = diff_line_anchor(hunk.source_file_path, line.original.lineno, 'o')
552 old_line_anchor = diff_line_anchor(hunk.source_file_path, line.original.lineno, 'o')
553 if line.modified.lineno:
553 if line.modified.lineno:
554 new_line_anchor = diff_line_anchor(hunk.target_file_path, line.modified.lineno, 'n')
554 new_line_anchor = diff_line_anchor(hunk.target_file_path, line.modified.lineno, 'n')
555 %>
555 %>
556
556
557 <tr class="cb-line">
557 <tr class="cb-line">
558 <td class="cb-data ${action_class(line.original.action)}"
558 <td class="cb-data ${action_class(line.original.action)}"
559 data-line-no="${line.original.lineno}"
559 data-line-no="${line.original.lineno}"
560 >
560 >
561 <div>
561 <div>
562
562
563 <% line_old_comments = None %>
563 <% line_old_comments = None %>
564 %if line.original.get_comment_args:
564 %if line.original.get_comment_args:
565 <% line_old_comments = get_comments_for('side-by-side', inline_comments, *line.original.get_comment_args) %>
565 <% line_old_comments = get_comments_for('side-by-side', inline_comments, *line.original.get_comment_args) %>
566 %endif
566 %endif
567 %if line_old_comments:
567 %if line_old_comments:
568 <% has_outdated = any([x.outdated for x in line_old_comments]) %>
568 <% has_outdated = any([x.outdated for x in line_old_comments]) %>
569 % if has_outdated:
569 % if has_outdated:
570 <i title="${_('comments including outdated')}:${len(line_old_comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
570 <i title="${_('comments including outdated')}:${len(line_old_comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
571 % else:
571 % else:
572 <i title="${_('comments')}: ${len(line_old_comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
572 <i title="${_('comments')}: ${len(line_old_comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
573 % endif
573 % endif
574 %endif
574 %endif
575 </div>
575 </div>
576 </td>
576 </td>
577 <td class="cb-lineno ${action_class(line.original.action)}"
577 <td class="cb-lineno ${action_class(line.original.action)}"
578 data-line-no="${line.original.lineno}"
578 data-line-no="${line.original.lineno}"
579 %if old_line_anchor:
579 %if old_line_anchor:
580 id="${old_line_anchor}"
580 id="${old_line_anchor}"
581 %endif
581 %endif
582 >
582 >
583 %if line.original.lineno:
583 %if line.original.lineno:
584 <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
584 <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
585 %endif
585 %endif
586 </td>
586 </td>
587 <td class="cb-content ${action_class(line.original.action)}"
587 <td class="cb-content ${action_class(line.original.action)}"
588 data-line-no="o${line.original.lineno}"
588 data-line-no="o${line.original.lineno}"
589 >
589 >
590 %if use_comments and line.original.lineno:
590 %if use_comments and line.original.lineno:
591 ${render_add_comment_button()}
591 ${render_add_comment_button()}
592 %endif
592 %endif
593 <span class="cb-code">${line.original.action} ${line.original.content or '' | n}</span>
593 <span class="cb-code">${line.original.action} ${line.original.content or '' | n}</span>
594
594
595 %if use_comments and line.original.lineno and line_old_comments:
595 %if use_comments and line.original.lineno and line_old_comments:
596 ${inline_comments_container(line_old_comments, inline_comments)}
596 ${inline_comments_container(line_old_comments, inline_comments)}
597 %endif
597 %endif
598
598
599 </td>
599 </td>
600 <td class="cb-data ${action_class(line.modified.action)}"
600 <td class="cb-data ${action_class(line.modified.action)}"
601 data-line-no="${line.modified.lineno}"
601 data-line-no="${line.modified.lineno}"
602 >
602 >
603 <div>
603 <div>
604
604
605 %if line.modified.get_comment_args:
605 %if line.modified.get_comment_args:
606 <% line_new_comments = get_comments_for('side-by-side', inline_comments, *line.modified.get_comment_args) %>
606 <% line_new_comments = get_comments_for('side-by-side', inline_comments, *line.modified.get_comment_args) %>
607 %else:
607 %else:
608 <% line_new_comments = None%>
608 <% line_new_comments = None%>
609 %endif
609 %endif
610 %if line_new_comments:
610 %if line_new_comments:
611 <% has_outdated = any([x.outdated for x in line_new_comments]) %>
611 <% has_outdated = any([x.outdated for x in line_new_comments]) %>
612 % if has_outdated:
612 % if has_outdated:
613 <i title="${_('comments including outdated')}:${len(line_new_comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
613 <i title="${_('comments including outdated')}:${len(line_new_comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
614 % else:
614 % else:
615 <i title="${_('comments')}: ${len(line_new_comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
615 <i title="${_('comments')}: ${len(line_new_comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
616 % endif
616 % endif
617 %endif
617 %endif
618 </div>
618 </div>
619 </td>
619 </td>
620 <td class="cb-lineno ${action_class(line.modified.action)}"
620 <td class="cb-lineno ${action_class(line.modified.action)}"
621 data-line-no="${line.modified.lineno}"
621 data-line-no="${line.modified.lineno}"
622 %if new_line_anchor:
622 %if new_line_anchor:
623 id="${new_line_anchor}"
623 id="${new_line_anchor}"
624 %endif
624 %endif
625 >
625 >
626 %if line.modified.lineno:
626 %if line.modified.lineno:
627 <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
627 <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
628 %endif
628 %endif
629 </td>
629 </td>
630 <td class="cb-content ${action_class(line.modified.action)}"
630 <td class="cb-content ${action_class(line.modified.action)}"
631 data-line-no="n${line.modified.lineno}"
631 data-line-no="n${line.modified.lineno}"
632 >
632 >
633 %if use_comments and line.modified.lineno:
633 %if use_comments and line.modified.lineno:
634 ${render_add_comment_button()}
634 ${render_add_comment_button()}
635 %endif
635 %endif
636 <span class="cb-code">${line.modified.action} ${line.modified.content or '' | n}</span>
636 <span class="cb-code">${line.modified.action} ${line.modified.content or '' | n}</span>
637 %if use_comments and line.modified.lineno and line_new_comments:
637 %if use_comments and line.modified.lineno and line_new_comments:
638 ${inline_comments_container(line_new_comments, inline_comments)}
638 ${inline_comments_container(line_new_comments, inline_comments)}
639 %endif
639 %endif
640 </td>
640 </td>
641 </tr>
641 </tr>
642 %endfor
642 %endfor
643 </%def>
643 </%def>
644
644
645
645
646 <%def name="render_hunk_lines_unified(hunk, use_comments=False, inline_comments=None)">
646 <%def name="render_hunk_lines_unified(hunk, use_comments=False, inline_comments=None)">
647 %for old_line_no, new_line_no, action, content, comments_args in hunk.unified:
647 %for old_line_no, new_line_no, action, content, comments_args in hunk.unified:
648 <%
648 <%
649 old_line_anchor, new_line_anchor = None, None
649 old_line_anchor, new_line_anchor = None, None
650 if old_line_no:
650 if old_line_no:
651 old_line_anchor = diff_line_anchor(hunk.source_file_path, old_line_no, 'o')
651 old_line_anchor = diff_line_anchor(hunk.source_file_path, old_line_no, 'o')
652 if new_line_no:
652 if new_line_no:
653 new_line_anchor = diff_line_anchor(hunk.target_file_path, new_line_no, 'n')
653 new_line_anchor = diff_line_anchor(hunk.target_file_path, new_line_no, 'n')
654 %>
654 %>
655 <tr class="cb-line">
655 <tr class="cb-line">
656 <td class="cb-data ${action_class(action)}">
656 <td class="cb-data ${action_class(action)}">
657 <div>
657 <div>
658
658
659 %if comments_args:
659 %if comments_args:
660 <% comments = get_comments_for('unified', inline_comments, *comments_args) %>
660 <% comments = get_comments_for('unified', inline_comments, *comments_args) %>
661 %else:
661 %else:
662 <% comments = None %>
662 <% comments = None %>
663 %endif
663 %endif
664
664
665 % if comments:
665 % if comments:
666 <% has_outdated = any([x.outdated for x in comments]) %>
666 <% has_outdated = any([x.outdated for x in comments]) %>
667 % if has_outdated:
667 % if has_outdated:
668 <i title="${_('comments including outdated')}:${len(comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
668 <i title="${_('comments including outdated')}:${len(comments)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
669 % else:
669 % else:
670 <i title="${_('comments')}: ${len(comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
670 <i title="${_('comments')}: ${len(comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
671 % endif
671 % endif
672 % endif
672 % endif
673 </div>
673 </div>
674 </td>
674 </td>
675 <td class="cb-lineno ${action_class(action)}"
675 <td class="cb-lineno ${action_class(action)}"
676 data-line-no="${old_line_no}"
676 data-line-no="${old_line_no}"
677 %if old_line_anchor:
677 %if old_line_anchor:
678 id="${old_line_anchor}"
678 id="${old_line_anchor}"
679 %endif
679 %endif
680 >
680 >
681 %if old_line_anchor:
681 %if old_line_anchor:
682 <a name="${old_line_anchor}" href="#${old_line_anchor}">${old_line_no}</a>
682 <a name="${old_line_anchor}" href="#${old_line_anchor}">${old_line_no}</a>
683 %endif
683 %endif
684 </td>
684 </td>
685 <td class="cb-lineno ${action_class(action)}"
685 <td class="cb-lineno ${action_class(action)}"
686 data-line-no="${new_line_no}"
686 data-line-no="${new_line_no}"
687 %if new_line_anchor:
687 %if new_line_anchor:
688 id="${new_line_anchor}"
688 id="${new_line_anchor}"
689 %endif
689 %endif
690 >
690 >
691 %if new_line_anchor:
691 %if new_line_anchor:
692 <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
692 <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
693 %endif
693 %endif
694 </td>
694 </td>
695 <td class="cb-content ${action_class(action)}"
695 <td class="cb-content ${action_class(action)}"
696 data-line-no="${new_line_no and 'n' or 'o'}${new_line_no or old_line_no}"
696 data-line-no="${new_line_no and 'n' or 'o'}${new_line_no or old_line_no}"
697 >
697 >
698 %if use_comments:
698 %if use_comments:
699 ${render_add_comment_button()}
699 ${render_add_comment_button()}
700 %endif
700 %endif
701 <span class="cb-code">${action} ${content or '' | n}</span>
701 <span class="cb-code">${action} ${content or '' | n}</span>
702 %if use_comments and comments:
702 %if use_comments and comments:
703 ${inline_comments_container(comments, inline_comments)}
703 ${inline_comments_container(comments, inline_comments)}
704 %endif
704 %endif
705 </td>
705 </td>
706 </tr>
706 </tr>
707 %endfor
707 %endfor
708 </%def>
708 </%def>
709
709
710
710
711 <%def name="render_hunk_lines(diff_mode, hunk, use_comments, inline_comments)">
711 <%def name="render_hunk_lines(diff_mode, hunk, use_comments, inline_comments)">
712 % if diff_mode == 'unified':
712 % if diff_mode == 'unified':
713 ${render_hunk_lines_unified(hunk, use_comments=use_comments, inline_comments=inline_comments)}
713 ${render_hunk_lines_unified(hunk, use_comments=use_comments, inline_comments=inline_comments)}
714 % elif diff_mode == 'sideside':
714 % elif diff_mode == 'sideside':
715 ${render_hunk_lines_sideside(hunk, use_comments=use_comments, inline_comments=inline_comments)}
715 ${render_hunk_lines_sideside(hunk, use_comments=use_comments, inline_comments=inline_comments)}
716 % else:
716 % else:
717 <tr class="cb-line">
717 <tr class="cb-line">
718 <td>unknown diff mode</td>
718 <td>unknown diff mode</td>
719 </tr>
719 </tr>
720 % endif
720 % endif
721 </%def>
721 </%def>
722
722
723
723
724 <%def name="render_add_comment_button()">
724 <%def name="render_add_comment_button()">
725 <button class="btn btn-small btn-primary cb-comment-box-opener" onclick="return Rhodecode.comments.createComment(this)">
725 <button class="btn btn-small btn-primary cb-comment-box-opener" onclick="return Rhodecode.comments.createComment(this)">
726 <span><i class="icon-comment"></i></span>
726 <span><i class="icon-comment"></i></span>
727 </button>
727 </button>
728 </%def>
728 </%def>
729
729
730 <%def name="render_diffset_menu()">
730 <%def name="render_diffset_menu()">
731
731
732 <div class="diffset-menu clearinner">
732 <div class="diffset-menu clearinner">
733 <div class="pull-right">
733 <div class="pull-right">
734 <div class="btn-group">
734 <div class="btn-group">
735
735
736 <a
736 <a
737 class="btn ${c.diffmode == 'sideside' and 'btn-primary'} tooltip"
737 class="btn ${(c.user_session_attrs["diffmode"] == 'sideside' and 'btn-primary')} tooltip"
738 title="${h.tooltip(_('View side by side'))}"
738 title="${h.tooltip(_('View side by side'))}"
739 href="${h.current_route_path(request, diffmode='sideside')}">
739 href="${h.current_route_path(request, diffmode='sideside')}">
740 <span>${_('Side by Side')}</span>
740 <span>${_('Side by Side')}</span>
741 </a>
741 </a>
742 <a
742 <a
743 class="btn ${c.diffmode == 'unified' and 'btn-primary'} tooltip"
743 class="btn ${(c.user_session_attrs["diffmode"] == 'unified' and 'btn-primary')} tooltip"
744 title="${h.tooltip(_('View unified'))}" href="${h.current_route_path(request, diffmode='unified')}">
744 title="${h.tooltip(_('View unified'))}" href="${h.current_route_path(request, diffmode='unified')}">
745 <span>${_('Unified')}</span>
745 <span>${_('Unified')}</span>
746 </a>
746 </a>
747 </div>
747 </div>
748 </div>
748 </div>
749
749
750 <div class="pull-left">
750 <div class="pull-left">
751 <div class="btn-group">
751 <div class="btn-group">
752 <a
752 <a
753 class="btn"
753 class="btn"
754 href="#"
754 href="#"
755 onclick="$('input[class=filediff-collapse-state]').prop('checked', false); return false">${_('Expand All Files')}</a>
755 onclick="$('input[class=filediff-collapse-state]').prop('checked', false); return false">${_('Expand All Files')}</a>
756 <a
756 <a
757 class="btn"
757 class="btn"
758 href="#"
758 href="#"
759 onclick="$('input[class=filediff-collapse-state]').prop('checked', true); return false">${_('Collapse All Files')}</a>
759 onclick="$('input[class=filediff-collapse-state]').prop('checked', true); return false">${_('Collapse All Files')}</a>
760 <a
760 <a
761 class="btn"
761 class="btn"
762 href="#"
762 href="#"
763 onclick="return Rhodecode.comments.toggleWideMode(this)">${_('Wide Mode Diff')}</a>
763 onclick="return Rhodecode.comments.toggleWideMode(this)">${_('Wide Mode Diff')}</a>
764 </div>
764 </div>
765 </div>
765 </div>
766 </div>
766 </div>
767 </%def>
767 </%def>
@@ -1,119 +1,129 b''
1 <%inherit file="/summary/summary_base.mako"/>
1 <%inherit file="/summary/summary_base.mako"/>
2
2
3 <%namespace name="components" file="/summary/components.mako"/>
3 <%namespace name="components" file="/summary/components.mako"/>
4
4
5
5
6 <%def name="menu_bar_subnav()">
6 <%def name="menu_bar_subnav()">
7 ${self.repo_menu(active='summary')}
7 ${self.repo_menu(active='summary')}
8 </%def>
8 </%def>
9
9
10 <%def name="main()">
10 <%def name="main()">
11
11
12 <div class="title">
12 <div class="title">
13 ${self.repo_page_title(c.rhodecode_db_repo)}
13 ${self.repo_page_title(c.rhodecode_db_repo)}
14 <ul class="links icon-only-links block-right">
14 <ul class="links icon-only-links block-right">
15 <li>
15 <li>
16 %if c.rhodecode_user.username != h.DEFAULT_USER:
16 %if c.rhodecode_user.username != h.DEFAULT_USER:
17 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}"><i class="icon-rss-sign"></i></a>
17 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}"><i class="icon-rss-sign"></i></a>
18 %else:
18 %else:
19 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name)}" title="${_('RSS Feed')}"><i class="icon-rss-sign"></i></a>
19 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name)}" title="${_('RSS Feed')}"><i class="icon-rss-sign"></i></a>
20 %endif
20 %endif
21 </li>
21 </li>
22 </ul>
22 </ul>
23 </div>
23 </div>
24
24
25 <div id="repo-summary" class="summary">
25 <div id="repo-summary" class="summary">
26 ${components.summary_detail(breadcrumbs_links=self.breadcrumbs_links(), show_downloads=True)}
26 ${components.summary_detail(breadcrumbs_links=self.breadcrumbs_links(), show_downloads=True)}
27 ${components.summary_stats(gravatar_function=self.gravatar_with_user)}
27 ${components.summary_stats(gravatar_function=self.gravatar_with_user)}
28 </div><!--end repo-summary-->
28 </div><!--end repo-summary-->
29
29
30
30
31 <div class="box" >
31 <div class="box" >
32 %if not c.repo_commits:
32 %if not c.repo_commits:
33 <div class="title">
33 <div class="title">
34 <h3>${_('Quick start')}</h3>
34 <h3>${_('Quick start')}</h3>
35 </div>
35 </div>
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='summary_commits.mako'/>
39 <%include file='summary_commits.mako'/>
40 </div>
40 </div>
41 </div>
41 </div>
42 </div>
42 </div>
43
43
44 %if c.readme_data:
44 %if c.readme_data:
45 <div id="readme" class="anchor">
45 <div id="readme" class="anchor">
46 <div class="box" >
46 <div class="box" >
47 <div class="title" title="${h.tooltip(_('Readme file from commit %s:%s') % (c.rhodecode_db_repo.landing_rev[0], c.rhodecode_db_repo.landing_rev[1]))}">
47 <div class="title" title="${h.tooltip(_('Readme file from commit %s:%s') % (c.rhodecode_db_repo.landing_rev[0], c.rhodecode_db_repo.landing_rev[1]))}">
48 <h3 class="breadcrumbs">
48 <h3 class="breadcrumbs">
49 <a href="${h.route_path('repo_files',repo_name=c.repo_name,commit_id=c.rhodecode_db_repo.landing_rev[1],f_path=c.readme_file)}">${c.readme_file}</a>
49 <a href="${h.route_path('repo_files',repo_name=c.repo_name,commit_id=c.rhodecode_db_repo.landing_rev[1],f_path=c.readme_file)}">${c.readme_file}</a>
50 </h3>
50 </h3>
51 </div>
51 </div>
52 <div class="readme codeblock">
52 <div class="readme codeblock">
53 <div class="readme_box">
53 <div class="readme_box">
54 ${c.readme_data|n}
54 ${c.readme_data|n}
55 </div>
55 </div>
56 </div>
56 </div>
57 </div>
57 </div>
58 </div>
58 </div>
59 %endif
59 %endif
60
60
61 <script type="text/javascript">
61 <script type="text/javascript">
62 $(document).ready(function(){
62 $(document).ready(function(){
63 $('#clone_option').on('change', function(e) {
63
64 var selected = $(this).val();
64 var showCloneField = function(clone_url_format){
65 $.each(['http', 'http_id', 'ssh'], function (idx, val) {
65 $.each(['http', 'http_id', 'ssh'], function (idx, val) {
66 if(val === selected){
66 if(val === clone_url_format){
67 $('#clone_option_' + val).show();
67 $('#clone_option_' + val).show();
68 $('#clone_option').val(val)
68 } else {
69 } else {
69 $('#clone_option_' + val).hide();
70 $('#clone_option_' + val).hide();
70 }
71 }
71 });
72 });
73 };
74 // default taken from session
75 showCloneField(templateContext.session_attrs.clone_url_format);
76
77 $('#clone_option').on('change', function(e) {
78 var selected = $(this).val();
79
80 storeUserSessionAttr('rc_user_session_attr.clone_url_format', selected);
81 showCloneField(selected)
72 });
82 });
73
83
74 var initialCommitData = {
84 var initialCommitData = {
75 id: null,
85 id: null,
76 text: 'tip',
86 text: 'tip',
77 type: 'tag',
87 type: 'tag',
78 raw_id: null,
88 raw_id: null,
79 files_url: null
89 files_url: null
80 };
90 };
81
91
82 select2RefSwitcher('#download_options', initialCommitData);
92 select2RefSwitcher('#download_options', initialCommitData);
83
93
84 // on change of download options
94 // on change of download options
85 $('#download_options').on('change', function(e) {
95 $('#download_options').on('change', function(e) {
86 // format of Object {text: "v0.0.3", type: "tag", id: "rev"}
96 // format of Object {text: "v0.0.3", type: "tag", id: "rev"}
87 var ext = '.zip';
97 var ext = '.zip';
88 var selected_cs = e.added;
98 var selected_cs = e.added;
89 var fname = e.added.raw_id + ext;
99 var fname = e.added.raw_id + ext;
90 var href = pyroutes.url('repo_archivefile', {'repo_name': templateContext.repo_name, 'fname':fname});
100 var href = pyroutes.url('repo_archivefile', {'repo_name': templateContext.repo_name, 'fname':fname});
91 // set new label
101 // set new label
92 $('#archive_link').html('<i class="icon-archive"></i> {0}{1}'.format(escapeHtml(e.added.text), ext));
102 $('#archive_link').html('<i class="icon-archive"></i> {0}{1}'.format(escapeHtml(e.added.text), ext));
93
103
94 // set new url to button,
104 // set new url to button,
95 $('#archive_link').attr('href', href)
105 $('#archive_link').attr('href', href)
96 });
106 });
97
107
98
108
99 // load details on summary page expand
109 // load details on summary page expand
100 $('#summary_details_expand').on('click', function() {
110 $('#summary_details_expand').on('click', function() {
101
111
102 var callback = function (data) {
112 var callback = function (data) {
103 % if c.show_stats:
113 % if c.show_stats:
104 showRepoStats('lang_stats', data);
114 showRepoStats('lang_stats', data);
105 % endif
115 % endif
106 };
116 };
107
117
108 showRepoSize(
118 showRepoSize(
109 'repo_size_container',
119 'repo_size_container',
110 templateContext.repo_name,
120 templateContext.repo_name,
111 templateContext.repo_landing_commit,
121 templateContext.repo_landing_commit,
112 callback);
122 callback);
113
123
114 })
124 })
115
125
116 })
126 })
117 </script>
127 </script>
118
128
119 </%def>
129 </%def>
General Comments 0
You need to be logged in to leave comments. Login now