Date: 2006-05-17
Tags: plone

COREBlog2用のCMFContentPanelsのViewletを作る

前回のBlogで、 CMFContentPanelsでトップ画面に複数のCOREBlog2エントリ一覧を表示 することが出来るようになりました。しかしこれだけだと何となく寂しい気がします。という無理矢理な導入で、CMFContentPanelsで使えるオリジナルViewletを作ってみましょう。

なお、今回使用したバージョンは、 CMFContentPanels-2.3COREBlog2-0.9b です。

Viewlet用PageTemplateを作る

CMFContentPanelsでは表示用のパネルをViewletと呼んでいて、CMFContentPanelsインスタンスページに複数並べることが出来ます。この方法を使って、一つのページの上半分をPloneのドキュメント、下半分にCOREBlog2のエントリ一覧表示するといった事が出来るようになります。

まずはViewletを表示するためのPageTemplateファイルを作成します。ZMIで /plone/portal_skins/custom へ移動してPageTemplateを新規に作成します。オブジェクトIDは何でも良いのですが、CMFContentPanelsの命名方法を真似て viewlet_coreblog2_list としておきます。次にこのオブジェクトの中身を作成します。とりあえず以下のようにスケルトンを用意しましょう。

<html xmlns:tal="http://xml.zope.org/namespaces/tal"
      xmlns:metal="http://xml.zope.org/namespaces/metal"
      i18n:domain="plone">

<body>

<div metal:define-macro="coreblog_contents_viewletconf">
</div>

<div metal:define-macro="coreblog_contents">
</div>

</body>
</html>

viewlet_coreblog2_listには2つのマクロを定義しています。CMFContentPanelsのドキュメントを読むと書いてあるのですが、Viewletのマクロ名を hoge とした場合、そのViewletの設定用UIのマクロ名を hoge_viewletconf とする必要があります。ここではViewletのマクロ名と設定用マクロ名は上記のように命名しています。coreblog2じゃなくてcoreblogになってますが、細かいことは気にしない方向でお願いします。

Viewletを登録する

空っぽのViewletを用意して、次はいきなりViewlet登録を行います。まずは空っぽのViewletが使えるモノかどうかを確認する意味で、実際にCMFContentPanelsで試してみようという作戦です。

設定のためにZMIで /plone/portal_contentpanels に移動すると、CMFContentPanelsで使えるViewletがずら~っと登録されています。このページの一番下に移動して、今作ったばかりの空Viewletを登録しましょう。

Id

coreblog2_list_viewlet

url

string: here/viewlet_coreblog2_list/macros/coreblog_contents

Permission

View

Category

GL:folder

Visible

On

設定値の中で、今作ったPageTemplateのIDとマクロ名が出てきています。タイトルやIDは適当に‥‥いちおうCMFContentPanelsの命名を真似て付けたつもりです。

../../_images/20060517_cpcb_1.png

これでCMFContentPanelsで上記のViewletを使えるようになったはずです。空っぽですが。Categoryを GL:folder としているので、 COREBlog2 list というViewlet名を選択できるのは、Related Contentでフォルダ型オブジェクトを選択しているときだけです。

../../_images/20060517_cpcb_2.png

とりあえず、問題が起きたときに切り分けしやすいように、ここまでの内容で動くことを確認しておいた方が良いでしょう。

表示用Viewletを書く

今回は、Ploneのフォルダの中身一覧を表示するViewletをコピーして、COREBlog2用に機能拡張したい部分だけカスタマイズします。以下で紹介するコードは、基本的にはCMFContentPanels/skins/cp_viewlets/viewlets_folder_recentのbase_portletマクロがやっている処理を真似して作ったのですが、CMFContentPanelsのPageTemplateはかなり細かく部品化されているので、共通する部分はmetal:use-macroとmetal:fill-slotを駆使して再利用することが出来ました。

<div metal:define-macro="coreblog_contents">
<style type="text/css">
.cp_cb2_header {
    border-width: 0 0 1px 0;
    border-color: blue;
    border-style: solid;
    overflow: hidden
}

.cp_cb2_title {
    color: black;
    font-weight: bold;
}

.cp_cb2_image {
    float: left;
    position: relative;
    z-index: 1;
    padding: 0 4px 0 0;
    margin: 0 0.5em 0 0;
    border-top: solid 1px LightGray;
    border-left: solid 1px LightGray;
    border-bottom: solid 2px DimGray;
    border-right: solid 2px DimGray;
    background-color: white;
    padding: 3px;
    text-align: center;
}
</style>
    <div metal:use-macro="here/viewlets_folder_recent/macros/base_portlet">
        <div metal:fill-slot="body">
            <tal:block tal:repeat="item results">

                <div class="portletContent odd"
                     tal:attributes="class python:test(path('repeat/item/even'), 'portletContent even', 'portletContent odd')">

                    <tal:block tal:define="result_url item/getURL;
                                           url string:$result_url/view;">

                        <a href="#"
                           tal:attributes="href url">

                            <p class="cp_cb2_header">
                                <img src="#"
                                     height="16"
                                     width="16"
                                     alt=""
                                     tal:on-error="structure python:path('here/linkOpaque.gif')"
                                     tal:replace="structure python:path('here/%s' % item.getIcon)"
                                     />

                                <span class="cp_cb2_title" tal:content="item/Title | item/getId">
                                  Title or Id
                                </span>

                                <span class="discreet"
                                      tal:define="modificationDate item/ModificationDate;
                                                  modificationDate python:here.toLocalizedTime(modificationDate)"
                                      tal:content="string:(${item/Creator}) $modificationDate">
                                      creator   08/19/2001 03:01 AM
                                </span>
                            </p>

                            <div class="cp_cb2_image"
                                 tal:define="cbentry_ref_tag python:context.getCBRefTag(item);"
                                 tal:condition="cbentry_ref_tag">
                                <img src="#"
                                     height="128"
                                     width="128"
                                     alt=""
                                     tal:replace="structure cbentry_ref_tag" />
                            </div>
                            <div class="formHelp">
                                <span tal:content="structure item/Description"/>
                            </div>
                        </a>

                    </tal:block>
                    <br class="visualClear" />
                    <span></span>
                </div>
            </tal:block>
        </div>
    </div>
</div>

上記コードのほとんどは CMFContentPanels/skins/cp_viewlets/content_list_macros.pt の <metal:block metal:define-macro="separate_line_description"> ブロック内をコピーしカスタマイズしました。CSSがPageTemplateに埋め込まれていてあまり美しくないので、本当は別ファイルに分離して、portal_cssとかに登録しておきたいところです。これは各自で工夫してみてください。

改造のポイントは、COREBlog2エントリに結びつけられた画像がある場合は一覧表示に画像をサムネイル表示するようにしている点です。ほかにもいろいろやってますが見た目的に大きな特徴はそれくらいです。で、関連づけられた画像を表示するために getCBRefTag というScript(Python)を呼び出しています。/plone/portal_skins/custom にgetCBRefTagを作成して、以下のような(テキトーな)スクリプトを記述します。パラメータに item を受け取るようにしましょう。

Id

getCBRefTag

parameters

item

cbentry_references = item.getObject().getRefsByKind()
try:
    cbentry_ref = cbentry_references[0]
    cbentry_ref_tag = cbentry_ref.tag(scale='thumb', css_class='referenceImage')
except:
    return None

return cbentry_ref_tag

これで表示画面は出来ました。あとは設定画面を用意すれば動くようになります。

Viewletの設定画面を作る

表示以外は変えなくても動くので、base_portletの設定画面をそのまま流用します。

<div metal:define-macro="coreblog_contents_viewletconf">
    <div metal:use-macro="here/viewlets_folder_recent/macros/base_portlet_viewletconf" />
</div>

これで設定画面も作成が完了しました。早速みてみましょう。

../../_images/20060517_cpcb_3.png

うまく表示されました。アイテムの種類については「エントリ」以外を選ぶ事は想定していないViewletではありますが、Previewしてみたところちゃんと表示されました。ところで、この設定画面は実は若干問題があります。link moreで folder default view を選択すると、Viewlet表示で「もっと...」のリンク先がフォルダコンテンツ一覧となってしまいます。COREBlog2用には、エントリ一覧等のページを表示したいところです。

Viewlet表示と言えば、カテゴリアイコンも表示したいとか、細かいことを言えばきりがないので、ここから先は各人でViewletを作成していろんなパターンのViewletが作られると良いなぁと思います。誰か作って公開してくれないかしら‥‥。

../../_images/20060517_cpcb_4.png