The relevant DB was migrated from 4.3 to 5.2 and then to Python 3. After migrating the DB from py2 to py3 I get a error in the console:
UUIDIndex: query_index tried to look up key <ComputedAttribute object at 0x7fdce6c5c450> from index 'UID' but key was of the wrong type.
The reason seems to be:
Traceback (most recent call last):
File "/opt/plone/u480phb/.cache/eggs/Products.ZCatalog-4.4-py3.6.egg/Products/PluginIndexes/unindex.py", line 567, in query_index
s = index.get(k, None)
TypeError: '<' not supported between instances of 'str' and 'ComputedAttribute'
When portlets moved to use z3c.form instead of formlib the navigation also started to store the root of the navigation as a UID rather than a path (root_uid instead of root). See https://github.com/plone/plone.app.portlets/commit/fc1581421#diff-728de4de835790e6e7b32769b29f1747R124
The fallback used when the attribute root_uid is not set at allbreaks in Python 3 - maybe because the implementation of ComputedAttribute changed.
To work around: Edit the offending portlet and set a new-navigation-root. I have no globally useful code to fix this at the moment.
In my project I wrote a upgrade-step like this :
def聽fix_portlet(setup):
#聽fix聽navigation_portlet聽on portal-root (has聽ComputedValue聽for聽portal聽instead聽of聽a聽UUID)
from聽plone.portlets.interfaces聽import聽IPortletAssignmentMapping
from聽plone.portlets.interfaces聽import聽IPortletManager
from聽zope.component聽import聽getMultiAdapter
from聽zope.component聽import聽getUtility
from聽ComputedAttribute聽import聽ComputedAttribute
column聽=聽getUtility(IPortletManager,聽u'plone.leftcolumn')
mappings聽=聽getMultiAdapter((portal,聽column),聽IPortletAssignmentMapping)
for聽key,聽assignment聽in聽mappings.items():
if聽assignment.__name__聽==聽'navigation'聽and聽isinstance(assignment.root_uid,聽ComputedAttribute):聽聽#聽noqa:聽E501
# we have no root set anyway.
assignment.root_uid聽=聽None
I had to update my fix for locally assigned portlets and navigation-portlets that were not called 'navigation':
from ComputedAttribute import ComputedAttribute
from plone import api
from plone.app.portlets.portlets.navigation import Renderer
from plone.portlets.interfaces import IPortletAssignmentMapping
from plone.portlets.interfaces import IPortletManager
from plone.portlets.interfaces import IPortletRenderer
from zope.component import queryMultiAdapter
from zope.component import queryUtility
from zope.globalrequest import getRequest
def fix_portlets():
# fix navigation_portlet (has ComputedValue for portal instead of a UUID)
catalog = api.portal.get_tool('portal_catalog')
portal = api.portal.get()
for brain in catalog.getAllBrains():
try:
obj = brain.getObject()
except KeyError:
log.info('Broken brain for {}'.format(brain.getPath()))
fix_navigation_portlet_for(obj)
fix_navigation_portlet_for(portal)
def fix_navigation_portlet_for(obj):
request = getRequest()
view = obj.restrictedTraverse('@@view')
for manager_name in ['plone.leftcolumn', 'plone.rightcolumn']:
manager = queryUtility(IPortletManager, name=manager_name, context=obj)
if not manager:
continue
mappings = queryMultiAdapter((obj, manager), IPortletAssignmentMapping)
if not mappings:
continue
for key, assignment in mappings.items():
renderer = queryMultiAdapter(
(obj, request, view, manager, assignment), IPortletRenderer)
if not renderer:
continue
if isinstance(renderer, Renderer) and isinstance(assignment.root_uid, ComputedAttribute): # noqa: E501:
# We have a navigation-portlet!
assignment.root_uid = None
log.info('Reset root of navigation-portlet assigned at {} in {}'.format(obj.absolute_url(), manager_name)) # noqa: E501
log.info('You may need to configure it manually at {}/manage-portlets'.format(obj.absolute_url())) # noqa: E501
@pbauer Thanks for the snippet !
I used it to migrate one of our site, and added this code in fix_navigation_portlet_for to fix portlets roots automatically :
root_uid = None
try:
root_path = assignment.root.lstrip("/")
root = portal.restrictedTraverse(root_path)
root_uid = root.UID()
logger.info('Fixed navigation portlet in {} : {}'.format(obj.absolute_url(), root_path))
except KeyError:
logger.warn('Unable to fix navigation portlet in {}'.format(obj.absolute_url()))
assignment.root_uid = root_uid
del assignment.root
Most helpful comment
I had to update my fix for locally assigned portlets and navigation-portlets that were not called 'navigation':