2010/12/23

Exploring Objects with iw.debug

iw.debug 是用於 Zope 或 Plone 的偵錯工具,看來是仿照 Debug Anywhere 原理做成,和 plonectl debug 的方式有所不同。以 Plone 4.0.2 為例來安裝,在 buildout.cfg 裡指定 eggs 和 zcml 加入 iw.debug 即可,iw.debug 0.3 的相依套件是 ipython 0.10.1。
執行方式是先 bin/plonecfg fg 在前景模式啟動,透過 http://localhost:8080/myportal/@@ipdb 之類的網址,就可以在 console 看到 ipdb> 提示符號,輸入 ll 可以查看 local 變數。

2010/12/22

Widget to show Localized Title

eea.facetednavigation 的 checkbox widget 預設不會顯示翻譯過的標題,下列是個解法:
$ diff widgets/checkbox/widget.pt widgets/checkbox/widget.pt-new
14,15c14,15
< <legend tal:define="title python:view.data.get('title', '')"
< tal:content="
< python:view.translate(title)" i18n:translate="">HTML Widget</legend>
---
> <legend tal:define="title python:view.data.get('title', '')"
> i18n:translate="" i18n:domain="plone">
> <span tal:replace="title"/></legend>
25c25
< wbr_term_label python:view.word_break(term_label);
---
> term_label_translated python:term[1];
35c35
< tal:content="structure wbr_term_label">term label</label>
---
> tal:content="structure term_label_translated">term label</label>

2010/12/01

Plone Folder

Plone 3 和 Plone 4 目錄的實作方式不同,前者分成「一般目錄」和「大型目錄」兩種目錄型別,後者則是只實作一種型別,將兩種型別的特色整併。

一般目錄 (Folder) 最適合存放 50 個項目之內的場合,在內容操作介面裡,使用者可以手動拖拉,調整項目的順序。缺點在於,存取目錄物件時,是使用 Python 的 pickle 指令,會將整個目錄資料載入記憶體,目錄裡的項目過多時,容易造成效能變差。同時,目錄裡的項目值是以 tuple 型別來指定,存取過程都是整批處理,容易造成 ZODB 的衝突錯誤。

大型目錄 (Large Folder) 則是適合存放大量項目的場合,內部以 B-tree 實作,對於記憶體和硬碟的衝擊較小,同時也能針對局部資料進行鎖定,不易造成 ZODB 的衝突錯誤。它的缺點則是沒有提供調整項目順序的介面,而且要額外到 portal_types 設定,才能把項目內容顯示在資訊方框。

Plone 4 重新實作一個 plone.folder 型別,混合上述的優點,不再分成兩種目錄型別。

透過 Plone 3 網站 ZMI 的 portal_types 工具,可以詳細觀察 Large Folder 的屬性值,特別留意 Implicitly addable 的設定值,勾選後就可以在 Plone 3 的前台介面新增,例如 Members、news、events 目錄,都是使用 Large Folder 型別。

2010/11/30

Plone 3.x Migration to Plone 4.0

migration 至少分成資料與模組的昇級,兩者相關,但處理的重點不同。前者可參考 quintagroup.transmogrifier 的範例,後者則要先閱讀 Upgrade Guide

找到一個 ethnomed project 當作模組昇級範例,它已具備 buildout policy theme contenttypes extrafields reviewlist 等模組,透過 svn co https://svn.hsl.washington.edu/repos/ethnomed/ethnomed.buildout/trunk 可以取得原始碼。

開發過程有使用 Products.CacheSetup 之類的相依關係,在 Plone 4 的場合會遇到問題,可參考這個 chageset 的處理方式。另外 ethnomed/contenttypes/browser/viewlets.py 裡的 membership = self.tools.membership() 要改成 membership = getToolByName(self.context, 'portal_membership') 才能正常顯示多位作者。

額外的範例還有 PloneBooking 的 changeset

2010/11/29

Title-to-Id Behavior

title-to-id 是 Plone 的行為特色,在建立文件的過程,會把 title 欄位的值,轉換成 URL 的值。事實上,也可以事後轉換,例如寫成 External Method 來批次執行

Archetypes 預設就使用 title-to-id 機制,相關的程式碼片段整理如下:

Products/Archetypes/BaseObject.py
# Import conditionally, so we don't introduce a hard dependency
try:
from plone.i18n.normalizer.interfaces import IUserPreferredURLNormalizer
from plone.i18n.normalizer.interfaces import IURLNormalizer
URL_NORMALIZER = True
except ImportError:
URL_NORMALIZER = False

class BaseObject(Referenceable):
...
def generateNewId(self):
"""Suggest an id for this object.
This id is used when automatically renaming an object after creation.
"""
title = self.Title()
# Can't work w/o a title
if not title:
return None

# Don't do anything without the plone.i18n package
if not URL_NORMALIZER:
return None

if not isinstance(title, unicode):
charset = self.getCharset()
title = unicode(title, charset)

request = getattr(self, 'REQUEST', None)
if request is not None:
return IUserPreferredURLNormalizer(request).normalize(title)

return queryUtility(IURLNormalizer).normalize(title)

def _renameAfterCreation(self, check_auto_id=False):
"""Renames an object like its normalized title.
"""
old_id = self.getId()
if check_auto_id and not self._isIDAutoGenerated(old_id):
# No auto generated id
return False

new_id = self.generateNewId()
plone/i18n/normalizer/__init__.py
class URLNormalizer(object):
...
def normalize(self, text, locale=None, max_length=MAX_URL_LENGTH):
...
text = baseNormalize(text)
base = text # use text.lower() to make it lower-case

把原本 base = text.lower() 改成 base = text。

另外,Dexterity 則利用 Name From Title 的行為設定來啟用,參考 plone.app.content 裡 interfaces.py 的 INameFromTitle。

2010/11/24

Archetype Content Customization

在 Plone 4 初期,Archetype 還活著,它仍是自製表單的常見方式,即使日後要改用 Dexterity,也有機會透過 transmogrifier轉移內容。想要客製化 Archetype 表單,常見的工作是修改 view 和 edit 的程式碼,以 MyType 的 view 為例,它是由 base_view.pt 來管理 js、css、header、body、folderlisting、footer 六個 macro 設定值,預設會去拉 mytype_view.pt 來顯示,找不到的話就使用 base.pt 來顯示,base.pt 提供四個 macro 設定值,能夠滿足小規模的調整需求。原則上不需要修改 base_view.pt 內容,想要大規模地修改 view 動作,可以在 browser/configure.zcml 裡註冊新的顯示方式。
使用 ReferenceField 的話,要搭配 ReferenceBrowserWidget,它要從 archetypes.referencebrowserwidget 載入,利用 allowed_types=('Document',) 之類的設定值,可以限定項目種類,利用 allow_search=True 可以決定是否提供搜尋欄位。
想要修改 schemata 的話,是使用類似 schema.changeSchemataForField('related_category', 'reference') 這樣的語法。預設的 Dublin Core metadata 相關欄位,設定在 Products/Archetypes/ExtensibleMetadata.py 檔案裡,例如 rights 欄位的資訊:
TextField(
'rights',
accessor="Rights",
default_method='defaultRights',
widget=TextAreaWidget(
label=_(u'label_copyrights', default=u'Rights'),
description=_(u'help_copyrights',
default=u'Copyright statement.'),
)),
透過 default_method 可以指定預設的內容,例子裡的 defaultRights 就是呼叫 portal_metadata 的 listSchemas() 來比對是否存在預設值,而且在 Products/ATContentTypes/content/schemata.py 檔案裡,利用 finalizeATCTSchema() 來指派各欄位的 schemata 位置。
另外,使用 Plone 4 的朋友,記得參考新的網頁管理方式

2010/11/05

Transmogrifier - Import/Export Made Easy

collective.transmogrifier 是處理資料轉換或匯出匯入的工具,常被用來支援 Plone 網站資料的匯出匯入,但其他網站同樣可以引用。

成功測試的是使用 Plone 3.3.5 UnfiedInstaller 來安裝,修改 buildout.cfg 內容如下:
eggs =
Pillow
Plone
collective.transmogrifier
plone.app.transmogrifier
transmogrify.sqlalchemy
pysqlite
argparse
iw.debug

zcml =
collective.transmogrifier-meta
collective.transmogrifier
plone.app.transmogrifier
transmogrify.sqlalchemy
iw.debug

[versions]
Pillow = 1.2
SQLAlchemy = 0.6.5
argparse = 1.1
distribute = 0.6.14
iw.debug = 0.3
plone.app.transmogrifier = 1.1
pysqlite = 2.6.0
transmogrify.sqlalchemy = 1.0.1
collective.transmogrifier = 1.2
ipdb = 0.2
ipython = 0.10.1

使用時要搭配設定檔,例如建立一個 import_test.cfg 檔案,來處理資料庫內容的匯入。設定檔的格式跟 buildout.cfg 類似,第一個設定區段 [transmogrifier] 先指定要用到的 pipeline 項目,也就是轉換資料的過程,需要哪些分解動作。以匯入的例子來說,資料來源是資料庫的內容,存取的資料會依序在 pipeline 傳遞,最後寫進 ZODB 並且更新 Archetype 之類的表單內容。
[transmogrifier]
pipeline =
source
add_type
add_path
constructor
schemaupdater

每個 pipeline 項目又有對應的 section 設定值,其中的 blueprint 是 named adapter,而且慣例是以 package name 來命名,完全避免撞名的問題。以 [source] 的設定值為例,它是搭配 SQLite 來讀取資料,傳遞中的資料以 dict 格式來處理,以 [add_type] 為例,它會新增一組 '_type': 'MyContentType' 資料併入 dict 裡,再往下一個 pipeline 傳遞。
[source]
blueprint = transmogrify.sqlalchemy
dsn = sqlite:///database.sqlite
query = SELECT gid, name as title, organizer, address, tel FROM test

[add_type]
blueprint = collective.transmogrifier.sections.inserter
key = string:_type
value = string:MyContentType

[add_path]
blueprint = collective.transmogrifier.sections.inserter
key = string:_path
value = python:'/MyFolder/'+str(item['gid'])

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

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

資料來源並不限於資料庫,也可以是 CSV 檔案,除了匯出匯入資料外,也可以轉換文字編碼之類的動作。如果想套用 PostgreSQL 來源,安裝的模組檔案改為 psycopg2,並參考 transmogrify.sqlalchemy 的範例,改成 dsn = postgres://scott:tiger@localhost:5432/mydatabase 的設定值。

2010/10/24

Building Policy Package

客製化 Plone 網站的方式,有兩種途徑,一種是在網頁設定介面進行,稱為 TTW (Through The Web),另一種是在檔案系統裡撰寫程式碼,稱為 TTF (Through The Filesystem)。
撰寫程式碼的好處之一,是日後可以重覆沿用設定值,Plone 利用 GenericSetupZopeSkel 等機制,協助管理員建立客製化的 Policy Package 模組。以 UnifiedInstaller 安裝方式為例,開發中的模組程式碼要放在 src 目錄裡,建立工具是 paster 程式,步驟如下:
$ cd src
$ ../bin/paster create -t plone zopenfoundry.policy
Selected and implied templates:
ZopeSkel#basic_namespace A basic Python project with a namespace package
ZopeSkel#plone A project for Plone products

Variables:
egg: zopenfoundry.policy
package: zopenfoundrypolicy
project: zopenfoundry.policy
Expert Mode? (What question mode would you like?
(easy/expert/all)?) ['easy']:
Version (Version number for project) ['1.0']:
Description (One-line description of the project) ['']:
ZOpenFoundry Policy Package
Register Profile (Should this package register a GS Profile)
[False]: True
特別注意,Register Profile 要指定為 True。
再編輯 buildout.cfg 內容,在 develop = 指定 src/zopenfoundry.policy,並在 eggs = 和 zcml = 指定 zopenfoundry.policy,執行 bin/buildout 指令,通知系統已建立了新模組。要留意的是,試過把 package name 寫在 base.cfg 的結果,並不會讓系統找到新模組。
想要測試模組是否能被系統存取,可用 zopepy 工具來檢查:
$ bin/zopepy

>>> from zopenfoundry import policy
>>>
沒有發現 ImportError 的話,就是成功了。
接著,要在 profiles 目錄裡建立設定檔,如果 profiles 目錄還未被建立,可以手動完成:
$ cd src/zopenfoundry.policy/zopenfoundry/policy
$ mkdir profiles
$ mkdir profiles/default
在 profiles/default 目錄裡建立 properties.xml 檔案,內容如下:
<?xml version="1.0"?>
<site>
<property name="title">ZOpenFoundry Site Policy</property>
<property name="description">Welcome to ZOpenFoundry Site</property>
</site>
至此,就可以重啟系統,到 Plone Site Setup 把模組啟用。

2010/10/20

Permission to Search Member

預設在 Plone 的 Users 資料夾裡,未登入的使用者無法看到會員資訊,而被通知 You are not allowed to list portal members. 訊息,這是因為 Anonymous User 沒有 List portal members 的權限,處理方式之一,是到 ZMI 的 Security tab 調整。
http://lh3.ggpht.com/_BESgcgeL9eA/TL65nS0_HXI/AAAAAAAACak/TyQ9KImyqt4/s800/ListPortalMember.png
如果 Plone Site Setup 裡有選項,讓管理員能夠設定的話,會更理想。關於權限的基本說明,可參考 Basic Roles and Permissions in Plone

2010/10/07

EEA Faceted Navigation

Faceted Navigation (FacetedNav) 是 Plone 顯示搜尋結果的新方式,以搜尋結果為中心,使用者可以在週圍選擇查詢條件,結合 jQuery 後,它能夠更快速地回應搜尋結果。

完成測試的環境是 Plone 3.3.5,有人貢獻 Plone 4 patch,現在也有 collective repository for Plone 4

可以結合 Syndication (XML) 和 Cache (memcache) 提供進階應用。開發人員來自 EEA (European Environment Agency) 是歐盟提供環保政策資訊的單位,已有生產許多 Plone 模組

2010/09/25

BLOB Support for Plone

經過一番努力Plone 4 已預設支援 blob 大型檔案,像是 Image 和 File 這兩個內容型別,上傳後的檔案,會存放在 filesystem 裡,而不再是 ZODB 了。例如上傳一個 4MB 的圖檔,到 zinstance/var/blobstorage 目錄裡,可以發現這個檔案實際存放於此,至於這個圖檔的 metadata 或 history 資訊,仍然是存在 ZODB 裡。
$ ls -l var/blobstorage/0x00/0x00/0x00/0x00/0x00/0x00
/0x0f/0xa9/0x03891a0bd54aebbb.blob
-r-------- 1 marr marr 4815658 2010-09-25 12:27
0x03891a0bd54aebbb.blob
$ ls -l var/blobstorage/0x00/0x00/0x00/0x00/0x00/0x00
/0x0f/0xb2/0x03891a0be6c98b00.blob
-r-------- 1 marr marr 20868 2010-09-25 12:27
0x03891a0be6c98b00.blob
內建的內容型別可以立即享受上述的功能,舊版的 Archetype 內容型別,必須要先完成昇級才行。昇級的主要步驟,是使用 schema extender 把 FileField 換成 BlobField,再利用 plone.app.blob 的 migrate 工具程式來執行昇級。細節可參考範例

使用 Dexterity 的場合,可參考 developer manuanl 的範例

2010/09/14

Features and Tips for Plone 4

前陣子 Plone 4 正式推出,由於使用 Python 2.6,執行效率大幅提昇支援 blob 大型檔案,有效改善 ZODB 過度成長的問題,預設搭配 SunBurst 佈景主題,以及 TinyMCE 編輯器,操作更加容易。想要知道更多改進之處,不妨直接下載安裝,或是到 demo site 親身體驗。

與開發人員有關的調整,包括 develop.cfg 的使用,可參考 Creating a theme package with ZopeSkel 的範例,另外要留意的是,訊息檔的翻譯方式也有新方式。這些調整,其實也是為了繼續朝 Plone 5 前進。

2010/08/22

Ploneboard 2.1 with Plone 3.3.5

雖然 Plone 4.0 近期就要正式問世,最近還是先搭配 Plone 3.3.5 安裝了 Ploneboard 2.1b2。執行 buildout 安裝過程沒問題,但在 quick install 過程遇到 AttributeError: portal_placeful_workflow 的錯誤訊息,原因在於 CMFPlacefulWorkflow 是它的相依模組,但沒有自動隨之啟用,解決方法很簡單,就是手動先把 CMFPlacefulWorkflow 啟用,再啟用 Ploneboard 就行,這個動作也會順便把 SimpleAttachment 啟用。正式的解法已寫成 trunk,訊息翻譯的成果也要放在這裡。
http://lh3.ggpht.com/_BESgcgeL9eA/THEo2QMMneI/AAAAAAAACRo/mKHhjQ2BD2o/s800/ploneboard01.png
選擇想要新增討論區的目錄,以首頁為例,從新增項目的下拉選單找到 Message Board。
http://lh5.ggpht.com/_BESgcgeL9eA/THEo2408f-I/AAAAAAAACRs/PNZtm9y0Fpc/s800/ploneboard02.png
設定討論區的基本資料,包括標題、摘要描述、分類關鍵詞等。
http://lh3.ggpht.com/_BESgcgeL9eA/THEo274-6II/AAAAAAAACRw/2sdOyNzuOyg/s800/ploneboard03.png
討論區的基本樣貌已經具備,接著,點選 Add Forum 就可新增討論板。
http://lh4.ggpht.com/_BESgcgeL9eA/THEo263Lf5I/AAAAAAAACR0/gO-X4akuGfg/s800/ploneboard04.png
設定討論板的資料,包括標題、摘要描述、分類關鍵詞、附件檔案最大數量等。
http://lh5.ggpht.com/_BESgcgeL9eA/THEo3PhfBII/AAAAAAAACR4/OhFSUuKJkeo/s800/ploneboard05.png
討論板的預設狀態是 Requite membership to post,表示註冊會員在登入系統後才能張貼討論文章,新的討論主題稱為 conversation。
http://lh3.ggpht.com/_BESgcgeL9eA/THErS9miToI/AAAAAAAACSU/7PKQ4d3y59U/s800/ploneboard06.png
設定討論主題的資料,包括標題、內文、附件檔案等。
http://lh6.ggpht.com/_BESgcgeL9eA/THErS67Vr1I/AAAAAAAACSY/2C6E3nnqO24/s800/ploneboard07.png

2010/08/16

bzr Tip: These branches have diverged

Bazaar 是搭配 Launchpad 所用的管理工具,屬於分散式版本管理方式,由 Canonical 公司支援發展。
最近使用 bzr 2.0.1 的經驗,遇到 "bzr: ERROR: These branches have diverged. Use the missing command to see how. Use the merge command to reconcile them." 訊息,有撇步表示可以使用 bzr pull --overwrite 指令,再把 patch 重新擺回去。

2010/08/08

Plone Theme Resource Customizer

想要更換 Plone 網站的 logo 或 CSS 設定值,傳統方式是到後台介面 (也就是 Zope Management Interface, ZMI) 進行設定,現在由前台介面 (也就是 Plone Site Setup) 也可以搞定了。

安裝 plone.app.themeeditor 後,原本要到 portal_skins 或 portal_view_customizations 才能編輯的佈景主題資源檔案,現在都可以在 Plone Site Setup 裡統一進行修改,操作方式請見教學影片,甚至可以搭配 windmill 進行測試工作

2010/08/05

How Sahana Eden Uses Launchpad

幾天前看了 launchpad 的介紹,網頁講得像是天下無敵,因此就問 Fran 有打算完全使用 launchpad 來開發嗎? Fran 表示 launchpad 的 branch 支援很棒,但 ticket 並不能搭配 branch 來排序,如果 branch 數量不多是夠用,但 branch 數量多時,就亂到難以管理。
因此 Sahana Eden 目前只使用 bzr 及 launchpad 來管理 branch,但使用 trac 來管理 ticket,另外 trac 本來就有 wiki 能整理文件。
還有一點,Eden 借用 lauchpad 的 BluePrint 概念,但直接在 trac wiki 上撰寫 BluePrint。
或許日後 launchpad 會改善上述的排序問題,

2010/06/16

Setting Random Times with zope.sendmail

This is an abstract from How to add a random time between two emails sent from zope.sendmail.

需要搭配 Plone 架設 newsletter 的場合,可以使用 collective.dancing,它能夠管理訂戶資料,並利用 zope.sendmail 協助自動發信。如果訂戶數量過大,批次發信可能會被列入黑名單,此時,試試設定亂數時點,來處理這樣的問題。方式是撰寫 plone.smtp 程式碼:
import random
import time

from zope.sendmail import mailer

class SMTP(mailer.SMTPMailer):
"""Override SMTPMailer to let a random time"""

def send(self, fromaddr, toaddrs, message):
time.sleep(random.uniform(0,2))
super(SMTP, self).send(fromaddr, toaddrs, message)

2010/06/07

Tips on LeadImage and Folder View

Folder 預設的標準顯示方式,如果覺得單調,想要增加圖示,可以藉助於 Content Lead Image



使用 buildout 新增擴充模組的方法,就可以安裝 collective.contentleadimage,在 Site Setup 裡可以指定使用 lead image 的 content type,包括 Folder。指定後,在 Folder 的 display 選項裡,就看得到 Folder lead-image view。

如果逐行顯示目錄的方式,還是覺得單調,想要改成兩欄式,可以在 ploneCustom.css 裡調整 vevent 的 class 設定值。
.vevent {
float: left;
height: 137px;
width: 42%;
margin-left: 4em;
}
如果發現 History 的位置亂掉,可以到 ZMI portal_view_customizations 修改 plone.belowcontentbody.contenthistory (zope.interface.interface-plone.belowcontentbody.contenthistory),在上方新增一行:
<div class="visualClear"></div>
以上是 Through The Web (TTW) 的修改方式,如果要在程式碼裡調整,可以參考 Plone Manual dynamic views 的說明

2010/05/24

Developer Options for Plone 4

從 Plone 4 beta 版本開始,buildout.cfg 裡關於
開發者的設定值,被獨立放在 develop.cfg 檔案裡,如此一來,paster 和 ZopeSkel 之類的工具,預設就不會馬上安裝,要額外執行指令,才會安裝開發工具:
bin/buildout -c develop.cfg
安裝開發工具後,還可以測試寄送通知信的工作,這類開發測試的工作獨立出來後,可以進一步簡化一般使用者的安裝步驟。

2010/05/22

web2py I18N Translation Tool

web2py 是個類似 Django 或 Ruby on Rails 的 web application framework,使用案例之一是 Sahana Eden,Sahana 是一套災難管理系統,最早以 PHP 語言開發,後來有 Python 語言的分支版本,稱為 Eden,便是採用 web2py 開發架構。
web2py 以 Python dictionary 為基礎,有個處理介面訊息翻譯的內部引擎,在管理頁面就可以編輯訊息檔內容,檔案放在 applications 目錄裡專案的 languages 目錄。不過,要是想利用 PO 檔來管理訊息翻譯,就得搭配 web2py2po 工具,原始碼在 nursixsourceforge 上。

2010/05/04

Get Id of Items

My customized content types need a link passing its ID to another PHP script. This PHP script looks into database and performs based on the ID. Here is the sample page template:
<div tal:define="itemId context/getId;
itemIdUpper python: itemId.upper()">
<a href=""
tal:attributes="href python: 'http://another.site.com/some.php?k='+itemIdUpper;
title itemIdUpper;">Show in Another App</a>
</div>

2010/04/30

Plone 4 Way to Get Icon

昇級 Plone 4 的過程,發現有些 content type icon 沒有顯示,在 log 裡找到線索:
WARNING Plone Deprecation Warning
The icon for the 'controlpanel/DropdownConfiguration' action
was obtained from the action icons tool.
The action icons tool has been deprecated
and will be removed in Plone 5.
You should register action icons directly on the action now,
using the 'icon_expr' setting.
繼續追查後,原來 icon 存取方式已經改變,在 CMF 2.2.0-alpha 正式引入新的方式,而 Plone 4 使用 CMF 2.2.0 版本,自然也受到影響。如果到 ZMI 裡的 portal_actionicons 查看比較,會發現大有不同。

2010/04/29

Plone 4 Upgrading

目前 Plone 4 處於 beta 2 階段,測試 Plone 3 昇級的結果,還算順利,值得一提的訊息有:
INFO PythonScripts Some of your Scripts have stale code cached.
Since Zope cannot use this code, starup will be slightly slower
until these Scripts are edited.
You can automatically recompile all Scripts that have this problem
by visiting /manage_addProduct/PythonScripts/recompile
of your server in a browser.
ERROR PortalTransforms Cannot register transform rtf_to_html,
using BrokenTransform: Error Unable to find binary "rtf-converter"
工作原理和以前一樣,把 Data.fs 放到新版環境裡的 var/filestorage 再啟動,同樣有 dry run mode 提示,但整體的介面訊息變得更清楚。
目前遇到的狀況是 webcouturier.dropdownmenu 在昇級後,造成 LocationError: (None, 'html_tag') 的錯誤訊息,來源之一是 webcouturier/dropdownmenu/browser/dropdown_recurse.pt Line 29, Column 12。事先把 dropdownmenu 模組停用,就可以避掉這問題。
另外,可能會遇到 Default Theme 跳到 Unstyled 的狀態,只要進 plone_control_panel 或 @@skins-controlpanel 指定為佈景主題為 Plone Classic Theme 或 Sunburst Theme 就搞定。

2010/04/13

Changing Tabs with Viewlet

在 Plone 的 Navigation Settings 裡,利用 automatically generate tabs 的設定,可以在 portal-globalnav 區域自動產生根目錄的 tab 項目,如果只想限定目錄自動產生為 tab 項目,那就把 Generate tabs for items other than folders 取消勾選,如果某個自動產生的目錄 tab 項目,不希望它出現在 portal-globalnav 區域,除了取消自動產生 tab 項目的方式外,還可以動手修改 viewlet 來達成。

上述的 portal-globalnav 名稱,是從 CSS 角度來稱呼它,用 firebug 查得到它在 CSS 檔案的位置,如果是從 viewlet 角度來稱呼它,它的名稱是 global_sections。

http://lh4.ggpht.com/_BESgcgeL9eA/S8QE_SICNpI/AAAAAAAACCE/deyF4e5CmIA/s400/portal-globalnav_firebug.png

例如根目錄有個名稱為 tmp 的目錄,不希望它出現在 portal-globalnav 區域,到 portal_view_customizations 裡的 plone.global_sections 加個 tal:condition 就行:
<ul id="portal-globalnav">
<tal:tabs tal:repeat="tab view/portal_tabs">
<li tal:attributes="
id string:portaltab-${tab/id};
class python:view.selected_portal_tab==tab['id']
and 'selected' or 'plain'">
<a href=""
tal:condition="python: tab['id'] != 'tmp'"
tal:content="tab/name"
tal:attributes="href tab/url;
title tab/description|nothing;">
Tab Name
</a></li></tal:tabs>
</ul>
想要知道有哪些 viewlet 以及它們的位置,可使用 http://localhost:8080/Plone/@@manage-viewlets 之類的網址來查詢。

2010/04/08

Quotes of the Day

"The best way to keep learning after you leave school is surround yourself with the smartest people you can find."

"If you want to build a ship, don't drum up men to collect wood and don't assign them tasks and work, but rather teach them to long for the endless immensity of the sea."

"Understanding how to write well and effectively communicate the nuances of your thoughts is the most important thing you will learn in school."

"I would have written a shorter letter if I'd had the time." -- Mark Twain

"Great leaders are great teachers. And great teachers are great storytellers."

"If you want to be a leader, you will teach. The two are inseparable."

"As leaders, you learn more by listening than by talking. Listening makes you mor humble, more intuitive, and smarter. Talking does none of these things."

"When you listen, you learn how things work as opposed to when you talk and state how you thing things work."

"If you must talk, ask questions."

"If we have data, let's look at the data. If all we have are opinions, let's go with mine." -- Jim Barksdale

"You shouldn't be able to figure out the pecking order or org chart by looking at a product."

"There is no such thing as a minor lapse of integrity." -- Tom Peters

"I have no special talent. I am only passionately curious." -- Albert Einstein

"Nothing great was ever achieved without enthusiasm."

"Team are not juries locked in a room until they reach a unanimous verdict. Don't spend hours in endless meetings striving for unanimity. Consensus is not unanimity. Guide the team to declare when 'good enough' is better."

"The perfect is the enemy of the good, from which there is no progress."

"If everyone is thinking alike, then somebody isn't thinking." -- George Patton

"Where there is harmony, there is no innovation."

"Innovation comes from creativity. Creativity cannot be managed. It can be allocated, it can be budgeted, it can be measured, it can be tracked and encouraged, but it can't be dictated."

"If you're the innovator, you're like a virus. The antibodies want to kill you.
Leaders protect people from antibodies."

"A leader's job is not to prevent risk, but to build the capability to recover when failures occur. There is no such thing as a good failure and a bad failure. Or there is such a thing as a good failure and a bad failure. A good one happens quickly, and it provides plenty of lessons. Sometimes you have to look at these lessons in the data. A bad failure takes a long time and you don't learn anything. Leaders don't prevent failures. They prevent bad failures."

"Learn something new so that you can remember how hard it is to learn."

"Teach something so you can learn."

"Humility is correlated with age. Arrogance is inversely correlated with age."

"As smart leader, you surround yourself with great people. You people understand what they're doing better than you do."

"Good judgment comes from experience, and a lot of that come from bad judgment." -- Will Rogers

"Show me a team that never makes a mistake, and I'll show you a team that has never done anything innovative."

"Smart people can smell hypocrisy, so think before you act or speak. You have to commit to your team's goal and vision? They can tell when you don't really mean it."

"Setting an example is not the main means of influencing another, it is the only means." -- Albert Einstein

"If you would not work for yourself, why do your people?"

All are from Rules to Success by Jonathan Rosenberg.

2010/04/03

Plone3 Theme

概念Plone3 Theme 包括 template (page template)、stylesheet (CSS)、image 三種類型,以及 browser、skin 兩種機制。剛接觸 Plone 的朋友,先從修改 image 和 CSS 下手,比較容易入門。

以 CSS 為例,開發的準備工作之一,要將 Plone 執行模式設定成 debug mode 而非 production mode,也就是 parts/instance/etc/zope.conf 檔案的內容,要有下列的設定結果:
debug-mode on
這樣 CSS 修改結果才能馬上生效,而不會受到 cache 和 compression 的影響。

在 ZMI 的 plone_skins/plone_styles 裡,找得到 ploneCustom.css 檔案,預設是個空白範例,可以用來測試設定效果。想要修改它,先確定下拉選單的選項是 custom,再按 Customize 鈕,這樣會把這份灰底設定檔複製到 portal_skins/custom 目錄,並打開在網頁上等待編輯。值得留意的是,僅管下列內容看似是註釋文字,但它們是會被執行的程式碼,通常不該被刪除:
/* <dtml-with base_properties> (do not remove this :) */
/* <dtml-call "REQUEST.set('portal_url', portal_url())"> (not this either :) */

/* </dtml-with> */
除此之外的 /* */ 內容都是註釋文字,要刪要留,悉聽尊便。

例如,下列是一段可以讓版面置中對齊的設定範例:
#visual-portal-wrapper {
background: #aaaaaa url(&dtml-portal_url;/banner.jpg) no-repeat top left;
margin-left: auto !important;
margin-right: auto !important;
width: 900px !important;
}
還有 sectional CSS 的範例,可以單獨修改首頁的顯示方式。更多的資訊,可參考 Plone 3 Theming 書籍。

2010/03/31

Utilizing JavaScript in Plone Without Knowing

This is an abstract from Utilize Available JavaScript in Plone without Knowing JavaScript by Chrissy Wainwright.

使用 Plone 預設操作介面時,有些 script 已經在默默運作,更棒的是,只要粗略了解程式碼運作的原理,不必精通 Javascript 的細節,就可以搭配 portal_skins/plone_ecmascript 裡的 .js 內容,將頁面調整成我們想要的效果。

編輯內容時,想要把內容拆開在不同的 tab 頁面顯示,不必重傳網頁內容,這樣的效果稱為 FormTabbing 技巧。

form_tabbing.js 檔案就是達成上述效果的工具,下列的程式碼示範它的運作方式:

<dl class="enableFormTabbing">
<dt id="fieldsetlegend-unique-id1">button one</dt>
<dt id="fieldsetlegend-unique-id2">button two</dt>
<dd id="fieldset-unique-id1">content one</dd>
<dd id="fieldset-unique-id2">content two</dd>
</dl>

範例中的兩個按鈕,分別由 <dt> 標籤語法所指定,按鈕的 id 要以 fieldsetlegend- 字樣開頭,按鈕所對應的內容,分別由 <dd> 標籤語法所指定,其 id 則要以 fieldset- 字樣開頭。同樣的道理,上述程式碼的原理,也可以用於 <form> 標籤裡:

<form class="enableFormTabbing">
<fieldset id="fieldset-[unique-id]">
<legend id="fieldsetlegend-[same-id-as-above]">Title</legend>
</fieldset>
</form>

除此之外,還有 table_sorter.js 和 collapsiblesections.js 的範例,有興趣就去試吧。

2010/03/26

ZopeSkel local commands

UnifiedInstaller 是目前開發 Plone 的建議起步方式,特別是 Plone 3 UnifiedInstaller 把 buildout 整合後,系統的安裝和擴充都變得更方便了。
不過,最近想用 paster 來建立 archetype 專案,還是讓我遇到 local commands 找不到的問題。雖然應該不是 paster_plugin 的問題,但在 buildout.cfg 調整下列的設定值,同樣能解決我的困擾:
[zopeskel]
recipe = zc.recipe.egg
dependent-scripts = true
eggs =
PasteScript
ZopeSkel
initialization =
import paste.script.command
paste.script.command.system_plugins.append('zopeskel')
假設在 src 目錄裡,先用 paster 建立一個名稱為 myproject.mytype 的 archetype 專案,就可以在專案目錄裡執行 paster --help 看到 addcontent 等的 local command 資訊。

ZPublisher.Conflict ConflictError

My log contains the following message:

INFO ZPublisher.Conflict ConflictError at /VirtualHostBase/http/my.host.org:80/MySite/VirtualHostRoot/_vh_MySite/some-folder/mportal_factory/MyTypes/mytype.2010-03-26.5609762949/@@plone_lock_operations/refresh_lock: database conflict error (oid 0x02 d2e1, class BTrees._IOBTree.IOBucket, serial this txn started with 0x0385090b54cf6f88 2010-03-26 02:51:19.877413, serial currently committed 0x0385090b54cf6f88 2010-03-26 02:51:35.450306) (1 conflicts (0 unresolved) since startup at Fri Mar 26 03:55:41 2010)

It seems similar to the message in http://n2.nabble.com/ZPublisher-Conflict-ConflictError-td339922.html. In my case, a package webcouturier.dropdownmenu is used and 'Depth of dropdown menus' is set as 3, without enabling caching. Since there are more and more items added to the folder, 3 levels of dropdown menus bring us problem. After the level is set to 2, the page response time much improves. Problably the BTrees object is from cataloguing for dropdown menus.

ZServer uncaptured python exception

http://aspn.activestate.com/ASPN/Mail/Message/zope-list/3492748 提到,使用者臨時取消網頁存取動作,例如按了瀏覽器的停止鍵,會產生這類的訊息,一般是無須追究的情況,真想探究 exception 內容的話,要主動去接取它。如果伴隨系統資源吃緊的狀況,可用 'debug spinning zope' 關鍵字來查詢除錯技巧,或是查詢 spider 或 bot 的造訪狀況,必要時採用 iptables -A INPUT -s -j DROP 的激烈手段。

2010/03/13

User SubGroup and Associated Roles

Plone 3.2 版本之前的會員管理介面 prefs_users_overview 執行搜尋時,只能針對 full name 欄位進行比對,不能比對 id 欄位,另外也有 subgroup 與 role 的顯示問題,症狀主要記錄於 #9317#8940。目前已在 changeset 33533 完成修訂,並整理出新的管理介面。

2010/03/08

Fake Eggs ?

This is an abstract from Scrambled eggs by Martin Aspeli.

藉由 egg 機制,我們可以更順暢地進行模組軟體的包裝與散佈,但是,遇到 buildout 想要自動昇級某些模組時,為了滿足相依衝突時,egg 也可能帶來許多痛苦,我們會被一堆版本昇級要求資訊淹沒。

由於 Plone 3.0 到 3.1 搭配的 Zope 並沒有包裝成 egg 檔案,導致 setuptools 無從得知 Zope 3 模組的 egg metadata 資訊。舉例來說,如果你安裝一個相依於 zope.component 的模組,但是 setuptools 並不知道你已經有 zope.component 了,因此會試著下載最新版的 zope.component。由於新版 zope.component 的下載動作會引發其他相依關係,很容易造成永無止境的昇級要求。

為了解決上述問題,plone.recipe.zope2install 這個 recipe 預設會安裝一些 fake egg,它的效果,就像是建立一堆指到 parts/zope2/lib/python/* 的 egg。如此一來,setuptools 就能知道系統裡存在 zope.component、zope.interface 等標準模組,不需要再去下載新版檔案。

常見的情況是,我們只知道需要安裝某個檔案,但不清楚所需的明確版本,因此 fake egg 會指定為 0.0 版本,除非你為特定的 recipe 額外指定它的版本號碼。也就是說,如果有個模組的相依關係是 zope.component >= 3.1,setuptools 仍然會以為系統既有的 zope.component 版本過舊,將嘗試下載新的版本。

那麼,模組的相依資訊被記錄於何處? 它的來源大致有兩類,一是在模組檔案的 setup.py 內容裡,二是從 buildout.cfg 檔案的 eggs 設定值尋找。相依資訊的常見形式,又可分成兩類,第一類的例子是「我需要和 zope.component 一起工作」,只指定模組名稱,而不指定模組版本,第二類的例子是「我需要和 zope.component >= 3.4 版本一起工作」,同時指定模組名稱與版本條件。

有時,在 setup.py 裡指定最小版本號碼是必要的,因為某些版本以上的模組,才提供我們需要的功能。當然,在 setup.py 裡明確指定「我需要和 zope.component == 3.4 版本工作」是合法的設定方式,但通常會造成日後的災難,而指定最大版本號碼的方式,例如「我需要和 zope.component >=3.4, <=3.4.999 版本工作」,通常也是很糟的形式。一般而言,除非有必要,永遠不要在 setup.py 裡指定「==」的版本要求,頂多使用「<=」的版本形式。因為,這類不適當的設定值一旦出錯,除錯工作極度困難。

另一方面,目前 setuptools 的運作方式,是找尋最新版本來下載,如果它先下載了某個模組的新版本,就會順著它的相依關係來下載其他模組,即使某個舊版本提供更合適的相依條件,setuptools 並無從得知,這會導致下列問題:

* setuptools 下載過新版本的模組,和稍後模組的最大版本條件相衝突。幸好,這個問題並不難解決。

* 原本 buildout 運作正常,但是有人在 PyPI 上傳新版模組,造成相依關係更新,接著你又執行 buildout 更新動作,下載了新版模組,引發更多新版模組的下載。運氣好的話,系統會通知版本衝突的訊息,運氣不好的話,表面上 build 動作完成,但執行系統時卻失敗。

2010/03/03

Quote Portlet

想在 Plone 網站加上 quote portlet 嗎? 利用 collective.portlet.quote 輕鬆就能完成,它還有 Quote Folder 專門來放 Quote Type。不過,並不喜歡預設的方框樣版,想要改個樣子,於是動手修改兩個檔案。 修改 randomquote.pt 內容如下:
    <dd class="portletItem odd">
      <q tal:content="structure view/get_quote">
        Body text
      </q>
    </dd>
    <dd class="QuoteSource">
        <span class="portletBottomLeft"></span>
        <p tal:condition="not:view/has_link">
            –
            <tal:block replace="view/get_source"/>
        </p>
        <tal:block condition="view/has_link">
            <a tal:attributes="href view/get_link">
               –
               <tal:block replace="view/get_source"/>
            </a>
        </tal:block>
        <span class="portletBottomRight"></span>
    </dd>
新增 ploneCustom.css 內容如下:
.QuoteSource
{
    border-color: #8CACBB;
    border-width: 1px;
    border-style: none solid none;
    margin: 0;
    text-align: right;
    padding: 0.25em 1em;
}
有圖有真相。
http://lh4.ggpht.com/_BESgcgeL9eA/S438RlzopCI/AAAAAAAAB7A/k4WRSyV9TRU/s800/QuotePortlet.png

2010/01/22

Bus Rapid Transit

本文取自友人 AB 轉寄來的內容,略作極小的排版修改。我支持大眾交通系統應慎重考量低耗能高效率的方案選項,盲目地 MRT 並非民眾之福。另外,據說嘉義也有 BRT (Bus Rapid Transit),但跟巴西 Curitiba 的實例相比,仍有不小差距。

之前,我一直覺得捷運是很棒的東西,但是我這幾年再也不這麼認為。

先講公視獨立特派員上週的報導:1800億的夢(高雄捷運)
線上觀看:http://www.peopo.org/innews/post/49974

非常值得花時間看完的報導。

不過我已經看到有人評論:「搞捷運 眼光要看遠」

這些論點看似有理,但是其所謂的捷運,都只侷限在台灣目前獨立於路面外的軌道捷運(高架或隧道)。

請看看上述影片中另一種不一樣的捷運:BRT

其實對於捷運的反省,我也是從這幾年才開始。

首先是四年前我從《你,還在開車嗎?》這本書中,看到夏鑄九教授為這本書所寫的序提到:
今天,一般市民或許不瞭解,台北市的財力已經無法承擔繼續興建目前這個光鮮亮麗卻不符永續城市原則、已經過了時的七Ο年代的昂貴捷運系統了。而高雄,卻因為政治原因,比照台北,亦步亦趨。這就是交通部為何不得不在城市運輸政策上終結高、中運量的捷運系統,改推輕軌的原因。我們若不另外選擇電車(或者說,有軌電車)、公車與腳踏車等運輸工具,台灣的城市交通就還是一條不歸路。

—《你,還在開車嗎?》序,夏鑄九

而《你,還在開車嗎?》這本書也提到一種捷運的替代方案:BRT (Bus Rapid Transit) 系統:
借用巴西的 Curitiba 的經驗,告訴我們一個公共交通系統,若是擁有專用的巴士道、底盤低矮的巴士、路邊收費站,及高於路面的登車月台,功能將不輸於任何有軌的公共運輸設施,而成本卻祇有軌道捷運的一小部份。
—《你,還在開車嗎?》,Alan Thein Durning,p.113

那時候光憑文字很難想像什麼是BRT,直到後來看到公視的報導。

之後,某次和蠻野心足生態協會理事長文魯彬經過台北某個捷運工地,他就指著工地說捷運不好,我就問他為什麼?捷運不是很環保嗎?他說:
捷運也是高耗能的交通系統,只是不像汽車,污染立即可見,捷運的污染產生在林口發電場。

之後,我從潘翰聲的文章也讀到類似的評論:
而捷運耗電量極大,將汽車污染物從都市轉移到鄉村區的燃煤電廠,是環境不正義的課題,二氧化碳的減量效果應有詳實的量化分析。

不過該文章另一個論點更點出另一個問題:
更嚴重的是,捷運的經濟效益被誇大,本案(台北捷運南北線)所評估的效益高達六成是土地增值效益,簡直是花公家的錢幫財團炒地皮。

這是經濟問題,民代和民選地方首長、官員似乎都不願意告訴民眾:
台北捷運系統每公里平均造價60多億元,以目前搭乘率的票務收入,也僅能維持台北捷運公司的操作營運,無法回收建設成本,更遑論未來還需更大一筆維修老舊車體的費用。大概估算,搭台北捷運的乘客約得到政府交叉補貼2/3的票價,即一段20元的票價,政府已補貼使用者40元,原應向乘客收60元。其他縣市想有樣學樣,要求中央政府補助興建捷運,但大眾運輸網絡都不如台北,其便利性及效益就大打折扣,真怕最終淪為載蚊子的捷運。 BRT 每公里造價約莫數千萬元,真的低廉又環保。
http://zh.wildatheart.org.tw/archives/mrtaeaeiebrtcaeece.html

果然不幸言中:「高雄捷運通車不到兩年,已經虧掉60億」

台灣最大的問題是不敢跟私人運具(汽車)搶道,所以目前還沒有任何一條捷運是利用現有路面。而公車專用道的興建,都還要接受民代質疑:是否會因為車道數量縮減而影響到汽車的行車速度?

真是奇怪,要顧全到汽車不會塞車,早就是都市交通中被實證為不可行的交通策略,看看美國城市的道路面積擴張的程度,還是無法解決都市交通問題的事實即可得知。

而且,讓私人運具不方便(塞車、停車問題),才會讓大家轉用大眾運輸系統,這不是很簡單的道理嗎!

看著台北捷運局的捷運願景圖密密麻麻的捷運線路,只希望它不會成真。

2010/01/18

Dexterity vs Archetypes

This blog is an abstract from How is Dexterity related to Archetypes by Martin Aspeli.

DexterityArchetypes 的替代方案,這兩種方案都是 Plone 建構 content type 的框架技術。由於 Archetypes 存在已久,是成熟的方案,而 Dexterity 是後起之秀,結合新版 Zope 技術,有些新技術能夠有效處理以往難以解決的問題。

下列是幾項主要的差異:

* Dexterity 結合新版 CMF、Zope 3 的技術與功能,程式碼更精簡,測試工作更能自動且完整。
* Dexterity 具備更高的模組化程度,更容易整合 SQL 資料庫。
* Archetypes 自有的 Schema 機制,並無法完全相容於 zope.interface 或 zope.schema 的介面。
* Archetypes 使用 accessor 與 mutator 來處理設定值,Dexterity 則使用 attribute notation 方式來處理,因此 Archetypes 的寫法類似 context.getFirstName() 而 Dexterity 是 context.first_name 這樣的寫法。
* Archetypes 自有的 field 和 widget 配合 content object 的脈絡來運作,並不容易被應用在獨立表單,Dexterity 則使用 z3c.form 函式庫,這是表單運作的標準工具。
* Archetypes 並不支援新增表單,Dexterity 則透過 z3c.form 來支援,這代表它不需要用到 portal_factory,執行效率能夠改善。
* Dexterity 支援 behavior,但 Archetypes 沒有直接支援。

2010/01/14

Archetypes id-to-title Normalizer

Archetypes 的 content type 在新建時,需要決定 Id 並讓它成為 URL 的一部份,決定 Id 的函式可以先從 Products/Archetypes/BaseObject.py 的 generateNewId() 和 _renameAfterCreation() 查起,它們會配合 plone.i18n.normalizer.interfaces 的 IURLNormalizer 和 IUserPreferredURLNormalizer,並分別來自 INormalizer 和 IUserPreferredNormalizer。

實作例子有 ZopeChinaPakC2FileNameNormalizer,也有個 External Method 的例子