backoffice: use a customisable template to render user info in sidebar (#40034)

This commit is contained in:
Frédéric Péters 2020-05-15 09:43:12 +02:00
parent 7134680e7c
commit e34366d5b5
4 changed files with 130 additions and 18 deletions

View File

@ -4727,31 +4727,31 @@ def test_settings_user(pub):
resp = app.get('/backoffice/settings/users').follow().follow()
# add a field
resp.forms[1]['label'] = 'foobar'
resp = resp.forms[1].submit()
resp.forms[2]['label'] = 'foobar'
resp = resp.forms[2].submit()
assert resp.location == 'http://example.net/backoffice/settings/users/fields/'
resp = resp.follow()
assert b'foobar' in pub.cfg['users']['formdef']
assert 'foobar' in resp.text
# set field as email
resp.forms[0]['field_email'] = '1'
resp = resp.forms[0].submit()
resp.forms['mapping']['field_email'] = '1'
resp = resp.forms['mapping'].submit()
assert resp.location == 'http://example.net/backoffice/settings/users/fields/'
resp = resp.follow()
assert pub.cfg['users']['field_email'] == '1'
# and unset it
resp.forms[0]['field_email'] = ''
resp = resp.forms[0].submit()
resp.forms['mapping']['field_email'] = ''
resp = resp.forms['mapping'].submit()
assert resp.location == 'http://example.net/backoffice/settings/users/fields/'
resp = resp.follow()
assert pub.cfg['users']['field_email'] == None
# add a comment field
resp.forms[1]['label'] = 'barfoo'
resp.forms[1]['type'] = 'Comment'
resp = resp.forms[1].submit()
resp.forms[2]['label'] = 'barfoo'
resp.forms[2]['type'] = 'Comment'
resp = resp.forms[2].submit()
assert resp.location == 'http://example.net/backoffice/settings/users/fields/'
resp = resp.follow()
assert b'barfoo' in pub.cfg['users']['formdef']
@ -4760,16 +4760,43 @@ def test_settings_user(pub):
# check fields are present in edit form
resp = app.get('/backoffice/users/%s/edit' % user.id)
assert 'barfoo' in resp.text
assert 'f1' in resp.forms[0].fields
assert 'email' in resp.forms[0].fields
assert 'f1' in resp.form.fields
assert 'email' in resp.form.fields
# check the email field is not displayed if it's overridden by a custom
# field.
pub.cfg['users']['field_email'] = '1'
pub.write_cfg()
resp = app.get('/backoffice/users/%s/edit' % user.id)
assert 'f1' in resp.forms[0].fields
assert 'email' not in resp.forms[0].fields
assert 'f1' in resp.form.fields
assert 'email' not in resp.form.fields
# set a sidebar template
app = login(get_app(pub))
resp = app.get('/backoffice/settings/users').follow().follow()
resp.forms['template']['sidebar_template'] = 'hello {{ form_user_display_name }}'
resp = resp.forms['template'].submit().follow()
assert pub.cfg['users']['sidebar_template'] == 'hello {{ form_user_display_name }}'
resp.forms['template']['sidebar_template'] = '{% if True %}'
resp = resp.forms['template'].submit().follow()
assert pub.cfg['users']['sidebar_template'] == 'hello {{ form_user_display_name }}'
assert 'syntax error in Django template' in resp
# disable users screen
if not pub.site_options.has_section('options'):
pub.site_options.add_section('options')
pub.site_options.set('options', 'settings-disabled-screens', 'users')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
resp = app.get('/backoffice/settings/')
resp = resp.click('Users', href='user-template')
resp.forms['template']['sidebar_template'] = '{% if True %}'
resp = resp.forms['template'].submit()
assert 'syntax error in Django template' in resp
resp.forms['template']['sidebar_template'] = 'hello {{ form_user_display_name }}'
resp = resp.forms['template'].submit()
assert pub.cfg['users']['sidebar_template'] == 'hello {{ form_user_display_name }}'
# restore config
pub.cfg['users']['field_email'] = None

View File

@ -1913,6 +1913,32 @@ def test_backoffice_submission_context(pub):
assert 'by %s' % user.get_display_name() in resp.text
def test_backoffice_sidebar_user_template(pub):
user = create_user(pub)
create_environment(pub)
form_class = FormDef.get_by_urlname('form-title').data_class()
number31 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0]
number31.user_id = user.id
number31.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp = resp.click(href='%s/' % number31.id)
assert 'Associated User' in resp.text
assert '<p>admin</p>' in resp.text
pub.cfg['users'] = {'sidebar_template': 'XXX{{ form_user_display_name }}YYY'}
pub.write_cfg()
resp = app.get(resp.request.url)
assert '<p>XXXadminYYY</p>' in resp.text
pub.cfg['users'] = {'sidebar_template': 'XXX<b>{{ form_user_display_name }}</b>YYY'}
pub.write_cfg()
resp = app.get(resp.request.url)
assert '<p>XXX<b>admin</b>YYY</p>' in resp.text
user.name = 'adm<i>n'
user.store()
resp = app.get(resp.request.url)
assert '<p>XXX<b>adm&lt;i&gt;n</b>YYY</p>' in resp.text
def test_backoffice_geolocation_info(pub):
user = create_user(pub)
create_environment(pub)

View File

@ -118,7 +118,7 @@ class UserFieldDefPage(FieldDefPage):
class UserFieldsDirectory(FieldsDirectory):
_q_exports = ['', 'update_order', 'new', 'mapping']
_q_exports = ['', 'update_order', 'new', 'mapping', 'template']
section = 'settings'
field_def_page_class = UserFieldDefPage
@ -128,11 +128,15 @@ class UserFieldsDirectory(FieldsDirectory):
def index_bottom(self):
r = TemplateIO(html=True)
r += get_session().display_message()
r += htmltext('<div class="bo-block">')
r += htmltext('<h2>%s</h2>') % _('Fields Mapping')
r += htmltext('<p>%s</p>') % _('These settings make it possible to assign custom user fields to standard user fields.')
form = self.mapping_form()
r += form.render()
r += self.mapping_form().render()
r += htmltext('</div>')
r += htmltext('<div class="bo-block">')
r += htmltext('<h2>%s</h2>') % _('Sidebar Template')
r += self.sidebar_template_form().render()
r += htmltext('</div>')
return r.getvalue()
@ -141,7 +145,7 @@ class UserFieldsDirectory(FieldsDirectory):
options = [(None, _('None'), '')] + [
(x.id, x.label, x.id) for x in self.objectdef.fields]
form = Form(action = 'mapping', enctype='multipart/form-data')
form = Form(action='mapping', id='mapping')
field_name_value = users_cfg.get('field_name')
if type(field_name_value) is str:
field_name_value = [field_name_value]
@ -161,6 +165,26 @@ class UserFieldsDirectory(FieldsDirectory):
cfg_submit(form, 'users', ['field_name', 'field_email'])
return redirect('.')
@classmethod
def sidebar_template_form(cls, action='template'):
users_cfg = get_cfg('users', {})
form = Form(action=action, id='template')
form.add(TextWidget, 'sidebar_template',
value=users_cfg.get('sidebar_template') or '{{ form_user_display_name }}',
required=False,
validation_function=ComputedExpressionWidget.validate_template,
rows=4)
form.add_submit('submit', _('Submit'))
return form
def template(self):
form = self.sidebar_template_form()
if form.has_errors():
get_session().message = ('error', form.get_widget('sidebar_template').get_error())
return redirect('.')
cfg_submit(form, 'users', ['sidebar_template'])
return redirect('.')
class UserFieldsFormDef(FormDef):
'''Class to handle custom user fields, it loads and saves from/to
@ -405,6 +429,7 @@ class SettingsDirectory(QommonSettingsDirectory):
'session', 'download_theme', 'smstest', 'postgresql',
('admin-permissions', 'admin_permissions'), 'geolocation',
'theme_preview', 'filetypes',
('user-template', 'user_template'),
('data-sources', 'data_sources'), 'wscalls', 'logs']
emails = EmailsDirectory()
@ -517,6 +542,10 @@ class SettingsDirectory(QommonSettingsDirectory):
if enabled('users'):
r += htmltext('<dt><a href="users/">%s</a></dt> <dd>%s</dd>') % (
_('Users'), _('Configure users'))
else:
# minimal options
r += htmltext('<dt><a href="user-template">%s</a></dt> <dd>%s</dd>') % (
_('Users'), _('Configure sidebar template for users'))
if enabled('emails'):
r += htmltext('<dt><a href="emails/">%s</a></dt> <dd>%s</dd>') % (
_('Emails'), _('Configure email settings'))
@ -1223,3 +1252,23 @@ class SettingsDirectory(QommonSettingsDirectory):
r += htmltext('<h2>%s</h2>') % _('Geolocation Settings')
r += form.render()
return r.getvalue()
def user_template(self):
users_cfg = get_cfg('users', {})
form = UserFieldsDirectory.sidebar_template_form(action='user-template')
form.get_widget('sidebar_template').set_title(_('Sidebar Template'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if form.is_submitted() and not form.has_errors():
cfg_submit(form, 'users', ['sidebar_template'])
return redirect('.')
get_response().breadcrumb.append(('user-template', _('Users')))
html_top('settings', title=_('Users'))
r = TemplateIO(html=True)
r += htmltext('<h2>%s</h2>') % _('Users')
r += form.render()
return r.getvalue()

View File

@ -53,6 +53,7 @@ from ..qommon import ods
from ..qommon.form import *
from ..qommon.storage import (Equal, NotEqual, LessOrEqual, GreaterOrEqual, Or,
Intersects, ILike, FtsMatch, Contains, Null, NotNull)
from ..qommon.template import Template
from wcs.api_utils import get_user_from_api_query_string
from wcs.carddef import CardDef
@ -2661,7 +2662,16 @@ class FormBackOfficeStatusPage(FormStatusPage):
if formdata.user_id and formdata.get_user():
r += htmltext('<div class="extra-context">')
r += htmltext('<h3>%s</h3>') % _('Associated User')
r += htmltext('<p>%s</p>') % formdata.get_user().display_name
users_cfg = get_cfg('users', {})
sidebar_user_template = users_cfg.get('sidebar_template')
if sidebar_user_template:
variables = get_publisher().substitutions.get_context_variables(mode='lazy')
sidebar_user = Template(sidebar_user_template).render(variables)
if not sidebar_user.startswith('<'):
sidebar_user = htmltext('<p>%s</p>' % sidebar_user)
r += sidebar_user
else:
r += htmltext('<p>%s</p>') % formdata.get_user().display_name
r += htmltext('</div>')
if formdata.formdef.geolocations and formdata.geolocations: