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:
helmutm 2007-10-29 16:40:00 +00:00
parent 89510875c9
commit dcbe6f15eb
8 changed files with 110 additions and 23 deletions

View file

@ -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 = ''

View file

@ -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:

View file

@ -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)

View file

@ -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:

View file

@ -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'),

View file

@ -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

View file

@ -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,)

View file

@ -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