general: keep submission context additional details (#8306)
This commit is contained in:
parent
f0e88b1cc3
commit
558c4df65e
|
@ -34,7 +34,8 @@ différentes pages définies dans le formulaire (mode flux).
|
|||
La complétion d'un formulaire se fait par une requête <code>POST</code> à
|
||||
l'adresse <code>/api/formdefs/<var>slug</var>/submit</code>, le contenu de
|
||||
la requête doit être un dictionnaire contenant obligatoirement un attribut
|
||||
<code>data</code> et optionnellement un attribut <code>meta</code>.
|
||||
<code>data</code> et optionnellement un attribut <code>meta</code> et un
|
||||
attribut <code>context</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
@ -62,6 +63,13 @@ différentes pages définies dans le formulaire (mode flux).
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
L'attribut <code>context</code> est également optionnel et contient une
|
||||
série de renseignements supplémentaires sur le contexte de l'envoi du
|
||||
formulaire. Les attributs reconnus sont <code>channel</code>,
|
||||
<code>thumbnail_url</code>, <code>user_id</code> et <code>comments</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
L'exemple suivant complète un formulaire d'inscription à une newsletter, qui
|
||||
demanderait trois champs, prénom, nom et adresse électronique.
|
||||
|
|
|
@ -314,6 +314,13 @@ def test_formdef_submit(local_user):
|
|||
resp = get_app(pub).post_json(url, {'meta': {'draft': True}, 'data': {}})
|
||||
assert data_class.get(resp.json['data']['id']).status == 'draft'
|
||||
|
||||
resp = get_app(pub).post_json(url, {'meta': {'backoffice-submission': True}, 'data': {},
|
||||
'context': {'channel': 'mail'} })
|
||||
assert data_class.get(resp.json['data']['id']).status == 'wf-new'
|
||||
assert data_class.get(resp.json['data']['id']).backoffice_submission is True
|
||||
assert data_class.get(resp.json['data']['id']).user_id is None
|
||||
assert data_class.get(resp.json['data']['id']).submission_context == {'channel': 'mail'}
|
||||
|
||||
data_class.wipe()
|
||||
|
||||
def test_categories():
|
||||
|
|
|
@ -395,6 +395,37 @@ def test_backoffice_handling(pub):
|
|||
assert FormDef.get_by_urlname('form-title').data_class().get(number31).status == 'wf-accepted'
|
||||
assert 'HELLO WORLD' in resp.body
|
||||
|
||||
def test_backoffice_submission_context(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]
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/management/form-title/')
|
||||
assert re.findall('<tbody.*\/tbody>', resp.body, re.DOTALL)[0].count('<tr') == 17
|
||||
|
||||
# click on a formdata
|
||||
resp = resp.click(href='%s/' % number31.id)
|
||||
assert (' with the number %s.' % number31.id) in resp.body
|
||||
|
||||
# check there's nothing in the sidebar
|
||||
assert not 'Channel' in resp.body
|
||||
|
||||
number31.submission_context = {
|
||||
'channel': 'mail',
|
||||
'thumbnail_url': 'http://www.example.com/thumbnail.png',
|
||||
'user_id': user.id,
|
||||
'comments': 'test_backoffice_submission_context',
|
||||
}
|
||||
number31.store()
|
||||
resp = app.get('/backoffice/management/form-title/')
|
||||
resp = resp.click(href='%s/' % number31.id)
|
||||
assert 'Channel' in resp.body
|
||||
assert 'http://www.example.com/thumbnail.png' in resp.body
|
||||
assert 'Associated User' in resp.body
|
||||
assert 'test_backoffice_submission_context' in resp.body
|
||||
|
||||
|
||||
def test_backoffice_info_text(pub):
|
||||
create_user(pub)
|
||||
create_environment(pub)
|
||||
|
|
|
@ -204,6 +204,8 @@ class ApiFormdefDirectory(Directory):
|
|||
formdata.backoffice_submission = True
|
||||
else:
|
||||
formdata.user_id = user.id
|
||||
if json_input.get('context'):
|
||||
formdata.submission_context = json_input['context']
|
||||
formdata.store()
|
||||
if self.formdef.enable_tracking_codes:
|
||||
code = get_publisher().tracking_code_class()
|
||||
|
|
|
@ -1079,8 +1079,39 @@ class FormBackOfficeStatusPage(FormStatusPage):
|
|||
return html_top('management', title)
|
||||
|
||||
def _q_index(self):
|
||||
get_response().filter['sidebar'] = self.get_sidebar()
|
||||
return self.status()
|
||||
|
||||
def get_sidebar(self):
|
||||
return self.get_extra_context_bar()
|
||||
|
||||
def get_extra_context_bar(self):
|
||||
formdata = self.filled
|
||||
if not formdata.submission_context:
|
||||
return ''
|
||||
|
||||
r = TemplateIO(html=True)
|
||||
extra_context = formdata.submission_context or {}
|
||||
r += htmltext('<div class="extra-context">')
|
||||
if extra_context.get('channel'):
|
||||
channel_labels = {
|
||||
'mail': _('Mail'),
|
||||
}
|
||||
r += htmltext('<h3>%s</h3>') % '%s: %s' % (
|
||||
_('Channel'), channel_labels.get(extra_context.get('channel'), '?'))
|
||||
if extra_context.get('thumbnail_url'):
|
||||
r += htmltext('<p class="thumbnail"><img src="%s" alt=""/></p>'
|
||||
) % extra_context.get('thumbnail_url')
|
||||
if extra_context.get('user_id'):
|
||||
r += htmltext('<h3>%s</h3>') % _('Associated User')
|
||||
r += htmltext('<p>%s</p>') % get_publisher().user_class.get(
|
||||
extra_context.get('user_id')).display_name
|
||||
if extra_context.get('comments'):
|
||||
r += htmltext('<h3>%s</h3>') % _('Comments')
|
||||
r += htmltext('<p>%s</p>') % extra_context.get('comments')
|
||||
r += htmltext('</div>')
|
||||
return r.getvalue()
|
||||
|
||||
|
||||
class FakeField(object):
|
||||
def __init__(self, id, type_, label):
|
||||
|
|
|
@ -25,6 +25,8 @@ from wcs.formdef import FormDef
|
|||
from wcs.categories import Category
|
||||
from wcs.forms.root import FormPage as PublicFormFillPage
|
||||
|
||||
from .management import FormBackOfficeStatusPage
|
||||
|
||||
|
||||
class FormFillPage(PublicFormFillPage):
|
||||
def html_top(self, *args, **kwargs):
|
||||
|
@ -42,19 +44,21 @@ class FormFillPage(PublicFormFillPage):
|
|||
def get_sidebar(self, data):
|
||||
r = TemplateIO(html=True)
|
||||
|
||||
formdata = None
|
||||
draft_formdata_id = data.get('draft_formdata_id')
|
||||
if draft_formdata_id:
|
||||
formdata = self.formdef.data_class().get(draft_formdata_id)
|
||||
if self.formdef.enable_tracking_codes:
|
||||
draft_formdata_id = data.get('draft_formdata_id')
|
||||
r += htmltext('<h3>%s</h3>') % _('Tracking Code')
|
||||
tracking_code = None
|
||||
if draft_formdata_id:
|
||||
formdata = self.formdef.data_class().get(draft_formdata_id)
|
||||
if formdata.tracking_code:
|
||||
tracking_code = formdata.tracking_code
|
||||
if tracking_code:
|
||||
r += htmltext('<p>%s</p>') % tracking_code
|
||||
if formdata and formdata.tracking_code:
|
||||
tracking_code = formdata.tracking_code
|
||||
r += htmltext('<p>%s</p>') % formdata.tracking_code
|
||||
else:
|
||||
r += htmltext('<p>-</p>')
|
||||
|
||||
if formdata and formdata.submission_context:
|
||||
r += FormBackOfficeStatusPage(self.formdef, formdata).get_extra_context_bar()
|
||||
|
||||
return r.getvalue()
|
||||
|
||||
def form_side(self, step_no, page_no=0, log_detail=None, data=None, editing=None):
|
||||
|
@ -72,6 +76,13 @@ class FormFillPage(PublicFormFillPage):
|
|||
filled.backoffice_submission = True
|
||||
filled.store()
|
||||
|
||||
magictoken = get_request().form.get('magictoken')
|
||||
form_data = get_session().get_by_magictoken(magictoken, {})
|
||||
draft_formdata_id = form_data.get('draft_formdata_id')
|
||||
if draft_formdata_id:
|
||||
old_draft_formdata = self.formdef.data_class().get(draft_formdata_id)
|
||||
filled.submission_context = old_draft_formdata.submission_context
|
||||
|
||||
self.keep_tracking_code(filled)
|
||||
get_session().remove_magictoken(get_request().form.get('magictoken'))
|
||||
|
||||
|
|
|
@ -158,6 +158,7 @@ class FormData(StorableObject):
|
|||
editable_by = None
|
||||
tracking_code = None
|
||||
backoffice_submission = False
|
||||
submission_context = None
|
||||
|
||||
workflow_data = None
|
||||
workflow_roles = None
|
||||
|
|
|
@ -509,7 +509,10 @@ class FormPage(Directory):
|
|||
if get_request().form.has_key('mt'):
|
||||
magictoken = get_request().form['mt']
|
||||
data = session.get_by_magictoken(magictoken, {})
|
||||
session.remove_magictoken(magictoken)
|
||||
if not get_request().is_in_backoffice():
|
||||
# don't remove magictoken as the backoffice agent may get
|
||||
# the page reloaded.
|
||||
session.remove_magictoken(magictoken)
|
||||
if data:
|
||||
# create a new one since the other has been exposed in a url
|
||||
magictoken = randbytes(8)
|
||||
|
|
|
@ -1079,6 +1079,15 @@ div.admin-permissions tbody tr:nth-child(even) {
|
|||
background: #eee;
|
||||
}
|
||||
|
||||
div.extra-context p.thumbnail {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.extra-context p.thumbnail img {
|
||||
box-shadow: 0 0 3px black;
|
||||
transform: rotate(2deg);
|
||||
}
|
||||
|
||||
@media print {
|
||||
div#sidebar {
|
||||
display: none;
|
||||
|
|
19
wcs/sql.py
19
wcs/sql.py
|
@ -308,7 +308,8 @@ def do_formdef_tables(formdef, conn=None, cur=None, rebuild_views=False, rebuild
|
|||
'status', 'workflow_data', 'id_display', 'fts', 'page_no',
|
||||
'anonymised', 'workflow_roles', 'workflow_roles_array',
|
||||
'concerned_roles_array', 'tracking_code',
|
||||
'actions_roles_array', 'backoffice_submission'])
|
||||
'actions_roles_array', 'backoffice_submission',
|
||||
'submission_context'])
|
||||
|
||||
# migrations
|
||||
if not 'fts' in existing_fields:
|
||||
|
@ -337,6 +338,9 @@ def do_formdef_tables(formdef, conn=None, cur=None, rebuild_views=False, rebuild
|
|||
if not 'backoffice_submission' in existing_fields:
|
||||
cur.execute('''ALTER TABLE %s ADD COLUMN backoffice_submission boolean''' % table_name)
|
||||
|
||||
if not 'submission_context' in existing_fields:
|
||||
cur.execute('''ALTER TABLE %s ADD COLUMN submission_context bytea''' % table_name)
|
||||
|
||||
# add new fields
|
||||
for field in formdef.fields:
|
||||
assert field.id is not None
|
||||
|
@ -927,6 +931,7 @@ class SqlFormData(SqlMixin, wcs.formdata.FormData):
|
|||
('actions_roles_array', 'text[]'),
|
||||
('tracking_code', 'varchar'),
|
||||
('backoffice_submission', 'boolean'),
|
||||
('submission_context', 'bytea'),
|
||||
]
|
||||
|
||||
def __init__(self, id=None):
|
||||
|
@ -1019,6 +1024,7 @@ class SqlFormData(SqlMixin, wcs.formdata.FormData):
|
|||
'anonymised': self.anonymised,
|
||||
'tracking_code': self.tracking_code,
|
||||
'backoffice_submission': self.backoffice_submission,
|
||||
'submission_context': self.submission_context,
|
||||
}
|
||||
if self.receipt_time:
|
||||
sql_dict['receipt_time'] = datetime.datetime.fromtimestamp(time.mktime(self.receipt_time)),
|
||||
|
@ -1030,6 +1036,10 @@ class SqlFormData(SqlMixin, wcs.formdata.FormData):
|
|||
else:
|
||||
sql_dict['workflow_roles'] = None
|
||||
sql_dict['workflow_roles_array'] = None
|
||||
if self.submission_context:
|
||||
sql_dict['submission_context'] = bytearray(cPickle.dumps(self.submission_context))
|
||||
else:
|
||||
sql_dict['submission_context'] = None
|
||||
|
||||
sql_dict['concerned_roles_array'] = [str(x) for x in self.concerned_roles if x]
|
||||
sql_dict['actions_roles_array'] = [str(x) for x in self.actions_roles if x]
|
||||
|
@ -1134,6 +1144,8 @@ class SqlFormData(SqlMixin, wcs.formdata.FormData):
|
|||
o.workflow_data = cPickle.loads(str(o.workflow_data))
|
||||
if o.workflow_roles:
|
||||
o.workflow_roles = cPickle.loads(str(o.workflow_roles))
|
||||
if o.submission_context:
|
||||
o.submission_context = cPickle.loads(str(o.submission_context))
|
||||
o.data = cls._row2obdata(row, cls._formdef)
|
||||
return o
|
||||
_row2ob = classmethod(_row2ob)
|
||||
|
@ -1659,7 +1671,7 @@ def get_yearly_totals(period_start=None, period_end=None, criterias=None):
|
|||
return result
|
||||
|
||||
|
||||
SQL_LEVEL = 7
|
||||
SQL_LEVEL = 8
|
||||
|
||||
def migrate_global_views(conn, cur):
|
||||
cur.execute('''SELECT COUNT(*) FROM information_schema.tables
|
||||
|
@ -1708,8 +1720,9 @@ def migrate():
|
|||
migrate_views(conn, cur)
|
||||
for formdef in FormDef.select():
|
||||
formdef.data_class().rebuild_security()
|
||||
if sql_level < 7:
|
||||
if sql_level < 8:
|
||||
# 7: add backoffice_submission to tables and views
|
||||
# 8: add submission_context to tables
|
||||
migrate_views(conn, cur)
|
||||
|
||||
cur.execute('''UPDATE wcs_meta SET value = %s WHERE key = %s''', (
|
||||
|
|
Loading…
Reference in New Issue