wcs/tests/test_admin_pages.py

1929 lines
65 KiB
Python

# -*- coding: utf-8 -*-
import logging
import os
import re
import zipfile
try:
import lasso
except ImportError:
lasso = None
import pytest
from webtest import Upload
import mock
from django.utils.six import BytesIO
from django.utils.six.moves.urllib import parse as urlparse
from quixote.http_request import Upload as QuixoteUpload
from wcs.qommon.form import UploadedFile
from wcs.qommon.ident.password_accounts import PasswordAccount
from wcs.qommon.http_request import HTTPRequest
from wcs.qommon.template import get_current_theme
from wcs.admin.settings import UserFieldsFormDef
from wcs.categories import Category
from wcs.data_sources import NamedDataSource
from wcs.wscalls import NamedWsCall
from wcs.roles import Role
from wcs.workflows import (
Workflow, CommentableWorkflowStatusItem,
ChoiceWorkflowStatusItem, JumpOnSubmitWorkflowStatusItem)
from wcs.wf.export_to_model import ExportToModel
from wcs.wf.redirect_to_url import RedirectToUrlWorkflowStatusItem
from wcs.wf.create_formdata import CreateFormdataWorkflowStatusItem, Mapping
from wcs.formdef import FormDef
from wcs.carddef import CardDef
from wcs.blocks import BlockDef
from wcs import fields
from utilities import get_app, login, create_temporary_pub, clean_temporary_pub
def pytest_generate_tests(metafunc):
if 'pub' in metafunc.fixturenames:
metafunc.parametrize('pub', ['pickle', 'sql', 'pickle-templates'], indirect=True)
@pytest.fixture
def pub(request):
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()
return pub
def create_superuser(pub):
if pub.user_class.select(lambda x: x.name == 'admin'):
user1 = pub.user_class.select(lambda x: x.name == 'admin')[0]
user1.is_admin = True
user1.store()
return user1
user1 = pub.user_class(name='admin')
user1.is_admin = True
user1.store()
account1 = PasswordAccount(id='admin')
account1.set_password('admin')
account1.user_id = user1.id
account1.store()
return user1
def create_role():
Role.wipe()
role = Role(name='foobar')
role.store()
return role
def teardown_module(module):
clean_temporary_pub()
def test_empty_site(pub):
resp = get_app(pub).get('/backoffice/')
resp = resp.click('Users', index=0)
resp = resp.click('New User')
resp = get_app(pub).get('/backoffice/')
resp = resp.click('Settings', index=0)
def test_empty_site_but_idp_settings(pub):
pub.cfg['idp'] = {'xxx': {}}
pub.write_cfg()
resp = get_app(pub).get('/backoffice/')
assert resp.location == 'http://example.net/login/?next=http%3A%2F%2Fexample.net%2Fbackoffice%2F'
def test_with_user(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_with_superuser(pub):
create_superuser(pub)
app = login(get_app(pub))
app.get('/backoffice/')
def test_admin_redirect(pub):
create_superuser(pub)
app = login(get_app(pub))
assert app.get('/admin/whatever', status=302).location == 'http://example.net/backoffice/whatever'
def test_admin_for_all(pub):
user = create_superuser(pub)
role = create_role()
try:
open(os.path.join(pub.app_dir, 'ADMIN_FOR_ALL'), 'w').close()
resp = get_app(pub).get('/backoffice/', status=200)
# check there is a CSS class
assert resp.pyquery.find('body.admin-for-all')
# check there are menu items
resp.click('Management', index=0)
resp.click('Forms', index=0)
resp.click('Settings', index=0)
# cheeck it's possible to get inside the subdirectories
resp = get_app(pub).get('/backoffice/settings/', status=200)
pub.cfg['admin-permissions'] = {'settings': [role.id]}
pub.write_cfg()
resp = get_app(pub).get('/backoffice/settings/', status=200)
# check it doesn't work with a non-empty ADMIN_FOR_ALL file
fd = open(os.path.join(pub.app_dir, 'ADMIN_FOR_ALL'), 'w')
fd.write('x.x.x.x')
fd.close()
resp = get_app(pub).get('/backoffice/settings/', status=302)
# check it works if the file contains the user IP address
fd = open(os.path.join(pub.app_dir, 'ADMIN_FOR_ALL'), 'w')
fd.write('127.0.0.1')
fd.close()
resp = get_app(pub).get('/backoffice/settings/', status=200)
# check it's also ok if the user is logged in but doesn't have the
# permissions
user.is_admin = False
user.store()
resp = login(get_app(pub)).get('/backoffice/settings/', status=200)
# check there are menu items
resp.click('Management', index=0)
resp.click('Forms', index=0)
resp.click('Settings', index=0)
finally:
if 'admin-permissions' in pub.cfg:
del pub.cfg['admin-permissions']
pub.write_cfg()
os.unlink(os.path.join(pub.app_dir, 'ADMIN_FOR_ALL'))
role.remove_self()
user.is_admin = True
user.store()
def test_users_roles_menu_entries(pub):
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/')
assert 'Users' in resp.text
assert 'Roles' in resp.text
resp = app.get('/backoffice/menu.json')
assert 'Users' in [x['label'] for x in resp.json]
assert 'Roles' in [x['label'] for x in resp.json]
# don't include users/roles in menu if roles are managed by an external
# identity provider.
pub.cfg['sp'] = {'idp-manage-roles': True}
pub.write_cfg()
resp = app.get('/backoffice/')
assert 'Users' not in resp.text
assert 'Roles' not in resp.text
resp = app.get('/backoffice/menu.json')
assert 'Users' not in [x['label'] for x in resp.json]
assert 'Roles' not in [x['label'] for x in resp.json]
def test_users(pub):
create_superuser(pub)
app = login(get_app(pub))
app.get('/backoffice/users/')
def test_users_new(pub):
pub.user_class.wipe()
create_superuser(pub)
user_count = pub.user_class.count()
account_count = PasswordAccount.count()
app = login(get_app(pub))
resp = app.get('/backoffice/users/')
resp = resp.click('New User')
resp.forms[0]['name'] = 'a new user'
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/backoffice/users/'
resp = resp.follow()
assert 'a new user' in resp.text
resp = resp.click('a new user')
assert 'User - a new user' in resp.text
assert pub.user_class.count() == user_count + 1
assert PasswordAccount.count() == account_count
def test_users_new_with_account(pub):
pub.user_class.wipe()
create_superuser(pub)
user_count = pub.user_class.count()
account_count = PasswordAccount.count()
app = login(get_app(pub))
resp = app.get('/backoffice/users/')
resp = resp.click('New User')
resp.forms[0]['name'] = 'a second user'
resp.forms[0]['method_password$username'] = 'second-user'
resp.forms[0]['method_password$password'] = 'foobar'
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/backoffice/users/'
resp = resp.follow()
assert 'a second user' in resp.text
resp = resp.click('a second user')
assert 'User - a second user' in resp.text
assert pub.user_class.count() == user_count + 1
assert PasswordAccount.count() == account_count + 1
def test_users_edit(pub):
pub.user_class.wipe()
create_superuser(pub)
user = pub.user_class(name='foo bar')
user.store()
app = login(get_app(pub))
resp = app.get('/backoffice/users/%s/' % user.id)
resp = resp.click(href='edit')
resp.forms[0]['is_admin'].checked = True
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/backoffice/users/%s/' % user.id
resp = resp.follow()
def test_users_edit_new_account(pub):
pub.user_class.wipe()
PasswordAccount.wipe()
create_superuser(pub)
user = pub.user_class(name='foo bar')
user.store()
account_count = PasswordAccount.count()
app = login(get_app(pub))
resp = app.get('/backoffice/users/%s/' % user.id)
resp = resp.click(href='edit')
resp.forms[0]['is_admin'].checked = True
resp.forms[0]['method_password$username'] = 'foo'
resp.forms[0]['method_password$password'] = 'bar'
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/backoffice/users/%s/' % user.id
resp = resp.follow()
assert PasswordAccount.count() == account_count + 1
def test_users_edit_edit_account(pub):
pub.user_class.wipe()
PasswordAccount.wipe()
create_superuser(pub)
user = pub.user_class(name='foo bar')
user.store()
account = PasswordAccount(id='test')
account.user_id = user.id
account.store()
assert PasswordAccount.has_key('test')
app = login(get_app(pub))
resp = app.get('/backoffice/users/%s/' % user.id)
resp = resp.click(href='edit')
resp.forms[0]['is_admin'].checked = True
resp.forms[0]['method_password$username'] = 'foo' # change username
resp.forms[0]['method_password$password'] = 'bar'
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/backoffice/users/%s/' % user.id
resp = resp.follow()
# makes sure the old account has been removed
assert not PasswordAccount.has_key('test')
assert PasswordAccount.has_key('foo')
assert PasswordAccount.get('foo').user_id == user.id
def test_users_edit_with_managing_idp(pub):
create_role()
pub.user_class.wipe()
pub.cfg['sp'] = {'idp-manage-user-attributes': True}
pub.write_cfg()
PasswordAccount.wipe()
create_superuser(pub)
user = pub.user_class(name='foo bar')
user.store()
app = login(get_app(pub))
resp = app.get('/backoffice/users/%s/' % user.id)
assert '>Manage Roles<' in resp.text
resp = resp.click(href='edit')
assert not 'email' in resp.form.fields
assert 'roles$add_element' in resp.form.fields
pub.cfg['sp'] = {'idp-manage-roles': True}
pub.write_cfg()
resp = app.get('/backoffice/users/%s/' % user.id)
assert '>Edit<' in resp.text
resp = resp.click(href='edit')
assert 'email' in resp.form.fields
assert 'roles$add_element' not in resp.form.fields
pub.cfg['sp'] = {'idp-manage-roles': True, 'idp-manage-user-attributes': True}
pub.write_cfg()
resp = app.get('/backoffice/users/%s/' % user.id)
assert not '/edit' in resp.text
def test_users_delete(pub):
pub.user_class.wipe()
PasswordAccount.wipe()
create_superuser(pub)
user = pub.user_class(name='foo bar')
user.store()
account = PasswordAccount(id='test')
account.user_id = user.id
account.store()
user_count = pub.user_class.count()
account_count = PasswordAccount.count()
app = login(get_app(pub))
resp = app.get('/backoffice/users/%s/' % user.id)
resp = resp.click(href='delete')
resp = resp.forms[0].submit()
assert resp.location == 'http://example.net/backoffice/users/'
resp = resp.follow()
assert pub.user_class.count() == user_count - 1
assert PasswordAccount.count() == account_count - 1
def test_users_view_deleted(pub):
pub.user_class.wipe()
PasswordAccount.wipe()
create_superuser(pub)
user = pub.user_class(name='foo bar')
user.store()
account = PasswordAccount(id='test')
account.user_id = user.id
account.store()
user.set_deleted()
app = login(get_app(pub))
resp = app.get('/backoffice/users/%s/' % user.id)
assert 'Marked as deleted on' in resp
def test_users_pagination(pub):
pub.user_class.wipe()
PasswordAccount.wipe()
create_superuser(pub)
for i in range(50):
user = pub.user_class(name='foo bar %s' % (i+1))
user.store()
app = login(get_app(pub))
resp = app.get('/backoffice/users/')
assert 'foo bar 10' in resp.text
assert 'foo bar 30' not in resp.text
resp = resp.click('Next Page')
assert 'foo bar 10' not in resp.text
assert 'foo bar 30' in resp.text
resp = resp.click('Previous Page')
assert 'foo bar 10' in resp.text
assert 'foo bar 30' not in resp.text
resp = resp.click('Next Page')
resp = resp.click('Next Page')
assert 'foo bar 50' in resp.text
def test_users_filter(pub):
pub.user_class.wipe()
PasswordAccount.wipe()
create_superuser(pub)
role = create_role()
for i in range(50):
user = pub.user_class(name='foo bar %s' % (i+1))
user.store()
for i in range(5):
user = pub.user_class(name='baz bar %s' % (i+1))
user.roles = [role.id]
user.store()
app = login(get_app(pub))
resp = app.get('/backoffice/users/')
assert 'admin' in resp.text # superuser
assert 'foo bar 10' in resp.text # simple user
# uncheck 'None'; unfortunately this doesn't work with webtest 1.3
# resp.forms[0].fields['role'][-1].checked = False
# resp = resp.forms[0].submit()
# therefore we fall back on using the URL
resp = app.get('/backoffice/users/?offset=0&limit=100&q=&filter=true&role=admin')
assert '>Number of filtered users: 1<' in resp.text
assert 'user-is-admin' in resp.text # superuser
assert 'foo bar 1' not in resp.text # simple user
assert 'baz bar 1' not in resp.text # user with role
resp = app.get('/backoffice/users/?offset=0&limit=100&q=&filter=true&role=1')
assert '>Number of filtered users: 5<' in resp.text
assert 'user-is-admin' not in resp.text # superuser
assert 'foo bar 10' not in resp.text # simple user
assert 'baz bar 1' in resp.text # user with role
def test_users_search(pub):
pub.user_class.wipe()
PasswordAccount.wipe()
create_superuser(pub)
for i in range(20):
user = pub.user_class(name='foo %s' % (i+1))
user.store()
for i in range(10):
user = pub.user_class(name='bar %s' % (i+1))
user.store()
app = login(get_app(pub))
resp = app.get('/backoffice/users/')
assert 'foo 10' in resp.text
resp.forms[0]['q'] = 'bar'
resp = resp.forms[0].submit()
assert 'foo 10' not in resp.text
assert 'bar 10' in resp.text
assert 'Number of filtered users: 10' in resp.text
def test_users_new_with_custom_formdef(pub):
pub.user_class.wipe()
formdef = UserFieldsFormDef(pub)
formdef.fields.append(fields.StringField(id='3', label='test', type='string'))
formdef.fields.append(fields.CommentField(id='4', label='test', type='comment'))
formdef.fields.append(fields.FileField(id='5', label='test', type='file', required=False))
formdef.store()
create_superuser(pub)
user_count = pub.user_class.count()
account_count = PasswordAccount.count()
app = login(get_app(pub))
resp = app.get('/backoffice/users/')
resp = resp.click('New User')
resp.form['name'] = 'a new user'
resp.form['f3'] = 'TEST'
resp = resp.form.submit('submit')
assert resp.location == 'http://example.net/backoffice/users/'
resp = resp.follow()
assert 'a new user' in resp.text
resp = resp.click('a new user')
assert 'User - a new user' in resp.text
assert 'TEST' in resp.text
assert pub.user_class.count() == user_count + 1
assert PasswordAccount.count() == account_count
def test_users_display_roles(pub):
pub.user_class.wipe()
user = create_superuser(pub)
role = create_role()
user.roles = [role.id, 'XXX']
user.store()
app = login(get_app(pub))
resp = app.get('/backoffice/users/%s/' % user.id)
assert role.name in resp.text
assert 'Unknown role (XXX)' in resp.text
def test_roles(pub):
app = login(get_app(pub))
app.get('/backoffice/roles/')
def test_roles_new(pub):
Role.wipe()
app = login(get_app(pub))
resp = app.get('/backoffice/roles/')
resp = resp.click('New Role')
resp.forms[0]['name'] = 'a new role'
resp.forms[0]['details'] = 'bla bla bla'
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/backoffice/roles/'
resp = resp.follow()
assert 'a new role' in resp.text
resp = resp.click('a new role')
assert '<h2>a new role' in resp.text
assert Role.get(1).name == 'a new role'
assert Role.get(1).details == 'bla bla bla'
def test_roles_edit(pub):
Role.wipe()
role = Role(name='foobar')
role.store()
app = login(get_app(pub))
resp = app.get('/backoffice/roles/1/')
assert 'Holders of this role are granted access to the backoffice' in resp.text
resp = resp.click(href='edit')
assert resp.forms[0]['name'].value == 'foobar'
resp.forms[0]['name'] = 'baz'
resp.forms[0]['details'] = 'bla bla bla'
resp.forms[0]['emails_to_members'].checked = True
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/backoffice/roles/1/'
resp = resp.follow()
assert '<h2>baz' in resp.text
assert 'Holders of this role will receive all emails adressed to the role.' in resp.text
assert Role.get(1).details == 'bla bla bla'
assert Role.get(1).emails_to_members == True
def test_roles_matching_formdefs(pub):
Role.wipe()
role = Role(name='foo')
role.store()
FormDef.wipe()
app = login(get_app(pub))
resp = app.get('/backoffice/roles/1/')
assert 'form bar' not in resp.text
formdef = FormDef()
formdef.name = 'form bar'
formdef.roles = [role.id]
formdef.fields = []
formdef.store()
resp = app.get('/backoffice/roles/1/')
assert 'form bar' in resp.text
assert 'form baz' not in resp.text
FormDef.wipe()
formdef = FormDef()
formdef.name = 'form baz'
formdef.fields = []
formdef.workflow_roles = {'_receiver': role.id}
formdef.store()
resp = app.get('/backoffice/roles/1/')
assert 'form baz' in resp.text
assert 'form bar' not in resp.text
def test_roles_delete(pub):
Role.wipe()
role = Role(name='foobar')
role.store()
app = login(get_app(pub))
resp = app.get('/backoffice/roles/1/')
resp = resp.click(href='delete')
resp = resp.forms[0].submit()
assert resp.location == 'http://example.net/backoffice/roles/'
resp = resp.follow()
assert Role.count() == 0
def test_categories(pub):
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/forms/categories/')
def test_categories_legacy_urls(pub):
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/categories/')
assert resp.location.endswith('/backoffice/forms/categories/')
resp = app.get('/backoffice/categories/1')
assert resp.location.endswith('/backoffice/forms/categories/1')
resp = app.get('/backoffice/categories/1/')
assert resp.location.endswith('/backoffice/forms/categories/1/')
def test_categories_new(pub):
create_superuser(pub)
Category.wipe()
app = login(get_app(pub))
# go to the page and cancel
resp = app.get('/backoffice/forms/categories/')
resp = resp.click('New Category')
resp = resp.forms[0].submit('cancel')
assert resp.location == 'http://example.net/backoffice/forms/categories/'
# go to the page and add a category
resp = app.get('/backoffice/forms/categories/')
resp = resp.click('New Category')
resp.forms[0]['name'] = 'a new category'
resp.forms[0]['description'] = 'description of the category'
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/backoffice/forms/categories/'
resp = resp.follow()
assert 'a new category' in resp.text
resp = resp.click('a new category')
assert '<h2>a new category' in resp.text
assert Category.get(1).name == 'a new category'
assert Category.get(1).description == 'description of the category'
def test_categories_edit(pub):
create_superuser(pub)
Category.wipe()
category = Category(name='foobar')
category.store()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/categories/1/')
assert 'no form associated to this category' in resp.text
resp = resp.click(href='edit')
assert resp.forms[0]['name'].value == 'foobar'
resp.forms[0]['description'] = 'category description'
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/backoffice/forms/categories/'
resp = resp.follow()
resp = resp.click('foobar')
assert '<h2>foobar' in resp.text
assert Category.get(1).description == 'category description'
def test_categories_edit_duplicate_name(pub):
Category.wipe()
category = Category(name='foobar')
category.store()
category = Category(name='foobar2')
category.store()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/categories/1/')
resp = resp.click(href='edit')
assert resp.forms[0]['name'].value == 'foobar'
resp.forms[0]['name'] = 'foobar2'
resp = resp.forms[0].submit('submit')
assert 'This name is already used' in resp.text
resp = resp.forms[0].submit('cancel')
assert resp.location == 'http://example.net/backoffice/forms/categories/'
def test_categories_with_formdefs(pub):
Category.wipe()
category = Category(name='foobar')
category.store()
FormDef.wipe()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/categories/1/')
assert 'form bar' not in resp.text
formdef = FormDef()
formdef.name = 'form bar'
formdef.fields = []
formdef.category_id = category.id
formdef.store()
resp = app.get('/backoffice/forms/categories/1/')
assert 'form bar' in resp.text
assert 'no form associated to this category' not in resp.text
def test_categories_delete(pub):
Category.wipe()
category = Category(name='foobar')
category.store()
FormDef.wipe()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/categories/1/')
resp = resp.click(href='delete')
resp = resp.forms[0].submit('cancel')
assert resp.location == 'http://example.net/backoffice/forms/categories/1/'
assert Category.count() == 1
resp = app.get('/backoffice/forms/categories/1/')
resp = resp.click(href='delete')
resp = resp.forms[0].submit()
assert resp.location == 'http://example.net/backoffice/forms/categories/'
resp = resp.follow()
assert Category.count() == 0
def test_categories_edit_description(pub):
Category.wipe()
category = Category(name='foobar')
category.description = 'category description'
category.store()
app = login(get_app(pub))
# this URL is used for editing from the frontoffice, there's no link
# pointing to it in the admin.
resp = app.get('/backoffice/forms/categories/1/description')
assert resp.forms[0]['description'].value == 'category description'
resp.forms[0]['description'] = 'updated description'
# check cancel doesn't save the change
resp2 = resp.forms[0].submit('cancel')
assert resp2.location == 'http://example.net/backoffice/forms/categories/1/'
assert Category.get(1).description == 'category description'
# check submit does it properly
resp2 = resp.forms[0].submit('submit')
assert resp2.location == 'http://example.net/backoffice/forms/categories/1/'
resp2 = resp2.follow()
assert Category.get(1).description == 'updated description'
def test_categories_new_duplicate_name(pub):
Category.wipe()
category = Category(name='foobar')
category.store()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/categories/')
resp = resp.click('New Category')
resp.forms[0]['name'] = 'foobar'
resp = resp.forms[0].submit('submit')
assert 'This name is already used' in resp.text
def test_categories_reorder(pub):
Category.wipe()
category = Category(name='foo')
category.store()
category = Category(name='bar')
category.store()
category = Category(name='baz')
category.store()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/categories/update_order?order=1;2;3;')
categories = Category.select()
Category.sort_by_position(categories)
assert [x.id for x in categories] == ['1', '2', '3']
resp = app.get('/backoffice/forms/categories/update_order?order=3;1;2;')
categories = Category.select()
Category.sort_by_position(categories)
assert [x.id for x in categories] == ['3', '1', '2']
def test_settings(pub):
create_superuser(pub)
app = login(get_app(pub))
app.get('/backoffice/settings/')
app.get('/backoffice/settings/debug_options')
app.get('/backoffice/settings/language')
app.get('/backoffice/settings/import')
app.get('/backoffice/settings/export')
app.get('/backoffice/settings/identification')
app.get('/backoffice/settings/sitename')
app.get('/backoffice/settings/sms')
app.get('/backoffice/settings/session')
app.get('/backoffice/settings/admin-permissions')
def test_settings_disabled_screens(pub):
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/settings/')
assert 'Identification' in resp.text
assert 'Theme' in resp.text
if not pub.site_options.has_section('options'):
pub.site_options.add_section('options')
pub.site_options.set('options', 'settings-disabled-screens', 'identification, theme')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
resp = app.get('/backoffice/settings/')
assert 'Identification' not in resp.text
assert 'Theme' not in resp.text
def test_settings_export_import(pub, studio):
def wipe():
FormDef.wipe()
CardDef.wipe()
Workflow.wipe()
Role.wipe()
Category.wipe()
NamedDataSource.wipe()
NamedWsCall.wipe()
wipe()
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/settings/export')
resp = resp.form.submit('cancel')
resp = app.get('/backoffice/settings/export')
resp = resp.form.submit('submit')
assert resp.location.startswith('http://example.net/backoffice/settings/export?job=')
job_id = urlparse.parse_qs(urlparse.urlparse(resp.location).query)['job'][0]
resp = resp.follow()
assert 'completed' in resp.text
resp = resp.click('Download Export')
zip_content = BytesIO(resp.body)
zipf = zipfile.ZipFile(zip_content, 'a')
filelist = zipf.namelist()
assert len(filelist) == 0
# check afterjob ajax call
status_resp = app.get('/afterjobs/' + job_id)
assert status_resp.text == 'completed|completed'
formdef = FormDef()
formdef.name = 'foo'
formdef.store()
carddef = CardDef()
carddef.name = 'bar'
carddef.store()
Category(name='baz').store()
Role(name='qux').store()
NamedDataSource(name='quux').store()
NamedWsCall(name='corge').store()
wf = Workflow(name='bar')
st1 = wf.add_status('Status1', 'st1')
export_to = ExportToModel()
export_to.label = 'test'
upload = QuixoteUpload('/foo/bar', content_type='application/vnd.oasis.opendocument.text')
file_content = b'''PK\x03\x04\x14\x00\x00\x08\x00\x00\'l\x8eG^\xc62\x0c\'\x00'''
upload.fp = BytesIO()
upload.fp.write(file_content)
upload.fp.seek(0)
export_to.model_file = UploadedFile('models', 'export_to_model-1.upload', upload)
st1.items.append(export_to)
export_to.parent = st1
wf.store()
resp = app.get('/backoffice/settings/export')
resp = resp.form.submit('submit')
assert resp.location.startswith('http://example.net/backoffice/settings/export?job=')
job_id = urlparse.parse_qs(urlparse.urlparse(resp.location).query)['job'][0]
resp = resp.follow()
resp = resp.click('Download Export')
zip_content = BytesIO(resp.body)
zipf = zipfile.ZipFile(zip_content, 'a')
filelist = zipf.namelist()
assert 'formdefs/1' not in filelist
assert 'formdefs_xml/1' in filelist
assert 'carddefs/1' not in filelist
assert 'carddefs_xml/1' in filelist
assert 'workflows/1' not in filelist
assert 'workflows_xml/1' in filelist
assert 'models/export_to_model-1.upload' not in filelist
assert 'roles/1' in filelist
assert 'categories/1' in filelist
assert 'datasources/1' in filelist
assert 'wscalls/corge' in filelist
for filename in filelist:
assert not '.indexes' in filename
wipe()
assert FormDef.count() == 0
resp = app.get('/backoffice/settings/import')
resp = resp.form.submit('cancel')
resp = app.get('/backoffice/settings/import')
resp.form['file'] = Upload('export.wcs', b'invalid content')
resp = resp.form.submit('submit')
assert 'Error: Not a valid export file' in resp.text
resp = app.get('/backoffice/settings/import')
resp.form['file'] = Upload('export.wcs', zip_content.getvalue())
resp = resp.form.submit('submit')
assert 'Imported successfully' in resp.text
assert '1 forms' in resp.text
assert '1 cards' in resp.text
assert FormDef.count() == 1
assert FormDef.select()[0].url_name == 'foo'
assert CardDef.count() == 1
assert CardDef.select()[0].url_name == 'bar'
# check roles are found by name
wipe()
role = Role(name='qux')
role.store()
workflow = Workflow(name='Workflow One')
st1 = workflow.add_status(name='st1')
commentable = CommentableWorkflowStatusItem()
commentable.id = '_commentable'
commentable.by = [role.id]
commentable.label = 'foobar'
st1.items.append(commentable)
commentable.parent = st1
workflow.store()
formdef = FormDef()
formdef.name = 'foo'
formdef.workflow_id = workflow.id
formdef.roles = [role.id]
formdef.backoffice_submission_roles = [role.id]
formdef.workflow_roles = {'_receiver': role.id}
formdef.fields = [fields.StringField(
id='1', type='string',
data_source={'type': 'carddef:unknown'})]
formdef.store()
resp = app.get('/backoffice/settings/export')
resp.form['formdefs'] = True
resp.form['workflows'] = True
resp.form['roles'] = False
resp.form['categories'] = False
resp.form['datasources'] = False
resp.form['wscalls'] = False
resp = resp.form.submit('submit')
assert resp.location.startswith('http://example.net/backoffice/settings/export?job=')
job_id = urlparse.parse_qs(urlparse.urlparse(resp.location).query)['job'][0]
resp = resp.follow()
resp = resp.click('Download Export')
zip_content = BytesIO(resp.body)
zipf = zipfile.ZipFile(zip_content, 'a')
filelist = zipf.namelist()
assert 'formdefs_xml/%s' % formdef.id in filelist
assert 'workflows_xml/%s' % workflow.id in filelist
assert 'roles/%s' % role.id not in filelist
FormDef.wipe()
Workflow.wipe()
Role.wipe()
# create role beforehand, it should be matched by name
role = Role(name='qux')
role.id = '012345'
role.store()
resp = app.get('/backoffice/settings/import')
resp.form['file'] = Upload('export.wcs', zip_content.getvalue())
resp = resp.form.submit('submit')
assert FormDef.select()[0].roles == ['012345']
assert FormDef.select()[0].backoffice_submission_roles == ['012345']
assert FormDef.select()[0].workflow_roles == {'_receiver': '012345'}
assert len(FormDef.select()[0].fields) == 1
assert Workflow.select()[0].possible_status[0].items[0].by == ['012345']
# do not export roles when managed by idp
pub.cfg['sp'] = {'idp-manage-roles': True}
pub.write_cfg()
resp = app.get('/backoffice/settings/export')
resp = resp.form.submit('submit')
assert resp.location.startswith('http://example.net/backoffice/settings/export?job=')
job_id = urlparse.parse_qs(urlparse.urlparse(resp.location).query)['job'][0]
resp = resp.follow()
resp = resp.click('Download Export')
zip_content = BytesIO(resp.body)
zipf = zipfile.ZipFile(zip_content, 'a')
filelist = zipf.namelist()
assert len([x for x in filelist if 'roles/' in x]) == 0
# check an error is displayed if such an import is then used and roles are
# missing.
FormDef.wipe()
Workflow.wipe()
Role.wipe()
resp = app.get('/backoffice/settings/import')
resp.form['file'] = Upload('export.wcs', zip_content.getvalue())
resp = resp.form.submit('submit')
assert 'Unknown referenced role (qux)' in resp
def test_settings_themes(pub):
create_superuser(pub)
app = login(get_app(pub))
# create mock theme
os.mkdir(os.path.join(pub.app_dir, 'themes'))
os.mkdir(os.path.join(pub.app_dir, 'themes', 'test'))
fd = open(os.path.join(pub.app_dir, 'themes', 'test', 'desc.xml'), 'w')
fd.write('<?xml version="1.0"?>'\
'<theme name="test" version="1.0">'\
' <label>Test Theme</label>'\
'</theme>')
fd.close()
resp = app.get('/backoffice/settings/themes')
assert 'biglist themes' in resp.text
assert 'Test Theme (1.0)' in resp.text
# just for the kick, there's no support for uploading file in webtest 1.3
resp = app.get('/backoffice/settings/themes')
resp.click('Install New Theme')
# select the theme
resp = app.get('/backoffice/settings/themes')
resp.forms[0]['theme'].value = 'test'
resp = resp.forms[0].submit()
assert resp.location == 'http://example.net/backoffice/settings/'
resp = app.get('/backoffice/settings/themes')
assert 'checked' in resp.text
assert get_current_theme()['name'] == 'test'
def test_settings_template(pub):
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/settings/template')
# change template
orig_value = resp.forms[0]['template'].value
assert not 'foobar' in orig_value
resp.forms[0]['template'] = orig_value + '<!-- foobar -->'
resp = resp.forms[0].submit('submit')
# restore default template
resp = app.get('/backoffice/settings/template')
assert 'foobar' in resp.forms[0]['template'].value
resp = resp.forms[0].submit('restore-default')
# check
resp = app.get('/backoffice/settings/template')
assert resp.forms[0]['template'].value == orig_value
def test_settings_user(pub):
user = create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/settings/users').follow().follow()
# add a field
resp.forms[2]['label'] = 'foobar'
resp = resp.forms[2].submit()
assert resp.location == 'http://example.net/backoffice/settings/users/fields/'
resp = resp.follow()
assert b'foobar' in pub.cfg['users']['formdef']
assert 'foobar' in resp.text
# set field as email
resp.forms['mapping']['field_email'] = '1'
resp = resp.forms['mapping'].submit()
assert resp.location == 'http://example.net/backoffice/settings/users/fields/'
resp = resp.follow()
assert pub.cfg['users']['field_email'] == '1'
# and unset it
resp.forms['mapping']['field_email'] = ''
resp = resp.forms['mapping'].submit()
assert resp.location == 'http://example.net/backoffice/settings/users/fields/'
resp = resp.follow()
assert pub.cfg['users']['field_email'] == None
# add a comment field
resp.forms[2]['label'] = 'barfoo'
resp.forms[2]['type'] = 'comment'
resp = resp.forms[2].submit()
assert resp.location == 'http://example.net/backoffice/settings/users/fields/'
resp = resp.follow()
assert b'barfoo' in pub.cfg['users']['formdef']
assert 'barfoo' in resp.text
# check fields are present in edit form
resp = app.get('/backoffice/users/%s/edit' % user.id)
assert 'barfoo' in resp.text
assert 'f1' in resp.form.fields
assert 'email' in resp.form.fields
# check the email field is not displayed if it's overridden by a custom
# field.
pub.cfg['users']['field_email'] = '1'
pub.write_cfg()
resp = app.get('/backoffice/users/%s/edit' % user.id)
assert 'f1' in resp.form.fields
assert 'email' not in resp.form.fields
# set a sidebar template
app = login(get_app(pub))
resp = app.get('/backoffice/settings/users').follow().follow()
resp.forms['template']['sidebar_template'] = 'hello {{ form_user_display_name }}'
resp = resp.forms['template'].submit().follow()
assert pub.cfg['users']['sidebar_template'] == 'hello {{ form_user_display_name }}'
resp.forms['template']['sidebar_template'] = '{% if True %}'
resp = resp.forms['template'].submit().follow()
assert pub.cfg['users']['sidebar_template'] == 'hello {{ form_user_display_name }}'
assert 'syntax error in Django template' in resp
# disable users screen
if not pub.site_options.has_section('options'):
pub.site_options.add_section('options')
pub.site_options.set('options', 'settings-disabled-screens', 'users')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
resp = app.get('/backoffice/settings/')
resp = resp.click('Users', href='user-template')
resp.forms['template']['sidebar_template'] = '{% if True %}'
resp = resp.forms['template'].submit()
assert 'syntax error in Django template' in resp
resp.forms['template']['sidebar_template'] = 'hello {{ form_user_display_name }}'
resp = resp.forms['template'].submit()
assert pub.cfg['users']['sidebar_template'] == 'hello {{ form_user_display_name }}'
# restore config
pub.cfg['users']['field_email'] = None
pub.write_cfg()
def test_settings_emails(pub):
create_superuser(pub)
app = login(get_app(pub))
pub.cfg['debug'] = {'mail_redirection': 'foo@example.net'}
pub.write_cfg()
resp = app.get('/backoffice/settings/emails/')
resp = resp.click('General Options')
assert 'Warning: all emails are sent to &lt;foo@example.net&gt;' in resp.text
resp.form['from'] = 'test@localhost'
resp = resp.form.submit('submit')
pub.reload_cfg()
assert pub.cfg['emails']['from'] == 'test@localhost'
assert pub.cfg['emails']['well_known_domains']
assert pub.cfg['emails']['valid_known_domains']
pub.cfg['debug'] = {}
pub.write_cfg()
resp = app.get('/backoffice/settings/emails/')
resp = resp.click('General Options')
assert 'Warning: all emails are sent to &lt;foo@example.net&gt;' not in resp.text
resp = app.get('/backoffice/settings/emails/')
resp = resp.click('Approval of new account')
resp.forms[0]['email-new-account-approved_subject'] = 'bla'
resp.forms[0]['email-new-account-approved'] = 'bla bla bla'
resp = resp.forms[0].submit()
assert pub.cfg['emails']['email-new-account-approved_subject'] == 'bla'
assert pub.cfg['emails']['email-new-account-approved'] == 'bla bla bla'
# reset to default value
resp = app.get('/backoffice/settings/emails/')
resp = resp.click('Approval of new account')
resp.forms[0]['email-new-account-approved_subject'] = 'Your account has been approved'
resp = resp.forms[0].submit()
assert pub.cfg['emails']['email-new-account-approved_subject'] is None
def test_settings_texts(pub):
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/settings/texts/')
resp = resp.click('Text on top of the login page')
resp.forms[0]['text-top-of-login'] = 'Hello world'
resp = resp.forms[0].submit()
assert resp.location == 'http://example.net/backoffice/settings/texts/'
assert pub.cfg['texts']['text-top-of-login'] == 'Hello world'
resp = app.get('/backoffice/settings/texts/')
resp = resp.click('Text on top of the login page')
resp = resp.forms[0].submit('restore-default')
assert resp.location == 'http://example.net/backoffice/settings/texts/'
assert pub.cfg['texts']['text-top-of-login'] == None
@pytest.mark.skipif('lasso is None')
def test_settings_auth(pub):
pub.user_class.wipe() # makes sure there are no users
pub.cfg['identification'] = {}
pub.write_cfg()
app = get_app(pub)
resp = app.get('/backoffice/settings/')
assert not 'identification/password/' in resp.text
assert not 'identification/idp/' in resp.text
resp = resp.click('Identification')
assert resp.forms[0]['methods$elementidp'].checked is False
assert resp.forms[0]['methods$elementpassword'].checked is False
resp.forms[0]['methods$elementidp'].checked = True
resp = resp.forms[0].submit()
resp = resp.follow()
assert 'identification/idp/' in resp.text
assert pub.cfg['identification']['methods'] == ['idp']
resp = resp.click('Identification')
assert resp.forms[0]['methods$elementidp'].checked is True
assert resp.forms[0]['methods$elementpassword'].checked is False
resp.forms[0]['methods$elementidp'].checked = False
resp.forms[0]['methods$elementpassword'].checked = True
resp = resp.forms[0].submit()
resp = resp.follow()
assert 'identification/password/' in resp.text
assert pub.cfg['identification']['methods'] == ['password']
@pytest.mark.skipif('lasso is None')
def test_settings_idp(pub):
# create admin session
create_superuser(pub)
app = login(get_app(pub))
pub.cfg['identification'] = {'methods': ['idp']}
pub.write_cfg()
app.get('/saml/metadata', status=404)
resp = app.get('/backoffice/settings/')
resp = resp.click(href='identification/idp/')
resp = resp.click('Service Provider')
resp = resp.form.submit('generate_rsa').follow()
resp = resp.form.submit('submit')
resp = resp.follow()
resp2 = resp.click('Identities')
resp2 = resp2.form.submit('cancel').follow()
resp2 = resp.click('Identities')
resp2 = resp2.form.submit('submit')
resp_metadata = app.get('/saml/metadata', status=200)
assert resp_metadata.text.startswith('<?xml')
resp2 = resp.click('Identity Providers')
resp2 = resp2.click('New')
idp_metadata_filename = os.path.join(os.path.dirname(__file__), 'idp_metadata.xml')
resp2.form['metadata'] = Upload('idp_metadata.xml', open(idp_metadata_filename, 'rb').read())
resp2 = resp2.form.submit('submit')
resp = resp.click('Identity Providers')
assert 'http://authentic.example.net/' in resp.text
resp2 = resp.click(href='http-authentic.example.net-idp-saml2-metadata/', index=0)
assert 'ns0:EntityDescriptor' in resp2.text
resp = resp.click(href='http-authentic.example.net-idp-saml2-metadata/edit')
resp = resp.forms[0].submit('submit')
resp = resp.follow()
# test that login initiates a SSO
login_resp = app.get('/login/', status=302)
assert login_resp.location.startswith('http://authentic.example.net/idp/saml2/sso?SAMLRequest')
resp = resp.click(href='/backoffice/settings/identification/idp/idp/', index=0) # breadcrumb
resp = resp.click(href='http-authentic.example.net-idp-saml2-metadata/delete')
resp = resp.forms[0].submit() # confirm delete
assert len(pub.cfg['idp']) == 0
with mock.patch('wcs.qommon.misc.urlopen') as urlopen:
idp_metadata_filename = os.path.join(os.path.dirname(__file__), 'idp_metadata.xml')
urlopen.side_effect = lambda *args: open(idp_metadata_filename, 'rb')
resp = app.get('/backoffice/settings/identification/idp/idp/')
resp = resp.click('Create new from remote URL')
resp.form['metadata_url'] = 'http://authentic.example.net/idp/saml2/metadata'
resp = resp.form.submit('submit')
resp = resp.follow()
assert 'http://authentic.example.net/idp/saml2/metadata' in resp.text
assert urlopen.call_count == 1
resp = resp.click('View')
resp = resp.click('Update from remote URL')
assert urlopen.call_count == 2
def test_settings_auth_password(pub):
Role.wipe()
pub.user_class.wipe() # makes sure there are no users
pub.cfg['identification'] = {'methods': ['password']}
assert pub.cfg['identification']['methods'] == ['password']
pub.write_cfg()
app = get_app(pub)
resp = app.get('/backoffice/settings/identification/password/')
resp = resp.click('Identities')
resp = resp.forms[0].submit()
resp = app.get('/backoffice/settings/identification/password/')
resp = resp.click('Passwords')
resp = resp.forms[0].submit()
resp = app.get('/backoffice/settings/identification/password/')
resp = resp.click('Bulk Import')
resp = resp.forms[0].submit()
# same with existing roles
Role(name='blah').store()
resp = app.get('/backoffice/settings/identification/password/')
resp = resp.click('Bulk Import')
resp = resp.forms[0].submit()
def test_settings_filetypes(pub):
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/settings/filetypes/')
assert 'There are no file type defined at the moment.' in resp.text
resp.forms[0]['label'] = 'Text files'
resp.forms[0]['mimetypes'] = '.odt'
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/backoffice/settings/filetypes/'
resp = resp.follow()
assert pub.cfg['filetypes'][1]['label'] == 'Text files'
resp = resp.click('Text files')
assert resp.forms[0]['mimetypes'].value == 'application/vnd.oasis.opendocument.text'
resp.forms[0]['mimetypes'] = 'application/vnd.oasis.opendocument.text, .doc, .docx, .pdf'
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/backoffice/settings/filetypes/'
resp = resp.follow()
assert 'application/msword (.' in resp.text
assert 'application/pdf' in pub.cfg['filetypes'][1]['mimetypes']
resp.forms[0]['label'] = 'HTML files'
resp.forms[0]['mimetypes'] = '.html'
resp = resp.forms[0].submit('submit')
resp = resp.follow()
resp = resp.click('HTML files') # go to form
resp = resp.forms[0].submit('cancel') # and cancel
assert resp.location == 'http://example.net/backoffice/settings/filetypes/'
resp = resp.follow()
assert 'HTML files' in resp.text
resp = resp.click('HTML files') # go to form
resp = resp.forms[0].submit('delete') # and delete
assert resp.location == 'http://example.net/backoffice/settings/filetypes/'
resp = resp.follow()
assert 'HTML files' not in resp.text
def test_settings_filetypes_update(pub):
create_superuser(pub)
app = login(get_app(pub))
pub.cfg['filetypes'] = {
1: {'mimetypes': ['application/pdf', 'application/msword'],
'label': 'Text files'}
}
pub.write_cfg()
resp = app.get('/backoffice/settings/filetypes/')
assert 'Text files' in resp.text
FormDef.wipe()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = [fields.FileField(
id='1', label='1st field', type='file',
document_type={
'id': 1,
'mimetypes': ['application/pdf', 'application/msword'],
'label': 'Text files',
})]
formdef.store()
assert FormDef.get(formdef.id).fields[0].document_type == {
'id': 1,
'mimetypes': ['application/pdf', 'application/msword'],
'label': 'Text files',
}
resp = resp.click('Text files')
resp.forms[0]['mimetypes'] = 'application/vnd.oasis.opendocument.text, .doc, .docx, .pdf'
resp = resp.forms[0].submit('submit')
assert 'application/pdf' in pub.cfg['filetypes'][1]['mimetypes']
assert FormDef.get(formdef.id).fields[0].document_type == {
'id': 1,
'mimetypes': ['application/vnd.oasis.opendocument.text',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/pdf' ],
'label': 'Text files',
}
def test_settings_logs(pub):
# reset logging state
logging.shutdown()
if os.path.exists(os.path.join(pub.app_dir, 'wcs.log')):
os.unlink(os.path.join(pub.app_dir, 'wcs.log'))
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/settings/')
assert 'Logs' in resp.text
resp = resp.click('Logs')
assert '<td class="message">login</td>' in resp.text
resp = app.get('/backoffice/settings/debug_options')
assert resp.form['logger'].checked is True
resp.form['logger'].checked = False
resp = resp.form.submit()
resp = resp.follow()
assert 'Logs' not in resp.text
def test_settings_geolocation(pub):
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/settings/')
resp = resp.click('Geolocation')
resp.form['default-position$latlng'].value = '1.234;-1.234'
resp = resp.form.submit('cancel').follow()
resp = resp.click('Geolocation')
assert not 'value="1.234;-1.234' in resp.text
resp.form['default-position$latlng'].value = '1.234;-1.234'
resp = resp.form.submit().follow()
resp = resp.click('Geolocation')
assert 'value="1.234;-1.234' in resp.text
pub.reload_cfg()
assert pub.cfg['misc']['default-position'] == '1.234;-1.234'
def test_settings_permissions(pub):
create_superuser(pub)
role1 = create_role()
role1.name = 'foobar1'
role1.store()
role2 = Role(name='foobar2')
role2.store()
role3 = Role(name='foobar3')
role3.store()
app = login(get_app(pub))
resp = app.get('/backoffice/settings/admin-permissions')
# assert all first checkboxes are checked
assert resp.forms[0]['permissions$c-0-0'].checked
assert resp.forms[0]['permissions$c-1-0'].checked
assert resp.forms[0]['permissions$c-2-0'].checked
role2.allows_backoffice_access = False
role2.store()
resp = app.get('/backoffice/settings/admin-permissions')
assert resp.forms[0]['permissions$c-0-0'].checked
assert not resp.forms[0]['permissions$c-1-0'].checked
assert resp.forms[0]['permissions$c-2-0'].checked
resp.forms[0]['permissions$c-0-0'].checked = False
resp.forms[0]['permissions$c-1-0'].checked = True
resp = resp.forms[0].submit()
assert Role.get(role1.id).allows_backoffice_access is False
assert Role.get(role2.id).allows_backoffice_access is True
# give some roles access to the forms workshop (2nd checkbox) and to the
# workflows workshop (4th)
resp = app.get('/backoffice/settings/admin-permissions')
resp.forms[0]['permissions$c-1-1'].checked = True
resp.forms[0]['permissions$c-2-1'].checked = True
resp.forms[0]['permissions$c-2-3'].checked = True
resp = resp.forms[0].submit()
pub.reload_cfg()
assert set(pub.cfg['admin-permissions']['forms']) == set([role2.id, role3.id])
assert set(pub.cfg['admin-permissions']['workflows']) == set([role3.id])
# remove accesses
resp = app.get('/backoffice/settings/admin-permissions')
resp.forms[0]['permissions$c-1-1'].checked = False
resp.forms[0]['permissions$c-2-1'].checked = False
resp.forms[0]['permissions$c-2-3'].checked = False
resp = resp.forms[0].submit()
pub.reload_cfg()
assert pub.cfg['admin-permissions']['forms'] == []
assert pub.cfg['admin-permissions']['workflows'] == []
def test_settings_theme_preview(pub):
create_superuser(pub)
FormDef.wipe()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = []
formdef.store()
app = login(get_app(pub))
assert not 'alto/wcs.css' in app.get('/').text
resp = app.get('/backoffice/settings/themes')
assert resp.form['theme'].value in ('default', 'django')
# visit theme preview
resp = resp.click(href='theme_preview/alto/')
assert 'alto/wcs.css' in resp.text
# get into a form, making sure we are kept in theme preview
resp = resp.click('form title')
assert 'alto/wcs.css' in resp.text
# verify submits are not allowed
resp = resp.form.submit('submit')
assert "The theme preview doesn't support this." in resp.text
def test_settings_theme_download_upload(pub):
create_superuser(pub)
# download existing theme
app = login(get_app(pub))
resp = app.get('/backoffice/settings/themes')
resp = resp.click('download', index=0)
assert resp.headers['content-type'] == 'application/zip'
zip_content = BytesIO(resp.body)
zipf = zipfile.ZipFile(zip_content, 'a')
filelist = zipf.namelist()
assert 'alto/icon.png' in filelist
assert 'alto/desc.xml' in filelist
assert 'alto/template.ezt' in filelist
assert 'alto/wcs.css' in filelist
# modify it
zipf.writestr('alto/foobar.txt', 'XXX')
zipf.close()
# upload it
resp = app.get('/backoffice/settings/themes')
resp = resp.click('Install New Theme')
resp.form['file'] = Upload('alto-modified.zip', zip_content.getvalue())
resp = resp.form.submit()
assert os.path.exists(os.path.join(pub.app_dir, 'themes/alto/foobar.txt'))
assert app.get('/themes/alto/foobar.txt').text == 'XXX'
assert 'Directory listing denied' in app.get('/themes/alto/', status=200).text
assert app.get('/themes/alto/plop', status=404)
assert app.get('/themes/alto/../', status=404)
assert app.get('/themes/xxx/../', status=404)
def test_postgresql_settings(pub):
if not pub.is_using_postgresql():
pytest.skip('this requires SQL')
return
create_superuser(pub)
database = pub.cfg['postgresql']['database']
assert pub.cfg['postgresql'].get('port') == None
app = login(get_app(pub))
resp = app.get('/backoffice/settings/postgresql')
assert resp.form['database'].value == database
assert resp.form['port'].value == ''
resp = resp.form.submit()
assert pub.cfg['postgresql']['port'] == None
pub.cfg['postgresql']['port'] = 5432
pub.write_cfg()
resp = app.get('/backoffice/settings/postgresql')
assert resp.form['port'].value == '5432'
resp = resp.form.submit()
assert pub.cfg['postgresql']['port'] == 5432
resp = app.get('/backoffice/settings/postgresql')
resp.form['port'] = ''
resp = resp.form.submit()
assert pub.cfg['postgresql']['port'] == None
pub.cfg['postgresql']['port'] = '5432' # from an old convert-to-sql (before #10170)
pub.write_cfg()
resp = app.get('/backoffice/settings/postgresql')
assert resp.form['port'].value == '5432'
resp = resp.form.submit()
assert pub.cfg['postgresql']['port'] == 5432
def test_studio_home(pub, studio):
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/')
assert 'studio' in resp.text
resp = app.get('/backoffice/studio/')
assert '../forms/' in resp.text
assert '../cards/' in resp.text
assert '../workflows/' in resp.text
pub.cfg['admin-permissions'] = {}
for part in ('forms', 'cards', 'workflows'):
# check section link are not displayed if user has no access right
pub.cfg['admin-permissions'].update({part: ['x']}) # block access
pub.write_cfg()
if part != 'workflows':
resp = app.get('/backoffice/studio/')
assert '../%s/' % part not in resp.text
else:
resp = app.get('/backoffice/studio/', status=403) # totally closed
resp = app.get('/backoffice/')
assert 'studio' not in resp.text
def test_studio_workflows(pub, studio):
create_superuser(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/workflows/')
resp = resp.click(r'Default \(cards\)')
assert 'status/recorded/' in resp.text
assert 'status/deleted/' in resp.text
assert 'This is the default workflow,' in resp.text
def test_create_formdata(pub):
create_superuser(pub)
create_role()
FormDef.wipe()
target_formdef = FormDef()
target_formdef.name = 'target form'
target_formdef.enable_tracking_codes = True
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()
Workflow.wipe()
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 = ['_submitter']
jump.status = st2.id
jump.parent = st1
st1.items.append(jump)
create_formdata = CreateFormdataWorkflowStatusItem()
create_formdata.id = '_create_formdata'
create_formdata.formdef_slug = target_formdef.url_name
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_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()
app = login(get_app(pub))
resp = app.get('/backoffice/workflows/%s/status/%s/' % (wf.id, st2.id))
pq = resp.pyquery.remove_namespaces()
assert pq('option[value="New Form Creation"]').text() == 'New Form Creation'
assert pq('#itemId__create_formdata a')[0].text == 'New Form Creation (target form)'
resp = resp.click('Edit', href='items/_create_formdata/', )
resp.form.set('varname', 'resubmitted')
resp = resp.form.submit(name='submit')
resp = resp.follow()
# checks that nothing changed after submit
wf2 = Workflow.select()[0]
item = wf2.get_status('2').items[0]
assert item.varname == 'resubmitted'
assert isinstance(item, CreateFormdataWorkflowStatusItem)
wf.get_status('2').items[0].label = 'really resubmit'
# duplicate
resp = app.get('/backoffice/workflows/%s/status/%s/items/_create_formdata/' % (wf.id, st2.id))
resp.form.set('mappings$element1$field_id', '0')
resp = resp.form.submit(name='submit')
pq = resp.pyquery.remove_namespaces()
assert pq('.error').text() == 'Some destination fields are duplicated'
# check setting map_fields_by_varname on new action
resp = app.get('/backoffice/workflows/%s/status/%s/' % (wf.id, st2.id))
resp.forms['new-action-form']['action-formdata-action'] = 'New Form Creation'
resp = resp.forms['new-action-form'].submit()
resp = resp.follow()
resp = resp.click(r'New Form Creation \(not configured\)')
resp.form['formdef_slug'] = 'target-form' # set target form
resp = resp.form.submit('submit')
assert 'Please define new mappings' in resp
assert resp.form['map_fields_by_varname'].checked is False
resp.form['map_fields_by_varname'].checked = True
resp = resp.form.submit('submit')
resp = resp.follow()
wf2 = Workflow.get(wf2.id)
assert wf2.possible_status[-1].items[-1].map_fields_by_varname is True
assert wf2.possible_status[-1].items[-1].mappings is None
def test_block_new(pub, blocks_feature):
create_superuser(pub)
create_role()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/')
resp = resp.click('Fields blocks')
resp = resp.click('New field block')
resp.form['name'] = 'field block'
resp = resp.form.submit()
assert resp.location == 'http://example.net/backoffice/forms/blocks/1/'
resp = resp.follow()
assert '<h2>field block' in resp
assert 'There are not yet any fields' in resp
resp.form['label'] = 'foobar'
resp.form['type'] = 'string'
resp = resp.form.submit()
assert resp.location == 'http://example.net/backoffice/forms/blocks/1/'
resp = resp.follow()
resp.form['label'] = 'barfoo'
resp.form['type'] = 'string'
resp = resp.form.submit()
assert resp.location == 'http://example.net/backoffice/forms/blocks/1/'
resp = resp.follow()
assert len(BlockDef.get(1).fields) == 2
assert str(BlockDef.get(1).fields[0].id) != '1' # don't use integers
def test_block_options(pub, blocks_feature):
create_superuser(pub)
create_role()
BlockDef.wipe()
block = BlockDef()
block.name = 'foobar'
block.fields = [fields.StringField(id='123', required=True, label='Test', type='string')]
block.store()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/blocks/%s/' % block.id)
resp = resp.click(href=re.compile('^settings$'))
assert 'readonly' not in resp.form['slug'].attrs
resp.form['name'] = 'foo bar'
resp = resp.form.submit('submit')
assert BlockDef.get(block.id).name == 'foo bar'
FormDef.wipe()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = [
fields.BlockField(id='0', label='test', type='block:%s' % block.slug),
]
formdef.store()
resp = app.get('/backoffice/forms/blocks/%s/' % block.id)
resp = resp.click(href=re.compile('^settings$'))
assert 'readonly' in resp.form['slug'].attrs
resp = resp.form.submit('cancel')
resp = resp.follow()
def test_block_export_import(pub, blocks_feature):
create_superuser(pub)
create_role()
BlockDef.wipe()
block = BlockDef()
block.name = 'foobar'
block.fields = [fields.StringField(id='123', required=True, label='Test', type='string')]
block.store()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/blocks/%s/' % block.id)
resp = resp.click(href=re.compile('^export$'))
xml_export = resp.text
resp = app.get('/backoffice/forms/blocks/')
resp = resp.click(href='import')
resp = resp.form.submit('cancel') # shouldn't block on missing file
resp = resp.follow()
resp = resp.click(href='import')
resp = resp.form.submit()
assert 'ere were errors processing your form.' in resp
resp.form['file'] = Upload('block', xml_export.encode('utf-8'))
resp = resp.form.submit()
resp = resp.follow()
assert BlockDef.count() == 2
new_blockdef = [x for x in BlockDef.select() if str(x.id) != str(block.id)][0]
assert new_blockdef.name == 'Copy of foobar'
assert new_blockdef.slug == 'foobar_1'
assert len(new_blockdef.fields) == 1
assert new_blockdef.fields[0].id == '123'
resp = app.get('/backoffice/forms/blocks/')
resp = resp.click(href='import')
resp.form['file'] = Upload('block', xml_export.encode('utf-8'))
resp = resp.form.submit()
assert 'Copy of foobar (2)' in [x.name for x in BlockDef.select()]
# import invalid content
resp = app.get('/backoffice/forms/blocks/')
resp = resp.click(href='import')
resp.form['file'] = Upload('block', b'whatever')
resp = resp.form.submit()
assert 'Invalid File' in resp
def test_block_delete(pub, blocks_feature):
create_superuser(pub)
create_role()
BlockDef.wipe()
FormDef.wipe()
block = BlockDef()
block.name = 'foobar'
block.fields = [fields.StringField(id='123', required=True, label='Test', type='string')]
block.store()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/blocks/%s/' % block.id)
resp = resp.click(href=re.compile('^delete$'))
assert 'You are about to irrevocably delete this block.' in resp
resp = resp.form.submit()
resp = resp.follow()
assert BlockDef.count() == 0
# in use
BlockDef.wipe()
block = BlockDef()
block.name = 'foobar'
block.fields = [fields.StringField(id='123', required=True, label='Test', type='string')]
block.store()
FormDef.wipe()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = [
fields.BlockField(id='0', label='test', type='block:%s' % block.slug),
]
formdef.store()
resp = app.get('/backoffice/forms/blocks/%s/' % block.id)
resp = resp.click(href=re.compile('^delete$'))
assert 'This block is still used' in resp
def test_block_edit_duplicate_delete_field(pub, blocks_feature):
create_superuser(pub)
create_role()
BlockDef.wipe()
block = BlockDef()
block.name = 'foobar'
block.fields = [fields.StringField(id='123', required=True, label='Test', type='string')]
block.store()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/blocks/%s/' % block.id)
resp = resp.click(href=re.compile('123/$'))
resp.form['required'].checked = False
resp.form['varname'] = 'test'
resp = resp.form.submit('submit')
resp = resp.follow()
assert BlockDef.get(block.id).fields[0].required is False
assert BlockDef.get(block.id).fields[0].varname == 'test'
resp = resp.click(href=re.compile('123/duplicate$'))
resp = resp.follow()
assert len(BlockDef.get(block.id).fields) == 2
resp = resp.click(href='%s/delete' % BlockDef.get(block.id).fields[1].id)
resp = resp.form.submit('submit')
resp = resp.follow()
assert len(BlockDef.get(block.id).fields) == 1
def test_block_use_in_formdef(pub, blocks_feature):
create_superuser(pub)
create_role()
FormDef.wipe()
BlockDef.wipe()
block = BlockDef()
block.name = 'foobar'
block.fields = [fields.StringField(id='123', required=True, label='Test', type='string')]
block.store()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = []
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/1/fields/')
resp.forms[0]['label'] = 'a block field'
resp.forms[0]['type'] = 'block:foobar'
resp = resp.forms[0].submit().follow()
assert 'a block field' in resp.text
resp = resp.click('Edit', href='1/')
assert resp.form['max_items'].value == '1'