Skip to content. | Skip to navigation

Personal tools

Navigation

You are here: Home / Tips / BrowserView and Viewlet

BrowserView and Viewlet

多數使用者介面是由 BrowserView 和 Viewlet 所控制,技術上 Viewlet 再由 Viewlet Manager 管理。

預設的 Plone4 或 Plone5 環境,在網址最後加上 /@@manage-viewlets 可以查看所有 Viewlet 的狀態,包括 Viewlet Manager 在內。修改 Viewlet 的方式分成兩類: TTW 方式是從 ZMI /portal_view_customization 修改 Template 內容,TTF 方式又可分成 z3c.jbot 取代 Template 內容,或是 browser/configure.zcml 註冊 Viewlet 來取代舊有的設定值。

由於 z3c.jbot 已經包括在 bobtemplates.plone 產生的骨架程式碼裡,很容易就在 overrides 目錄裡新增 Template 的覆蓋內容。以覆蓋 plone.app.layout 的 colophon.pt 為例,先要找到它的檔案位置 plone.app.layout/viewlets/colophon.pt 把覆蓋檔案名稱設為 plone.app.layout.viewlets.colophon.pt 放在 overrides 目錄裡,重新啟動 Plone 系統就能生效。

重複註冊的 z3c.jbot 覆蓋檔案,可能造成不穩定的顯示問題

取代 Event Item Template 範例教學

BrowserView 跟 BrowserPage 差別不大Navigation Portlet and FolderContents Order

wildcard.foldercontents: No Dependency on Base Tag - Data Attribute Instead of Viewlet

<div class="fc fc-container" tal:attributes="data-context-base-url view/context_base_url">

Programmatically Get Portlet Assignment for Portlet Displayed by Item Display Dynamic View

動態加總數量

Streaming Zope HTTP responses with proxy views

Programmatically Find and Change HTML on Pages

Override Default View: collective.polls archetypes.referencebrowserwidget collective.upload - override optional folder_full_view_item.pt

Viewlet

想要修改 __getitem__() 嗎?

# src/my.theme/my/theme/browser/viewletmanager.py
from Products.Five.viewlet.manager import ViewletManagerBase

class MyHeaderViewletManager(ViewletManagerBase):
    """ A custom viewlet manager to re-organize some stock plone viewlets.
    """
    
    def __getitem__(self, name):
        """ Overriding getitem to call update() on access
        """
        viewlet = super(MyHeaderViewletManager, self).__getitem__(name)
        viewlet.update()
        return viewlet

Video: #1 #2

Using five.grok to Add Viewlets

Override Viewlet: overrides.zcml

Look up Folders where Portlet Defined Backreference Example plone.app.referenceablebehavior

Checking if a certain context portlet is active on a page

Override Schema Fields from collective.documentviewer

IStandardViewlets ISearchView IPloneSiteRoot Viewlet for HomePage Only

def update(self):
    self.isHome = False
    context_state = self.context.restrictedTraverse('@@plone_context_state')
    if context_state.is_portal_root() and context_state.is_default_page():
        self.isHome = True

Overriding a viewlet template

基本上 viewlet 可以分成 template 或 class 兩種類型,但實際上,還可以用到繼承 default class 的 template。

假設我們想要修改 portal header 區域,讓它顯示額外的資訊, layout/viewlets/configure.zcml

<browser:viewlet
    name="plone.header"
    manager=".interfaces.IPortalTop"
    template="portal_header.pt"
    permission="zope2.View"
    />

複製到 my/theme/browser/configure.zcml 裡,再修改其中的三行內容:

manager="plone.app.layout.viewlets.interfaces.IPortalTop"
template="mynewportal_header.pt"
layer=".interfaces.IThemeSpecific"

複製 plone/app/layout/viewlets/portal_header.pt 到 my/theme/browser 目錄裡,再把檔案從 portal_header.pt 改成 mynewportal_header.pt,內容增加三行:

<h1>
  Title of the portal
</h1>
<div id="portal-header">
  ...
</div>

因為沒有移動或調整 viewlet 順序,不需要更改 viewlets.xml 檔案。

<!-- The breadcrumbs -->
<browser:viewlet
  name="plone.path_bar"
  manager=".interfaces.IPortalTop"
  class=".common.PathBarViewlet"
  permission="zope2.View" />

zope.annotation vs setter

Look up Viewlet from Portlet

Navigation Portlet Shows Only in Non-folderish Children

import Acquisition
from zope.component import getUtility, getMultiAdapter


from plone.portlets.interfaces import IPortletRetriever, IPortletManager

for column in ["plone.leftcolumn", "plone.rightcolumn"]:

    manager = getUtility(IPortletManager, name=column)

    retriever = getMultiAdapter((self.context, manager), IPortletRetriever)

    portlets = retriever.getPortlets()

    for portlet in portlets:

        # portlet is {'category': 'context', 'assignment': , 'name': u'facebook-like-box', 'key': '/isleofback/sisalto/huvit-ja-harrasteet
        # Identify portlet by interface provided by assignment
        if IFacebookLikeBoxData.providedBy(portlet["assignment"]):
            return True

return False

Example from collective.googleanalytics/portlets/analyticsportlet.py

    def available(self):
       """
       Determines whether the user has permission to see the portlet.
       """

       mtool = getToolByName(self.context, 'portal_membership')
       allowed = mtool.checkPermission('Google Analytics: View Analytics Results', self.context)
       context_state = self.context.restrictedTraverse('@@plone_context_state')
       return allowed and context_state.is_view_template()

Portlet Compatible for All Versions

static portlet example to subscribe a list

Programmatically Asign Portlets

collective.upload viewlet refactoring

collective.weather portlet utility refactoring

quadra.app.mediaBase/borwser/mediaFolderContents.py

Icon Image 之類資源可以註冊在 Resource Folder 裡透過 ++resource++my.pkg/my-icon.png 存取。

Browser View to Update Data

sample code for Archetype Document

from Products.Five.browser import BrowserView

class Update(BrowserView):
    def __call__(self):
        context = self.context
        request = self.request
        catalog = context.portal_catalog
        query = {'portal_type': 'Document', 'sort_on': 'getId'}
        brains = catalog(query)
        for brain in brains:
            item = brain.getObject()
            item.setText(item.getText()+'\r\n<hr />\r\n<p>Data Source: <a href="https://example.com/">Example</a></p>')
            item.reindexObject()
item.setText(item.getText().replace('old', 'new'))
mystr = '<p>Field1: 12345</p>\r\n<p>Field2: string</p>'
import re
regex = r"(<p>Field1: (\d+)</p>)"
re.sub(regex, r"<!-- Field1: \2 -->", mystr)
# <!-- Field1: 12345 -->\r\n<p>Field2: string</p>
Related content
Portlet Management