Back to index

plone3  3.1.7
UniqueIdHandlerTool.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # Copyright (c) 2004 Zope Corporation and Contributors. All Rights Reserved.
00004 #
00005 # This software is subject to the provisions of the Zope Public License,
00006 # Version 2.1 (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 """Unique Id Handler Tool
00014 
00015 Provides support for accessing unique ids on content object.
00016 
00017 $Id: UniqueIdHandlerTool.py 77186 2007-06-28 19:06:19Z yuppie $
00018 """
00019 
00020 import logging
00021 import os
00022 import Missing
00023 
00024 from AccessControl import ClassSecurityInfo
00025 from Acquisition import aq_base
00026 from Globals import InitializeClass
00027 from Globals import package_home
00028 from OFS.SimpleItem import SimpleItem
00029 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
00030 from zope.component import getUtility
00031 from zope.interface import implements
00032 
00033 from Products.CMFCore.permissions import ManagePortal
00034 from Products.CMFCore.utils import getToolByName
00035 from Products.CMFCore.utils import UniqueObject
00036 
00037 from Products.CMFUid.interfaces import IUniqueIdAnnotationManagement
00038 from Products.CMFUid.interfaces import IUniqueIdBrainQuery
00039 from Products.CMFUid.interfaces import IUniqueIdGenerator
00040 from Products.CMFUid.interfaces import IUniqueIdHandler
00041 from Products.CMFUid.interfaces import IUniqueIdUnrestrictedQuery
00042 from Products.CMFUid.interfaces import UniqueIdError
00043 
00044 UID_ATTRIBUTE_NAME = 'cmf_uid'
00045 
00046 _wwwdir = os.path.join( package_home( globals() ), 'www' )
00047 
00048 
00049 class UniqueIdHandlerTool(UniqueObject, SimpleItem):
00050 
00051     __doc__ = __doc__ # copy from module
00052 
00053     implements(IUniqueIdHandler, IUniqueIdBrainQuery,
00054                IUniqueIdUnrestrictedQuery)
00055     __implements__ = (
00056         SimpleItem.__implements__,
00057     )
00058 
00059     id = 'portal_uidhandler'
00060 
00061     manage_options = ( ({'label': 'Query',
00062                          'action': 'manage_queryObject'},)
00063                      + SimpleItem.manage_options
00064                      )
00065 
00066     alternative_id = "portal_standard_uidhandler"
00067     meta_type = 'Unique Id Handler Tool'
00068 
00069     # make the uid attribute name available for the unit tests
00070     # not meant to be altered as long you don't know what you do!!!
00071     UID_ATTRIBUTE_NAME = UID_ATTRIBUTE_NAME
00072 
00073     # make the exception class available through the tool
00074     UniqueIdError = UniqueIdError
00075 
00076     security = ClassSecurityInfo()
00077 
00078     def _reindexObject(self, obj):
00079         # XXX: this method violates the rules for tools/utilities:
00080         # it depends on a non-utility tool
00081         # add uid index and colums to catalog if not yet done
00082         UID_ATTRIBUTE_NAME = self.UID_ATTRIBUTE_NAME
00083         catalog = getToolByName(obj, 'portal_catalog')
00084         if UID_ATTRIBUTE_NAME not in catalog.indexes():
00085             catalog.addIndex(UID_ATTRIBUTE_NAME, 'FieldIndex')
00086             catalog.addColumn(UID_ATTRIBUTE_NAME)
00087 
00088         # reindex
00089         catalog.reindexObject(obj, idxs=[UID_ATTRIBUTE_NAME])
00090 
00091     def _setUid(self, obj, uid):
00092         """Attaches a unique id to the object and does reindexing.
00093         """
00094         # attach a unique id annotation to the object
00095         anno_tool = getUtility(IUniqueIdAnnotationManagement)
00096         annotation = anno_tool(obj, self.UID_ATTRIBUTE_NAME)
00097         annotation.setUid(uid)
00098 
00099         # reindex the object
00100         self._reindexObject(obj)
00101 
00102     security.declarePublic('register')
00103     def register(self, obj):
00104         """See IUniqueIdSet.
00105         """
00106         uid = self.queryUid(obj, default=None)
00107         if uid is None:
00108             # generate a new unique id and set it
00109             generator = getUtility(IUniqueIdGenerator)
00110             uid = generator()
00111             self._setUid(obj, uid)
00112 
00113         return uid
00114 
00115     security.declareProtected(ManagePortal, 'unregister')
00116     def unregister(self, obj):
00117         """See IUniqueIdSet.
00118         """
00119         UID_ATTRIBUTE_NAME = self.UID_ATTRIBUTE_NAME
00120         if getattr(aq_base(obj), UID_ATTRIBUTE_NAME, None) is None:
00121             raise UniqueIdError, \
00122                   "No unique id available to be unregistered on '%s'" % obj
00123 
00124         # delete the uid and reindex
00125         delattr(obj, UID_ATTRIBUTE_NAME)
00126         self._reindexObject(obj)
00127 
00128     security.declarePublic('queryUid')
00129     def queryUid(self, obj, default=None):
00130         """See IUniqueIdQuery.
00131         """
00132         uid = getattr(aq_base(obj), self.UID_ATTRIBUTE_NAME, None)
00133         # If 'obj' is a content object the 'uid' attribute is usually a
00134         # callable object. If 'obj' is a catalog brain the uid attribute
00135         # is non callable and possibly equals the 'Missing.MV' value.
00136         if uid is Missing.MV or uid is None:
00137             return default
00138         if callable(uid):
00139             return uid()
00140         return uid
00141 
00142     security.declarePublic('getUid')
00143     def getUid(self, obj):
00144         """See IUniqueIdQuery.
00145         """
00146         uid = self.queryUid(obj, None)
00147         if uid is None:
00148             raise UniqueIdError, "No unique id available on '%s'" % obj
00149         return uid
00150 
00151     security.declarePrivate('setUid')
00152     def setUid(self, obj, uid, check_uniqueness=True):
00153         """See IUniqueIdSet.
00154         """
00155         # None is the only value a unique id shall never have!
00156         if uid is None:
00157             raise UniqueIdError, "It's forbidden to set a unique id to 'None'."
00158 
00159         # check for uniqueness if enabled
00160         if check_uniqueness:
00161             result = self.queryObject(uid)
00162             if result is not None and result != obj:
00163                 if callable(uid):
00164                     uid = uid()
00165                 raise UniqueIdError, \
00166                       "The unique id '%s' is already in use" % uid
00167 
00168         # everything is ok: set it!
00169         self._setUid(obj, uid)
00170 
00171     def _queryBrain(self, uid, searchMethodName, default=None):
00172         """This helper method does the "hard work" of querying the catalog
00173            and interpreting the results.
00174         """
00175         # XXX: this method violates the rules for tools/utilities:
00176         # it depends on a non-utility tool
00177         if uid is None:
00178             return default
00179 
00180         # convert the uid to the right format
00181         generator = getUtility(IUniqueIdGenerator)
00182         uid = generator.convert(uid)
00183 
00184         catalog = getToolByName(self, 'portal_catalog')
00185         searchMethod = getattr(catalog, searchMethodName)
00186         result = searchMethod({self.UID_ATTRIBUTE_NAME: uid})
00187         len_result = len(result)
00188 
00189         # return None if no object found with this uid
00190         if len_result == 0:
00191             return default
00192 
00193         # print a message to the log  if more than one object has
00194         # the same uid (uups!)
00195         if len_result > 1:
00196             logging.getLogger('CMFUid').error(
00197                 "ASSERT: %d objects have %r as uid!!!", len_result, uid)
00198 
00199         return result[0]
00200 
00201     security.declarePublic('queryBrain')
00202     def queryBrain(self, uid, default=None):
00203         """See IUniqueIdBrainQuery.
00204         """
00205         return self._queryBrain(uid, 'searchResults', default)
00206 
00207     def _getBrain(self, uid, queryBrainMethod):
00208         brain = queryBrainMethod(uid, default=None)
00209         if brain is None:
00210             raise UniqueIdError, "No object found with '%s' as uid." % uid
00211         return brain
00212 
00213     security.declarePublic('getBrain')
00214     def getBrain(self, uid):
00215         """See IUniqueIdBrainQuery.
00216         """
00217         return self._getBrain(uid, self.queryBrain)
00218 
00219     security.declarePublic('getObject')
00220     def getObject(self, uid):
00221         """See IUniqueIdQuery.
00222         """
00223         return self.getBrain(uid).getObject()
00224 
00225     security.declarePublic('queryObject')
00226     def queryObject(self, uid, default=None):
00227         """See IUniqueIdQuery.
00228         """
00229         try:
00230             return self.getObject(uid)
00231         except UniqueIdError:
00232             return default
00233 
00234     security.declarePrivate('unrestrictedQueryBrain')
00235     def unrestrictedQueryBrain(self, uid, default=None):
00236         """See IUniqueIdUnrestrictedQuery.
00237         """
00238         return self._queryBrain(uid, 'unrestrictedSearchResults', default)
00239 
00240     security.declarePrivate('unrestrictedGetBrain')
00241     def unrestrictedGetBrain(self, uid):
00242         """See IUniqueIdUnrestrictedQuery.
00243         """
00244         return self._getBrain(uid, self.unrestrictedQueryBrain)
00245 
00246     security.declarePrivate('unrestrictedGetObject')
00247     def unrestrictedGetObject(self, uid):
00248         """See IUniqueIdUnrestrictedQuery.
00249         """
00250         return self.unrestrictedGetBrain(uid).getObject()
00251 
00252     security.declarePrivate('unrestrictedQueryObject')
00253     def unrestrictedQueryObject(self, uid, default=None):
00254         """See IUniqueIdUnrestrictedQuery.
00255         """
00256         try:
00257             return self.unrestrictedGetObject(uid)
00258         except UniqueIdError:
00259             return default
00260 
00261     security.declareProtected(ManagePortal, 'manage_queryObject')
00262     manage_queryObject = PageTemplateFile('queryUID.pt', _wwwdir)
00263 
00264 InitializeClass(UniqueIdHandlerTool)