RegistrationTemplate: headlines, error messages, capacity check; ServiceManager: redirect to registration; dropdown field: empty default entry with informational text
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@2126 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
89510875c9
commit
dcbe6f15eb
8 changed files with 110 additions and 23 deletions
|
@ -124,7 +124,10 @@ class BaseView(object):
|
||||||
submit=getCheckoutView,
|
submit=getCheckoutView,
|
||||||
)
|
)
|
||||||
|
|
||||||
#@Lazy # must be method for Zope 2.9 compatibility :-( ???
|
@Lazy
|
||||||
|
def nextUrl(self):
|
||||||
|
return self.getNextUrl()
|
||||||
|
|
||||||
def getNextUrl(self):
|
def getNextUrl(self):
|
||||||
#viewName = 'thankyou.html'
|
#viewName = 'thankyou.html'
|
||||||
viewName = ''
|
viewName = ''
|
||||||
|
|
|
@ -73,7 +73,7 @@ class SchemaView(BaseView):
|
||||||
return True
|
return True
|
||||||
if self.isManageMode:
|
if self.isManageMode:
|
||||||
# Don't store anything when editing
|
# Don't store anything when editing
|
||||||
self.request.response.redirect(self.nextUrl())
|
self.request.response.redirect(self.getNextUrl())
|
||||||
return False
|
return False
|
||||||
manager = self.context.getManager()
|
manager = self.context.getManager()
|
||||||
if clientName:
|
if clientName:
|
||||||
|
|
|
@ -93,9 +93,11 @@ class Field(Component):
|
||||||
def getFieldTypeInfo(self):
|
def getFieldTypeInfo(self):
|
||||||
return fieldTypes.getTerm(self.fieldType)
|
return fieldTypes.getTerm(self.fieldType)
|
||||||
|
|
||||||
def getFieldInstance(self):
|
def getFieldInstance(self, clientInstance=None):
|
||||||
instanceName = self.getFieldTypeInfo().instanceName
|
instanceName = self.getFieldTypeInfo().instanceName
|
||||||
return component.getAdapter(self, IFieldInstance, name=instanceName)
|
fi = component.getAdapter(self, IFieldInstance, name=instanceName)
|
||||||
|
fi.clientInstance = clientInstance
|
||||||
|
return fi
|
||||||
|
|
||||||
|
|
||||||
class FieldInstance(object):
|
class FieldInstance(object):
|
||||||
|
@ -111,7 +113,7 @@ class FieldInstance(object):
|
||||||
self.change = None
|
self.change = None
|
||||||
|
|
||||||
def marshall(self, value):
|
def marshall(self, value):
|
||||||
return value
|
return value or u''
|
||||||
#return toStr(value)
|
#return toStr(value)
|
||||||
|
|
||||||
def display(self, value):
|
def display(self, value):
|
||||||
|
@ -140,7 +142,7 @@ class NumberFieldInstance(FieldInstance):
|
||||||
|
|
||||||
def display(self, value):
|
def display(self, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
return '-'
|
return ''
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
def unmarshall(self, value):
|
def unmarshall(self, value):
|
||||||
|
@ -166,3 +168,9 @@ class FileUploadFieldInstance(FieldInstance):
|
||||||
|
|
||||||
def unmarshall(self, value):
|
def unmarshall(self, value):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class CalculatedFieldInstance(FieldInstance):
|
||||||
|
|
||||||
|
def marshall(self, value):
|
||||||
|
return str(value)
|
||||||
|
|
|
@ -50,7 +50,7 @@ class Instance(BaseInstance):
|
||||||
if not f.storeData:
|
if not f.storeData:
|
||||||
# a dummy field, e.g. a spacer
|
# a dummy field, e.g. a spacer
|
||||||
continue
|
continue
|
||||||
fi = f.getFieldInstance()
|
fi = f.getFieldInstance(self)
|
||||||
name = f.name
|
name = f.name
|
||||||
value = getattr(self.context, name, f.defaultValue)
|
value = getattr(self.context, name, f.defaultValue)
|
||||||
#value = getattr(self.context, name, u'')
|
#value = getattr(self.context, name, u'')
|
||||||
|
@ -147,9 +147,10 @@ class ClientInstance(object):
|
||||||
if not f.storeData:
|
if not f.storeData:
|
||||||
# a dummy field, e.g. a spacer
|
# a dummy field, e.g. a spacer
|
||||||
continue
|
continue
|
||||||
fi = f.getFieldInstance()
|
fi = f.getFieldInstance(self)
|
||||||
name = f.name
|
name = f.name
|
||||||
value = values.get(name, u'')
|
#value = values.get(name, u'')
|
||||||
|
value = values.get(name, f.defaultValue)
|
||||||
value = mode == 'view' and fi.display(value) or fi.marshall(value)
|
value = mode == 'view' and fi.display(value) or fi.marshall(value)
|
||||||
result[name] = value
|
result[name] = value
|
||||||
# update result with standard fields:
|
# update result with standard fields:
|
||||||
|
|
|
@ -91,17 +91,21 @@ fieldTypes = SimpleVocabulary((
|
||||||
instanceName='fileupload'),
|
instanceName='fileupload'),
|
||||||
#FieldType('checkbox', 'checkbox', u'Checkbox'),
|
#FieldType('checkbox', 'checkbox', u'Checkbox'),
|
||||||
FieldType('dropdown', 'dropdown', u'Drop-down selection'),
|
FieldType('dropdown', 'dropdown', u'Drop-down selection'),
|
||||||
|
FieldType('calculated', 'display', u'Calculated Value',
|
||||||
|
instanceName='calculated'),
|
||||||
FieldType('spacer', 'spacer', u'Spacer',
|
FieldType('spacer', 'spacer', u'Spacer',
|
||||||
fieldRenderer='field_spacer', storeData=False),
|
fieldRenderer='field_spacer', storeData=False),
|
||||||
))
|
))
|
||||||
|
|
||||||
# TODO: move this to organize.service...
|
# TODO: move this to organize.service... (???)
|
||||||
standardFieldNames = SimpleVocabulary((
|
standardFieldNames = SimpleVocabulary((
|
||||||
SimpleTerm('', '', 'Not selected'),
|
SimpleTerm('', '', 'Not selected'),
|
||||||
SimpleTerm('lastName', 'lastName', 'Last name'),
|
SimpleTerm('lastName', 'lastName', 'Last name'),
|
||||||
SimpleTerm('firstName', 'firstName', 'First name'),
|
SimpleTerm('firstName', 'firstName', 'First name'),
|
||||||
SimpleTerm('organization', 'organization', 'Organization'),
|
SimpleTerm('organization', 'organization', 'Organization'),
|
||||||
SimpleTerm('email', 'email', 'E-Mail address'),
|
SimpleTerm('email', 'email', 'E-Mail address'),
|
||||||
|
SimpleTerm('number', 'number', 'Number of participants'),
|
||||||
|
# TODO: on organize.service: extend this list, e.g. with 'totalCost'
|
||||||
))
|
))
|
||||||
|
|
||||||
class IField(IComponent):
|
class IField(IComponent):
|
||||||
|
@ -135,7 +139,8 @@ class IField(IComponent):
|
||||||
vocabulary=standardFieldNames,)
|
vocabulary=standardFieldNames,)
|
||||||
defaultValue = schema.TextLine(
|
defaultValue = schema.TextLine(
|
||||||
title=_(u'Default'),
|
title=_(u'Default'),
|
||||||
description=_(u'Value with which to pre-set the field contents'),
|
description=_(u'Value with which to pre-set the field contents. '
|
||||||
|
'Use this also for populating a calculated field.'),
|
||||||
required=False,)
|
required=False,)
|
||||||
required = schema.Bool(
|
required = schema.Bool(
|
||||||
title=_(u'Required'),
|
title=_(u'Required'),
|
||||||
|
|
|
@ -72,7 +72,11 @@ class BaseView(SchemaBaseView):
|
||||||
|
|
||||||
class ServiceManagerView(BaseView):
|
class ServiceManagerView(BaseView):
|
||||||
|
|
||||||
|
isManageMode = False
|
||||||
|
|
||||||
def getCustomView(self):
|
def getCustomView(self):
|
||||||
|
if self.isManageMode:
|
||||||
|
return None
|
||||||
viewName = self.context.getViewName()
|
viewName = self.context.getViewName()
|
||||||
if viewName:
|
if viewName:
|
||||||
return component.getMultiAdapter((self.context, self.request),
|
return component.getMultiAdapter((self.context, self.request),
|
||||||
|
@ -103,6 +107,10 @@ class ServiceManagerView(BaseView):
|
||||||
tpl = self.getRegistrationTemplate()
|
tpl = self.getRegistrationTemplate()
|
||||||
return self.getUrlForObject(tpl)
|
return self.getUrlForObject(tpl)
|
||||||
|
|
||||||
|
def redirectToRegistration(self):
|
||||||
|
self.request.response.redirect(self.registrationUrl())
|
||||||
|
return 'redirect' # let template skip rendering
|
||||||
|
|
||||||
def overview(self, includeCategories=None):
|
def overview(self, includeCategories=None):
|
||||||
result = []
|
result = []
|
||||||
classific = []
|
classific = []
|
||||||
|
@ -270,6 +278,8 @@ class ServiceView(BaseView):
|
||||||
|
|
||||||
class RegistrationTemplateView(BaseView):
|
class RegistrationTemplateView(BaseView):
|
||||||
|
|
||||||
|
state = None
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
def services(self):
|
def services(self):
|
||||||
return self.getServices()
|
return self.getServices()
|
||||||
|
@ -278,6 +288,11 @@ class RegistrationTemplateView(BaseView):
|
||||||
return self.context.getServices()
|
return self.context.getServices()
|
||||||
#return sorted(self.context.getServices().values(), key=self.sortKey)
|
#return sorted(self.context.getServices().values(), key=self.sortKey)
|
||||||
|
|
||||||
|
def overview(self):
|
||||||
|
categories = self.context.categories or None
|
||||||
|
mv = ServiceManagerView(self.context.getManager(), self.request)
|
||||||
|
return mv.overview(categories)
|
||||||
|
|
||||||
def sortKey(self, svc):
|
def sortKey(self, svc):
|
||||||
return (svc.category, svc.getClassification(), svc.start)
|
return (svc.category, svc.getClassification(), svc.start)
|
||||||
|
|
||||||
|
@ -314,6 +329,7 @@ class RegistrationTemplateView(BaseView):
|
||||||
return instance.applyTemplate()
|
return instance.applyTemplate()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
newClient = False
|
||||||
form = self.request.form
|
form = self.request.form
|
||||||
clientName = self.getClientName()
|
clientName = self.getClientName()
|
||||||
if not form.get('action'):
|
if not form.get('action'):
|
||||||
|
@ -325,13 +341,11 @@ class RegistrationTemplateView(BaseView):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
client = IClientFactory(manager)()
|
client = IClientFactory(manager)()
|
||||||
clientName = manager.addClient(client)
|
newClient = True # make persistent later
|
||||||
self.setClientName(clientName)
|
regs = self.state = IClientRegistrations(client)
|
||||||
regs = IClientRegistrations(client)
|
|
||||||
regs.template = self.context
|
regs.template = self.context
|
||||||
services = manager.getServices() # a mapping!
|
services = manager.getServices() # a mapping!
|
||||||
allServices = services.values()
|
allServices = services.values()
|
||||||
oldServices = [r.service for r in regs.getRegistrations()]
|
|
||||||
# collect check boxes:
|
# collect check boxes:
|
||||||
newServices = [services[token]
|
newServices = [services[token]
|
||||||
for token in form.get('service_tokens', [])]
|
for token in form.get('service_tokens', [])]
|
||||||
|
@ -345,10 +359,18 @@ class RegistrationTemplateView(BaseView):
|
||||||
if value > 0:
|
if value > 0:
|
||||||
newServices.append(svc)
|
newServices.append(svc)
|
||||||
numbers.append(value)
|
numbers.append(value)
|
||||||
|
regs.validate(clientName, newServices, numbers)
|
||||||
|
if regs.severity > 0:
|
||||||
|
return True
|
||||||
|
if newClient:
|
||||||
|
clientName = manager.addClient(client)
|
||||||
|
self.setClientName(clientName)
|
||||||
regs.register(newServices, numbers=numbers)
|
regs.register(newServices, numbers=numbers)
|
||||||
|
oldServices = [r.service for r in regs.getRegistrations()]
|
||||||
toDelete = [s for s in oldServices
|
toDelete = [s for s in oldServices
|
||||||
if s in allServices and s not in newServices]
|
if s in allServices and s not in newServices]
|
||||||
regs.unregister(toDelete)
|
regs.unregister(toDelete)
|
||||||
#return True
|
|
||||||
self.request.response.redirect(self.getNextUrl())
|
self.request.response.redirect(self.getNextUrl())
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -128,6 +128,8 @@ class ITask(Interface):
|
||||||
serviceManagerViews = SimpleVocabulary((
|
serviceManagerViews = SimpleVocabulary((
|
||||||
SimpleTerm('', '', u'Default view'),
|
SimpleTerm('', '', u'Default view'),
|
||||||
SimpleTerm('events_overview.html', 'events_overview.html', u'Events overview'),
|
SimpleTerm('events_overview.html', 'events_overview.html', u'Events overview'),
|
||||||
|
SimpleTerm('redirect_registration.html', 'redirect_registration.html',
|
||||||
|
u'Redirect to registration')
|
||||||
))
|
))
|
||||||
|
|
||||||
class IServiceManager(Interface):
|
class IServiceManager(Interface):
|
||||||
|
@ -145,7 +147,7 @@ class IServiceManager(Interface):
|
||||||
viewName = schema.Choice(
|
viewName = schema.Choice(
|
||||||
title=_(u'View name'),
|
title=_(u'View name'),
|
||||||
description=_(u'Select the name of a specialized view to be used '
|
description=_(u'Select the name of a specialized view to be used '
|
||||||
'for presenting this object.'),
|
'for presenting this object for visitors.'),
|
||||||
vocabulary=serviceManagerViews,
|
vocabulary=serviceManagerViews,
|
||||||
default='',
|
default='',
|
||||||
required=False,)
|
required=False,)
|
||||||
|
|
|
@ -160,9 +160,10 @@ class Service(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def availableCapacity(self):
|
def availableCapacity(self):
|
||||||
if self.capacity >= 0 and len(self.registrations) >= self.capacity:
|
number = self.getNumberRegistered()
|
||||||
|
if self.capacity >= 0 and number >= self.capacity:
|
||||||
return 0
|
return 0
|
||||||
return self.capacity - len(self.registrations)
|
return self.capacity - number
|
||||||
|
|
||||||
def register(self, client, number=1):
|
def register(self, client, number=1):
|
||||||
clientName = client.__name__
|
clientName = client.__name__
|
||||||
|
@ -170,20 +171,22 @@ class Service(object):
|
||||||
reg = self.registrations[clientName]
|
reg = self.registrations[clientName]
|
||||||
if number != reg.number:
|
if number != reg.number:
|
||||||
reg.number = number
|
reg.number = number
|
||||||
#self.registrations[clientName] = reg # persistence hack
|
|
||||||
return reg
|
return reg
|
||||||
reg = self.registrationFactory(client, self, number)
|
reg = self.registrationFactory(client, self, number)
|
||||||
self.registrations[clientName] = reg
|
self.registrations[clientName] = reg
|
||||||
return reg
|
return reg
|
||||||
#if self.availableCapacity:
|
|
||||||
# TODO: handle case when no capacity available -
|
|
||||||
# probably on 'submit' transition; UI feedback?
|
|
||||||
|
|
||||||
def unregister(self, client):
|
def unregister(self, client):
|
||||||
clientName = client.__name__
|
clientName = client.__name__
|
||||||
if clientName in self.registrations:
|
if clientName in self.registrations:
|
||||||
del self.registrations[clientName]
|
del self.registrations[clientName]
|
||||||
|
|
||||||
|
def getNumberRegistered(self):
|
||||||
|
result = 0
|
||||||
|
for r in self.registrations.values():
|
||||||
|
result += r.number
|
||||||
|
return result
|
||||||
|
|
||||||
# default methods
|
# default methods
|
||||||
def getAllowRegWithNumberFromManager(self):
|
def getAllowRegWithNumberFromManager(self):
|
||||||
return getattr(self.getManager(), 'allowRegWithNumber', None)
|
return getattr(self.getManager(), 'allowRegWithNumber', None)
|
||||||
|
@ -240,6 +243,9 @@ class ClientRegistrations(object):
|
||||||
|
|
||||||
registrationsAttributeName = '__service_registrations__'
|
registrationsAttributeName = '__service_registrations__'
|
||||||
|
|
||||||
|
errors = None
|
||||||
|
severity = 0
|
||||||
|
|
||||||
def __init__(self, context):
|
def __init__(self, context):
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
|
@ -266,6 +272,46 @@ class ClientRegistrations(object):
|
||||||
regs = (r for r in regs if r.service in svcs)
|
regs = (r for r in regs if r.service in svcs)
|
||||||
return regs
|
return regs
|
||||||
|
|
||||||
|
def validate(self, clientName, services, numbers=None):
|
||||||
|
self.errors = {}
|
||||||
|
if numbers is None:
|
||||||
|
numbers = len(services) * [1]
|
||||||
|
for svc, n in zip(services, numbers):
|
||||||
|
oldReg = svc.registrations.get(clientName, None)
|
||||||
|
oldN = oldReg and oldReg.number or 0
|
||||||
|
if svc.capacity and svc.capacity > 0 and svc.availableCapacity < n - oldN:
|
||||||
|
error = registrationErrors['capacity_exceeded']
|
||||||
|
entry = self.errors.setdefault(svc.token, [])
|
||||||
|
entry.append(error)
|
||||||
|
self.severity = max(self.severity, error.severity)
|
||||||
|
|
||||||
|
|
||||||
|
class RegistrationError(object):
|
||||||
|
|
||||||
|
def __init__(self, title, description=None, severity=5, **kw):
|
||||||
|
self.title = title
|
||||||
|
self.description = description or title
|
||||||
|
self.severity = severity
|
||||||
|
for k, v in kw.items():
|
||||||
|
setattr(self, k, v)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "RegistrationError('%s')" % self.title
|
||||||
|
|
||||||
|
|
||||||
|
registrationErrors = dict(
|
||||||
|
capacity_exceeded=RegistrationError(
|
||||||
|
u'The capacity for this service has been exceeded.'),
|
||||||
|
time_conflict=RegistrationError(
|
||||||
|
u'You have registered already for another service at the same time.'),
|
||||||
|
number_exceeded=RegistrationError(
|
||||||
|
u'The total number of participants you are registering is less than the '
|
||||||
|
'number of persons you want to register for this service.'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# registration states
|
# registration states
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue