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() resp = app.get('/backoffice/settings/users').follow().follow()
# add a field # add a field
resp.forms[1]['label'] = 'foobar' resp.forms[2]['label'] = 'foobar'
resp = resp.forms[1].submit() resp = resp.forms[2].submit()
assert resp.location == 'http://example.net/backoffice/settings/users/fields/' assert resp.location == 'http://example.net/backoffice/settings/users/fields/'
resp = resp.follow() resp = resp.follow()
assert b'foobar' in pub.cfg['users']['formdef'] assert b'foobar' in pub.cfg['users']['formdef']
assert 'foobar' in resp.text assert 'foobar' in resp.text
# set field as email # set field as email
resp.forms[0]['field_email'] = '1' resp.forms['mapping']['field_email'] = '1'
resp = resp.forms[0].submit() resp = resp.forms['mapping'].submit()
assert resp.location == 'http://example.net/backoffice/settings/users/fields/' assert resp.location == 'http://example.net/backoffice/settings/users/fields/'
resp = resp.follow() resp = resp.follow()
assert pub.cfg['users']['field_email'] == '1' assert pub.cfg['users']['field_email'] == '1'
# and unset it # and unset it
resp.forms[0]['field_email'] = '' resp.forms['mapping']['field_email'] = ''
resp = resp.forms[0].submit() resp = resp.forms['mapping'].submit()
assert resp.location == 'http://example.net/backoffice/settings/users/fields/' assert resp.location == 'http://example.net/backoffice/settings/users/fields/'
resp = resp.follow() resp = resp.follow()
assert pub.cfg['users']['field_email'] == None assert pub.cfg['users']['field_email'] == None
# add a comment field # add a comment field
resp.forms[1]['label'] = 'barfoo' resp.forms[2]['label'] = 'barfoo'
resp.forms[1]['type'] = 'Comment' resp.forms[2]['type'] = 'Comment'
resp = resp.forms[1].submit() resp = resp.forms[2].submit()
assert resp.location == 'http://example.net/backoffice/settings/users/fields/' assert resp.location == 'http://example.net/backoffice/settings/users/fields/'
resp = resp.follow() resp = resp.follow()
assert b'barfoo' in pub.cfg['users']['formdef'] assert b'barfoo' in pub.cfg['users']['formdef']
@ -4760,16 +4760,43 @@ def test_settings_user(pub):
# check fields are present in edit form # check fields are present in edit form
resp = app.get('/backoffice/users/%s/edit' % user.id) resp = app.get('/backoffice/users/%s/edit' % user.id)
assert 'barfoo' in resp.text assert 'barfoo' in resp.text
assert 'f1' in resp.forms[0].fields assert 'f1' in resp.form.fields
assert 'email' in resp.forms[0].fields assert 'email' in resp.form.fields
# check the email field is not displayed if it's overridden by a custom # check the email field is not displayed if it's overridden by a custom
# field. # field.
pub.cfg['users']['field_email'] = '1' pub.cfg['users']['field_email'] = '1'
pub.write_cfg() pub.write_cfg()
resp = app.get('/backoffice/users/%s/edit' % user.id) resp = app.get('/backoffice/users/%s/edit' % user.id)
assert 'f1' in resp.forms[0].fields assert 'f1' in resp.form.fields
assert 'email' not in resp.forms[0].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 # restore config
pub.cfg['users']['field_email'] = None 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 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): def test_backoffice_geolocation_info(pub):
user = create_user(pub) user = create_user(pub)
create_environment(pub) create_environment(pub)

View File

@ -118,7 +118,7 @@ class UserFieldDefPage(FieldDefPage):
class UserFieldsDirectory(FieldsDirectory): class UserFieldsDirectory(FieldsDirectory):
_q_exports = ['', 'update_order', 'new', 'mapping'] _q_exports = ['', 'update_order', 'new', 'mapping', 'template']
section = 'settings' section = 'settings'
field_def_page_class = UserFieldDefPage field_def_page_class = UserFieldDefPage
@ -128,11 +128,15 @@ class UserFieldsDirectory(FieldsDirectory):
def index_bottom(self): def index_bottom(self):
r = TemplateIO(html=True) r = TemplateIO(html=True)
r += get_session().display_message()
r += htmltext('<div class="bo-block">') r += htmltext('<div class="bo-block">')
r += htmltext('<h2>%s</h2>') % _('Fields Mapping') 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.') r += htmltext('<p>%s</p>') % _('These settings make it possible to assign custom user fields to standard user fields.')
form = self.mapping_form() r += self.mapping_form().render()
r += 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>') r += htmltext('</div>')
return r.getvalue() return r.getvalue()
@ -141,7 +145,7 @@ class UserFieldsDirectory(FieldsDirectory):
options = [(None, _('None'), '')] + [ options = [(None, _('None'), '')] + [
(x.id, x.label, x.id) for x in self.objectdef.fields] (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') field_name_value = users_cfg.get('field_name')
if type(field_name_value) is str: if type(field_name_value) is str:
field_name_value = [field_name_value] field_name_value = [field_name_value]
@ -161,6 +165,26 @@ class UserFieldsDirectory(FieldsDirectory):
cfg_submit(form, 'users', ['field_name', 'field_email']) cfg_submit(form, 'users', ['field_name', 'field_email'])
return redirect('.') 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 UserFieldsFormDef(FormDef):
'''Class to handle custom user fields, it loads and saves from/to '''Class to handle custom user fields, it loads and saves from/to
@ -405,6 +429,7 @@ class SettingsDirectory(QommonSettingsDirectory):
'session', 'download_theme', 'smstest', 'postgresql', 'session', 'download_theme', 'smstest', 'postgresql',
('admin-permissions', 'admin_permissions'), 'geolocation', ('admin-permissions', 'admin_permissions'), 'geolocation',
'theme_preview', 'filetypes', 'theme_preview', 'filetypes',
('user-template', 'user_template'),
('data-sources', 'data_sources'), 'wscalls', 'logs'] ('data-sources', 'data_sources'), 'wscalls', 'logs']
emails = EmailsDirectory() emails = EmailsDirectory()
@ -517,6 +542,10 @@ class SettingsDirectory(QommonSettingsDirectory):
if enabled('users'): if enabled('users'):
r += htmltext('<dt><a href="users/">%s</a></dt> <dd>%s</dd>') % ( r += htmltext('<dt><a href="users/">%s</a></dt> <dd>%s</dd>') % (
_('Users'), _('Configure users')) _('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'): if enabled('emails'):
r += htmltext('<dt><a href="emails/">%s</a></dt> <dd>%s</dd>') % ( r += htmltext('<dt><a href="emails/">%s</a></dt> <dd>%s</dd>') % (
_('Emails'), _('Configure email settings')) _('Emails'), _('Configure email settings'))
@ -1223,3 +1252,23 @@ class SettingsDirectory(QommonSettingsDirectory):
r += htmltext('<h2>%s</h2>') % _('Geolocation Settings') r += htmltext('<h2>%s</h2>') % _('Geolocation Settings')
r += form.render() r += form.render()
return r.getvalue() 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.form import *
from ..qommon.storage import (Equal, NotEqual, LessOrEqual, GreaterOrEqual, Or, from ..qommon.storage import (Equal, NotEqual, LessOrEqual, GreaterOrEqual, Or,
Intersects, ILike, FtsMatch, Contains, Null, NotNull) Intersects, ILike, FtsMatch, Contains, Null, NotNull)
from ..qommon.template import Template
from wcs.api_utils import get_user_from_api_query_string from wcs.api_utils import get_user_from_api_query_string
from wcs.carddef import CardDef from wcs.carddef import CardDef
@ -2661,7 +2662,16 @@ class FormBackOfficeStatusPage(FormStatusPage):
if formdata.user_id and formdata.get_user(): if formdata.user_id and formdata.get_user():
r += htmltext('<div class="extra-context">') r += htmltext('<div class="extra-context">')
r += htmltext('<h3>%s</h3>') % _('Associated User') 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>') r += htmltext('</div>')
if formdata.formdef.geolocations and formdata.geolocations: if formdata.formdef.geolocations and formdata.geolocations: