35. Dexterity Types III: Sponsors

Without sponsors, a conference would be hard to finance! Plus it is a good opportunity for Plone companies to advertise their services.

In this part we will:

  • Create a sponsor contenttype to manage sponsors

  • Store non-visible information about the sponsor in the sponsor-type

The topics we cover are:

  • Schema hint and directives

  • Field permissions

  • Vocabularies

35.1. The Python schema

First we create the schema for the new content type.

Add a new file content/sponsor.py.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# -*- coding: utf-8 -*-
from plone.app.textfield import RichText
from plone.autoform import directives
from plone.dexterity.content import Container
from plone.namedfile import field as namedfile
from plone.supermodel import model
from plone.supermodel.directives import fieldset
from ploneconf.site import _
from z3c.form.browser.radio import RadioFieldWidget
from zope import schema
from zope.schema.vocabulary import SimpleTerm
from zope.schema.vocabulary import SimpleVocabulary


LevelVocabulary = SimpleVocabulary(
    [SimpleTerm(value=u'platinum', title=_(u'Platinum Sponsor')),
     SimpleTerm(value=u'gold', title=_(u'Gold Sponsor')),
     SimpleTerm(value=u'silver', title=_(u'Silver Sponsor')),
     SimpleTerm(value=u'bronze', title=_(u'Bronze Sponsor'))]
    )


class ISponsor(model.Schema):
    """Dexterity Schema for Sponsors
    """

    directives.widget(level=RadioFieldWidget)
    level = schema.Choice(
        title=_(u'Sponsoring Level'),
        vocabulary=LevelVocabulary,
        required=True
    )

    text = RichText(
        title=_(u'Text'),
        required=False
    )

    url = schema.URI(
        title=_(u'Link'),
        required=False
    )

    fieldset('Images', fields=['logo', 'advertisement'])
    logo = namedfile.NamedBlobImage(
        title=_(u'Logo'),
        required=False,
    )

    advertisement = namedfile.NamedBlobImage(
        title=_(u'Advertisement (Gold-sponsors and above)'),
        required=False,
    )

    directives.read_permission(notes='cmf.ManagePortal')
    directives.write_permission(notes='cmf.ManagePortal')
    notes = RichText(
        title=_(u'Secret Notes (only for site-admins)'),
        required=False
    )

@implementer(ISponsor)
class Sponsor(Container):
    """Sponsor instance class"""

Some things are notable here:

  • LevelVocabulary is used to create the options used in the field level. This way we could easily translate the displayed value.

  • fieldset('Images', fields=['logo', 'advertisement']) moves the two image fields to another tab.

  • directives.read_permission(...) sets the read and write permission for the field notes to users who can add new members. Usually this permission is only granted to Site Administrators and Managers. We use it to store information that should not be publicly visible. Please note that obj.notes is still accessible in templates and Python.

See also

See the chapter Dexterity: Reference for a reference of all field-types and directives you can use in dexterity.

35.2. The Factory Type Information, or FTI

Next, we create the factory type information (“FTI”) for the new type in profiles/default/types/sponsor.xml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?xml version="1.0"?>
<object name="sponsor" meta_type="Dexterity FTI" i18n:domain="plone"
   xmlns:i18n="http://xml.zope.org/namespaces/i18n">
 <property name="title" i18n:translate="">Sponsor</property>
 <property name="description" i18n:translate=""></property>
 <property name="icon_expr">string:${portal_url}/document_icon.png</property>
 <property name="factory">sponsor</property>
 <property name="add_view_expr">string:${folder_url}/++add++sponsor</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="schema">ploneconf.site.content.sponsor.ISponsor</property>
 <property name="klass">ploneconf.site.content.sponsor.Sponsor</property>
 <property name="behaviors">
  <element value="plone.dublincore"/>
  <element value="plone.namefromtitle"/>
  <element value="plone.versioning"/>
 </property>
 <property name="model_source"></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>

Then we register the FTI in profiles/default/types.xml

1
2
3
4
5
<?xml version="1.0"?>
<object name="portal_types" meta_type="Plone Types Tool">
 <object name="talk" meta_type="Dexterity FTI"/>
 <object name="sponsor" meta_type="Dexterity FTI"/>
</object>

After reinstalling our package we can create the new type.

35.2.1. Exercise 1

Sponsors are containers but they don’t need to be. Turn them into items by changing their class to plone.dexterity.content.Item.

Solution

Modify the instance class.

1
2
3
4
5
from plone.dexterity.content import Item

@implementer(ISponsor)
class Sponsor(Item):
    """Sponsor instance class"""

Note

Changing the base-class of existing content from Item to Container or the other way around is possible but requires. See https://github.com/plone/plone.app.contenttypes#changing-the-base-class-for-existing-objects for details.

35.3. Summary

  • You created a new content type to store information on sponsors

  • You learned how to protect individual fields from being edited with permissions

  • Next you will learn how to display the sponsors at the bottom of every page