Per Erik Strandberg /cv /kurser /blog

Summary

This tutorial demonstrates a few ways of listing data about objects in a folder. I also demonstrate ways of adding your own index (or your own indices also known as indexes) via the schema and later via generic setup.

Download version without generic setup here: [1] (Warning: has the same bug in workflow as described in Plone Simple Folder And Permissions).

Download a version with generic setup here: [2] (without the bug in the above one).

Files used

I use more or less the same set of files as in Plone Archetypes View Template Modifications (so if you are unfamiliar with page templates take a look at that one first):

./__init__.py
./config.py
./metabrain.py
./Extensions/Install.py
./skins/metabrain/geekfolder_view.pt

The content types

I made a simple class "GeekProfile" and a simple folder class "GeekFolder". What I work on in this tutorial is the page template of the folder view. I also make a little method that returns the latest objects of the folder.

The content type is really simple. It has two integer fields, and two multi-selections. It also has a method that computes the sum of the two integers (the result from this call is to be indexed later). The method looks like this:

    security.declarePublic('SumOfNumbers')
    def SumOfNumbers(self):
        "get the sum of all integers in the type or 0."
        try:
            return self.FavNumber + self.TheBigSolutionNumber
        except:
            return 0

An instance of it with the default view looks like this:
http://www.pererikstrandberg.se/blog/plone/MetaBrain0.png

listFolderContents from the page template

Here we just get an iteration over the objects with the code tal:repeat="item context/listFolderContents". We can't see it but this is actually an iteration over the objects themselves - this may be very expensive especially if your server is slow and your objects are large. This is generally not what you want.

But if you do it this is code you can use...

        [...]
        <p tal:repeat="item context/listFolderContents">
          <span tal:content="item/Title"/> is a 
          <span tal:replace="item/portal_type" />.
          <span tal:replace="item/FavNumber" /> +
          <span tal:replace="item/TheBigSolutionNumber" /> =
          <span tal:replace="item/SumOfNumbers" /><br />
          absolute URL: <span tal:replace="item/absolute_url" /><br />
        </p>


...that produces something like this:
http://www.pererikstrandberg.se/blog/plone/MetaBrain1.png

The extra method that calls the portal catalog and proxies

In the folder class I have a function called Latest that returns the latest items in the folder. I declare it public so page templates and so on can access it. As you can see it calls searchResults on portal_catalog and returns the n latest items. The list generated by this function is not a list of the objects themselves but a proxy to it.

The proxy pattern is a well known design pattern and described here [3] (in English Wikipedia)

    security.declarePublic('Latest')
    def Latest(self, n=5):
        "Get five latest geeks"
        catalog = getToolByName(self, 'portal_catalog')

        path = '/'.join(self.getPhysicalPath()),

        results = catalog.searchResults(
            sort_on='created',
            sort_order='reverse',
            path = {'query' : path, 'level' : 0, 'depth' : 1}
            )

        return results[:n]

The method is called and used like this in my page template:

        [...]
        <p tal:repeat="item context/Latest">
          Title: <span tal:content="item/Title"/><br />
          title: <span tal:content="item/title"/><br />
          portal_type: <span tal:replace="item/portal_type" /><br />
          [...]
        </p>

The above code produces this principal look:
http://www.pererikstrandberg.se/blog/plone/MetaBrain2.png

Out of the box Meta Data

By default the proxy objects (brains) do not store all information about the object - that would make them as cumbersome as the objects themselves. Instead they only store Meta Data. That is like looking at a bunch of songs on a CD, you read the title, artist, production year and duration. You do not want to listen to all tracks - often you just want the overview.

What meta data you want depends on the situation. If you create content types in like I have with Archetypes you get a lot of meta data for free without having to specify it. This is an illustration of some of the meta data you get (read below for info on how to add your own):

        [...]
        <p tal:repeat="item context/Latest">
          <img tal:attributes="src item/getIcon" />
          <a tal:attributes="href item/getURL"
             tal:content="item/Title"
             />
          Created <span tal:replace="item/CreationDate" />
          by <span tal:replace="item/Creator" />
          (<span tal:replace="item/getObjSize" />)
       </p>

This is often what you want. A nice display of the contents with links, icons and so on. It looks a little something like this:
http://www.pererikstrandberg.se/blog/plone/MetaBrain3.png

Add your own meta data

There are at least a few ways of telling that a field in your content type is to be considered meta data, and/or indexing it. (Hopefully you have already seen it if you have been browsing though the code.)

When making the schemata the specification index = 'KeywordIndex:schema', or index = 'FieldIndex:schema', does the trick. As you might have seen the schemata I use for the geek profile content type looks like this:

        IntegerField(
            name = 'FavNumber', # ...
            index = 'FieldIndex:schema',
            ),

        IntegerField(
            name = 'TheBigSolutionNumber', # ...
            ),

        LinesField(
            name='OperatingSystem', # ...
            index = 'KeywordIndex:schema',
            ),

        LinesField(
            name='Editor', # ...
            ),

This means that the favourite number and the operating systems are to be indexed and made meta data. The second integer and the editor selection will not be meta data.

We can now use this meta data to access information about the objects by asking the proxy objects. We get what we want and we let the objects sleep. That's a win-win-situation. Here is one way of getting what we want via the page template:

        [...]
        <p class="discreet">This is useful for folder listings</p>
        <p tal:repeat="item context/Latest">
          <span tal:content="item/Title"/>:
          <span tal:replace="item/getFavNumber" />,
          <span tal:repeat="os item/getOperatingSystem">
            <span tal:replace="os" />
          </span>
        </p>

And a screen shot of it.
http://www.pererikstrandberg.se/blog/plone/MetaBrain4.png

Draco Dormiens Nunquam Titillandus

Never Tickle a Sleeping Dragon - and do not wake objects up unless you have to. It is possible to do so and here is how you do it, but remember: you can but you shouldn't.

        [...]
        <p class="discreet">
          Doing this on the real object will break things.
        </p>
        <p tal:repeat="item context/Latest">
          <span tal:content="item/Title" />:
          <span tal:replace="python: item.getObject().TheBigSolutionNumber" />,
        </p>

And a screen shot. My note that doing this on real objects will break things is also one of the ways you can tell if you have the object itself or the proxy. And of course trying to get the proxy object of an the object itself is of course impossible.
http://www.pererikstrandberg.se/blog/plone/MetaBrain5.png

Add your own meta data part 2: adding results from function calls

You can add an index if you want in ZMI under portal_catalog -> metadata. If you do that it looks a little something like this:
http://www.pererikstrandberg.se/blog/plone/metabrain-metadata.png

And we can now view the sum of the two integers in the page template without waking the object up.

        [...]
        <p tal:repeat="item context/Latest">
          <span tal:content="item/Title"/>:
          <span tal:replace="item/SumOfNumbers" />
        </p>

And the screenshot:
http://www.pererikstrandberg.se/blog/plone/MetaBrain6.png

Do it with generic setup

In order to do these changes with generic setup I created a blank Plone site and took a snapshot (see Plone Generic Setup First Contact for more on what a snapshot is). Manually added the meta data I wanted and took a new snapshot. After looking at the diff and also exporting the entire profile to see what the beginnings and ends of the files should look like I found two files that might be interesting:

I think that the only thing the second file does is stores data on what collection (aka topics aka smart folder) should store as meta data - and it seems to work fine without it.

So the first one it to be stored.

Here is what the beginning of my diff looks like:
http://www.pererikstrandberg.se/blog/plone/metabrain-generic-setup.png

My product now has the following files:

./__init__.py
./config.py
./configure.zcml
./metabrain.py
./Extensions/Install.py
./profiles/default/catalog.xml
./skins/metabrain/geekfolder_view.pt

The magic is in the file profiles/default/catalog.xml that looks like this:

<?xml version="1.0"?>
<object name="portal_catalog" meta_type="Plone Catalog Tool">
 <column value="SumOfNumbers"/>
 <column value="getFavNumber"/>
 <column value="getOperatingSystem"/>
</object>

Summary

Download version without generic setup here: [4] (Warning: has the same bug in workflow as described in Plone Simple Folder And Permissions).

Download a version with generic setup here: [5] (without the bug in the above one).


This page belongs in Plone Cms.
This page belongs in Kategori Programmering.