1367 lines
52 KiB
Python
1367 lines
52 KiB
Python
# w.c.s. - web application for online forms
|
|
# Copyright (C) 2005-2010 Entr'ouvert
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import copy
|
|
import hashlib
|
|
import io
|
|
import mimetypes
|
|
import os
|
|
|
|
try:
|
|
import lasso
|
|
except ImportError:
|
|
lasso = None
|
|
import shutil
|
|
import xml.etree.ElementTree as ET
|
|
import zipfile
|
|
|
|
from django.utils.encoding import force_bytes, force_text
|
|
from quixote import get_publisher, get_request, get_response, get_session, redirect
|
|
from quixote.directory import Directory
|
|
from quixote.html import TemplateIO, htmltext
|
|
|
|
from wcs.backoffice.studio import StudioDirectory
|
|
from wcs.blocks import BlockDef
|
|
from wcs.carddef import CardDef
|
|
from wcs.data_sources import NamedDataSource
|
|
from wcs.formdef import FormDef
|
|
from wcs.qommon import N_, _, errors, get_cfg, ident, misc, template
|
|
from wcs.qommon.admin.cfg import cfg_submit
|
|
from wcs.qommon.admin.emails import EmailsDirectory
|
|
from wcs.qommon.admin.logger import LoggerDirectory
|
|
from wcs.qommon.admin.menu import error_page
|
|
from wcs.qommon.admin.settings import SettingsDirectory as QommonSettingsDirectory
|
|
from wcs.qommon.admin.texts import TextsDirectory
|
|
from wcs.qommon.afterjobs import AfterJob
|
|
from wcs.qommon.backoffice.menu import html_top
|
|
from wcs.qommon.form import (
|
|
CheckboxesTableWidget,
|
|
CheckboxesWidget,
|
|
CheckboxWidget,
|
|
ComputedExpressionWidget,
|
|
FileWidget,
|
|
Form,
|
|
IntWidget,
|
|
MapWidget,
|
|
PasswordWidget,
|
|
SingleSelectWidget,
|
|
StringWidget,
|
|
TextWidget,
|
|
UrlWidget,
|
|
WidgetList,
|
|
)
|
|
from wcs.workflows import Workflow, WorkflowImportError
|
|
|
|
from .api_access import ApiAccessDirectory
|
|
from .data_sources import NamedDataSourcesDirectory
|
|
from .fields import FieldDefPage, FieldsDirectory
|
|
from .wscalls import NamedWsCallsDirectory
|
|
|
|
|
|
class UserFormDirectory(Directory):
|
|
_q_exports = ['']
|
|
|
|
|
|
class IdentificationDirectory(Directory):
|
|
_q_exports = ['']
|
|
|
|
def _q_index(self):
|
|
get_response().breadcrumb.append(('identification/', _('Identification')))
|
|
identification_cfg = get_cfg('identification', {})
|
|
form = Form(enctype='multipart/form-data')
|
|
methods = [
|
|
('password', _('Simple local username / password'), 'password'),
|
|
]
|
|
if lasso is not None:
|
|
methods.insert(0, ('idp', _('Delegated to SAML identity provider'), 'idp'))
|
|
methods.append(('fc', _('Delegated to FranceConnect'), 'fc'))
|
|
form.add(
|
|
CheckboxesWidget,
|
|
'methods',
|
|
title=_('Methods'),
|
|
value=identification_cfg.get('methods'),
|
|
options=methods,
|
|
inline=False,
|
|
required=True,
|
|
)
|
|
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('..')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
cfg_submit(form, 'identification', ['methods'])
|
|
if not form.has_errors():
|
|
return redirect('..')
|
|
|
|
html_top('settings', title=_('Identification'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('Identification')
|
|
if lasso is None:
|
|
cls = 'infonotice'
|
|
if identification_cfg.get('methods') and 'idp' in identification_cfg.get('methods'):
|
|
cls = 'errornotice'
|
|
r += htmltext('<p class="%s">%s</p>') % (
|
|
cls,
|
|
_(
|
|
'Delegated to SAML identity provider \
|
|
authentication is unavailable. Lasso must be installed to use it.'
|
|
),
|
|
)
|
|
r += form.render()
|
|
return r.getvalue()
|
|
|
|
def _q_lookup(self, component):
|
|
get_response().breadcrumb.append(('identification/', _('Identification')))
|
|
return ident.get_method_admin_directory(component)
|
|
|
|
|
|
class UserFieldDefPage(FieldDefPage):
|
|
section = 'settings'
|
|
blacklisted_attributes = ['condition']
|
|
|
|
|
|
class UserFieldsDirectory(FieldsDirectory):
|
|
_q_exports = ['', 'update_order', 'new', 'mapping', 'template']
|
|
|
|
section = 'settings'
|
|
field_def_page_class = UserFieldDefPage
|
|
support_import = False
|
|
blacklisted_types = ['page']
|
|
field_var_prefix = '..._user_var_'
|
|
|
|
def index_bottom(self):
|
|
r = TemplateIO(html=True)
|
|
r += get_session().display_message()
|
|
r += htmltext('<div class="bo-block">')
|
|
r += htmltext('<h2>%s</h2>') % _('Fields Mapping')
|
|
r += htmltext('<p>%s</p>') % _(
|
|
'These settings make it possible to assign custom user fields to standard user fields.'
|
|
)
|
|
r += self.mapping_form().render()
|
|
r += htmltext('</div>')
|
|
r += htmltext('<div class="bo-block">')
|
|
r += htmltext('<h2>%s</h2>') % _('Sidebar Template')
|
|
r += self.sidebar_template_form().render()
|
|
r += htmltext('</div>')
|
|
return r.getvalue()
|
|
|
|
def mapping_form(self):
|
|
users_cfg = get_cfg('users', {})
|
|
options = [(None, _('None'), '')] + [(x.id, x.label, x.id) for x in self.objectdef.fields]
|
|
|
|
form = Form(action='mapping', id='mapping')
|
|
field_name_value = users_cfg.get('field_name')
|
|
if isinstance(field_name_value, str):
|
|
field_name_value = [field_name_value]
|
|
form.add(
|
|
WidgetList,
|
|
'field_name',
|
|
title=_('Field(s) for Name'),
|
|
element_type=SingleSelectWidget,
|
|
value=field_name_value,
|
|
element_kwargs={'render_br': False, 'options': options},
|
|
)
|
|
form.add(
|
|
SingleSelectWidget,
|
|
'field_email',
|
|
title=_('Field for Email'),
|
|
value=users_cfg.get('field_email'),
|
|
options=options,
|
|
)
|
|
form.add_submit('submit', _('Submit'))
|
|
return form
|
|
|
|
def mapping(self):
|
|
form = self.mapping_form()
|
|
cfg_submit(form, 'users', ['field_name', 'field_email'])
|
|
return redirect('.')
|
|
|
|
@classmethod
|
|
def sidebar_template_form(cls, action='template'):
|
|
users_cfg = get_cfg('users', {})
|
|
form = Form(action=action, id='template')
|
|
form.add(
|
|
TextWidget,
|
|
'sidebar_template',
|
|
value=users_cfg.get('sidebar_template') or '{{ form_user_display_name }}',
|
|
required=False,
|
|
validation_function=ComputedExpressionWidget.validate_template,
|
|
rows=4,
|
|
)
|
|
form.add_submit('submit', _('Submit'))
|
|
return form
|
|
|
|
def template(self):
|
|
form = self.sidebar_template_form()
|
|
if form.has_errors():
|
|
get_session().message = ('error', form.get_widget('sidebar_template').get_error())
|
|
return redirect('.')
|
|
cfg_submit(form, 'users', ['sidebar_template'])
|
|
return redirect('.')
|
|
|
|
|
|
class UserFieldsFormDef(FormDef):
|
|
"""Class to handle custom user fields, it loads and saves from/to
|
|
an XML string stored in the configuration (at users/formdef)"""
|
|
|
|
def __init__(self, publisher=None):
|
|
if publisher is None:
|
|
publisher = get_publisher()
|
|
self.publisher = publisher
|
|
users_cfg = publisher.cfg.get('users', {})
|
|
xml_import = users_cfg.get('formdef')
|
|
self.fields = [] # make sure fields is a list
|
|
self.id = None # required for XML import/export
|
|
if xml_import:
|
|
try:
|
|
tree = ET.fromstring(xml_import)
|
|
except Exception:
|
|
pass
|
|
else:
|
|
obj = FormDef.import_from_xml_tree(tree, include_id=True)
|
|
self.fields = obj.fields
|
|
self.max_field_id = obj.max_field_id
|
|
else:
|
|
# compatibility with older location
|
|
filename = os.path.join(publisher.app_dir, 'config', 'user')
|
|
if os.path.exists(filename):
|
|
try:
|
|
formdef = FormDef.get_filename(filename)
|
|
except KeyError:
|
|
pass
|
|
else:
|
|
self.fields = formdef.fields
|
|
# and remove old file, to keep clean
|
|
self.store()
|
|
os.unlink(filename)
|
|
|
|
@property
|
|
def name(self):
|
|
return _('User Fields')
|
|
|
|
def get_admin_url(self):
|
|
base_url = get_publisher().get_backoffice_url()
|
|
return '%s/settings/users/fields/' % base_url
|
|
|
|
def store(self, comment=None):
|
|
xml_export = self.export_to_xml(include_id=True)
|
|
users_cfg = self.publisher.cfg.get('users', {})
|
|
users_cfg['formdef'] = ET.tostring(xml_export)
|
|
self.publisher.cfg['users'] = users_cfg
|
|
self.publisher.write_cfg()
|
|
if self.publisher.is_using_postgresql():
|
|
from wcs import sql
|
|
|
|
sql.do_user_table()
|
|
|
|
|
|
class UsersDirectory(Directory):
|
|
_q_exports = ['', 'fields']
|
|
|
|
def _q_index(self):
|
|
return redirect('fields/')
|
|
|
|
def _q_traverse(self, path):
|
|
get_response().breadcrumb.append(('users/', _('Users')))
|
|
self.fields = UserFieldsDirectory(UserFieldsFormDef())
|
|
return Directory._q_traverse(self, path)
|
|
|
|
|
|
class FileTypesDirectory(Directory):
|
|
_q_exports = ['', 'add']
|
|
|
|
def get_form(self, filetype=None):
|
|
filetype = filetype or {}
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(StringWidget, 'label', title=_('Label'), required=True, value=filetype.get('label'))
|
|
form.add(
|
|
StringWidget,
|
|
'mimetypes',
|
|
title=_('Mime types'),
|
|
required=True,
|
|
size=70,
|
|
value=', '.join(filetype.get('mimetypes', [])),
|
|
)
|
|
form.add_submit('submit', _('Save'))
|
|
return form
|
|
|
|
@classmethod
|
|
def parse_mimetypes(cls, value):
|
|
def ensure_mimetype(x):
|
|
x = x.strip()
|
|
if x.startswith('.'):
|
|
mime_type = mimetypes.guess_type('foobar' + x)[0]
|
|
if mime_type:
|
|
return mime_type
|
|
return x
|
|
|
|
return [ensure_mimetype(x) for x in value.split(',')]
|
|
|
|
@classmethod
|
|
def format_mimetypes(cls, types):
|
|
if not types:
|
|
return ''
|
|
l = []
|
|
ellipsis = '...'
|
|
for mimetype in types:
|
|
if sum([len(x) for x in l]) > 80:
|
|
# string got too long already, stop this now and we'll get an
|
|
# ellipsis
|
|
break
|
|
ext = mimetypes.guess_extension(mimetype)
|
|
if ext:
|
|
l.append('%s (%s)' % (mimetype, ext))
|
|
else:
|
|
l.append(mimetype)
|
|
else:
|
|
# we got to the end of the list, we won't need an ellipsis
|
|
ellipsis = ''
|
|
return ', '.join(l) + ellipsis
|
|
|
|
def _q_index(self):
|
|
html_top('settings', title=_('File Types'))
|
|
filetypes_cfg = get_cfg('filetypes', {})
|
|
|
|
form = self.get_form()
|
|
if form.get_submit() == 'submit':
|
|
if filetypes_cfg:
|
|
new_filetype_id = max(filetypes_cfg.keys()) + 1
|
|
else:
|
|
new_filetype_id = 1
|
|
new_filetype = {
|
|
'label': form.get_widget('label').parse(),
|
|
'mimetypes': self.parse_mimetypes(form.get_widget('mimetypes').parse()),
|
|
}
|
|
filetypes_cfg[new_filetype_id] = new_filetype
|
|
get_publisher().cfg['filetypes'] = filetypes_cfg
|
|
get_publisher().write_cfg()
|
|
return redirect('.')
|
|
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('File Types')
|
|
if filetypes_cfg:
|
|
r += htmltext('<ul class="objects-list biglist filetypes">')
|
|
for filetype_id, filetype in filetypes_cfg.items():
|
|
r += htmltext('<li>')
|
|
r += htmltext(' <a href="%s">%s <span class="extra-info">(%s)</span></a>') % (
|
|
filetype_id,
|
|
filetype.get('label'),
|
|
self.format_mimetypes(filetype.get('mimetypes')),
|
|
)
|
|
r += htmltext('</li>')
|
|
r += htmltext('</ul>')
|
|
else:
|
|
r += htmltext('<div class="infonotice"><p>')
|
|
r += _('There are no file type defined at the moment.')
|
|
r += htmltext('</p></div>')
|
|
|
|
r += htmltext('<h3>%s</h3>') % _('New file type')
|
|
r += form.render()
|
|
|
|
return r.getvalue()
|
|
|
|
def _q_traverse(self, path):
|
|
get_response().breadcrumb.append(('filetypes/', _('File Types')))
|
|
return Directory._q_traverse(self, path)
|
|
|
|
def _q_lookup(self, component):
|
|
filetypes_cfg = get_cfg('filetypes', {})
|
|
try:
|
|
filetype_id = int(component)
|
|
filetype = filetypes_cfg[filetype_id]
|
|
except (ValueError, KeyError):
|
|
raise errors.TraversalError()
|
|
|
|
form = self.get_form(filetype)
|
|
form.add_submit('cancel', _('Cancel'))
|
|
form.add_submit('delete', _('Delete'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('.')
|
|
|
|
if form.get_submit() == 'submit':
|
|
old_filetype = filetype.copy()
|
|
filetype['label'] = form.get_widget('label').parse()
|
|
filetype['mimetypes'] = self.parse_mimetypes(form.get_widget('mimetypes').parse())
|
|
FormDef.update_filetype(filetype_id, old_filetype, filetype)
|
|
get_publisher().write_cfg()
|
|
return redirect('.')
|
|
|
|
if form.get_submit() == 'delete':
|
|
del filetypes_cfg[filetype_id]
|
|
get_publisher().write_cfg()
|
|
return redirect('.')
|
|
|
|
html_top('settings', title=_('File Types'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s - %s</h2>') % (_('File Type'), filetype['label'])
|
|
r += form.render()
|
|
return r.getvalue()
|
|
|
|
|
|
class ThemePreviewDirectory(Directory):
|
|
def _q_traverse(self, path):
|
|
if len(path) < 2:
|
|
return error_page('settings', _('Invalid URL'))
|
|
|
|
theme_id = path[0]
|
|
branding = get_publisher().cfg.get('branding', {})
|
|
original_branding = branding.copy()
|
|
get_publisher().cfg['branding'] = branding
|
|
get_publisher().cfg['branding']['theme'] = theme_id
|
|
if 'template' in get_publisher().cfg['branding']:
|
|
del get_publisher().cfg['branding']['template']
|
|
|
|
root_directory = get_publisher().root_directory_class()
|
|
|
|
response = get_response()
|
|
response.reset_includes()
|
|
response.filter = {}
|
|
del response.breadcrumb
|
|
|
|
if path[1] in ('backoffice', 'admin') or get_request().get_method() == 'POST':
|
|
from wcs.qommon.template import error_page as base_error_page
|
|
|
|
output = base_error_page(_("The theme preview doesn't support this."))
|
|
else:
|
|
output = root_directory._q_traverse(path[1:])
|
|
|
|
from wcs.qommon.template import decorate
|
|
|
|
if isinstance(output, template.QommonTemplateResponse):
|
|
output = template.render(output.templates, output.context)
|
|
theme_preview = decorate(output, response)
|
|
|
|
# restore original branding in case it has been changed
|
|
get_publisher().cfg['branding'] = original_branding
|
|
get_publisher().write_cfg()
|
|
response.filter['raw'] = True
|
|
|
|
return theme_preview
|
|
|
|
|
|
class SettingsDirectory(QommonSettingsDirectory):
|
|
_q_exports = [
|
|
'',
|
|
'themes',
|
|
'users',
|
|
'template',
|
|
'emails',
|
|
'debug_options',
|
|
'language',
|
|
('import', 'p_import'),
|
|
'export',
|
|
'identification',
|
|
'sitename',
|
|
'sms',
|
|
'certificates',
|
|
'texts',
|
|
'install_theme',
|
|
'session',
|
|
'download_theme',
|
|
'postgresql',
|
|
('admin-permissions', 'admin_permissions'),
|
|
'geolocation',
|
|
'theme_preview',
|
|
'filetypes',
|
|
('user-template', 'user_template'),
|
|
('data-sources', 'data_sources'),
|
|
'wscalls',
|
|
'logs',
|
|
('api-access', 'api_access'),
|
|
]
|
|
|
|
emails = EmailsDirectory()
|
|
identification = IdentificationDirectory()
|
|
users = UsersDirectory()
|
|
texts = TextsDirectory()
|
|
theme_preview = ThemePreviewDirectory()
|
|
filetypes = FileTypesDirectory()
|
|
data_sources = NamedDataSourcesDirectory()
|
|
wscalls = NamedWsCallsDirectory()
|
|
logs = LoggerDirectory()
|
|
api_access = ApiAccessDirectory()
|
|
|
|
def _q_index(self):
|
|
html_top('settings', title=_('Settings'))
|
|
r = TemplateIO(html=True)
|
|
|
|
disabled_screens_option = get_publisher().get_site_option('settings-disabled-screens') or ''
|
|
disabled_screens = [x.strip() for x in disabled_screens_option.split(',')]
|
|
|
|
def enabled(screen):
|
|
return screen not in disabled_screens
|
|
|
|
r += htmltext('<div class="splitcontent-left">')
|
|
|
|
if enabled('storage') and (
|
|
get_publisher().has_site_option('postgresql') or get_cfg('postgresql', {})
|
|
):
|
|
r += htmltext('<div class="section">')
|
|
r += htmltext('<h2>%s</h2>') % _('Storage')
|
|
r += htmltext('<dl> <dt><a href="postgresql">%s</a></dt> <dd>%s</dd> </dl>') % (
|
|
_('PostgreSQL Settings'),
|
|
_('Configure access to PostgreSQL database'),
|
|
)
|
|
r += htmltext('</div>')
|
|
|
|
r += htmltext('<div class="section">')
|
|
r += htmltext('<h2>%s</h2>') % _('Security')
|
|
r += htmltext('<dl>')
|
|
|
|
if enabled('identification'):
|
|
r += htmltext('<dt><a href="identification/">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Identification'),
|
|
_('Configure identification parameters'),
|
|
)
|
|
|
|
identification_cfg = get_cfg('identification', {})
|
|
for method in identification_cfg.get('methods', []):
|
|
try:
|
|
method_admin = ident.get_method_admin_directory(method)
|
|
except AttributeError:
|
|
continue
|
|
|
|
r += htmltext('<dt><a href="identification/%s/">%s</a></dt> <dd>%s</dd>') % (
|
|
method,
|
|
_(method_admin.title),
|
|
_(method_admin.label),
|
|
)
|
|
|
|
if enabled('session'):
|
|
r += htmltext('<dt><a href="session">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Session'),
|
|
_('Configure session management'),
|
|
)
|
|
|
|
if enabled('permissions'):
|
|
roles = list(get_publisher().role_class.select())
|
|
if roles:
|
|
r += htmltext('<dt><a href="admin-permissions">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Admin Permissions'),
|
|
_('Configure access to the administration interface'),
|
|
)
|
|
|
|
if enabled('api-access'):
|
|
r += htmltext('<dt><a href="api-access">%s</a></dt> <dd>%s</dd>') % (
|
|
_('API access'),
|
|
_('Configure access to the API endpoints'),
|
|
)
|
|
|
|
r += htmltext('</dl></div>')
|
|
|
|
if enabled('import-export'):
|
|
r += htmltext('<div class="section">')
|
|
r += htmltext('<h2>%s</h2>') % _('Import / Export')
|
|
|
|
r += htmltext('<dl>')
|
|
r += htmltext('<dt><a href="import">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Import'),
|
|
_('Import data from another site'),
|
|
)
|
|
r += htmltext('<dt><a href="export">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Export'),
|
|
_('Export data for another site'),
|
|
)
|
|
r += htmltext('</dl>')
|
|
r += htmltext('</div>')
|
|
|
|
if enabled('misc'):
|
|
r += htmltext('<div class="section">')
|
|
r += htmltext('<h2>%s</h2>') % _('Misc')
|
|
r += htmltext('<dl>')
|
|
r += htmltext('<dt><a href="debug_options">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Debug Options'),
|
|
_('Configure options useful for debugging'),
|
|
)
|
|
if get_cfg('debug', {}).get('logger', True):
|
|
r += htmltext('<dt><a href="logs/">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Logs'),
|
|
_('Access application log files'),
|
|
)
|
|
r += htmltext('</dl>')
|
|
r += htmltext('</div>')
|
|
|
|
r += htmltext('</div>')
|
|
|
|
r += htmltext('<div class="splitcontent-right">')
|
|
r += htmltext('<div class="section">')
|
|
r += htmltext('<h2>%s</h2>') % _('Customisation')
|
|
r += htmltext('<div>')
|
|
|
|
r += htmltext('<dl>')
|
|
if enabled('sitename'):
|
|
r += htmltext('<dt><a href="sitename">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Site Name and Addresses'),
|
|
_('Configure site name and addresses'),
|
|
)
|
|
if enabled('language'):
|
|
r += htmltext('<dt><a href="language">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Language'),
|
|
_('Configure site language'),
|
|
)
|
|
if enabled('theme'):
|
|
r += htmltext('<dt><a href="themes">%s</a></dt> <dd>%s</dd>') % (_('Theme'), _('Configure theme'))
|
|
if enabled('template'):
|
|
r += htmltext('<dt><a href="template">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Template'),
|
|
_('Configure template'),
|
|
)
|
|
if enabled('geolocation'):
|
|
r += htmltext('<dt><a href="geolocation">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Geolocation'),
|
|
_('Configure geolocation'),
|
|
)
|
|
if enabled('users'):
|
|
r += htmltext('<dt><a href="users/">%s</a></dt> <dd>%s</dd>') % (_('Users'), _('Configure users'))
|
|
else:
|
|
# minimal options
|
|
r += htmltext('<dt><a href="user-template">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Users'),
|
|
_('Configure sidebar template for users'),
|
|
)
|
|
if enabled('emails'):
|
|
r += htmltext('<dt><a href="emails/">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Emails'),
|
|
_('Configure email settings'),
|
|
)
|
|
if enabled('sms') and get_publisher().use_sms_feature:
|
|
r += htmltext('<dt><a href="sms">%s</a></dt> <dd>%s</dd>') % (
|
|
_('SMS'),
|
|
_('Configure SMS settings'),
|
|
)
|
|
if enabled('texts') and self.texts.texts_dict:
|
|
r += htmltext('<dt><a href="texts/">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Texts'),
|
|
_('Configure text that appears on some pages'),
|
|
)
|
|
if enabled('filetypes'):
|
|
r += htmltext('<dt><a href="filetypes/">%s</a></dt> <dd>%s</dd>') % (
|
|
_('File Types'),
|
|
_('Configure known file types'),
|
|
)
|
|
r += htmltext('<dt><a href="data-sources/">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Data sources'),
|
|
_('Configure data sources'),
|
|
)
|
|
r += htmltext('<dt><a href="wscalls/">%s</a></dt> <dd>%s</dd>') % (
|
|
_('Webservice calls'),
|
|
_('Configure webservice calls'),
|
|
)
|
|
r += htmltext('</dl>')
|
|
r += htmltext('</div>')
|
|
r += htmltext('</div>')
|
|
|
|
r += htmltext('</div>')
|
|
return r.getvalue()
|
|
|
|
def admin_permissions(self):
|
|
permissions_cfg = get_cfg('admin-permissions', {})
|
|
form = Form(enctype='multipart/form-data')
|
|
|
|
permissions = [_('Backoffice')]
|
|
|
|
permission_keys = []
|
|
admin_sections = [
|
|
('forms', N_('Forms')),
|
|
('cards', N_('Card Models')),
|
|
('workflows', N_('Workflows')),
|
|
('users', N_('Users')),
|
|
('roles', N_('Roles')),
|
|
('categories', N_('Categories')),
|
|
('settings', N_('Settings')),
|
|
]
|
|
for k, v in admin_sections:
|
|
if k == 'cards' and not StudioDirectory.is_visible():
|
|
continue
|
|
permissions.append(_(v))
|
|
permission_keys.append(k)
|
|
|
|
rows = []
|
|
value = []
|
|
roles = [x for x in get_publisher().role_class.select(order_by='name') if not x.is_internal()]
|
|
for role in roles:
|
|
rows.append(role.name)
|
|
value.append([role.allows_backoffice_access])
|
|
for k in permission_keys:
|
|
authorised_roles = [str(x) for x in permissions_cfg.get(k) or []]
|
|
value[-1].append(bool(str(role.id) in authorised_roles))
|
|
colrows_hash = hashlib.md5(force_bytes('%r-%r' % (rows, permissions))).hexdigest()
|
|
|
|
form.add_hidden('hash', colrows_hash)
|
|
form.add(CheckboxesTableWidget, 'permissions', rows=rows, columns=permissions)
|
|
form.get_widget('permissions').set_value(value)
|
|
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('.')
|
|
|
|
if form.get_widget('hash').parse() != colrows_hash:
|
|
# The columns and rows are made of indices; permissions could be
|
|
# wrongly assigned if there were some changes to the columns and
|
|
# rows between the form being displayed and submitted.
|
|
form.get_widget('permissions').set_error(
|
|
_('Changes were made to roles or permissions while the table was displayed.')
|
|
)
|
|
|
|
if not form.is_submitted() or form.has_errors():
|
|
get_response().breadcrumb.append(('admin-permissions', _('Admin Permissions')))
|
|
html_top('settings', title=_('Admin Permissions'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<div class="admin-permissions">')
|
|
r += htmltext('<h2>%s</h2>') % _('Admin Permissions')
|
|
r += form.render()
|
|
r += htmltext('</div>')
|
|
return r.getvalue()
|
|
else:
|
|
value = form.get_widget('permissions').parse()
|
|
permissions = {}
|
|
for key in permission_keys:
|
|
permissions[key] = []
|
|
for i, role in enumerate(roles):
|
|
permission_row = value[i]
|
|
if role.allows_backoffice_access != permission_row[0]:
|
|
role.allows_backoffice_access = permission_row[0]
|
|
role.store()
|
|
for j, key in enumerate(permission_keys):
|
|
if permission_row[j + 1]:
|
|
permissions[key].append(role.id)
|
|
get_publisher().cfg['admin-permissions'] = permissions
|
|
get_publisher().write_cfg()
|
|
return redirect('.')
|
|
|
|
def themes(self):
|
|
request = get_request()
|
|
|
|
if 'theme' not in request.form:
|
|
current_theme = get_cfg('branding', {}).get('theme', 'default')
|
|
|
|
get_response().breadcrumb.append(('themes', _('Themes')))
|
|
html_top('settings', title=_('Themes'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext("<h2>%s</h2>") % _('Themes')
|
|
|
|
r += get_session().display_message()
|
|
|
|
r += htmltext('<a rel="popup" href="install_theme">%s</a>') % _('Install New Theme')
|
|
|
|
r += htmltext('<form action="themes" enctype="multipart/form-data" method="post">')
|
|
themes = template.get_themes_dict()
|
|
r += htmltext('<ul class="biglist themes">')
|
|
for theme, theme_dict in sorted(themes.items()):
|
|
label = theme_dict.get('label')
|
|
if 'version' in theme_dict:
|
|
label = '%s (%s)' % (label, theme_dict.get('version'))
|
|
if current_theme == theme:
|
|
checked = ' checked="checked"'
|
|
else:
|
|
checked = ''
|
|
r += htmltext('<li>')
|
|
r += htmltext('<strong class="label"><label>')
|
|
r += htmltext(' <input name="theme" value="%s" type="radio"%s>%s</input>') % (
|
|
theme,
|
|
checked,
|
|
label,
|
|
)
|
|
r += htmltext('</label></strong>')
|
|
if theme_dict.get('icon'):
|
|
r += htmltext('<img src="/themes/%s/icon.png" alt="" class="theme-icon" />') % theme
|
|
r += htmltext('<p class="details">%s') % theme_dict.get('desc', '')
|
|
r += htmltext(' [<a href="download_theme?theme=%s">%s</a>]') % (theme, _('download'))
|
|
r += htmltext(' [<a class="theme-preview" href="theme_preview/%s/">%s</a>]') % (
|
|
theme,
|
|
_('preview'),
|
|
)
|
|
if theme_dict.get('author'):
|
|
r += htmltext('<br/>')
|
|
r += htmltext(_('by %s')) % theme_dict.get('author')
|
|
r += htmltext('</p>')
|
|
r += htmltext('</li>')
|
|
r += htmltext('</ul>')
|
|
r += htmltext('<div class="buttons">')
|
|
r += htmltext('<input type="submit" name="submit" value="%s" />') % _('Submit')
|
|
r += htmltext('</div>')
|
|
r += htmltext('</form>')
|
|
return r.getvalue()
|
|
else:
|
|
themes = template.get_themes()
|
|
if str(request.form['theme']) in themes:
|
|
branding_cfg = get_cfg('branding', {})
|
|
branding_cfg[str('theme')] = str(request.form['theme'])
|
|
get_publisher().cfg[str('branding')] = branding_cfg
|
|
get_publisher().write_cfg()
|
|
return redirect('.')
|
|
|
|
def download_theme(self):
|
|
theme_id = get_request().form.get('theme')
|
|
if not theme_id:
|
|
return redirect('themes')
|
|
|
|
theme_directory = template.get_theme_directory(theme_id)
|
|
if not theme_directory:
|
|
return redirect('themes')
|
|
|
|
parent_theme_directory = os.path.dirname(theme_directory)
|
|
c = io.BytesIO()
|
|
z = zipfile.ZipFile(c, 'w')
|
|
for base, dummy, filenames in os.walk(theme_directory):
|
|
basetheme = base[len(parent_theme_directory) + 1 :]
|
|
for filename in filenames:
|
|
z.write(os.path.join(base, filename), os.path.join(basetheme, filename))
|
|
z.close()
|
|
|
|
response = get_response()
|
|
response.set_content_type('application/zip')
|
|
response.set_header('content-disposition', 'attachment; filename=%s.zip' % theme_id)
|
|
return c.getvalue()
|
|
|
|
def install_theme(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(FileWidget, 'file', title=_('Theme File'), required=False)
|
|
form.add(UrlWidget, 'url', title=_('Theme Address'), required=False, size=50)
|
|
form.add_submit('submit', _('Install'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
|
|
if form.get_submit() == 'cancel':
|
|
return redirect('.')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
try:
|
|
return self.install_theme_submit(form)
|
|
except ValueError:
|
|
form.get_widget('file').set_error(_('Invalid Theme'))
|
|
|
|
get_response().breadcrumb.append(('install_theme', _('Install Theme')))
|
|
html_top('forms', title=_('Install Theme'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('Install Theme')
|
|
r += htmltext('<p>%s</p>') % _(
|
|
'You can install a new theme by uploading a file ' 'or by pointing to the theme URL.'
|
|
)
|
|
r += form.render()
|
|
return r.getvalue()
|
|
|
|
def install_theme_submit(self, form):
|
|
if form.get_widget('url').parse():
|
|
return self.install_theme_from_url(form.get_widget('url').parse())
|
|
if form.get_widget('file').parse():
|
|
return self.install_theme_from_file(form.get_widget('file').parse().fp)
|
|
get_session().message = ('error', _('You have to enter a file or a URL.'))
|
|
return redirect('themes')
|
|
|
|
def install_theme_from_file(self, fp):
|
|
try:
|
|
z = zipfile.ZipFile(fp, 'r')
|
|
except Exception as e:
|
|
get_session().message = ('error', _('Failed to read theme file. (%s)') % str(e))
|
|
return redirect('themes')
|
|
theme_dir = os.path.join(get_publisher().app_dir, 'themes')
|
|
filename_list = [x for x in z.namelist() if x[0] != '/' and x[-1] != '/']
|
|
if len(filename_list) == 0:
|
|
get_session().message = ('error', _('Empty theme file.'))
|
|
return redirect('themes')
|
|
theme_name = filename_list[0].split('/')[0]
|
|
if ('%s/desc.xml' % theme_name) not in filename_list:
|
|
get_session().message = ('error', _('Theme is missing a desc.xml file.'))
|
|
return redirect('themes')
|
|
desc_xml = z.read('%s/desc.xml' % theme_name)
|
|
theme_dict = template.get_theme_dict(io.StringIO(force_text(desc_xml)))
|
|
if theme_dict.get('name') != theme_name:
|
|
get_session().message = ('error', _('desc.xml is missing a name attribute.'))
|
|
return redirect('themes')
|
|
if os.path.exists(os.path.join(theme_dir, theme_name)):
|
|
shutil.rmtree(os.path.join(theme_dir, theme_name))
|
|
for f in z.namelist():
|
|
if f[-1] == '/':
|
|
continue
|
|
path = os.path.join(theme_dir, f)
|
|
data = z.read(f)
|
|
if not os.path.exists(os.path.dirname(path)):
|
|
os.makedirs(os.path.dirname(path))
|
|
open(path, 'wb').write(data)
|
|
z.close()
|
|
return redirect('themes')
|
|
|
|
def install_theme_from_url(self, url):
|
|
try:
|
|
fp = misc.urlopen(url)
|
|
except misc.ConnectionError as e:
|
|
get_session().message = ('error', _('Error loading theme (%s).') % str(e))
|
|
return redirect('themes')
|
|
|
|
return self.install_theme_from_file(io.StringIO(fp.read()))
|
|
|
|
def template(self):
|
|
from wcs.qommon.template import get_default_ezt_template
|
|
|
|
default_template_ezt = get_default_ezt_template()
|
|
branding_cfg = get_cfg('branding', {})
|
|
template = branding_cfg.get('template', default_template_ezt)
|
|
form = Form(enctype="multipart/form-data")
|
|
form.add(TextWidget, 'template', title=_('Site Template'), value=template, cols=80, rows=25)
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('restore-default', _('Restore default template'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('.')
|
|
|
|
if form.get_submit() == 'cancel':
|
|
return redirect('.')
|
|
|
|
if form.get_submit() == 'restore-default':
|
|
self.template_submit()
|
|
return redirect('.')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
self.template_submit(form)
|
|
return redirect('.')
|
|
|
|
get_response().breadcrumb.append(('template', _('Template')))
|
|
html_top('settings', title=_('Template'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('Template')
|
|
r += form.render()
|
|
return r.getvalue()
|
|
|
|
def template_submit(self, form=None):
|
|
from wcs.qommon.template import DEFAULT_TEMPLATE_EZT, get_default_ezt_template
|
|
|
|
theme_default_template_ezt = get_default_ezt_template()
|
|
|
|
get_publisher().reload_cfg()
|
|
branding_cfg = get_cfg('branding', {})
|
|
if not form:
|
|
template = None
|
|
else:
|
|
template = form.get_widget('template').parse()
|
|
if template in (DEFAULT_TEMPLATE_EZT, theme_default_template_ezt) or not template:
|
|
if 'template' in branding_cfg:
|
|
del branding_cfg['template']
|
|
else:
|
|
branding_cfg['template'] = template
|
|
get_publisher().cfg['branding'] = branding_cfg
|
|
get_publisher().write_cfg()
|
|
|
|
def export(self):
|
|
if get_request().form.get('download'):
|
|
return self.export_download()
|
|
|
|
form = Form(enctype="multipart/form-data")
|
|
form.add(CheckboxWidget, 'formdefs', title=_('Forms'), value=True)
|
|
if StudioDirectory.is_visible():
|
|
form.add(CheckboxWidget, 'carddefs', title=_('Card Models'), value=True)
|
|
form.add(CheckboxWidget, 'workflows', title=_('Workflows'), value=True)
|
|
if get_publisher().has_site_option('fields-blocks'):
|
|
form.add(CheckboxWidget, 'blockdefs', title=_('Fields Blocks'), value=True)
|
|
if not get_cfg('sp', {}).get('idp-manage-roles'):
|
|
form.add(CheckboxWidget, 'roles', title=_('Roles'), value=True)
|
|
form.add(CheckboxWidget, 'categories', title=_('Categories'), value=True)
|
|
form.add(CheckboxWidget, 'carddef_categories', title=_('Card Model Categories'), value=True)
|
|
form.add(CheckboxWidget, 'settings', title=_('Settings'), value=False)
|
|
form.add(CheckboxWidget, 'datasources', title=_('Data sources'), value=True)
|
|
form.add(CheckboxWidget, 'mail-templates', title=_('Mail templates'), value=True)
|
|
form.add(CheckboxWidget, 'wscalls', title=_('Webservice calls'), value=True)
|
|
form.add(CheckboxWidget, 'apiaccess', title=_('API access'), value=True)
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
if form.get_submit() == 'cancel':
|
|
return redirect('.')
|
|
|
|
if not form.is_submitted():
|
|
get_response().breadcrumb.append(('export', _('Export')))
|
|
html_top('settings', title=_('Export'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('Export')
|
|
r += form.render()
|
|
return r.getvalue()
|
|
|
|
class Exporter:
|
|
def __init__(self, dirs, settings):
|
|
self.app_dir = get_publisher().app_dir
|
|
self.dirs = dirs
|
|
self.settings = settings
|
|
|
|
def export(self, job):
|
|
c = io.BytesIO()
|
|
z = zipfile.ZipFile(c, 'w')
|
|
for d in self.dirs:
|
|
if d not in (
|
|
'categories',
|
|
'carddef_categories',
|
|
'wscalls',
|
|
'mail-templates',
|
|
'apiaccess',
|
|
):
|
|
continue
|
|
path = os.path.join(self.app_dir, d)
|
|
if not os.path.exists(path):
|
|
continue
|
|
for f in os.listdir(path):
|
|
if f == '.indexes':
|
|
continue
|
|
z.write(os.path.join(path, f), os.path.join(d, f))
|
|
if 'datasources' in self.dirs:
|
|
for ds in NamedDataSource.select():
|
|
if ds.external == 'agenda':
|
|
continue
|
|
node = ds.export_to_xml(include_id=True)
|
|
misc.indent_xml(node)
|
|
z.writestr(
|
|
os.path.join('datasources', str(ds.id)),
|
|
ET.tostring(node),
|
|
)
|
|
if 'formdefs' in self.dirs:
|
|
for formdef in FormDef.select():
|
|
node = formdef.export_to_xml(include_id=True)
|
|
misc.indent_xml(node)
|
|
z.writestr(
|
|
os.path.join('formdefs_xml', str(formdef.id)),
|
|
b'<?xml version="1.0"?>\n' + ET.tostring(node),
|
|
)
|
|
if 'carddefs' in self.dirs:
|
|
for formdef in CardDef.select():
|
|
node = formdef.export_to_xml(include_id=True)
|
|
misc.indent_xml(node)
|
|
z.writestr(
|
|
os.path.join('carddefs_xml', str(formdef.id)),
|
|
b'<?xml version="1.0"?>\n' + ET.tostring(node),
|
|
)
|
|
if 'workflows' in self.dirs:
|
|
for workflow in Workflow.select():
|
|
node = workflow.export_to_xml(include_id=True)
|
|
misc.indent_xml(node)
|
|
z.writestr(
|
|
os.path.join('workflows_xml', str(workflow.id)),
|
|
b'<?xml version="1.0"?>\n' + ET.tostring(node),
|
|
)
|
|
if 'blockdefs' in self.dirs:
|
|
for blockdef in BlockDef.select():
|
|
node = blockdef.export_to_xml(include_id=True)
|
|
misc.indent_xml(node)
|
|
z.writestr(
|
|
os.path.join('blockdefs_xml', str(blockdef.id)),
|
|
b'<?xml version="1.0"?>\n' + ET.tostring(node),
|
|
)
|
|
if 'roles' in self.dirs:
|
|
for role in get_publisher().role_class.select():
|
|
node = role.export_to_xml(include_id=True)
|
|
misc.indent_xml(node)
|
|
z.writestr(
|
|
os.path.join('roles_xml', str(role.id)),
|
|
b'<?xml version="1.0"?>\n' + ET.tostring(node),
|
|
)
|
|
|
|
if self.settings:
|
|
z.write(os.path.join(self.app_dir, 'config.pck'), 'config.pck')
|
|
for f in os.listdir(self.app_dir):
|
|
if f.startswith('idp-') and os.path.splitext(f)[-1] in ('.pem', '.xml'):
|
|
z.write(os.path.join(self.app_dir, f), f)
|
|
if os.path.exists(os.path.join(self.app_dir, 'config')):
|
|
for f in os.listdir(os.path.join(self.app_dir, 'config')):
|
|
z.write(os.path.join(self.app_dir, 'config', f), os.path.join('config', f))
|
|
z.close()
|
|
|
|
job.file_content = c.getvalue()
|
|
job.store()
|
|
|
|
dirs = []
|
|
for w in (
|
|
'formdefs',
|
|
'carddefs',
|
|
'workflows',
|
|
'roles',
|
|
'categories',
|
|
'carddef_categories',
|
|
'datasources',
|
|
'wscalls',
|
|
'mail-templates',
|
|
'blockdefs',
|
|
'apiaccess',
|
|
):
|
|
if form.get_widget(w) and form.get_widget(w).parse():
|
|
dirs.append(w)
|
|
if not dirs and not form.get_widget('settings').parse():
|
|
return redirect('.')
|
|
|
|
exporter = Exporter(dirs, settings=form.get_widget('settings').parse())
|
|
|
|
job = get_response().add_after_job(N_('Exporting site settings'), exporter.export)
|
|
job.done_action_url = get_request().get_url() + '?download=%s' % job.id
|
|
job.done_action_label = _('Download Export')
|
|
job.done_button_attributes = {'download': 'export.wcs'}
|
|
job.store()
|
|
return redirect(job.get_processing_url())
|
|
|
|
def export_download(self):
|
|
job_id = get_request().form.get('download')
|
|
try:
|
|
job = AfterJob.get(job_id)
|
|
except KeyError:
|
|
return redirect('.')
|
|
|
|
response = get_response()
|
|
response.set_content_type('application/x-wcs')
|
|
response.set_header('content-disposition', 'attachment; filename=export.wcs')
|
|
return job.file_content
|
|
|
|
def p_import(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(FileWidget, 'file', title=_('File'), required=True)
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
if form.get_submit() == 'cancel':
|
|
return redirect('.')
|
|
|
|
if not form.is_submitted() or form.has_errors():
|
|
get_response().breadcrumb.append(('import', _('Import')))
|
|
html_top('settings', title=_('Import'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('Import')
|
|
r += form.render()
|
|
return r.getvalue()
|
|
else:
|
|
try:
|
|
results = self.import_submit(form)
|
|
except zipfile.BadZipfile:
|
|
results = None
|
|
reason = _('Not a valid export file')
|
|
except WorkflowImportError as e:
|
|
results = None
|
|
msg = _(e.msg) % e.msg_args
|
|
if e.details:
|
|
msg += ' [%s]' % e.details
|
|
reason = _('Failed to import a workflow (%s); site import did not complete.') % (msg)
|
|
html_top('settings', title=_('Import'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('Import')
|
|
if results:
|
|
r += htmltext('<p>%s</p>') % _('Imported successfully:')
|
|
r += htmltext('<ul>')
|
|
if results['formdefs']:
|
|
r += htmltext('<li>%d %s</li>') % (results['formdefs'], _('forms'))
|
|
if results['carddefs']:
|
|
r += htmltext('<li>%d %s</li>') % (results['carddefs'], _('cards'))
|
|
if results['blockdefs']:
|
|
r += htmltext('<li>%d %s</li>') % (results['blockdefs'], _('fields blocks'))
|
|
if results['workflows']:
|
|
r += htmltext('<li>%d %s</li>') % (results['workflows'], _('workflows'))
|
|
if results['roles']:
|
|
r += htmltext('<li>%d %s</li>') % (results['roles'], _('roles'))
|
|
if results['categories']:
|
|
r += htmltext('<li>%d %s</li>') % (results['categories'], _('categories'))
|
|
if results['settings']:
|
|
r += htmltext('<li>%s</li>') % _('Settings')
|
|
if results['datasources']:
|
|
r += htmltext('<li>%d %s</li>') % (results['datasources'], _('data sources'))
|
|
if results['mail-templates']:
|
|
r += htmltext('<li>%d %s</li>') % (results['mail-templates'], _('mail templates'))
|
|
if results['wscalls']:
|
|
r += htmltext('<li>%d %s</li>') % (results['wscalls'], _('webservice calls'))
|
|
if results['apiaccess']:
|
|
r += htmltext('<li>%d %s</li>') % (results['apiaccess'], _('API access'))
|
|
r += htmltext('</ul>')
|
|
else:
|
|
r += htmltext('<p>%s %s</p>') % (_('Error:'), reason)
|
|
r += htmltext('<a href=".">%s</a>') % _('Back')
|
|
return r.getvalue()
|
|
|
|
def import_submit(self, form):
|
|
return get_publisher().import_zip(form.get_widget('file').parse().fp)
|
|
|
|
def sitename(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
misc_cfg = get_cfg('misc', {})
|
|
form.add(StringWidget, 'sitename', title=_('Site Name'), value=misc_cfg.get('sitename', ''))
|
|
form.add(
|
|
StringWidget,
|
|
'frontoffice-url',
|
|
size=32,
|
|
title=_('Frontoffice base URL'),
|
|
value=misc_cfg.get('frontoffice-url', ''),
|
|
)
|
|
form.add(
|
|
StringWidget,
|
|
'backoffice-url',
|
|
size=32,
|
|
title=_('Backoffice base URL'),
|
|
value=misc_cfg.get('backoffice-url', ''),
|
|
)
|
|
form.add(
|
|
StringWidget,
|
|
'homepage-redirect-url',
|
|
size=32,
|
|
title=_('Homepage redirection'),
|
|
value=misc_cfg.get('homepage-redirect-url', ''),
|
|
)
|
|
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('.')
|
|
|
|
if not form.is_submitted() or form.has_errors():
|
|
get_response().breadcrumb.append(('sitename', _('Site Name and Addresses')))
|
|
html_top('settings', title=_('Site Name and Addresses'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('Site Name and Addresses')
|
|
r += form.render()
|
|
return r.getvalue()
|
|
else:
|
|
cfg_submit(
|
|
form, 'misc', ['sitename', 'frontoffice-url', 'backoffice-url', 'homepage-redirect-url']
|
|
)
|
|
return redirect('.')
|
|
|
|
def sms(self):
|
|
get_response().breadcrumb.append(('sms', _('SMS')))
|
|
html_top('settings', title=_('SMS'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('SMS Options')
|
|
sms_cfg = get_cfg('sms', {})
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(StringWidget, 'sender', title=_('Sender (number or name)'), value=sms_cfg.get('sender'))
|
|
form.add(StringWidget, 'passerelle_url', title=_('URL'), value=sms_cfg.get('passerelle_url'))
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('.')
|
|
|
|
if form.get_submit() and not form.has_errors():
|
|
cfg_submit(form, 'sms', ['sender', 'passerelle_url'])
|
|
return redirect('.')
|
|
|
|
r += form.render()
|
|
return r.getvalue()
|
|
|
|
def postgresql(self):
|
|
postgresql_cfg = get_cfg('postgresql', {})
|
|
if not get_publisher().has_site_option('postgresql') and not postgresql_cfg:
|
|
raise errors.TraversalError()
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(
|
|
StringWidget,
|
|
'database',
|
|
title=_('Database Name'),
|
|
required=True,
|
|
value=postgresql_cfg.get('database'),
|
|
)
|
|
form.add(
|
|
StringWidget,
|
|
'user',
|
|
title=_('User'),
|
|
required=False,
|
|
value=postgresql_cfg.get('user'),
|
|
hint=_('User name used to authenticate'),
|
|
)
|
|
form.add(
|
|
PasswordWidget,
|
|
'password',
|
|
title=_('Password'),
|
|
required=False,
|
|
value=postgresql_cfg.get('password'),
|
|
hint=_('Password used to authenticate'),
|
|
)
|
|
form.add(
|
|
StringWidget,
|
|
'host',
|
|
title=_('Host'),
|
|
required=False,
|
|
value=postgresql_cfg.get('host'),
|
|
hint=_('Database host address'),
|
|
)
|
|
try:
|
|
port = int(postgresql_cfg.get('port'))
|
|
except (ValueError, TypeError):
|
|
port = None
|
|
form.add(
|
|
IntWidget, 'port', title=_('Port'), required=False, value=port, hint=_('Connection port number')
|
|
)
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('.')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
postgresql_cfg = copy.copy(get_cfg('postgresql', {}))
|
|
cfg_submit(form, 'postgresql', ['database', 'user', 'password', 'host', 'port'])
|
|
try:
|
|
get_publisher().initialize_sql()
|
|
except Exception as e:
|
|
postgresql_cfg[str('postgresql')] = postgresql_cfg
|
|
form.set_error('database', str(e))
|
|
else:
|
|
return redirect('.')
|
|
|
|
get_response().breadcrumb.append(('postgresql', _('PostgreSQL Settings')))
|
|
html_top('settings', title=_('PostgreSQL Settings'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('PostgreSQL Settings')
|
|
r += form.render()
|
|
return r.getvalue()
|
|
|
|
def geolocation(self):
|
|
misc_cfg = get_cfg('misc', {})
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(
|
|
MapWidget,
|
|
'default-position',
|
|
title=_('Default Map Position'),
|
|
value=misc_cfg.get('default-position'),
|
|
default_zoom='9',
|
|
required=False,
|
|
)
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('.')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
cfg_submit(form, 'misc', ['default-position'])
|
|
return redirect('.')
|
|
|
|
get_response().breadcrumb.append(('geolocation', _('Geolocation Settings')))
|
|
html_top('settings', title=_('Geolocation Settings'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('Geolocation Settings')
|
|
r += form.render()
|
|
return r.getvalue()
|
|
|
|
def user_template(self):
|
|
form = UserFieldsDirectory.sidebar_template_form(action='user-template')
|
|
form.get_widget('sidebar_template').set_title(_('Sidebar Template'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('.')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
cfg_submit(form, 'users', ['sidebar_template'])
|
|
return redirect('.')
|
|
|
|
get_response().breadcrumb.append(('user-template', _('Users')))
|
|
html_top('settings', title=_('Users'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('Users')
|
|
r += form.render()
|
|
return r.getvalue()
|