Skip to content

Instantly share code, notes, and snippets.

@macagua
Created August 25, 2014 23:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save macagua/4fa954022a0145da9afd to your computer and use it in GitHub Desktop.
Save macagua/4fa954022a0145da9afd to your computer and use it in GitHub Desktop.
A Zope command line script to delete content with missing BLOB in Plone
"""
A Zope command line script to delete content with missing BLOB in Plone, causing
POSKeyErrors when content is being accessed or during portal_catalog rebuild.
Tested on Plone 4.1 + Dexterity 1.1.
http://stackoverflow.com/questions/8655675/cleaning-up-poskeyerror-no-blob-file-content-from-plone-site
Also see:
http://pypi.python.org/pypi/experimental.gracefulblobmissing/
Source http://www.plone-entwicklerhandbuch.de/plone-entwicklerhandbuch/produktivserver/fixblobs.py/view
"""
# Zope imports
from ZODB.POSException import POSKeyError
from zope.component import getMultiAdapter
from zope.component import queryUtility
from Products.CMFCore.interfaces import IPropertiesTool
from Products.CMFCore.interfaces import IFolderish, ISiteRoot
# Plone imports
from five import grok
from Products.Archetypes.Field import FileField
from Products.Archetypes.interfaces import IBaseContent
from plone.namedfile.interfaces import INamedFile
from plone.dexterity.content import DexterityContent
def check_at_blobs(context):
""" Archetypes content checker.
Return True if purge needed
"""
if IBaseContent.providedBy(context):
schema = context.Schema()
for field in schema.fields():
id = field.getName()
if isinstance(field, FileField):
try:
field.get_size(context)
except POSKeyError:
print "Found damaged AT FileField %s on %s" % (id, context.absolute_url())
return True
return False
def check_dexterity_blobs(context):
""" Check Dexterity content for damaged blob fields
XXX: NOT TESTED - THEORETICAL, GUIDELINING, IMPLEMENTATION
Return True if purge needed
"""
# Assume dexterity contennt inherits from Item
if isinstance(context, DexterityContent):
# Iterate through all Python object attributes
# XXX: Might be smarter to use zope.schema introspection here?
for key, value in context.__dict__.items():
# Ignore non-contentish attributes to speed up us a bit
if not key.startswith("_"):
if INamedFile.providedBy(value):
try:
value.getSize()
except POSKeyError:
print "Found damaged Dexterity plone.app.NamedFile %s on %s" % (key, context.absolute_url())
return True
return False
def fix_blobs(context):
"""
Iterate through the object variables and see if they are blob fields
and if the field loading fails then poof
"""
if check_at_blobs(context) or check_dexterity_blobs(context):
print "Bad blobs found on %s" % context.absolute_url() + " -> deleting"
parent = context.aq_parent
parent.manage_delObjects([context.getId()])
def recurse(tree):
""" Walk through all the content on a Plone site """
for id, child in tree.contentItems():
fix_blobs(child)
if IFolderish.providedBy(child):
recurse(child)
class FixBlobs(grok.CodeView):
"""
A management view to clean up content with damaged BLOB files
You can call this view by
1) Starting Plone in debug mode (console output available)
2) Visit site.com/@@fix-blobs URL
"""
grok.name("fix-blobs")
grok.context(ISiteRoot)
grok.require("cmf.ManagePortal")
def disable_integrity_check(self):
""" Content HTML may have references to this broken image - we cannot fix that HTML
but link integriry check will yell if we try to delete the bad image.
http://collective-docs.readthedocs.org/en/latest/content/deleting.html#bypassing-link-integrity-check "
"""
ptool = queryUtility(IPropertiesTool)
props = getattr(ptool, 'site_properties', None)
self.old_check = props.getProperty('enable_link_integrity_checks', False)
props.enable_link_integrity_checks = False
def enable_integrity_check(self):
""" """
ptool = queryUtility(IPropertiesTool)
props = getattr(ptool, 'site_properties', None)
props.enable_link_integrity_checks = self.old_check
def render(self):
#plone = getMultiAdapter((self.context, self.request), name="plone_portal_state")
print "Checking blobs"
portal = self.context
self.disable_integrity_check()
recurse(portal)
self.enable_integrity_check()
print "All done"
return "OK - check console for status messages"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment