Back to index

plone3  3.1.7
annotation.py
Go to the documentation of this file.
00001 ################################################################################
00002 #
00003 # Copyright (c) 2002-2005, Benjamin Saller <bcsaller@ideasuite.com>, and
00004 #                              the respective authors. All rights reserved.
00005 # For a list of Archetypes contributors see docs/CREDITS.txt.
00006 #
00007 # Redistribution and use in source and binary forms, with or without
00008 # modification, are permitted provided that the following conditions are met:
00009 #
00010 # * Redistributions of source code must retain the above copyright notice, this
00011 #   list of conditions and the following disclaimer.
00012 # * Redistributions in binary form must reproduce the above copyright notice,
00013 #   this list of conditions and the following disclaimer in the documentation
00014 #   and/or other materials provided with the distribution.
00015 # * Neither the name of the author nor the names of its contributors may be used
00016 #   to endorse or promote products derived from this software without specific
00017 #   prior written permission.
00018 #
00019 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
00020 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
00022 # FOR A PARTICULAR PURPOSE.
00023 #
00024 ################################################################################
00025 
00026 from Acquisition import aq_base
00027 from AccessControl import ClassSecurityInfo
00028 
00029 from Products.Archetypes.interfaces.storage import IStorage
00030 from Products.Archetypes.interfaces.layer import ILayer
00031 from Products.Archetypes.Storage import Storage
00032 from Products.Archetypes.Storage import StorageLayer
00033 from Products.Archetypes.Storage import _marker
00034 from Products.Archetypes.annotations import AT_ANN_STORAGE
00035 from Products.Archetypes.annotations import AT_MD_STORAGE
00036 from Products.Archetypes.annotations import getAnnotation
00037 from Products.Archetypes.Registry import setSecurity
00038 from Products.Archetypes.Registry import registerStorage
00039 from Products.Archetypes.utils import shasattr
00040 from Products.CMFCore.utils import getToolByName
00041 
00042 class BaseAnnotationStorage(Storage):
00043     """Stores data using annotations on the instance
00044     """
00045 
00046     __implements__ = IStorage
00047 
00048     security = ClassSecurityInfo()
00049 
00050     _key = None
00051     
00052     def __init__(self, migrate=False):
00053         self._migrate = migrate
00054         
00055     def _migration(self, name, instance, **kwargs):
00056         """Migrates data from the original storage
00057         """
00058         raise NotImplementedError
00059         
00060     def _cleanup(self, name, instance, value, **kwargs):
00061         """Clean up data in set method
00062         """
00063         raise NotImplementedError
00064 
00065     security.declarePrivate('get')
00066     def get(self, name, instance, **kwargs):
00067         ann = getAnnotation(instance)
00068         value = ann.getSubkey(self._key, subkey=name, default=_marker)
00069         if value is _marker:
00070             if self._migrate:
00071                 value = self._migration(name, instance, **kwargs)
00072             else:
00073                 raise AttributeError(name)
00074         return value
00075 
00076     security.declarePrivate('set')
00077     def set(self, name, instance, value, **kwargs):
00078         # Remove acquisition wrappers
00079         value = aq_base(value)
00080         ann = getAnnotation(instance)
00081         ann.setSubkey(self._key, value, subkey=name)
00082         if self._migrate:
00083             self._cleanup(name, instance, value, **kwargs) 
00084 
00085     security.declarePrivate('unset')
00086     def unset(self, name, instance, **kwargs):
00087         ann = getAnnotation(instance)
00088         try:
00089             ann.delSubkey(self._key, subkey=name)
00090         except KeyError:
00091             pass
00092 
00093 setSecurity(BaseAnnotationStorage)
00094 
00095 class AnnotationStorage(BaseAnnotationStorage):
00096     """Stores values as ATAnnotations on the object
00097     """
00098 
00099     _key = AT_ANN_STORAGE
00100 
00101     security = ClassSecurityInfo()
00102     
00103     def _migration(self, name, instance, **kwargs):
00104         """Migrates data from the original storage
00105         """
00106         value = getattr(aq_base(instance), name, _marker)
00107         if value is _marker:
00108                 raise AttributeError(name)
00109         delattr(instance, name) # explicit del althought set would do the job, too
00110         self.set(name, instance, value, **kwargs)
00111         return value
00112     
00113     def _cleanup(self, name, instance, value, **kwargs):
00114         if shasattr(instance, name):
00115             delattr(instance, name)
00116 
00117 registerStorage(AnnotationStorage)
00118 
00119 class MetadataAnnotationStorage(BaseAnnotationStorage, StorageLayer):
00120     """Stores metadata as ATAnnotations on the object
00121     """
00122 
00123     _key = AT_MD_STORAGE
00124 
00125     security = ClassSecurityInfo()
00126 
00127     __implements__ = (IStorage, ILayer,)
00128     
00129     def _migration(self, name, instance, **kwargs):
00130         """Migrates data from the original storage
00131         """
00132         try:
00133             md = aq_base(instance)._md
00134             value = md[name]
00135         except KeyError, msg:
00136             # We are acting like an attribute, so
00137             # raise AttributeError instead of KeyError
00138             raise AttributeError(name, msg)
00139         self.set(name, instance, value, **kwargs)
00140         del md[name]
00141         return value
00142         
00143     def _cleanup(self, name, instance, value, **kwargs):
00144         md = aq_base(instance)._md
00145         try:
00146             del md[name]
00147         except KeyError:
00148             pass
00149 
00150     security.declarePrivate('initializeInstance')
00151     def initializeInstance(self, instance, item=None, container=None):
00152         # annotations are initialized on first access
00153         pass
00154 
00155     security.declarePrivate('initializeField')
00156     def initializeField(self, instance, field):
00157         # Check for already existing field to avoid  the reinitialization
00158         # (which means overwriting) of an already existing field after a
00159         # copy or rename operation
00160         ann = getAnnotation(instance)
00161         if not ann.hasSubkey(self._key, subkey=field.getName()):
00162             self.set(field.getName(), instance, field.getDefault(instance))
00163 
00164     security.declarePrivate('cleanupField')
00165     def cleanupField(self, instance, field, **kwargs):
00166         # Don't clean up the field self to avoid problems with copy/rename. The
00167         # python garbarage system will clean up if needed.
00168         pass
00169 
00170     security.declarePrivate('cleanupInstance')
00171     def cleanupInstance(self, instance, item=None, container=None):
00172         # Don't clean up the instance self to avoid problems with copy/rename. The
00173         # python garbarage system will clean up if needed.
00174         pass
00175 
00176 registerStorage(MetadataAnnotationStorage)
00177 
00178 def migrateStorageOfType(portal, portal_type, schema):
00179     """Migrate storage from attribute to annotation storage
00180     
00181     portal - portal
00182     portal_type - portal type name to migrate
00183     schema - schema of the type
00184     
00185     The schema is used to detect annotation and metadata annotation stored field for
00186     migration.
00187     """
00188     catalog = getToolByName(portal, 'portal_catalog')
00189     brains = catalog(Type = portal_type)
00190     
00191     fields = [ field.getName()
00192         for field in schema.fields() 
00193         if field.storage.__class__ == AnnotationStorage
00194         ]
00195     md_fields = [ field.getName()
00196         for field in schema.fields() 
00197         if field.storage.__class__ == MetadataAnnotationStorage
00198         ]
00199     
00200     for brain in brains:
00201         obj = brain.getObject()
00202         if obj is None:
00203             continue
00204         
00205         try: state = obj._p_changed
00206         except: state = 0
00207         
00208         ann = getAnnotation(obj)
00209         clean_obj = aq_base(obj)
00210         _attr2ann(clean_obj, ann, fields)
00211         _meta2ann(clean_obj, ann, md_fields)
00212         
00213         if state is None: obj._p_deactivate()
00214 
00215 def _attr2ann(clean_obj, ann, fields):
00216     """Attribute 2 annotation
00217     """
00218     for field in fields:
00219         if not ann.hasSubkey(AT_ANN_STORAGE, field):
00220             value = getattr(clean_obj, field, _marker)
00221             if value is not _marker:
00222                 delattr(clean_obj, field)
00223                 ann.setSubkey(AT_ANN_STORAGE, value, subkey=field)
00224         else:
00225             value = getattr(clean_obj, field, _marker)
00226             if value is not _marker:
00227                 delattr(clean_obj, field)
00228     
00229 def _meta2ann(clean_obj, ann, fields):
00230     """metadata 2 annotation
00231     """
00232     md = clean_obj._md
00233     for field in fields:
00234         if not ann.hasSubkey(AT_MD_STORAGE, field):
00235             value = md.get(field, _marker)
00236             if value is not _marker:
00237                 del md[field]
00238                 ann.setSubkey(AT_MD_STORAGE, value, subkey=field)
00239         else:
00240             try:
00241                 del md[field]
00242             except KeyError:
00243                 pass