1.3. Archetypesベースの開発

一つ上に移動

このセクションではArchetypesの高度な機能をいくつか紹介します

このセクションではArchetypesの高度な機能をいくつか紹介します。これらの機能はコンテンツタイプを開発するのに便利ないくつかのツール を提供してくれるでしょう。たとえば関連づけを行ったり、新しくウィジェットを作成したり、コンテンツを変換したりといったことです。

まずは変更を加えたときにそれがどのようにしてPloneへ伝わるのかということを理解することが大切です。これまで見てきたようにプロダクトをセットアップするには様々な段階があります。プロダクトを変更したときにどの段階から始めればよいのかを理解しましょう。

スキンに何か変更を加えたときには、ただデバッグモードで動かせば変更が反映していることを確認できます。アクションやアイコンなど、portal_actionsへの変更を行った場合にはPloneをリスタートし、プロダクトを再インストールする必要があります。

スキーマを変更した場合にはPloneをリスタートするだけです。そうすれば新しいインスタンスはアップデートされるでしょう。では古いインスタン スはどうなるのでしょう?幸運にもArchetypesはそういった古いインスタンスをダイナミックに新しいスキーマへアップデートしてくれるツールを提 供してくれます。ZMIでarchetype_toolをクリックしましょう。

ユニークIDを使う

ユニークIDのコンセプトはシンプルですが、Zopeには欠けています。もともとZopeの開発者はオブジェクトへのパスを利用しようとしていまし たが、残念ながらこれは適当ではありませんでした。例えば誰かがあるドキュメントの位置を変更したとしましょう。このときそのドキュメントへのユニークな リファレンスは失われてしまいます。ユニークIDは本当に便利なツールです。ArchetypesはすべてのオブジェクトにユニークIDを生成し、そのオ ブジェクトへのリファレンスを保存します。これはuid_ctalogという別のカタログへ保存されます。

uid_catalogはZMIから見ることができます。これはAPIとインデクスを欠いているという点を除いてportal_catalogとほ とんど同じです。このカタログによってオブジェクトを得ることは非常に簡単です。オブジェクトをカタログから探すだけです。例えば以下のScript (Python)オブジェクトを使えば、既知のUIDによって登録されたオブジェクトを探すことができます。

##paramaters=objectId
results = context.uid_catalog(UID=objectId)
return results[0].getObject()

しかし本当に便利なのは既存のオブジェクト間のリファレンスを考えるときです。例えば記事に関係する1つ以上の別々の場所に存在する画像を考えてみ ましょう。これらの画像は他のユーザによって別の時間にアップロードされている可能性があります。もしかしたら画像データベースの一部かもしれません。こ れらの画像がArchetypesオブジェクトであれば、articleスキーマに以下のようにフィールドを追加します。

ReferenceField("images",
    allowed_types=("Archetype Images",),
    multivalued=1,
    ),

これでユーザはすべてのArchetype Imageオブジェクトのドラッグドロップメニューを使って、そこから画像を選ぶことができるようになります。

このスキーマの裏では、カタログを通したUIDのリファレンスが生成されています。リファレンスに関するすばらしい例がACMEというプロダクトに見ることができます(http://sf.net/projects/archetypes)。

ウィジェットを変える

何故このウィジェットはこういうふうに動作するのか、とか、何故このウィジェットはこのように見えるのか、とかいったFAQを耳にします。これに対 する答えは、そのように書かれているからだ、となります。ウィジェットはクライアントが目にするものなので、これに対する要求はしばしばクライアントによ るものです。

すべてのウィジェットはファイルシステム上にあるページテンプレートマクロによって表現されます。すなわちウィジェットを変更したり自分で書いたりするのは非常に簡単です。

viewマクロは「表示」ページで表示され、リードオンリーで、ユーザフレンドリーなアイテムビューです。editマクロは「編集」ページで表示さ れ、ユーザがデータを編集するためのページです。searchマクロは特定のコードに対応するページを集めるときに呼ばれます。これはeditマクロのよ うに見える場合もあります。stringフィールドはstringフィールドとして編集され、文字列として表示されるかもしれませんが、ドロップダウンメ ニューを使って検索されます。

例としてメールアドレスを含むstringフィールドがあり、これをクリッカブルなEメールリンクとして表示する場合を考えてみましょう。まずはマ クロを書く必要があります。今回の場合はeditマクロやsearchマクロを別個に書く必要はありません(それらも結局ただのstringですから)。 さぁそれではemail_widget.ptというページテンプレートを以下のように作成して、プロダクトのskinディレクトリへ置いておきましょう。

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

    <body>
        <div metal:define-macro="edit">
            <div metal:use-macro="here/widgets/string/macros/edit" />
        </div>

        <div metal:define-macro="search">
            <div metal:use-macro="here/widgets/string/macros/search" />
        </div>

ビューでは文字列をmailtoリンクとしてみせる必要がありますので、以下のように調整しておきます。

        <div class="field" metal:define-macro="view">
            <a href="#" tal:attributes="href string:mailto:${accessor}"
                tal:content="accessor">email</a>
        </div>
    </body>
</html>

これでページテンプレートを定義できました。あとはただマクロのテンプレート名をウィジェットに組み込むだけです。以下のコードではe-mail フィールドを通常のStringWidgetとして定義し、ここで利用するマクロをemail_templateマクロに変更しています。

StringField('email',
    validators = ('isEmail',),
    widget = StringWidget(
        label='Email',
        macro='email_template'
    )
)

この時点では既存のウィジェットのマクロを変更しただけです。完全に新しいウィジェットを作成するには新しいウィジェットを定義し、これを登録しま す。すべてのウィジェットは同じベースクラスを共有しています。以下が新しいモジュールである(ArcheExampleディレクトリの中にあ る)EmailWidget.pyとなります。

これによって新しいウィジェットが生成され、レジストリに登録されます。ウィジェットのプロパティがテンプレートの値にセットされていることに注意してください。

from Products.Archetypes.Widget import TypesWidget
from Products.Archetypes.Registry import registerWidget

class EmailWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
       'macro' : "email_template",
       'size' : '30',
       'maxlength' : '255',
       })
registerWidget(EmailWidget,
    title='String',
    description='Renders a clickable email field',
    used_for=('Products.Archetypes.Field.StringField',)
)

これをarticleに含めるためにはEmailWidgetを直接importしますが、マクロを明示的に定義する必要はありません。

from EmailWidget import EmailWidget

StringField('email',
    validators = ('isEmail',),
    widget = EmailWidget(
        label='Email',
    )
)

フォルダ風オブジェクトの開発

あなたは既にPloneでフォルダ風オブジェクトを使っていることでしょうが、もしかしたらまだこれを理解していないかもしれません。フォルダ風オ ブジェクトはフォルダやディレクトリの性質を持っており、これはすなわち個別のオブジェクトをこれに含むことができると言うことです。フォルダ風オブジェ クトには特別なことは何もありません。ただプロパティを持ったり新規のコンテンツを加えたりできるようにするために特定のベースクラスを継承しているだけ です。

フォルダ風オブジェクトはいくつかの理由でとても便利なものです。例えば個々のオブジェクトを一カ所に集めておくことができますし、標準的なユーザ インターフェイスでコンテンツを管理したりすることもできます。一般的に、フォルダはきわめてシンプルにしておき、ロジックはオブジェクトやワークフロー に組み込むべきです。しかしながら何事にも例外はつきもので、collectorとしてはCMFCollectorやPloneCollectorが例と してあげられます。これらはとても複雑なコレクタオブジェクトです。

フォルダ風オブジェクトを作成する簡単な方法は、Archetypesを使うことです。フォルダ風でないオブジェクトを生成するのに必要な仕事をす べて処理してくれるBaseContentタイプがあることはすでに見てきました。さて、実はフォルダ風オブジェクトを作成するためのものをすべて含んだ BaseFolderコンテンツタイプというものも存在します。フォルダのための特別なスキーマがあるのがわかるでしょう。フォルダ風オブジェクトは通常 descriptionも持っています。フォルダ風のコンテンツタイプを作成するためにはベースクラスとスキーマを変更するだけです。例えばきわめてシン プルなフォルダは以下のようになります:

from Products.Archetypes.public import BaseFolder, BaseFolderSchema
schema = BaseFolderSchema
class SimpleFolder(BaseFolder):
    """A simple folderish archetype"""
    schema = schema
registerType(SimpleFolder)

フォルダの中にあまりにもたくさんのコンテンツを保存しようとすると、効率が悪くなる傾向にあります(だいたい100オブジェクト)。通常のフォル ダ風オブジェクトは少数のオブジェクトを素早く扱うようにデザインされているからです。もし大量のオブジェクトを扱いたいのなら、バイナリツリーフォルダ を使うことができます。これはオブジェクトを、Pythonのディクショナリよりもより効率的なバイナリツリーとして保存します。これを使うためには BaseBTreeFolderおよびBaseBTreeFolderSchemaをインポートするだけです。プロダクトの開発に限って言えばこれらは同 じように動作しますが、そこに大量のデータを置かない限り特に違いは感じられないでしょう。私は100000以上のオブジェクトをBTreeFolder に保存していますが、いつもうまい具合に反応してくれています。

Microsoft Officeのコンテンツを処理する

WordやExcelなどのMicrosoft Officeドキュメントを処理することは、すべてのCMSがいつか直面する難問です。しかしこれはMicrosoft Officeのみならず、OpeOfficeやpdf、画像などすべてのタイプのコンテンツにもあてはまることでしょう。これらのコンテンツをウェブサイ トで扱うことはいくつかの問題を引き起こすことがよくあることが、当然のようによく知られています。これらを編集するのはとても難儀をします。なぜならド キュメントをクリックするとダウンロードしたり、もっと悪い場合にはブラウザの中でそれを開いてしまうからです。編集が終わったら今度はいろんなところを 何回もクリックしてそれをウェブサイトにアップロードしなければなりません。また、コンテンツはオンラインでありながら検索対象には含まれません。なぜな らそれらはプレーンテキストではない、カタログが理解不能なフォーマットだからです。さらにそれらはウェブフレンドリーなフォーマットではないため、コン テンツをオンラインで見ることもできません。

こういったコンテンツを編集する問題について解決策が2つあります。ユーザのほとんどがWindowsを使っているならばWebDAVを使うことが 一つの策ですが、Microsoftのウェブフォルダの実装にはやや問題があるのでこの策を採る場合には注意が必要です。もう一つの策として、 ExternalEditorはWebDAVの仕組みをうまく機能させています。PloneがExternalEditorに「これはWordファイル だ」と教えてやればWordファイルとして開くことでしょう。

Archetypesはコンテンツタイプの変換を処理するPortal Transformsという組み込みの変換パッケージを持っています。例えばあるフォーマットのファイルをHTMLへ変換し、これをカタログ化し、ユーザ インターフェイスでHTMLを表示する、といったことをすることができるのです。これはデータを変換し、その結果を読み込むための外部処理を利用すること によって行っています。例えばWindowsを使っているなら、Portal TransformsはアップロードされたWordドキュメントをCOMオブジェクトをスタートします。

このようなことが舞台裏ですべて行われています。気にかけるべきことは変換のための要件が満たされ利用可能になっているかという、ただ一つのことだけです。たくさんの異なる変換形式を扱うこともできますし、データを変換するやり方は一つではありません。

OpenOffice.orgはMicrosoftのコンテンツの華麗な変換を提供してくれているので、Microsoftでないプラットフォーム でもこれを変換することができます。wvWareを使ってもうまく変換することができます。これらの選択肢はもちろん利用可能ですが、別に特別な設定が必 要なわけではありません。これらを一度セットアップしてしまえばMicrosoftサーバを利用していたときよりも質の良い変換を得ることができます。

まずは何を変換したいのかということを考えると良いでしょう。そしてPortal Transformsのソースコードを眺めてみてください。そこに自分のやりたい変換があればそれを使えばよいのです。