2012/12/28

Add Author Name to Collection Portlet

By default the Plone collection portlet will display item's title only. We might want add other info for the item listing. Here is an example to add Author Name in plone.portlet.collection/collection.pt:

     itemUrl    python:useView and obj.getURL() + '/view' or obj.getURL();
-    item_icon python:getIcon(obj);"
+    item_icon python:getIcon(obj);
+    itemCreator python:obj.getObject().Creator();
+    author python:context.portal_membership.getMemberInfo(itemCreator);
+    authorName python: author and author['fullname'] or itemCreator"

Note that using Creator() instead of Creator, or you will fail when passing itemCreator to portal_membership.getMemberInfo(). This is adapted from codes in plone.app.layout/viewlets/content.py

Prof. Chen Interview by PanSci

Quotes from the interview by PanSci

如果學術界不被論文發表綁架,培養出的優秀學生可以在社會各角落,像是檢驗公司,成為專業又公正的檢驗員,未必要留在學校作學術研究;只要不背離知識的核心價值,就能成為校正社會的力量。

就像我年輕的時候讀過的《天地一沙鷗》,族群裡總有幾隻個體認為自己的翅膀是為了某些更偉大的事而存在的,不該留自己在地上;人類社會也是如此,我認為大學裡頭這樣的個體應該比例上要多一點。

如果全台灣的大學-包括台大的研究背後真正的目的都是為了教學,而不是學術發表,那我們的視野會變得很不一樣。

2012/12/18

Plone Template Global Variables

It is a common task to get information about the state of your site and other global variables. For Plone 4+, the recommended approach is to use one of the following browser views:

  • @@plone_portal_state
  • @@plone_context_state
  • @@plone_tools

You can find how they are defined in plone.app.layout/globals/configure.zcml:

<browser:page
   name="plone_tools"
   for="*"
   permission="zope.Public"
   class=".tools.Tools"
   allowed_interface=".interfaces.ITools"
   />

<browser:page
   name="plone_context_state"
   for="*"
   permission="zope.Public"
   class=".context.ContextState"
   allowed_interface=".interfaces.IContextState"
   />

<browser:page
   name="plone_portal_state"
   for="*"
   permission="zope.Public"
   class=".portal.PortalState"
   allowed_interface=".interfaces.IPortalState"
   />

For example, a straight way to add the homepage link:

<a href="http://www.yoursite.com/">home</a>

This works only for the site running on the specific domain name. When testing, you are likely to run on localhost or IP address, that the homepage URL needs updated accordingly. The recommended style looks like this:

<a href="#"
   tal:define="home_url context/@@plone_portal_state/portal_url"
   tal:attributes="href home_url">home</a>

2012/12/04

Ushahidi on Ubuntu

這次裝的是 Ushahidi 2.6.1 版本,想從 git 下載安裝的話,可以試試其他文件說明

在 Ubuntu 12.04 大致裝了這些:

$ apt-get install mysql-server
$ apt-get install php5-mysql php5-curl php5-gd php5-mcrypt php5-imap

搭配 Apache 的話,可以設定 sites-available/default 的內容:

<Directory “/var/www/ushahidi”>
    AllowOverride All
</Directory>

還要啟用 mod_rewrite 模組才能使用 Clean URLs 功能。

設定好 MySQL 帳號權限後,差不多就可以透過瀏覽器執行安裝步驟。

Error!
This deployment spans within one country only.
Please make sure the report location is within the country Kenya.

太急著要新增表單的話,可能會遇到上述訊息,到 http://your.site.org/ushahidi/admin 進入管理介面,設定地圖等參數值,想新增自己的圖層,可以編輯 application/helpers/map.php 找 Generate the Map Base layer Object 內容,依樣畫葫蘆。首頁的內容可以先查看 themes/default/views/main/layout.php 檔案。

訊息翻譯檔案位於 application/i18n 目錄裡,用不到的語系檔案可以直接刪掉,語系選單可以到 application/helpers/ush_locale.php 找,操作介面的翻譯內容可以到 application/i18n/zh_TW/ui_main.php 找。

2012/11/19

Plone Theme Upgrade

記錄 Plone 3 昇級到 Plone 4 的經驗,特別是處理舊式 Theme 的情況。先到 ZMI portal_view_customization, portal_skins/custom 如果有客製化的程式碼,或是有相依的 portlet 設定值,都要先移除,再到 Plone Setup 設定 Theme 為 Plone Default,再到 Add-ons 把舊的模組取消安裝。

備份 Data.fs 以防意外,把這個 Plone 3 的 Data.fs 搬到 Plone 4 的新系統 var/filestorage 目錄裡,啟動系統後,到 ZMI 執行 portal_migration。到 portal_skins 的 Properties 把舊模組的 Skin 設定值刪除。

如果不幸遇到 PicklingError: Can't pickle <class 'Products.OldPkg.browser.interfaces.IThemeSpecific'>: import of module Products.OldPkg.browser.interfaces failed,只好試試 wildcard.fixpersistentutilities

2012/11/17

plone.app.jquery Upgrade

Plone 4.2.2 預設的 plone.app.jquery 版本是 1.4.4,想要單獨昇級 jQuery 版本的話,可以在 versions.cfg 直接指定 plone.app.jquery = 1.7.1.1,再執行 buildout:

$ bin/buildout -c develop.cfg
Getting distribution for 'zc.recipe.testrunner==1.2.1'.
zip_safe flag not set; analyzing archive contents...
Got zc.recipe.testrunner 1.2.1.
Uninstalling zopeskel.
Uninstalling zopepy.
Uninstalling instance.
Installing _mr.developer.
Generated script '/home/marr/plone422/zinstance/bin/develop'.
Installing instance.
Getting distribution for 'plone.app.jquery==1.7.1.1'.
Got plone.app.jquery 1.7.1.1.
Generated script '/home/marr/plone422/zinstance/bin/instance'.
Installing zopepy.
Generated interpreter '/home/marr/plone422/zinstance/bin/zopepy'.
Installing zopeskel.
Generated script '/home/marr/plone422/zinstance/bin/zopeskel'.
Generated script '/home/marr/plone422/zinstance/bin/paster'.
Installing omelette.
Updating backup.
Updating chown.
chown: Running echo Dummy references to force this to execute after referenced parts
echo /home/marr/plone422/zinstance/var/backups
chmod 600 .installed.cfg
find /home/marr/plone422/zinstance/var -type d -exec chmod 700 {} \;
chmod 744 /home/marr/plone422/zinstance/bin/*
Dummy references to force this to execute after referenced parts
/home/marr/plone422/zinstance/var/backups
Installing test.
Generated script '/home/marr/plone422/zinstance/bin/test'.
Updating repozo.
Updating unifiedinstaller.
*************** PICKED VERSIONS ****************
[versions]

*************** /PICKED VERSIONS ***************

直接或間接用到 jQuery 的模組至少有:Products.CMFPlone, collective.z3cform.datetimewidget, plone.app.jquerytools, plone.app.controlpanel, plonetheme.sunburst, archetypes.querywidget。

PloneTheme Bootstrap

Responsive Design 已成顯學,也有人把 Bootstrap 和 Plone 整合,範例成果像是 diazotheme.bootstrapplonetheme.bootstrap

plonetheme.bootstrap Screenshot

由於 Diazo 技術已經成熟,視覺設計成果可以壓成 zip 檔,上傳給 Plone 直接套用。專門展示 Plone Theme 的 plonethemes.org 網站,同時也收集這些 zip 檔,方便設計師和工程師進行整合工作。

2012/11/14

Sahana Internship Program

Sahana Software Foundation 在 2012 十二月到 2013 三月,推出線上實習計畫讓全球學生參與,在教練的指導下,實習生將學習完成數個軟體開發任務,整個實習期間,平均每週花上四小時,順利結業的話,會頒與實習證明和美金 500 元。

實習名額有三位:包括兩名軟體開發實習生,一名媒體行銷實習生。原則上要開發的是 Sahana Eden 系統軟體,它是以 Python 語言及 Web2Py 框架寫成,學習過程將認識這些細節。

有興趣報名的學生,請將履歷及下列問題的資訊,一併寄到 internship@sahanafoundation.org:

  • 申請實習的理由是?
  • 自認有哪些經歷可能有助於實習工作?
  • 實習期間有何承諾與規劃?
  • 想申請軟體開發實習的話,請附上 GitHub 個人網址。
  • 想申請媒體行銷實習的話,請附上個人文件連結,或是 blog 和參與社群的資訊。

當然,事先了解更多關於 Sahana 人道救援系統的資訊,肯定對申請實習會有幫助。

2012/11/05

Plone True Gallery

collective.plonetruegallery 是 Plone 展示圖檔的優秀模組,在 2.1 版本之後支援 Products.Collage,頁面的展示效果就更加豐富。

Flickr 和 Picasa 相簿的整合,是另一個很棒的功能,新版仍然保有這項特色,但程式碼將被獨立出來,要到 collective.ptg.flickrcollective.ptg.picasa 尋找安裝。

2012/10/30

Quotes Of The Day

Summarized from http://bytes.com/topic/python/answers/28338-python-productivity-gain

They say that when you have a hammer, everything looks like a nail. When using Python, most everything is a nail.

using a tool like Python doesn't just influence the programmers, but the whole project! If prototyping and coding in general becomes significantly faster, the trade off for how much analysis and design you should do will change. Why spend weeks at a conference table arguing about different design alternatives if the programmers can supply several different implementations within a day?

Python is designed with the objective of making it easy to do the right thing, rather than making it difficult to do the wrong thing.

And Python just "felt" clean. I can't explain it very well, but Python's syntax just never got in the way of my reading, which left me free to concentrate all my attention on what the code was actually doing.

2012/10/26

Plone Isotope jQuery Plugin

isotope 是一個 jQuery plugin,可以呈現炫麗的網頁畫面。下列是我把範例檔案嵌進 Plone 網站的心得和步驟。
解開 isotope-site.zip 原始檔後,先觀察目錄結構和 index.html 內容:
isotope-site/
├── CONTRIBUTING.mdown
├── README.mdown
├── css
│   └── style.css
├── custom-layout-modes
│   ├── big-graph.html
│   ├── category-rows.html
│   ├── centered-masonry.html
│   ├── masonry-column-shift.html
│   ├── masonry-corner-stamp.html
│   ├── masonry-gutters.html
│   └── spine-align.html
├── demos
│   ├── adding-items.html
│   ├── basic.html
│   ├── combination-filters.html
│   ├── elements-complete.html
│   ├── elements-partial.html
│   ├── filtering.html
│   ├── fluid-responsive.html
│   ├── hash-history.html
│   ├── images.html
│   ├── infinite-scroll.html
│   ├── layout-modes.html
│   ├── relayout.html
│   ├── removing.html
│   └── sorting.html
├── docs
│   ├── adding-items.html
│   ├── animating.html
│   ├── extending-isotope.html
│   ├── filtering.html
│   ├── hash-history-jquery-bbq.html
│   ├── help.html
│   ├── introduction.html
│   ├── layout-modes.html
│   ├── license.html
│   ├── methods.html
│   ├── options.html
│   └── sorting.html
├── index.html
├── jquery.isotope.js
├── jquery.isotope.min.js
├── js
│   ├── fake-element.js
│   ├── jquery-1.7.1.min.js
│   ├── jquery.ba-bbq.min.js
│   ├── jquery.infinitescroll.min.js
│   └── make-big-graph-projects.js
├── pages
│   ├── 2.html
│   ├── 3.html
│   ├── 4.html
│   ├── 5.html
│   └── 6.html
└── tests
    ├── callbacks.html
    ├── combo-sort-history.html
    ├── destroy.html
    ├── elements-complete-test.html
    ├── flash.html
    ├── index.html
    ├── item-position-data.html
    ├── jquery-animation.html
    ├── no-items.html
    ├── onlayout.html
    ├── right-to-left.html
    ├── tiny-text.html
    └── unclickable-filtered.html

7 directories, 62 files
首先,把 JavaScript 和 CSS 檔案上傳到 ZMI 的 /portal_skins/custom 目錄,至少有三個檔案:

  1. jquery-1.7.1.min.js - 這個檔案在原始檔的 js 目錄裡找得到,從 ZMI 下拉選單點選 File 來新增。
  2. jquery.isotope.min.js - 仿照上述方式上傳。
  3. style.css - 這個檔案在原始檔的 css 目錄裡找得到,從下拉選單點選 DTML Method 來新增。由於 Plone 原本也有 style.css 同名稱的檔案,為避免衝突,我改用 isotope.css 名稱來上傳。
再到 ZMI 的 portal_css 新增 stylesheet 設定值,在這個例子裡,名稱就是上述新增的 isotope.css 檔案。

接著,要選一個「網頁」位址,來實際展示畫面,實作方法很多種,以根目錄為例,我是在 ZMI 根目錄裡新增一個 Page Template,請留意,這裡的 Id 要指定為 index_html,系統發現目錄裡有這類名稱時,會優先執行它,index_html 內容先複製自 Sunburst main_templage.pt 檔案,可參考這系列 gist 的第一次版本

這時候的畫面,很可能會變得清爽,主內容的區塊全部清空了:

我們想把 Plone Demo 字樣換掉,也就是改用新標題。從 ZMI 根目錄點選剛才建立的 index_html 檔案,在 Title 欄位填上 Python Periodic Table 當作新標題:

還要修改一段程式碼才能真的生效,把 <h1> 改成 TAL 敘述式,讀取 template/title 變數值:

重頭戲上場了,要把原始碼 index.html 內容嵌入 Page Template 裡。依序是:
接下來是許多微調的動作,特別是要把檔案路徑、CSS 衝突或不適宜的設定值改掉,例如:
如果 poortal-column-content 的 CSS class 改用 width-full 的話,可以擁有全螢幕的顯示效果。

2012/10/24

Plone Development Quick Tips

This document hints how to build a Plone development environment with minimum effort. Here we focus on development baby steps, not deployment. If possible, Ubuntu Linux 12.04 or later is preferred. Make sure you have installed the needed libraries. For newbies, it's much easier to install Plone with regular user account, not with root permission.

Useful Unified Installer Options

Linux Unified Installer provides a install.sh script, that makes things easy. Installation target directory and admin password can be specified.

$ tar xvpzf Plone-4.2.2-UnifiedInstaller
$ ./install.sh standalone \
  --target=/home/marr/plone422 --password=secret

Stand-Alone Zope Instance selected

Detailed installation log being written to
/home/marr/Downloads/Plone-4.2.2-UnifiedInstaller/install.log

Rootless install method chosen. Will install for use by system user marr

Installing Plone 4.2.2 at /home/marr/plone422

Skipping libjpeg build
Skipping readline build
Installing Python-2.7.3. This takes a while...
Installing distribute...
Python build looks OK.
Unpacking buildout cache to /home/marr/plone422/buildout-cache
Compiling .py files in egg cache
Copying Plone-docs
Your platform's xml2/xslt are missing or out-of-date. We'll need to build them.
Copying buildout skeleton
Fixing up bin/buildout
Building lxml with static libxml2/libxslt; this takes a while...
Building Zope/Plone; this takes a while...

#####################################################################
######################  Installation Complete  ######################

Plone successfully installed at /home/marr/plone422
See /home/marr/plone422/zinstance/README.html
for startup instructions

Use the account information below to log into the Zope Management Interface
The account has full 'Manager' privileges.

  Username: admin
  Password: secret

This account is created when the object database is initialized. If you change
the password later (which you should!), you'll need to use the new password.

- If you need help, ask the mailing lists or #plone on irc.freenode.net.
- The live support channel also exists at http://plone.org/chat
- You can read/post to the lists via http://plone.org/forums

- Submit feedback and report errors at http://dev.plone.org/plone
(For install problems, specify component "Installer (Unified)")

Preparing Package in the src Directory

In the above case, the directory ~/plone422/zinstance/src is used to store development source codes. Here is an example checking out the package source code from GitHub:

$ cd ~/plone422/zinstance/src
$ git clone https://github.com/collective/collective.masonry.git
Initialized empty Git repository in
/home/marr/plone422/zinstance/src/collective.masonry/.git/
remote: Counting objects: 314, done.
remote: Compressing objects: 100% (228/228), done.
remote: Total 314 (delta 147), reused 185 (delta 73)
Receiving objects: 100% (314/314), 52.36 KiB, done.
Resolving deltas: 100% (147/147), done.

Running buildout

buildout.cfg is the main file, which is extended by develop.cfg, so that development tools are included. After configuring the develop.cfg file, we run buildout to make it effective:

$ cd ~/plone422/zinstance
$ vi develop.cfg

[sources]
collective.masonry = fs collective.masonry

eggs +=
    collective.masonry

$ bin/buildout -c develop.cfg
mr.developer: Queued 'collective.masonry' for checkout.
mr.developer: Filesystem package 'collective.masonry' doesn't need a checkout.
Develop: '/home/marr/masonry/zinstance/src/collective.masonry'
...
Got collective.registry 1.0.
Got collective.js.imagesloaded 2.1.
Got collective.js.masonry 1.0.
Generated script '/home/marr/masonry/zinstance/bin/instance'.
Installing zopepy.
Generated interpreter '/home/marr/masonry/zinstance/bin/zopepy'.
Installing zopeskel.
Generated script '/home/marr/masonry/zinstance/bin/zopeskel'.
Generated script '/home/marr/masonry/zinstance/bin/paster'.
Installing omelette.
Updating backup.
Updating chown.
chown: Running echo Dummy references to force this to execute after referenced parts
echo /home/marr/masonry/zinstance/var/backups
chmod 600 .installed.cfg
find /home/marr/masonry/zinstance/var -type d -exec chmod 700 {} \;
chmod 744 /home/marr/masonry/zinstance/bin/*
Dummy references to force this to execute after referenced parts
/home/marr/masonry/zinstance/var/backups
Installing test.
Generated script '/home/marr/masonry/zinstance/bin/test'.
Updating repozo.
Updating unifiedinstaller.
*************** PICKED VERSIONS ****************
[versions]
collective.js.imagesloaded = 2.1
collective.js.masonry = 1.0
collective.registry = 1.0

*************** /PICKED VERSIONS ***************

Reading Buildout Messages

Don't panic when you see messages like these:

Thery are non-fatal errors.

Creating Dexterity Product Skeleton

For those who want Dexterity development environment for Plone 4.2.x, zopeskel.dexterity is your friend. After zopeskel.dexterity installed, you can run zopeskel to create dexterity package project:

$ cd src
$ ../bin/zopeskel dexterity

dexterity: A Dexterity-based product

This template expects a project name with 1 dot in it (a 'basic
namespace', like 'foo.bar').

Enter project name: myproj.dxcontent

In this case, add myproj.dxcontent in the develop.cfg file and run buildout -c develop.cfg to make it effective:

[sources]
# Examples: to check out plonetheme.sunburst from Plone's svn, use:
# plonetheme.sunburst = svn https://svn.plone.org/svn/plone/plonetheme.sunburst/trunk
#
# To use a python package that is being developed in your src subdirectory, use:
# myproduct.betterplone = fs myproduct.betterplone
myproj.dxcontent = fs myproj.dxcontent

Later, run paster addcontent to create dexterity_content or dexterity_behavior.

Here are the activated packages for Plone 4.2.2:

  • Dexterity Content Types 1.2.1
  • collective.z3cform.datetimewidget 1.2.1
  • plone.app.intid: install utility 1.0.1

2012/10/23

folder_contents Customization

之前看過 collective.edm.listing 例子,它提供類似 Contents 頁籤看到的顯示介面,不過,究竟 Contents 頁籤的表格式管理介面該如何修改呢?

每個目錄網址後面,加上 /folder_contents 就可以進入 Contents 管理介面,它的程式碼到 plone.app.content 找得到,先看 browser/configure.zcml 檔案:

<browser:page
    for="*"
    class=".foldercontents.FolderContentsView"
    name="folder_contents"
    template="folder_contents.pt"
    permission="cmf.ListFolderContents" />

再看 browser/foldercontents.py 檔案:

class FolderContentsView(BrowserView):
    ...
    def contents_table(self):
        table = FolderContentsTable(aq_inner(self.context), self.request)
        return table.render()

class FolderContentsTable(object):
    ...
    def __init__(self, context, request, contentFilter=None):
        self.context = context
        self.request = request
        self.contentFilter = contentFilter is not None and contentFilter or {}
        self.items = self.folderitems()

        url = context.absolute_url()
        view_url = url + '/folder_contents'
        self.table = Table(request, url, view_url, self.items,
                           show_sort_column=self.show_sort_column,
                           buttons=self.buttons)

    def render(self):
        return self.table.render()

其中的 Table() 是載自 plone.app.content.browser.tableview:

class Table(object):
    """
    The table renders a table with sortable columns etc.

    It is meant to be subclassed to provide methods for getting specific table info.
    """
    def __init__(self, request, base_url, view_url, items,
                 show_sort_column=False, buttons=[],
                 pagesize=20, show_select_column=True, show_size_column=True,
                 show_modified_column=True, show_status_column=True):

這 Table() 執行的結果,不只是產生內容項目的列表,它可以透過 checkbox 選擇項目,再個別或批次執行 portal_actions 的 Copy、Cut、Rename、Delete、Change State 等動作,或是利用 drag 功能 (配合 draggable 和 draggingHook 的 CSS 設定值) 來調整項目的順序,項目數量超過預設的 20 之話,就會啟用分頁功能。

2012/10/17

News from PloneConf 2012

PloneConf 2012 在荷蘭 Arnhem 舉行,研討會有許多簡報檔講題值得記錄分享:

其他同時期的進展包括:使用介面打算多整合 jQuery 和 JSONplone.batching 將讓 batch 資料有一致的處理方式Diazo Theme Editor 範例影片collective.z3cform.widgets 持續更新,目前 AutocompleteWidget 推薦使用 plone.formwidget.autocomplete,到 Plone 5 預計會有新的 widget 套件冒出頭,看來 plone.app.widgets 是候選之一。

2012/10/12

Heavy Usage of Python at Google

summary from http://stackoverflow.com/questions/2560310/heavy-usage-of-python-at-google

有人問道:Google 大量使用 Python,只是想要展現品味,還是真的帶來競爭優勢?

在 Google 擔任 Über Tech Lead 的 Alex Martelli 說:很難有明確的答案,不過,2004 年之際,Python 已在 Google 扮演重要角色,Google 確實召募許多 Python 大咖,但選用 Python 的思惟早在招雇之前就已決定,他們的技術策略是 Python where we can, C++ where we must,在緊密操控硬體資源的場合使用 C++,在快速開發和需要維護的場合使用 Python。早期雇用的系統管理員通常是 Perl 或 Bash 好手,許多內部系統先用 Perl 寫成,日後再用 Python 改寫,因為併購而帶進來的系統,如果是用 C# 寫成,常會用 Java 改寫。

想要獲得更多資訊的話,可以參考 Weisley Chun 在 Google I/O 2011 的演講

2012/10/05

Geo-Statistics Tools

summary from http://stats.stackexchange.com/questions/3069/among-matlab-and-python-which-one-is-good-for-statistical-analysis

很多人認為 R 是很棒的統計工具,像 gstat、geoR 讓它更切合特定領域的應用,也有人會考慮 Matlab,特別它有強大的 profile 工具。

當然,也有人快樂地拿 Python 當作統計平台,豐富的函式庫和整合其他系統的彈性肯定是一大賣點,例如在 R 跑得太慢的情況下,用 ctypes 來建置 C data structure,大幅改善效能問題,例如搭配 Natural Language Toolkit 處理文字資料。更多實務經驗,可參考 Data Crunching: Solve Everyday Problems Using Java, Python, and more 一書,作者 Greg Wilson 致力於讓科學家更有生產力

終究 Python 不是一個 Domain Specific Language,拿來和 R 相比,有其天生侷限,"Compared to R, Python is a low-level language for model building." - 有人如是說。

不過,也有人這樣評論:"In fact, I’ve anecdotally observed that becoming better at R leads to skill at interacting with data, becoming better at MATLAB leads to skill at quick-and-dirty scripting, but becoming better at Python leads to genuine programming skill."

如果有人想要用 Ruby 的話,可以貨比三家

2012/09/27

Disable Products.TinyMCE URL Conversions

有時候,你會想輸入 /some_folder/some_item 這樣的網址,但 Products.TinyMCE 預設會雞婆地改成 ../some_folder/some_item 相對網址。想要取消這樣的行為,可以修改 Products.TinyMCE/skins/tinymce/tiny_mce_init.js 檔案,加上幾個設定值:

  fix_list_elements : false,
+ convert_urls: false,
+ relative_urls: false,
+ remove_script_host: false,
  // allow embed tag if user removes it from
  // list of nasty tags - see #10681
  media_strict: false

2012/09/26

Plone on OpenShift

Here are some notes on setting up Plone instances on OpenShift based on https://github.com/kagesenshi/plone-openshift-quickstart documentation.

First, get your OpenShift client installed.

$sudo apt-get install git-core openssh-client ruby ruby-dev rubygems libopenssl-ruby
$ sudo su -
# gem install --source http://gems.rubyforge.org --source https://openshift.redhat.com/app/repo/ rhc

===========================================================================

If this is your first time installing the RHC tools, please run 'rhc setup'

===========================================================================


$ rhc app create -a openplone -t diy-0.1

Starting Interactive Setup for OpenShift's command line interface

It looks like you have not configured or used OpenShift client tools
on this computer. We'll help you configure the client tools with
a few quick questions. You can skip this in the future by copying
your configuration files to other machines you use to manage
your OpenShift account:

/home/marr/.openshift/express.conf
/home/marr/.ssh/

To connect to openshift.redhat.com enter your OpenShift login
(email or Red Hat login id): marr
Password: ********

Created local config file: /home/marr/.openshift/express.conf
The express.conf file contains user configuration,
and can be transferred to different computers.

We will now check to see if you have the necessary client tools installed.

Checking for git ... found

Checking for your namespace ... found namespace:
    marr

Checking for applications ... found
    * py - http://py-marr.rhcloud.com/

The OpenShift client tools have been configured on your computer.
You can run this setup wizard at any time by using the command
'rhc setup' We will now execute your original command
(rhc app create -a openplone -t diy-0.1)

Password: ********

Creating application: openplone in marr
Now your new domain name is being propagated worldwide
(this might take a minute)...
The authenticity of host 'openplone-marr.rhcloud.com (50.16.52.158)'
can't be established.
RSA key fingerprint is cf:ee:77:cb:0e:fc:02:d7:27:7e:ae:90:c0:90:88:a7.
Are you sure you want to continue connecting (yes/no)? yes
Enter passphrase for key '/home/marr/.ssh/id_rsa':
Checking if the application is available #1
Application openplone is available at: http://openplone-marr.rhcloud.com/
  Git URL: ssh://2abab67cb28b4684ab722d0064554394@
openplone-marr.rhcloud.com/~/git/openplone.git/
Disclaimer: This is an experimental cartridge that provides a way
to try unsupported languages, frameworks, and middleware on Openshift.

接著就進去新建的 openplone 目錄:

$ cd openplone
$ git remote add upstream -m master \
  git@github.com:kagesenshi/plone-openshift-quickstart
$ git pull -s recursive -X theirs upstream master
Enter passphrase for key '/home/marr/.ssh/id_rsa':
warning: no common commits
remote: Counting objects: 67, done.
remote: Compressing objects: 100% (47/47), done.
remote: Total 67 (delta 26), reused 59 (delta 18)
Unpacking objects: 100% (67/67), done.
From github.com:kagesenshi/plone-openshift-quickstart
 * branch            master     -> FETCH_HEAD
Auto-merging .openshift/action_hooks/stop
Auto-merging .openshift/action_hooks/start
Auto-merging .openshift/action_hooks/deploy
Auto-merging .openshift/action_hooks/build
Merge made by the 'recursive' strategy.
 .openshift/action_hooks/build  |    3 +-
 .openshift/action_hooks/deploy |   51 ++++++++++-
 .openshift/action_hooks/start  |   15 +++-
 .openshift/action_hooks/stop   |   14 +++-
 README.rst                     |   76 +++++++++++++++++
 bootstrap.py                   |  181 ++++++++++++++++++++++++++++++++++++++++
 buildout.cfg                   |   40 +++++++++
 7 files changed, 371 insertions(+), 9 deletions(-)
 create mode 100644 README.rst
 create mode 100644 bootstrap.py
 create mode 100644 buildout.cfg

檢查 domain 的話,會看到所有的 app 資訊:

$ rhc domain
Password: ********


Applications in marr:

py @ http://py-marr.rhcloud.com/
================================
Created: 2:08 AM
Git URL:
ssh://26cc6f56ea4744c19c2ad6a43cc7a8a2@py-marr.rhcloud.com/~/git/py.git/

Cartridges:
  python-2.6


openplone @ http://openplone-marr.rhcloud.com/
==============================================
Created: 2:38 AM
Git URL:
ssh://2abab67cb28b4684ab722d0064554494@openplone-marr.rhcloud.com/~/git/ope
nplone.git/

Cartridges:
  diy-0.1

.git/config 內容長得像這樣,為了版面,我用了 \ 符號:

[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = ssh://2abab67cb28b4684ab722d0064554394@ \
              openplone-marr.rhcloud.com/~/git/openplone.git/
        fetch = +refs/heads/*:refs/remotes/upstream/*
[branch "master"]
        remote = origin
        merge = refs/heads/master

執行 git push 後,buildout 就會執行,這會花一段時間,我跑了至少 40分鐘。

$ git push
...
remote: ~/git/openplone.git
remote: Starting application...
remote: . . . . . . . . . . .
remote: daemon manager not running
remote: Done
remote: Running .openshift/action_hooks/post_deploy

打開瀏覽器,在 http://openplone-marr.rhcloud.com/ 看得到起始畫面。利用 ssh 也看得到檔案系統。

$ ssh 2abab67cb28b4684ab722d0064554394@openplone-marr.rhcloud.com "ls -l app-root/data/filestorage"

設定 Virtual Hosting 的資訊:

$ rhc app add-alias -a openplone --alias plone.mydomain.org
Password: ********


RESULT:
Success

$ rhc-port-forward -a openplone
Warning: This command is deprecated and will be removed in the future. Please use 'rhc port-forward' instead.

Password: ********

Checking available ports...
Enter passphrase for /home/marr/.ssh/id_rsa:

Binding python -> 127.7.35.1:8080...
Enter passphrase for /home/marr/.ssh/id_rsa:
Use ctl + c to stop

Error trying to forward ports. You can try to forward manually by running:

ssh -N -L 127.7.35.1:8080:127.7.35.1:8080 2abab67cb28b4684ab722d0064554394@openplone-marr.rhcloud.com

再設定 DNS entry 和 ZMI 裡的 VHM 就行了:

2012/09/18

PyHUG September Seminar

PyHUG 九月17日的活動很成功,電腦教室塞滿人,學習和交流的氣氛也很熱烈。

感謝很多人的幫忙,讓我有機會講了 Plone - Evolving Python CMS 題目,在聽眾的回應裡,我覺得 ZODB vs Relational DB 和 Javascript Form Builder (例 backbone-form ) 值得日後繼續探討。沒試過的東西,一般人總是不容易想像,如果學習門檻能夠降低,讓人多些機會做中學,這樣的學習效果才容易提高。

最後,台北地區的朋友,想要感受現場交流的氣氛,最簡單的方法當然就是參加 Taipei.py 活動囉。

2012/09/13

SQL Integration to Plone

SQL 是一個成熟的世界,即使 ZODB or SQL 是個討論議題,實務上還是要想辦法讓它們互通有無。整合的方法主要有三大類:

  1. Database Adapter + Z SQL Method
  2. 這是傳統方法,先安裝 Database Adapter 再用 ZSQL Method 建立存取邏輯的程式碼,最後由 Page Template 建立操作和顯示的介面。整個流程邏輯跟 PHP / ASP 早期硬幹應用程式很像,原則上就是按照 CRUD 概念去刻。常見的 MySQL 可以搭 zmysqlda,PostgreSQL 可以搭 psycopg2,更多細節可參照範例文件

  3. ORM (SQLAlchemy)
  4. 在 Database Adapter 的基礎上,還可以利用 Object Relation Mapper (ORM) 來協助開發,像 SQLAlchemy 就是一個 ORM 工具,越來越多 SQL 整合是透過它來進行,成熟的例子之一是 ContentMirror,它把 ZODB 的內容資料同步儲存在 SQL 資料庫裡,目前支援 Archetypes 的型別內容,據說傳到 Google App Engine 沒有問題,還沒機會實測。

  5. Pickle
  6. 另一種思惟方式,是直接把 Pickle 資料塞到 SQL 資料庫裡,像 RelStorage 就是這類例子。

2012/09/06

portal_setup Import Steps

Importing steps of base profile by mistake, will be a disaster for the Plone site. At least I run into it twice. For example, a message like this might appear in log file:

TypeError: unhashable type: 'Missing.Value'

The webpage output simply keeps showing errors. What I do wrong is to import the Catalog Tool step of base profile, that ends up cleaning all the indexes in portal_catalog. Indexes such as Creator, Date, Description are there, but # distrinct values are 0. Luckily, after clicking Updated Catalog in Advanced tab, and the Plone site comes live again.

2012/08/30

Essential ArcPy Vocabulary

ArcPy site-package 搭在 Python 2.6 版本之上,它有幾個重要的模組、類別、函式,像是:

  • mapping module (arcpy.mapping)
  • Spatial Analyst module (arcpy.sa)
  • Geostatistical Analyst module (arcpy.ga)
  • SpatialReference class
  • Extent class
  • ListFeatureClasses()

技術層面來看,tool 和 function 有所區別,前者會回傳 result 物件或產生 message,後者不會這樣做,另一方面,tool 會依 product (ArcView, ArcEditor, ArcInfo) 或 extension (Network Analyst, Spatial Analyst) 來授權使用,function 則是直接安裝在 ArcPy 裡。

設定 workspace 時,路徑寫法如下,用 "C:\data\my_folder" 可能會有問題:

>>> arcpy.env.workspace = "C:/data/my_folder"

在 geodatabase 裡建立 feature class 時,需要指定 feature class 的類別 (point, line, polygon, etc)。

Removing PasteScript Dependency

看到消息說沒人想解決 PasteScript / PasteDeploy 的臭虫,之前相依的軟體,也可能連帶造成問題,開始思考要跳船,像 Cheetah 就可能被 Jinja2 取代。

究竟這三個人到底是命運的安排,還是情感的糾結,抑或是另有隱情,真相到底是什麼,讓我們繼續看下去。

2012/08/28

ftw.meeting Reviewed

ftw.meeting 是一個 Archetypes 模組,功能跟 Event 類似,不過設計成 folderish 因此可以包含 Meeting Item,搭配 ftw.pdfgenerator 還可以匯出 PDF 檔案。

安裝時的相依模組,主要包括 Products.DataGridField、ftw.calendarwidget。

它使用 ftw.calendarwidget 作為月曆輸入介面,特色是整合了 jQuery UI calendar widget。

在 Event type 欄位選擇 Meeting 項目的話,會在下方即時展開 Meeting form 等四個欄位,它們都指定為 schemata='meeting'。

DataGridField 用於動態新增與會名單,不過實際用到的程式碼有新增 select all 功能,細節可參考 content/widget.py 檔案的 DataGridWidgetExtended 類別內容。

網站新增會員帳號後,就可以成為與會人員的選項。

程式碼範例片段如下:

atapi.StringField(
    name='meeting_type',
    searchable=False,
    schemata='default',
    required=True,
    default='event',
    vocabulary_factory='ftw.meeting.types',

    widget=atapi.SelectionWidget(
        label=_(u"meeting_label_type", default=u"Event type"),
        description=_(u"meeting_help_type",
                      default=u"Choose your event type."),
        helper_js=['meeting_toggle_date.js', ],
        format='radio')),

DataGridField(
    name='responsibility',
    searchable=False,
    schemata='default',
    columns=('contact', ),
    allow_empty_rows=False,

    widget=DataGridWidgetExtended(
        label=_(u"meeting_label_responsibility",
                default=u"Responsibility"),
        description=_(
            u"meeting_help_responsibility",
            default=u"Enter the responsible of the meeting."),
        auto_insert=True,
        select_all_column='contact',
        columns={
            'contact':
                SelectColumn(
                title=_(
                    u"meeting_label_responsibility",
                    default="Responsibility"),
                vocabulary='getAttendeesVocabulary'
                ),
            })),

DataGridField(
    name='attendees',
    searchable=True,
    schemata='meeting',
    columns=('contact', 'present'),
    allow_empty_rows=False,

    widget=DataGridWidgetExtended(
        label=_(u"meeting_label_attendees",
                default=u"Attendees"),
        description=_(u"meeting_help_attendees",
                      default=u"Enter the attendees of the meeting."),
        auto_insert=True,
        select_all_column='contact',

        columns={
            'contact': SelectColumn(
                title=_(
                    u"meeting_label_attendees_attendee",
                    default=u"Attendee"),
                vocabulary='getAttendeesVocabulary'
                ),
            'present': SelectColumn(
                title=_(
                    u"meeting_label_attendees_present",
                    default=u"Present"),
                vocabulary='getPresentOptions',
                ),
            })),

ZODB 裡的儲存範例如下:

>>> app.mysite['my-meeting'].responsibility
({'contact': 'ababy'}, {'contact': 'jsmith'})

>>> app.mysite['my-meeting'].attendees
({'contact': 'ababy', 'present': 'present'},
 {'contact': 'jsmith', 'present': 'excused'})

在顯示上,實作了 Drag and Drop Order 功能,可以即時拖拉來決定 Meeting Item 的順序,它會更新 getObjPositionInParent 的索引值。

點選各別的 Meeting Item 則會展開更多欄位資訊。

2012/08/22

Path Listing Script

Plone 網站使用 /TaipeiCity/nangang 之類的路徑,來表示行政地區資訊,下列程式碼可以列出清單:

for k,v in context.objectItems():
    if v.meta_type == 'ATFolder':
        print k, v.Title()
        for x,y in v.objectItems():
            if y.meta_type == 'ATFolder':
                print k+'/'+x, y.Title()
return printed

2012/08/13

Dexterity QuickStart

透過 ZopeSkel 可以協助客製 Archetype 程式碼,同樣的原理方法也適用於 Dexterity,搭配 Plone 4.2 Unified Installer 的話,只需要在 base.cfg [zopeskel] section 加上 zopeskel.dexterity

[zopeskel]
# installs paster and Zopeskel
recipe = zc.recipe.egg
eggs =
    ZopeSkel
    Paste
    PasteDeploy
    PasteScript
    zopeskel.dexterity
    ${buildout:eggs}

再執行 bin/buildout 就能在 src 目錄裡用 ../bin/zopeskel dexterity 來建立骨架程式碼。

典型的 Dexterity 程式碼,__init__.py 的主要功能是設定 i18n message factory,內容類似如下:

from zope.i18nmessageid import MessageFactory

# Set up the i18n message factory for our package
MessageFactory = MessageFactory('tws.dxcontent')

接著,重頭戲是從下列的 mytype.py 內容開始。在 zope.schema 找得到標準欄位的定義,除此之外,通常也會用到 plone.app.textfield 和 z3c.relationfield 這兩個模組:

from five import grok
from plone.directives import dexterity, form

from zope import schema
from zope.schema.interfaces import IContextSourceBinder
from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm

from zope.interface import invariant, Invalid

from z3c.form import group, field

from plone.namedfile.interfaces import IImageScaleTraversable
from plone.namedfile.field import NamedImage, NamedFile
from plone.namedfile.field import NamedBlobImage, NamedBlobFile

from plone.app.textfield import RichText

from z3c.relationfield.schema import RelationList, RelationChoice
from plone.formwidget.contenttree import ObjPathSourceBinder

from myproj.dxcontent import MyTypeMessageFactory as _

註:ObjPathSourceBinder 之前出現在 z3c.relationfield.schema 裡面,新的版本出現在 plone.formwidget.contenttree 裡。另外,也明確可以看到 schema-defined interface 和 model-based interface 兩種設定方式。

如果要用到 validator,下列是個例子:

def codeIsValid(value):
    """Contraint function to make sure the given code is valid
    """
    if value:
        if len(value) < 4 or len(value) > 6 or \
            not value.startswith('C'):
        raise Invalid(
            _(u"The code is not of the correct format")
        )
    return True

透過網頁可以管理設定這些型別:

點選 Export Schema Models 按鈕,可以儲存 dexterity_models-20120801030127.zip 內含 models/*.xml 檔案,點選 Export Type Profiles 按鈕,可以儲存 dexterity_export-20120801031015.zip 內含 types.xml 和 types 目錄。

不過,網頁式的 Model 設定結果,如果使用中文的話,長得像這樣:

<field name="text" type="plone.app.textfield.RichText">
     <description />
     <title>內文</title>

2012/08/09

PDF Preview in Plone

之前試過 wc.pageturner 模組,缺點就是要加裝 SWFTools 工具,最近再試了 collective.pdfpeek,遇到 TypeError,這錯誤如果早在 Plone 4.0.2 時代就存在,那 Plone 4.2.x 跑不動也就不足為奇。

既有的結論是,在沒有更好模組出來前,必要時就用 wc.pageturner 吧。

編譯步驟:

  • wget http://www.swftools.org/swftools-0.9.2.tar.gz
  • tar zxvf swftools-0.9.2.tar.gz
  • cd swftools-0.9.2/
  • apt-get install libgif-dev
  • LDFLAGS=-lstdc++ ./configure
  • make
  • make install

2012/07/27

Plone 4.2 Installation Tips

Plone 4.2 的 Unified Installer 上架一段時日了,安裝用到的參數並沒改變,它搭配 Python 2.7.3 版本,在 Ubuntu 12.04 環境裡,接近完整的編譯訊息如下:

$ ./install.sh standalone --target=/home/marr/plone420
Stand-Alone Zope Instance selected

Detailed installation log being written to
 /home/marr/Downloads/Plone-4.2-UnifiedInstaller/install.log

Rootless install method chosen. Will install for use by system user marr

Installing Plone 4.2 at /home/marr/plone420

Skipping libjpeg build
Skipping readline build
Installing Python-2.7.3. This takes a while...
Installing distribute...
Python build looks OK.
Unpacking buildout cache to /home/marr/plone420/buildout-cache
Compiling .py files in egg cache
Copying Plone-docs
Your platform's xml2/xslt are up-to-date. No need to build them.
Copying buildout skeleton
Fixing up bin/buildout
Skipping static libxml2/libxslt build.
Building Zope/Plone; this takes a while...

如果 xml2/xslt 需要編譯的話,就會顯示下列訊息:

Your platform's xml2/xslt are missing or out-of-date. We'll need to build them.

對於初學者,第一個叮嚀是,不要用 root 權限安裝 Plone 會比較單純。

從 4.0.7 昇級到 4.2 的過程,遇到幾個值得留意的訊息:

INFO PythonScripts Some of your Scripts have stale code cached.
Since Zope cannot use this code, startup 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.

Converting to IIBTree for index `end`.
Converting to IITreeSet for index `object_provides`.
Converting to IITreeSet for index `Subject`.
Converting to IITreeSet for index `getRawRelatedItems`.
Converting to IIBTree for index `effective`.
Converting index `UID` to UUIDIndex.
Converting index `is_folderish` to BooleanIndex.

INFO plone.app.upgrade Ran upgrade step:
Add Member role to 'Portlets: View dashboard' permission
INFO plone.app.upgrade Ran upgrade step:
Install the CMFEditions component registry bases modifier

把 readline 支援放進 Python 是很重要的事,記得事先安裝 libreadline-dev 套件。

Warning: This Python does not have readline support.
It may still be usable for Zope, but interacting directly with Python will be painful.

通常 Plone 開發團隊會等到主要平台的 Unified Installer 都就緒,才會在網頁發佈新版號正式釋出的消息,我關心的是 Linux 版本進度,必要時自行到 http://dist.plone.org/release/ 找檔案就行。

2012/07/17

GeoPDF: Consumable GIS File

GeoPDFTerraGo 公司開發的軟體,可以處理具備地理資訊的 PDF 檔案,包括顯示地圖、座標、計算距離面積等功能,並可使用 Adobe Reader 之類常見軟體來開啟。它的特點包括:

  1. PDF 格式讓一般使用者也能簡單操作。
  2. 現場人員可輸入數據和其他人員共享。
  3. 縮小檔案容量方便散佈與攜帶。
  4. 使用期間可以不依賴網路。
  5. 可附帶位置訊息寫入註釋等。

目前在美國有超過 900 個組織使用 GeoPDF 工具,大多屬於政府單位,常見的使用場合包括:

  • 災後重建領域:用於災情掌握、確認避難路徑、分發救援資訊。
  • 警戒護送領域:維安工作的場合,可顯示特定行進路途的重要警戒地點、布置警衛人員、利用實況報告來推斷逃跑路線等。
  • 基礎設施管理領域:用於電力公司、上下水道、交通設施等的設施管理。
  • 保險理賠領域:保險公司事故調查人員在事故發生現場將發生的事故資訊登記到 GeoPDF 文件中,作為理賠審查時的調查表。

GeoPDF 是以 ISO32000 中規定的 PDF 1.7 作為基礎,目前正朝向標準化的準備工作邁進。

2012/07/13

IOError: decoder jpeg not available

Currently PIL is not easy to work with Plone, that is why there is a PIL fork for this issue. The common error message looks like this:

ERROR root could not scale ImageField "image" ...
...
IOError: decoder jpeg not available
...
AttributeError: image_mini

Here is my environment running into this issue:

  • Plone 4.0.7
  • Ubuntu 12.04

A quick search leads me to an answer. My working fix is like this:

  • edit buildout.cfg to pin version: Pillow = 1.7.5
  • remove the installed Pillow 1.7.2 and re-run buildout

If everything goes well, you should see JPEG support available after downloading Pillow.

2012/07/10

ValueError: Unable to find update_version_before_edit

在 Plone 4.2 安裝 Products.Collage + collective.collage.megamenu 後,在新增 Collage 時遇到 ValueError: Unable to find update_version_before_edit 訊息,暫時還不清楚細節,但處理方式是到 portal_setup 的 Import 執行 CMFEditions 的所有步驟。

ContentMigration Example

When migrating Plone from 2.5 to 4.x, you might want to migrate the custom Archetypes packages. Here is one simple example. Note that this might not be best practice, but should be working if your migration case is simple as mine.

First create a migration.py file in myproj.atcontent/myproj/atcontent/Extensions/

#!/usr/bin/python
# -*- coding: utf-8 -*-

from Products.contentmigration.archetypes import InplaceATItemMigrator, ATItemMigrator
from Products.contentmigration.walker import CustomQueryWalker
from Products.contentmigration.archetypes import *
from Products.contentmigration.common import unrestricted_rename

from transaction import savepoint
from Products.CMFCore.utils import getToolByName


def getMigrationWalker(context, migrator):
    """ set up migration walker using the given item migrator """
    portal = getToolByName(context, 'portal_url').getPortalObject()
    return CustomQueryWalker(portal, migrator, use_savepoint=False)


class OldTypeToNewTypeMigrator(InplaceATItemMigrator):
    src_portal_type = 'OldType'
    src_meta_type = 'OldType'
    dst_portal_type = 'NewType'
    dst_meta_type = 'NewType'

    def last_migrate_reindex(self):
        self.new.reindexObject(idxs=['object_provides', 'portal_type',
            'Type', 'UID'])

    def renameOld(self):
        self.code_field = self.old.getCode_field()
        InplaceATItemMigrator.renameOld(self)

    def remove(self):
        if self.new.getId() != self.code_field:
            unrestricted_rename(self.new.aq_inner.aq_parent, self.new.getId(), self.code_field)

    fields_map = {
        'datasource': 'data_src',
        'old_field': 'new_field',
    }


def getOldTypeToNewTypeMigrationWalker(self):
    return getMigrationWalker(self, migrator=OldTypeToNewTypeMigrator)


def migrateOldType(self):
    walker = getOldTypeToNewTypeMigrationWalker(self)
    savepoint(optimistic=True)
    walker.go()
    return walker.getOutput()


class ImagesToPhotosMigrator(InplaceATItemMigrator):
    src_portal_type = 'Image'
    src_meta_type = 'ATBlob'
    dst_portal_type = 'Photo'
    dst_meta_type = 'Photo'

    def last_migrate_reindex(self):

        self.new.reindexObject(idxs=['object_provides', 'portal_type',
            'Type', 'UID'])

    fields_map = {
    }

def getImagesToPhotosMigrationWalker(self):
    return getMigrationWalker(self, migrator=ImagesToPhotosMigrator)

def migrateImages(self):
    walker = getImagesToPhotosMigrationWalker(self, {'path': '/my_folder'})
    #savepoint(optimistic=False)
    walker.go()
    return walker.getOutput()

Then, go ZMI and add an External Method in Plone Site root:

Id: myproj.atcontent.migration
Module Name: myproj.atcontent.migration
Function Name: migrateOldType

Click Test tab to run the External Method. If everything goes well, you might want go portal_catalog Advanced tab to Update Catalog.

2012/07/09

Responsive Design

已經有不少人討論 Responsive Design 並且找得到許多範例,甚至也有人提出它的缺點技術上它是用了 CSS3 media queries 的 W3C 規格,據說 IE 6-8 沒有支援,所以要另外找處理方法

同時間 twitter 在此風潮下,有推出一套稱為 bootstrap 的 CSS toolkit。已有 Plone 好手討論整合 bootstrap 這項工具。試過像 plonetheme.responsivetheme, plonetheme.responsive1140, beyondskins.reponsive 這些例子,但暫時最喜歡的是 collective.responsivetheme。實際的網站範例有: www.arpa.piemonte.it, netimpact.org。有空要測試 redomino.css3theme 的範例。

2012/07/06

Plone Member Approval Process

利用 pas.plugins.memberapprovalcollective.memberapprovalcollective.memberemails 三個模組的合作,可以擴充 Plone 的帳號審核功能,新增一個 Approval Status 欄位,也就是申請帳號後,經由管理員同意的話,才可以登入系統。

狀態分成 Pending、Approved、Disapproved 三種。

啟用三個模組後,會出現 Member creation email settings 設定項目,用來指定通知信要寄給誰。

接著,要到 Security 設定勾選 Enable self-registration 和 Let users select their own passwords,還有確認 Mail 項目裡完成 SMTP 設定。

自建 SMTP server 常見方法之一是安裝 postfix,現在 Python 粉絲多了 lamson 選項,用它進行簡易測試算是很方便。

2012/07/03

TinyMCE Output Transformations

Plone 4.0.x 到 4.1.x 之際,針對 custom output transformation 進行程式碼重構,以往各個編輯器需要個別實作 filter 功能,改由獨立的模組來統一處理。在這個過程中,搭配的 Products.TinyMCE 由 1.1.x 改為 1.2.x,許多檔案受到調整,原本的 resolveuid.pyparser.py 被幹掉了,其中的 filter 功能由 plone.outputfilters 取代,並盡量相容於 Plone 4.1 之前的程式碼

2012/07/02

Linux Succeeded Thanks to Selfishness and Trust

BCC 前陣子刊載 Linus Torvalds 的訪談稿,其中有個回應如下:

@GeKaTiek

然而值得討論的是,Linus 的說法,恐怕無法為他的論點提出有力的支持。上面讀來,Linus 認為,開源之所以能貢獻於社會公益,乃是因為程子們「追求寫程式的樂趣」,然而,顯而易見的,那些待在封閉的公司當中的人,就無法追求這種樂趣麼?或者說,真的只有開源才能體現這種樂趣麼?顯然不是的。
我們其實可以從當中窺探到所謂樂趣背後的可望,應該是受人景仰。於是,自私地追求樂趣的下場,程式碼越寫越「讓人看不懂」,就好像很厲害一樣、給別人看程式碼的心態就成為「炫耀」,於是大家追逐這種自私的結果,使得程式碼的品質下降、難以維護,更關鍵的是,如同 Linus 說的,「不是只有我有這種想法」,於是沒有人真的要讀你的 code,因為「大家都想自己來」,在這種自私的驅動下,開源只會更糟,不會更好。也正因為亞當斯密看透這種驚駭的思維(別忘了,他除了《國富論》之外,還有另外一本巨著《道德情操論》)我們才能在現代發現名言的新詮釋:因為屠夫與釀酒師、麵包師父自私自利,於是我們只能吃到黑心食品。
真正推動開放世界能夠持續進步的,還是得回到本初的心意:「樂」於分享。不可諱言,因為追求分享本身帶來的樂趣,這種「自私XD」,才是促使開放能夠持續前進的動力。

Linus 並不是第一次接受訪問,他已經多次闡述「當初會在 Usenet 公佈自己作品的初衷」,我也相信當中並沒有太多了不起的念頭。當他意識到 BBC 的提問想要導引出「某種預期規劃」時,他便順勢「扯到更一邊」去,如果你了解科學研究社群的文化,就會知道開源只是必要的程序之一,它跟自私或公益都沒太大直接關係。我猜 Linus 體諒 BBC 記者並不是科學圈的人,才懶得提這段原因。

自私追求樂趣的下場,會讓程式碼越寫越亂嗎? 首先,讓自己去讀一套具有上萬行程式碼的軟體,然後試著加上自己喜歡的功能,最後問自己是否能體驗到樂趣? 答案通常是否定的。於是,大家都想自己來囉,所以開源還是只會更糟? 看看這年頭,是否每個人都想實作自己的 kernel 吧 ^^

我在好久以前問過 Eric Raymond 這個問題,他的答案至今仍然有效:軟體的品質來自程式員願意投注的心力。當程式員形成社群,願意長期投注心力時,軟體的品質就更容易獲得提昇。形成社群的關鍵,又在於「信任」,只靠少數程式員自爽來支撐的軟體,很難長期提昇品質。

比較少人去理解:同樣是開源社群,GNU/Linux/BSD 就展現很不同的文化和發展模式,光憑這個實例,就能知道開源並不是影響軟體品質的重要因素,當然,開源肯定是培養信任的重要基礎之一。進一步地看,同樣是使用 GPL 的 GNU 和 Linux,仍然呈現不同的社群文化,可以窺見 license 也只是形塑社群文化的一部份因素。Linus Torvalds 在意別人是否公平地對待 contribution,Richard Stallman 在意別人是否公平地對待 credit,顯然地,說教式地要求公平,成效總是有限。

Linus 嘴裡的「樂趣」,就是他現在仍在參與及帶領的程式員社群,他們可以在社群裡持續追求程式員的聖杯:共同累積最棒的創意和實作。時至今日,Linux kernel 大約每隔三個月推出新版,包括超過 1,000 位核心程式員的協同心力,如果每一行程式碼都要讀過,恐怕很難有樂趣可言。如果 Linux kernel 全然變成某個公司的產品,那麼這項樂趣很可能就會變質或消失。那怕你是 FSF, IBM, Apple, Linux Foundation,都不能影響這個初衷,Linus Torvalds 並不希望有個委員會來決定 kernel 的方向,他信任的是那群在意軟體品質的程式員們,同時,他也自我要求,不讓信任他的程式員們感到擔心,有天 kernel 的發展方向會被特定單位把持。它變成一個全球規模的信任關係。

2012/06/30

Creating Plone Content Types with Dexterity

collective.dexterity_class 程式碼包含幾個簡單範例,可以拿來練習 Dexterity 入門。

Plone 4.2 (release candidate) 搭配 Dexterity 1.2.1,安裝過程的相依模組還包括 collective.z3cform.datetimewidget 和 plone.app.intid。

啟用後就有新型別可以新增,還有欄位統計建立的數量。

container 就是 folderish object,在 profiles/default/types 的 XML 程式碼裡,用 filter_content_types 和 allowed_content_types 可以選擇特定型別才能在 container 裡被新增。

型別的細節定義,可由 Fields 和 Behaviors 來檢視。

還可以編輯各別欄位的屬性值。

利用 Export Type Profiles 來匯出 XML 檔案。

model_source 包括一段 XML 格式的設定內容,它可以改由 model_file 指定 filesystem 裡的檔案內容。

以 everything_model 為例,它會搭配 everything_model_templates/view.pt 處理預設的顯示方式。

2012/06/27

Start Plone Development with One Script

你可以用 Paster Script 或 ZopeSkel 建立全新的 Plone Buildout 專案環境,不過,現在又多了一種選項 -- plone-devstart

只需要一個 plone-devstart.py 檔案,就能夠啟動整個環境,甚至不需要事先存在 easy_install 或 pip。

你可以選定 Plone 4.1.2 之類的特定版號,它著重在提供開發階段所需要的工具,例如:

devtools =
    plone.reload
    Products.PDBDebugMode
    Products.PrintingMailHost
    Products.DocFinderTab
    plone.app.debugtoolbar

值得一提 PrintingMailHost 這工具,它會 monkey patch 系統程式碼,取消實際的寄信工作,改為在螢幕上顯示。

整體試用心得是: 電腦資源夠用的話,直接使用 Unified Installer 還是比較單純。另外,我經常要用到 i18ndude,自己動手加上。

PyCon Taiwan 2012 Review

整體來看,這是相當成功的活動,台灣第一次正式的 PyCon 活動,票價 1500 元,平均在場人數達 200 餘人,兩天講次近 20 場,對很多籌辦人員而言,說是出乎意料的成功,並不為過。

2011 年六月的非正式聚會,時逢風雨,仍有近 50 人到場,由此估算死忠蟒迷應有百人規模,這個動能激勵 PyCon Taiwan 2012 的催生。但是,第一次舉辦超過 200 人規模的 Python 專屬活動,對籌辦團隊仍是極大的挑戰,事後來看,整個活動能夠圓滿,參與來賓的高素質和投入,肯定是重要的因素。

以活動主題和想要達到的效果來看,我們想盡可能地呈現各行各業應用 Python 的狀況,方向是做到的,但深度跟比例還可以再改進,至少我知道影音動畫領域的代表來不及上台介紹。

很期待下一次的活動,除了演講型式之外,多些動手操作和相互交流的活動,應該能讓來賓收獲更多。有了技術分享研討的園地,就能期待人才種子的蓬勃茁壯,這是值得在台灣好好經營的事。

2012/06/26

什麼是 hacker 社群

一九七○年代之際,隨著 Unix 系統和網際網路在美國大學校園串連散佈,形成了早期的技術研究社群,這群人自稱為 hacker,組成份子包括工程師、科學家、大學師生等,他們喜歡專研各式系統的運作原理,總是想把機器拆解再重組,甚至不吃不喝,只為了搞定電腦開機的問題。

Unix 是一套作業系統的名稱,由 Ken ThompsonDenis Ritchie 在 1969 年發明,發展至今,成為世界上通用性最高的系統,這個系統同時促成 C 程式語言的誕生。

hacker 主張的態度是,對世界上的問題抱持極高的興趣,樂於運用智慧解決問題,他們認為自己的行動富有意義,而且是自發自主的,所以不喜歡浪費時間做無意義的事情,尤其不要重覆別人做過的事,因此,他們很強調資訊的分享與交流,即使做著重覆又乏味的事情,也該有助於累積自己的能力與素養。他們重視公平與正義,發展 hacker 倫理和次文化,認為累積自身專長後,應投入社會的貢獻與建設。

hacker 這個字常被譯為「駭客」,並被賦予負面的意涵,但了解源由後,可以知道 hacker 並不等同於「電腦入侵者」,諸如危害電腦網路安全的行為,由於違反 hacker 倫理,成為 hacker 想要遏止的事情。真正的 hacker 必須獲得專家同儕的相互認同,他們熱中利用工具技能去創造藝術與美麗的事物,重視名譽而非權力,也就是貢獻給社群,才能獲致其他成員的敬重。

一九八○年代初期,電腦產業蓬勃發展,電腦公司大量雇用工程師,並把軟體產品化,以法律條文和商業合約限制軟體的使用和散佈,此舉造成傳統 hacker 社群的崩解凋零。一位 MIT 大學實驗室程式員 Richard Stallman 在 1983 年發起 GNU 計劃,想要打造一套自由分享的 Unix 系統,重拾 hacker 社群文化,這項計劃沒多久變成 Free Software 運動的核心,並成立自由軟體基金會持續推動。

hacker 社群的運作原理跟其他工程技術團體很類似,他們形塑了獨特的哲學理念、文化傳統,並具備關懷世人的胸懷,他們致力於讓大眾自由享有並掌握知識,深刻地影響著整個軟體和網路世界。

2012/06/25

Plone Quick Upload

Plone Quick Upload 提供 JavaScript 或 Flash 介面,讓使用者可以批次上傳圖片或檔案。上傳的檔名使用中文的話,會將檔名編碼,較特別的是它會有長度限制,處理這工作的是 fileuploader.js 檔案。

2012/06/23

New Collections for Plone 4.2

Plone 4.2 即將正式推出,最大的改變之一,就是新版 collection 不再以 ATTopic 實作,換句話說,新版 collection 和舊的模組並不相容

新版 collection 的設定介面變得簡明,與 Archetype 不再相依之後,未來的應用會更具彈性。

2012/06/14

Luban User Interface

初步覺得 Luban 設計理念還不錯,據說這名稱就是取自魯班,作者想要透過簡化的規格來產生酷炫的網頁效果,這構想造就一個純用 Python 語法就能打造的 web app UI,搭配 SQLAlchemy 還可以建立 workflow。

這結果跟 Pyjamas 想做的事很像,但 Luban 並不是把 Python 程式碼轉成 JavaScript,而是把 JavaScript 和 CSS 包在 Python package 裡,它可以用 XML 或其他語言來實作細節,但目前的開發是以 Python 3 為主角,相容 Python 2.7 的努力額外在進行,安裝過程瞄到底層相依於 cherrypy 和 docutils。

2012/06/10

PyCon Taiwan 2012, Day 2

今天早上在台灣遇到地震,當下有驚醒起身,沒多久又睡了,僅管有點嚇到,但身為關東人算是很習慣了。我想這裡的人也都熟悉這種情況吧。 -- Manabu Terada

台湾のPython会議2日目。真夏の日差し。 -- 西本卓也 @24motz

去年 PyCon JP 來了 2位台灣朋友,今年 PyCon TW 來了 8位日本朋友,希望接下來的 PyCon JP 會有 32位台灣朋友。 -- Shoma Hosaka

體感程式能套在寵物身上嗎? 或許猴子可以吧 -- ericsk

想說先用 Python,以後有問題再換用 C++,到最後就全用 Python 了。 -- Honder Tzou

以前幹壞事都用 Perl,現在想做點正經事,那就用 Python 吧。 -- lwhsu

meta class 的好處之一,是可以減少打字,讓我活得更快樂。 -- hychen

什麼事比沒買到票還慘? PyCon 期間長水痘 -- marr

2012/06/09

PyCon Taiwan 2012, Day 1

看了 argparse 後,心想「有這麼麻煩嗎?」... -- mosky

其實女人的興趣不是只有化妝買衣服! Python 才是我的最愛 -- niki

平常身邊都沒有 Python 的人,現在這裡有超過 200 個 -- Jay Johnson

會場又大又乾淨,夠容納 300人,而且網路很棒 -- Tetsuya Morimoto ‏@t2y_en

大型ハドロン衝突型加速器 LHC の紹介。スライドは英語だがスピーチは中国語なのでぐぐって内容を補完しながら聞く。 -- 西本卓也 @24motz

預防 demo 跑不出來,就直接看這張圖吧 -- jslee

接下來想想怎樣讓這個 project 變得更慢更蠢,例如用 JavaScript 改寫 -- tzerjen

議程內容大概都可以直接回饋到我的工作! -- yychen

2012/06/07

一 Py 即合: 探索程式員的快樂秘密

我是程式猿,走在前往程式員的修練之路。

前方的程式員,也邁著腳步,我知道他們在前往大師的修練路上。

我們的位置不同,成就不同,但是驅使前進的動力,都來自一份快樂

成功的遊戲軟體,通常要做到上手輕鬆,卻很難精通,才容易吸引人,自學 Python 的過程,能讓人享受遊戲般的樂趣,在 Python 社群裡打滾,能見識的風貌也很多樣,因此我說,Python 程式員是快樂的。

今年的 PyCon Taiwan 報名額滿,不禁讓我好奇到場朋友是抱著怎樣的心情或期待,但我相信,那裡有許多快樂秘密等著被發掘。

Add Manager Role to Sharing Tab

在 Plone 的 Content Item 編輯畫面,可以看到 Sharing 頁籤,裡面的角色權限預設值是 Can add, Can edit, Can view, Can review,這樣的設定介面已存在多時。

在 Plone 4 環境,可以利用 GenericSetup 的檔案設定,在 Sharing 頁籤裡顯示新的角色權限,以 Manager 角色權限為例,在自製模組的 profiles/default 目錄裡,新增一個 sharing.xml 檔案:

<sharing xmlns:i18n="http://xml.zope.org/namespaces/i18n"
         i18n:domain="plone">
  <!-- Adds a "Can manage" checkbox to Sharing tab -->
  <role
    id="Manager"
    title="Can manage"
    permission="Manage portal"
    i18n:attributes="title"
    />
</sharing>

重新安裝模組後,就可以看到效果。

更詳細的說明和範例,可以參考 http://plone.org/documentation/manual/developer-manual/generic-setup/reference/sharing-ref

2012/06/01

PloneFormGen Installation

PloneFormGen 是透過網頁介面自製表單的 Plone 模組,能夠搭配 Archetypes 或 Dexterity 內容型別來使用,產生的表單資料可以寄信通知管理員,也可以存成 CSV 或放在 SQL 資料表裡。

以 Plone 4.0.7 為例,成功搭配的是 PloneFormGen 1.6.5 (tag from github),使用 1.7 的話,可能會遇到 Missing Dependency 的問題:

最常見的應用方式,是建立活動報名的表單到 pypi 找 pfg 可以發現更多擴充可能。

2012/05/31

Ploneboard Migration Revisited

之前經驗是 Plone 2.5 昇級到 Plone 3.1,這次是 Plone 4.0.x 到 Plone 4.1.x。總結地說,這段昇級的挑戰主要是因應 Products.TinyMCE 的調整。

測試流程是先在 Plone 4.0.7 (+ TinyMCE 1.1.10) 安裝 Ploneboard 2.2,安裝過程中遇到 Kupu 的 Resource Type 設定內容要把 Large Plone Folder 取消勾選,存檔生效,不然會有 KupuError,在 Products/SimpleAttachment/setuphandlers.py 第 29 行,已經不認得 Large Plone Folder 型別。

KupuError:
 Resource type: linkable, invalid type: Large Plone Folder

另外 Products/Ploneboard/skins/ploneboard_templates/add_conversation_form.cpt 第 36 行要檢查 context 和 python:context 的差別。

如果 Plone 4.0.x 改裝 Ploneboard 3.0 以上,會遇到 Products.CMFPlone >= 4.1 的相依問題。另外,Products.Ploneboard/vocabulary.py 視情況要修改:

-from zope.app.schema.vocabulary import IVocabularyFactory
+from zope.schema.interfaces import IVocabularyFactory

接著,在 Plone 4.1.5 (+ TinyMCE 1.2.11) 安裝 Ploneboard 3.3 (src from github),把 var/filestorage/Data.fs 和 var/blobstorage 複製到新環境,到 ZMI 執行昇級,過程沒遇到問題,但實際的 Ploneboard conversation view 可能會遇到 RuntimeError:

   - URL: file:/home/marr/plone415/buildout-cache/eggs/Products.TinyMCT-1.2.11-py2.6.egg \
     /Products/TinyMCE/skins/tinymce/tinymce_wysiwyg_support.pt
   - Line 6, Column 2
   - Expression: <PathExpr standard:u'object|here'>
  ...
   - Warning: Macro expansion failed
   - Warning: <type 'exceptions.KeyError'>: 'support'
  ...
RuntimeError: maximum recursion depth exceeded while calling a Python object

猜想這只發生在使用 TinyMCE 的場合,根據 https://github.com/plone/Products.TinyMCE/pull/22 的說明,改用 nocall 方式,可以解決這問題。

2012/05/30

Listing Plone Member Properties

Be sure first see member profile documentation for more examples. Here is a Script in ZMI to list member property on wysiwyg_editor setting. Member property listing can be found at /portal_memberdata Properties tab.

from Products.CMFCore.utils import getToolByName

membership = getToolByName(context, 'portal_membership')
for member in membership.listMembers():
   print member, member.getProperty('wysiwyg_editor')

return printed

Plone Theme Transition

試用 plonetheme.transition 的心得:優點是基本配色還不錯,列表用的小圖示很亮眼,缺點是 layout 不會隨瀏覽器縮放,預設字級太小,字級變大的話,會造成管理選單的字樣被擠壓,personal tool 放在頁尾處,mouse over 的底色會讓選項字樣消失,左側 portlet 被改到下方。

2012/05/25

Plone Undocumented Content Rules Limit

One of my Plone instances has around 100 content rules added, and then hit by an error:

... ...
 Module plone.app.contentrules.namechooser, line 30, in chooseName
 Module zope.container.contained, line 761, in checkName
KeyError: u'The given name is already being used'

From #plone channel, @MacYET tells me to use PDB. Here is the info log:

> /opt/myplone/buildout-cache/eggs/zope.container-3.8.2-py2.6-linux-i686.egg
 /zope/container/contained.py(761)checkName()
-> raise KeyError(
(Pdb) dir()
['name', 'object', 'pdb', 'reserved', 'self']
(Pdb) pp name
u'rule-101'
(Pdb) pp self
<plone.app.contentrules.namechooser.RuleNameChooser object at 0x10a33c6c>
(Pdb) pp self.context
<plone.contentrules.engine.storage.RuleStorage object at 0x10c2f0ac>
(Pdb) pp self.context.__dict__
{'_data': <BTrees.OOBTree.OOBTree object at 0xd97238c>,
 '_order': ['rule-1', ... ... 'rule-99', 'rule-100', 'rule-101']}

In plone/app/contentrules/namechooser.py:

ATTEMPTS = 100


class RuleNameChooser(NameChooser):
    """A name chooser for content rules.
    """

    implements(INameChooser)

    def __init__(self, context):
        self.context = context

    def chooseName(self, name, object):
        container = self.context

        if not name:
            name = object.__class__.__name__.lower()

        i = 1
        new_name = "%s-%d" % (name, i)
        while new_name in container and i <= ATTEMPTS:
            i += 1
            new_name = "%s-%d" % (name, i)

        self.checkName(new_name, object)
        return new_name

I can accept this is not a bug, but a documentation should be helpful.

2012/05/23

Plone Content Rules

Content Rule 是 Plone 依據內容變更的條件,執行特定動作的規則,例如某個目錄裡新增了文件,就寄信通知管理員,類似 trigger 的概念。

設定通知信時,還可以指定變數,例如 ${contributor_emails} 代表系統及目錄所指派的貢獻者電子郵件,${owner_emails} 代表項目的擁有者電子郵件。

想要檢查 owner 設定值的話,可以在網址後面加上 /manage_listLocalRoles。

2012/05/16

Google Analytics for Plone

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 選項。

2012/05/12

Plone SQLAlchemy PAS Plugin

In addition to Plone instances, we have PHP applications heavily relying on PostgreSQL. Then there is a need to have them sharing the same 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 these 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)

By switching, one of source_users or sql plugins can be chosen to provide authentication service. For example, when using sql plugin, after changing the user's password via Plone user interface, you will see in the SQL table the corresponding columns, password and salt, values updated.

2012/05/03

Turn On/Off Plone Print CSS

ZMI portal_css 取消勾選 print.css 儲存設定後,列印樣式會有不同,設定前後的樣式如下:

有個 Print All 擴充模組,可以把目錄裡的所有項目,分頁顯示各項目的內容,例如新聞目錄的多則新聞,就可以一次印完。

想要 hardcopy 的列印效果,看來還是使用 print screen 的軟體。

2012/04/28

音樂人也選擇 Python

阿海跟小怪,都是愛玩科技的音樂人。

阿海: 該學 Rails 嗎? 小怪: 音樂人當然是學 Python 囉 Django 就是少了嘴炮的 Rails

他們選擇 Python,也值得你來認識。 PyCon Taiwan 2012 等你哦!