misc: create session substitution variables from query string (#7858)

This commit is contained in:
Frédéric Péters 2015-08-17 16:14:32 +02:00
parent e5b689bc99
commit d367031503
5 changed files with 147 additions and 0 deletions

View File

@ -213,4 +213,50 @@ champs, etc.
</section>
<section id="session-variables">
<title>Variables de session</title>
<p>
La session de l'usager contient une série d'informations fixes (par exemple
le <code>session_user_display_name</code> décrit en haut de page), il est
aussi possible d'y ajouter de nouvelles données par l'intermédiaire de
liens contenant des paramètres. Cela permet par exemple d'inclure une URL
personnalisée dans un courriel vers l'usager qui assurera le
préremplissage automatique de champs.
</p>
<p>
Le fonctionnement est simple, si le site reçoit une adresse avec des
paramètres de la forme <code>session_var_<var>nom</var></code>, une
variable du même nom (<code>session_var_<var>nom</var></code>) est rendue
disponible.
</p>
<p>
Un autre exemple pourrait être de faire varier le thème en fonction du site
extérieur ayant pointé vers le formulaire, une URL serait par exemple
<code>https://www.example.net/?session_var_departement=42</code> et le
squelette du site pourrait avoir le code suivant :
</p>
<example>
[if-any session_var_departement]
&lt;link rel="stylesheet" type="text/css" href="extra/[session_var_departement].css"/&gt;
[end]
</example>
<note style="important">
<p>
Ce fonctionnement doit explicitement être autorisé par
l'administrateur système, la liste des variables permises doit être ajoutée
au fichier <code>site_options.cfg</code>, dans la section
<code>[options]</code>, par exemple :
</p>
<example>
<code>query_string_allowed_vars = departement,une_autre_variable</code>
</example>
</note>
</section>
</page>

View File

@ -1,5 +1,6 @@
import pytest
import hashlib
import os
from wcs.qommon.ident.password_accounts import PasswordAccount
from wcs.formdef import FormDef
@ -884,3 +885,59 @@ def test_form_items_data_source_field_submit(pub):
'0_structured': [
{'id': '1', 'more': 'foo', 'text': 'un'},
{'id': '3', 'more': 'baz', 'text': 'trois'}]}
def test_form_page_query_string_prefill(pub):
user = create_user(pub)
formdef = create_formdef()
formdef.data_class().wipe()
formdef.fields = [fields.StringField(id='0', label='string',
prefill={'type': 'formula', 'value': 'session_var_foo'})]
formdef.store()
# check it's empty if it doesn't exist
resp = get_app(pub).get('/test/')
assert resp.forms[0]['f0'].value == ''
# check it's not set if it's not whitelisted
resp = get_app(pub).get('/?session_var_foo=hello')
assert resp.location == 'http://example.net/'
resp = resp.follow()
resp = resp.click('test')
assert resp.forms[0]['f0'].value == ''
# check it works
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write('''[options]
query_string_allowed_vars = foo,bar
''')
resp = get_app(pub).get('/?session_var_foo=hello')
assert resp.location == 'http://example.net/'
resp = resp.follow()
resp = resp.click('test')
assert resp.forms[0]['f0'].value == 'hello'
# check it survives a login
resp = get_app(pub).get('/?session_var_foo=hello2')
assert resp.location == 'http://example.net/'
resp = resp.follow()
resp = resp.click('Login')
resp = resp.follow()
resp.forms[0]['username'] = 'foo'
resp.forms[0]['password'] = 'foo'
resp = resp.forms[0].submit()
resp = resp.follow()
resp = resp.click('test')
assert resp.forms[0]['f0'].value == 'hello2'
# check repeated options are ignored
resp = get_app(pub).get('/?session_var_foo=hello&session_var_foo=hello2')
assert resp.location == 'http://example.net/'
resp = resp.follow()
resp = resp.click('test')
assert resp.forms[0]['f0'].value == ''
# check extra query string parameters are not lost
resp = get_app(pub).get('/?session_var_foo=hello&foo=bar')
assert resp.location == 'http://example.net/?foo=bar'
os.unlink(os.path.join(pub.app_dir, 'site-options.cfg'))

View File

@ -578,6 +578,35 @@ class QommonPublisher(Publisher):
if request.get_environ('QOMMON_PAGE_TEMPLATE_KEY'):
request.response.page_template_key = request.get_environ('QOMMON_PAGE_TEMPLATE_KEY')
# handle session_var_<xxx> in query strings, add them to session and
# redirect to same URL without the parameters
if request.get_method() == 'GET' and request.form:
query_string_allowed_vars = self.get_site_option(
'query_string_allowed_vars') or ''
query_string_allowed_vars = [x.strip() for x in
query_string_allowed_vars.split(',')]
had_session_variables = False
session_variables = {}
for k, v in request.form.items():
if k.startswith('session_var_'):
had_session_variables = True
session_variable = str(k[len('session_var_'):])
# only add variable to session if it's a string, this
# handles the case of repeated parameters producing a
# list of values (by ignoring those parameters altogether).
if session_variable in query_string_allowed_vars and (
isinstance(v, str)):
session_variables[session_variable] = v
del request.form[k]
if had_session_variables:
self.start_request() # creates session
request.session.add_extra_variables(**session_variables)
self.finish_successful_request() # commits session
new_query_string = ''
if request.form:
new_query_string = '?' + urllib.urlencode(request.form)
return redirect(request.get_path() + new_query_string)
request.language = self.get_site_language()
self.install_lang(request.language)
self.substitutions.feed(self)

View File

@ -80,6 +80,7 @@ class Session(QommonSession, CaptchaSession, StorableObject):
ident_idp_token = None
tempfiles = None
jsonp_display_values = None
extra_variables = None
username = None # only set on password authentication
@ -92,6 +93,7 @@ class Session(QommonSession, CaptchaSession, StorableObject):
self.ident_idp_token or \
self.tempfiles or \
self.jsonp_display_values or \
self.extra_variables or \
CaptchaSession.has_info(self) or \
QuixoteSession.has_info(self)
is_dirty = has_info
@ -205,6 +207,18 @@ class Session(QommonSession, CaptchaSession, StorableObject):
value.fp = open(filename)
return value
def add_extra_variables(self, **kwargs):
if not self.extra_variables:
self.extra_variables = {}
self.extra_variables.update(kwargs)
def get_substitution_variables(self, prefix='session_var_'):
d = {}
if self.extra_variables:
for k, v in self.extra_variables.items():
d[prefix + k] = v
return d
class QommonSessionManager(QuixoteSessionManager):
def start_request(self):

View File

@ -283,6 +283,7 @@ class RootDirectory(Directory):
if not hasattr(response, 'breadcrumb'):
response.breadcrumb = [ ('', _('Home')) ]
get_publisher().substitutions.feed(get_session())
get_publisher().substitutions.feed(get_request().user)
get_publisher().substitutions.feed(NamedDataSource)