Viewlets strange behaviour

Hi!

this code:

# -*- coding: utf-8 -*-
  
from plone.app.layout.viewlets import ViewletBase
from zope.component import getMultiAdapter

class TopBarLinks(ViewletBase):

    def update(self):
        self.actions = self.get_actions(self.context)

    def get_actions(self, context):

        return self.context.portal_actions.listFilteredActionsFor(context)['portal_tabs']

    def index(self):
        return super(TopBarLinks, self).render()

works. But this does not work:

# -*- coding: utf-8 -*-

from plone.app.layout.viewlets import ViewletBase
from zope.component import getMultiAdapter

class TopBarLinks(ViewletBase):

    def update(self):
        self.actions = self.get_actions()

    def get_actions(self):

        return self.context.portal_actions.listFilteredActionsFor(context)['portal_tabs']

    def index(self):
        return super(TopBarLinks, self).render()

self.context exists before calling get_actions but in get_actions, self.context does not exists. This is the error:

topbarlinks, line 15, in get_actions
NameError: name 'context' is not defined

Maybe I miss a super update()?

You are calling context, not self.context :wink:

Sorry, I posted the wrong code. I just cheked it and it fails. I've updated the question with the correct code.

This is what happen, with breakpoint():

(Pdb++) ll
  11         def get_actions(self):                                             
  12                                                                            
  13             breakpoint()                                                   
  14  ->         left, right = [], []                                           
  15             try:                                                           
  16                 actions = self.context.portal_actions.listFilteredActionsFo
  17                 for a in actions:                                          
  18                     (left, right)['left' in a['description']].append(a)    
  19                 return (left, right)                                       
  20             except KeyError:                                               
  21                 return (None, None)                                        
(Pdb++) self.context
<Document at /Plone6/front-page>
(Pdb++) n
[27] > /home/yurj/old_home/progetti/plone/plone6/src/myaddon/src/myaddon/viewlets/topbarlinks.py(15)get_actions()
-> try:
(Pdb++) n
[27] > /home/yurj/old_home/progetti/plone/plone6/src/myaddon/src/myaddon/viewlets/topbarlinks.py(16)get_actions()
-> actions = self.context.portal_actions.listFilteredActionsFor(context)['top_links']
(Pdb++) n
NameError: name 'context' is not defined
[27] > /home/yurj/old_home/progetti/plone/plone6/src/myaddon/src/myaddon/viewlets/topbarlinks.py(16)get_actions()
-> actions = self.context.portal_actions.listFilteredActionsFor(context)['top_links']

Another strange thing is that if I continue with pdb++ (n) it calls the viewlet again, the same error happen and then Plone renders the error.

(Pdb++) n
NameError: name 'context' is not defined
[26] > /home/yurj/old_home/progetti/plone/plone6/src/myaddon/src/myaddon/viewlets/topbarlinks.py(9)update()
-> self.actions = self.get_actions()
(Pdb++) n
--Return--
[26] > /home/yurj/old_home/progetti/plone/plone6/src/myaddon/src/myaddon/viewlets/topbarlinks.py(9)update()->None
-> self.actions = self.get_actions()
(Pdb++) n
NameError: name 'context' is not defined
[25] > /home/yurj/old_home/progetti/plone/plone6/eggs/zope.viewlet-4.2.1-py3.7.egg/zope/viewlet/manager.py(161)_updateViewlets()
-> viewlet.update()
(Pdb++) c
[27] > /home/yurj/old_home/progetti/plone/plone6/src/myaddon/src/myaddon/viewlets/topbarlinks.py(14)get_actions()
-> left, right = [], [] <- here again!

In your edited wrong code there is still:

return self.context.portal_actions.listFilteredActionsFor(context)['portal_tabs']

1 Like

woops, sorry! I was just looking at the first one, thanks!

I ended using this viewlet code to get a list of action of a particular category:

# -*- coding: utf-8 -*-

from plone.app.layout.viewlets import ViewletBase
# from zope.component import getMultiAdapter
from plone import api


class TopBarLinks(ViewletBase):

    def update(self):
        self.actions = self.get_actions()

    def get_actions(self):

        left, right = [], []

        # old way to get actions or get complete actions
        # actions = self.context.portal_actions.listFilteredActionsFor(
        #                                               self.context)['top_links']
        # context_state = getMultiAdapter((self.context, self.request),
        #                            name=u'plone_context_state')
        # actions = context_state.actions('top_links')

        # get filtered actions
        actions = api.portal.get_tool(
            name='portal_actions').listFilteredActionsFor(
            self.context)['top_links']

        for a in actions:
            (left, right)['left' in a['description']].append(a)
        return (left, right)

    def index(self):
        return super(TopBarLinks, self).render()

This is the template, which uses left and right to position them in the viewlet link bar (use Bootstrap 5):

<nav class="navbar navbar-expand-md" tal:define="acts view/actions;
                                                 left python:acts[0];
                                                 right python:acts[1]">
    <div class="navbar-collapse collapse w-100 order-1 order-md-0 dual-collapse2">
        <ul class="navbar-nav me-auto">
      <tal:actions repeat="laction left">
        <li class="nav-item">
              <a class="nav-link" tal:attributes="href laction/url; title laction/title;" tal:content="laction/title">
                Action title
              </a>
        </li>
      </tal:actions>
        </ul>
    </div>
    <div class="mx-auto order-0">
        <a class="navbar-brand mx-auto" href="#">My Brand</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".dual-collapse2">
            <span class="navbar-toggler-icon"></span>
        </button>
    </div>
    <div class="navbar-collapse collapse w-100 order-3 dual-collapse2">
        <ul class="navbar-nav ms-auto">
      <tal:actions repeat="raction right">
        <li class="nav-item">
              <a class="nav-link" tal:attributes="href raction/url; title raction/title;" tal:content="raction/title">
                Action title
              </a>
        </li>
      </tal:actions>
        </ul>
    </div>
</nav>