Commit a542a9ca authored by Yoshinori Okuji's avatar Yoshinori Okuji

Split ZopePatch into small parts.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@4461 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 36ec4a21
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# Copyright (c) 2002,2005 Nexedi SARL and Contributors. All Rights Reserved.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# Stribger repair of BTreeFolder2
import sys
from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2Base
from Acquisition import aq_base
from BTrees.OOBTree import OOBTree
from BTrees.OIBTree import OIBTree, union
from BTrees.Length import Length
from OFS.ObjectManager import BadRequestException, BeforeDeleteException
from Products.ZCatalog.Lazy import LazyMap
from zLOG import LOG
class ERP5BTreeFolder2Base(BTreeFolder2Base):
"""
This class is only for backward compatibility.
"""
pass
def _cleanup(self):
"""Cleans up errors in the BTrees.
Certain ZODB bugs have caused BTrees to become slightly insane.
Fortunately, there is a way to clean up damaged BTrees that
always seems to work: make a new BTree containing the items()
of the old one.
Returns 1 if no damage was detected, or 0 if damage was
detected and fixed.
"""
from BTrees.check import check
path = '/'.join(self.getPhysicalPath())
try:
check(self._tree)
for key in self._tree.keys():
if not self._tree.has_key(key):
raise AssertionError(
"Missing value for key: %s" % repr(key))
check(self._mt_index)
for key, object in self._tree.items():
meta_type = getattr(object, 'meta_type', None)
if meta_type is not None:
if not self._mt_index.has_key(meta_type):
raise AssertionError(
"Missing meta_type index for key: %s" % repr(key))
for key, value in self._mt_index.items():
if (not self._mt_index.has_key(key)
or self._mt_index[key] is not value):
raise AssertionError(
"Missing or incorrect meta_type index: %s"
% repr(key))
check(value)
for k in value.keys():
if not value.has_key(k) or not self._tree.has_key(k):
raise AssertionError(
"Missing values for meta_type index: %s"
% repr(key))
return 1
except (AssertionError, KeyError):
LOG('BTreeFolder2', WARNING,
'Detected damage to %s. Fixing now.' % path,
error=sys.exc_info())
try:
self._tree = OOBTree(self._tree)
mt_index = OOBTree()
for id, object in self._tree.items():
# Update the meta type index.
meta_type = getattr(object, 'meta_type', None)
if meta_type is not None:
ids = mt_index.get(meta_type, None)
if ids is None:
ids = OIBTree()
mt_index[meta_type] = ids
ids[id] = 1
#LOG('Added All Object in BTree mti',0, map(lambda x:str(x), mt_index.keys()))
self._mt_index = OOBTree(mt_index)
except:
LOG('BTreeFolder2', ERROR, 'Failed to fix %s.' % path,
error=sys.exc_info())
raise
else:
LOG('BTreeFolder2', INFO, 'Fixed %s.' % path)
return 0
BTreeFolder2Base._cleanup = _cleanup
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2002,2005 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# ZPublisher should drop requests without a good http referer
from ZPublisher.BaseRequest import BaseRequest
import AccessControl
BaseRequest.erp5_old_traverse = BaseRequest.traverse
def erp5_new_traverse(request, path, response=None, validated_hook=None):
if response is None: response=request.response
object = BaseRequest.erp5_old_traverse(request, path, response=response, validated_hook=validated_hook)
http_url = request.get('ACTUAL_URL', '').strip()
http_referer = request.get('HTTP_REFERER', '').strip()
security_manager = AccessControl.getSecurityManager()
user = security_manager.getUser()
user_roles = user.getRolesInContext(object)
# Manager can do anything
if 'Manager' in user_roles:
return object
# are we within a portal ?
try:
context = getattr(object, 'im_self', None)
if context is not None:
try:
portal_object = context.getPortalObject()
except AttributeError:
portal_object = object.getPortalObject()
else :
portal_object = object.getPortalObject()
except AttributeError:
pass
else:
if not getattr(portal_object, 'require_referer', 0):
return object
portal_url = portal_object.absolute_url()
if http_referer != '':
# if HTTP_REFERER is set, user can acces the object if referer is ok
if http_referer.startswith(portal_url):
return object
else:
LOG('HTTP_REFERER_CHECK : BAD REFERER !', 0, 'request : "%s", referer : "%s"' % (http_url, referer))
response.unauthorized()
else:
# no HTTP_REFERER, we only allow to reach portal_url
for i in ('/', '/index_html', '/login_form', '/view'):
if http_url.endswith(i):
http_url = http_url[:-len(i)]
break
if len(http_url) == 0 or not portal_url.startswith(http_url):
LOG('HTTP_REFERER_CHECK : NO REFERER !', 0, 'request : "%s"' % http_url)
response.unauthorized()
return object
BaseRequest.traverse = erp5_new_traverse
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2002,2005 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# CMFCatalogAware patch for accepting arbitrary parameters.
from Products.CMFCore.CMFCatalogAware import CMFCatalogAware
from Acquisition import aq_base
from Products.CMFCore.utils import getToolByName
def reindexObject(self, idxs=[], *args, **kw):
"""
Reindex the object in the portal catalog.
If idxs is present, only those indexes are reindexed.
The metadata is always updated.
Also update the modification date of the object,
unless specific indexes were requested.
"""
if idxs == []:
# Update the modification date.
if hasattr(aq_base(self), 'notifyModified'):
self.notifyModified()
catalog = getToolByName(self, 'portal_catalog', None)
if catalog is not None:
catalog.reindexObject(self, idxs=idxs, *args, **kw)
CMFCatalogAware.reindexObject = reindexObject
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2002,2005 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# XML content of zsql methods
import re
try: from IOBTree import Bucket
except: Bucket=lambda:{}
from Shared.DC.ZRDB.Aqueduct import decodestring, parse
from Shared.DC.ZRDB.DA import DA
def DA_fromFile(self, filename):
"""
Read the file and update self
"""
f = file(filename)
s = f.read()
f.close()
self.fromText(s)
def DA_fromText(self, text):
"""
Read the string 'text' and updates self
"""
start = text.find('<dtml-comment>')
end = text.find('</dtml-comment>')
block = text[start+14:end]
parameters = {}
for line in block.split('\n'):
pair = line.split(':',1)
if len(pair)!=2:
continue
parameters[pair[0].strip().lower()]=pair[1].strip()
# check for required and optional parameters
max_rows = parameters.get('max_rows',1000)
max_cache = parameters.get('max_cache',100)
cache_time = parameters.get('cache_time',0)
class_name = parameters.get('class_name','')
class_file = parameters.get('class_file','')
title = parameters.get('title','')
connection_id = parameters.get('connection_id','')
arguments = parameters.get('arguments','')
start = text.rfind('<params>')
end = text.rfind('</params>')
arguments = text[start+8:end]
template = text[end+9:]
while template.find('\n')==0:
template=template.replace('\n','',1)
self.manage_edit(title=title, connection_id=connection_id,
arguments=arguments, template=template)
self.manage_advanced(max_rows, max_cache, cache_time, class_name, class_file)
def DA_manage_FTPget(self):
"""Get source for FTP download"""
self.REQUEST.RESPONSE.setHeader('Content-Type', 'text/plain')
return """<dtml-comment>
title:%s
connection_id:%s
max_rows:%s
max_cache:%s
cache_time:%s
class_name:%s
class_file:%s
</dtml-comment>
<params>%s</params>
%s""" % (self.title, self.connection_id,
self.max_rows_, self.max_cache_, self.cache_time_,
self.class_name_, self.class_file_,
self.arguments_src, self.src)
# This function doesn't take care about properties by default
def DA_PUT(self, REQUEST, RESPONSE):
"""Handle put requests"""
if RESPONSE is not None: self.dav__init(REQUEST, RESPONSE)
if RESPONSE is not None: self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
body = REQUEST.get('BODY', '')
m = re.match('\s*<dtml-comment>(.*?)</dtml-comment>\s*\n', body, re.I | re.S)
if m:
property_src = m.group(1)
parameters = {}
for line in property_src.split('\n'):
pair = line.split(':',1)
if len(pair)!=2:
continue
parameters[pair[0].strip().lower()]=pair[1].strip()
# check for required and optional parameters
max_rows = parameters.get('max_rows',1000)
max_cache = parameters.get('max_cache',100)
cache_time = parameters.get('cache_time',0)
class_name = parameters.get('class_name','')
class_file = parameters.get('class_file','')
title = parameters.get('title','')
connection_id = parameters.get('connection_id','')
self.manage_advanced(max_rows, max_cache, cache_time, class_name, class_file)
self.title = str(title)
self.connection_id = str(connection_id)
body = body[m.end():]
m = re.match('\s*<params>(.*)</params>\s*\n', body, re.I | re.S)
if m:
self.arguments_src = m.group(1)
self._arg=parse(self.arguments_src)
body = body[m.end():]
template = body
self.src = template
self.template=t=self.template_class(template)
t.cook()
self._v_cache={}, Bucket()
if RESPONSE is not None: RESPONSE.setStatus(204)
return RESPONSE
DA.fromFile = DA_fromFile
DA.fromText = DA_fromText
DA.manage_FTPget = DA_manage_FTPget
DA.PUT = DA_PUT
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2002,2005 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# Expression patch
from Products.CMFCore.Expression import Expression
def Expression_hash(self):
return hash(self.text)
Expression.__hash__ = Expression_hash
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2002,2005 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# Folder naming: member folder should be names as a singular in small caps
from Products.CMFDefault.MembershipTool import MembershipTool
MembershipTool.membersfolder_id = 'member'
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2002,2005 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# Import: add rename feature
from OFS.ObjectManager import ObjectManager, customImporters
def ObjectManager_importObjectFromFile(self, filepath, verify=1, set_owner=1, id=None):
#LOG('_importObjectFromFile, filepath',0,filepath)
# locate a valid connection
connection=self._p_jar
obj=self
while connection is None:
obj=obj.aq_parent
connection=obj._p_jar
ob=connection.importFile(
filepath, customImporters=customImporters)
if verify: self._verifyObjectPaste(ob, validate_src=0)
if id is None:
id=ob.id
if hasattr(id, 'im_func'): id=id()
self._setObject(id, ob, set_owner=set_owner)
# try to make ownership implicit if possible in the context
# that the object was imported into.
ob=self._getOb(id)
ob.manage_changeOwnershipType(explicit=0)
ObjectManager._importObjectFromFile=ObjectManager_importObjectFromFile
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2002,2005 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# AttrDict patch for more dict-like methods.
try:
from App.ProductContext import AttrDict
def AttrDict_getitem(self, name):
try:
return getattr(self.ob, name)
except AttributeError:
raise KeyError
def AttrDict_has_key(self, name):
return hasattr(self.ob, name)
AttrDict.__getitem__ = AttrDict_getitem
AttrDict.has_key = AttrDict_has_key
except ImportError:
pass
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights
# Reserved.
# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this
# distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
# Locale roles acquisition patch for PAS
from Acquisition import aq_inner, aq_parent
try:
from PluggableAuthService.PropertiedUser import PropertiedUser
except ImportError:
PropertiedUser = None
def getRolesInContext( self, object ):
""" Return the list of roles assigned to the user.
o Include local roles assigned in context of the passed-in object.
o Include *both* local roles assigned directly to us *and* those
assigned to our groups.
o Ripped off from AccessControl.User.BasicUser, which provides
no other extension mechanism. :(
"""
user_id = self.getId()
# [ x.getId() for x in self.getGroups() ]
group_ids = self.getGroups()
principal_ids = list( group_ids )
principal_ids.insert( 0, user_id )
local ={}
object = aq_inner( object )
while 1:
local_roles = getattr( object, '__ac_local_roles__', None )
if local_roles:
if callable( local_roles ):
local_roles = local_roles()
dict = local_roles or {}
for principal_id in principal_ids:
for role in dict.get( principal_id, [] ):
local[ role ] = 1
# patch by Klaus for LocalRole blocking
if hasattr(object, '_getAcquireLocalRoles'):
if not object._getAcquireLocalRoles():
break
inner = aq_inner( object )
parent = aq_parent( inner )
if parent is not None:
object = parent
continue
new = getattr( object, 'im_self', None )
if new is not None:
object = aq_inner( new )
continue
break
return list( self.getRoles() ) + local.keys()
def allowed( self, object, object_roles=None ):
""" Check whether the user has access to object.
o The user must have one of the roles in object_roles to allow access.
o Include *both* local roles assigned directly to us *and* those
assigned to our groups.
o Ripped off from AccessControl.User.BasicUser, which provides
no other extension mechanism. :(
"""
if object_roles is _what_not_even_god_should_do:
return 0
# Short-circuit the common case of anonymous access.
if object_roles is None or 'Anonymous' in object_roles:
return 1
# Provide short-cut access if object is protected by 'Authenticated'
# role and user is not nobody
if 'Authenticated' in object_roles and (
self.getUserName() != 'Anonymous User'):
return 1
# Check for ancient role data up front, convert if found.
# This should almost never happen, and should probably be
# deprecated at some point.
if 'Shared' in object_roles:
object_roles = self._shared_roles(object)
if object_roles is None or 'Anonymous' in object_roles:
return 1
# Check for a role match with the normal roles given to
# the user, then with local roles only if necessary. We
# want to avoid as much overhead as possible.
user_roles = self.getRoles()
for role in object_roles:
if role in user_roles:
if self._check_context(object):
return 1
return None
# Still have not found a match, so check local roles. We do
# this manually rather than call getRolesInContext so that
# we can incur only the overhead required to find a match.
inner_obj = aq_inner( object )
user_id = self.getId()
# [ x.getId() for x in self.getGroups() ]
group_ids = self.getGroups()
principal_ids = list( group_ids )
principal_ids.insert( 0, user_id )
while 1:
local_roles = getattr( inner_obj, '__ac_local_roles__', None )
if local_roles:
if callable( local_roles ):
local_roles = local_roles()
dict = local_roles or {}
for principal_id in principal_ids:
local_roles = dict.get( principal_id, [] )
for role in object_roles:
if role in local_roles:
if self._check_context( object ):
return 1
return 0
# patch by Klaus for LocalRole blocking
if hasattr(object, '_getAcquireLocalRoles'):
if not object._getAcquireLocalRoles():
break
inner = aq_inner( inner_obj )
parent = aq_parent( inner )
if parent is not None:
inner_obj = parent
continue
new = getattr( inner_obj, 'im_self', None )
if new is not None:
inner_obj = aq_inner( new )
continue
break
return None
if PropertiedUser is not None:
PropertiedUser.getRolesInContext = getRolesInContext
PropertiedUser.allowed = allowed
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2002,2005 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# 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.ERP5Type.ERP5Type import ERP5TypeInformation
from Products.CMFCore.Expression import Expression
from Products.ERP5Type import _dtmldir
class ERP5PropertyManager(PropertyManager):
"""
This class is only for backward compatibility.
"""
pass
PropertyManager_manage_propertiesForm=DTMLFile('properties',
_dtmldir,
property_extensible_schema__=1)
def PropertyManager_updateProperty(self, id, value):
# Update the value of an existing property. If value
# is a string, an attempt will be made to convert
# the value to the type of the existing property.
self._wrapperCheck(value)
if not hasattr(self, 'isRADContent'):
if not self.hasProperty(id):
raise 'Bad Request', 'The property %s does not exist' % escape(id)
if type(value)==type(''):
proptype=self.getPropertyType(id) or 'string'
if type_converters.has_key(proptype):
value=type_converters[proptype](value)
#LOG('_updateProperty', 0, 'self = %r, id = %r, value = %r' % (self, id, value))
self._setPropValue(id, value)
def PropertyManager_hasProperty(self, id):
"""Return true if object has a property 'id'"""
for p in self.propertyIds():
if id==p:
return 1
return 0
def PropertyManager_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 PropertyManager_getPropertyType(self, id):
"""Get the type of property 'id', returning None if no
such property exists"""
for md in self._propertyMap():
if md['id']==id:
return md.get('type', 'string')
return None
def PropertyManager_setProperty(self, id, value, type=None):
# for selection and multiple selection properties
# the value argument indicates the select variable
# of the property
if type is None:
# Generate a default type
value_type = type(value)
if value_type in (type([]), type(())):
type = 'lines'
elif value_type is type(1):
type = 'int'
elif value_type is type(1L):
type = 'long'
elif value_type is type(1.0):
type = 'float'
elif value_type is type('a'):
if len(value_type.split('\n')) > 1:
type = 'text'
else:
type = 'string'
else:
type = 'string'
self._wrapperCheck(value)
if not self.valid_property_id(id):
raise 'Bad Request', 'Invalid or duplicate property id'
if type in ('selection', 'multiple selection'):
if not hasattr(self, value):
raise 'Bad Request', 'No select variable %s' % value
self._local_properties=getattr(self, '_local_properties', ()) + (
{'id':id, 'type':type, 'select_variable':value},)
if type=='selection':
self._setPropValue(id, '')
else:
self._setPropValue(id, [])
else:
self._local_properties=getattr(self, '_local_properties', ())+({'id':id,'type':type},)
self._setPropValue(id, value)
def PropertyManager_delProperty(self, id):
if not self.hasProperty(id):
raise ValueError, 'The property %s does not exist' % escape(id)
self._delPropValue(id)
self._local_properties=tuple(filter(lambda i, n=id: i['id'] != n,
getattr(self, '_local_properties', ())))
def PropertyManager_propertyIds(self):
"""Return a list of property ids """
return map(lambda i: i['id'], self._propertyMap())
def PropertyManager_propertyValues(self):
"""Return a list of actual property objects """
return map(lambda i,s=self: getattr(s,i['id']), self._propertyMap())
def PropertyManager_propertyItems(self):
"""Return a list of (id,property) tuples """
return map(lambda i,s=self: (i['id'],getattr(s,i['id'])), self._propertyMap())
def PropertyManager_propertyMap(self):
"""Return a tuple of mappings, giving meta-data for properties """
return tuple(list(self._properties) + list(getattr(self, '_local_properties', ())))
def PropertyManager_propdict(self):
dict={}
for p in self._propertyMap():
dict[p['id']]=p
return dict
def PropertyManager_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 = PropertyManager_manage_addProperty
PropertyManager.manage_propertiesForm = PropertyManager_manage_propertiesForm
PropertyManager._updateProperty = PropertyManager_updateProperty
PropertyManager.getPropertyType = PropertyManager_getPropertyType
PropertyManager._setProperty = PropertyManager_setProperty
PropertyManager._delProperty = PropertyManager_delProperty
PropertyManager.propertyIds = PropertyManager_propertyIds
PropertyManager.propertyValues = PropertyManager_propertyValues
PropertyManager.propertyItems = PropertyManager_propertyItems
PropertyManager._propertyMap = PropertyManager_propertyMap
PropertyManager.propdict = PropertyManager_propdict
PropertyManager.hasProperty = PropertyManager_hasProperty
PropertyManager.getProperty = PropertyManager_getProperty
ERP5TypeInformation.manage_propertiesForm = PropertyManager_manage_propertiesForm
from ZPublisher.Converters import type_converters, field2string
type_converters['tales'] = field2string
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# State types patch for DCWorkflow
from Products.DCWorkflow.States import StateDefinition
from Globals import DTMLFile
from Products.ERP5Type import _dtmldir
_properties_form = DTMLFile('state_properties', _dtmldir)
def getAvailableTypeList(self):
"""This is a method specific to ERP5. This returns a list of state types, which are used for portal methods.
"""
return ('current_inventory', 'reserved_inventory', 'future_inventory',
'draft_order', 'planned_order', )
def setProperties(self, title='', transitions=(), REQUEST=None, description='', type_list=()):
'''
'''
self.title = str(title)
self.description = str(description)
self.transitions = tuple(map(str, transitions))
# This is patched by yo.
self.type_list = tuple(type_list)
if REQUEST is not None:
return self.manage_properties(REQUEST, 'Properties changed.')
StateDefinition._properties_form = _properties_form
StateDefinition.getAvailableTypeList = getAvailableTypeList
StateDefinition.setProperties = setProperties
StateDefinition.type_list = ()
\ No newline at end of file
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# Copyright (c) 2002,2005 Nexedi SARL and Contributors. All Rights Reserved.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# Adding commit_prepare to the zodb transaction
try:
from ZODB import Transaction, POSException
hosed = Transaction.hosed
free_transaction = Transaction.free_transaction
jar_cmp = Transaction.jar_cmp
def commit(self, subtransaction=None):
"""Finalize the transaction."""
objects = self._objects
subjars = []
if subtransaction:
if self._sub is None:
# Must store state across multiple subtransactions
# so that the final commit can commit all subjars.
self._sub = {}
else:
if self._sub is not None:
# This commit is for a top-level transaction that
# has previously committed subtransactions. Do
# one last subtransaction commit to clear out the
# current objects, then commit all the subjars.
if objects:
self.commit(1)
objects = []
subjars = self._sub.values()
subjars.sort(jar_cmp)
self._sub = None
# If there were any non-subtransaction-aware jars
# involved in earlier subtransaction commits, we need
# to add them to the list of jars to commit.
if self._non_st_objects is not None:
objects.extend(self._non_st_objects)
self._non_st_objects = None
if (objects or subjars) and hosed:
# Something really bad happened and we don't
# trust the system state.
raise POSException.TransactionError, hosed_msg
# It's important that:
#
# - Every object in self._objects is either committed or
# aborted.
#
# - For each object that is committed we call tpc_begin on
# it's jar at least once
#
# - For every jar for which we've called tpc_begin on, we
# either call tpc_abort or tpc_finish. It is OK to call
# these multiple times, as the storage is required to ignore
# these calls if tpc_begin has not been called.
#
# - That we call tpc_begin() in a globally consistent order,
# so that concurrent transactions involving multiple storages
# do not deadlock.
try:
ncommitted = 0
# Do prepare until number of jars is stable - this could
# create infinite loop
jars_len = -1
jars = self._get_jars(objects, subtransaction)
objects_len = len(self._objects)
while len(jars) != jars_len:
jars_len = len(jars)
self._commit_prepare(jars, subjars, subtransaction)
if len(self._objects) != objects_len:
objects.extend(self._objects[objects_len:])
objects_len = len(self._objects)
jars = self._get_jars(objects, subtransaction)
try:
# If not subtransaction, then jars will be modified.
self._commit_begin(jars, subjars, subtransaction)
ncommitted += self._commit_objects(objects)
if not subtransaction:
# Unless this is a really old jar that doesn't
# implement tpc_vote(), it must raise an exception
# if it can't commit the transaction.
for jar in jars:
try:
vote = jar.tpc_vote
except AttributeError:
pass
else:
vote(self)
# Handle multiple jars separately. If there are
# multiple jars and one fails during the finish, we
# mark this transaction manager as hosed.
if len(jars) == 1:
self._finish_one(jars[0])
else:
self._finish_many(jars)
except:
# Ugh, we got an got an error during commit, so we
# have to clean up. First save the original exception
# in case the cleanup process causes another
# exception.
error = sys.exc_info()
try:
self._commit_error(objects, ncommitted, jars, subjars)
except:
LOG('ZODB', ERROR,
"A storage error occured during transaction "
"abort. This shouldn't happen.",
error=error)
raise error[0], error[1], error[2]
finally:
del objects[:] # clear registered
if not subtransaction and self._id is not None:
free_transaction()
def _commit_prepare(self, jars, subjars, subtransaction):
if subtransaction:
assert not subjars
for jar in jars:
try:
jar.tpc_prepare(self, subtransaction)
except TypeError:
# Assume that TypeError means that tpc_begin() only
# takes one argument, and that the jar doesn't
# support subtransactions.
jar.tpc_prepare(self)
except AttributeError:
# Assume that KeyError means that tpc_prepare
# not available
pass
else:
# Merge in all the jars used by one of the subtransactions.
# When the top-level subtransaction commits, the tm must
# call commit_sub() for each jar involved in one of the
# subtransactions. The commit_sub() method should call
# tpc_begin() on the storage object.
# It must also call tpc_begin() on jars that were used in
# a subtransaction but don't support subtransactions.
# These operations must be performed on the jars in order.
# Modify jars inplace to include the subjars, too.
jars += subjars
jars.sort(jar_cmp)
# assume that subjars is small, so that it's cheaper to test
# whether jar in subjars than to make a dict and do has_key.
for jar in jars:
#if jar in subjars:
# pass
#else:
try:
jar.tpc_prepare(self)
except AttributeError:
# Assume that KeyError means that tpc_prepare
# not available
pass
Transaction.Transaction.commit = commit
Transaction.Transaction._commit_prepare = _commit_prepare
except ImportError:
# On Zope 2.8, do not patch Transaction. Instead, we use a before commit hook.
pass
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2002,2005 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# Make sure Interaction Workflows are called even if method not wrapped
from Products.CMFCore.WorkflowTool import WorkflowTool
from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition
def WorkflowTool_wrapWorkflowMethod(self, ob, method_id, func, args, kw):
""" To be invoked only by WorkflowCore.
Allows a workflow definition to wrap a WorkflowMethod.
By default, the workflow tool takes the first workflow wich
support the method_id. In ERP5, with Interaction Worfklows, we
may have many workflows wich can support a worfklow method,
that's why we need this patch
We should have 1 or 0 classic workflow (ie a DCWorkflow), and
0 or many Interaction workflows. We should take care that the
method will be called once
"""
# Check workflow containing the workflow method
wf_list = []
wfs = self.getWorkflowsFor(ob)
if wfs:
for w in wfs:
# LOG('ERP5WorkflowTool.wrapWorkflowMethod, is wfMSupported', 0,
# repr((w.isWorkflowMethodSupported(ob, method_id),
# w.getId(), ob, method_id )))
if (hasattr(w, 'isWorkflowMethodSupported')
and w.isWorkflowMethodSupported(ob, method_id)):
#wf = w
#break
wf_list.append(w)
else:
wfs = ()
# If no transition matched, simply call the method
# And return
if len(wf_list)==0:
return apply(func, args, kw)
# Call notifyBefore on each workflow
for w in wfs:
w.notifyBefore(ob, method_id, args=args, kw=kw)
# Call the method on matching workflows
only_interaction_defined = 1
for w in wf_list:
if w.__class__.__name__ != 'InteractionWorkflowDefinition':
only_interaction_defined = 0
result = self._invokeWithNotification(
[], ob, method_id, w.wrapWorkflowMethod,
(ob, method_id, func, args, kw), {})
# If only interaction workflows are defined, we need to call the method
# manually
if only_interaction_defined:
result = apply(func, args, kw)
# Call notifySuccess on each workflow
for w in wfs:
w.notifySuccess(ob, method_id, result, args=args, kw=kw)
return result
WorkflowTool.wrapWorkflowMethod = WorkflowTool_wrapWorkflowMethod
def DCWorkflowDefinition_notifyBefore(self, ob, action, args=None, kw=None):
'''
Notifies this workflow of an action before it happens,
allowing veto by exception. Unless an exception is thrown, either
a notifySuccess() or notifyException() can be expected later on.
The action usually corresponds to a method name.
'''
pass
def DCWorkflowDefinition_notifySuccess(self, ob, action, result, args=None, kw=None):
'''
Notifies this workflow that an action has taken place.
'''
pass
DCWorkflowDefinition.notifyBefore = DCWorkflowDefinition_notifyBefore
DCWorkflowDefinition.notifySuccess = DCWorkflowDefinition_notifySuccess
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2002,2005 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# Make sure the xml export will be ordered
from Shared.DC.xml import ppml
from base64 import encodestring
from cStringIO import StringIO
try:
from ZODB.serialize import referencesf
except ImportError:
from ZODB.referencesf import referencesf
from ZODB.ExportImport import TemporaryFile
from pickle import Pickler, EMPTY_DICT, MARK, DICT
from cPickle import loads, dumps
from types import *
from OFS import XMLExportImport
# Jython has PyStringMap; it's a dict subclass with string keys
try:
from org.python.core import PyStringMap
except ImportError:
PyStringMap = None
# Ordered pickles
class OrderedPickler(Pickler):
dispatch = Pickler.dispatch.copy()
def save_dict(self, obj):
write = self.write
if self.bin:
write(EMPTY_DICT)
else: # proto 0 -- can't use EMPTY_DICT
write(MARK + DICT)
self.memoize(obj)
item_list = obj.items() # New version by JPS for sorting
item_list.sort(lambda a, b: cmp(a[0], b[0])) # New version by JPS for sorting
self._batch_setitems(item_list.__iter__())
dispatch[DictionaryType] = save_dict
if not PyStringMap is None:
dispatch[PyStringMap] = save_dict
def reorderPickle(jar, p):
from ZODB.ExportImport import Ghost, Unpickler, Pickler, StringIO, persistent_id
oids = {}
storage = jar._storage
new_oid = storage.new_oid
store = storage.store
def persistent_load(ooid,
Ghost=Ghost,
oids=oids, wrote_oid=oids.has_key,
new_oid=storage.new_oid):
"Remap a persistent id to an existing ID and create a ghost for it."
if type(ooid) is TupleType: ooid, klass = ooid
else: klass=None
try:
Ghost=Ghost()
Ghost.oid=ooid
except TypeError:
Ghost=Ghost(ooid)
return Ghost
# Reorder pickle by doing I/O
pfile = StringIO(p)
unpickler=Unpickler(pfile)
unpickler.persistent_load=persistent_load
newp=StringIO()
pickler=OrderedPickler(newp,1)
pickler.persistent_id=persistent_id
pickler.dump(unpickler.load())
obj = unpickler.load()
pickler.dump(obj)
p=newp.getvalue()
return obj, p
def XMLrecord(oid, plen, p, id_mapping):
# Proceed as usual
q=ppml.ToXMLUnpickler
f=StringIO(p)
u=q(f)
id=ppml.u64(oid)
id = id_mapping[id]
old_aka = encodestring(oid)[:-1]
aka=encodestring(ppml.p64(long(id)))[:-1] # Rebuild oid based on mapped id
id_mapping.setConvertedAka(old_aka, aka)
u.idprefix=str(id)+'.'
p=u.load(id_mapping=id_mapping).__str__(4)
if f.tell() < plen:
p=p+u.load(id_mapping=id_mapping).__str__(4)
String=' <record id="%s" aka="%s">\n%s </record>\n' % (id, aka, p)
return String
XMLExportImport.XMLrecord = XMLrecord
def exportXML(jar, oid, file=None):
if file is None: file=TemporaryFile()
elif type(file) is StringType: file=open(file,'w+b')
id_mapping = ppml.MinimalMapping()
#id_mapping = ppml.IdentityMapping()
write=file.write
write('<?xml version="1.0"?>\012<ZopeData>\012')
version=jar._version
ref=referencesf
oids=[oid]
done_oids={}
done=done_oids.has_key
load=jar._storage.load
original_oid = oid
reordered_pickle = []
# Build mapping for refs
while oids:
oid=oids[0]
del oids[0]
if done(oid): continue
done_oids[oid]=1
try: p, serial = load(oid, version)
except: pass # Ick, a broken reference
else:
o, p = reorderPickle(jar, p)
reordered_pickle.append((oid, o, p))
XMLrecord(oid,len(p),p, id_mapping)
# Determine new oids added to the list after reference calculation
old_oids = tuple(oids)
ref(p, oids)
new_oids = []
for i in oids:
if i not in old_oids: new_oids.append(i)
# Sort new oids based on id of object
new_oidict = {}
for oid in new_oids:
try:
p, serial = load(oid, version)
o, p = reorderPickle(jar, p)
new_oidict[oid] = getattr(o, 'id', None)
except:
new_oidict[oid] = None # Ick, a broken reference
new_oids.sort(lambda a,b: cmp(new_oidict[a], new_oidict[b]))
# Build new sorted oids
oids = list(old_oids) + new_oids
# Do real export
for (oid, o, p) in reordered_pickle:
write(XMLrecord(oid,len(p),p, id_mapping))
write('</ZopeData>\n')
return file
XMLExportImport.exportXML = exportXML
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2002,2005 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# dtml-sqlvar patch to convert None to NULL, and deal with DateTime
from Shared.DC.ZRDB.sqlvar import SQLVar
from Shared.DC.ZRDB import sqlvar
from string import atoi,atof
from types import StringType
def SQLVar_render(self, md):
name=self.__name__
args=self.args
t=args['type']
try:
expr=self.expr
if type(expr) is type(''): v=md[expr]
else: v=expr(md)
except:
if args.has_key('optional') and args['optional']:
return 'null'
if type(expr) is not type(''):
raise
raise ValueError, 'Missing input variable, <em>%s</em>' % name
if t=='int':
try:
if type(v) is StringType:
if v[-1:]=='L':
v=v[:-1]
atoi(v)
else: v=str(int(v))
except:
if not v and args.has_key('optional') and args['optional']:
return 'null'
raise ValueError, (
'Invalid integer value for <em>%s</em>' % name)
elif t=='float':
try:
if type(v) is StringType:
if v[-1:]=='L':
v=v[:-1]
atof(v)
else: v=str(float(v))
except:
if not v and args.has_key('optional') and args['optional']:
return 'null'
raise ValueError, (
'Invalid floating-point value for <em>%s</em>' % name)
# Patched by yo
elif t=='datetime':
if v is None:
if args.has_key('optional') and args['optional']:
return 'null'
else:
raise ValueError, (
'Invalid datetime value for <em>%s</em>: %r' % (name, v))
try:
if hasattr(v, 'ISO'):
v=v.ISO()
if hasattr(v, 'strftime'):
v=v.strftime('%Y-%m-%d %H:%M:%S')
else: v=str(v)
except:
if not v and args.has_key('optional') and args['optional']:
return 'null'
raise ValueError, (
'Invalid datetime value for <em>%s</em>: %r' % (name, v))
v=md.getitem('sql_quote__',0)(v)
# End of patch
else:
# Patched by yo
if v is None:
if args.has_key('optional') and args['optional']:
return 'null'
else:
raise ValueError, (
'Invalid string value for <em>%s</em>' % name)
# End of patch
if not isinstance(v, (str, unicode)):
v=str(v)
if not v and t=='nb':
if args.has_key('optional') and args['optional']:
return 'null'
else:
raise ValueError, (
'Invalid empty string value for <em>%s</em>' % name)
v=md.getitem('sql_quote__',0)(v)
#if find(v,"\'") >= 0: v=join(split(v,"\'"),"''")
#v="'%s'" % v
return v
# Patched by yo. datetime is added.
valid_type={'int':1, 'float':1, 'string':1, 'nb': 1, 'datetime' : 1}.has_key
SQLVar.render = SQLVar_render
SQLVar.__call__ = SQLVar_render
sqlvar.valid_type = valid_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