898 lines
35 KiB
Plaintext
898 lines
35 KiB
Plaintext
# 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 cStringIO
|
|
import cPickle
|
|
import random
|
|
import re
|
|
import os
|
|
try:
|
|
import lasso
|
|
except ImportError:
|
|
lasso = None
|
|
import zipfile
|
|
import base64
|
|
|
|
try:
|
|
import elementtree.ElementTree as ET
|
|
except ImportError:
|
|
try:
|
|
import xml.etree.ElementTree as ET
|
|
except ImportError:
|
|
ET = None
|
|
|
|
|
|
from quixote import get_publisher, get_request, get_response, redirect
|
|
from quixote.directory import Directory, AccessControlled
|
|
|
|
from qommon import misc, get_cfg
|
|
from qommon import errors
|
|
from qommon.form import *
|
|
from qommon.sms import SMS
|
|
|
|
from qommon.admin.menu import html_top, command_icon, error_page
|
|
from qommon.admin.certificates import CertificatesDirectory, m2crypto
|
|
from qommon.admin.cfg import cfg_submit
|
|
from qommon.admin.emails import EmailsDirectory
|
|
from qommon.admin.texts import TextsDirectory
|
|
from qommon.admin.settings import SettingsDirectory as QommonSettingsDirectory
|
|
import qommon.ident
|
|
import qommon.template
|
|
|
|
from formdef import FormDef
|
|
from fields import FieldWidget, FieldDefPage, FieldsDirectory
|
|
|
|
class UserFormDirectory(Directory):
|
|
_q_exports = ['']
|
|
|
|
|
|
class IdentificationDirectory(Directory):
|
|
_q_exports = ['']
|
|
|
|
def _q_index [html] (self):
|
|
get_response().breadcrumb.append( ('identification/', _('Identification')) )
|
|
identification_cfg = get_cfg('identification', {})
|
|
form = Form(enctype='multipart/form-data')
|
|
methods = [ ('password', _('Simple local username / password')), ]
|
|
if lasso is not None:
|
|
methods.insert(0,
|
|
('idp', _('Delegated to Liberty/SAML2 identity provider')))
|
|
form.add(CheckboxesWidget, 'methods', title = _('Methods'),
|
|
value = identification_cfg.get('methods'),
|
|
elements = methods,
|
|
inline = False,
|
|
required = True)
|
|
|
|
form.add(CheckboxWidget, 'use_user_hash', title=_('One-way association between user and forms'),
|
|
value=bool(identification_cfg.get('use_user_hash', False)),
|
|
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, 'identification', ['methods', 'use_user_hash'])
|
|
if not identification_cfg.get('user_hash_secret_key') and form.get_widget('use_user_hash').parse():
|
|
identification_cfg = get_cfg('identification', {})
|
|
identification_cfg[str('user_hash_secret_key')] = \
|
|
str(random.SystemRandom().getrandbits(64))
|
|
get_publisher().cfg[str('identification')] = identification_cfg
|
|
get_publisher().write_cfg()
|
|
if not form.has_errors():
|
|
return redirect('..')
|
|
|
|
html_top('settings', title = _('Identification'))
|
|
'<h2>%s</h2>' % _('Identification')
|
|
if lasso is None:
|
|
cls = 'infonotice'
|
|
if identification_cfg.get('methods') and 'idp' in identification_cfg.get('methods'):
|
|
cls = 'errornotice'
|
|
'<p class="%s">%s</p>' % (cls, _('Delegated to Liberty/SAML2 identity provider \
|
|
authentication is unavailable. Lasso must be installed to use it.'))
|
|
form.render()
|
|
|
|
def _q_lookup(self, component):
|
|
get_response().breadcrumb.append( ('identification/', _('Identification')) )
|
|
return qommon.ident.get_method_admin_directory(component)
|
|
|
|
class UserFieldDefPage(FieldDefPage):
|
|
section = 'settings'
|
|
wsf_support = False
|
|
|
|
class UserFieldsDirectory(FieldsDirectory):
|
|
_q_exports = ['', 'update_order', 'new', 'mapping']
|
|
|
|
section = 'settings'
|
|
field_def_page_class = UserFieldDefPage
|
|
support_import = False
|
|
blacklisted_types = ['page']
|
|
|
|
def index_bottom [html] (self):
|
|
'<h2>%s</h2>' % _('Fields Mapping')
|
|
form = self.mapping_form()
|
|
form.render()
|
|
|
|
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', enctype='multipart/form-data')
|
|
field_name_value = users_cfg.get('field_name')
|
|
if type(field_name_value) is 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('.')
|
|
|
|
|
|
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):
|
|
self.name = _('Custom User Fields')
|
|
users_cfg = get_cfg('users', {})
|
|
xml_import = users_cfg.get('formdef')
|
|
self.fields = [] # make sure fields is a list
|
|
if xml_import:
|
|
try:
|
|
tree = ET.fromstring(xml_import)
|
|
except:
|
|
pass
|
|
else:
|
|
obj = FormDef.import_from_xml_tree(tree, include_id=True)
|
|
self.fields = obj.fields
|
|
else:
|
|
# compatibility with older location
|
|
filename = os.path.join(get_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)
|
|
|
|
def store(self):
|
|
xml_export = self.export_to_xml(include_id=True)
|
|
users_cfg = get_cfg('users', {})
|
|
users_cfg['formdef'] = ET.tostring(xml_export)
|
|
get_publisher().cfg['users'] = users_cfg
|
|
get_publisher().write_cfg()
|
|
|
|
|
|
class UsersDirectory(Directory):
|
|
_q_exports = ['', 'fields']
|
|
|
|
def _q_index(self):
|
|
return redirect('fields/')
|
|
|
|
def _q_traverse(self, path):
|
|
get_response().breadcrumb.append(('users/', _('Users')))
|
|
filename = os.path.join(get_publisher().app_dir, 'config', 'user')
|
|
self.fields = UserFieldsDirectory(UserFieldsFormDef())
|
|
return Directory._q_traverse(self, path)
|
|
|
|
|
|
class SettingsDirectory(QommonSettingsDirectory):
|
|
_q_exports = ['', 'themes', 'users',
|
|
'template', 'misc', 'emails', 'debug_options', 'language',
|
|
('import', 'p_import'), 'export', 'identification', 'sitename',
|
|
'sms', 'certificates', 'texts', 'utf8switch', 'upload_theme',
|
|
'session', 'download_theme', 'smstest', 'postgresql']
|
|
|
|
certificates = CertificatesDirectory()
|
|
emails = EmailsDirectory()
|
|
identification = IdentificationDirectory()
|
|
users = UsersDirectory()
|
|
texts = TextsDirectory()
|
|
|
|
def _q_index [html] (self):
|
|
html_top('settings', title = _('Settings'))
|
|
|
|
'<div class="splitcontent-left">'
|
|
|
|
if get_publisher().has_site_option('postgresql'):
|
|
'<div class="bo-block">'
|
|
'<h2>%s</h2>' % _('Storage')
|
|
'<dl> <dt><a href="postgresql">%s</a></dt> <dd>%s</dd> </dl>' % (
|
|
_('PostgreSQL Settings'),
|
|
_('Configure access to PostgreSQL database'))
|
|
'</div>'
|
|
|
|
'<div class="bo-block">'
|
|
'<h2>%s</h2>' % _('Security')
|
|
|
|
'<dl> <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 = qommon.ident.get_method_admin_directory(method)
|
|
except AttributeError:
|
|
continue
|
|
|
|
'<dl> <dt><a href="identification/%s/">%s</a></dt> <dd>%s</dd>' % (
|
|
method, _(method_admin.title), _(method_admin.label))
|
|
|
|
'<dl> <dt><a href="session">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Session'), _('Configure session management'))
|
|
|
|
if m2crypto:
|
|
'<dl> <dt><a href="certificates/">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Certificates'), _('Configure certificate authorities'))
|
|
'</div>'
|
|
|
|
'<div class="bo-block">'
|
|
'<h2>%s</h2>' % _('Import / Export')
|
|
|
|
'<dl>'
|
|
'<dt><a href="import">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Import'), _('Import data from another site'))
|
|
'<dt><a href="export">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Export'), _('Export data for another site'))
|
|
'</dl>'
|
|
'</div>'
|
|
|
|
'<div class="bo-block">'
|
|
'<h2>%s</h2>' % _('Misc')
|
|
'<dl>'
|
|
'<dt><a href="misc">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Misc'), _('Configure misc options'))
|
|
'<dt><a href="debug_options">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Debug Options'), _('Configure options useful for debugging'))
|
|
'</dl>'
|
|
'</div>'
|
|
|
|
|
|
'</div>'
|
|
|
|
'<div class="splitcontent-right">'
|
|
'<div class="bo-block">'
|
|
'<h2>%s</h2>' % _('Customisation')
|
|
|
|
if not get_cfg('misc', {}).get('charset'):
|
|
'<div class="infonotice">'
|
|
'<p>'
|
|
_('This site is still using ISO-8859-15 as its character set; '\
|
|
'it is advised to update to UTF-8.')
|
|
' '
|
|
'<a href="utf8switch">%s</a>' % _('Switch to UTF-8 encoding')
|
|
'</p>'
|
|
'</div>'
|
|
|
|
'<dl>'
|
|
'<dt><a href="sitename">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Site Name and Addresses'), _('Configure site name and addresses'))
|
|
'<dt><a href="language">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Language'), _('Configure site language'))
|
|
'<dt><a href="themes">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Theme'), _('Configure theme'))
|
|
'<dt><a href="template">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Template'), _('Configure template'))
|
|
'<dt><a href="users/">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Users'), _('Configure users'))
|
|
'<dt><a href="emails/">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Emails'), _('Configure email settings'))
|
|
if get_publisher().use_sms_feature:
|
|
'<dt><a href="sms">%s</a></dt> <dd>%s</dd>' % (
|
|
_('SMS'), _('Configure SMS settings'))
|
|
if self.texts.texts_dict:
|
|
'<dt><a href="texts/">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Texts'), _('Configure text that appears on some pages'))
|
|
'</dl>'
|
|
'</div>'
|
|
|
|
'</div>'
|
|
|
|
def themes [html] (self):
|
|
request = get_request()
|
|
|
|
if not request.form.has_key('theme'):
|
|
current_theme = get_cfg('branding', {}).get('theme', 'default')
|
|
|
|
get_response().breadcrumb.append(('themes', _('Themes')))
|
|
html_top('settings', title = _('Themes'))
|
|
"<h2>%s</h2>" % _('Themes')
|
|
|
|
get_session().display_message()
|
|
|
|
'<a rel="popup" href="upload_theme">%s</a>' % _('Upload New Theme')
|
|
|
|
'<form action="themes" enctype="multipart/form-data" method="post">'
|
|
themes = qommon.template.get_themes()
|
|
'<ul class="biglist themes">'
|
|
for theme, (label, desc, author, icon) in sorted(themes.items()):
|
|
if current_theme == theme:
|
|
checked = ' checked="checked"'
|
|
else:
|
|
checked = ''
|
|
'<li>'
|
|
'<strong class="label">'
|
|
' <input name="theme" value="%s" type="radio"%s>%s</input></strong>' % (
|
|
theme, checked, label)
|
|
if icon:
|
|
'<img src="/themes/%s/icon.png" alt="" class="theme-icon" />' % theme
|
|
'<p class="details">%s' % desc
|
|
' [<a href="download_theme?theme=%s">%s</a>]' % (theme, _('download'))
|
|
if author:
|
|
'<br/>by %s' % author
|
|
'</p>'
|
|
'</li>'
|
|
'</ul>'
|
|
'<div class="buttons">'
|
|
'<input type="submit" name="submit" value="%s" />' % _('Submit')
|
|
'</div>'
|
|
'</form>'
|
|
else:
|
|
themes = qommon.template.get_themes()
|
|
if themes.has_key(str(request.form['theme'])):
|
|
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 = qommon.template.get_theme_directory(theme_id)
|
|
if not theme_directory:
|
|
return redirect('themes')
|
|
|
|
parent_theme_directory = os.path.dirname(theme_directory)
|
|
c = cStringIO.StringIO()
|
|
z = zipfile.ZipFile(c, 'w')
|
|
for base, dirnames, 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 upload_theme [html] (self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(FileWidget, 'file', title = _('Theme'), required = True)
|
|
form.add_submit('submit', _('Upload'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
|
|
if form.get_submit() == 'cancel':
|
|
return redirect('.')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
try:
|
|
return self.upload_theme_submit(form)
|
|
except ValueError:
|
|
form.get_widget('file').set_error(_('Invalid Theme'))
|
|
|
|
get_response().breadcrumb.append( ('upload_theme', _('Upload Theme')) )
|
|
html_top('forms', title = _('Upload Theme'))
|
|
'<h2>%s</h2>' % _('Upload Theme')
|
|
form.render()
|
|
|
|
def upload_theme_submit(self, form):
|
|
try:
|
|
z = zipfile.ZipFile(form.get_widget('file').parse().fp, 'r')
|
|
except:
|
|
get_session().message = ('error', _('Failed to read theme file.'))
|
|
return redirect('themes')
|
|
theme_dir = os.path.join(get_publisher().app_dir, 'themes')
|
|
# TODO: should check and read desc.xml first; and if theme already
|
|
# exists, it should remove the whole directory
|
|
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, 'w').write(data)
|
|
z.close()
|
|
return redirect('themes')
|
|
|
|
def template [html] (self):
|
|
from 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'))
|
|
'<h2>%s</h2>' % _('Template')
|
|
form.render()
|
|
|
|
def template_submit(self, form = None):
|
|
from 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 branding_cfg.has_key('template'):
|
|
del branding_cfg['template']
|
|
else:
|
|
branding_cfg['template'] = template
|
|
get_publisher().cfg['branding'] = branding_cfg
|
|
get_publisher().write_cfg()
|
|
|
|
def misc [html] (self):
|
|
misc_cfg = get_cfg('misc', {})
|
|
form = Form(enctype="multipart/form-data")
|
|
form.add(CheckboxWidget, 'do-not-token',
|
|
title = _('Do not show anything about identification tokens'),
|
|
value = misc_cfg.get('do-not-token', False))
|
|
form.add(CheckboxWidget, 'include-user-details',
|
|
title = _('Show user details on public pages'),
|
|
value = misc_cfg.get('include-user-details', False))
|
|
form.add(WidgetDict, 'namespaces',
|
|
title = _('Namespaces for prefilling'),
|
|
value = misc_cfg.get('namespaces', {}))
|
|
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(('misc', _('Misc')))
|
|
html_top('settings', title = _('Misc'))
|
|
'<h2>%s</h2>' % _('Misc')
|
|
form.render()
|
|
else:
|
|
cfg_submit(form, 'misc', ('do-not-token', 'include-user-details','namespaces'))
|
|
redirect('.')
|
|
|
|
def export [html] (self):
|
|
form = Form(enctype="multipart/form-data")
|
|
form.add(CheckboxWidget, 'formdefs', title = _('Forms'), value = True)
|
|
form.add(CheckboxWidget, 'workflows', title = _('Workflows'), value = True)
|
|
form.add(CheckboxWidget, 'roles', title = _('Roles'), value = True)
|
|
form.add(CheckboxWidget, 'categories', title = _('Categories'), value = True)
|
|
form.add(CheckboxWidget, 'settings', title = _('Settings'), value = False)
|
|
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'))
|
|
'<h2>%s</h2>' % _('Export')
|
|
form.render()
|
|
else:
|
|
return self.export_submit(form)
|
|
|
|
def export_submit [plain] (self, form):
|
|
dirs = []
|
|
for w in ('formdefs', 'workflows', 'roles', 'categories'):
|
|
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('.')
|
|
|
|
c = cStringIO.StringIO()
|
|
z = zipfile.ZipFile(c, 'w')
|
|
app_dir = get_publisher().app_dir
|
|
for d in dirs:
|
|
path = os.path.join(app_dir, d)
|
|
if not os.path.exists(path):
|
|
continue
|
|
for f in os.listdir(path):
|
|
z.write(os.path.join(path, f), os.path.join(d, f))
|
|
if form.get_widget('settings').parse():
|
|
z.write(os.path.join(app_dir, 'config.pck'), 'config.pck')
|
|
for f in os.listdir(app_dir):
|
|
if f.startswith('idp-') and os.path.splitext(f)[-1] in ('.pem', '.xml'):
|
|
z.write(os.path.join(app_dir, f), f)
|
|
if os.path.exists(os.path.join(app_dir, 'config')):
|
|
for f in os.listdir(os.path.join(app_dir, 'config')):
|
|
z.write(os.path.join(app_dir, 'config', f), os.path.join('config', f))
|
|
|
|
z.close()
|
|
|
|
response = get_response()
|
|
response.set_content_type('application/x-wcs')
|
|
response.set_header('content-disposition', 'attachment; filename=export.wcs')
|
|
return c.getvalue()
|
|
|
|
def p_import [html] (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'))
|
|
'<h2>%s</h2>' % _('Import')
|
|
form.render()
|
|
else:
|
|
try:
|
|
results = self.import_submit(form)
|
|
except zipfile.BadZipfile:
|
|
results = None
|
|
html_top('settings', title = _('Import'))
|
|
'<h2>%s</h2>' % _('Import')
|
|
if results:
|
|
'<p>%s</p>' % _('Imported successfully:')
|
|
'<ul>'
|
|
if results['formdefs']:
|
|
'<li>%d %s</li>' % (results['formdefs'], _('forms'))
|
|
if results['workflows']:
|
|
'<li>%d %s</li>' % (results['workflows'], _('workflows'))
|
|
if results['roles']:
|
|
'<li>%d %s</li>' % (results['roles'], _('roles'))
|
|
if results['categories']:
|
|
'<li>%d %s</li>' % (results['categories'], _('categories'))
|
|
if results['settings']:
|
|
'<li>%s</li>' % _('Settings')
|
|
'</ul>'
|
|
else:
|
|
'<p>%s</p>' % _('Error: Not a valid export file')
|
|
'<a href=".">%s</a>' % _('Back')
|
|
|
|
def import_submit(self, form):
|
|
z = zipfile.ZipFile(form.get_widget('file').parse().fp, 'r')
|
|
app_dir = get_publisher().app_dir
|
|
results = {'formdefs': 0, 'workflows': 0, 'categories': 0, 'roles': 0, 'settings': 0}
|
|
for f in z.namelist():
|
|
path = os.path.join(app_dir, f)
|
|
data = z.read(f)
|
|
if not os.path.exists(os.path.dirname(path)):
|
|
os.mkdir(os.path.dirname(path))
|
|
if f == 'config.pck':
|
|
results['settings'] = 1
|
|
d = cPickle.loads(data)
|
|
if get_publisher().cfg.has_key('sp'):
|
|
current_sp = get_publisher().cfg['sp']
|
|
else:
|
|
current_sp = None
|
|
get_publisher().cfg = d
|
|
if current_sp:
|
|
get_publisher().cfg['sp'] = current_sp
|
|
elif get_publisher().cfg.has_key('sp'):
|
|
del get_publisher().cfg['sp']
|
|
get_publisher().write_cfg()
|
|
continue
|
|
open(path, 'w').write(data)
|
|
if results.has_key(os.path.split(f)[0]):
|
|
results[os.path.split(f)[0]] += 1
|
|
|
|
# rebuild indexes for imported objects
|
|
for k, v in results.items():
|
|
if k == 'settings':
|
|
continue
|
|
if v == 0:
|
|
continue
|
|
klass = None
|
|
if k == 'formdefs':
|
|
from formdef import FormDef
|
|
klass = FormDef
|
|
elif k == 'categories':
|
|
from categories import Category
|
|
klass = Category
|
|
elif k == 'roles':
|
|
from roles import Role
|
|
klass = Role
|
|
elif k == 'workflows':
|
|
from workflows import Workflow
|
|
klass = Workflow
|
|
if klass:
|
|
klass.rebuild_indexes()
|
|
|
|
z.close()
|
|
return results
|
|
|
|
def sitename [html] (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_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'))
|
|
'<h2>%s</h2>' % _('Site Name and Addresses')
|
|
form.render()
|
|
else:
|
|
cfg_submit(form, 'misc', ['sitename', 'frontoffice-url', 'backoffice-url'])
|
|
redirect('.')
|
|
|
|
def sms [html] (self):
|
|
get_response().breadcrumb.append(('sms', _('SMS')))
|
|
html_top('settings', title = _('SMS'))
|
|
'<h2>%s</h2>' % _('SMS Options')
|
|
sms_cfg = get_cfg('sms', {})
|
|
mode = sms_cfg.get('mode', 'none')
|
|
sms = SMS.get_sms_class(mode)
|
|
if sms:
|
|
'<ul>'
|
|
try:
|
|
try:
|
|
'<li>%s %s</li>' % (_('SMS Credit:'), sms.get_money_left())
|
|
except NotImplementedError:
|
|
pass
|
|
try:
|
|
'<li>%s %s</li>' % (_('SMS Left:'), sms.get_sms_left())
|
|
except NotImplementedError:
|
|
pass
|
|
except errors.SMSError:
|
|
"<p>%s</li>" % _("Connection with SMS provider failed")
|
|
'</ul>'
|
|
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(SingleSelectWidget, 'mode', title = _('SMS Mode'),
|
|
value = mode,
|
|
options = [(str('none'), _('No support'), str('none'))]+
|
|
[(str(k), _(SMS.providers.get(k)[0]), str(k)) for k in SMS.providers.keys()])
|
|
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('.')
|
|
|
|
if sms:
|
|
for widget, name, title in sms.parameters:
|
|
form.add(widget, name, title=_(title),
|
|
value=sms_cfg.get(name, ''),
|
|
required=True)
|
|
if form.get_submit() and not form.has_errors():
|
|
cfg_submit(form, 'sms', ['mode'] + [x[1] for x in sms.parameters])
|
|
if mode != form.get_widget('mode').parse():
|
|
return redirect('sms')
|
|
else:
|
|
return redirect('.')
|
|
elif mode != form.get_widget('mode').parse():
|
|
cfg_submit(form, 'sms', ['mode',])
|
|
return redirect('sms')
|
|
else:
|
|
if form.get_submit() and form.get_widget('mode').parse() == str('none'):
|
|
return redirect('.')
|
|
|
|
if form.get_submit() and not form.has_errors():
|
|
cfg_submit(form, 'sms', ['mode',])
|
|
return redirect('sms')
|
|
else:
|
|
form.render()
|
|
|
|
if mode != 'none':
|
|
'<p><a href="smstest">%s</a></p>' % _('SMS Test')
|
|
|
|
def smstest [html] (self):
|
|
form = Form(enctype='multipart/form-data', action='smstest')
|
|
form.add(StringWidget, 'sender', title=_('Sender'), required=True)
|
|
form.add(StringWidget, 'destinations', title=_('Destinations'), required=True)
|
|
form.add(StringWidget, 'text', title=_('Text'), required=True)
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('sms')
|
|
|
|
get_response().breadcrumb.append(('sms', _('SMS')))
|
|
get_response().breadcrumb.append(('smstest', _('SMS Test')))
|
|
html_top('settings', title = _('SMS Test'))
|
|
'<h2>%s</h2>' % _('SMS Test')
|
|
form.render()
|
|
|
|
if form.get_submit() and not form.has_errors():
|
|
sms_cfg = get_cfg('sms', {})
|
|
mode = sms_cfg.get('mode', 'none')
|
|
sms = SMS.get_sms_class(mode)
|
|
|
|
sender = str(form.get_widget('sender').parse())
|
|
destinations = str(form.get_widget('destinations').parse()).split(str(','))
|
|
text = str(form.get_widget('text').parse())
|
|
|
|
try:
|
|
sms.send(sender, destinations, text)
|
|
except Exception, e:
|
|
'<pre>'
|
|
repr(e)
|
|
'</pre>'
|
|
else:
|
|
'<p>'
|
|
_('Success')
|
|
'</p>'
|
|
|
|
|
|
def utf8switch(self):
|
|
def toutf8(x):
|
|
if x is None:
|
|
return None
|
|
return unicode(x, 'iso-8859-1').encode('utf-8')
|
|
|
|
all_elems = []
|
|
|
|
from formdef import FormDef
|
|
for formdef in FormDef.select():
|
|
formdef.name = toutf8(formdef.name)
|
|
for field in formdef.fields:
|
|
for attr in ('label', 'hint', 'condition', 'items'):
|
|
if hasattr(field, attr) and type(getattr(field, attr)) is str:
|
|
setattr(field, attr, toutf8(getattr(field, attr)))
|
|
elif hasattr(field, attr) and type(getattr(field, attr)) is list:
|
|
setattr(field, attr, [toutf8(x) for x in getattr(field, attr)])
|
|
if hasattr(field, 'prefill') and getattr(field, 'prefill'):
|
|
if field.prefill.get('type') == 'string':
|
|
field.prefill['value'] = toutf8(field.prefill.get('value'))
|
|
|
|
form_data_class = formdef.data_class()
|
|
for form in form_data_class.select():
|
|
for k, v in form.data.items():
|
|
if type(v) is str:
|
|
form.data[k] = toutf8(v)
|
|
if form.evolution:
|
|
for evo in form.evolution:
|
|
if evo.comment:
|
|
evo.comment = toutf8(evo.comment)
|
|
all_elems.append(form)
|
|
all_elems.append(formdef)
|
|
|
|
from workflows import Workflow
|
|
for workflow in Workflow.select():
|
|
workflow.name = toutf8(workflow.name)
|
|
if workflow.possible_status:
|
|
for status in workflow.possible_status:
|
|
status.name = toutf8(status.name)
|
|
if not status.items:
|
|
continue
|
|
for item in status.items:
|
|
for attr in ('label', 'subject', 'body', 'message'):
|
|
if hasattr(item, attr) and type(getattr(item, attr)) is str:
|
|
setattr(item, attr, toutf8(getattr(item, attr)))
|
|
all_elems.append(workflow)
|
|
|
|
from categories import Category
|
|
for category in Category.select():
|
|
category.name = toutf8(category.name)
|
|
category.description = toutf8(category.description)
|
|
all_elems.append(category)
|
|
|
|
from roles import Role
|
|
for role in Role.select():
|
|
role.name = toutf8(role.name)
|
|
role.details = toutf8(role.details)
|
|
all_elems.append(role)
|
|
|
|
for user in get_publisher().user_class.select():
|
|
user.name = toutf8(user.name)
|
|
if hasattr(user, 'formdata') and user.formdata:
|
|
for k, v in user.formdata.items():
|
|
if type(v) is str:
|
|
user.formdata[k] = toutf8(v)
|
|
all_elems.append(user)
|
|
|
|
for cfg_section in get_publisher().cfg.values():
|
|
for k, v in cfg_section.items():
|
|
if type(v) is str:
|
|
cfg_section[k] = toutf8(v)
|
|
|
|
for elem in all_elems:
|
|
elem.store()
|
|
|
|
misc_cfg = get_cfg('misc', {})
|
|
misc_cfg['charset'] = 'utf-8'
|
|
get_publisher().cfg['misc'] = misc_cfg
|
|
get_publisher().write_cfg()
|
|
|
|
redirect('.')
|
|
|
|
def postgresql [html] (self):
|
|
if not get_publisher().has_site_option('postgresql'):
|
|
raise errors.TraversalError()
|
|
postgresql_cfg = get_cfg('postgresql', {})
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(StringWidget, 'dbname',
|
|
title=_('Database Name'), required=True,
|
|
value=postgresql_cfg.get('dbname'))
|
|
form.add(StringWidget, 'user',
|
|
title=_('User'), required=True,
|
|
value=postgresql_cfg.get('user'),
|
|
hint=_('User name used to authenticate'))
|
|
form.add(PasswordWidget, 'password',
|
|
title=_('Password'), required=True,
|
|
value=postgresql_cfg.get('password'),
|
|
hint=_('Password used to authenticate'))
|
|
form.add(StringWidget, 'host',
|
|
title=_('Host'), required=True,
|
|
value=postgresql_cfg.get('host', 'localhost'),
|
|
hint=_('Database host address'))
|
|
form.add(IntWidget, 'port',
|
|
title=_('Port'), required=True,
|
|
value=int(postgresql_cfg.get('port', 5432)),
|
|
hint=_('Connection port number'))
|
|
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(('postgresql', _('PostgreSQL Settings')))
|
|
html_top('settings', title=_('PostgreSQL Settings'))
|
|
'<h2>%s</h2>' % _('PostgreSQL Settings')
|
|
form.render()
|
|
else:
|
|
cfg_submit(form, 'postgresql', ['dbname', 'user', 'password',
|
|
'host', 'port'])
|
|
import sql
|
|
sql.do_user_table()
|
|
redirect('.')
|