thanks, David; it's working! the hardest part was find out what was the folder containing the offending objects (there were lots!).
I used this modified version of Martijn's script to walk over the site:
from collections import deque
from plone.dexterity.interfaces import IDexterityContent
from Products.ATContentTypes.interfaces.folder import IATFolder
import transaction
def tree_walker(root):
# stack holds (parent, id, obj) tuples
stack = deque([(None, None, root)])
while stack:
parent, id, next = stack.popleft()
if callable(getattr(next, 'objectItems', None)):
try:
stack.extend((next, id, child) for id, child in next.objectItems())
except AttributeError:
print (parent, id, next)
yield parent, id, next
def is_folder(obj):
return IATFolder.providedBy(obj)
count = items = 0
for parent, id_, obj in tree_walker(site):
if is_folder(obj):
print 'Found folder {0} at {1}'.format(id_, '/'.join(obj.getPhysicalPath()))
obj._p_changed = True
count += 1
if count % 100 == 0:
transaction.savepoint(True)
else:
items += 1
if items % 1000 == 0:
print '{0} items processed, {1} matches so far'.format(items, count)
transaction.commit()
then, when finding a folder with issues I ran the following script to clean it up:
def clean_folder(folder):
while True:
try:
folder.objectItems()
except AttributeError as e:
id_ = str(e.message)[1:-1]
print id_
folder.getOrdering().notifyRemoved(id_)
else:
break
I'm trying to rebuild the catalog now, but seems to be working so far.