appy.gen: generalized use of appy.Object; existence of a field value (that triggers search for a default value) is now based on Type.isEmptyValue, and not '==None'; bugfix with default values for List fields; prevent search icon to be shown for a Ref when there is no linked object; appy.pod: added class appy.pod.parts.OdtTable that allows to create a complex (ie, with a dynamic number of columns) table programmatically (to be imported with a statement 'do ... from'); appy.shared.diff: improvements in the multiple XHTML diff; appy.shared.xml_parser.XmlMarshaller: support for default namespaces and 'any' tags.

This commit is contained in:
Gaetan Delannay 2011-10-26 10:21:09 +02:00
parent 1ebcbb7b34
commit 3ab6cec7d6
9 changed files with 147 additions and 68 deletions

View file

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
import re, time, copy, sys, types, os, os.path, mimetypes, string, StringIO
from appy import Object
from appy.gen.layout import Table
from appy.gen.layout import defaultFieldLayouts
from appy.gen.po import PoMessage
from appy.gen.utils import sequenceTypes, GroupDescr, Keywords, FileWrapper, \
getClassName, SomeObjects, AppyObject
getClassName, SomeObjects
import appy.pod
from appy.pod.renderer import Renderer
from appy.shared.data import countries
@ -16,7 +17,7 @@ r, w, d = ('read', 'write', 'delete')
digit = re.compile('[0-9]')
alpha = re.compile('[a-zA-Z0-9]')
letter = re.compile('[a-zA-Z]')
nullValues = (None, '', ' ')
nullValues = (None, '', [])
validatorTypes = (types.FunctionType, types.UnboundMethodType,
type(re.compile('')))
emptyTuple = ()
@ -729,7 +730,7 @@ class Type:
def getValue(self, obj):
'''Gets, on_obj, the value conforming to self's type definition.'''
value = getattr(obj.aq_base, self.name, None)
if (value == None):
if self.isEmptyValue(value):
# If there is no value, get the default value if any
if not self.editDefault:
# Return self.default, of self.default() if it is a method
@ -2243,10 +2244,6 @@ class List(Type):
for n, field in self.fields:
if n == name: return field
def isEmptyValue(self, value, obj=None):
'''Returns True if the p_value must be considered as an empty value.'''
return not value
def getRequestValue(self, request):
'''Concatenates the list from distinct form elements in the request.'''
prefix = self.name + '*' + self.fields[0][0] + '*'
@ -2254,7 +2251,7 @@ class List(Type):
for key in request.keys():
if not key.startswith(prefix): continue
# I have found a row. Gets its index
row = AppyObject()
row = Object()
rowIndex = int(key.split('*')[-1])
if rowIndex == -1: continue # Ignore the template row.
for name, field in self.fields:
@ -2271,10 +2268,6 @@ class List(Type):
keys = res.keys()
keys.sort()
res = [res[key] for key in keys]
print 'REQUEST VALUE FOR LIST (%d)' % len(res)
for value in res:
for k, v in value.__dict__.iteritems():
print k, '=', v
# I store in the request this computed value. This way, when individual
# subFields will need to get their value, they will take it from here,
# instead of taking it from the specific request key. Indeed, specific
@ -2290,14 +2283,13 @@ class List(Type):
setattr(v, name, field.getStorableValue(getattr(v, name)))
return value
def getInnerValue(self, obj, name, i):
def getInnerValue(self, outerValue, name, i):
'''Returns the value of inner field named p_name in row number p_i
with the list of values from this field on p_obj.'''
within the whole list of values p_outerValue.'''
if i == -1: return ''
value = getattr(obj, self.name, None)
if not value: return ''
if i >= len(value): return ''
return getattr(value[i], name, '')
if not outerValue: return ''
if i >= len(outerValue): return ''
return getattr(outerValue[i], name, '')
# Workflow-specific types and default workflows --------------------------------
appyToZopePermissions = {

View file

@ -4,6 +4,7 @@
# ------------------------------------------------------------------------------
import os, os.path, sys, types, mimetypes, urllib, cgi
from appy import Object
import appy.gen
from appy.gen import Type, String, Selection, Role, No, WorkflowAnonymous, \
Transition, Permission
@ -206,9 +207,9 @@ class BaseMixin:
return self.goto(urlBack)
# Object for storing validation errors
errors = AppyObject()
errors = Object()
# Object for storing the (converted) values from the request
values = AppyObject()
values = Object()
# Trigger field-specific validation
self.intraFieldValidation(errors, values)
@ -435,7 +436,8 @@ class BaseMixin:
# broken on returned object.
return getattr(self, methodName, None)
def getFieldValue(self, name, onlyIfSync=False, layoutType=None):
def getFieldValue(self, name, onlyIfSync=False, layoutType=None,
outerValue=None):
'''Returns the database value of field named p_name for p_self.
If p_onlyIfSync is True, it returns the value only if appyType can be
retrieved in synchronous mode.'''
@ -445,7 +447,8 @@ class BaseMixin:
if '*' not in name: return appyType.getValue(self)
# The field is an inner field from a List.
listName, name, i = name.split('*')
return self.getAppyType(listName).getInnerValue(self, name, int(i))
listType = self.getAppyType(listName)
return listType.getInnerValue(outerValue, name, int(i))
def getFormattedFieldValue(self, name, value):
'''Gets a nice, string representation of p_value which is a value from

View file

@ -145,7 +145,7 @@
</tal:noObject>
<tal:comment replace="nothing">If there is an object...</tal:comment>
<tal:objectIsPresent condition="objs">
<tal:objectIsPresent condition="python: objs">
<tal:obj repeat="obj objs">
<td tal:define="includeShownInfo python:True"><metal:showObjectTitle use-macro="app/skyn/widgets/ref/macros/objectTitle" /></td>
</tal:obj>
@ -161,7 +161,7 @@
(<span tal:replace="totalNumber"/>)
<metal:plusIcon use-macro="app/skyn/widgets/ref/macros/plusIcon"/>
<tal:comment replace="nothing">The search icon if field is queryable</tal:comment>
<a tal:condition="appyType/queryable"
<a tal:condition="python: objs and appyType['queryable']"
tal:attributes="href python: '%s/skyn/search?type_name=%s&ref=%s:%s' % (tool.absolute_url(), linkedPortalType, contextObj.UID(), appyType['name'])">
<img src="search.gif" tal:attributes="title python: _('search_objects')"/></a>
</legend>

View file

@ -59,7 +59,8 @@
layout python: widget['layouts'][layoutType];
name widgetName| widget/name;
sync python: widget['sync'][layoutType];
rawValue python: contextObj.getFieldValue(name, onlyIfSync=True, layoutType=layoutType);
outerValue value|nothing;
rawValue python: contextObj.getFieldValue(name,onlyIfSync=True,layoutType=layoutType,outerValue=outerValue);
value python: contextObj.getFormattedFieldValue(name, rawValue);
requestValue python: contextObj.getRequestFieldValue(name);
inRequest python: request.has_key(name);

View file

@ -158,9 +158,6 @@ def produceNiceMessage(msg):
res += c
return res
# ------------------------------------------------------------------------------
class AppyObject: pass
# ------------------------------------------------------------------------------
class SomeObjects:
'''Represents a bunch of objects retrieved from a reference or a query in