2011/09/30

Page Template Tip on Creators

Plone Archetype content type 可以存取 Dublin Core Metadata 內容,例如作者、著作權、發佈日期等資訊,從 Products/Archetypes/ExtensibleMetadata.py 可以看到作者欄位識別碼是 LinesField: creators,還有 Creator 和 listCreators 兩個函式,想要存取作者資訊的話,分成 Creator 和 Creators 兩個方式:例如 <tal:content="context/Creator"> 會顯示第一筆資料,<tal:content="context/Creator"> 則會以多筆資料方式顯示。常見的範例是:

Authos:
<metal:field use-macro="python:context.widget('creators', mode='view')">
John Smith, et al.</metal:field>

2011/09/29

Plone Theme Editor with Diazo

Plone 佈景主題的開發方式,自從 DeliveranceDiazo 問世之後,邁入新的里程碑,加上 Plone 4.1 開始搭配 plone.app.theming 模組,建立 Diazo 工作環境的門檻大幅降低。

覺得編輯規則檔 (rules.xml) 太麻煩嗎? 現在有個視覺編輯工具正在開發,預計在 Plone 4.3 之後納入,這段影片讓你先睹一快: http://www.screenr.com/b1Rs

2011/09/27

OFS.Uninstalled Could not import class

嘗試移除 Plone 模組時,經常會遇到這個警告訊息,這代表模組並未完全移除乾淨,至少我遇過 Add-on Configuration 還留有設定項目,或是 ZMI 根目錄裡看得到 broken object 的情況。

broken object 應該可以直接刪除,Add-on Configuration 的項目要到 ZMI portal_controlpanel 裡刪除。這裡有個 DropDownMenu 的範例畫面。

如果症狀比上述情況嚴重,就要參考 Removing a persistent local utility 的除錯步驟。

2011/09/23

Moving collective.geo.kml Viewlet

collective.geo.* 是一組提供 GIS 服務的 Plone 模組,它的地圖顯示區塊,預設是在 description 欄位下方,也就是在 <div id="viewlet-above-content-body"> 的下方,想要把它移到 <div id="viewlet-below-content"> 旁邊。 首先,用 firebug 得知顯示區塊被包在 <div id="kml-content-viewlet"> 裡,那麼它是由哪個檔案提供的呢?

$ grep -r kml-content-viewlet buildout-cache/eggs

collective/geo/kml/browser/kmlcontentviewlet.pt 檔案有我們要找的 div,照慣例,先到 collective/geo/kml/browser/configure.zcml 找線索:

  <browser:viewlet
      name="collective.geo.kml.kmlcontentviewlet"
      for="collective.geo.geographer.interfaces.IGeoreferenceable"
      class=".viewlets.ContentViewlet"
      manager="plone.app.layout.viewlets.interfaces.IAboveContentBody"
      template="kmlcontentviewlet.pt"
      layer="..interfaces.IGeoKmlLayer"
      permission="zope2.View"
  />

看來把 viewlet 的 manager 由 IAboveContentBody 改到 IBelowContent 就行。

2011/09/22

Transmogrifier in Action

最近用 collective.transmogrifier 將資料匯入 Plone 4.0.7,同時支援 CSV 和 PostgreSQL 兩種來源格式。

首先,用 paster 建立一個 my.importer 的專案,其中的 configure.zcml 內容範例如下:
<configure
    xmlns="http://namespaces.zope.org/zope"
    xmlns:five="http://namespaces.zope.org/five"
    xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
    xmlns:transmogrifier="http://namespaces.plone.org/transmogrifier">

  <includeDependencies package="." />

  <genericsetup:registerProfile
      name="default"
      title="my.importer"
      directory="profiles/default"
      description="To set context for transmogrifier profiles."
      provides="Products.GenericSetup.interfaces.EXTENSION"
      />

  <include package="collective.transmogrifier" />
  <include package="collective.transmogrifier" file="meta.zcml" />
  <transmogrifier:registerConfig
    name="my.importer.importMyNews"
    title="My Importer for MyNews"
    description="Transmogrifier Pipeline config to import contents."
    configuration="confs/import_mynews.cfg"
    />

</configure>
在 confs 目錄裡,我們編輯 import_mynews.cfg 設定檔,範例內容如下:
[transmogrifier]
pipeline =
#    csvsource
    sqlsource
    type-inserter
    path-inserter
    folders
    constructor
    schemaupdater
    state-inserter
    workflowupdater
    reindexobject

#[csvsource]
#blueprint = collective.transmogrifier.sections.csvsource
#filename = my.importer:data/mynews.csv

[sqlsource]
blueprint = transmogrify.sqlalchemy
dsn = postgresql://username:secret@localhost:5432/dbname
query = SELECT id, title, description FROM my_table

[type-inserter]
blueprint = collective.transmogrifier.sections.inserter
key = string:_type
value = string:News Item

[path-inserter]
blueprint = collective.transmogrifier.sections.inserter
key = string:_path
value = string:/myfolder/${item/id}

[folders]
blueprint = collective.transmogrifier.sections.folders

[constructor]
blueprint = collective.transmogrifier.sections.constructor

[schemaupdater]
blueprint = plone.app.transmogrifier.atschemaupdater

[state-inserter]
blueprint = collective.transmogrifier.sections.inserter
key = string:_transitions
value = string:publish

[workflowupdater]
blueprint = plone.app.transmogrifier.workflowupdater

[reindexobject]
blueprint = plone.app.transmogrifier.reindexobject
來源是 SQL 資料時,使用的是 [sqlsource],來源是 CSV 資料時,就改用 csvsource,並在 data 目錄裡放好 mynews.csv 檔案,內容範例如下:
id,title,description
data1,My Data #1,Description of My Data #1
接著,編輯 my/importer/profiles/default/transmogrifier.txt 檔案,內容如下:
# this file contains transmogrifier profile names to run
# on GenericSetup transmogirifier's step
my.importer.importMyNews
以上就是 my.importer 專案的主要設定內容。最後,編輯 buildout.cfg 的內容如下:
eggs =
     SQLAlchemy == 0.6.5
    psycopg2
    collective.transmogrifier
    plone.app.transmogrifier
    transmogrify.sqlalchemy
   my.importer

zcml =
    collective.transmogrifier-meta
    collective.transmogrifier
    plone.app.transmogrifier
    transmogrify.sqlalchemy
上述 SQLAlchemy == 0.6.5 語法,代表要指定 (pin) 安裝 0.6.5 版本的 SQLAlchemy,不指定的話,可能會安裝太新的版本,未必合用,我的經驗是 SQLAlchemy 0.7.2 會遇到 ImportError: No module named exceptions 錯誤訊息。

一切搞定後,要讓上述設定值生效的話,可以直接到 ZMI 的 portal_setup,在 Import 頁籤裡,從 Select Profile or Snapshot 找尋 my.importer 選項,再找到 Run transmogrifier pipeline (第41項) 並勾選它,最後點選 Import selected steps 按鈕。

額外一提,PostgreSQL 裡的欄位資料如果更新為空值,執行 transmogrifier 後,也會將 Plone 表單欄位清空。

2011/09/21

argument 2 to map() must support iteration

使用 collective.transmogrifier 和 transmogrify.sqlalchemy 讀取 PostgreSQL 內容匯入 Plone 表單時,遇到 TypeError: argument 2 to map() must support iteration 錯誤訊息,原因是表單所需要的 id 是 string 型別,來自 SQL 的 gid 內容則是 number 型別,想法子傳支援 iteration 的型別就行了。

AttributeError: getLocallyAllowedTypes

import 一個 folder zexp 到 Plone 4.1 根目錄後,遇到 AttributeError: getLocallyAllowedTypes 錯誤訊息,查到 #11950 討論這個問題,並附有 patch 檔案,預計在 Plone 4.1.1 會修掉這問題。

2011/09/19

Document Byline

有人並不喜歡看到網頁的「作者」「最近修改」資訊,這些資訊被 Plone 稱為 Byline,想要不顯示 Byline 資訊,除了在 portal_skinsCSS 可以修改外,在 ZMI 的 portal_view_customizations 裡,也可以修改 plone.belowcontenttitle.documentbyline 來達到效果: 原則上就是從 <tal:creator /> 開始,拿掉這些 page template 內容。

2011/09/15

pkg_resources.DistributionNotFound

執行 paster addcontent 時,遇到 pkg_resources.DistributionNotFound 訊息,處理方式是在搜尋哪個 .cfg 檔案包含 [zopeske] 設定區段,在裡面加上 ${instance:eggs} 設定值,例如:

[zopeskel]
recipe = zc.recipe.egg
eggs =
   ...
   ${instance:eggs}

重新執行 buildout 後,就能正常使用。

2011/09/08

Macro Expansion Failed

在 ZMI portal_skins 裡,想要客製 newsitem_view 時,看到一個訊息:
Macro expansion failed
<type 'exceptions.AttributeError'>: widget
這只是個煩人的訊息,直接修改內容並存檔,還是能夠運作。如果去處理它的話,會造成 page template 變得太複雜,在 Ticket #7173 被歸為暫不處理的項目。

cmf.ManagePortal ComponentLookupError

在 Plone 4.1 遇到下列訊息:

File "/home/marr/Plone/buildout-cache/eggs/
AccessControl-2.13.4-py2.6-linux-i686.egg/AccessControl/security.py",
 line 165, in protectClass
  permission = getUtility(IPermission, name=permission_id)
File "/home/marr/Plone/buildout-cache/eggs/
zope.component-3.9.5-py2.6.egg/zope/component/_api.py",
 line 169, in getUtility
  raise ComponentLookupError(interface, name)
zope.configuration.config.ConfigurationExecutionError:
 <class 'zope.component.interfaces.componentlookuperror'="">:
 (<interfaceclass zope.security.interfaces.ipermission="">, 'cmf.ManagePortal')
in:
File "/home/marr/Plone/buildout-cache/eggs/
p4a.subtyper-1.2.0-py2.6.egg/p4a/subtyper/configure.zcml",
 line 49.2-55.8
  <browser:page
      name="subtyper"
      for="*"
      permission="cmf.ManagePortal"
      class=".browser.SubtyperView"
      allowed_interface=".browser.ISubtyperView"
      />

臨時的解法是修改 p4a/subtyper/configure.zcml 內容,加上:

<include package="Products.CMFCore" 
file="permissions.zcml" />

以上,應該是 add-on 昇級的議題。另外,類似的訊息還有 ComponentLookupError: (<InterfaceClass plone.keyring.interfaces.IKeyManager>, '') 的錯誤,在 ZMI portal_setup 重新載入所有的 plone.keyring KeyManager Registration 步驟就行。