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,
|
||||
)
|
||||
|
||||
#@Lazy # must be method for Zope 2.9 compatibility :-( ???
|
||||
@Lazy
|
||||
def nextUrl(self):
|
||||
return self.getNextUrl()
|
||||
|
||||
def getNextUrl(self):
|
||||
#viewName = 'thankyou.html'
|
||||
viewName = ''
|
||||
|
|
|
@ -73,7 +73,7 @@ class SchemaView(BaseView):
|
|||
return True
|
||||
if self.isManageMode:
|
||||
# Don't store anything when editing
|
||||
self.request.response.redirect(self.nextUrl())
|
||||
self.request.response.redirect(self.getNextUrl())
|
||||
return False
|
||||
manager = self.context.getManager()
|
||||
if clientName:
|
||||
|
|
|
@ -93,9 +93,11 @@ class Field(Component):
|
|||
def getFieldTypeInfo(self):
|
||||
return fieldTypes.getTerm(self.fieldType)
|
||||
|
||||
def getFieldInstance(self):
|
||||
def getFieldInstance(self, clientInstance=None):
|
||||
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):
|
||||
|
@ -111,7 +113,7 @@ class FieldInstance(object):
|
|||
self.change = None
|
||||
|
||||
def marshall(self, value):
|
||||
return value
|
||||
return value or u''
|
||||
#return toStr(value)
|
||||
|
||||
def display(self, value):
|
||||
|
@ -140,7 +142,7 @@ class NumberFieldInstance(FieldInstance):
|
|||
|
||||
def display(self, value):
|
||||
if value is None:
|
||||
return '-'
|
||||
return ''
|
||||
return str(value)
|
||||
|
||||
def unmarshall(self, value):
|
||||
|
@ -166,3 +168,9 @@ class FileUploadFieldInstance(FieldInstance):
|
|||
|
||||
def unmarshall(self, value):
|
||||
return value
|
||||
|
||||
|
||||
class CalculatedFieldInstance(FieldInstance):
|
||||
|
||||
def marshall(self, value):
|
||||
return str(value)
|
||||
|
|
|
@ -50,7 +50,7 @@ class Instance(BaseInstance):
|
|||
if not f.storeData:
|
||||
# a dummy field, e.g. a spacer
|
||||
continue
|
||||
fi = f.getFieldInstance()
|
||||
fi = f.getFieldInstance(self)
|
||||
name = f.name
|
||||
value = getattr(self.context, name, f.defaultValue)
|
||||
#value = getattr(self.context, name, u'')
|
||||
|
@ -147,9 +147,10 @@ class ClientInstance(object):
|
|||
if not f.storeData:
|
||||
# a dummy field, e.g. a spacer
|
||||
continue
|
||||
fi = f.getFieldInstance()
|
||||
fi = f.getFieldInstance(self)
|
||||
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)
|
||||
result[name] = value
|
||||
# update result with standard fields:
|
||||
|
|
|
@ -91,17 +91,21 @@ fieldTypes = SimpleVocabulary((
|
|||
instanceName='fileupload'),
|
||||
#FieldType('checkbox', 'checkbox', u'Checkbox'),
|
||||
FieldType('dropdown', 'dropdown', u'Drop-down selection'),
|
||||
FieldType('calculated', 'display', u'Calculated Value',
|
||||
instanceName='calculated'),
|
||||
FieldType('spacer', 'spacer', u'Spacer',
|
||||
fieldRenderer='field_spacer', storeData=False),
|
||||
))
|
||||
|
||||
# TODO: move this to organize.service...
|
||||
# TODO: move this to organize.service... (???)
|
||||
standardFieldNames = SimpleVocabulary((
|
||||
SimpleTerm('', '', 'Not selected'),
|
||||
SimpleTerm('lastName', 'lastName', 'Last name'),
|
||||
SimpleTerm('firstName', 'firstName', 'First name'),
|
||||
SimpleTerm('organization', 'organization', 'Organization'),
|
||||
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):
|
||||
|
@ -135,7 +139,8 @@ class IField(IComponent):
|
|||
vocabulary=standardFieldNames,)
|
||||
defaultValue = schema.TextLine(
|
||||
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 = schema.Bool(
|
||||
title=_(u'Required'),
|
||||
|
|
|
@ -72,7 +72,11 @@ class BaseView(SchemaBaseView):
|
|||
|
||||
class ServiceManagerView(BaseView):
|
||||
|
||||
isManageMode = False
|
||||
|
||||
def getCustomView(self):
|
||||
if self.isManageMode:
|
||||
return None
|
||||
viewName = self.context.getViewName()
|
||||
if viewName:
|
||||
return component.getMultiAdapter((self.context, self.request),
|
||||
|
@ -103,6 +107,10 @@ class ServiceManagerView(BaseView):
|
|||
tpl = self.getRegistrationTemplate()
|
||||
return self.getUrlForObject(tpl)
|
||||
|
||||
def redirectToRegistration(self):
|
||||
self.request.response.redirect(self.registrationUrl())
|
||||
return 'redirect' # let template skip rendering
|
||||
|
||||
def overview(self, includeCategories=None):
|
||||
result = []
|
||||
classific = []
|
||||
|
@ -270,6 +278,8 @@ class ServiceView(BaseView):
|
|||
|
||||
class RegistrationTemplateView(BaseView):
|
||||
|
||||
state = None
|
||||
|
||||
@Lazy
|
||||
def services(self):
|
||||
return self.getServices()
|
||||
|
@ -278,6 +288,11 @@ class RegistrationTemplateView(BaseView):
|
|||
return self.context.getServices()
|
||||
#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):
|
||||
return (svc.category, svc.getClassification(), svc.start)
|
||||
|
||||
|
@ -314,6 +329,7 @@ class RegistrationTemplateView(BaseView):
|
|||
return instance.applyTemplate()
|
||||
|
||||
def update(self):
|
||||
newClient = False
|
||||
form = self.request.form
|
||||
clientName = self.getClientName()
|
||||
if not form.get('action'):
|
||||
|
@ -325,13 +341,11 @@ class RegistrationTemplateView(BaseView):
|
|||
return True
|
||||
else:
|
||||
client = IClientFactory(manager)()
|
||||
clientName = manager.addClient(client)
|
||||
self.setClientName(clientName)
|
||||
regs = IClientRegistrations(client)
|
||||
newClient = True # make persistent later
|
||||
regs = self.state = IClientRegistrations(client)
|
||||
regs.template = self.context
|
||||
services = manager.getServices() # a mapping!
|
||||
allServices = services.values()
|
||||
oldServices = [r.service for r in regs.getRegistrations()]
|
||||
# collect check boxes:
|
||||
newServices = [services[token]
|
||||
for token in form.get('service_tokens', [])]
|
||||
|
@ -345,10 +359,18 @@ class RegistrationTemplateView(BaseView):
|
|||
if value > 0:
|
||||
newServices.append(svc)
|
||||
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)
|
||||
oldServices = [r.service for r in regs.getRegistrations()]
|
||||
toDelete = [s for s in oldServices
|
||||
if s in allServices and s not in newServices]
|
||||
regs.unregister(toDelete)
|
||||
#return True
|
||||
self.request.response.redirect(self.getNextUrl())
|
||||
return False
|
||||
|
||||
|
||||
|
|
|
@ -128,6 +128,8 @@ class ITask(Interface):
|
|||
serviceManagerViews = SimpleVocabulary((
|
||||
SimpleTerm('', '', u'Default view'),
|
||||
SimpleTerm('events_overview.html', 'events_overview.html', u'Events overview'),
|
||||
SimpleTerm('redirect_registration.html', 'redirect_registration.html',
|
||||
u'Redirect to registration')
|
||||
))
|
||||
|
||||
class IServiceManager(Interface):
|
||||
|
@ -145,7 +147,7 @@ class IServiceManager(Interface):
|
|||
viewName = schema.Choice(
|
||||
title=_(u'View name'),
|
||||
description=_(u'Select the name of a specialized view to be used '
|
||||
'for presenting this object.'),
|
||||
'for presenting this object for visitors.'),
|
||||
vocabulary=serviceManagerViews,
|
||||
default='',
|
||||
required=False,)
|
||||
|
|
|
@ -160,9 +160,10 @@ class Service(object):
|
|||
|
||||
@property
|
||||
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 self.capacity - len(self.registrations)
|
||||
return self.capacity - number
|
||||
|
||||
def register(self, client, number=1):
|
||||
clientName = client.__name__
|
||||
|
@ -170,20 +171,22 @@ class Service(object):
|
|||
reg = self.registrations[clientName]
|
||||
if number != reg.number:
|
||||
reg.number = number
|
||||
#self.registrations[clientName] = reg # persistence hack
|
||||
return reg
|
||||
reg = self.registrationFactory(client, self, number)
|
||||
self.registrations[clientName] = reg
|
||||
return reg
|
||||
#if self.availableCapacity:
|
||||
# TODO: handle case when no capacity available -
|
||||
# probably on 'submit' transition; UI feedback?
|
||||
|
||||
def unregister(self, client):
|
||||
clientName = client.__name__
|
||||
if clientName in self.registrations:
|
||||
del self.registrations[clientName]
|
||||
|
||||
def getNumberRegistered(self):
|
||||
result = 0
|
||||
for r in self.registrations.values():
|
||||
result += r.number
|
||||
return result
|
||||
|
||||
# default methods
|
||||
def getAllowRegWithNumberFromManager(self):
|
||||
return getattr(self.getManager(), 'allowRegWithNumber', None)
|
||||
|
@ -240,6 +243,9 @@ class ClientRegistrations(object):
|
|||
|
||||
registrationsAttributeName = '__service_registrations__'
|
||||
|
||||
errors = None
|
||||
severity = 0
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
|
@ -266,6 +272,46 @@ class ClientRegistrations(object):
|
|||
regs = (r for r in regs if r.service in svcs)
|
||||
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
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue