Commit 609fd8c5 authored by Yoshinori Okuji's avatar Yoshinori Okuji

Add support for tales.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@1873 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 681a65e9
......@@ -50,7 +50,8 @@ class DefaultGetter(Method):
acquisition_sync_value,
storage_id=None,
alt_accessor_id = None,
is_list_type = 0
is_list_type = 0,
is_tales_type = 0
):
self._id = id
self.__name__ = id
......@@ -69,12 +70,21 @@ class DefaultGetter(Method):
self._storage_id = storage_id
self._alt_accessor_id = alt_accessor_id
self._is_list_type = is_list_type
self._is_tales_type = is_tales_type
def __call__(self, instance, *args, **kw):
if len(args) > 0:
default = args[0]
else:
default = self._default
# If this is a TALES expression and the option 'evaluate' is false,
# deal with the value as a simple object.
if self._is_tales_type and not kw.get('evaluate', 1):
is_list_type = 0
is_tales_type = 0
else:
is_list_type = self._is_list_type
is_tales_type = self._is_tales_type
return instance._getDefaultAcquiredProperty(self._key, default, self._null,
base_category=self._acquisition_base_category,
portal_type=self._acquisition_portal_type,
......@@ -84,7 +94,8 @@ class DefaultGetter(Method):
sync_value=self._acquisition_sync_value,
storage_id=self._storage_id,
alt_accessor_id=self._alt_accessor_id,
is_list_type=self._is_list_type
is_list_type=is_list_type,
is_tales_type=is_tales_type
)
Getter = DefaultGetter
......@@ -111,7 +122,8 @@ class ListGetter(Method):
acquisition_sync_value,
storage_id=None,
alt_accessor_id = None,
is_list_type = 0
is_list_type = 0,
is_tales_type = 0
):
self._id = id
self.__name__ = id
......@@ -130,12 +142,21 @@ class ListGetter(Method):
self._storage_id = storage_id
self._alt_accessor_id = alt_accessor_id
self._is_list_type = is_list_type
self._is_tales_type = is_tales_type
def __call__(self, instance, *args, **kw):
if len(args) > 0:
default = args[0]
else:
default = self._default
# If this is a TALES expression and the option 'evaluate' is false,
# deal with the value as a simple object.
if self._is_tales_type and not kw.get('evaluate', 1):
is_list_type = 0
is_tales_type = 0
else:
is_list_type = self._is_list_type
is_tales_type = self._is_tales_type
return instance._getAcquiredPropertyList(self._key, default, self._null,
base_category=self._acquisition_base_category,
portal_type=self._acquisition_portal_type,
......@@ -145,6 +166,8 @@ class ListGetter(Method):
sync_value=self._acquisition_sync_value,
storage_id=self._storage_id,
alt_accessor_id=self._alt_accessor_id,
is_list_type=self._is_list_type)
is_list_type=is_list_type,
is_tales_type=is_tales_type
)
SetGetter = ListGetter # ERROR
......@@ -51,6 +51,7 @@ class Getter(Method):
storage_id=None,
alt_accessor_id = None,
is_list_type = 0,
is_tales_type = 0
):
if type(portal_type) == type('a'): portal_type = (portal_type, )
self._id = id
......@@ -71,6 +72,7 @@ class Getter(Method):
self._storage_id = storage_id
self._alt_accessor_id = alt_accessor_id
self._is_list_type = is_list_type
self._is_tales_type = is_tales_type
def __call__(self, instance, *args, **kw):
if len(args) > 0:
......@@ -86,10 +88,11 @@ class Getter(Method):
sync_value=self._acquisition_sync_value,
storage_id=self._storage_id,
alt_accessor_id=self._alt_accessor_id,
is_list_type=self._is_list_type
is_list_type=self._is_list_type,
is_tales_type=self._is_tales_type,
)
if value is not None:
return value.getProperty(self._acquired_property, default)
return value.getProperty(self._acquired_property, default, **kw)
else:
return default
......@@ -116,6 +119,7 @@ class Setter(Method):
storage_id=None,
alt_accessor_id = None,
is_list_type = 0,
is_tales_type = 0,
reindex = 0
):
if type(portal_type) == type('a'): portal_type = (portal_type, )
......@@ -137,6 +141,7 @@ class Setter(Method):
self._storage_id = storage_id
self._alt_accessor_id = alt_accessor_id
self._is_list_type = is_list_type
self._is_tales_type = is_tales_type
self._reindex = reindex
def __call__(self, instance, *args, **kw):
......@@ -149,7 +154,8 @@ class Setter(Method):
sync_value=self._acquisition_sync_value,
storage_id=self._storage_id,
alt_accessor_id=self._alt_accessor_id,
is_list_type=self._is_list_type
is_list_type=self._is_list_type,
is_tales_type=self._is_tales_type
)
if o is None:
from Products.ERP5Type.Utils import assertAttributePortalType
......
......@@ -31,6 +31,8 @@ from TypeDefinition import type_definition, list_types, ATTRIBUTE_PREFIX
from Accessor import Accessor as Method
#from MethodObject import Method
from Products.ERP5Type.Cache import CachingMethod
# Creation of default constructor
class func_code: pass
......@@ -91,6 +93,13 @@ class Setter(Method):
method(*args, **kw)
if self._reindex: instance.reindexObject()
def _evaluate_tales(instance=None, value=None):
expression = Expression(value)
econtext = createExpressionContext(instance)
return expression(econtext)
evaluate_tales = CachingMethod(_evaluate_tales, id = 'evaluate_tales', cache_duration=300)
class Getter(Method):
"""
Gets an attribute value. A default value can be
......@@ -115,21 +124,20 @@ class Getter(Method):
if storage_id is None:
storage_id = "%s%s" % (ATTRIBUTE_PREFIX, key)
self._storage_id = storage_id
self._is_tales_type = (property_type == 'tales')
def __call__(self, instance, *args, **kw):
# We return the
if len(args) > 0:
# We should not use here self._null but None instead XXX
if getattr(instance, self._storage_id, None) not in self._null:
return getattr(instance, self._storage_id)
else:
return args[0]
default = args[0]
else:
# We should not use here self._null but None instead XXX
if getattr(instance, self._storage_id, None) not in self._null:
return getattr(instance, self._storage_id)
default = self._default
value = getattr(instance, self._storage_id, None)
if value is not None:
if self._is_tales_type and kw.get('evaluate', 1):
return evaluate_tales(instance, value)
else:
return self._default
return value
return default
class Tester(Method):
"""
......
......@@ -30,6 +30,12 @@ from Base import func_code, type_definition, list_types, ATTRIBUTE_PREFIX, Metho
from TypeDefinition import asList, identity
import Base
from Products.CMFCore.Expression import Expression
from Products.ERP5Type.Utils import createExpressionContext
from Products.ERP5Type.Cache import CachingMethod
from zLOG import LOG
class DefaultSetter(Method):
"""
Sets the default attribute in a list
......@@ -60,13 +66,18 @@ class DefaultSetter(Method):
if storage_id is None:
storage_id = "%s%s" % (ATTRIBUTE_PREFIX, key)
self._storage_id = storage_id
self._is_tales_type = (property_type == 'tales')
def __call__(self, instance, *args, **kw):
# Turn the value into a list
value = args[0]
if not self._reindex:
# Modify the property
if self._is_tales_type:
if value in self._null:
value = None
setattr(instance, self._storage_id, value)
elif value in self._null:
# The value has no default property -> it is empty
setattr(instance, self._storage_id, ())
else:
......@@ -102,6 +113,8 @@ class Setter(DefaultSetter):
# Modify the property
if value in self._null:
setattr(instance, self._storage_id, None)
elif self._is_tales_type:
setattr(instance, self._storage_id, str(value))
else:
value = self._cast(args[0])
if self._item_cast is not identity:
......@@ -146,13 +159,18 @@ class SetSetter(Method):
if storage_id is None:
storage_id = "%s%s" % (ATTRIBUTE_PREFIX, key)
self._storage_id = storage_id
self._is_tales_type = (property_type == 'tales')
def __call__(self, instance, *args, **kw):
# Turn the value into a list
value = args[0]
if not self._reindex:
# Modify the property
if self._is_tales_type:
if value in self._null:
value = None
setattr(instance, self._storage_id, value)
elif value in self._null:
# The value has no default property -> it is empty
setattr(instance, self._storage_id, ())
else:
......@@ -186,6 +204,14 @@ class SetSetter(Method):
method(*args, **kw)
if self._reindex: instance.reindexObject()
def _evaluate_tales(instance=None, value=None):
expression = Expression(value)
econtext = createExpressionContext(instance)
return expression(econtext)
evaluate_tales = CachingMethod(_evaluate_tales, id = 'evaluate_tales', cache_duration=300)
class DefaultGetter(Method):
"""
Gets the first item of a list
......@@ -209,13 +235,23 @@ class DefaultGetter(Method):
if storage_id is None:
storage_id = "%s%s" % (ATTRIBUTE_PREFIX, key)
self._storage_id = storage_id
self._is_tales_type = (property_type == 'tales')
def __call__(self, instance, *args, **kw):
if len(args) > 0:
default = args[0]
else:
default = self._default
list_value = getattr(instance, self._storage_id, None)
if list_value is not None:
if self._is_tales_type:
if kw.get('evaluate', 1):
list_value = evaluate_tales(instance=instance, value=list_value)
else:
return list_value
if len(list_value) > 0:
return list_value[0]
return self._default
return default
Getter = DefaultGetter
......@@ -243,21 +279,23 @@ class ListGetter(Method):
if storage_id is None:
storage_id = "%s%s" % (ATTRIBUTE_PREFIX, key)
self._storage_id = storage_id
self._is_tales_type = (property_type == 'tales')
def __call__(self, instance, *args, **kw):
# We return the
if len(args) > 0:
# We should not use here self._null but None instead XXX
if getattr(instance, self._storage_id, None) not in self._null:
return list(getattr(instance, self._storage_id))
else:
return args[0]
default = args[0]
else:
default = self._default
list_value = getattr(instance, self._storage_id, None)
# We should not use here self._null but None instead XXX
if getattr(instance, self._storage_id, None) not in self._null:
return list(getattr(instance, self._storage_id))
if list_value not in self._null:
if self._is_tales_type:
if kw.get('evaluate', 1):
list_value = evaluate_tales(instance=instance, value=list_value)
else:
return self._default
return list_value
return list(list_value)
return default
SetGetter = ListGetter
......
......@@ -152,6 +152,11 @@ type_definition = {
'default' : None,
'isList' : 0,
},
'tales' : { 'cast' : identity,
'null' : ('', 'None', None,),
'default' : None,
'isList' : 0,
},
}
list_types = ('lines', 'tokens', 'selection', 'multiple selection')
......
......@@ -34,6 +34,7 @@ from Acquisition import aq_base, aq_inner, aq_acquire, aq_chain
from Products.CMFCore.WorkflowCore import WorkflowMethod
from Products.CMFCore.PortalContent import PortalContent
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.Expression import Expression
from Products.DCWorkflow.Transitions import TRIGGER_WORKFLOW_METHOD
......@@ -43,8 +44,10 @@ from Products.ERP5Type import PropertySheet
from Products.ERP5Type import Permissions
from Products.ERP5Type.Utils import UpperCase
from Products.ERP5Type.Utils import convertToUpperCase, convertToMixedCase
from Products.ERP5Type.Utils import createExpressionContext
from Products.ERP5Type.Utils2 import _getListFor
from Products.ERP5Type.Accessor.TypeDefinition import list_types
from Products.ERP5Type.Accessor import Base as BaseAccessor
from Products.ERP5Type.XMLExportImport import Base_asXML
from Accessor import WorkflowState
......@@ -216,6 +219,14 @@ class Base( CopyContainer, PortalContent, ActiveObject, ERP5PropertyManager ):
self.uid = uid # Else it will be generated when we need it
self.sid = sid
# XXX This is necessary to override getId which is also defined in SimpleItem.
security.declareProtected( Permissions.AccessContentsInformation, 'getId' )
for prop in PropertySheet.Base._properties:
if prop['id'] == 'id':
getId = BaseAccessor.Getter('getId', 'id', prop['type'],
default_value = prop.get('default'), storage_id = prop.get('storage_id'))
break
# Debug
def getOid(self):
"""
......@@ -235,7 +246,7 @@ class Base( CopyContainer, PortalContent, ActiveObject, ERP5PropertyManager ):
def _getDefaultAcquiredProperty(self, key, default_value, null_value,
base_category=None, portal_type=None, copy_value=0, mask_value=0, sync_value=0,
accessor_id=None, depends=None, storage_id=None, alt_accessor_id=None,
is_list_type=0):
is_list_type=0, is_tales_type=0):
"""
This method implements programmable acquisition of values in ERP5.
......@@ -296,7 +307,7 @@ class Base( CopyContainer, PortalContent, ActiveObject, ERP5PropertyManager ):
if not hasattr(TRANSACTION, '_erp5_acquisition_stack'): TRANSACTION._erp5_acquisition_stack = {}
acquisition_key = ('_getDefaultAcquiredProperty', self.getPath(), key, base_category,
portal_type, copy_value, mask_value, sync_value,
accessor_id, depends, storage_id, alt_accessor_id, is_list_type)
accessor_id, depends, storage_id, alt_accessor_id, is_list_type, is_tales_type)
if TRANSACTION._erp5_acquisition_stack.has_key(acquisition_key): return null_value
TRANSACTION._erp5_acquisition_stack[acquisition_key] = 1
......@@ -304,11 +315,16 @@ class Base( CopyContainer, PortalContent, ActiveObject, ERP5PropertyManager ):
if storage_id is None: storage_id=key
#LOG("Get Acquired Property storage_id",0,str(storage_id))
# If we hold an attribute and mask_value is set, return the attribute
if mask_value and hasattr(self, storage_id):
if getattr(self, storage_id) != None:
value = getattr(self, storage_id, None)
if mask_value and value is not None:
# Pop context
del TRANSACTION._erp5_acquisition_stack[acquisition_key]
return getattr(self, storage_id)
if is_tales_type:
expression = Expression(value)
econtext = createExpressionContext(self)
return expression(econtext)
else:
return value
# Retrieve the list of related objects
#LOG("Get Acquired Property self",0,str(self))
#LOG("Get Acquired Property portal_type",0,str(portal_type))
......@@ -408,34 +424,26 @@ class Base( CopyContainer, PortalContent, ActiveObject, ERP5PropertyManager ):
if not hasattr(TRANSACTION, '_erp5_acquisition_stack'): TRANSACTION._erp5_acquisition_stack = {}
acquisition_key = ('_getAcquiredPropertyList', self.getPath(), key, base_category,
portal_type, copy_value, mask_value, sync_value,
accessor_id, depends, storage_id, alt_accessor_id, is_list_type)
accessor_id, depends, storage_id, alt_accessor_id, is_list_type, is_tales_type)
if TRANSACTION._erp5_acquisition_stack.has_key(acquisition_key): return null_value
TRANSACTION._erp5_acquisition_stack[acquisition_key] = 1
if storage_id is None: storage_id=key
if mask_value and hasattr(self, storage_id):
if getattr(self, storage_id) != None:
value = getattr(self, storage_id, None)
if mask_value and value is not None:
# Pop context
del TRANSACTION._erp5_acquisition_stack[acquisition_key]
return getattr(self, storage_id)
if type(base_category) == 'a':
base_category = (base_category, )
if type(portal_type) == 'a':
portal_type = (portal_type, )
super = None
psuper = []
for cat in base_category:
#super_list = self._getValueList(cat) # We only do a single jump - no acquisition
super_list = self._getAcquiredValueList(cat) # Full acquisition
for super in super_list:
if super is not None:
# Performance should be increased
for ptype in portal_type:
if ptype == super.portal_type:
psuper.append(super)
if len(psuper) > 0:
if is_tales_type:
expression = Expression(value)
econtext = createExpressionContext(self)
return expression(econtext)
else:
return value
super_list = self._getAcquiredValueList(base_category, portal_type=portal_type) # Full acquisition
super_list = filter(lambda o: o.getPhysicalPath() != self.getPhysicalPath(), super_list) # Make sure we do not create stupid loop here
if len(super_list) > 0:
value = []
for super in psuper:
for super in super_list:
if accessor_id is None:
if is_list_type:
result = super.getPropertyList(key)
......@@ -473,7 +481,7 @@ class Base( CopyContainer, PortalContent, ActiveObject, ERP5PropertyManager ):
return default_value
security.declareProtected( Permissions.AccessContentsInformation, 'getProperty' )
def getProperty(self, key, d=None):
def getProperty(self, key, d=None, **kw):
"""
Previous Name: getValue
......@@ -483,13 +491,13 @@ class Base( CopyContainer, PortalContent, ActiveObject, ERP5PropertyManager ):
aq_self = aq_base(self)
if hasattr(aq_self, accessor_name):
method = getattr(self, accessor_name)
return method()
elif hasattr(aq_self, key):
value = getattr(aq_self, key)
if callable(value): value = value()
return value
return method(**kw)
#elif hasattr(aq_self, key):
# value = getattr(aq_self, key)
# if callable(value): value = value()
# return value
else:
return ERP5PropertyManager.getProperty(self, key, d=d)
return ERP5PropertyManager.getProperty(self, key, d=d, **kw)
security.declareProtected( Permissions.AccessContentsInformation, 'getPropertyList' )
def getPropertyList(self, key, d=None):
......@@ -524,6 +532,7 @@ class Base( CopyContainer, PortalContent, ActiveObject, ERP5PropertyManager ):
**kw allows to call setProperty as a generic setter (ex. setProperty(value_uid, portal_type=))
"""
LOG('_setProperty', 0, 'key = %r, value = %r, type = %r, kw = %r' % (key, value, type, kw))
#LOG('In _setProperty',0, str(key))
if type is not 'string': # Speed
if type in list_types: # Patch for OFS PropertyManager
......@@ -534,7 +543,7 @@ class Base( CopyContainer, PortalContent, ActiveObject, ERP5PropertyManager ):
# since we will change the value on self
# rather than through implicit aquisition
if hasattr(aq_self, accessor_name):
#LOG("Calling: ",0, '%s %s ' % (accessor_name, kw[key]))
#LOG("Calling: ",0, '%r %r ' % (accessor_name, key))
method = getattr(self, accessor_name)
return method(value, **kw)
"""# Make sure we change the default value again
......@@ -547,7 +556,7 @@ class Base( CopyContainer, PortalContent, ActiveObject, ERP5PropertyManager ):
method(kw[new_key])"""
public_accessor_name = 'set' + UpperCase(key)
if hasattr(aq_self, public_accessor_name):
#LOG("Calling: ",0, '%s %s ' % (public_accessor_name, kw[key]))
#LOG("Calling: ",0, '%r %r ' % (public_accessor_name, key))
method = getattr(self, public_accessor_name)
method(value, **kw)
else:
......
......@@ -1018,7 +1018,8 @@ def createDefaultAccessors(klass, id, prop = None):
prop.get('acquisition_sync_value',0),
storage_id = prop.get('storage_id'),
alt_accessor_id = prop.get('alt_accessor_id'),
is_list_type = (prop['type'] in list_types or prop.get('multivalued', 0))
is_list_type = (prop['type'] in list_types or prop.get('multivalued', 0)),
is_tales_type = (prop['type'] == 'tales')
)
# The default accessor returns the first item in a list
default_accessor = base_accessor
......@@ -1036,7 +1037,8 @@ def createDefaultAccessors(klass, id, prop = None):
prop.get('acquisition_sync_value',0),
storage_id = prop.get('storage_id'),
alt_accessor_id = prop.get('alt_accessor_id'),
is_list_type = (prop['type'] in list_types or prop.get('multivalued', 0))
is_list_type = (prop['type'] in list_types or prop.get('multivalued', 0)),
is_tales_type = (prop['type'] == 'tales')
)
# Base Getter
accessor_name = 'get' + UpperCase(id)
......@@ -1108,7 +1110,8 @@ def createDefaultAccessors(klass, id, prop = None):
prop.get('acquisition_sync_value',0),
storage_id = prop.get('storage_id'),
alt_accessor_id = prop.get('alt_accessor_id'),
is_list_type = (prop['type'] in list_types or prop.get('multivalued', 0))
is_list_type = (prop['type'] in list_types or prop.get('multivalued', 0)),
is_tales_type = (prop['type'] == 'tales')
)
if not hasattr(klass, accessor_name) or prop.get('override',0):
setattr(klass, accessor_name, base_accessor)
......@@ -1135,6 +1138,7 @@ def createDefaultAccessors(klass, id, prop = None):
storage_id = prop.get('storage_id'),
alt_accessor_id = prop.get('alt_accessor_id'),
is_list_type = (prop['type'] in list_types or prop.get('multivalued', 0)),
is_tales_type = (prop['type'] == 'tales'),
reindex = 1
)
if not hasattr(klass, accessor_name) or prop.get('override',0):
......@@ -1642,7 +1646,7 @@ def createDefaultAccessors(klass, id, prop = None):
tester_name = '_baseHas' + UpperCase(id)
if not hasattr(BaseClass, tester_name):
setattr(BaseClass, tester_name, tester)
if prop['type'] == 'object':
elif prop['type'] == 'object':
tester_name = 'has' + UpperCase(id)
tester = Object.Tester(tester_name, id, prop['type'],
storage_id = prop.get('storage_id'))
......
......@@ -65,9 +65,15 @@ ObjectManager._importObjectFromFile=PatchedObjectManager._importObjectFromFile
# Properties
from OFS.PropertyManager import PropertyManager, type_converters
from OFS.PropertyManager import escape
from Globals import DTMLFile
from Products.ERP5Type.Utils import createExpressionContext
from Products.CMFCore.Expression import Expression
class ERP5PropertyManager(PropertyManager):
manage_propertiesForm=DTMLFile('dtml/properties', globals(),
property_extensible_schema__=1)
def _updateProperty(self, id, value):
# Update the value of an existing property. If value
# is a string, an attempt will be made to convert
......@@ -89,6 +95,19 @@ class ERP5PropertyManager(PropertyManager):
return 1
return 0
def getProperty(self, id, d=None, evaluate=1):
"""Get the property 'id', returning the optional second
argument or None if no such property is found."""
type = self.getPropertyType(id)
if evaluate and type == 'tales':
value = getattr(self, id)
expression = Expression(value)
econtext = createExpressionContext(self)
return expression(econtext)
elif type:
return getattr(self, id)
return d
def getPropertyType(self, id):
"""Get the type of property 'id', returning None if no
such property exists"""
......@@ -167,6 +186,18 @@ class ERP5PropertyManager(PropertyManager):
dict[p['id']]=p
return dict
def manage_addProperty(self, id, value, type, REQUEST=None):
"""Add a new property via the web. Sets a new property with
the given id, type, and value."""
if type_converters.has_key(type):
value=type_converters[type](value)
LOG('manage_addProperty', 0, 'id = %r, value = %r, type = %r, REQUEST = %r' % (id, value, type, REQUEST))
self._setProperty(id.strip(), value, type)
if REQUEST is not None:
return self.manage_propertiesForm(self, REQUEST)
PropertyManager.manage_addProperty = ERP5PropertyManager.manage_addProperty
PropertyManager.manage_propertiesForm = ERP5PropertyManager.manage_propertiesForm
PropertyManager._updateProperty = ERP5PropertyManager._updateProperty
PropertyManager.getPropertyType = ERP5PropertyManager.getPropertyType
PropertyManager._setProperty = ERP5PropertyManager._setProperty
......@@ -177,7 +208,11 @@ PropertyManager.propertyItems = ERP5PropertyManager.propertyItems
PropertyManager._propertyMap = ERP5PropertyManager._propertyMap
PropertyManager.propdict = ERP5PropertyManager.propdict
PropertyManager.hasProperty = ERP5PropertyManager.hasProperty
PropertyManager.getProperty = ERP5PropertyManager.getProperty
from ZPublisher.Converters import type_converters, field2string
type_converters['tales'] = field2string
##############################################################################
# XML content of zsql methods
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment