2013/12/23

Plone portal_css Order Matters

My Plone instances run with EEA Faceted Navigation. But they appear differently between debug mode and production mode with my custom theme. For example, after clicking on the Faceted Criteria tab, we should see Faceted Add Buttons that help assigning widgets. The problem is that Faceted Add Buttons do not appear when debug mode is set as off.

With Chrome browser element inspector, we can check CSS details and find where goes wrong. It is collective.js.jqueryui.custom.min.css missing when I run Plone in production mode.

My workaround is go to portal_css in ZMI, and move down collective.js.jqueryui.custom.min.css. The working order is ploneCustom.css, faceted_edit.css, faceted_view.css, and collective.js.jqueryui.custom.min.css.

2013/12/20

TAL Expression on Conditional Repeat

TAL (Template Attribute Language) Expressions are used in Zope Page Templates (ZPT) to allow template elements to be replaced, repeated, or omitted. The tal:repeat statement replicates a sub-tree of your document once for each item in a sequence. Here is an example to conditionally repeat and display values based on its value quantity and position.

<div tal:define="choices context/my_field|nothing"
     tal:condition="choices">
  <tal:onlyone condition="python: len(choices) == 1">
  <tal:field i18n:translate="">Primary</tal:field>:
  <span tal:replace="python: view.term_title(choices[0])" />
  </tal:onlyone>
  <tal:many condition="python: len(choices) > 1"
            repeat="choice choices">
    <tal:first condition="repeat/choice/start">
    <tal:field i18n:translate="">Primary</tal:field>:
    <span tal:replace="python: view.term_title(choice)" />
    <br />
    <tal:field i18n:translate="">Secondary</tal:field>:
    </tal:first>
    <tal:others condition="not:repeat/choice/start">
    <span tal:replace="python: view.term_title(choice)" />
    <span class="separator" tal:condition="not: repeat/choice/end">,</span>
    </tal:others>
  </tal:many>
</div>

Another common usage is to test a variable before inserting it (the first example tests for existence and truth, while the second only tests for existence):

<p tal:condition="request/message | nothing"
   tal:content="request/message">message goes here</p>

<p tal:condition="exists:request/message"
   tal:content="request/message">message goes here</p>

2013/11/27

Sprint Like Mad

荷蘭 Arnhem 是 Four Digits 公司的所在地,它們舉辦了十一月 Plone Sprint,為期五天,共 35 人參與。

活動裡,有個 Don't Break The Build 規則,會場的大螢幕持續顯示 CI server 的狀態,如果爛掉了,會把爐主的名字秀出來,然後爐主要請其他人吃蛋糕。一週的 sprint 後,多數人都增加不少肥肉。

活動結束時,還有 Party 和 Award。決定 Sprint Award 的方法,是利用分貝計,看哪個隊伍的歡呼聲最大。

技術上的成果包括: New Theme for Plone 5, New Way to Work with JavaScript: Mockup (widgets), Remove Selected Portal Tools from Plone, Make main_template More HTML5 Friendly,更多記錄在 Day 1, Day 2, Day 3, Day 4, Wrapup

2013/11/13

Plone Header I18N

On creating a new Plone instance, you will be asked to set its title and description. If localized strings are used for the title field, they are literally displayed in <head><title>Title Here</title></head>. We can not easily switch the the title strings based on a chosen language.

Maybe plone.app.multilingual will take care of this issue, as a whole multilingual story. Anyway, I try to solve this issue by manipulating viewlets.

My theme is based on Sunburst, that uses main_template.pt as the starting point. plone.htmlhead provider (viewlet manager) is where we need to hack into:

<div tal:replace="structure provider:plone.htmlhead" />

See plone.app.layout/viewlets/configure.zcml and we will find the related viewlet manager and viewlets:

<browser:viewletManager
  name="plone.htmlhead"
  provides=".interfaces.IHtmlHead"
  permission="zope2.View" ...>

<browser:viewlet
  name="plone.htmlhead.title"
  manager=".interfaces.IHtmlHead"
  class=".common.TitleViewlet" ...>

Now we know TitleViewlet in plone.app.layout/viewlets/common.py is the target:

class TitleViewlet(ViewletBase):
  index = ViewPageTemplateFile('title.pt')

  @property
  @memoize
  def page_title(self):
    '''
    Get the page title.
    '''

Each page has its site_title set as page_titleportal_title. With a little help from my friends -- zope.i18n.translate:

from zope.i18n import translate

def update(self):
  ...
  self.site_title = u"%s &mdash; %s" % (self.page_title,
    translate('portal_title',
              domain='my.theme',
              context=self.request,
              default='I18N Site'))

Finally we need my.theme.po in the locales folder:

#: Default: I18N Site
msgid "portal_title"
msgstr "中文名稱"

2013/11/09

Event Listing View

event_listing 是 plone.app.events 模組裡定義的 Browser Page,可以被指定成為 Plone Site 或 Folder 的 view method 選項,提供豐富的顯示方式。

event_listing.pt 有定義 mode 變數,預設值是 'future':

tal:define="mode request/mode|string:future;"

如果 @@event_listing 沒有指定 mode 變數值,它會再檢查 _date 變數,最後決定 mode 預設值是 'date' 或 'future'。

if self.mode is None:
    self.mode = self._date and 'day' or 'future'

點選 Past 頁籤時,它會指定 'past' 給 mode 變數,此時 header_string() 會回傳 main_msgid 的翻譯值,其他情況下,還可能搭配 sub_msgid 變數值。

頁籤的連結網址,也配合變數值來產生,例如 mode_past_url 會把 'past' 傳給 _date_nav_url():

def _date_nav_url(self, mode, datestr=''):
    return '%s?mode=%s%s' % (
        self.request.getURL(),
        mode,
        datestr and '&date=%s' % datestr or ''
    )

event_listing.pt 經由 view/mode_past_url 來指派網址值:

browser/event_listing.pt
<a class="mode_past" href=""
  tal:attributes="href view/mode_past_url"
  i18n:translate="mode_past_link">Past</a>

2013/11/05

Transmogrifier Revisited

This is detailed sample instruction to Transmogrifier in Action on Plone 4.3.2.

$ cd src
$ ../bin/zopeskel plone crgis.transmogrifier

plone: A project for Plone add-ons

This creates a Plone project (to create a Plone *site*, you probably
want to use the one of the templates for a buildout).

To create a Plone project with a name like 'plone.app.myproject'
(2 dots, a 'nested namespace'), use the 'plone_app' template.


If at any point, you need additional help for a question, you can enter
'?' and press RETURN.

Expert Mode? (What question mode would you like? (easy/expert/all)?) ['easy']:
Version (Version number for project) ['1.0']: 0.1
Description (One-line description of the project) ['']: CRGIS Transmogrifier Package
Register Profile (Should this package register a GS Profile) [False]: True
Creating directory ./crgis.transmogrifier
Replace 0 bytes with 119 bytes (0/0 lines changed; 5 lines added)
Replace 918 bytes with 1074 bytes (0/32 lines changed; 6 lines added)
------------------------------------------------------------------------------
The project you just created has local commands. These can be used from within
the product.

usage: paster COMMAND

Commands:
  addcontent  Adds plone content types to your project

For more information: paster help COMMAND
------------------------------------------------------------------------------

Update configure.zcml as follows:

<configure
  xmlns="http://namespaces.zope.org/zope"
  xmlns:five="http://namespaces.zope.org/five"
  xmlns:i18n="http://namespaces.zope.org/i18n"
  xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
  xmlns:transmogrifier="http://namespaces.plone.org/transmogrifier"
  i18n_domain="crgis.transmogrifier">

  <five:registerPackage package="." initialize=".initialize" />

  <genericsetup:registerProfile
    name="default"
    title="crgis.transmogrifier"
    directory="profiles/default"
    description="CRGIS Transmogrifier Package Extension Profile"
    provides="Products.GenericSetup.interfaces.EXTENSION"
    />

  <include package="collective.transmogrifier" />
  <include package="collective.transmogrifier" file="meta.zcml" />
  <transmogrifier:registerConfig
    name="crgis.importer.temple"
    title="CRGIS Importer for Temple Type"
    description="Transmogrifier Pipeline Config to Import Contents."
    configuration="import-temple.cfg"
    />

Update import-temple.cfg then:

[transmogrifier]
pipeline =
    csvsource
    sqlsource
    type-inserter
    path-inserter
    folders
    constructor
    schema-updater
    state-inserter
    workflow-updater
    reindex-object

[csvsource]
blueprint = collective.transmogrifier.sections.csvsource
filename = crgis.transmogrifier:temple-data.csv

[sqlsource]
blueprint = transmogrify.sqlalchemy
dsn = postgresql://USERNAME:PASSWORD@localhost:5432/DB_NAME
query = SELECT id, title, description FROM TB_NAME

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

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

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

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

[schema-updater]
blueprint = plone.app.transmogrifier.atschemaupdater

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

[workflow-updater]
blueprint = plone.app.transmogrifier.workflowupdater

[reindex-object]
blueprint = plone.app.transmogrifier.reindexobject

Note that csvsource section is for CSV source, and sqlsource section is for SQL source.

Add a file crgis/transmogrifier/profiles/default/transmogrifier.txt with these:

# this file contains transmogrifier profile names to run
# on GenericSetup transmogrifier's step
crgis.importer.temple

Update setup.py to include SQLAlchemy and psycopg2 dependency.

Finally, edit buildout.cfg:

eggs =
    plone.app.transmogrifier
    collective.transmogrifier
    transmogrify.sqlalchemy
    crgis.transmogrifier

zcml =
    plone.app.transmogrifier
    collective.transmogrifier
    transmogrify.sqlalchemy

2013/11/03

Plone5 Is On Its Way

Plone 5 正由核心團隊努力開發,目前看來已達到堪用地步,或許明年初之際,就能正式發佈。

最大的改變,是 Dexterity 成為預設的 Content Type Framework,長期挑大樑的 Archetypes 成為選項模組。這個改變屬於底層調整,對於直接使用 Plone 5 的新專案而言,操作上仍和 Plone 4 差不多。

另一個大改變,是 AJAX 架構導入 mockup 專案,它讓 Plone 與 JavaScript 模組更容易整合和測試。

像 Diazo 技術在 Plone 5 仍在努力站穩腳步,大的方向上,是期待改版的結果,能協助 JavaScript Programmer 和 Theme Designer 更容易在 Plone 專案裡工作。

範例影片: Widget DemoFolder Content Management Demo

測試 Plone 5 的過程,把看到的改版細節資訊記錄下來。舊版 plone.app.imaging 的 imaging_properties 設定值,無法在 Plone 5 出現,因為它跟 Archetypes 相依,必須進行調整,它在 control panel 的 icon 也需要調整

update on testing: https://github.com/plone/Products.CMFPlone/issues/78

$ bin/test -s Products.CMFPlone
Removing stale bytecode file /home/marr/plone5dev/src/Products.CMFPlone/Products/CMFPlone/CalendarTool.pyc
...
Running Products.CMFPlone.testing.CMFPloneLayer:Functional tests:
  Set up plone.testing.zca.LayerCleanup in 0.000 seconds.
  ...
  Running:

  Ran 28 tests with 0 failures and 0 errors in 32.189 seconds.
Running plone.app.testing.bbb.PloneTestCase:Functional tests:
  Tear down Products.CMFPlone.testing.CMFPloneLayer:Functional in 0.000 seconds.
  ...
  Set up plone.app.testing.bbb.PloneTestCaseFixture in 5.792 seconds.
  ...
  Ran 70 tests with 2 failures and 0 errors in 0.624 seconds.
Tearing down left over layers:
  Tear down zope.testing.testrunner.layer.UnitTests in 0.000 seconds.
Total: 1061 tests, 4 failures, 0 errors in 9 minutes 22.008 seconds.

跟 Archetypes 有關的模組資訊:

eggs/archetypes.schemaextender
src/Products.Archetypes
src/Products.ATContentTypes
src/plone.formwidget.recurrence