Projet

Général

Profil

0003-backoffice-set-default-view-as-default-47492.patch

Lauréline Guérin, 15 octobre 2020 10:35

Télécharger (19,8 ko)

Voir les différences:

Subject: [PATCH 3/3] backoffice: set default view as default (#47492)

 tests/backoffice_pages/test_custom_view.py | 195 +++++++++++++++++++++
 wcs/backoffice/management.py               |  83 ++++++++-
 wcs/custom_views.py                        |   5 +-
 wcs/qommon/static/js/wcs.listing.js        |   5 +
 wcs/sql.py                                 |  14 +-
 5 files changed, 290 insertions(+), 12 deletions(-)
tests/backoffice_pages/test_custom_view.py
13 13
from wcs.qommon.http_request import HTTPRequest
14 14
from wcs.carddef import CardDef
15 15
from wcs.formdef import FormDef
16
from wcs.roles import Role
16 17
from wcs import fields
17 18

  
18 19
from utilities import (get_app, login, create_temporary_pub, clean_temporary_pub)
......
291 292
            [('custom-test-view', 'owner'), ('shared-view', 'any'), ('shared-view-2', 'any')])
292 293

  
293 294

  
295
def test_backoffice_custom_view_is_default(pub):
296
    create_superuser(pub)
297

  
298
    FormDef.wipe()
299
    pub.custom_view_class.wipe()
300
    formdef = FormDef()
301
    formdef.name = 'form title'
302
    formdef.fields = []
303
    formdef.workflow_roles = {'_receiver': 1}
304
    formdef.store()
305

  
306
    # private custom view
307
    agent = pub.user_class(name='agent')
308
    agent.roles = [formdef.workflow_roles['_receiver']]
309
    agent.store()
310
    account = PasswordAccount(id='agent')
311
    account.set_password('agent')
312
    account.user_id = agent.id
313
    account.store()
314
    app = login(get_app(pub), username='agent', password='agent')
315
    resp = app.get('/backoffice/management/form-title/')
316
    resp = resp.forms['listing-settings'].submit()
317
    resp.forms['save-custom-view']['title'] = 'view 1'
318
    resp.forms['save-custom-view']['is_default'] = True
319
    resp = resp.forms['save-custom-view'].submit()
320

  
321
    # other private custom view
322
    app = login(get_app(pub))
323
    resp = app.get('/backoffice/management/form-title/')
324
    resp = resp.forms['listing-settings'].submit()
325
    resp.forms['save-custom-view']['title'] = 'view 2'
326
    resp.forms['save-custom-view']['visibility'] = 'owner'
327
    resp.forms['save-custom-view']['is_default'] = True
328
    resp = resp.forms['save-custom-view'].submit()
329

  
330
    # shared custom view
331
    resp = app.get('/backoffice/management/form-title/')
332
    resp = resp.forms['listing-settings'].submit()
333
    resp.forms['save-custom-view']['title'] = 'view 3'
334
    resp.forms['save-custom-view']['visibility'] = 'any'
335
    resp.forms['save-custom-view']['is_default'] = True
336
    resp = resp.forms['save-custom-view'].submit()
337

  
338
    assert pub.custom_view_class.count() == 3
339
    assert pub.custom_view_class.get(1).is_default is True  # simple user - private
340
    assert pub.custom_view_class.get(2).is_default is True  # super user - private
341
    assert pub.custom_view_class.get(3).is_default is True  # super user - shared
342

  
343
    # not possible to define more than one default private view
344
    resp = app.get('/backoffice/management/form-title/')
345
    resp = resp.forms['listing-settings'].submit()
346
    resp.forms['save-custom-view']['title'] = 'view 4'
347
    resp.forms['save-custom-view']['visibility'] = 'owner'
348
    resp.forms['save-custom-view']['is_default'] = True
349
    resp = resp.forms['save-custom-view'].submit()
350
    assert pub.custom_view_class.count() == 4
351
    assert pub.custom_view_class.get(1).is_default is True  # simple user - private
352
    assert pub.custom_view_class.get(2).is_default is False  # super user - private
353
    assert pub.custom_view_class.get(3).is_default is True  # super user - shared
354
    assert pub.custom_view_class.get(4).is_default is True  # super user - private 2
355

  
356
    # not possible to define more than one default shared view
357
    resp = app.get('/backoffice/management/form-title/')
358
    resp = resp.forms['listing-settings'].submit()
359
    resp.forms['save-custom-view']['title'] = 'view 5'
360
    resp.forms['save-custom-view']['visibility'] = 'any'
361
    resp.forms['save-custom-view']['is_default'] = True
362
    resp = resp.forms['save-custom-view'].submit()
363
    assert pub.custom_view_class.count() == 5
364
    assert pub.custom_view_class.get(1).is_default is True  # simple user - private
365
    assert pub.custom_view_class.get(2).is_default is False  # super user - private
366
    assert pub.custom_view_class.get(3).is_default is False  # super user - shared
367
    assert pub.custom_view_class.get(4).is_default is True  # super user - private 2
368
    assert pub.custom_view_class.get(5).is_default is True  # super user - shared 2
369

  
370

  
371
def test_backoffice_default_custom_view(pub):
372
    create_superuser(pub)
373

  
374
    FormDef.wipe()
375
    pub.custom_view_class.wipe()
376
    formdef = FormDef()
377
    formdef.name = 'form title'
378
    formdef.fields = [
379
        fields.ItemField(
380
            id='1', label='field 1', type='item',
381
            items=['foo', 'bar', 'baz'],
382
            display_locations=['validation', 'summary', 'listings']),
383
    ]
384
    formdef.workflow_roles = {'_receiver': 1}
385
    formdef.store()
386

  
387
    formdef.data_class().wipe()
388
    for i in range(3):
389
        formdata = formdef.data_class()()
390
        formdata.data = {}
391
        if i == 0:
392
            formdata.data['1'] = 'foo'
393
            formdata.data['1_display'] = 'foo'
394
        else:
395
            formdata.data['1'] = 'baz'
396
            formdata.data['1_display'] = 'baz'
397
        if i < 2:
398
            formdata.jump_status('new')
399
        formdata.store()
400

  
401
    # define a shared defautl view
402
    app = login(get_app(pub))
403
    resp = app.get('/backoffice/management/form-title/')
404
    # columns
405
    resp.forms['listing-settings']['user-label'].checked = False
406
    resp.forms['listing-settings']['1'].checked = False
407
    # filters
408
    resp.forms['listing-settings']['filter-1'].checked = True
409
    resp = resp.forms['listing-settings'].submit()
410
    resp.forms['listing-settings']['filter-1-value'] = 'baz'
411
    resp.forms['listing-settings']['filter'] = 'all'
412
    resp = resp.forms['listing-settings'].submit()
413
    # view
414
    resp.forms['save-custom-view']['title'] = 'shared custom test view'
415
    resp.forms['save-custom-view']['visibility'] = 'any'
416
    resp.forms['save-custom-view']['is_default'] = True
417
    resp = resp.forms['save-custom-view'].submit()
418
    assert resp.location.endswith('/shared-custom-test-view/')
419
    resp = resp.follow()
420
    assert resp.text.count('<span>User Label</span>') == 0
421
    assert resp.text.count('<span>field 1</span>') == 0
422
    assert resp.text.count('<tr') == 3
423

  
424
    # check that default view is applied
425
    resp = app.get('/backoffice/management/form-title/')
426
    assert resp.text.count('<span>User Label</span>') == 0
427
    assert resp.text.count('<span>field 1</span>') == 0
428
    assert resp.text.count('<tr') == 3
429

  
430
    # define a user default view
431
    # columns
432
    resp.forms['listing-settings']['1'].checked = True
433
    # filters
434
    resp.forms['listing-settings']['filter-1'].checked = True
435
    resp = resp.forms['listing-settings'].submit()
436
    resp.forms['listing-settings']['filter-1-value'] = 'foo'
437
    resp.forms['listing-settings']['filter'] = 'all'
438
    resp = resp.forms['listing-settings'].submit()
439
    # view
440
    resp.forms['save-custom-view']['title'] = 'private custom test view'
441
    resp.forms['save-custom-view']['visibility'] = 'owner'
442
    resp.forms['save-custom-view']['is_default'] = True
443

  
444
    resp = resp.forms['save-custom-view'].submit()
445
    assert resp.location.endswith('/user-private-custom-test-view/')
446
    resp = resp.follow()
447
    assert resp.text.count('<span>User Label</span>') == 0
448
    assert resp.text.count('<span>field 1</span>') == 1
449
    assert resp.text.count('<tr') == 2
450

  
451
    # check that private default view is applied, and not shared default view
452
    resp = app.get('/backoffice/management/form-title/')
453
    assert resp.text.count('<span>User Label</span>') == 0
454
    assert resp.text.count('<span>field 1</span>') == 1
455
    assert resp.text.count('<tr') == 2
456

  
457

  
294 458
def test_backoffice_missing_custom_view(pub):
295 459
    create_superuser(pub)
296 460

  
......
348 512
    resp = resp.follow()
349 513

  
350 514

  
515
def test_carddata_custom_view_is_default(pub):
516
    user = create_superuser(pub)
517
    Role.wipe()
518
    role = Role(name='foobar')
519
    role.store()
520
    user.roles = [role.id]
521
    user.store()
522

  
523
    CardDef.wipe()
524
    pub.custom_view_class.wipe()
525
    carddef = CardDef()
526
    carddef.name = 'foo'
527
    carddef.fields = [
528
        fields.StringField(id='1', label='Test', type='string', varname='foo'),
529
    ]
530
    carddef.backoffice_submission_roles = user.roles
531
    carddef.digest_template = '{{ form_var_foo }}'
532
    carddef.store()
533
    carddef.data_class().wipe()
534

  
535
    # datasource custom view
536
    app = login(get_app(pub))
537
    resp = app.get('/backoffice/data/foo/')
538
    resp = resp.forms['listing-settings'].submit()
539
    resp.forms['save-custom-view']['title'] = 'view 3'
540
    resp.forms['save-custom-view']['visibility'] = 'datasource'
541
    resp.forms['save-custom-view']['is_default'] = True
542
    resp = resp.forms['save-custom-view'].submit()
543
    assert pub.custom_view_class.get(1).is_default is False  # not for datasource
544

  
545

  
351 546
def test_backoffice_custom_view_keep_filters(pub):
352 547
    create_superuser(pub)
353 548
    datasource = {
wcs/backoffice/management.py
1027 1027
    _q_exports = ['', 'csv', 'stats', 'xls', 'ods', 'json', 'export', 'map',
1028 1028
                  'geojson', ('filter-options', 'filter_options'),
1029 1029
                  ('save-view', 'save_view'), ('delete-view', 'delete_view'),]
1030
    view = None
1030
    _view = None
1031
    default_view = None
1032
    use_default_view = False
1031 1033
    admin_permission = 'forms'
1032 1034
    formdef_class = FormDef
1033 1035
    search_label = N_('Search in form content')
......
1043 1045
                get_response().breadcrumb.append((component + '/', self.formdef.name))
1044 1046
        else:
1045 1047
            self.formdef = formdef
1046
            self.view = view
1048
            self._view = view
1047 1049
            if update_breadcrumbs:
1048 1050
                get_response().breadcrumb.append((view.get_url_slug() + '/', view.title))
1051
        self.set_default_view()
1052

  
1053
    def set_default_view(self):
1054
        if not get_request():
1055
            return
1056
        custom_views = list(self.get_custom_views())
1057

  
1058
        # search for first default user custom view
1059
        for view in custom_views:
1060
            if view.visibility != 'owner':
1061
                continue
1062
            if not view.is_default:
1063
                continue
1064
            self.default_view = view
1065
            return
1066

  
1067
        # default user custom view not found, search in 'any' custom views
1068
        for view in custom_views:
1069
            if view.visibility != 'any':
1070
                continue
1071
            if not view.is_default:
1072
                continue
1073
            self.default_view = view
1074
            return
1075

  
1076
    @property
1077
    def view(self):
1078
        view = self._view
1079
        if self.use_default_view:
1080
            view = view or self.default_view
1081
        return view
1049 1082

  
1050 1083
    def check_access(self, api_name=None):
1051 1084
        session = get_session()
......
1093 1126
            r += htmltext('<ul id="sidebar-custom-views">')
1094 1127
            view_type = 'map' if self.view_type == 'map' else ''
1095 1128
            for view in sorted(views, key=lambda x: getattr(x, 'title')):
1096
                if self.view:
1097
                    active = bool(self.view.get_url_slug() == view.get_url_slug())
1129
                if self._view:
1130
                    active = bool(self._view.get_url_slug() == view.get_url_slug())
1098 1131
                    r += htmltext('<li class="active">' if active else '<li>')
1099 1132
                    r += htmltext('<a href="../%s/%s">%s</a>') % (view.get_url_slug(), view_type, view.title)
1100 1133
                else:
1101 1134
                    r += htmltext('<li><a href="%s/%s">%s</a>') % (view.get_url_slug(), view_type, view.title)
1102 1135
                if view.visibility == 'datasource':
1103 1136
                    r += htmltext(' <span class="as-data-source">(%s)</span>') % _('for data sources')
1137
                elif self.default_view and view.id == self.default_view.id:
1138
                    r += htmltext(' <span class="default-custom-view">(%s)</span>') % _('default')
1104 1139
                r += htmltext('</li>')
1105 1140
            r += htmltext('</ul>')
1106 1141
        return r.getvalue()
......
1479 1514
            if isinstance(self.formdef, CardDef) and self.formdef.digest_template:
1480 1515
                options.append(('datasource', _('as data source'), 'datasource'))
1481 1516

  
1482
            form.add(RadiobuttonsWidget, 'visibility', title=_('Visibility'),
1483
                    value=self.view.visibility if self.view else 'owner',
1484
                    options=options)
1517
            form.add(
1518
                RadiobuttonsWidget,
1519
                'visibility',
1520
                title=_('Visibility'),
1521
                value=self.view.visibility if self.view else 'owner',
1522
                options=options,
1523
                attrs={'data-dynamic-display-parent': 'true'})
1524
            form.add(
1525
                CheckboxWidget,
1526
                'is_default',
1527
                title=_('Set as default view'),
1528
                value=self.view.is_default if self.view else False,
1529
                attrs={
1530
                    'data-dynamic-display-child-of': 'visibility',
1531
                    'data-dynamic-display-value-in': 'owner|any',
1532
                })
1533
        else:
1534
            form.add(
1535
                CheckboxWidget,
1536
                'is_default',
1537
                title=_('Set as default view'),
1538
                value=self.view.is_default if self.view else False)
1485 1539
        if self.view and (self.view.user_id == get_request().user.id or
1486 1540
                          get_publisher().get_backoffice_root().is_accessible(self.admin_permission)):
1487 1541
            form.add(CheckboxWidget, 'update', title=_('Update existing view settings'), value=True)
......
1505 1559
        if not custom_view.columns['list']:
1506 1560
            get_session().message = ('error', _('Views must have at least one column.'))
1507 1561
            return redirect('.')
1562
        if form.get_widget('is_default'):
1563
            custom_view.is_default = form.get_widget('is_default').parse()
1508 1564
        if form.get_widget('visibility'):
1509 1565
            custom_view.visibility = form.get_widget('visibility').parse()
1566
            if custom_view.visibility == 'datasource':
1567
                custom_view.is_default = False
1510 1568
        custom_view.store()
1569

  
1570
        if custom_view.is_default and custom_view.visibility != 'datasource':
1571
            # need to clean other views to have only one default per owner/any visibility
1572
            for view in self.get_custom_views():
1573
                if view.id == custom_view.id:
1574
                    continue
1575
                if custom_view.visibility == view.visibility and view.is_default:
1576
                    view.is_default = False
1577
                    view.store()
1578

  
1511 1579
        if self.view:
1512 1580
            return redirect('../' + custom_view.get_url_slug() + '/')
1513 1581
        else:
......
1788 1856
        if 'job' in get_request().form:
1789 1857
            return self.job_multi()
1790 1858

  
1859
        self.use_default_view = True
1791 1860
        fields = self.get_fields_from_query()
1792 1861
        selected_filter = self.get_filter_from_query()
1793 1862
        criterias = self.get_criterias_from_query()
wcs/custom_views.py
32 32
    visibility = 'owner'
33 33
    formdef_type = None
34 34
    formdef_id = None
35
    is_default = False
35 36
    columns = None
36 37
    filters = None
37 38
    order_by = None
......
57 58
        self.formdef_type = value.xml_root_node
58 59

  
59 60
    def match(self, user, formdef):
60
        if self.visibility == 'owner' and self.user_id != str(user.id):
61
            return False
62 61
        if self.formdef_type != formdef.xml_root_node:
63 62
            return False
64 63
        if self.formdef_id != str(formdef.id):
65 64
            return False
65
        if self.visibility == 'owner' and self.user_id != str(user.id):
66
            return False
66 67
        return True
67 68

  
68 69
    def set_from_qs(self, qs):
wcs/qommon/static/js/wcs.listing.js
334 334
              }
335 335
            ]
336 336
    });
337
    $(document).trigger('wcs:dialog-loaded', $(dialog));
337 338
    return false;
338 339
  });
339 340

  
......
376 377
  prepare_page_links();
377 378
  prepare_row_links();
378 379
  prepare_column_headers();
380

  
381
  $('a[data-base-href]').each(function(idx, elem) {
382
    $(elem).attr('href', $(elem).data('base-href') + '?' + $('form#listing-settings').serialize());
383
  });
379 384
});
wcs/sql.py
803 803
                                        visibility varchar,
804 804
                                        formdef_type varchar,
805 805
                                        formdef_id varchar,
806
                                        is_default boolean,
806 807
                                        order_by varchar,
807 808
                                        columns jsonb,
808 809
                                        filters jsonb
......
814 815

  
815 816
    needed_fields = set([x[0] for x in CustomView._table_static_fields])
816 817

  
818
    # migrations
819
    if 'is_default' not in existing_fields:
820
        cur.execute('''ALTER TABLE %s ADD COLUMN is_default boolean DEFAULT FALSE''' % table_name)
821

  
817 822
    # delete obsolete fields
818 823
    for field in (existing_fields - needed_fields):
819 824
        cur.execute('''ALTER TABLE %s DROP COLUMN %s''' % (table_name, field))
......
2298 2303
        ('visibility', 'varchar'),
2299 2304
        ('formdef_type', 'varchar'),
2300 2305
        ('formdef_id', 'varchar'),
2306
        ('is_default', 'boolean'),
2301 2307
        ('order_by', 'varchar'),
2302 2308
        ('columns', 'jsonb'),
2303 2309
        ('filters', 'jsonb'),
......
2315 2321
            'visibility': self.visibility,
2316 2322
            'formdef_type': self.formdef_type,
2317 2323
            'formdef_id': self.formdef_id,
2324
            'is_default': self.is_default,
2318 2325
            'order_by': self.order_by,
2319 2326
            'columns': self.columns,
2320 2327
            'filters': self.filters,
......
2358 2365
        for field, value in zip(cls._table_static_fields, tuple(row)):
2359 2366
            if field[1] == 'varchar':
2360 2367
                setattr(o, field[0], str_encode(value))
2361
            elif field[1] == 'jsonb':
2368
            elif field[1] in ('jsonb', 'boolean'):
2362 2369
                setattr(o, field[0], value)
2363 2370
        return o
2364 2371

  
......
2817 2824
        # 25: create session_table
2818 2825
        # 32: add last_update_time column to session table
2819 2826
        do_session_table()
2820
    if sql_level < 37:
2821
        # 37: create custom_views tabl
2827
    if sql_level < 43:
2828
        # 37: create custom_views table
2829
        # 43: add is_default column to custom_views table
2822 2830
        do_custom_views_table()
2823 2831
    if sql_level < 30:
2824 2832
        # 30: actually remove evo.who on anonymised formdatas
2825
-