4.2 SchemaとFTI

Schemaを書きます。

example.conference/example/conference/__init__.py

from zope.i18nmessageid import MessageFactory

_ = MessageFactory("example.conference")

example.conference/example/conference/presenter.py

from five import grok
from zope import schema

from plone.directives import form, dexterity

from plone.app.textfield import RichText
from plone.namedfile.field import NamedImage

from example.conference import _

class IPresenter(form.Schema):
    """A conference presenter. Presenters can be added anywhere.
    """
    
    title = schema.TextLine(
            title=_(u"Name"),
        )
    
    description = schema.Text(
            title=_(u"A short summary"),
        )
    
    bio = RichText(
            title=_(u"Bio"),
            required=False
        )
    
    picture = NamedImage(
            title=_(u"Picture"),
            description=_(u"Please upload an image"),
            required=False,
        )

example.conference/example/conference/program.py

from five import grok
from zope import schema

from plone.directives import form, dexterity
from plone.app.textfield import RichText

from example.conference import _

class IProgram(form.Schema):
    """A conference program. Programs can contain Sessions.
    """
    
    title = schema.TextLine(
            title=_(u"Program name"),
        )
    
    description = schema.Text(
            title=_(u"Program summary"),
        )
    
    start = schema.Datetime(
            title=_(u"Start date"),
            required=False,
        )

    end = schema.Datetime(
            title=_(u"End date"),
            required=False,
        )
    
    details = RichText(
            title=_(u"Details"),
            description=_(u"Details about the program"),
            required=False,
        )

example.conference/example/conference/session.py
サンプルはMessageFactoryのインポートが抜けているので書き足します。

from five import grok
from zope import schema

from plone.directives import form, dexterity
from plone.app.textfield import RichText

from example.conference import _

class ISession(form.Schema):
    """A conference session. Sessions are managed inside Programs.                              
    """

    title = schema.TextLine(
            title=_(u"Title"),
            description=_(u"Session title"),
        )

    description = schema.Text(
            title=_(u"Session summary"),
        )

    details = RichText(
            title=_(u"Session details"),
            required=False
        )

FTI(Factory Type Information)を書きます。

example.conference/example/conference/profiles/default/types.xml

<object name="portal_types">
 <object name="example.conference.presenter" meta_type="Dexterity FTI" />
 <object name="example.conference.program" meta_type="Dexterity FTI" />
 <object name="example.conference.session" meta_type="Dexterity FTI" />
</object>

example.conference/example/conference/profiles/default/typesというディレクトリを作成して、ファイルを追加します。

example.conference/example/conference/profiles/default/types/example.conference.presenter.xml

<?xml version="1.0"?>
<object name="example.conference.presenter" meta_type="Dexterity FTI"
   i18n:domain="example.conference" xmlns:i18n="http://xml.zope.org/namespaces/i18n">
 
 <!-- Basic metadata -->
 <property name="title" i18n:translate="">Presenter</property>
 <property name="description" i18n:translate="">A person presenting sessions</property>
 <property name="content_icon">user.gif</property>
 <property name="allow_discussion">True</property>
 <property name="global_allow">True</property>
 <property name="filter_content_types">True</property>
 <property name="allowed_content_types" />
 
 <!-- schema interface -->
 <property name="schema">example.conference.presenter.IPresenter</property> 
 
 <!-- class used for content items -->
 <property name="klass">plone.dexterity.content.Item</property>
 
 <!-- add permission -->
 <property name="add_permission">cmf.AddPortalContent</property>
 
 <!-- enabled behaviors -->
 <property name="behaviors">
     <element value="plone.app.content.interfaces.INameFromTitle" />
 </property>
 
 <!-- View information -->
 <property name="default_view">view</property>
 <property name="default_view_fallback">False</property>
 <property name="view_methods">
  <element value="view"/>
 </property>
 
 <!-- Method aliases -->
 <alias from="(Default)" to="(dynamic view)"/>
 <alias from="edit" to="@@edit"/>
 <alias from="sharing" to="@@sharing"/>
 <alias from="view" to="(selected layout)"/>
 
 <!-- Actions -->
 <action title="View" action_id="view" category="object" condition_expr=""
    url_expr="string:${object_url}" visible="True">
  <permission value="View"/>
 </action>
 <action title="Edit" action_id="edit" category="object" condition_expr=""
    url_expr="string:${object_url}/edit" visible="True">
  <permission value="Modify portal content"/>
 </action>
</object>

example.conference/example/conference/profiles/default/types/example.conference.session.xml

<?xml version="1.0"?>
<object name="example.conference.session" meta_type="Dexterity FTI"
   i18n:domain="example.conference" xmlns:i18n="http://xml.zope.org/namespaces/i18n">
 
 <!-- Basic metadata -->
 <property name="title" i18n:translate="">Session</property>
 <property name="description" i18n:translate="">A session on the program</property>
 <property name="content_icon">document_icon.gif</property>
 <property name="allow_discussion">True</property>
 <property name="global_allow">False</property>
 <property name="filter_content_types">True</property>
 <property name="allowed_content_types" />
 
 <!-- schema interface -->
 <property name="schema">example.conference.session.ISession</property> 
 
 <!-- class used for content items -->
 <property name="klass">plone.dexterity.content.Item</property>
 
 <!-- add permission -->
 <property name="add_permission">cmf.AddPortalContent</property>
 
 <!-- enabled behaviors -->
 <property name="behaviors">
     <element value="plone.app.content.interfaces.INameFromTitle" />
 </property>
 
 <!-- View information -->
 <property name="default_view">view</property>
 <property name="default_view_fallback">False</property>
 <property name="view_methods">
  <element value="view"/>
 </property>
 
 <!-- Method aliases -->
 <alias from="(Default)" to="(dynamic view)"/>
 <alias from="edit" to="@@edit"/>
 <alias from="sharing" to="@@sharing"/>
 <alias from="view" to="(selected layout)"/>
 
 <!-- Actions -->
 <action title="View" action_id="view" category="object" condition_expr=""
    url_expr="string:${object_url}" visible="True">
  <permission value="View"/>
 </action>
 <action title="Edit" action_id="edit" category="object" condition_expr=""
    url_expr="string:${object_url}/edit" visible="True">
  <permission value="Modify portal content"/>
 </action>
</object>

example.conference/example/conference/profiles/default/types/example.conference.program.xml

<?xml version="1.0"?>
<object name="example.conference.program" meta_type="Dexterity FTI"
   i18n:domain="example.conference" xmlns:i18n="http://xml.zope.org/namespaces/i18n">
 
 <!-- Basic metadata -->
 <property name="title" i18n:translate="">Program</property>
 <property name="description" i18n:translate="">A conference program</property>
 <property name="content_icon">folder_icon.gif</property>
 <property name="allow_discussion">True</property>
 <property name="global_allow">True</property>
 <property name="filter_content_types">True</property>
 <property name="allowed_content_types">
     <element value="example.conference.session" />
 </property>
 
 <!-- schema interface -->
 <property name="schema">example.conference.program.IProgram</property> 
 
 <!-- class used for content items -->
 <property name="klass">plone.dexterity.content.Container</property>
 
 <!-- add permission -->
 <property name="add_permission">cmf.AddPortalContent</property>
 
 <!-- enabled behaviors -->
 <property name="behaviors">
     <element value="plone.app.content.interfaces.INameFromTitle" />
 </property>
 
 <!-- View information -->
 <property name="default_view">view</property>
 <property name="default_view_fallback">False</property>
 <property name="view_methods">
  <element value="view"/>
 </property>
 
 <!-- Method aliases -->
 <alias from="(Default)" to="(dynamic view)"/>
 <alias from="edit" to="@@edit"/>
 <alias from="sharing" to="@@sharing"/>
 <alias from="view" to="(selected layout)"/>
 
 <!-- Actions -->
 <action title="View" action_id="view" category="object" condition_expr=""
    url_expr="string:${object_url}" visible="True">
  <permission value="View"/>
 </action>
 <action title="Edit" action_id="edit" category="object" condition_expr=""
    url_expr="string:${object_url}/edit" visible="True">
  <permission value="Modify portal content"/>
 </action>
</object>

bin/buildoutして利用できるアドオンにします。

とりあえず、使えるようになりました。

ちなみに、TTWでファイルから作成したContent Typeを編集できるようになっています。

次回のハッカソンでも続けていきます。

ハッカソン後の懇親会で話をしているうちに、PyQtをやるべきだという気になってきました。
あとは、Common Lisp再開です。