[gen] Added attribute 'xml' on every field allowing to customize the XML marshalling process. [gen] Added new layout 'xml', now different from the 'view' layout, allowing to define which fields are to be dumped in the XML version of some object. [gen] Security fix in ToolMixin::getUser. [gen] Bugfix in Mixin::getUrl. [shared] dav.py: method 'get' can now accept parameters. [shared] xml_parser: changes to the XmlMarshaller (due to XML-related changes).

This commit is contained in:
Gaetan Delannay 2014-12-08 14:52:04 +01:00
parent f055ec1754
commit c53654a1a1
19 changed files with 119 additions and 80 deletions

View file

@ -141,7 +141,7 @@ class Field:
layouts, move, indexed, searchable, specificReadPermission,
specificWritePermission, width, height, maxChars, colspan,
master, masterValue, focus, historized, mapping, label,
sdefault, scolspan, swidth, sheight, persist):
sdefault, scolspan, swidth, sheight, persist, xml):
# The validator restricts which values may be defined. It can be an
# interval (1,None), a list of string values ['choice1', 'choice2'],
# a regular expression, a custom function, a Selection instance, etc.
@ -250,6 +250,13 @@ class Field:
# For some fields it is not wanted (ie, fields used only as masters to
# update slave's selectable values).
self.persist = persist
# Standard marshallers are provided for converting values of this field
# into XML. If you want to customize the marshalling process, you can
# define a method in "xml" that will accept a field value and will
# return a possibly different value. Be careful: do not return a chunk
# of XML here! Simply return an alternate value, that will be
# XML-marshalled.
self.xml = xml
def init(self, name, klass, appName):
'''When the application server starts, this secondary constructor is
@ -351,7 +358,7 @@ class Field:
for r in res:
if r == layoutType: return True
return
elif res in ('view', 'edit', 'result', 'buttons'):
elif res in ('view', 'edit', 'result', 'buttons', 'xml'):
return res == layoutType
# For showing a field on layout "buttons", the "buttons" layout must
# explicitly be returned by the show method.
@ -570,6 +577,12 @@ class Field:
method in string.py).'''
return self.getFormattedValue(obj, value, showChanges)
def getXmlValue(self, obj, value):
'''This method allows a developer to customize the value that will be
marshalled into XML. It makes use of attribute "xml".'''
if not self.xml: return value
return self.xml(obj, value)
def getIndexType(self):
'''Returns the name of the technical, Zope-level index type for this
field.'''

View file

@ -59,7 +59,7 @@ class Action(Field):
width=None, height=None, maxChars=None, colspan=1, action=None,
result='computation', confirm=False, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, icon=None):
label=None, icon=None, xml=None):
# Can be a single method or a list/tuple of methods
self.action = action
# For the 'result' param:
@ -80,7 +80,7 @@ class Action(Field):
move, indexed, False, specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, mapping, label,
None, None, None, None, False)
None, None, None, None, False, xml)
self.validable = False
self.renderLabel = False # Label is rendered directly within the button

View file

@ -84,7 +84,7 @@ class Boolean(Field):
maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
sdefault=False, scolspan=1, swidth=None, sheight=None,
persist=True, render='checkbox'):
persist=True, render='checkbox', xml=None):
# By default, a boolean is edited via a checkbox. It can also be edited
# via 2 radio buttons (p_render="radios").
self.render = render
@ -93,7 +93,7 @@ class Boolean(Field):
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,
sheight, persist)
sheight, persist, xml)
self.pythonType = bool
def getDefaultLayouts(self):

View file

@ -210,19 +210,19 @@ class Calendar(Field):
pxEdit = pxSearch = ''
def __init__(self, eventTypes, eventNameMethod=None, validator=None,
default=None, show='view', page='main', group=None,
default=None, show=('view', 'xml'), page='main', group=None,
layouts=None, move=0, specificReadPermission=False,
specificWritePermission=False, width=None, height=300,
colspan=1, master=None, masterValue=None, focus=False,
mapping=None, label=None, maxEventLength=50,
otherCalendars=None, additionalInfo=None, startDate=None,
endDate=None, defaultDate=None, preCompute=None,
applicableEvents=None):
applicableEvents=None, xml=None):
Field.__init__(self, validator, (0,1), default, show, page, group,
layouts, move, False, False, specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, False, mapping, label, None,
None, None, None, True)
None, None, None, True, xml)
# eventTypes can be a "static" list or tuple of strings that identify
# the types of events that are supported by this calendar. It can also
# be a method that computes such a "dynamic" list or tuple. When

View file

@ -31,14 +31,13 @@ class Computed(Field):
value=":field.sdefault"/>''')
def __init__(self, validator=None, multiplicity=(0,1), default=None,
show=('view', 'result'), page='main', group=None,
layouts=None, move=0, indexed=False, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, maxChars=None, colspan=1, method=None,
formatMethod=None, plainText=False, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, sdefault='', scolspan=1, swidth=None, sheight=None,
context=None):
show=None, page='main', group=None, layouts=None, move=0,
indexed=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, method=None, formatMethod=None,
plainText=False, master=None, masterValue=None, focus=False,
historized=False, mapping=None, label=None, sdefault='',
scolspan=1, swidth=None, sheight=None, context=None, xml=None):
# The Python method used for computing the field value, or a PX.
self.method = method
# A specific method for producing the formatted value of this field.
@ -55,6 +54,13 @@ class Computed(Field):
if isinstance(method, Px):
# When field computation is done with a PX, the result is XHTML.
self.plainText = False
# Determine default value for "show"
if show == None:
# XHTML content in a Computed field generally corresponds to some
# custom XHTML widget. This is why, by default, we do not render it
# in the xml layout.
show = self.plainText and ('view', 'result', 'xml') or \
('view', 'result')
# If method is a PX, its context can be given in p_context.
self.context = context
Field.__init__(self, None, multiplicity, default, show, page, group,
@ -62,7 +68,7 @@ class Computed(Field):
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,
sheight, False)
sheight, False, xml)
self.validable = False
def getValue(self, obj):

View file

@ -170,7 +170,7 @@ class Date(Field):
maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
sdefault=None, scolspan=1, swidth=None, sheight=None,
persist=True):
persist=True, xml=None):
self.format = format
self.calendar = calendar
self.startYear = startYear
@ -183,7 +183,7 @@ class Date(Field):
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,
sheight, persist)
sheight, persist, xml)
def getCss(self, layoutType, res):
# CSS files are only required if the calendar must be shown.

View file

@ -324,14 +324,14 @@ class File(Field):
maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
isImage=False, sdefault='', scolspan=1, swidth=None,
sheight=None):
sheight=None, xml=None):
self.isImage = isImage
Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, False,
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,
sheight, True)
sheight, True, xml)
def getRequestValue(self, obj, requestName=None):
name = requestName or self.name

View file

@ -56,7 +56,8 @@ class Float(Field):
maxChars=13, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
sdefault=('',''), scolspan=1, swidth=None, sheight=None,
persist=True, precision=None, sep=(',', '.'), tsep=' '):
persist=True, precision=None, sep=(',', '.'), tsep=' ',
xml=None):
# The precision is the number of decimal digits. This number is used
# for rendering the float, but the internal float representation is not
# rounded.
@ -78,7 +79,7 @@ class Float(Field):
specificReadPermission, specificWritePermission, width,
height, maxChars, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,
sheight, persist)
sheight, persist, xml)
self.pythonType = float
def getFormattedValue(self, obj, value, showChanges=False, language=None):

View file

@ -29,11 +29,12 @@ class Info(Field):
indexed=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None):
focus=False, historized=False, mapping=None, label=None,
xml=None):
Field.__init__(self, None, (0,1), default, show, page, group, layouts,
move, indexed, False, specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, mapping, label,
None, None, None, None, False)
None, None, None, None, False, xml)
self.validable = False
# ------------------------------------------------------------------------------

View file

@ -53,13 +53,13 @@ class Integer(Field):
maxChars=13, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
sdefault=('',''), scolspan=1, swidth=None, sheight=None,
persist=True):
persist=True, xml=None):
Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, searchable,
specificReadPermission, specificWritePermission, width,
height, maxChars, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,
sheight, persist)
sheight, persist, xml)
self.pythonType = long
def validateValue(self, obj, value):

View file

@ -78,12 +78,13 @@ class List(Field):
specificWritePermission=False, width='', height=None,
maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
subLayouts=Table('frv', width=None), widths=None):
subLayouts=Table('frv', width=None), widths=None, xml=None):
Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, False,
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, mapping, label, None, None, None, None, True)
historized, mapping, label, None, None, None, None,
True, xml)
self.validable = True
# Tuples of (names, Field instances) determining the format of every
# element in the list.

View file

@ -49,12 +49,12 @@ class Ogone(Field):
group=None, layouts=None, move=0, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
colspan=1, master=None, masterValue=None, focus=False,
mapping=None, label=None):
mapping=None, label=None, xml=None):
Field.__init__(self, None, (0,1), None, show, page, group, layouts,
move, False, False,specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, False, mapping, label, None,
None, None, None, False)
None, None, None, False, xml)
# orderMethod must contain a method returning a dict containing info
# about the order. Following keys are mandatory:
# * orderID An identifier for the order. Don't use the object UID

View file

@ -158,7 +158,8 @@ class Pod(Field):
template=None, templateName=None, showTemplate=None,
freezeTemplate=None, maxPerRow=5, context=None,
stylesMapping={}, formats=None, getChecked=None, mailing=None,
mailingName=None, showMailing=None, mailingInfo=None):
mailingName=None, showMailing=None, mailingInfo=None,
xml=None):
# Param "template" stores the path to the pod template(s). If there is
# a single template, a string is expected. Else, a list or tuple of
# strings is expected. Every such path must be relative to your
@ -275,7 +276,7 @@ class Pod(Field):
move, indexed, searchable, specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, mapping, label,
None, None, None, None, True)
None, None, None, None, True, xml)
# Param "persist" is set to True but actually, persistence for a pod
# field is determined by freezing.
self.validable = False

View file

@ -528,7 +528,7 @@ class Ref(Field):
checkboxes=True, checkboxesDefault=None, sdefault='',
scolspan=1, swidth=None, sheight=None, sselect=None,
persist=True, render='list', menuIdMethod=None,
menuInfoMethod=None, menuUrlMethod=None):
menuInfoMethod=None, menuUrlMethod=None, xml=None):
self.klass = klass
self.attribute = attribute
# May the user add new objects through this ref ? "add" may also contain
@ -708,7 +708,7 @@ class Ref(Field):
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,
sheight, persist)
sheight, persist, xml)
self.validable = bool(self.link)
self.checkParameters()
@ -794,6 +794,12 @@ class Ref(Field):
# Return a copy: it can be dangerous to give the real database value.
if res: return list(res)
def getXmlValue(self, obj, value):
'''The default XML value for a Ref is the list of tied object URLs.'''
# Bypass the default behaviour if a custom method is given
if self.xml: return self.xml(obj, value)
return ['%s/xml' % tied.o.absolute_url() for tied in value]
def getPossibleValues(self, obj, startNumber=None, someObjects=False,
removeLinked=False):
'''This method returns the list of all objects that can be selected

View file

@ -381,7 +381,7 @@ class String(Field):
persist=True, transform='none', placeholder=None,
styles=('p','h1','h2','h3','h4'), allowImageUpload=True,
spellcheck=False, languages=('en',), languagesLayouts=None,
inlineEdit=False):
inlineEdit=False, xml=None):
# According to format, the widget will be different: input field,
# textarea, inline editor... Note that there can be only one String
# field of format CAPTCHA by page, because the captcha challenge is
@ -425,7 +425,7 @@ class String(Field):
specificReadPermission, specificWritePermission, width,
height, maxChars, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,
sheight, persist)
sheight, persist, xml)
self.isSelect = self.isSelection()
# If self.isSelect, self.sdefault must be a list of value(s).
if self.isSelect and not sdefault: