Building a Plone 3 Product Pt.1: Archetypes

12
Apr
0

If you’re reading this, you probably know what Plone is. A Content Management System based off of the Zope framework and written in Python. You’ve probably set up your own instance, installed a product or two, maybe even added your own external method. Well now you want more. You want to be able to customize your Plone instance and make into exactly what you want it to. That’s the journey we’re starting on today. We’re going to make a basic product that contains a content type for books.

Note: This tutorial is pretty much identical to the tutorial done by Christopher Warner. The reason I made this is one is because I plan on explaining more about Plone, and this is a good starting point. Please visit his blog as well.

Using Paster

Start by going to the /src directory of your Plone instance. When making new products, it’s standard to put all the products your developing here.

While you’re in the directory, you’ll need to use paster to generate all the basic files in a product:

paster create -t archetype

If you don’t have paster, install it now:
easy_install PasteScript

Paster is the best friend you could ask for when it comes to making new Products. Paster has several different templates built in (you can see a list using “paster create -t list-template”) that you can use to generate templates for all kind of Plone-related products and instances. For today though, we’ll be using the “archetype” template.

The Archetype template generates code that you’ll want for a product that extends the “Archetypes” product in Plone. The Archetypes product is there for only one reason: as a provider of schemas that we can use to make new content types quickly.  This makes it an ideal choice for our project today.

When you run the script it’ll ask you a lot of questions.  Let’s talk a little bit about what each of them are about:

  • project name: This name should reflect what your project is for. This will determine the name of the egg, package, and project, so keep it short. One word is good.
  • Project Title:  This name should reflect what your product is for. A one or two word title would be good
  • Namespace Package Name: This is the namespace that you want your content to be a part of. If you have a group of products you want to be related to each other, they should all have the same namespaces. However, it doesn’t really matter for the most part. Most people just use “Products”.
  • Package name: this name should describe your product. When people look at your product, they’re going to see this and the Namespace Package name. For example, if my namespace package name is “Products”, and the package name is “books”, they’re going to see your product as “Products.books”.
  • Version: version of your product, generally 1.0.
  • Description: a short, one sentence description of your product.
  • Long Description: a longer description of your product.
  • Author: your name, usually. Possibly your company’s name.
  • Author Email: your e-mail.
  • Keywords: You’ll want to fill this out so people can find your product. If it’s just for that once instance, it’s not as important.
  • Project URL: URL to the product’s main page.
  • Project License: The license you want your project to be released under.
  • Zip-Safe: Generally I would leave this as false. If your project is usable as a zipped egg, put true. But generally, putting False is staying on the safe side.

For this tutorial, fill it out with these entries:

Project Name: books
At this point you’ll be prompted for an easy, advanced or all install. For this tutorial, you can just choose easy.
Project Title: Books Content
Version: 1.0
Description: Contains Contenttypes to represent books.

after you fill out all the necessary fields, paster will generate a folder containing the basic files needed.

At this point, you also want to go into configure.zcml and remove the line talking about locales. We will not need it for this part of the tutorial.

Using Paster for Content

At this point, you should now have a folder called “books” in /src. Now navigate into the books/books/books/ directory, as that will be the main directory you’ll be working in. You’ll know you’re in the right place when you see a “configure.zcml” file, as well as a few other folders, such as “browser” and “contents”.

Once you know you’re in the right directory, you’ll use paster again, this time to add the files needed for a basic content type. Use the following:


paster addcontent contenttype

Paster can also generate other kinds of content types. You can see these with “paster addcontent -l”.

As with the previous paster command, this one also comes with it’s own questions:

  • contenttype_name: The name of the content type (Books)
  • contenttype_description: This is what the user will see when they look at your content type. This should describe what the content is for. It should be only one sentence (Displays and stores information about a book).
  • folderish: put true if you want your content type to be a like a folder (it can hold other content types) In our case, we’ll put false.
  • global_allow: means that it can be put anywhere on the site. Generally, and for this tutorial, put true.
  • allow_discussion: if you put in true here, this basically puts a thread onto each instance of your content so people can discuss it if need be. Generally I would put this as true, unless you’re sure it’s unnecessary.  For this tutorial, we’ll put False.

Once the script is done, we’ll can finally start coding.

Developing Content

Now that we have our basics, we’re going to start editing our books content type. Navigate into the “content” folder, and you should find a “books.py” file. Using a text editor (such as vim or emacs), modify the books.py file. You should see something almost exactly like this:

——-

“”"Definition of the Books content type
“”"

from zope.interface import implements

from Products.Archetypes import atapi
from Products.ATContentTypes.content import base
from Products.ATContentTypes.content import schemata

from books.books import booksMessageFactory as _
from books.books.interfaces import IBooks
from books.books.config import PROJECTNAME

BooksSchema = schemata.ATContentTypeSchema.copy() + atapi.Schema((

# -*- Your Archetypes field definitions here … -*-

))

# Set storage on fields copied from ATContentTypeSchema, making sure
# they work well with the python bridge properties.

BooksSchema['title'].storage = atapi.AnnotationStorage()
BooksSchema['description'].storage = atapi.AnnotationStorage()

schemata.finalizeATCTSchema(BooksSchema, moveDiscussion=False)

class Books(base.ATCTContent):
“”"Description of the Example Type”"”
implements(IBooks)

meta_type = “Books”
schema = BooksSchema

title = atapi.ATFieldProperty(‘title’)
description = atapi.ATFieldProperty(‘description’)

# -*- Your ATSchema to Python Property Bridges Here … -*-

atapi.registerType(Books, PROJECTNAME)

———–

The only important part here really is the “Books” class. At a glance, you can see that it extends the “base.ATCTContent” type.  By default, this content type only contains two fields: “title” and “description”. However, we’re going to need add more fields to describe more about our books. Let’s say we need the following fields to complete our product:

1.Author
2.Publisher
3.ISBN

We can add these fields by putting them into the Archetypes field definition, between the (( )) in the BooksSchema declaration.

When adding more fields to our schema, there are two helpful resources: The Fields Reference. This gives you a good luck at the basic field types you can use to build a content type out of.

Now let’s make a new field for Author. But this into the BookSchema declaration:

atapi.StringField(
name=’bookAuthor’,
widget=atapi.StringWidget(
label=u’Book Author’,
label_msgid=’bookbook_label_bookAuthor’,
i18n_domain=’bookbook’,
maxlength=100,
size=100,
),
required=True
searchable=True

),

We gave our schema a name, and a widget that it uses to modify and view the data. If you want Plone to do automatically generate views and edit pages for you, you’re going to want it to have a widget.

Now we can do the same exact thing for the Publisher and ISBN. The formatting i for the most part the same:
atapi.StringField(
name=’bookPublisher’,
widget=atapi.IntegerWidget(
label=u”Book Publisher”,
label_msgid=’bookbook_label_bookPublisher’,
i18n_domain=’bookbook’,
),
required=False,
searchable=True),
),
atapi.StringField(
name=’bookISBN’,
widget=atapi.StringWidget(
label=u’Book ISBN’,
label_msgid=’bookbook_label_bookISBN’,
i18n_domain=’bookbook’,
maxlength=13,
size=13
),
required=False,
searchable=True

),

And there you have it. adding it all together, you’ll have something like this:

——-

“”"Definition of the Books content type
“”"

from zope.interface import implements

from Products.Archetypes import atapi
from Products.ATContentTypes.content import base
from Products.ATContentTypes.content import schemata

from books.books import booksMessageFactory as _
from books.books.interfaces import IBooks
from books.books.config import PROJECTNAME

BooksSchema = schemata.ATContentTypeSchema.copy() + atapi.Schema((

# -*- Your Archetypes field definitions here … -*-

atapi.StringField(
name=’bookAuthor’,
widget=atapi.StringWidget(
label=u’Book Author’,
label_msgid=’bookbook_label_bookAuthor’,
i18n_domain=’bookbook’,
maxlength=100,
size=100,
),
required=True
searchable=True

),

atapi.StringField(
name=’bookPublisher’,
widget=atapi.IntegerWidget(
label=u”Book Publisher”,
label_msgid=’bookbook_label_bookPublisher’,
i18n_domain=’bookbook’,
),
required=False,
searchable=True),
),
atapi.StringField(
name=’bookISBN’,
widget=atapi.StringWidget(
label=u’Book ISBN’,
label_msgid=’bookbook_label_bookISBN’,
i18n_domain=’bookbook’,
maxlength=13,
size=13
),
required=False,
searchable=True

),

))

# Set storage on fields copied from ATContentTypeSchema, making sure
# they work well with the python bridge properties.

BooksSchema['title'].storage = atapi.AnnotationStorage()
BooksSchema['description'].storage = atapi.AnnotationStorage()

schemata.finalizeATCTSchema(BooksSchema, moveDiscussion=False)

class Books(base.ATCTContent):
“”"Description of the Example Type”"”
implements(IBooks)

meta_type = “Books”
schema = BooksSchema

title = atapi.ATFieldProperty(‘title’)
description = atapi.ATFieldProperty(‘description’)

# -*- Your ATSchema to Python Property Bridges Here … -*-

atapi.registerType(Books, PROJECTNAME)

———–

Once we’ve got all the addition types tucked in nicely in the script, we just have to tell Plone to find our product so we can install it. The method we used today allows us to dynamically generate both the “view” and “edit” pages for our content type, so all we had to do was develop the schema.

Installing a Product

Now we’re on our final step: installing the product into Plone. To get Plone to see the product, you need to first add the namespace into the buildout configuraiton. In buildout.cfg (or whatever buildout configuration your using, add the following:

1.”books” (or your namespaces) into eggs.
2.”src/books” (or src/ your namespace) into develop.
3.”books.books” (or the whole name of your package) into zcml.

Once you have done all this, buildout (buildout -c buildout.cfg) and restart your instance. You should then see the “Book Content” product in your Add/Remove products, and can now add book products and modify them.

Congratulations! You have officially made your first Plone Content Type.

Go ahead and give it a try and see where your hard work got you. Next time we’ll go into a more in-depth look at the innards of a Plone product.