<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-318830256542227528</id><updated>2012-01-30T17:39:50.367+08:00</updated><category term='Python'/><category term='Geek'/><title type='text'>marr weblog</title><subtitle type='html'>learning to lead a life of awareness.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default?start-index=101&amp;max-results=100'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>193</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-4796752781205220152</id><published>2012-01-11T17:11:00.003+08:00</published><updated>2012-01-11T17:11:55.746+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Geek'/><title type='text'>Map Should Be Art Work</title><content type='html'>&lt;p&gt;在&lt;a href="http://gis.rchss.sinica.edu.tw/index.php?option=com_content&amp;view=article&amp;id=585:-gis-&amp;catid=48:2008-05-14-10-34-07&amp;Itemid=78"&gt;黃清琦老師的演講&lt;/a&gt;裡，他提到「地圖應該可以包含藝術美感的元素，而不只是客觀數據的呈現，有時候必須加入製圖人的主觀美學意見」。舉的例子是，從 GIS 精準的角度，標示的點可能出現在不符規範的位置，例如某個城市被標在河湖裡，此時適度地移標是必要的。臺灣輿圖暨解說圖研究的完成，是以台灣地形圖為基礎來整合舊的史地資料，必須假設長期以來地形未變，如果有局部的資料顯示地形隨時間曾改變，則需要修正。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-4796752781205220152?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/4796752781205220152/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=4796752781205220152' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4796752781205220152'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4796752781205220152'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2012/01/map-should-be-art-work.html' title='Map Should Be Art Work'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-4001789832032531625</id><published>2011-12-29T17:38:00.001+08:00</published><updated>2011-12-29T17:38:47.510+08:00</updated><title type='text'>KeyError: 'constrainTypesMode'</title><content type='html'>&lt;p&gt;Here is an excerpt of error I run into:&lt;/p&gt;&lt;pre&gt;URL: plone/app/layout/viewlets/contentactions.pt&lt;br /&gt;Line 32, Column 8&lt;br /&gt;Expression: &amp;lt;StringExpr u'plone.contentmenu'&amp;gt;&lt;br /&gt;...&lt;br /&gt;URL: plone/app/contentmenu/contentmenu.pt&lt;br /&gt;Line 1, Column 0&lt;br /&gt;Expression: &amp;lt;PathExpr standard:u'view/menu'&amp;gt;&lt;br /&gt;...&lt;br /&gt;KeyError: 'constrainTypesMode'&lt;/pre&gt;&lt;p&gt;It turns out from my typo in content/mytype.py, by the time when I hesitate whether if using a folderish type:&lt;/p&gt;&lt;pre&gt;-ArticleSchema = folder.ATContentTypeSchema.copy()&lt;br /&gt;+ArticleSchema = folder.ATFolderSchema.copy()&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-4001789832032531625?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/4001789832032531625/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=4001789832032531625' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4001789832032531625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4001789832032531625'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/12/keyerror-constraintypesmode.html' title='KeyError: &apos;constrainTypesMode&apos;'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7506839308763983148</id><published>2011-12-29T15:05:00.002+08:00</published><updated>2011-12-29T15:05:50.522+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Files to Edit for Adding Archetypes Type</title><content type='html'>&lt;p&gt;除了用 &lt;a href="http://marrtw.blogspot.com/2010/10/building-policy-package.html"&gt;zopeskel&lt;/a&gt; 來建立 Content Type 的方法，手動編輯檔案的話，下列是相關檔案的順序列表：&lt;/p&gt;&lt;pre&gt;interfaces/__init__.py&lt;br /&gt;interfaces/mytype.py&lt;br /&gt;content/configure.zcml&lt;br /&gt;content/mytype.py&lt;br /&gt;profiles/default/factorytool.xml&lt;br /&gt;profiles/default/types.xml&lt;br /&gt;profiles/default/types/mytype.xml&lt;br /&gt;config.py&lt;br /&gt;browser/configure.zcml&lt;br /&gt;browser/mytype.py&lt;br /&gt;browser/templates/mytype.pt&lt;br /&gt;locales/&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7506839308763983148?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7506839308763983148/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7506839308763983148' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7506839308763983148'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7506839308763983148'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/12/files-to-edit-for-adding-archetypes.html' title='Files to Edit for Adding Archetypes Type'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-8955624198794748485</id><published>2011-12-27T12:43:00.001+08:00</published><updated>2011-12-27T12:43:07.711+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>typesUseViewActionInListings in Plone PropertiesTool</title><content type='html'>&lt;p&gt;In my archetypes-based project, I create a folderish type Book to contain Chapter type. One of the last steps is to add Chapter type in typesUseViewActionInListings field.&lt;/p&gt;&lt;a href="https://picasaweb.google.com/lh/photo/2VtNkW-M6P2lH0uisAnMYtMTjNZETYmyPJy0liipFm0?feat=embedwebsite"&gt;&lt;img src="https://lh3.googleusercontent.com/-l0DqOq8osdM/TvlKhjwX69I/AAAAAAAAAEc/OVxU55HkO-I/s800/typesUse.png" height="109" width="589" /&gt;&lt;/a&gt;&lt;p&gt;For GenericSetup, it is in profiles/default/propertiestool.xml:&lt;/p&gt;&lt;pre&gt;&amp;lt;property name="typesUseViewActionInListings" type="lines"&amp;gt;&lt;br /&gt; &amp;lt;element value="Image"/&amp;gt;&lt;br /&gt; &amp;lt;element value="File"/&amp;gt;&lt;br /&gt; &amp;lt;element value="Chapter"/&amp;gt;&lt;br /&gt;&amp;lt;/property&amp;gt;&lt;/pre&gt;&lt;p&gt;Note that if Book added to that field, it will be bothering when you browse Book items in Contents tab.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-8955624198794748485?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/8955624198794748485/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=8955624198794748485' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8955624198794748485'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8955624198794748485'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/12/typesuseviewactioninlistings-in-plone.html' title='typesUseViewActionInListings in Plone PropertiesTool'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh3.googleusercontent.com/-l0DqOq8osdM/TvlKhjwX69I/AAAAAAAAAEc/OVxU55HkO-I/s72-c/typesUse.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-3243642929641578065</id><published>2011-12-19T23:37:00.000+08:00</published><updated>2011-12-19T23:38:33.204+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Collective TinyMCE Templates</title><content type='html'>&lt;p&gt;&lt;a href="http://pypi.python.org/pypi/collective.tinymcetemplates/"&gt;collective.tinymcetemplates&lt;/a&gt; is a TinyMCE Plugin for templates and snippets. It works with Plone 4.1.3. You can see a "Insert predefined template content" icon added:&lt;/p&gt;&lt;a href="https://picasaweb.google.com/lh/photo/UwUZPKZLaOSS1VjGqAinFNMTjNZETYmyPJy0liipFm0?feat=embedwebsite"&gt;&lt;img src="https://lh3.googleusercontent.com/-bo8yALrNV1E/Tu9YEp99GuI/AAAAAAAAAEE/sBeIErfPXXM/s800/tinymcetemplates.png" height="402" width="569" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-3243642929641578065?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/3243642929641578065/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=3243642929641578065' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3243642929641578065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3243642929641578065'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/12/collective-tinymce-templates.html' title='Collective TinyMCE Templates'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh3.googleusercontent.com/-bo8yALrNV1E/Tu9YEp99GuI/AAAAAAAAAEE/sBeIErfPXXM/s72-c/tinymcetemplates.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7992312322925285780</id><published>2011-12-19T11:29:00.001+08:00</published><updated>2011-12-19T11:29:18.437+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Transmogrifier Export in Action</title><content type='html'>&lt;p&gt;&lt;a href="http://marrtw.blogspot.com/2010/11/transmogrifier-importexport-made-easy.html"&gt;transmogrifier&lt;/a&gt; 可以匯入或匯出 Plone 網站的內容，之前的經驗以匯入 CSV 或 PostgreSQL 資料為主，現在完成匯出到 PostgreSQL 的實作。&lt;/p&gt;&lt;p&gt;以 develop.cfg 為例，要修改的內容如下：&lt;/p&gt;&lt;pre&gt;eggs +=&lt;br /&gt;    psycopg2&lt;br /&gt;    SQLAlchemy == 0.6.5&lt;br /&gt;    zope.sqlalchemy&lt;br /&gt;    plone.app.transmogrifier&lt;/pre&gt;&lt;p&gt;客製 myproj.transmogrifier 的內容摘要如下：&lt;/p&gt;&lt;pre&gt;myproj.transmogrifier/configure.zcml&lt;br /&gt;&lt;br /&gt;  &amp;lt;transmogrifier:registerConfig&lt;br /&gt;    name="myproj.transmogrifier.exportContent"&lt;br /&gt;    title="MyProject Export Content"&lt;br /&gt;    description="Transmogrifier Pipeline config to export contents."&lt;br /&gt;    configuration="confs/export.cfg"&lt;br /&gt;    /&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;!-- register our blueprints --&amp;gt;&lt;br /&gt;  &amp;lt;utility&lt;br /&gt;    component=".contentexport.ContentExporterSection"&lt;br /&gt;    name="myproj.transmogrifier.contentexporter"&lt;br /&gt;    /&amp;gt;&lt;br /&gt;&lt;br /&gt;myproj.transmogrifier/browser/configure.zcml&lt;br /&gt;&lt;br /&gt;&amp;lt;configure&lt;br /&gt;    xmlns="http://namespaces.zope.org/zope"&lt;br /&gt;    xmlns:browser="http://namespaces.zope.org/browser"&lt;br /&gt;    xmlns:zcml="http://namespaces.zope.org/zcml"&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;browser:page&lt;br /&gt;        for="*"&lt;br /&gt;        class=".contentexport.ContentExport"&lt;br /&gt;        name="content-export"&lt;br /&gt;        permission="cmf.ManagePortal"&lt;br /&gt;        /&amp;gt;&lt;br /&gt;&amp;lt;configure&amp;gt;&lt;br /&gt;&lt;br /&gt;myproj.transmogrifier/browser/contentexport.py&lt;br /&gt;&lt;br /&gt;class ContentExport(BrowserView):&lt;br /&gt;&lt;br /&gt;    def __call__(self):&lt;br /&gt;        transmogrifier = ITransmogrifier(self.context)&lt;br /&gt;        transmogrifier('myproj.transmogrifier.exportContent')&lt;br /&gt;        self.request.response.write('exported')&lt;br /&gt;&lt;br /&gt;myproj.transmogrifier/confs/export.cfg&lt;br /&gt;&lt;br /&gt;[transmogrifier]&lt;br /&gt;pipeline =&lt;br /&gt;    contentexport&lt;br /&gt;&lt;br /&gt;[contentexport]&lt;br /&gt;blueprint = myproj.transmogrifier.contentexporter&lt;br /&gt;dsn = postgresql://myuser:mysecret@localhost:5432/mydb&lt;br /&gt;portal-types = News Item, Event&lt;br /&gt;review-states = published&lt;/pre&gt;&lt;p&gt;另外，還有 myproj.transmogrifier/models.py 和 myproj.transmogrifier/contentexport.py 兩個主檔，分別負責資料庫連線和匯出工作。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7992312322925285780?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7992312322925285780/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7992312322925285780' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7992312322925285780'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7992312322925285780'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/12/transmogrifier-export-in-action.html' title='Transmogrifier Export in Action'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-5398609300535571575</id><published>2011-12-18T00:13:00.000+08:00</published><updated>2011-12-18T00:13:46.269+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Search Portlet for Current Section</title><content type='html'>&lt;p&gt;By default, Plone provides a searchbox viewlet and a search portlet. With the viewlet, you can decide whether to search only within the current section. However, the porlet does not apply this behavior. My requirement could be illustrated by this screenshot:&lt;/p&gt;&lt;a href="https://picasaweb.google.com/lh/photo/uTiNC6u8IW0jOFRb4Em1c9MTjNZETYmyPJy0liipFm0?feat=embedwebsite"&gt;&lt;img src="https://lh6.googleusercontent.com/-wIhjcWqXN4s/Tuyx3EJp2LI/AAAAAAAAADc/egRXWU1s7jE/s800/search-current-section.png" height="132" width="321" /&gt;&lt;/a&gt;&lt;p&gt;Here are the files to be modified:&lt;/p&gt;&lt;pre&gt;$ diff plone/app/portlets/portlets/search.py&lt;br /&gt;55a56,61&lt;br /&gt;&amp;gt;     def folder_path(self):&lt;br /&gt;&amp;gt;         context_state = getMultiAdapter((self.context, self.request),&lt;br /&gt;&amp;gt;                                          name=u'plone_context_state')&lt;br /&gt;&amp;gt;         folder = context_state.folder()&lt;br /&gt;&amp;gt;         return '/'.join(folder.getPhysicalPath())&lt;br /&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;$ diff plone/app/portlets/portlets/search.pt&lt;br /&gt;29a30,44&lt;br /&gt;&amp;gt;&lt;br /&gt;&amp;gt;         &amp;lt;div class="searchSection"&amp;gt;&lt;br /&gt;&amp;gt;             &amp;lt;input id="searchbox_currentfolder_only"&lt;br /&gt;&amp;gt;                    class="noborder"&lt;br /&gt;&amp;gt;                    type="hidden"&lt;br /&gt;&amp;gt;                    name="path"&lt;br /&gt;&amp;gt;                    tal:attributes="value view/folder_path"&lt;br /&gt;&amp;gt;                    /&amp;gt;&lt;br /&gt;&amp;gt;             &amp;lt;label for="searchbox_currentfolder_only"&lt;br /&gt;&amp;gt;                    i18n:translate="label_searchbox_currentfolder_only"&lt;br /&gt;&amp;gt;                    style="cursor: pointer"&amp;gt;&lt;br /&gt;&amp;gt;                 only in current section&lt;br /&gt;&amp;gt;             &amp;lt;/label&amp;gt;&lt;br /&gt;&amp;gt;         &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-5398609300535571575?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/5398609300535571575/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=5398609300535571575' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5398609300535571575'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5398609300535571575'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/12/search-portlet-for-current-section.html' title='Search Portlet for Current Section'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh6.googleusercontent.com/-wIhjcWqXN4s/Tuyx3EJp2LI/AAAAAAAAADc/egRXWU1s7jE/s72-c/search-current-section.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7851279965156004579</id><published>2011-12-16T16:37:00.003+08:00</published><updated>2011-12-16T18:05:29.846+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Table View in Contents Tab</title><content type='html'>&lt;p&gt;Here is the default view for Plone.&lt;/p&gt;&lt;a href="https://picasaweb.google.com/lh/photo/dOpo5wZUxgH-FZnxJHa5C9MTjNZETYmyPJy0liipFm0?feat=embedwebsite"&gt;&lt;img src="https://lh3.googleusercontent.com/-ifUO3K5vsXs/Turb4UizqWI/AAAAAAAAADA/DX8IiufkHak/s800/contents-table-view.png" height="310" width="461" /&gt;&lt;/a&gt;&lt;p&gt;I want Published column instead of Modified column. By 'grep -r listing-table' I find the target file is at plone.app.content/browser/table.pt. Related python files are batching.py, container.py and browser/tavleview.py. My naive guess of changing keyword modified to effective, does not work, showing LocationError.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7851279965156004579?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7851279965156004579/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7851279965156004579' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7851279965156004579'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7851279965156004579'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/12/table-view-in-contents-tab.html' title='Table View in Contents Tab'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh3.googleusercontent.com/-ifUO3K5vsXs/Turb4UizqWI/AAAAAAAAADA/DX8IiufkHak/s72-c/contents-table-view.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-299175358073685381</id><published>2011-12-16T10:31:00.001+08:00</published><updated>2011-12-16T10:31:28.505+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Display Logos Based on Paths</title><content type='html'>&lt;p&gt;Here is a customization in plone.app.layout/viewlets/common.py that can display customized logos based on tab paths.&lt;/p&gt;&lt;pre&gt;class LogoViewlet(ViewletBase):&lt;br /&gt;    index = ViewPageTemplateFile('logo.pt')&lt;br /&gt;&lt;br /&gt;    def update(self):&lt;br /&gt;        super(LogoViewlet, self).update()&lt;br /&gt;&lt;br /&gt;        portal = self.portal_state.portal()&lt;br /&gt;        bprops = portal.restrictedTraverse('base_properties', None)&lt;br /&gt;        if bprops is not None:&lt;br /&gt;            logoName = bprops.logoName&lt;br /&gt;        else:&lt;br /&gt;            logoName = 'logo.jpg'&lt;br /&gt;&lt;b&gt;        plone_url = getToolByName(self.context, 'portal_url')()&lt;br /&gt;        plone_url_len = len(plone_url)&lt;br /&gt;        request = self.request&lt;br /&gt;        url = request['URL']&lt;br /&gt;        path = url[plone_url_len:]&lt;br /&gt;        if path.startswith('/news'):&lt;br /&gt;            logoName = 'logo-news.png'&lt;br /&gt;        if path.startswith('/events'):&lt;br /&gt;            logoName = 'logo-events.png'&lt;/b&gt;&lt;/pre&gt;&lt;p&gt;To illustrate how the path variable works, add a Python Script in ZMI and use the following sample code:&lt;/p&gt;&lt;pre&gt;from Products.CMFCore.utils import getToolByName&lt;br /&gt;&lt;br /&gt;plone_url = getToolByName(context, 'portal_url')()&lt;br /&gt;print "plone_url = %s\n" % (plone_url),&lt;br /&gt;plone_url_len = len(plone_url)&lt;br /&gt;request = context.REQUEST&lt;br /&gt;url = request['URL']&lt;br /&gt;path = url[plone_url_len:]&lt;br /&gt;print "url = %s, path = %s" % (url, path)&lt;br /&gt;return printed&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-299175358073685381?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/299175358073685381/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=299175358073685381' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/299175358073685381'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/299175358073685381'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/12/display-logos-based-on-paths.html' title='Display Logos Based on Paths'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-4743839197279172542</id><published>2011-12-15T18:11:00.002+08:00</published><updated>2011-12-15T18:16:12.131+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>portal_tab globalnav customization</title><content type='html'>&lt;p&gt;portal_tab, or globalnav, or global_section in Plone, provides clickable links. Let's say, we want a tab to open in new browser window when clicked. The trick is in plone.app.layout/viewlets/sections.pt.&lt;/p&gt;&lt;pre&gt;&amp;lt;ul id="portal-globalnav"&lt;br /&gt;    tal:define="selected_tab python:view.selected_portal_tab"&lt;br /&gt;    &amp;gt;&amp;lt;tal:tabs tal:repeat="tab portal_tabs"&lt;br /&gt;    &amp;gt;&amp;lt;li tal:define="tid tab/id;&lt;br /&gt;                     turl tab/url"&lt;br /&gt;         tal:attributes="id string:portaltab-${tid};&lt;br /&gt;                        class python:selected_tab==tid and 'selected' or 'plain'"&lt;br /&gt;        &amp;gt;&amp;lt;a href=""&lt;br /&gt;           tal:content="tab/name"&lt;br /&gt;           tal:attributes="href tab/url;&lt;br /&gt;                           title tab/description|nothing;&lt;br /&gt;                           &lt;b&gt;target python: (turl.endswith('/my-url') and '_blank' or '')&lt;/b&gt;"&amp;gt;&lt;br /&gt;        Tab Name&lt;br /&gt;        &amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/tal:tabs&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-4743839197279172542?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/4743839197279172542/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=4743839197279172542' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4743839197279172542'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4743839197279172542'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/12/portaltab-globalnav-customization.html' title='portal_tab globalnav customization'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-9164842958812976625</id><published>2011-12-02T21:48:00.001+08:00</published><updated>2011-12-02T22:37:54.677+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>File-explorer like Document Management Tool</title><content type='html'>&lt;p&gt;&lt;a href="http://pypi.python.org/pypi/collective.edm.listing"&gt;collective.edm.listing&lt;/a&gt; provides a custom view for folder. It works with &lt;a href="http://plone.org/products/collective.quickupload"&gt;Plone Quick Upload&lt;/a&gt; to upload new files.&lt;/p&gt;&lt;p&gt;&lt;a href="https://picasaweb.google.com/lh/photo/1Pfw7bVzTCDY8WFHHS2SL9MTjNZETYmyPJy0liipFm0?feat=embedwebsite"&gt;&lt;img src="https://lh5.googleusercontent.com/-k0OClJfYzos/TtjWfEKcKXI/AAAAAAAAABg/9th7iSIXWqw/s640/edm-listing.png" height="387" width="640" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="https://picasaweb.google.com/lh/photo/K1yZc8QEh-lLaQgAK6w71tMTjNZETYmyPJy0liipFm0?feat=embedwebsite"&gt;&lt;img src="https://lh4.googleusercontent.com/-nV81DeN8Pac/TtjZiZqCzDI/AAAAAAAAAB4/iu2G9VhttMU/s800/edm-listing2.png" height="363" width="388" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-9164842958812976625?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/9164842958812976625/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=9164842958812976625' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/9164842958812976625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/9164842958812976625'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/12/file-explorer-like-document-management.html' title='File-explorer like Document Management Tool'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh5.googleusercontent.com/-k0OClJfYzos/TtjWfEKcKXI/AAAAAAAAABg/9th7iSIXWqw/s72-c/edm-listing.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-2464832875444685969</id><published>2011-11-30T21:49:00.001+08:00</published><updated>2011-11-30T23:12:23.598+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Geek'/><title type='text'>Workteam with oDesk</title><content type='html'>&lt;p&gt;感謝 &lt;a href="http://blog.pofeng.org/"&gt;pofeng&lt;/a&gt; 的安排，有機會到 &lt;a href="https://groups.google.com/group/cloudtw"&gt;CloudTW&lt;/a&gt; &lt;a href="https://groups.google.com/group/cloudtw/t/a113f43941d7db33?hl=zh-TW"&gt;聚會&lt;/a&gt;分享 oDesk 的使用經驗。&lt;/p&gt;&lt;p&gt;簡報檔:&amp;nbsp;&lt;a href="http://www.slideshare.net/marr/workteam-with-odesk"&gt;http://www.slideshare.net/marr/workteam-with-odesk&lt;/a&gt;&lt;/p&gt;&lt;p&gt;討論過程中，有人回應「這是個讓 developer 找 developer 的服務」，沒錯，所以適合在技術人員聚會裡介紹 oDesk 服務，用它來累積專案管理的經驗，oDesk 試著促成一個良性循環，讓雇主和工程師都願意重視評價結果，彼此長期建立良好名聲。&lt;/p&gt;&lt;p&gt;自己的使用經驗，只以 employer 身份發包 Plone web development，值得找機會以 developer 角色登入，了解專業技能認證的流程。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-2464832875444685969?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/2464832875444685969/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=2464832875444685969' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2464832875444685969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2464832875444685969'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/11/workteam-with-odesk.html' title='Workteam with oDesk'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-4474752699187752271</id><published>2011-11-29T13:39:00.001+08:00</published><updated>2011-11-29T14:40:51.658+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Geek'/><title type='text'>Joyent Cloud Computing</title><content type='html'>&lt;p&gt;&lt;a href="https://groups.google.com/group/cloudtw/browse_thread/thread/ec1d361045c50c71"&gt;CloudTW 有段 SmartOS 的討論&lt;/a&gt;，於是查了相關資訊。&lt;a href="http://joyent.com/"&gt;Joyent&lt;/a&gt; 成立於 2004 年，業務內容與日俱進，目前已發展為 cloud computing software stack 提供者，據說公司接收了 &lt;a href="http://en.wikipedia.org/wiki/OpenSolaris"&gt;OpenSolaris&lt;/a&gt; 和 &lt;a href="http://en.wikipedia.org/wiki/Illumos"&gt;Illumos&lt;/a&gt; 的開發人員，&lt;a href="http://www.worldjournal.com/view/full_hof/12446291/article-%E5%90%B3%E6%89%BF%E6%BE%94-%E5%8F%9B%E9%80%86%E5%B0%8F%E5%AD%90%E9%A3%9B%E4%B8%8A%E9%9B%B2%E7%AB%AF"&gt;併購 LayerBoom 的案例&lt;/a&gt;，也能跟&lt;a href="http://www.youtube.com/watch?v=4qSdxuAu28o"&gt;台灣教改挫敗&lt;/a&gt;扯上關係，總之，這些都算是藉由 cloud computing 時代造英雄的例子。&lt;/p&gt;&lt;p&gt;網路上找到的既有資料，都顯示 Joyent 是家強調技術本位的公司，看了 &lt;a href="http://smartos.org/2011/09/14/introducing-your-smartos-community-manager/"&gt;Community Manager&lt;/a&gt; 也就是技術公關，心想，台灣何時會有這種角色?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-4474752699187752271?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/4474752699187752271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=4474752699187752271' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4474752699187752271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4474752699187752271'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/11/joyent-cloud-computing.html' title='Joyent Cloud Computing'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-5361423947352878988</id><published>2011-11-26T09:25:00.001+08:00</published><updated>2011-11-26T09:33:29.019+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>POSKeyError: 'No blob file'</title><content type='html'>&lt;p&gt;執行 Plone migration 時，遇到 POSKeyError 錯誤，發現某個目錄裡的 File 內容出了問題，刪除這些 File 之後，就可以順利從 Plone 4.0.5 昇級到 4.1.2。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-5361423947352878988?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/5361423947352878988/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=5361423947352878988' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5361423947352878988'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5361423947352878988'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/11/poskeyerror-no-blob-file.html' title='POSKeyError: &apos;No blob file&apos;'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-1644027875378999297</id><published>2011-11-23T16:53:00.001+08:00</published><updated>2011-11-23T17:11:50.586+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Content Not Existing?</title><content type='html'>&lt;p&gt;遇到 Plone instance 透過 http://mysite.com/news-events 之類的網址，出現「網頁並不存在」的訊息，透過 http://mysite.com/news-events/folder_contents 網址是可以看到內容，最後發現用 http://mysite.com/news-events/selectViewTemplate?templateId=folder_listing 之類的方式，就能解決問題。發生原因是之前選用了 lead image 擴充模組的顯示方式，當這個顯示方式不存在時，就會造成網頁無法正常顯示的問題。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-1644027875378999297?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/1644027875378999297/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=1644027875378999297' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1644027875378999297'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1644027875378999297'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/11/content-not-existing.html' title='Content Not Existing?'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-6728197668874703315</id><published>2011-11-19T13:32:00.001+08:00</published><updated>2011-11-19T13:32:32.598+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Content Type Extension</title><content type='html'>&lt;a href="http://pypi.python.org/pypi/archetypes.schemaextender"&gt;SchemaExtender&lt;/a&gt; 可以動態調整 Archetypes 型別定義值，下列是一個 extender.py 範例：&lt;br /&gt;&lt;pre&gt;from zope.component import adapts&lt;br /&gt;from zope.interface import implements&lt;br /&gt;&lt;br /&gt;from archetypes.schemaextender.interfaces import ISchemaExtender&lt;br /&gt;from archetypes.schemaextender.field import ExtensionField&lt;br /&gt;from plone.app.blob.field import BlobField&lt;br /&gt;&lt;br /&gt;from Products.Archetypes import atapi&lt;br /&gt;&lt;br /&gt;from example.blobattype.interfaces import IExampleATType&lt;br /&gt;from example.blobattype import blobattypeMessageFactory as _&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class ExtensionBlobField(ExtensionField, BlobField):&lt;br /&gt;    """ derivative of blobfield for extending schemas """&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class ExampleATTypeExtender(object):&lt;br /&gt;    adapts(IExampleATType)&lt;br /&gt;    implements(ISchemaExtender)&lt;br /&gt;&lt;br /&gt;    fields = [&lt;br /&gt;        ExtensionBlobField('afile',&lt;br /&gt;            widget=atapi.FileWidget(&lt;br /&gt;                label=_(u"A file"),&lt;br /&gt;                description=_(u"Some file"),&lt;br /&gt;            ),&lt;br /&gt;            required=True,&lt;br /&gt;            validators=('isNonEmptyFile'),&lt;br /&gt;        ),&lt;br /&gt;&lt;br /&gt;        ExtensionBlobField('secondfile',&lt;br /&gt;            widget=atapi.FileWidget(&lt;br /&gt;                label=_(u"Some other file"),&lt;br /&gt;                description=_(u"Some other file"),&lt;br /&gt;            ),&lt;br /&gt;            required=True,&lt;br /&gt;            validators=('isNonEmptyFile'),&lt;br /&gt;        ),&lt;br /&gt;    ]&lt;br /&gt;&lt;br /&gt;    def __init__(self, context):&lt;br /&gt;        self.context = context&lt;br /&gt;&lt;br /&gt;    def getFields(self):&lt;br /&gt;        return self.fields&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-6728197668874703315?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/6728197668874703315/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=6728197668874703315' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6728197668874703315'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6728197668874703315'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/11/content-type-extension.html' title='Content Type Extension'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-590639588369848965</id><published>2011-11-17T22:45:00.001+08:00</published><updated>2011-11-17T22:47:56.094+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>ATBTreeFolder vs ATFolder</title><content type='html'>&lt;p&gt;最近把一個 Plone 網站從 3.3.5 昇級到 4.0.7，在 ZMI 裡看得到 ATBTreeFolder 和 ATFolder 兩種目錄型別，ATBTreeFolder 就是所謂的 Large Folder，在 Plone 3 之前的時代，用它來儲存大量的內容項目，到 Plone 4 之後，&lt;a href="http://plone.org/products/plone/roadmap/191"&gt;目錄型別被統合了&lt;/a&gt;。&lt;/p&gt;&lt;a href="https://picasaweb.google.com/lh/photo/DVZFc_PpM9sIj_wLc0IFBA?feat=embedwebsite"&gt;&lt;img height="204" src="https://lh4.googleusercontent.com/-gUm10uy4UM0/TsUL0Fwe6pI/AAAAAAAAAAo/-gWZQir1QZE/s640/folder-types.png" width="640" /&gt;&lt;/a&gt;&lt;p&gt;ATBTreeFolder 在 ZMI 裡提供 Security 設定頁籤，可以指定 &lt;a href="http://collective-docs.readthedocs.org/en/latest/security/local_roles.html"&gt;Local Roles&lt;/a&gt;，不過 Plone 4 裡的 ATFolder 並沒有這樣的設定介面，必須搭配 &lt;a href="http://pypi.python.org/pypi/plone.app.workflow/"&gt;plone.app.workflow&lt;/a&gt; &lt;a href="http://stackoverflow.com/questions/8137197/local-roles-for-atbtreefolder-vs-atfolder"&gt;編輯 sharing.xml 來擴充權限設定介面&lt;/a&gt;。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-590639588369848965?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/590639588369848965/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=590639588369848965' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/590639588369848965'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/590639588369848965'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/11/atbtreefolder-vs-atfolder.html' title='ATBTreeFolder vs ATFolder'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh4.googleusercontent.com/-gUm10uy4UM0/TsUL0Fwe6pI/AAAAAAAAAAo/-gWZQir1QZE/s72-c/folder-types.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-2981607894364253536</id><published>2011-11-08T00:58:00.000+08:00</published><updated>2011-11-08T00:58:09.017+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>CMF Action Condition</title><content type='html'>&lt;p&gt;使用 Products.Carousel 時，會新增一個 Carousel tab，如果想在特定的目錄裡才出現它，可以在 ZMI portal_actions/object/carousel 的 Condition (Expression) 欄位輸入下列表示式：&lt;/p&gt;&lt;pre&gt;python: (context.id in ['carousel']) and plone_context_state.is_folderish()&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-2981607894364253536?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/2981607894364253536/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=2981607894364253536' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2981607894364253536'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2981607894364253536'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/11/cmf-action-condition.html' title='CMF Action Condition'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-8481680271838547047</id><published>2011-11-06T23:23:00.001+08:00</published><updated>2011-12-07T10:57:13.818+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Localization with i18ndude</title><content type='html'>&lt;p&gt;以 develop.cfg 為例，先在 parts 裡加一行 i18ndude，再加一整段的 [i18ndude] 內容：&lt;/p&gt;&lt;pre&gt;parts =&lt;br /&gt;    ...&lt;br /&gt;    i18ndude&lt;br /&gt;&lt;br /&gt;[i18ndude]&lt;br /&gt;unzip = true&lt;br /&gt;recipe = zc.recipe.egg&lt;br /&gt;eggs = i18ndude&lt;/pre&gt;&lt;p&gt;以 my.example 模組專案為例，處理 .po 檔案的步驟如下：&lt;/p&gt;&lt;pre&gt;$ cd src/my.example/my/example&lt;br /&gt;$ vi configure.zcml&lt;br /&gt;&amp;lt;configure&lt;br /&gt;  xmlns:i18n="http://namespaces.zope.org/i18n"&lt;br /&gt;&lt;br /&gt;  &amp;lt;i18n:registerTranslations directory="locales" /&amp;gt;&lt;br /&gt;&lt;br /&gt;$ mkdir -p locales/zh_TW/LC_MESSAGES&lt;br /&gt;$ i18ndude rebuild-pot --pot locales/my.example.pot --create my.example .&lt;br /&gt;$ touch locales/zh_TW/LC_MESSAGES/my.example.po&lt;br /&gt;$ i18ndude sync --pot locales/my.example.pot locales/zh_TW/LC_MESSAGES/my.example.po&lt;br /&gt;$ msgfmt -o locales/zh_TW/LC_MESSAGES/my.example.mo locales/zh_TW/LC_MESSAGES/my.example.po&lt;/pre&gt;&lt;p&gt;如果有手動增加的 po 內容，通常是寫在 manual.pot 裡，再用下列指令併進 pot 檔案裡。&lt;/p&gt;&lt;pre&gt;$ i18ndude rebuild-pot --pot locales/my.example.pot --create my.example --merge manual.pot .&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-8481680271838547047?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/8481680271838547047/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=8481680271838547047' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8481680271838547047'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8481680271838547047'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/11/localization-with-i18ndude.html' title='Localization with i18ndude'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7514745369130401853</id><published>2011-10-20T00:15:00.000+08:00</published><updated>2011-12-06T23:26:33.592+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Creating Windows Buildout Project</title><content type='html'>&lt;p&gt;試著&lt;a href="http://plone.org/documentation/kb/using-buildout-on-windows"&gt;在 Windows 建立 Plone&lt;/a&gt; 4.1.2 的 Buildout 專案環境，事先要安裝 Python 2.6.7、&lt;a href="http://sourceforge.net/projects/pywin32/files/pywin32/"&gt;PyWin32&lt;/a&gt;、&lt;a href="http://effbot.org/media/downloads/PIL-1.1.6.win32-py2.6.exe"&gt;PIL 1.1.6&lt;/a&gt;、&lt;a href="http://pypi.python.org/pypi/setuptools#windows"&gt;easy_install&lt;/a&gt;、&lt;a href="http://www.collab.net/downloads/subversion/"&gt;Subversion&lt;/a&gt;、&lt;a href="http://code.google.com/p/msysgit/"&gt;Git&lt;/a&gt;。再下載安裝 zopeskel：&lt;/p&gt;&lt;pre&gt;C:\&amp;gt; easy_install zopeskel&lt;/pre&gt;&lt;p&gt;接著，可以用 zopeskel 來建立 Buildout 專案，範例指令如下：&lt;/p&gt;&lt;pre&gt;C:\&amp;gt; mkdir Plone&lt;br /&gt;C:\&amp;gt; cd Plone&lt;br /&gt;C:\Plone&amp;gt; zopeskel basic_buildout&lt;br /&gt;&lt;br /&gt;basic_buildout: A basic buildout skeleton&lt;br /&gt;This creates a basic skeleton for a buildout.&lt;br /&gt;Enter project name: myproj&lt;br /&gt;&lt;br /&gt;If at any point, you need additional help for a question, you can enter&lt;br /&gt;'?' and press RETURN.&lt;br /&gt;&lt;br /&gt;Expert Mode? (What question mode would you like? (easy/expert/all)?) ['easy']:&lt;/pre&gt;&lt;p&gt;如果遇到下列訊息，可以予以略過不理會：&lt;/p&gt;&lt;pre&gt;UserWarning:&lt;br /&gt;You don't have the C version of NameMapper installed!&lt;br /&gt;I'm disabling Cheetah's useStackFrames option as it is painfully slow&lt;br /&gt;with the Python version of NameMapper.&lt;br /&gt;You should get a copy of Cheetah with the compiled C version of NameMapper.&lt;br /&gt;You don't have the C version of NameMapper installed!&lt;/pre&gt;&lt;p&gt;剛建立的 myproj 專案目錄裡，只有 bootstrap.py 和 buildout.cfg 兩個檔案，執行 python bootstrap.py 之後，會建立必要的目錄架構，特別是 bin 目錄裡會建立 buildout 工具程式，額外的撇步是指定 zc.buildout 版本是 1.4.4，不然預設裝的 1.5.2 版本會造成衝突，範例訊息如下：&lt;/p&gt;&lt;pre&gt;C:\Plone\myproj&gt; python bootstrap.py -v 1.4.4&lt;br /&gt;Downloading http://pypi.python.org/packages/2.6/s/setuptools/setup&lt;br /&gt;tools-0.6c11-py2.6.egg&lt;br /&gt;Creating directory 'C:\\Plone\\myproj\\bin'.&lt;br /&gt;Creating directory 'C:\\Plone\\myproj\\parts'.&lt;br /&gt;Creating directory 'C:\\Plone\\myproj\\eggs'.&lt;br /&gt;Creating directory 'C:\\Plone\\myproj\\develop-eggs'.&lt;br /&gt;Getting distribution for 'setuptools'.&lt;br /&gt;Got setuptools 0.6c12dev-r88846.&lt;br /&gt;Generated script 'C:\\Plone\\myproj\\bin\\buildout'.&lt;/pre&gt;&lt;p&gt;預設的 buildout.cfg 檔案內容如下：&lt;/p&gt;&lt;pre&gt;[buildout]&lt;br /&gt;parts =&lt;br /&gt;    templer&lt;br /&gt;&lt;br /&gt;[templer]&lt;br /&gt;recipe = zc.recipe.egg&lt;br /&gt;eggs =&lt;br /&gt;    PasteScript&lt;br /&gt;    templer.core&lt;/pre&gt;&lt;p&gt;不過，我並沒使用這個 buildout.cfg 內容，而是直接複製 Linux 環境上安裝的 *.cfg 檔案，修改 base.cfg 和 lxml_static.cfg 的 eggs-directory 和 download-cache 配合 Windows 環境的目錄，再執行 buildout：&lt;/p&gt;&lt;pre&gt;C:\Plone\myproj&gt; bin\buildout -c develop.cfg&lt;br /&gt;mr.developer: Creating missing sources dir C:\Plone\myproj\src.&lt;br /&gt;Getting distribution for 'zc.recipe.egg==1.2.2'.&lt;br /&gt;Got zc.recipe.egg 1.2.2.&lt;br /&gt;Getting distribution for 'plone.recipe.zope2instance==4.1.9'.&lt;br /&gt;Got plone.recipe.zope2instance 4.1.9.&lt;br /&gt;Getting distribution for 'Zope2==2.13.10'.&lt;br /&gt;Installing Zope2 2.13.10&lt;br /&gt;Caused installation of a distribution:&lt;br /&gt;zope2 2.13.10&lt;br /&gt;with a different project name.&lt;br /&gt;Got zope2 2.13.10.&lt;br /&gt;Getting distribution for 'mailinglogger==3.3.3'.&lt;br /&gt;Got mailinglogger 3.3.3.&lt;br /&gt;Getting distribution for 'nt-svcutils==2.13.0'.&lt;br /&gt;Got nt-svcutils 2.13.0.&lt;br /&gt;Getting distribution for 'Products.StandardCacheManagers==2.13.0'.&lt;br /&gt;Installing Products.StandardCacheManagers 2.13.0&lt;br /&gt;Caused installation of a distribution:&lt;br /&gt;products.standardcachemanagers 2.13.0&lt;br /&gt;with a different project name.&lt;br /&gt;Got products.standardcachemanagers 2.13.0.&lt;br /&gt;Getting distribution for 'Products.PythonScripts==2.13.0'.&lt;br /&gt;...&lt;/pre&gt;&lt;p&gt;安裝 Pillow 時，需要安裝 &lt;a href="http://www.microsoft.com/visualstudio/en-us/products/2008-editions/express"&gt;Visual Studio 2008 Express&lt;/a&gt;，據說 2008 之外的版本並不行：&lt;/p&gt;&lt;pre&gt;error: Setup script exited with error: Unable to find vcvarsall.bat&lt;br /&gt;An error occured when trying to install Pillow 1.7.2. Look above this message fo&lt;br /&gt;r any errors that were output by easy_install.&lt;br /&gt;While:&lt;br /&gt;  Installing instance.&lt;br /&gt;  Getting distribution for 'Pillow==1.7.2'.&lt;br /&gt;Error: Couldn't install: Pillow 1.7.2&lt;/pre&gt;&lt;p&gt;編譯 lxml 如果遇到錯誤，要&lt;a href="http://lxml.de/build.html#static-linking-on-windows"&gt;補足需要的檔案&lt;/a&gt;，以 Plone 4.1.3 為例，要下載 lxml 2.3.2 的原始碼。如果沒成功的話，還可以試試直接&lt;a href="http://vvincl.blogspot.com/2010/10/install-lxml-in-windows.html"&gt;安裝較低的 lxml 版本&lt;/a&gt;。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7514745369130401853?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7514745369130401853/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7514745369130401853' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7514745369130401853'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7514745369130401853'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/10/creating-windows-buildout-project.html' title='Creating Windows Buildout Project'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-8709665061711963631</id><published>2011-10-17T16:21:00.000+08:00</published><updated>2011-10-17T16:21:14.041+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Search Events and News via Calendar Portlet</title><content type='html'>&lt;p&gt;Plone 的月曆方框預設是搭配 Event 來顯示，如果某一天是在 Event 發生的期間，就可以在月曆上點選來顯示，它會先利用類似 search?review_state=published&amp;start.query:record:list:date... 網址格式來搜尋，如果想要&lt;a href="http://stackoverflow.com/questions/7525735"&gt;增加其他內容型別到月曆方框&lt;/a&gt;，例如新聞的話，就要到 ZMI 的 portal_calendar 新增型別，並為型別新增 start 和 end 屬性值。&lt;/p&gt;&lt;a href="https://picasaweb.google.com/lh/photo/OBNNoATrNQedmpB6RzYIFA?feat=embedwebsite"&gt;&lt;img src="https://lh4.googleusercontent.com/-OKH5M4OBfqI/TpvjS3YNvxI/AAAAAAAADHQ/iJHfD_vcHQA/s800/calendar.png" height="536" width="492" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-8709665061711963631?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/8709665061711963631/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=8709665061711963631' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8709665061711963631'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8709665061711963631'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/10/search-events-and-news-via-calendar.html' title='Search Events and News via Calendar Portlet'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh4.googleusercontent.com/-OKH5M4OBfqI/TpvjS3YNvxI/AAAAAAAADHQ/iJHfD_vcHQA/s72-c/calendar.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-8566564759728439787</id><published>2011-10-15T23:40:00.000+08:00</published><updated>2011-10-15T23:40:42.433+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>RelStorage Installation</title><content type='html'>&lt;p&gt;&lt;a href="http://pypi.python.org/pypi/RelStorage"&gt;RelStorage&lt;/a&gt; 是 ZODB 的後端儲存工具，能夠將 pickle 資料存到關聯式資料庫，也就是說，經由 RelStorage 和 ZODB 的合作，Plone 裡的內容可以直接轉存到 MySQL、PostgreSQL 之類的資料庫裡，讓&lt;a href="http://marrtw.blogspot.com/2009/05/zodb-or-sql.html"&gt;資料庫的類型選擇和轉換&lt;/a&gt;更具彈性。&lt;/p&gt;&lt;p&gt;經過原作者 &lt;a href="http://shane.willowrise.com/"&gt;Shane Hathaway&lt;/a&gt; 不斷的改進，安裝 RelStorage 變得很簡單，以 Ubuntu 和 PostgreSQL 為搭配範例，先要建立適當的帳號和資料庫：&lt;/p&gt;&lt;pre&gt;$ sudo su - postgres&lt;br /&gt;$ createuser --pwprompt zodbuser&lt;br /&gt;$ createdb -O zodbuser zodb&lt;/pre&gt;&lt;p&gt;編輯 /etc/postgresql/8.4/main/pg_hba.conf 存取控制規則，範例如下：&lt;pre&gt;local  zodb  zodbuser  md5&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;接著可以修改 develop.cfg 的 eggs 設定區段：&lt;pre&gt;eggs +=&lt;br /&gt;    psycopg2&lt;br /&gt;    RelStorage&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;修改 base.cfg 的 instance 設定區段，其中 blob-dir 參數指定一個目錄名稱，就是用來儲存 ZODB 的 BLOB 檔案：&lt;pre&gt;rel-storage =&lt;br /&gt;    type postgresql&lt;br /&gt;    dbname zodb&lt;br /&gt;    host localhost&lt;br /&gt;    user zodbuser&lt;br /&gt;    password mypass&lt;br /&gt;    blob-dir var/blobs&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;執行 buildout 生效並啟動系統後，可以查看到資料庫的表單內容：&lt;pre&gt;public | blob_chunk        | table    | zodbuser&lt;br /&gt;public | commit_lock       | table    | zodbuser&lt;br /&gt;public | current_object    | table    | zodbuser&lt;br /&gt;public | object_ref        | table    | zodbuser&lt;br /&gt;public | object_refs_added | table    | zodbuser&lt;br /&gt;public | object_state      | table    | zodbuser&lt;br /&gt;public | pack_object       | table    | zodbuser&lt;br /&gt;public | pack_state        | table    | zodbuser&lt;br /&gt;public | pack_state_tid    | table    | zodbuser&lt;br /&gt;public | transaction       | table    | zodbuser&lt;br /&gt;public | zoid_seq          | sequence | zodbuser&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;object_state 表單用來記錄 Page、Folder 等的內容，它的欄位包括：&lt;pre&gt;zoid, tid, prev_tid, md5, state_size, state&lt;/pre&gt;其中 state 欄位記錄的就是內容項目的資料值。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-8566564759728439787?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/8566564759728439787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=8566564759728439787' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8566564759728439787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8566564759728439787'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/10/relstorage-installation.html' title='RelStorage Installation'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-5481009291093210738</id><published>2011-10-14T12:17:00.001+08:00</published><updated>2011-10-14T12:17:47.192+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>EEA Faceted Navigation Installation</title><content type='html'>&lt;p&gt;&lt;a href="http://marrtw.blogspot.com/2010/10/eea-faceted-navigation.html"&gt;EEA Faceted Navigation&lt;/a&gt; 已經正式&lt;a href="http://svn.eionet.europa.eu/projects/Zope/wiki/FacetedNavigation#Sourcecode"&gt;支援 Plone 4.x 版本&lt;/a&gt;，測試成功的安裝方式是，先到 zinstance/src 目錄下載程式碼：&lt;/p&gt;&lt;pre&gt;$ cd ~/Plone/zinstance/src&lt;br /&gt;$ svn co http://svn.plone.org/svn/collective/eea.facetednavigation/trunk eea.facetednavigation&lt;br /&gt;$ svn co http://svn.plone.org/svn/collective/eea.jquery/trunk eea.jquery&lt;br /&gt;$ svn co http://svn.plone.org/svn/collective/eea.faceted.vocabularies/trunk eea.faceted.vocabularies&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;編輯 develop.cfg 內容，下列是範例：&lt;/p&gt;&lt;pre&gt;[sources]&lt;br /&gt;    eea.facetednavigation = fs eea.facetednavigation&lt;br /&gt;    eea.jquery = fs eea.jquery&lt;br /&gt;    eea.faceted.vocabularies = fs eea.faceted.vocabularies&lt;br /&gt;&lt;br /&gt;[buildout]&lt;br /&gt;eggs +=&lt;br /&gt;    eea.facetednavigation&lt;br /&gt;&lt;br /&gt;zcml +=&lt;br /&gt;    eea.facetednavigation-meta&lt;br /&gt;    eea.facetednavigation-overrides&lt;br /&gt;    eea.facetednavigation&lt;/pre&gt;&lt;p&gt;執行 buildout 生效：&lt;/p&gt;&lt;pre&gt;$ cd ~/Plone/zinstance&lt;br /&gt;$ bin/buildout -c develop.cfg&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-DEUoatIYWXo/Tpe2QRXAdoI/AAAAAAAADGg/6uxU08E1I5Y/s1600/image.png" imageanchor="1" style=""&gt;&lt;img border="0" height="169" width="330" src="http://3.bp.blogspot.com/-DEUoatIYWXo/Tpe2QRXAdoI/AAAAAAAADGg/6uxU08E1I5Y/s400/image.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;啟用模組後，在目錄的 Action 下拉選單，可以看到 Enable faceted navigation 選項，生效後可以再到 Faceted settings 和 Faceted criteria 設定細項。&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-8dWD0ja3e2U/Tpe3QBqdIsI/AAAAAAAADGw/0wsHfg9TBFc/s1600/eea.png" imageanchor="1" style=""&gt;&lt;img border="0" height="325" width="400" src="http://3.bp.blogspot.com/-8dWD0ja3e2U/Tpe3QBqdIsI/AAAAAAAADGw/0wsHfg9TBFc/s400/eea.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-5481009291093210738?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/5481009291093210738/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=5481009291093210738' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5481009291093210738'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5481009291093210738'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/10/eea-faceted-navigation-installation.html' title='EEA Faceted Navigation Installation'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-DEUoatIYWXo/Tpe2QRXAdoI/AAAAAAAADGg/6uxU08E1I5Y/s72-c/image.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-4760556640006854363</id><published>2011-10-11T16:04:00.001+08:00</published><updated>2011-10-11T16:05:02.081+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>RichDocument SimpleAttachment Title</title><content type='html'>&lt;p&gt;&lt;a href="http://pypi.python.org/pypi/Products.RichDocument"&gt;RichDocument&lt;/a&gt; 是個 content type 模組，它跟 Page 功能很像，但編輯介面提供特定欄位來上傳圖檔和檔案，這些特定欄位又搭配 &lt;a href="http://pypi.python.org/pypi/Products.SimpleAttachment"&gt;SimpleAttachment&lt;/a&gt; 模組，讓上傳檔案的工作變得更容易擴充維護，SimpleAttachment 也是 &lt;a href="http://marrtw.blogspot.com/2010/08/ploneboard-21-with-plone-335.html"&gt;Ploneboard 的相依模組&lt;/a&gt;。&lt;/p&gt;&lt;p&gt;以前 Plone 3.x 時代，就試用過 RichDocument 和 SimpleAttachment，當時覺得成熟度不足，現在新版支援 Plone 4.x 的 blob 功能，再次試用，仍然遇到無法處理附檔中文標題的問題。&lt;/p&gt;&lt;p&gt;不過，它的設計概念和實作方式，是學習開發技巧時的參考，下列是幾個程式碼片段和錯誤訊息的記錄：&lt;/p&gt;&lt;pre&gt;BooleanField('displayImages',&lt;br /&gt;    default=False,&lt;br /&gt;    languageIndependent=0,&lt;br /&gt;    widget=ImagesManagerWidget(&lt;br /&gt;        label="Display images download box"&lt;br /&gt;    ),&lt;br /&gt;),&lt;br /&gt;&lt;br /&gt;BooleanField('displayAttachments',&lt;br /&gt;    default=True,&lt;br /&gt;    languageIndependent=0,&lt;br /&gt;    widget=AttachmentsManagerWidget(&lt;br /&gt;        label="Display attachments download box"&lt;br /&gt;    ),&lt;br /&gt;),&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;tal:main-macro metal:define-macro="main"&lt;br /&gt; tal:define="text context/getText;&lt;br /&gt;             images python:context.getFolderContents(&lt;br /&gt;                        contentFilter = {'portal_type' : ['ImageAttachment']});&lt;br /&gt;             firstImage python:images and images[0] or None;&lt;br /&gt;             is_editable context/@@plone_context_state/is_editable;"&amp;gt;&lt;/pre&gt;&lt;pre&gt;&amp;lt;div metal:use-macro="python:context.widget('displayImages')"/&amp;gt;&lt;br /&gt;&amp;lt;div metal:use-macro="python:context.widget('displayAttachments')"/&amp;gt;&lt;/pre&gt;&lt;pre&gt;ERROR Zope.SiteErrorLog 1317974487.770.594350019308&lt;br /&gt; http://localhost:8080/mysite/Members/marr/my-page/richdocument_view_float&lt;br /&gt;Traceback (innermost last):&lt;br /&gt;  Module ZPublisher.Publish, line 126, in publish&lt;br /&gt;  Module ZPublisher.mapply, line 77, in mapply&lt;br /&gt;  Module ZPublisher.Publish, line 46, in call_object&lt;br /&gt;  Module Shared.DC.Scripts.Bindings, line 322, in __call__&lt;br /&gt;  Module Shared.DC.Scripts.Bindings, line 359, in _bindAndExec&lt;br /&gt;  Module Products.CMFCore.FSPageTemplate, line 240, in _exec&lt;br /&gt;  Module Products.CMFCore.FSPageTemplate, line 180, in pt_render&lt;br /&gt;  Module Products.PageTemplates.PageTemplate, line 79, in pt_render&lt;br /&gt;  Module zope.pagetemplate.pagetemplate, line 113, in pt_render&lt;br /&gt;  Module zope.tal.talinterpreter, line 271, in __call__&lt;br /&gt;  Module zope.tal.talinterpreter, line 343, in interpret&lt;br /&gt;  Module zope.tal.talinterpreter, line 888, in do_useMacro&lt;br /&gt;...&lt;br /&gt;  Module zope.tal.talinterpreter, line 343, in interpret&lt;br /&gt;  Module zope.tal.talinterpreter, line 531, in do_optTag_tal&lt;br /&gt;  Module zope.tal.talinterpreter, line 513, in no_tag&lt;br /&gt;  Module zope.tal.talinterpreter, line 343, in interpret&lt;br /&gt;  Module zope.tal.talinterpreter, line 742, in do_insertStructure_tal&lt;br /&gt;  Module Products.PageTemplates.Expressions, line 219, in evaluateStructure&lt;br /&gt;  Module Products.PageTemplates.Expressions, line 264, in _handleText&lt;br /&gt;UnicodeDecodeError: 'ascii' codec can't decode byte 0xe8 in position 94:&lt;br /&gt; ordinal not in range(128)&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-4760556640006854363?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/4760556640006854363/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=4760556640006854363' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4760556640006854363'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4760556640006854363'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/10/richdocument-simpleattachment-title.html' title='RichDocument SimpleAttachment Title'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-5211708615750842170</id><published>2011-10-05T10:24:00.001+08:00</published><updated>2011-10-05T10:24:27.253+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Link Integrity Checks</title><content type='html'>&lt;p&gt;Plone Site Setup 裡的 Editing 設定介面，勾選 Enable link integrity checks 後，可以啟動連結檢查功能，也就是說，某個項目被刪除或搬移時，如果發現有其他項目連結到它，系統會提醒無法刪除或搬移，並在 var/log/instance.log 裡，記錄 TypeError: ('Could not adapt', None, &amp;lt;InterfaceCalss plone.uuid.interfaces.IUUID&amp;gt;) 的錯誤訊息。只要停用連結檢查功能，完成動作後再恢復即可。&lt;/p&gt;&lt;p&gt;&lt;a href="http://plone.org/documentation/manual/plone-community-developer-documentation/content/uid"&gt;UID (Unique identifier)&lt;/a&gt; 是系統唯一的識別碼，即使內容項目被改名或搬移，它的 UID 仍然維持一致。像 Archetypes、ReferenceField、Kupu 都有用到 UID 服務，不過，Dexterity 使用 integer id 來管理關連，並未使用 UID。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-5211708615750842170?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/5211708615750842170/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=5211708615750842170' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5211708615750842170'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5211708615750842170'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/10/link-integrity-checks.html' title='Link Integrity Checks'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-8077199353077937880</id><published>2011-10-04T00:34:00.003+08:00</published><updated>2011-10-04T00:34:58.504+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>PIL vs Pillow</title><content type='html'>&lt;p&gt;安裝 Plone 4.x 的時候，通常會看到 &lt;a href="http://pypi.python.org/pypi/Pillow"&gt;Pillow&lt;/a&gt; 的身影，它是 &lt;a href="http://www.pythonware.com/products/pil/"&gt;PIL (Python Image Library)&lt;/a&gt; 的分支，因為 Plone 社群需要一份&lt;a href="http://dev.plone.org/plone/ticket/11472"&gt;相容 setuptools 的版本&lt;/a&gt;，於是開始維護這個分支，考量點在於簡化安裝流程。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-8077199353077937880?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/8077199353077937880/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=8077199353077937880' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8077199353077937880'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8077199353077937880'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/10/pil-vs-pillow.html' title='PIL vs Pillow'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7797409569993900712</id><published>2011-09-30T11:42:00.001+08:00</published><updated>2011-09-30T11:42:41.800+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Page Template Tip on Creators</title><content type='html'>&lt;p&gt;Plone Archetype content type 可以存取 Dublin Core Metadata 內容，例如作者、著作權、發佈日期等資訊，從 Products/Archetypes/ExtensibleMetadata.py 可以看到作者欄位識別碼是 LinesField: creators，還有 Creator 和 listCreators 兩個函式，想要存取作者資訊的話，分成 Creator 和 Creators 兩個方式：例如 &amp;lt;tal:content="context/Creator"&amp;gt; 會顯示第一筆資料，&amp;lt;tal:content="context/Creator"&amp;gt; 則會以多筆資料方式顯示。常見的範例是：&lt;/p&gt;&lt;pre&gt;Authos:&lt;br /&gt;&amp;lt;metal:field use-macro="python:context.widget('creators', mode='view')"&amp;gt;&lt;br /&gt;John Smith, et al.&amp;lt;/metal:field&amp;gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7797409569993900712?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7797409569993900712/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7797409569993900712' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7797409569993900712'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7797409569993900712'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/09/page-template-tip-on-creators.html' title='Page Template Tip on Creators'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-9098787310480443232</id><published>2011-09-29T21:04:00.000+08:00</published><updated>2011-09-29T21:04:59.711+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Plone Theme Editor with Diazo</title><content type='html'>&lt;p&gt;Plone &lt;a href="http://marrtw.blogspot.com/2010/04/plone3-theme.html"&gt;佈景主題&lt;/a&gt;的開發方式，自從 &lt;a href=http://marrtw.blogspot.com/2009/07/deliverance-installation.html&gt;Deliverance&lt;/a&gt; 和 &lt;a href=http://marrtw.blogspot.com/2011/04/diazo-theming-tool.html&gt;Diazo&lt;/a&gt; 問世之後，邁入新的里程碑，加上 Plone 4.1 開始搭配 &lt;a href="http://pypi.python.org/pypi/plone.app.theming"&gt;plone.app.theming&lt;/a&gt; 模組，建立 Diazo 工作環境的門檻大幅降低。&lt;/p&gt;&lt;p&gt;覺得編輯規則檔 (rules.xml) 太麻煩嗎? 現在有個視覺編輯工具&lt;a href="http://plone.293351.n2.nabble.com/Request-for-help-plone-app-theming-template-theme-tp6843083p6843083.html"&gt;正在開發&lt;/a&gt;，預計在 Plone 4.3 之後納入，這段影片讓你先睹一快: &lt;a href="http://www.screenr.com/b1Rs"&gt;http://www.screenr.com/b1Rs&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-9098787310480443232?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/9098787310480443232/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=9098787310480443232' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/9098787310480443232'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/9098787310480443232'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/09/plone-theme-editor-with-diazo.html' title='Plone Theme Editor with Diazo'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-3168414028842304473</id><published>2011-09-27T11:36:00.001+08:00</published><updated>2011-09-27T11:36:46.134+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>OFS.Uninstalled Could not import class</title><content type='html'>&lt;p&gt;嘗試移除 Plone 模組時，經常會遇到這個警告訊息，這代表模組並未完全移除乾淨，至少我遇過 Add-on Configuration 還留有設定項目，或是 ZMI 根目錄裡看得到 broken object 的情況。&lt;/p&gt;&lt;p&gt;broken object 應該可以直接刪除，Add-on Configuration 的項目要到 ZMI portal_controlpanel 裡刪除。這裡有個 DropDownMenu 的範例畫面。&lt;/p&gt;&lt;a href="https://picasaweb.google.com/lh/photo/X-fPqFWBzqAb4YHuwAy7OQ?feat=embedwebsite"&gt;&lt;img src="https://lh4.googleusercontent.com/-0ircsipeq-M/ToFEa4HD4bI/AAAAAAAADFg/zPpspswi4dU/s800/portal_controlpanel.png" height="374" width="517" /&gt;&lt;/a&gt;&lt;p&gt;如果症狀比上述情況嚴重，就要參考 &lt;a href="http://blog.fourdigits.nl/removing-a-persistent-local-utility-part-ii"&gt;Removing a persistent local utility&lt;/a&gt; 的除錯步驟。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-3168414028842304473?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/3168414028842304473/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=3168414028842304473' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3168414028842304473'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3168414028842304473'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/09/ofsuninstalled-could-not-import-class.html' title='OFS.Uninstalled Could not import class'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh4.googleusercontent.com/-0ircsipeq-M/ToFEa4HD4bI/AAAAAAAADFg/zPpspswi4dU/s72-c/portal_controlpanel.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-3619567422458704672</id><published>2011-09-23T17:33:00.000+08:00</published><updated>2011-09-23T17:33:07.486+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Moving collective.geo.kml Viewlet</title><content type='html'>&lt;p&gt;&lt;a href="http://plone.org/products/collective.geo"&gt;collective.geo.*&lt;/a&gt; 是一組提供 GIS 服務的 Plone 模組，它的地圖顯示區塊，預設是在 description 欄位下方，也就是在 &amp;lt;div id="viewlet-above-content-body"&amp;gt; 的下方，想要把它移到 &amp;lt;div id="viewlet-below-content"&amp;gt; 旁邊。首先，用 firebug 得知顯示區塊被包在 &amp;lt;div id="kml-content-viewlet"&amp;gt; 裡，那麼它是由哪個檔案提供的呢?&lt;/p&gt;&lt;pre&gt;$ grep -r kml-content-viewlet buildout-cache/eggs&lt;/pre&gt;&lt;p&gt;collective/geo/kml/browser/kmlcontentviewlet.pt 檔案有我們要找的 div，照慣例，先到 collective/geo/kml/browser/configure.zcml 找線索：&lt;/p&gt;&lt;pre&gt;  &amp;lt;browser:viewlet&lt;br /&gt;      name="collective.geo.kml.kmlcontentviewlet"&lt;br /&gt;      for="collective.geo.geographer.interfaces.IGeoreferenceable"&lt;br /&gt;      class=".viewlets.ContentViewlet"&lt;br /&gt;      manager="plone.app.layout.viewlets.interfaces.IAboveContentBody"&lt;br /&gt;      template="kmlcontentviewlet.pt"&lt;br /&gt;      layer="..interfaces.IGeoKmlLayer"&lt;br /&gt;      permission="zope2.View"&lt;br /&gt;  /&amp;gt;&lt;/pre&gt;&lt;p&gt;看來把 viewlet 的 manager 由 IAboveContentBody 改到 IBelowContent 就行。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-3619567422458704672?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/3619567422458704672/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=3619567422458704672' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3619567422458704672'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3619567422458704672'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/09/moving-collectivegeokml-viewlet.html' title='Moving collective.geo.kml Viewlet'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-660926188558278004</id><published>2011-09-22T22:23:00.001+08:00</published><updated>2011-09-23T16:33:00.259+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Transmogrifier in Action</title><content type='html'>&lt;p&gt;最近用 &lt;a href="http://marrtw.blogspot.com/2010/11/transmogrifier-importexport-made-easy.html"&gt;collective.transmogrifier&lt;/a&gt; 將資料匯入 Plone 4.0.7，同時支援 CSV 和 PostgreSQL 兩種來源格式。&lt;/p&gt;首先，用 paster 建立一個 my.importer 的專案，其中的 configure.zcml 內容範例如下：&lt;pre&gt;&amp;lt;configure&lt;br /&gt;    xmlns="http://namespaces.zope.org/zope"&lt;br /&gt;    xmlns:five="http://namespaces.zope.org/five"&lt;br /&gt;    xmlns:genericsetup="http://namespaces.zope.org/genericsetup"&lt;br /&gt;    xmlns:transmogrifier="http://namespaces.plone.org/transmogrifier"&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;includeDependencies package="." /&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;genericsetup:registerProfile&lt;br /&gt;      name="default"&lt;br /&gt;      title="my.importer"&lt;br /&gt;      directory="profiles/default"&lt;br /&gt;      description="To set context for transmogrifier profiles."&lt;br /&gt;      provides="Products.GenericSetup.interfaces.EXTENSION"&lt;br /&gt;      /&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;include package="collective.transmogrifier" /&amp;gt;&lt;br /&gt;  &amp;lt;include package="collective.transmogrifier" file="meta.zcml" /&amp;gt;&lt;br /&gt;  &amp;lt;transmogrifier:registerConfig&lt;br /&gt;    name="my.importer.importMyNews"&lt;br /&gt;    title="My Importer for MyNews"&lt;br /&gt;    description="Transmogrifier Pipeline config to import contents."&lt;br /&gt;    configuration="confs/import_mynews.cfg"&lt;br /&gt;    /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/configure&amp;gt;&lt;/pre&gt;在 confs 目錄裡，我們編輯 import_mynews.cfg 設定檔，範例內容如下：&lt;pre&gt;[transmogrifier]&lt;br /&gt;pipeline =&lt;br /&gt;#    csvsource&lt;br /&gt;    sqlsource&lt;br /&gt;    type-inserter&lt;br /&gt;    path-inserter&lt;br /&gt;    folders&lt;br /&gt;    constructor&lt;br /&gt;    schemaupdater&lt;br /&gt;    state-inserter&lt;br /&gt;    workflowupdater&lt;br /&gt;    reindexobject&lt;br /&gt;&lt;br /&gt;#[csvsource]&lt;br /&gt;#blueprint = collective.transmogrifier.sections.csvsource&lt;br /&gt;#filename = my.importer:data/mynews.csv&lt;br /&gt;&lt;br /&gt;[sqlsource]&lt;br /&gt;blueprint = transmogrify.sqlalchemy&lt;br /&gt;dsn = postgresql://username:secret@localhost:5432/dbname&lt;br /&gt;query = SELECT id, title, description FROM my_table&lt;br /&gt;&lt;br /&gt;[type-inserter]&lt;br /&gt;blueprint = collective.transmogrifier.sections.inserter&lt;br /&gt;key = string:_type&lt;br /&gt;value = string:News Item&lt;br /&gt;&lt;br /&gt;[path-inserter]&lt;br /&gt;blueprint = collective.transmogrifier.sections.inserter&lt;br /&gt;key = string:_path&lt;br /&gt;value = string:/myfolder/${item/id}&lt;br /&gt;&lt;br /&gt;[folders]&lt;br /&gt;blueprint = collective.transmogrifier.sections.folders&lt;br /&gt;&lt;br /&gt;[constructor]&lt;br /&gt;blueprint = collective.transmogrifier.sections.constructor&lt;br /&gt;&lt;br /&gt;[schemaupdater]&lt;br /&gt;blueprint = plone.app.transmogrifier.atschemaupdater&lt;br /&gt;&lt;br /&gt;[state-inserter]&lt;br /&gt;blueprint = collective.transmogrifier.sections.inserter&lt;br /&gt;key = string:_transitions&lt;br /&gt;value = string:publish&lt;br /&gt;&lt;br /&gt;[workflowupdater]&lt;br /&gt;blueprint = plone.app.transmogrifier.workflowupdater&lt;br /&gt;&lt;br /&gt;[reindexobject]&lt;br /&gt;blueprint = plone.app.transmogrifier.reindexobject&lt;/pre&gt;來源是 SQL 資料時，使用的是 [sqlsource]，來源是 CSV 資料時，就改用 csvsource，並在 data 目錄裡放好 mynews.csv 檔案，內容範例如下：&lt;pre&gt;id,title,description&lt;br /&gt;data1,My Data #1,Description of My Data #1&lt;/pre&gt;接著，編輯 my/importer/profiles/default/transmogrifier.txt 檔案，內容如下：&lt;pre&gt;# this file contains transmogrifier profile names to run&lt;br /&gt;# on GenericSetup transmogirifier's step&lt;br /&gt;my.importer.importMyNews&lt;/pre&gt;以上就是 my.importer 專案的主要設定內容。最後，編輯 buildout.cfg 的內容如下：&lt;pre&gt;eggs =&lt;br /&gt;     SQLAlchemy == 0.6.5&lt;br /&gt;    psycopg2&lt;br /&gt;    collective.transmogrifier&lt;br /&gt;    plone.app.transmogrifier&lt;br /&gt;    transmogrify.sqlalchemy&lt;br /&gt;   my.importer&lt;br /&gt;&lt;br /&gt;zcml =&lt;br /&gt;    collective.transmogrifier-meta&lt;br /&gt;    collective.transmogrifier&lt;br /&gt;    plone.app.transmogrifier&lt;br /&gt;    transmogrify.sqlalchemy&lt;/pre&gt;上述 SQLAlchemy == 0.6.5 語法，代表要指定 (pin) 安裝 0.6.5 版本的 SQLAlchemy，不指定的話，可能會安裝太新的版本，未必合用，我的經驗是 SQLAlchemy 0.7.2 會遇到 ImportError: No module named exceptions 錯誤訊息。&lt;p&gt;一切搞定後，要讓上述設定值生效的話，可以直接到 ZMI 的 portal_setup，在 Import 頁籤裡，從 Select Profile or Snapshot 找尋 my.importer 選項，再找到 Run transmogrifier pipeline (第41項) 並勾選它，最後點選 Import selected steps 按鈕。&lt;/p&gt;&lt;a href="https://picasaweb.google.com/lh/photo/LekMhLaSS3sJ0qOvIucABw?feat=embedwebsite"&gt;&lt;img src="https://lh6.googleusercontent.com/-D5whHvagyg4/TntB4BmeQDI/AAAAAAAADD4/Ka86oU9FhVA/s640/portal_setup-transmo.png" height="148" width="640" /&gt;&lt;/a&gt;&lt;p&gt;額外一提，PostgreSQL 裡的欄位資料如果更新為空值，執行 transmogrifier 後，也會將 Plone 表單欄位清空。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-660926188558278004?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/660926188558278004/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=660926188558278004' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/660926188558278004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/660926188558278004'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/09/transmogrifier-in-action.html' title='Transmogrifier in Action'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh6.googleusercontent.com/-D5whHvagyg4/TntB4BmeQDI/AAAAAAAADD4/Ka86oU9FhVA/s72-c/portal_setup-transmo.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-101503743655941270</id><published>2011-09-21T23:53:00.000+08:00</published><updated>2011-09-21T23:53:55.392+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>argument 2 to map() must support iteration</title><content type='html'>使用 &lt;a href="http://marrtw.blogspot.com/2010/11/transmogrifier-importexport-made-easy.html"&gt;collective.transmogrifier&lt;/a&gt; 和 transmogrify.sqlalchemy 讀取 PostgreSQL 內容匯入 Plone 表單時，遇到 TypeError: argument 2 to map() must support iteration 錯誤訊息，原因是表單所需要的 id 是 string 型別，來自 SQL 的 gid 內容則是 number 型別，想法子傳支援 iteration 的型別就行了。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-101503743655941270?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/101503743655941270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=101503743655941270' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/101503743655941270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/101503743655941270'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/09/argument-2-to-map-must-support.html' title='argument 2 to map() must support iteration'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-5162311273431939254</id><published>2011-09-21T01:49:00.000+08:00</published><updated>2011-09-21T01:49:10.062+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>AttributeError: getLocallyAllowedTypes</title><content type='html'>import 一個 folder zexp 到 Plone 4.1 根目錄後，遇到 AttributeError: getLocallyAllowedTypes 錯誤訊息，查到 &lt;a href="http://dev.plone.org/plone/ticket/11950"&gt;#11950&lt;/a&gt; 討論這個問題，並附有 patch 檔案，預計在 Plone 4.1.1 會修掉這問題。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-5162311273431939254?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/5162311273431939254/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=5162311273431939254' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5162311273431939254'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5162311273431939254'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/09/attributeerror-getlocallyallowedtypes.html' title='AttributeError: getLocallyAllowedTypes'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-6646083379360796144</id><published>2011-09-19T22:06:00.002+08:00</published><updated>2011-09-19T22:06:40.838+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Document Byline</title><content type='html'>有人並不喜歡看到網頁的「作者」「最近修改」資訊，這些資訊被 Plone 稱為 &lt;a href="http://plone.org/documentation/manual/theme-reference/elements/visibleelements/plone.belowcontenttitle.documentbyline"&gt;Byline&lt;/a&gt;，想要不顯示 Byline 資訊，除了在 &lt;a href="http://marrtw.blogspot.com/2011/04/custom-folder-listing-tips.html"&gt;portal_skins&lt;/a&gt; 和 &lt;a href="http://marrtw.blogspot.com/2009/11/custom-plone-home-page.html"&gt;CSS&lt;/a&gt; 可以修改外，在 ZMI 的 portal_view_customizations 裡，也可以修改 plone.belowcontenttitle.documentbyline 來達到效果：&lt;a href="https://picasaweb.google.com/lh/photo/TAheE_N09zvHoYa9qDOlHQ?feat=embedwebsite"&gt;&lt;img src="https://lh4.googleusercontent.com/-ako0hXBDesI/TndKrtlaJFI/AAAAAAAADDk/Mk_QZJuxzzI/s800/document-byline.png" height="266" width="639" /&gt;&lt;/a&gt;原則上就是從 &amp;lt;tal:creator /&amp;gt; 開始，拿掉這些 page template 內容。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-6646083379360796144?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/6646083379360796144/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=6646083379360796144' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6646083379360796144'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6646083379360796144'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/09/document-byline.html' title='Document Byline'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh4.googleusercontent.com/-ako0hXBDesI/TndKrtlaJFI/AAAAAAAADDk/Mk_QZJuxzzI/s72-c/document-byline.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-6291968000447093815</id><published>2011-09-15T00:03:00.000+08:00</published><updated>2011-09-15T00:03:21.926+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>pkg_resources.DistributionNotFound</title><content type='html'>&lt;p&gt;執行 paster addcontent 時，遇到 pkg_resources.DistributionNotFound 訊息，處理方式是在搜尋哪個 .cfg 檔案包含 [zopeske] 設定區段，在裡面加上 ${instance:eggs} 設定值，例如：&lt;/p&gt;&lt;pre&gt;[zopeskel]&lt;br /&gt;recipe = zc.recipe.egg&lt;br /&gt;eggs =&lt;br /&gt;   ...&lt;br /&gt;   ${instance:eggs}&lt;/pre&gt;&lt;p&gt;重新執行 buildout 後，就能正常使用。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-6291968000447093815?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/6291968000447093815/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=6291968000447093815' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6291968000447093815'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6291968000447093815'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/09/pkgresourcesdistributionnotfound.html' title='pkg_resources.DistributionNotFound'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7776806614338384010</id><published>2011-09-08T17:20:00.000+08:00</published><updated>2011-09-08T17:20:08.259+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Macro Expansion Failed</title><content type='html'>在 ZMI portal_skins 裡，想要客製 newsitem_view 時，看到一個訊息：&lt;pre&gt;Macro expansion failed&lt;br /&gt;&amp;lt;type 'exceptions.AttributeError'&amp;gt;: widget&lt;/pre&gt;這只是個煩人的訊息，直接修改內容並存檔，還是能夠運作。如果去處理它的話，會造成 page template 變得太複雜，在 &lt;a href=http://dev.plone.org/plone/ticket/7173&gt;Ticket #7173&lt;/a&gt; 被歸為暫不處理的項目。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7776806614338384010?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7776806614338384010/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7776806614338384010' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7776806614338384010'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7776806614338384010'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/09/macro-expansion-failed.html' title='Macro Expansion Failed'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-3053922064117003630</id><published>2011-09-08T11:57:00.000+08:00</published><updated>2011-09-27T12:12:59.214+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>cmf.ManagePortal ComponentLookupError</title><content type='html'>&lt;p&gt;在 Plone 4.1 遇到下列訊息：&lt;/p&gt;&lt;pre&gt;File "/home/marr/Plone/buildout-cache/eggs/&lt;br /&gt;AccessControl-2.13.4-py2.6-linux-i686.egg/AccessControl/security.py",&lt;br /&gt; line 165, in protectClass&lt;br /&gt;  permission = getUtility(IPermission, name=permission_id)&lt;br /&gt;File "/home/marr/Plone/buildout-cache/eggs/&lt;br /&gt;zope.component-3.9.5-py2.6.egg/zope/component/_api.py",&lt;br /&gt; line 169, in getUtility&lt;br /&gt;  raise ComponentLookupError(interface, name)&lt;br /&gt;zope.configuration.config.ConfigurationExecutionError:&lt;br /&gt; &amp;lt;class 'zope.component.interfaces.componentlookuperror'=""&amp;gt;:&lt;br /&gt; (&amp;lt;interfaceclass zope.security.interfaces.ipermission=""&amp;gt;, 'cmf.ManagePortal')&lt;br /&gt;in:&lt;br /&gt;File "/home/marr/Plone/buildout-cache/eggs/&lt;br /&gt;p4a.subtyper-1.2.0-py2.6.egg/p4a/subtyper/configure.zcml",&lt;br /&gt; line 49.2-55.8&lt;br /&gt;  &amp;lt;browser:page&lt;br /&gt;      name="subtyper"&lt;br /&gt;      for="*"&lt;br /&gt;      permission="cmf.ManagePortal"&lt;br /&gt;      class=".browser.SubtyperView"&lt;br /&gt;      allowed_interface=".browser.ISubtyperView"&lt;br /&gt;      /&amp;gt;&lt;/pre&gt;&lt;p&gt;臨時的解法是修改 p4a/subtyper/configure.zcml 內容，加上：&lt;/p&gt;&lt;pre&gt;&amp;lt;include package="Products.CMFCore" &lt;br /&gt;file="permissions.zcml" /&amp;gt;&lt;/pre&gt;&lt;p&gt;以上，應該是 add-on 昇級的議題。另外，類似的訊息還有 ComponentLookupError: (&amp;lt;InterfaceClass plone.keyring.interfaces.IKeyManager&amp;gt;, '') 的錯誤，在 ZMI portal_setup 重新載入所有的 plone.keyring KeyManager Registration 步驟就行。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-3053922064117003630?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/3053922064117003630/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=3053922064117003630' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3053922064117003630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3053922064117003630'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/09/cmfmanageportal-componentlookuperror.html' title='cmf.ManagePortal ComponentLookupError'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-2788799200882192473</id><published>2011-09-02T14:31:00.000+08:00</published><updated>2011-09-02T14:31:37.199+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>PloneFormGen + Dexterity</title><content type='html'>在 &lt;a href="http://www.youtube.com/watch?v=7X01-ujcVEw"&gt;TTW is Back on Plone 影片&lt;/a&gt;裡，示範三分鐘線上建立報價單的方法，包括工作流程設定，利用的工具有 &lt;a href="http://plone.org/products/ploneformgen"&gt;Products.PloneFormGen&lt;/a&gt;、&lt;a href="http://plone.org/products/dexterity"&gt;plone.app.dexterity&lt;/a&gt;、&lt;a href="http://dev.plone.org/plone/browser/sandbox/experimental.dexteritytypeviews/trunk"&gt;experimental.dexteritytypeviews&lt;/a&gt;、&lt;a href="http://pypi.python.org/pypi/collective.pfg.dexterity"&gt;collective.pfg.dexterity&lt;/a&gt;、&lt;a href="http://plone.org/products/uwosh.northstar"&gt;uwosh.northstar&lt;/a&gt;。&lt;br /&gt;看起來 Dexterity 相當成熟了，不過&lt;a href="http://plone.293351.n2.nabble.com/atcontenttypes-replacement-with-dexterity-tp6751909p6751909.html"&gt;預設的 ATContentTypes 還沒被取代&lt;/a&gt;。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-2788799200882192473?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/2788799200882192473/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=2788799200882192473' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2788799200882192473'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2788799200882192473'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/09/ploneformgen-dexterity.html' title='PloneFormGen + Dexterity'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7643787031894682640</id><published>2011-08-18T10:06:00.003+08:00</published><updated>2011-08-18T10:26:06.037+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>AddRemoveWidget and ComboBoxWidget</title><content type='html'>&lt;a href="http://pypi.python.org/pypi/Products.AddRemoveWidget"&gt;AddRemoveWidget&lt;/a&gt; permits to add self-entered data, which makes it a good replacement for KeywordWidget. Example code looks like:&lt;pre&gt;from Products.AddRemoveWidget.AddRemoveWidget import AddRemoveWidget&lt;br /&gt;...&lt;br /&gt;  atapi.LinesField(&lt;br /&gt;      'watershed',&lt;br /&gt;      storage=atapi.AnnotationStorage(),&lt;br /&gt;      widget=AddRemoveWidget(&lt;br /&gt;          label=_(u"Watershed"),&lt;br /&gt;          description=_(u"Enter one tag per line, multiple words allowed."),&lt;br /&gt;      ),&lt;br /&gt;      vocabulary_factory='watershed',&lt;br /&gt;  ),&lt;/pre&gt;&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/_-RxfM_5wycWfPXQJEI9cg?feat=embedwebsite"&gt;&lt;img src="https://lh5.googleusercontent.com/-U4rAwl-WBsE/Tkx16-e5reI/AAAAAAAAC_o/RAauAsz3agI/s800/01.png" height="223" width="499" /&gt;&lt;/a&gt;&lt;br /&gt;AddRemoveWidget works with multiple-value fileds, if you need just single-value fields, try ComboBoxWidget instead. Example code looks like:&lt;pre&gt;from Products.AddRemoveWidget.ComboBoxWidget import ComboBoxWidget&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;  atapi.StringField(&lt;br /&gt;      'vote',&lt;br /&gt;      storage=atapi.AnnotationStorage(),&lt;br /&gt;      widget=ComboBoxWidget(&lt;br /&gt;          label=_(u"Best Band Ever"),&lt;br /&gt;          description=_(u"Single Choice."),&lt;br /&gt;      ),&lt;br /&gt;      vocabulary=['The Beatles', 'The Smiths', 'Led Zeppelin', 'Joy Division'],&lt;br /&gt;  ),&lt;/pre&gt;&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/LIfeVUDAzk6yMJIhXuDhXg?feat=embedwebsite"&gt;&lt;img src="https://lh3.googleusercontent.com/-BZKJw_kW4T8/Tkx19p7c4oI/AAAAAAAAC_o/oJOAHMeBz8o/s800/02.png" height="182" width="317" /&gt;&lt;/a&gt;&lt;br /&gt;Look into the source code for more config options.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7643787031894682640?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7643787031894682640/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7643787031894682640' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7643787031894682640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7643787031894682640'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/08/addremovewidget-and-comboboxwidget.html' title='AddRemoveWidget and ComboBoxWidget'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh5.googleusercontent.com/-U4rAwl-WBsE/Tkx16-e5reI/AAAAAAAAC_o/RAauAsz3agI/s72-c/01.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-4756234452616018057</id><published>2011-08-17T14:59:00.001+08:00</published><updated>2011-08-17T15:02:35.175+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Editing Plone Short Name</title><content type='html'>Short Name 可以譯成「識別碼」，它是構成 URL 的一部份，這個欄位在編輯時，預設是不顯示。如果網站包含整批具備流水編號的內容，例如想用 ISBN 當作識別碼，這類場合就適合在編輯時顯示識別碼欄位。&lt;br /&gt;在 Plone Setup 的 Editing 設定項目，可以指定是否顯示 Short Name 欄位。&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/9BEarJJN244KsZTPbbLddg?feat=embedwebsite"&gt;&lt;img src="https://lh5.googleusercontent.com/-y1Mqa6JUyNc/TktYdx2C5kI/AAAAAAAAC_I/2lS-dnMbHrY/s800/1-editing-settings.png" height="620" width="546" /&gt;&lt;/a&gt;&lt;br /&gt;系統設定值勾選啟用後，還要確定使用者偏好設定，必須同步設定啟用才真的生效。&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/YQv2GVEfoffMGrjCVU8Wow?feat=embedwebsite"&gt;&lt;img src="https://lh4.googleusercontent.com/-SI8edpOIfBU/TktYd-DFGjI/AAAAAAAAC_I/tVjdcSSGYec/s640/2-personal-preferences.png" height="501" width="640" /&gt;&lt;/a&gt;&lt;br /&gt;生效後的範例如下圖：&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/OqFkXEPQ7GKuDdkdWXcZ7g?feat=embedwebsite"&gt;&lt;img src="https://lh3.googleusercontent.com/-2BbG-ZEjbrM/TktYd9AUKSI/AAAAAAAAC_I/ZXWQIBy2KtY/s800/3-editing.png" height="595" width="487" /&gt;&lt;/a&gt;&lt;br /&gt;以 Archetype 的表單為例，Short Name 在程式碼內部使用 id 這個欄位名稱，剛新增時的網址會使用 book.2011-08-17.8631510722 之類的字串，如果 Short Name 欄位留空未填，那麼預設會使用 &lt;a href="http://marrtw.blogspot.com/2010/11/title-to-id-behavior.html"&gt;title-to-id&lt;/a&gt; 行為方式來更改 id 欄位值。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-4756234452616018057?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/4756234452616018057/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=4756234452616018057' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4756234452616018057'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4756234452616018057'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/08/editing-plone-short-name.html' title='Editing Plone Short Name'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh5.googleusercontent.com/-y1Mqa6JUyNc/TktYdx2C5kI/AAAAAAAAC_I/2lS-dnMbHrY/s72-c/1-editing-settings.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-3645211536037451263</id><published>2011-08-09T22:27:00.002+08:00</published><updated>2011-08-09T23:14:43.472+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Diazo Installation</title><content type='html'>在 Plone 裡安裝 &lt;a href="http://marrtw.blogspot.com/2011/04/diazo-theming-tool.html"&gt;Diazo&lt;/a&gt; 最簡單的方法，是搭配安裝 &lt;a href="http://pypi.python.org/pypi/plone.app.theming"&gt;plone.app.theming&lt;/a&gt;，它需要 Plone 4.1 之後的環境，使用 4.1 版本的 Unified Installer 是最簡單的方法。&lt;br /&gt;&lt;pre&gt;$ diff -Nu develop.cfg.orig develop.cfg&lt;br /&gt;--- develop.cfg.orig    2011-08-09 22:21:25.495261571 +0800&lt;br /&gt;+++ develop.cfg 2011-08-09 22:23:31.063243116 +0800&lt;br /&gt;@@ -110,6 +110,7 @@&lt;br /&gt; # we're extending buildout.cfg&lt;br /&gt; extends =&lt;br /&gt;     buildout.cfg&lt;br /&gt;+    http://good-py.appspot.com/release/plone.app.theming/1.0b8&lt;br /&gt;&lt;br /&gt; extensions +=&lt;br /&gt;     mr.developer&lt;br /&gt;@@ -117,6 +118,7 @@&lt;br /&gt; eggs +=&lt;br /&gt;     Products.DocFinderTab&lt;br /&gt;     plone.reload&lt;br /&gt;+    plone.app.theming&lt;br /&gt;&lt;br /&gt; parts +=&lt;br /&gt;     test&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;$ bin/buildout -c develop.cfg&lt;br /&gt;Uninstalling zopepy.&lt;br /&gt;Uninstalling instance.&lt;br /&gt;Installing _mr.developer.&lt;br /&gt;Generated script '/home/marr/plone410/zinstance/bin/develop'.&lt;br /&gt;Installing instance.&lt;br /&gt;Getting distribution for 'plone.app.theming==1.0b8'.&lt;br /&gt;Got plone.app.theming 1.0b8.&lt;br /&gt;Getting distribution for 'repoze.xmliter==0.4'.&lt;br /&gt;Got repoze.xmliter 0.4.&lt;br /&gt;Getting distribution for 'plone.resource==1.0b5'.&lt;br /&gt;Got plone.resource 1.0b5.&lt;br /&gt;Getting distribution for 'plone.subrequest==1.6.1'.&lt;br /&gt;Got plone.subrequest 1.6.1.&lt;br /&gt;Getting distribution for 'diazo==1.0rc3'.&lt;br /&gt;Got diazo 1.0rc3.&lt;br /&gt;Getting distribution for 'experimental.cssselect==0.1'.&lt;br /&gt;Got experimental.cssselect 0.1.&lt;br /&gt;Generated script '/home/marr/plone410/zinstance/bin/instance'.&lt;br /&gt;Installing zopepy.&lt;br /&gt;Generated interpreter '/home/marr/plone410/zinstance/bin/zopepy'.&lt;br /&gt;Updating zopeskel.&lt;br /&gt;Installing omelette.&lt;br /&gt;Updating backup.&lt;br /&gt;Updating chown.&lt;br /&gt;chown: Running echo Dummy references to force this to execute after referenced parts&lt;br /&gt;echo /home/marr/plone410/zinstance/var/backups&lt;br /&gt;chmod 600 .installed.cfg&lt;br /&gt;find /home/marr/plone410/zinstance/var -type d -exec chmod 700 {} \;&lt;br /&gt;chmod 744 /home/marr/plone410/zinstance/bin/*&lt;br /&gt;Dummy references to force this to execute after referenced parts&lt;br /&gt;/home/marr/plone410/zinstance/var/backups&lt;br /&gt;Installing test.&lt;br /&gt;Generated script '/home/marr/plone410/zinstance/bin/test'.&lt;br /&gt;Updating repozo.&lt;br /&gt;Updating unifiedinstaller.&lt;br /&gt;*************** PICKED VERSIONS ****************&lt;br /&gt;[versions]&lt;br /&gt;&lt;br /&gt;*************** /PICKED VERSIONS ***************&lt;/pre&gt;&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/aD7Fx-auWr6Z1uDQ1wvpgA?feat=embedwebsite"&gt;&lt;img src="https://lh6.googleusercontent.com/-jWc6GKumIM0/TkFN_JhEDTI/AAAAAAAAC-Q/I6EzrrHlNSY/s800/01.png" height="246" width="455" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/RjO3nxA6ootTLztwljY0oA?feat=embedwebsite"&gt;&lt;img src="https://lh4.googleusercontent.com/-H5TO-tmIpBA/TkFOBb6tfDI/AAAAAAAAC-Q/e7ijuoBu-P0/s800/02.png" height="139" width="460" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/p6Zdw0BvJ-S3VDRtLbG3EQ?feat=embedwebsite"&gt;&lt;img src="https://lh4.googleusercontent.com/-NN_sSHUeBCk/TkFOD6PzyoI/AAAAAAAAC-Q/ugK1SEPx8f4/s800/03.png" height="449" width="449" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-3645211536037451263?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/3645211536037451263/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=3645211536037451263' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3645211536037451263'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3645211536037451263'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/08/diazo-installation.html' title='Diazo Installation'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh6.googleusercontent.com/-jWc6GKumIM0/TkFN_JhEDTI/AAAAAAAAC-Q/I6EzrrHlNSY/s72-c/01.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-6542351686758440259</id><published>2011-08-03T11:40:00.003+08:00</published><updated>2011-08-03T11:49:39.007+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Development Eggs</title><content type='html'>When using paster to create "development eggs", there will install Paste, PasteScript, PasteDeploy eggs in Plone src directory. These eggs are for development purpose, will enable paster options like local commands. However, along with my.package.egg-info, they are not into version control repository.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-6542351686758440259?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/6542351686758440259/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=6542351686758440259' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6542351686758440259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6542351686758440259'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/08/development-eggs.html' title='Development Eggs'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7107590487758683686</id><published>2011-08-02T23:39:00.002+08:00</published><updated>2011-08-02T23:44:52.009+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Vocabulary Import Tips</title><content type='html'>Run into this error:&lt;pre&gt;ImportError: No module named schema.vocabulary&lt;/pre&gt;&lt;br /&gt;Fix it by changing the imports in vocabulary.py, from:&lt;pre&gt;from zope.interface import implements&lt;br /&gt;from zope.app.schema.vocabulary import IVocabularyFactory&lt;br /&gt;from zope.schema.vocabulary import SimpleVocabulary&lt;/pre&gt;&lt;br /&gt;to:&lt;pre&gt;from zope.interface import implements&lt;br /&gt;from zope.schema.interfaces import IVocabularyFactory&lt;br /&gt;from zope.schema.vocabulary import SimpleVocabulary&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://pypi.python.org/pypi/zope.app.schema"&gt;zope.app.schema&lt;/a&gt; 3.5.0 removes deprecated vocabulary directive.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7107590487758683686?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7107590487758683686/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7107590487758683686' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7107590487758683686'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7107590487758683686'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/08/vocabulary-import-tips.html' title='Vocabulary Import Tips'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7736450220496420500</id><published>2011-08-01T17:09:00.003+08:00</published><updated>2011-08-01T18:40:17.790+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Transmogrifier Tips</title><content type='html'>&lt;h3&gt;SQLAlchemy: No module named exceptions&lt;/h3&gt;After running buildout, my Plone fails to start and show this message -- ImportError: No module named exceptions.&lt;br /&gt;Adding a pin for SQLAlchemy to version 0.6.8 fixes the issue. For details see &lt;a href=http://www.sqlalchemy.org/trac/wiki/07Migration#Thesqlalchemy.exceptionsaliasinsys.modulesisremoved&gt;SQLAlchemy changelog&lt;/a&gt;&lt;br /&gt;&lt;h3&gt;Named Transmogrifier Pipelines&lt;/h3&gt;In collective/transmogrifier/genericsetup.py, 'transmogrifier.txt' is assigned to be read. So place the file in profiles/default directory, pipeline identifiers are read one per line, for example 'mysite.migration.importFromCSV'. In ZMI portal_setup 'Import' tab, you can run the selected steps.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7736450220496420500?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7736450220496420500/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7736450220496420500' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7736450220496420500'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7736450220496420500'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/08/transmogrifier-tips.html' title='Transmogrifier Tips'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-8625998536548646357</id><published>2011-08-01T10:09:00.002+08:00</published><updated>2011-08-01T10:40:27.937+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>CSV Import with Transmogrifier</title><content type='html'>假設你的欄位資料，都放在 mydata.csv 檔案裡，想要批次匯入來建立 MyContentType 內容，下列是主要的步驟範例。&lt;br /&gt;&lt;br /&gt;以自製模組 mysite.migration 為例，它的 configure.zcml 內容範例如下：&lt;br /&gt;&lt;pre&gt;&amp;lt;configure&lt;br /&gt;    xmlns="http://namespaces.zope.org/zope"&lt;br /&gt;    xmlns:five="http://namespaces.zope.org/five"&lt;br /&gt;    xmlns:i18n="http://namespaces.zope.org/i18n"&lt;br /&gt;    xmlns:genericsetup="http://namespaces.zope.org/genericsetup"&lt;br /&gt;    xmlns:transmogrifier="http://namespaces.plone.org/transmogrifier"&lt;br /&gt;    i18n_domain="mysite.migration"&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;!-- Include zcml files of all required packages --&amp;gt;&lt;br /&gt;  &amp;lt;includeDependencies package="." /&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;genericsetup:registerProfile&lt;br /&gt;      name="default"&lt;br /&gt;      title="mysite.migration"&lt;br /&gt;      directory="profiles/default"&lt;br /&gt;      description="MySite transmogrifier profile."&lt;br /&gt;      provides="Products.GenericSetup.interfaces.EXTENSION"&lt;br /&gt;      /&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;!-- Register Transmogrifier Profile --&amp;gt;&lt;br /&gt;  &amp;lt;include package="collective.transmogrifier" /&amp;gt;&lt;br /&gt;  &amp;lt;include package="collective.transmogrifier" file="meta.zcml" /&amp;gt;&lt;br /&gt;  &amp;lt;transmogrifier:registerConfig&lt;br /&gt;&lt;br /&gt;    name="mysite.migration.importFromCSV"&lt;br /&gt;    title="Import Content Data From CSV"&lt;br /&gt;    description="Transmogrifier config to import contents from CSV"&lt;br /&gt;    configuration="confs/import_from_csv.cfg"&lt;br /&gt;    /&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;!-- -*- extra stuff goes here -*- --&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/configure&amp;gt;&lt;/pre&gt;&lt;br /&gt;在 confs/import_from_csv.cfg 內容範例如下：&lt;br /&gt;&lt;pre&gt;[transmogrifier]&lt;br /&gt;pipeline =&lt;br /&gt;    csvsource&lt;br /&gt;    type-inserter&lt;br /&gt;    path-inserter&lt;br /&gt;    folders&lt;br /&gt;    constructor&lt;br /&gt;    schemaupdater&lt;br /&gt;    state-inserter&lt;br /&gt;    workflowupdater&lt;br /&gt;    reindexobject&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;[csvsource]&lt;br /&gt;blueprint = collective.transmogrifier.sections.csvsource&lt;br /&gt;filename = crgis.migration:data/mydata.csv&lt;br /&gt;&lt;br /&gt;[type-inserter]&lt;br /&gt;blueprint = collective.transmogrifier.sections.inserter&lt;br /&gt;key = string:_type&lt;br /&gt;value = string:MyContentType&lt;br /&gt;&lt;br /&gt;[path-inserter]&lt;br /&gt;blueprint = collective.transmogrifier.sections.inserter&lt;br /&gt;key = string:_path&lt;br /&gt;value = string:/myfolder/${item/id}&lt;br /&gt;&lt;br /&gt;[folders]&lt;br /&gt;blueprint = collective.transmogrifier.sections.folders&lt;br /&gt;&lt;br /&gt;[constructor]&lt;br /&gt;blueprint = collective.transmogrifier.sections.constructor&lt;br /&gt;&lt;br /&gt;[schemaupdater]&lt;br /&gt;blueprint = plone.app.transmogrifier.atschemaupdater&lt;br /&gt;&lt;br /&gt;[state-inserter]&lt;br /&gt;blueprint = collective.transmogrifier.sections.inserter&lt;br /&gt;key = string:_transitions&lt;br /&gt;value = string:publish&lt;br /&gt;&lt;br /&gt;[workflowupdater]&lt;br /&gt;blueprint = plone.app.transmogrifier.workflowupdater&lt;br /&gt;&lt;br /&gt;[reindexobject]&lt;br /&gt;blueprint = plone.app.transmogrifier.reindexobject&lt;/pre&gt;&lt;br /&gt;profiles/default/transmogrifier.txt 的內容範例如下：&lt;br /&gt;&lt;pre&gt;# this file contains transmogrifier profile names to run&lt;br /&gt;# on GenericSetup transmogrifier's step&lt;br /&gt;crgis.migration.importFromCSV&lt;/pre&gt;&lt;br /&gt;以上模組檔案內容就緒後，在 buildout.cfg 加上：&lt;br /&gt;&lt;pre&gt;[buildout]&lt;br /&gt;...&lt;br /&gt;develop =&lt;br /&gt;    src/mysite.migration&lt;br /&gt;&lt;br /&gt;[instance]&lt;br /&gt;...&lt;br /&gt;eggs =&lt;br /&gt;    mysite.migration&lt;/pre&gt;&lt;br /&gt;執行 bin/buildout 之後，進入 ZMI 的 portal_setup 設定畫面，從 "Select Profile Or Snapshot" 下拉選單，點選 mysite.migration 項目，再點選 "Run transmogrifier pipeline" 項目，最後點選 "Import selected steps" 按鈕。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-8625998536548646357?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/8625998536548646357/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=8625998536548646357' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8625998536548646357'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8625998536548646357'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/08/csv-import-with-transmogrifier.html' title='CSV Import with Transmogrifier'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-1099248031099653026</id><published>2011-07-19T15:48:00.003+08:00</published><updated>2011-07-19T16:27:12.443+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Geek'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Bio by Pictures</title><content type='html'>第一台電腦是 PC XT 8088 的時代，當時是買 Acer 但找不到照片，只好用這張類似的畫面。全機黑色的 Acer XT 比這漂亮太多了。&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/CQjByIGjSQ5nLzr3d9yCTw?feat=embedwebsite"&gt;&lt;img src="https://lh4.googleusercontent.com/-CAehBv2qfcw/TiU007x_8hI/AAAAAAAAC40/QbDVZxsdTiA/s800/XT8088.jpg" height="357" width="400" /&gt;&lt;/a&gt;&lt;br /&gt;中學時代的電腦，主要是遊戲機的功能，學 BASIC 的目的，也是想寫出自己的遊戲。&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/uAzEHXMiKRFMY6JXSDCBUQ?feat=embedwebsite"&gt;&lt;img src="https://lh5.googleusercontent.com/-lk743Hvu-iA/TiU1mSIKYbI/AAAAAAAAC40/2lT5ICuMecU/s640/PCgames.png" height="480" width="640" /&gt;&lt;/a&gt;&lt;br /&gt;自己是搖滾迷，id nickname 是取自英國樂團吉他手的名字。&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/5geDCeBH03sqcJpamfFSnQ?feat=embedwebsite"&gt;&lt;img src="https://lh5.googleusercontent.com/-Gf8rsyRkzQ4/TiU1vXkyHBI/AAAAAAAAC40/Th3VHf5eC_4/s800/HappyGroup.jpg" height="239" width="300" /&gt;&lt;/a&gt;&lt;br /&gt;第一個網站，內容是介紹搖滾樂。&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/PcflrxwkwvolGV4mmn4mSQ?feat=embedwebsite"&gt;&lt;img src="https://lh6.googleusercontent.com/-GovpwBh2nNk/TiU10quKEZI/AAAAAAAAC40/Du0Y0LkKD-I/s800/tang-dynasty.jpg" height="325" width="342" /&gt;&lt;/a&gt;&lt;br /&gt;架站總是要學 Unix 知識，後來發現歷史故事也充滿趣味。&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/NHtmA32uDP5xJ5QKHRYZ9Q?feat=embedwebsite"&gt;&lt;img src="https://lh5.googleusercontent.com/-zb0ErisW0gg/TiU2NSou3bI/AAAAAAAAC40/Pt9aXUcBe2o/s640/hacker-story.png" height="480" width="640" /&gt;&lt;/a&gt;&lt;br /&gt;現在是 Python 愛好者一枚。&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/PlBB-ygExZxLLOY-e_MQFg?feat=embedwebsite"&gt;&lt;img src="https://lh5.googleusercontent.com/-SHuYsv5OZLE/TiU3MdrqWeI/AAAAAAAAC40/-m5tm_JGRfY/s640/python-plone.png" height="480" width="640" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-1099248031099653026?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/1099248031099653026/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=1099248031099653026' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1099248031099653026'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1099248031099653026'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/07/bio-by-pictures.html' title='Bio by Pictures'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh4.googleusercontent.com/-CAehBv2qfcw/TiU007x_8hI/AAAAAAAAC40/QbDVZxsdTiA/s72-c/XT8088.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-6607223869231804977</id><published>2011-07-03T08:33:00.004+08:00</published><updated>2011-07-03T12:16:37.132+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>New User Interface Preview</title><content type='html'>Plone &lt;a href=https://simple-note.appspot.com/publish/QPtnLr&gt;操作介面的發展規劃&lt;/a&gt;，經過 &lt;a href=http://www.netsight.co.uk/blog/plone-ui-sprint-2011-writeup&gt;Plone UI Sprint&lt;/a&gt; 的努力工作後，終於有了 &lt;a href=http://pypi.python.org/pypi/plone.app.cmsui&gt;plone.app.cmsui&lt;/a&gt; 測試版實作，搭配 Plone 4.1 環境，讓我們可以體驗新介面的預覽版。&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/aH44IuKRCOY24poNT1GMhw?feat=embedwebsite"&gt;&lt;img src="https://lh3.googleusercontent.com/-jnpfZ9f-6KI/Tg_WHllBbTI/AAAAAAAAC1k/avTn8KV-JkQ/s640/cmsui-frontpage.png" height="331" width="640" /&gt;&lt;/a&gt;&lt;br /&gt;另一個改版重點，是導入&lt;a href="http://code.google.com/p/plone-deco/"&gt;新的 page layout 管理機制&lt;/a&gt;，底層是一個稱為 &lt;a href="http://code.google.com/p/plone-deco/wiki/blocks"&gt;Block&lt;/a&gt; 的引擎，它將網頁分成結構式和動態的顯示元件，經過組合再呈現。短期內，Plone 則推出 &lt;a href=http://dev.plone.org/plone/browser/plone.app.page/trunk&gt;plone.app.page&lt;/a&gt; 讓 Folderish Page 取代 Folder，許多&lt;a href=http://plone.293351.n2.nabble.com/Page-composition-or-home-page-in-Plone-tp6540956p6541994.html&gt;測試正在演進中&lt;/a&gt;。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-6607223869231804977?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/6607223869231804977/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=6607223869231804977' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6607223869231804977'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6607223869231804977'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/07/new-user-interface-preview.html' title='New User Interface Preview'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh3.googleusercontent.com/-jnpfZ9f-6KI/Tg_WHllBbTI/AAAAAAAAC1k/avTn8KV-JkQ/s72-c/cmsui-frontpage.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-2021566336241610814</id><published>2011-06-30T00:17:00.003+08:00</published><updated>2011-06-30T00:29:34.992+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Change Archetypes CreationDate</title><content type='html'>建立 Plone 內容時，系統會自動指定建立時間，想要竄改這個時間，可以用 bin/plonectl debug 存取 Data.fs，以 app.mysite.news['my-news'] 為例，使用 created() 這個 accessor 可以查詢建立時間，使用 setCreationDate() 這個 mutator 可以指定建立時間，完成之後要記得 transaction.commit()。再進 ZMI 的 portal_catalog 重新執行 created 的索引動作。&lt;br /&gt;在 Products/Archetypes/ExtensibleMetadata.py 可以看到程式碼細節。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-2021566336241610814?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/2021566336241610814/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=2021566336241610814' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2021566336241610814'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2021566336241610814'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/06/change-archetypes-creationdate.html' title='Change Archetypes CreationDate'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-8970142280951310316</id><published>2011-06-28T15:17:00.005+08:00</published><updated>2011-06-28T16:54:45.793+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Plone BlobStorage and Image</title><content type='html'>下列是使用 Plone 4.0.7 實測 &lt;a href=http://marrtw.blogspot.com/2010/09/blob-support-for-plone.html&gt;Blob 功能&lt;/a&gt;的記錄。&lt;br /&gt;&lt;br /&gt;新增一個 Image，附的圖檔約 1.4MB，看到 var/blobstorage 裡出現一個 0x00/0x00/0x00/0x00/0x00/0x00/0x20/0x5b/0x038f4c3af3551d22.blob 檔案，大小 1486990，驗證圖檔放進 blobstorage 裡，值得注意的是，同時伴隨產生一個 0x00/0x00/0x00/0x00/0x00/0x00/0x20/0x66/0x038f4c3af6acde66.blob 檔案，大小 40615。&lt;br /&gt;&lt;br /&gt;在 Plone 裡把上述圖檔刪除，原本 blobstorage 對應的檔案仍然存在，並未馬上被刪除，直到執行 pack 之後，blobstorage 裡面剩下 0x00/0x00/0x00/0x00/0x00/0x00/0x20 目錄，底下的檔案都被刪除。&lt;br /&gt;&lt;br /&gt;接著，即使重複上述的圖檔新增動作，上傳同樣圖檔，blobstorage 會產生新的對應檔名，例如 0x00/0x00/0x00/0x00/0x00/0x00/0x20/0x6e/0x038f4c4d455447cc.blob (size 1486990) 和 0x00/0x00/0x00/0x00/0x00/0x00/0x20/0x75/0x038f4c4d482af9ee.blob (size 40615)。&lt;br /&gt;&lt;br /&gt;新增一個 File，附的檔案指定上述的圖檔，blobstorage 則會產生 0x00/0x00/0x00/0x00/0x00/0x00/0x20/0x7d/0x038f4c7f2e73e400.blob (size 1486990)，但是沒有產生大小為 40615 的伴隨檔案。&lt;br /&gt;&lt;br /&gt;如果編輯舊的 File，上傳新的檔案，則會對應產生 0x00/0x00/0x00/0x00/0x00/0x00/0x20/0x88/0x038f4cd0ba71df00.blob (size 11080872) 新檔案，原本的 0x00/0x00/0x00/0x00/0x00/0x00/0x20/0x7d/0x038f4c7f2e73e400.blob (size 1486990) 仍然存在，直到執行 pack 之後，才會只剩 0x00/0x00/0x00/0x00/0x00/0x00/0x20/0x88/0x038f4cd0ba71df00.blob (size 11080872)。&lt;br /&gt;&lt;br /&gt;另外，新增 News Item 時，預設有兩種方式來上傳圖檔。一種是從 Image 欄位上傳圖檔，這種方式並不會在 blobstorage 裡產生對應的檔案，相同目錄裡也看不到對應的 ATImage 檔案，暫不清楚跟 &lt;a href="http://plone.org/products/archetypes/documentation/manual-1-3/quickref-1-3/widget-reference"&gt;show_content_type&lt;/a&gt; 是否相關。另一種方式是從 RichWidget 欄位上傳圖檔，這種方式會在 News Item 相同的目錄裡，建立對應的 ATImage 檔案，同時也在 blobstorage 裡產生 0x00/0x00/0x00/0x00/0x00/0x00/0x20/0xd1/0x038f4cde1e238bee.blob (size 1486990) 和 0x00/0x00/0x00/0x00/0x00/0x00/0x20/0xd6/0x038f4cde20a1d711.blob (size 6285) 的對應檔案，效果等同先建立 ATImage 再於 RichWidget 裡指定連結。&lt;br /&gt;&lt;br /&gt;額外一提的是，如果 RichWidget 欄位上傳的圖檔，目錄已存在相同檔名的檔案，則會自動更名為 copy_of_SomeImage.jpg 之類的識別碼。&lt;br /&gt;&lt;br /&gt;使用 blobstorage 後，即使所有的 Image File 都清掉，在檔案系統裡還是會留下之前建立過的目錄： &lt;pre&gt;var/blobstorage/&lt;br /&gt;├── 0x00&lt;br /&gt;│   └── 0x00&lt;br /&gt;│       └── 0x00&lt;br /&gt;│           └── 0x00&lt;br /&gt;│               └── 0x00&lt;br /&gt;│                   └── 0x00&lt;br /&gt;│                       ├── 0x20&lt;br /&gt;│                       └── 0x21&lt;br /&gt;└── tmp&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-8970142280951310316?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/8970142280951310316/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=8970142280951310316' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8970142280951310316'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8970142280951310316'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/06/plone-blobstorage-and-image.html' title='Plone BlobStorage and Image'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-8908791520305193456</id><published>2011-06-21T13:26:00.002+08:00</published><updated>2011-06-21T14:13:53.208+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Add Custom Field to Criteria in ATTopic</title><content type='html'>以 Plone site 的 events/aggregator 為例，點選 Criteria 頁籤，可以編輯選集的條件，這個編輯頁面使用 Products/ATContentTypes/skins/ATContentTypes/criterion_edit_form.cpt 程式碼，摘要如下：&lt;pre&gt;&lt;br /&gt;&amp;lt;div class="field" style="float:left"&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;label for="field"&amp;gt;Field name&amp;lt;/label&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;div class="formHelp"&lt;br /&gt;       id="fieldHelpSort"&amp;gt;&lt;br /&gt;      List Available Fields&lt;br /&gt;  &amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;select name="field"&lt;br /&gt;          id="sortfield"&lt;br /&gt;          tal:define="fields context/listSortFields"&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;option value="no_sort"&lt;br /&gt;          tal:attributes="selected python:not context.hasSortCriterion();"&lt;br /&gt;          &amp;gt;No sort order&amp;lt;/option&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;tal:block tal:repeat="field fields"&amp;gt;&lt;br /&gt;      &amp;lt;option value=""&lt;br /&gt;              tal:define="explanation python:field[2]"&lt;br /&gt;              tal:attributes="value python:field[0];&lt;br /&gt;                              selected python:context.hasSortCriterion()&lt;br /&gt;                         and context.getSortCriterion().field==field[0];&lt;br /&gt;                              title explanation"&lt;br /&gt;              tal:content="python:field[1]"&lt;br /&gt;                i18n:attributes="title"&lt;br /&gt;                i18n:translate=""&amp;gt;Field&amp;lt;/option&amp;gt;&lt;br /&gt;  &amp;lt;/tal:block&amp;gt;&lt;br /&gt;  &amp;lt;/select&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;其中的 listSortFields 定義在 content/topic.py 裡。&lt;br /&gt;想&lt;a href="http://stackoverflow.com/questions/6409810/add-custom-field-to-criteria-in-attopic"&gt;在條件裡新增自製欄位&lt;/a&gt;的話，最簡單的方式，是到 Site Setup 的 Collections 裡設定，例如點擊 All fields 後，再勾選 Contributors 選項，讓它成為啟用的項目。&lt;br /&gt;&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/91BGa0BwUDRUEQssyUHywg?feat=embedwebsite"&gt;&lt;img src="https://lh4.googleusercontent.com/-sjn9-Kzs9V8/TgAzjwrAi_I/AAAAAAAAC0I/s5uYBx36cgk/s800/collection-indexes.png" height="622" width="619" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-8908791520305193456?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/8908791520305193456/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=8908791520305193456' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8908791520305193456'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8908791520305193456'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/06/add-custom-field-to-criteria-in-attopic.html' title='Add Custom Field to Criteria in ATTopic'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh4.googleusercontent.com/-sjn9-Kzs9V8/TgAzjwrAi_I/AAAAAAAAC0I/s5uYBx36cgk/s72-c/collection-indexes.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-352049346975462076</id><published>2011-06-01T15:23:00.003+08:00</published><updated>2011-06-08T20:57:16.157+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Migration In Action</title><content type='html'>看過&lt;a href=http://marrtw.blogspot.com/2010/11/plone-3x-migration-to-plone-40.html&gt;昇級的背景資訊&lt;/a&gt;後，可以真的動手了。&lt;br /&gt;&lt;br /&gt;Plone 3.3.5 昇級到 4.0.x，算是相對容易的情境，原則上，把 Plone 3.3.5 選用或自製的模組版本，盡量先更新到合適的號碼，這樣就可以準備一份夠好的 Data.fs。再次提醒，實際昇級前，一定要把 Data.fs 備份好，完成昇級步驟後，還要進行許多測試工作，途中遇到錯誤的話，還是可能要用到 Data.fs 備份檔。&lt;br /&gt;&lt;br /&gt;Plone 3.3.5 和 4.0.x 都有 buildout 工具，利用 buildout 就可以把許多昇級工作搞定，昇級 buildout.cfg 最簡單的方式，是先用 Unified Installer 安裝系統，再把 egg 和其他設定值搬到 buildout.cfg 裡。&lt;br /&gt;&lt;br /&gt;依據上述的原則，下列是實戰過程的記錄。&lt;br /&gt;&lt;br /&gt;舉例來說，原本 Plone 3.3.5 搭配 &lt;a href="http://marrtw.blogspot.com/2010/08/ploneboard-21-with-plone-335.html"&gt;Ploneboard&lt;/a&gt; 2.0，先昇到 Ploneboard 2.2 之後，就可以同時在 Plone3 和 Plone4 使用。不過，&lt;a href=http://plone.293351.n2.nabble.com/Ploneboard-2-2-on-PLone-4-0-4-errors-when-try-to-access-a-conversation-td6248629.html&gt;Ploneboard 搭配的 python-dateutil&lt;/a&gt;，要使用 1.5 版本，處理方式是在 [versions] 設定區段，指定為 python-dateutil = 1.5，如果安裝 python-dateutil 2.0 的話，會造成 Ploneboard 無法執行，處理方法是刪掉 python-dateutil 2.0 再裝 1.5 版本。&lt;br /&gt;&lt;br /&gt;&lt;a href="http://plone.org/products/webcouturier-dropdownmenu"&gt;webcouturier.dropdownmenu&lt;/a&gt; 模組也有類似的情況，它的 2.0 版本是搭配 Plone 3.x，到 2.1 版本就同時支援 Plone 3 和 4。另外，我是昇級 Plone4 後才發現 webcouturier 有 2.1 版，事後再昇級 webcouturier 並沒問題。&lt;br /&gt;&lt;br /&gt;設定 Plone 4.0.5 的 buildout.cfg 時，記得把 &lt;a href="http://plone.org/products/plone-hotfix/"&gt;Products.PloneHotfix&lt;/a&gt; 加上去，執行 bin/buildout 之後，把舊系統的 Data.fs 放到 var/filestorage 目錄，執行 bin/plonectl debug 來啟動新系統，總之，要等完成昇級測試後，再改用 bin/plonectl start。&lt;br /&gt;&lt;br /&gt;進 ZMI 的昇級畫面後，先勾選 dry run 執行模擬，接著有一系列的昇級動作，其中的 plone.app.upgrade 在 v40/betas.py 檔案裡，定義了 convertToBlobs() 函式，會把 File 和 Image 轉存為 Blob 檔案，這個過程會把舊檔刪除，再建立新檔，如果舊站已存在許多 File 和 Image 的話，這個步驟會花很長的時間。&lt;br /&gt;&lt;br /&gt;手邊一個 9.6G Data.fs 建立 6.5G blobstorage，最後 Data.fs 剩下 278.9M，因此，執行昇級前最好留有 Data.fs 一倍以上的硬碟空間。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-352049346975462076?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/352049346975462076/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=352049346975462076' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/352049346975462076'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/352049346975462076'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/06/migration-in-action.html' title='Migration In Action'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-2455486067291728867</id><published>2011-05-22T19:14:00.007+08:00</published><updated>2011-05-24T00:48:25.541+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>The Python Paradox</title><content type='html'>This is a zh-tw translation of &lt;a href=http://www.paulgraham.com/pypar.html&gt;The Python Paradox&lt;/a&gt; by &lt;a href=http://www.paulgraham.com/bio.html&gt;Paul Graham&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;在某次&lt;a href=http://www.paulgraham.com/gh.html&gt;演講&lt;/a&gt;中，我的話語惹毛不少人，那段話是這樣說的 -「使用 Python 的專案，會比 Java 的專案，容易吸引到更聰明的程式人員。」&lt;br /&gt;&lt;br /&gt;我可沒說 Java 程式員是笨的，那句話的焦點在於 Python 程式員是聰明的。學習一個新的程式語言，需要花費不少工夫，因此，人們不會為了混口飯吃，而去學 Python，那些學 Python 的程式員，是基於對編程的熱愛，而且他們對已知的語言都感到不滿意。&lt;br /&gt;&lt;br /&gt;這類的程式員，應該是公司想要雇用的。由於找不到好的形容詞，我姑且稱這種現象是 Python 的弔詭 - 如果公司的軟體專案使用密教式的語言來開發，他們會雇用到較好的程式員，因為這樣的條件會吸引那些願意學這類語言的好手。對程式人員而言，下列的說法，更是無庸置疑：如果你想要找到好工作，該學的語言，必然不是那些只能混口飯吃的語言。&lt;br /&gt;&lt;br /&gt;迄今，只有很少的公司夠聰明，能明白這樣的道理，不過，還有另一種演化趨勢，來自於那些最受程式員喜愛的公司，例如 Google，當他們要找 Java 工程師時，也會要求同時具備 Python 經驗。&lt;br /&gt;&lt;br /&gt;我有個朋友，熟知所有廣被使用的語言，他手邊的專案，多數就用 Python 開發，而選用 Python 的最大原因，是程式碼看起來舒服。比較程式語言的選用條件時，這樣的理由聽起來可能好笑，但搭配下列的情境，這句話就不像鬧著玩了：動手編程時，你會花很多時間在讀程式碼，而不是寫程式碼。編修程式碼的過程，就像雕塑師在作品上捏塑泥巴的過程，如果語言讓程式碼變醜，嚴謹的程式員看了可會抓狂，就像糊成一團的泥巴，會讓雕塑師無法忍受一樣。&lt;br /&gt;&lt;br /&gt;提到醜的程式碼，人們很容易想到 Perl，不過，Perl 表面上的醜，或是生硬語法所帶來的醜，並不是我想講的，錯誤概念所造成的醜，才是真的讓人無法接受的事。有時候 Perl 程式碼看起來像卡通人物幹譙時的一串符號，但&lt;a href=http://www.paulgraham.com/icad.html&gt;有些情況&lt;/a&gt;下，Perl 在概念上比 Python 來得優異。&lt;br /&gt;&lt;br /&gt;話說回來，這兩個語言仍然各自&lt;a href=http://www.paulgraham.com/hundred.html&gt;演化&lt;/a&gt;中，和 Ruby (Icon, Joy, J, Lisp, Smalltalk) 一樣，投身它們的程式員，都是熱愛編程的族群，而且是更有機會搞好編程的一群。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-2455486067291728867?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/2455486067291728867/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=2455486067291728867' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2455486067291728867'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2455486067291728867'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/05/python-paradox.html' title='The Python Paradox'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-2035386763419149065</id><published>2011-05-14T12:57:00.008+08:00</published><updated>2011-05-17T18:03:40.978+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Related Items and ReferenceField</title><content type='html'>預設的 ATContentType 提供 relatedItems 欄位，可以用來&lt;a href=http://plone.org/documentation/kb/add-content-programmatically&gt;建立&lt;/a&gt;或指定關連項目，想&lt;a href=https://weblion.psu.edu/trac/weblion/wiki/AutomatingObjectCreation&gt;在程式裡指定&lt;/a&gt;的話，可以參考下列的資訊：&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; app.mysite['front-page'].setRelatedItems(app.mysite.news)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; app.mysite['front-page'].getRelatedItems()&lt;br /&gt;[&amp;lt;ATFolder at /mysite/news&amp;gt;]&lt;/pre&gt;setRelatedItems() 每次只接一個參數，因此後來接的參數，原則上就是蓋掉之前的設定值。想要清空內容的話，就是使用 setRelatedItems('')，想要指定多值，或是加入新值的話，可用串列的技巧：&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; app.mysite['front-page'].setRelatedItems&lt;br /&gt;([app.mysite.news,app.mysite.events])&lt;/pre&gt;還有個 getRawRelatedItems() 可以傳回 UID：&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; app.mysite['front-page'].getRawRelatedItems()&lt;br /&gt;['f8a2a96821f6e3df8dafd19d16b68fb2',&lt;br /&gt; '04b2e1267906691beccdff58dd861176']&lt;/pre&gt;另外，在plone.app.layout/viewlets/content.py 裡的 ContentRelatedItems 程式碼片段，示範如何存取及顯示：&lt;pre&gt;class ContentRelatedItems(ViewletBase):&lt;br /&gt;    index = ViewPageTemplateFile("document_relateditems.pt")&lt;br /&gt;    def related_items(self):&lt;br /&gt;        context = aq_inner(self.context)&lt;br /&gt;        res = ()&lt;br /&gt;        if base_hasattr(context, 'getRawRelatedItems'):&lt;br /&gt;            catalog = getToolByName(context, 'portal_catalog')&lt;br /&gt;            related = context.getRawRelatedItems()&lt;br /&gt;            if not related:&lt;br /&gt;                return ()&lt;br /&gt;            brains = catalog(UID=related)&lt;br /&gt;            res = list(brains)&lt;br /&gt;        return res&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-2035386763419149065?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/2035386763419149065/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=2035386763419149065' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2035386763419149065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2035386763419149065'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/05/related-items-and-referencefield.html' title='Related Items and ReferenceField'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-3157705196551328040</id><published>2011-05-12T15:40:00.002+08:00</published><updated>2011-05-16T17:06:37.446+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Values for Archetypes Field and Widget</title><content type='html'>StringField 搭配 StringWidget 或 SelectionWidget 是最簡化的情況，儲存的資料是 string，如果 StringField 搭配 MultiSelectionWidget 或 InAndOutWidget，儲存的資料就是 list。前者的空值是 ''，後者的空值是 ['']，使用 page template 顯示時，就要額外處理它們的差異。例如：&lt;pre&gt;&amp;lt;div tal:condition="context/myfield"&amp;gt;&lt;br /&gt;My Field:&lt;br /&gt;&amp;lt;span metal:use-macro="python:context.widget('myfield', mode='view')" /&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/pre&gt;當資料是空的 string 時，整個 &amp;lt;div /&amp;gt; 並不會顯示，當資料是空的 list 時，還是會出現 My Field: [''] 之類的畫面。&lt;br /&gt;一個簡單的處理方式是：&lt;pre&gt;&amp;lt;div tal:condition="python:context.myfield != ['']"&amp;gt;&lt;br /&gt;My Field:&lt;br /&gt;&amp;lt;span metal:use-macro="python:context.widget('myfield', mode='view')" /&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-3157705196551328040?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/3157705196551328040/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=3157705196551328040' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3157705196551328040'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3157705196551328040'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/05/values-for-archetypes-field-and-widget.html' title='Values for Archetypes Field and Widget'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-1762064177553862378</id><published>2011-05-10T17:30:00.004+08:00</published><updated>2011-05-10T18:29:53.203+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Couldn't find index page for 'PasteScript'</title><content type='html'>在 Plone 4.0.2 正常運作一段時間後，因為修改 src 目錄裡的自製模組，需要重新執行 bin/buildout，遇到錯誤訊息：&lt;pre&gt;Download error: (-2, 'Name or service not known') -- Some packages may not be found!&lt;br /&gt;Couldn't find index page for 'PasteScript' (maybe misspelled?)&lt;br /&gt;&lt;br /&gt;distutils.errors.DistutilsError:&lt;br /&gt; Could not find suitable distribution for Requirement.parse('PasteScript')&lt;br /&gt;While:&lt;br /&gt;  Installing.&lt;br /&gt;  Processing develop directory '/home/marr/mysite/zinstance/src/my.package'.&lt;br /&gt;&lt;/pre&gt;檢查 my.package/setup.py 裡面有一行 setup_requires=["PasteScript"], 設定值，註解掉暫時有排除問題，但仍不清楚原理。&lt;br /&gt;另外在 &lt;a href=http://plone.293351.n2.nabble.com/Problems-running-Buildout-to-install-a-theme-created-with-Paster-td4767981.html&gt;Problems running Buildout to install a them created with Paster&lt;/a&gt; 有類似的討論，或許會有正確解答的線索。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-1762064177553862378?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/1762064177553862378/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=1762064177553862378' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1762064177553862378'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1762064177553862378'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/05/couldnt-find-index-page-for-pastescript.html' title='Couldn&apos;t find index page for &apos;PasteScript&apos;'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-5101846828931274793</id><published>2011-04-28T14:29:00.003+08:00</published><updated>2011-04-28T15:51:17.550+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>eea.jquery datepicker localization</title><content type='html'>eea.jquery 的 UI 模組，已經把 localization 需要檔案準備好，在 ui/development-bundle/ui/i18n 目錄裡找得到。想要將 datepicker 日曆裡的月份星期以中文顯示，在 Plone 裡至少要修改兩個檔案，首先是利用 jsregistry.xml 註冊需要的 javascript resource，接著在 ui/configure.zcml 裡註冊實際的檔案位置。&lt;br /&gt;&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/Jv-XJm6T1v0_SLjm7MlsRQ?feat=embedwebsite"&gt;&lt;img src="https://lh6.googleusercontent.com/_BESgcgeL9eA/Tbkahoxm3NI/AAAAAAAACwc/2ar93_VuvQw/s800/datepicker.png" height="241" width="345" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;eea/jquery/profiles/ui/jsregistry.xml&lt;br /&gt;&lt;pre&gt;  &amp;lt;javascript id="++resource++jquery.ui-1.7.js"&lt;br /&gt;   cacheable="True" compression="safe" cookable="True" enabled="True" inline="False"&lt;br /&gt;   expression="" position-after="jquery.js" /&amp;gt;&lt;br /&gt;+ &amp;lt;javascript id="++resource++ui.datepicker-zh-TW.js"&lt;br /&gt;+  cacheable="True" compression="safe" cookable="True" enabled="True" inline="False"&lt;br /&gt;+  expression="" position-after="jquery.js" /&amp;gt;&lt;br /&gt; &amp;lt;/object&amp;gt;&lt;/pre&gt;&lt;br /&gt;eea/jquery/ui/configure.zcml&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;+    &amp;lt;browser:resource&lt;br /&gt;+        name="ui.datepicker-zh-TW.js"&lt;br /&gt;+        file="development-bundle/ui/i18n/ui.datepicker-zh-TW.js"&lt;br /&gt;+        permission="zope.Public"&lt;br /&gt;+        /&amp;gt;&lt;br /&gt;+&lt;br /&gt;     &amp;lt;browser:resource&lt;br /&gt;         name="jquery-ui-1.7.custom.css"&lt;br /&gt;         file="css/smoothness/jquery-ui-1.7.custom.css"&lt;br /&gt;         permission="zope.Public"&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-5101846828931274793?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/5101846828931274793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=5101846828931274793' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5101846828931274793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5101846828931274793'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/04/eeajquery-datepicker-localization.html' title='eea.jquery datepicker localization'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh6.googleusercontent.com/_BESgcgeL9eA/Tbkahoxm3NI/AAAAAAAACwc/2ar93_VuvQw/s72-c/datepicker.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7046437823426328548</id><published>2011-04-24T00:32:00.002+08:00</published><updated>2011-04-24T00:59:10.903+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Diazo Theming Tool</title><content type='html'>&lt;a href="http://diazo.org/"&gt;Diazo&lt;/a&gt; 能夠讀取設計師的&lt;a href="http://en.wikipedia.org/wiki/Website_wireframe"&gt;網頁藍圖&lt;/a&gt;，搭配內容管理系統，即時產生網站的佈景主題，同時可以佈署數個網站，&lt;a href="http://valentinewebsystems.com/en/blog/2011/plone-4-themeing-aka-diazo-is-simple"&gt;快速提供一致的操作介面&lt;/a&gt;。它是&lt;a href="http://www.sixfeetup.com/blog/plone-theming-diazo-deliverance-themeeditor"&gt;改善使用經驗的強大工具&lt;/a&gt;，整個過程不需要存取系統原有的程式碼。這項技術概念，早期&lt;a href="http://collective-docs.plone.org/templates_css_and_javascripts/xdv.html"&gt;由 Deliverance 和 XDV 提供實作&lt;/a&gt;，Diazo 著眼在簡化流程，讓佈署的門檻降得更低。&lt;br /&gt;&lt;br /&gt;為了&lt;a href="http://www.treebrolly.com/blog/turbo-plone-theming-with-xdv-diazo"&gt;使用 Diazo&lt;/a&gt;，通常要進行下列的準備工作：&lt;br /&gt;1. 找出佈景主題的檔案裡，哪些是動態顯示的部份，最好使用 HTML 的 id 識別碼，將它們標識出來。&lt;br /&gt;2. 編寫規則設定檔的內容，使用 replace 或 copy 的語法，用來產生上述網頁裡的動態內容。&lt;br /&gt;3. 找出佈景主題的檔案裡，哪些是共用的部份，指的通常是 &amp;lt;head /&amp;gt; 裡的 CSS 或 JavaScript 的程式碼，使用 append 或 prepend 的語法，將它們寫到規則設定檔裡。&lt;br /&gt;4. 找出佈景主題的檔案裡，哪些部份是多餘的，在規則設定檔裡使用 drop 語法來刪除它們。&lt;br /&gt;&lt;br /&gt;規則設定檔使用 XML 格式，可以利用 CSS3 或 XPath 的 selector，來指定負責顯示和內容的元件。&lt;br /&gt;&lt;br /&gt;有了佈景主題的 HTML 檔案，還有規則設定的 XML 檔案，就可以利用 Diazo 的編譯器，把它們轉成一個 &lt;a href="http://www.w3.org/TR/xslt"&gt;XSLT&lt;/a&gt; 檔案，接著佈署在網站應用程式裡，透過 Apache &lt;a href="http://www.outoforder.cc/projects/apache/mod_transform/"&gt;mod_transform&lt;/a&gt; 之類的 XSLT 處理器，就能動態產生搭配佈景主題的網站內容。&lt;br /&gt;&lt;br /&gt;XSLT 的轉換動作很快，靜態的網頁資源，例如圖檔、CSS、JavaScript 等，也可以直接由靜態網站提供服務。&lt;br /&gt;&lt;br /&gt;實用的情境之一，是使用一份佈景主題的 HTML 檔案，配合不同的網站應用程式，產生不同的 XSLT 檔案，再用網址比對的技巧，依照網頁請求來回應不同的 XSLT 檔案。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7046437823426328548?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7046437823426328548/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7046437823426328548' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7046437823426328548'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7046437823426328548'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/04/diazo-theming-tool.html' title='Diazo Theming Tool'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-1696239310784387163</id><published>2011-04-22T15:41:00.003+08:00</published><updated>2011-04-22T15:51:10.724+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>GenericSetup Circular Dependency</title><content type='html'>使用 Plone 4.0.5 import 一個 theme 檔案時，遇到 GenericSetup There are unresolved or circular dependencies. 訊息，因為相依關係造成 import 無法正常完成，似乎只是 warning 而不是 error。之前已有&lt;a href=http://plone.293351.n2.nabble.com/Circular-dependency-in-Plone-import-steps-td6276104.html&gt;討論&lt;/a&gt;，而且應該已經&lt;a href=https://dev.plone.org/plone/ticket/8350&gt;解決&lt;/a&gt;。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-1696239310784387163?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/1696239310784387163/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=1696239310784387163' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1696239310784387163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1696239310784387163'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/04/genericsetup-circular-dependency.html' title='GenericSetup Circular Dependency'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-927760304547210460</id><published>2011-04-21T17:59:00.004+08:00</published><updated>2011-04-22T15:41:37.679+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Blob Storage Restore</title><content type='html'>前幾天執行了 Plone 4.0 到 Plone 4.0.5 的昇級，記錄幾個心得。&lt;br /&gt;主機在 vm 裡，新的 Plone instance 只能先從 localhost 存取，又由於想要整頓帳號，因此使用 w3m http://localhost:8080/ 先建立 Plone Site，再從前台逐步把帳號和資料恢復。&lt;br /&gt;線上直接執行 cp -a ~/plone400/zinstance/var/blobstorage/0x00 ~/plone405/zinstance/var/blobstorage 可能不會正確回復資料，根據&lt;a href=http://plone.293351.n2.nabble.com/1-inconsistencies-between-var-filestorage-Data-fs-and-var-blobstorage-and-2-repozo-recover-in-that-ct-td1106945.html&gt;討論資訊&lt;/a&gt;，配合備份 Data.fs 的時間，也要同時備份 var/blobstorage 的資料。&lt;br /&gt;以 File 為例，舊的檔案如果上傳新的來覆蓋，在 blobstorage 裡會存在兩筆資料，使用 pack 讓 ZODB 更新後，則會只留一筆。這代表 blobstorage 裡的關連，是動態維護的。&lt;br /&gt;目前找到的&lt;a href=http://stackoverflow.com/questions/1851996/backing-up-and-restoring-a-plone-instance&gt;文件資訊&lt;/a&gt;，建議使用 &lt;a href=http://pypi.python.org/pypi/collective.recipe.backup&gt;collective.recipe.backup&lt;/a&gt;，再找時間繼續測試。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-927760304547210460?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/927760304547210460/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=927760304547210460' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/927760304547210460'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/927760304547210460'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/04/blob-storage-restore.html' title='Blob Storage Restore'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-2904688573150616009</id><published>2011-04-19T13:59:00.003+08:00</published><updated>2011-04-28T14:28:47.384+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>DateIndex Patch for DateError</title><content type='html'>在 Plone 4.0.2 ZMI portal_catalog/Indexes 裡，點選 DateIndex 後，想要 Browse 時，偶而會遇到 DateError: Invalid date: (1915, 0, 7, 4, 59, 0, 'UTC') 的錯誤，雖然前台還沒遇到問題，但這樣的狀況總叫人不安。&lt;br /&gt;&lt;br /&gt;在 &lt;a href=http://weblion.psu.edu/chatlogs/%23plone/2010/06/21.txt&gt;plone IRC&lt;/a&gt; 有人提到，這是 manage_browse 的臭虫，使用 browseIndex.dtml DateIndex 當關鍵字，找到 &lt;a href=https://mail.zope.org/pipermail/zope2-tracker/2011-March/001582.html&gt;Browse DateIndex Broken for edge cases&lt;/a&gt;，將 Zope2 的 Products/PluginIndexes/dtml/browseIndex.dtml 加上 &lt;a href=https://bugs.launchpad.net/bugs/727981&gt;patch&lt;/a&gt; 之後，有暫時處理這個臭虫。不過，後來發現搭配 eea.jquery 的 datepicker 功能時，還是會有月份數字少1的問題，例如輸入 1999-01-01 會變成 1999-00-01，看來沒有一勞永逸。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-2904688573150616009?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/2904688573150616009/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=2904688573150616009' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2904688573150616009'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2904688573150616009'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/04/dateindex-patch-for-dateerror.html' title='DateIndex Patch for DateError'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-4623797121954463030</id><published>2011-04-13T16:54:00.009+08:00</published><updated>2011-05-20T12:03:30.750+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Custom Folder Listing Tips</title><content type='html'>常見的 Plone 目錄頁面，可以顯示標題、作者、日期、摘要描述，不過，從 /mysite/@@security-controlpanel 進入 Security settings 設定畫面，有個「Allow anyone to view 'about' information」選項，可以決定一般訪客是否看得到作者和日期資訊。&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/KLupsbxgLEQ6FEjW0GEeug?feat=embedwebsite"&gt;&lt;img src="https://lh5.googleusercontent.com/_BESgcgeL9eA/TaVr1V8KcYI/AAAAAAAACu0/jzWFbEbhp0w/s800/01.png" height="258" width="351" /&gt;&lt;/a&gt;&lt;br /&gt;完整資訊都呈現的效果如下：&lt;br /&gt;&lt;a href="https://picasaweb.google.com/lh/photo/S_y8jdSpZnmb4O1L_pPdyw?feat=embedwebsite"&gt;&lt;img src="https://lh3.googleusercontent.com/_BESgcgeL9eA/TaVp2S7siMI/AAAAAAAACus/NdxIttNHh-o/s800/01.png" height="317" width="457" /&gt;&lt;/a&gt;&lt;br /&gt;希望登入後，還是不顯示作者、日期嗎? 這裡有幾個暴力修改法。&lt;br /&gt;我先用 firebug 查看 HTML 的部份內容，找到「&amp;lt;span class="documentByLine"&amp;gt;」的關鍵字，再到 Plone/buildout-cache/eggs 目錄，用 grep -r documentByLine | grep '.pt' 指令，找到 Products/CMFPlone/skins/plone_content/folder_listing.pt 檔案。&lt;pre&gt;&lt;br /&gt;&amp;lt;tal:byline condition="show_about"&amp;gt;&lt;/pre&gt;show_about 是控制的變數，我們只要調整 condition 條件值，就可以改變它顯示的行為。&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;tal:byline condition="python: show_about and (context.getId() == 'events')"&amp;gt;&lt;/pre&gt;在手邊的例子裡，打算讓它在 events 裡才顯示。&lt;br /&gt;使用 context.id 很可能也行，另外 context.getId() in ('events', 'news') 則可以指定數個目錄值。&lt;br /&gt;&lt;br /&gt;如果自製的 content type 裡，有個 my_field 欄位，在 &lt;tal:entry tal:repeat="item batch"&gt; 的迴圈段落，加上 item_myfield item/getMy_field;，就可以用 &amp;lt;span tal:content="item_myfield"&amp;gt; 來顯示。&lt;br /&gt;&lt;br /&gt;不過，我遇到的例子 my_field 是個 list，顯示結果是 ['\xe6\xb1\x89\xe6\x97\x8f']，如果只是想要顯示 list 裡的第一個值，最簡化的處理方式是使用 context.my_field[0]。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-4623797121954463030?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/4623797121954463030/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=4623797121954463030' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4623797121954463030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4623797121954463030'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/04/custom-folder-listing-tips.html' title='Custom Folder Listing Tips'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh5.googleusercontent.com/_BESgcgeL9eA/TaVr1V8KcYI/AAAAAAAACu0/jzWFbEbhp0w/s72-c/01.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-8244128428934230771</id><published>2011-02-02T01:38:00.004+08:00</published><updated>2011-02-02T02:13:25.618+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Archetypes SelectionWidget</title><content type='html'>建立 Archetypes 表單時，可用的 widget 很多種，這裡特別整理跟選項有關的設定方式。對於「單選」的欄位，使用 SelectionWidget 就行，頂多只需要指定 format 設定值。下列範例提示了值得留意之處：&lt;pre&gt;from Products.Archetypes.Field import StringField&lt;br /&gt;from Products.Archetypes.Widget import SelectionWidget&lt;br /&gt;...&lt;br /&gt;    StringField(&lt;br /&gt;        name='myfield',&lt;br /&gt;        vocabulary='_get_selection_vocab',&lt;br /&gt;        widget=SelectionWidget(&lt;br /&gt;            label_msgid='label_myfield',&lt;br /&gt;            format='select',&lt;br /&gt;        ),&lt;br /&gt;    ),&lt;br /&gt;...&lt;br /&gt;    def _get_selection_vocab(self):&lt;br /&gt;        """sample selection voc&lt;br /&gt;        """&lt;br /&gt;        return DisplayList((('item01','Item One'),('item02','Item Two'),))&lt;/pre&gt;&lt;br /&gt;對於「複選」的欄位，使用 MultiSelectionWidget 就可搞定，如果選項過多，就要考慮是否改用 InAndOutWidget。&lt;br /&gt;更多細節，從 &lt;a href="http://plone.org/documentation/manual/developer-manual/archetypes"&gt;Archetypes Developer Manual&lt;/a&gt; 找得到說明。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-8244128428934230771?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/8244128428934230771/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=8244128428934230771' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8244128428934230771'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8244128428934230771'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/02/archetypes-selectionwidget.html' title='Archetypes SelectionWidget'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-5524129782568380001</id><published>2011-01-29T17:39:00.005+08:00</published><updated>2011-04-24T23:19:09.127+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>EEA Vocabulary Setup</title><content type='html'>了解 &lt;a href="http://marrtw.blogspot.com/2011/01/study-on-vocabulary.html"&gt;vocabulary 的運作原理&lt;/a&gt;後，可以參考 Products.EEAContentTypes 的設定範例，在 Products/EEAContentTypes/setup/vocabularies.py 檔案裡看得到變數設定值：&lt;pre&gt;vocabs = {}&lt;br /&gt;vocabs['themes'] = (&lt;br /&gt;    ('default', 'Default'),&lt;br /&gt;    ...&lt;br /&gt;    ('various', 'Various other issues'),&lt;br /&gt;    ('waste', 'Waste'),&lt;br /&gt;    ('water', 'Water'),&lt;br /&gt;)&lt;/pre&gt;在 Products/EEAContentTypes/setup/ConfigurationMethods.py 裡看得到設定方法：&lt;pre&gt;def setupATVocabularies(self, portal):&lt;br /&gt;    """ Installs all AT-based Vocabularies """&lt;br /&gt;&lt;br /&gt;    from Products.ATVocabularyManager.config import TOOL_NAME \&lt;br /&gt;         as ATVOCABULARYTOOL&lt;br /&gt;    from vocabularies import vocabs&lt;br /&gt;&lt;br /&gt;    vkeys = vocabs.keys()&lt;br /&gt;    atvm = getToolByName(portal, ATVOCABULARYTOOL, None)&lt;br /&gt;    if atvm is None:&lt;br /&gt;        return&lt;br /&gt;&lt;br /&gt;    for vkey in vkeys:&lt;br /&gt;&lt;br /&gt;        if hasattr(atvm, vkey):&lt;br /&gt;            continue&lt;br /&gt;&lt;br /&gt;        print "adding vocabulary %s" % vkey&lt;br /&gt;&lt;br /&gt;        atvm.invokeFactory('SimpleVocabulary', vkey)&lt;br /&gt;        vocab = atvm[vkey]&lt;br /&gt;        for (ikey, value) in vocabs[vkey]:&lt;br /&gt;            vocab.invokeFactory('SimpleVocabularyTerm', ikey)&lt;br /&gt;            vocab[ikey].setTitle(value)&lt;/pre&gt;不過，ConfigurationMethods.py 裡用的 SetupWidget 是舊式方法，新版已改用 &lt;a href="http://marrtw.blogspot.com/2008/08/genericsetup.html"&gt;GenericSetup&lt;/a&gt; 方式安裝。另外，&lt;a href=http://dev.plone.org/collective/browser/collective.realestatebroker/trunk/collective/realestatebroker/content/vocabularies.py&gt;collective.realestatebroker 使用 unicode_vocabulary 來取代 SimpleVocabulary&lt;/a&gt;，&lt;a href="http://collective-docs.plone.org/models/vocabularies.html"&gt;collective-docs 裡說明 vocabulary 的新式建立方式&lt;/a&gt;也和 Archetypes 不同，目前使用 &lt;a href="http://rpatterson.net/blog/ttw-dexterity-vocabularies"&gt;dexterity&lt;/a&gt; 來搭配測試。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-5524129782568380001?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/5524129782568380001/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=5524129782568380001' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5524129782568380001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5524129782568380001'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/01/eea-vocabulary-setup.html' title='EEA Vocabulary Setup'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7277739481862767908</id><published>2011-01-21T09:41:00.004+08:00</published><updated>2011-01-21T11:10:25.506+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Drop Down Menu</title><content type='html'>目前試過三種 Plone 的 dropdown menu 模組，最早用的是 &lt;a href=http://marrtw.blogspot.com/2010/03/zpublisherconflict-conflicterror.html&gt;webcouturier.dropdownmenu&lt;/a&gt;，最近再裝了 &lt;a href=http://projects.quintagroup.com/products/wiki/quintagroup.dropdownmenu&gt;quintagroup.dropdownmenu&lt;/a&gt; 和 &lt;a href=http://projects.quintagroup.com/products/wiki/qPloneDropDownMenu&gt;Products.qPloneDropDownMenu&lt;/a&gt;，初步看來 quintagroup.dropdownmenu 可以配合 Plone 4 環境，Products.qPloneDropDownMenu 則有兩個版本分支，分別配合 Plone 3 和 Plone 4 環境。&lt;br /&gt;安裝 quintagroup.dropdownmenu 的方式之一，是到 src 目錄下載原始碼：&lt;pre&gt;$ cd src&lt;br /&gt;$ svn co \&lt;br /&gt;http://svn.quintagroup.com/products/quintagroup.dropdownmenu/trunk \&lt;br /&gt;quintagroup.dropdownmenu&lt;/pre&gt;再編輯 &lt;a href=http://plone.org/documentation/manual/plone-community-developer-documentation/tutorials/buildout/buildoutcfg&gt;buildout.cfg&lt;/a&gt; 內容，在 eggs, zcml, devel 變數加上 quintagroup.dropdownmenu 設定值，最後執行 buildout 讓設定值生效，我用的是指定 &lt;a href=http://marrtw.blogspot.com/2010/05/developer-options-for-plone-4.html&gt;develop.cfg&lt;/a&gt; 來當執行參數：&lt;pre&gt;$ bin/buildout -c develop.cfg&lt;/pre&gt;過程遇到版本衝突的訊息：&lt;pre&gt;While:&lt;br /&gt;  Installing instance.&lt;br /&gt;Error: There is a version conflict.&lt;br /&gt;We already have: zope.schema 3.5.4&lt;br /&gt;but z3c.form 2.4.1 requires 'zope.schema&gt;=3.6.0'.&lt;/pre&gt;再次編輯 buildout.cfg 裡的 versions 設定區段，加上 z3c.form = 2.2.0 後，重新執行上述指令就行。&lt;br /&gt;&lt;br /&gt;&lt;a href="http://picasaweb.google.com/lh/photo/JyVrk6eOBG9QVMVbM7DigQ?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_BESgcgeL9eA/TTjpZCcyH1I/AAAAAAAACiI/cTVtb-zMrqU/s800/01-products.png" height="147" width="359" /&gt;&lt;/a&gt;&lt;br /&gt;安裝後的設定方式，是到 ZMI portal_actions 裡的 portal_tabs 新增 CMF Action 和 CMF Action Category，由於它用了 RAM Cache，想要馬上看到更改後的效果，從 http://localhost:8080/mysite/@@ramcache-controlpanel 之類的網址，清除 cache 記錄即可。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7277739481862767908?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7277739481862767908/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7277739481862767908' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7277739481862767908'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7277739481862767908'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/01/drop-down-menu.html' title='Drop Down Menu'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_BESgcgeL9eA/TTjpZCcyH1I/AAAAAAAACiI/cTVtb-zMrqU/s72-c/01-products.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-1235492843212540468</id><published>2011-01-16T16:37:00.008+08:00</published><updated>2011-02-15T13:56:32.535+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Study on Vocabulary</title><content type='html'>在 &lt;a href=http://www.universalwebservices.net/web-programming-resources/zope-plone/dynamic-vocabularies-in-plone-archetypes&gt;Dynamic Vocabularies in Plone Archetypes&lt;/a&gt; 文件裡，介紹幾種常見的 vocabulary 範例。最簡單的設定方式，是在 config.py 檔案列出清單：&lt;pre&gt;HOUR = DisplayList((&lt;br /&gt;  ('1', '1'),&lt;br /&gt;  ('2', '2'),&lt;br /&gt;  etc...&lt;br /&gt;))&lt;/pre&gt;再於 content type 的定義檔案裡，指定 vocabulary 變數值：&lt;pre&gt;StringField(&lt;br /&gt;    'hour',&lt;br /&gt;    vocabulary=HOUR,&lt;br /&gt;    widget=SelectionWidget(&lt;br /&gt;        label='Hour',&lt;br /&gt;    ),&lt;br /&gt;    required=1,&lt;br /&gt;),&lt;/pre&gt;這個例子的清單，還可以用程式碼來產生：&lt;pre&gt;class Event(ATContent):&lt;br /&gt;    def getHours(self):&lt;br /&gt;        dl = DisplayList()&lt;br /&gt;        for x in range(1, 13):&lt;br /&gt;          dl.add(str(x))&lt;br /&gt;          dl.add(str(x))&lt;br /&gt;        return dl&lt;/pre&gt;這樣就可以改用 method 存取：&lt;br /&gt;&lt;pre&gt;StringField(&lt;br /&gt;    'hour',&lt;br /&gt;    vocabulary="getHours",&lt;br /&gt;    widget=SelectionWidget(&lt;br /&gt;        label='Hour',&lt;br /&gt;    ),&lt;br /&gt;    required=1,&lt;br /&gt;),&lt;/pre&gt;進一步來看，想要動態存取目錄裡的物件，可以借助 Acquisition 的服務：&lt;br /&gt;&lt;pre&gt;from Acquisition import aq_parent&lt;/pre&gt;程式碼範例如下：&lt;br /&gt;&lt;pre&gt;def getLinks(self):&lt;br /&gt;    dl = DisplayList()&lt;br /&gt;    linklist = aq_parent(self).contentValues(&lt;br /&gt;                   filter={'portal_type': 'Link'}&lt;br /&gt;               )&lt;br /&gt;    for link in linklist:&lt;br /&gt;        dl.add(link['id'], link['title'])&lt;br /&gt;       &lt;br /&gt;    return dl&lt;/pre&gt;接著，還可以參考 eea.faceted.vocabularies 的範例：&lt;pre&gt;eea/faceted/vocabularies&lt;br /&gt;|-- README.txt&lt;br /&gt;|-- __init__.py&lt;br /&gt;|-- catalog.py&lt;br /&gt;|-- configure.zcml&lt;br /&gt;|-- faceted.py&lt;br /&gt;|-- portal.py&lt;br /&gt;|-- position.py&lt;br /&gt;|-- section.py&lt;br /&gt;|-- simple.py&lt;br /&gt;|-- tests.py&lt;br /&gt;|-- types.py&lt;br /&gt;|-- utils.py&lt;br /&gt;`-- version.txt&lt;/pre&gt;以 faceted.py 為例，裡面的 FacetedPortalTypesVocabulary 會回傳 portal_types 和 portal_facedted 裡定義的項目，而 portal_facedted 是由 eea.faceted.tool 提供，如果要調整項目的排序方式，可以查詢 items.sort(key=operator.itemgetter(1), cmp=compare) 程式碼片段。&lt;br /&gt;&lt;br /&gt;測試過程遇到 ValueError: Supplied vocabulary values resulted in duplicate term tokens 錯誤訊息，狀況是混用 Products.ATVocabularyManager 和 vocabularies.zcml 兩種方式，把 vocabulary name 都設成一樣，結果在進入 facetednavigation 的 configlet 畫面，它要把所有 vocabulary 清單列出時，發生這樣的錯誤。&lt;br /&gt;&lt;br /&gt;更底層的細節，則可參考 zope.schema.vocabulary 和 zope.app.schema.vocabulary 的內容：&lt;br /&gt;SimpleVocabulary : implements(IVocabularyTokenized)&lt;br /&gt;SimpleTerm : implements(ITokenizedTerm)&lt;br /&gt;FacetedPortalTypesVocabulary : implements(IVocabularyFactory)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-1235492843212540468?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/1235492843212540468/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=1235492843212540468' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1235492843212540468'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1235492843212540468'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2011/01/study-on-vocabulary.html' title='Study on Vocabulary'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-3695568424368157930</id><published>2010-12-23T21:07:00.003+08:00</published><updated>2010-12-23T21:25:01.874+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Exploring Objects with iw.debug</title><content type='html'>&lt;a href=http://pypi.python.org/pypi/iw.debug&gt;iw.debug&lt;/a&gt; 是用於 Zope 或 Plone 的偵錯工具，看來是仿照 &lt;a href=http://plone.org/documentation/kb/debug-anywhere&gt;Debug Anywhere&lt;/a&gt; 原理做成，和 &lt;a href=http://marrtw.blogspot.com/2009/07/plonectl-debug.html&gt;plonectl debug&lt;/a&gt; 的方式有所不同。以 Plone 4.0.2 為例來安裝，在 buildout.cfg 裡指定 eggs 和 zcml 加入 iw.debug 即可，iw.debug 0.3 的相依套件是 ipython 0.10.1。&lt;br /&gt;執行方式是先 bin/plonecfg fg 在前景模式啟動，透過 http://localhost:8080/myportal/@@ipdb 之類的網址，就可以在 console 看到 ipdb&amp;gt; 提示符號，輸入 ll 可以查看 local 變數。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-3695568424368157930?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/3695568424368157930/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=3695568424368157930' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3695568424368157930'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3695568424368157930'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/12/exploring-objects-with-iwdebug.html' title='Exploring Objects with iw.debug'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-8017221192446021003</id><published>2010-12-22T17:37:00.003+08:00</published><updated>2010-12-22T18:26:45.338+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Widget to show Localized Title</title><content type='html'>&lt;a href="http://marrtw.blogspot.com/2010/10/eea-faceted-navigation.html"&gt;eea.facetednavigation&lt;/a&gt; 的 checkbox widget 預設不會顯示翻譯過的標題，下列是個解法：&lt;br /&gt;&lt;pre&gt;$ diff widgets/checkbox/widget.pt widgets/checkbox/widget.pt-new&lt;br /&gt;14,15c14,15&lt;br /&gt;&amp;lt;   &amp;lt;legend tal:define="title python:view.data.get('title', '')"&lt;br /&gt;&amp;lt;     tal:content="&lt;br /&gt;&amp;lt;     python:view.translate(title)" i18n:translate=""&amp;gt;HTML Widget&amp;lt;/legend&amp;gt;&lt;br /&gt;---&lt;br /&gt;&amp;gt;   &amp;lt;legend tal:define="title python:view.data.get('title', '')"&lt;br /&gt;&amp;gt;     i18n:translate="" i18n:domain="plone"&amp;gt;&lt;br /&gt;&amp;gt;     &amp;lt;span tal:replace="title"/&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;25c25&lt;br /&gt;&amp;lt;         wbr_term_label python:view.word_break(term_label);&lt;br /&gt;---&lt;br /&gt;&amp;gt;         term_label_translated python:term[1];&lt;br /&gt;35c35&lt;br /&gt;&amp;lt;           tal:content="structure wbr_term_label"&amp;gt;term label&amp;lt;/label&amp;gt;&lt;br /&gt;---&lt;br /&gt;&amp;gt;           tal:content="structure term_label_translated"&amp;gt;term label&amp;lt;/label&amp;gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-8017221192446021003?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/8017221192446021003/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=8017221192446021003' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8017221192446021003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/8017221192446021003'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/12/widget-to-show-localized-title.html' title='Widget to show Localized Title'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-4495374606548145202</id><published>2010-12-01T10:27:00.002+08:00</published><updated>2010-12-01T10:33:27.595+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Plone Folder</title><content type='html'>Plone 3 和 Plone 4 目錄的實作方式不同，前者分成「一般目錄」和「大型目錄」兩種目錄型別，後者則是只實作一種型別，將兩種型別的特色整併。&lt;br /&gt;&lt;br /&gt;一般目錄 (Folder) 最適合存放 50 個項目之內的場合，在內容操作介面裡，使用者可以手動拖拉，調整項目的順序。缺點在於，存取目錄物件時，是使用 Python 的 pickle 指令，會將整個目錄資料載入記憶體，目錄裡的項目過多時，容易造成效能變差。同時，目錄裡的項目值是以 tuple 型別來指定，存取過程都是整批處理，容易造成 ZODB 的衝突錯誤。&lt;br /&gt;&lt;br /&gt;大型目錄 (Large Folder) 則是適合存放大量項目的場合，內部以 B-tree 實作，對於記憶體和硬碟的衝擊較小，同時也能針對局部資料進行鎖定，不易造成 ZODB 的衝突錯誤。它的缺點則是沒有提供調整項目順序的介面，而且要額外到 portal_types 設定，才能把項目內容顯示在資訊方框。&lt;br /&gt;&lt;br /&gt;Plone 4 重新實作一個 &lt;a href="http://pypi.python.org/pypi/plone.folder"&gt;plone.folder&lt;/a&gt; 型別，混合上述的優點，不再分成兩種目錄型別。&lt;br /&gt;&lt;br /&gt;透過 Plone 3 網站 ZMI 的 portal_types 工具，可以詳細觀察 Large Folder 的屬性值，特別留意 Implicitly addable 的設定值，勾選後就可以在 Plone 3 的前台介面新增，例如 Members、news、events 目錄，都是使用 Large Folder 型別。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-4495374606548145202?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/4495374606548145202/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=4495374606548145202' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4495374606548145202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4495374606548145202'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/12/plone-folder.html' title='Plone Folder'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7982306618068345620</id><published>2010-11-30T14:18:00.003+08:00</published><updated>2011-05-10T10:22:51.327+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Plone 3.x Migration to Plone 4.0</title><content type='html'>migration 至少分成資料與模組的昇級，兩者相關，但處理的重點不同。前者可參考 &lt;a href=http://plone.org/products/quintagroup.transmogrifier/documentation/reference-manual/content-migration-plone-3-to-plone-4&gt;quintagroup.transmogrifier 的範例&lt;/a&gt;，後者則要先閱讀 &lt;a href=http://plone.org/documentation/manual/upgrade-guide/version/upgrading-plone-3-x-to-4.0&gt;Upgrade Guide&lt;/a&gt;。&lt;br /&gt;&lt;br /&gt;找到一個 &lt;a href=http://trac.hsl.washington.edu/ethnomed/&gt;ethnomed project&lt;/a&gt; 當作模組昇級範例，它已具備 buildout policy theme contenttypes extrafields reviewlist 等模組，透過 svn co https://svn.hsl.washington.edu/repos/ethnomed/ethnomed.buildout/trunk 可以取得原始碼。&lt;br /&gt;&lt;br /&gt;開發過程有使用 Products.CacheSetup 之類的相依關係，在 Plone 4 的場合會遇到問題，可參考這個 &lt;a href="http://dev.plone.org/collective/changeset/100740"&gt;chageset&lt;/a&gt; 的處理方式。另外 ethnomed/contenttypes/browser/viewlets.py 裡的 membership = self.tools.membership() 要改成 membership = getToolByName(self.context, 'portal_membership') 才能正常顯示多位作者。&lt;br /&gt;&lt;br /&gt;額外的範例還有 &lt;a href="http://dev.plone.org/collective/changeset/237990"&gt;PloneBooking 的 changeset&lt;/a&gt;。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7982306618068345620?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7982306618068345620/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7982306618068345620' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7982306618068345620'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7982306618068345620'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/11/plone-3x-migration-to-plone-40.html' title='Plone 3.x Migration to Plone 4.0'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-1423478005468511583</id><published>2010-11-29T22:11:00.005+08:00</published><updated>2011-04-25T12:01:01.478+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Title-to-Id Behavior</title><content type='html'>title-to-id 是 Plone 的行為特色，在建立文件的過程，會把 title 欄位的值，轉換成 URL 的值。事實上，也可以事後轉換，例如寫成 &lt;a href=http://keeshink.blogspot.com/2008/11/using-plones-title-to-id-conversion-in.html&gt;External Method 來批次執行&lt;/a&gt;。&lt;br /&gt;&lt;br /&gt;Archetypes 預設就使用 title-to-id 機制，相關的程式碼片段整理如下：&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Products/Archetypes/BaseObject.py&lt;/span&gt;&lt;pre&gt;# Import conditionally, so we don't introduce a hard dependency&lt;br /&gt;try:&lt;br /&gt;    from plone.i18n.normalizer.interfaces import IUserPreferredURLNormalizer&lt;br /&gt;    from plone.i18n.normalizer.interfaces import IURLNormalizer&lt;br /&gt;    URL_NORMALIZER = True&lt;br /&gt;except ImportError:&lt;br /&gt;    URL_NORMALIZER = False&lt;br /&gt;&lt;br /&gt;class BaseObject(Referenceable):&lt;br /&gt;    ...&lt;br /&gt;    def generateNewId(self):&lt;br /&gt;        """Suggest an id for this object.&lt;br /&gt;        This id is used when automatically renaming an object after creation.&lt;br /&gt;        """&lt;br /&gt;        title = self.Title()&lt;br /&gt;        # Can't work w/o a title&lt;br /&gt;        if not title:&lt;br /&gt;            return None&lt;br /&gt;&lt;br /&gt;        # Don't do anything without the plone.i18n package&lt;br /&gt;        if not URL_NORMALIZER:&lt;br /&gt;            return None&lt;br /&gt;&lt;br /&gt;        if not isinstance(title, unicode):&lt;br /&gt;            charset = self.getCharset()&lt;br /&gt;            title = unicode(title, charset)&lt;br /&gt;&lt;br /&gt;        request = getattr(self, 'REQUEST', None)&lt;br /&gt;        if request is not None:&lt;br /&gt;            return IUserPreferredURLNormalizer(request).normalize(title)&lt;br /&gt;&lt;br /&gt;        return queryUtility(IURLNormalizer).normalize(title)&lt;br /&gt;&lt;br /&gt;    def _renameAfterCreation(self, check_auto_id=False):&lt;br /&gt;        """Renames an object like its normalized title.&lt;br /&gt;        """&lt;br /&gt;        old_id = self.getId()&lt;br /&gt;        if check_auto_id and not self._isIDAutoGenerated(old_id):&lt;br /&gt;            # No auto generated id&lt;br /&gt;            return False&lt;br /&gt;&lt;br /&gt;        new_id = self.generateNewId()&lt;/pre&gt;&lt;span style="font-weight:bold;"&gt;plone/i18n/normalizer/__init__.py&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;class URLNormalizer(object):&lt;br /&gt;    ...&lt;br /&gt;    def normalize(self, text, locale=None, max_length=MAX_URL_LENGTH):&lt;br /&gt;        ...&lt;br /&gt;        text = baseNormalize(text)&lt;br /&gt;        base = text # use text.lower() to make it lower-case&lt;/pre&gt;&lt;br /&gt;把原本 base = text.lower() 改成 base = text。&lt;br /&gt;&lt;br /&gt;另外，Dexterity 則利用 &lt;a href="http://pypi.python.org/pypi/plone.app.dexterity"&gt;Name From Title&lt;/a&gt; 的行為設定來啟用，參考 plone.app.content 裡 interfaces.py 的 INameFromTitle。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-1423478005468511583?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/1423478005468511583/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=1423478005468511583' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1423478005468511583'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1423478005468511583'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/11/title-to-id-behavior.html' title='Title-to-Id Behavior'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-6889105228443758142</id><published>2010-11-24T22:02:00.007+08:00</published><updated>2010-11-25T00:57:58.017+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Archetype Content Customization</title><content type='html'>在 Plone 4 初期，Archetype 還活著，它仍是自製表單的常見方式，即使日後要改用 &lt;a href="http://marrtw.blogspot.com/2010/01/dexterity-vs-archetypes.html"&gt;Dexterity&lt;/a&gt;，也有機會透過 &lt;a href="http://marrtw.blogspot.com/2010/11/transmogrifier-importexport-made-easy.html"&gt;transmogrifier&lt;/a&gt; 來&lt;a href="http://marrtw.blogspot.com/2009/11/content-type-migration.html"&gt;轉移內容&lt;/a&gt;。想要客製化 Archetype 表單，常見的工作是修改 view 和 edit 的程式碼，以 MyType 的 view 為例，它是由 &lt;a href="http://plone.org/documentation/manual/theme-reference/buildingblocks/skin/templates/customizing-at-templates/what-makes-it-tick"&gt;base_view.pt&lt;/a&gt; 來管理 js、css、header、body、folderlisting、footer 六個 macro 設定值，預設會去拉 mytype_view.pt 來顯示，找不到的話就使用 base.pt 來顯示，base.pt 提供四個 macro 設定值，能夠滿足小規模的調整需求。原則上不需要修改 base_view.pt 內容，想要大規模地修改 view 動作，可以在 browser/configure.zcml 裡註冊新的顯示方式。&lt;br /&gt;使用 ReferenceField 的話，要搭配 ReferenceBrowserWidget，它要從 archetypes.referencebrowserwidget 載入，利用 allowed_types=('Document',) 之類的設定值，可以限定項目種類，利用 allow_search=True 可以決定是否提供搜尋欄位。&lt;br /&gt;想要修改 schemata 的話，是使用類似 schema.changeSchemataForField('related_category', 'reference') 這樣的語法。預設的 Dublin Core metadata 相關欄位，設定在 Products/Archetypes/ExtensibleMetadata.py 檔案裡，例如 rights 欄位的資訊：&lt;pre&gt;TextField(&lt;br /&gt;    'rights',&lt;br /&gt;    accessor="Rights",&lt;br /&gt;    default_method='defaultRights',&lt;br /&gt;    widget=TextAreaWidget(&lt;br /&gt;        label=_(u'label_copyrights', default=u'Rights'),&lt;br /&gt;        description=_(u'help_copyrights',&lt;br /&gt;                      default=u'Copyright statement.'),&lt;br /&gt;        )),&lt;/pre&gt;透過 default_method 可以指定預設的內容，例子裡的 defaultRights 就是呼叫 portal_metadata 的 listSchemas() 來比對是否存在預設值，而且在 Products/ATContentTypes/content/schemata.py 檔案裡，利用 finalizeATCTSchema() 來指派各欄位的 schemata 位置。&lt;br /&gt;另外，使用 Plone 4 的朋友，記得參考&lt;a href="http://plone.org/documentation/kb/how-to-write-templates-for-plone-4"&gt;新的網頁管理方式&lt;/a&gt;。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-6889105228443758142?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/6889105228443758142/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=6889105228443758142' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6889105228443758142'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6889105228443758142'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/11/archetype-content-customization.html' title='Archetype Content Customization'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-1502277487271229213</id><published>2010-11-05T16:22:00.006+08:00</published><updated>2011-08-01T10:08:44.953+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Transmogrifier - Import/Export Made Easy</title><content type='html'>&lt;a href=http://pypi.python.org/pypi/collective.transmogrifier&gt;collective.transmogrifier&lt;/a&gt; 是處理資料轉換或匯出匯入的工具，常被用來支援 Plone 網站資料的匯出匯入，但其他網站同樣可以引用。&lt;br /&gt;&lt;br /&gt;成功測試的是使用 Plone 3.3.5 UnfiedInstaller 來安裝，修改 buildout.cfg 內容如下：&lt;pre&gt;eggs =&lt;br /&gt;    Pillow&lt;br /&gt;    Plone&lt;br /&gt;    collective.transmogrifier&lt;br /&gt;    plone.app.transmogrifier&lt;br /&gt;    transmogrify.sqlalchemy&lt;br /&gt;    pysqlite&lt;br /&gt;    argparse&lt;br /&gt;    iw.debug&lt;br /&gt;&lt;br /&gt;zcml =&lt;br /&gt;    collective.transmogrifier-meta&lt;br /&gt;    collective.transmogrifier&lt;br /&gt;    plone.app.transmogrifier&lt;br /&gt;    transmogrify.sqlalchemy&lt;br /&gt;    iw.debug&lt;br /&gt;&lt;br /&gt;[versions]&lt;br /&gt;Pillow = 1.2&lt;br /&gt;SQLAlchemy = 0.6.5&lt;br /&gt;argparse = 1.1&lt;br /&gt;distribute = 0.6.14&lt;br /&gt;iw.debug = 0.3&lt;br /&gt;plone.app.transmogrifier = 1.1&lt;br /&gt;pysqlite = 2.6.0&lt;br /&gt;transmogrify.sqlalchemy = 1.0.1&lt;br /&gt;collective.transmogrifier = 1.2&lt;br /&gt;ipdb = 0.2&lt;br /&gt;ipython = 0.10.1&lt;/pre&gt;&lt;br /&gt;使用時要搭配設定檔，例如建立一個 import_test.cfg 檔案，來處理資料庫內容的匯入。設定檔的格式跟 buildout.cfg 類似，第一個設定區段 [transmogrifier] 先指定要用到的 pipeline 項目，也就是轉換資料的過程，需要哪些分解動作。以匯入的例子來說，資料來源是資料庫的內容，存取的資料會依序在 pipeline 傳遞，最後寫進 ZODB 並且更新 Archetype 之類的表單內容。&lt;pre&gt;[transmogrifier]&lt;br /&gt;pipeline =&lt;br /&gt;    source&lt;br /&gt;    add_type&lt;br /&gt;    add_path&lt;br /&gt;    constructor&lt;br /&gt;    schemaupdater&lt;/pre&gt;&lt;br /&gt;每個 pipeline 項目又有對應的 section 設定值，其中的 blueprint 是 named adapter，而且慣例是以 package name 來命名，完全避免撞名的問題。以 [source] 的設定值為例，它是搭配 SQLite 來讀取資料，傳遞中的資料以 dict 格式來處理，以 [add_type] 為例，它會新增一組 '_type': 'MyContentType' 資料併入 dict 裡，再往下一個 pipeline 傳遞。&lt;pre&gt;[source]&lt;br /&gt;blueprint = transmogrify.sqlalchemy&lt;br /&gt;dsn = sqlite:///database.sqlite&lt;br /&gt;query = SELECT gid, name as title, organizer, address, tel FROM test&lt;br /&gt;&lt;br /&gt;[add_type]&lt;br /&gt;blueprint = collective.transmogrifier.sections.inserter&lt;br /&gt;key = string:_type&lt;br /&gt;value = string:MyContentType&lt;br /&gt;&lt;br /&gt;[add_path]&lt;br /&gt;blueprint = collective.transmogrifier.sections.inserter&lt;br /&gt;key = string:_path&lt;br /&gt;value = python:'/MyFolder/'+str(item['gid'])&lt;br /&gt;&lt;br /&gt;[constructor]&lt;br /&gt;blueprint = collective.transmogrifier.sections.constructor&lt;br /&gt;required = True&lt;br /&gt;&lt;br /&gt;[schemaupdater]&lt;br /&gt;blueprint = plone.app.transmogrifier.atschemaupdater&lt;/pre&gt;&lt;br /&gt;資料來源並不限於資料庫，也可以是 CSV 檔案，除了匯出匯入資料外，也可以轉換文字編碼之類的動作。如果想套用 PostgreSQL 來源，安裝的模組檔案改為 &lt;a href=http://pypi.python.org/pypi/psycopg2&gt;psycopg2&lt;/a&gt;，並參考 &lt;a href=http://pypi.python.org/pypi/transmogrify.sqlalchemy&gt;transmogrify.sqlalchemy&lt;/a&gt; 的範例，改成 dsn = postgres://scott:tiger@localhost:5432/mydatabase 的設定值。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-1502277487271229213?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/1502277487271229213/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=1502277487271229213' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1502277487271229213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1502277487271229213'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/11/transmogrifier-importexport-made-easy.html' title='Transmogrifier - Import/Export Made Easy'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-2667846617941799753</id><published>2010-10-24T12:49:00.004+08:00</published><updated>2010-10-25T16:29:52.207+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Building Policy Package</title><content type='html'>客製化 Plone 網站的方式，有兩種途徑，一種是在網頁設定介面進行，稱為 &lt;a href="http://marrtw.blogspot.com/2010/06/tips-on-leadimage-and-folder-view.html"&gt;TTW (Through The Web)&lt;/a&gt;，另一種是在檔案系統裡撰寫程式碼，稱為 TTF (Through The Filesystem)。&lt;br /&gt;撰寫程式碼的好處之一，是日後可以重覆沿用設定值，Plone 利用 &lt;a href="http://marrtw.blogspot.com/2008/08/genericsetup.html"&gt;GenericSetup&lt;/a&gt; 和 &lt;a href=http://marrtw.blogspot.com/2010/03/zopeskel-local-commands.html&gt;ZopeSkel&lt;/a&gt; 等機制，協助管理員建立客製化的 Policy Package 模組。以 &lt;a href="http://marrtw.blogspot.com/2009/01/plone-installer-updates.html"&gt;UnifiedInstaller&lt;/a&gt; 安裝方式為例，開發中的模組程式碼要放在 src 目錄裡，建立工具是 &lt;a href="http://marrtw.blogspot.com/2008/03/plone-package-management-with-buildout.html"&gt;paster&lt;/a&gt; 程式，步驟如下：&lt;pre&gt;$ cd src&lt;br /&gt;$ ../bin/paster create -t plone zopenfoundry.policy&lt;br /&gt;Selected and implied templates:&lt;br /&gt;  ZopeSkel#basic_namespace  A basic Python project with a namespace package&lt;br /&gt;  ZopeSkel#plone            A project for Plone products&lt;br /&gt;&lt;br /&gt;Variables:&lt;br /&gt;  egg:      zopenfoundry.policy&lt;br /&gt;  package:  zopenfoundrypolicy&lt;br /&gt;  project:  zopenfoundry.policy&lt;br /&gt;Expert Mode? (What question mode would you like?&lt;br /&gt; (easy/expert/all)?) ['easy']:&lt;br /&gt;Version (Version number for project) ['1.0']:&lt;br /&gt;Description (One-line description of the project) ['']:&lt;br /&gt; ZOpenFoundry Policy Package&lt;br /&gt;Register Profile (Should this package register a GS Profile)&lt;br /&gt; [False]: True&lt;/pre&gt;特別注意，Register Profile 要指定為 True。&lt;br /&gt;再編輯 buildout.cfg 內容，在 develop = 指定 src/zopenfoundry.policy，並在 eggs = 和 zcml = 指定 zopenfoundry.policy，執行 bin/buildout 指令，通知系統已建立了新模組。要留意的是，試過把 package name 寫在 base.cfg 的結果，並不會讓系統找到新模組。&lt;br /&gt;想要測試模組是否能被系統存取，可用 zopepy 工具來檢查：&lt;pre&gt;$ bin/zopepy&lt;br /&gt;&lt;br /&gt;&gt;&gt;&gt; from zopenfoundry import policy&lt;br /&gt;&gt;&gt;&gt;&lt;/pre&gt;沒有發現 ImportError 的話，就是成功了。&lt;br /&gt;接著，要在 profiles 目錄裡建立設定檔，如果 profiles 目錄還未被建立，可以手動完成：&lt;pre&gt;$ cd src/zopenfoundry.policy/zopenfoundry/policy&lt;br /&gt;$ mkdir profiles&lt;br /&gt;$ mkdir profiles/default&lt;/pre&gt;在 profiles/default 目錄裡建立 properties.xml 檔案，內容如下：&lt;pre&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;site&amp;gt;&lt;br /&gt; &amp;lt;property name="title"&amp;gt;ZOpenFoundry Site Policy&amp;lt;/property&amp;gt;&lt;br /&gt; &amp;lt;property name="description"&amp;gt;Welcome to ZOpenFoundry Site&amp;lt;/property&amp;gt;&lt;br /&gt;&amp;lt;/site&amp;gt;&lt;/pre&gt;至此，就可以重啟系統，到 Plone Site Setup 把模組啟用。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-2667846617941799753?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/2667846617941799753/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=2667846617941799753' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2667846617941799753'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2667846617941799753'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/10/building-policy-package.html' title='Building Policy Package'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-5006822860081201425</id><published>2010-10-20T17:28:00.003+08:00</published><updated>2010-10-20T18:01:05.168+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Permission to Search Member</title><content type='html'>預設在 Plone 的 Users 資料夾裡，未登入的使用者無法看到會員資訊，而被通知 You are not allowed to list portal members. 訊息，這是因為 Anonymous User 沒有 List portal members 的權限，處理方式之一，是到 ZMI 的 Security tab 調整。&lt;br /&gt;&lt;a href="http://picasaweb.google.com.tw/lh/photo/YDgCmlXqR9qnz9r0q-Txfw?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_BESgcgeL9eA/TL65nS0_HXI/AAAAAAAACak/TyQ9KImyqt4/s800/ListPortalMember.png" height="256" width="738" /&gt;&lt;/a&gt;&lt;br /&gt;如果 Plone Site Setup 裡有選項，讓管理員能夠設定的話，會更理想。關於權限的基本說明，可參考 &lt;a href=http://www.sixfeetup.com/blog/basic-roles-and-permissions-in-plone&gt;Basic Roles and Permissions in Plone&lt;/a&gt;。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-5006822860081201425?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/5006822860081201425/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=5006822860081201425' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5006822860081201425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5006822860081201425'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/10/permission-to-search-member.html' title='Permission to Search Member'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_BESgcgeL9eA/TL65nS0_HXI/AAAAAAAACak/TyQ9KImyqt4/s72-c/ListPortalMember.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-6749512862056948292</id><published>2010-10-07T18:29:00.003+08:00</published><updated>2011-05-03T09:41:29.377+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>EEA Faceted Navigation</title><content type='html'>&lt;a href="http://plone.org/products/eea.facetednavigation"&gt;Faceted Navigation (FacetedNav)&lt;/a&gt; 是 Plone 顯示搜尋結果的新方式，以搜尋結果為中心，使用者可以在週圍選擇查詢條件，結合 jQuery 後，它能夠更快速地回應搜尋結果。&lt;br /&gt;&lt;br /&gt;完成測試的環境是 &lt;a href=http://svn.eionet.europa.eu/repositories/Zope/trunk/eea.facetednavigation/buildouts&gt;Plone 3.3.5&lt;/a&gt;，有人貢獻 &lt;a href=http://svn.eionet.europa.eu/projects/Zope/ticket/3658&gt;Plone 4 patch&lt;/a&gt;，現在也有 &lt;a href=http://svn.plone.org/svn/collective/eea.facetednavigation/trunk&gt;collective repository for Plone 4&lt;/a&gt;。&lt;br /&gt;&lt;br /&gt;可以結合 Syndication (XML) 和 Cache (memcache) 提供進階應用。開發人員來自 EEA (European Environment Agency) 是歐盟提供環保政策資訊的單位，已有生產&lt;a href=http://svn.eionet.europa.eu/projects/Zope/browser/trunk&gt;許多 Plone 模組&lt;/a&gt;。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-6749512862056948292?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/6749512862056948292/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=6749512862056948292' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6749512862056948292'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6749512862056948292'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/10/eea-faceted-navigation.html' title='EEA Faceted Navigation'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-4412778671913050755</id><published>2010-09-25T15:23:00.001+08:00</published><updated>2010-09-25T15:28:18.335+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>BLOB Support for Plone</title><content type='html'>經過&lt;a href=http://plone.org/events/sprints/past-sprints/snow-sprint3/large-file-handling&gt;一番努力&lt;/a&gt;，&lt;a href=http://marrtw.blogspot.com/2010/09/features-and-tips-for-plone-4.html&gt;Plone 4&lt;/a&gt; 已預設支援 blob 大型檔案，像是 Image 和 File 這兩個內容型別，上傳後的檔案，會存放在 filesystem 裡，而不再是 ZODB 了。例如上傳一個 4MB 的圖檔，到 zinstance/var/blobstorage 目錄裡，可以發現這個檔案實際存放於此，至於這個圖檔的 metadata 或 history 資訊，仍然是存在 ZODB 裡。&lt;pre&gt;$ ls -l var/blobstorage/0x00/0x00/0x00/0x00/0x00/0x00&lt;br /&gt;  /0x0f/0xa9/0x03891a0bd54aebbb.blob&lt;br /&gt;-r-------- 1 marr marr 4815658 2010-09-25 12:27&lt;br /&gt;  0x03891a0bd54aebbb.blob&lt;br /&gt;$ ls -l var/blobstorage/0x00/0x00/0x00/0x00/0x00/0x00&lt;br /&gt;  /0x0f/0xb2/0x03891a0be6c98b00.blob&lt;br /&gt;-r-------- 1 marr marr   20868 2010-09-25 12:27&lt;br /&gt;  0x03891a0be6c98b00.blob&lt;/pre&gt;內建的內容型別可以立即享受上述的功能，舊版的 Archetype 內容型別，必須要先完成昇級才行。昇級的主要步驟，是使用 &lt;a href=http://pypi.python.org/pypi/archetypes.schemaextender&gt;schema extender&lt;/a&gt; 把 FileField 換成 BlobField，再利用 &lt;a href=http://pypi.python.org/pypi/plone.app.blob&gt;plone.app.blob&lt;/a&gt; 的 migrate 工具程式來執行昇級。細節可參考&lt;a href=http://plone.org/products/example.blobattype&gt;範例&lt;/a&gt;。&lt;br /&gt;&lt;br /&gt;使用 &lt;a href=http://marrtw.blogspot.com/2010/01/dexterity-vs-archetypes.html&gt;Dexterity&lt;/a&gt; 的場合，可參考 &lt;a href=http://plone.org/products/dexterity/documentation/manual/developer-manual/advanced/files-and-images&gt;developer manuanl 的範例&lt;/a&gt;。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-4412778671913050755?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/4412778671913050755/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=4412778671913050755' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4412778671913050755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4412778671913050755'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/09/blob-support-for-plone.html' title='BLOB Support for Plone'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-5903676736589553232</id><published>2010-09-14T22:16:00.003+08:00</published><updated>2010-09-25T15:23:46.830+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Features and Tips for Plone 4</title><content type='html'>前陣子 &lt;a href=http://plone.org/products/plone&gt;Plone 4&lt;/a&gt; 正式推出，由於使用 Python 2.6，&lt;a href=http://www.netsight.co.uk/blog/2010/1/19/plone-4-speed-in-real-life&gt;執行效率大幅提昇&lt;/a&gt;，&lt;a href=http://plone.org/products/plone/features/massively-improved-handling-of-large-files-media&gt;支援 blob 大型檔案&lt;/a&gt;，有效改善 ZODB 過度成長的問題，預設搭配 &lt;a href=http://plone.org/products/plonetheme.sunburst&gt;SunBurst&lt;/a&gt; 佈景主題，以及 &lt;a href=http://plone.org/products/tinymce&gt;TinyMCE&lt;/a&gt; 編輯器，操作更加容易。想要知道&lt;a href=http://davisagli.com/blog/ten-lesser-known-improvements-in-plone-4&gt;更多改進之處&lt;/a&gt;，不妨直接&lt;a href=http://plone.org/products/plone&gt;下載安裝&lt;/a&gt;，或是到 &lt;a href=http://plone-demo.sixfeetup.com/&gt;demo site&lt;/a&gt; 親身體驗。&lt;br /&gt;&lt;br /&gt;與開發人員有關的調整，包括 &lt;a href=http://marrtw.blogspot.com/2010/05/developer-options-for-plone-4.html&gt;develop.cfg&lt;/a&gt; 的使用，可參考 &lt;a href=http://www.packtpub.com/article/creating-theme-package-zopeSkel&gt;Creating a theme package with ZopeSkel&lt;/a&gt; 的範例，另外要留意的是，&lt;a href=http://vincentfretin.ecreall.com/articles/my-translation-doesnt-show-up-in-plone-4&gt;訊息檔的翻譯方式&lt;/a&gt;也有新方式。這些調整，其實也是為了繼續朝 &lt;a href=http://www.cmswire.com/news/topic/plone+5&gt;Plone 5&lt;/a&gt; 前進。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-5903676736589553232?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/5903676736589553232/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=5903676736589553232' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5903676736589553232'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5903676736589553232'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/09/features-and-tips-for-plone-4.html' title='Features and Tips for Plone 4'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-1055760338365024100</id><published>2010-08-22T11:55:00.005+08:00</published><updated>2010-08-22T22:00:55.827+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Ploneboard 2.1 with Plone 3.3.5</title><content type='html'>雖然 &lt;a href="http://marrtw.blogspot.com/2009/12/plone-4-preview.html"&gt;Plone 4.0&lt;/a&gt; 近期就要正式問世，最近還是先搭配 Plone 3.3.5 安裝了 &lt;a href="http://marrtw.blogspot.com/2009/05/ploneboard-migration.html"&gt;Ploneboard&lt;/a&gt; 2.1b2。執行 &lt;a href="http://marrtw.blogspot.com/2009/08/disply-portlets-where-you-want.html"&gt;buildout 安裝過程&lt;/a&gt;沒問題，但在 quick install 過程遇到 &lt;a href="http://plone.org/products/ploneboard/issues/201"&gt;AttributeError: portal_placeful_workflow&lt;/a&gt; 的錯誤訊息，原因在於 CMFPlacefulWorkflow 是它的相依模組，但沒有自動隨之啟用，解決方法很簡單，就是手動先把 CMFPlacefulWorkflow 啟用，再啟用 Ploneboard 就行，這個動作也會順便把 SimpleAttachment 啟用。正式的解法已寫成 &lt;a href="http://svn.plone.org/svn/collective/Products.Ploneboard"&gt;trunk&lt;/a&gt;，訊息翻譯的成果也要放在這裡。&lt;br /&gt;&lt;a href="http://picasaweb.google.com.tw/lh/photo/eztwTiIEblzs7xHxpJhadQ?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_BESgcgeL9eA/THEo2QMMneI/AAAAAAAACRo/mKHhjQ2BD2o/s800/ploneboard01.png" /&gt;&lt;/a&gt;&lt;br /&gt;選擇想要新增討論區的目錄，以首頁為例，從新增項目的下拉選單找到 Message Board。&lt;br /&gt;&lt;a href="http://picasaweb.google.com.tw/lh/photo/Fv8Fqohbm1btjITgLzFppw?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_BESgcgeL9eA/THEo2408f-I/AAAAAAAACRs/PNZtm9y0Fpc/s800/ploneboard02.png" /&gt;&lt;/a&gt;&lt;br /&gt;設定討論區的基本資料，包括標題、摘要描述、分類關鍵詞等。&lt;br /&gt;&lt;a href="http://picasaweb.google.com.tw/lh/photo/mdy7P2ldQ9-hjVo0Ax0u0w?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_BESgcgeL9eA/THEo274-6II/AAAAAAAACRw/2sdOyNzuOyg/s800/ploneboard03.png" /&gt;&lt;/a&gt;&lt;br /&gt;討論區的基本樣貌已經具備，接著，點選 Add Forum 就可新增討論板。&lt;br /&gt;&lt;a href="http://picasaweb.google.com.tw/lh/photo/ISJXPh35xniTATyk5PiWmw?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_BESgcgeL9eA/THEo263Lf5I/AAAAAAAACR0/gO-X4akuGfg/s800/ploneboard04.png" /&gt;&lt;/a&gt;&lt;br /&gt;設定討論板的資料，包括標題、摘要描述、分類關鍵詞、附件檔案最大數量等。&lt;br /&gt;&lt;a href="http://picasaweb.google.com.tw/lh/photo/_Owwe1R1vBlKz2ZrA54YCA?feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_BESgcgeL9eA/THEo3PhfBII/AAAAAAAACR4/OhFSUuKJkeo/s800/ploneboard05.png" /&gt;&lt;/a&gt;&lt;br /&gt;討論板的預設狀態是 Requite membership to post，表示註冊會員在登入系統後才能張貼討論文章，新的討論主題稱為 conversation。&lt;br /&gt;&lt;a href="http://picasaweb.google.com.tw/lh/photo/0cpMMVfantjCopWBe4dQ4g?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_BESgcgeL9eA/THErS9miToI/AAAAAAAACSU/7PKQ4d3y59U/s800/ploneboard06.png" /&gt;&lt;/a&gt;&lt;br /&gt;設定討論主題的資料，包括標題、內文、附件檔案等。&lt;br /&gt;&lt;a href="http://picasaweb.google.com.tw/lh/photo/HwIY1GwvpSh1NNWf9_G5Hw?feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_BESgcgeL9eA/THErS67Vr1I/AAAAAAAACSY/2C6E3nnqO24/s800/ploneboard07.png" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-1055760338365024100?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/1055760338365024100/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=1055760338365024100' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1055760338365024100'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1055760338365024100'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/08/ploneboard-21-with-plone-335.html' title='Ploneboard 2.1 with Plone 3.3.5'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_BESgcgeL9eA/THEo2QMMneI/AAAAAAAACRo/mKHhjQ2BD2o/s72-c/ploneboard01.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-2628230945931383911</id><published>2010-08-16T16:15:00.003+08:00</published><updated>2010-08-16T16:35:37.082+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>bzr Tip: These branches have diverged</title><content type='html'>&lt;a href="http://bazaar-vcs.org/"&gt;Bazaar&lt;/a&gt; 是搭配 &lt;a href="http://launchpad.net/"&gt;Launchpad&lt;/a&gt; 所用的管理工具，屬於分散式版本管理方式，由 &lt;a href="http://canonical.com/"&gt;Canonical&lt;/a&gt; 公司支援發展。&lt;br /&gt;最近使用 bzr 2.0.1 的經驗，遇到 "bzr: ERROR: These branches have diverged. Use the missing command to see how. Use the merge command to reconcile them." 訊息，有&lt;a href=https://lists.ubuntu.com/archives/bazaar/2007q1/023985.html&gt;撇步&lt;/a&gt;表示可以使用 bzr pull --overwrite 指令，再把 patch 重新擺回去。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-2628230945931383911?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/2628230945931383911/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=2628230945931383911' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2628230945931383911'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2628230945931383911'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/08/bzr-tip-these-branches-have-diverged.html' title='bzr Tip: These branches have diverged'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-9122858600028176427</id><published>2010-08-08T20:43:00.003+08:00</published><updated>2010-08-08T21:28:48.623+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Plone Theme Resource Customizer</title><content type='html'>想要更換 Plone 網站的 logo 或 CSS 設定值，傳統方式是到&lt;a href=http://marrtw.blogspot.com/2010/04/plone3-theme.html&gt;後台介面&lt;/a&gt; (也就是 Zope Management Interface, ZMI) 進行設定，現在由前台介面 (也就是 Plone Site Setup) 也可以搞定了。&lt;br /&gt;&lt;br /&gt;安裝 &lt;a href=http://pypi.python.org/pypi/plone.app.themeeditor&gt;plone.app.themeeditor&lt;/a&gt; 後，原本要到 portal_skins 或 portal_view_customizations 才能編輯的佈景主題資源檔案，現在都可以在 Plone Site Setup 裡統一進行修改，操作方式請見&lt;a href=http://davisagli.com/blog/introducing-the-plone-resource-customizer&gt;教學影片&lt;/a&gt;，甚至可以&lt;a href=http://davisagli.com/blog/in-browser-integration-testing-with-windmill&gt;搭配 windmill 進行測試工作&lt;/a&gt;。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-9122858600028176427?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/9122858600028176427/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=9122858600028176427' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/9122858600028176427'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/9122858600028176427'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/08/plone-theme-resource-customizer.html' title='Plone Theme Resource Customizer'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7391132995490752798</id><published>2010-08-05T17:03:00.004+08:00</published><updated>2010-08-05T17:28:37.517+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>How Sahana Eden Uses Launchpad</title><content type='html'>幾天前看了 &lt;a href="https://launchpad.net/+tour"&gt;launchpad 的介紹&lt;/a&gt;，網頁講得像是天下無敵，因此就問 &lt;a href=http://socialsourcecommons.org/profile/flavour&gt;Fran&lt;/a&gt; 有打算完全使用 launchpad 來開發嗎? Fran 表示 launchpad 的 &lt;a href=https://launchpad.net/+tour/branch-hosting-tracking&gt;branch&lt;/a&gt; 支援很棒，但 ticket 並不能搭配 branch 來排序，如果 branch 數量不多是夠用，但 branch 數量多時，就亂到難以管理。&lt;br /&gt;因此 &lt;a href=http://marrtw.blogspot.com/2010/05/web2py-i18n-translation-tool.html&gt;Sahana Eden&lt;/a&gt; 目前只使用 bzr 及 launchpad 來管理 branch，但使用 &lt;a href="http://trac.edgewall.org/"&gt;trac&lt;/a&gt; 來管理 ticket，另外 trac 本來就有 wiki 能整理文件。&lt;br /&gt;還有一點，Eden 借用 lauchpad 的 &lt;a href=https://blueprints.launchpad.net/&gt;BluePrint&lt;/a&gt; 概念，但直接在 trac wiki 上撰寫 BluePrint。&lt;br /&gt;或許日後 launchpad 會改善上述的排序問題，&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7391132995490752798?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7391132995490752798/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7391132995490752798' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7391132995490752798'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7391132995490752798'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/08/how-sahana-eden-uses-launchpad.html' title='How Sahana Eden Uses Launchpad'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-233415520724048039</id><published>2010-06-16T08:58:00.003+08:00</published><updated>2010-07-23T23:02:30.989+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Setting Random Times with zope.sendmail</title><content type='html'>This is an abstract from &lt;a href=http://toutpt.wordpress.com/2010/06/15/how-to-add-a-random-time-between-two-emails-sent-from-zope-sendmail&gt;How to add a random time between two emails sent from zope.sendmail&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;需要搭配 Plone 架設 newsletter 的場合，可以使用 &lt;a href=http://pypi.python.org/pypi/collective.dancing&gt;collective.dancing&lt;/a&gt;，它能夠管理訂戶資料，並利用 &lt;a href=http://pypi.python.org/pypi/zope.sendmail&gt;zope.sendmail&lt;/a&gt; 協助自動發信。如果訂戶數量過大，批次發信可能會被列入黑名單，此時，試試設定亂數時點，來處理這樣的問題。方式是撰寫 plone.smtp 程式碼：&lt;pre&gt;import random&lt;br /&gt;import time&lt;br /&gt;&lt;br /&gt;from zope.sendmail import mailer&lt;br /&gt;&lt;br /&gt;class SMTP(mailer.SMTPMailer):&lt;br /&gt;    """Override SMTPMailer to let a random time"""&lt;br /&gt;&lt;br /&gt;    def send(self, fromaddr, toaddrs, message):&lt;br /&gt;        time.sleep(random.uniform(0,2))&lt;br /&gt;        super(SMTP, self).send(fromaddr, toaddrs, message)&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-233415520724048039?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/233415520724048039/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=233415520724048039' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/233415520724048039'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/233415520724048039'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/06/setting-random-times-with-zopesendmail.html' title='Setting Random Times with zope.sendmail'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7931245597071984983</id><published>2010-06-07T14:26:00.003+08:00</published><updated>2010-06-07T20:24:34.624+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Tips on LeadImage and Folder View</title><content type='html'>Folder 預設的標準顯示方式，如果覺得單調，想要增加圖示，可以藉助於 &lt;a href="http://plone.org/products/content-lead-image"&gt;Content Lead Image&lt;/a&gt;。&lt;br /&gt;&lt;br /&gt;&lt;img src=http://plone.org/documentation/manual/plone-3-user-manual/managing-content/folderviewstandard.png&gt;&lt;br /&gt;&lt;br /&gt;使用 &lt;a href="http://marrtw.blogspot.com/2009/08/disply-portlets-where-you-want.html"&gt;buildout 新增擴充模組的方法&lt;/a&gt;，就可以安裝 collective.contentleadimage，在 Site Setup 裡可以指定使用 lead image 的 content type，包括 Folder。指定後，在 Folder 的 display 選項裡，就看得到 Folder lead-image view。&lt;br /&gt;&lt;br /&gt;如果逐行顯示目錄的方式，還是覺得單調，想要改成兩欄式，可以在 &lt;a href="http://marrtw.blogspot.com/2010/04/plone3-theme.html"&gt;ploneCustom.css&lt;/a&gt; 裡調整 vevent 的 class 設定值。&lt;pre&gt;.vevent {&lt;br /&gt;  float: left;&lt;br /&gt;  height: 137px;&lt;br /&gt;  width: 42%;&lt;br /&gt;  margin-left: 4em;&lt;br /&gt;}&lt;/pre&gt;如果發現 History 的位置亂掉，可以到 ZMI portal_view_customizations 修改 plone.belowcontentbody.contenthistory (zope.interface.interface-plone.belowcontentbody.contenthistory)，在上方新增一行：&lt;pre&gt;&amp;lt;div class="visualClear"&amp;gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;以上是 Through The Web (TTW) 的修改方式，如果要在程式碼裡調整，可以參考 &lt;a href="http://plonemanual.twinapex.fi/content/dynamic_views.html"&gt;Plone Manual dynamic views 的說明&lt;/a&gt;。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7931245597071984983?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7931245597071984983/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7931245597071984983' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7931245597071984983'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7931245597071984983'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/06/tips-on-leadimage-and-folder-view.html' title='Tips on LeadImage and Folder View'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-3231389501963745515</id><published>2010-05-24T12:27:00.002+08:00</published><updated>2010-05-24T12:30:47.121+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Developer Options for Plone 4</title><content type='html'>從 Plone 4 beta 版本開始，buildout.cfg 裡關於&lt;br /&gt;&lt;a href=http://www.stevemcmahon.com/steves-blog/develop-cfg&gt;開發者的設定值&lt;/a&gt;，被獨立放在 develop.cfg 檔案裡，如此一來，paster 和 ZopeSkel 之類的工具，預設就不會馬上安裝，要額外執行指令，才會安裝開發工具：&lt;pre&gt;bin/buildout -c develop.cfg&lt;/pre&gt;安裝開發工具後，還可以&lt;a href=http://my-experiments-with-plone.blogspot.com/2010/05/collectivemockmailhost.html&gt;測試寄送通知信&lt;/a&gt;的工作，這類開發測試的工作獨立出來後，可以進一步簡化一般使用者的安裝步驟。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-3231389501963745515?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/3231389501963745515/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=3231389501963745515' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3231389501963745515'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3231389501963745515'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/05/developer-options-for-plone-4.html' title='Developer Options for Plone 4'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-847098268279229926</id><published>2010-05-22T12:12:00.005+08:00</published><updated>2010-05-24T21:28:37.962+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>web2py I18N Translation Tool</title><content type='html'>&lt;a href="http://www.web2py.com/"&gt;web2py&lt;/a&gt; 是個類似 Django 或 Ruby on Rails 的 &lt;a href="http://marrtw.blogspot.com/2008/06/how-do-python-and-rubyonrails-relate.html"&gt;web application framework&lt;/a&gt;，使用案例之一是 &lt;a href="http://eden.sahanafoundation.org/"&gt;Sahana Eden&lt;/a&gt;，Sahana 是一套&lt;a href="http://sahana.tw/"&gt;災難管理系統&lt;/a&gt;，最早以 PHP 語言開發，後來有 Python 語言的分支版本，稱為 Eden，便是採用 web2py 開發架構。&lt;br /&gt;web2py 以 Python dictionary 為基礎，有個處理介面訊息翻譯的內部引擎，在管理頁面就可以編輯訊息檔內容，檔案放在 applications 目錄裡專案的 languages 目錄。不過，要是想利用 PO 檔來管理訊息翻譯，就得搭配 &lt;a href="http://translate.sourceforge.net/wiki/toolkit/py2web2po"&gt;web2py2po&lt;/a&gt; 工具，原始碼在 &lt;a href="http://pub.nursix.org/translate/web2py2po.tar.gz"&gt;nursix&lt;/a&gt; 或 &lt;a href="http://translate.svn.sourceforge.net/viewvc/translate/src/trunk/translate/convert/"&gt;sourceforge&lt;/a&gt; 上。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-847098268279229926?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/847098268279229926/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=847098268279229926' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/847098268279229926'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/847098268279229926'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/05/web2py-i18n-translation-tool.html' title='web2py I18N Translation Tool'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-1342917824254801014</id><published>2010-05-04T11:25:00.004+08:00</published><updated>2010-05-04T11:51:44.471+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Get Id of Items</title><content type='html'>My customized &lt;a href="http://marrtw.blogspot.com/2010/01/dexterity-vs-archetypes.html"&gt;content types&lt;/a&gt; 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 &lt;a href="http://plone.org/documentation/glossary/zpt"&gt;page template&lt;/a&gt;:&lt;pre&gt;&amp;lt;div tal:define="itemId context/getId;&lt;br /&gt;                 itemIdUpper python: itemId.upper()"&amp;gt;&lt;br /&gt;&amp;lt;a href=""&lt;br /&gt; tal:attributes="href python: 'http://another.site.com/some.php?k='+itemIdUpper;&lt;br /&gt;                 title itemIdUpper;"&amp;gt;Show in Another App&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-1342917824254801014?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/1342917824254801014/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=1342917824254801014' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1342917824254801014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1342917824254801014'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/05/get-id-of-items.html' title='Get Id of Items'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-836096414441247185</id><published>2010-04-30T10:12:00.007+08:00</published><updated>2010-08-27T00:01:13.819+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Plone 4 Way to Get Icon</title><content type='html'>&lt;a href="http://marrtw.blogspot.com/2010/04/plone-4-upgrading.html"&gt;昇級 Plone 4&lt;/a&gt; 的過程，發現有些 content type icon 沒有顯示，在 log 裡找到線索：&lt;pre&gt;WARNING Plone Deprecation Warning&lt;br /&gt;The icon for the 'controlpanel/DropdownConfiguration' action &lt;br /&gt;was obtained from the action icons tool. &lt;br /&gt;The action icons tool has been deprecated &lt;br /&gt;and will be removed in Plone 5. &lt;br /&gt;You should register action icons directly on the action now, &lt;br /&gt;using the 'icon_expr' setting.&lt;/pre&gt;繼續追查後，原來 &lt;a href="https://mail.zope.org/pipermail/zope-cmf/2009-November/028720.html"&gt;icon 存取方式已經改變&lt;/a&gt;，在 &lt;a href="https://mail.zope.org/pipermail/zope-cmf/2009-November/028744.html"&gt;CMF 2.2.0-alpha&lt;/a&gt; 正式引入&lt;a href="http://plone.org/documentation/manual/upgrade-guide/version/upgrading-plone-3-x-to-4.0/updating-add-on-products-for-plone-4.0/the-action-icons-tool-portal_actionicons-has-been-deprecated"&gt;新的方式&lt;/a&gt;，而 Plone 4 使用 CMF 2.2.0 版本，自然也受到影響。如果到 ZMI 裡的 portal_actionicons 查看比較，會發現大有不同。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-836096414441247185?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/836096414441247185/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=836096414441247185' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/836096414441247185'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/836096414441247185'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/04/plone-4-way-to-get-icon.html' title='Plone 4 Way to Get Icon'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-4991802739223355087</id><published>2010-04-29T11:26:00.003+08:00</published><updated>2010-04-29T12:48:43.532+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Plone 4 Upgrading</title><content type='html'>目前 Plone 4 處於 beta 2 階段，測試 Plone 3 昇級的結果，還算順利，值得一提的訊息有：&lt;pre&gt;INFO PythonScripts Some of your Scripts have stale code cached.&lt;br /&gt;Since Zope cannot use this code, starup will be slightly slower&lt;br /&gt;until these Scripts are edited.&lt;br /&gt;You can automatically recompile all Scripts that have this problem&lt;br /&gt;by visiting /manage_addProduct/PythonScripts/recompile&lt;br /&gt;of your server in a browser.&lt;br /&gt;ERROR PortalTransforms Cannot register transform rtf_to_html,&lt;br /&gt;using BrokenTransform: Error Unable to find binary "rtf-converter"&lt;/pre&gt;工作原理和以前一樣，把 Data.fs 放到新版環境裡的 var/filestorage 再啟動，同樣有 dry run mode 提示，但整體的介面訊息變得更清楚。&lt;br /&gt;目前遇到的狀況是 &lt;a href="http://plone.org/products/webcouturier-dropdownmenu"&gt;webcouturier.dropdownmenu&lt;/a&gt; 在昇級後，造成 LocationError: (None, 'html_tag') 的錯誤訊息，來源之一是 webcouturier/dropdownmenu/browser/dropdown_recurse.pt Line 29, Column 12。事先把 dropdownmenu 模組停用，就可以避掉這問題。&lt;br /&gt;另外，可能會遇到 Default Theme 跳到 Unstyled 的狀態，只要進 plone_control_panel 或 @@skins-controlpanel 指定為佈景主題為 Plone Classic Theme 或 Sunburst Theme 就搞定。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-4991802739223355087?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/4991802739223355087/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=4991802739223355087' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4991802739223355087'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4991802739223355087'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/04/plone-4-upgrading.html' title='Plone 4 Upgrading'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-4161113179015500069</id><published>2010-04-13T13:33:00.003+08:00</published><updated>2010-04-13T14:00:06.540+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Changing Tabs with Viewlet</title><content type='html'>在 Plone 的 Navigation Settings 裡，利用 &lt;a href="http://marrtw.blogspot.com/2009/09/changing-tabs-in-plone.html"&gt;automatically generate tabs&lt;/a&gt; 的設定，可以在 portal-globalnav 區域自動產生根目錄的 tab 項目，如果只想限定目錄自動產生為 tab 項目，那就把 Generate tabs for items other than folders 取消勾選，如果某個自動產生的目錄 tab 項目，不希望它出現在 portal-globalnav 區域，除了取消自動產生 tab 項目的方式外，還可以動手修改 viewlet 來達成。&lt;br /&gt;&lt;br /&gt;上述的 portal-globalnav 名稱，是從 CSS 角度來稱呼它，用 firebug 查得到它在 CSS 檔案的位置，如果是從 viewlet 角度來稱呼它，它的名稱是 global_sections。&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lh4.ggpht.com/_BESgcgeL9eA/S8QE_SICNpI/AAAAAAAACCE/deyF4e5CmIA/s800/portal-globalnav_firebug.png"&gt;&lt;img src="http://lh4.ggpht.com/_BESgcgeL9eA/S8QE_SICNpI/AAAAAAAACCE/deyF4e5CmIA/s400/portal-globalnav_firebug.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;例如根目錄有個名稱為 tmp 的目錄，不希望它出現在 portal-globalnav 區域，到 portal_view_customizations 裡的 plone.global_sections 加個 tal:condition 就行：&lt;pre&gt;&amp;lt;ul id="portal-globalnav"&amp;gt;&lt;br /&gt;  &amp;lt;tal:tabs tal:repeat="tab view/portal_tabs"&amp;gt;&lt;br /&gt;    &amp;lt;li tal:attributes="&lt;br /&gt;      id string:portaltab-${tab/id};&lt;br /&gt;   class python:view.selected_portal_tab==tab['id']&lt;br /&gt;         and 'selected' or 'plain'"&amp;gt;&lt;br /&gt;     &amp;lt;a href="" &lt;br /&gt;        tal:condition="python: tab['id'] != 'tmp'"&lt;br /&gt;        tal:content="tab/name"&lt;br /&gt;        tal:attributes="href tab/url;&lt;br /&gt;                        title tab/description|nothing;"&amp;gt;&lt;br /&gt;     Tab Name&lt;br /&gt;     &amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/tal:tabs&amp;gt;&lt;br /&gt;&amp;lt;/ul&amp;gt;&lt;/pre&gt;想要知道有哪些 viewlet 以及它們的位置，可使用 http://localhost:8080/Plone/@@manage-viewlets 之類的網址來查詢。&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://plone.org/documentation/kb/customizing-main-template-viewlets/manage-viewlets.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 600px; height: 1626px;" src="http://plone.org/documentation/kb/customizing-main-template-viewlets/manage-viewlets.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-4161113179015500069?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/4161113179015500069/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=4161113179015500069' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4161113179015500069'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4161113179015500069'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/04/changing-tabs-with-viewlet.html' title='Changing Tabs with Viewlet'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_BESgcgeL9eA/S8QE_SICNpI/AAAAAAAACCE/deyF4e5CmIA/s72-c/portal-globalnav_firebug.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-1230068057224722743</id><published>2010-04-08T18:27:00.003+08:00</published><updated>2010-04-08T18:35:07.258+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Geek'/><title type='text'>Quotes of the Day</title><content type='html'>"The best way to keep learning after you leave school is surround yourself with the smartest people you can find."&lt;br /&gt;&lt;br /&gt;"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."&lt;br /&gt;&lt;br /&gt;"Understanding how to write well and effectively communicate the nuances of your thoughts is the most important thing you will learn in school."&lt;br /&gt;&lt;br /&gt;"I would have written a shorter letter if I'd had the time." -- Mark Twain&lt;br /&gt;&lt;br /&gt;"Great leaders are great teachers. And great teachers are great storytellers."&lt;br /&gt;&lt;br /&gt;"If you want to be a leader, you will teach. The two are inseparable."&lt;br /&gt;&lt;br /&gt;"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."&lt;br /&gt;&lt;br /&gt;"When you listen, you learn how things work as opposed to when you talk and state how you thing things work."&lt;br /&gt;&lt;br /&gt;"If you must talk, ask questions."&lt;br /&gt;&lt;br /&gt;"If we have data, let's look at the data. If all we have are opinions, let's go with mine." -- Jim Barksdale&lt;br /&gt;&lt;br /&gt;"You shouldn't be able to figure out the pecking order or org chart by looking at a product."&lt;br /&gt;&lt;br /&gt;"There is no such thing as a minor lapse of integrity." -- Tom Peters&lt;br /&gt;&lt;br /&gt;"I have no special talent. I am only passionately curious." -- Albert Einstein&lt;br /&gt;&lt;br /&gt;"Nothing great was ever achieved without enthusiasm."&lt;br /&gt;&lt;br /&gt;"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."&lt;br /&gt;&lt;br /&gt;"The perfect is the enemy of the good, from which there is no progress."&lt;br /&gt;&lt;br /&gt;"If everyone is thinking alike, then somebody isn't thinking." -- George Patton&lt;br /&gt;&lt;br /&gt;"Where there is harmony, there is no innovation."&lt;br /&gt;&lt;br /&gt;"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."&lt;br /&gt;&lt;br /&gt;"If you're the innovator, you're like a virus. The antibodies want to kill you.&lt;br /&gt; Leaders protect people from antibodies."&lt;br /&gt;&lt;br /&gt;"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."&lt;br /&gt;&lt;br /&gt;"Learn something new so that you can remember how hard it is to learn."&lt;br /&gt;&lt;br /&gt;"Teach something so you can learn."&lt;br /&gt;&lt;br /&gt;"Humility is correlated with age. Arrogance is inversely correlated with age."&lt;br /&gt;&lt;br /&gt;"As smart leader, you surround yourself with great people. You people understand what they're doing better than you do."&lt;br /&gt;&lt;br /&gt;"Good judgment comes from experience, and a lot of that come from bad judgment." -- Will Rogers&lt;br /&gt;&lt;br /&gt;"Show me a team that never makes a mistake, and I'll show you a team that has never done anything innovative."&lt;br /&gt;&lt;br /&gt;"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."&lt;br /&gt;&lt;br /&gt;"Setting an example is not the main means of influencing another, it is the only means." -- Albert Einstein&lt;br /&gt;&lt;br /&gt;"If you would not work for yourself, why do your people?"&lt;br /&gt;&lt;br /&gt;All are from &lt;a href="http://www.youtube.com/watch?v=P1T-1FqUBVY"&gt;Rules to Success by Jonathan Rosenberg&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-1230068057224722743?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/1230068057224722743/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=1230068057224722743' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1230068057224722743'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/1230068057224722743'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/04/quotes-of-day.html' title='Quotes of the Day'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-4718563120038474164</id><published>2010-04-03T23:51:00.006+08:00</published><updated>2010-11-02T06:48:44.865+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Plone3 Theme</title><content type='html'>&lt;a href="http://weblion.psu.edu/trac/weblion/wiki/PloneThreeThemingBackgroundConcepts"&gt;概念&lt;/a&gt;上 &lt;a href="http://plone.org/documentation/manual/theme-reference"&gt;Plone3 Theme&lt;/a&gt; 包括 template (page template)、stylesheet (CSS)、image 三種類型，以及 browser、skin 兩種機制。剛接觸 Plone 的朋友，先從修改 image 和 CSS 下手，比較容易入門。&lt;br /&gt;&lt;br /&gt;以 CSS 為例，開發的&lt;a href="http://plone.org/documentation/kb/how-to-make-your-css-changes-take-effect-instantly"&gt;準備工作&lt;/a&gt;之一，要將 Plone 執行模式設定成 debug mode 而非 production mode，也就是 parts/instance/etc/zope.conf 檔案的內容，要有下列的設定結果：&lt;pre&gt;debug-mode on&lt;/pre&gt;這樣 CSS 修改結果才能馬上生效，而不會受到 cache 和 compression 的影響。&lt;br /&gt;&lt;br /&gt;在 ZMI 的 plone_skins/plone_styles 裡，找得到 ploneCustom.css 檔案，預設是個&lt;a href="http://marrtw.blogspot.com/2008/09/nuplone-hacking.html"&gt;空白範例&lt;/a&gt;，可以用來測試設定效果。想要修改它，先確定下拉選單的選項是 custom，再按 Customize 鈕，這樣會把這份灰底設定檔複製到 portal_skins/custom 目錄，並打開在網頁上等待編輯。值得留意的是，僅管下列內容看似是註釋文字，但它們是會被執行的程式碼，通常不該被刪除：&lt;pre&gt;/* &amp;lt;dtml-with base_properties&amp;gt; (do not remove this :) */&lt;br /&gt;/* &amp;lt;dtml-call "REQUEST.set('portal_url', portal_url())"&amp;gt; (not this either :) */&lt;br /&gt;&lt;br /&gt;/* &amp;lt;/dtml-with&amp;gt; */&lt;/pre&gt;除此之外的 /* */ 內容都是註釋文字，要刪要留，悉聽尊便。&lt;br /&gt;&lt;br /&gt;例如，下列是一段可以讓版面置中對齊的設定範例：&lt;pre&gt;#visual-portal-wrapper {&lt;br /&gt;  background: #aaaaaa url(&amp;dtml-portal_url;/banner.jpg) no-repeat top left;&lt;br /&gt;  margin-left: auto !important;&lt;br /&gt;  margin-right: auto !important;&lt;br /&gt;  width: 900px !important;&lt;br /&gt;}&lt;/pre&gt;還有 &lt;a href="http://marrtw.blogspot.com/2009/11/custom-plone-home-page.html"&gt;sectional CSS&lt;/a&gt; 的範例，可以單獨修改首頁的顯示方式。更多的資訊，可參考 &lt;a href=https://www.packtpub.com/plone-3-theming-create-flexible-powerful-professional-templates/book&gt;Plone 3 Theming&lt;/a&gt; 書籍。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-4718563120038474164?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/4718563120038474164/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=4718563120038474164' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4718563120038474164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4718563120038474164'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/04/plone3-theme.html' title='Plone3 Theme'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-6006244186706419005</id><published>2010-03-31T18:04:00.004+08:00</published><updated>2010-03-31T18:28:52.810+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Utilizing JavaScript in Plone Without Knowing</title><content type='html'>This is an abstract from &lt;a href=http://www.sixfeetup.com/blog/2009/7/31/utilize-available-javascript-in-plone-without-knowing-javascript&gt;Utilize Available JavaScript in Plone without Knowing JavaScript&lt;/a&gt; by &lt;a href=http://ploneconf2008.eventvue.com/profile/Chrissy-Wainwright/13553&gt;Chrissy Wainwright&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;使用 Plone 預設操作介面時，有些 script 已經在默默運作，更棒的是，只要粗略了解程式碼運作的原理，不必精通 Javascript 的細節，就可以搭配 portal_skins/plone_ecmascript 裡的 .js 內容，將頁面調整成我們想要的效果。&lt;br /&gt;&lt;br /&gt;編輯內容時，想要把內容拆開在不同的 tab 頁面顯示，不必重傳網頁內容，這樣的效果稱為 FormTabbing 技巧。&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.sixfeetup.com/files/blog-images/form-tabbing.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 483px; height: 34px;" src="http://www.sixfeetup.com/files/blog-images/form-tabbing.gif" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;form_tabbing.js 檔案就是達成上述效果的工具，下列的程式碼示範它的運作方式：&lt;br /&gt;&lt;br /&gt;&amp;lt;dl class="enableFormTabbing"&amp;gt;&lt;br /&gt;  &amp;lt;dt id="fieldsetlegend-unique-id1"&amp;gt;button one&amp;lt;/dt&amp;gt;&lt;br /&gt;  &amp;lt;dt id="fieldsetlegend-unique-id2"&amp;gt;button two&amp;lt;/dt&amp;gt;&lt;br /&gt;  &amp;lt;dd id="fieldset-unique-id1"&amp;gt;content one&amp;lt;/dd&amp;gt;&lt;br /&gt;  &amp;lt;dd id="fieldset-unique-id2"&amp;gt;content two&amp;lt;/dd&amp;gt;&lt;br /&gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;&lt;br /&gt;範例中的兩個按鈕，分別由 &amp;lt;dt&amp;gt; 標籤語法所指定，按鈕的 id 要以 fieldsetlegend- 字樣開頭，按鈕所對應的內容，分別由 &amp;lt;dd&amp;gt; 標籤語法所指定，其 id 則要以 fieldset- 字樣開頭。同樣的道理，上述程式碼的原理，也可以用於 &amp;lt;form&amp;gt; 標籤裡：&lt;br /&gt;&lt;br /&gt;&amp;lt;form class="enableFormTabbing"&amp;gt;&lt;br /&gt;  &amp;lt;fieldset id="fieldset-[unique-id]"&amp;gt;&lt;br /&gt;    &amp;lt;legend id="fieldsetlegend-[same-id-as-above]"&amp;gt;Title&amp;lt;/legend&amp;gt;&lt;br /&gt;  &amp;lt;/fieldset&amp;gt;&lt;br /&gt;&amp;lt;/form&amp;gt;&lt;br /&gt;&lt;br /&gt;除此之外，還有 table_sorter.js 和 collapsiblesections.js 的範例，有興趣就去試吧。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-6006244186706419005?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/6006244186706419005/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=6006244186706419005' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6006244186706419005'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6006244186706419005'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/03/utilizing-javascript-in-plone-without.html' title='Utilizing JavaScript in Plone Without Knowing'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-2746417176323625193</id><published>2010-03-26T15:01:00.003+08:00</published><updated>2010-03-26T15:44:53.030+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>ZopeSkel local commands</title><content type='html'>&lt;a href="http://marrtw.blogspot.com/2009/01/plone-installer-updates.html"&gt;UnifiedInstaller&lt;/a&gt; 是目前開發 Plone 的建議起步方式，特別是 Plone 3 UnifiedInstaller 把 &lt;a href="http://marrtw.blogspot.com/2008/03/plone-package-management-with-buildout.html"&gt;buildout&lt;/a&gt; 整合後，系統的安裝和擴充&lt;a href="http://plone.org/documentation/kb/installing-plone-3-with-the-unified-installer"&gt;都變得更方便&lt;/a&gt;了。&lt;br /&gt;不過，最近想用 paster 來建立 archetype 專案，還是讓我遇到 local commands 找不到的問題。雖然應該不是 &lt;a href=http://n2.nabble.com/Correct-way-to-use-paster-with-buildout-td4555533.html&gt;paster_plugin 的問題&lt;/a&gt;，但在 buildout.cfg 調整下列的設定值，同樣能解決我的困擾：&lt;pre&gt;[zopeskel]&lt;br /&gt;recipe = zc.recipe.egg&lt;br /&gt;dependent-scripts = true&lt;br /&gt;eggs =&lt;br /&gt;    PasteScript&lt;br /&gt;    ZopeSkel&lt;br /&gt;initialization =&lt;br /&gt;    import paste.script.command&lt;br /&gt;    paste.script.command.system_plugins.append('zopeskel')&lt;/pre&gt;假設在 src 目錄裡，先用 paster 建立一個名稱為 myproject.mytype 的 archetype 專案，就可以在專案目錄裡執行 paster --help 看到 addcontent 等的 local command 資訊。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-2746417176323625193?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/2746417176323625193/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=2746417176323625193' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2746417176323625193'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/2746417176323625193'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/03/zopeskel-local-commands.html' title='ZopeSkel local commands'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-7627992991774709008</id><published>2010-03-26T11:37:00.003+08:00</published><updated>2010-03-26T12:07:49.932+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>ZPublisher.Conflict ConflictError</title><content type='html'>My log contains the following message:&lt;br /&gt;&lt;br /&gt;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)&lt;br /&gt;&lt;br /&gt;It seems similar to the message in &lt;a href="http://n2.nabble.com/ZPublisher-Conflict-ConflictError-td339922.html"&gt;http://n2.nabble.com/ZPublisher-Conflict-ConflictError-td339922.html&lt;/a&gt;. In my case, a package &lt;a href="http://plone.org/products/webcouturier-dropdownmenu"&gt;webcouturier.dropdownmenu&lt;/a&gt; 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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-7627992991774709008?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/7627992991774709008/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=7627992991774709008' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7627992991774709008'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/7627992991774709008'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/03/zpublisherconflict-conflicterror.html' title='ZPublisher.Conflict ConflictError'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-4406698047758804617</id><published>2010-03-26T10:06:00.003+08:00</published><updated>2010-03-26T10:23:34.447+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>ZServer uncaptured python exception</title><content type='html'>在 &lt;a href="http://aspn.activestate.com/ASPN/Mail/Message/zope-list/3492748"&gt;http://aspn.activestate.com/ASPN/Mail/Message/zope-list/3492748&lt;/a&gt; 提到，使用者臨時取消網頁存取動作，例如按了瀏覽器的停止鍵，會產生這類的訊息，一般是無須追究的情況，真想探究 exception 內容的話，要主動去接取它。如果伴隨系統資源吃緊的狀況，可用 'debug spinning zope' 關鍵字來查詢除錯技巧，或是查詢 spider 或 bot 的造訪狀況，必要時採用 iptables -A INPUT -s &lt;source-IP&gt; -j DROP 的激烈手段。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-4406698047758804617?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/4406698047758804617/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=4406698047758804617' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4406698047758804617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4406698047758804617'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/03/zserver-uncaptured-python-exception.html' title='ZServer uncaptured python exception'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-6732423627851537620</id><published>2010-03-13T10:42:00.004+08:00</published><updated>2010-03-13T11:19:27.060+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>User SubGroup and Associated Roles</title><content type='html'>Plone 3.2 版本之前的會員管理介面 prefs_users_overview 執行搜尋時，只能針對 full name 欄位進行比對，不能比對 id 欄位，另外也有 subgroup 與 role 的顯示問題，症狀主要記錄於 &lt;a href="http://dev.plone.org/plone/ticket/9317"&gt;#9317&lt;/a&gt; 和 &lt;a href="http://dev.plone.org/plone/ticket/8940"&gt;#8940&lt;/a&gt;。目前已在 &lt;a href="http://dev.plone.org/plone/changeset/33533"&gt;changeset 33533&lt;/a&gt; 完成修訂，並整理出新的管理介面。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-6732423627851537620?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/6732423627851537620/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=6732423627851537620' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6732423627851537620'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/6732423627851537620'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/03/user-subgroup-and-associated-roles.html' title='User SubGroup and Associated Roles'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-5137452868622960457</id><published>2010-03-08T11:42:00.002+08:00</published><updated>2010-03-08T11:52:05.949+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Fake Eggs ?</title><content type='html'>This is an abstract from &lt;a href="http://www.martinaspeli.net/articles/scrambled-eggs"&gt;Scrambled eggs&lt;/a&gt; by Martin Aspeli.&lt;br /&gt;&lt;br /&gt;藉由 &lt;a href="http://marrtw.blogspot.com/2009/06/eggs-and-plone3.html"&gt;egg&lt;/a&gt; 機制，我們可以更順暢地進行模組軟體的包裝與散佈，但是，遇到 &lt;a href="http://marrtw.blogspot.com/2009/01/plone-installer-updates.html"&gt;buildout&lt;/a&gt; 想要自動昇級某些模組時，為了滿足相依衝突時，egg 也可能帶來許多痛苦，我們會被一堆版本昇級要求資訊淹沒。&lt;br /&gt;&lt;br /&gt;由於 Plone 3.0 到 3.1 搭配的 Zope 並沒有包裝成 egg 檔案，導致 &lt;a href="http://marrtw.blogspot.com/2008/03/plone-package-management-with-buildout.html"&gt;setuptools&lt;/a&gt; 無從得知 Zope 3 模組的 egg metadata 資訊。舉例來說，如果你安裝一個相依於 zope.component 的模組，但是 setuptools 並不知道你已經有 zope.component 了，因此會試著下載最新版的 zope.component。由於新版 zope.component 的下載動作會引發其他相依關係，很容易造成永無止境的昇級要求。&lt;br /&gt;&lt;br /&gt;為了解決上述問題，plone.recipe.zope2install 這個 recipe 預設會安裝一些 fake egg，它的效果，就像是建立一堆指到 parts/zope2/lib/python/* 的 egg。如此一來，setuptools 就能知道系統裡存在 zope.component、zope.interface 等標準模組，不需要再去下載新版檔案。&lt;br /&gt;&lt;br /&gt;常見的情況是，我們只知道需要安裝某個檔案，但不清楚所需的明確版本，因此 fake egg 會指定為 0.0 版本，除非你為特定的 recipe 額外指定它的版本號碼。也就是說，如果有個模組的相依關係是 zope.component &gt;= 3.1，setuptools 仍然會以為系統既有的 zope.component 版本過舊，將嘗試下載新的版本。&lt;br /&gt;&lt;br /&gt;那麼，模組的相依資訊被記錄於何處? 它的來源大致有兩類，一是在模組檔案的 setup.py 內容裡，二是從 buildout.cfg 檔案的 eggs 設定值尋找。相依資訊的常見形式，又可分成兩類，第一類的例子是「我需要和 zope.component 一起工作」，只指定模組名稱，而不指定模組版本，第二類的例子是「我需要和 zope.component &gt;= 3.4 版本一起工作」，同時指定模組名稱與版本條件。&lt;br /&gt;&lt;br /&gt;有時，在 setup.py 裡指定最小版本號碼是必要的，因為某些版本以上的模組，才提供我們需要的功能。當然，在 setup.py 裡明確指定「我需要和 zope.component == 3.4 版本工作」是合法的設定方式，但通常會造成日後的災難，而指定最大版本號碼的方式，例如「我需要和 zope.component &gt;=3.4, &lt;=3.4.999 版本工作」，通常也是很糟的形式。一般而言，除非有必要，永遠不要在 setup.py 裡指定「==」的版本要求，頂多使用「&lt;=」的版本形式。因為，這類不適當的設定值一旦出錯，除錯工作極度困難。&lt;br /&gt;&lt;br /&gt;另一方面，目前 setuptools 的運作方式，是找尋最新版本來下載，如果它先下載了某個模組的新版本，就會順著它的相依關係來下載其他模組，即使某個舊版本提供更合適的相依條件，setuptools 並無從得知，這會導致下列問題：&lt;br /&gt;&lt;br /&gt;* setuptools 下載過新版本的模組，和稍後模組的最大版本條件相衝突。幸好，這個問題並不難解決。&lt;br /&gt;&lt;br /&gt;* 原本 buildout 運作正常，但是有人在 PyPI 上傳新版模組，造成相依關係更新，接著你又執行 buildout 更新動作，下載了新版模組，引發更多新版模組的下載。運氣好的話，系統會通知版本衝突的訊息，運氣不好的話，表面上 build 動作完成，但執行系統時卻失敗。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-5137452868622960457?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/5137452868622960457/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=5137452868622960457' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5137452868622960457'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5137452868622960457'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/03/fake-eggs.html' title='Fake Eggs ?'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-3832472549590283807</id><published>2010-03-03T14:07:00.002+08:00</published><updated>2010-03-03T14:36:01.793+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Quote Portlet</title><content type='html'>想在 Plone 網站加上 &lt;a href="http://marrtw.blogspot.com/2009/07/quote-of-day.html"&gt;quote&lt;/a&gt; portlet 嗎? 利用 &lt;a href="http://pypi.python.org/pypi/collective.portlet.quote"&gt;collective.portlet.quote&lt;/a&gt; 輕鬆就能完成，它還有 Quote Folder 專門來放 Quote Type。不過，並不喜歡預設的方框樣版，想要改個樣子，於是動手修改兩個檔案。&lt;br /&gt;&lt;br /&gt;修改 randomquote.pt 內容如下：&lt;pre&gt;    &amp;lt;dd class="portletItem odd"&amp;gt;&lt;br /&gt;      &amp;lt;q tal:content="structure view/get_quote"&amp;gt;&lt;br /&gt;        Body text&lt;br /&gt;      &amp;lt;/q&amp;gt;&lt;br /&gt;    &amp;lt;/dd&amp;gt;&lt;br /&gt;    &amp;lt;dd class="QuoteSource"&amp;gt;&lt;br /&gt;        &amp;lt;span class="portletBottomLeft"&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;        &amp;lt;p tal:condition="not:view/has_link"&amp;gt;&lt;br /&gt;            &amp;ndash;&lt;br /&gt;            &amp;lt;tal:block replace="view/get_source"/&amp;gt;&lt;br /&gt;        &amp;lt;/p&amp;gt;&lt;br /&gt;        &amp;lt;tal:block condition="view/has_link"&amp;gt;&lt;br /&gt;            &amp;lt;a tal:attributes="href view/get_link"&amp;gt;&lt;br /&gt;               &amp;ndash;&lt;br /&gt;               &amp;lt;tal:block replace="view/get_source"/&amp;gt;&lt;br /&gt;            &amp;lt;/a&amp;gt;&lt;br /&gt;        &amp;lt;/tal:block&amp;gt;&lt;br /&gt;        &amp;lt;span class="portletBottomRight"&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;    &amp;lt;/dd&amp;gt;&lt;/pre&gt;&lt;br /&gt;新增 ploneCustom.css 內容如下：&lt;pre&gt;.QuoteSource&lt;br /&gt;{&lt;br /&gt;    border-color: #8CACBB;&lt;br /&gt;    border-width: 1px;&lt;br /&gt;    border-style: none solid none;&lt;br /&gt;    margin: 0;&lt;br /&gt;    text-align: right;&lt;br /&gt;    padding: 0.25em 1em;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;有圖有真相。&lt;table style="width:auto;"&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://picasaweb.google.com.tw/lh/photo/rsRpVkHWUr-bhzSCHQhCLw?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_BESgcgeL9eA/S438RlzopCI/AAAAAAAAB7A/k4WRSyV9TRU/s800/QuotePortlet.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-3832472549590283807?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/3832472549590283807/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=3832472549590283807' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3832472549590283807'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/3832472549590283807'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/03/quote-portlet.html' title='Quote Portlet'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_BESgcgeL9eA/S438RlzopCI/AAAAAAAAB7A/k4WRSyV9TRU/s72-c/QuotePortlet.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-5886516157203231320</id><published>2010-01-22T15:43:00.006+08:00</published><updated>2010-01-25T14:17:21.135+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Geek'/><title type='text'>Bus Rapid Transit</title><content type='html'>&lt;span style="font-style:italic;"&gt;本文取自友人 AB 轉寄來的內容，略作極小的排版修改。我支持大眾交通系統應慎重考量低耗能高效率的方案選項，盲目地 MRT 並非民眾之福。另外，據說&lt;a href="http://zh.wikipedia.org/zh-tw/%E5%98%89%E7%BE%A9%E5%85%AC%E8%BB%8A%E6%8D%B7%E9%81%8B"&gt;嘉義也有 BRT (Bus Rapid Transit)&lt;/a&gt;，但跟巴西 Curitiba 的實例相比，仍有不小差距。&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;之前，我一直覺得捷運是很棒的東西，但是我這幾年再也不這麼認為。&lt;br /&gt;&lt;br /&gt;先講公視獨立特派員上週的報導：1800億的夢（高雄捷運）&lt;br /&gt;線上觀看：&lt;a href="http://www.peopo.org/innews/post/49974"&gt;http://www.peopo.org/innews/post/49974&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;非常值得花時間看完的報導。&lt;br /&gt;&lt;br /&gt;不過我已經看到有人評論：「搞捷運 眼光要看遠」&lt;br /&gt;&lt;br /&gt;這些論點看似有理，但是其所謂的捷運，都只侷限在台灣目前獨立於路面外的軌道捷運（高架或隧道）。&lt;br /&gt;&lt;br /&gt;請看看上述影片中另一種不一樣的捷運：BRT&lt;br /&gt;&lt;br /&gt;其實對於捷運的反省，我也是從這幾年才開始。&lt;br /&gt;&lt;br /&gt;首先是四年前我從《你，還在開車嗎？》這本書中，看到夏鑄九教授為這本書所寫的序提到：&lt;br /&gt;&lt;blockquote&gt;今天，一般市民或許不瞭解，台北市的財力已經無法承擔繼續興建目前這個光鮮亮麗卻不符永續城市原則、已經過了時的七Ο年代的昂貴捷運系統了。而高雄，卻因為政治原因，比照台北，亦步亦趨。這就是交通部為何不得不在城市運輸政策上終結高、中運量的捷運系統，改推輕軌的原因。我們若不另外選擇電車（或者說，有軌電車）、公車與腳踏車等運輸工具，台灣的城市交通就還是一條不歸路。&lt;br /&gt;&lt;br /&gt;—《你，還在開車嗎？》序，夏鑄九&lt;/blockquote&gt;&lt;br /&gt;而《你，還在開車嗎？》這本書也提到一種捷運的替代方案：BRT (Bus Rapid Transit) 系統：&lt;br /&gt;&lt;blockquote&gt;借用巴西的 Curitiba 的經驗，告訴我們一個公共交通系統，若是擁有專用的巴士道、底盤低矮的巴士、路邊收費站，及高於路面的登車月台，功能將不輸於任何有軌的公共運輸設施，而成本卻祇有軌道捷運的一小部份。&lt;br /&gt;—《你，還在開車嗎？》，Alan Thein Durning，p.113&lt;/blockquote&gt;&lt;br /&gt;那時候光憑文字很難想像什麼是BRT，直到後來看到公視的報導。&lt;br /&gt;&lt;br /&gt;之後，某次和蠻野心足生態協會理事長文魯彬經過台北某個捷運工地，他就指著工地說捷運不好，我就問他為什麼？捷運不是很環保嗎？他說：&lt;br /&gt;&lt;blockquote&gt;捷運也是高耗能的交通系統，只是不像汽車，污染立即可見，捷運的污染產生在林口發電場。&lt;/blockquote&gt;&lt;br /&gt;之後，我從潘翰聲的文章也讀到類似的評論：&lt;br /&gt;&lt;blockquote&gt;而捷運耗電量極大，將汽車污染物從都市轉移到鄉村區的燃煤電廠，是環境不正義的課題，二氧化碳的減量效果應有詳實的量化分析。&lt;/blockquote&gt;&lt;br /&gt;不過該文章另一個論點更點出另一個問題：&lt;br /&gt;&lt;blockquote&gt;更嚴重的是，捷運的經濟效益被誇大，本案（台北捷運南北線）所評估的效益高達六成是土地增值效益，簡直是花公家的錢幫財團炒地皮。&lt;/blockquote&gt;&lt;br /&gt;這是經濟問題，民代和民選地方首長、官員似乎都不願意告訴民眾：&lt;br /&gt;&lt;blockquote&gt;台北捷運系統每公里平均造價60多億元，以目前搭乘率的票務收入，也僅能維持台北捷運公司的操作營運，無法回收建設成本，更遑論未來還需更大一筆維修老舊車體的費用。大概估算，搭台北捷運的乘客約得到政府交叉補貼2/3的票價，即一段20元的票價，政府已補貼使用者40元，原應向乘客收60元。其他縣市想有樣學樣，要求中央政府補助興建捷運，但大眾運輸網絡都不如台北，其便利性及效益就大打折扣，真怕最終淪為載蚊子的捷運。 BRT 每公里造價約莫數千萬元，真的低廉又環保。&lt;br /&gt;&lt;a href="http://zh.wildatheart.org.tw/archives/mrtaeaeiebrtcaeece.html"&gt;http://zh.wildatheart.org.tw/archives/mrtaeaeiebrtcaeece.html&lt;/a&gt;&lt;/blockquote&gt;&lt;br /&gt;果然不幸言中：「高雄捷運通車不到兩年，已經虧掉60億」&lt;br /&gt;&lt;br /&gt;台灣最大的問題是不敢跟私人運具（汽車）搶道，所以目前還沒有任何一條捷運是利用現有路面。而公車專用道的興建，都還要接受民代質疑：是否會因為車道數量縮減而影響到汽車的行車速度？&lt;br /&gt;&lt;br /&gt;真是奇怪，要顧全到汽車不會塞車，早就是都市交通中被實證為不可行的交通策略，看看美國城市的道路面積擴張的程度，還是無法解決都市交通問題的事實即可得知。&lt;br /&gt;&lt;br /&gt;而且，讓私人運具不方便（塞車、停車問題），才會讓大家轉用大眾運輸系統，這不是很簡單的道理嗎！&lt;br /&gt;&lt;br /&gt;看著台北捷運局的&lt;a href="http://www.dorts.gov.tw/public/Attachment/91013913882.jpg"&gt;捷運願景圖&lt;/a&gt;密密麻麻的捷運線路，只希望它不會成真。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-5886516157203231320?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/5886516157203231320/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=5886516157203231320' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5886516157203231320'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/5886516157203231320'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/01/bus-rapid-transit.html' title='Bus Rapid Transit'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-318830256542227528.post-4673150397330101046</id><published>2010-01-18T17:30:00.003+08:00</published><updated>2011-04-29T21:00:05.803+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Dexterity vs Archetypes</title><content type='html'>This blog is an abstract from &lt;a href="http://plone.org/products/dexterity/documentation/faq/how-is-dexterity-related-to-archetypes"&gt;How is Dexterity related to Archetypes&lt;/a&gt; by Martin Aspeli.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://plone.org/products/dexterity/documentation/manual/developer-manual"&gt;Dexterity&lt;/a&gt; 是 &lt;a href="http://plone.org/documentation/manual/developer-manual/archetypes"&gt;Archetypes&lt;/a&gt; 的替代方案，這兩種方案都是 Plone 建構 content type 的&lt;a href="http://marrtw.blogspot.com/2009/10/plone-developer-manual.html"&gt;框架技術&lt;/a&gt;。由於 Archetypes 存在已久，是成熟的方案，而 Dexterity 是後起之秀，結合新版 Zope 技術，有些新技術能夠有效處理以往難以解決的問題。&lt;br /&gt;&lt;br /&gt;下列是幾項主要的差異：&lt;br /&gt;&lt;br /&gt;* Dexterity 結合新版 CMF、&lt;a href="http://marrtw.blogspot.com/2009/06/eggs-and-plone3.html"&gt;Zope 3&lt;/a&gt; 的技術與功能，程式碼更精簡，測試工作更能自動且完整。&lt;br /&gt;* Dexterity 具備更高的模組化程度，更容易整合 SQL 資料庫。&lt;br /&gt;* Archetypes 自有的 Schema 機制，並無法完全相容於 zope.interface 或 zope.schema 的介面。&lt;br /&gt;* Archetypes 使用 accessor 與 mutator 來處理設定值，Dexterity 則使用 attribute notation 方式來處理，因此 Archetypes 的寫法類似 context.getFirstName() 而 Dexterity 是 context.first_name 這樣的寫法。&lt;br /&gt;* Archetypes 自有的 field 和 widget 配合 content object 的脈絡來運作，並不容易被應用在獨立表單，Dexterity 則使用 &lt;a href="http://marrtw.blogspot.com/2009/03/make-plone3-forms-easy-way.html"&gt;z3c.form&lt;/a&gt; 函式庫，這是表單運作的標準工具。&lt;br /&gt;* Archetypes 並不支援新增表單，Dexterity 則透過 z3c.form 來支援，這代表它不需要用到 &lt;a href="http://www.vasudevaservice.com/documentation/archetypes-tips/using-portal-factory"&gt;portal_factory&lt;/a&gt;，執行效率能夠改善。&lt;br /&gt;* Dexterity 支援 &lt;a href=http://marrtw.blogspot.com/2010/11/title-to-id-behavior.html&gt;behavior&lt;/a&gt;，但 Archetypes 沒有直接支援。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/318830256542227528-4673150397330101046?l=marrtw.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marrtw.blogspot.com/feeds/4673150397330101046/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=318830256542227528&amp;postID=4673150397330101046' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4673150397330101046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/318830256542227528/posts/default/4673150397330101046'/><link rel='alternate' type='text/html' href='http://marrtw.blogspot.com/2010/01/dexterity-vs-archetypes.html' title='Dexterity vs Archetypes'/><author><name>TsungWei Hu</name><uri>https://profiles.google.com/102441227483829530605</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-mvYQdBmEuVw/AAAAAAAAAAI/AAAAAAAAAAA/e8vMLM8BnrE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry></feed>
