Projet

Général

Profil

0001-backoffice-add-a-360-User-View-2125.patch

Frédéric Péters, 13 novembre 2015 11:37

Télécharger (12,9 ko)

Voir les différences:

Subject: [PATCH] =?UTF-8?q?backoffice:=20add=20a=20"360=C2=B0=20User=20Vie?=
 =?UTF-8?q?w"=20(#2125)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

 tests/test_backoffice_pages.py      |  31 +++++
 wcs/backoffice/management.py        | 220 +++++++++++++++++++++++++++++++++++-
 wcs/qommon/static/css/dc2/admin.css |   4 +
 3 files changed, 253 insertions(+), 2 deletions(-)
tests/test_backoffice_pages.py
49 49
        user1.store()
50 50
        return user1
51 51
    user1 = pub.user_class(name='admin')
52
    user1.email = 'admin@localhost'
52 53
    user1.is_admin = is_admin
53 54
    user1.store()
54 55

  
......
1237 1238
        assert 'Justificatif de domicile validated by Jean Bono on 2014-01-01T01:01:01' in resp.body
1238 1239
        assert 'Code postal: 13400' in resp.body
1239 1240
        assert 'Valid from 2014-01-01T01:01:01 to 2014-01-01T01:01:01' in resp.body
1241

  
1242
def test_360_user_view(pub):
1243
    if not pub.is_using_postgresql():
1244
        pytest.skip('this requires SQL')
1245
        return
1246

  
1247
    user = create_user(pub)
1248
    create_environment(pub)
1249
    app = login(get_app(pub))
1250
    resp = app.get('/backoffice/management/')
1251
    assert '360 User View' in resp.body
1252
    resp = resp.click('360 User View')
1253
    assert 'Use the search field on the right' in resp.body
1254

  
1255
    resp.form['q'] = 'admin'
1256
    resp = resp.form.submit()
1257

  
1258
    assert resp.body.count('<tr') == 2 # header + user
1259

  
1260
    form_class = FormDef.get_by_urlname('form-title').data_class()
1261
    to_match = []
1262
    for formdata in form_class.select():
1263
        if formdata.data['1'] in ('FOO BAR 30', 'FOO BAR 33'):
1264
            formdata.user_id = user.id
1265
            formdata.store()
1266
            to_match.append('/management/form-title/%s/' % formdata.id)
1267

  
1268
    resp = app.get('/backoffice/management/users/%s/' % user.id)
1269
    for item in to_match:
1270
        assert item in resp.body
wcs/backoffice/management.py
36 36
from qommon import errors
37 37
from qommon import ods
38 38
from qommon.form import *
39
from qommon.storage import Equal, NotEqual, LessOrEqual, GreaterOrEqual, Or, Intersects
39
from qommon.storage import (Equal, NotEqual, LessOrEqual, GreaterOrEqual, Or,
40
        Intersects, ILike)
40 41

  
41 42
from wcs.forms.backoffice import FormDefUI
42 43
from wcs.forms.common import FormStatusPage
44
from wcs.admin.settings import UserFieldsFormDef
43 45

  
44 46
from wcs.categories import Category
45 47
from wcs.formdef import FormDef
46 48
from wcs.roles import logged_users_role
47 49

  
48 50

  
51

  
52
class UserViewDirectory(Directory):
53
    _q_exports = ['']
54

  
55
    user = None
56

  
57
    def __init__(self, user):
58
        self.user = user
59

  
60
    def _q_index(self):
61
        get_response().breadcrumb.append(('%s/' % self.user.id, self.user.display_name))
62
        html_top('management', _('Management'))
63
        # display list of open formdata for the user
64
        user_roles = [logged_users_role().id] + (get_request().user.roles or [])
65
        criterias = [Equal('is_at_endpoint', False),
66
                     Equal('user_id', str(self.user.id)),
67
                     Intersects('concerned_roles_array', user_roles),
68
                    ]
69
        from wcs import sql
70
        formdatas = sql.AnyFormData.select(criterias, order_by='-receipt_time', limit=50)
71

  
72
        r = TemplateIO(html=True)
73
        r += htmltext('<div class="bo-block">')
74
        r += htmltext('<h2>%s</h2>') % self.user.display_name
75
        formdef = UserFieldsFormDef()
76
        r += htmltext('<div class="form">')
77
        for field in formdef.fields:
78
            if not hasattr(field, str('get_view_value')):
79
                continue
80
            value = self.user.form_data.get(field.id)
81
            if not value:
82
                continue
83
            r += htmltext('<div class="title">')
84
            r += field.label
85
            r += htmltext('</div>')
86
            r += htmltext('<div class="StringWidget content">')
87
            r += field.get_view_value(value)
88
            r += htmltext('</div>')
89
        r += htmltext('</div>')
90
        r += htmltext('</div>')
91

  
92
        if formdatas:
93
            categories = {}
94
            formdata_by_category = {}
95
            for formdata in formdatas:
96
                if not formdata.formdef.category_id in categories:
97
                    categories[formdata.formdef.category_id] = formdata.formdef.category
98
                    formdata_by_category[formdata.formdef.category_id] = []
99
                formdata_by_category[formdata.formdef.category_id].append(formdata)
100
            cats = categories.values()
101
            Category.sort_by_position(cats)
102
            for cat in cats:
103
                r += htmltext('<div class="bo-block">')
104
                if len(cats) > 1:
105
                    if cat is None:
106
                        r += htmltext('<h2>%s</h2>') % _('Misc')
107
                        cat_formdatas = formdata_by_category[None]
108
                    else:
109
                        r += htmltext('<h2>%s</h2>') % cat.name
110
                        cat_formdatas = formdata_by_category[cat.id]
111
                else:
112
                    cat_formdatas = formdatas
113
                r += htmltext('<ul class="biglist">')
114
                for formdata in cat_formdatas:
115
                    status = formdata.get_status()
116
                    if status:
117
                        status_label = status.name
118
                    else:
119
                        status_label = _('Unknown')
120
                    submit_date = misc.strftime.strftime(
121
                            misc.date_format(), formdata.receipt_time)
122
                    r += htmltext('<li><a href="%s">%s, '
123
                                  '<span class="datetime">%s</span> '
124
                                  '<span class="status">(%s)</span></a>' % (
125
                            formdata.get_url(backoffice=True),
126
                            formdata.formdef.name,
127
                            submit_date, status_label))
128
                r += htmltext('</ul>')
129
                r += htmltext('</div>')
130
            r += htmltext('</div>')
131

  
132
        return r.getvalue()
133

  
134

  
135
class UsersViewDirectory(Directory):
136
    _q_exports = ['']
137

  
138
    def _q_traverse(self, path):
139
        if not get_publisher().is_using_postgresql():
140
            raise errors.TraversalError()
141
        get_response().breadcrumb.append(('users', _('360 User View')))
142
        return super(UsersViewDirectory, self)._q_traverse(path)
143

  
144
    def get_search_sidebar(self, offset=None, limit=None, order_by=None):
145
        get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'wcs.listing.js'])
146
        get_response().add_css_include('../js/smoothness/jquery-ui-1.10.0.custom.min.css')
147

  
148
        r = TemplateIO(html=True)
149
        r += htmltext('<form id="listing-settings" action=".">')
150
        if offset or limit:
151
            if not offset:
152
                offset = 0
153
            r += htmltext('<input type="hidden" name="offset" value="%s"/>') % offset
154
        if limit:
155
            r += htmltext('<input type="hidden" name="limit" value="%s"/>') % limit
156

  
157
        if order_by is None:
158
            order_by = ''
159
        r += htmltext('<input type="hidden" name="order_by" value="%s"/>') % order_by
160

  
161
        r += htmltext('<h3>%s</h3>') % _('Search')
162
        if get_request().form.get('q'):
163
            q = get_request().form.get('q')
164
            if type(q) is not unicode:
165
                q = unicode(q, get_publisher().site_charset)
166
            r += htmltext('<input name="q" value="%s">') % q.encode(get_publisher().site_charset)
167
        else:
168
            r += htmltext('<input name="q">')
169
        r += htmltext('<input type="submit" value="%s"/>') % _('Search')
170

  
171
        return r.getvalue()
172

  
173
    def _q_index(self):
174
        html_top('management', _('Management'))
175
        r = TemplateIO(html=True)
176

  
177
        limit = int(get_request().form.get('limit',
178
                get_publisher().get_site_option('default-page-size')) or 20)
179
        offset = int(get_request().form.get('offset', 0))
180
        order_by = get_request().form.get('order_by', None) or 'name'
181
        query = get_request().form.get('q')
182

  
183
        get_response().filter['sidebar'] = self.get_search_sidebar(
184
                limit=limit, offset=offset, order_by=order_by)
185

  
186
        if not query:
187
            r += htmltext('<div id="listing">')
188
            r += htmltext('<div class="big-msg-info">')
189
            r += htmltext('<p>%s</p>') % _('Use the search field on the right '
190
                                           'to look for an user.')
191
            r += htmltext('</div>')
192
            r += htmltext('</div>')
193
            if get_request().form.get('ajax') == 'true':
194
                get_response().filter = None
195
            return r.getvalue()
196

  
197
        criterias = []
198
        criteria_fields = [ILike('name', query), ILike('email', query)]
199
        formdef = UserFieldsFormDef()
200
        for field in formdef.fields:
201
            if field.type in ('string', 'text', 'email'):
202
                criteria_fields.append(ILike('f%s' % field.id, query))
203
        criterias.append(Or(criteria_fields))
204

  
205
        users = get_publisher().user_class.select(order_by=order_by,
206
                clause=criterias, limit=limit, offset=offset)
207
        total_count = get_publisher().user_class.count(criterias)
208

  
209
        users_cfg = get_cfg('users', {})
210
        include_name_column = not(users_cfg.get('field_name'))
211
        include_email_column = not(users_cfg.get('field_email'))
212
        r += htmltext('<div id="listing">')
213
        r += htmltext('<table class="main">')
214
        r += htmltext('<thead>')
215
        r += htmltext('<tr>')
216
        if include_name_column:
217
            r += htmltext('<th data-field-sort-key="name"><span>%s</span></th>') % _('Name')
218
        if include_email_column:
219
            r += htmltext('<th data-field-sort-key="email"><span>%s</span></th>') % _('Email')
220
        for field in formdef.fields:
221
            if field.in_listing:
222
                r += htmltext('<th data-field-sort-key="f%s"><span>%s</span></th>') % (
223
                        field.id, field.label)
224
        r += htmltext('</tr>')
225
        r += htmltext('</thead>')
226
        r += htmltext('<tbody>')
227

  
228
        for user in users:
229
            r += htmltext('<tr data-link="%s/">') % user.id
230
            if include_name_column:
231
                r += htmltext('<td>%s</td>') % (user.name or '')
232
            if include_email_column:
233
                r += htmltext('<td>%s</td>') % (user.email or '')
234
            for field in formdef.fields:
235
                if field.in_listing:
236
                    r += htmltext('<td>%s</td>') % (user.form_data.get(field.id) or '')
237
            r += htmltext('</tr>')
238

  
239
        r += htmltext('</tbody>')
240
        r += htmltext('</table>')
241

  
242
        if (offset > 0) or (total_count > limit > 0):
243
            r += pagination_links(offset, limit, total_count)
244

  
245
        r += htmltext('</div>')
246

  
247
        if get_request().form.get('ajax') == 'true':
248
            get_response().filter = None
249
            return r.getvalue()
250

  
251
        return r.getvalue()
252

  
253
    def _q_lookup(self, component):
254
        try:
255
            user = get_publisher().user_class.get(component)
256
            return UserViewDirectory(user)
257
        except KeyError:
258
            pass
259
        raise errors.TraversalError()
260

  
261

  
49 262
class ManagementDirectory(Directory):
50
    _q_exports = ['', 'listing', 'statistics', 'code', 'count']
263
    _q_exports = ['', 'listing', 'statistics', 'code', 'count', 'users']
264

  
265
    users = UsersViewDirectory()
51 266

  
52 267
    def is_accessible(self, user):
53 268
        return user.can_go_in_backoffice()
......
120 335
        if get_publisher().is_using_postgresql() and \
121 336
                get_publisher().get_site_option('postgresql_views') != 'false':
122 337
            r += htmltext('<li><a href="listing">%s</a></li>') % _('Global View')
338
            r += htmltext('<li><a href="users/">%s</a></li>') % _('360 User View')
123 339
        r += htmltext('</ul>')
124 340
        r += htmltext('</div>')
125 341
        r += self.get_tracking_code_sidebox()
wcs/qommon/static/css/dc2/admin.css
335 335
	background-image: none;
336 336
}
337 337

  
338
#listing {
339
	margin-bottom: 1em;
340
}
341

  
338 342
ul.biglist, table#listing {
339 343
	-webkit-transition: opacity 500ms ease-out;
340 344
	-moz-transition: opacity 500ms ease-out;
341
-