BrowserView and Viewlet
預設的 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 覆蓋檔案,可能造成不穩定的顯示問題。
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
# 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
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" />
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>