Back to index

plone3  3.1.7
nonversioned.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
00004 # 
00005 # This software is subject to the provisions of the Zope Public License,
00006 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
00007 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
00008 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00009 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
00010 # FOR A PARTICULAR PURPOSE
00011 # 
00012 ##############################################################################
00013 """Support for non-versioned data embedded in versioned objects.
00014 
00015 $Id: nonversioned.py 72373 2007-02-05 16:18:41Z tseaver $
00016 """
00017 
00018 from Acquisition import aq_base
00019 from OFS.ObjectManager import ObjectManager
00020 
00021 from IVersionControl import INonVersionedData
00022 from VersionSupport import isAVersionableResource
00023 
00024 
00025 try:
00026     # Optional support for references.
00027     from Products.References.Proxy import proxyBase
00028     from Products.References.PathReference import PathReference
00029 except ImportError:
00030     isProxyOrReference = None
00031 else:
00032     def isProxyOrReference(obj):
00033         if proxyBase(obj) is not aq_base(obj):
00034             return 1
00035         if isinstance(obj, PathReference):
00036             return 1
00037         return 0
00038 
00039 
00040 def getNonVersionedDataAdapter(obj):
00041     """Returns an INonVersionedData adapter for any object.
00042 
00043     This is a super-simplistic adapter implementation.
00044     """
00045     base = aq_base(obj)
00046     # If the object implements INonVersionedData, let it say
00047     # what its items are.
00048     if INonVersionedData.isImplementedBy(base):
00049         return obj
00050     # If the object is an ObjectManager, use the ObjectManager adapter.
00051     try:
00052         is_obj_mgr = isinstance(base, ObjectManager)
00053     except TypeError:
00054         # Python 2.1 isinstance() dislikes ExtensionClass instances.
00055         # This is an adequate workaround.
00056         pass
00057     else:
00058         if is_obj_mgr:
00059             return ObjectManagerNonVersionedDataAdapter(obj)
00060     # Otherwise use the standard adapter.
00061     return StandardNonVersionedDataAdapter(obj)
00062 
00063 
00064 def listNonVersionedObjects(obj):
00065     return getNonVersionedDataAdapter(obj).listNonVersionedObjects()
00066 
00067 def getNonVersionedData(obj):
00068     return getNonVersionedDataAdapter(obj).getNonVersionedData()
00069 
00070 def removeNonVersionedData(obj):
00071     getNonVersionedDataAdapter(obj).removeNonVersionedData()
00072 
00073 def restoreNonVersionedData(obj, dict):
00074     getNonVersionedDataAdapter(obj).restoreNonVersionedData(dict)
00075 
00076 
00077 
00078 class StandardNonVersionedDataAdapter:
00079     """Non-versioned data adapter for arbitrary things.
00080     """
00081     __implements__ = INonVersionedData
00082 
00083     def __init__(self, obj):
00084         self.obj = obj
00085         # __vc_ignore__, if set, is a tuple of attribute names to
00086         # manage independently of version control.
00087         self.attrs = getattr(obj, "__vc_ignore__", ())
00088 
00089     def listNonVersionedObjects(self):
00090         # Assume it's OK to clone all of the attributes.
00091         # They will be removed later by removeNonVersionedData.
00092         return ()
00093 
00094     def removeNonVersionedData(self):
00095         for attr in self.attrs:
00096             try:
00097                 delattr(aq_base(self.obj), attr)
00098             except (AttributeError, KeyError):
00099                 pass
00100 
00101     def getNonVersionedData(self):
00102         data = {}
00103         for attr in self.attrs:
00104             if hasattr( aq_base(self.obj), attr ):
00105                 data[attr] = aq_base(getattr(aq_base(self.obj), attr))
00106         return data
00107 
00108     def restoreNonVersionedData(self, data):
00109         for attr in self.attrs:
00110             if data.has_key(attr):
00111                 setattr(aq_base(self.obj), attr, data[attr])
00112 
00113 
00114 class ObjectManagerNonVersionedDataAdapter(StandardNonVersionedDataAdapter):
00115     """Non-versioned data adapter for object managers.
00116     """
00117     __implements__ = INonVersionedData
00118 
00119     def listNonVersionedObjects(self):
00120         contents = self.getNonVersionedData()['contents']
00121         return contents.values()
00122 
00123     def removeNonVersionedData(self):
00124         StandardNonVersionedDataAdapter.removeNonVersionedData(self)
00125         obj = self.obj
00126         removed = {}
00127         contents = self.getNonVersionedData()['contents']
00128         for name, value in contents.items():
00129             obj._delOb(name)
00130             removed[name] = 1
00131         if obj._objects:
00132             obj._objects = tuple([info for info in obj._objects
00133                                   if not removed.has_key(info['id'])])
00134 
00135     def getNonVersionedData(self):
00136         contents = {}
00137         attributes = StandardNonVersionedDataAdapter.getNonVersionedData(self)
00138         for name, value in self.obj.objectItems():
00139             if not isAVersionableResource(value):
00140                 # This object should include the state of subobjects
00141                 # that won't be versioned independently.
00142                 continue
00143             if isProxyOrReference is not None:
00144                 if isProxyOrReference(value):
00145                     # This object should include the state of
00146                     # subobjects that are references.
00147                     continue
00148             contents[name] = aq_base(value)
00149         return {'contents': contents, 'attributes': attributes}
00150 
00151     def restoreNonVersionedData(self, data):
00152         StandardNonVersionedDataAdapter.restoreNonVersionedData(
00153             self, data['attributes'])
00154         # First build "ignore", a dictionary that lists which
00155         # items were stored in the repository.
00156         # Don't restore over those.
00157         obj = self.obj
00158         ignore = {}
00159         for name in obj.objectIds():
00160             ignore[name] = 1
00161         # Restore the items of the container.
00162         for name, value in data['contents'].items():
00163             if not ignore.has_key(name):
00164                 obj._setOb(name, aq_base(value))
00165                 if not hasattr(obj, '_tree'):
00166                     # Avoid generating events, since nothing was ever really
00167                     # removed or added.
00168                     obj._objects += ({'meta_type': value.meta_type,
00169                                       'id': name},)
00170                 # If there is a _tree attribute, it's very likely
00171                 # a BTreeFolder2, which doesn't need or want the
00172                 # _objects attribute.
00173                 # XXX This is a hackish way to check for BTreeFolder2s.