320 lines
12 KiB
Python
320 lines
12 KiB
Python
# w.c.s. (asec) - w.c.s. extension for poll & survey service
|
|
# Copyright (C) 2010-2011 Entr'ouvert
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
# under the terms of the GNU Affero General Public License as published by
|
|
# the Free Software Foundation, either version 3 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 Affero General Public
|
|
# License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import imghdr
|
|
|
|
from quixote import get_request, get_response, get_session, redirect
|
|
from quixote.directory import Directory
|
|
from quixote.html import TemplateIO, htmltext
|
|
|
|
from qommon import misc, get_cfg, get_logger
|
|
from qommon.admin.cfg import cfg_submit
|
|
from qommon.backoffice.menu import html_top
|
|
import qommon.ident.password
|
|
from qommon.ident.password_accounts import PasswordAccount
|
|
import qommon.admin.texts
|
|
|
|
from qommon.form import *
|
|
|
|
import quota
|
|
|
|
|
|
def get_logo_url():
|
|
pages_data_dir = os.path.join(get_publisher().app_dir, 'pages', 'data')
|
|
for f in ('png', 'jpeg', 'gif'):
|
|
logo_path = os.path.join(pages_data_dir, 'logo.%s' % f)
|
|
if os.path.exists(logo_path):
|
|
return '%spages/data/logo.%s' % (get_publisher().get_root_url(), f)
|
|
return None
|
|
|
|
|
|
class TextsDirectory(qommon.admin.texts.TextsDirectory):
|
|
def html_top(self, title):
|
|
html_top('config', title)
|
|
|
|
class ConfigDirectory(Directory):
|
|
_q_exports = ['', 'password', 'contact', 'sitetitle', 'homepage', 'texts',
|
|
'appearance', 'logo']
|
|
|
|
def _q_traverse (self, path):
|
|
get_response().breadcrumb.append( ('config/', _('Configuration')) )
|
|
self.texts = TextsDirectory()
|
|
self.texts.texts_dict = self.texts.texts_dict.copy()
|
|
for k in self.texts.texts_dict.keys():
|
|
if k not in ('asec-recorded-vote',):
|
|
del self.texts.texts_dict[k]
|
|
return Directory._q_traverse(self, path)
|
|
|
|
def _q_index(self):
|
|
return redirect('..')
|
|
|
|
def contact(self):
|
|
get_response().breadcrumb.append( ('contact', _('Contact Information')) )
|
|
r = TemplateIO(html=True)
|
|
|
|
form = Form(enctype='multipart/form-data')
|
|
user = get_request().user
|
|
|
|
form.add(StringWidget, 'name', title=_('Name'), required=True, size=30,
|
|
value = user.name)
|
|
form.add(EmailWidget, 'email', title=_('Email'), required=False, size=30,
|
|
value=user.email)
|
|
|
|
form.add_submit('submit', _('Apply Changes'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
|
|
if form.get_submit() == 'cancel':
|
|
return redirect('.')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
self.contact_submit(form)
|
|
return redirect('.')
|
|
|
|
html_top('config', _('Contact Information'))
|
|
r += htmltext('<h2>%s</h2>') % _('Contact Information')
|
|
r += form.render()
|
|
return r.getvalue()
|
|
|
|
def contact_submit(self, form):
|
|
user = get_request().user
|
|
for f in ('name', 'email'):
|
|
widget = form.get_widget(f)
|
|
if widget:
|
|
setattr(user, f, widget.parse())
|
|
user.store()
|
|
|
|
def password(self):
|
|
get_response().breadcrumb.append( ('password', _('Password Change')) )
|
|
ident_method = get_cfg('identification', {}).get('methods', ['idp'])[0]
|
|
if ident_method != 'password':
|
|
raise errors.TraversalError()
|
|
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(PasswordWidget, 'current_password', title=_('Current Password'),
|
|
required=True)
|
|
form.add(PasswordWidget, 'new_password', title=_('New Password'),
|
|
required=True)
|
|
form.add(PasswordWidget, 'new2_password', title=_('New Password (confirm)'),
|
|
required=True)
|
|
|
|
form.add_submit('submit', _('Change Password'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
|
|
if form.get_submit() == 'cancel':
|
|
return redirect('.')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
account = PasswordAccount.get(get_session().username)
|
|
if not account.is_password_ok(form.get_widget('current_password').parse()):
|
|
form.set_error('current_password', _('Wrong Password'))
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
qommon.ident.password.check_password(form, 'new_password')
|
|
new_password = form.get_widget('new_password').parse()
|
|
new2_password = form.get_widget('new2_password').parse()
|
|
if new_password != new2_password:
|
|
form.set_error('new2_password', _('Passwords do not match'))
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
passwords_cfg = get_cfg('passwords', {})
|
|
account = PasswordAccount.get(get_session().username)
|
|
account.hashing_algo = passwords_cfg.get('hashing_algo')
|
|
account.set_password(new_password)
|
|
account.store()
|
|
return redirect('.')
|
|
|
|
html_top('config', _('Password Change'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('Password Change')
|
|
r += form.render()
|
|
return r.getvalue()
|
|
|
|
def sitetitle(self):
|
|
get_response().breadcrumb.append( ('sitetitle', _('Site Title')) )
|
|
|
|
misc_cfg = get_cfg('misc', {})
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(StringWidget, 'sitename', title=_('Site Title'),
|
|
value=misc_cfg.get('sitename', ''), size=30)
|
|
|
|
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():
|
|
html_top('config', title = _('Site Title'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('Site Title')
|
|
r += form.render()
|
|
return r.getvalue()
|
|
else:
|
|
cfg_submit(form, 'misc', ['sitename'])
|
|
redirect('.')
|
|
|
|
def homepage(self):
|
|
get_response().breadcrumb.append( ('homepage', _('Home Page Text')) )
|
|
texts_cfg = get_cfg('texts', {})
|
|
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(TextWidget, 'welcome-unlogged', title=_('For unlogged users'),
|
|
value=texts_cfg.get('text-welcome-unlogged'), cols=80, rows=10)
|
|
form.add(TextWidget, 'welcome-logged', title=_('For logged users'),
|
|
value=texts_cfg.get('text-welcome-logged'), cols=80, rows=10)
|
|
|
|
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():
|
|
html_top('config', title = _('Home Page Text'))
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<h2>%s</h2>') % _('Home Page Text')
|
|
r += form.render()
|
|
return r.getvalue()
|
|
else:
|
|
texts_cfg[str('text-welcome-unlogged')] = form.get_widget('welcome-unlogged').parse()
|
|
texts_cfg[str('text-welcome-logged')] = form.get_widget('welcome-logged').parse()
|
|
get_publisher().cfg[str('texts')] = texts_cfg
|
|
get_publisher().write_cfg()
|
|
redirect('.')
|
|
|
|
def appearance(self):
|
|
get_response().breadcrumb.append(('appearance', _('Appearance')))
|
|
html_top('config', title = _('Appearance'))
|
|
r = TemplateIO(html=True)
|
|
|
|
if quota.can_theme():
|
|
r += htmltext("<h2>%s</h2>") % _('Appearance')
|
|
r += self.appearance_theme()
|
|
|
|
if quota.can_logo():
|
|
r += self.appearance_logo()
|
|
|
|
return r.getvalue()
|
|
|
|
def appearance_theme(self):
|
|
request = get_request()
|
|
if request.form.has_key('theme'):
|
|
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()
|
|
|
|
current_theme = get_cfg('branding', {}).get('theme', 'default')
|
|
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<div class="themes">')
|
|
|
|
r += htmltext('<p>')
|
|
r += _('Click on a thumbnail to change the appearance of your site.')
|
|
r += htmltext('</p>')
|
|
|
|
themes = qommon.template.get_themes()
|
|
for theme, (label, desc, author, icon) in sorted(themes.items()):
|
|
if not theme.startswith(str('asec')):
|
|
# ignore themes that were not made for asec
|
|
continue
|
|
if current_theme == theme:
|
|
active = ' active'
|
|
else:
|
|
active = ''
|
|
r += htmltext('<div class="theme%s">') % active
|
|
r += htmltext('<a href="appearance?theme=%s">') % theme
|
|
r += htmltext('<img src="/themes/%s/icon.png" alt="" class="theme-icon" />') % theme
|
|
r += htmltext('</a>')
|
|
r += htmltext('</div>')
|
|
r += htmltext('<hr class="clear" />')
|
|
r += htmltext('</div>')
|
|
return r.getvalue()
|
|
|
|
def appearance_logo(self):
|
|
r = TemplateIO(html=True)
|
|
r += htmltext('<div id="logo-selection">')
|
|
r += htmltext('<h2>%s</h2>') % _('Logo')
|
|
r += get_session().display_message()
|
|
|
|
logo_url = get_logo_url()
|
|
if logo_url:
|
|
r += htmltext('<img src="%s" />') % logo_url
|
|
|
|
r += htmltext('<form action="logo" enctype="multipart/form-data" method="post">')
|
|
r += htmltext('<input type="file" name="file" />')
|
|
if logo_url:
|
|
r += htmltext('<input type="submit" name="submit" value="%s" />') % _('Upload New Logo')
|
|
r += htmltext('<input type="submit" name="remove" value="%s" />') % _('Remove Current Logo')
|
|
else:
|
|
r += htmltext('<input type="submit" name="submit" value="%s" />') % _('Upload Logo')
|
|
r += htmltext('</form>')
|
|
r += htmltext('</div>')
|
|
return r.getvalue()
|
|
|
|
def logo(self):
|
|
if not quota.can_logo():
|
|
raise quota.NotAvailableFeature()
|
|
|
|
form = Form(enctype='multipart/form-data', use_tokens=False)
|
|
form.add(FileWidget, 'file', title = _('Theme'), required=True)
|
|
form.add_submit('submit', _('Upload'))
|
|
form.add_submit('remove', _('Remove'))
|
|
|
|
if not form.is_submitted():
|
|
return redirect('appearance')
|
|
|
|
pages_data_dir = os.path.join(get_publisher().app_dir, 'pages', 'data')
|
|
|
|
if form.get_submit() == 'remove':
|
|
for f in ('png', 'jpeg', 'gif'):
|
|
logo_path = os.path.join(pages_data_dir, 'logo.%s' % f)
|
|
if os.path.exists(logo_path):
|
|
os.unlink(logo_path)
|
|
return redirect('appearance')
|
|
# how come?
|
|
return redirect('appearance')
|
|
|
|
if form.has_errors():
|
|
get_session().message = ('error', _('Failed to set logo.'))
|
|
return redirect('appearance')
|
|
|
|
logo = form.get_widget('file').parse().fp.read()
|
|
try:
|
|
format = imghdr.what(None, logo)
|
|
except:
|
|
get_session().message = ('error', _('Failed to set logo (unknown format).'))
|
|
return redirect('appearance')
|
|
|
|
if format not in ('png', 'jpeg', 'gif'):
|
|
get_session().message = ('error',
|
|
_('Failed to set logo (format must be png, jpeg, or gif).'))
|
|
return redirect('appearance')
|
|
|
|
if not os.path.exists(pages_data_dir):
|
|
os.makedirs(pages_data_dir)
|
|
|
|
for f in ('png', 'jpeg', 'gif'):
|
|
logo_path = os.path.join(pages_data_dir, 'logo.%s' % f)
|
|
if f == format:
|
|
file(logo_path, 'w').write(logo)
|
|
else:
|
|
# make sure older logo are removed
|
|
if os.path.exists(logo_path):
|
|
os.unlink(logo_path)
|
|
|
|
return redirect('appearance')
|