diff --git a/tests/backoffice_pages/test_custom_view.py b/tests/backoffice_pages/test_custom_view.py
index 34f97818c..fbbb61a6a 100644
--- a/tests/backoffice_pages/test_custom_view.py
+++ b/tests/backoffice_pages/test_custom_view.py
@@ -13,6 +13,7 @@ from wcs.qommon.ident.password_accounts import PasswordAccount
from wcs.qommon.http_request import HTTPRequest
from wcs.carddef import CardDef
from wcs.formdef import FormDef
+from wcs.roles import Role
from wcs import fields
from utilities import (get_app, login, create_temporary_pub, clean_temporary_pub)
@@ -290,6 +291,156 @@ def test_backoffice_custom_view_visibility(pub):
[('custom-test-view', 'owner'), ('shared-view', 'any'), ('shared-view-2', 'any')])
+def test_backoffice_custom_view_is_default(pub):
+ create_superuser(pub)
+
+ FormDef.wipe()
+ pub.custom_view_class.wipe()
+ formdef = FormDef()
+ formdef.name = 'form title'
+ formdef.fields = []
+ formdef.workflow_roles = {'_receiver': 1}
+ formdef.store()
+
+ # private custom view
+ agent = pub.user_class(name='agent')
+ agent.roles = [formdef.workflow_roles['_receiver']]
+ agent.store()
+ account = PasswordAccount(id='agent')
+ account.set_password('agent')
+ account.user_id = agent.id
+ account.store()
+ app = login(get_app(pub), username='agent', password='agent')
+ resp = app.get('/backoffice/management/form-title/')
+ resp = resp.forms['listing-settings'].submit()
+ resp.forms['save-custom-view']['title'] = 'view 1'
+ resp.forms['save-custom-view']['is_default'] = True
+ resp = resp.forms['save-custom-view'].submit()
+
+ # other private custom view
+ app = login(get_app(pub))
+ resp = app.get('/backoffice/management/form-title/')
+ resp = resp.forms['listing-settings'].submit()
+ resp.forms['save-custom-view']['title'] = 'view 2'
+ resp.forms['save-custom-view']['visibility'] = 'owner'
+ resp.forms['save-custom-view']['is_default'] = True
+ resp = resp.forms['save-custom-view'].submit()
+
+ # shared custom view
+ resp = app.get('/backoffice/management/form-title/')
+ resp = resp.forms['listing-settings'].submit()
+ resp.forms['save-custom-view']['title'] = 'view 3'
+ resp.forms['save-custom-view']['visibility'] = 'any'
+ resp.forms['save-custom-view']['is_default'] = True
+ resp = resp.forms['save-custom-view'].submit()
+
+ assert pub.custom_view_class.count() == 3
+ assert pub.custom_view_class.get(1).is_default is True # simple user - private
+ assert pub.custom_view_class.get(2).is_default is True # super user - private
+ assert pub.custom_view_class.get(3).is_default is True # super user - shared
+
+ # not possible to define more than one default private view
+ resp = app.get('/backoffice/management/form-title/')
+ resp = resp.forms['listing-settings'].submit()
+ resp.forms['save-custom-view']['title'] = 'view 4'
+ resp.forms['save-custom-view']['visibility'] = 'owner'
+ resp.forms['save-custom-view']['is_default'] = True
+ resp = resp.forms['save-custom-view'].submit()
+ assert pub.custom_view_class.count() == 4
+ assert pub.custom_view_class.get(1).is_default is True # simple user - private
+ assert pub.custom_view_class.get(2).is_default is False # super user - private
+ assert pub.custom_view_class.get(3).is_default is True # super user - shared
+ assert pub.custom_view_class.get(4).is_default is True # super user - private 2
+
+ # not possible to define more than one default shared view
+ resp = app.get('/backoffice/management/form-title/')
+ resp = resp.forms['listing-settings'].submit()
+ resp.forms['save-custom-view']['title'] = 'view 5'
+ resp.forms['save-custom-view']['visibility'] = 'any'
+ resp.forms['save-custom-view']['is_default'] = True
+ resp = resp.forms['save-custom-view'].submit()
+ assert pub.custom_view_class.count() == 5
+ assert pub.custom_view_class.get(1).is_default is True # simple user - private
+ assert pub.custom_view_class.get(2).is_default is False # super user - private
+ assert pub.custom_view_class.get(3).is_default is False # super user - shared
+ assert pub.custom_view_class.get(4).is_default is True # super user - private 2
+ assert pub.custom_view_class.get(5).is_default is True # super user - shared 2
+
+
+def test_backoffice_default_custom_view(pub):
+ user = create_superuser(pub)
+
+ FormDef.wipe()
+ pub.custom_view_class.wipe()
+ formdef = FormDef()
+ formdef.name = 'form title'
+ formdef.fields = [
+ fields.ItemField(
+ id='1', label='field 1', type='item',
+ items=['foo', 'bar', 'baz'],
+ display_locations=['validation', 'summary', 'listings']),
+ ]
+ formdef.workflow_roles = {'_receiver': 1}
+ formdef.store()
+
+ formdef.data_class().wipe()
+ for i in range(3):
+ formdata = formdef.data_class()()
+ formdata.data = {}
+ if i == 0:
+ formdata.data['1'] = 'foo'
+ formdata.data['1_display'] = 'foo'
+ else:
+ formdata.data['1'] = 'baz'
+ formdata.data['1_display'] = 'baz'
+ if i < 2:
+ formdata.jump_status('new')
+ formdata.store()
+
+ app = login(get_app(pub))
+ # define a shared defautl view
+ custom_view = pub.custom_view_class()
+ custom_view.title = 'shared custom test view'
+ custom_view.formdef = formdef
+ custom_view.visibility = 'any'
+ custom_view.columns = {'list': [{'id': 'id'}]}
+ custom_view.filters = {'filter-1-value': 'baz', 'filter': 'all', 'filter-1': 'on', 'filter-status': 'on'}
+ custom_view.user = user
+ custom_view.is_default = True
+ custom_view.store()
+ resp = app.get('/backoffice/management/form-title/shared-custom-test-view/')
+ assert resp.text.count('User Label') == 0
+ assert resp.text.count('field 1') == 0
+ assert resp.text.count('
User Label') == 0
+ assert resp.text.count('field 1') == 0
+ assert resp.text.count('
User Label') == 0
+ assert resp.text.count('field 1') == 1
+ assert resp.text.count('
User Label') == 0
+ assert resp.text.count('field 1') == 1
+ assert resp.text.count('
= 2 and path[1] == 'ics':
for view in self.get_custom_views([Equal('visibility', 'any'), Equal('slug', path[0])]):
- self.view = view
+ self._view = view
path = path[1:]
self.is_webhook = False
diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py
index c048421a3..8dbb2726c 100644
--- a/wcs/backoffice/management.py
+++ b/wcs/backoffice/management.py
@@ -1027,7 +1027,9 @@ class FormPage(Directory):
_q_exports = ['', 'csv', 'stats', 'xls', 'ods', 'json', 'export', 'map',
'geojson', ('filter-options', 'filter_options'),
('save-view', 'save_view'), ('delete-view', 'delete_view'),]
- view = None
+ _view = None
+ default_view = None
+ use_default_view = False
admin_permission = 'forms'
formdef_class = FormDef
search_label = N_('Search in form content')
@@ -1043,9 +1045,40 @@ class FormPage(Directory):
get_response().breadcrumb.append((component + '/', self.formdef.name))
else:
self.formdef = formdef
- self.view = view
+ self._view = view
if update_breadcrumbs:
get_response().breadcrumb.append((view.get_url_slug() + '/', view.title))
+ self.set_default_view()
+
+ def set_default_view(self):
+ if not get_request():
+ return
+ custom_views = list(self.get_custom_views())
+
+ # search for first default user custom view
+ for view in custom_views:
+ if view.visibility != 'owner':
+ continue
+ if not view.is_default:
+ continue
+ self.default_view = view
+ return
+
+ # default user custom view not found, search in 'any' custom views
+ for view in custom_views:
+ if view.visibility != 'any':
+ continue
+ if not view.is_default:
+ continue
+ self.default_view = view
+ return
+
+ @property
+ def view(self):
+ view = self._view
+ if self.use_default_view:
+ view = view or self.default_view
+ return view
def check_access(self, api_name=None):
session = get_session()
@@ -1093,14 +1126,16 @@ class FormPage(Directory):
r += htmltext('')
return r.getvalue()
@@ -1479,9 +1514,28 @@ class FormPage(Directory):
if isinstance(self.formdef, CardDef) and self.formdef.digest_template:
options.append(('datasource', _('as data source'), 'datasource'))
- form.add(RadiobuttonsWidget, 'visibility', title=_('Visibility'),
- value=self.view.visibility if self.view else 'owner',
- options=options)
+ form.add(
+ RadiobuttonsWidget,
+ 'visibility',
+ title=_('Visibility'),
+ value=self.view.visibility if self.view else 'owner',
+ options=options,
+ attrs={'data-dynamic-display-parent': 'true'})
+ form.add(
+ CheckboxWidget,
+ 'is_default',
+ title=_('Set as default view'),
+ value=self.view.is_default if self.view else False,
+ attrs={
+ 'data-dynamic-display-child-of': 'visibility',
+ 'data-dynamic-display-value-in': 'owner|any',
+ })
+ else:
+ form.add(
+ CheckboxWidget,
+ 'is_default',
+ title=_('Set as default view'),
+ value=self.view.is_default if self.view else False)
if self.view and (self.view.user_id == get_request().user.id or
get_publisher().get_backoffice_root().is_accessible(self.admin_permission)):
form.add(CheckboxWidget, 'update', title=_('Update existing view settings'), value=True)
@@ -1505,9 +1559,23 @@ class FormPage(Directory):
if not custom_view.columns['list']:
get_session().message = ('error', _('Views must have at least one column.'))
return redirect('.')
+ if form.get_widget('is_default'):
+ custom_view.is_default = form.get_widget('is_default').parse()
if form.get_widget('visibility'):
custom_view.visibility = form.get_widget('visibility').parse()
+ if custom_view.visibility == 'datasource':
+ custom_view.is_default = False
custom_view.store()
+
+ if custom_view.is_default and custom_view.visibility != 'datasource':
+ # need to clean other views to have only one default per owner/any visibility
+ for view in self.get_custom_views():
+ if view.id == custom_view.id:
+ continue
+ if custom_view.visibility == view.visibility and view.is_default:
+ view.is_default = False
+ view.store()
+
if self.view:
return redirect('../' + custom_view.get_url_slug() + '/')
else:
@@ -1788,6 +1856,7 @@ class FormPage(Directory):
if 'job' in get_request().form:
return self.job_multi()
+ self.use_default_view = True
fields = self.get_fields_from_query()
selected_filter = self.get_filter_from_query()
criterias = self.get_criterias_from_query()
diff --git a/wcs/custom_views.py b/wcs/custom_views.py
index 26de1fbe8..7ae95e72a 100644
--- a/wcs/custom_views.py
+++ b/wcs/custom_views.py
@@ -32,6 +32,7 @@ class CustomView(StorableObject):
visibility = 'owner'
formdef_type = None
formdef_id = None
+ is_default = False
columns = None
filters = None
order_by = None
@@ -57,12 +58,12 @@ class CustomView(StorableObject):
self.formdef_type = value.xml_root_node
def match(self, user, formdef):
- if self.visibility == 'owner' and self.user_id != str(user.id):
- return False
if self.formdef_type != formdef.xml_root_node:
return False
if self.formdef_id != str(formdef.id):
return False
+ if self.visibility == 'owner' and self.user_id != str(user.id):
+ return False
return True
def set_from_qs(self, qs):
diff --git a/wcs/qommon/static/js/wcs.listing.js b/wcs/qommon/static/js/wcs.listing.js
index f99843824..28cb8f416 100644
--- a/wcs/qommon/static/js/wcs.listing.js
+++ b/wcs/qommon/static/js/wcs.listing.js
@@ -334,6 +334,7 @@ $(function() {
}
]
});
+ $(document).trigger('wcs:dialog-loaded', $(dialog));
return false;
});
@@ -376,4 +377,8 @@ $(function() {
prepare_page_links();
prepare_row_links();
prepare_column_headers();
+
+ $('a[data-base-href]').each(function(idx, elem) {
+ $(elem).attr('href', $(elem).data('base-href') + '?' + $('form#listing-settings').serialize());
+ });
});
diff --git a/wcs/sql.py b/wcs/sql.py
index aa326b179..85ccd9861 100644
--- a/wcs/sql.py
+++ b/wcs/sql.py
@@ -803,6 +803,7 @@ def do_custom_views_table():
visibility varchar,
formdef_type varchar,
formdef_id varchar,
+ is_default boolean,
order_by varchar,
columns jsonb,
filters jsonb
@@ -814,6 +815,10 @@ def do_custom_views_table():
needed_fields = set([x[0] for x in CustomView._table_static_fields])
+ # migrations
+ if 'is_default' not in existing_fields:
+ cur.execute('''ALTER TABLE %s ADD COLUMN is_default boolean DEFAULT FALSE''' % table_name)
+
# delete obsolete fields
for field in (existing_fields - needed_fields):
cur.execute('''ALTER TABLE %s DROP COLUMN %s''' % (table_name, field))
@@ -2298,6 +2303,7 @@ class CustomView(SqlMixin, wcs.custom_views.CustomView):
('visibility', 'varchar'),
('formdef_type', 'varchar'),
('formdef_id', 'varchar'),
+ ('is_default', 'boolean'),
('order_by', 'varchar'),
('columns', 'jsonb'),
('filters', 'jsonb'),
@@ -2315,6 +2321,7 @@ class CustomView(SqlMixin, wcs.custom_views.CustomView):
'visibility': self.visibility,
'formdef_type': self.formdef_type,
'formdef_id': self.formdef_id,
+ 'is_default': self.is_default,
'order_by': self.order_by,
'columns': self.columns,
'filters': self.filters,
@@ -2358,7 +2365,7 @@ class CustomView(SqlMixin, wcs.custom_views.CustomView):
for field, value in zip(cls._table_static_fields, tuple(row)):
if field[1] == 'varchar':
setattr(o, field[0], str_encode(value))
- elif field[1] == 'jsonb':
+ elif field[1] in ('jsonb', 'boolean'):
setattr(o, field[0], value)
return o
@@ -2817,8 +2824,9 @@ def migrate():
# 25: create session_table
# 32: add last_update_time column to session table
do_session_table()
- if sql_level < 37:
- # 37: create custom_views tabl
+ if sql_level < 43:
+ # 37: create custom_views table
+ # 43: add is_default column to custom_views table
do_custom_views_table()
if sql_level < 30:
# 30: actually remove evo.who on anonymised formdatas