Back to index

plone3  3.1.7
handlers.py
Go to the documentation of this file.
00001 from Acquisition import aq_parent
00002 from Products.Archetypes.interfaces import IReference
00003 from Products.Archetypes.Field import TextField
00004 from Products.Archetypes.exceptions import ReferenceException
00005 from OFS.interfaces import IItem
00006 from exceptions import LinkIntegrityNotificationException
00007 from interfaces import ILinkIntegrityInfo, IOFSImage
00008 from urlparse import urlsplit, urlunsplit
00009 from parser import extractLinks
00010 import urllib
00011 
00012 
00013 referencedRelationship = 'isReferencing'
00014 
00015 
00016 def findObject(base, path):
00017     """ traverse to given path and find the upmost object """
00018     obj = base
00019     components = path.split('/')
00020     while components:
00021         child_id = urllib.unquote(components[0])
00022         try: child = obj.restrictedTraverse(child_id)
00023         except: return None, None
00024         if not IItem.providedBy(child):
00025             break
00026         obj = child
00027         components.pop(0)
00028     return obj, '/'.join(components)
00029 
00030 
00031 def getObjectsFromLinks(base, links):
00032     """ determine actual objects refered to by given links """
00033     objects = set()
00034     url = base.absolute_url()
00035     scheme, host, path, query, frag = urlsplit(url)
00036     site = urlunsplit((scheme, host, '', '', ''))
00037     for link in links:
00038         s, h, path, q, f = urlsplit(link)
00039         if (not s and not h) or (s == scheme and h == host):    # relative or local url
00040             obj, extra = findObject(base, path)
00041             if obj:
00042                 if IOFSImage.providedBy(obj):
00043                     obj = aq_parent(obj)    # use atimage object for scaled images
00044                 objects.add(obj)
00045     return objects
00046 
00047 
00048 def modifiedArchetype(obj, event):
00049     """ an archetype based object was modified """
00050     try:    # TODO: is this a bug or a needed workaround?
00051         existing = set(obj.getReferences(relationship=referencedRelationship))
00052     except AttributeError:
00053         return
00054     refs = set()
00055     for field in obj.Schema().fields():
00056         if isinstance(field, TextField):
00057             accessor = field.getAccessor(obj)
00058             links = extractLinks(accessor())
00059             refs = refs.union(getObjectsFromLinks(obj, links))
00060     for ref in refs.difference(existing):   # add new references and...
00061         try:
00062             obj.addReference(ref, relationship=referencedRelationship)
00063         except ReferenceException:
00064             pass
00065     for ref in existing.difference(refs):   # removed leftovers
00066         obj.deleteReference(ref, relationship=referencedRelationship)
00067 
00068 
00069 def referenceRemoved(obj, event):
00070     """ store information about the removed link integrity reference """
00071     assert IReference.providedBy(obj)
00072     assert obj is event.object          # just making sure...
00073     if not obj.relationship == referencedRelationship:
00074         return                          # skip for other removed references
00075     # if the object the event was fired on doesn't have a `REQUEST` attribute
00076     # we can safely assume no direct user action was involved and therefore
00077     # never raise a link integrity exception...
00078     if not hasattr(obj, 'REQUEST'):
00079         return
00080     storage = ILinkIntegrityInfo(obj.REQUEST)
00081     breaches = storage.getIntegrityBreaches()
00082     breaches.setdefault(obj.getTargetObject(), set()).add(obj.getSourceObject())
00083     storage.setIntegrityBreaches(breaches)
00084 
00085 
00086 def referencedObjectRemoved(obj, event):
00087     """ check if the removal was already confirmed or redirect to the form """
00088     # if the object the event was fired on doesn't have a `REQUEST` attribute
00089     # we can safely assume no direct user action was involved and therefore
00090     # never raise a link integrity exception...
00091     # (this should also fix http://plone.org/products/cachefu/issues/86)
00092     if not hasattr(obj, 'REQUEST'):
00093         return
00094     info = ILinkIntegrityInfo(obj.REQUEST)
00095 
00096     # first we check if link integrity checking was enabled
00097     if not info.integrityCheckingEnabled():
00098         return
00099 
00100     # since the event gets called for every subobject before it's
00101     # called for the item deleted directly via _delObject (event.object)
00102     # itself, but we do not want to present the user with a confirmation
00103     # form for every (referred) subobject, so we remember and skip them...
00104     info.addDeletedItem(obj)
00105     if obj is not event.object:
00106         return
00107 
00108     # if the number of expected events has been stored to help us prevent
00109     # multiple forms (i.e. in folder_delete), we wait for the next event
00110     # if we know there will be another...
00111     if info.moreEventsToExpect():
00112         return
00113 
00114     # at this point all subobjects have been removed already, so all
00115     # link integrity breaches caused by that have been collected as well;
00116     # if there aren't any (after things have been cleaned up),
00117     # we keep lurking in the shadows...
00118     if not info.getIntegrityBreaches():
00119         return
00120 
00121     # if the user has confirmed to remove the currently handled item in a
00122     # previous confirmation form we won't need it anymore this time around...    
00123     if info.isConfirmedItem(obj):
00124         return
00125 
00126     # otherwise we raise an exception and pass the object that is supposed
00127     # to be removed as the exception value so we can use it as the context
00128     # for the view triggered by the exception;  this is needed since the
00129     # view is an adapter for the exception and a request, so it gets the
00130     # exception object as the context, which is not very useful...
00131     raise LinkIntegrityNotificationException, obj
00132