# 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 . import copy import hashlib import io import mimetypes import os try: import lasso except ImportError: lasso = None import xml.etree.ElementTree as ET import zipfile from django.utils.encoding import force_bytes 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.blocks import BlockDef from wcs.carddef import CardDef from wcs.data_sources import NamedDataSource from wcs.fields import MapOptionsMixin from wcs.formdef import FormDef, FormdefImportError from wcs.qommon import _, 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.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, 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('

%s

') % _('Identification') if lasso is None: cls = 'infonotice' if identification_cfg.get('methods') and 'idp' in identification_cfg.get('methods'): cls = 'errornotice' r += htmltext('

%s

') % ( 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', 'search_result_template'] section = 'settings' field_def_page_class = UserFieldDefPage support_import = False blacklisted_types = ['page', 'computed'] field_var_prefix = '..._user_var_' def index_bottom(self): r = TemplateIO(html=True) r += get_session().display_message() r += htmltext('
') r += htmltext('

%s

') % _('Fields Mapping') r += htmltext('

%s

') % _( 'These settings make it possible to assign custom user fields to standard user fields.' ) r += self.mapping_form().render() r += htmltext('
') r += htmltext('
') r += htmltext('

%s

') % _('Sidebar Template') r += self.sidebar_template_form().render() r += htmltext('
') r += htmltext('
') r += htmltext('

%s

') % _('User search result template') r += self.search_result_template_form().render() r += htmltext('
') 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('.') @classmethod def search_result_template_form(cls, action='search_result_template'): users_cfg = get_cfg('users', {}) form = Form(action=action, id='search_result_template') form.add( TextWidget, 'search_result_template', value=users_cfg.get('search_result_template') or get_publisher().user_class.default_search_result_template, required=False, validation_function=ComputedExpressionWidget.validate_template, rows=7, ) form.add_submit('submit', _('Submit')) return form def search_result_template(self): form = self.search_result_template_form() if form.has_errors(): get_session().message = ('error', form.get_widget('search_result_template').get_error()) return redirect('.') cfg_submit(form, 'users', ['search_result_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)""" @staticmethod def singleton(publisher=None): if publisher is None: publisher = get_publisher() if not getattr(publisher, '_cached_user_fields_formdef', None): publisher._cached_user_fields_formdef = UserFieldsFormDef(publisher) return publisher._cached_user_fields_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() self.publisher._cached_user_fields_formdef = None 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' and not form.has_errors(): 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('

%s

') % _('File Types') if filetypes_cfg: r += htmltext('') else: r += htmltext('

') r += str(_('There are no file type defined at the moment.')) r += htmltext('

') r += htmltext('

%s

') % _('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' and not form.has_errors(): 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('

%s - %s

') % (_('File Type'), filetype['label']) r += form.render() return r.getvalue() class SettingsDirectory(QommonSettingsDirectory): _q_exports = [ '', 'users', 'template', 'emails', 'debug_options', 'language', ('import', 'p_import'), 'export', 'identification', 'sitename', 'sms', 'certificates', 'texts', 'postgresql', ('admin-permissions', 'admin_permissions'), 'geolocation', 'filetypes', ('user-templates', 'user_templates'), ('data-sources', 'data_sources'), 'wscalls', ('api-access', 'api_access'), ] emails = EmailsDirectory() identification = IdentificationDirectory() users = UsersDirectory() texts = TextsDirectory() filetypes = FileTypesDirectory() data_sources = NamedDataSourcesDirectory() wscalls = NamedWsCallsDirectory() 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('
') if enabled('storage') and ( get_publisher().has_site_option('postgresql') or get_cfg('postgresql', {}) ): r += htmltext('
') r += htmltext('

%s

') % _('Storage') r += htmltext('
%s
%s
') % ( _('PostgreSQL Settings'), _('Configure access to PostgreSQL database'), ) r += htmltext('
') r += htmltext('
') r += htmltext('

%s

') % _('Security') r += htmltext('
') if enabled('identification'): r += htmltext('
%s
%s
') % ( _('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('
%s
%s
') % ( method, _(method_admin.title), _(method_admin.label), ) if enabled('permissions'): roles = list(get_publisher().role_class.select()) if roles: r += htmltext('
%s
%s
') % ( _('Admin Permissions'), _('Configure access to the administration interface'), ) if enabled('api-access'): r += htmltext('
%s
%s
') % ( _('API access'), _('Configure access to the API endpoints'), ) r += htmltext('
') if enabled('import-export'): r += htmltext('
') r += htmltext('

%s

') % _('Import / Export') r += htmltext('
') r += htmltext('
%s
%s
') % ( _('Import'), _('Initialise with data from another site'), ) r += htmltext('
%s
%s
') % ( _('Export'), _('Export data for another site'), ) r += htmltext('
') r += htmltext('
') if enabled('misc'): r += htmltext('
') r += htmltext('

%s

') % _('Misc') r += htmltext('
') r += htmltext('
%s
%s
') % ( _('Debug Options'), _('Configure options useful for debugging'), ) r += htmltext('
') r += htmltext('
') r += htmltext('
') r += htmltext('
') r += htmltext('
') r += htmltext('

%s

') % _('Customisation') r += htmltext('
') r += htmltext('
') if enabled('sitename'): r += htmltext('
%s
%s
') % ( _('Site Name and Addresses'), _('Configure site name and addresses'), ) if enabled('language'): r += htmltext('
%s
%s
') % ( _('Language'), _('Configure site language'), ) if enabled('geolocation'): r += htmltext('
%s
%s
') % ( _('Geolocation'), _('Configure geolocation'), ) if enabled('users'): r += htmltext('
%s
%s
') % (_('Users'), _('Configure users')) else: # minimal options r += htmltext('
%s
%s
') % ( _('Users'), _('Configure templates for users'), ) if enabled('emails'): r += htmltext('
%s
%s
') % ( _('Emails'), _('Configure email settings'), ) if enabled('sms') and get_publisher().use_sms_feature: r += htmltext('
%s
%s
') % ( _('SMS'), _('Configure SMS settings'), ) if enabled('texts') and self.texts.texts_dict: r += htmltext('
%s
%s
') % ( _('Texts'), _('Configure text that appears on some pages'), ) if enabled('filetypes'): r += htmltext('
%s
%s
') % ( _('File Types'), _('Configure known file types'), ) r += htmltext('
%s
%s
') % ( _('Data sources'), _('Configure data sources'), ) r += htmltext('
%s
%s
') % ( _('Webservice calls'), _('Configure webservice calls'), ) r += htmltext('
') r += htmltext('
') r += htmltext('
') r += htmltext('
') 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', _('Forms')), ('cards', _('Card Models')), ('workflows', _('Workflows')), ('users', _('Users')), ('roles', _('Roles')), ('categories', _('Categories')), ('settings', _('Settings')), ] for k, v in admin_sections: 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('
') r += htmltext('

%s

') % _('Admin Permissions') r += form.render() r += htmltext('
') 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 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) form.add(CheckboxWidget, 'carddefs', title=_('Card Models'), value=True) form.add(CheckboxWidget, 'workflows', title=_('Workflows'), value=True) 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, 'workflow_categories', title=_('Workflow Categories'), value=True) form.add(CheckboxWidget, 'block_categories', title=_('Fields Blocks Categories'), value=True) form.add(CheckboxWidget, 'mail_template_categories', title=_('Mail Templates Categories'), value=True) form.add(CheckboxWidget, 'data_source_categories', title=_('Data Sources 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('

%s

') % _('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() with zipfile.ZipFile(c, 'w') as z: for d in self.dirs: if d not in ( 'categories', 'carddef_categories', 'workflow_categories', 'block_categories', 'mail_template_categories', 'data_source_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 in ('.indexes', '.max_id'): continue z.write(os.path.join(path, f), os.path.join(d, f)) job.increment_count() 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), ) job.increment_count() 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'\n' + ET.tostring(node), ) job.increment_count() 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'\n' + ET.tostring(node), ) job.increment_count() 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'\n' + ET.tostring(node), ) job.increment_count() 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'\n' + ET.tostring(node), ) job.increment_count() 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'\n' + ET.tostring(node), ) job.increment_count() if self.settings: z.write(os.path.join(self.app_dir, 'config.pck'), 'config.pck') job.increment_count() 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) job.increment_count() 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)) job.increment_count() job.total_count = job.current_count job.file_content = c.getvalue() job.store() dirs = [] for w in ( 'formdefs', 'carddefs', 'workflows', 'roles', 'categories', 'carddef_categories', 'workflow_categories', 'block_categories', 'mail_template_categories', 'data_source_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( _('Exporting site settings'), exporter.export, done_action_url=get_request().get_url() + '?download=%(job_id)s', done_action_label=_('Download Export'), 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('

%s

') % _('Import') if FormDef.count() or CardDef.count() or Workflow.count(): r += htmltext('
%s
') % _( 'This site has existing forms, cards or workflows, beware re-importing ' 'content is dangerous and will probably break existing data and configuration.' ) r += htmltext('
') r += form.render() r += htmltext('
') return r.getvalue() else: reason = None try: results = self.import_submit(form) results['mail_templates'] = results['mail-templates'] except zipfile.BadZipfile: results = None reason = _('Not a valid export file') except (FormdefImportError, 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')) return template.QommonTemplateResponse( templates=['wcs/backoffice/settings/import.html'], context={'results': results, 'error': reason}, ) 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=_('Site base URL'), value=misc_cfg.get('frontoffice-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('

%s

') % _('Site Name and Addresses') r += form.render() return r.getvalue() else: cfg_submit(form, 'misc', ['sitename', 'frontoffice-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('

%s

') % _('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['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('

%s

') % _('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, ) zoom_levels = [(x[0], x[1], x[0]) for x in MapOptionsMixin.get_zoom_levels()] form.add( SingleSelectWidget, 'default-zoom-level', title=_('Default zoom level'), value=get_publisher().get_default_zoom_level(), options=zoom_levels, 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', 'default-zoom-level']) return redirect('.') get_response().breadcrumb.append(('geolocation', _('Geolocation Settings'))) html_top('settings', title=_('Geolocation Settings')) r = TemplateIO(html=True) r += htmltext('

%s

') % _('Geolocation Settings') r += form.render() r += htmltext( '''''' ) return r.getvalue() def user_templates(self): form = UserFieldsDirectory.sidebar_template_form(action='user-templates') form.get_widget('sidebar_template').set_title(_('Sidebar Template')) form.add_submit('cancel', _('Cancel')) form2 = UserFieldsDirectory.search_result_template_form(action='user-templates') widget = form2.get_widget('search_result_template') widget.set_title(_('Search Result Template')) form.widgets.append(widget) form._names['search_result_template'] = widget if form.get_widget('cancel').parse(): return redirect('.') if form.is_submitted() and not form.has_errors(): cfg_submit(form, 'users', ['sidebar_template', 'search_result_template']) return redirect('.') get_response().breadcrumb.append(('user-templates', _('Users'))) html_top('settings', title=_('Users')) r = TemplateIO(html=True) r += htmltext('

%s

') % _('Users') r += form.render() return r.getvalue()