##// END OF EJS Templates
pyramid: expose a temporary TemplateContext under `_c`
marcink -
r1924:9104b27d default
parent child Browse files
Show More
@@ -1,372 +1,376 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 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 time
21 import time
22 import logging
22 import logging
23
23
24 from pyramid.httpexceptions import HTTPFound
24 from pyramid.httpexceptions import HTTPFound
25
25
26 from rhodecode.lib import helpers as h
26 from rhodecode.lib import helpers as h
27 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int, datetime_to_time
27 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int, datetime_to_time
28 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
28 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
29 from rhodecode.model import repo
29 from rhodecode.model import repo
30 from rhodecode.model import repo_group
30 from rhodecode.model import repo_group
31 from rhodecode.model.db import User
31 from rhodecode.model.db import User
32 from rhodecode.model.scm import ScmModel
32 from rhodecode.model.scm import ScmModel
33
33
34 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
35
35
36
36
37 ADMIN_PREFIX = '/_admin'
37 ADMIN_PREFIX = '/_admin'
38 STATIC_FILE_PREFIX = '/_static'
38 STATIC_FILE_PREFIX = '/_static'
39
39
40
40
41 def add_route_with_slash(config,name, pattern, **kw):
41 def add_route_with_slash(config,name, pattern, **kw):
42 config.add_route(name, pattern, **kw)
42 config.add_route(name, pattern, **kw)
43 if not pattern.endswith('/'):
43 if not pattern.endswith('/'):
44 config.add_route(name + '_slash', pattern + '/', **kw)
44 config.add_route(name + '_slash', pattern + '/', **kw)
45
45
46
46
47 def get_format_ref_id(repo):
47 def get_format_ref_id(repo):
48 """Returns a `repo` specific reference formatter function"""
48 """Returns a `repo` specific reference formatter function"""
49 if h.is_svn(repo):
49 if h.is_svn(repo):
50 return _format_ref_id_svn
50 return _format_ref_id_svn
51 else:
51 else:
52 return _format_ref_id
52 return _format_ref_id
53
53
54
54
55 def _format_ref_id(name, raw_id):
55 def _format_ref_id(name, raw_id):
56 """Default formatting of a given reference `name`"""
56 """Default formatting of a given reference `name`"""
57 return name
57 return name
58
58
59
59
60 def _format_ref_id_svn(name, raw_id):
60 def _format_ref_id_svn(name, raw_id):
61 """Special way of formatting a reference for Subversion including path"""
61 """Special way of formatting a reference for Subversion including path"""
62 return '%s@%s' % (name, raw_id)
62 return '%s@%s' % (name, raw_id)
63
63
64
64
65 class TemplateArgs(StrictAttributeDict):
65 class TemplateArgs(StrictAttributeDict):
66 pass
66 pass
67
67
68
68
69 class BaseAppView(object):
69 class BaseAppView(object):
70
70
71 def __init__(self, context, request):
71 def __init__(self, context, request):
72 self.request = request
72 self.request = request
73 self.context = context
73 self.context = context
74 self.session = request.session
74 self.session = request.session
75 self._rhodecode_user = request.user # auth user
75 self._rhodecode_user = request.user # auth user
76 self._rhodecode_db_user = self._rhodecode_user.get_instance()
76 self._rhodecode_db_user = self._rhodecode_user.get_instance()
77 self._maybe_needs_password_change(
77 self._maybe_needs_password_change(
78 request.matched_route.name, self._rhodecode_db_user)
78 request.matched_route.name, self._rhodecode_db_user)
79
79
80 def _maybe_needs_password_change(self, view_name, user_obj):
80 def _maybe_needs_password_change(self, view_name, user_obj):
81 log.debug('Checking if user %s needs password change on view %s',
81 log.debug('Checking if user %s needs password change on view %s',
82 user_obj, view_name)
82 user_obj, view_name)
83 skip_user_views = [
83 skip_user_views = [
84 'logout', 'login',
84 'logout', 'login',
85 'my_account_password', 'my_account_password_update'
85 'my_account_password', 'my_account_password_update'
86 ]
86 ]
87
87
88 if not user_obj:
88 if not user_obj:
89 return
89 return
90
90
91 if user_obj.username == User.DEFAULT_USER:
91 if user_obj.username == User.DEFAULT_USER:
92 return
92 return
93
93
94 now = time.time()
94 now = time.time()
95 should_change = user_obj.user_data.get('force_password_change')
95 should_change = user_obj.user_data.get('force_password_change')
96 change_after = safe_int(should_change) or 0
96 change_after = safe_int(should_change) or 0
97 if should_change and now > change_after:
97 if should_change and now > change_after:
98 log.debug('User %s requires password change', user_obj)
98 log.debug('User %s requires password change', user_obj)
99 h.flash('You are required to change your password', 'warning',
99 h.flash('You are required to change your password', 'warning',
100 ignore_duplicate=True)
100 ignore_duplicate=True)
101
101
102 if view_name not in skip_user_views:
102 if view_name not in skip_user_views:
103 raise HTTPFound(
103 raise HTTPFound(
104 self.request.route_path('my_account_password'))
104 self.request.route_path('my_account_password'))
105
105
106 def _get_local_tmpl_context(self, include_app_defaults=False):
106 def _get_local_tmpl_context(self, include_app_defaults=False):
107 c = TemplateArgs()
107 c = TemplateArgs()
108 c.auth_user = self.request.user
108 c.auth_user = self.request.user
109 # TODO(marcink): migrate the usage of c.rhodecode_user to c.auth_user
109 # TODO(marcink): migrate the usage of c.rhodecode_user to c.auth_user
110 c.rhodecode_user = self.request.user
110 c.rhodecode_user = self.request.user
111
111
112 if include_app_defaults:
112 if include_app_defaults:
113 # NOTE(marcink): after full pyramid migration include_app_defaults
113 # NOTE(marcink): after full pyramid migration include_app_defaults
114 # should be turned on by default
114 # should be turned on by default
115 from rhodecode.lib.base import attach_context_attributes
115 from rhodecode.lib.base import attach_context_attributes
116 attach_context_attributes(c, self.request, self.request.user.user_id)
116 attach_context_attributes(c, self.request, self.request.user.user_id)
117
117
118 return c
118 return c
119
119
120 def _register_global_c(self, tmpl_args):
120 def _register_global_c(self, tmpl_args):
121 """
121 """
122 Registers attributes to pylons global `c`
122 Registers attributes to pylons global `c`
123 """
123 """
124
124
125 # TODO(marcink): remove once pyramid migration is finished
125 # TODO(marcink): remove once pyramid migration is finished
126 from pylons import tmpl_context as c
126 from pylons import tmpl_context as c
127 try:
127 try:
128 for k, v in tmpl_args.items():
128 for k, v in tmpl_args.items():
129 setattr(c, k, v)
129 setattr(c, k, v)
130 except TypeError:
130 except TypeError:
131 log.exception('Failed to register pylons C')
131 log.exception('Failed to register pylons C')
132 pass
132 pass
133
133
134 def _get_template_context(self, tmpl_args):
134 def _get_template_context(self, tmpl_args):
135 self._register_global_c(tmpl_args)
135 self._register_global_c(tmpl_args)
136
136
137 local_tmpl_args = {
137 local_tmpl_args = {
138 'defaults': {},
138 'defaults': {},
139 'errors': {},
139 'errors': {},
140 # register a fake 'c' to be used in templates instead of global
141 # pylons c, after migration to pyramid we should rename it to 'c'
142 # make sure we replace usage of _c in templates too
143 '_c': tmpl_args
140 }
144 }
141 local_tmpl_args.update(tmpl_args)
145 local_tmpl_args.update(tmpl_args)
142 return local_tmpl_args
146 return local_tmpl_args
143
147
144 def load_default_context(self):
148 def load_default_context(self):
145 """
149 """
146 example:
150 example:
147
151
148 def load_default_context(self):
152 def load_default_context(self):
149 c = self._get_local_tmpl_context()
153 c = self._get_local_tmpl_context()
150 c.custom_var = 'foobar'
154 c.custom_var = 'foobar'
151 self._register_global_c(c)
155 self._register_global_c(c)
152 return c
156 return c
153 """
157 """
154 raise NotImplementedError('Needs implementation in view class')
158 raise NotImplementedError('Needs implementation in view class')
155
159
156
160
157 class RepoAppView(BaseAppView):
161 class RepoAppView(BaseAppView):
158
162
159 def __init__(self, context, request):
163 def __init__(self, context, request):
160 super(RepoAppView, self).__init__(context, request)
164 super(RepoAppView, self).__init__(context, request)
161 self.db_repo = request.db_repo
165 self.db_repo = request.db_repo
162 self.db_repo_name = self.db_repo.repo_name
166 self.db_repo_name = self.db_repo.repo_name
163 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
167 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
164
168
165 def _handle_missing_requirements(self, error):
169 def _handle_missing_requirements(self, error):
166 log.error(
170 log.error(
167 'Requirements are missing for repository %s: %s',
171 'Requirements are missing for repository %s: %s',
168 self.db_repo_name, error.message)
172 self.db_repo_name, error.message)
169
173
170 def _get_local_tmpl_context(self, include_app_defaults=False):
174 def _get_local_tmpl_context(self, include_app_defaults=False):
171 c = super(RepoAppView, self)._get_local_tmpl_context(
175 c = super(RepoAppView, self)._get_local_tmpl_context(
172 include_app_defaults=include_app_defaults)
176 include_app_defaults=include_app_defaults)
173
177
174 # register common vars for this type of view
178 # register common vars for this type of view
175 c.rhodecode_db_repo = self.db_repo
179 c.rhodecode_db_repo = self.db_repo
176 c.repo_name = self.db_repo_name
180 c.repo_name = self.db_repo_name
177 c.repository_pull_requests = self.db_repo_pull_requests
181 c.repository_pull_requests = self.db_repo_pull_requests
178
182
179 c.repository_requirements_missing = False
183 c.repository_requirements_missing = False
180 try:
184 try:
181 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
185 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
182 except RepositoryRequirementError as e:
186 except RepositoryRequirementError as e:
183 c.repository_requirements_missing = True
187 c.repository_requirements_missing = True
184 self._handle_missing_requirements(e)
188 self._handle_missing_requirements(e)
185
189
186 return c
190 return c
187
191
188
192
189 class DataGridAppView(object):
193 class DataGridAppView(object):
190 """
194 """
191 Common class to have re-usable grid rendering components
195 Common class to have re-usable grid rendering components
192 """
196 """
193
197
194 def _extract_ordering(self, request, column_map=None):
198 def _extract_ordering(self, request, column_map=None):
195 column_map = column_map or {}
199 column_map = column_map or {}
196 column_index = safe_int(request.GET.get('order[0][column]'))
200 column_index = safe_int(request.GET.get('order[0][column]'))
197 order_dir = request.GET.get(
201 order_dir = request.GET.get(
198 'order[0][dir]', 'desc')
202 'order[0][dir]', 'desc')
199 order_by = request.GET.get(
203 order_by = request.GET.get(
200 'columns[%s][data][sort]' % column_index, 'name_raw')
204 'columns[%s][data][sort]' % column_index, 'name_raw')
201
205
202 # translate datatable to DB columns
206 # translate datatable to DB columns
203 order_by = column_map.get(order_by) or order_by
207 order_by = column_map.get(order_by) or order_by
204
208
205 search_q = request.GET.get('search[value]')
209 search_q = request.GET.get('search[value]')
206 return search_q, order_by, order_dir
210 return search_q, order_by, order_dir
207
211
208 def _extract_chunk(self, request):
212 def _extract_chunk(self, request):
209 start = safe_int(request.GET.get('start'), 0)
213 start = safe_int(request.GET.get('start'), 0)
210 length = safe_int(request.GET.get('length'), 25)
214 length = safe_int(request.GET.get('length'), 25)
211 draw = safe_int(request.GET.get('draw'))
215 draw = safe_int(request.GET.get('draw'))
212 return draw, start, length
216 return draw, start, length
213
217
214
218
215 class BaseReferencesView(RepoAppView):
219 class BaseReferencesView(RepoAppView):
216 """
220 """
217 Base for reference view for branches, tags and bookmarks.
221 Base for reference view for branches, tags and bookmarks.
218 """
222 """
219 def load_default_context(self):
223 def load_default_context(self):
220 c = self._get_local_tmpl_context()
224 c = self._get_local_tmpl_context()
221
225
222 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
226 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
223 c.repo_info = self.db_repo
227 c.repo_info = self.db_repo
224
228
225 self._register_global_c(c)
229 self._register_global_c(c)
226 return c
230 return c
227
231
228 def load_refs_context(self, ref_items, partials_template):
232 def load_refs_context(self, ref_items, partials_template):
229 _render = self.request.get_partial_renderer(partials_template)
233 _render = self.request.get_partial_renderer(partials_template)
230 pre_load = ["author", "date", "message"]
234 pre_load = ["author", "date", "message"]
231
235
232 is_svn = h.is_svn(self.rhodecode_vcs_repo)
236 is_svn = h.is_svn(self.rhodecode_vcs_repo)
233 is_hg = h.is_hg(self.rhodecode_vcs_repo)
237 is_hg = h.is_hg(self.rhodecode_vcs_repo)
234
238
235 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
239 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
236
240
237 closed_refs = {}
241 closed_refs = {}
238 if is_hg:
242 if is_hg:
239 closed_refs = self.rhodecode_vcs_repo.branches_closed
243 closed_refs = self.rhodecode_vcs_repo.branches_closed
240
244
241 data = []
245 data = []
242 for ref_name, commit_id in ref_items:
246 for ref_name, commit_id in ref_items:
243 commit = self.rhodecode_vcs_repo.get_commit(
247 commit = self.rhodecode_vcs_repo.get_commit(
244 commit_id=commit_id, pre_load=pre_load)
248 commit_id=commit_id, pre_load=pre_load)
245 closed = ref_name in closed_refs
249 closed = ref_name in closed_refs
246
250
247 # TODO: johbo: Unify generation of reference links
251 # TODO: johbo: Unify generation of reference links
248 use_commit_id = '/' in ref_name or is_svn
252 use_commit_id = '/' in ref_name or is_svn
249 files_url = h.url(
253 files_url = h.url(
250 'files_home',
254 'files_home',
251 repo_name=self.db_repo_name,
255 repo_name=self.db_repo_name,
252 f_path=ref_name if is_svn else '',
256 f_path=ref_name if is_svn else '',
253 revision=commit_id if use_commit_id else ref_name,
257 revision=commit_id if use_commit_id else ref_name,
254 at=ref_name)
258 at=ref_name)
255
259
256 data.append({
260 data.append({
257 "name": _render('name', ref_name, files_url, closed),
261 "name": _render('name', ref_name, files_url, closed),
258 "name_raw": ref_name,
262 "name_raw": ref_name,
259 "date": _render('date', commit.date),
263 "date": _render('date', commit.date),
260 "date_raw": datetime_to_time(commit.date),
264 "date_raw": datetime_to_time(commit.date),
261 "author": _render('author', commit.author),
265 "author": _render('author', commit.author),
262 "commit": _render(
266 "commit": _render(
263 'commit', commit.message, commit.raw_id, commit.idx),
267 'commit', commit.message, commit.raw_id, commit.idx),
264 "commit_raw": commit.idx,
268 "commit_raw": commit.idx,
265 "compare": _render(
269 "compare": _render(
266 'compare', format_ref_id(ref_name, commit.raw_id)),
270 'compare', format_ref_id(ref_name, commit.raw_id)),
267 })
271 })
268
272
269 return data
273 return data
270
274
271
275
272 class RepoRoutePredicate(object):
276 class RepoRoutePredicate(object):
273 def __init__(self, val, config):
277 def __init__(self, val, config):
274 self.val = val
278 self.val = val
275
279
276 def text(self):
280 def text(self):
277 return 'repo_route = %s' % self.val
281 return 'repo_route = %s' % self.val
278
282
279 phash = text
283 phash = text
280
284
281 def __call__(self, info, request):
285 def __call__(self, info, request):
282
286
283 if hasattr(request, 'vcs_call'):
287 if hasattr(request, 'vcs_call'):
284 # skip vcs calls
288 # skip vcs calls
285 return
289 return
286
290
287 repo_name = info['match']['repo_name']
291 repo_name = info['match']['repo_name']
288 repo_model = repo.RepoModel()
292 repo_model = repo.RepoModel()
289 by_name_match = repo_model.get_by_repo_name(repo_name, cache=True)
293 by_name_match = repo_model.get_by_repo_name(repo_name, cache=True)
290
294
291 if by_name_match:
295 if by_name_match:
292 # register this as request object we can re-use later
296 # register this as request object we can re-use later
293 request.db_repo = by_name_match
297 request.db_repo = by_name_match
294 return True
298 return True
295
299
296 by_id_match = repo_model.get_repo_by_id(repo_name)
300 by_id_match = repo_model.get_repo_by_id(repo_name)
297 if by_id_match:
301 if by_id_match:
298 request.db_repo = by_id_match
302 request.db_repo = by_id_match
299 return True
303 return True
300
304
301 return False
305 return False
302
306
303
307
304 class RepoTypeRoutePredicate(object):
308 class RepoTypeRoutePredicate(object):
305 def __init__(self, val, config):
309 def __init__(self, val, config):
306 self.val = val or ['hg', 'git', 'svn']
310 self.val = val or ['hg', 'git', 'svn']
307
311
308 def text(self):
312 def text(self):
309 return 'repo_accepted_type = %s' % self.val
313 return 'repo_accepted_type = %s' % self.val
310
314
311 phash = text
315 phash = text
312
316
313 def __call__(self, info, request):
317 def __call__(self, info, request):
314 if hasattr(request, 'vcs_call'):
318 if hasattr(request, 'vcs_call'):
315 # skip vcs calls
319 # skip vcs calls
316 return
320 return
317
321
318 rhodecode_db_repo = request.db_repo
322 rhodecode_db_repo = request.db_repo
319
323
320 log.debug(
324 log.debug(
321 '%s checking repo type for %s in %s',
325 '%s checking repo type for %s in %s',
322 self.__class__.__name__, rhodecode_db_repo.repo_type, self.val)
326 self.__class__.__name__, rhodecode_db_repo.repo_type, self.val)
323
327
324 if rhodecode_db_repo.repo_type in self.val:
328 if rhodecode_db_repo.repo_type in self.val:
325 return True
329 return True
326 else:
330 else:
327 log.warning('Current view is not supported for repo type:%s',
331 log.warning('Current view is not supported for repo type:%s',
328 rhodecode_db_repo.repo_type)
332 rhodecode_db_repo.repo_type)
329 #
333 #
330 # h.flash(h.literal(
334 # h.flash(h.literal(
331 # _('Action not supported for %s.' % rhodecode_repo.alias)),
335 # _('Action not supported for %s.' % rhodecode_repo.alias)),
332 # category='warning')
336 # category='warning')
333 # return redirect(
337 # return redirect(
334 # route_path('repo_summary', repo_name=cls.rhodecode_db_repo.repo_name))
338 # route_path('repo_summary', repo_name=cls.rhodecode_db_repo.repo_name))
335
339
336 return False
340 return False
337
341
338
342
339 class RepoGroupRoutePredicate(object):
343 class RepoGroupRoutePredicate(object):
340 def __init__(self, val, config):
344 def __init__(self, val, config):
341 self.val = val
345 self.val = val
342
346
343 def text(self):
347 def text(self):
344 return 'repo_group_route = %s' % self.val
348 return 'repo_group_route = %s' % self.val
345
349
346 phash = text
350 phash = text
347
351
348 def __call__(self, info, request):
352 def __call__(self, info, request):
349 if hasattr(request, 'vcs_call'):
353 if hasattr(request, 'vcs_call'):
350 # skip vcs calls
354 # skip vcs calls
351 return
355 return
352
356
353 repo_group_name = info['match']['repo_group_name']
357 repo_group_name = info['match']['repo_group_name']
354 repo_group_model = repo_group.RepoGroupModel()
358 repo_group_model = repo_group.RepoGroupModel()
355 by_name_match = repo_group_model.get_by_group_name(
359 by_name_match = repo_group_model.get_by_group_name(
356 repo_group_name, cache=True)
360 repo_group_name, cache=True)
357
361
358 if by_name_match:
362 if by_name_match:
359 # register this as request object we can re-use later
363 # register this as request object we can re-use later
360 request.db_repo_group = by_name_match
364 request.db_repo_group = by_name_match
361 return True
365 return True
362
366
363 return False
367 return False
364
368
365
369
366 def includeme(config):
370 def includeme(config):
367 config.add_route_predicate(
371 config.add_route_predicate(
368 'repo_route', RepoRoutePredicate)
372 'repo_route', RepoRoutePredicate)
369 config.add_route_predicate(
373 config.add_route_predicate(
370 'repo_accepted_types', RepoTypeRoutePredicate)
374 'repo_accepted_types', RepoTypeRoutePredicate)
371 config.add_route_predicate(
375 config.add_route_predicate(
372 'repo_group_route', RepoGroupRoutePredicate)
376 'repo_group_route', RepoGroupRoutePredicate)
General Comments 0
You need to be logged in to leave comments. Login now