wcs/tests/backoffice_pages/test_all.py

6545 lines
232 KiB
Python

import datetime
import io
import json
import os
import random
import re
import time
import zipfile
from unittest import mock
import pytest
from quixote import get_publisher
from quixote.http_request import Upload as QuixoteUpload
import wcs.qommon.storage as st
from wcs import fields
from wcs.api_access import ApiAccess
from wcs.blocks import BlockDef
from wcs.carddef import CardDef
from wcs.categories import Category
from wcs.formdef import FormDef
from wcs.qommon.form import UploadedFile
from wcs.qommon.http_request import HTTPRequest
from wcs.qommon.ident.password_accounts import PasswordAccount
from wcs.qommon.upload_storage import PicklableUpload
from wcs.roles import logged_users_role
from wcs.wf.backoffice_fields import SetBackofficeFieldsWorkflowStatusItem
from wcs.wf.create_carddata import CreateCarddataWorkflowStatusItem
from wcs.wf.create_formdata import CreateFormdataWorkflowStatusItem, Mapping
from wcs.wf.dispatch import DispatchWorkflowStatusItem
from wcs.wf.export_to_model import ExportToModel
from wcs.wf.form import FormWorkflowStatusItem, WorkflowFormFieldsFormDef
from wcs.wf.jump import JumpWorkflowStatusItem
from wcs.wf.redirect_to_url import RedirectToUrlWorkflowStatusItem
from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem
from wcs.wf.resubmit import ResubmitWorkflowStatusItem
from wcs.wf.wscall import WebserviceCallStatusItem
from wcs.workflows import (
ChoiceWorkflowStatusItem,
CommentableWorkflowStatusItem,
DisplayMessageWorkflowStatusItem,
EditableWorkflowStatusItem,
JumpOnSubmitWorkflowStatusItem,
Workflow,
WorkflowBackofficeFieldsFormDef,
WorkflowCriticalityLevel,
)
from wcs.wscalls import NamedWsCall
from ..utilities import clean_temporary_pub, create_temporary_pub, get_app, login
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()
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
fd.write(
'''
[api-secrets]
coucou = 1234
'''
)
return pub
def create_user(pub, is_admin=False):
user1 = None
for user in pub.user_class.select():
if user.name == 'admin':
user1 = user
user1.is_admin = is_admin
user1.roles = [x.id for x in pub.role_class.select() if x.name == 'foobar']
user1.store()
elif user.email == 'jean.darmette@triffouilis.fr':
pass # don't remove user created by local_user fixture
else:
user.remove_self()
if user1:
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()
pub.role_class.wipe()
role = pub.role_class(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()
BlockDef.wipe()
CardDef.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 = []
formdef.store() # make sure sql columns are removed
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, 0, i).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 pub.role_class.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 pub.role_class.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 pub.role_class.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 'Forms in your care' not 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
# anonymise some formdata, they should no longer be included
if pub.is_using_postgresql():
formdef = FormDef.get_by_urlname('form-title')
for i, formdata in enumerate(
formdef.data_class().select([st.Equal('status', 'wf-finished')], order_by='id')
):
if i >= 20:
break
formdata.anonymise()
for i, formdata in enumerate(
formdef.data_class().select([st.Equal('status', 'wf-new')], order_by='id')
):
if i >= 5:
break
formdata.anonymise()
resp = app.get('/backoffice/')
resp = resp.click('Management', index=0)
resp = resp.follow()
assert 'Forms in your care' in resp.text
assert '4 open on 25' 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')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
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 'submission_channel' not 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')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
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 'submission_agent' not 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 '>agent<' not in resp.text
resp = resp.click('Export a Spreadsheet')
resp.form['format'] = 'csv'
resp = resp.form.submit('submit')
assert len(resp.text.splitlines()) == 18 # 17 + header line
assert ',agent,' not in resp.text
for formdata in formdef.data_class().select():
formdata.submission_agent_id = str(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 a Spreadsheet')
resp.form['format'] = 'csv'
resp = resp.form.submit('submit')
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')
with open(os.path.join(os.path.dirname(__file__), '..', 'image-with-gps-data.jpeg'), 'rb') as fd:
upload.receive([fd.read()])
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_file_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('a filename that is too long "and" will be ellipsised.txt', 'text/plain')
upload.receive([b'text'])
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 'title="a filename that is too long &quot;and&quot; will be ellipsised.txt"' in resp
assert '<span>a filename that is too(…).txt</span>' in resp
def test_backoffice_user_columns(pub):
create_superuser(pub)
create_environment(pub)
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()
pub.cfg['users']['field_name'] = ['3', '4']
pub.write_cfg()
user1 = pub.user_class(name='userA')
user1.form_data = {'_first_name': 'toto', '3': 'nono'}
user1.set_attributes_from_formdata(user1.form_data)
user1.store()
user2 = pub.user_class(name='userB')
user2.form_data = {'_first_name': 'tutu', '3': 'nunu'}
user2.set_attributes_from_formdata(user2.form_data)
user2.store()
formdef = FormDef.get_by_urlname('form-title')
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/')
assert resp.text.count('</th>') == 8 # six columns
if not pub.is_using_postgresql():
# no support for relation columns unless using SQL
assert 'user-label$3' not in resp.forms['listing-settings'].fields
return
resp.forms['listing-settings']['user-label$3'].checked = True
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('</th>') == 9
assert '<td>nono</td' in resp
def test_backoffice_card_field_columns(pub):
user = create_superuser(pub)
create_environment(pub)
datasource = {'type': 'formula', 'value': repr([('A', 'aa'), ('B', 'bb'), ('C', 'cc')])}
CardDef.wipe()
carddef = CardDef()
carddef.name = 'foo'
carddef.fields = [
fields.CommentField(id='0', label='...', type='comment'),
fields.StringField(id='1', label='Test', type='string', varname='foo'),
fields.DateField(id='2', label='Date', type='date'),
fields.BoolField(id='3', label='Bool', type='bool'),
fields.ItemField(id='4', label='Item', type='item', data_source=datasource),
]
carddef.backoffice_submission_roles = user.roles
carddef.workflow_roles = {'_editor': user.roles[0]}
carddef.digest_templates = {'default': 'card {{ form_var_foo }}'}
carddef.store()
carddef.data_class().wipe()
card = carddef.data_class()()
card.data = {
'1': 'plop',
'2': time.strptime('2020-04-24', '%Y-%m-%d'),
'3': True,
'4': 'A',
'4_display': 'aa',
}
card.store()
formdef = FormDef.get_by_urlname('form-title')
formdef.geolocations = {'base': 'Geolocation'}
formdef.fields.append(
fields.ItemField(
id='4', label='card field', type='item', data_source={'type': 'carddef:foo', 'value': ''}
)
)
formdef.store()
for formdata in formdef.data_class().select(lambda x: x.status == 'wf-new'):
formdata.data['4'] = str(card.id)
formdata.data['4_display'] = formdef.fields[-1].store_display_value(formdata.data, '4')
formdata.data['4_structured'] = formdef.fields[-1].store_structured_value(formdata.data, '4')
formdata.geolocations = {'base': {'lat': 48.83, 'lon': 2.32}}
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert resp.text.count('</th>') == 8 # six columns
if not pub.is_using_postgresql():
# no support for relation columns unless using SQL
assert '4$1' not in resp.forms['listing-settings'].fields
return
assert '4$0' not in resp.forms['listing-settings'].fields
resp.forms['listing-settings']['4$1'].checked = True
resp.forms['listing-settings']['4$2'].checked = True
resp.forms['listing-settings']['4$3'].checked = True
resp.forms['listing-settings']['4$4'].checked = True
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('</th>') == 12
assert resp.text.count('data-link') == 17 # 17 rows
assert resp.text.count('<td>plop</td>') == 17
assert resp.text.count('<td>2020-04-24</td>') == 17
assert resp.text.count('<td>Yes</td>') == 17
assert resp.text.count('<td>aa</td>') == 17
resp_csv = resp.click('Export a Spreadsheet')
resp_csv.form['format'] = 'csv'
resp_csv = resp_csv.form.submit('submit')
assert resp_csv.text.splitlines()[1].endswith(',plop,2020-04-24,Yes,aa')
resp_map = resp.click('Plot on a Map')
geojson_url = re.findall(r'data-geojson-url="(.*?)"', resp_map.text)[0]
resp_geojson = app.get(geojson_url)
assert {
'varname': None,
'label': 'card field - Test',
'value': 'plop',
'html_value': 'plop',
} in resp_geojson.json['features'][0]['properties']['display_fields']
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 is 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()
assert '>invalid date<' not in resp
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 'filter-2-value' not 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 'filter-4-value' not 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('<td>â</td>') > 0
assert resp.text.count('<td>b</td>') == 0
assert resp.text.count('<td>d</td>') == 0
resp.forms['listing-settings']['filter-4-value'].value = 'b'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>â</td>') == 0
assert resp.text.count('<td>b</td>') > 0
assert resp.text.count('<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('<td>â</td>') == 0
assert resp.text.count('<td>b</td>') == 0
assert resp.text.count('<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']] == ['â', 'b', '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']] == ['â', 'b', '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=['E', 'F', 'G', 'H'],
display_locations=['validation', 'summary', 'listings'],
)
)
formdef.store()
for i, formdata in enumerate(formdef.data_class().select()):
formdata.jump_status('new')
if i % 4 == 0:
formdata.data['4'] = 'a'
formdata.data['4_display'] = 'a'
formdata.data['5'] = 'E'
formdata.data['5_display'] = 'E'
elif i % 4 == 1:
formdata.data['4'] = 'a'
formdata.data['4_display'] = 'a'
formdata.data['5'] = 'F'
formdata.data['5_display'] = 'F'
elif i % 4 == 2:
formdata.data['4'] = 'a'
formdata.data['4_display'] = 'a'
formdata.data['5'] = 'G'
formdata.data['5_display'] = 'G'
elif i % 4 == 3:
formdata.data['4'] = 'b'
formdata.data['4_display'] = 'b'
formdata.data['5'] = 'F'
formdata.data['5_display'] = 'F'
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] == ['', 'E', 'F', 'G']
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] == ['', 'E', 'F', 'G']
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] == ['', 'F']
resp.forms['listing-settings']['filter-5-value'].value = 'F'
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] == ['', 'F']
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] == ['', 'E', 'F', 'G']
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('<td>â</td>') > 0
assert resp.text.count('<td>b</td>') == 0
assert resp.text.count('<td>d</td>') == 0
resp.forms['listing-settings']['filter-bo0-1-value'].value = 'b'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>â</td>') == 0
assert resp.text.count('<td>b</td>') > 0
assert resp.text.count('<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('<td>â</td>') == 0
assert resp.text.count('<td>b</td>') == 0
assert resp.text.count('<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']] == ['â', 'b', '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']] == ['â', 'b', '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('<td>â, b</td>') > 0
assert resp.text.count('<td>â</td>') > 0
assert resp.text.count('<td>b, d</td>') == 0
resp.forms['listing-settings']['filter-4-value'].value = 'b'
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('<td>â, b</td>') > 0
assert resp.text.count('<td>â</td>') == 0
assert resp.text.count('<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('<td>â, b</td>') == 0
assert resp.text.count('<td>â</td>') == 0
assert resp.text.count('<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/')
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 '<option value="%s" selected="selected">userA</option>' % user1.id in resp
# 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 '<option value="%s" selected="selected">userA</option>' % user1.id in resp
# 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_submission_agent_filter(pub):
pub.user_class.wipe()
user = create_superuser(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.backoffice_submission_roles = user.roles
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.submission_agent_id = str(user1.id if i < 10 else user2.id)
formdata.status = 'wf-new'
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/?limit=100')
# enable submission-agent column
resp.forms['listing-settings']['submission_agent'].checked = True
resp = resp.forms['listing-settings'].submit()
assert resp.text.count('>userA<') > 0
assert resp.text.count('>userB<') > 0
# check the filter is hidden
assert resp.pyquery.find('li[hidden] input[name=filter-submission-agent]')
base_url = resp.request.url
resp = app.get(base_url + '&filter-submission-agent=on&filter-submission-agent-value=%s' % user1.id)
assert resp.text.count('>userA<') > 0
assert resp.text.count('>userB<') == 0
assert resp.text.count('<tr') == 11
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
assert resp.text.count('<tr') == 11
resp = app.get(base_url + '&filter-submission-agent=on&filter-submission-agent-value=%s' % user2.id)
assert resp.text.count('>userA<') == 0
assert resp.text.count('>userB<') > 0
assert resp.text.count('<tr') == 42
resp = app.get(
'/backoffice/management/form-title/?limit=100&filter-submission-agent=on&filter-submission-agent-value=%s'
% user2.id
)
assert resp.text.count('<tr') == 42
# filter on uuid
user1.name_identifiers = ['0123456789']
user1.store()
resp = app.get(base_url + '&filter-submission-agent-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(base_url + '&filter-submission-agent-uuid=XXX')
assert resp.text.count('>userA<') == 0
assert resp.text.count('>userB<') == 0
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 '<h2>Filters</h2>' not 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_form_category_permissions(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
assert 'Export a Spreadsheet' in resp.text
assert 'Statistics' in resp.text
cat1 = Category(name='cat1')
cat1.store()
formdef.category_id = cat1.id
formdef.store()
resp = app.get('/backoffice/management/form-title/')
assert 'Export a Spreadsheet' in resp.text
assert 'Statistics' in resp.text
role = pub.role_class(name='limited perms')
role.store()
cat1.export_roles = [role]
cat1.store()
resp = app.get('/backoffice/management/form-title/')
assert 'Export a Spreadsheet' not in resp.text
assert 'Statistics' in resp.text
cat1.statistics_roles = [role]
cat1.store()
resp = app.get('/backoffice/management/form-title/')
assert 'Export a Spreadsheet' not in resp.text
assert 'Statistics' not in resp.text
app.get('/backoffice/management/form-title/stats', status=403)
app.get('/backoffice/management/form-title/export-spreadsheet', status=403)
app.get('/backoffice/management/form-title/csv', status=403)
app.get('/backoffice/management/form-title/ods', status=403)
# check it's ok for admins
user.is_admin = True
user.store()
resp = app.get('/backoffice/management/form-title/')
assert 'Export a Spreadsheet' in resp.text
assert 'Statistics' in resp.text
app.get('/backoffice/management/form-title/stats', status=200)
app.get('/backoffice/management/form-title/export-spreadsheet', status=200)
app.get('/backoffice/management/form-title/csv', status=200)
app.get('/backoffice/management/form-title/ods', status=200)
# check it's ok for agents with roles
user.is_admin = False
user.roles.append(role.id)
user.store()
resp = app.get('/backoffice/management/form-title/')
assert 'Export a Spreadsheet' in resp.text
assert 'Statistics' in resp.text
app.get('/backoffice/management/form-title/stats', status=200)
app.get('/backoffice/management/form-title/export-spreadsheet', status=200)
app.get('/backoffice/management/form-title/csv', status=200)
app.get('/backoffice/management/form-title/ods', status=200)
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 pub.role_class.select() if x.name == 'foobar']
workflow.store()
resp = app.get('/backoffice/management/form-title/?limit=20')
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
assert (
resp.pyquery.find('[data-redirect-auto]').attr['href']
== '/backoffice/management/form-title/?limit=20'
)
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 'OTHER ACTION' not 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_multi_actions_oldest_form(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('Mark as duplicates')
jump = action.append_item('jump')
jump.condition = {'type': 'django', 'value': "mass_action_index != 0"}
jump.status = 'rejected'
jump2 = action.append_item('jump')
jump2.condition = {'type': 'django', 'value': "mass_action_index == 0"}
jump2.status = 'accepted'
register_comment = RegisterCommenterWorkflowStatusItem()
register_comment.id = '_comment'
register_comment.comment = '<p>Original form: {{ oldest_form_number }}.</p>'
assert workflow.possible_status[2].id == 'rejected'
workflow.possible_status[2].items.append(register_comment)
register_comment.parent = workflow.possible_status[2]
trigger = action.triggers[0]
trigger.roles = [x.id for x in pub.role_class.select() if x.name == 'foobar']
workflow.store()
formdef.workflow_id = workflow.id
formdef.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;Mark as duplicates&quot; on forms' in resp.text
assert '>completed<' in resp.text
oldest_formdata = None
for i, id in enumerate(sorted(ids, key=int)):
if i == 0:
oldest_formdata = formdef.data_class().get(id)
assert formdef.data_class().get(id).status == 'wf-accepted'
else:
assert formdef.data_class().get(id).status == 'wf-rejected'
assert (
formdef.data_class().get(id).evolution[-1].parts[0].content
== '<p>Original form: %s.</p>' % oldest_formdata.get_display_id()
)
def test_backoffice_multi_actions_using_session_user(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('Show user')
register_comment = action.append_item('register-comment')
register_comment.comment = 'session_user={{session_user}}'
trigger = action.triggers[0]
trigger.roles = [x.id for x in pub.role_class.select() if x.name == 'foobar']
workflow.store()
formdef.workflow_id = workflow.id
formdef.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;Show user&quot; on forms' in resp.text
assert '>completed<' in resp.text
for id in sorted(ids, key=int):
content = formdef.data_class().get(id).evolution[-1].parts[0].content
assert 'session_user=admin' in content
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 drop_global_views, get_connection_and_cursor
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 '<h2>Filters</h2>' not 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 '<h2>Filters</h2>' not 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 'filter-2-value' not 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):
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(r'<tbody.*\/tbody>', resp.text, re.DOTALL)[0].count('<tr') == 17
# check there's no link to map the sidebar
assert 'Plot on a Map' not 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')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
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):
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(r'<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 pub.role_class.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')
action.append_item('remove')
trigger = action.triggers[0]
trigger.roles = [x.id for x in pub.role_class.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(r'<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 'Channel' not 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',
}
number31.submission_agent_id = str(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 = io.BytesIO(resp.body)
with zipfile.ZipFile(zip_content, 'a') as zipf:
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):
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(r'<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 'Geolocation' not 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 'CLICK ME2!' not in resp.text
assert 'backoffice-description' not 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 = pub.role_class.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 'backoffice-description' not in resp.text
assert 'CLICK ME!' not in resp.text
assert 'CLICK ME2!' not 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 '<p>Foo</p>' not 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 '<p>Foo</p>' not in resp.text
assert '<p>Bar</p>' in resp.text
assert '<p>Baz</p>' not 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 'backoffice-description' not 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 = pub.role_class(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 'form-title/' not 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(r'<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_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 'Error during webservice call' not in resp.text
@pytest.mark.parametrize('notify_on_errors', [True, False])
@pytest.mark.parametrize('record_on_errors', [True, False])
def test_backoffice_wscall_on_error(http_requests, pub, emails, notify_on_errors, record_on_errors):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
pub.cfg['debug'] = {'error_email': 'errors@localhost.invalid'}
pub.cfg['emails'] = {'from': 'from@localhost.invalid'}
pub.write_cfg()
create_user(pub)
create_environment(pub)
pub.loggederror_class.wipe()
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.notify_on_errors = notify_on_errors
wscall.record_on_errors = record_on_errors
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
if notify_on_errors:
assert emails.count() == 1
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['from'] == 'from@localhost.invalid'
assert error_email['email_rcpt'] == ['errors@localhost.invalid']
if record_on_errors:
assert error_email['msg']['References']
else:
assert emails.count() == 0
# check pub.loggederror_class
if record_on_errors:
assert pub.loggederror_class.count() == 1
pub.loggederror_class.wipe()
else:
assert pub.loggederror_class.count() == 0
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_channel = 'mail'
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
assert len(form_class().get(number31.id).evolution) == 2 # (just submitted, new)
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 'Tracking Code' not 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'
assert len(form_class().get(number31.id).evolution) == 3
assert form_class().get(number31.id).evolution[-1].who == str(user.id)
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 io.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_backoffice_wfedit_and_workflow_data(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/?test={% firstof some_workflow_data "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.workflow_data = {'some_workflow_data': 'foobar'}
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 '?test=foobar' in url
return io.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_backoffice_wfedit_and_data_source_with_field_info(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields[0].varname = 'bar'
formdef.fields[2].data_source = {
'type': 'json',
'value': 'https://www.example.invalid/?xxx={{ form_var_bar }}',
}
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.data['3'] = 'EE'
number31.data['3_display'] = 'EE'
number31.store()
app = login(get_app(pub))
with mock.patch('wcs.qommon.misc.urlopen') as urlopen:
data = {'data': [{'id': 'DD', 'text': 'DD'}, {'id': 'EE', 'text': 'EE'}]}
def side_effect(url, *args):
assert '?xxx=FOO BAR 30' in url
return io.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
assert 'invalid value' not in resp
assert resp.form['f3'].value == 'EE'
resp.form['f3'].value = 'DD'
resp = resp.form.submit('submit')
assert urlopen.call_count == 2
resp = resp.follow()
def test_backoffice_wfedit_and_user_selection(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.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
assert 'Associated User' not in resp
resp = resp.form.submit('button_wfedit')
resp = resp.follow()
assert resp.pyquery('.submit-user-selection')
resp.form['user_id'] = str(user.id) # happens via javascript
resp = resp.form.submit('submit')
resp = resp.follow()
assert 'Associated User' in resp
assert formdef.data_class().get(number31.id).user_id == str(user.id)
def test_backoffice_wfedit_and_user_selection_multi_page(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
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'))
form_class = formdef.data_class()
number31 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0]
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
assert 'Associated User' not in resp
resp = resp.form.submit('button_wfedit')
resp = resp.follow()
assert resp.pyquery('.submit-user-selection')
resp.form['user_id'] = str(user.id) # happens via javascript
resp = resp.form.submit('submit') # -> 2nd page
assert resp.pyquery('.submit-user-selection')
resp = resp.form.submit('submit') # -> save changes
resp = resp.follow()
assert 'Associated User' in resp
assert formdef.data_class().get(number31.id).user_id == str(user.id)
number31.store() # save and lose associated user id
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert (' with the number %s.' % number31.get_display_id()) in resp.text
assert 'Associated User' not in resp
resp = resp.form.submit('button_wfedit')
resp = resp.follow()
assert resp.pyquery('.submit-user-selection')
resp = resp.form.submit('submit') # -> 2nd page
assert resp.pyquery('.submit-user-selection')
resp.form['user_id'] = str(user.id) # happens via javascript
resp = resp.form.submit('submit') # -> save changes
resp = resp.follow()
assert 'Associated User' in resp
assert formdef.data_class().get(number31.id).user_id == str(user.id)
def test_backoffice_wfedit_and_live_condition(pub):
user = create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields[0].varname = 'foo'
formdef.fields[1].condition = {'type': 'django', 'value': 'form_var_foo == "test"'}
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()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
resp = resp.form.submit('button_wfedit').follow()
live_url = resp.html.find('form').attrs['data-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'].value = 'test'
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']
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 'Map View' not 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 'http://example.net/backoffice/management/form-title/' not 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_templates = {'default': 'digest of number <{{form_number}}>'}
formdata.store()
assert formdata.get(formdata.id).digests['default']
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 'Channel' not 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')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
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 = pub.role_class(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 'form-title' not 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 'form-title' not 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 'form-title' not 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 'form-title' not 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 drop_global_views, get_connection_and_cursor
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
FormDef.wipe()
Category.wipe()
create_user(pub)
formdef = FormDef()
formdef.name = 'form-3'
formdef.workflow_roles = {'_receiver': 1}
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.jump_status('new')
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/listing?limit=500')
assert 'category_ids$element0' not in resp.forms['listing-settings'].fields
cat1 = Category(name='cat1')
cat1.position = 1
cat1.store()
formdef = FormDef()
formdef.name = 'form-1'
formdef.category_id = cat1.id
formdef.workflow_roles = {'_receiver': 1}
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.jump_status('new')
formdata.store()
cat2 = Category(name='cat2')
cat1.position = 2
cat2.store()
formdef = FormDef()
formdef.name = 'form-2'
formdef.category_id = cat2.id
formdef.workflow_roles = {'_receiver': 1}
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.jump_status('new')
formdata.store()
resp = app.get('/backoffice/management/listing')
assert 'category_ids$element0' in resp.forms['listing-settings'].fields
assert 'management/form-1/' in resp.text
assert 'management/form-2/' in resp.text
assert 'management/form-3/' in resp.text
resp.forms['listing-settings']['category_ids$element0'] = cat1.id
resp = resp.forms['listing-settings'].submit()
assert 'management/form-1/' in resp.text
assert 'management/form-2/' not in resp.text
assert 'management/form-3/' not in resp.text
resp.forms['listing-settings']['category_ids$element0'] = cat2.id
resp = resp.forms['listing-settings'].submit()
assert 'management/form-1/' not in resp.text
assert 'management/form-2/' in resp.text
assert 'management/form-3/' not in resp.text
resp = resp.forms['listing-settings'].submit('category_ids$add_element')
resp.forms['listing-settings']['category_ids$element0'] = cat1.id
resp.forms['listing-settings']['category_ids$element1'] = cat2.id
resp = resp.forms['listing-settings'].submit()
assert 'management/form-1/' in resp.text
assert 'management/form-2/' in resp.text
assert 'management/form-3/' not in resp.text
resp = app.get('/backoffice/management/listing?category_slugs=cat1')
assert resp.forms['listing-settings']['category_ids$element0'].value == cat1.id
assert 'category_ids$element1' not in resp.forms['listing-settings'].fields
assert 'management/form-1/' in resp.text
assert 'management/form-2/' not in resp.text
assert 'management/form-3/' not in resp.text
resp = app.get('/backoffice/management/listing?category_slugs=cat1,cat2')
assert resp.forms['listing-settings']['category_ids$element0'].value == cat1.id
assert resp.forms['listing-settings']['category_ids$element1'].value == cat2.id
assert 'management/form-1/' in resp.text
assert 'management/form-2/' in resp.text
assert 'management/form-3/' not in resp.text
def test_datetime_in_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/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(r'<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 '/user-pending-forms' not in resp.text
number31.formdef.digest_templates = {'default': '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 number31.get_url(backoffice=True) not in partial_resp.text
assert number31.digests['default'] 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 number31.get_url(backoffice=True) not 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_backoffice_sidebar_lateral_block(pub):
create_user(pub)
FormDef.wipe()
Workflow.wipe()
wf = Workflow(name='WF')
wf.add_status('Status1')
wf.store()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = [
fields.StringField(id='0', label='string', varname='string'),
]
formdef.workflow_roles = {'_receiver': 1}
formdef.workflow = wf
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.data = {'0': 'bouh'}
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % formdata.id)
assert '/lateral-block' not in resp.text
formdef.lateral_template = 'XX{{ form_var_string }}XX'
formdef.store()
resp = app.get('/backoffice/management/form-title/%s/' % formdata.id)
assert '/lateral-block' in resp.text
lateral_block_url = re.findall('data-async-url="(.*/lateral-block)"', resp.text)[0]
partial_resp = app.get(lateral_block_url)
assert partial_resp.text == '<div class="lateral-block">XXbouhXX</div>'
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):
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')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
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 item not 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 'Send tracking code' not 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'] = {}
pub.write_cfg()
resp = user_view_resp.click('Send tracking code')
assert 'sms' not 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'] = {'passerelle_url': 'xx', 'sender': '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):
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(r'data-link="(\d+)/?"', resp.text)[0]
assert 'backoffice-submission' not 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 'backoffice-submission' not 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 = pub.role_class.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(r'data-link="(\d+/)?"', resp.text)[0]
assert 'advisory-lock' not in resp.text
app2 = login(get_app(pub), username='foobar', password='foobar')
resp = app2.get('/backoffice/management/form-title/')
assert 'advisory-lock' not 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 'advisory-lock' not 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 'advisory-lock' not in resp.text
resp = app.get('/backoffice/management/form-title/' + first_link)
assert 'Be warned forms of this user are also being looked' not 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 'button_commentable' not in resp.text
assert len(resp.forms) == 0
# revisit with first user, no change
resp = app.get('/backoffice/management/form-title/' + first_link)
assert 'Be warned forms of this user are also being looked' not 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 'button_commentable' not 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 '(unlock actions)' not 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 'advisory-lock' not in app2.get('/backoffice/management/form-title/')
assert 'advisory-lock' not 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 'advisory-lock' not 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 = pub.role_class.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 'Be warned forms of this user are also being looked' not 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 'Be warned forms of this user are also being looked' not 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 'formdata-other-form-%d' % other_formdata.id not 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 'fblah_1' in resp.form.fields
resp.form['fblah_1'] = '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['fblah_2'] = '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 'fblah_1' in resp.form.fields
assert 'fblah_2' 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 'fblah_1' in resp.form.fields
assert 'fblah_2' 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 'fblah_1' in resp.form.fields
assert 'fblah_2' 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 'fblah_1' in resp.form.fields
assert 'fblah_2' 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 'fblah_1' in resp.form.fields
assert 'fblah_2' in resp.form.fields
assert resp.html.find('div', {'data-field-id': 'blah_1'}).attrs['data-live-source'] == 'true'
assert resp.html.find('div', {'data-field-id': 'blah_2'}).attrs.get('style') == 'display: none'
live_url = resp.html.find('form').attrs['data-live-url']
assert '/backoffice/' in live_url
resp.form['fblah_1'] = ''
live_resp = app.post(live_url, params=resp.form.submit_fields())
assert live_resp.json['result']['blah_1']['visible']
assert not live_resp.json['result']['blah_2']['visible']
resp.form['fblah_1'] = 'xxx'
live_resp = app.post(live_url, params=resp.form.submit_fields())
assert live_resp.json['result']['blah_1']['visible']
assert live_resp.json['result']['blah_2']['visible']
# check submit doesn't work
resp = resp.form.submit('submit')
assert 'There were errors processing your form.' in resp.text
resp.form['fblah_1'] = 'xxx2'
live_resp = app.post(live_url, params=resp.form.submit_fields())
assert live_resp.json['result']['blah_1']['visible']
assert not live_resp.json['result']['blah_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 io.StringIO(json.dumps(data1))
else:
return io.StringIO(json.dumps(data2))
urlopen.side_effect = side_effect
resp = login(app).get(formdata.get_url(backoffice=True))
assert 'fblah_1' in resp.form.fields
assert 'fblah_2' in resp.form.fields
assert resp.form.fields['fblah_2'][0].options == [('A', False, 'hello'), ('B', False, 'world')]
live_url = resp.html.find('form').attrs['data-live-url']
resp.form['fblah_1'] = 'toto'
live_resp = app.post(live_url + '?modified_field_id=blah_1', params=resp.form.submit_fields())
assert live_resp.json['result']['blah_2']['items'] == [
{'text': 'hello', 'id': 'C'},
{'text': 'world', 'id': 'D'},
]
def test_backoffice_workflow_display_form_with_block_add(pub):
user = create_user(pub)
create_environment(pub)
block = BlockDef()
block.name = 'foobar'
block.fields = [
fields.StringField(id='123', required=True, label='Test', type='string'),
]
block.store()
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.BlockField(id='2', label='Blocks', type='block:foobar', varname='data', max_items=3),
]
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))
resp.form['fblah_1'] = 'blah'
resp.form['fblah_2$element0$f123'] = 'foo'
resp = resp.form.submit('fblah_2$add_element')
resp.form['fblah_2$element1$f123'] = 'bar'
resp = resp.form.submit('submit')
assert formdef.data_class().get(formdata.id).workflow_data == {
'blah_var_data': 'foobar, foobar',
'blah_var_data_raw': {'data': [{'123': 'foo'}, {'123': 'bar'}], 'schema': {'123': 'string'}},
'blah_var_str': 'blah',
}
def test_backoffice_criticality_in_formdef_listing(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
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
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):
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:
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')
formdef.fields.append(fields.FileField(id='4', label='file field', type='file', varname='file'))
formdef.store()
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': 'uné',
'str_but_non_utf8': b'\xed\xa0\x00', # not actually supposed to happen
'non_unicode_convertible': IHateUnicode(),
'very_long_string': '0' * 100000,
}
# add a PicklableUpload in field 4
upload = PicklableUpload('hello.txt', content_type='text/plain')
upload.receive([b'hello world'])
formdata.data['4'] = upload
formdata.user_id = local_user.id
formdata.workflow_data = {
'foo': {
'bar_coin': 'yy',
},
'foo_bar': 'xx',
}
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() == '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
# make sure workflow data itself is not displayed, as it's available in
# expanded variables
assert not pq('[title="form_workflow_data"]')
# but do display it if it has some invalid contents
formdata.workflow_data['invalid key'] = 'foobar'
formdata.store()
resp = login(get_app(pub)).get(resp.request.url)
assert resp.pyquery('[title="form_workflow_data"]')
# check functions
assert re.findall('Recipient.*foobar', resp.text)
role = pub.role_class(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
assert 'rendered as an object' 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
assert 'rendered as an object' not in resp.text
resp.form['template'] = '{{ form_var_file }}'
resp = resp.form.submit()
assert 'Template rendering' in resp.text
assert '<div class="test-tool-result-plain">hello.txt</div>' in resp.text
assert 'rendered as an object' in resp.text
assert '<div class="test-tool-result-plain">hello.txt (PicklableUpload)</div>' in resp.text
assert 'HTML Source' not 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_inspect_page_with_related_objects(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
user = create_user(pub, is_admin=True)
FormDef.wipe()
CardDef.wipe()
# test ExternalWorkflowGlobalAction
external_wf = Workflow(name='External Workflow')
st1 = external_wf.add_status(name='New')
action = external_wf.add_global_action('Delete', 'delete')
action.append_item('remove')
trigger = action.append_trigger('webservice')
trigger.identifier = 'delete'
external_wf.store()
external_formdef = FormDef()
external_formdef.name = 'External Form'
external_formdef.fields = [
fields.StringField(id='0', label='string', varname='form_string'),
]
external_formdef.workflow = external_wf
external_formdef.store()
external_carddef = CardDef()
external_carddef.name = 'External Card'
external_carddef.fields = [
fields.StringField(id='0', label='string', varname='card_string'),
]
external_carddef.backoffice_submission_roles = user.roles
external_carddef.workflow = external_wf
external_carddef.store()
wf = Workflow(name='External actions')
st1 = wf.add_status('Create external formdata')
# add a message to history, to check it doesn't interfer when searching for
# linked data.
register_comment = RegisterCommenterWorkflowStatusItem()
register_comment.id = '_register'
register_comment.comment = '<p>test</p>'
st1.items.append(register_comment)
register_comment.parent = st1
create_formdata = CreateFormdataWorkflowStatusItem()
create_formdata.label = 'create linked form'
create_formdata.formdef_slug = external_formdef.url_name
create_formdata.varname = 'created_form'
create_formdata.id = '_create_form'
mappings = [Mapping(field_id='0', expression='{{ form_var_string }}')]
create_formdata.mappings = mappings
create_formdata.parent = st1
create_carddata = CreateCarddataWorkflowStatusItem()
create_carddata.label = 'create linked card'
create_carddata.formdef_slug = external_carddef.url_name
create_carddata.varname = 'created_card'
create_carddata.id = '_create_card'
create_carddata.mappings = mappings
create_carddata.parent = st1
st1.items.append(create_formdata)
st1.items.append(create_carddata)
global_action = wf.add_global_action('Delete external linked object', 'delete')
action = global_action.append_item('external_workflow_global_action')
action.slug = 'formdef:%s' % external_formdef.url_name
action.trigger_id = 'action:%s' % trigger.identifier
wf.store()
formdef = FormDef()
formdef.name = 'External action form'
formdef.fields = [
fields.StringField(id='0', label='string', varname='string'),
]
formdef.workflow = wf
formdef.store()
carddef = CardDef()
carddef.name = 'External action card'
carddef.fields = [
fields.StringField(id='0', label='string', varname='string'),
]
carddef.backoffice_submission_roles = user.roles
carddef.workflow = wf
carddef.store()
assert formdef.data_class().count() == 0
assert carddef.data_class().count() == 0
assert external_formdef.data_class().count() == 0
assert external_carddef.data_class().count() == 0
formdata = formdef.data_class()()
formdata.data = {'0': 'test form'}
formdata.user = user
formdata.store()
formdata.just_created()
formdata.perform_workflow()
assert formdef.data_class().count() == 1
assert carddef.data_class().count() == 0
# related form and card
assert external_formdef.data_class().count() == 1
assert external_carddef.data_class().count() == 1
app = login(get_app(pub))
resp = app.get('/backoffice/management/external-action-form/1/inspect')
assert 'Related Forms/Cards' in resp.text
# related form and card
assert (
'<li><a href="http://example.net/backoffice/management/external-form/1/">External Form #1-1 (Evolution)</a></li>'
in resp.text
)
assert (
'<li><a href="http://example.net/backoffice/data/external-card/1/">External Card #1-1 (Evolution)</a></li>'
in resp.text
)
# check related form
resp = app.get('/backoffice/management/external-form/1/')
assert (
'<h3>Original form</h3><p><a href="http://example.net/backoffice/management/external-action-form/1/">External action form #2-1</a></p>'
in resp.text
)
resp = app.get('/backoffice/management/external-form/1/inspect')
# parent
assert (
'<li><a href="http://example.net/backoffice/management/external-action-form/1/">External action form #2-1 (Parent)</a></li>'
in resp.text
)
# check related card
resp = app.get('/backoffice/data/external-card/1/')
assert (
'<h3>Original form</h3><p><a href="http://example.net/backoffice/management/external-action-form/1/">External action form #2-1</a></p>'
in resp.text
)
resp = app.get('/backoffice/data/external-card/1/inspect')
# parent
assert (
'<li><a href="http://example.net/backoffice/management/external-action-form/1/">External action form #2-1 (Parent)</a></li>'
in resp.text
)
external_formdef.data_class().wipe()
external_carddef.data_class().wipe()
# missing form/card data
resp = app.get('/backoffice/management/external-action-form/1/inspect')
assert 'Related Forms/Cards' in resp.text
assert (
'<li><a href="">Linked &quot;External Form&quot; object by id 1 (Evolution - not found)</a></li>'
in resp.text
)
assert (
'<li><a href="">Linked &quot;External Card&quot; object by id 1 (Evolution - not found)</a></li>'
in resp.text
)
formdef.data_class().wipe()
carddef.data_class().wipe()
carddata = carddef.data_class()()
carddata.data = {'0': 'test card'}
carddata.user = user
carddata.store()
carddata.just_created()
carddata.perform_workflow()
assert formdef.data_class().count() == 0
assert carddef.data_class().count() == 1
# related form and card
assert external_formdef.data_class().count() == 1
assert external_carddef.data_class().count() == 1
resp = app.get('/backoffice/data/external-action-card/1/inspect')
assert 'Related Forms/Cards' in resp.text
# related form and card
assert (
'<li><a href="http://example.net/backoffice/management/external-form/2/">External Form #1-2 (Evolution)</a></li>'
in resp.text
)
assert (
'<li><a href="http://example.net/backoffice/data/external-card/2/">External Card #1-2 (Evolution)</a></li>'
in resp.text
)
# check related form
resp = app.get('/backoffice/management/external-form/2/')
assert (
'<h3>Original card</h3><p><a href="http://example.net/backoffice/data/external-action-card/1/">External action card #2-1</a></p>'
in resp.text
)
resp = app.get('/backoffice/management/external-form/2/inspect')
# parent
assert (
'<li><a href="http://example.net/backoffice/data/external-action-card/1/">External action card #2-1 (Parent)</a></li>'
in resp.text
)
# check related card
resp = app.get('/backoffice/data/external-card/2/')
assert (
'<h3>Original card</h3><p><a href="http://example.net/backoffice/data/external-action-card/1/">External action card #2-1</a></p>'
in resp.text
)
resp = app.get('/backoffice/data/external-card/2/inspect')
# parent
assert (
'<li><a href="http://example.net/backoffice/data/external-action-card/1/">External action card #2-1 (Parent)</a></li>'
in resp.text
)
FormDef.wipe()
CardDef.wipe()
Workflow.wipe()
# test linked Card in datasource
wf = Workflow(name='WF')
st1 = wf.add_status('NEW')
wf.store()
wfc = Workflow(name='WFC')
wfc.add_status('NEW')
wfc.store()
carddef = CardDef()
carddef.name = 'CARD A'
carddef.fields = [
fields.StringField(id='0', label='string', varname='string'),
]
carddef.backoffice_submission_roles = user.roles
carddef.workflow = wfc
carddef.store()
datasource = {'type': 'carddef:%s' % carddef.url_name}
formdef = FormDef()
formdef.name = 'FORM A'
formdef.fields = [
fields.ItemField(id='0', label='Card', type='item', varname='card', data_source=datasource),
]
formdef.workflow = wf
formdef.store()
carddata = carddef.data_class()()
carddata.data = {'0': 'Text'}
carddata.store()
formdata = formdef.data_class()()
formdata.data = {'0': '1'}
formdata.store()
formdata.just_created()
formdata.perform_workflow()
assert formdef.data_class().count() == 1
assert carddef.data_class().count() == 1
resp = app.get('/backoffice/management/form-a/1/inspect')
assert 'Related Forms/Cards' in resp.text
# related card
assert (
'<li><a href="http://example.net/backoffice/data/card-a/1/">CARD A #1-1 (Data Source - varname &quot;card&quot;)</a></li>'
in resp.text
)
# missing carddata
carddef.data_class().wipe()
resp = app.get('/backoffice/management/form-a/1/inspect')
assert 'Related Forms/Cards' in resp.text
assert (
'<li><a href="">Linked &quot;CARD A&quot; object by id 1 (Data Source - varname &quot;card&quot; - not found)</a></li>'
in resp.text
)
# missing carddef
CardDef.wipe()
resp = app.get('/backoffice/management/form-a/1/inspect')
assert 'Related Forms/Cards' in resp.text
assert (
'<li><a href="">Linked object def by id card-a (Data Source - varname &quot;card&quot; - not found)</a></li>'
in resp.text
)
def test_inspect_page_actions_traces(pub):
create_environment(pub)
create_user(pub, is_admin=True)
formdef = FormDef.get_by_urlname('form-title')
formdef.data_class().wipe()
formdata = formdef.data_class()()
formdata.data = {}
formdata.just_created()
formdata.perform_workflow()
formdata.store()
resp = login(get_app(pub)).get(formdata.get_url(backoffice=True), status=200)
resp = resp.click('Data Inspector')
assert '<h2>Actions Tracing</h2>' in resp
assert [x.text for x in resp.pyquery('#inspect-timeline strong')] == ['Just Submitted', 'New']
assert [x.text for x in resp.pyquery('#inspect-timeline a.tracing-link') if x.text] == [
'Email',
'Email',
'Automatic Jump',
]
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):
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):
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
),
]
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 'Backoffice Data' not in resp.text
assert '1st backoffice field' not 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):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
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()
# create a carddef with the same id
CardDef.wipe()
carddef = CardDef()
carddef.id = '34'
carddef.name = 'test'
carddef.fields = []
carddef.store()
pub.loggederror_class.wipe()
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/forms/%s/' % formdef.id)
assert 'ZeroDivisionError' not in resp.text
resp = app.get('/backoffice/cards/%s/' % carddef.id)
assert 'ZeroDivisionError' not in resp.text
resp = app.get('/backoffice/workflows/%s/' % workflow.id)
assert 'ZeroDivisionError' not in resp.text
app = get_app(pub)
resp = app.get('/test/')
resp = resp.form.submit('submit').follow()
resp = resp.form.submit('submit')
assert pub.loggederror_class.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/cards/%s/' % carddef.id)
assert 'ZeroDivisionError' not in resp.text
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
resp = resp.click('Delete').follow()
assert pub.loggederror_class.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 pub.loggederror_class.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 pub.loggederror_class.count() == 2
app = login(get_app(pub))
resp = app.get('/backoffice/workflows/%s/' % workflow.id)
assert 'Failed to evaluate condition' in resp.text
assert "error NameError (name 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx…" in resp.text
# remove formdef
FormDef.wipe()
resp = resp2.click('Failed to evaluate condition')
assert 'href="http://example.net/backoffice/management/test/' not 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):
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
fd.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.jump_status('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 'message-to-all' not 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 == {'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.user_association_mode = 'keep-user'
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
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.StringField(id='bo1', label='Foo Bar 1', varname='foo_bar'),
fields.StringField(id='bo2', label='Foo Bar 2', varname='foo_bar'),
fields.StringField(id='bo3', label='Foo Bar 3', varname='foo_bar'),
]
setbo = SetBackofficeFieldsWorkflowStatusItem()
setbo.parent = st2
setbo.fields = [
{'field_id': 'bo1', 'value': 'go'},
{'field_id': 'bo2', 'value': ''},
{'field_id': 'bo3', 'value': None},
{'field_id': 'unknown', 'value': 'foobar'},
]
st2.items.append(setbo)
workflow.store()
resp = app.get('/backoffice/workflows/%s/inspect' % workflow.id)
assert (
'<ul class="fields"><li>Foo Bar 1 → go</li>'
'<li>Foo Bar 2 → </li>'
'<li>Foo Bar 3 → None</li>'
'<li>#unknown → foobar</li></ul>'
) in resp.text
st3 = workflow.add_status('Status3', 'st3')
export_to = ExportToModel()
export_to.convert_to_pdf = False
export_to.label = 'create doc'
upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
upload.fp = io.BytesIO()
upload.fp.write(b'HELLO WORLD')
upload.fp.seek(0)
export_to.model_file = UploadedFile(pub.app_dir, None, upload)
export_to.id = '_export_to'
export_to.by = ['_submitter']
st3.items.append(export_to)
export_to.parent = st3
workflow.store()
resp = app.get('/backoffice/workflows/%s/inspect' % workflow.id)
assert (
'<li><span class="parameter">Model:</span> '
'<a href="status/st3/items/_export_to/?file=model_file">test.rtf</a></li>'
) in resp.text
def test_workflow_comment_required(pub):
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_lazy_eval_with_conditional_workflow_form(pub):
role = pub.role_class(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]['flocal_1'] = 'a'
resp.forms[0]['flocal_2'] = '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.user_association_mode = 'keep-user'
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()
if pub.is_using_postgresql():
pub.loggederror_class.wipe()
return locals()
def test_backoffice_create_formdata_backoffice_submission(pub, 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')
if pub.is_using_postgresql():
assert pub.loggederror_class.count() == 0
assert target_data_class.count() == 1
target_formdata = target_data_class.select()[0]
assert target_formdata.submission_context == {
'orig_object_type': 'formdef',
'orig_formdata_id': '1',
'orig_formdef_id': '1',
}
assert target_formdata.submission_agent_id == str(create_formdata['admin'].id)
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(pub, 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()
pub.substitutions.reset()
pub.substitutions.feed(formdata)
substvars = pub.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(pub, 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')
if pub.is_using_postgresql():
assert pub.loggederror_class.count() == 0
assert target_data_class.count() == 1
target_formdata = target_data_class.select()[0]
assert target_formdata.submission_context == {
'orig_object_type': 'formdef',
'orig_formdata_id': '1',
'orig_formdef_id': '1',
}
assert target_formdata.submission_agent_id == str(create_formdata['admin'].id)
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):
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_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
def test_backoffice_http_basic_auth(pub):
access = ApiAccess()
access.name = 'test'
access.access_identifier = 'test'
access.access_key = '12345'
access.store()
create_superuser(pub)
app = get_app(pub)
app.set_authorization(('Basic', ('test', '12345')))
app.get('/backoffice/', status=403)
def test_backoffice_dispatch_lose_access(pub):
user = create_user(pub)
create_environment(pub)
role1 = pub.role_class(name='xxx1')
role1.store()
role2 = pub.role_class(name='xxx2')
role2.store()
user.roles.append(role1.id)
user.store()
formdef = FormDef()
formdef.name = 'test dispatch lose access'
formdef.fields = []
wf = Workflow(name='dispatch')
st1 = wf.add_status('Status1')
dispatch = DispatchWorkflowStatusItem()
dispatch.id = '_dispatch'
dispatch.role_key = '_receiver'
dispatch.role_id = role2.id
st1.items.append(dispatch)
dispatch.parent = st1
add_function = ChoiceWorkflowStatusItem()
add_function.id = '_change_function'
add_function.label = 'Change function'
add_function.by = ['_receiver']
add_function.status = st1.id
st1.items.append(add_function)
add_function.parent = st1
wf.store()
formdef.workflow_id = wf.id
formdef.workflow_roles = {'_receiver': role1.id}
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))
resp = resp.form.submit('button_add_function')
assert resp.location == '..' # no access -> to listing
def test_backoffice_dispatch_multi(pub):
user = create_user(pub)
create_environment(pub)
role1 = pub.role_class(name='xxx1')
role1.store()
role2 = pub.role_class(name='xxx2')
role2.store()
user.roles.append(role1.id)
user.store()
formdef = FormDef()
formdef.name = 'test dispatch multi'
formdef.fields = []
wf = Workflow(name='dispatch')
wf.roles['_foobar'] = 'Foobar'
st1 = wf.add_status('Status1')
dispatch = DispatchWorkflowStatusItem()
dispatch.id = '_dispatch'
dispatch.role_key = '_receiver'
dispatch.role_id = role2.id
dispatch.operation_mode = 'add'
st1.items.append(dispatch)
dispatch.parent = st1
add_function = ChoiceWorkflowStatusItem()
add_function.id = '_add_function'
add_function.label = 'Add function'
add_function.by = ['_receiver']
add_function.status = st1.id
st1.items.append(add_function)
add_function.parent = st1
wf.store()
formdef.workflow_id = wf.id
formdef.workflow_roles = {'_receiver': role1.id}
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))
resp = resp.form.submit('button_add_function').follow(status=200) # access to role1 is still ok
formdata.refresh_from_storage()
assert formdata.workflow_roles == {'_receiver': [role1.id, role2.id]}
@pytest.mark.parametrize(
'user_template',
[
'{{ session_user }}',
'{{ session_user_email }}',
'{{ session_user_nameid }}',
'{{ session_user_name }}',
'foobar', # a role, not an user
],
)
def test_backoffice_dispatch_single_user(pub, user_template):
pub.user_class.wipe()
user = create_user(pub)
user.name_identifiers = ['0123456789']
user.store()
create_environment(pub)
formdef = FormDef()
formdef.name = 'test dispatch user'
formdef.fields = []
wf = Workflow(name='dispatch')
wf.roles['_foobar'] = 'Foobar'
st1 = wf.add_status('Status1')
dispatch = DispatchWorkflowStatusItem()
dispatch.id = '_dispatch'
dispatch.role_key = '_foobar'
dispatch.role_id = user_template
st1.items.append(dispatch)
dispatch.parent = st1
add_function = ChoiceWorkflowStatusItem()
add_function.id = '_add_function'
add_function.label = 'Add function'
add_function.by = ['_receiver']
add_function.status = st1.id
st1.items.append(add_function)
add_function.parent = st1
a_button = ChoiceWorkflowStatusItem()
a_button.id = '_a_button'
a_button.label = 'A button'
a_button.by = ['_foobar']
a_button.status = st1.id
st1.items.append(a_button)
a_button.parent = st1
wf.store()
formdef.workflow_id = wf.id
formdef.workflow_roles = {'_receiver': user.roles[0]}
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_a_button' not in resp.text
resp = resp.form.submit('button_add_function').follow(status=200)
formdata.refresh_from_storage()
if user_template != 'foobar':
assert formdata.workflow_roles == {'_foobar': ['_user:%s' % user.id]}
else:
# check role are still dispatched by name
assert formdata.workflow_roles == {'_foobar': [user.roles[0]]}
assert 'button_a_button' in resp.text