Personal tools
You are here: Home Documentation Tutorials Benefit NOW from using GenericSetup and Zope 3 technologies
Support

Get Help

Join our chat rooms or support forums if you have more specific questions.

Plone Training
Learn how to design, build, and deploy a website in Plone through one of the numerous Plone training sessions around the world.
Find Plone training…
 
Document Actions

Benefit NOW from using GenericSetup and Zope 3 technologies

Note: Return to tutorial view.

Impress your colleagues by using GenericSetup and Zope 3 views efficiently and with minimal effort! This tutorial shows you how to add a new view, how to use it, how to add a new content type and how to hook it all up.

Introduction

Goals of this tutorial and what you need to get started

Goals

The goal of this tutorial is to get you familiar with GenericSetup and Zope 3 techniques and help you understand how these tools can help you become a more efficient Plone developer.

Step by step you will learn how to

  • create a Plone Product with minimal effort,
  • register a new view for an existing content type,
  • do something useful with that view and
  • register a new content type that uses that view as its default view.

What do you need?

You'll need a Zope instance that has Plone 2.5 installed.

Registering a view

This is where we create the Product and register our own view for folders

First, we'll create a Product called MYPRODUCT. This is how you do it: In your Zope instance's Products folder, create a MYPRODUCT folder and therein create a file called __init__.py. Leave the __init__.py empty for now.

Then, in the same folder, create a file called configure.zcml with the following contents:

<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">

<browser:page
for="Products.ATContentTypes.interface.IATFolder"
name="my-view.html"
class=".browser.MyView"
template="my_view.pt"
permission="zope2.View"
/>

</configure>

What we did here is register a new view for the IATFolder interface with the name my-view.html. The implementation of that view goes into the class .browser.MyView, which uses the template my_view.pt. This view is guarded with the zope2.View permission.

Now we'll create that MyView class and the my_view.pt page template. Inside the MYPRODUCT folder on the filesystem, create a file called browser.py and insert the following:

from Products.Five import BrowserView

class MyView(BrowserView):
pass

This is where we will define methods later on that our template can use like view/my_method. Right now, we'll leave the view class empty.

For the template, we'll copy and modify one of the templates that comes with Plone. Let's use folder_summary_view.pt from CMFPlone/skins/plone_content and strip out everything that's in the metal:listingmacro tag so that what we have left is this:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
lang="en"
metal:use-macro="here/main_template/macros/master"
i18n:domain="plone">

<body>

<div metal:fill-slot="main">
<metal:main-macro define-macro="main">

<div metal:use-macro="here/document_actions/macros/document_actions">
Document actions (print, sendto etc)
</div>

<h1 tal:content="object_title" class="documentFirstHeading">
Title or id
</h1>

<a href=""
class="link-parent"
tal:define="parent_url python:here.navigationParent(here, template_id)"
tal:condition="parent_url"
tal:attributes="href parent_url"
i18n:translate="go_to_parent_url">
Up one level
</a>

<p class="documentDescription"
tal:content="here/Description"
tal:condition="here/Description">
Description
</p>

<p> Hello world! </p>

</metal:main-macro>
</div>

</body>
</html>

Save this as MYPRODUCT/my_view.pt.

Now start up your Zope, go into the Plone interface, create a folder called whatever and then visit .../portal/whatever/my-view.html.

Do something useful

In this part, we'll do something (remotely) useful with our view: We create a basic search interface for looking up objects that have certain keywords.

You can skip this part if you don't feel like doing something useful right now. :-)

Now that we have our view in place, we can start actually doing something with it. Like listing all keywords of the items that the folder contains, and letting the user do a search based on those.

For that, we'll add a couple of methods to our existing view in browser.py for our template to use.

listAllTags returns a list of keywords in the order of their popularity. This is the implementation:

class MyView(BrowserView):
def listAllTags(self):
weighted_tags = dict()
for item in self.query():
for keyword in item.Subject:
if keyword in weighted_tags:
weighted_tags[keyword] = weighted_tags[keyword] + 1
else:
weighted_tags[keyword] = 1
items = sorted(weighted_tags.items(), lambda a,b:cmp(b[1], a[1]))
return [i[0] for i in items]

def query(self, **kw):
path = '/'.join(self.context.getPhysicalPath())
return self.context.portal_catalog(path=path, **kw)

Then we'll create two other methods for our template: tagSelected will tell us whether a tag has been selected already, and addTagToURL will add a tag to the query parameters. The implementation of these methods goes like this:

from urllib import quote_plus

# ...

def tagSelected(self, tag):
return tag in self.getTags()

def addTagToURL(self, tag):
base = self.request.URL + '?'
query = ''
for existing_tag in self.getTags():
query += 'tags:list=%s&' % quote_plus(existing_tag)
query += 'tags:list=' + quote_plus(tag)
return base + query

def getTags(self):
return self.request.form.get('tags', [])

This is how we present the list of tags in the template:

<h2>Tags</h2>
<ul>
<li tal:repeat="tag view/listAllTags">
<a href="#"
tal:omit-tag="python:view.tagSelected(tag)"
tal:attributes="href python:view.addTagToURL(tag)"
tal:content="tag"
/>
</li>
</ul>

However, this is hardly useful without any results. Again, the real work is done in the view:

def searchResults(self):
return self.query(Subject=dict(query=self.getTags(), operator='and'))

and the template just calls searchResults:

<h2>Results</h2>
<ul>
<li tal:repeat="item view/searchResults">
<a href="#"
tal:attributes="href item/getURL;
title item/Description"
tal:content="item/Title"
/>
</li>
</ul>

Registering a setup profile

In this part, you'll learn what GenericSetup profiles are for and how you can make use of them. Herein, we create our own content type and register our view as its default view.

Next, we're going to register our own content type that uses the view that we just made (my-view.html) as its default view. The type will otherwise behave just like a normal folder.

We'll do this by registering a GenericSetup profile, which is a bunch of configuration settings that you can install in your Plone site. In order to register the profile, we need to add this to our MYPRODUCT/configure.zcml:

<gs:registerProfile
name="default"
title="MYPRODUCT profile"
directory="profiles/default"
description=""
provides="Products.GenericSetup.interfaces.EXTENSION"
for="Products.CMFPlone.interfaces.IPloneSiteRoot"
/>

If you're familiar with XML, you may have noticed that we're using another XML namespace. We'll have to define the gs namespace in the root configure statement in the same file, so that it becomes:

<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:gs="http://namespaces.zope.org/genericsetup">

Now that we registered the configuration, we can write the actual configuration, which goes into the MYPRODUCT/profiles/default folder. This is what we need to put into MYPRODUCT/profiles/default/types.xml to register our own type:

<?xml version="1.0"?>
<object name="portal_types" meta_type="Plone Types Tool">
<object name="My Folder"
meta_type="Factory-based Type Information with dynamic views"/>
</object>

Now copy Plone's CMFPlone/profiles/default/types/Folder.xml into the MYPRODUCT/profiles/default/types/ directory (which you create first) and rename the file to My_Folder.xml. This file corresponds to what you see in the portal_types tool in the ZMI when you go to .../portal/portal_types/Folder/manage_propertiesForm.

In your new My_Folder.xml, change this:

<property name="default_view">folder_listing</property>

into:

<property name="default_view">my-view.html</property>

and change this:

<object name="Folder"
meta_type="Factory-based Type Information with dynamic views"

into:

<object name="My Folder"
meta_type="Factory-based Type Information with dynamic views"

Also, add my-view.html to the list of selectable views in the view_methods property in the same XML file. And then you might want to change the title of the new type in the title property.

Done! Now you can visit the portal_setup tool in the ZMI and apply your MYPRODUCT profile to your site. But that user interface isn't really nice. So let's write a QuickInstaller method instead, so that your Product can be installed as usual through the Plone interface.

For your Product to be quick-installable, add a MYPRODUCT/Extensions/install.py and do:

from Products.CMFCore.utils import getToolByName

def install(portal):
setup_tool = getToolByName(portal, 'portal_setup')
old_context = setup_tool.getImportContextID()
setup_tool.setImportContext('profile-Products.MYPRODUCT:default')
setup_tool.runAllImportSteps()
setup_tool.setImportContext(old_context)
return "Ran all import steps."

Note that this step will not be necessary anymore in the future, because the portal_quickinstaller tool is going to be aware of GenericSetup profiles starting with Plone 3.0.

Of course all of these steps work also of you use sensible names instead of MYPRODUCT and my-view.html etc., but be sure you change those names throughout your whole Product!

Where to go from here

From here to... a few very useful links that will help you strengthen your development skills.

Happy view-ing!


For any issues with the web site functionality, please file a ticket.

Please consult the policy on plone.org content if you want your content published on this site.

Servers and hosting by