wcs/tests/test_backoffice_pages.py

6921 lines
252 KiB
Python

# -*- coding: utf-8 -*-
import datetime
import json
import os
import re
import shutil
import time
import hashlib
import random
import xml.etree.ElementTree as ET
import zipfile
import mock
import pytest
try:
import xlwt
except ImportError:
xlwt = None
from django.utils import six
from django.utils.six import StringIO, BytesIO
from django.utils.six.moves.urllib import parse as urllib
from django.utils.six.moves.urllib import parse as urlparse
from webtest import Upload
from quixote import cleanup, get_publisher, get_response
from wcs.qommon import ods
from wcs.api_utils import sign_url
from wcs.qommon import errors, sessions
from wcs.qommon.form import PicklableUpload
from wcs.qommon.ident.password_accounts import PasswordAccount
from wcs.qommon.http_request import HTTPRequest
from wcs.qommon.substitution import CompatibilityNamesDict
from wcs.roles import Role, logged_users_role
from wcs.workflows import (Workflow, CommentableWorkflowStatusItem,
ChoiceWorkflowStatusItem, EditableWorkflowStatusItem,
DisplayMessageWorkflowStatusItem,
JumpOnSubmitWorkflowStatusItem, WorkflowCriticalityLevel,
WorkflowBackofficeFieldsFormDef)
from wcs.wf.backoffice_fields import SetBackofficeFieldsWorkflowStatusItem
from wcs.wf.dispatch import DispatchWorkflowStatusItem
from wcs.wf.form import FormWorkflowStatusItem, WorkflowFormFieldsFormDef
from wcs.wf.jump import JumpWorkflowStatusItem
from wcs.wf.wscall import WebserviceCallStatusItem
from wcs.wf.redirect_to_url import RedirectToUrlWorkflowStatusItem
from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem
from wcs.wf.resubmit import ResubmitWorkflowStatusItem
from wcs.wf.create_formdata import CreateFormdataWorkflowStatusItem, Mapping
from wcs.wf.create_carddata import CreateCarddataWorkflowStatusItem
from wcs.carddef import CardDef
from wcs.categories import Category
from wcs.formdef import FormDef
from wcs.logged_errors import LoggedError
from wcs import fields
from wcs.wscalls import NamedWsCall
from utilities import (get_app, login, create_temporary_pub,
clean_temporary_pub)
def pytest_generate_tests(metafunc):
if 'pub' in metafunc.fixturenames:
metafunc.parametrize('pub', ['pickle', 'sql', 'pickle-templates'], indirect=True)
@pytest.fixture
def pub(request, emails):
pub = create_temporary_pub(
sql_mode=bool('sql' in request.param),
templates_mode=bool('templates' in request.param)
)
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
pub.set_app_dir(req)
pub.cfg['identification'] = {'methods': ['password']}
pub.cfg['language'] = {'language': 'en'}
pub.write_cfg()
fd = open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')
fd.write('''
[api-secrets]
coucou = 1234
''')
fd.close()
return pub
def create_user(pub, is_admin=False):
if pub.user_class.select(lambda x: x.name == 'admin'):
user1 = pub.user_class.select(lambda x: x.name == 'admin')[0]
user1.is_admin = is_admin
user1.roles = [x.id for x in Role.select() if x.name == 'foobar']
user1.store()
return user1
user1 = pub.user_class(name='admin')
user1.email = 'admin@localhost'
user1.is_admin = is_admin
user1.store()
account1 = PasswordAccount(id='admin')
account1.set_password('admin')
account1.user_id = user1.id
account1.store()
Role.wipe()
role = Role(name='foobar')
role.store()
user1.roles = [role.id]
user1.store()
return user1
def create_superuser(pub): return create_user(pub, is_admin=True)
def create_environment(pub, set_receiver=True):
pub.session_manager.session_class.wipe()
Workflow.wipe()
Category.wipe()
FormDef.wipe()
pub.custom_view_class.wipe()
formdef = FormDef()
formdef.name = 'form title'
if set_receiver:
formdef.workflow_roles = {'_receiver': 1}
datasource = {'type': 'formula',
'value': repr([('A', 'aa'), ('B', 'bb'), ('C', 'cc')])}
formdef.fields = [
fields.StringField(id='1', label='1st field', type='string',
display_locations=['validation', 'summary', 'listings']),
fields.ItemField(id='2', label='2nd field', type='item',
items=['foo', 'bar', 'baz'],
display_locations=['validation', 'summary', 'listings']),
fields.ItemField(id='3', label='3rd field', type='item',
data_source=datasource, varname='foo'),
]
formdef.store()
formdef.data_class().wipe()
for i in range(50):
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = datetime.datetime(2015, 1, 1).timetuple()
formdata.data = {'1': 'FOO BAR %d' % i}
if i%4 == 0:
formdata.data['2'] = 'foo'
formdata.data['2_display'] = 'foo'
formdata.data['3'] = 'A'
formdata.data['3_display'] = 'aa'
elif i%4 == 1:
formdata.data['2'] = 'bar'
formdata.data['2_display'] = 'bar'
formdata.data['3'] = 'B'
formdata.data['3_display'] = 'bb'
else:
formdata.data['2'] = 'baz'
formdata.data['2_display'] = 'baz'
formdata.data['3'] = 'C'
formdata.data['3_display'] = 'cc'
if i%3 == 0:
formdata.jump_status('new')
else:
formdata.jump_status('finished')
formdata.store()
formdata = formdef.data_class()()
formdata.data = {'1': 'XXX', '2': 'foo', '2_display': 'foo'}
formdata.status = 'draft'
formdata.store()
formdef = FormDef()
if set_receiver:
formdef.workflow_roles = {'_receiver': 1}
formdef.name = 'other form'
formdef.fields = []
formdef.store()
formdef.data_class().wipe()
for i in range(20):
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = datetime.datetime(2014, 1, 1).timetuple()
formdata.jump_status('new')
formdata.store()
def teardown_module(module):
clean_temporary_pub()
def test_backoffice_unlogged(pub):
create_superuser(pub)
resp = get_app(pub).get('/backoffice/', status=302)
assert resp.location == 'http://example.net/login/?next=http%3A%2F%2Fexample.net%2Fbackoffice%2F'
def test_backoffice_home(pub):
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/')
assert 'Management' in resp.text
assert 'Forms' in resp.text
assert 'Workflows' in resp.text
def test_backoffice_role_user(pub):
create_user(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/')
assert 'Management' in resp.text
assert 'Forms' not in resp.text
assert 'Workflows' not in resp.text
pub.cfg['admin-permissions'] = {'forms': [x.id for x in Role.select()]}
pub.write_cfg()
resp = app.get('/backoffice/')
assert 'Management' in resp.text
assert 'Forms' in resp.text
assert 'Workflows' not in resp.text
pub.cfg['admin-permissions'] = {'workflows': [x.id for x in Role.select()]}
pub.write_cfg()
resp = app.get('/backoffice/')
assert 'Management' in resp.text
assert 'Forms' not in resp.text
assert 'Workflows' in resp.text
# check role id int->str migration
pub.cfg['admin-permissions'] = {'workflows': [int(x.id) for x in Role.select()]}
pub.write_cfg()
resp = app.get('/backoffice/')
assert 'Management' in resp.text
assert 'Forms' not in resp.text
assert 'Workflows' in resp.text
def test_backoffice_forms(pub):
create_superuser(pub)
create_environment(pub, set_receiver=False)
# 1st time with user not handling those forms
app = login(get_app(pub))
resp = app.get('/backoffice/')
resp = resp.click('Management', index=0)
resp = resp.follow()
assert not 'Forms in your care' in resp.text
assert re.findall('Other Forms.*form-title', resp.text)
# 2nd time with user set as receiver of the forms
create_environment(pub, set_receiver=True)
app = login(get_app(pub))
resp = app.get('/backoffice/')
resp = resp.click('Management', index=0)
resp = resp.follow()
assert 'Forms in your care' in resp.text
assert '17 open on 50' in resp.text
# disable form, make sure it's still displayed
formdef = FormDef.get_by_urlname('form-title')
formdef.disabled = True
formdef.store()
resp = app.get('/backoffice/management/forms')
assert 'form-title' in resp.text
assert '17 open on 50' in resp.text
formdef.disabled = False
formdef.store()
# add an extra status to workflow and move a few formdatas to it, they
# should then be marked as open but not waiting for actions.
workflow = Workflow.get_default_workflow()
workflow.id = '2'
st1 = workflow.add_status('Status1')
jump = JumpWorkflowStatusItem()
jump.id = '_jump'
jump.timeout = 86400
jump.status = 'finished'
st1.items.append(jump)
jump.parent = st1
workflow.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow = workflow
formdef.store()
for i, formdata in enumerate(formdef.data_class().select(order_by='id')):
if formdata.status == 'wf-new' and i % 2:
formdata.status = 'wf-%s' % st1.id
formdata.store()
resp = app.get('/backoffice/')
resp = resp.click('Management', index=0)
resp = resp.follow()
assert 'Forms in your care' in resp.text
assert '9 open on 50' in resp.text
def test_backoffice_management_css_class(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert resp.pyquery.find('body.section-management')
def test_backoffice_listing(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert resp.text.count('data-link') == 17
# check status filter <select>
resp = app.get('/backoffice/management/form-title/')
resp.forms['listing-settings']['filter'] = 'all'
resp = resp.forms['listing-settings'].submit()
if pub.is_using_postgresql():
assert resp.text.count('data-link') == 20
else:
# not using sql -> no pagination
assert resp.text.count('data-link') == 50
# check status filter <select>
resp = app.get('/backoffice/management/form-title/')
resp.forms['listing-settings']['filter'] = 'done'
resp = resp.forms['listing-settings'].submit()
if pub.is_using_postgresql():
assert resp.text.count('data-link') == 20
resp = resp.click('Next Page')
assert resp.text.count('data-link') == 13
else:
assert resp.text.count('data-link') == 33
# add an extra status to workflow and move a few formdatas to it, they
# should then be marked as open but not waiting for actions.
workflow = Workflow.get_default_workflow()
workflow.id = '2'
st1 = workflow.add_status('Status1')
st1.id = 'plop'
jump = JumpWorkflowStatusItem()
jump.id = '_jump'
jump.timeout = 86400
jump.status = 'finished'
st1.items.append(jump)
jump.parent = st1
workflow.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow = workflow
formdef.store()
for i, formdata in enumerate(formdef.data_class().select(order_by='id')):
if formdata.status == 'wf-new' and i % 2:
formdata.status = 'wf-%s' % st1.id
formdata.store()
resp = app.get('/backoffice/management/form-title/')
assert resp.text.count('data-link') == 9
resp.forms['listing-settings']['filter'] = 'pending'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('data-link') == 17
# check status forced as endpoints are not part of the "actionable" list.
workflow = Workflow.get_default_workflow()
workflow.id = '3'
st1 = workflow.add_status('Status1')
st1.id = 'plop'
st1.forced_endpoint = False
again = ChoiceWorkflowStatusItem()
again.id = '_again'
again.label = 'Again'
again.by = ['_receiver']
again.status = st1.id
st1.items.append(again)
again.parent = st1
workflow.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow = workflow
formdef.store()
formdef.data_class().rebuild_security()
for i, formdata in enumerate(formdef.data_class().select(order_by='id')):
if formdata.status == 'wf-new' and i % 2:
formdata.status = 'wf-%s' % st1.id
formdata.store()
resp = app.get('/backoffice/management/form-title/')
assert resp.text.count('data-link') == 17
resp.forms['listing-settings']['filter'] = 'pending'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('data-link') == 17
# mark status as an endpoint
st1.forced_endpoint = True
workflow.store()
formdef.data_class().rebuild_security()
resp = app.get('/backoffice/management/form-title/')
assert resp.text.count('data-link') == 9
resp.forms['listing-settings']['filter'] = 'pending'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('data-link') == 9
def test_backoffice_listing_pagination(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert resp.text.count('data-link') == 17
resp = app.get('/backoffice/management/form-title/?limit=5')
assert resp.text.count('data-link') == 5
assert '<div id="page-links">' in resp.text
resp = resp.click(re.compile('^2$')) # second page
assert resp.text.count('data-link') == 5
assert resp.forms['listing-settings']['offset'].value == '5'
resp = resp.click(re.compile('^3$')) # third page
assert resp.text.count('data-link') == 5
assert resp.forms['listing-settings']['offset'].value == '10'
resp = resp.click(re.compile('^4$')) # fourth page
assert resp.text.count('data-link') == 2
assert resp.forms['listing-settings']['offset'].value == '15'
with pytest.raises(IndexError): # no fifth page
resp = resp.click(re.compile('^5$'))
resp = resp.click(re.compile('^10$')) # per page: 10
assert resp.text.count('data-link') == 10
resp = resp.click(re.compile('^20$')) # per page: 20
assert resp.text.count('data-link') == 17
# try an overbound offset
resp = app.get('/backoffice/management/form-title/?limit=5&offset=30')
resp = resp.follow()
assert resp.forms['listing-settings']['offset'].value == '0'
# try invalid values
resp = app.get('/backoffice/management/form-title/?limit=toto&offset=30', status=400)
resp = app.get('/backoffice/management/form-title/?limit=5&offset=toto', status=400)
def test_backoffice_listing_order(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
ids = []
for i, formdata in enumerate(formdef.data_class().select(order_by='id')):
if formdata.status in ('wf-new', 'wf-accepted'):
ids.append(formdata.id)
formdata.receipt_time = datetime.datetime(2015, 1, 1, 10, i).timetuple()
# ordered with odd-numbered ids then even-numbered ids
formdata.evolution[-1].time = datetime.datetime(2015, 2, 1, 10 + i % 2, i).timetuple()
formdata.store()
inversed_receipt_time_order = list(reversed([str(x) for x in sorted(ids)]))
last_update_time_order = [str(x) for x in sorted(ids, key=lambda x: int(x) if int(x) % 2 else int(x)+1000)]
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert resp.text.count('data-link') == 17
ids = [x.strip('/') for x in re.findall(r'data-link="(.*?)"', resp.text)]
assert ids == inversed_receipt_time_order
resp = app.get('/backoffice/management/form-title/?order_by=receipt_time')
assert resp.text.count('data-link') == 17
ids = [x.strip('/') for x in re.findall(r'data-link="(.*?)"', resp.text)]
assert ids == list(reversed(inversed_receipt_time_order))
resp = app.get('/backoffice/management/form-title/?order_by=last_update_time')
assert resp.text.count('data-link') == 17
ids = [x.strip('/') for x in re.findall(r'data-link="(.*?)"', resp.text)]
assert ids == last_update_time_order
resp = app.get('/backoffice/management/form-title/?order_by=-last_update_time')
assert resp.text.count('data-link') == 17
ids = [x.strip('/') for x in re.findall(r'data-link="(.*?)"', resp.text)]
assert ids == list(reversed(last_update_time_order))
if not pub.site_options.has_section('options'):
pub.site_options.add_section('options')
pub.site_options.set('options', 'default-sort-order', '-last_update_time')
fd = open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')
pub.site_options.write(fd)
fd.close()
resp = app.get('/backoffice/management/form-title/')
assert resp.text.count('data-link') == 17
ids = [x.strip('/') for x in re.findall(r'data-link="(.*?)"', resp.text)]
assert ids == list(reversed(last_update_time_order))
# try invalid values
resp = app.get('/backoffice/management/form-title/?order_by=toto.plop', status=400)
def test_backoffice_listing_anonymised(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/?limit=500')
assert resp.text.count('data-link') == 17
formdef = FormDef.get_by_urlname('form-title')
for i, formdata in enumerate(formdef.data_class().select(order_by='id')):
if i % 2:
formdata.anonymise()
resp = app.get('/backoffice/management/form-title/?limit=500')
assert resp.text.count('data-link') == 9
def test_backoffice_legacy_urls(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/form-title/')
assert resp.location == 'http://example.net/backoffice/management/form-title/'
resp = app.get('/backoffice/form-title/1/')
assert resp.location == 'http://example.net/backoffice/management/form-title/1/'
resp = app.get('/backoffice/form-title/listing/?bla')
assert resp.location == 'http://example.net/backoffice/management/form-title/listing/?bla'
resp = app.get('/backoffice/form-title/listing/foo?bla')
assert resp.location == 'http://example.net/backoffice/management/form-title/listing/foo?bla'
resp = app.get('/backoffice/not-form-title/', status=404)
def test_backoffice_columns(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert resp.text.count('</th>') == 8 # six columns
resp.forms['listing-settings']['1'].checked = False
assert not 'submission_channel' in resp.forms['listing-settings'].fields
assert 'last_update_time' in resp.forms['listing-settings'].fields
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('</th>') == 7 # fixe columns
assert resp.text.count('data-link') == 17 # 17 rows
assert resp.text.count('FOO BAR') == 0 # no field 1 column
# change column order
assert resp.forms['listing-settings']['columns-order'].value == 'id,time,last_update_time,user-label,2,status,1,3,anonymised'
resp.forms['listing-settings']['columns-order'].value = 'user-label,id,time,last_update_time,2,status,1,3,anonymised'
resp = resp.forms['listing-settings'].submit()
assert resp.text.find('<span>User Label</span>') < resp.text.find('<span>Number</span>')
def test_backoffice_channel_column(pub):
if not pub.site_options.has_section('variables'):
pub.site_options.add_section('variables')
pub.site_options.set('variables', 'welco_url', 'xxx')
fd = open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')
pub.site_options.write(fd)
fd.close()
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert resp.text.count('</th>') == 8 # six columns
resp.forms['listing-settings']['submission_channel'].checked = True
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('</th>') == 9 # seven columns
assert resp.text.count('data-link') == 17 # 17 rows
assert resp.text.count('<td>Web</td>') == 17
def test_backoffice_submission_agent_column(pub):
user = create_user(pub)
create_environment(pub)
agent = pub.user_class(name='agent')
agent.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert not 'submission_agent' in resp.forms['listing-settings'].fields
formdef = FormDef.get_by_urlname('form-title')
formdef.backoffice_submission_roles = user.roles
formdef.store()
resp = app.get('/backoffice/management/form-title/')
assert resp.text.count('</th>') == 8 # six columns
resp.forms['listing-settings']['submission_agent'].checked = True
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('</th>') == 9 # seven columns
assert resp.text.count('data-link') == 17 # 17 rows
assert not '>agent<' in resp.text
resp = resp.click('Export as CSV File')
assert len(resp.text.splitlines()) == 18 # 17 + header line
assert not ',agent,' in resp.text
for formdata in formdef.data_class().select():
formdata.submission_context = {'agent_id': agent.id}
formdata.store()
resp = app.get('/backoffice/management/form-title/')
resp.forms['listing-settings']['submission_agent'].checked = True
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('>agent<') == 17
resp = resp.click('Export as CSV File')
assert len(resp.text.splitlines()) == 18 # 17 + header line
assert resp.text.count(',agent') == 17
def test_backoffice_image_column(pub):
create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields.append(fields.FileField(id='4', label='file field', type='file',
display_locations=['validation', 'summary', 'listings']))
formdef.store()
upload = PicklableUpload('test.jpeg', 'image/jpeg')
jpg = open(os.path.join(os.path.dirname(__file__), 'image-with-gps-data.jpeg'), 'rb').read()
upload.receive([jpg])
for formdata in formdef.data_class().select(lambda x: x.status == 'wf-new'):
formdata.data['4'] = upload
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert 'download?f=4&thumbnail=1' not in resp.text
def test_backoffice_filter(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert resp.forms['listing-settings']['filter-status'].checked == True
resp.forms['listing-settings']['filter-status'].checked = False
resp.forms['listing-settings']['filter-2'].checked = True
resp = resp.forms['listing-settings'].submit()
assert '<select name="filter">' not in resp.text
resp.forms['listing-settings']['filter-2-value'] = 'baz'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>baz</td>') == 8
assert resp.text.count('<td>foo</td>') == 0
assert resp.text.count('<td>bar</td>') == 0
resp.forms['listing-settings']['filter-start'].checked = True
resp = resp.forms['listing-settings'].submit()
resp.forms['listing-settings']['filter-start-value'] = datetime.datetime(2015, 2, 1).strftime('%Y-%m-%d')
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>baz</td>') == 0
resp.forms['listing-settings']['filter-start-value'] = datetime.datetime(2014, 2, 1).strftime('%Y-%m-%d')
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>baz</td>') == 8
# check there's no crash on invalid date values
resp.forms['listing-settings']['filter-start-value'] = 'whatever'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>baz</td>') == 8
# check two-digit years are handled correctly
resp.forms['listing-settings']['filter-start-value'] = datetime.datetime(2014, 2, 1).strftime('%y-%m-%d')
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>baz</td>') == 8
# check it's also ok for end filter
resp.forms['listing-settings']['filter-end'].checked = True
resp = resp.forms['listing-settings'].submit()
resp.forms['listing-settings']['filter-end-value'] = datetime.datetime(2014, 2, 2).strftime('%y-%m-%d')
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>baz</td>') == 0
def test_backoffice_default_filter(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert not 'filter-2-value' in resp.forms['listing-settings'].fields
formdef = FormDef.get_by_urlname('form-title')
formdef.fields[1].in_filters = True
formdef.store()
resp = app.get('/backoffice/management/form-title/')
assert 'filter-2-value' in resp.forms['listing-settings'].fields
# same check for items field
formdef.fields.append(
fields.ItemsField(id='4', label='4th field', type='items', items=['foo', 'bar', 'baz']))
formdef.store()
resp = app.get('/backoffice/management/form-title/')
assert not 'filter-4-value' in resp.forms['listing-settings'].fields
formdef.fields[-1].in_filters = True
formdef.store()
resp = app.get('/backoffice/management/form-title/')
assert 'filter-4-value' in resp.forms['listing-settings'].fields
def test_backoffice_bool_filter(pub):
create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields.append(fields.BoolField(id='4', label='4th field',
type='bool',
display_locations=['validation', 'summary', 'listings']))
formdef.store()
for i, formdata in enumerate(formdef.data_class().select()):
formdata.data['4'] = bool(i%2)
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp.forms['listing-settings']['filter-4'].checked = True
resp = resp.forms['listing-settings'].submit()
assert resp.forms['listing-settings']['filter-4-value'].value == ''
resp.forms['listing-settings']['filter-4-value'].value = 'true'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>Yes</td>') > 0
assert resp.text.count('<td>No</td>') == 0
resp.forms['listing-settings']['filter-4-value'].value = 'false'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>Yes</td>') == 0
assert resp.text.count('<td>No</td>') > 0
def test_backoffice_item_filter(pub):
create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields.append(fields.ItemField(id='4', label='4th field', type='item',
items=['â', 'b', 'c', 'd'],
display_locations=['validation', 'summary', 'listings']))
formdef.store()
for i, formdata in enumerate(formdef.data_class().select()):
if i%4 == 0:
formdata.data['4'] = 'â'
formdata.data['4_display'] = 'â'
elif i%4 == 1:
formdata.data['4'] = 'b'
formdata.data['4_display'] = 'b'
elif i%4 == 2:
formdata.data['4'] = 'd'
formdata.data['4_display'] = 'd'
else:
formdata.data['4'] = ''
formdata.data['4_display'] = ''
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp.forms['listing-settings']['filter-4'].checked = True
resp = resp.forms['listing-settings'].submit()
assert resp.forms['listing-settings']['filter-4-value'].value == ''
resp.forms['listing-settings']['filter-4-value'].value = 'â'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count(u'<td>â</td>') > 0
assert resp.text.count(u'<td>b</td>') == 0
assert resp.text.count(u'<td>d</td>') == 0
resp.forms['listing-settings']['filter-4-value'].value = 'b'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count(u'<td>â</td>') == 0
assert resp.text.count(u'<td>b</td>') > 0
assert resp.text.count(u'<td>d</td>') == 0
if not pub.is_using_postgresql():
# in pickle all options are always displayed
resp.forms['listing-settings']['filter-4-value'].value = 'c'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count(u'<td>â</td>') == 0
assert resp.text.count(u'<td>b</td>') == 0
assert resp.text.count(u'<td>c</td>') == 0
else:
# in postgresql, option 'c' is never used so not even listed
with pytest.raises(ValueError):
resp.forms['listing-settings']['filter-4-value'].value = 'c'
# check json view used to fill select filters from javascript
resp2 = app.get(resp.request.path + 'filter-options?filter_field_id=4&' + resp.request.query_string)
assert [x['id'] for x in resp2.json['data']] == [u'â', u'b', u'd']
resp2 = app.get(resp.request.path + 'filter-options?filter_field_id=4&_search=d&' + resp.request.query_string)
assert [x['id'] for x in resp2.json['data']] == ['d']
resp2 = app.get(resp.request.path + 'filter-options?filter_field_id=7&' + resp.request.query_string, status=404)
for status in ('all', 'waiting', 'pending', 'done', 'accepted'):
resp.forms['listing-settings']['filter'] = status
resp = resp.forms['listing-settings'].submit()
resp2 = app.get(resp.request.path + 'filter-options?filter_field_id=4&' + resp.request.query_string)
if status == 'accepted':
assert [x['id'] for x in resp2.json['data']] == []
else:
assert [x['id'] for x in resp2.json['data']] == [u'â', u'b', u'd']
def test_backoffice_item_double_filter(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields.append(fields.ItemField(id='4', label='4th field', type='item',
items=['a', 'b', 'c', 'd'],
display_locations=['validation', 'summary', 'listings']))
formdef.fields.append(fields.ItemField(id='5', label='5th field', type='item',
items=['A', 'B', 'C', 'D'],
display_locations=['validation', 'summary', 'listings']))
formdef.store()
for i, formdata in enumerate(formdef.data_class().select()):
if i%4 == 0:
formdata.data['4'] = 'a'
formdata.data['4_display'] = 'a'
formdata.data['5'] = 'A'
formdata.data['5_display'] = 'A'
elif i%4 == 1:
formdata.data['4'] = 'a'
formdata.data['4_display'] = 'a'
formdata.data['5'] = 'B'
formdata.data['5_display'] = 'B'
elif i%4 == 2:
formdata.data['4'] = 'a'
formdata.data['4_display'] = 'a'
formdata.data['5'] = 'C'
formdata.data['5_display'] = 'C'
elif i%4 == 3:
formdata.data['4'] = 'b'
formdata.data['4_display'] = 'b'
formdata.data['5'] = 'B'
formdata.data['5_display'] = 'B'
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp.forms['listing-settings']['filter-4'].checked = True
resp.forms['listing-settings']['filter-5'].checked = True
resp = resp.forms['listing-settings'].submit()
assert resp.forms['listing-settings']['filter-4-value'].value == ''
assert resp.forms['listing-settings']['filter-5-value'].value == ''
assert [x[0] for x in resp.forms['listing-settings']['filter-4-value'].options] == ['', 'a', 'b']
assert [x[0] for x in resp.forms['listing-settings']['filter-5-value'].options] == ['', 'A', 'B', 'C']
resp.forms['listing-settings']['filter-4-value'].value = 'a'
resp = resp.forms['listing-settings'].submit()
assert [x[0] for x in resp.forms['listing-settings']['filter-4-value'].options] == ['', 'a', 'b']
assert [x[0] for x in resp.forms['listing-settings']['filter-5-value'].options] == ['', 'A', 'B', 'C']
resp.forms['listing-settings']['filter-4-value'].value = 'b'
resp = resp.forms['listing-settings'].submit()
assert [x[0] for x in resp.forms['listing-settings']['filter-4-value'].options] == ['', 'a', 'b']
assert [x[0] for x in resp.forms['listing-settings']['filter-5-value'].options] == ['', 'B']
resp.forms['listing-settings']['filter-5-value'].value = 'B'
resp = resp.forms['listing-settings'].submit()
assert [x[0] for x in resp.forms['listing-settings']['filter-4-value'].options] == ['', 'a', 'b']
assert [x[0] for x in resp.forms['listing-settings']['filter-5-value'].options] == ['', 'B']
resp.forms['listing-settings']['filter-4-value'].value = ''
resp = resp.forms['listing-settings'].submit()
assert [x[0] for x in resp.forms['listing-settings']['filter-4-value'].options] == ['', 'a', 'b']
assert [x[0] for x in resp.forms['listing-settings']['filter-5-value'].options] == ['', 'A', 'B', 'C']
def test_backoffice_bofield_item_filter(pub):
create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
workflow = Workflow.get_default_workflow()
workflow.id = '2'
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.ItemField(id='bo0-1', label='4th field', type='item',
items=['â', 'b', 'c', 'd'],
display_locations=['validation', 'summary', 'listings'])
]
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
for i, formdata in enumerate(formdef.data_class().select()):
if i%4 == 0:
formdata.data['bo0-1'] = 'â'
formdata.data['bo0-1_display'] = 'â'
elif i%4 == 1:
formdata.data['bo0-1'] = 'b'
formdata.data['bo0-1_display'] = 'b'
elif i%4 == 2:
formdata.data['bo0-1'] = 'd'
formdata.data['bo0-1_display'] = 'd'
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp.forms['listing-settings']['filter-bo0-1'].checked = True
resp = resp.forms['listing-settings'].submit()
assert resp.forms['listing-settings']['filter-bo0-1-value'].value == ''
resp.forms['listing-settings']['filter-bo0-1-value'].value = 'â'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count(u'<td>â</td>') > 0
assert resp.text.count(u'<td>b</td>') == 0
assert resp.text.count(u'<td>d</td>') == 0
resp.forms['listing-settings']['filter-bo0-1-value'].value = 'b'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count(u'<td>â</td>') == 0
assert resp.text.count(u'<td>b</td>') > 0
assert resp.text.count(u'<td>d</td>') == 0
if not pub.is_using_postgresql():
# in pickle all options are always displayed
resp.forms['listing-settings']['filter-bo0-1-value'].value = 'c'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count(u'<td>â</td>') == 0
assert resp.text.count(u'<td>b</td>') == 0
assert resp.text.count(u'<td>c</td>') == 0
else:
# in postgresql, option 'c' is never used so not even listed
with pytest.raises(ValueError):
resp.forms['listing-settings']['filter-bo0-1-value'].value = 'c'
# check json view used to fill select filters from javascript
resp2 = app.get(resp.request.path + 'filter-options?filter_field_id=bo0-1&' + resp.request.query_string)
assert [x['id'] for x in resp2.json['data']] == [u'â', u'b', u'd']
resp2 = app.get(resp.request.path + 'filter-options?filter_field_id=bo0-1&_search=d&' + resp.request.query_string)
assert [x['id'] for x in resp2.json['data']] == ['d']
for status in ('all', 'waiting', 'pending', 'done', 'accepted'):
resp.forms['listing-settings']['filter'] = status
resp = resp.forms['listing-settings'].submit()
resp2 = app.get(resp.request.path + 'filter-options?filter_field_id=bo0-1&' + resp.request.query_string)
if status == 'accepted':
assert [x['id'] for x in resp2.json['data']] == []
else:
assert [x['id'] for x in resp2.json['data']] == [u'â', u'b', u'd']
def test_backoffice_items_filter(pub):
create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields.append(fields.ItemsField(id='4', label='4th field', type='items',
items=['â', 'b', 'c', 'd'],
display_locations=['validation', 'summary', 'listings']))
formdef.store()
for i, formdata in enumerate(formdef.data_class().select()):
if i%4 == 0:
formdata.data['4'] = ['â', 'b']
formdata.data['4_display'] = 'â, b'
elif i%4 == 1:
formdata.data['4'] = ['b', 'd']
formdata.data['4_display'] = 'b, d'
elif i%4 == 2:
formdata.data['4'] = ['â']
formdata.data['4_display'] = 'â'
else:
formdata.data['4'] = []
formdata.data['4_display'] = None
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp.forms['listing-settings']['filter-4'].checked = True
resp = resp.forms['listing-settings'].submit()
assert resp.forms['listing-settings']['filter-4-value'].value == ''
resp.forms['listing-settings']['filter-4-value'].value = 'â'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count(u'<td>â, b</td>') > 0
assert resp.text.count(u'<td>â</td>') > 0
assert resp.text.count(u'<td>b, d</td>') == 0
resp.forms['listing-settings']['filter-4-value'].value = 'b'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count(u'<td>â, b</td>') > 0
assert resp.text.count(u'<td>â</td>') == 0
assert resp.text.count(u'<td>b, d</td>') > 0
if pub.is_using_postgresql():
# option 'c' is never used so not even listed
with pytest.raises(ValueError):
resp.forms['listing-settings']['filter-4-value'].value = 'c'
else:
resp.forms['listing-settings']['filter-4-value'].value = 'c'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count(u'<td>â, b</td>') == 0
assert resp.text.count(u'<td>â</td>') == 0
assert resp.text.count(u'<td>b, d</td>') == 0
assert resp.text.count('data-link') == 0 # no rows
def test_backoffice_string_filter(pub):
create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields.append(fields.StringField(id='4', label='4th field',
type='string',
display_locations=['validation', 'summary', 'listings']))
formdef.store()
for i, formdata in enumerate(formdef.data_class().select()):
formdata.data['4'] = 'a' if bool(i % 2) else 'b'
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp.forms['listing-settings']['filter-4'].checked = True
resp = resp.forms['listing-settings'].submit()
assert resp.forms['listing-settings']['filter-4-value'].value == ''
resp.forms['listing-settings']['filter-4-value'].value = 'a'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>a</td>') > 0
assert resp.text.count('<td>b</td>') == 0
resp.forms['listing-settings']['filter-4-value'].value = 'b'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>a</td>') == 0
assert resp.text.count('<td>b</td>') > 0
def test_backoffice_email_filter(pub):
create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields.append(fields.EmailField(id='4', label='4th field',
type='email',
display_locations=['validation', 'summary', 'listings']))
formdef.store()
for i, formdata in enumerate(formdef.data_class().select()):
formdata.data['4'] = 'a@localhost' if bool(i % 2) else 'b@localhost'
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp.forms['listing-settings']['filter-4'].checked = True
resp = resp.forms['listing-settings'].submit()
assert resp.forms['listing-settings']['filter-4-value'].value == ''
resp.forms['listing-settings']['filter-4-value'].value = 'a@localhost'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('>a@localhost</') > 0
assert resp.text.count('>b@localhost</') == 0
resp.forms['listing-settings']['filter-4-value'].value = 'b@localhost'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('>a@localhost</') == 0
assert resp.text.count('>b@localhost</') > 0
def test_backoffice_date_filter(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields.append(fields.DateField(id='4', label='4th field',
type='date',
display_locations=['validation', 'summary', 'listings']))
formdef.store()
for i, formdata in enumerate(formdef.data_class().select()):
formdata.data['4'] = time.strptime('2020-04-24' if bool(i % 2) else '2015-05-12', '%Y-%m-%d')
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp.forms['listing-settings']['filter-4'].checked = True
resp = resp.forms['listing-settings'].submit()
assert resp.forms['listing-settings']['filter-4-value'].value == ''
resp.forms['listing-settings']['filter-4-value'].value = '2020-04-24'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>2020-04-24</td>') > 0
assert resp.text.count('<td>2015-05-12</td>') == 0
resp.forms['listing-settings']['filter-4-value'].value = '2015-05-12'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>2020-04-24</td>') == 0
assert resp.text.count('<td>2015-05-12</td>') > 0
# date in a different format
resp.forms['listing-settings']['filter-4-value'].value = '12/05/2015'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>2020-04-24</td>') == 0
assert resp.text.count('<td>2015-05-12</td>') > 0
def test_backoffice_user_filter(pub):
create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.store()
user1 = pub.user_class(name='userA')
user1.store()
user2 = pub.user_class(name='userB')
user2.store()
for i, formdata in enumerate(formdef.data_class().select()):
formdata.user_id = user1.id if bool(i % 2) else user2.id
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
# check the filter is hidden
assert resp.pyquery.find('li[hidden] input[name=filter-user]')
resp = app.get('/backoffice/management/form-title/?filter-user=on&filter-user-value=%s' % user1.id)
assert resp.text.count('>userA<') > 0
assert resp.text.count('>userB<') == 0
assert resp.pyquery.find('input[value=userA]') # displayed in sidebar
# check it persits on filter changes
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('>userA<') > 0
assert resp.text.count('>userB<') == 0
resp = app.get('/backoffice/management/form-title/?filter-user=on&filter-user-value=%s' % user2.id)
assert resp.text.count('>userA<') == 0
assert resp.text.count('>userB<') > 0
# filter on uuid
user1.name_identifiers = ['0123456789']
user1.store()
resp = app.get('/backoffice/management/form-title/?filter-user-uuid=0123456789')
assert resp.text.count('>userA<') > 0
assert resp.text.count('>userB<') == 0
assert resp.pyquery.find('input[value=userA]') # displayed in sidebar
# check it persists on filter changes
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('>userA<') > 0
assert resp.text.count('>userB<') == 0
# check with unknown uuid
resp = app.get('/backoffice/management/form-title/?filter-user-uuid=XXX')
assert resp.text.count('>userA<') == 0
assert resp.text.count('>userB<') == 0
def test_backoffice_csv(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Export as CSV File')
assert resp.headers['content-type'].startswith('text/')
assert len(resp.text.splitlines()) == 18 # 17 + header line
assert len(resp.text.splitlines()[0].split(',')) == 7
formdef = FormDef.get_by_urlname('form-title')
formdef.fields[-1].display_locations = ['validation', 'summary', 'listings']
formdef.store()
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Export as CSV File')
assert len(resp.text.splitlines()[0].split(',')) == 9
# check item fields with datasources get two columns (id & text)
assert resp.text.splitlines()[0].split(',')[6] == '3rd field'
assert resp.text.splitlines()[0].split(',')[7] == '' # 3rd field, continue
assert resp.text.splitlines()[1].split(',')[6] == 'A'
assert resp.text.splitlines()[1].split(',')[7] == 'aa'
resp = app.get('/backoffice/management/form-title/')
resp.forms['listing-settings']['filter'] = 'all'
resp = resp.forms['listing-settings'].submit()
resp_csv = resp.click('Export as CSV File')
assert len(resp_csv.text.splitlines()) == 51
# test status filter
resp.forms['listing-settings']['filter'] = 'pending'
resp.forms['listing-settings']['filter-2'].checked = True
resp = resp.forms['listing-settings'].submit()
resp.forms['listing-settings']['filter-2-value'] = 'baz'
resp = resp.forms['listing-settings'].submit()
resp_csv = resp.click('Export as CSV File')
assert len(resp_csv.text.splitlines()) == 9
# test criteria filters
resp.forms['listing-settings']['filter-start'].checked = True
resp = resp.forms['listing-settings'].submit()
resp.forms['listing-settings']['filter-start-value'] = datetime.datetime(2015, 2, 1).strftime('%Y-%m-%d')
resp = resp.forms['listing-settings'].submit()
resp_csv = resp.click('Export as CSV File')
assert len(resp_csv.text.splitlines()) == 1
resp.forms['listing-settings']['filter-start-value'] = datetime.datetime(2014, 2, 1).strftime('%Y-%m-%d')
resp = resp.forms['listing-settings'].submit()
resp.forms['listing-settings']['filter-2-value'] = 'baz'
resp = resp.forms['listing-settings'].submit()
resp_csv = resp.click('Export as CSV File')
assert len(resp_csv.text.splitlines()) == 9
assert 'Created' in resp_csv.text.splitlines()[0]
# test column selection
resp.forms['listing-settings']['time'].checked = False
resp = resp.forms['listing-settings'].submit()
resp_csv = resp.click('Export as CSV File')
assert 'Created' not in resp_csv.text.splitlines()[0]
def test_backoffice_export_long_listings(pub):
create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
for i in range(100):
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = datetime.datetime(2015, 1, 1).timetuple()
formdata.data = {'1': 'BAZ BAZ %d' % i}
formdata.jump_status('new')
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Export as CSV File')
assert resp.location.startswith('http://example.net/backoffice/management/form-title/export?job=')
resp = resp.follow()
assert 'completed' in resp.text
resp = resp.click('Download Export')
resp_lines = resp.text.splitlines()
assert resp_lines[0] == 'Number,Created,Last Modified,User Label,1st field,2nd field,Status'
assert len(resp_lines) == 118
assert resp_lines[1].split(',')[1].startswith(
time.strftime('%Y-%m-%d', formdata.receipt_time))
assert resp_lines[1].split(',')[2].startswith(
time.strftime('%Y-%m-%d', formdata.last_update_time))
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Export a Spreadsheet')
assert resp.location.startswith('http://example.net/backoffice/management/form-title/export?job=')
job_id = urlparse.parse_qs(urlparse.urlparse(resp.location).query)['job'][0]
resp = resp.follow()
assert 'completed' in resp.text
resp = resp.click('Download Export')
assert resp.content_type == 'application/vnd.oasis.opendocument.spreadsheet'
# check afterjob ajax call
status_resp = app.get('/afterjobs/' + job_id)
assert status_resp.text == 'completed|completed'
# check error handling
app.get('/afterjobs/whatever', status=404)
def test_backoffice_csv_export_channel(pub):
if not pub.site_options.has_section('variables'):
pub.site_options.add_section('variables')
pub.site_options.set('variables', 'welco_url', 'xxx')
fd = open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')
pub.site_options.write(fd)
fd.close()
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp_csv = resp.click('Export as CSV File')
assert 'Channel' not in resp_csv.text.splitlines()[0]
# add submission channel column
resp.forms['listing-settings']['submission_channel'].checked = True
resp = resp.forms['listing-settings'].submit()
resp_csv = resp.click('Export as CSV File')
assert resp_csv.text.splitlines()[0].split(',')[-1] == 'Channel'
assert resp_csv.text.splitlines()[1].split(',')[-1] == 'Web'
def test_backoffice_csv_export_anonymised(pub):
fd = open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')
pub.site_options.write(fd)
fd.close()
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp_csv = resp.click('Export as CSV File')
assert resp_csv.text.splitlines()[0].split(',')[-1] != 'Anonymised'
# add anonymised column
resp.forms['listing-settings']['anonymised'].checked = True
resp = resp.forms['listing-settings'].submit()
resp_csv = resp.click('Export as CSV File')
assert resp_csv.text.splitlines()[0].split(',')[-1] == 'Anonymised'
assert resp_csv.text.splitlines()[1].split(',')[-1] == 'No'
def test_backoffice_ods(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Export a Spreadsheet')
assert resp.headers['content-type'] == 'application/vnd.oasis.opendocument.spreadsheet'
assert 'filename=form-title.ods' in resp.headers['content-disposition']
assert resp.body[:2] == b'PK' # ods has a zip container
formdef = FormDef.get_by_urlname('form-title')
formdef.fields.append(fields.FileField(id='4', label='file field', type='file',
display_locations=['validation', 'summary', 'listings']))
formdef.fields.append(fields.DateField(id='5', label='date field', type='date',
display_locations=['validation', 'summary', 'listings']))
formdef.fields.append(fields.StringField(id='6', label='number field', type='string',
display_locations=['validation', 'summary', 'listings']))
formdef.fields.append(fields.StringField(id='7', label='phone field', type='string',
display_locations=['validation', 'summary', 'listings']))
formdef.fields.append(fields.DateField(id='8', label='very old field', type='date',
display_locations=['validation', 'summary', 'listings']))
formdef.fields.append(fields.StringField(id='9', label='string field', type='string',
display_locations=['validation', 'summary', 'listings']))
formdef.store()
formdata = formdef.data_class().select(lambda x: x.status == 'wf-new')[0]
formdata.data['4'] = PicklableUpload('/foo/bar', content_type='text/plain')
formdata.data['4'].receive([b'hello world'])
formdata.data['5'] = time.strptime('2015-05-12', '%Y-%m-%d')
formdata.data['6'] = '12345'
formdata.data['7'] = '0102030405'
formdata.data['8'] = time.strptime('1871-03-18', '%Y-%m-%d')
formdata.data['9'] = 'plop\npl\x1dop' # with control characters
formdata.store()
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Export a Spreadsheet')
assert resp.headers['content-type'] == 'application/vnd.oasis.opendocument.spreadsheet'
assert 'filename=form-title.ods' in resp.headers['content-disposition']
assert resp.body[:2] == b'PK' # ods has a zip container
zipf = zipfile.ZipFile(BytesIO(resp.body))
ods_sheet = ET.parse(zipf.open('content.xml'))
# check the ods contains a link to the document
elem = ods_sheet.findall('.//{%s}a' % ods.NS['text'])[0]
assert elem.attrib['{%s}href' % ods.NS['xlink']] == 'http://example.net/backoffice/management/form-title/%s/files/4/bar' % formdata.id
resp = app.get(elem.attrib['{%s}href' % ods.NS['xlink']])
assert resp.text == 'hello world'
all_texts = [x.text for x in ods_sheet.findall('.//{%s}table-row//{%s}p' % (ods.NS['table'], ods.NS['text']))]
created_column = all_texts.index('Created')
date_column = all_texts.index('date field')
number_column = all_texts.index('number field')
phone_column = all_texts.index('phone field')
old_column = all_texts.index('very old field')
string_column = all_texts.index('string field')
for row in ods_sheet.findall('.//{%s}table-row' % ods.NS['table']):
if row.findall('.//{%s}table-cell/{%s}p' % (
ods.NS['table'], ods.NS['text']))[0].text == formdata.get_display_id():
break
else:
assert False, 'failed to find data row'
assert row.findall('.//{%s}table-cell' % ods.NS['table'])[created_column].attrib[
'{%s}value-type' % ods.NS['office']] == 'date'
assert row.findall('.//{%s}table-cell' % ods.NS['table'])[created_column].attrib[
'{%s}style-name' % ods.NS['table']] == 'DateTime'
assert row.findall('.//{%s}table-cell' % ods.NS['table'])[date_column].attrib[
'{%s}value-type' % ods.NS['office']] == 'date'
assert row.findall('.//{%s}table-cell' % ods.NS['table'])[date_column].attrib[
'{%s}style-name' % ods.NS['table']] == 'Date'
assert row.findall('.//{%s}table-cell' % ods.NS['table'])[number_column].attrib[
'{%s}value-type' % ods.NS['office']] == 'float'
assert row.findall('.//{%s}table-cell' % ods.NS['table'])[number_column].attrib[
'{%s}value' % ods.NS['office']] == '12345'
assert row.findall('.//{%s}table-cell' % ods.NS['table'])[phone_column].attrib[
'{%s}value-type' % ods.NS['office']] == 'string'
assert row.findall('.//{%s}table-cell' % ods.NS['table'])[old_column].attrib[
'{%s}value-type' % ods.NS['office']] == 'date'
assert row.findall('.//{%s}table-cell' % ods.NS['table'])[old_column].attrib[
'{%s}date-value' % ods.NS['office']] == '1871-03-18'
assert row.findall('.//{%s}table-cell' % ods.NS['table'])[
string_column].find('{%s}p' % ods.NS['text']).text == 'plop\nplop'
@pytest.mark.skipif('xlwt is None')
def test_backoffice_xls(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert not 'Excel Export' in resp.text
if not pub.site_options.has_section('options'):
pub.site_options.add_section('options')
pub.site_options.set('options', 'legacy-excel-export', 'true')
fd = open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')
pub.site_options.write(fd)
fd.close()
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Excel Export')
assert resp.headers['content-type'].startswith('application/vnd.ms-excel')
def test_backoffice_statistics(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Statistics')
assert 'Total number of records: 50' in resp.text
assert 'New: 17' in resp.text
assert 'Finished: 33' in resp.text
assert re.findall('foo.*26.*bar.*26.*bar.*48', resp.text) # percentages
assert 'Resolution time' in resp.text
assert 'To Status &quot;New&quot;' in resp.text
assert 'To Status &quot;Finished&quot;' in resp.text
assert not '<h2>Filters</h2>' in resp.text
resp.forms['listing-settings']['filter-end-value'] = '2013-01-01'
resp = resp.forms['listing-settings'].submit()
assert 'Total number of records: 0' in resp.text
assert '<h2>Filters</h2>' in resp.text
assert 'End: 2013-01-01' in resp.text
def test_backoffice_multi_actions(pub):
create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert 'id="multi-actions"' in resp.text # always there
workflow = Workflow.get_default_workflow()
workflow.id = '2'
action = workflow.add_global_action('FOOBAR')
jump = action.append_item('jump')
jump.status = 'finished'
trigger = action.triggers[0]
trigger.roles = ['whatever']
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
resp = app.get('/backoffice/management/form-title/')
assert 'id="multi-actions"' in resp.text
trigger.roles = [x.id for x in Role.select() if x.name == 'foobar']
workflow.store()
resp = app.get('/backoffice/management/form-title/')
assert 'id="multi-actions"' in resp.text
ids = []
for checkbox in resp.forms[0].fields['select[]'][1:6]:
ids.append(checkbox._value)
checkbox.checked = True
resp = resp.forms[0].submit('button-action-1')
assert '?job=' in resp.location
resp = resp.follow()
assert 'Executing task &quot;FOOBAR&quot; on forms' in resp.text
assert '>completed<' in resp.text
for id in ids:
assert formdef.data_class().get(id).status == 'wf-finished'
draft_ids = [x.id for x in formdef.data_class().select() if x.status == 'draft']
resp = app.get('/backoffice/management/form-title/')
assert resp.forms[0].fields['select[]'][0]._value == '_all'
resp.forms[0].fields['select[]'][0].checked = True
resp = resp.forms[0].submit('button-action-1')
for formdata in formdef.data_class().select():
if formdata.id in draft_ids:
assert formdata.status == 'draft'
else:
assert formdata.status == 'wf-finished'
for formdata in formdef.data_class().select():
if formdata.status != 'draft':
formdata.jump_status('new')
formdata.store()
# action for other role
action2 = workflow.add_global_action('OTHER ACTION')
jump = action2.append_item('jump')
jump.status = 'accepted'
trigger = action2.triggers[0]
trigger.roles = ['whatever']
workflow.store()
resp = app.get('/backoffice/management/form-title/')
assert 'id="multi-actions"' in resp.text
assert not 'OTHER ACTION' in resp.text
# action for function
trigger.roles = ['_foobar']
workflow.store()
resp = app.get('/backoffice/management/form-title/')
assert 'id="multi-actions"' in resp.text
assert 'OTHER ACTION' not in resp.text
workflow.roles['_foobar'] = 'Foobar'
workflow.store()
resp = app.get('/backoffice/management/form-title/')
assert 'id="multi-actions"' in resp.text
assert 'OTHER ACTION' in resp.text
# alter some formdata to simulate dispatch action
stable_ids = []
for checkbox in resp.forms[0].fields['select[]'][1:6]:
formdata = formdef.data_class().get(checkbox._value)
formdata.workflow_roles = {'_foobar': formdef.workflow_roles['_receiver']}
formdata.store()
stable_ids.append(formdata.id)
resp = app.get('/backoffice/management/form-title/')
assert 'OTHER ACTION' in resp.text
resp.forms[0].fields['select[]'][0].checked = True # _all
resp = resp.forms[0].submit('button-action-2')
assert '?job=' in resp.location
resp = resp.follow()
assert 'Executing task &quot;OTHER ACTION&quot; on forms' in resp.text
# check only dispatched formdata have been moved by global action executed
# on all formdatas
for formdata in formdef.data_class().select():
if formdata.id in draft_ids:
assert formdata.status == 'draft'
elif formdata.id in stable_ids:
assert formdata.status == 'wf-accepted'
else:
assert formdata.status != 'wf-accepted'
def test_backoffice_multi_actions_jump(pub):
create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
workflow = Workflow.get_default_workflow()
workflow.id = '2'
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
resp = app.get('/backoffice/management/form-title/')
assert 'select[]' not in resp.forms['multi-actions'].fields
resp.forms['listing-settings']['filter'] = 'new'
resp = resp.forms['listing-settings'].submit()
assert 'select[]' not in resp.forms['multi-actions'].fields
# add identifier to jumps
workflow.get_status('new').items[1].identifier = 'accept'
workflow.get_status('new').items[2].identifier = 'reject'
workflow.get_status('new').items[2].require_confirmation = True
workflow.store()
resp = resp.forms['listing-settings'].submit()
assert 'select[]' in resp.forms['multi-actions'].fields
assert len(resp.pyquery.find('#multi-actions div.buttons button')) == 2
assert len(resp.pyquery.find('#multi-actions div.buttons button[data-ask-for-confirmation]')) == 1
ids = []
for checkbox in resp.forms[0].fields['select[]'][1:6]:
ids.append(checkbox._value)
checkbox.checked = True
resp = resp.forms['multi-actions'].submit('button-action-st-accept')
assert '?job=' in resp.location
resp = resp.follow()
assert 'Executing task &quot;Accept&quot; on forms' in resp.text
assert '>completed<' in resp.text
for id in ids:
assert formdef.data_class().get(id).status == 'wf-accepted'
def test_backoffice_statistics_with_no_formdefs(pub):
create_user(pub)
create_environment(pub)
FormDef.wipe()
if pub.is_using_postgresql():
from wcs.sql import get_connection_and_cursor, drop_global_views
conn, cur = get_connection_and_cursor()
drop_global_views(conn, cur)
conn.commit()
cur.close()
app = login(get_app(pub))
resp = app.get('/backoffice/management/statistics')
assert 'This site is currently empty.' in resp
def test_backoffice_statistics_status_filter(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Statistics')
assert 'filter' not in resp.forms['listing-settings'].fields # status is not displayed by default
assert not '<h2>Filters</h2>' in resp.text
# add 'status' as a filter
resp.forms['listing-settings']['filter-status'].checked = True
resp = resp.forms['listing-settings'].submit()
assert 'filter' in resp.forms['listing-settings'].fields
assert not '<h2>Filters</h2>' in resp.text
assert resp.forms['listing-settings']['filter'].value == 'all'
resp.forms['listing-settings']['filter'].value = 'pending'
resp = resp.forms['listing-settings'].submit()
assert 'Total number of records: 17' in resp.text
assert '<h2>Filters</h2>' in resp.text
assert 'Status: Pending' in resp.text
resp.forms['listing-settings']['filter'].value = 'done'
resp = resp.forms['listing-settings'].submit()
assert 'Total number of records: 33' in resp.text
assert '<h2>Filters</h2>' in resp.text
assert 'Status: Done' in resp.text
resp.forms['listing-settings']['filter'].value = 'rejected'
resp = resp.forms['listing-settings'].submit()
assert 'Total number of records: 0' in resp.text
assert '<h2>Filters</h2>' in resp.text
assert 'Status: Rejected' in resp.text
resp.forms['listing-settings']['filter'].value = 'all'
resp = resp.forms['listing-settings'].submit()
assert 'Total number of records: 50' in resp.text
def test_backoffice_statistics_status_select(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Statistics')
assert not 'filter-2-value' in resp.form.fields
resp.forms['listing-settings']['filter-2'].checked = True
resp = resp.forms['listing-settings'].submit()
resp.forms['listing-settings']['filter-2-value'].value = 'bar'
resp = resp.forms['listing-settings'].submit()
assert 'Total number of records: 13' in resp.text
resp.forms['listing-settings']['filter-2-value'].value = 'baz'
resp = resp.forms['listing-settings'].submit()
assert 'Total number of records: 24' in resp.text
resp.forms['listing-settings']['filter-2-value'].value = 'foo'
resp = resp.forms['listing-settings'].submit()
assert 'Total number of records: 13' in resp.text
assert '<h2>Filters</h2>' in resp.text
assert '2nd field: foo' in resp.text
# check it's also possible to get back to the complete list
resp.forms['listing-settings']['filter-2-value'].value = ''
resp = resp.forms['listing-settings'].submit()
assert 'Total number of records: 50' in resp.text
# check it also works with item fields with a data source
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Statistics')
resp.forms['listing-settings']['filter-3'].checked = True
resp = resp.forms['listing-settings'].submit()
resp.forms['listing-settings']['filter-3-value'].value = 'A'
resp = resp.forms['listing-settings'].submit()
assert 'Total number of records: 13' in resp.text
assert '<h2>Filters</h2>' in resp.text
assert '3rd field: aa' in resp.text
# set field to be displayed by default in filters
formdef = FormDef.get_by_urlname('form-title')
formdef.fields[1].in_filters = True
formdef.store()
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Statistics')
assert 'filter-2-value' in resp.form.fields
def test_backoffice_map(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.text, re.DOTALL)[0].count('<tr') == 17
# check there's no link to map the sidebar
assert not 'Plot on a Map' in resp.text
formdef = FormDef.get_by_urlname('form-title')
formdef.geolocations = {'base': 'Geolocafoobar'}
formdef.store()
number31.geolocations = {'base': {'lat': 48.83, 'lon': 2.32}}
number31.store()
resp = app.get('/backoffice/management/form-title/')
assert 'Plot on a Map' in resp.text
resp = resp.click('Plot on a Map')
assert 'data-geojson-url' in resp.text
assert 'tiles.entrouvert.org/' in resp.text
if not pub.site_options.has_section('options'):
pub.site_options.add_section('options')
pub.site_options.set('options', 'map-tile-urltemplate', 'https://{s}.tile.example.net/{z}/{x}/{y}.png')
fd = open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')
pub.site_options.write(fd)
fd.close()
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Plot on a Map')
assert 'tile.example.net/' in resp.text
def test_backoffice_geojson(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields.append(fields.MapField(id='4', label='4th field', type='map'))
formdef.fields.append(fields.MapField(id='5', label='5th field', type='string'))
form_class = formdef.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/geojson', status=404)
formdef = FormDef.get_by_urlname('form-title')
formdef.geolocations = {'base': 'Geolocafoobar'}
formdef.store()
number31 = formdef.data_class().get(number31.id)
number31.geolocations = {'base': {'lat': 48.83, 'lon': 2.32}}
number31.store()
resp = app.get('/backoffice/management/form-title/geojson?1=on&4=on&5=on')
assert len(resp.json['features']) == 1
assert resp.json['features'][0]['geometry']['coordinates'] == [2.32, 48.83]
assert 'status_colour' in resp.json['features'][0]['properties']
assert resp.json['features'][0]['properties']['status_name'] == 'New'
assert resp.json['features'][0]['properties']['status_colour'] == '#66FF00'
assert resp.json['features'][0]['properties']['view_label'] == 'View'
assert 'display_fields' in resp.json['features'][0]['properties']
assert len(resp.json['features'][0]['properties']['display_fields']) == 1
resp = app.get('/backoffice/management/form-title/geojson?filter=pending&filter-status=on')
assert len(resp.json['features']) == 1
resp = app.get('/backoffice/management/form-title/geojson?filter=done&filter-status=on')
assert len(resp.json['features']) == 0
def test_backoffice_handling(pub):
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.text, re.DOTALL)[0].count('<tr') == 17
# check sidebar links are ok
assert 'Statistics' in resp.text
assert 'Export' in resp.text
app.get('/backoffice/management/form-title/stats', status=200)
app.get('/backoffice/management/form-title/csv', status=200)
app.get('/backoffice/management/form-title/ods', status=200)
app.get('/backoffice/management/form-title/json', status=200)
# click on a formdata
resp = resp.click(href='%s/' % number31.id)
assert (' with the number %s.' % number31.get_display_id()) in resp.text
resp.forms[0]['comment'] = 'HELLO WORLD'
resp = resp.forms[0].submit('button_accept')
resp = resp.follow()
assert FormDef.get_by_urlname('form-title').data_class().get(number31.id).status == 'wf-accepted'
assert 'HELLO WORLD' in resp.text
def test_backoffice_handling_global_action(pub):
create_user(pub)
create_environment(pub)
formdef = FormDef()
formdef.name = 'test global action'
formdef.fields = []
workflow = Workflow.get_default_workflow()
workflow.id = '2'
action = workflow.add_global_action('FOOBAR')
register_comment = action.append_item('register-comment')
register_comment.comment = 'HELLO WORLD GLOBAL ACTION'
jump = action.append_item('jump')
jump.status = 'finished'
trigger = action.triggers[0]
trigger.roles = [x.id for x in Role.select() if x.name == 'foobar']
workflow.store()
formdef.workflow_id = workflow.id
formdef.workflow_roles = {'_receiver': 1}
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/%s/%s/' % (formdef.url_name, formdata.id))
assert 'button-action-1' in resp.form.fields
resp = resp.form.submit('button-action-1')
resp = app.get('/backoffice/management/%s/%s/' % (formdef.url_name, formdata.id))
assert 'HELLO WORLD GLOBAL ACTION' in resp.text
assert formdef.data_class().get(formdata.id).status == 'wf-finished'
def test_backoffice_global_remove_action(pub):
create_user(pub)
create_environment(pub)
formdef = FormDef()
formdef.name = 'test global remove'
formdef.fields = []
workflow = Workflow.get_default_workflow()
workflow.id = '2'
action = workflow.add_global_action('FOOBAR')
remove = action.append_item('remove')
trigger = action.triggers[0]
trigger.roles = [x.id for x in Role.select() if x.name == 'foobar']
workflow.store()
formdef.workflow_id = workflow.id
formdef.workflow_roles = {'_receiver': 1}
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/%s/%s/' % (formdef.url_name, formdata.id))
assert 'remove' in resp.text
assert 'button-action-1' in resp.form.fields
resp = resp.form.submit('button-action-1')
resp = resp.follow()
assert resp.request.url == 'http://example.net/backoffice/management/test-global-remove/'
assert 'The form has been deleted.' in resp.text
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.text, re.DOTALL)[0].count('<tr') == 17
# click on a formdata
resp = resp.click(href='%s/' % number31.id)
assert (' with the number %s.' % number31.get_display_id()) in resp.text
# check there's nothing in the sidebar
assert not 'Channel' in resp.text
number31.submission_channel = 'mail'
number31.user_id = user.id
number31.submission_context = {
'mail_url': 'http://www.example.com/test.pdf',
'thumbnail_url': 'http://www.example.com/thumbnail.png',
'comments': 'test_backoffice_submission_context',
'summary_url': 'http://www.example.com/summary',
'agent_id': user.id,
}
number31.store()
resp = app.get('/backoffice/management/form-title/')
resp = resp.click(href='%s/' % number31.id)
assert 'Channel' in resp.text
assert 'http://www.example.com/thumbnail.png' in resp.text
assert 'http://www.example.com/test.pdf' in resp.text
assert 'Associated User' in resp.text
assert 'test_backoffice_submission_context' in resp.text
assert 'http://www.example.com/summary' in resp.text
assert 'by %s' % user.get_display_name() in resp.text
def test_backoffice_download_as_zip(pub):
create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields.append(fields.FileField(id='4', label='file1 field', type='file'))
formdef.fields.append(fields.FileField(id='5', label='file2 field', type='file2'))
formdef.store()
number31 = [x for x in formdef.data_class().select() if x.data['1'] == 'FOO BAR 30'][0]
number31.data['4'] = PicklableUpload('/foo/bar', content_type='text/plain')
number31.data['4'].receive([b'hello world'])
number31.data['5'] = PicklableUpload('/foo/bar', content_type='text/plain')
number31.data['5'].receive([b'hello world2'])
number31.store()
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.store()
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
resp = resp.click('Download all files as .zip')
zip_content = BytesIO(resp.body)
zipf = zipfile.ZipFile(zip_content, 'a')
filelist = zipf.namelist()
assert set(filelist) == {'1_bar', '2_bar'}
for zipinfo in zipf.infolist():
content = zipf.read(zipinfo)
if zipinfo.filename == '1_bar':
assert content == b'hello world'
elif zipinfo.filename == '2_bar':
assert content == b'hello world2'
else:
assert False # unknown zip part
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)
formdef = FormDef.get_by_urlname('form-title')
formdef.geolocations = {'base': 'Geolocafoobar'}
formdef.store()
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.text, re.DOTALL)[0].count('<tr') == 17
# click on a formdata
resp = resp.click(href='%s/' % number31.id)
assert (' with the number %s.' % number31.get_display_id()) in resp.text
# check there's nothing in the sidebar
assert not 'Geolocation' in resp.text
number31.geolocations = {'base': {'lat': 48.83, 'lon': 2.32}}
number31.store()
resp = app.get('/backoffice/management/form-title/')
resp = resp.click(href='%s/' % number31.id)
assert 'Geolocafoobar' in resp.text
assert 'class="qommon-map"' in resp.text
assert 'data-init-lng="2.32"' in resp.text
assert 'data-init-lat="48.83' in resp.text
def test_backoffice_info_text(pub):
create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
form_class = formdef.data_class()
number31 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0]
# attach a custom workflow
workflow = Workflow(name='info texts')
st1 = workflow.add_status('Status1', number31.status.split('-')[1])
commentable = CommentableWorkflowStatusItem()
commentable.id = '_commentable'
commentable.by = ['_submitter', '_receiver']
commentable.button_label = 'CLICK ME!'
st1.items.append(commentable)
commentable.parent = st1
commentable2 = CommentableWorkflowStatusItem()
commentable2.id = '_commentable2'
commentable2.by = ['_submitter']
commentable2.button_label = 'CLICK ME2!'
st1.items.append(commentable2)
commentable2.parent = st1
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert (' with the number %s.' % number31.get_display_id()) in resp.text
assert 'CLICK ME!' in resp.text
assert not 'CLICK ME2!' in resp.text
assert not 'backoffice-description' in resp.text
# add an info text to the status
st1.backoffice_info_text = '<p>Foo</p>'
workflow.store()
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert 'backoffice-description' in resp.text
assert '<p>Foo</p>' in resp.text
# add an info text to the button
commentable.backoffice_info_text = '<p>Bar</p>'
workflow.store()
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert 'backoffice-description' in resp.text
assert '<p>Foo</p>' in resp.text
assert '<p>Bar</p>' in resp.text
# info text is not visible if form is locked
second_user = pub.user_class(name='foobar')
second_user.roles = Role.keys()
second_user.store()
account = PasswordAccount(id='foobar')
account.set_password('foobar')
account.user_id = second_user.id
account.store()
app2 = login(get_app(pub), username='foobar', password='foobar')
resp = app2.get('/backoffice/management/form-title/%s/' % number31.id)
assert 'Be warned forms of this user are also being looked' in resp.text
assert not 'backoffice-description' in resp.text
assert not 'CLICK ME!' in resp.text
assert not 'CLICK ME2!' in resp.text
# remove info text from the status
st1.backoffice_info_text = None
workflow.store()
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert 'backoffice-description' in resp.text
assert not '<p>Foo</p>' in resp.text
assert '<p>Bar</p>' in resp.text
# add info text to second button
commentable2.backoffice_info_text = '<p>Baz</p>'
workflow.store()
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert 'backoffice-description' in resp.text
assert not '<p>Foo</p>' in resp.text
assert '<p>Bar</p>' in resp.text
assert not '<p>Baz</p>' in resp.text
# remove info text from first button
commentable.backoffice_info_text = None
workflow.store()
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert not 'backoffice-description' in resp.text
def test_backoffice_handling_post_dispatch(pub):
# check a formdata that has been dispatched to another role is accessible
# by an user with that role.
user1 = create_user(pub)
role = Role(name='foobaz')
role.store()
user1.roles = [role.id]
user1.store()
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))
# check there's no access at the moment
resp = app.get('/backoffice/management/').follow()
assert not 'form-title/' in resp.text
resp = app.get('/backoffice/management/form-title/', status=403)
resp = app.get('/backoffice/management/form-title/%s/' % number31.id, status=403)
# emulate a dispatch (setting formdata.workflow_roles), receiver of that
# formdata is now the local role we gave to the user.
formdata31 = form_class.get(number31.id)
formdata31.workflow_roles = {'_receiver': role.id}
formdata31.store()
# check listing is accessible, with a single item
resp = app.get('/backoffice/management/').follow()
assert 'form-title/' in resp.text
resp = app.get('/backoffice/management/form-title/', status=200)
assert re.findall('<tbody.*\/tbody>', resp.text, re.DOTALL)[0].count('<tr') == 1
# check statistics and exports are also available
assert 'Statistics' in resp.text
assert 'Export' in resp.text
app.get('/backoffice/management/form-title/stats', status=200)
app.get('/backoffice/management/form-title/csv', status=200)
app.get('/backoffice/management/form-title/ods', status=200)
app.get('/backoffice/management/form-title/json', status=200)
# check formdata is accessible, and that it's possible to perform an action
# on it.
resp = resp.click(href='%s/' % number31.id)
assert (' with the number %s.' % number31.get_display_id()) in resp.text
resp.forms[0]['comment'] = 'HELLO WORLD'
resp = resp.forms[0].submit('button_accept')
resp = resp.follow()
assert FormDef.get_by_urlname('form-title').data_class().get(number31.id).status == 'wf-accepted'
assert 'HELLO WORLD' in resp.text
def test_global_statisticspub(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/')
resp = resp.click('Global statistics')
assert 'Total count: 70' in resp.text
resp.forms[0]['start'] = '2014-01-01'
resp.forms[0]['end'] = '2014-12-31'
resp = resp.forms[0].submit()
assert 'Total count: 20' in resp.text
def test_backoffice_submission(pub):
user = create_user(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/')
assert not 'Submission' in resp.text
app.get('/backoffice/submission/', status=403)
formdef = FormDef.get_by_urlname('form-title')
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
resp = app.get('/backoffice/')
assert 'Submission' in resp.text
resp = app.get('/backoffice/submission/')
assert formdef.url_name in resp.text
resp = resp.click(formdef.name)
resp.form['f1'] = 'test submission'
resp.form['f2'] = 'baz'
resp.form['f3'] = 'C'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
# going back to first page, to check
resp = resp.form.submit('previous')
assert resp.form['f1'].value == 'test submission'
resp = resp.form.submit('submit')
# final submit
resp = resp.form.submit('submit')
formdata_no = resp.location.split('/')[-2]
data_class = formdef.data_class()
assert data_class.get(formdata_no).data['1'] == 'test submission'
assert data_class.get(formdata_no).data['2'] == 'baz'
assert data_class.get(formdata_no).status == 'wf-new'
assert data_class.get(formdata_no).user is None
assert data_class.get(formdata_no).backoffice_submission is True
resp = resp.follow() # get to the formdata page
formdata_count = data_class.count()
# test submission when agent is not receiver
formdef.workflow_roles = {}
formdef.store()
resp = app.get('/backoffice/submission/')
resp = resp.click(formdef.name)
resp.form['f1'] = 'test submission'
resp.form['f2'] = 'baz'
resp.form['f3'] = 'C'
resp = resp.form.submit('submit') # to validation screen
resp = resp.form.submit('submit') # final submit
assert resp.location == 'http://example.net/backoffice/submission/'
resp = resp.follow() # should go back to submission screen
assert data_class.count() == formdata_count + 1
# test redirection on cancel
resp = app.get('/backoffice/submission/')
assert formdef.url_name in resp.text
resp = resp.click(formdef.name)
resp.form['f1'] = 'test submission'
resp = resp.form.submit('cancel')
assert resp.location == 'http://example.net/backoffice/submission/'
# test submission when agent is not receiver but there's a redirect action
# in the workflow.
formdef = FormDef.get_by_urlname('form-title')
wf = Workflow(name='dispatch')
st1 = wf.add_status('Status1')
item = RedirectToUrlWorkflowStatusItem()
item.id = '_redirect'
item.url = 'http://www.example.org/'
st1.items.append(item)
item.parent = st1
wf.store()
formdef.workflow_id = wf.id
formdef.store()
resp = app.get('/backoffice/submission/')
resp = resp.click(formdef.name)
resp.form['f1'] = 'test submission'
resp.form['f2'] = 'baz'
resp.form['f3'] = 'C'
resp = resp.form.submit('submit') # to validation screen
resp = resp.form.submit('submit') # final submit
assert resp.location == 'http://www.example.org/'
def test_backoffice_submission_with_tracking_code(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.backoffice_submission_roles = user.roles[:]
formdef.enable_tracking_codes = True
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/submission/')
resp = resp.click(formdef.name)
resp.form['f1'] = 'test submission'
resp.form['f2'] = 'baz'
resp.form['f3'] = 'C'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
# final submit
validation_resp_body = resp.text
resp = resp.form.submit('submit')
formdata_no = resp.location.split('/')[-2]
data_class = formdef.data_class()
formdata = data_class.get(formdata_no)
assert formdata.tracking_code in validation_resp_body
formdata_location = resp.location
resp = resp.follow() # get to the formdata page
# check tracking code is still displayed in formdata page
assert 'test submission' in resp.text
assert formdata.tracking_code in resp.text
# check access by different user
formdata.submission_context = {'agent_id': '10000'}
formdata.store()
resp = app.get(formdata_location)
assert 'test submission' in resp.text
assert not formdata.tracking_code in resp.text
# restore user
formdata.submission_context = {'agent_id': user.id}
formdata.store()
resp = app.get(formdata_location)
assert formdata.tracking_code in resp.text
# check access at a later time
formdata.receipt_time = time.localtime(time.time() - 3600)
formdata.store()
resp = app.get(formdata_location)
assert not formdata.tracking_code in resp.text
def test_backoffice_submission_welco(pub, welco_url):
user = create_user(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/')
assert not 'Submission' in resp.text
app.get('/backoffice/submission/', status=403)
formdef = FormDef.get_by_urlname('form-title')
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
# if it's empty, redirect to welco
resp = app.get('/backoffice/')
assert 'Submission' in resp.text
resp = app.get('/backoffice/submission/')
assert resp.location == 'http://welco.example.net'
# if there are pending submissions, display them
formdata = formdef.data_class()()
formdata.data = {}
formdata.status = 'draft'
formdata.backoffice_submission = True
formdata.submission_context = {'agent_id': user.id}
formdata.store()
resp = app.get('/backoffice/')
assert 'Submission' in resp.text
resp = app.get('/backoffice/submission/')
assert 'Submission to complete' in resp.text
# check agent name is displayed next to pending submission
assert '(%s)' % user.display_name in resp.text
def test_backoffice_submission_initiated_from_welco(pub, welco_url):
user = create_user(pub)
create_environment(pub)
app = login(get_app(pub))
formdef = FormDef.get_by_urlname('form-title')
formdef.fields = [
fields.PageField(id='0', label='1st PAGE', type='page',
condition={'type': 'python', 'value': 'form_submission_channel != "counter" and is_in_backoffice'}),
fields.StringField(id='1', label='Field on 1st page', type='string'),
fields.PageField(id='2', label='2nd PAGE', type='page'),
fields.StringField(id='3', label='Field on 2nd page', type='string'),
]
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
def post_formdata():
signed_url = sign_url('http://example.net/api/formdefs/form-title/submit' +
'?format=json&orig=coucou&email=%s' % urllib.quote(user.email), '1234')
url = signed_url[len('http://example.net'):]
resp = get_app(pub).post_json(url, {
'meta': {
'draft': True,
'backoffice-submission': True,
},
'data': {},
'context': {
'channel': 'counter'
}
})
return resp.json['data']['id']
resp = app.get('/backoffice/management/form-title/%s/' % post_formdata())
resp = resp.follow() # -> /backoffice/submission/form-title/XXX/
resp = resp.follow() # -> /backoffice/submission/form-title/?mt=XYZ
# second page should be shown
pq = resp.pyquery.remove_namespaces()
assert pq('#steps li.current .label').text() == '2nd PAGE'
assert 'Field on 2nd page' in resp.text # and in fields
# reverse condition
formdef.fields[0].condition['value'] = 'form_submission_channel == "counter" and is_in_backoffice'
formdef.store()
resp = app.get('/backoffice/management/form-title/%s/' % post_formdata())
resp = resp.follow() # -> /backoffice/submission/form-title/XXX/
resp = resp.follow() # -> /backoffice/submission/form-title/?mt=XYZ
pq = resp.pyquery.remove_namespaces()
assert pq('#steps li.current .label').text() == '1st PAGE'
assert 'Field on 1st page' in resp.text # and in fields
def test_backoffice_submission_with_return_url(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.backoffice_submission_roles = user.roles[:]
formdef.enable_tracking_codes = True
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/submission/form-title/?ReturnURL=https://example.org')
resp = resp.follow().follow()
resp = resp.form.submit('cancel')
assert resp.location == 'https://example.org'
resp = app.get('/backoffice/submission/form-title/?ReturnURL=https://example.org')
resp = resp.follow().follow()
resp.form['f1'] = 'test submission'
resp.form['f2'] = 'baz'
resp.form['f3'] = 'C'
resp = resp.form.submit('submit') # -> to validation
resp = resp.form.submit('submit') # -> to submit
assert resp.location.startswith('http://example.net/backoffice/management/form-title/')
# test submission when agent is not receiver
formdef.workflow_roles = {}
formdef.store()
resp = app.get('/backoffice/submission/form-title/?ReturnURL=https://example.org')
resp = resp.follow().follow()
resp.form['f1'] = 'test submission'
resp.form['f2'] = 'baz'
resp.form['f3'] = 'C'
resp = resp.form.submit('submit') # -> to validation
resp = resp.form.submit('submit') # -> to submit
assert resp.location == 'https://example.org'
def test_backoffice_parallel_submission(pub):
user = create_user(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/')
app.get('/backoffice/submission/', status=403)
formdef = FormDef.get_by_urlname('form-title')
formdef.backoffice_submission_roles = user.roles[:]
formdef.enable_tracking_codes = True
formdef.store()
formdata = formdef.data_class()()
formdata.data = {}
formdata.status = 'draft'
formdata.backoffice_submission = True
formdata.submission_context = {'agent_id': user.id}
formdata.store()
resp = app.get('/backoffice/submission/')
assert 'Submission to complete' in resp.text
resp1 = app.get('/backoffice/submission/form-title/%s/' % formdata.id)
resp1 = resp1.follow()
resp2 = app.get('/backoffice/submission/form-title/%s/' % formdata.id)
resp2 = resp2.follow()
resp3 = app.get('/backoffice/submission/form-title/%s/' % formdata.id)
resp3 = resp3.follow()
resp1.form['f1'] = 'foo'
resp1.form['f2'] = 'bar'
resp1.form['f3'] = 'C'
resp1 = resp1.form.submit('submit') # to validation page
# also move the second form to the validation page
resp2.form['f1'] = 'bar'
resp2.form['f2'] = 'bar'
resp2.form['f3'] = 'C'
resp2 = resp2.form.submit('submit') # to validation page
resp1 = resp1.form.submit('submit') # final validation
resp1 = resp1.follow()
resp2 = resp2.form.submit('submit') # final validation
assert resp2.status_code == 302
resp2 = resp2.follow()
assert 'This form has already been submitted.' in resp2.text
# do the third form from the start
resp3.form['f1'] = 'baz'
resp3.form['f2'] = 'bar'
resp3.form['f3'] = 'C'
resp_autosave = app.post('/backoffice/submission/form-title/autosave',
params=resp3.form.submit_fields())
assert resp_autosave.json['result'] == 'error'
assert resp_autosave.json['reason'] == 'form has already been submitted'
resp3 = resp3.form.submit('submit') # to validation page
assert resp3.status_code == 302
resp3 = resp3.follow()
assert 'This form has already been submitted.' in resp3.text
assert formdef.data_class().get(formdata.id).data['1'] == 'foo'
# try again, very late.
resp4 = app.get('/backoffice/submission/form-title/%s/' % formdata.id)
resp4 = resp4.follow()
assert 'This form has already been submitted.' in resp4.text
def test_backoffice_submission_dispatch(pub):
user = create_user(pub)
create_environment(pub)
wf = Workflow(name='dispatch')
st1 = wf.add_status('Status1')
dispatch = DispatchWorkflowStatusItem()
dispatch.id = '_dispatch'
dispatch.role_key = '_receiver'
dispatch.role_id = '2'
st1.items.append(dispatch)
dispatch.parent = st1
wf.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow_id = wf.id
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/submission/')
resp = resp.click(formdef.name)
resp.form['f1'] = 'test submission'
resp.form['f2'] = 'baz'
resp.form['f3'] = 'C'
resp = resp.form.submit('submit') # to validation screen
resp = resp.form.submit('submit') # final submit
# should go to the formdata because the formdef is defined as is
assert resp.location.startswith('http://example.net/backoffice/management/form-title/')
# remove function from formdef
formdef.workflow_roles = {}
formdef.store()
resp = app.get('/backoffice/submission/')
resp = resp.click(formdef.name)
resp.form['f1'] = 'test submission'
resp.form['f2'] = 'baz'
resp.form['f3'] = 'C'
resp = resp.form.submit('submit') # to validation screen
resp = resp.form.submit('submit') # final submit
# should NOT go to the formdata
assert resp.location == 'http://example.net/backoffice/submission/'
# if there's no function but the dispatch sets the right function, should
# go to the formdata screen
dispatch.role_id = '1'
wf.store()
resp = app.get('/backoffice/submission/')
resp = resp.click(formdef.name)
resp.form['f1'] = 'test submission'
resp.form['f2'] = 'baz'
resp.form['f3'] = 'C'
resp = resp.form.submit('submit') # to validation screen
resp = resp.form.submit('submit') # final submit
# should go to the formdata because the formdata was dispatched to the
# right role
assert resp.location.startswith('http://example.net/backoffice/management/form-title/')
def test_backoffice_submission_tracking_code(pub):
user = create_user(pub)
create_environment(pub)
app = login(get_app(pub))
formdef = FormDef.get_by_urlname('form-title')
formdef.enable_tracking_codes = True
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
data_class = formdef.data_class()
data_class.wipe()
resp = app.get('/backoffice/submission/')
assert formdef.url_name in resp.text
resp = resp.click(formdef.name)
resp.form['f1'] = 'test submission'
resp.form['f2'] = 'baz'
resp.form['f3'] = 'C'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
# stop here, don't validate, let user finish it.
assert data_class.count() == 1
formdata_no = data_class.select()[0].id
tracking_code = data_class.select()[0].tracking_code
assert data_class.get(formdata_no).data['1'] == 'test submission'
assert data_class.get(formdata_no).data['2'] == 'baz'
assert data_class.get(formdata_no).status == 'draft'
assert data_class.get(formdata_no).user is None
resp = get_app(pub).get('/code/%s/load' % data_class.select()[0].tracking_code)
resp = resp.follow()
assert resp.location.startswith('http://example.net/form-title/?mt=')
resp = resp.follow()
assert 'Check values then click submit.' in resp.text
assert 'test submission' in resp.text
def test_backoffice_submission_drafts(pub):
user = create_user(pub)
create_environment(pub)
app = login(get_app(pub))
formdef = FormDef.get_by_urlname('form-title')
formdef.enable_tracking_codes = True
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
data_class = formdef.data_class()
data_class.wipe()
resp = app.get('/backoffice/submission/')
assert formdef.url_name in resp.text
resp = resp.click(formdef.name)
resp.form['f1'] = 'test submission'
resp.form['f2'] = 'baz'
resp.form['f3'] = 'C'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert data_class.count() == 1
formdata = data_class.select()[0]
formdata_no = formdata.id
tracking_code = data_class.select()[0].tracking_code
# stop here, go back to index
resp = app.get('/backoffice/submission/')
assert '%s/%s' % (formdef.url_name, formdata_no) in resp.text
assert '>#%s' % formdata_no in resp.text
formdata.submission_channel = 'mail'
formdata.store()
resp = app.get('/backoffice/submission/')
assert '>Mail #%s' % formdata_no in resp.text
# check it can also be accessed using its final URL
resp2 = app.get('/backoffice/management/%s/%s/' % (formdef.url_name, formdata_no))
assert resp2.location == 'http://example.net/backoffice/submission/%s/%s/' % (
formdef.url_name, formdata_no)
resp = resp.click('#%s' % formdata_no)
resp = resp.follow()
assert tracking_code in resp.text
resp = resp.form.submit('previous')
assert resp.form['f1'].value == 'test submission'
resp = resp.form.submit('submit')
assert "Check values then click submit." in resp.text
resp = resp.form.submit('submit')
# check it kept the same id
assert resp.location == 'http://example.net/backoffice/management/form-title/%s/' % formdata_no
def test_backoffice_submission_remove_drafts(pub):
user = create_user(pub)
create_environment(pub)
app = login(get_app(pub))
formdef = FormDef.get_by_urlname('form-title')
formdef.enable_tracking_codes = True
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
data_class = formdef.data_class()
data_class.wipe()
pub.tracking_code_class.wipe()
resp = app.get('/backoffice/submission/')
assert formdef.url_name in resp.text
resp = resp.click(formdef.name)
resp.form['f1'] = 'test submission'
resp.form['f2'] = 'baz'
resp.form['f3'] = 'C'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert data_class.count() == 1
formdata = data_class.select()[0]
formdata_no = formdata.id
tracking_code = data_class.select()[0].tracking_code
# stop here, go back to the index
resp = app.get('/backoffice/submission/')
resp = resp.click('#%s' % formdata_no)
resp = resp.follow()
# and try to delete the form (but cancel)
resp = resp.click('Delete this form')
resp = resp.form.submit('cancel')
assert resp.location == 'http://example.net/backoffice/submission/'
assert data_class.count() == 1
assert pub.tracking_code_class().count() == 1
# and this time for real
resp = app.get('/backoffice/submission/')
resp = resp.click('#%s' % formdata_no)
resp = resp.follow()
resp = resp.click('Delete this form')
resp = resp.form.submit('delete')
assert resp.location == 'http://example.net/backoffice/submission/'
assert data_class.count() == 0
assert pub.tracking_code_class().count() == 0
# check it's not possible to delete an actual formdata
formdata = data_class()
formdata.store()
resp = app.get('/backoffice/submission/form-title/remove/%s' % formdata.id,
status=403)
def test_backoffice_submission_live_condition(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
formdef.fields = [
fields.StringField(type='string', id='1', label='Bar', size='40',
required=True, varname='bar'),
fields.StringField(type='string', id='2', label='Foo', size='40',
required=True, varname='foo',
condition={'type': 'django', 'value': 'form_var_bar == "bye"'}),
]
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/submission/form-title/')
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') == 'display: none'
resp.form['f1'] = 'hello'
live_url = resp.html.find('form').attrs['data-live-url']
assert '/backoffice/' in live_url
live_resp = app.post(live_url, params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert not live_resp.json['result']['2']['visible']
resp.form['f1'] = 'bye'
live_resp = app.post(live_url, params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert live_resp.json['result']['2']['visible']
resp.form['f1'] = 'hello'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert 'name="f1"' in resp.text
assert 'name="f2"' not in resp.text
resp = resp.form.submit('submit')
resp = resp.follow()
assert '<span class="label">Bar</span>' in resp.text
assert '<span class="label">Foo</span>' not in resp.text
def test_backoffice_submission_conditional_jump_based_on_bo_field(pub):
user = create_superuser(pub)
Workflow.wipe()
workflow = Workflow(name='form-title')
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.StringField(
id='bo0', varname='foo_bovar', type='string', label='bo variable'),
]
st1 = workflow.add_status('Status1', 'st1')
st2 = workflow.add_status('Status2', 'st2')
setbo = SetBackofficeFieldsWorkflowStatusItem()
setbo.parent = st1
setbo.fields = [{'field_id': 'bo0', 'value': 'go'}]
st1.items.append(setbo)
jump = JumpWorkflowStatusItem()
jump.condition = {'type': 'django', 'value': "form_var_foo_bovar == 'go'"}
jump.status = 'st2'
st1.items.append(jump)
jump.parent = st1
workflow.store()
FormDef.wipe()
formdef = FormDef()
formdef.name = 'form-title'
formdef.fields = [
fields.TextField(id='1', varname='foo', type='text', label='fo variable'),
fields.TextField(id='2', varname='var3', type='text', label='n/a',
condition={'type': 'django', 'value': 'True'}),
]
formdef.workflow_id = workflow.id
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
formdef.data_class().wipe()
# check jump condition being True
app = login(get_app(pub))
resp = app.get('/backoffice/submission/form-title/')
resp.form['f1'] = 'foo'
resp.form['f2'] = 'bar'
resp = resp.form.submit('submit') # -> confirmation page
resp = resp.form.submit('submit').follow()
assert formdef.data_class().count() == 1
assert formdef.data_class().select()[0].status == 'wf-st2'
# check jump condition being False
setbo.fields = [{'field_id': 'bo0', 'value': 'nogo'}]
workflow.store()
formdef.data_class().wipe()
resp = app.get('/backoffice/submission/form-title/')
resp.form['f1'] = 'foo'
resp.form['f2'] = 'bar'
resp = resp.form.submit('submit') # -> confirmation page
resp = resp.form.submit('submit').follow()
assert formdef.data_class().count() == 1
assert formdef.data_class().select()[0].status == 'wf-st1'
def test_backoffice_submission_sections(pub):
user = create_user(pub)
create_environment(pub)
app = login(get_app(pub))
formdef = FormDef.get_by_urlname('form-title')
formdef.enable_tracking_codes = True
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
data_class = formdef.data_class()
data_class.wipe()
resp = app.get('/backoffice/submission/')
assert not 'Submission to complete' in resp.text
assert not 'Running submission' in resp.text
formdata = data_class()
formdata.data = {}
formdata.status = 'draft'
formdata.backoffice_submission = True
formdata.receipt_time = datetime.datetime(2015, 1, 1).timetuple()
formdata.store()
resp = app.get('/backoffice/submission/')
assert 'Submission to complete' in resp.text
assert not 'Running submission' in resp.text
assert '>#%s' % formdata.id in resp.text
formdata.data = {'1': 'xxx'}
formdata.store()
resp = app.get('/backoffice/submission/')
assert not 'Submission to complete' in resp.text
assert 'Running submission' in resp.text
assert '>#%s' % formdata.id in resp.text
def test_backoffice_submission_prefill_user(pub):
user = create_user(pub)
create_environment(pub)
other_user = pub.user_class(name='other user')
other_user.email = 'other@example.net'
other_user.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.fields[0].prefill = {'type': 'user', 'value': 'email'}
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
formdata = formdef.data_class()()
formdata.backoffice_submission = True
formdata.status = 'draft'
formdata.data = {}
formdata.submission_channel = 'mail'
formdata.user_id = other_user.id
formdata.submission_context = {}
formdata.store()
formdata2 = formdef.data_class()()
formdata2.backoffice_submission = True
formdata2.status = 'draft'
formdata2.data = {}
formdata.submission_channel = 'mail'
formdata.user_id = None
formdata2.submission_context = {}
formdata2.store()
app = login(get_app(pub))
resp = app.get('/backoffice/submission/form-title/')
assert resp.form['f1'].value == ''
# restore a draft
resp = app.get('/backoffice/submission/form-title/%s/' % formdata.id)
resp = resp.follow()
# and check it got prefilled with the user from context
assert resp.form['f1'].value == 'other@example.net'
# restore another, without user id
resp = app.get('/backoffice/submission/form-title/%s/' % formdata2.id)
resp = resp.follow()
# and check it was not prefilled
assert resp.form['f1'].value == ''
def test_backoffice_submission_prefill_user_via_formula(pub):
user = create_user(pub)
create_environment(pub)
other_user = pub.user_class(name='other user')
other_user.email = 'other@example.net'
other_user.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.fields[0].prefill = {'type': 'formula', 'value': 'form_user_email'}
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
formdata = formdef.data_class()()
formdata.backoffice_submission = True
formdata.status = 'draft'
formdata.data = {}
formdata.submission_channel = 'mail'
formdata.user_id = other_user.id
formdata.submission_context = {}
formdata.store()
formdata2 = formdef.data_class()()
formdata2.backoffice_submission = True
formdata2.status = 'draft'
formdata2.data = {}
formdata.submission_channel = 'mail'
formdata.user_id = None
formdata2.submission_context = {}
formdata2.store()
app = login(get_app(pub))
resp = app.get('/backoffice/submission/form-title/')
assert resp.form['f1'].value == ''
# restore a draft
resp = app.get('/backoffice/submission/form-title/%s/' % formdata.id)
resp = resp.follow()
# and check it got prefilled with the user from context
assert resp.form['f1'].value == 'other@example.net'
# restore another, without user id
resp = app.get('/backoffice/submission/form-title/%s/' % formdata2.id)
resp = resp.follow()
# and check it was not prefilled
assert resp.form['f1'].value == ''
def test_backoffice_submission_prefill_user_multiple_pages(pub):
user = create_user(pub)
create_environment(pub)
other_user = pub.user_class(name='other user')
other_user.email = 'other@example.net'
other_user.store()
for enable_tracking_code in (False, True):
formdef = FormDef.get_by_urlname('form-title')
formdef.fields = [
fields.PageField(id='0', label='1st page', type='page'),
fields.StringField(id='1', label='1st field', type='string', required=False),
fields.PageField(id='4', label='2nd page', type='page'),
fields.StringField(id='5', label='field on 2nd page',
prefill = {'type': 'user', 'value': 'email'}),
]
formdef.backoffice_submission_roles = user.roles[:]
formdef.enable_tracking_codes = enable_tracking_code
formdef.store()
formdef.data_class().wipe()
formdata = formdef.data_class()()
formdata.backoffice_submission = True
formdata.status = 'draft'
formdata.data = {}
formdata.submission_channel = 'mail'
formdata.user_id = other_user.id
formdata.submission_context = {}
formdata.store()
formdata2 = formdef.data_class()()
formdata2.backoffice_submission = True
formdata2.status = 'draft'
formdata2.data = {}
formdata.submission_channel = 'mail'
formdata.user_id = None
formdata2.submission_context = {}
formdata2.store()
app = login(get_app(pub))
resp = app.get('/backoffice/submission/form-title/')
resp = resp.form.submit('submit')
assert resp.form['f5'].value == ''
# restore a draft
resp = app.get('/backoffice/submission/form-title/%s/' % formdata.id)
resp = resp.follow()
resp = resp.form.submit('submit')
# and check it got prefilled with the user from context
assert resp.form['f5'].value == 'other@example.net'
# restore another, without user id
resp = app.get('/backoffice/submission/form-title/%s/' % formdata2.id)
resp = resp.follow()
resp = resp.form.submit('submit')
# and check it was not prefilled
assert resp.form['f5'].value == ''
# continue with additional tests when drafts are enabled, using autosave
# restore a draft
formdata.page_no = 0
formdata.user_id = other_user.id
formdata.store()
resp = app.get('/backoffice/submission/form-title/%s/' % formdata.id)
resp = resp.follow()
resp.form['f1'] = 'Hello'
resp_autosave = app.post('/backoffice/submission/form-title/autosave', params=resp.form.submit_fields())
assert formdef.data_class().get(formdata.id).user_id == str(other_user.id)
assert formdef.data_class().get(formdata.id).data['1'] == 'Hello'
resp = resp.form.submit('submit')
# and check it got prefilled with the user from context
assert resp.form['f5'].value == 'other@example.net'
assert formdef.data_class().get(formdata.id).user_id == str(other_user.id)
assert formdef.data_class().get(formdata.id).data['1'] == 'Hello'
formdef.data_class().wipe()
resp = app.get('/backoffice/submission/form-title/')
resp.form['f1'] = 'Hello'
resp_autosave = app.post('/backoffice/submission/form-title/autosave', params=resp.form.submit_fields())
assert formdef.data_class().count() == 1
assert formdef.data_class().select()[0].user_id is None
resp = resp.form.submit('submit')
assert formdef.data_class().count() == 1
assert formdef.data_class().select()[0].user_id is None
def test_backoffice_submission_multiple_page_restore_on_validation(pub):
user = create_user(pub)
create_environment(pub)
other_user = pub.user_class(name='other user')
other_user.email = 'other@example.net'
other_user.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.fields = [
fields.PageField(id='0', label='1st page', type='page'),
fields.StringField(id='1', label='1st field', type='string', required=False),
fields.PageField(id='2', label='2nd page', type='page',
condition={'type': 'python', 'value': 'False'}),
fields.PageField(id='3', label='3rd page', type='page'),
fields.StringField(id='5', label='field on 3rd page'),
]
formdef.backoffice_submission_roles = user.roles[:]
formdef.enable_tracking_codes = True
formdef.store()
formdef.data_class().wipe()
app = login(get_app(pub))
resp = app.get('/backoffice/submission/form-title/')
resp.form['f1'] = 'foo'
resp = resp.form.submit('submit')
resp.form['f5'] = 'bar'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
# restore draft
resp = app.get('/backoffice/submission/')
resp = resp.click(href='form-title/%s' % formdata.id)
resp = resp.follow()
assert 'Check values then click submit.' in resp.text
def test_backoffice_submission_substitution_vars(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields = [
fields.PageField(id='0', label='1st page', type='page'),
fields.StringField(id='1', label='1st field', type='string',
required=False, varname='foobar'),
fields.ItemField(id='10', label='2nd field', type='item',
items=['foo', 'bar', 'baz'], varname='foobar2'),
fields.PageField(id='4', label='2nd page', type='page'),
fields.CommentField(id='5', label='X[form_var_foobar]Y[form_var_foobar2_raw]Z', type='comment'),
]
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/submission/form-title/')
resp.form['f1'].value = 'PLOP'
resp.form['f10'].value = 'bar'
resp = resp.form.submit('submit')
assert 'XPLOPYbarZ' in resp.text
# django-templated comment
formdef.fields[4] = fields.CommentField(id='5',
label='dj-{{ form_var_foobar }}-an-{{ form_var_foobar2_raw}}-go', type='comment')
formdef.store()
formdef.data_class().wipe()
resp = app.get('/backoffice/submission/form-title/')
resp.form['f1'].value = 'foo'
resp.form['f10'].value = 'bar'
resp = resp.form.submit('submit')
assert 'dj-foo-an-bar-go' in resp.text
formdef.data_class().wipe()
# same but starting from a draft, as if it was initiated by welco
formdata = formdef.data_class()()
formdata.data = {}
formdata.status = 'draft'
formdata.backoffice_submission = True
formdata.store()
resp = app.get('/backoffice/submission/form-title/%s/' % formdata.id)
resp = resp.follow()
resp.form['f1'].value = 'PLOP'
resp.form['f10'].value = 'bar'
resp = resp.form.submit('submit')
assert 'dj-PLOP-an-bar-go' in resp.text
def test_backoffice_submission_manual_channel(pub):
user = create_user(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/')
assert not 'Submission' in resp.text
app.get('/backoffice/submission/', status=403)
formdef = FormDef.get_by_urlname('form-title')
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
resp = app.get('/backoffice/')
assert 'Submission' in resp.text
resp = app.get('/backoffice/submission/')
assert formdef.url_name in resp.text
resp = resp.click(formdef.name)
assert resp.form['submission_channel'].attrs['type'] == 'hidden'
resp.form['submission_channel'] = 'mail'
resp.form['f1'] = 'test submission'
resp.form['f2'] = 'baz'
resp.form['f3'] = 'C'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
# final submit
resp = resp.form.submit('submit')
formdata_no = resp.location.split('/')[-2]
data_class = formdef.data_class()
assert data_class.get(formdata_no).data['1'] == 'test submission'
assert data_class.get(formdata_no).data['2'] == 'baz'
assert data_class.get(formdata_no).status == 'wf-new'
assert data_class.get(formdata_no).user is None
assert data_class.get(formdata_no).backoffice_submission is True
assert data_class.get(formdata_no).submission_channel == 'mail'
def test_backoffice_submission_no_manual_channel_with_welco(pub, welco_url):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/submission/%s/' % formdef.url_name)
assert 'submission_channel' not in resp.form.fields
def test_backoffice_submission_with_nameid_and_channel(pub, local_user):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields[0].prefill = {'type': 'formula', 'value': 'form_user_email'}
formdef.backoffice_submission_roles = user.roles[:]
formdef.enable_tracking_codes = True
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/submission/form-title/?NameID=%s&channel=mail' % local_user.name_identifiers[0])
assert resp.location.startswith('http://example.net/backoffice/submission/form-title/')
formdata_no = resp.location.split('/')[-2]
formdata = formdef.data_class().get(formdata_no)
assert formdata.user_id == str(local_user.id)
assert formdata.submission_channel == 'mail'
assert formdata.status == 'draft'
resp = resp.follow() # redirect to created draft
resp = resp.follow() # redirect to ?mt=
assert resp.form['f1'].value == local_user.email # prefill with form_user_email
resp.form['f2'] = 'baz'
resp.form['f3'] = 'C'
resp = resp.form.submit('submit')
assert 'Check values then click submit.' in resp.text
# final submit
resp = resp.form.submit('submit')
formdata_no = resp.location.split('/')[-2]
data_class = formdef.data_class()
formdata = data_class.get(formdata_no)
assert formdata.user_id == str(local_user.id)
assert formdata.submission_channel == 'mail'
assert formdata.status == 'wf-new'
def test_backoffice_wscall_failure_display(http_requests, pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
form_class = formdef.data_class()
number31 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0]
# attach a custom workflow
workflow = Workflow(name='wscall')
st1 = workflow.add_status('Status1', number31.status.split('-')[1])
wscall = WebserviceCallStatusItem()
wscall.id = '_wscall'
wscall.varname = 'xxx'
wscall.url = 'http://remote.example.net/xml'
wscall.action_on_bad_data = ':stop'
wscall.record_errors = True
st1.items.append(wscall)
wscall.parent = st1
again = ChoiceWorkflowStatusItem()
again.id = '_again'
again.label = 'Again'
again.by = ['_receiver']
again.status = st1.id
st1.items.append(again)
again.parent = st1
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert (' with the number %s.' % number31.get_display_id()) in resp.text
assert 'Again' in resp.text
resp = resp.forms[0].submit('button_again')
resp = resp.follow()
assert 'Error during webservice call' in resp.text
number31.user_id = user.id # change ownership to stay in frontoffice
number31.store()
# the failure message shouldn't be displayed in the frontoffice
resp = app.get('/form-title/%s/' % number31.id)
assert (' with the number %s.' % number31.get_display_id()) in resp.text
assert not 'Error during webservice call' in resp.text
def test_backoffice_wscall_error_email(http_requests, pub, emails):
pub.cfg['debug'] = {'error_email': 'errors@localhost.invalid'}
pub.write_cfg()
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
form_class = formdef.data_class()
number31 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0]
# attach a custom workflow
workflow = Workflow(name='wscall')
st1 = workflow.add_status('Status1', number31.status.split('-')[1])
wscall = WebserviceCallStatusItem()
wscall.id = '_wscall'
wscall.varname = 'xxx'
wscall.url = 'http://remote.example.net/xml'
wscall.action_on_bad_data = ':stop'
wscall.record_errors = True
st1.items.append(wscall)
wscall.parent = st1
again = ChoiceWorkflowStatusItem()
again.id = '_again'
again.label = 'Again'
again.by = ['_receiver']
again.status = st1.id
st1.items.append(again)
again.parent = st1
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert (' with the number %s.' % number31.get_display_id()) in resp.text
assert 'Again' in resp.text
resp = resp.forms[0].submit('button_again')
resp = resp.follow()
assert 'Error during webservice call' in resp.text
# check email box
error_email = emails.get('[ERROR] [WSCALL] json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)')
assert '/form-title/%s/' % number31.id in error_email['payload']
assert error_email['msg']['References']
def test_backoffice_wscall_attachment(http_requests, pub):
create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
form_class = formdef.data_class()
number31 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0]
# attach a custom workflow
workflow = Workflow(name='wscall')
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.FileField(id='bo1', label='bo field 1', type='file'),
]
st1 = workflow.add_status('Status1', number31.status.split('-')[1])
wscall = WebserviceCallStatusItem()
wscall.id = '_wscall'
wscall.varname = 'xxx'
wscall.response_type = 'attachment'
wscall.backoffice_filefield_id = 'bo1'
wscall.url = 'http://remote.example.net/xml'
wscall.action_on_bad_data = ':stop'
wscall.record_errors = True
st1.items.append(wscall)
wscall.parent = st1
again = ChoiceWorkflowStatusItem()
again.id = '_again'
again.label = 'Again'
again.by = ['_receiver']
again.status = st1.id
st1.items.append(again)
again.parent = st1
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert (' with the number %s.' % number31.get_display_id()) in resp.text
assert 'Again' in resp.text
resp = resp.forms[0].submit('button_again')
resp = resp.follow()
# get the two generated files from backoffice: in backoffice fields
# (wscall.backoffice_filefield_id), and in history
for index in (0, 1):
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
resp = resp.click('xxx.xml', index=index)
assert resp.location.endswith('/xxx.xml')
resp = resp.follow()
assert resp.content_type == 'text/xml'
assert resp.text == '<?xml version="1.0"><foo/>'
formdata = formdef.data_class().get(number31.id)
assert formdata.evolution[-1].parts[0].orig_filename == 'xxx.xml'
assert formdata.evolution[-1].parts[0].content_type == 'text/xml'
assert formdata.get_substitution_variables()['attachments'].xxx.filename == 'xxx.xml'
resp = app.get(formdata.get_substitution_variables()['attachments'].xxx.url)
resp = resp.follow()
assert resp.text == '<?xml version="1.0"><foo/>'
def test_backoffice_wfedit(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
form_class = formdef.data_class()
number31 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0]
number31.submission_context = {
'mail_url': 'http://www.example.com/test.pdf',
}
number31.store()
# attach a custom workflow
workflow = Workflow(name='wfedit')
st1 = workflow.add_status('Status1', number31.status.split('-')[1])
wfedit = EditableWorkflowStatusItem()
wfedit.id = '_wfedit'
wfedit.by = [user.roles[0]]
st1.items.append(wfedit)
wfedit.parent = st1
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert (' with the number %s.' % number31.get_display_id()) in resp.text
resp = resp.form.submit('button_wfedit')
resp = resp.follow()
assert 'http://www.example.com/test.pdf' in resp.text # make sure sidebar has details
assert not 'Tracking Code' in resp.text # make sure it doesn't display a tracking code
assert resp.form['f1'].value == number31.data['1']
assert resp.form['f2'].value == number31.data['2']
assert resp.form['f3'].value == number31.data['3']
assert 'value="Save Changes"' in resp.text
resp.form['f2'].value = 'bar'
resp = resp.form.submit('submit')
resp = resp.follow()
assert form_class().get(number31.id).data['2'] == 'bar'
number31.store()
def test_backoffice_wfedit_disabled(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
form_class = formdef.data_class()
number31 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0]
number31.submission_context = {
'mail_url': 'http://www.example.com/test.pdf',
}
number31.store()
# attach a custom workflow
workflow = Workflow(name='wfedit')
st1 = workflow.add_status('Status1', number31.status.split('-')[1])
wfedit = EditableWorkflowStatusItem()
wfedit.id = '_wfedit'
wfedit.by = [user.roles[0]]
st1.items.append(wfedit)
wfedit.parent = st1
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
app = login(get_app(pub))
formdef.disabled = True
formdef.store()
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
resp = resp.form.submit('button_wfedit')
resp = resp.follow()
resp.form['f2'].value = 'bar'
resp = resp.form.submit('submit')
resp = resp.follow()
assert form_class().get(number31.id).data['2'] == 'bar'
def test_backoffice_wfedit_submission(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.backoffice_submission_roles = user.roles[:]
formdef.enable_tracking_codes = True
formdef.fields.insert(0, fields.PageField(id='0', label='1st page', type='page'))
formdef.fields.append(fields.PageField(id='4', label='2nd page', type='page'))
formdef.store()
form_class = formdef.data_class()
number31 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0]
number31.backoffice_submission = True
number31.store()
formdata_count = form_class.count()
# attach a custom workflow
workflow = Workflow(name='wfedit')
st1 = workflow.add_status('Status1', number31.status.split('-')[1])
wfedit = EditableWorkflowStatusItem()
wfedit.id = '_wfedit'
wfedit.by = [user.roles[0]]
st1.items.append(wfedit)
wfedit.parent = st1
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert (' with the number %s.' % number31.get_display_id()) in resp.text
resp = resp.form.submit('button_wfedit')
resp = resp.follow()
assert resp.form['f1'].value == number31.data['1']
assert resp.form['f2'].value == number31.data['2']
assert resp.form['f3'].value == number31.data['3']
resp.form['f2'].value = 'bar'
resp = resp.form.submit('submit')
assert 'value="Save Changes"' in resp.text
resp = resp.form.submit('submit')
resp = resp.follow()
assert form_class().get(number31.id).data['2'] == 'bar'
number31.store()
assert formdata_count == form_class.count()
def test_backoffice_wfedit_and_backoffice_fields(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
form_class = formdef.data_class()
number31 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0]
# attach a custom workflow
workflow = Workflow(name='wfedit')
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.StringField(id='bo1', label='1st backoffice field',
type='string', varname='backoffice_blah', required=False),
]
st1 = workflow.add_status('Status1', number31.status.split('-')[1])
wfedit = EditableWorkflowStatusItem()
wfedit.id = '_wfedit'
wfedit.by = [user.roles[0]]
st1.items.append(wfedit)
wfedit.parent = st1
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
number31 = form_class().get(number31.id)
number31.data['bo1'] = 'plop'
number31.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
resp = resp.form.submit('button_wfedit')
resp = resp.follow()
resp.form['f2'].value = 'bar'
resp = resp.form.submit('submit')
resp = resp.follow()
assert form_class().get(number31.id).data['bo1'] == 'plop'
def test_backoffice_wfedit_and_data_source_with_user_info(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields[2].data_source = {'type': 'json',
'value': 'https://www.example.invalid/?name_id={% firstof form_user_display_name "XXX" %}'
}
formdef.store()
form_class = formdef.data_class()
number31 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0]
# attach a custom workflow
workflow = Workflow(name='wfedit')
st1 = workflow.add_status('Status1', number31.status.split('-')[1])
wfedit = EditableWorkflowStatusItem()
wfedit.id = '_wfedit'
wfedit.by = [user.roles[0]]
st1.items.append(wfedit)
wfedit.parent = st1
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
number31 = form_class().get(number31.id)
number31.user_id = user.id
number31.store()
app = login(get_app(pub))
with mock.patch('wcs.qommon.misc.urlopen') as urlopen:
data = {'data': [{'id': 'A', 'text': 'hello'}, {'id': 'B', 'text': 'world'}]}
def side_effect(url, *args):
assert '?name_id=admin' in url
return StringIO(json.dumps(data))
urlopen.side_effect = side_effect
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
resp = resp.form.submit('button_wfedit')
resp = resp.follow()
assert urlopen.call_count == 1
resp.form['f3'].value = 'A'
resp = resp.form.submit('submit')
assert urlopen.call_count == 2
resp = resp.follow()
def test_global_listing(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_user(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/').follow()
assert 'Global View' in resp.text
resp = resp.click('Global View')
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 20
assert not 'Map View' in resp.text
resp = app.get('/backoffice/management/listing?limit=500')
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 37 # 17 formdef1 + 20 formdef2
resp = app.get('/backoffice/management/listing?offset=20&limit=20')
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 17
# try an overbound offset
resp = app.get('/backoffice/management/listing?offset=40&limit=20')
resp = resp.follow()
assert resp.forms['listing-settings']['offset'].value == '0'
resp = app.get('/backoffice/management/listing')
resp.forms['listing-settings']['end'] = '2014-02-01'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 20
assert 'http://example.net/backoffice/management/other-form/' in resp.text
assert not 'http://example.net/backoffice/management/form-title/' in resp.text
formdef = FormDef.get_by_urlname('form-title')
last_update_time = formdef.data_class().select(lambda x: not x.is_draft())[0].last_update_time
# check created and last modified columns
assert '>2014-01-01 00:00<' in resp.text
assert time.strftime('>%Y-%m-%d', last_update_time) in resp.text
# check digest is included
formdata = formdef.data_class().get(
re.findall(r'data-link="(.*?)"', app.get('/backoffice/management/listing').text)[0].split('/')[-2])
formdata.formdef.digest_template = 'digest of number <{{form_number}}>'
formdata.store()
assert formdata.get(formdata.id).digest
resp = app.get('/backoffice/management/listing')
assert formdata.get_url(backoffice=True) in resp.text
assert 'digest of number &lt;%s&gt;' % formdata.id_display in resp.text
# check a Channel column is added when welco is available
assert not 'Channel' in resp.text
if not pub.site_options.has_section('variables'):
pub.site_options.add_section('variables')
pub.site_options.set('variables', 'welco_url', 'xxx')
fd = open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')
pub.site_options.write(fd)
fd.close()
resp = app.get('/backoffice/management/listing?limit=500')
formdata = formdef.data_class().select(lambda x: x.status == 'wf-new')[0]
formdata.submission_channel = 'mail'
formdata.store()
assert 'Channel' in resp.text
assert '>Web<' in resp.text
resp.forms['listing-settings']['submission_channel'] = 'web'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 36
resp.forms['listing-settings']['submission_channel'] = 'mail'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 1
resp = app.get('/backoffice/management/listing?limit=500')
resp.forms['listing-settings']['q'] = 'foo'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 17
resp = app.get('/backoffice/management/listing?limit=500')
resp.forms['listing-settings']['status'] = 'waiting'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 37
resp.forms['listing-settings']['status'] = 'open'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 37
resp.forms['listing-settings']['status'] = 'all'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 70
resp.forms['listing-settings']['status'] = 'done'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 33
# change role handling a formdef, make sure they do not appear anylonger in
# the all/done views.
role = Role(name='whatever')
role.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow_roles = {'_receiver': role.id}
formdef.store()
formdef.data_class().rebuild_security()
resp = app.get('/backoffice/management/listing?limit=500')
resp.forms['listing-settings']['status'] = 'waiting'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 20
assert not 'form-title' in resp.text
resp.forms['listing-settings']['status'] = 'open'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 20
assert not 'form-title' in resp.text
resp.forms['listing-settings']['status'] = 'all'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 20
assert not 'form-title' in resp.text
resp.forms['listing-settings']['status'] = 'done'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 0
assert not 'form-title' in resp.text
def test_global_listing_parameters_from_query_string(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_user(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/listing')
assert resp.forms['listing-settings']['status'].value == 'waiting'
assert resp.forms['listing-settings']['limit'].value == '20'
resp = app.get('/backoffice/management/listing?status=done')
assert resp.forms['listing-settings']['status'].value == 'done'
assert resp.forms['listing-settings']['limit'].value == '20'
resp = app.get('/backoffice/management/listing?status=done&limit=50')
assert resp.forms['listing-settings']['status'].value == 'done'
assert resp.forms['listing-settings']['limit'].value == '50'
resp = app.get('/backoffice/management/listing?status=done&limit=50&q=test')
assert resp.forms['listing-settings']['status'].value == 'done'
assert resp.forms['listing-settings']['limit'].value == '50'
assert resp.forms['listing-settings']['q'].value == 'test'
def test_global_listing_user_label(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_user(pub)
FormDef.wipe()
from wcs.admin.settings import UserFieldsFormDef
user_formdef = UserFieldsFormDef(pub)
user_formdef.fields.append(fields.StringField(id='3', label='first_name', type='string'))
user_formdef.fields.append(fields.StringField(id='4', label='last_name', type='string'))
user_formdef.store()
pub.cfg['users']['field_name'] = ['3', '4']
pub.write_cfg()
formdef = FormDef()
formdef.name = 'foobar'
formdef.url_name = 'foobar'
formdef.workflow_roles = {'_receiver': 1}
formdef.fields = [
fields.StringField(id='1', label='first_name',
prefill={'type': 'user', 'value': '3'}),
fields.StringField(id='2', label='last_name',
prefill={'type': 'user', 'value': '4'}),
]
formdef.store()
formdata = formdef.data_class()()
formdata.data = {'1': 'blah', '2': 'xxx'}
formdata.just_created()
formdata.store()
formdata.jump_status('new')
app = login(get_app(pub))
resp = app.get('/backoffice/management/').follow()
resp = resp.click('Global View')
assert '<td class="cell-user">blah xxx</td>' in resp.text
def test_management_views_with_no_formdefs(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_user(pub)
create_environment(pub)
FormDef.wipe()
from wcs.sql import get_connection_and_cursor, drop_global_views
conn, cur = get_connection_and_cursor()
drop_global_views(conn, cur)
conn.commit()
cur.close()
app = login(get_app(pub))
resp = app.get('/backoffice/management/forms')
assert 'This site is currently empty.' in resp.text
resp = app.get('/backoffice/management/listing')
assert 'This site is currently empty.' in resp.text
def test_category_in_global_listing(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
user = create_user(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/listing?limit=500')
assert not 'category_id' in resp.forms['listing-settings'].fields
cat1 = Category(name='cat1')
cat1.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.category_id = cat1.id
formdef.store()
cat2 = Category(name='cat2')
cat2.store()
formdef = FormDef.get_by_urlname('other-form')
formdef.category_id = cat2.id
formdef.store()
resp = app.get('/backoffice/management/listing?limit=500')
assert 'category_id' in resp.forms['listing-settings'].fields
assert 'management/other-form/' in resp.text
assert 'management/form-title/' in resp.text
resp.forms['listing-settings']['category_id'] = cat1.id
resp = resp.forms['listing-settings'].submit()
assert not 'management/other-form/' in resp.text
assert 'management/form-title/' in resp.text
resp.forms['listing-settings']['category_id'] = cat2.id
resp = resp.forms['listing-settings'].submit()
assert 'management/other-form/' in resp.text
assert not 'management/form-title/' in resp.text
def test_datetime_in_global_listing(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
user = create_user(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/listing?limit=500')
resp.forms['listing-settings']['end'] = '01/01/2010'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 0
resp = app.get('/backoffice/management/listing?limit=500')
resp.forms['listing-settings']['start'] = '01/01/2010'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 37
resp.forms['listing-settings']['start'] = '01/01/2016'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 0
resp.forms['listing-settings']['start'] = '01/01/16'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 0
resp.forms['listing-settings']['start'] = '01/01/10'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 37
resp.forms['listing-settings']['end'] = '01/01/10'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 0
# check invalid values are simply ignored
resp.forms['listing-settings']['end'] = 'whatever'
resp = resp.forms['listing-settings'].submit()
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 37
def test_global_listing_anonymised(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_user(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/listing?limit=500&status=all')
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 70
formdef = FormDef.get_by_urlname('other-form')
for formdata in formdef.data_class().select():
formdata.anonymise()
resp = app.get('/backoffice/management/listing?limit=500&status=all')
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 50
resp = app.get('/backoffice/management/listing?limit=500&status=open')
assert resp.text[resp.text.index('<tbody'):].count('<tr') == 17
def test_global_listing_geojson(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.geolocations = {'base': 'Geolocafoobar'}
formdef.store()
for formdata in formdef.data_class().select():
formdata.geolocations = {'base': {'lat': 48.83, 'lon': 2.32}}
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/geojson')
assert len(resp.json['features']) == 17
assert resp.json['features'][0]['geometry']['coordinates'] == [2.32, 48.83]
for feature in resp.json['features']:
assert feature['properties']['status_colour'] == '#66FF00'
assert feature['properties']['view_label'] == 'View'
assert feature['properties']['status_name'] == 'New'
assert feature['properties']['display_fields']
assert feature['properties']['display_fields'][0]['label'] == 'Name'
assert feature['properties']['display_fields'][0]['value'].startswith('form title #')
resp = app.get('/backoffice/management/geojson?q=aa')
assert len(resp.json['features']) == 5
resp = app.get('/backoffice/management/geojson?q=bb')
assert len(resp.json['features']) == 4
resp = app.get('/backoffice/management/geojson?q=cc')
assert len(resp.json['features']) == 8
def test_global_map(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.geolocations = {'base': 'Geolocafoobar'}
formdef.store()
for formdata in formdef.data_class().select():
formdata.geolocations = {'base': {'lat': 48.83, 'lon': 2.32}}
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/listing')
assert 'Map View' in resp.text
resp = app.get('/backoffice/management/forms')
assert 'Map View' in resp.text
resp = resp.click('Map View')
assert re.findall(r'data-geojson-url="(.*?)"', resp.text) == ['http://example.net/backoffice/management/geojson?']
resp = app.get('/backoffice/management/map?q=test')
assert re.findall(r'data-geojson-url="(.*?)"', resp.text) == ['http://example.net/backoffice/management/geojson?q=test']
def test_formdata_lookup(pub):
create_user(pub)
create_environment(pub, set_receiver=False)
formdef = FormDef.get_by_urlname('form-title')
formdef.enable_tracking_codes = True
formdef.store()
formdata, formdata2 = formdef.data_class().select(order_by='id')[:2]
code = pub.tracking_code_class()
code.formdata = formdata
app = login(get_app(pub))
resp = app.get('/backoffice/management/').follow()
assert 'id="lookup-box"' in resp.text
resp.forms[0]['query'] = formdata.tracking_code
resp = resp.forms[0].submit()
assert resp.location == 'http://example.net/backoffice/management/form-title/%s/' % formdata.id
resp = resp.follow()
assert 'The form has been recorded' in resp.text
assert 'This form has been accessed via its tracking code' in resp.text
# check there's no access to other formdata
app.get('http://example.net/backoffice/management/form-title/%s/' % formdata2.id, status=403)
resp = app.get('/backoffice/management/').follow()
resp.forms[0]['query'] = 'AAAAAAAA'
resp = resp.forms[0].submit()
assert resp.location == 'http://example.net/backoffice/management/'
resp = resp.follow().follow()
assert 'No such tracking code or identifier.' in resp.text
# check looking up a formdata id
resp.form['query'] = formdata.get_display_id()
resp = resp.form.submit()
assert resp.location == 'http://example.net/backoffice/management/form-title/%s/' % formdata.id
if pub.is_using_postgresql():
# check looking up on a custom display_id
formdata.id_display = '999999'
formdata.store()
assert formdata.get_display_id() == '999999'
resp = app.get('/backoffice/management/').follow()
resp.form['query'] = formdata.get_display_id()
resp = resp.form.submit()
assert resp.location == 'http://example.net/backoffice/management/form-title/%s/' % formdata.id
# try it from the global listing
resp = app.get('/backoffice/management/listing')
assert 'id="lookup-box"' in resp.text
resp.forms[0]['query'] = formdata.tracking_code
resp = resp.forms[0].submit()
assert resp.location == 'http://example.net/backoffice/management/form-title/%s/' % formdata.id
resp = resp.follow()
assert 'The form has been recorded' in resp.text
resp = app.get('/backoffice/management/listing')
resp.forms[0]['query'] = 'AAAAAAAA'
resp = resp.forms[0].submit()
assert resp.location == 'http://example.net/backoffice/management/listing'
resp = resp.follow()
assert 'No such tracking code or identifier.' in resp.text
def test_backoffice_sidebar_user_context(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
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.text, re.DOTALL)[0].count('<tr') == 17
# click on a formdata
resp = resp.click(href='%s/' % number31.id)
assert (' with the number %s.' % number31.get_display_id()) in resp.text
# check there's nothing in the sidebar
assert not '/user-pending-forms' in resp.text
number31.formdef.digest_template = 'digest of number {{form_number}}'
number31.user_id = user.id
number31.store()
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert '/user-pending-forms' in resp.text
user_pending_form_url = re.findall('data-async-url="(.*/user-pending-forms)"', resp.text)[0]
partial_resp = app.get(user_pending_form_url)
assert not number31.get_url(backoffice=True) in partial_resp.text
assert number31.digest in partial_resp.text
assert '<span class="formname">%s</span>' % number31.formdef.name in partial_resp.text
# another item with status = new
number34 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 33'][0]
number34.user_id = user.id
number34.store()
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert '/user-pending-forms' in resp.text
user_pending_form_url = re.findall('data-async-url="(.*/user-pending-forms)"', resp.text)[0]
partial_resp = app.get(user_pending_form_url)
assert not number31.get_url(backoffice=True) in partial_resp.text
assert number34.get_url(backoffice=True) in partial_resp.text
cat1 = Category(name='cat1')
cat1.store()
formdef = FormDef.get_by_urlname('other-form')
formdef.category_id = cat1.id
formdef.store()
other_formdata = formdef.data_class().select()[0]
other_formdata.user_id = user.id
other_formdata.store()
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert '/user-pending-forms' in resp.text
user_pending_form_url = re.findall('data-async-url="(.*/user-pending-forms)"', resp.text)[0]
partial_resp = app.get(user_pending_form_url)
assert number34.get_url(backoffice=True) in partial_resp.text
assert other_formdata.get_url(backoffice=True) in partial_resp.text
# categories are displayed, and current formdata category is on top
assert '>cat1<' in partial_resp.text
assert '>Misc<' in partial_resp.text
assert partial_resp.text.index('>Misc<') < partial_resp.text.index('>cat1<')
def test_count_open(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_user(pub)
FormDef.wipe()
resp = login(get_app(pub)).get('/backoffice/management/count')
assert resp.json['count'] == 0
create_environment(pub)
resp = login(get_app(pub)).get('/backoffice/management/count')
assert resp.json['count'] == 37
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow_roles = {'_receiver': 2} # role the user doesn't have
formdef.store()
formdef.data_class().rebuild_security()
resp = login(get_app(pub)).get('/backoffice/management/count')
assert resp.json['count'] == 20
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow_roles = {'_receiver': 2, '_foobar': 1}
formdef.store()
formdef.data_class().rebuild_security()
resp = login(get_app(pub)).get('/backoffice/management/count')
assert resp.json['count'] == 20
resp = login(get_app(pub)).get('/backoffice/management/count?waiting=yes')
assert resp.json['count'] == 20
formdef = FormDef.get_by_urlname('form-title')
workflow = Workflow.get_default_workflow()
workflow.roles['_foobar'] = 'Foobar'
workflow.id = '2'
workflow.store()
formdef.workflow_id = workflow.id
formdef.workflow_roles = {'_receiver': 2, '_foobar': '1'}
formdef.store()
formdef.data_class().rebuild_security()
resp = login(get_app(pub)).get('/backoffice/management/count?waiting=no')
assert resp.json['count'] == 37
resp = login(get_app(pub)).get('/backoffice/management/count?waiting=yes')
assert resp.json['count'] == 20
resp = login(get_app(pub)).get('/backoffice/management/count')
assert resp.json['count'] == 20
# check the callback parameter is ignored, that we still get the default
# criterias when it's set.
resp = login(get_app(pub)).get('/backoffice/management/count?callback=toto')
assert "20" in resp.text
def test_count_backoffice_drafts(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.backoffice_submission_roles = user.roles[:]
formdef.store()
resp = login(get_app(pub)).get('/backoffice/submission/count')
assert resp.json['count'] == 0
formdata1, formdata2, formdata3 = formdef.data_class().select()[:3]
for formdata in (formdata1, formdata2, formdata3):
formdata.status = 'draft'
formdata.store()
resp = login(get_app(pub)).get('/backoffice/submission/count')
assert resp.json['count'] == 0
for formdata in (formdata1, formdata2, formdata3):
formdata.backoffice_submission = True
formdata.store()
resp = login(get_app(pub)).get('/backoffice/submission/count')
assert resp.json['count'] == 3
formdata1.data = {}
formdata1.store()
resp = login(get_app(pub)).get('/backoffice/submission/count?mode=empty')
assert resp.json['count'] == 1
resp = login(get_app(pub)).get('/backoffice/submission/count?mode=existing')
assert resp.json['count'] == 2
def test_menu_json(pub):
user = create_user(pub)
create_environment(pub)
resp = login(get_app(pub)).get('/backoffice/menu.json')
menu_json_str = resp.text
assert len(resp.json) == 1
assert resp.json[0]['slug'] == 'management'
assert resp.headers['content-type'] == 'application/json'
resp = login(get_app(pub)).get('/backoffice/menu.json?jsonpCallback=foo')
assert resp.text == 'foo(%s);' % menu_json_str
assert resp.headers['content-type'] == 'application/javascript'
def test_per_user_view(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
user = create_user(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/').follow()
assert 'Per User View' not in resp.text
if not pub.site_options.has_section('options'):
pub.site_options.add_section('options')
pub.site_options.set('options', 'per-user-view', 'true')
fd = open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')
pub.site_options.write(fd)
fd.close()
resp = app.get('/backoffice/management/').follow()
assert 'Per User View' in resp.text
resp = resp.click(' User View')
assert 'Use the search field on the right' in resp.text
resp.form['q'] = 'admin'
resp = resp.form.submit()
assert resp.text.count('<tr') == 2 # header + user
form_class = FormDef.get_by_urlname('form-title').data_class()
to_match = []
for formdata in form_class.select():
if formdata.data['1'] in ('FOO BAR 30', 'FOO BAR 33'):
formdata.user_id = user.id
formdata.store()
to_match.append('/management/form-title/%s/' % formdata.id)
resp = app.get('/backoffice/management/users/%s/' % user.id)
for item in to_match:
assert item in resp.text
count_li = resp.text.count('<li')
# check list items are displayed, without links, if we cannot view them.
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow_roles['_receiver'] = 'XXX'
formdef.store()
formdef.data_class().rebuild_security()
resp = app.get('/backoffice/management/users/%s/' % user.id)
for item in to_match:
assert not item in resp.text # not linked
assert resp.text.count('<li') == count_li
# mark formdef so it's not listed in per-user view
formdef.skip_from_360_view = True
formdef.store()
# check formdatas are no longer part of the page
resp = app.get('/backoffice/management/users/%s/' % user.id)
assert resp.text.count('<li') == (count_li - 2)
def test_per_user_view_tracking_code(pub, emails, sms_mocking):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
user = create_user(pub)
create_environment(pub)
app = login(get_app(pub))
formdef = FormDef.get_by_urlname('form-title')
form_class = formdef.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()
resp = app.get('/backoffice/management/users/%s/' % user.id)
assert not 'Send tracking code' in resp.text
formdef.enable_tracking_codes = True
formdef.store()
user_view_resp = app.get('/backoffice/management/users/%s/' % user.id)
assert 'Send tracking code' in user_view_resp.text
pub.cfg['sms'] = {'mode': 'none'}
pub.write_cfg()
resp = user_view_resp.click('Send tracking code')
assert not 'sms' in resp.form.fields
assert resp.form['email'].value == user.email
resp = resp.form.submit()
resp = resp.follow()
assert emails.count() == 1
assert emails.get('Tracking Code reminder')['email_rcpt'] == [user.email]
assert form_class.get(number31.id).tracking_code in emails.get('Tracking Code reminder')['payload']
pub.cfg['sms'] = {'mode': 'xx'}
pub.write_cfg()
resp = user_view_resp.click('Send tracking code', index=0)
resp.form['method'].value = 'SMS'
resp.form['sms'].value = '0123456789'
resp = resp.form.submit()
resp = resp.follow()
assert sms_mocking.sms[-1]['destinations'] == ['0123456789']
assert form_class.get(number31.id).tracking_code in sms_mocking.sms[-1]['text']
def test_backoffice_resume_folded(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))
# first access: summary is not folded
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert '<div class="section foldable" id="summary">' in resp.text
# do something: summary is folded
resp = resp.form.submit('button_commentable')
resp = resp.follow()
assert '<div class="section foldable folded" id="summary">' in resp.text
def test_backoffice_backoffice_submission_in_listings(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
first_link = re.findall('data-link="(\d+)/?"', resp.text)[0]
assert not 'backoffice-submission' in resp.text
formdata = FormDef.get_by_urlname('form-title').data_class().get(first_link)
formdata.backoffice_submission = True
formdata.store()
resp = app.get('/backoffice/management/form-title/')
assert 'backoffice-submission' in resp.text
def test_backoffice_backoffice_submission_in_global_listing(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/listing?limit=100')
assert not 'backoffice-submission' in resp.text
formdef = FormDef.get_by_urlname('form-title')
formdata = formdef.data_class().get(
re.findall(r'data-link="(.*?)"', app.get('/backoffice/management/listing').text)[0].split('/')[-2])
formdata.backoffice_submission = True
formdata.store()
resp = app.get('/backoffice/management/listing?limit=100')
assert 'backoffice-submission' in resp.text
def test_backoffice_advisory_lock(pub):
create_superuser(pub)
create_environment(pub)
second_user = pub.user_class(name='foobar')
second_user.roles = Role.keys()
second_user.store()
account = PasswordAccount(id='foobar')
account.set_password('foobar')
account.user_id = second_user.id
account.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
first_link = re.findall('data-link="(\d+/)?"', resp.text)[0]
assert not 'advisory-lock' in resp.text
app2 = login(get_app(pub), username='foobar', password='foobar')
resp = app2.get('/backoffice/management/form-title/')
assert not 'advisory-lock' in resp.text
resp = app.get('/backoffice/management/form-title/' + first_link)
resp = app2.get('/backoffice/management/form-title/')
assert 'advisory-lock' in resp.text
resp = app.get('/backoffice/management/form-title/')
assert not 'advisory-lock' in resp.text
if pub.is_using_postgresql():
# check global view
resp = app2.get('/backoffice/management/listing?limit=100')
assert 'advisory-lock' in resp.text
resp = app.get('/backoffice/management/listing?limit=100')
assert not 'advisory-lock' in resp.text
resp = app.get('/backoffice/management/form-title/' + first_link)
assert not 'Be warned forms of this user are also being looked' in resp.text
assert 'button_commentable' in resp.text
assert len(resp.forms)
resp = app2.get('/backoffice/management/form-title/' + first_link)
assert 'Be warned forms of this user are also being looked' in resp.text
assert not 'button_commentable' in resp.text
assert len(resp.forms) == 0
# revisit with first user, no change
resp = app.get('/backoffice/management/form-title/' + first_link)
assert not 'Be warned forms of this user are also being looked' in resp.text
assert 'button_commentable' in resp.text
# back to second
resp = app2.get('/backoffice/management/form-title/' + first_link)
assert 'Be warned forms of this user are also being looked' in resp.text
assert not 'button_commentable' in resp.text
resp = resp.click('(unlock actions)')
resp = resp.follow()
assert 'Be warned forms of this user are also being looked' in resp.text
assert 'button_commentable' in resp.text
assert not '(unlock actions)' in resp.text
assert len(resp.forms)
# submit action form
resp.form['comment'] = 'HELLO'
resp = resp.form.submit('button_commentable')
# locks are reset after an action
assert not 'advisory-lock' in app2.get('/backoffice/management/form-title/')
assert not 'advisory-lock' in app.get('/backoffice/management/form-title/')
# but as the current user is redirected to the form, a lock will be
# acquired (unless the user didn't have actions anymore, but it's not the
# case here)
resp = resp.follow()
assert not 'advisory-lock' in app2.get('/backoffice/management/form-title/')
assert 'advisory-lock' in app.get('/backoffice/management/form-title/')
def test_backoffice_advisory_lock_related_formdatas(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
pub.session_manager.session_class.wipe()
user = create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdatas = formdef.data_class().select(lambda x: x.status == 'wf-new')
second_user = pub.user_class(name='foobar')
second_user.roles = Role.keys()
second_user.store()
account = PasswordAccount(id='foobar')
account.set_password('foobar')
account.user_id = second_user.id
account.store()
third_user = pub.user_class(name='user')
third_user.store()
for formdata in formdatas[:2]:
formdata.user_id = third_user.id
formdata.store()
second_formdef = FormDef.get_by_urlname('other-form')
second_formdef.workflow_roles = {}
second_formdef.store()
other_formdatas = second_formdef.data_class().select(lambda x: x.status == 'wf-new')
other_formdatas[0].user_id = third_user.id
other_formdatas[0].store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % formdatas[0].id)
assert not 'Be warned forms of this user are also being looked' in resp.text
app.get(re.findall('data-async-url="(.*/user-pending-forms)"', resp.text)[0])
app2 = login(get_app(pub), username='foobar', password='foobar')
resp2 = app2.get('/backoffice/management/form-title/%s/' % formdatas[0].id)
assert 'Be warned forms of this user are also being looked' in resp2.text
app2.get(re.findall('data-async-url="(.*/user-pending-forms)"', resp.text)[0])
# another by the same user
resp2 = app2.get('/backoffice/management/form-title/%s/' % formdatas[1].id)
assert 'Be warned forms of this user are also being looked' in resp2.text
app2.get(re.findall('data-async-url="(.*/user-pending-forms)"', resp.text)[0])
# another by another user
resp2 = app2.get('/backoffice/management/form-title/%s/' % formdatas[3].id)
assert not 'Be warned forms of this user are also being looked' in resp2.text
app2.get(re.findall('data-async-url="(.*/user-pending-forms)"', resp.text)[0])
# check another formdef is only marked as visited if the user has potential
# actions on it.
session = pub.session_manager.session_class.select(lambda x: x.user == user.id)[0]
second_formdef.workflow_roles = {'_receiver': 1}
second_formdef.store()
other_formdata = second_formdef.data_class().get(other_formdatas[0].id)
other_formdata.store() # update concerned_roles
assert not 'formdata-other-form-%d' % other_formdata.id in session.visiting_objects.keys()
session.visiting_objects = {}
session.store()
resp = app.get('/backoffice/management/form-title/%s/' % formdatas[0].id)
app.get(re.findall('data-async-url="(.*/user-pending-forms)"', resp.text)[0])
session = pub.session_manager.session_class.select(lambda x: x.user == user.id)[0]
assert 'formdef-other-form-%d' % other_formdata.id in session.visiting_objects.keys()
def test_backoffice_resubmit(pub):
user = create_user(pub)
create_environment(pub)
wf = Workflow(name='resubmit')
st1 = wf.add_status('Status1')
st2 = wf.add_status('Status2')
resubmit = ResubmitWorkflowStatusItem()
resubmit.id = '_resubmit'
resubmit.by = [user.roles[0]]
st1.items.append(resubmit)
resubmit.parent = st1
jump = JumpOnSubmitWorkflowStatusItem()
jump.id = '_jump'
jump.status = st2.id
st1.items.append(jump)
jump.parent = st1
register_comment = RegisterCommenterWorkflowStatusItem()
register_comment.id = '_register'
register_comment.comment = '<p><a href="[resubmit_formdata_backoffice_url]">resubmitted</a></p>'
st2.items.append(register_comment)
register_comment.parent = st2
wf.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.fields[0].varname = 'foo'
formdef.workflow_id = wf.id
formdef.store()
formdef2 = FormDef()
formdef2.name = 'form title bis'
formdef2.backoffice_submission_roles = user.roles[:]
formdef2.fields = [
fields.StringField(id='2', label='1st field', type='string', varname='foo')
]
formdef2.store()
formdef2.data_class().wipe()
formdata = formdef.data_class()()
formdata.just_created()
formdata.data= {'1': 'XXX'}
formdata.store()
app = login(get_app(pub))
resp = app.get(formdata.get_url(backoffice=True))
resp.form['resubmit'].value = formdef2.id
resp = resp.form.submit('button_resubmit')
resp = resp.follow()
assert 'resubmitted' in resp.text
assert formdef2.data_class().select()[0].status == 'draft'
assert formdef2.data_class().select()[0].data == {'2': 'XXX'}
resp = resp.click('resubmitted')
resp = resp.follow()
resp = resp.follow()
assert resp.form['f2'].value == 'XXX'
assert 'Original form' in resp.text
assert formdata.get_url(backoffice=True) in resp.text
def test_backoffice_workflow_display_form(pub):
user = create_user(pub)
create_environment(pub)
wf = Workflow.get_default_workflow()
wf.id = '2'
wf.store()
wf = Workflow.get(wf.id)
status = wf.get_status('new')
status.items = []
display_form = FormWorkflowStatusItem()
display_form.id = '_display_form'
display_form.by = [user.roles[0]]
display_form.varname = 'blah'
display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
display_form.formdef.fields.append(
fields.StringField(
id='1', label='Test', varname='str', type='string', required=True))
display_form.formdef.fields.append(
# mimick special case: https://dev.entrouvert.org/issues/14691
# item field displayed as radio buttons, with prefill of a value
# that doesn't exist.
fields.ItemField(
id='2', label='Test2', type='item',
prefill={'type': 'string', 'value': ''},
display_mode='radio',
varname='radio',
items=['a', 'b', 'c'], required=True))
status.items.append(display_form)
display_form.parent = status
jump = JumpOnSubmitWorkflowStatusItem()
jump.id = '_jump'
jump.status = 'accepted'
status.items.append(jump)
jump.parent = status
wf.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow_id = wf.id
formdef.store()
for formdata in formdef.data_class().select():
if formdata.status == 'wf-new':
break
app = login(get_app(pub))
resp = app.get(formdata.get_url(backoffice=True))
assert 'f1' in resp.form.fields
resp.form['f1'] = 'blah'
# don't fill required radio button
resp = resp.form.submit('submit')
assert formdef.data_class().get(formdata.id).status == 'wf-new'
assert 'There were errors processing your form.' in resp.text
resp.form['f2'] = 'c'
resp = resp.form.submit('submit')
assert formdef.data_class().get(formdata.id).status == 'wf-accepted'
assert formdef.data_class().get(formdata.id).workflow_data == {
'blah_var_str': 'blah',
'blah_var_radio': 'c',
'blah_var_radio_raw': 'c',
'blah_var_radio_structured': None,
'blah_var_radio_structured_raw': None}
def test_backoffice_workflow_form_with_conditions(pub):
user = create_user(pub)
create_environment(pub)
wf = Workflow.get_default_workflow()
wf.id = '2'
wf.store()
wf = Workflow.get(wf.id)
status = wf.get_status('new')
status.items = []
display_form = FormWorkflowStatusItem()
display_form.id = '_display_form'
display_form.by = [user.roles[0]]
display_form.varname = 'blah'
display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
display_form.formdef.fields = [
fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
fields.StringField(id='2', label='Test2', varname='str2', type='string', required=True),
]
status.items.append(display_form)
display_form.parent = status
wf.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow_id = wf.id
formdef.fields[0].varname = 'plop'
formdef.store()
for formdata in formdef.data_class().select():
if formdata.status == 'wf-new':
break
app = login(get_app(pub))
resp = app.get(formdata.get_url(backoffice=True))
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
# check with static condition
display_form.formdef.fields = [
fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
fields.StringField(id='2', label='Test2', varname='str2',
type='string', required=True,
condition={'type': 'django', 'value': '0'}),
]
wf.store()
resp = login(get_app(pub)).get(formdata.get_url(backoffice=True))
assert 'f1' in resp.form.fields
assert 'f2' not in resp.form.fields
# check condition based on formdata
display_form.formdef.fields = [
fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
fields.StringField(id='2', label='Test2', varname='str2',
type='string', required=True,
condition={'type': 'django', 'value': 'form_var_plop'}),
]
wf.store()
resp = login(get_app(pub)).get(formdata.get_url(backoffice=True))
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
display_form.formdef.fields = [
fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
fields.StringField(id='2', label='Test2', varname='str2',
type='string', required=True,
condition={'type': 'django', 'value': 'form_var_plop != "xxx"'}),
]
wf.store()
resp = login(get_app(pub)).get(formdata.get_url(backoffice=True))
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
# check with live conditions
display_form.formdef.fields = [
fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
fields.StringField(id='2', label='Test2', varname='str2',
type='string', required=True,
condition={'type': 'django', 'value': 'blah_var_str == "xxx"'}),
]
wf.store()
resp = login(get_app(pub)).get(formdata.get_url(backoffice=True))
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') == 'display: none'
live_url = resp.html.find('form').attrs['data-live-url']
assert '/backoffice/' in live_url
resp.form['f1'] = ''
live_resp = app.post(live_url, params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert not live_resp.json['result']['2']['visible']
resp.form['f1'] = 'xxx'
live_resp = app.post(live_url, params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert live_resp.json['result']['2']['visible']
# check submit doesn't work
resp = resp.form.submit('submit')
assert 'There were errors processing your form.' in resp.text
resp.form['f1'] = 'xxx2'
live_resp = app.post(live_url, params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert not live_resp.json['result']['2']['visible']
# check submit does work when second field is hidden
resp = resp.form.submit('submit').follow()
assert formdef.data_class().get(formdata.id).workflow_data == {'blah_var_str': 'xxx2', 'blah_var_str2': None}
def test_backoffice_workflow_form_with_live_data_source(pub):
user = create_user(pub)
create_environment(pub)
wf = Workflow.get_default_workflow()
wf.id = '2'
wf.store()
wf = Workflow.get(wf.id)
status = wf.get_status('new')
status.items = []
display_form = FormWorkflowStatusItem()
display_form.id = '_display_form'
display_form.by = [user.roles[0]]
display_form.varname = 'blah'
display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
display_form.formdef.fields = [
fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
fields.ItemField(id='2', label='Test2', varname='str2', type='item', required=True,
data_source={'type': 'json', 'value': 'https://www.example.invalid/{{ blah_var_str }}'}),
]
status.items.append(display_form)
display_form.parent = status
wf.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow_id = wf.id
formdef.fields[0].varname = 'plop'
formdef.store()
for formdata in formdef.data_class().select():
if formdata.status == 'wf-new':
break
app = get_app(pub)
with mock.patch('wcs.qommon.misc.urlopen') as urlopen:
data1 = {'data': [{'id': 'A', 'text': 'hello'}, {'id': 'B', 'text': 'world'}]}
data2 = {'data': [{'id': 'C', 'text': 'hello'}, {'id': 'D', 'text': 'world'}]}
def side_effect(url, *args):
if 'toto' not in url:
return StringIO(json.dumps(data1))
else:
return StringIO(json.dumps(data2))
urlopen.side_effect = side_effect
resp = login(app).get(formdata.get_url(backoffice=True))
assert 'f1' in resp.form.fields
assert 'f2' in resp.form.fields
assert resp.form.fields['f2'][0].options == [(u'A', False, u'hello'), (u'B', False, u'world')]
live_url = resp.html.find('form').attrs['data-live-url']
resp.form['f1'] = 'toto'
live_resp = app.post(live_url + '?modified_field_id=1', params=resp.form.submit_fields())
assert live_resp.json['result']['2']['items'] == [{u'text': u'hello', u'id': u'C'}, {u'text': u'world', u'id': u'D'}]
def test_backoffice_criticality_in_formdef_listing(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
user = create_user(pub)
create_environment(pub)
wf = Workflow.get_default_workflow()
wf.id = '2'
wf.criticality_levels = [
WorkflowCriticalityLevel(name='green'),
WorkflowCriticalityLevel(name='yellow'),
WorkflowCriticalityLevel(name='red'),
]
wf.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow_id = wf.id
formdef.store()
formdata1, formdata2, formdata3, formdata4 = [
x for x in formdef.data_class().select() if x.status == 'wf-new'][:4]
formdata1.set_criticality_level(1)
formdata1.store()
formdata1_str = '>%s<' % formdata1.get_display_id()
formdata2.set_criticality_level(2)
formdata2.store()
formdata2_str = '>%s<' % formdata2.get_display_id()
formdata3.set_criticality_level(2)
formdata3.store()
formdata3_str = '>%s<' % formdata3.get_display_id()
formdata4_str = '>%s<' % formdata4.get_display_id()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/?order_by=-criticality_level&limit=100')
assert resp.text.index(formdata1_str) > resp.text.index(formdata2_str)
assert resp.text.index(formdata1_str) > resp.text.index(formdata3_str)
assert resp.text.index(formdata1_str) < resp.text.index(formdata4_str)
resp = app.get('/backoffice/management/form-title/?order_by=criticality_level&limit=100')
assert resp.text.index(formdata1_str) < resp.text.index(formdata2_str)
assert resp.text.index(formdata1_str) < resp.text.index(formdata3_str)
assert resp.text.index(formdata1_str) > resp.text.index(formdata4_str)
def test_backoffice_criticality_in_global_listing(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
user = create_user(pub)
create_environment(pub)
wf = Workflow.get_default_workflow()
wf.id = '2'
wf.criticality_levels = [
WorkflowCriticalityLevel(name='green'),
WorkflowCriticalityLevel(name='yellow'),
WorkflowCriticalityLevel(name='red'),
]
wf.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow_id = wf.id
formdef.store()
formdata1, formdata2, formdata3, formdata4 = [
x for x in formdef.data_class().select() if x.status == 'wf-new'][:4]
formdata1.set_criticality_level(1)
formdata1.store()
formdata1_str = '>%s<' % formdata1.get_display_id()
formdata2.set_criticality_level(2)
formdata2.store()
formdata2_str = '>%s<' % formdata2.get_display_id()
formdata3.set_criticality_level(2)
formdata3.store()
formdata3_str = '>%s<' % formdata3.get_display_id()
formdata4_str = '>%s<' % formdata4.get_display_id()
app = login(get_app(pub))
resp = app.get('/backoffice/management/listing?order_by=-criticality_level&limit=100')
assert resp.text.index(formdata1_str) > resp.text.index(formdata2_str)
assert resp.text.index(formdata1_str) > resp.text.index(formdata3_str)
assert resp.text.index(formdata1_str) < resp.text.index(formdata4_str)
resp = app.get('/backoffice/management/listing?order_by=criticality_level&limit=100')
assert resp.text.index(formdata1_str) < resp.text.index(formdata2_str)
assert resp.text.index(formdata1_str) < resp.text.index(formdata3_str)
assert resp.text.index(formdata1_str) > resp.text.index(formdata4_str)
def test_backoffice_criticality_formdata_view(pub):
user = create_user(pub)
create_environment(pub)
wf = Workflow.get_default_workflow()
wf.id = '2'
wf.criticality_levels = [
WorkflowCriticalityLevel(name='green'),
WorkflowCriticalityLevel(name='yellow'),
WorkflowCriticalityLevel(name='red'),
]
wf.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow_id = wf.id
formdef.store()
formdef = FormDef.get_by_urlname('form-title')
formdata = [x for x in formdef.data_class().select() if x.status == 'wf-new'][0]
formdata.set_criticality_level(1)
formdata.store()
app = login(get_app(pub))
resp = app.get(formdata.get_url(backoffice=True))
assert 'Criticality Level: yellow' in resp.text
formdata.set_criticality_level(2)
formdata.store()
resp = app.get(formdata.get_url(backoffice=True))
assert 'Criticality Level: red' in resp.text
class IHateUnicode(object):
def __unicode__(self):
raise Exception('HATE!!')
def __repr__(self):
return 'ok'
@pytest.fixture
def local_user():
get_publisher().user_class.wipe()
user = get_publisher().user_class()
user.name = 'Jean Darmette'
user.email = 'jean.darmette@triffouilis.fr'
user.name_identifiers = ['0123456789']
user.store()
return user
def test_inspect_page(pub, local_user):
create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdata = [x for x in formdef.data_class().select() if x.status == 'wf-new'][0]
# temper with field 3 structured values
formdata.data['3_structured'] = {
'unicode': u'uné',
'str_but_non_utf8': b'\xed\xa0\x00', # not actually supposed to happen
'non_unicode_convertible': IHateUnicode(),
'very_long_string': '0' * 100000,
}
formdata.user_id = local_user.id
formdata.store()
from wcs.admin.settings import UserFieldsFormDef
user_formdef = UserFieldsFormDef(pub)
user_formdef.fields.append(fields.StringField(id='_first_name', label='name', type='string'))
user_formdef.fields.append(fields.StringField(id='3', label='test', type='string'))
user_formdef.store()
local_user.form_data = {'_first_name': 'toto', '3': 'nono'}
local_user.set_attributes_from_formdata(local_user.form_data)
local_user.store()
resp = login(get_app(pub)).get(formdata.get_url(backoffice=True), status=200)
assert 'Data Inspector' not in resp.text
resp = login(get_app(pub)).get('%sinspect' % formdata.get_url(backoffice=True), status=403)
create_user(pub, is_admin=True)
resp = login(get_app(pub)).get(formdata.get_url(backoffice=True), status=200)
resp = resp.click('Data Inspector')
assert '0' * 1000 in resp.text
assert len(resp.text) < 100000
pq = resp.pyquery.remove_namespaces()
assert (pq('[title="form_var_foo_unicode"]')
.parents('li').children('div.value span')
.text() == u'uné')
assert (pq('[title="form_var_foo_non_unicode_convertible"]')
.parents('li').children('div.value span')
.text().startswith('ok '))
assert (pq('[title="form_var_foo_str_but_non_utf8"]')
.parents('li').children('div.value span')
.text() == "b'\\xed\\xa0\\x00' (<class 'bytes'>)")
# don't show «unusable» variables
assert 'form_f1' not in resp.text
assert 'form_field_' not in resp.text
assert 'form_user_field_' not in resp.text
assert 'form_user_f3' not in resp.text
assert 'form_user_f_' not in resp.text
# check functions
assert re.findall('Recipient.*foobar', resp.text)
role = Role(name='plop')
role.store()
formdata.workflow_roles = {'_receiver': role.id}
formdata.store()
resp = login(get_app(pub)).get('%sinspect' % formdata.get_url(backoffice=True), status=200)
assert re.findall('Recipient.*plop', resp.text)
workflow = Workflow.get_default_workflow()
workflow.id = None
workflow.roles.update({'_plop': 'New Function'})
workflow.store()
formdef.workflow_id = workflow.id
formdef.store()
resp = login(get_app(pub)).get('%sinspect' % formdata.get_url(backoffice=True), status=200)
assert re.findall('New Function.*unset', resp.text)
formdata.workflow_roles = {'_receiver': role.id, '_plop': '123'}
formdata.store()
resp = login(get_app(pub)).get('%sinspect' % formdata.get_url(backoffice=True), status=200)
assert re.findall('New Function.*(deleted)', resp.text)
# test tools
app = login(get_app(pub))
resp = app.get('%sinspect' % formdata.get_url(backoffice=True), status=200)
assert "Test tool" in resp.text
resp.form['test_mode'] = 'python-condition'
resp.form['python-condition'] = 'len(form_name) == 10' # "form title"
resp = resp.form.submit()
assert 'Condition result' in resp.text
assert 'result-true' in resp.text
assert '<code>True</code>' in resp.text
resp.form['python-condition'] = 'len(form_name) == 5'
resp = resp.form.submit()
assert 'Condition result' in resp.text
assert 'result-false' in resp.text
assert '<code>False</code>' in resp.text
resp.form['python-condition'] = 'form_do_not_exist == 3'
resp = resp.form.submit()
assert 'Condition result' not in resp.text
assert 'Failed to evaluate condition' in resp.text
assert 'NameError' in resp.text
resp.form['test_mode'] = 'django-condition'
resp.form['django-condition'] = 'form_name|length == 10'
resp = resp.form.submit()
assert 'Condition result' in resp.text
assert 'result-true' in resp.text
resp.form['django-condition'] = 'form_name|length == 5'
resp = resp.form.submit()
assert 'Condition result' in resp.text
assert 'result-false' in resp.text
resp.form['django-condition'] = 'foo bar'
resp = resp.form.submit()
assert 'Condition result' not in resp.text
assert 'TemplateSyntaxError: Unused' in resp.text
resp.form['django-condition'] = 'form_name|length == 5'
ajax_resp = app.post('%sinspect-tool' % formdata.get_url(backoffice=True), params=resp.form.submit_fields())
assert ajax_resp.text.startswith('<div class="test-tool-result')
resp.form['test_mode'] = 'template'
resp.form['template'] = '{{ form_name }}'
resp = resp.form.submit()
assert 'Template rendering' in resp.text
assert '<div class="test-tool-result-plain">form title</div>' in resp.text
assert 'HTML Source' not in resp.text
resp.form['template'] = '<p>{{ form_name }}</p>'
resp = resp.form.submit()
assert 'Template rendering' in resp.text
assert '<p>form title</p>' in resp.text
assert 'HTML Source' in resp.text
resp.form['template'] = '{% for x in 1 %}ok{% endfor %}'
resp = resp.form.submit()
assert 'Failed to evaluate template' in resp.text
assert 'TypeError' in resp.text
resp.form['template'] = '{% for x in 1 %}ok{% end %}'
resp = resp.form.submit()
assert 'syntax error' in resp.text
assert 'Invalid block tag' in resp.text
resp.form['template'] = ''
resp.form['test_mode'] = 'html_template'
resp.form['html_template'] = '<p>{{ form_name }}</p>'
resp = resp.form.submit()
resp = resp.form.submit()
assert 'Template rendering' in resp.text
assert '<p>form title</p>' in resp.text
assert 'HTML Source' in resp.text
resp.form['html_template'] = '{% for x in 1 %}ok{% endfor %}'
resp = resp.form.submit()
assert 'Failed to evaluate HTML template' in resp.text
assert 'TypeError' in resp.text
resp.form['html_template'] = '{% for x in 1 %}ok{% end %}'
resp = resp.form.submit()
assert 'syntax error' in resp.text
assert 'Invalid block tag' in resp.text
def test_workflow_jump_previous(pub):
user = create_user(pub)
create_environment(pub)
wf = Workflow(name='jump around')
# North
# / \
# West <----> East
# | |
# | autojump
# | |
# \ /
# South
st1 = wf.add_status('North')
st1.id = 'north'
st2 = wf.add_status('West')
st2.id = 'west'
st3 = wf.add_status('East')
st3.id = 'east'
st4 = wf.add_status('Autojump')
st4.id = 'autojump'
st5 = wf.add_status('South')
st5.id = 'south'
button_by_id = {}
def add_jump(label, src, dst_id):
jump = ChoiceWorkflowStatusItem()
jump.id = str(random.random())
jump.label = label
jump.by = ['logged-users']
jump.status = dst_id
src.items.append(jump)
jump.parent = src
if dst_id != '_previous':
jump.set_marker_on_status = True
button_by_id[label] = 'button%s' % jump.id
return jump
add_jump('Go West', st1, st2.id)
add_jump('Go East', st1, st3.id)
add_jump('Go South', st2, st5.id)
add_jump('Go Autojump', st3, st4.id)
add_jump('Go Back', st5, '_previous')
add_jump('Jump West', st3, st2.id)
add_jump('Jump East', st2, st3.id)
jump = JumpWorkflowStatusItem()
jump.id = '_auto-jump'
jump.status = st5.id
st4.items.append(jump)
jump.parent = st4
wf.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.data_class().wipe()
formdef.workflow = wf
formdef.store()
formdata = formdef.data_class()()
formdata.data = {}
formdata.just_created()
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % formdata.id)
# jump around using buttons
resp = resp.form.submit(button_by_id['Go West']).follow() # push (north)
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st2.id
resp = resp.form.submit(button_by_id['Go South']).follow() # push (north, west)
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st5.id
resp = resp.form.submit(button_by_id['Go Back']).follow() # pop (north)
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st2.id
resp = resp.form.submit(button_by_id['Go South']).follow() # push (north, west)
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st5.id
resp = resp.form.submit(button_by_id['Go Back']).follow() # pop (north)
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st2.id
resp = resp.form.submit(button_by_id['Jump East']).follow() # push (north, west)
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st3.id
resp = resp.form.submit(button_by_id['Go Autojump']).follow() # push (north, west, east)
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st5.id
# check markers are displayed in /inspect page
user.is_admin = True
user.store()
resp2 = app.get('/backoffice/management/form-title/%s/inspect' % formdata.id)
assert 'Markers Stack' in resp2.text
assert '<span class="status">East</span>' in resp2.text
assert '<span class="status">West</span>' in resp2.text
assert '<span class="status">North</span>' in resp2.text
assert resp2.text.find('<span class="status">East</span>') < resp2.text.find('<span class="status">West</span>')
assert resp2.text.find('<span class="status">West</span>') < resp2.text.find('<span class="status">North</span>')
resp = resp.form.submit(button_by_id['Go Back']).follow() # pop (north, west)
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st3.id
# and do a last jump using the API
formdata = formdef.data_class().get(formdata.id)
formdata.jump_status('_previous') # pop (north)
assert formdata.status == 'wf-%s' % st2.id
formdata = formdef.data_class().get(formdata.id)
formdata.jump_status('_previous') # pop ()
assert formdata.status == 'wf-%s' % st1.id
def test_workflow_jump_previous_on_submit(pub):
user = create_user(pub)
create_environment(pub)
wf = Workflow(name='blah')
st1 = wf.add_status('North')
st1.id = 'north'
st2 = wf.add_status('South')
st2.id = 'south'
commentable = CommentableWorkflowStatusItem()
commentable.id = '_commentable'
commentable.by = ['_submitter', '_receiver']
commentable.button_label = 'CLICK ME!'
st1.items.append(commentable)
commentable.parent = st1
jump = JumpOnSubmitWorkflowStatusItem()
jump.id = '_jump'
jump.status = st2.id
st1.items.append(jump)
jump.parent = st1
jump.set_marker_on_status = True
back = ChoiceWorkflowStatusItem()
back.id = '_back'
back.label = 'Back'
back.by = ['_receiver']
back.status = '_previous'
st2.items.append(back)
back.parent = st2
wf.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.data_class().wipe()
formdef.workflow = wf
formdef.store()
formdata = formdef.data_class()()
formdata.data = {}
formdata.just_created()
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % formdata.id)
resp.form['comment'] = 'HELLO WORLD'
resp = resp.form.submit('button_commentable')
resp = resp.follow()
assert formdef.data_class().get(formdata.id).status == 'wf-south'
assert formdef.data_class().get(formdata.id).workflow_data['_markers_stack']
resp = resp.form.submit('button_back')
resp = resp.follow()
assert formdef.data_class().get(formdata.id).status == 'wf-north'
assert not formdef.data_class().get(formdata.id).workflow_data['_markers_stack']
def test_workflow_jump_previous_auto(pub):
create_user(pub)
create_environment(pub)
wf = Workflow(name='blah')
st1 = wf.add_status('North')
st1.id = 'north'
st2 = wf.add_status('South')
st2.id = 'south'
jump = JumpWorkflowStatusItem()
jump.id = '_auto-jump'
jump.set_marker_on_status = True
jump.status = st2.id
st1.items.append(jump)
jump.parent = st1
back = ChoiceWorkflowStatusItem()
back.id = '_back'
back.label = 'Back'
back.by = ['_receiver']
back.status = '_previous'
st2.items.append(back)
back.parent = st2
wf.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.data_class().wipe()
formdef.workflow = wf
formdef.store()
formdata = formdef.data_class()()
formdata.data = {}
formdata.just_created()
formdata.store()
formdata.perform_workflow()
assert formdata.status == 'wf-south'
assert formdata.workflow_data['_markers_stack'] == [{'status_id': 'north'}]
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % formdata.id)
resp = resp.form.submit('button_back')
resp = resp.follow()
# jumped and got back to north, then re-jump to south again (auto-jump)
formdata = formdef.data_class().get(formdata.id)
statuses = [evo.status for evo in formdata.evolution]
assert statuses == ['wf-north', 'wf-south', 'wf-north', 'wf-south']
# formdata went through north->south auto-jump again, status and marker are still here
assert formdata.status == 'wf-south'
assert formdata.workflow_data['_markers_stack'] == [{'status_id': 'north'}]
# no marker (workflow inconsistency)
formdata.workflow_data['_markers_stack'] = []
formdata.store()
resp = app.get('/backoffice/management/form-title/%s/' % formdata.id)
resp = resp.form.submit('button_back')
resp = resp.follow()
formdata = formdef.data_class().get(formdata.id)
assert formdata.status == 'wf-south'
assert not formdata.workflow_data['_markers_stack']
# unknown marker (workflow inconsistency)
formdata.workflow_data['_markers_stack'] = [{'status_id': 'unknown_status'}]
formdata.store()
resp = app.get('/backoffice/management/form-title/%s/' % formdata.id)
resp = resp.form.submit('button_back')
resp = resp.follow()
formdata = formdef.data_class().get(formdata.id)
assert formdata.status == 'wf-south'
def test_backoffice_fields(pub):
user = create_user(pub)
create_environment(pub)
wf = Workflow(name='bo fields')
wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf)
wf.backoffice_fields_formdef.fields = [
fields.StringField(id='bo1', label='1st backoffice field',
type='string', varname='backoffice_blah', required=False),
]
st1 = wf.add_status('Status1')
wf.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow_id = wf.id
formdef.store()
formdata = formdef.data_class()()
formdata.data = {}
formdata.just_created()
formdata.store()
app = login(get_app(pub))
resp = app.get(formdata.get_url(backoffice=True))
assert not 'Backoffice Data' in resp.text
assert not '1st backoffice field' in resp.text
formdata.data = {'bo1': 'HELLO WORLD'}
formdata.store()
resp = app.get(formdata.get_url(backoffice=True))
assert 'Backoffice Data' in resp.text
assert '1st backoffice field' in resp.text
assert 'HELLO WORLD' in resp.text
wf.backoffice_fields_formdef.fields = [
fields.StringField(id='bo1', label='1st backoffice field',
type='string', varname='backoffice_blah', required=True),
]
wf.store()
formdata = formdef.data_class()()
formdata.data = {}
formdata.just_created()
formdata.store()
app = login(get_app(pub))
resp = app.get(formdata.get_url(backoffice=True))
assert 'Backoffice Data' in resp.text
assert 'Not set' in resp.text
def test_backoffice_logged_errors(pub):
Workflow.wipe()
workflow = Workflow.get_default_workflow()
workflow.id = '12'
jump = JumpWorkflowStatusItem()
jump.id = '_jump'
jump.status = 'rejected'
jump.condition = {'type': 'python', 'value': '1//0'} # ZeroDivisionError
st1 = workflow.possible_status[0]
st1.items.insert(0, jump)
jump.parent = st1
workflow.store()
FormDef.wipe()
formdef = FormDef()
formdef.id = '34'
formdef.workflow = workflow
formdef.name = 'test'
formdef.confirmation = False
formdef.fields = []
formdef.store()
LoggedError.wipe()
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/forms/%s/' % formdef.id)
assert not 'ZeroDivisionError' in resp.text
resp = app.get('/backoffice/workflows/%s/' % workflow.id)
assert not 'ZeroDivisionError' in resp.text
app = get_app(pub)
resp = app.get('/test/')
resp = resp.form.submit('submit').follow()
resp = resp.form.submit('submit')
assert LoggedError.count() == 1
app = login(get_app(pub))
resp = app.get('/backoffice/forms/%s/' % formdef.id)
assert 'Failed to evaluate condition' in resp.text
assert 'ZeroDivisionError' in resp.text
resp = resp.click('1 error')
resp = app.get('/backoffice/workflows/%s/' % workflow.id)
resp2 = resp.click('1 error')
assert 'Failed to evaluate condition' in resp2.text
assert 'ZeroDivisionError' in resp2.text
resp = resp2.click('Failed to evaluate condition')
assert 'ZeroDivisionError: integer division or modulo by zero' in resp.text
assert 'Python Expression: <code>1//0</code>' in resp.text
assert not 'Acked' in resp.text
resp = resp.click('Ack').follow()
assert 'Acked' in resp.text
assert LoggedError.select()[0].acked is True
resp = resp.click('Delete').follow()
assert LoggedError.count() == 0
pub.cfg.update({'debug': {'error_email': None}})
pub.write_cfg()
app = get_app(pub)
resp = app.get('/test/')
resp = resp.form.submit('submit').follow()
resp = resp.form.submit('submit')
assert LoggedError.count() == 1
app = login(get_app(pub))
resp = app.get('/backoffice/workflows/%s/' % workflow.id)
assert 'Failed to evaluate condition' in resp2.text
assert 'ZeroDivisionError' in resp2.text
resp2 = resp.click('1 error')
resp = resp2.click('Failed to evaluate condition')
assert 'href="http://example.net/backoffice/management/test/' in resp.text
# very long error string (check it creates a viable tech_id)
jump.condition = {'type': 'python', 'value': 'x'*500} # NameError + very long string
workflow.store()
app = get_app(pub)
resp = app.get('/test/')
resp = resp.form.submit('submit').follow()
resp = resp.form.submit('submit')
assert LoggedError.count() == 2
# remove formdef
FormDef.wipe()
resp = resp2.click('Failed to evaluate condition')
assert not 'href="http://example.net/backoffice/management/test/' in resp.text
def test_backoffice_formdata_named_wscall(http_requests, pub):
user = create_user(pub)
create_environment(pub)
NamedWsCall.wipe()
wscall = NamedWsCall()
wscall.name = 'Hello world'
wscall.request = {'url': 'http://remote.example.net/json'}
wscall.store()
assert wscall.slug == 'hello_world'
formdef = FormDef()
formdef.name = 'test'
formdef.backoffice_submission_roles = user.roles[:]
formdef.fields = [
fields.CommentField(id='7', label='X[webservice.hello_world.foo]Y', type='comment'),
]
formdef.store()
formdef.data_class().wipe()
app = login(get_app(pub))
resp = app.get('/backoffice/submission/test/')
assert resp.html.find('div', {'data-field-id': '7'}).text.strip() == 'XbarY'
# check with publisher variable in named webservice call
if not pub.site_options.has_section('variables'):
pub.site_options.add_section('variables')
pub.site_options.set('variables', 'example_url', 'http://remote.example.net/')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
wscall = NamedWsCall()
wscall.name = 'Hello world'
wscall.request = {'url': '[example_url]json'}
wscall.store()
resp = app.get('/backoffice/submission/test/')
assert resp.html.find('div', {'data-field-id': '7'}).text.strip() == 'XbarY'
# django-templated URL
wscall.request = {'url': '{{ example_url }}json'}
wscall.store()
resp = app.get('/backoffice/submission/test/')
assert resp.html.find('div', {'data-field-id': '7'}).text.strip() == 'XbarY'
# webservice call in django template
formdef.fields = [
fields.CommentField(id='7', label='dja-{{ webservice.hello_world.foo}}-ngo', type='comment'),
]
formdef.store()
formdef.data_class().wipe()
resp = app.get('/backoffice/submission/test/')
assert resp.html.find('div', {'data-field-id': '7'}).text.strip() == 'dja-bar-ngo'
def test_backoffice_session_var(pub):
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write('''[options]
query_string_allowed_vars = foo,bar
''')
user = create_user(pub)
create_environment(pub)
formdef = FormDef()
formdef.name = 'test'
formdef.backoffice_submission_roles = user.roles[:]
formdef.fields = [
fields.CommentField(id='7', label='X[session_var_foo]Y', type='comment'),
]
formdef.store()
formdef.data_class().wipe()
app = login(get_app(pub))
resp = app.get('/backoffice/submission/test/?session_var_foo=bar')
assert resp.location.endswith('/backoffice/submission/test/')
resp = resp.follow()
assert resp.html.find('div', {'data-field-id': '7'}).text.strip() == 'XbarY'
# django template
formdef.fields = [
fields.CommentField(id='7', label='d{{ session_var_foo }}o', type='comment'),
]
formdef.store()
formdef.data_class().wipe()
resp = app.get('/backoffice/submission/test/?session_var_foo=jang')
assert resp.location.endswith('/backoffice/submission/test/')
resp = resp.follow()
assert resp.html.find('div', {'data-field-id': '7'}).text.strip() == 'django'
def test_backoffice_display_message(pub):
user = create_user(pub)
create_environment(pub)
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
display1 = DisplayMessageWorkflowStatusItem()
display1.message = 'message-to-all'
display1.to = []
st1.items.append(display1)
display1.parent = st1
display2 = DisplayMessageWorkflowStatusItem()
display2.message = 'message-to-submitter'
display2.to = ['_submitter']
st1.items.append(display2)
display2.parent = st1
display3 = DisplayMessageWorkflowStatusItem()
display3.message = 'message-to-receiver'
display3.to = [user.roles[0]]
st1.items.append(display3)
display3.parent = st1
workflow.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow = workflow
formdef.store()
formdata = formdef.data_class().select()[0]
formdata.status = 'wf-st1'
formdata.store()
app = login(get_app(pub))
resp = app.get(formdata.get_url(backoffice=True))
assert 'message-to-all' in resp.text
assert 'message-to-submitter' not in resp.text
assert 'message-to-receiver' in resp.text
# display first message at the bottom of the page
display1.position = 'bottom'
workflow.store()
resp = app.get(formdata.get_url(backoffice=True))
assert resp.text.index('message-to-all') > resp.text.index('message-to-receiver')
# display first message on top of actions
display1.position = 'actions'
workflow.store()
resp = app.get(formdata.get_url(backoffice=True))
assert not 'message-to-all' in resp.text # no actions no message
again = ChoiceWorkflowStatusItem()
again.id = '_again'
again.label = 'Again'
again.by = ['_receiver']
again.status = st1.id
st1.items.append(again)
again.parent = st1
workflow.store()
resp = app.get(formdata.get_url(backoffice=True))
assert 'message-to-all' in resp.text
assert resp.text.index('message-to-all') > resp.text.index('message-to-receiver')
def test_backoffice_forms_condition_on_button(pub):
create_superuser(pub)
create_environment(pub, set_receiver=True)
workflow = Workflow.get_default_workflow()
workflow.id = '2'
workflow.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.workflow = workflow
formdef.store()
# move some forms from new to accepted
for i, formdata in enumerate(formdef.data_class().select(lambda x: x.status == 'wf-new')):
if i % 2:
formdata.status = 'wf-accepted'
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/')
resp = resp.click('Management', index=0)
resp = resp.follow()
assert '17 open on 50' in resp.text
formdata = [x for x in formdef.data_class().select() if x.status == 'wf-new'][0]
resp = app.get(formdata.get_url(backoffice=True))
assert 'button_commentable' in resp.text
assert 'button_accept' in resp.text
assert 'button_reject' in resp.text
# commentable
workflow.possible_status[1].items[0].condition = {'type': 'python', 'value': 'False'}
# reject
workflow.possible_status[1].items[2].condition = {'type': 'python', 'value': 'False'}
workflow.store()
resp = app.get(formdata.get_url(backoffice=True))
assert 'button_commentable' not in resp.text
assert 'button_accept' in resp.text
assert 'button_reject' not in resp.text
formdef = FormDef.get_by_urlname('form-title')
assert formdef.data_class().get(formdata.id).actions_roles == set(['1'])
# accept
workflow.possible_status[1].items[1].condition = {'type': 'python', 'value': 'False'}
workflow.store()
resp = app.get(formdata.get_url(backoffice=True))
assert 'button_commentable' not in resp.text
assert 'button_accept' not in resp.text
assert 'button_reject' not in resp.text
formdef = FormDef.get_by_urlname('form-title')
assert formdef.data_class().get(formdata.id).actions_roles == set()
app = login(get_app(pub))
resp = app.get('/backoffice/')
resp = resp.click('Management', index=0)
resp = resp.follow()
assert '8 open on 50' in resp.text # only the accepted ones
def test_workflow_inspect_page(pub):
admin = create_user(pub, is_admin=True)
create_environment(pub, set_receiver=True)
workflow = Workflow.get_default_workflow()
workflow.id = '2'
st1 = workflow.add_status('Status1')
jump = JumpWorkflowStatusItem()
jump.id = '_jump'
jump.timeout = '=86400'
jump.status = 'finished'
st1.items.append(jump)
jump.parent = st1
workflow.store()
app = login(get_app(pub))
resp = app.get('/backoffice/workflows/%s/inspect' % workflow.id)
assert '=86400' in resp.text
jump.timeout = '82800'
workflow.store()
resp = app.get('/backoffice/workflows/%s/inspect' % workflow.id)
assert '23 hours' in resp.text
target_formdef = FormDef()
target_formdef.name = 'target form'
target_formdef.workflow_roles = {'_receiver': 1}
target_formdef.backoffice_submission_roles = admin.roles[:]
target_formdef.fields = [
fields.StringField(id='0', label='string', varname='foo_string'),
fields.FileField(id='1', label='file', type='file', varname='foo_file'),
]
st2 = workflow.add_status('Status2')
target_formdef.store()
create_formdata = CreateFormdataWorkflowStatusItem()
create_formdata.id = '_create_formdata'
create_formdata.varname = 'resubmitted'
create_formdata.draft = True
create_formdata.formdef_slug = target_formdef.url_name
create_formdata.keep_user = True
create_formdata.backoffice_submission = True
create_formdata.mappings = [
Mapping(field_id='0', expression='=form_var_toto_string'),
Mapping(field_id='1', expression='=form_var_toto_file_raw'),
Mapping(field_id='2', expression='=form_var_foobar_raw'),
]
create_formdata.parent = st2
st2.items.append(create_formdata)
workflow.store()
resp = app.get('/backoffice/workflows/%s/inspect' % workflow.id)
assert (
'<ul class="mappings"><li>string → =form_var_toto_string</li>'
'<li>file → =form_var_toto_file_raw</li>'
'<li>#2 → =form_var_foobar_raw</li></ul>') in resp.text
def test_workflow_comment_required(pub):
user = create_user(pub)
create_environment(pub)
wf = Workflow(name='blah')
st1 = wf.add_status('Comment')
st1.id = 'comment'
commentable = CommentableWorkflowStatusItem()
commentable.id = '_commentable'
commentable.by = ['_submitter', '_receiver']
st1.items.append(commentable)
commentable.parent = st1
wf.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.data_class().wipe()
formdef.workflow = wf
formdef.store()
formdata = formdef.data_class()()
formdata.data = {}
formdata.just_created()
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % formdata.id)
assert 'widget-required' not in resp.text
resp.form['comment'] = 'HELLO WORLD 1'
resp = resp.form.submit('button_commentable')
resp = resp.follow()
assert 'HELLO WORLD 1' in resp.text
assert 'widget-required' not in resp.text
resp.form['comment'] = None
resp = resp.form.submit('button_commentable')
resp = resp.follow()
commentable.required = True
wf.store()
resp = app.get('/backoffice/management/form-title/%s/' % formdata.id)
assert 'widget-required' in resp.text
resp.form['comment'] = ' ' # spaces == empty
resp = resp.form.submit('button_commentable')
assert 'widget-with-error' in resp.text
resp.form['comment'] = 'HELLO WORLD 2'
resp = resp.form.submit('button_commentable')
resp = resp.follow()
assert 'widget-with-error' not in resp.text
assert 'HELLO WORLD 2' in resp.text
def test_carddata_management(pub, studio):
CardDef.wipe()
user = create_user(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/')
assert not 'Cards' in resp.text
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = [
fields.StringField(id='1', label='Test', type='string', varname='foo'),
fields.StringField(id='2', label='Condi', type='string', varname='bar',
required=True, condition={'type': 'django', 'value': 'form_var_foo == "ok"'}),
]
carddef.store()
carddef.data_class().wipe()
resp = app.get('/backoffice/')
assert not 'Cards' in resp.text
carddef.backoffice_submission_roles = user.roles
carddef.store()
resp = app.get('/backoffice/')
assert 'Cards' in resp.text
carddef.backoffice_submission_roles = None
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.store()
resp = app.get('/backoffice/')
assert 'Cards' in resp.text
resp = app.get('/backoffice/data/')
resp = resp.click('foo')
assert 'Add' not in resp.text
carddef.backoffice_submission_roles = user.roles
carddef.store()
resp = app.get('/backoffice/data/')
resp = resp.click('foo')
assert resp.text.count('<tr') == 1 # header
assert 'Add' in resp.text
resp = resp.click('Add')
resp.form['f1'] = 'blah'
live_url = resp.html.find('form').attrs['data-live-url']
assert '/backoffice/data/foo/add/live' in live_url
live_resp = app.post(live_url, params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert not live_resp.json['result']['2']['visible']
resp.form['f1'] = 'ok'
live_resp = app.post(live_url, params=resp.form.submit_fields())
assert live_resp.json['result']['1']['visible']
assert live_resp.json['result']['2']['visible']
resp.form['f2'] = 'blah'
resp = resp.form.submit('submit')
assert resp.location.endswith('/backoffice/data/foo/1/')
resp = resp.follow()
assert 'Edit Card' in resp.text
assert 'Delete Card' in resp.text
carddata = carddef.data_class().select()[0]
assert carddata.data == {'1': 'ok', '2': 'blah'}
assert carddata.user_id is None
assert carddata.submission_context == {'agent_id': user.id}
assert carddata.evolution[0].who == str(user.id)
assert 'Original Submitter' not in resp.text
resp = app.get('/backoffice/data/')
resp = resp.click('foo')
assert resp.text.count('<tr') == 2 # header + row of data
def test_studio_card_item_link(pub, studio):
user = create_user(pub)
CardDef.wipe()
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = [
fields.StringField(id='1', label='Test', type='string', varname='foo'),
]
carddef.backoffice_submission_roles = user.roles
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.digest_template = 'card {{form_var_foo}}'
carddef.store()
carddef.data_class().wipe()
card = carddef.data_class()()
card.data = {'1': 'plop'}
card.store()
carddef2 = CardDef()
carddef2.name = 'bar'
carddef2.fields = [
fields.ItemField(id='1', label='Test', type='item',
data_source={'type': 'carddef:foo', 'value': ''}),
]
carddef2.backoffice_submission_roles = user.roles
carddef2.workflow_roles = {'_editor': user.roles[0]}
carddef2.store()
carddef2.data_class().wipe()
app = login(get_app(pub))
resp = app.get('/backoffice/data/')
resp = resp.click('bar')
resp = resp.click('Add')
resp.form['f1'] = card.id
resp = resp.form.submit('submit')
assert resp.location.endswith('/backoffice/data/bar/1/')
resp = resp.follow()
resp = resp.click('card plop')
assert '<div class="value">plop</div>' in resp
# link to a unknown carddef
carddef2.fields = [
fields.ItemField(id='1', label='Test', type='item',
data_source={'type': 'carddef:unknown', 'value': ''}),
]
carddef2.store()
app = login(get_app(pub))
resp = app.get('/backoffice/data/')
resp = resp.click('bar')
resp = resp.click('Add') # no error
# look without access rights
carddef.backoffice_submission_roles = None
carddef.workflow_roles = {'_editor': None}
carddef.store()
resp = app.get('/backoffice/data/bar/1/')
with pytest.raises(IndexError):
resp.click('card plop')
def test_backoffice_cards_import_data_from_csv(pub, studio):
user = create_user(pub)
data_source = {
'type': 'formula',
'value': repr([
{'id': '1', 'text': 'un', 'more': 'foo'},
{'id': '2', 'text': 'deux', 'more': 'bar'}])
}
CardDef.wipe()
carddef = CardDef()
carddef.name = 'test'
carddef.fields = [
fields.TableField(id='0', label='Table'),
fields.MapField(id='1', label='Map'),
fields.StringField(id='2', label='Test'),
fields.BoolField(id='3', label='Boolean'),
fields.ItemField(id='4', label='List',
items=['item1', 'item2']),
fields.DateField(id='5', label='Date'),
fields.TitleField(id='6', label='Title', type='title'),
fields.FileField(id='7', label='File'),
fields.EmailField(id='8', label='Email'),
fields.TextField(id='9', label='Long'),
fields.ItemField(id='10', label='List2', data_source=data_source),
]
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.store()
carddef.data_class().wipe()
app = login(get_app(pub))
resp = app.get(carddef.get_url())
assert 'Import data from a CSV file' not in resp.text
resp = app.get(carddef.get_url() + 'import-csv', status=403)
carddef.backoffice_submission_roles = user.roles
carddef.store()
resp = app.get(carddef.get_url())
resp = resp.click('Import data from a CSV file')
assert 'Table, File are required but cannot be filled from CSV.' in resp
assert 'Download sample file for this card' not in resp
carddef.fields[0].required = False
carddef.fields[7].required = False
carddef.store()
resp = app.get(carddef.get_url())
resp = resp.click('Import data from a CSV file')
sample_resp = resp.click('Download sample file for this card')
today = datetime.date.today()
assert sample_resp.text == (
"Table,Map,Test,Boolean,List,Date,File,Email,Long,List2\r\n"
"will be ignored - type Table not supported,"
"%s,"
"value,"
"Yes,"
"value,"
"%s,"
"will be ignored - type File Upload not supported,"
"foo@example.com,"
"value,"
"value\r\n" % (pub.get_default_position(), today))
resp.forms[0]['file'] = Upload('test.csv', b'\0', 'text/csv')
resp = resp.forms[0].submit()
assert 'Invalid file format.' in resp
resp.forms[0]['file'] = Upload('test.csv', b'', 'text/csv')
resp = resp.forms[0].submit()
assert 'Invalid CSV file.' in resp
resp.forms[0]['file'] = Upload('test.csv',
b'Test,List,Date\ndata1,item1,invalid',
'text/csv')
resp = resp.forms[0].submit()
assert 'CSV file contains less columns than card fields.' in resp.text
data = [b'Table,Map,Test,Boolean,List,Date,File,Email,Long,List2']
for i in range(1, 150):
data.append(b'table,48.81;2.37,data%d ,%s,item%d,2020-01-%02d,filename-%d,test@localhost,plop,1' % (
i, str(bool(i % 2)).encode('utf-8'), i, i % 31 + 1, i))
resp.forms[0]['file'] = Upload('test.csv', b'\n'.join(data),
'text/csv')
resp = resp.forms[0].submit().follow()
assert 'Importing data into cards' in resp
assert 'Column File will be ignored: type File Upload not supported.' in resp
assert carddef.data_class().count() == 149
card1, card2 = carddef.data_class().select(order_by='id')[:2]
assert card1.data['1'] == '48.81;2.37'
assert card1.data['2'] == 'data1'
assert card1.data['3'] is True
assert card1.data['5'].tm_mday == 2
assert card1.data['10'] == '1'
assert card1.data['10_display'] == 'un'
assert card1.data['10_structured'] == {'id': '1', 'text': 'un', 'more': 'foo'}
assert card2.data['2'] == 'data2'
assert card2.data['3'] is False
assert card2.data['5'].tm_mday == 3
def test_backoffice_cards_import_data_csv_invalid_columns(pub):
user = create_user(pub)
CardDef.wipe()
carddef = CardDef()
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.backoffice_submission_roles = user.roles
carddef.name = 'test'
carddef.fields = [
fields.StringField(id='1', label='String1'),
fields.StringField(id='2', label='String2'),
fields.TextField(id='3', label='Text'),
]
carddef.store()
app = login(get_app(pub))
resp = app.get(carddef.get_url())
resp = resp.click('Import data from a CSV file')
csv_data = '''String1,String2,Text
1,2,3
4,5,6
7,
8,9,10,11
12,13,14
'''
resp.forms[0]['file'] = Upload('test.csv', csv_data.encode('utf-8'), 'text/csv')
resp = resp.forms[0].submit()
assert 'CSV file contains lines with wrong number of columns.' in resp.text
assert '(line numbers 4, 5, 7)' in resp.text
csv_data += '\n' * 10
resp.forms[0]['file'] = Upload('test.csv', csv_data.encode('utf-8'), 'text/csv')
resp = resp.forms[0].submit()
assert 'CSV file contains lines with wrong number of columns.' in resp.text
assert '(line numbers 4, 5, 7, 8, 9 and more)' in resp.text
def test_backoffice_cards_wscall_failure_display(http_requests, pub, studio):
LoggedError.wipe()
user = create_user(pub)
Workflow.wipe()
workflow = Workflow(name='wscall')
workflow.roles = {
'_viewer': 'Viewer',
'_editor': 'Editor',
}
st1 = workflow.add_status('Recorded', 'recorded')
wscall = WebserviceCallStatusItem()
wscall.id = '_wscall'
wscall.varname = 'xxx'
wscall.url = 'http://remote.example.net/xml'
wscall.action_on_bad_data = ':stop'
wscall.record_errors = True
st1.items.append(wscall)
wscall.parent = st1
again = ChoiceWorkflowStatusItem()
again.id = '_again'
again.label = 'Again'
again.by = ['_editor']
again.status = st1.id
st1.items.append(again)
again.parent = st1
workflow.store()
CardDef.wipe()
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = [
fields.StringField(id='1', label='Test', type='string', varname='foo'),
]
carddef.backoffice_submission_roles = user.roles
carddef.workflow_id = workflow.id
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.digest_template = 'card {{form_var_foo}}'
carddef.store()
carddef.data_class().wipe()
carddata = carddef.data_class()()
carddata.data = {'1': 'plop'}
carddata.just_created()
carddata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/data/foo/%s/' % carddata.id)
assert 'Again' in resp.text
resp = resp.forms[0].submit('button_again')
resp = resp.follow()
assert 'Error during webservice call' in resp.text
assert LoggedError.count() == 1
assert LoggedError.select()[0].get_formdata().data == {'1': 'plop'}
def test_lazy_eval_with_conditional_workflow_form(pub):
role = Role(name='foobar')
role.store()
user = create_user(pub)
app = login(get_app(pub))
FormDef.wipe()
wf = Workflow(name='lazy backoffice form')
wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf)
wf.backoffice_fields_formdef.fields = [
fields.StringField(id='bo1', label='Foo Bar', varname='foo_bar'),
]
st1 = wf.add_status('New', 'new')
st2 = wf.add_status('Choose', 'choice')
st3 = wf.add_status('Done', 'done')
# first status with a workflow form, with a live conditional field.
display_form = FormWorkflowStatusItem()
display_form.id = '_display_form'
display_form.by = [user.roles[0]]
display_form.varname = 'local'
display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
display_form.formdef.fields = [
fields.StringField(id='1', label='Test', varname='str', type='string', required=True),
fields.StringField(id='2', label='Test 2', varname='str2', type='string',
condition={'type': 'django', 'value': 'local_var_str'})
]
display_form.parent = st1
submit_choice = JumpOnSubmitWorkflowStatusItem()
submit_choice.parent = st1
submit_choice.status = st2.id
st1.items.append(display_form)
st1.items.append(submit_choice)
# jump to a second status, that set's a backoffice field data
setbo = SetBackofficeFieldsWorkflowStatusItem()
setbo.parent = st2
setbo.fields = [{'field_id': 'bo1', 'value': 'go'}]
# and jump to the third status if the evoluation succeeds
jump = JumpWorkflowStatusItem()
jump.condition = {'type': 'django', 'value': "form_var_foo_bar == 'go'"}
jump.status = st3.id
jump.parent = st2
st2.items.append(setbo)
st2.items.append(jump)
wf.store()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.StringField(id='0', label='Foo', varname='foo')
]
formdef.workflow_roles = {'_receiver': role.id}
formdef.workflow_id = wf.id
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.data = {
'0': 'test'
}
formdata.store()
resp = app.get(formdata.get_url(backoffice=True))
resp.forms[0]['f1'] = 'a'
resp.forms[0]['f2'] = 'b'
resp = resp.forms[0].submit()
assert formdata.select()[0].status == 'wf-done'
context = pub.substitutions.get_context_variables(mode='lazy')
assert context['form_var_foo_bar'] == 'go'
@pytest.fixture(params=[{'attach_to_history': True}, {}])
def create_formdata(request, pub):
admin = create_user(pub, is_admin=True)
FormDef.wipe()
source_formdef = FormDef()
source_formdef.name = 'source form'
source_formdef.workflow_roles = {'_receiver': 1}
source_formdef.fields = [
fields.StringField(id='0', label='string', varname='toto_string'),
fields.FileField(id='1', label='file', type='file', varname='toto_file'),
]
source_formdef.store()
target_formdef = FormDef()
target_formdef.name = 'target form'
target_formdef.workflow_roles = {'_receiver': 1}
target_formdef.backoffice_submission_roles = admin.roles[:]
target_formdef.fields = [
fields.StringField(id='0', label='string', varname='foo_string'),
fields.FileField(id='1', label='file', type='file', varname='foo_file'),
]
target_formdef.store()
wf = Workflow(name='create-formdata')
st1 = wf.add_status('New')
st2 = wf.add_status('Resubmit')
jump = ChoiceWorkflowStatusItem()
jump.id = '_resubmit'
jump.label = 'Resubmit'
jump.by = ['_receiver']
jump.status = st2.id
jump.parent = st1
st1.items.append(jump)
create_formdata = CreateFormdataWorkflowStatusItem()
create_formdata.id = '_create_formdata'
create_formdata.varname = 'resubmitted'
create_formdata.draft = True
create_formdata.formdef_slug = target_formdef.url_name
create_formdata.keep_user = True
create_formdata.backoffice_submission = True
create_formdata.attach_to_history = request.param.get('attach_to_history', False)
create_formdata.mappings = [
Mapping(field_id='0', expression='=form_var_toto_string'),
Mapping(field_id='1', expression='=form_var_toto_file_raw'),
]
create_formdata.parent = st2
st2.items.append(create_formdata)
redirect = RedirectToUrlWorkflowStatusItem()
redirect.id = '_redirect'
redirect.url = '{{ form_links_resubmitted.form_backoffice_url }}'
redirect.parent = st2
st2.items.append(redirect)
jump = JumpOnSubmitWorkflowStatusItem()
jump.id = '_jump'
jump.status = st1.id
jump.parent = st2
st2.items.append(jump)
wf.store()
source_formdef.workflow_id = wf.id
source_formdef.store()
source_formdef.data_class().wipe()
target_formdef.data_class().wipe()
LoggedError.wipe()
return locals()
def test_backoffice_create_formdata_backoffice_submission(create_formdata):
# create submitting user
user = create_formdata['pub'].user_class()
user.name = 'Jean Darmette'
user.email = 'jean.darmette@triffouilis.fr'
user.store()
# create source formdata
formdata = create_formdata['source_formdef'].data_class()()
upload = PicklableUpload('/foo/bar', content_type='text/plain')
upload.receive([b'hello world'])
formdata.data = {
'0': 'coucou',
'1': upload,
}
formdata.user = user
formdata.just_created()
formdata.store()
formdata.perform_workflow()
# agent login and go to backoffice management pages
app = get_app(create_formdata['pub'])
app = login(app)
resp = app.get(create_formdata['source_formdef'].get_url(backoffice=True))
# click on first available formdata
resp = resp.click(href='%s/' % formdata.id)
target_data_class = create_formdata['target_formdef'].data_class()
assert target_data_class.count() == 0
# resubmit it through backoffice submission
resp = resp.form.submit(name='button_resubmit')
assert LoggedError.count() == 0
assert target_data_class.count() == 1
target_formdata = target_data_class.select()[0]
assert target_formdata.submission_context == {
'agent_id': str(create_formdata['admin'].id),
'orig_object_type': 'formdef',
'orig_formdata_id': '1',
'orig_formdef_id': '1'
}
assert target_formdata.user.id == user.id
assert target_formdata.status == 'draft'
assert resp.location == 'http://example.net/backoffice/management/target-form/%s/' % target_formdata.id
resp = resp.follow()
assert resp.location == 'http://example.net/backoffice/submission/target-form/%s/' % target_formdata.id
resp = resp.follow()
# second redirect with magic-token
resp = resp.follow()
resp = resp.form.submit(name='submit') # -> validation
resp = resp.form.submit(name='submit') # -> submission
target_formdata = target_data_class.get(id=target_formdata.id)
assert target_formdata.user.id == user.id
assert target_formdata.status == 'wf-new'
resp = resp.follow()
pq = resp.pyquery.remove_namespaces()
assert pq('.field-type-string .value').text() == 'coucou'
assert pq('.field-type-file .value').text() == 'bar'
def test_linked_forms_variables(create_formdata):
# create source formdata
formdata = create_formdata['source_formdef'].data_class()()
upload = PicklableUpload('/foo/bar', content_type='text/plain')
upload.receive([b'hello world'])
formdata.data = {
'0': 'coucou',
'1': upload,
}
formdata.just_created()
formdata.store()
formdata.perform_workflow()
formdata.store()
formdata.jump_status('2')
formdata.perform_workflow()
formdata.store()
get_publisher().substitutions.reset()
get_publisher().substitutions.feed(formdata)
substvars = get_publisher().substitutions.get_context_variables(mode='lazy')
assert str(substvars['form_links_resubmitted_form_var_foo_string']) == 'coucou'
assert 'form_links_resubmitted_form_var_foo_string' in substvars.get_flat_keys()
source_formdata = create_formdata['source_formdef'].data_class().select()[0]
app = get_app(create_formdata['pub'])
app = login(app)
resp = app.get(source_formdata.get_url(backoffice=True) + 'inspect')
assert 'form_links_resubmitted_form_var_foo_string' in resp
# delete target formdata
create_formdata['target_formdef'].data_class().wipe()
resp = app.get(source_formdata.get_url(backoffice=True) + 'inspect')
resp = app.get(source_formdata.get_url(backoffice=True) + 'inspect')
assert 'form_links_resubmitted_form_var_foo_string' not in resp
def test_backoffice_create_formdata_map_fields_by_varname(create_formdata):
create_formdata['create_formdata'].map_fields_by_varname = True
create_formdata['create_formdata'].mappings = []
create_formdata['wf'].store()
create_formdata['source_formdef'].fields = [
fields.StringField(id='0', label='string', varname='string0'),
fields.FileField(id='1', label='file', type='file', varname='file1'),
fields.StringField(id='2', label='string', varname='string2', required=False),
fields.FileField(id='3', label='file', type='file', varname='file3', required=False),
]
create_formdata['source_formdef'].store()
create_formdata['target_formdef'].fields = [
fields.StringField(id='0', label='string', varname='string0'),
fields.FileField(id='1', label='file', type='file', varname='file1'),
fields.StringField(id='2', label='string', varname='string2', required=False),
fields.FileField(id='3', label='file', type='file', varname='file3', required=False),
]
create_formdata['target_formdef'].store()
# create submitting user
user = create_formdata['pub'].user_class()
user.name = 'Jean Darmette'
user.email = 'jean.darmette@triffouilis.fr'
user.store()
# create source formdata
formdata = create_formdata['source_formdef'].data_class()()
create_formdata['formdata'] = formdata
upload = PicklableUpload('/foo/bar', content_type='text/plain')
upload.receive([b'hello world'])
formdata.data = {
'0': 'coucou',
'1': upload,
}
formdata.user = user
formdata.just_created()
formdata.store()
formdata.perform_workflow()
# agent login and go to backoffice management pages
app = get_app(create_formdata['pub'])
app = login(app)
resp = app.get(create_formdata['source_formdef'].get_url(backoffice=True))
# click on first available formdata
resp = resp.click(href='%s/' % formdata.id)
target_data_class = create_formdata['target_formdef'].data_class()
assert target_data_class.count() == 0
# resubmit it through backoffice submission
resp = resp.form.submit(name='button_resubmit')
assert LoggedError.count() == 0
assert target_data_class.count() == 1
target_formdata = target_data_class.select()[0]
assert target_formdata.submission_context == {
'agent_id': str(create_formdata['admin'].id),
'orig_object_type': 'formdef',
'orig_formdata_id': '1',
'orig_formdef_id': '1'
}
assert target_formdata.user.id == user.id
assert target_formdata.status == 'draft'
assert resp.location == 'http://example.net/backoffice/management/target-form/%s/' % target_formdata.id
resp = resp.follow()
assert resp.location == 'http://example.net/backoffice/submission/target-form/%s/' % target_formdata.id
resp = resp.follow()
# second redirect with magic-token
resp = resp.follow()
resp = resp.form.submit(name='submit') # -> validation
resp = resp.form.submit(name='submit') # -> submission
target_formdata = target_data_class.get(id=target_formdata.id)
assert target_formdata.user.id == user.id
assert target_formdata.status == 'wf-new'
resp = resp.follow()
pq = resp.pyquery.remove_namespaces()
assert pq('.field-type-string .value').text() == 'coucou'
assert pq('.field-type-file .value').text() == 'bar'
resp = app.get(create_formdata['formdata'].get_url(backoffice=True))
pq = resp.pyquery.remove_namespaces()
assert pq('.field-type-string .value').text() == 'coucou'
if create_formdata['create_formdata'].attach_to_history:
assert pq('.wf-links')
else:
assert not pq('.wf-links')
def test_backoffice_create_carddata_from_formdata(pub, studio):
CardDef.wipe()
FormDef.wipe()
user = create_user(pub, is_admin=True)
user.name = 'Foo Bar'
user.email = 'foo@example.com'
user.store()
carddef = CardDef()
carddef.name = 'My card'
carddef.fields = [
fields.StringField(id='1', label='string'),
fields.ItemField(id='2', label='List', items=['item1', 'item2']),
fields.DateField(id='3', label='Date')
]
carddef.store()
wf = Workflow(name='create-carddata')
st1 = wf.add_status('New')
st2 = wf.add_status('Create card')
jump = ChoiceWorkflowStatusItem()
jump.id = '_createcard'
jump.label = 'Create card'
jump.by = ['_receiver']
jump.status = st2.id
jump.parent = st1
st1.items.append(jump)
create_card = CreateCarddataWorkflowStatusItem()
create_card.label = 'Create Card Data'
create_card.varname = 'mycard'
create_card.id = '_create'
create_card.formdef_slug = carddef.url_name
create_card.mappings = [
Mapping(field_id='1', expression='Simple String'),
Mapping(field_id='2', expression='{{ form_var_list_raw }}'),
Mapping(field_id='3', expression='{{ form_var_date }}'),
]
create_card.parent = st2
st2.items.append(create_card)
display_message = DisplayMessageWorkflowStatusItem()
display_message.message = 'Card nr. {{ form_links_mycard_form_number }} created'
display_message.parent = st2
st2.items.append(display_message)
wf.store()
formdef = FormDef()
formdef.name = 'Source form'
formdef.workflow_roles = {'_receiver': 1}
formdef.fields = [
fields.ItemField(id='1', label='List', items=['item1', 'item2'],
varname='list'),
fields.DateField(id='2', label='Date', varname='date')
]
formdef.workflow_id = wf.id
formdef.store()
formdata = formdef.data_class()()
today = time.strptime('2020-01-01', '%Y-%m-%d')
formdata.data = {
'1': 'item2',
'2': today
}
formdata.user = user
formdata.just_created()
formdata.store()
formdata.perform_workflow()
app = login(get_app(pub))
resp = app.get(formdata.get_url(backoffice=True))
resp = resp.form.submit(name='button_createcard').follow()
assert 'Card nr. 1-1 created' in resp
# visit inspect page
resp = app.get(formdata.get_url(backoffice=True) + 'inspect')
assert "variables from parent's request" in resp
def test_backoffice_after_submit_location(pub):
create_superuser(pub)
create_environment(pub)
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
commentable = CommentableWorkflowStatusItem()
commentable.id = '_commentable'
commentable.by = [logged_users_role().id]
commentable.required = True
st1.items.append(commentable)
commentable.parent = st1
workflow.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.store()
formdef.workflow_id = workflow.id
formdef.store()
for formdata in formdef.data_class().select():
formdata.status = 'wf-%s' % st1.id
formdata.store()
app = login(get_app(pub))
resp = app.get(formdata.get_url(backoffice=True))
resp.form['comment'] = 'plop'
resp = resp.form.submit('submit')
assert resp.location == 'http://example.net/backoffice/management/form-title/%s/#action-zone' % formdata.id
resp = resp.follow()
display = DisplayMessageWorkflowStatusItem()
display.message = 'message-to-all'
display.to = []
st1.items.append(display)
display.parent = st1
workflow.store()
resp.form['comment'] = 'plop'
resp = resp.form.submit('submit')
assert resp.location == 'http://example.net/backoffice/management/form-title/%s/#' % formdata.id
def test_backoffice_custom_view(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert resp.text.count('<span>User Label</span>') == 1
assert resp.text.count('<tr') == 18
# columns
resp.forms['listing-settings']['user-label'].checked = False
resp = resp.forms['listing-settings'].submit()
# filters
resp.forms['listing-settings']['filter-2'].checked = True
resp = resp.forms['listing-settings'].submit()
resp.forms['listing-settings']['filter-2-value'] = 'baz'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<span>User Label</span>') == 0
assert resp.text.count('<tr') == 9
resp.forms['save-custom-view']['title'] = 'custom test view'
resp = resp.forms['save-custom-view'].submit()
assert resp.location.endswith('/user-custom-test-view/')
resp = resp.follow()
assert resp.text.count('<span>User Label</span>') == 0
assert resp.text.count('<tr') == 9
resp.forms['listing-settings']['filter-2-value'] = 'foo'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<tr') == 6
assert resp.forms['save-custom-view']['update'].checked is True
resp = resp.forms['save-custom-view'].submit()
assert resp.location.endswith('/user-custom-test-view/')
resp = resp.follow()
assert resp.text.count('<tr') == 6
resp = app.get('/backoffice/management/other-form/')
assert 'custom test view' not in resp
# check it's not possible to create a view without any columns
for field_key in resp.forms['listing-settings'].fields:
if not field_key:
continue
if field_key.startswith('filter'):
continue
if resp.forms['listing-settings'][field_key].attrs.get('type') != 'checkbox':
continue
resp.forms['listing-settings'][field_key].checked = False
resp = resp.forms['listing-settings'].submit()
resp.forms['save-custom-view']['title'] = 'custom test view'
resp = resp.forms['save-custom-view'].submit().follow()
assert 'Views must have at least one column.' in resp.text
def test_backoffice_custom_view_delete(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
# columns
resp.forms['listing-settings']['user-label'].checked = False
resp = resp.forms['listing-settings'].submit()
resp.forms['save-custom-view']['title'] = 'custom test view'
resp = resp.forms['save-custom-view'].submit()
assert resp.location.endswith('/user-custom-test-view/')
resp = resp.follow()
resp = resp.click('Delete')
resp = resp.form.submit()
assert resp.location.endswith('/management/form-title/')
resp = resp.follow()
assert 'custom test view' not in resp.text
def test_backoffice_custom_map_view(pub):
test_backoffice_custom_view(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.geolocations = {'base': 'Geolocafoobar'}
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('custom test view')
assert resp.text.count('<span>User Label</span>') == 0
assert resp.text.count('<tr') == 6
resp = resp.click('Plot on a Map')
assert resp.forms['listing-settings']['filter-2-value'].value == 'foo'
def test_backoffice_custom_view_reserved_slug(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp.forms['listing-settings']['user-label'].checked = False
resp = resp.forms['listing-settings'].submit()
resp.forms['save-custom-view']['title'] = 'user custom test view'
resp = resp.forms['save-custom-view'].submit()
# check slug not created with "user" as prefix
assert resp.location.endswith('/user-userx-custom-test-view/')
resp = resp.follow()
def test_backoffice_custom_view_visibility(pub):
create_environment(pub)
create_superuser(pub)
formdef = FormDef.get_by_urlname('form-title')
agent = pub.user_class(name='agent')
agent.roles = [formdef.workflow_roles['_receiver']]
agent.store()
account = PasswordAccount(id='agent')
account.set_password('agent')
account.user_id = agent.id
account.store()
app = login(get_app(pub), username='agent', password='agent')
resp = app.get('/backoffice/management/form-title/')
# columns
resp.forms['listing-settings']['user-label'].checked = False
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<span>User Label</span>') == 0
resp.forms['save-custom-view']['title'] = 'custom test view'
assert 'visibility' not in resp.forms['save-custom-view'].fields
resp = resp.forms['save-custom-view'].submit()
assert resp.location.endswith('/user-custom-test-view/')
resp = resp.follow()
assert resp.text.count('<span>User Label</span>') == 0
# second agent
agent2 = pub.user_class(name='agent2')
agent2.roles = [formdef.workflow_roles['_receiver']]
agent2.store()
account = PasswordAccount(id='agent2')
account.set_password('agent2')
account.user_id = agent2.id
account.store()
app = login(get_app(pub), username='agent2', password='agent2')
resp = app.get('/backoffice/management/form-title/')
assert 'custom test view' not in resp
# shared custom view
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp = resp.forms['listing-settings'].submit()
resp.forms['save-custom-view']['title'] = 'shared view'
resp.forms['save-custom-view']['visibility'] = 'any'
resp = resp.forms['save-custom-view'].submit()
app = login(get_app(pub), username='agent2', password='agent2')
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('shared view')
# don't allow a second "any" view with same slug
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp = resp.forms['listing-settings'].submit()
resp.forms['save-custom-view']['title'] = 'shared view'
resp.forms['save-custom-view']['visibility'] = 'any'
resp = resp.forms['save-custom-view'].submit()
assert set([(x.slug, x.visibility) for x in get_publisher().custom_view_class.select()]) == set(
[('custom-test-view', 'owner'), ('shared-view', 'any'), ('shared-view-2', 'any')])
def test_backoffice_missing_custom_view(pub):
create_superuser(pub)
create_environment(pub, set_receiver=False)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/user-plop/')
assert resp.location == 'http://example.net/backoffice/management/form-title/'
resp = resp.follow()
assert 'A missing or invalid custom view was referenced' in resp
resp = app.get('/backoffice/management/form-title/user-plop/1/')
assert resp.location == 'http://example.net/backoffice/management/form-title/1/'
resp = app.get('/backoffice/management/form-title/user-plop/1/?plop')
assert resp.location == 'http://example.net/backoffice/management/form-title/1/?plop'
def test_carddata_custom_view(pub, studio):
CardDef.wipe()
user = create_user(pub)
app = login(get_app(pub))
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = [
fields.StringField(id='1', label='Test', type='string', varname='foo'),
]
carddef.backoffice_submission_roles = user.roles
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.store()
carddef.data_class().wipe()
for i in range(50):
carddata = carddef.data_class()()
carddata.data = {'1': 'FOO %s' % i}
carddata.just_created()
carddata.store()
resp = app.get('/backoffice/data/foo/')
if pub.is_using_postgresql():
assert resp.text.count('<tr') == 21 # header + rows of data
else:
# no pagination
assert resp.text.count('<tr') == 51 # header + rows of data
resp = resp.forms['listing-settings'].submit()
resp.forms['save-custom-view']['title'] = 'card view'
resp = resp.forms['save-custom-view'].submit()
assert resp.location.endswith('/user-card-view/')
resp = resp.follow()
def test_backoffice_custom_view_keep_filters(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert resp.text.count('<span>User Label</span>') == 1
assert resp.text.count('<tr') == 18
# filters
resp.forms['listing-settings']['filter-2'].checked = True
resp = resp.forms['listing-settings'].submit()
resp.forms['listing-settings']['filter-2-value'] = 'baz'
resp = resp.forms['listing-settings'].submit()
resp.forms['save-custom-view']['title'] = 'custom test view'
resp = resp.forms['save-custom-view'].submit()
resp = app.get('/backoffice/management/form-title/user-custom-test-view/')
assert 'filter-2-value' in resp.forms['listing-settings'].fields
assert 'filter-3-value' not in resp.forms['listing-settings'].fields
resp = app.get('/backoffice/management/form-title/user-custom-test-view/?filter-foo=A')
assert 'filter-2-value' not in resp.forms['listing-settings'].fields
assert 'filter-3-value' in resp.forms['listing-settings'].fields
resp = app.get('/backoffice/management/form-title/user-custom-test-view/?filter-foo=A&keep-view-filters=on')
assert 'filter-2-value' in resp.forms['listing-settings'].fields
assert 'filter-3-value' in resp.forms['listing-settings'].fields
def test_backoffice_table_varname_filter(pub):
create_superuser(pub)
create_environment(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/?filter-foo=A')
# check filter is applied
assert resp.text.count('<tr') == 6
# and kept in parameters
assert resp.forms['listing-settings']['filter-3'].checked
assert resp.forms['listing-settings']['filter-3-value'].value == 'A'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<tr') == 6