Skip to content

Commit

Permalink
next stab on accepting missing vocabulary terms
Browse files Browse the repository at this point in the history
  • Loading branch information
Adam Groszer committed Sep 7, 2012
1 parent 1953cae commit ef304f3
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 60 deletions.
4 changes: 2 additions & 2 deletions src/z3c/form/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@
<adapter
factory=".term.ChoiceTerms"
/>
<adapter
<!--<adapter
factory=".term.ChoiceTermsVocabulary"
/>
/>-->
<adapter
factory=".term.MissingChoiceTermsVocabulary"
/>
Expand Down
21 changes: 9 additions & 12 deletions src/z3c/form/form.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,19 @@ def applyChanges(form, content, data):
changes = {}
for name, field in form.fields.items():
# If the field is not in the data, then go on to the next one
if name not in data:
try:
newValue = data[name]
except KeyError:
continue
# If the value is NOT_CHANGED, ignore it, since the widget/converter
# sent a strong message not to do so.
if data[name] is interfaces.NOT_CHANGED:
if newValue is interfaces.NOT_CHANGED:
continue
# Get the datamanager and get the original value
dm = zope.component.getMultiAdapter(
(content, field.field), interfaces.IDataManager)
# Only update the data, if it is different
# Or we can not get the original value, in which case we can not check
# Or it is an Object, in case we'll never know
if (not dm.canAccess() or
dm.query() != data[name] or
zope.schema.interfaces.IObject.providedBy(field.field)):
dm.set(data[name])
if util.changedField(field.field, newValue, context=content):
# Only update the data, if it is different
dm = zope.component.getMultiAdapter(
(content, field.field), interfaces.IDataManager)
dm.set(newValue)
# Record the change using information required later
changes.setdefault(dm.field.interface, []).append(name)
return changes
Expand Down
15 changes: 0 additions & 15 deletions src/z3c/form/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -1111,18 +1111,3 @@ class IWidgetEvent(zope.interface.Interface):
class IAfterWidgetUpdateEvent(IWidgetEvent):
"""An event sent out after the widget was updated."""




class IForgivingChoice(zope.schema.interfaces.IChoice):
pass

class ForgivingChoice(zope.schema.Choice):
zope.interface.implements(IForgivingChoice)

def _validate(self, value):
if self.context is not None:
if self.query(self.context) == value:
return

super(ForgivingChoice, self)._validate(value)
25 changes: 11 additions & 14 deletions src/z3c/form/term.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from zope.schema import vocabulary

from z3c.form import interfaces
from z3c.form import util
from z3c.form.i18n import MessageFactory as _


Expand Down Expand Up @@ -135,14 +136,13 @@ def getTerm(self, value):
return self.makeMissingTerm(value)

def makeToken(self, value):
return unicode(value).encode('utf8').encode('base64').strip()
# create a unique valid ASCII token
return util.createCSSId(unicode(value))

def makeMissingTerm(self, value, token=None):
def makeMissingTerm(self, value):
"""Return a term that should be displayed for the missing token or
raise LookupError if it's really invalid"""
if token is None:
token = self.makeToken(value)
return vocabulary.SimpleTerm(value, token,
return vocabulary.SimpleTerm(value, self.makeToken(value),
title=_(u'Missing: ${value}', mapping=dict(value=unicode(value))))

def getTermByToken(self, token):
Expand All @@ -154,7 +154,12 @@ def getTermByToken(self, token):
value = zope.component.getMultiAdapter(
(self.widget.context, self.widget.field),
interfaces.IDataManager).query()
return self.makeMissingTerm(value, token=token)
term = self.makeMissingTerm(value)
if term.token == token:
# check if the given token matches the value, if not
# fall back on LookupError, otherwise we might accept
# any crap coming from the request
return term

raise LookupError(token)

Expand All @@ -163,14 +168,6 @@ class MissingChoiceTermsVocabulary(MissingTermsMixin, ChoiceTermsVocabulary):
"""ITerms adapter for zope.schema.IChoice based implementations using
vocabulary with missing terms support"""

zope.component.adapts(
zope.interface.Interface,
interfaces.IFormLayer,
zope.interface.Interface,
interfaces.IForgivingChoice,
zope.schema.interfaces.IBaseVocabulary,
interfaces.IWidget)


class ChoiceTermsSource(SourceTerms):
"ITerms adapter for zope.schema.IChoice based implementations using source."
Expand Down
41 changes: 41 additions & 0 deletions src/z3c/form/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,32 @@
import string
import zope.interface
import zope.contenttype
import zope.schema

from z3c.form import interfaces
from z3c.form.i18n import MessageFactory as _


_identifier = re.compile('[A-Za-z][a-zA-Z0-9_]*$')


def createId(name):
if _identifier.match(name):
return str(name).lower()
return name.encode('utf-8').encode('hex')


_acceptableChars = string.letters + string.digits + '_-'


def createCSSId(name):
return str(''.join([((char in _acceptableChars and char) or
char.encode('utf-8').encode('hex'))
for char in name]))

classTypes = type, types.ClassType


def getSpecification(spec, force=False):
"""Get the specification of the given object.
Expand Down Expand Up @@ -128,6 +134,41 @@ def extractFileName(form, id, cleanup=True, allowEmptyPostfix=False):
return widget.filename


def changedField(field, value, context=None):
"""Figure if a field's value changed
Comparing the value of the context attribute and the given value"""
if context is None:
context = field.context

# Get the datamanager and get the original value
dm = zope.component.getMultiAdapter(
(context, field), interfaces.IDataManager)
# now figure value chaged status
# Or we can not get the original value, in which case we can not check
# Or it is an Object, in case we'll never know
if (not dm.canAccess() or
dm.query() != value or
zope.schema.interfaces.IObject.providedBy(field)):
return True
else:
return False


def changedWidget(widget, value, field=None):
"""figure if a widget's value changed
Comparing the value of the widget context attribute and the given value"""
if (interfaces.IContextAware.providedBy(widget)
and not widget.ignoreContext):
# if the widget is context aware, figure if it's field changed
if field is None:
field = widget.field
return changedField(field, value)
# otherwise we cannot, return 'always changed'
return True


class UniqueOrderedKeys(object):
"""Ensures that we only use unique keys in a list.
Expand Down
3 changes: 3 additions & 0 deletions src/z3c/form/util.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ All other characters will be converted to ordinal numbers:
>>> util.createCSSId(u'This has spaces')
'This20has20spaces'

>>> util.createCSSId(unicode(dict({1:'x', 'foobar': 42})))
'7b13a2027x272c2027foobar273a20427d'


``getWidgetById(form, id)`` Function
------------------------------------
Expand Down
25 changes: 8 additions & 17 deletions src/z3c/form/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ def __init__(self, context, request, view, field, widget):

def validate(self, value):
"""See interfaces.IValidator"""
if value is interfaces.NOT_CHANGED:
# no need to validate unchanged values
return True

context = self.context
field = self.field
widget = self.widget
Expand All @@ -54,23 +58,10 @@ def validate(self, value):
field.required = False
if context is not None:
field = field.bind(context)
if value is interfaces.NOT_CHANGED:
if (interfaces.IContextAware.providedBy(widget) and
not widget.ignoreContext):
# get value from context
value = zope.component.getMultiAdapter(
(context, field),
interfaces.IDataManager).query()
else:
value = interfaces.NO_VALUE
if value is interfaces.NO_VALUE:
# look up default value
value = field.default
adapter = zope.component.queryMultiAdapter(
(context, self.request, self.view, field, widget),
interfaces.IValue, name='default')
if adapter:
value = adapter.get()

if widget and not util.changedWidget(widget, value, field=field):
# if new value == old value, no need to validate
return True
return field.validate(value)

def __repr__(self):
Expand Down

0 comments on commit ef304f3

Please sign in to comment.