Content Rule 是 Plone 依據內容變更的條件,執行特定動作的規則,例如某個目錄裡新增了文件,就寄信通知管理員,類似 trigger 的概念。
設定通知信時,還可以指定變數,例如 ${contributor_emails} 代表系統及目錄所指派的貢獻者電子郵件,${owner_emails} 代表項目的擁有者電子郵件。

想要檢查 owner 設定值的話,可以在網址後面加上 /manage_listLocalRoles。
Content Rule 是 Plone 依據內容變更的條件,執行特定動作的規則,例如某個目錄裡新增了文件,就寄信通知管理員,類似 trigger 的概念。
設定通知信時,還可以指定變數,例如 ${contributor_emails} 代表系統及目錄所指派的貢獻者電子郵件,${owner_emails} 代表項目的擁有者電子郵件。

想要檢查 owner 設定值的話,可以在網址後面加上 /manage_listLocalRoles。
collective.googleanalytics 是提供 Google Analytics 資訊的 Plone 模組,啟用前最好先用同一個瀏覽器登入 Google 帳號,在 Plone Site Setup 裡就可以建立授權要求。

在 Tracking Profile 下拉選單裡,會看到 Google Analytics 的設定項目,再指定報表項目,就可以產生報表結果。報表項目分成 site wide 和 per page 兩大類,它們可以由 GenericSetup analytics.xml 檔案來新增,或是在 ZMI portal_analytics 裡新增。

我們可以新增 portlet 來顯示報表,不過預設的選項都是 Page 類型的報表,想要增加 Site Wide 的報表,可到 ZMI portal_analytics 調整 Categories 選項。
In addition to Plone instances, we have PHP applications heavily relying on PostgreSQL. Then there is a need to have them sharing user accounts. Luckily Zope2 product PluggableAuthService (PAS) serves such needs well. For our case pas.plugins.sqlalchemy is an ideal fit.
With instructions, my working installation is for:
Ubuntu = 12.04
postgresql-server-dev = 9.1
Plone = 4.1.5
SQLAlchemy = 0.7.7
pas.plugins.sqlalchemy = 0.3
psycopg2 = 2.4.5
z3c.saconfig = 0.13
zope.sqlalchemy = 0.7
Here are some hints worth notice. First I add lines in devlop.cfg:
eggs +=
psycopg2
pas.plugins.sqlalchemy
zcml +=
pas.plugins.sqlalchemy
And the "zcml-additional" parameter goes in [instance] section of base.cfg:
zcml-additional =
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:db="http://namespaces.zope.org/db">
<include package="z3c.saconfig" file="meta.zcml" />
<db:engine name="pas" url="postgresql://postgres:mypass@localhost/plonepas" />
<db:session name="pas.plugins.sqlalchemy" engine="pas" />
</configure>
If you use postgres instead postgresql in the URL format, a warning will be added in the log:
SADeprecationWarning: The SQLAlchemy PostgreSQL dialect has been renamed from 'postgres' to 'postgresql'. The new URL format is postgresql[+driver]://<user>:<pass>@<host>/<dbname>
Run bin/buildout -c devlop.cfg and activate SQLAlchemy PAS in Site Setup.

Now let's see how it works. By default, the admin creates an account, and the account data will appear in ZMI /mysite/acl_users/source_users.

Go /mysite/acl_users/plugins, check User_adder Plugins and move sql up as the first Active Plugin, this will switch account data stored in SQL tables.

Check /mysite/acl_users/sql for more info about SQLAlchemy user/group/prop manager.

Here is the list of relations:
$ psql -d plonepas
psql (9.1.3)
Type "help" for help.
plonepas=# \d
List of relations
Schema | Name | Type | Owner
--------+--------------------+----------+----------
public | group_members | table | postgres
public | groups | table | postgres
public | principals | table | postgres
public | principals_id | sequence | postgres
public | role_assignment_id | sequence | postgres
public | role_assignments | table | postgres
public | users | table | postgres
(7 rows)
plonepas=# \d users
Table "public.users"
Column | Type | Modifiers
------------------+-----------------------------+-----------
id | integer | not null
login | character varying(64) |
password | character varying(64) |
salt | character varying(12) |
enabled | boolean | not null
email | character varying(40) |
portal_skin | character varying(20) |
listed | integer |
login_time | timestamp without time zone |
last_login_time | timestamp without time zone |
fullname | character varying(40) |
error_log_update | double precision |
home_page | character varying(40) |
location | character varying(40) |
description | text |
language | character varying(20) |
ext_editor | integer |
wysiwyg_editor | character varying(10) |
visible_ids | integer |
Indexes:
"users_pkey" PRIMARY KEY, btree (id)
"ix_users_login" UNIQUE, btree (login)
"ix_users_email" btree (email)
"ix_users_enabled" btree (enabled)
"ix_users_fullname" btree (fullname)
Foreign-key constraints:
"users_id_fkey" FOREIGN KEY (id) REFERENCES principals(id)
source_users and sql plugins can co-work to provide authentication service.
ZMI portal_css 取消勾選 print.css 儲存設定後,列印樣式會有不同,設定前後的樣式如下:


有個 Print All 擴充模組,可以把目錄裡的所有項目,分頁顯示各項目的內容,例如新聞目錄的多則新聞,就可以一次印完。
想要 hardcopy 的列印效果,看來還是使用 print screen 的軟體。
Walking Papers 是 OpenStreetMap 的衍生服務,它讓地圖資訊的更新有了新途徑,藉此希望大幅降低 OSM 資訊更新的門檻。應用的工具包括了 Scale Invariant Feature Transform 和 QR Code 等。
服務網站的程式碼是 paperwalking,附的文件說明了 Ubuntu 9 環境的安裝方式,下列是對應的 CentOS rpm 資訊:
curl vim-enhanced screen tcsh sudo git
python-imaging numpy java-openjdk
php-gd php-mysql mysql-server php-pear gdal-python gdal
一時沒找到 python-pyproj 的 rpm,必要時要改用 pip 去裝,另外也遇到抱怨 libgeos.so.2 相依問題的訊息。看來要在 CentOS 5.8 成功安裝是要花一番工夫。
我實際上是在 Ubuntu 11.04 安裝成功,要留意的地方是 Smarty 下載網址改變了,原本寫死在 site/lib/Makefile 裡的程式碼要對應修改。啟動系統前,要編輯 site/lib/init.php 檔案內容,至少要修改的有 TZ=Asia/Taipei, DB_DSN, API_PASSWORD, DEFAULT_LATITUDE, DEFAULT_LONGITUDE, DEFAULT_ZOOM 等設定值,想要新增地圖服務,要修改 TILE_PROVIDERS 設定值,格式是 URL, tab, ServiceName。
其中離線編輯圖檔的工作,由 decoder/poll.py 執行,在 decoder/compose.py 檔案裡有 geotiff, print_url, preview_url 的處理函式,網頁顯示的主檔案在 site/templates/index.html.tpl,顯示 PDF 檔案的網頁模版在 site/templates/en/print-info.htmlf.tpl,處理 PDF 位置 (pdf_url) 的網頁在 site/lib/data.php,產生 PDF 檔案的程式碼在 site/lib/composition.php 裡,包括標頭文字:
$pdf = new FPDF( \
get_page_orientation($print['paper']), \
'pt', get_page_size($print['paper']));
$pdf->addPage();
...
$pdf->text(62.61, 68.49, 'Waling Papers');
...
$print['pdf_url'] = \
post_file("prints/{$print['id']}/walking-paper-{$print['id']}.pdf", \
$pdf_content, 'application/pdf');
不需要建立模組,在 ZMI 新增 Python Script 就能在 Plone 環境執行簡單的查詢工作,例如顯示 News Item 的基本欄位資料。
from Products.CMFCore.utils import getToolByName
request = container.REQUEST
catalog = getToolByName(context, 'portal_catalog')
path = '/mysite/news'
for brain in catalog(portal_type='News Item', path=path):
try:
obj = brain.getObject()
print "%s, %s, %s, %s" % \
(brain.getPath(), obj.Title(), obj.Contributors()[0], obj.EffectiveDate())
except:
pass
return printed
上述執行結果,是方便另存成 CSV 檔案格式。
from Products.CMFCore.utils import getToolByName
catalog = getToolByName(context, 'portal_catalog')
areas = ['TaipeiCity', 'NewTaipeiCity']
path = '/mysite/myfolder'
for i in areas:
brains = catalog(portal_type='Image', path=path+'/'+i)
print "%s: %d" % (i, len(brains))
print "Total: %d" % len(catalog(portal_type='Image', path=path))
return printed
If the sun should tumble from the sky
If the sea should suddenly run dry
If you love Python, really love Python
Let it happen, I do care.
If it seems that everything is lost
I will smile and never count the cost
If you love PyCon, really love PyCon
Let it happen, darling I do care
Shall I catch a shooting star
Shall I bring it where you are
If you want me to, I will.
You can set me any task
I'll do anything you ask
If you'll only love Python still.
When at last, our life on earth is through
I will share eternity with you
If you love Python, really love Python
Then whatever happens, I won't care
When at last, our life on earth is through
I will share eternity with you
If you love PyCon, really love PyCon
Then whatever happens, I won't care
建立新的內容型別後,最基本的啟用方式是到 Plone Setup 用 QuickInstall 來生效,另一種方式,是到 ZMI 的 portal_setup 透過 Import 指定 import step 來生效。
下列是幾個可能相關的 import step 項目:
假設有個名稱是 my.packages 的模組,用了一陣子,產生許多 MyType 的內容項目,想要把它改名為 my.package,下列是處理經驗記錄。
第一次的嘗試,是在 src 目錄裡把 my.packages 複製並修改成 my.package,停用 my.packages 再啟用 my.package 模組後,存取 http://localhost:8080/mysite/myfolder/my-item 時,會遇到 <persistent broken my.packages.content.mytype.MyType instance '\x00\x00\x00\x00\x00Un^'> 訊息。當然,使用 migration 程式碼是個方法,但實在是殺雞用牛刀。
有個輕量級的方案,是先在模組的 __init__.py 裡用 sys.modules 來建立相容資訊:
import sys
sys.modules['my.packages'] = sys.modules[__name__]
不過,我的模組還額外有用到 content type based portlet,它同樣把舊的模組名稱記到 instance 裡,在 https://dev.plone.org/ticket/7375 有提到處理方法,試了沒成功,有成功的方式是到 @@types-controlpanel 先取消 portlet 的設定值,再啟用 my.package 模組。這時候出現一些警告訊息,但是系統看起來運作正常,應該是提供相容資訊後的暫時訊息:
INFO Archetypes
ArchetypesTool: Trying to register "my.package.MyType" which has already been registered. The new type my.packages.content.mytype.MyType is going to override my.package.content.mytype.MyType
再到 ZMI 裡 archetype_tool,執行 Update Schema 後,希望這樣就算是把 instance 更新了。
想在執行 buildout 時,自動啟用或更新 Plone Site 的擴充模組設定值,可以藉助 collective.recipe.plonesite 來控制。下列是執行成功時的訊息範例:
Added Plone Site
Quick installing: []
Running profiles: []
Finished
Running profiles: ['first.product:default', 'second.product:default']
Archetypes 的 ReferenceField 搭配 ReferenceBrowserWidget 可以選取參照項目,不過問題之一,是重複的有序項目只能被選擇一次。後來想出一個變通方法,是建立一組內容型別,第一個內容型別具備目錄屬性,用來包含另一個內容型別,花點工夫改善顯示方式就行。
接著按照範例,透過 reference_catalog 的 getBackReferences() 可以顯示 bidirectional reference 的結果,不過,除了直接記錄 reference 資訊的項目外,我們也想顯示上一層的資訊。
在之前的討論裡提到,如果物件是繼承 Acquisition.Implicit 或 Acquisition.Explicit 而來,稱之為 Acquisition aware,它們就能使用 context.aq_parent 或 context.aq_inner 功能,除此之外的物件,要用下列的方法才行:
from Acquisition import aq_inner, aq_parent
parent = aq_parent(aq_inner(context))
在 Products.ATContentTypes/skins/ATContentTypes/unittestGetTitleOf.py 看得到四種範例:
## Script (Python) "unittestGetTitleOf"
##title=Helper method for function tests
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=
return "%s,%s,%s,%s" % (
context.title_or_id(),
context.aq_parent.title_or_id(),
context.aq_inner.title_or_id(),
context.aq_inner.aq_parent.title_or_id())
jQuery prepOverlay 在 Plone 裡經常被使用,它是透過 plone.app.jquerytools 提供 AJAX form helper 服務,像 tile 就可以結合這項工具,提供更具彈性的版面設計。

"Everyone can be taught to sculpt: Michelangelo would have had to be taught how not to. So it is with the great programmers." -- Alan Perlis
當我要回答「為什麼喜愛 Python」這問題,最好是配合提問者的背景。
對於沒接觸任何程式語言的入門者,我會說: 入門 Python 很容易,而且很有趣。
對於某種語言邊用邊幹譙的朋友,我會說: Python 支援豐富,讓你快速處理問題核心。
對於某種語言用得很愉快的朋友,呃,大概不會問我「為什麼喜愛 Python」。
那麼,我又如何對自己回答這個問題呢?
「我想要一個用起來直覺,十年百年之後,還能用來解決問題的程式語言。」
可見的未來,越來越多人需要使用 scripting 語言,客製化自己的應用程式,想像我們要解決某個問題,總不希望解決問題的工具本身也帶來太多問題,學習新程式語言的成本,應該要越低越好,帶來的效益,應該要越高越好。
如果一個人只能選一種程式語言來學習,Python 當然是很好的選擇,如果要學習不只一種程式語言,Python 的基礎也對其他語言有所幫助。Peter Norvig 在 Teach Yourself Programming in Ten Years 裡提到,學習程式語言的核心,在於思惟,而不在於語法,一個程式語言如果不能帶來新的思惟,不值得花時間去學它。
"There should be one -- and preferably only one -- obvious way to do it." -- The Zen of Python
讓世界變得更美好,有很多事情該做,PyCon Taiwan 就是其中之一。你也不要錯過。
collective.contentleadimage 可以為 Plone Content Type 新增一個 lead image 欄位,上傳圖檔來改善內容項目的顯示效果。不過,在管理編輯的 Content 頁籤裡,lead image 還是會顯示出來,這就造成畫面的困擾。
在處理顯示工作的 page template 裡,像是 collective.contentleadimage/borwser/leadimage-body.pt 檔案,加上 not context.REQUEST['URL'].endswith('folder_contents') 的判斷式,應該有助改善這問題。
Plone 裡使用 collective.flowplayer 來嵌入影片,如果檔案放在 dropbox 之類的空間,可以試用下列的方式,先在 Page 裡編輯下列的 HTML 內容:
<object data="http://mysite.com/++resource++collective.flowplayer/flowplayer.swf"
height="214" type="application/x-shockwave-flash" width="280">
<param name="data"
value="http://mysite.com/++resource++collective.flowplayer/flowplayer.swf" />
<param name="allowfullscreen" value="true" />
<param name="allowscriptaccess" value="always" />
<param name="flashvars" value="config=http://mysite.com/my-folder/config.js" />
<param name="src"
value="http://mysite.com/++resource++collective.flowplayer/flowplayer.swf" />
</object>
然後到 ZMI 的 my-folder 目錄裡,建立 config.js 檔案,內容如下:
{'clip':
{'scaling':'fit',
'autoBuffering':true,
'autoPlay':true,
'baseUrl':'http://dl.dropbox.com',
'url':'http://dl.dropbox.com/u/myid/myfile.flv'
},
'canvas':{'backgroundColor':'#112233'},
'plugins':
{'controls':
{'time':true,
'volume':true,
'fullscreen':true
},
'content':
{'url':'flowplayer.swf',
'html':'Flash plugins work too'}
}
}
原本運作正常的 CMF Action 編輯表單的程式碼如下:
<input type="text"
name="new_author:list"
value="author"
size="40"
tal:condition="canModifyItem"
tal:attributes="value obj/getAuthor;
id string:${item}_author;" />
現在要改成 authors 資料,也就是 LinesField 欄位格式,根據提示,<input; /> 要改用 <textarea />,找到 Products/CMFDefault/skins/zpt_content/metadata_edit_template.pt 裡面有範例:
<th i18n:translate="">Contributors</th>
<td tal:define="contrib_lines python: '\n'.join(options['contributors'])">
<textarea name="contributors:lines" rows="5" cols="31"
tal:content="contrib_lines"></textarea>
</td>
這個問題原本在 http://stackoverflow.com/questions/7034187/change-tinymce-default-image-dimension 提出,當時 Plone 4.0.7 環境按照回答去改,並沒有成功,後來 Plone 4.1.3 環境有成功。暫時懷疑是 Products.TinyMCE 有更動,有空再比對了。
不過,還有另一種處理途徑,是修改 Products/TinyMCE/skins/tinymce/plugins/ploneimage/js/ploneimage.js 的內容,把
if (nd.value == dimension) {
nd.selected = true;
}改成
if (dimension != '' && nd.value == dimension) {
nd.selected = true;
}
if (dimension == '' && nd.value == '@@images/image/preview') {
nd.selected = true;
}