backoffice: make form management sidebar items configurable (#75957) #978
|
@ -241,13 +241,26 @@ def test_forms_edit_management(pub, formdef):
|
|||
# Misc management
|
||||
assert_option_display(resp, 'Management', 'Default')
|
||||
resp = resp.click('Management', href='options/management')
|
||||
assert resp.forms[0]['include_download_all_button'].checked is False
|
||||
resp.forms[0]['include_download_all_button'].checked = True
|
||||
assert resp.forms[0]['management_sidebar_items$elementgeneral'].checked is True
|
||||
assert resp.forms[0]['management_sidebar_items$elementdownload-files'].checked is False
|
||||
resp.forms[0]['management_sidebar_items$elementdownload-files'].checked = True
|
||||
resp = resp.forms[0].submit()
|
||||
assert resp.location == 'http://example.net/backoffice/forms/1/'
|
||||
resp = resp.follow()
|
||||
assert_option_display(resp, 'Management', 'Custom')
|
||||
assert FormDef.get(1).include_download_all_button is True
|
||||
assert 'general' in FormDef.get(1).management_sidebar_items
|
||||
assert 'download-files' in FormDef.get(1).management_sidebar_items
|
||||
|
||||
resp = resp.click('Management', href='options/management')
|
||||
resp.forms[0]['management_sidebar_items$elementgeneral'].checked = False
|
||||
resp = resp.forms[0].submit().follow()
|
||||
assert 'general' not in FormDef.get(1).management_sidebar_items
|
||||
|
||||
resp = resp.click('Management', href='options/management')
|
||||
resp.forms[0]['management_sidebar_items$elementgeneral'].checked = True
|
||||
resp.forms[0]['management_sidebar_items$elementdownload-files'].checked = False
|
||||
resp = resp.forms[0].submit().follow()
|
||||
assert FormDef.get(1).management_sidebar_items == {'__default__'}
|
||||
|
||||
|
||||
def test_forms_edit_tracking_code(pub, formdef):
|
||||
|
|
|
@ -2142,7 +2142,7 @@ def test_backoffice_download_as_zip(pub):
|
|||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
|
||||
assert 'Download all files as .zip' not in resp
|
||||
formdef.include_download_all_button = True
|
||||
formdef.management_sidebar_items.add('download-files')
|
||||
formdef.store()
|
||||
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
|
||||
resp = resp.click('Download all files as .zip')
|
||||
|
@ -2222,6 +2222,48 @@ def test_backoffice_geolocation_info(pub):
|
|||
assert 'data-init-lat="48.83' in resp.text
|
||||
|
||||
|
||||
def test_backoffice_sidebar_elements(pub):
|
||||
user = create_user(pub)
|
||||
|
||||
FormDef.wipe()
|
||||
formdef = FormDef()
|
||||
formdef.name = 'form title'
|
||||
formdef.geolocations = {'base': 'Geolocation'}
|
||||
formdef.store()
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.geolocations = {'base': {'lat': 48.83, 'lon': 2.32}}
|
||||
formdata.user_id = user.id
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
|
||||
other_formdata = formdef.data_class()()
|
||||
other_formdata.just_created()
|
||||
other_formdata.just_created()
|
||||
other_formdata.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get(formdata.get_backoffice_url())
|
||||
assert [x.text for x in resp.pyquery('#sidebar .extra-context h3')] == [
|
||||
'General Information',
|
||||
'Associated User',
|
||||
'Geolocation',
|
||||
]
|
||||
assert len(resp.pyquery('[data-async-url$="/user-pending-forms"]')) == 1
|
||||
|
||||
formdef.management_sidebar_items = ['general', 'pending-forms']
|
||||
formdef.store()
|
||||
resp = app.get(formdata.get_backoffice_url())
|
||||
assert [x.text for x in resp.pyquery('#sidebar .extra-context h3')] == ['General Information']
|
||||
assert len(resp.pyquery('[data-async-url$="/user-pending-forms"]')) == 1
|
||||
|
||||
formdef.management_sidebar_items = ['geolocation']
|
||||
formdef.store()
|
||||
resp = app.get(formdata.get_backoffice_url())
|
||||
assert [x.text for x in resp.pyquery('#sidebar .extra-context h3')] == ['Geolocation']
|
||||
assert len(resp.pyquery('[data-async-url$="/user-pending-forms"]')) == 0
|
||||
|
||||
|
||||
def test_backoffice_info_text(pub):
|
||||
create_user(pub)
|
||||
create_environment(pub)
|
||||
|
|
|
@ -1077,3 +1077,12 @@ def test_tracking_code_attributes(pub):
|
|||
assert f2.enable_tracking_codes == formdef.enable_tracking_codes
|
||||
assert f2.tracking_code_verify_fields == formdef.tracking_code_verify_fields
|
||||
assert f2.confirmation == formdef.confirmation
|
||||
|
||||
|
||||
def test_management_sidebar_items(pub):
|
||||
formdef = FormDef()
|
||||
formdef.name = 'Foo'
|
||||
formdef.url_name = 'foo'
|
||||
formdef.management_sidebar_items = {'general', 'pending-forms'}
|
||||
f2 = assert_xml_import_export_works(formdef)
|
||||
assert f2.management_sidebar_items == {'general', 'pending-forms'}
|
||||
|
|
|
@ -332,10 +332,12 @@ class OptionsDirectory(Directory):
|
|||
def management(self):
|
||||
form = Form(enctype='multipart/form-data')
|
||||
form.add(
|
||||
CheckboxWidget,
|
||||
'include_download_all_button',
|
||||
title=_('Include button to download all files'),
|
||||
value=self.formdef.include_download_all_button,
|
||||
CheckboxesWidget,
|
||||
'management_sidebar_items',
|
||||
title=_('Sidebar elements'),
|
||||
options=[(x[0], x[1], x[0]) for x in self.formdef.get_management_sidebar_available_items()],
|
||||
value=self.formdef.get_management_sidebar_items(),
|
||||
inline=False,
|
||||
)
|
||||
form.add(
|
||||
CheckboxWidget,
|
||||
|
@ -494,6 +496,7 @@ class OptionsDirectory(Directory):
|
|||
'submission_lateral_template',
|
||||
'drafts_lifespan',
|
||||
'user_support',
|
||||
'management_sidebar_items',
|
||||
]
|
||||
for attr in attrs:
|
||||
widget = form.get_widget(attr)
|
||||
|
@ -503,6 +506,10 @@ class OptionsDirectory(Directory):
|
|||
if has_error:
|
||||
continue
|
||||
new_value = widget.parse()
|
||||
if attr == 'management_sidebar_items':
|
||||
new_value = set(new_value)
|
||||
if new_value == self.formdef.__class__.management_sidebar_items:
|
||||
new_value = {'__default__'}
|
||||
if attr == 'digest_template':
|
||||
if self.formdef.default_digest_template != new_value:
|
||||
self.changed = True
|
||||
|
@ -777,7 +784,10 @@ class FormDefPage(Directory, TempfileDirectoryMixin):
|
|||
'options/management',
|
||||
_('Management'),
|
||||
_('Custom')
|
||||
if (self.formdef.skip_from_360_view or self.formdef.include_download_all_button)
|
||||
if (
|
||||
self.formdef.skip_from_360_view
|
||||
or self.formdef.management_sidebar_items != {'__default__'}
|
||||
)
|
||||
else _('Default'),
|
||||
),
|
||||
'tracking_code': self.add_option_line(
|
||||
|
|
|
@ -3411,6 +3411,7 @@ class FormBackOfficeStatusPage(FormStatusPage):
|
|||
|
||||
def get_extra_context_bar(self, parent=None):
|
||||
formdata = self.filled
|
||||
management_sidebar_items = self.formdef.get_management_sidebar_items()
|
||||
|
||||
r = TemplateIO(html=True)
|
||||
|
||||
|
@ -3418,7 +3419,6 @@ class FormBackOfficeStatusPage(FormStatusPage):
|
|||
r += htmltext('<p><a class="button" id="back-to-listing" href="..">%s</a></p>') % _(
|
||||
'Back to Listing'
|
||||
)
|
||||
r += htmltext('<div class="extra-context">')
|
||||
if (
|
||||
formdata.backoffice_submission
|
||||
and formdata.submission_agent_id == str(get_request().user.id)
|
||||
|
@ -3427,9 +3427,13 @@ class FormBackOfficeStatusPage(FormStatusPage):
|
|||
):
|
||||
# keep displaying tracking code to submission agent for 30
|
||||
# minutes after submission
|
||||
r += htmltext('<div class="extra-context">')
|
||||
r += htmltext('<h3>%s</h3>') % _('Tracking Code')
|
||||
r += htmltext('<p>%s</p>') % formdata.tracking_code
|
||||
r += htmltext('</div>')
|
||||
|
||||
if not formdata.is_draft() and 'general' in management_sidebar_items:
|
||||
r += htmltext('<div class="extra-context sidebar-general-information">')
|
||||
r += htmltext('<h3>%s</h3>') % _('General Information')
|
||||
r += htmltext('<p>')
|
||||
tm = misc.localstrftime(formdata.receipt_time)
|
||||
|
@ -3481,43 +3485,54 @@ class FormBackOfficeStatusPage(FormStatusPage):
|
|||
'date': formdata.anonymised.strftime(misc.date_format())
|
||||
}
|
||||
r += htmltext('</div>')
|
||||
r += htmltext('</div>') # .extra-context
|
||||
|
||||
if formdata.formdef.include_download_all_button:
|
||||
has_attached_files = False
|
||||
for value in (formdata.data or {}).values():
|
||||
if isinstance(value, PicklableUpload):
|
||||
has_attached_files = True
|
||||
if isinstance(value, dict) and isinstance(value.get('data'), list):
|
||||
# block fields
|
||||
for subvalue in value.get('data'):
|
||||
for subvalue_elem in subvalue.values():
|
||||
if isinstance(subvalue_elem, PicklableUpload):
|
||||
has_attached_files = True
|
||||
break
|
||||
if has_attached_files:
|
||||
break
|
||||
|
||||
if not formdata.is_draft() and 'download-files' in management_sidebar_items:
|
||||
has_attached_files = False
|
||||
for value in (formdata.data or {}).values():
|
||||
if isinstance(value, PicklableUpload):
|
||||
has_attached_files = True
|
||||
if isinstance(value, dict) and isinstance(value.get('data'), list):
|
||||
# block fields
|
||||
for subvalue in value.get('data'):
|
||||
for subvalue_elem in subvalue.values():
|
||||
if isinstance(subvalue_elem, PicklableUpload):
|
||||
has_attached_files = True
|
||||
break
|
||||
if has_attached_files:
|
||||
r += htmltext('<p><a class="button" href="download-as-zip">%s</a></p>') % _(
|
||||
'Download all files as .zip'
|
||||
)
|
||||
break
|
||||
|
||||
r += htmltext('</div>')
|
||||
if has_attached_files:
|
||||
r += htmltext('<div class="extra-context sidebar-download-files">')
|
||||
r += htmltext('<p><a class="button" href="download-as-zip">%s</a></p>') % _(
|
||||
'Download all files as .zip'
|
||||
)
|
||||
r += htmltext('</div>')
|
||||
|
||||
r += self.get_extra_submission_context_bar()
|
||||
r += self.get_extra_submission_channel_bar()
|
||||
r += self.get_extra_submission_user_id_bar(parent=parent)
|
||||
r += self.get_extra_geolocation_bar()
|
||||
if formdata.formdef.lateral_template:
|
||||
if 'submission-context' in management_sidebar_items:
|
||||
r += self.get_extra_submission_context_bar()
|
||||
r += self.get_extra_submission_channel_bar()
|
||||
|
||||
if 'user' in management_sidebar_items:
|
||||
r += self.get_extra_submission_user_id_bar(parent=parent)
|
||||
|
||||
if 'geolocation' in management_sidebar_items:
|
||||
r += self.get_extra_geolocation_bar()
|
||||
|
||||
if 'custom-template' in management_sidebar_items and formdata.formdef.lateral_template:
|
||||
r += htmltext('<div data-async-url="%slateral-block"></div>' % formdata.get_url(backoffice=True))
|
||||
|
||||
if not isinstance(formdata.formdef, CardDef) and formdata.user_id:
|
||||
if (
|
||||
'pending-forms' in management_sidebar_items
|
||||
and not isinstance(formdata.formdef, CardDef)
|
||||
and formdata.user_id
|
||||
):
|
||||
r += htmltext(
|
||||
'<div data-async-url="%suser-pending-forms"></div>' % formdata.get_url(backoffice=True)
|
||||
)
|
||||
|
||||
if not formdata.is_draft() and self.can_go_in_inspector():
|
||||
r += htmltext('<div class="extra-context">')
|
||||
r += htmltext('<div class="extra-context sidebar-data-inspector">')
|
||||
r += htmltext('<p><a href="%sinspect">' % formdata.get_url(backoffice=True))
|
||||
r += htmltext('%s</a></p>') % _('Data Inspector')
|
||||
r += htmltext('</div>')
|
||||
|
@ -3529,7 +3544,7 @@ class FormBackOfficeStatusPage(FormStatusPage):
|
|||
r = TemplateIO(html=True)
|
||||
if formdata.submission_context or formdata.submission_channel:
|
||||
extra_context = formdata.submission_context or {}
|
||||
r += htmltext('<div class="extra-context">')
|
||||
r += htmltext('<div class="extra-context sidebar-submission-context">')
|
||||
if extra_context.get('orig_formdef_id'):
|
||||
object_type = extra_context.get('orig_object_type', 'formdef')
|
||||
if object_type == 'formdef':
|
||||
|
@ -3572,7 +3587,7 @@ class FormBackOfficeStatusPage(FormStatusPage):
|
|||
r += htmltext('<p>%s</p>') % extra_context.get('comments')
|
||||
if extra_context.get('summary_url'):
|
||||
r += htmltext('<div data-content-url="%s"></div>' % (extra_context.get('summary_url')))
|
||||
r += htmltext('</div>')
|
||||
r += htmltext('</div>') # closes .extra-context from get_extra_submission_context_bar
|
||||
|
||||
return r.getvalue()
|
||||
|
||||
|
@ -3580,7 +3595,7 @@ class FormBackOfficeStatusPage(FormStatusPage):
|
|||
formdata = self.filled
|
||||
r = TemplateIO(html=True)
|
||||
if formdata and formdata.user_id and formdata.get_user():
|
||||
r += htmltext('<div class="extra-context">')
|
||||
r += htmltext('<div class="extra-context sidebar--user">')
|
||||
r += htmltext('<h3>%s</h3>') % _('Associated User')
|
||||
users_cfg = get_cfg('users', {})
|
||||
sidebar_user_template = users_cfg.get('sidebar_template')
|
||||
|
@ -3617,7 +3632,7 @@ class FormBackOfficeStatusPage(FormStatusPage):
|
|||
formdata = self.filled
|
||||
r = TemplateIO(html=True)
|
||||
if formdata.formdef.geolocations and formdata.geolocations:
|
||||
r += htmltext('<div class="geolocations">')
|
||||
r += htmltext('<div class="extra-context geolocations sidebar-geolocations">')
|
||||
for geoloc_key in formdata.formdef.geolocations:
|
||||
if geoloc_key not in formdata.geolocations:
|
||||
continue
|
||||
|
@ -3676,7 +3691,7 @@ class FormBackOfficeStatusPage(FormStatusPage):
|
|||
self.filled.related_user_forms = formdatas
|
||||
|
||||
if formdatas:
|
||||
r += htmltext('<div class="user-pending-forms">')
|
||||
r += htmltext('<div class="extra-context user-pending-forms">')
|
||||
r += htmltext('<h3>%s</h3>') % _('User Pending Forms')
|
||||
categories = {}
|
||||
formdata_by_category = {}
|
||||
|
|
|
@ -174,6 +174,14 @@ class FormDef(StorableObject):
|
|||
expiration_date = None
|
||||
has_captcha = False
|
||||
skip_from_360_view = False
|
||||
management_sidebar_items = {
|
||||
'general',
|
||||
'submission-context',
|
||||
'user',
|
||||
'geolocation',
|
||||
'custom-template',
|
||||
'pending-forms',
|
||||
}
|
||||
include_download_all_button = False
|
||||
appearance_keywords = None
|
||||
digest_templates = None
|
||||
|
@ -221,7 +229,6 @@ class FormDef(StorableObject):
|
|||
'enable_tracking_codes',
|
||||
'confirmation',
|
||||
'always_advertise',
|
||||
'include_download_all_button',
|
||||
'has_captcha',
|
||||
'skip_from_360_view',
|
||||
]
|
||||
|
@ -231,6 +238,7 @@ class FormDef(StorableObject):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields = []
|
||||
self.management_sidebar_items = {'__default__'}
|
||||
|
||||
def __eq__(self, other):
|
||||
return bool(
|
||||
|
@ -267,6 +275,12 @@ class FormDef(StorableObject):
|
|||
changed = True
|
||||
break
|
||||
|
||||
if self.include_download_all_button: # 2023-12-30
|
||||
self.management_sidebar_items = self.__class__.management_sidebar_items.copy()
|
||||
self.management_sidebar_items.add('download-files')
|
||||
self.include_download_all_button = False
|
||||
changed = True
|
||||
|
||||
for f in self.fields or []:
|
||||
changed |= f.migrate()
|
||||
|
||||
|
@ -287,6 +301,29 @@ class FormDef(StorableObject):
|
|||
sql.clean_global_views(conn, cur)
|
||||
cur.close()
|
||||
|
||||
def get_management_sidebar_available_items(self):
|
||||
return [
|
||||
('general', _('General Information')),
|
||||
('download-files', _('Button to download all files')),
|
||||
('submission-context', _('Submission context')),
|
||||
('user', _('Associated User')),
|
||||
('geolocation', _('Geolocation')),
|
||||
('custom-template', _('Custom template')),
|
||||
('pending-forms', _('User Pending Forms')),
|
||||
]
|
||||
|
||||
def management_sidebar_items_labels(self):
|
||||
# return ordered labels
|
||||
management_sidebar_items = self.get_management_sidebar_items()
|
||||
for key, label in self.get_management_sidebar_available_items():
|
||||
if key in management_sidebar_items:
|
||||
yield label
|
||||
|
||||
def get_management_sidebar_items(self):
|
||||
if self.management_sidebar_items == {'__default__'}:
|
||||
return self.__class__.management_sidebar_items
|
||||
return self.management_sidebar_items or []
|
||||
|
||||
@property
|
||||
def data_class_name(self):
|
||||
return '_wcs_%s' % self.url_name.title()
|
||||
|
@ -1078,6 +1115,9 @@ class FormDef(StorableObject):
|
|||
if self.required_authentication_contexts:
|
||||
root['required_authentication_contexts'] = self.required_authentication_contexts[:]
|
||||
|
||||
if self.management_sidebar_items:
|
||||
root['management_sidebar_items'] = sorted(self.management_sidebar_items)
|
||||
|
||||
if isinstance(self, CardDef):
|
||||
all_carddefs = CardDef.select(ignore_errors=True)
|
||||
all_carddefs = [c for c in all_carddefs if c]
|
||||
|
@ -1245,6 +1285,9 @@ class FormDef(StorableObject):
|
|||
str(x) for x in value.get('required_authentication_contexts')
|
||||
]
|
||||
|
||||
if value.get('management_sidebar_items'):
|
||||
formdef.management_sidebar_items = {str(x) for x in value.get('management_sidebar_items')}
|
||||
|
||||
return formdef
|
||||
|
||||
def export_to_xml(self, include_id=False):
|
||||
|
@ -1394,6 +1437,11 @@ class FormDef(StorableObject):
|
|||
for auth_context in self.required_authentication_contexts:
|
||||
ET.SubElement(element, 'method').text = force_str(auth_context)
|
||||
|
||||
if self.management_sidebar_items:
|
||||
element = ET.SubElement(root, 'management_sidebar_items')
|
||||
for item in sorted(self.management_sidebar_items):
|
||||
ET.SubElement(element, 'item').text = force_str(item)
|
||||
|
||||
if self.digest_templates:
|
||||
digest_templates = ET.SubElement(root, 'digest_templates')
|
||||
for key, value in self.digest_templates.items():
|
||||
|
@ -1593,6 +1641,12 @@ class FormDef(StorableObject):
|
|||
for child in node:
|
||||
formdef.required_authentication_contexts.append(str(child.text))
|
||||
|
||||
if tree.find('management_sidebar_items') is not None:
|
||||
node = tree.find('management_sidebar_items')
|
||||
formdef.management_sidebar_items = set()
|
||||
for child in node:
|
||||
formdef.management_sidebar_items.add(str(child.text))
|
||||
|
||||
if tree.find('digest_templates') is not None:
|
||||
digest_templates_node = tree.find('digest_templates')
|
||||
formdef.digest_templates = {}
|
||||
|
|
|
@ -56,7 +56,11 @@
|
|||
{% if formdef.roles %}
|
||||
<li><span class="parameter">{% trans "Display to unlogged users" %}{% trans ":" %}</span> {{ formdef.always_advertise|yesno }}</li>
|
||||
{% endif %}
|
||||
<li><span class="parameter">{% trans "Include button to download all files" %}{% trans ":" %}</span> {{ formdef.include_download_all_button|yesno }}</li>
|
||||
<li><span class="parameter">{% trans "Management sidebar elements" %}{% trans ":" %}</span>
|
||||
<ul>
|
||||
{% for label in formdef.management_sidebar_items_labels %} <li>{{ label }}</li>{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
<li><span class="parameter">{% trans "Skip from per user view" %}{% trans ":" %}</span> {{ formdef.skip_from_360_view|yesno }}</li>
|
||||
<li><span class="parameter">{% trans "Tracking codes" %}{% trans ":" %}</span> {{ formdef.enable_tracking_codes|yesno }}</li>
|
||||
{% if formdef.enable_tracking_codes %}
|
||||
|
|
Loading…
Reference in New Issue