Back to index

plone3  3.1.7
ModifierRegistryTool.py
Go to the documentation of this file.
00001 #########################################################################
00002 # Copyright (c) 2004, 2005 Alberto Berti, Gregoire Weber. 
00003 # All Rights Reserved.
00004 # 
00005 # This file is part of CMFEditions.
00006 # 
00007 # CMFEditions is free software; you can redistribute it and/or modify
00008 # it under the terms of the GNU General Public License as published by
00009 # the Free Software Foundation; either version 2 of the License, or
00010 # (at your option) any later version.
00011 # 
00012 # CMFEditions is distributed in the hope that it will be useful,
00013 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 # GNU General Public License for more details.
00016 # 
00017 # You should have received a copy of the GNU General Public License
00018 # along with CMFEditions; if not, write to the Free Software
00019 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00020 #########################################################################
00021 """Registry of Modifiers
00022 
00023 $Id: ModifierRegistryTool.py,v 1.17 2005/06/24 11:42:01 gregweb Exp $
00024 """
00025 __version__ = "$Revision: 1.17 $"
00026 
00027 from zope.component import getUtility
00028 from zope.interface import implements
00029 
00030 from Globals import InitializeClass
00031 from Missing import MV
00032 
00033 from Acquisition import aq_base
00034 from AccessControl import ClassSecurityInfo
00035 from OFS.OrderedFolder import OrderedFolder
00036 
00037 from Products.CMFCore.utils import UniqueObject, getToolByName
00038 
00039 from Products.CMFCore.permissions import ManagePortal
00040 
00041 from Products.CMFEditions.utilities import KwAsAttributes
00042 
00043 from Products.CMFEditions.interfaces import IPortalModifierTool
00044 from Products.CMFEditions.interfaces.IModifier import IAttributeModifier
00045 from Products.CMFEditions.interfaces.IModifier import ICloneModifier
00046 from Products.CMFEditions.interfaces.IModifier import ISaveRetrieveModifier
00047 from Products.CMFEditions.interfaces.IModifier import IModifierRegistrySet
00048 from Products.CMFEditions.interfaces.IModifier import IModifierRegistryQuery
00049 from Products.CMFEditions.interfaces.IModifier import IConditionalModifier
00050 from Products.CMFEditions.interfaces.IModifier import IConditionalTalesModifier
00051 
00052 from Products.CMFEditions import StandardModifiers
00053 from Products.CMFEditions.Modifiers import ConditionalModifier
00054 from Products.CMFEditions.Modifiers import ConditionalTalesModifier
00055 
00056 
00057 class ModifierRegistryTool(UniqueObject, OrderedFolder):
00058     __doc__ = __doc__ # copy from module
00059 
00060     __implements__ = (
00061         OrderedFolder.__implements__,   # hide underspecified interfaces :-(
00062         IAttributeModifier, ISaveRetrieveModifier, ICloneModifier,
00063         IModifierRegistrySet,
00064         IModifierRegistryQuery,
00065 #        IBulkEditableSubscriberRegistry,        # not yet implemented
00066     )
00067 
00068     implements(IPortalModifierTool)
00069 
00070     id = 'portal_modifier'
00071     alternative_id = 'portal_modifierregistry'
00072 
00073     meta_type = 'Version Data Modifier Registry'
00074     
00075     # make interfaces, exceptions and classes available through the tool
00076     interfaces = KwAsAttributes(
00077         IConditionalModifier=IConditionalModifier,
00078         IConditionalTalesModifier=IConditionalTalesModifier,
00079     )
00080     exceptions = KwAsAttributes(
00081     )
00082     classes = KwAsAttributes(
00083         ConditionalModifier=ConditionalModifier,
00084         ConditionalTalesModifier=ConditionalTalesModifier,
00085     )
00086     modules = KwAsAttributes(
00087         StandardModifiers=StandardModifiers,
00088     )
00089     
00090     security = ClassSecurityInfo()
00091     
00092     def all_meta_types(self, interfaces=None):
00093         """Allow adding of objects implementing 'IConditionalModifier' only.
00094         """
00095         if interfaces is None:
00096             interfaces = (IConditionalModifier, )
00097         return OrderedFolder.all_meta_types(self, interfaces)
00098     
00099     # be aware that the tool implements also the OrderedObjectManager API
00100     
00101     orderedFolderSetObject = OrderedFolder._setObject
00102     def _setObject(self, id, object, roles=None, user=None, set_owner=1):
00103         """Wrap condition and modifier into one object if necessary.
00104         """
00105         
00106         # wrap the object by a conditional tales modifier if it isn't one yet
00107         if not IConditionalModifier.isImplementedBy(object):
00108             object = ConditionalTalesModifier(id, object)
00109         
00110         return self.orderedFolderSetObject(id, object, roles=roles, 
00111                                            user=user, set_owner=set_owner)
00112     
00113     def _collectModifiers(self, obj, interface, reversed=False):
00114         """ Returns a list of valid modifiers
00115         """
00116         modifier_list = []
00117         portal = getToolByName(self, 'portal_url').getPortalObject()
00118         for id, o in self.objectItems():
00119             # collect objects modifier only when appropriate
00120             if IConditionalModifier.isImplementedBy(o) \
00121                and o.isApplicable(obj, portal):
00122                 mod = o.getModifier()
00123                 if interface.isImplementedBy(mod):
00124                     modifier_list.append((id, mod))
00125                 
00126         if reversed:
00127             modifier_list.reverse()
00128             
00129         return modifier_list
00130 
00131     # -------------------------------------------------------------------
00132     # methods implementing IModifier
00133     #
00134     #    From the viewpoint of a repository the ModifierRegistryTool is
00135     #    an IModifier.
00136     # -------------------------------------------------------------------
00137     
00138     security.declarePrivate('getReferencedAttributes')
00139     def getReferencedAttributes(self, obj):
00140         """See IModifier
00141         """
00142         # just loop over all objects implementing the IModifier interface.
00143         referenced_data = {}
00144         for id, mod in self._collectModifiers(obj, IAttributeModifier):
00145             # prepend the modifiers id to the attributes name
00146             template = '%s/%%s' % id
00147             for name, attrs in mod.getReferencedAttributes(obj).items():
00148                 referenced_data[template % name] = attrs
00149                 
00150         # the return value is of the format:
00151         #     {'<modifier_id>/<name>': <refrenced_data>, ...}
00152         return referenced_data
00153         
00154     security.declarePrivate('reattachReferencedAttributes')
00155     def reattachReferencedAttributes(self, obj, referenced_data):
00156         """
00157         """
00158         # the input of 'referenced_data' is of the format:
00159         #     {'<modifier_id>/<name>': <refrenced_data>, ...}
00160         # build a structure by modifier id
00161         #     {'<modifier_id>': {'<name>':< refrenced_data>, ...}, ...}
00162         data_by_modid = {}
00163         
00164         for id_name, data in referenced_data.items():
00165             id, name = id_name.split('/', 1)
00166             if not id in data_by_modid:
00167                 data_by_modid[id] = {}
00168             data_by_modid[id][name] = data
00169         
00170         # loop over modifiers in reverse
00171         if data_by_modid:
00172             for id, mod in self._collectModifiers(obj, IAttributeModifier, reversed=True):
00173                 if id in data_by_modid:
00174                     mod.reattachReferencedAttributes(obj, data_by_modid[id])
00175         
00176     security.declarePrivate('getOnCloneModifiers')
00177     def getOnCloneModifiers(self, obj):
00178         """See IModifier
00179         """
00180         # First check if there is at least one ICloneModifier to loop over.
00181         # The clone operation is much fater if there are no 'persistent_id'
00182         # hooks to call.
00183         modifiers = self._collectModifiers(obj, ICloneModifier)
00184         if not modifiers:
00185             return None
00186             
00187         # collect callbacks of all modifiers uplying one
00188         pers_id_list = []
00189         pers_id_nameByMeth = {}
00190         pers_load_byname = {}
00191         inside_orefs = []
00192         outside_orefs = []
00193         
00194         for id, m in modifiers:
00195             clone_mod = m.getOnCloneModifiers(obj)
00196             if clone_mod is not None:
00197                 pers_id_list.append(clone_mod[0])
00198                 pers_id_nameByMeth[clone_mod[0]] = id
00199                 inside_orefs.extend(clone_mod[2])
00200                 outside_orefs.extend(clone_mod[3])
00201                 pers_load_byname[id] = clone_mod[1]
00202         
00203         def persistent_id(obj):
00204             # loop over modifiers having a persistent_id callback
00205             for pers_id in pers_id_list:
00206                 pid = pers_id(obj)
00207                 if pid is not None:
00208                     # found a modifier, add the modifiers name to its pid
00209                     return "%s/%s" % (pers_id_nameByMeth[pers_id], pid)
00210         
00211         def persistent_load(named_pid):
00212             # call the right modifiers persistent_load callback
00213             obj = None
00214             name, pid = named_pid.split('/', 1)
00215             return pers_load_byname[name](pid)
00216         
00217         return persistent_id, persistent_load, inside_orefs, outside_orefs, ''
00218     
00219     security.declarePrivate('beforeSaveModifier')
00220     def beforeSaveModifier(self, obj, obj_clone):
00221         """See IModifier
00222         """
00223         inside_crefs = []
00224         outside_crefs = []
00225         metadata = {}
00226         
00227         # just loop over all modifiers
00228         for ignored_id, mod in self._collectModifiers(obj, ISaveRetrieveModifier):
00229             mdata, icrefs, ocrefs = mod.beforeSaveModifier(obj, obj_clone)
00230             inside_crefs.extend(icrefs)
00231             outside_crefs.extend(ocrefs)
00232             metadata.update(mdata)
00233         
00234         return metadata, inside_crefs, outside_crefs
00235     
00236     security.declarePrivate('afterRetrieveModifier')
00237     def afterRetrieveModifier(self, obj, repo_clone, preserve=[]):
00238         """See IModifier
00239         """
00240         # before letting the after retrieve modifiers replace 
00241         # attributes save those attributes away that may got 
00242         # overwritten but have to be preserved.
00243         _marker = []
00244         preserved = {}
00245         for key in preserve:
00246             v = getattr(repo_clone, key, MV)
00247             preserved[key] = v
00248 
00249         orig_preserved = preserved.copy()
00250         # just loop over all modifiers in reverse order
00251         refs_to_be_deleted = []
00252         attrs_handling_subobjects = []
00253         for ignored_id, mod in self._collectModifiers(obj, ISaveRetrieveModifier, reversed=True):
00254             to_be_del, attrs, preserve = mod.afterRetrieveModifier(obj, repo_clone)
00255             refs_to_be_deleted.extend(to_be_del)
00256             attrs_handling_subobjects.extend(attrs)
00257             preserved.update(preserve)
00258 
00259         # Make sure that the original preserved values override
00260         preserved.update(orig_preserved)
00261 
00262         return refs_to_be_deleted, attrs_handling_subobjects, preserved
00263 
00264     # -------------------------------------------------------------------
00265     # methods implementing IModifierRegistrySet and IModifierRegistryQuery
00266     #
00267     #    The ModifierRegistryTool is also a registry of IModifier objects.
00268     # -------------------------------------------------------------------
00269     
00270     security.declareProtected(ManagePortal, 'register')
00271     def register(self, id, modifier, pos=-1):
00272         """See IModifierRegistrySet
00273         """
00274         # add the modifier
00275         id = self._setObject(id, modifier)
00276         
00277         # move it to the specified position, unfortunately the
00278         # the ordered object manager isn't able to count the position from 
00279         # the end using negative numbers.
00280         if pos < 0:
00281             pos += max(0, len(self.objectIds()) + 1)
00282         self.moveObjectToPosition(id, pos)
00283         
00284     security.declareProtected(ManagePortal, 'unregister')
00285     def unregister(self, id):
00286         """See IModifierRegistrySet
00287         """
00288         self.manage_delObjects(ids=[id])
00289         
00290     security.declareProtected(ManagePortal, 'edit')
00291     def edit(self, id, enabled=None, condition=None):
00292         """See IModifierRegistrySet
00293         """
00294         modifier = self.get(id)
00295         if IConditionalTalesModifier.isImplementedBy(modifier):
00296             modifier.edit(enabled, condition)
00297         else:
00298             if condition:
00299                 raise NotImplementedError(
00300                     '%s does not implement conditions.' % modifier)
00301             modifier.edit(enabled)
00302     
00303     security.declareProtected(ManagePortal, 'get')
00304     def get(self, id):
00305         """See IModifierRegistryQuery
00306         """
00307         # raises the correct exception for us
00308         getattr(aq_base(self), id)
00309         return getattr(self, id)
00310     
00311     security.declareProtected(ManagePortal, 'query')
00312     def query(self, id, default=None):
00313         """See IModifierRegistryQuery
00314         """
00315         try:
00316             return self.get(id)
00317         except AttributeError:
00318             return default
00319     
00320     
00321     # -------------------------------------------------------------------
00322     # methods implementing IBulkModifierRegistry
00323     # -------------------------------------------------------------------
00324     
00325     # not yet implemented: used for building a better UI than the Folder UI
00326     # It's needed as soon as possible to be able to enable/disable the 
00327     # modifiers through the web
00328     
00329 
00330 InitializeClass(ModifierRegistryTool)