[SOLVED] Plone.toolbar broken with custom content after upgrade from 6.0.7 to 6.0.11

After upgrading to plone-latest my custom content appears to be breaking the toolbar.

Edited with more background info.

Traceback:

Traceback (innermost last):
  Module ZPublisher.WSGIPublisher, line 181, in transaction_pubevents
  Module ZPublisher.WSGIPublisher, line 391, in publish_module
  Module ZPublisher.WSGIPublisher, line 285, in publish
  Module ZPublisher.mapply, line 98, in mapply
  Module Products.PDBDebugMode.wsgi_runcall, line 60, in pdb_runcall
  Module pnz.erpediem.core.browser.article, line 47, in __call__
  Module plone.autoform.view, line 41, in __call__
  Module plone.autoform.view, line 32, in render
  Module Products.Five.browser.pagetemplatefile, line 126, in __call__
  Module Products.Five.browser.pagetemplatefile, line 58, in __call__
  Module zope.pagetemplate.pagetemplate, line 134, in pt_render
  Module Products.PageTemplates.engine, line 365, in __call__
  Module z3c.pt.pagetemplate, line 174, in render
  Module chameleon.zpt.template, line 331, in render
  Module chameleon.template, line 217, in render
  Module chameleon.utils, line 20, in raise_with_traceback
  Module chameleon.template, line 193, in render
  Module ad13a8c3ed69f330492a556b54c44077, line 544, in render
  Module d29fb316ff1e0998e496596f928a90d0, line 698, in render_master
  Module zope.contentprovider.tales, line 76, in __call__
  Module zope.viewlet.manager, line 157, in update
  Module zope.viewlet.manager, line 163, in _updateViewlets
  Module plone.app.layout.viewlets.common, line 470, in update
  Module plone.memoize.view, line 50, in memogetter
  Module plone.app.layout.viewlets.common, line 486, in getTabSets
  Module plone.memoize.view, line 50, in memogetter
  Module plone.app.layout.globals.context, line 244, in actions
  Module Products.CMFPlone.ActionsTool, line 83, in listActionInfos
  Module Products.CMFCore.ActionInformation, line 211, in __getitem__
  Module Products.CMFCore.Expression, line 53, in __call__
  Module Products.PageTemplates.ZRPythonExpr, line 49, in __call__
   - __traceback_info__: object.portal_type == 'EasyForm' and object.restrictedTraverse('@@is-sub-easyform')()
  Module PythonExpr, line 1, in <module>
zExceptions.unauthorized.Unauthorized: AccessControl.unauthorized.Unauthorized: You are not allowed to access 'portal_type' in this context

 - Expression: "provider:plone.toolbar"
 - Filename:   ... ges/Products/CMFPlone/browser/templates/main_template.pt
 - Location:   (line 55: col 32)
 - Source:     ... al:replace="structure provider:plone.toolbar" />
                                         ^^^^^^^^^^^^^^^^^^^^^^
 - Expression: "context/@@main_template/macros/master"
 - Filename:   ... /src/pnz/erpediem/core/browser/templates/traject_view.pt
 - Location:   (line 6: col 21)
 - Source:     ... tal:use-macro="context/@@main_template/macros/master"
                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 - Arguments:  template: <Products.Five.browser.pagetemplatefile.ViewPageTemplateFile object at 0x7f7389a79d00>
               options: {}
               args: ()
               nothing: None
               modules: <Products.PageTemplates.ZRPythonExpr._SecureModuleImporter object at 0x7f7397c1bb20>
               request: <WSGIRequest, URL=http://localhost:8888/Plone/articles/P3406/@@view>
               view: <Products.Five.browser.metaconfigure.SimpleViewClass from /usr/local/Plone6/src/pnz.erpediem.core/src/pnz/erpediem/core/browser/templates/traject_view.pt object at 0x7f7389a74fd0>
               context: <pnz.erpediem.core.trajects.article.ArticleWrapper object at 0x7f738f665cd0>
               views: <Products.Five.browser.pagetemplatefile.ViewMapper object at 0x7f73897b0d60>
               here: <pnz.erpediem.core.trajects.article.ArticleWrapper object at 0x7f738f665cd0>
               container: <pnz.erpediem.core.trajects.article.ArticleWrapper object at 0x7f738f665cd0>
               root: <Application at >
               traverse_subpath: []
               user: <PropertiedUser 'admin'>
               default: <DEFAULT>
               repeat: <Products.PageTemplates.engine.RepeatDictWrapper object at 0x7f738e11fb40>
               loop: {}
               target_language: None
               translate: <function BaseTemplate.render.<locals>.translate at 0x7f73898225e0>
               macroname: 'master'
               attrs: {}

I've narrowed it down to the ActionInfo class in Products.CMFCore.ActionInformation not being able to parse the econtext. This is getting over my head, assistance or hints are much appreciated!

ActionInfo fails when it encounters a key that itself is an Expression inside a Connection object (huh?) <Products.CMFCore.Expression.Expression object at 0x7f63894500b0 oid 0x6dae in <ZODB.Connection.Connection object at 0x7f6394e1b250>> rather than a key inside a pagetemplate expression.

I inserted some logging in the __getitem__ function.

        self._ec = ec
        self._lazy_keys = lazy_keys
        self._permissions = permissions

        # Logging for initialization
        log.info('ActionInfo initialized with ec: %s' % self._ec)

    def __getitem__(self, key):
        value = UserDict.__getitem__(self, key)
        if key in self._lazy_keys:
            try:
                value = self.data[key] = value(self._ec)
                log.info('ActionInfo in self._ec is ok: value %s in key %s' % (self.data[key], key))
                
            except:                
                log.info('ActionInfo in self._ec is NOT ok: value %s in key %s' % (self.data[key], key))
                value = self.data[key]

            self._lazy_keys.remove(key)

        return value

For my content, I see that some items in self._ec are not of the expected type:

ActionInfo initialized with ec: <Products.PageTemplates.Expressions.ZopeContext object at 0x7f63796d96a0>
ActionInfo in self._ec is ok: value True in key allowed
ActionInfo in self._ec is ok: value True in key available
ActionInfo in self._ec is ok: value True in key allowed
ActionInfo in self._ec is ok: value True in key available
ActionInfo in self._ec is ok: value True in key allowed
ActionInfo in self._ec is ok: value 0 in key available
ActionInfo in self._ec is ok: value True in key allowed
ActionInfo in self._ec is ok: value True in key available
ActionInfo in self._ec is ok: value True in key allowed
ActionInfo in self._ec is ok: value True in key available
ActionInfo in self._ec is ok: value True in key allowed
ActionInfo in self._ec is ok: value True in key available
ActionInfo in self._ec is ok: value True in key allowed
ActionInfo in self._ec is ok: value None in key available
ActionInfo in self._ec is ok: value True in key allowed
ActionInfo in self._ec is ok: value None in key available
ActionInfo in self._ec is ok: value True in key allowed
ActionInfo in self._ec is NOT ok: value <Products.CMFCore.Expression.Expression object at 0x7f63894500b0 oid 0x6dae in <ZODB.Connection.Connection object at 0x7f6394e1b250>> in key available
ActionInfo in self._ec is ok: value True in key allowed
ActionInfo in self._ec is NOT ok: value <Products.CMFCore.Expression.Expression object at 0x7f6389450190 oid 0x6dac in <ZODB.Connection.Connection object at 0x7f6394e1b250>> in key available
ActionInfo in self._ec is ok: value True in key allowed
ActionInfo in self._ec is NOT ok: value <Products.CMFCore.Expression.Expression object at 0x7f6389450270 oid 0x6daa in <ZODB.Connection.Connection object at 0x7f6394e1b250>> in key available
ActionInfo in self._ec is ok: value True in key allowed
ActionInfo in self._ec is NOT ok: value <Products.CMFCore.Expression.Expression object at 0x7f6389450350 oid 0x6da8 in <ZODB.Connection.Connection object at 0x7f6394e1b250>> in key available
ActionInfo in self._ec is ok: value True in key allowed
ActionInfo in self._ec is NOT ok: value <Products.CMFCore.Expression.Expression object at 0x7f6389450430 oid 0x6da6 in <ZODB.Connection.Connection object at 0x7f6394e1b250>> in key available
ActionInfo in self._ec is ok: value plone-cut in key icon
ActionInfo in self._ec is ok: value http://localhost:8888/Plone/articles/P3407/object_cut in key url
ActionInfo in self._ec is ok: value plone-copy in key icon
ActionInfo in self._ec is ok: value http://localhost:8888/Plone/articles/P3407/object_copy in key url
ActionInfo in self._ec is ok: value plone-delete in key icon
ActionInfo in self._ec is ok: value http://localhost:8888/Plone/articles/P3407/delete_confirmation in key url
ActionInfo in self._ec is ok: value plone-rename in key icon
ActionInfo in self._ec is ok: value http://localhost:8888/Plone/articles/P3407/object_rename in key url
ActionInfo in self._ec is ok: value plone-redirection in key icon
ActionInfo in self._ec is ok: value  http://localhost:8888/Plone/articles/P3407/@@manage-aliases in key url
ActionInfo in self._ec is ok: value ./@@export-easyform in key url
ActionInfo in self._ec is ok: value ./@@import-easyform in key url
ActionInfo in self._ec is ok: value ./@@saveddata in key url
ActionInfo in self._ec is ok: value ./fields in key url
ActionInfo in self._ec is ok: value ./actions in key url
ActionInfo in self._ec is ok: value toolbar-action/history in key icon
ActionInfo in self._ec is ok: value toolbar-action/sharing in key icon

Updated: my current understanding is that ZRPythonExpr in Products.PageTemplates can not access the portal_type of my custom content, but I have no idea where to look to allow it to to so beyond what is already there:

@implementer(IArticle)
class ArticleWrapper(Model):
    """A simple object representing the SQLAlchemy content within the Plone
    context.
    """

    def __init__(self, id=None ):
        self.id  = id
        self.portal_type = 'Article'

My custom content was not fully adhering to "The Contract" :wink:

This used to be sufficient:

    def PortalType(self):
        return self.portal_type

Now, this works for me...

[..]
from AccessControl.SecurityInfo import ClassSecurityInfo
from Products.CMFCore import permissions

@implementer(IArticle)
class ArticleWrapper(Model):
    """A simple object representing the SQLAlchemy content within the Plone
    context.
    """

    security = ClassSecurityInfo()

    [..]

    # @security.protected(permissions.View)
    @security.public
    def portal_type(self):
        return self.portal_type