trivial: apply black

This commit is contained in:
Frédéric Péters 2021-01-19 22:08:54 +01:00
parent 1fd2171ea2
commit 2663e08e2d
19 changed files with 603 additions and 332 deletions

View File

@ -27,9 +27,17 @@ rdb.register_menu_item('payments/', _('Payments'))
rdb.register_directory('settings', admin.SettingsDirectory())
import wcs.admin.forms
wcs.admin.forms.FormsDirectory.categories = categories_admin.CategoriesDirectory()
import wcs.categories
wcs.categories.Category.XML_NODES = [('name', 'str'), ('url_name', 'str'),
('description', 'str'), ('position', 'int'),
('homepage_position', 'str'), ('redirect_url', 'str'), ('limit', 'int')]
wcs.categories.Category.XML_NODES = [
('name', 'str'),
('url_name', 'str'),
('description', 'str'),
('position', 'int'),
('homepage_position', 'str'),
('redirect_url', 'str'),
('limit', 'int'),
]

View File

@ -1,4 +1,5 @@
from wcs.workflows import WorkflowStatusItem
class AbeliumDominoRegisterFamilyWorkflowStatusItem(WorkflowStatusItem):
key = 'abelium-domino-register-family'

View File

@ -36,13 +36,21 @@ class PanelDirectory(Directory):
def permissions(self):
permissions_cfg = get_cfg('aq-permissions', {})
form = Form(enctype='multipart/form-data')
form.add(SingleSelectWidget, 'forms', title = _('Admin role for forms'),
value = permissions_cfg.get('forms', None),
options = [(None, _('Nobody'), None)] + get_user_roles())
form.add(
SingleSelectWidget,
'forms',
title=_('Admin role for forms'),
value=permissions_cfg.get('forms', None),
options=[(None, _('Nobody'), None)] + get_user_roles(),
)
if get_publisher().has_site_option('auquotidien-payments'):
form.add(SingleSelectWidget, 'payments', title = _('Admin role for payments'),
value = permissions_cfg.get('payments', None),
options = [(None, _('Nobody'), None)] + get_user_roles())
form.add(
SingleSelectWidget,
'payments',
title=_('Admin role for payments'),
value=permissions_cfg.get('payments', None),
options=[(None, _('Nobody'), None)] + get_user_roles(),
)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
@ -58,6 +66,7 @@ class PanelDirectory(Directory):
return r.getvalue()
else:
from wcs.admin.settings import cfg_submit
cfg_submit(form, 'aq-permissions', ('forms', 'payments'))
return redirect('..')
@ -83,4 +92,5 @@ class SettingsDirectory(wcs.admin.settings.SettingsDirectory):
return PanelDirectory()
return super(SettingsDirectory, self)._q_lookup(component)
from . import categories_admin

View File

@ -17,6 +17,7 @@ from wcs.qommon.form import *
CURRENT_USER = object()
def check_visibility(target, user=CURRENT_USER):
if not get_publisher().has_site_option('auquotidien-%s' % target):
# option not explicitely enabled, -> off.
@ -48,4 +49,5 @@ class BackofficeRootDirectory(wcs.backoffice.root.RootDirectory):
def home(self):
return redirect('management/')
get_publisher_class().backoffice_directory_class = BackofficeRootDirectory

View File

@ -26,6 +26,7 @@ from wcs.qommon.backoffice.menu import html_top
from wcs.qommon.admin.menu import command_icon, error_page
import wcs.admin.categories
class CategoryUI:
def __init__(self, category):
self.category = category
@ -34,28 +35,47 @@ class CategoryUI:
def get_form(self):
form = Form(enctype='multipart/form-data')
form.add(StringWidget, 'name', title = _('Category Name'), required = True, size=30,
value = self.category.name)
form.add(TextWidget, 'description', title = _('Description'), cols = 80, rows = 10,
value = self.category.description)
form.add(
StringWidget, 'name', title=_('Category Name'), required=True, size=30, value=self.category.name
)
form.add(
TextWidget,
'description',
title=_('Description'),
cols=80,
rows=10,
value=self.category.description,
)
homepage_redirect_url = get_cfg('misc', {}).get('homepage-redirect-url')
if not homepage_redirect_url:
form.add(SingleSelectWidget, 'homepage_position',
title=_('Position on homepage'),
value=self.category.get_homepage_position(),
options = [('1st', _('First Column')),
('2nd', _('Second Column')),
('side', _('Sidebar')),
('none', _('None'))])
form.add(IntWidget, 'limit',
title=_('Limit number of items displayed on homepage'),
value=self.category.get_limit())
form.add(
SingleSelectWidget,
'homepage_position',
title=_('Position on homepage'),
value=self.category.get_homepage_position(),
options=[
('1st', _('First Column')),
('2nd', _('Second Column')),
('side', _('Sidebar')),
('none', _('None')),
],
)
form.add(
IntWidget,
'limit',
title=_('Limit number of items displayed on homepage'),
value=self.category.get_limit(),
)
form.add(StringWidget, 'redirect_url', size=32,
title=_('URL Redirection'),
hint=_('If set, redirect the site category page to the given URL.'),
value=self.category.redirect_url)
form.add(
StringWidget,
'redirect_url',
size=32,
title=_('URL Redirection'),
hint=_('If set, redirect the site category page to the given URL.'),
value=self.category.redirect_url,
)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
@ -66,7 +86,7 @@ class CategoryUI:
self.category.name = form.get_widget('name').parse()
category = self.category
else:
category = Category(name = form.get_widget('name').parse())
category = Category(name=form.get_widget('name').parse())
name = form.get_widget('name').parse()
category_names = [x.name for x in Category.select() if x.id != category.id]
@ -85,7 +105,6 @@ class CategoryUI:
category.store()
class CategoryPage(wcs.admin.categories.CategoryPage):
def __init__(self, component):
self.category = Category.get(component)
@ -97,7 +116,7 @@ class CategoriesDirectory(wcs.admin.categories.CategoriesDirectory):
label = N_('Categories')
def new(self):
get_response().breadcrumb.append( ('new', _('New')) )
get_response().breadcrumb.append(('new', _('New')))
category_ui = CategoryUI(None)
form = category_ui.get_form()
if form.get_widget('cancel').parse():
@ -111,7 +130,7 @@ class CategoriesDirectory(wcs.admin.categories.CategoriesDirectory):
else:
return redirect('.')
html_top('categories', title = _('New Category'))
html_top('categories', title=_('New Category'))
r = TemplateIO(html=True)
r += htmltext('<h2>%s</h2>') % _('New Category')
r += form.render()

View File

@ -2,6 +2,7 @@ class AppointmentPart(object):
def view(self):
return ''
class AppointmentErrorPart(object):
def view(self):
return ''

View File

@ -17,6 +17,7 @@ from wcs.qommon import emails
OldFormPage = wcs.forms.root.FormPage
class AlternateFormPage(OldFormPage):
def __call__(self):
# Fix missing trailing slash; lose query string on purpose.
@ -31,6 +32,7 @@ class AlternateFormPage(OldFormPage):
get_response().filter['steps'] = form_side_html
return
wcs.forms.root.FormPage = AlternateFormPage
wcs.forms.root.PublicFormStatusPage.form_page_class = AlternateFormPage
wcs.forms.preview.PreviewFormPage.__bases__ = (AlternateFormPage,)
@ -38,9 +40,11 @@ wcs.forms.preview.PreviewFormPage.__bases__ = (AlternateFormPage,)
OldFormsRootDirectory = wcs.forms.root.RootDirectory
class AlternateFormsRootDirectory(OldFormsRootDirectory):
def form_list(self, *args, **kwargs):
form_list = OldFormsRootDirectory.form_list(self, *args, **kwargs)
return htmltext(str(form_list).replace('h2>', 'h3>'))
wcs.forms.root.RootDirectory = AlternateFormsRootDirectory

View File

@ -28,6 +28,7 @@ import wcs.myspace
from .payments import Invoice, Regie, is_payment_supported
class MyInvoicesDirectory(Directory):
_q_exports = ['']
@ -49,8 +50,7 @@ class MyInvoicesDirectory(Directory):
r += get_session().display_message()
invoices = []
invoices.extend(Invoice.get_with_indexed_value(
str('user_id'), str(user.id)))
invoices.extend(Invoice.get_with_indexed_value(str('user_id'), str(user.id)))
invoices.sort(key=lambda x: (str(x.regie_id), -x.date.toordinal()))
last_regie_id = None
@ -88,8 +88,10 @@ class MyInvoicesDirectory(Directory):
r += _('paid on %s') % misc.localstrftime(invoice.paid_date)
r += ' - '
button = _('Details')
r += htmltext('<a href="%s/invoices/%s">%s</a>' % (get_publisher().get_frontoffice_url(),
invoice.id, button))
r += htmltext(
'<a href="%s/invoices/%s">%s</a>'
% (get_publisher().get_frontoffice_url(), invoice.id, button)
)
r += htmltext('</li>')
if last_regie_id:
@ -102,8 +104,8 @@ class MyInvoicesDirectory(Directory):
class JsonDirectory(Directory):
'''Export of several lists in json, related to the current user or the
SAMLv2 NameID we'd get in the URL'''
"""Export of several lists in json, related to the current user or the
SAMLv2 NameID we'd get in the URL"""
_q_exports = ['forms']
@ -119,14 +121,12 @@ class JsonDirectory(Directory):
formdefs = FormDef.select(order_by='name', ignore_errors=True)
user_forms = []
for formdef in formdefs:
user_forms.extend(formdef.data_class().get_with_indexed_value(
'user_id', self.user.id))
user_forms.extend(formdef.data_class().get_with_indexed_value('user_id', self.user.id))
epoch = time.localtime(1970)
user_forms.sort(key=lambda x: x.receipt_time or epoch)
get_response().set_content_type('application/json')
forms_output = []
for form in user_forms:
visible_status = form.get_visible_status(user=self.user)
@ -136,21 +136,16 @@ class JsonDirectory(Directory):
name = form.formdef.name
id = form.get_display_id()
status = visible_status.name
title = _('%(name)s #%(id)s (%(status)s)') % {
'name': name,
'id': id,
'status': status
}
title = _('%(name)s #%(id)s (%(status)s)') % {'name': name, 'id': id, 'status': status}
url = form.get_url()
d = { 'title': title, 'url': url }
d = {'title': title, 'url': url}
d.update(form.get_substitution_variables(minimal=True))
forms_output.append(d)
return json.dumps(forms_output, cls=misc.JSONEncoder)
class MyspaceDirectory(wcs.myspace.MyspaceDirectory):
_q_exports = ['', 'profile', 'new', 'password', 'remove', 'drafts', 'forms',
'invoices', 'json']
_q_exports = ['', 'profile', 'new', 'password', 'remove', 'drafts', 'forms', 'invoices', 'json']
invoices = MyInvoicesDirectory()
json = JsonDirectory()
@ -161,15 +156,13 @@ class MyspaceDirectory(wcs.myspace.MyspaceDirectory):
# Migrate custom text settings
texts_cfg = get_cfg('texts', {})
if 'text-aq-top-of-profile' in texts_cfg and (
not 'text-top-of-profile' in texts_cfg):
if 'text-aq-top-of-profile' in texts_cfg and (not 'text-top-of-profile' in texts_cfg):
texts_cfg['text-top-of-profile'] = texts_cfg['text-aq-top-of-profile']
del texts_cfg['text-aq-top-of-profile']
get_publisher().write_cfg()
return Directory._q_traverse(self, path)
def _q_index(self):
user = get_request().user
if not user:
@ -186,8 +179,7 @@ class MyspaceDirectory(wcs.myspace.MyspaceDirectory):
formdefs = FormDef.select(order_by='name', ignore_errors=True)
user_forms = []
for formdef in formdefs:
user_forms.extend(formdef.data_class().get_with_indexed_value(
'user_id', user.id))
user_forms.extend(formdef.data_class().get_with_indexed_value('user_id', user.id))
epoch = time.localtime(1970)
user_forms.sort(key=lambda x: x.receipt_time or epoch)
@ -223,15 +215,20 @@ class MyspaceDirectory(wcs.myspace.MyspaceDirectory):
pass
else:
r += htmltext('<p>')
r += _('You can delete your account freely from the services portal. '
'This action is irreversible; it will destruct your personal '
'datas and destruct the access to your request history.')
r += htmltext(' <strong><a href="remove" rel="popup">%s</a></strong>.') % _('Delete My Account')
r += _(
'You can delete your account freely from the services portal. '
'This action is irreversible; it will destruct your personal '
'datas and destruct the access to your request history.'
)
r += htmltext(' <strong><a href="remove" rel="popup">%s</a></strong>.') % _(
'Delete My Account'
)
r += htmltext('</p>')
if user_forms:
r += htmltext('<h3 id="my-forms">%s</h3>') % _('My Forms')
from . import root
r += root.FormsRootDirectory().user_forms(user_forms)
return r.getvalue()
@ -267,10 +264,14 @@ class MyspaceDirectory(wcs.myspace.MyspaceDirectory):
ident_method = 'idp'
if form_data and ident_method != 'idp':
r += htmltext('<p class="command"><a href="profile" rel="popup">%s</a></p>') % _('Edit My Profile')
r += htmltext('<p class="command"><a href="profile" rel="popup">%s</a></p>') % _(
'Edit My Profile'
)
if ident_method == 'password' and passwords_cfg.get('can_change', False):
r += htmltext('<p class="command"><a href="password" rel="popup">%s</a></p>') % _('Change My Password')
r += htmltext('<p class="command"><a href="password" rel="popup">%s</a></p>') % _(
'Change My Password'
)
return r.getvalue()
@ -279,9 +280,9 @@ class MyspaceDirectory(wcs.myspace.MyspaceDirectory):
if not user or user.anonymous:
raise errors.AccessUnauthorizedError()
form = Form(enctype = 'multipart/form-data')
form = Form(enctype='multipart/form-data')
formdef = user.get_formdef()
formdef.add_fields_to_form(form, form_data = user.form_data)
formdef.add_fields_to_form(form, form_data=user.form_data)
form.add_submit('submit', _('Apply Changes'))
form.add_submit('cancel', _('Cancel'))
@ -314,11 +315,9 @@ class MyspaceDirectory(wcs.myspace.MyspaceDirectory):
if not user or user.anonymous:
raise errors.AccessUnauthorizedError()
form = Form(enctype = 'multipart/form-data')
form.add(PasswordWidget, 'new_password', title = _('New Password'),
required=True)
form.add(PasswordWidget, 'new2_password', title = _('New Password (confirm)'),
required=True)
form = Form(enctype='multipart/form-data')
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'))
@ -351,7 +350,7 @@ class MyspaceDirectory(wcs.myspace.MyspaceDirectory):
if not get_request().user or not get_request().user.anonymous:
raise errors.AccessUnauthorizedError()
form = Form(enctype = 'multipart/form-data')
form = Form(enctype='multipart/form-data')
formdef = get_publisher().user_class.get_formdef()
if formdef:
formdef.add_fields_to_form(form)
@ -376,15 +375,15 @@ class MyspaceDirectory(wcs.myspace.MyspaceDirectory):
template.html_top(_('Welcome'))
return form.render()
def remove(self):
user = get_request().user
if not user or user.anonymous:
raise errors.AccessUnauthorizedError()
form = Form(enctype = 'multipart/form-data')
form.widgets.append(HtmlWidget('<p>%s</p>' % _(
'Are you really sure you want to remove your account?')))
form = Form(enctype='multipart/form-data')
form.widgets.append(
HtmlWidget('<p>%s</p>' % _('Are you really sure you want to remove your account?'))
)
form.add_submit('submit', _('Remove my account'))
form.add_submit('cancel', _('Cancel'))
@ -394,7 +393,7 @@ class MyspaceDirectory(wcs.myspace.MyspaceDirectory):
if form.is_submitted() and not form.has_errors():
user = get_request().user
account = PasswordAccount.get_on_index(user.id, str('user_id'))
get_session_manager().expire_session()
get_session_manager().expire_session()
account.remove_self()
return redirect(get_publisher().get_root_url())
@ -402,7 +401,4 @@ class MyspaceDirectory(wcs.myspace.MyspaceDirectory):
return form.render()
TextsDirectory.register('aq-myspace-invoice',
N_('Message on top of invoices page'),
category = N_('Invoices'))
TextsDirectory.register('aq-myspace-invoice', N_('Message on top of invoices page'), category=N_('Invoices'))

View File

@ -8,8 +8,7 @@ from decimal import Decimal
from django.utils.six.moves.urllib import parse as urllib
from quixote import (redirect, get_publisher, get_request, get_session,
get_response)
from quixote import redirect, get_publisher, get_request, get_session, get_response
from quixote.directory import Directory
from quixote.html import TemplateIO, htmltext
@ -26,14 +25,14 @@ from wcs.qommon import _, N_
from wcs.qommon import force_str
from wcs.qommon import errors, get_logger, get_cfg, emails
from wcs.qommon.storage import StorableObject
from wcs.qommon.form import htmltext, StringWidget, TextWidget, SingleSelectWidget, \
WidgetDict
from wcs.qommon.form import htmltext, StringWidget, TextWidget, SingleSelectWidget, WidgetDict
from wcs.qommon.misc import simplify
from wcs.formdef import FormDef
from wcs.formdata import Evolution
from wcs.workflows import WorkflowStatusItem, register_item_class, template_on_formdata
def is_payment_supported():
if not eopayment:
return False
@ -49,8 +48,7 @@ class Regie(StorableObject):
service_options = None
def get_payment_object(self):
return eopayment.Payment(kind=self.service,
options=self.service_options)
return eopayment.Payment(kind=self.service, options=self.service_options)
class Invoice(StorableObject):
@ -97,12 +95,14 @@ class Invoice(StorableObject):
r = random.SystemRandom()
self.fresh = True
while True:
id = '-'.join([
dt.now().strftime('%Y%m%d'),
'r%s' % (self.regie_id or 'x'),
'f%s' % (self.formdef_id or 'x'),
''.join([r.choice(string.digits) for x in range(5)])
])
id = '-'.join(
[
dt.now().strftime('%Y%m%d'),
'r%s' % (self.regie_id or 'x'),
'f%s' % (self.formdef_id or 'x'),
''.join([r.choice(string.digits) for x in range(5)]),
]
)
crc = '%0.2d' % (hashlib.md5(id.encode('utf-8')).digest()[0] % 100)
id = id + '-' + crc
if not self.has_key(id):
@ -119,6 +119,7 @@ class Invoice(StorableObject):
return int(id[-2:]) == (hashlib.md5(id[:-3].encode('utf-8')).digest()[0] % 100)
except:
return False
check_crc = classmethod(check_crc)
def pay(self):
@ -150,11 +151,16 @@ class Invoice(StorableObject):
INVOICE_EVO_VIEW = {
'create': N_('Create Invoice <a href="%(url)s">%(id)s</a>: %(subject)s - %(amount)s &euro;'),
'pay': N_('Invoice <a href="%(url)s">%(id)s</a> is paid with transaction number %(transaction_order_id)s'),
'pay': N_(
'Invoice <a href="%(url)s">%(id)s</a> is paid with transaction number %(transaction_order_id)s'
),
'cancel': N_('Cancel Invoice <a href="%(url)s">%(id)s</a>'),
'try': N_('Try paying invoice <a href="%(url)s">%(id)s</a> with transaction number %(transaction_order_id)s'),
'try': N_(
'Try paying invoice <a href="%(url)s">%(id)s</a> with transaction number %(transaction_order_id)s'
),
}
class InvoiceEvolutionPart:
action = None
id = None
@ -180,8 +186,9 @@ class InvoiceEvolutionPart:
return ''
if self.transaction:
vars['transaction_order_id'] = self.transaction.order_id
return htmltext('<p class="invoice-%s">' % self.action + \
_(INVOICE_EVO_VIEW[self.action]) % vars + '</p>')
return htmltext(
'<p class="invoice-%s">' % self.action + _(INVOICE_EVO_VIEW[self.action]) % vars + '</p>'
)
class Transaction(StorableObject):
@ -206,8 +213,10 @@ class Transaction(StorableObject):
id = ''.join([r.choice(string.digits) for x in range(16)])
if not cls.has_key(id):
return id
get_new_id = classmethod(get_new_id)
class PaymentWorkflowStatusItem(WorkflowStatusItem):
description = N_('Payment Creation')
key = 'payment'
@ -224,6 +233,7 @@ class PaymentWorkflowStatusItem(WorkflowStatusItem):
def is_available(self, workflow=None):
return is_payment_supported()
is_available = classmethod(is_available)
def render_as_line(self):
@ -236,38 +246,49 @@ class PaymentWorkflowStatusItem(WorkflowStatusItem):
return _('Payable (not completed)')
def get_parameters(self):
return ('subject', 'details', 'amount', 'regie_id', 'next_status',
'request_kwargs')
return ('subject', 'details', 'amount', 'regie_id', 'next_status', 'request_kwargs')
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
if 'subject' in parameters:
form.add(StringWidget, '%ssubject' % prefix, title=_('Subject'),
value=self.subject, size=40)
form.add(StringWidget, '%ssubject' % prefix, title=_('Subject'), value=self.subject, size=40)
if 'details' in parameters:
form.add(TextWidget, '%sdetails' % prefix, title=_('Details'),
value=self.details, cols=80, rows=10)
form.add(
TextWidget, '%sdetails' % prefix, title=_('Details'), value=self.details, cols=80, rows=10
)
if 'amount' in parameters:
form.add(StringWidget, '%samount' % prefix, title=_('Amount'), value=self.amount)
if 'regie_id' in parameters:
form.add(SingleSelectWidget, '%sregie_id' % prefix,
title=_('Regie'), value=self.regie_id,
options = [(None, '---')] + [(x.id, x.label) for x in Regie.select()])
form.add(
SingleSelectWidget,
'%sregie_id' % prefix,
title=_('Regie'),
value=self.regie_id,
options=[(None, '---')] + [(x.id, x.label) for x in Regie.select()],
)
if 'next_status' in parameters:
form.add(SingleSelectWidget, '%snext_status' % prefix,
title=_('Status after validation'), value = self.next_status,
hint=_('Used only if the current status of the form does not contain any "Payment Validation" item'),
options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status])
form.add(
SingleSelectWidget,
'%snext_status' % prefix,
title=_('Status after validation'),
value=self.next_status,
hint=_(
'Used only if the current status of the form does not contain any "Payment Validation" item'
),
options=[(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status],
)
if 'request_kwargs' in parameters:
keys = ['name', 'email', 'address', 'phone', 'info1', 'info2', 'info3']
hint = ''
hint +=_('If the value starts by = it will be '
'interpreted as a Python expression.')
hint += _('If the value starts by = it will be ' 'interpreted as a Python expression.')
hint += ' '
hint += _('Standard keys are: %s.') % (', '.join(keys))
form.add(WidgetDict, 'request_kwargs',
form.add(
WidgetDict,
'request_kwargs',
title=_('Parameters for the payment system'),
hint=hint,
value = self.request_kwargs)
value=self.request_kwargs,
)
def perform(self, formdata):
invoice = Invoice(regie_id=self.regie_id, formdef_id=formdata.formdef.id)
@ -278,8 +299,9 @@ class PaymentWorkflowStatusItem(WorkflowStatusItem):
invoice.subject = template_on_formdata(formdata, self.compute(self.subject))
else:
invoice.subject = _('%(form_name)s #%(formdata_id)s') % {
'form_name': formdata.formdef.name,
'formdata_id': formdata.id }
'form_name': formdata.formdef.name,
'formdata_id': formdata.id,
}
invoice.details = template_on_formdata(formdata, self.compute(self.details))
invoice.amount = Decimal(self.compute(self.amount))
invoice.date = dt.now()
@ -300,8 +322,10 @@ class PaymentWorkflowStatusItem(WorkflowStatusItem):
# redirect the user to "my invoices"
return get_publisher().get_frontoffice_url() + '/myspace/invoices/'
register_item_class(PaymentWorkflowStatusItem)
class PaymentCancelWorkflowStatusItem(WorkflowStatusItem):
description = N_('Payment Cancel')
key = 'payment-cancel'
@ -313,6 +337,7 @@ class PaymentCancelWorkflowStatusItem(WorkflowStatusItem):
def is_available(self, workflow=None):
return is_payment_supported()
is_available = classmethod(is_available)
def render_as_line(self):
@ -332,13 +357,16 @@ class PaymentCancelWorkflowStatusItem(WorkflowStatusItem):
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
if 'reason' in parameters:
form.add(StringWidget, '%sreason' % prefix, title=_('Reason'),
value=self.reason, size=40)
form.add(StringWidget, '%sreason' % prefix, title=_('Reason'), value=self.reason, size=40)
if 'regie_id' in parameters:
form.add(SingleSelectWidget, '%sregie_id' % prefix,
title=_('Regie'), value=self.regie_id,
options = [(None, '---'), ('_all', _('All Regies'))] + \
[(x.id, x.label) for x in Regie.select()])
form.add(
SingleSelectWidget,
'%sregie_id' % prefix,
title=_('Regie'),
value=self.regie_id,
options=[(None, '---'), ('_all', _('All Regies'))]
+ [(x.id, x.label) for x in Regie.select()],
)
def perform(self, formdata):
invoices_id = []
@ -356,8 +384,9 @@ class PaymentCancelWorkflowStatusItem(WorkflowStatusItem):
# security filter: check user
invoices = [i for i in invoices if i.user_id == formdata.user_id]
# security filter: check formdata & formdef
invoices = [i for i in invoices if (i.formdata_id == formdata.id) \
and (i.formdef_id == formdata.formdef.id)]
invoices = [
i for i in invoices if (i.formdata_id == formdata.id) and (i.formdef_id == formdata.formdef.id)
]
evo = Evolution()
evo.time = time.localtime()
for invoice in invoices:
@ -370,6 +399,7 @@ class PaymentCancelWorkflowStatusItem(WorkflowStatusItem):
formdata.store()
return get_publisher().get_frontoffice_url() + '/myspace/invoices/'
register_item_class(PaymentCancelWorkflowStatusItem)
@ -377,8 +407,8 @@ def request_payment(invoice_ids, url, add_regie=True):
for invoice_id in invoice_ids:
if not Invoice.check_crc(invoice_id):
raise errors.QueryError()
invoices = [ Invoice.get(invoice_id) for invoice_id in invoice_ids ]
invoices = [ i for i in invoices if not (i.paid or i.canceled) ]
invoices = [Invoice.get(invoice_id) for invoice_id in invoice_ids]
invoices = [i for i in invoices if not (i.paid or i.canceled)]
regie_ids = set([invoice.regie_id for invoice in invoices])
# Do not apply if more than one regie is used or no invoice is not paid or canceled
if len(invoices) == 0 or len(regie_ids) != 1:
@ -420,8 +450,7 @@ def request_payment(invoice_ids, url, add_regie=True):
evo = Evolution()
evo.time = time.localtime()
evo.status = formdata.status
evo.add_part(InvoiceEvolutionPart('try', invoice,
transaction=transaction))
evo.add_part(InvoiceEvolutionPart('try', invoice, transaction=transaction))
if not formdata.evolution:
formdata.evolution = []
formdata.evolution.append(evo)
@ -434,15 +463,20 @@ def request_payment(invoice_ids, url, add_regie=True):
else:
raise NotImplementedError()
def return_eopayment_form(form):
r = TemplateIO(html=True)
r += htmltext('<html><body onload="document.payform.submit()">')
r += htmltext('<form action="%s" method="%s" name="payform">') % (force_str(form.url), force_str(form.method))
r += htmltext('<form action="%s" method="%s" name="payform">') % (
force_str(form.url),
force_str(form.method),
)
for field in form.fields:
r += htmltext('<input type="%s" name="%s" value="%s"/>') % (
force_str(field['type']),
force_str(field['name']),
force_str(field['value']))
force_str(field['type']),
force_str(field['name']),
force_str(field['value']),
)
r += htmltext('<input type="submit" name="submit" value="%s"/>') % _('Pay')
r += htmltext('</body></html>')
return r.getvalue()
@ -458,6 +492,7 @@ class PaymentValidationWorkflowStatusItem(WorkflowStatusItem):
def is_available(self, workflow=None):
return is_payment_supported()
is_available = classmethod(is_available)
def render_as_line(self):
@ -468,9 +503,14 @@ class PaymentValidationWorkflowStatusItem(WorkflowStatusItem):
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
if 'next_status' in parameters:
form.add(SingleSelectWidget, '%snext_status' % prefix,
title=_('Status once validated'), value = self.next_status,
options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status])
form.add(
SingleSelectWidget,
'%snext_status' % prefix,
title=_('Status once validated'),
value=self.next_status,
options=[(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status],
)
register_item_class(PaymentValidationWorkflowStatusItem)
@ -507,8 +547,10 @@ class PublicPaymentRegieBackDirectory(Directory):
transaction.bank_data = bank_data
transaction.store()
if payment_response.signed and payment_response.is_paid() and commit:
logger.info('transaction %s successful, bankd_id:%s bank_data:%s' % (
order_id, payment_response.transaction_id, bank_data))
logger.info(
'transaction %s successful, bankd_id:%s bank_data:%s'
% (order_id, payment_response.transaction_id, bank_data)
)
for invoice_id in transaction.invoice_ids:
# all invoices are now paid
@ -530,8 +572,7 @@ class PublicPaymentRegieBackDirectory(Directory):
evo = Evolution()
evo.time = time.localtime()
evo.status = formdata.status
evo.add_part(InvoiceEvolutionPart('pay', invoice,
transaction=transaction))
evo.add_part(InvoiceEvolutionPart('pay', invoice, transaction=transaction))
if not formdata.evolution:
formdata.evolution = []
formdata.evolution.append(evo)
@ -540,11 +581,9 @@ class PublicPaymentRegieBackDirectory(Directory):
formdata.perform_workflow()
elif payment_response.is_error() and commit:
logger.info('transaction %s finished with failure, bank_data:%s' % (
order_id, bank_data))
logger.info('transaction %s finished with failure, bank_data:%s' % (order_id, bank_data))
elif commit:
logger.info('transaction %s is in intermediate state, bank_data:%s' % (
order_id, bank_data))
logger.info('transaction %s is in intermediate state, bank_data:%s' % (order_id, bank_data))
if payment_response.return_content != None and self.asynchronous:
get_response().set_content_type('text/plain')
return payment_response.return_content
@ -560,6 +599,7 @@ class PublicPaymentRegieBackDirectory(Directory):
url += '/myspace/invoices/'
return redirect(url)
class PublicPaymentDirectory(Directory):
_q_exports = ['init', 'back', 'back_asynchronous']
@ -579,23 +619,26 @@ class PublicPaymentDirectory(Directory):
return request_payment(invoice_ids, url)
def notify_new_invoice(invoice):
notify_invoice(invoice, 'payment-new-invoice-email')
def notify_paid_invoice(invoice):
notify_invoice(invoice, 'payment-invoice-paid-email')
def notify_canceled_invoice(invoice):
notify_invoice(invoice, 'payment-invoice-canceled-email')
def notify_invoice(invoice, template):
user = invoice.get_user()
assert user is not None
regie = Regie.get(id=invoice.regie_id)
emails.custom_template_email(template, {
'user': user,
'invoice': invoice,
'regie': regie,
'invoice_url': invoice.payment_url()
}, user.email, fire_and_forget = True)
emails.custom_template_email(
template,
{'user': user, 'invoice': invoice, 'regie': regie, 'invoice_url': invoice.payment_url()},
user.email,
fire_and_forget=True,
)

View File

@ -19,14 +19,14 @@ from wcs.qommon.admin.emails import EmailsDirectory
from wcs.qommon.backoffice.menu import html_top
from wcs.qommon import get_cfg
from .payments import (eopayment, Regie, is_payment_supported, Invoice,
Transaction, notify_paid_invoice)
from .payments import eopayment, Regie, is_payment_supported, Invoice, Transaction, notify_paid_invoice
from wcs.qommon.admin.texts import TextsDirectory
if not set:
from sets import Set as set
def invoice_as_html(invoice):
r = TemplateIO(html=True)
r += htmltext('<div id="invoice">')
@ -40,16 +40,20 @@ def invoice_as_html(invoice):
r += '\nTransaction:\n'
r += pprint.pformat(transaction.__dict__)
r += htmltext('\n-->')
if invoice.formdef_id and invoice.formdata_id and \
get_session().user == invoice.user_id:
if invoice.formdef_id and invoice.formdata_id and get_session().user == invoice.user_id:
formdef = FormDef.get(invoice.formdef_id)
if formdef:
formdata = formdef.data_class().get(invoice.formdata_id, ignore_errors=True)
if formdata:
name = _('%(form_name)s #%(formdata_id)s') % {
'form_name': formdata.formdef.name,
'formdata_id': formdata.id }
r += htmltext('<p class="from">%s <a href="%s">%s</a></p>') % (_('From:'), formdata.get_url(), name)
'form_name': formdata.formdef.name,
'formdata_id': formdata.id,
}
r += htmltext('<p class="from">%s <a href="%s">%s</a></p>') % (
_('From:'),
formdata.get_url(),
name,
)
r += htmltext('<p class="regie">%s</p>') % _('Regie: %s') % Regie.get(invoice.regie_id).label
r += htmltext('<p class="date">%s</p>') % _('Created on: %s') % misc.localstrftime(invoice.date)
if invoice.details:
@ -114,7 +118,9 @@ class InvoicesDirectory(Directory):
if len(regies_id) == 1:
r += htmltext('<p class="command">')
r += htmltext('<a href="%s/payment/init?invoice_ids=%s">') % (
get_publisher().get_frontoffice_url(), component)
get_publisher().get_frontoffice_url(),
component,
)
if len(invoice_ids) > 1:
r += _('Pay Selected Invoices')
else:
@ -136,7 +142,7 @@ class RegieDirectory(Directory):
self.regie = regie
def _q_index(self):
html_top('payments', title = _('Regie: %s') % self.regie.label)
html_top('payments', title=_('Regie: %s') % self.regie.label)
r = TemplateIO(html=True)
get_response().filter['sidebar'] = self.get_sidebar()
r += htmltext('<h2>%s</h2>') % _('Regie: %s') % self.regie.label
@ -182,26 +188,31 @@ class RegieDirectory(Directory):
self.submit(form)
return redirect('..')
html_top('payments', title = _('Edit Regie: %s') % self.regie.label)
html_top('payments', title=_('Edit Regie: %s') % self.regie.label)
r = TemplateIO(html=True)
r += htmltext('<h2>%s</h2>') % _('Edit Regie: %s') % self.regie.label
r += form.render()
return r.getvalue()
def form(self):
form = Form(enctype='multipart/form-data')
form.add(StringWidget, 'label', title=_('Label'), required=True,
value=self.regie.label)
form.add(TextWidget, 'description', title=_('Description'),
value=self.regie.description, rows=5, cols=60)
form.add(SingleSelectWidget, 'service', title=_('Banking Service'),
value=self.regie.service, required=True,
options = [
('dummy', _('Dummy (for tests)')),
('sips', 'SIPS'),
('systempayv2', 'systempay (Banque Populaire)'),
('spplus', _('SP+ (Caisse d\'epargne)'))])
form.add(StringWidget, 'label', title=_('Label'), required=True, value=self.regie.label)
form.add(
TextWidget, 'description', title=_('Description'), value=self.regie.description, rows=5, cols=60
)
form.add(
SingleSelectWidget,
'service',
title=_('Banking Service'),
value=self.regie.service,
required=True,
options=[
('dummy', _('Dummy (for tests)')),
('sips', 'SIPS'),
('systempayv2', 'systempay (Banque Populaire)'),
('spplus', _('SP+ (Caisse d\'epargne)')),
],
)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
return form
@ -215,8 +226,7 @@ class RegieDirectory(Directory):
def delete(self):
form = Form(enctype='multipart/form-data')
form.widgets.append(HtmlWidget('<p>%s</p>' % _(
'You are about to irrevocably delete this regie.')))
form.widgets.append(HtmlWidget('<p>%s</p>' % _('You are about to irrevocably delete this regie.')))
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_submit() == 'cancel':
@ -224,7 +234,7 @@ class RegieDirectory(Directory):
if not form.is_submitted() or form.has_errors():
get_response().breadcrumb.append(('delete', _('Delete')))
r = TemplateIO(html=True)
html_top('payments', title = _('Delete Regie'))
html_top('payments', title=_('Delete Regie'))
r += htmltext('<h2>%s</h2>') % _('Deleting Regie: %s') % self.regie.label
r += form.render()
return r.getvalue()
@ -267,11 +277,9 @@ class RegieDirectory(Directory):
kwargs['cols'] = 80
kwargs['rows'] = 5
if 'type' not in infos or infos['type'] is str:
form.add(widget, name, title=_(title),
value=service_options.get(name), **kwargs)
form.add(widget, name, title=_(title), value=service_options.get(name), **kwargs)
elif infos['type'] is bool:
form.add(CheckboxWidget, name, title=title,
value=service_options.get(name), **kwargs)
form.add(CheckboxWidget, name, title=title, value=service_options.get(name), **kwargs)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
return form
@ -297,7 +305,7 @@ class RegieDirectory(Directory):
return redirect('.')
if form.is_submitted() and not form.has_errors():
if self.submit_options(form, module):
if self.submit_options(form, module):
return redirect('..')
html_top('payments', title=_('Edit Service Options'))
@ -360,8 +368,7 @@ class RegieDirectory(Directory):
def get_invoices(self):
sort_by = self.get_sort_by()
invoices = list(Invoice.get_with_indexed_value('regie_id', self.regie.id,
ignore_errors=True))
invoices = list(Invoice.get_with_indexed_value('regie_id', self.regie.id, ignore_errors=True))
if 'date' in sort_by:
reverse = True
key = lambda i: getattr(i, sort_by) or datetime.datetime.now()
@ -372,31 +379,35 @@ class RegieDirectory(Directory):
return invoices
def unpay(self, request, invoice):
get_logger().info(_('manually set unpaid invoice %(invoice_id)s in regie %(regie)s')
% dict(invoice_id=invoice.id, regie=self.regie.id))
get_logger().info(
_('manually set unpaid invoice %(invoice_id)s in regie %(regie)s')
% dict(invoice_id=invoice.id, regie=self.regie.id)
)
transaction = Transaction()
transaction.invoice_ids = [ invoice.id ]
transaction.invoice_ids = [invoice.id]
transaction.order_id = 'Manual action'
transaction.start = datetime.datetime.now()
transaction.end = transaction.start
transaction.bank_data = {
'action': 'Set unpaid',
'by': request.user.get_display_name() + ' (%s)' % request.user.id
transaction.bank_data = {
'action': 'Set unpaid',
'by': request.user.get_display_name() + ' (%s)' % request.user.id,
}
transaction.store()
invoice.unpay()
def pay(self, request, invoice):
get_logger().info(_('manually set paid invoice %(invoice_id)s in regie %(regie)s')
% dict(invoice_id=invoice.id, regie=self.regie.id))
get_logger().info(
_('manually set paid invoice %(invoice_id)s in regie %(regie)s')
% dict(invoice_id=invoice.id, regie=self.regie.id)
)
transaction = Transaction()
transaction.invoice_ids = [ invoice.id ]
transaction.invoice_ids = [invoice.id]
transaction.order_id = 'Manual action'
transaction.start = datetime.datetime.now()
transaction.end = transaction.start
transaction.bank_data = {
'action': 'Set paid',
'by': request.user.get_display_name() + ' (%s)' % request.user.id
transaction.bank_data = {
'action': 'Set paid',
'by': request.user.get_display_name() + ' (%s)' % request.user.id,
}
transaction.store()
invoice.pay()
@ -430,7 +441,7 @@ class RegieDirectory(Directory):
r += htmltext('</tr>')
r += htmltext('</thead>')
invoices = self.get_invoices()
for invoice in invoices[offset:offset+self.PAGINATION]:
for invoice in invoices[offset : offset + self.PAGINATION]:
r += htmltext('<tbody class="invoice-rows">')
r += htmltext('<tr class="invoice-row"><td>')
r += misc.localstrftime(invoice.date)
@ -456,8 +467,7 @@ class RegieDirectory(Directory):
r += htmltext('</form>')
r += htmltext('</td></tr>')
transactions = list(Transaction.get_with_indexed_value('invoice_ids',
invoice.id))
transactions = list(Transaction.get_with_indexed_value('invoice_ids', invoice.id))
for transaction in sorted(transactions, key=lambda x: x.start):
r += htmltext('<tr>')
r += htmltext('<td></td>')
@ -473,11 +483,9 @@ class RegieDirectory(Directory):
r += htmltext('</tbody>')
r += htmltext('</tbody></table>')
if offset != 0:
r += htmltext('<a href="?offset=%d>%s</a> ') % (
max(0, offset-self.PAGINATION), _('Previous'))
r += htmltext('<a href="?offset=%d>%s</a> ') % (max(0, offset - self.PAGINATION), _('Previous'))
if offset + self.PAGINATION < len(invoices):
r += htmltext('<a href="?offset=%d>%s</a> ') % (
max(0, offset-self.PAGINATION), _('Previous'))
r += htmltext('<a href="?offset=%d>%s</a> ') % (max(0, offset - self.PAGINATION), _('Previous'))
return r.getvalue()
@ -503,7 +511,7 @@ class RegiesDirectory(Directory):
return redirect('%s/' % regie_ui.regie.id)
get_response().breadcrumb.append(('new', _('New Regie')))
html_top('payments', title = _('New Regie'))
html_top('payments', title=_('New Regie'))
r = TemplateIO(html=True)
r += htmltext('<h2>%s</h2>') % _('New Regie')
r += form.render()
@ -526,6 +534,7 @@ class PaymentsDirectory(AccessControlled, Directory):
def is_accessible(self, user):
from .backoffice import check_visibility
return check_visibility('payments', user)
def _q_access(self):
@ -535,8 +544,8 @@ class PaymentsDirectory(AccessControlled, Directory):
if not self.is_accessible(user):
raise errors.AccessForbiddenError(
public_msg = _('You are not allowed to access Payments Management'),
location_hint = 'backoffice')
public_msg=_('You are not allowed to access Payments Management'), location_hint='backoffice'
)
get_response().breadcrumb.append(('payments/', _('Payments')))
@ -573,33 +582,43 @@ class PaymentsDirectory(AccessControlled, Directory):
return r.getvalue()
TextsDirectory.register('aq-invoice',
N_('Message on top of an invoice'),
category = N_('Invoices'))
TextsDirectory.register('aq-invoice', N_('Message on top of an invoice'), category=N_('Invoices'))
EmailsDirectory.register('payment-new-invoice-email',
N_('New invoice'),
N_('Available variables: user, regie, invoice, invoice_url'),
category = N_('Invoices'),
default_subject = N_('New invoice'),
default_body = N_('''
EmailsDirectory.register(
'payment-new-invoice-email',
N_('New invoice'),
N_('Available variables: user, regie, invoice, invoice_url'),
category=N_('Invoices'),
default_subject=N_('New invoice'),
default_body=N_(
'''
A new invoice is available at [invoice_url].
'''))
'''
),
)
EmailsDirectory.register('payment-invoice-paid-email',
N_('Paid invoice'),
N_('Available variables: user, regie, invoice, invoice_url'),
category = N_('Invoices'),
default_subject = N_('Paid invoice'),
default_body = N_('''
EmailsDirectory.register(
'payment-invoice-paid-email',
N_('Paid invoice'),
N_('Available variables: user, regie, invoice, invoice_url'),
category=N_('Invoices'),
default_subject=N_('Paid invoice'),
default_body=N_(
'''
The invoice [invoice_url] has been paid.
'''))
'''
),
)
EmailsDirectory.register('payment-invoice-canceled-email',
N_('Canceled invoice'),
N_('Available variables: user, regie, invoice, invoice_url'),
category = N_('Invoices'),
default_subject = N_('Canceled invoice'),
default_body = N_('''
EmailsDirectory.register(
'payment-invoice-canceled-email',
N_('Canceled invoice'),
N_('Available variables: user, regie, invoice, invoice_url'),
category=N_('Invoices'),
default_subject=N_('Canceled invoice'),
default_body=N_(
'''
The invoice [invoice.id] has been canceled.
'''))
'''
),
)

View File

@ -62,12 +62,17 @@ def category_get_homepage_position(self):
if self.url_name == 'consultations':
return '2nd'
return '1st'
Category.get_homepage_position = category_get_homepage_position
def category_get_limit(self):
if hasattr(self, 'limit') and self.limit is not None:
return self.limit
return 7
Category.get_limit = category_get_limit
Category.TEXT_ATTRIBUTES = ['name', 'url_name', 'description', 'homepage_position']
@ -75,6 +80,7 @@ Category.INT_ATTRIBUTES = ['position', 'limit']
OldRegisterDirectory = wcs.root.RegisterDirectory
class AlternateRegisterDirectory(OldRegisterDirectory):
def _q_traverse(self, path):
get_response().filter['bigdiv'] = 'new_member'
@ -88,7 +94,7 @@ class AlternateRegisterDirectory(OldRegisterDirectory):
idps = get_cfg('idp', {})
if len(idps) == 0:
return template.error_page(_('Authentication subsystem is not yet configured.'))
ident_methods = ['idp'] # fallback to old behaviour; saml.
ident_methods = ['idp'] # fallback to old behaviour; saml.
if len(ident_methods) == 1:
method = ident_methods[0]
@ -97,8 +103,10 @@ class AlternateRegisterDirectory(OldRegisterDirectory):
return wcs.qommon.ident.register(method)
OldLoginDirectory = wcs.root.LoginDirectory
class AlternateLoginDirectory(OldLoginDirectory):
def _q_traverse(self, path):
get_response().filter['bigdiv'] = 'member'
@ -125,9 +133,8 @@ class AlternateLoginDirectory(OldLoginDirectory):
if after_url:
root_url = get_publisher().get_root_url()
after_path = urlparse.urlparse(after_url)[2]
after_path = after_path[len(root_url):]
if after_path.startswith(str('admin')) or \
after_path.startswith(str('backoffice')):
after_path = after_path[len(root_url) :]
if after_path.startswith(str('admin')) or after_path.startswith(str('backoffice')):
ident_methods = ['idp']
# don't display authentication system choice
@ -143,12 +150,12 @@ class AlternateLoginDirectory(OldLoginDirectory):
r = TemplateIO(html=True)
get_response().breadcrumb.append(('login', _('Login')))
identities_cfg = get_cfg('identities', {})
form = Form(enctype = 'multipart/form-data', id = 'login-form', use_tokens = False)
form = Form(enctype='multipart/form-data', id='login-form', use_tokens=False)
if identities_cfg.get('email-as-username', False):
form.add(StringWidget, 'username', title = _('Email'), size=25, required=True)
form.add(StringWidget, 'username', title=_('Email'), size=25, required=True)
else:
form.add(StringWidget, 'username', title = _('Username'), size=25, required=True)
form.add(PasswordWidget, 'password', title = _('Password'), size=25, required=True)
form.add(StringWidget, 'username', title=_('Username'), size=25, required=True)
form.add(PasswordWidget, 'password', title=_('Password'), size=25, required=True)
form.add_submit('submit', _('Connect'))
if form.is_submitted() and not form.has_errors():
tmp = wcs.qommon.ident.password.MethodDirectory().login_submit(form)
@ -161,20 +168,24 @@ class AlternateLoginDirectory(OldLoginDirectory):
base_url = get_publisher().get_root_url()
r += htmltext('<p><a href="%sident/password/forgotten">%s</a></p>') % (
base_url, _('Forgotten password ?'))
base_url,
_('Forgotten password ?'),
)
r += htmltext('</div>')
# XXX: this part only supports a single IdP
r += htmltext('<div id="login-sso">')
r += TextsDirectory.get_html_text('aq-sso-text')
form = Form(enctype='multipart/form-data',
action = '%sident/idp/login' % base_url)
form = Form(enctype='multipart/form-data', action='%sident/idp/login' % base_url)
form.add_hidden('method', 'idp')
for kidp, idp in get_cfg('idp', {}).items():
p = lasso.Provider(lasso.PROVIDER_ROLE_IDP,
misc.get_abs_path(idp['metadata']),
misc.get_abs_path(idp.get('publickey')), None)
p = lasso.Provider(
lasso.PROVIDER_ROLE_IDP,
misc.get_abs_path(idp['metadata']),
misc.get_abs_path(idp.get('publickey')),
None,
)
form.add_hidden('idp', p.providerId)
break
form.add_submit('submit', _('Connect'))
@ -184,15 +195,19 @@ class AlternateLoginDirectory(OldLoginDirectory):
get_request().environ['REQUEST_METHOD'] = 'GET'
r += htmltext("""<script type="text/javascript">
r += htmltext(
"""<script type="text/javascript">
document.getElementById('login-form')['username'].focus();
</script>""")
</script>"""
)
return r.getvalue()
else:
return OldLoginDirectory._q_index(self)
OldIdentDirectory = wcs.root.IdentDirectory
class AlternateIdentDirectory(OldIdentDirectory):
def _q_traverse(self, path):
get_response().filter['bigdiv'] = 'member'
@ -206,15 +221,41 @@ class AlternatePreviewDirectory(PreviewDirectory):
class AlternateRootDirectory(OldRootDirectory):
_q_exports = ['', 'admin', 'backoffice', 'forms', 'login', 'logout',
'saml', 'register', 'ident', 'afterjobs',
('informations-editeur', 'informations_editeur'),
'myspace', 'services', 'categories', 'user',
('tmp-upload', 'tmp_upload'), 'json', '__version__',
'themes', 'pages', 'payment', 'invoices', 'roles',
'api', 'code', 'fargo', 'tryauth', 'auth', 'preview',
('reload-top', 'reload_top'), 'static',
('i18n.js', 'i18n_js'), 'actions',]
_q_exports = [
'',
'admin',
'backoffice',
'forms',
'login',
'logout',
'saml',
'register',
'ident',
'afterjobs',
('informations-editeur', 'informations_editeur'),
'myspace',
'services',
'categories',
'user',
('tmp-upload', 'tmp_upload'),
'json',
'__version__',
'themes',
'pages',
'payment',
'invoices',
'roles',
'api',
'code',
'fargo',
'tryauth',
'auth',
'preview',
('reload-top', 'reload_top'),
'static',
('i18n.js', 'i18n_js'),
'actions',
]
register = AlternateRegisterDirectory()
login = AlternateLoginDirectory()
@ -250,7 +291,7 @@ class AlternateRootDirectory(OldRootDirectory):
response.filter['keywords'] = template.get_current_theme().get('keywords')
get_publisher().substitutions.feed(self)
response.breadcrumb = [ ('', _('Home')) ]
response.breadcrumb = [('', _('Home'))]
if not self.admin:
self.admin = get_publisher().admin_directory_class()
@ -279,7 +320,7 @@ class AlternateRootDirectory(OldRootDirectory):
if get_request().get_query():
uri_rest += '?' + get_request().get_query()
if uri_rest.startswith(base_url):
uri_rest = uri_rest[len(base_url):]
uri_rest = uri_rest[len(base_url) :]
if f.category:
if f.category.url_name == f.url_name:
return FormsRootDirectory(f.category)._q_traverse(path[1:])
@ -298,7 +339,6 @@ class AlternateRootDirectory(OldRootDirectory):
raise e
def _q_lookup(self, component):
# is this a category ?
try:
@ -316,8 +356,7 @@ class AlternateRootDirectory(OldRootDirectory):
# if the form has no category, or the request is a POST, or the
# slug matches both a category and a formdef, directly call
# into FormsRootDirectory.
if formdef.category_id is None or get_request().get_method() == 'POST' or (
formdef and category):
if formdef.category_id is None or get_request().get_method() == 'POST' or (formdef and category):
get_response().filter['bigdiv'] = 'rub_service'
return FormsRootDirectory()._q_lookup(component)
@ -347,8 +386,9 @@ class AlternateRootDirectory(OldRootDirectory):
redirect_url = get_cfg('misc', {}).get('homepage-redirect-url')
if redirect_url:
return redirect(misc.get_variadic_url(redirect_url,
get_publisher().substitutions.get_context_variables()))
return redirect(
misc.get_variadic_url(redirect_url, get_publisher().substitutions.get_context_variables())
)
template.html_top()
r = TemplateIO(html=True)
@ -384,7 +424,7 @@ class AlternateRootDirectory(OldRootDirectory):
def services(self):
template.html_top()
get_response().filter['bigdiv'] = 'rub_service'
return self.box_services(level = 2)
return self.box_services(level=2)
def box_services(self, level=3, position=None):
## Services
@ -393,17 +433,18 @@ class AlternateRootDirectory(OldRootDirectory):
else:
accepted_roles = []
cats = Category.select(order_by = 'name')
cats = Category.select(order_by='name')
cats = [x for x in cats if x.url_name != 'consultations']
Category.sort_by_position(cats)
all_formdefs = FormDef.select(lambda x: not x.is_disabled() or x.disabled_redirection,
order_by = 'name')
all_formdefs = FormDef.select(
lambda x: not x.is_disabled() or x.disabled_redirection, order_by='name'
)
if position:
t = self.display_list_of_formdefs(
[x for x in cats if x.get_homepage_position() == position],
all_formdefs, accepted_roles)
[x for x in cats if x.get_homepage_position() == position], all_formdefs, accepted_roles
)
else:
t = self.display_list_of_formdefs(cats, all_formdefs, accepted_roles)
@ -445,7 +486,7 @@ class AlternateRootDirectory(OldRootDirectory):
formdefs_advertise = []
for formdef in formdefs[:]:
if formdef.is_disabled(): # is a redirection
if formdef.is_disabled(): # is a redirection
continue
if not formdef.roles:
continue
@ -473,7 +514,9 @@ class AlternateRootDirectory(OldRootDirectory):
keywords[keyword] = True
r += htmltext('<li id="category-%s" data-keywords="%s">') % (
category.url_name, ' '.join(keywords))
category.url_name,
' '.join(keywords),
)
r += htmltext('<strong>')
r += htmltext('<a href="%s/">') % category.url_name
r += category.name
@ -487,18 +530,28 @@ class AlternateRootDirectory(OldRootDirectory):
if formdef.is_disabled() and formdef.disabled_redirection:
classes.append('redirection')
r += htmltext('<a class="%s" href="%s/%s/">%s</a>') % (
' '.join(classes), category.url_name, formdef.url_name, formdef.name)
' '.join(classes),
category.url_name,
formdef.url_name,
formdef.name,
)
r += htmltext('</li>\n')
if len(formdefs) < limit:
for formdef in formdefs_advertise[:limit-len(formdefs)]:
for formdef in formdefs_advertise[: limit - len(formdefs)]:
r += htmltext('<li class="required-authentication">')
r += htmltext('<a href="%s/%s/">%s</a>') % (category.url_name, formdef.url_name, formdef.name)
r += htmltext('<a href="%s/%s/">%s</a>') % (
category.url_name,
formdef.url_name,
formdef.name,
)
r += htmltext('<span> (%s)</span>') % _('authentication required')
r += htmltext('</li>\n')
if (len(formdefs)+len(formdefs_advertise)) > limit:
r += htmltext('<li class="all-forms"><a href="%s/" title="%s">%s</a></li>') % (category.url_name,
_('Access to all forms of the "%s" category') % category.name,
_('Access to all forms in this category'))
if (len(formdefs) + len(formdefs_advertise)) > limit:
r += htmltext('<li class="all-forms"><a href="%s/" title="%s">%s</a></li>') % (
category.url_name,
_('Access to all forms of the "%s" category') % category.name,
_('Access to all forms in this category'),
)
r += htmltext('</ul>')
r += htmltext('</li>\n')
@ -509,10 +562,13 @@ class AlternateRootDirectory(OldRootDirectory):
if not cats:
return
consultations_category = cats[0]
formdefs = FormDef.select(lambda x: (
str(x.category_id) == str(consultations_category.id) and
(not x.is_disabled() or x.disabled_redirection)),
order_by = 'name')
formdefs = FormDef.select(
lambda x: (
str(x.category_id) == str(consultations_category.id)
and (not x.is_disabled() or x.disabled_redirection)
),
order_by='name',
)
if not formdefs:
return
## Consultations
@ -523,8 +579,11 @@ class AlternateRootDirectory(OldRootDirectory):
r += htmltext('<ul>')
for formdef in formdefs:
r += htmltext('<li>')
r += htmltext('<a href="%s/%s/">%s</a>') % (consultations_category.url_name,
formdef.url_name, formdef.name)
r += htmltext('<a href="%s/%s/">%s</a>') % (
consultations_category.url_name,
formdef.url_name,
formdef.name,
)
r += htmltext('</li>')
r += htmltext('</ul>')
r += htmltext('</div>')
@ -534,16 +593,18 @@ class AlternateRootDirectory(OldRootDirectory):
r = TemplateIO(html=True)
root_url = get_publisher().get_root_url()
if (path == [''] and
'include-tracking-code-form' in get_response().filter.get('keywords', []) and
self.has_anonymous_access_codes()):
if (
path == ['']
and 'include-tracking-code-form' in get_response().filter.get('keywords', [])
and self.has_anonymous_access_codes()
):
r += htmltext('<form id="follow-form" action="%scode/load">') % root_url
r += htmltext('<h3>%s</h3>') % _('Tracking code')
r += htmltext('<input size="12" name="code" placeholder="%s"/>') % _('ex: RPQDFVCD')
r += htmltext('<input type="submit" value="%s"/>') % _('Load')
r += htmltext('</form>')
cats = Category.select(order_by = 'name')
cats = Category.select(order_by='name')
cats = [x for x in cats if x.url_name != 'consultations' and x.get_homepage_position() == 'side']
Category.sort_by_position(cats)
if cats:
@ -574,7 +635,9 @@ class AlternateRootDirectory(OldRootDirectory):
r += htmltext('<h3>%s</h3>') % _('My Space')
r += htmltext('<ul>')
if get_request().user and not get_request().user.anonymous:
r += htmltext(' <li><a href="myspace/" id="member">%s</a></li>') % _('Access to your personal space')
r += htmltext(' <li><a href="myspace/" id="member">%s</a></li>') % _(
'Access to your personal space'
)
r += htmltext(' <li><a href="logout" id="logout">%s</a></li>') % _('Logout')
else:
r += htmltext(' <li><a href="register/" id="inscr">%s</a></li>') % _('Registration')
@ -583,7 +646,7 @@ class AlternateRootDirectory(OldRootDirectory):
r += htmltext('</div>')
return r.getvalue()
def page_view(self, key, title, urlname = None):
def page_view(self, key, title, urlname=None):
if not urlname:
urlname = key[3:].replace(str('_'), str('-'))
get_response().breadcrumb.append((urlname, title))
@ -596,8 +659,7 @@ class AlternateRootDirectory(OldRootDirectory):
def informations_editeur(self):
get_response().filter['bigdiv'] = 'info'
return self.page_view('aq-editor-info', _('Editor Informations'),
urlname = 'informations_editeur')
return self.page_view('aq-editor-info', _('Editor Informations'), urlname='informations_editeur')
def accessibility(self):
get_response().filter['bigdiv'] = 'accessibility'
@ -613,6 +675,7 @@ class AlternateRootDirectory(OldRootDirectory):
from qommon.publisher import get_publisher_class
get_publisher_class().root_directory_class = AlternateRootDirectory
get_publisher_class().after_login_url = 'myspace/'
get_publisher_class().use_sms_feature = True
@ -622,9 +685,14 @@ TextsDirectory.register('aq-editor-info', N_('Editor Informations'))
TextsDirectory.register('aq-accessibility', N_('Accessibility Statement'))
TextsDirectory.register('aq-contact', N_('Contact Information'))
TextsDirectory.register('aq-help', N_('Help'))
TextsDirectory.register('aq-sso-text', N_('Connecting with Identity Provider'),
default = N_('''<h3>Connecting with Identity Provider</h3>
TextsDirectory.register(
'aq-sso-text',
N_('Connecting with Identity Provider'),
default=N_(
'''<h3>Connecting with Identity Provider</h3>
<p>You can also use your identity provider to connect.
</p>'''))
</p>'''
),
)
TextsDirectory.register('aq-home-page', N_('Home Page'), wysiwyg = True)
TextsDirectory.register('aq-home-page', N_('Home Page'), wysiwyg=True)

View File

@ -9,8 +9,8 @@ import wcs.qommon.saml2
class Saml2Directory(wcs.qommon.saml2.Saml2Directory):
def extract_attributes(self, session, login):
'''Separate attributes as two dictionaries: one for last value, one for
the list of values.'''
"""Separate attributes as two dictionaries: one for last value, one for
the list of values."""
d = {}
m = {}
@ -59,7 +59,7 @@ class Saml2Directory(wcs.qommon.saml2.Saml2Directory):
# name field, this only works if there's a single field for the name
field_name_values = users_cfg.get('field_name')
if field_name_values:
if type(field_name_values) is str: # it was a string in previous versions
if type(field_name_values) is str: # it was a string in previous versions
field_name_values = [field_name_values]
if len(field_name_values) == 1:
user.form_data[field_name_values[0]] = d.get('cn')
@ -67,18 +67,81 @@ class Saml2Directory(wcs.qommon.saml2.Saml2Directory):
# other fields, matching is done on known LDAP attribute names and
# common variable names
extra_field_mappings = [
('gn', ('firstname', 'prenom')),
('givenName', ('firstname', 'prenom')),
('surname', ('surname', 'name', 'nom',)),
('sn', ('surname', 'name', 'nom',)),
('personalTitle', ('personalTitle', 'civilite',)),
('l', ('location', 'commune', 'ville',)),
('streetAddress', ('streetAddress', 'address', 'adresse', 'street',)),
('street', ('streetAddress', 'address', 'adresse', 'street',)),
('postalCode', ('postalCode', 'codepostal', 'cp',)),
('telephoneNumber', ('telephoneNumber', 'telephonefixe', 'telephone',)),
('mobile', ('mobile', 'telephonemobile',)),
('faxNumber', ('faxNumber', 'fax')),
('gn', ('firstname', 'prenom')),
('givenName', ('firstname', 'prenom')),
(
'surname',
(
'surname',
'name',
'nom',
),
),
(
'sn',
(
'surname',
'name',
'nom',
),
),
(
'personalTitle',
(
'personalTitle',
'civilite',
),
),
(
'l',
(
'location',
'commune',
'ville',
),
),
(
'streetAddress',
(
'streetAddress',
'address',
'adresse',
'street',
),
),
(
'street',
(
'streetAddress',
'address',
'adresse',
'street',
),
),
(
'postalCode',
(
'postalCode',
'codepostal',
'cp',
),
),
(
'telephoneNumber',
(
'telephoneNumber',
'telephonefixe',
'telephone',
),
),
(
'mobile',
(
'mobile',
'telephonemobile',
),
),
('faxNumber', ('faxNumber', 'fax')),
]
for attribute_key, field_varnames in extra_field_mappings:

View File

@ -10,6 +10,7 @@ from wcs.categories import Category
wcs_error_page = template.error_page
wcs_get_decorate_vars = template.get_decorate_vars
def get_decorate_vars(body, response, generate_breadcrumb=True, template_context=None, **kwargs):
if template_context and 'form_side' in template_context:
# force rendering as it will put new variables in the context
@ -30,7 +31,7 @@ def render_response(publisher, body):
body = str(body)
root_url = publisher.get_root_url()
wcs_path = publisher.get_request().get_path()[len(root_url):]
wcs_path = publisher.get_request().get_path()[len(root_url) :]
section = wcs_path.split('/')[0]
if section in ('backoffice', 'admin'):
@ -78,6 +79,7 @@ def error_page(*args, **kwargs):
get_response().filter['title'] = None
return htmltext('<div id="info"><h2>%s</h2>' % title) + error_page + htmltext('</div>')
template.error_page = error_page
template.get_decorate_vars = get_decorate_vars
get_publisher_class().render_response = render_response

View File

@ -12,16 +12,17 @@ from setuptools import setup
def get_version():
'''Use the VERSION, if absent generates a version with git describe, if not
tag exists, take 0.0- and add the length of the commit log.
'''
"""Use the VERSION, if absent generates a version with git describe, if not
tag exists, take 0.0- and add the length of the commit log.
"""
if os.path.exists('VERSION'):
with open('VERSION', 'r') as v:
return v.read()
if os.path.exists('.git'):
p = subprocess.Popen(
['git', 'describe', '--dirty=.dirty', '--match=v*'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
result = p.communicate()[0]
if p.returncode == 0:
@ -33,9 +34,7 @@ def get_version():
version = result
return version
else:
return '0.0.post%s' % len(
subprocess.check_output(
['git', 'rev-list', 'HEAD']).splitlines())
return '0.0.post%s' % len(subprocess.check_output(['git', 'rev-list', 'HEAD']).splitlines())
return '0.0'
@ -44,7 +43,7 @@ def data_tree(destdir, sourcedir):
r = []
for root, dirs, files in os.walk(sourcedir):
l = [os.path.join(root, x) for x in files if os.path.splitext(x)[1] in extensions]
r.append( (root.replace(sourcedir, destdir, 1), l) )
r.append((root.replace(sourcedir, destdir, 1), l))
if 'CVS' in dirs:
dirs.remove('CVS')
if '.svn' in dirs:
@ -65,6 +64,7 @@ class compile_translations(Command):
def run(self):
try:
from django.core.management import call_command
for path, dirs, files in os.walk('auquotidien'):
if 'locale' not in dirs:
continue
@ -87,7 +87,6 @@ class install_lib(_install_lib):
class eo_sdist(sdist):
def run(self):
print("creating VERSION file")
if os.path.exists('VERSION'):
@ -101,21 +100,31 @@ class eo_sdist(sdist):
if os.path.exists('VERSION'):
os.remove('VERSION')
setup(
name = 'wcs-au-quotidien',
version = get_version(),
maintainer = 'Frederic Peters',
maintainer_email = 'fpeters@entrouvert.com',
package_dir = {'auquotidien': 'auquotidien'},
packages = ['auquotidien', 'auquotidien.modules'],
cmdclass={'build': build,
'compile_translations': compile_translations,
'install_lib': install_lib,
'sdist': eo_sdist},
include_package_data=True,
data_files = data_tree('share/wcs/texts', 'texts') +\
data_tree('share/wcs/themes/auquotidien', 'theme') +\
data_tree('share/wcs/themes/', 'data/themes/') + \
data_tree('share/auquotidien/apache-errors', 'apache-errors') +\
[('share/wcs/', ['au-quotidien-wcs-settings.xml',])]
)
name='wcs-au-quotidien',
version=get_version(),
maintainer='Frederic Peters',
maintainer_email='fpeters@entrouvert.com',
package_dir={'auquotidien': 'auquotidien'},
packages=['auquotidien', 'auquotidien.modules'],
cmdclass={
'build': build,
'compile_translations': compile_translations,
'install_lib': install_lib,
'sdist': eo_sdist,
},
include_package_data=True,
data_files=data_tree('share/wcs/texts', 'texts')
+ data_tree('share/wcs/themes/auquotidien', 'theme')
+ data_tree('share/wcs/themes/', 'data/themes/')
+ data_tree('share/auquotidien/apache-errors', 'apache-errors')
+ [
(
'share/wcs/',
[
'au-quotidien-wcs-settings.xml',
],
)
],
)

View File

@ -23,6 +23,7 @@ from wcs import fields
from utilities import get_app, login, create_temporary_pub
def setup_module(module):
cleanup()
@ -35,6 +36,7 @@ def setup_module(module):
pub.cfg['identification'] = {'methods': ['password']}
pub.write_cfg()
def create_superuser():
global user1
if pub.user_class.has_key('admin'):
@ -56,19 +58,23 @@ def create_superuser():
pub.cfg['identification'] = {'methods': ['password']}
pub.write_cfg()
def create_role():
Role.wipe()
role = Role(name='foobar')
role.store()
return role
def teardown_module(module):
shutil.rmtree(pub.APP_DIR)
@pytest.fixture
def empty_siteoptions():
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').close()
def test_with_superuser():
create_superuser()
app = login(get_app(pub))
@ -76,6 +82,7 @@ def test_with_superuser():
# this makes sure the extension loaded properly
assert '<span id="applabel">Publik</span>' in resp.text
def test_general_admin_permissions():
create_superuser()
app = login(get_app(pub))
@ -89,6 +96,7 @@ def test_general_admin_permissions():
del pub.cfg['admin-permissions']
pub.write_cfg()
def test_aq_permissions_panel(empty_siteoptions):
create_superuser()
app = login(get_app(pub))
@ -105,6 +113,7 @@ def test_aq_permissions_panel(empty_siteoptions):
assert 'aq/permissions' in resp.text
resp = app.get('/backoffice/settings/aq/permissions')
def test_menu_items(empty_siteoptions):
create_superuser()
role = create_role()

View File

@ -18,6 +18,7 @@ from wcs import fields
from utilities import get_app, login, create_temporary_pub
def setup_module(module):
cleanup()
@ -30,6 +31,7 @@ def setup_module(module):
pub.cfg['identification'] = {'methods': ['password']}
pub.write_cfg()
def create_agent():
if pub.user_class.has_key('agent'):
return
@ -48,6 +50,7 @@ def create_agent():
return user1
def create_role():
Role.wipe()
role = Role(name='foobar')
@ -55,9 +58,11 @@ def create_role():
role.store()
return role
def teardown_module(module):
shutil.rmtree(pub.APP_DIR)
def test_with_agent():
user = create_agent()
app = login(get_app(pub), username='agent', password='agent')
@ -71,6 +76,7 @@ def test_with_agent():
# check user is automatically redirected to management/
assert resp.location == 'http://example.net/backoffice/management/'
def test_with_agent_submitter():
user = create_agent()
role = create_role()
@ -89,7 +95,7 @@ def test_with_agent_submitter():
formdef.backoffice_submission_roles = [role.id]
formdef.store()
resp = app.get('/backoffice/', status=200) # no redirect
resp = app.get('/backoffice/', status=200) # no redirect
# check the management and submission links are presend twice, once in the
# sidebar and once in the page body
assert resp.text.count('//example.net/backoffice/management/') == 2

View File

@ -6,6 +6,7 @@ from auquotidien.modules import payments
from utilities import get_app, login, create_temporary_pub
def setup_module(module):
cleanup()

View File

@ -18,6 +18,7 @@ from wcs import fields
from utilities import get_app, login, create_temporary_pub
def setup_module(module):
cleanup()
@ -30,6 +31,7 @@ def setup_module(module):
pub.cfg['identification'] = {'methods': ['password']}
pub.write_cfg()
def create_user():
if pub.user_class.has_key('user'):
return
@ -48,6 +50,7 @@ def create_user():
return user1
def create_formdef():
FormDef.wipe()
formdef = FormDef()
@ -56,15 +59,18 @@ def create_formdef():
formdef.store()
return formdef
def teardown_module(module):
shutil.rmtree(pub.APP_DIR)
def test_with_user():
user = create_user()
app = login(get_app(pub), username='user', password='user')
resp = app.get('/', status=200)
resp = app.get('/myspace/', status=200)
def test_myspace_with_user_forms():
user = create_user()
formdef = create_formdef()

View File

@ -16,12 +16,14 @@ import wcs.middleware
wcs.middleware.AfterJobsMiddleware.ASYNC = False
def create_temporary_pub():
config = ConfigParser.ConfigParser()
APP_DIR = tempfile.mkdtemp()
compat.CompatWcsPublisher.APP_DIR = APP_DIR
compat.CompatWcsPublisher.DATA_DIR = os.path.abspath(
os.path.join(os.path.dirname(wcs.__file__), '..', 'data'))
os.path.join(os.path.dirname(wcs.__file__), '..', 'data')
)
compat.CompatWcsPublisher.cronjobs = None
config.add_section('extra')
config.set('extra', 'auquotidien', os.path.join(os.path.dirname(__file__), '..', 'auquotidien'))
@ -34,6 +36,7 @@ def create_temporary_pub():
os.mkdir(pub.app_dir)
return pub
def get_app(pub, https=False):
extra_environ = {'HTTP_HOST': 'example.net', 'REMOTE_ADDR': '127.0.0.1'}
if https:
@ -43,6 +46,7 @@ def get_app(pub, https=False):
extra_environ['HTTPS'] = 'off'
return TestApp(wcs.wsgi.application, extra_environ=extra_environ)
def login(app, username='admin', password='admin'):
login_page = app.get('/login/')
login_form = login_page.forms['login-form']