17. Return to Dexterity: Moving contenttypes into Code#
In this part you will:
Move the Talk type into
ploneconf.site
Improve the schema and the FTI
Topics covered:
Content type definitions with generic setup
FTI
XML schema
more widgets
Remember the Talk content type that we created through-the-web with Dexterity? Let's move that new content type into our add-on package so that it may be installed in other sites without TTW manipulation.
Steps:
Return to the Dexterity control panel
Export the Talk Type Profile and save the file
Delete the Talk from the site before installing it from the file system
Extract the files from the exported tar file and add them to our add-on package in
profiles/default/
Note
From the buildout directory perspective that is src/ploneconf.site/src/ploneconf/site/profiles/default/
The file profiles/default/types.xml
tells Plone that there is a new content type defined in file talk.xml
.
<?xml version="1.0"?>
<object name="portal_types" meta_type="Plone Types Tool">
<property name="title">Controls the available contenttypes in your portal</property>
<object name="talk" meta_type="Dexterity FTI"/>
<!-- -*- more types can be added here -*- -->
</object>
Upon installing, Plone reads the file profiles/default/types/talk.xml
and registers a new type in portal_types
(you can find and inspect this tool in the ZMI!) with the information taken from that file.
<?xml version="1.0"?>
<object name="talk" meta_type="Dexterity FTI" i18n:domain="plone"
xmlns:i18n="http://xml.zope.org/namespaces/i18n">
<property name="title" i18n:translate="">Talk</property>
<property name="description" i18n:translate="">None</property>
<property name="icon_expr">string:${portal_url}/document_icon.png</property>
<property name="factory">talk</property>
<property name="add_view_expr">string:${folder_url}/++add++talk</property>
<property name="link_target"></property>
<property name="immediate_view">view</property>
<property name="global_allow">True</property>
<property name="filter_content_types">True</property>
<property name="allowed_content_types"/>
<property name="allow_discussion">False</property>
<property name="default_view">view</property>
<property name="view_methods">
<element value="view"/>
</property>
<property name="default_view_fallback">False</property>
<property name="add_permission">cmf.AddPortalContent</property>
<property name="klass">plone.dexterity.content.Container</property>
<property name="behaviors">
<element value="plone.dublincore"/>
<element value="plone.namefromtitle"/>
</property>
<property name="schema"></property>
<property
name="model_source"><?xml version='1.0' encoding='utf8'?>
<model xmlns:lingua="http://namespaces.plone.org/supermodel/lingua" xmlns:users="http://namespaces.plone.org/supermodel/users" xmlns:form="http://namespaces.plone.org/supermodel/form" xmlns:i18n="http://xml.zope.org/namespaces/i18n" xmlns:security="http://namespaces.plone.org/supermodel/security" xmlns:marshal="http://namespaces.plone.org/supermodel/marshal" xmlns="http://namespaces.plone.org/supermodel/schema">
<schema>
<field name="type_of_talk" type="zope.schema.Choice">
<description/>
<title>Type of talk</title>
<values>
<element>Talk</element>
<element>Training</element>
<element>Keynote</element>
</values>
</field>
<field name="details" type="plone.app.textfield.RichText">
<description>Add a short description of the talk (max. 2000 characters)</description>
<max_length>2000</max_length>
<title>Details</title>
</field>
<field name="audience" type="zope.schema.Set">
<description/>
<title>Audience</title>
<value_type type="zope.schema.Choice">
<values>
<element>Beginner</element>
<element>Advanced</element>
<element>Professionals</element>
</values>
</value_type>
</field>
<field name="speaker" type="zope.schema.TextLine">
<description>Name (or names) of the speaker</description>
<title>Speaker</title>
</field>
<field name="email" type="plone.schema.email.Email">
<description>Adress of the speaker</description>
<title>Email</title>
</field>
<field name="image" type="plone.namedfile.field.NamedBlobImage">
<description/>
<required>False</required>
<title>Image</title>
</field>
<field name="speaker_biography" type="plone.app.textfield.RichText">
<description/>
<max_length>1000</max_length>
<required>False</required>
<title>Speaker Biography</title>
</field>
</schema>
</model></property>
<property name="model_file"></property>
<property name="schema_policy">dexterity</property>
<alias from="(Default)" to="(dynamic view)"/>
<alias from="edit" to="@@edit"/>
<alias from="sharing" to="@@sharing"/>
<alias from="view" to="(selected layout)"/>
<action title="View" action_id="view" category="object" condition_expr=""
description="" icon_expr="" link_target="" url_expr="string:${object_url}"
visible="True">
<permission value="View"/>
</action>
<action title="Edit" action_id="edit" category="object" condition_expr=""
description="" icon_expr="" link_target=""
url_expr="string:${object_url}/edit" visible="True">
<permission value="Modify portal content"/>
</action>
</object>
Now our package has new configuration for Generic Setup. Generic Setup store the configiuration for the site in the folder profiles/
. This configuration is applied to your site upon installing the package. So, we'll need to reinstall it (if installed before).
Restart Plone.
Re-install ploneconf.site (deactivate and activate).
Test the type by adding an object or editing one of the old ones.
Look at how the talks are presented in the browser.
The escaped inline xml is simply too ugly to look at. You should move it to a separate file!
Create a new folder content
in the main directory (from the buildout directory perspective that is src/ploneconf.site/src/ploneconf/site/content/
). Inside add an empty file __init__.py
and a file talk.xml
that contains the real XML (copied from http://localhost:8080/Plone/dexterity-types/talk/@@modeleditor and beautified with some online XML formatter (https://www.google.com/?q=xml+formatter))
1<?xml version='1.0' encoding='utf8'?>
2 <model xmlns="http://namespaces.plone.org/supermodel/schema"
3 xmlns:form="http://namespaces.plone.org/supermodel/form"
4 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
5 xmlns:lingua="http://namespaces.plone.org/supermodel/lingua"
6 xmlns:marshal="http://namespaces.plone.org/supermodel/marshal"
7 xmlns:security="http://namespaces.plone.org/supermodel/security"
8 xmlns:users="http://namespaces.plone.org/supermodel/users">
9 <schema>
10 <field name="type_of_talk" type="zope.schema.Choice">
11 <description/>
12 <title>Type of Talk</title>
13 <values>
14 <element>Talk</element>
15 <element>Training</element>
16 <element>Keynote</element>
17 </values>
18 </field>
19 <field name="details" type="plone.app.textfield.RichText">
20 <description>Add a short description of the talk (max. 2000 characters)</description>/>
21 <max_length>2000</max_length>
22 <title>Details</title>
23 </field>
24 <field name="audience" type="zope.schema.Set">
25 <description/>
26 <title>Audience</title>
27 <value_type type="zope.schema.Choice">
28 <values>
29 <element>Beginner</element>
30 <element>Advanced</element>
31 <element>Professional</element>
32 </values>
33 </value_type>
34 </field>
35 <field name="speaker" type="zope.schema.TextLine">
36 <description>Name (or names) of the speaker</description>/>
37 <title>Speaker</title>
38 </field>
39 <field name="email" type="plone.schema.email.Email">
40 <description>Adress of the speaker</description>/>
41 <title>Email</title>
42 </field>
43 <field name="image" type="plone.namedfile.field.NamedBlobImage">
44 <description/>
45 <required>False</required>
46 <title>Image</title>
47 </field>
48 <field name="speaker_biography" type="plone.app.textfield.RichText">
49 <description/>
50 <max_length>1000</max_length>
51 <required>False</required>
52 <title>Speaker Biography</title>
53 </field>
54 </schema>
55 </model>
Now remove the ugly model_source and instead point to the new XML file in the FTI by using the property model_file
:
<property name="model_source"></property>
<property name="model_file">ploneconf.site.content:talk.xml</property>
ploneconf.site.content:talk.xml
points to a file talk.xml
to be found in the Python path ploneconf.site.content
. The __init__.py
is needed to turn the folder content
into a Python package. It is best-practice to add schemas in this folder, and in later chapters you will add new types with pythons-schemata in the same folder.
Note
The default types of Plone 5 also have an xml schema like this since that allows the fields of the types to be editable trough the web! Fields for types with a python schema are not editable ttw.
17.1. Changing a widget#
Dexterity XML is very powerful. By editing it (not all features have a UI) you should be able to do everything you can do with a Python schema. Sadly not every feature also is exposed in the UI of the dexterity schema editor. For example you cannot yet change the widgets or permissions for fields in the UI. We need to do this in the xml- or python-schema.
Our talks use a dropdown for type_of_talk and a multiselect for audience. Radio-buttons and checkboxes would be the better choice here. Modify the XML to make that change happen:
1<?xml version="1.0" encoding="UTF-8"?>
2<model xmlns="http://namespaces.plone.org/supermodel/schema"
3 xmlns:form="http://namespaces.plone.org/supermodel/form"
4 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
5 xmlns:lingua="http://namespaces.plone.org/supermodel/lingua"
6 xmlns:marshal="http://namespaces.plone.org/supermodel/marshal"
7 xmlns:security="http://namespaces.plone.org/supermodel/security"
8 xmlns:users="http://namespaces.plone.org/supermodel/users">
9 <schema>
10 <field name="type_of_talk" type="zope.schema.Choice"
11 form:widget="z3c.form.browser.radio.RadioFieldWidget">
12 <description />
13 <title>Type of talk</title>
14 <values>
15 <element>Talk</element>
16 <element>Training</element>
17 <element>Keynote</element>
18 </values>
19 </field>
20 <field name="details" type="plone.app.textfield.RichText">
21 <description>Add a short description of the talk (max. 2000 characters)</description>
22 <max_length>2000</max_length>
23 <title>Details</title>
24 </field>
25 <field name="audience" type="zope.schema.Set"
26 form:widget="z3c.form.browser.checkbox.CheckBoxFieldWidget">
27 <description />
28 <title>Audience</title>
29 <value_type type="zope.schema.Choice">
30 <values>
31 <element>Beginner</element>
32 <element>Advanced</element>
33 <element>Professionals</element>
34 </values>
35 </value_type>
36 </field>
37 <field name="speaker" type="zope.schema.TextLine">
38 <description>Name (or names) of the speaker</description>
39 <title>Speaker</title>
40 </field>
41 <field name="email" type="plone.schema.email.Email">
42 <description>Adress of the speaker</description>
43 <title>Email</title>
44 </field>
45 <field name="image" type="plone.namedfile.field.NamedBlobImage">
46 <description />
47 <required>False</required>
48 <title>Image</title>
49 </field>
50 <field name="speaker_biography" type="plone.app.textfield.RichText">
51 <description />
52 <max_length>1000</max_length>
53 <required>False</required>
54 <title>Speaker Biography</title>
55 </field>
56 </schema>
57</model>
17.2. Protect fields with permissions#
We also want to have a add a new field room
to show where a talk will take place.
Our case-study says the speakers will submit the talks online.
How should they know in which room the talk will take place (if it got accepted at all)?
So we need to hide this field from them by requiring a permission that they do not have.
Let's assume the prospective speakers will not have the permission to review content (i.e. edit submitted content and publish it) but the organizing commitee has.
You can then protect the field using the permission Review portal content
in this case the name of the permission-utility for this permission: cmf.ReviewPortalContent
.
We only want to prevent writing, not reading, so we'll only manage the write-permission
:
1<?xml version="1.0" encoding="UTF-8"?>
2<model xmlns="http://namespaces.plone.org/supermodel/schema"
3 xmlns:form="http://namespaces.plone.org/supermodel/form"
4 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
5 xmlns:lingua="http://namespaces.plone.org/supermodel/lingua"
6 xmlns:marshal="http://namespaces.plone.org/supermodel/marshal"
7 xmlns:security="http://namespaces.plone.org/supermodel/security"
8 xmlns:users="http://namespaces.plone.org/supermodel/users">
9 <schema>
10 <field name="type_of_talk" type="zope.schema.Choice"
11 form:widget="z3c.form.browser.radio.RadioFieldWidget">
12 <description />
13 <title>Type of talk</title>
14 <values>
15 <element>Talk</element>
16 <element>Training</element>
17 <element>Keynote</element>
18 </values>
19 </field>
20 <field name="details" type="plone.app.textfield.RichText">
21 <description>Add a short description of the talk (max. 2000 characters)</description>
22 <max_length>2000</max_length>
23 <title>Details</title>
24 </field>
25 <field name="audience"
26 type="zope.schema.Set"
27 form:widget="z3c.form.browser.checkbox.CheckBoxFieldWidget">
28 <description />
29 <title>Audience</title>
30 <value_type type="zope.schema.Choice">
31 <values>
32 <element>Beginner</element>
33 <element>Advanced</element>
34 <element>Professionals</element>
35 </values>
36 </value_type>
37 </field>
38 <field name="room"
39 type="zope.schema.Choice"
40 form:widget="z3c.form.browser.radio.RadioFieldWidget"
41 security:write-permission="cmf.ReviewPortalContent">
42 <description></description>
43 <required>False</required>
44 <title>Room</title>
45 <values>
46 <element>101</element>
47 <element>201</element>
48 <element>Auditorium</element>
49 </values>
50 </field>
51 <field name="speaker" type="zope.schema.TextLine">
52 <description>Name (or names) of the speaker</description>
53 <title>Speaker</title>
54 </field>
55 <field name="email" type="plone.schema.email.Email">
56 <description>Adress of the speaker</description>
57 <title>Email</title>
58 </field>
59 <field name="image" type="plone.namedfile.field.NamedBlobImage">
60 <description />
61 <required>False</required>
62 <title>Image</title>
63 </field>
64 <field name="speaker_biography" type="plone.app.textfield.RichText">
65 <description />
66 <max_length>1000</max_length>
67 <required>False</required>
68 <title>Speaker Biography</title>
69 </field>
70 </schema>
71</model>
See also
Exercise 1#
Go to the ZMI and look for the definition of the new Talk
content type in portal_types
. Now deactivate Implicitly addable? and save. Go back to the site. Can you identify what this change has caused? And why is that useful?
17.3. Summary#
You can now create new content-types and store them in a reproduceable way
You installed the package to apply the Generic Setup configuration
You learned how to read and modify the content type schema in xml