##// END OF EJS Templates
base-grid-view: allow specifing custom mapping for sort columns.
marcink -
r1649:1c00b1d7 default
parent child Browse files
Show More
@@ -1,191 +1,189 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 from pylons import tmpl_context as c
23 from pylons import tmpl_context as c
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
27 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int
28 from rhodecode.model import repo
28 from rhodecode.model import repo
29 from rhodecode.model.db import User
29 from rhodecode.model.db import User
30 from rhodecode.model.scm import ScmModel
30 from rhodecode.model.scm import ScmModel
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34
34
35 ADMIN_PREFIX = '/_admin'
35 ADMIN_PREFIX = '/_admin'
36 STATIC_FILE_PREFIX = '/_static'
36 STATIC_FILE_PREFIX = '/_static'
37
37
38
38
39 class TemplateArgs(StrictAttributeDict):
39 class TemplateArgs(StrictAttributeDict):
40 pass
40 pass
41
41
42
42
43 class BaseAppView(object):
43 class BaseAppView(object):
44
44
45 def __init__(self, context, request):
45 def __init__(self, context, request):
46 self.request = request
46 self.request = request
47 self.context = context
47 self.context = context
48 self.session = request.session
48 self.session = request.session
49 self._rhodecode_user = request.user # auth user
49 self._rhodecode_user = request.user # auth user
50 self._rhodecode_db_user = self._rhodecode_user.get_instance()
50 self._rhodecode_db_user = self._rhodecode_user.get_instance()
51 self._maybe_needs_password_change(
51 self._maybe_needs_password_change(
52 request.matched_route.name, self._rhodecode_db_user)
52 request.matched_route.name, self._rhodecode_db_user)
53
53
54 def _maybe_needs_password_change(self, view_name, user_obj):
54 def _maybe_needs_password_change(self, view_name, user_obj):
55 log.debug('Checking if user %s needs password change on view %s',
55 log.debug('Checking if user %s needs password change on view %s',
56 user_obj, view_name)
56 user_obj, view_name)
57 skip_user_views = [
57 skip_user_views = [
58 'logout', 'login',
58 'logout', 'login',
59 'my_account_password', 'my_account_password_update'
59 'my_account_password', 'my_account_password_update'
60 ]
60 ]
61
61
62 if not user_obj:
62 if not user_obj:
63 return
63 return
64
64
65 if user_obj.username == User.DEFAULT_USER:
65 if user_obj.username == User.DEFAULT_USER:
66 return
66 return
67
67
68 now = time.time()
68 now = time.time()
69 should_change = user_obj.user_data.get('force_password_change')
69 should_change = user_obj.user_data.get('force_password_change')
70 change_after = safe_int(should_change) or 0
70 change_after = safe_int(should_change) or 0
71 if should_change and now > change_after:
71 if should_change and now > change_after:
72 log.debug('User %s requires password change', user_obj)
72 log.debug('User %s requires password change', user_obj)
73 h.flash('You are required to change your password', 'warning',
73 h.flash('You are required to change your password', 'warning',
74 ignore_duplicate=True)
74 ignore_duplicate=True)
75
75
76 if view_name not in skip_user_views:
76 if view_name not in skip_user_views:
77 raise HTTPFound(
77 raise HTTPFound(
78 self.request.route_path('my_account_password'))
78 self.request.route_path('my_account_password'))
79
79
80 def _get_local_tmpl_context(self):
80 def _get_local_tmpl_context(self):
81 c = TemplateArgs()
81 c = TemplateArgs()
82 c.auth_user = self.request.user
82 c.auth_user = self.request.user
83 return c
83 return c
84
84
85 def _register_global_c(self, tmpl_args):
85 def _register_global_c(self, tmpl_args):
86 """
86 """
87 Registers attributes to pylons global `c`
87 Registers attributes to pylons global `c`
88 """
88 """
89 # TODO(marcink): remove once pyramid migration is finished
89 # TODO(marcink): remove once pyramid migration is finished
90 for k, v in tmpl_args.items():
90 for k, v in tmpl_args.items():
91 setattr(c, k, v)
91 setattr(c, k, v)
92
92
93 def _get_template_context(self, tmpl_args):
93 def _get_template_context(self, tmpl_args):
94 self._register_global_c(tmpl_args)
94 self._register_global_c(tmpl_args)
95
95
96 local_tmpl_args = {
96 local_tmpl_args = {
97 'defaults': {},
97 'defaults': {},
98 'errors': {},
98 'errors': {},
99 }
99 }
100 local_tmpl_args.update(tmpl_args)
100 local_tmpl_args.update(tmpl_args)
101 return local_tmpl_args
101 return local_tmpl_args
102
102
103 def load_default_context(self):
103 def load_default_context(self):
104 """
104 """
105 example:
105 example:
106
106
107 def load_default_context(self):
107 def load_default_context(self):
108 c = self._get_local_tmpl_context()
108 c = self._get_local_tmpl_context()
109 c.custom_var = 'foobar'
109 c.custom_var = 'foobar'
110 self._register_global_c(c)
110 self._register_global_c(c)
111 return c
111 return c
112 """
112 """
113 raise NotImplementedError('Needs implementation in view class')
113 raise NotImplementedError('Needs implementation in view class')
114
114
115
115
116 class RepoAppView(BaseAppView):
116 class RepoAppView(BaseAppView):
117
117
118 def __init__(self, context, request):
118 def __init__(self, context, request):
119 super(RepoAppView, self).__init__(context, request)
119 super(RepoAppView, self).__init__(context, request)
120 self.db_repo = request.db_repo
120 self.db_repo = request.db_repo
121 self.db_repo_name = self.db_repo.repo_name
121 self.db_repo_name = self.db_repo.repo_name
122 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
122 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
123
123
124 def _get_local_tmpl_context(self):
124 def _get_local_tmpl_context(self):
125 c = super(RepoAppView, self)._get_local_tmpl_context()
125 c = super(RepoAppView, self)._get_local_tmpl_context()
126 # register common vars for this type of view
126 # register common vars for this type of view
127 c.rhodecode_db_repo = self.db_repo
127 c.rhodecode_db_repo = self.db_repo
128 c.repo_name = self.db_repo_name
128 c.repo_name = self.db_repo_name
129 c.repository_pull_requests = self.db_repo_pull_requests
129 c.repository_pull_requests = self.db_repo_pull_requests
130 return c
130 return c
131
131
132
132
133 class DataGridAppView(object):
133 class DataGridAppView(object):
134 """
134 """
135 Common class to have re-usable grid rendering components
135 Common class to have re-usable grid rendering components
136 """
136 """
137
137
138 def _extract_ordering(self, request):
138 def _extract_ordering(self, request, column_map=None):
139 column_map = column_map or {}
139 column_index = safe_int(request.GET.get('order[0][column]'))
140 column_index = safe_int(request.GET.get('order[0][column]'))
140 order_dir = request.GET.get(
141 order_dir = request.GET.get(
141 'order[0][dir]', 'desc')
142 'order[0][dir]', 'desc')
142 order_by = request.GET.get(
143 order_by = request.GET.get(
143 'columns[%s][data][sort]' % column_index, 'name_raw')
144 'columns[%s][data][sort]' % column_index, 'name_raw')
144
145
145 # translate datatable to DB columns
146 # translate datatable to DB columns
146 order_by = {
147 order_by = column_map.get(order_by) or order_by
147 'first_name': 'name',
148 'last_name': 'lastname',
149 }.get(order_by) or order_by
150
148
151 search_q = request.GET.get('search[value]')
149 search_q = request.GET.get('search[value]')
152 return search_q, order_by, order_dir
150 return search_q, order_by, order_dir
153
151
154 def _extract_chunk(self, request):
152 def _extract_chunk(self, request):
155 start = safe_int(request.GET.get('start'), 0)
153 start = safe_int(request.GET.get('start'), 0)
156 length = safe_int(request.GET.get('length'), 25)
154 length = safe_int(request.GET.get('length'), 25)
157 draw = safe_int(request.GET.get('draw'))
155 draw = safe_int(request.GET.get('draw'))
158 return draw, start, length
156 return draw, start, length
159
157
160
158
161 class RepoRoutePredicate(object):
159 class RepoRoutePredicate(object):
162 def __init__(self, val, config):
160 def __init__(self, val, config):
163 self.val = val
161 self.val = val
164
162
165 def text(self):
163 def text(self):
166 return 'repo_route = %s' % self.val
164 return 'repo_route = %s' % self.val
167
165
168 phash = text
166 phash = text
169
167
170 def __call__(self, info, request):
168 def __call__(self, info, request):
171 repo_name = info['match']['repo_name']
169 repo_name = info['match']['repo_name']
172 repo_model = repo.RepoModel()
170 repo_model = repo.RepoModel()
173 by_name_match = repo_model.get_by_repo_name(repo_name, cache=True)
171 by_name_match = repo_model.get_by_repo_name(repo_name, cache=True)
174 # if we match quickly from database, short circuit the operation,
172 # if we match quickly from database, short circuit the operation,
175 # and validate repo based on the type.
173 # and validate repo based on the type.
176 if by_name_match:
174 if by_name_match:
177 # register this as request object we can re-use later
175 # register this as request object we can re-use later
178 request.db_repo = by_name_match
176 request.db_repo = by_name_match
179 return True
177 return True
180
178
181 by_id_match = repo_model.get_repo_by_id(repo_name)
179 by_id_match = repo_model.get_repo_by_id(repo_name)
182 if by_id_match:
180 if by_id_match:
183 request.db_repo = by_id_match
181 request.db_repo = by_id_match
184 return True
182 return True
185
183
186 return False
184 return False
187
185
188
186
189 def includeme(config):
187 def includeme(config):
190 config.add_route_predicate(
188 config.add_route_predicate(
191 'repo_route', RepoRoutePredicate)
189 'repo_route', RepoRoutePredicate)
General Comments 0
You need to be logged in to leave comments. Login now