Commit bb176d50 authored by Arnaud Fontaine's avatar Arnaud Fontaine

Allow a StandardProperty to generate its accessors without relying on

Utils setDefaultProperties


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@43881 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 1ad490f7
......@@ -30,12 +30,31 @@ from AccessControl import ClassSecurityInfo
from Products.CMFCore.Expression import Expression
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type.Accessor.Base import Getter as BaseGetter
from zLOG import LOG, WARNING, INFO
from Products.ERP5Type.Accessor import Base, List, Alias, Translation
from Products.ERP5Type.Accessor.TypeDefinition import type_definition, list_types
from Products.ERP5Type.Utils import UpperCase, createExpressionContext, \
evaluateExpressionFromString
from Products.ERP5Type.id_as_reference import IdAsReferenceMixin
class StandardProperty(IdAsReferenceMixin('_property'), XMLObject):
"""
Define an Acquired Property Document for a ZODB Property Sheet
Define a Standard Property Document for a ZODB Property Sheet
A Standard Property contains the following attributes:
- reference: string
- description: string
- elementary_type: string
- storage_id: string
- multivalued: boolean (default: False)
- property_default: TALES Expression as a string
- range: boolean (default: False)
- preference: boolean (default: False)
- read_permission: string (default: Permissions.AccessContentsInformation)
- write_permission: string (default: Permissions.ModifyPortalContent)
- translatable: boolean (default: False)
- translation_domain: string
"""
meta_type = 'ERP5 Standard Property'
portal_type = 'Standard Property'
......@@ -64,7 +83,7 @@ class StandardProperty(IdAsReferenceMixin('_property'), XMLObject):
# There is no need to define the setter as this static definition of
# the getter is only meaningful for the Standard Properties defined
# within an Standard Property.
getDescription = BaseGetter('getDescription', 'description', 'string',
getDescription = Base.Getter('getDescription', 'description', 'string',
default='')
def getElementaryType(self):
......@@ -78,38 +97,41 @@ class StandardProperty(IdAsReferenceMixin('_property'), XMLObject):
return getattr(self, 'elementary_type', None)
getStorageId = BaseGetter('getStorageId', 'storage_id', 'string')
# The following getters have been defined to address bootstrap
# issues
getStorageId = Base.Getter('getStorageId', 'storage_id', 'string')
getMultivalued = BaseGetter('getMultivalued', 'multivalued', 'boolean',
getMultivalued = Base.Getter('getMultivalued', 'multivalued', 'boolean',
default=False)
# Define as a TALES expression string, use for Expression
# instanciation when exporting the property to the filesystem
# definition
getPropertyDefault = BaseGetter('getPropertyDefault', 'property_default',
getPropertyDefault = Base.Getter('getPropertyDefault', 'property_default',
'string')
getRange = BaseGetter('getRange', 'range', 'boolean', default=False)
getRange = Base.Getter('getRange', 'range', 'boolean', default=False)
getPreference = BaseGetter('getPreference', 'preference', 'boolean',
getPreference = Base.Getter('getPreference', 'preference', 'boolean',
default=False)
getReadPermission = BaseGetter(
getReadPermission = Base.Getter(
'getReadPermission', 'read_permission', 'string',
default=Permissions.AccessContentsInformation)
getWritePermission = BaseGetter('getWritePermission',
getWritePermission = Base.Getter('getWritePermission',
'write_permission',
'string',
default=Permissions.ModifyPortalContent)
getTranslatable = BaseGetter('getTranslatable', 'translatable', 'boolean',
getTranslatable = Base.Getter('getTranslatable', 'translatable', 'boolean',
default=False)
getTranslationDomain = BaseGetter('getTranslationDomain',
getTranslationDomain = Base.Getter('getTranslationDomain',
'translation_domain',
'string')
# TODO: REMOVE
@staticmethod
def _getExpressionFromString(expression_string):
"""
......@@ -120,11 +142,15 @@ class StandardProperty(IdAsReferenceMixin('_property'), XMLObject):
return Expression(expression_string)
# TODO: REMOVE
security.declareProtected(Permissions.AccessContentsInformation,
'exportToFilesystemDefinition')
def exportToFilesystemDefinition(self):
"""
Return the filesystem definition of this ZODB property
NOTE: Only meaningful for testing export of filesystem Property
Sheet to the ZODB
"""
property_default_value = self._getExpressionFromString(self.getPropertyDefault())
......@@ -143,11 +169,519 @@ class StandardProperty(IdAsReferenceMixin('_property'), XMLObject):
'translatable': self.getTranslatable(),
'translation_domain': self.getTranslationDomain()}
@classmethod
def _asPropertyMap(cls, property_dict):
"""
Return the Zope definition of this ZODB property, as used by the
PropertyManager (for the ZMI for example).
ERP5 _properties and Zope _properties are somehow different. The
id is converted to the Zope standard - we keep the original id as
base_id.
@param property_dict: ZODB property dict
@type property_dict: dict
@return: PropertyManager definition
@rtype: dict
"""
property_dict['type'] = property_dict.pop('elementary_type')
property_dict['default'] = property_dict.pop('property_default')
property_dict['id'] = property_dict.pop('reference')
# In case, this property is a list, then display it as a list
if property_dict['type'] in list_types or property_dict['multivalued']:
property_dict['base_id'] = property_dict['id']
property_dict['id'] = property_dict['id'] + '_list'
return property_dict
@staticmethod
def _applyDefinitionFormatDictOnAccessorHolder(reference,
definition_dict,
accessor_holder,
argument_list,
permission):
"""
Apply a definition dict, a format string defining the accessor
name as the key (formatted with the given reference) and the
accessor class to be used as the value, on the given accessor
holder class.
The accessor class given in the definition dict is instanciated
with the given argument list and the the accessor is protected
using the given permission (which may be either read if it is a
getter or tester, or write if it is a setter), it is then
registered on the accessor holder.
For most cases, the reference is used in the accessor name but
there are exceptions, such as translation accessors.
@param reference: Reference to be used to format accessor name
@type reference: str
@param definition_dict: Definition of accessors being created
@type definition_dict: dict
@param accessor_holder: Accessor holder to applied the accessors on
@type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType
@param argument_list: Arguments to be given to the accessor class constructor
@type argument_list: list
@param permission: Permission to be applied on the accessor
@type permission: str
"""
uppercase_reference = UpperCase(reference)
for format, klass in definition_dict.iteritems():
name = format % uppercase_reference
instance = klass(name, reference, *argument_list)
accessor_holder.registerAccessor(instance, permission)
# Public setters actually just calls the private one and then
# perform a re-indexing
if name.startswith('_set'):
instance = Alias.Reindex(name[1:], name)
accessor_holder.registerAccessor(instance, permission)
@classmethod
def _applyRangeOnAccessorHolder(cls,
property_dict,
accessor_holder,
kind,
portal):
"""
Apply range accessors.
@param property_dict: Property to generate getter for
@type property_dict: dict
@param accessor_holder: Accessor holder to applied the accessors on
@type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType
@param kind: 'min' or 'max'
@type kind: string
@param portal: Portal object
@type portal: Products.ERP5.ERP5Site.ERP5Site
"""
property_dict['reference'] = '%s_range_%s' % (property_dict['reference'],
kind)
# Override storage_id to not store the value on the same attribute
# as the "normal" accessor
property_dict['storage_id'] = None
# Set range to False to avoid infinite recursion upon
# applyDefinitionOnAccessorHolder call
property_dict['range'] = False
cls.applyDefinitionOnAccessorHolder(property_dict,
accessor_holder,
portal,
do_register=False)
_translation_language_getter_definition_dict = {
'get%s': Translation.TranslatedPropertyGetter,
'_baseGet%s': Translation.TranslatedPropertyGetter
}
_translation_language_getter_definition_dict = {
'get%s': Translation.TranslatedPropertyGetter,
'_baseGet%s': Translation.TranslatedPropertyGetter
}
_translation_language_tester_definition_dict = {
'has%s': Translation.TranslatedPropertyTester
}
_translation_language_setter_definition_dict = {
'_set%s': Translation.TranslationPropertySetter
}
@classmethod
def _applyTranslationLanguageOnAccessorHolder(cls,
property_dict,
accessor_holder,
portal):
"""
Apply translation language accessors.
@param property_dict: Property to generate getter for
@type property_dict: dict
@param accessor_holder: Accessor holder to applied the accessors on
@type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType
@param portal: Portal object
@type portal: Products.ERP5.ERP5Site.ERP5Site
"""
try:
localizer = portal._getOb('Localizer')
except AttributeError:
if not getattr(portal, '_v_bootstrapping', False):
LOG("ERP5Type.Core.StandardProperty", WARNING,
"Localizer is missing. Accessors can not be generated")
return
# Apply language-specific accessors
for language in localizer.get_languages():
translation_language_id = '%s_translated_%s' % \
(language.replace('-', '_'),
property_dict['reference'])
# Prepare accessor arguments for getters
getter_argument_list = (property_dict['reference'],
property_dict['elementary_type'],
language,
property_dict['property_default'])
cls._applyDefinitionFormatDictOnAccessorHolder(
translation_language_id,
cls._translation_language_getter_definition_dict,
accessor_holder,
getter_argument_list,
property_dict['read_permission'])
# Prepare accessor arguments for testers and setters
tester_setter_argument_list = (property_dict['reference'],
property_dict['elementary_type'],
language)
cls._applyDefinitionFormatDictOnAccessorHolder(
translation_language_id,
cls._translation_language_tester_definition_dict,
accessor_holder,
tester_setter_argument_list,
property_dict['read_permission'])
cls._applyDefinitionFormatDictOnAccessorHolder(
translation_language_id,
cls._translation_language_setter_definition_dict,
accessor_holder,
tester_setter_argument_list,
property_dict['write_permission'])
_primitive_getter_definition_dict = {
'get%s': Base.Getter,
'_baseGet%s': Base.Getter
}
_list_getter_definition_dict = {
'get%s': List.Getter,
'_baseGet%s': List.Getter,
'getDefault%s': List.DefaultGetter,
'_baseGetDefault%s': List.DefaultGetter,
'get%sList': List.ListGetter,
'_baseGet%sList': List.ListGetter,
'get%sSet': List.SetGetter,
'_baseGet%sSet': List.SetGetter
}
@classmethod
def _applyGetterDefinitionDictOnAccessorHolder(cls,
property_dict,
accessor_holder):
"""
Apply getters for the given property on the given accessor holder.
This method is overriden in AcquiredProperty for example to add
accessors specific to Acquired Properties.
@param property_dict: Property to generate getter for
@type property_dict: dict
@param accessor_holder: Accessor holder to applied the accessors on
@type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType
@see _applyDefinitionFormatDictOnAccessorHolder
"""
argument_list = (property_dict['elementary_type'],
property_dict['property_default'],
property_dict['storage_id'])
if property_dict['elementary_type'] in list_types or \
property_dict['multivalued']:
definition_dict = cls._list_getter_definition_dict
else:
definition_dict = cls._primitive_getter_definition_dict
cls._applyDefinitionFormatDictOnAccessorHolder(
property_dict['reference'], definition_dict, accessor_holder,
argument_list, property_dict['read_permission'])
_list_setter_definition_dict = {
'_set%s': List.Setter,
'_baseSet%s': List.Setter,
'_setDefault%s': List.DefaultSetter,
'_baseSetDefault%s': List.DefaultSetter,
'_set%sList': List.ListSetter,
'_baseSet%sList': List.ListSetter,
'_set%sSet': List.SetSetter,
'_baseSet%sSet': List.SetSetter
}
_primitive_setter_definition_dict = {
'_set%s': Base.Setter,
'_baseSet%s': Base.Setter
}
@classmethod
def _applySetterDefinitionDictOnAccessorHolder(cls,
property_dict,
accessor_holder):
"""
Apply setters for the given property on the given accessor holder.
@param property_dict: Property to generate getter for
@type property_dict: dict
@param accessor_holder: Accessor holder to applied the accessors on
@type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType
@see _applyGetterDefinitionDictOnAccessorHolder
"""
argument_list = (property_dict['elementary_type'],
property_dict['storage_id'])
if property_dict['elementary_type'] in list_types or \
property_dict['multivalued']:
definition_dict = cls._list_setter_definition_dict
else:
definition_dict = cls._primitive_setter_definition_dict
cls._applyDefinitionFormatDictOnAccessorHolder(
property_dict['reference'], definition_dict, accessor_holder,
argument_list, property_dict['write_permission'])
_tester_definition_dict = {
'has%s': Base.Tester,
'_baseHas%s': Base.Tester,
'has%sList': List.Tester,
'_baseHas%sList': List.Tester,
'hasDefault%s': List.Tester,
'_baseHasDefault%s': List.Tester
}
_boolean_definition_dict = {
'is%s': Base.Getter,
'_baseIs%s': Base.Getter
}
@classmethod
def _applyTesterDefinitionDictOnAccessorHolder(cls,
property_dict,
accessor_holder):
"""
Apply testers and boolean accessors for the given property on the
given accessor holder.
@param property_dict: Property to generate getter for
@type property_dict: dict
@param accessor_holder: Accessor holder to applied the accessors on
@type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType
@see _applyGetterDefinitionDictOnAccessorHolder
"""
tester_argument_list = (property_dict['elementary_type'],
property_dict['storage_id'])
cls._applyDefinitionFormatDictOnAccessorHolder(
property_dict['reference'], cls._tester_definition_dict, accessor_holder,
tester_argument_list, property_dict['read_permission'])
boolean_argument_list = (property_dict['elementary_type'],
property_dict['property_default'],
property_dict['storage_id'])
cls._applyDefinitionFormatDictOnAccessorHolder(
property_dict['reference'], cls._boolean_definition_dict, accessor_holder,
boolean_argument_list, property_dict['read_permission'])
_translated_getter_definition_dict = {
'get%s': Translation.TranslatedPropertyGetter,
'_baseGet%s': Translation.TranslatedPropertyGetter
}
security.declareProtected(Permissions.ModifyPortalContent,
'applyDefinitionOnAccessorHolder')
@classmethod
def applyDefinitionOnAccessorHolder(cls,
property_dict,
accessor_holder,
portal,
do_register=True):
"""
Apply getters, setters and testers for a list property or a
primitive property.
This class method may be called to apply a property dictionnary on
an accessor holder. While applyOnAccessorHolder is commonly used
to apply a property on an *existing* ZODB Property Sheet, this
method can be used to apply accessors from a Property not defined
in a ZODB Property Sheet.
The property dictionnary must define all the attributes listed in
the class docstring.
The TALES Expression in the given property dict are considered to
have been already evaluated, as performed through
applyOnAccessorHolder by asDict method.
@param property_dict: Property to generate getter for
@type property_dict: dict
@param accessor_holder: Accessor holder to applied the accessors on
@type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType
@param portal: Portal object
@type portal: Products.ERP5.ERP5Site.ERP5Site
@param do_register: Register the property in the Zope property map
@type do_register: bool
"""
# Some attributes are required to generate accessors, if they have
# not been set properly, then don't generate them at all for this
# Property
if property_dict['reference'] is None or \
property_dict['elementary_type'] is None or \
property_dict['elementary_type'] not in type_definition:
raise ValueError("Invalid type or reference")
# Create range accessors if relevant
if property_dict['range']:
for kind in ('min', 'max'):
cls._applyRangeOnAccessorHolder(property_dict.copy(),
accessor_holder, kind, portal)
# Create translation accessors if relevant
if property_dict['translatable']:
translated_property_dict = property_dict.copy()
if translated_property_dict['property_default'] is None:
translated_property_dict['property_default'] = ''
# Make accessors such as getTranslatedProperty
translated_reference = 'translated_' + property_dict['reference']
argument_list = (translated_property_dict['reference'],
translated_property_dict['elementary_type'],
None,
translated_property_dict['property_default'])
cls._applyDefinitionFormatDictOnAccessorHolder(
translated_reference, cls._translated_getter_definition_dict,
accessor_holder, argument_list,
translated_property_dict['read_permission'])
cls._applyTranslationLanguageOnAccessorHolder(translated_property_dict,
accessor_holder, portal)
# make accessor to translation_domain
# first create default one as a normal property
translation_domain_reference = translated_property_dict['reference'] + \
'_translation_domain'
translation_domain_property_dict = {
'reference': translation_domain_reference,
'elementary_type': 'string',
'property_default': '',
'multivalued': False,
'storage_id': None,
'range': False,
'translatable': False,
'read_permission': translated_property_dict['read_permission'],
'write_permission': translated_property_dict['write_permission']}
# This will always be a StandardProperty, so avoid calling
# super() here
StandardProperty.applyDefinitionOnAccessorHolder(
translation_domain_property_dict, accessor_holder, portal,
do_register=False)
# Then override getPropertyTranslationDomain accessor
accessor = Translation.PropertyTranslationDomainGetter(
'get' + UpperCase(translation_domain_reference),
translation_domain_reference, 'string',
property_dict['translation_domain'])
accessor_holder.registerAccessor(
accessor, translated_property_dict['read_permission'])
# After applying specific getters, setters and testers, apply
# common getters, setters and testers
cls._applyGetterDefinitionDictOnAccessorHolder(property_dict,
accessor_holder)
cls._applySetterDefinitionDictOnAccessorHolder(property_dict,
accessor_holder)
cls._applyTesterDefinitionDictOnAccessorHolder(property_dict,
accessor_holder)
# By default, register the property as a Zope property map, by
# adding it to _properties, which will be later used by
# PropertyManager
if do_register:
property_map = cls._asPropertyMap(property_dict)
if property_map:
accessor_holder._properties.append(property_map)
security.declareProtected(Permissions.AccessContentsInformation,
'asDict')
def asDict(self, expression_context=None):
"""
Convert the current property to a dict, which is then applied on
the accessor holder.
@param expression_context: Expression context for TALES Expression
@type expression_context: Products.PageTemplates.Expressions.ZopeContext
@return: The current property as a dict
@rtype: dict
"""
# If no expression context has been given, create one, meaningful
# when being called from the browser for example
if expression_context is None:
expression_context = createExpressionContext(self.getPortalObject())
property_default = evaluateExpressionFromString(expression_context,
self.getPropertyDefault())
return {'reference': self.getReference(),
'description': self.getDescription(),
'elementary_type': self.getElementaryType(),
'storage_id': self.getStorageId(),
'multivalued': self.getMultivalued(),
'property_default': property_default,
'range': self.getRange(),
'preference': self.getPreference(),
'read_permission': self.getReadPermission(),
'write_permission': self.getWritePermission(),
'translatable': self.getTranslatable(),
'translation_domain': self.getTranslationDomain()}
security.declareProtected(Permissions.ModifyPortalContent,
'applyOnAccessorHolder')
def applyOnAccessorHolder(self,
accessor_holder,
expression_context,
portal):
"""
Apply the ZODB Property to the given accessor holder
@param accessor_holder: Accessor holder to applied the accessors on
@type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType
@param expression_context: Expression context for TALES Expression
@type expression_context: Products.PageTemplates.Expressions.ZopeContext
@param portal: Portal object
@type portal: Products.ERP5.ERP5Site.ERP5Site
@see applyDefinitionOnAccessorHolder
"""
self.applyDefinitionOnAccessorHolder(self.asDict(expression_context),
accessor_holder,
portal)
@classmethod
def _convertFromFilesystemPropertyDict(cls, filesystem_property_dict):
"""
Convert a property dict coming from a Property Sheet on the
filesystem to a ZODB property dict
filesystem to a ZODB property dict.
This method is just kept for backward-compatibility with
filesystem Property Sheets used before ZODB Property Sheets.
@param filesystem_property_dict: Filesystem property definition
@type filesystem_property_dict: dict
@return: ZODB property definition
@rtype: dict
"""
# Prepare a dictionnary of the ZODB property
zodb_property_dict = {}
......@@ -173,12 +707,23 @@ class StandardProperty(IdAsReferenceMixin('_property'), XMLObject):
return zodb_property_dict
security.declareProtected(Permissions.AccessContentsInformation,
security.declareProtected(Permissions.ModifyPortalContent,
'importFromFilesystemDefinition')
@classmethod
def importFromFilesystemDefinition(cls, context, filesystem_property_dict):
"""
Set attributes from the filesystem definition of a property
Create a new property on the given context from the given
filesystem definition dict.
This method is just kept for backward-compatibility with
filesystem Property Sheets used before ZODB Property Sheets.
@param context: Context to create the property in
@type context: Products.ERP5Type.Core.PropertySheet
@param filesystem_property_dict: Filesystem definition
@param filesystem_property_dict: dict
@return: The new Standard Property
@rtype: StandardProperty
"""
return context.newContent(
portal_type=cls.portal_type,
......
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