diff --git a/composer/schema/browser/schema.py b/composer/schema/browser/schema.py index 71bc9b5..d802280 100644 --- a/composer/schema/browser/schema.py +++ b/composer/schema/browser/schema.py @@ -28,11 +28,12 @@ from zope.cachedescriptors.property import Lazy from cybertools.composer.schema.browser.common import BaseView from cybertools.composer.interfaces import IInstance from cybertools.composer.schema.interfaces import IClientFactory +from cybertools.composer.schema.schema import FormState class SchemaView(BaseView): - formState = None + formState = FormState() @Lazy def fields(self): @@ -55,10 +56,15 @@ class SchemaView(BaseView): return {} instance = IInstance(client) instance.template = self.context - return instance.applyTemplate() - # TODO: overwrite data with values from form + data = instance.applyTemplate(mode='edit') + for k, v in data.items(): + #overwrite data with values from form + if k in form: + data[k] = form[k] + return data def update(self): + newClient = False form = self.request.form if not self.clientName: self.clientName = form.get('id') @@ -69,19 +75,21 @@ class SchemaView(BaseView): if clientName: client = manager.getClients().get(clientName) if client is None: + # no valid clientName - show empty form return True else: - # if not self.hasData(form) and 'submit' not in form: - # self.request.response.redirect(self.nextUrl()) - # return False client = IClientFactory(manager)() - clientName = self.clientName = manager.addClient(client) + # only add client to manager after validation, so we have + # to keep the info about new client here + newClient = True instance = component.getAdapter(client, IInstance, name='editor') instance.template = self.context self.formState = formState = instance.applyTemplate(form) if formState.severity > 0: - # show form again + # show form again; do not add client to manager return True + if newClient: + clientName = self.clientName = manager.addClient(client) self.request.response.redirect(self.nextUrl()) return False diff --git a/composer/schema/field.py b/composer/schema/field.py index 27de35f..be1dc56 100644 --- a/composer/schema/field.py +++ b/composer/schema/field.py @@ -23,10 +23,10 @@ $Id$ """ from zope.interface import implements -from zope import schema from cybertools.composer.base import Component from cybertools.composer.schema.interfaces import IField, IFieldState +from cybertools.composer.schema.schema import formErrors from cybertools.util.format import toStr, toUnicode @@ -53,23 +53,24 @@ class Field(Component): def getTitleValue(self): return self.title or self.name - def marshallValue(self, value): + def marshall(self, value): return value #return toStr(value) - def displayValue(self, value): + def display(self, value): return value #return toStr(value) - def unmarshallValue(self, strValue): + def unmarshall(self, strValue): return toUnicode(strValue) or u'' def validateValue(self, value): errors = [] severity = 0 if not value and self.required: - errors.append('required_missing') - severity = 5 + error = formErrors['required_missing'] + errors.append(error) + severity = error.severity return FieldState(self.name, errors, severity) diff --git a/composer/schema/instance.py b/composer/schema/instance.py index 8ca41a7..0079b26 100644 --- a/composer/schema/instance.py +++ b/composer/schema/instance.py @@ -67,6 +67,7 @@ class ClientInstance(object): to the corresponding values from the context object. """ result = {} + mode = kw.get('mode', 'view') attrs = getattr(self.context, self.attrsName, None) if attrs is None: return result @@ -75,7 +76,9 @@ class ClientInstance(object): if template is not None: for f in template.fields: name = f.name - result[name] = f.displayValue(values.get(name, u'')) + value = values.get(name, u'') + value = mode == 'view' and f.display(value) or f.marshall(value) + result[name] = value result['__name__'] = self.context.__name__ return result @@ -87,25 +90,36 @@ class ClientInstanceEditor(ClientInstance): using corresponding values from the data argument. Return the resulting form state (an object providing IFormState). """ - formState = FormState() + template = self.template + if template is None: + return FormState() + formState = self.validate(data) + if formState.severity > 0: + # don't do anything if there is an error + return formState attrs = getattr(self.context, self.attrsName, None) if attrs is None: attrs = OOBTree() setattr(self.context, self.attrsName, attrs) - template = self.template values = attrs.setdefault(self.aspect, OOBTree()) - if template is not None: - for f in template.fields: - name = f.name - value = f.unmarshallValue(data.get(name)) - fieldState = f.validateValue(value) - if name in data: - oldValue = values.get(name) - if value != oldValue: - values[name] = value - fieldState.change = (oldValue, value) - formState.changed = True - formState.fieldStates.append(fieldState) - formState.severity = max(formState.severity, fieldState.severity) + for f in template.fields: + name = f.name + fieldState = formState.fieldStates[name] + value = f.unmarshall(data.get(name)) + if name in data: + oldValue = values.get(name) + if value != oldValue: + values[name] = value + fieldState.change = (oldValue, value) + formState.changed = True + return formState + + def validate(self, data): + formState = FormState() + for f in self.template.fields: + value = f.unmarshall(data.get(f.name)) + fieldState = f.validateValue(value) + formState.fieldStates.append(fieldState) + formState.severity = max(formState.severity, fieldState.severity) return formState diff --git a/composer/schema/interfaces.py b/composer/schema/interfaces.py index 4c44d77..d709de6 100644 --- a/composer/schema/interfaces.py +++ b/composer/schema/interfaces.py @@ -78,7 +78,8 @@ class IField(IComponent): description=_(u'The type of the field'), required=True, default='textline', - values=('textline', 'textarea', 'date', 'checkbox')) + values=('textline', 'textarea', 'number', + 'date', 'checkbox', 'spacer')) defaultValue = schema.TextLine( title=_(u'Default'), description=_(u'Value with which to pre-set the field contents'), @@ -177,4 +178,3 @@ class IClientManager(Interface): """ Add the client object given to the collection of clients. """ - diff --git a/composer/schema/schema.py b/composer/schema/schema.py index 9a6b1eb..216492e 100644 --- a/composer/schema/schema.py +++ b/composer/schema/schema.py @@ -69,3 +69,19 @@ class FormState(object): self.changed = changed self.severity = severity + +class FormError(object): + + def __init__(self, title, description=None, severity=5): + self.title = title + self.description = description or title + self.severity = severity + + def __str__(self): + return self.title + + +formErrors = dict( + required_missing=FormError(u'Missing data for required field', + u'Please enter data for required field.') +)