Customizing plone sites
Well, I’ve now customized a fair enough amount of plone sites to publish a few helpful hints on the subject. Here’s what I’ve learned.
Who should read this article
This article is written for someone who knows how to program, knows a bit about python, and wants to learn more about setting up plone web sites. It is written for people who want to do significant customization of the look and feel of plone sites and want a quick read on how I did this one in particular. I have included much of the code I used for the customization here, to better describe exactly how I did what I did. You will need a bit of exposure to Zope Page Templates. To try this out, you will need to have plone installed on your Zope web server.
1. Overview
Customizing a plone site is usually nothing more than customizing the default skin, so it’s in that light that you want to think about it.
I’m going to describe how I recently customized a plone site to look like letsgetready.org. The new site is here. There were many things involved that are common for plone customizations. They are:
- Having a splash page
- Rebuilding the main menu to be “section-aware”
- Rebuilding the left menu to use only published content.
- Making image alignment work
- Properly handling external links
- Adding a link in the footer to manage the site.
- Changing the logo
- Optimizing for performance.
2. Getting started.
2a. Uploading images:
Create a folder /images/ off the root and upload images there throughout this project. You might want to do this part entirely in advance, or not.
3. Customize the columns
We want to have two columns (not three), so go and set that in the properties of the root of the plone site. There is a list of portlets to be displayed on the left side and the right side. Remove those on the right and leave only the navigation on the front. You might want to leave logging in on the left.
4. Set the colors
That would be here:
plone_styles/base_properties (for setting colors)
5. Change the footer and colophon to what you want.
plone_templates/colophon (making it smaller and adding link to RoboCommerce)
plone_templates/footer (adding a “manage” link to /folder_contents")
The styles here are in:
plone_styles/plone.css
Colophon:
<p i18n:translate="text_conforms_to_standards" class="discreet">
Powered by <a target="blank" href="http://www.plone.org">Plone</a>
and <a target="blank"
href="http://www.robocommerce.com">RoboCommerce</a>
</p>
Footer:
<span i18n:translate="description_copyright" tal:omit-tag="">
© 1998-2005 Let's Get Ready! 45 Columbus Avenue New York, NY 10023-6992.
<a href="folder_contents">manage</a>
</span>
6. Remove most of this, to not include the byline (leave the date modified here)
plone_content/document_byline (to remove the username)
7. I wanted this on the bottom left, so I customized its look:
plone_templates/global_searchbox
8. Change the logo:
plone_images/logo.jpg (to logo.gif, possibly)… note, you must also change the base_properties to do this
Also, change the logo’s position here:
plone_styles/plone.css
9. Make external links open in _blank windows, and also not have little icons next to them by looking in this file:
plone_ecmascript/plone_javascripts.css (to make external links not have an icon next to them)
Here is the link, valid as of March, 2005:
http://plone.org/documentation/how-to/open-external-links-in-new-window
10. This is where we’re going to put the main menu:
plone_templates/global_sections
Remove most of it, and instead use a macro defined in
/includes
… called top_menu. Notice how it is “section-aware":
<div tal:omit-tag="" metal:define-macro="top_menu">
<table cellpadding=0 cellspacing=0 border=0 align=left>
<tr>
<td><a href="/about/"><img alt="About"
border=0
tal:define="selected python:test(here.getSectionFromURL() == 'section-about', 1, 0)"
tal:attributes="src python:test(selected, '/images/mainmenu/about_selected.gif'
, '/images/mainmenu/about_unselected.gif');
onmouseout python:test(selected
, 'this.src='/images/mainmenu/about_selected.gif''
, 'this.src='/images/mainmenu/about_unselected.gif'');"
onmouseover="this.src='/images/mainmenu/about_mouseover.gif';"
onmouseout="this.src='/images/mainmenu/about_unselected.gif';"
src="/images/mainmenu/about_unselected.gif"></a></td>
<td bgcolor="white"><img src="/images/tp.gif" width="1" height="1"></td>
<td><a href="/programs/"><img alt="Programs"
border=0
tal:define="selected python:test(here.getSectionFromURL() == 'section-programs', 1, 0)"
tal:attributes="src python:test(selected, '/images/mainmenu/programs_selected.gif'
, '/images/mainmenu/programs_unselected.gif');
onmouseout python:test(selected
, 'this.src='/images/mainmenu/programs_selected.gif''
, 'this.src='/images/mainmenu/programs_unselected.gif'');"
onmouseover="this.src='/images/mainmenu/programs_mouseover.gif';"
onmouseout="this.src='/images/mainmenu/programs_unselected.gif';"
src="/images/mainmenu/programs_unselected.gif"></a></td>
<td bgcolor="white"><img src="/images/tp.gif" width="1" height="1"></td>
<td><a href="/get_involved/"><img alt="Get Involved"
border=0
tal:define="selected python:test(here.getSectionFromURL() == 'section-get_involved', 1, 0)"
tal:attributes="src python:test(selected, '/images/mainmenu/getinvolved_selected.gif'
, '/images/mainmenu/getinvolved_unselected.gif');
onmouseout python:test(selected
, 'this.src='/images/mainmenu/getinvolved_selected.gif''
, 'this.src='/images/mainmenu/getinvolved_unselected.gif'');"
onmouseover="this.src='/images/mainmenu/getinvolved_mouseover.gif';"
onmouseout="this.src='/images/mainmenu/getinvolved_unselected.gif';"
src="/images/mainmenu/getinvolved_unselected.gif"></a></td>
<td bgcolor="white"><img src="/images/tp.gif" width="1" height="1"></td>
<td><a href="/resources/"><img alt="College Access Resources"
border="0"
tal:define="selected python:test(here.getSectionFromURL() == 'section-resources', 1, 0)"
tal:attributes="src python:test(selected, '/images/mainmenu/resources_selected.gif'
, '/images/mainmenu/resources_unselected.gif');
onmouseout python:test(selected
, 'this.src='/images/mainmenu/resources_selected.gif''
, 'this.src='/images/mainmenu/resources_unselected.gif'');"
onmouseover="this.src='/images/mainmenu/resources_mouseover.gif';"
onmouseout="this.src='/images/mainmenu/resources_unselected.gif';"
src="/images/mainmenu/resources_unselected.gif"></a></td>
<td bgcolor="white"><img src="/images/tp.gif" width="1" height="1"></td>
<td><a href="/donate/"><img alt="Donate"
border="0"
tal:define="selected python:test(here.getSectionFromURL() == 'section-donate', 1, 0)"
tal:attributes="src python:test(selected, '/images/mainmenu/donate1_selected.gif'
, '/images/mainmenu/donate1_unselected.gif');
onmouseout python:test(selected
, 'this.src='/images/mainmenu/donate1_selected.gif''
, 'this.src='/images/mainmenu/donate1_unselected.gif'');"
onmouseover="this.src='/images/mainmenu/donate1_mouseover.gif';"
onmouseout="this.src='/images/mainmenu/donate1_unselected.gif';"
src="/images/mainmenu/donate1_unselected.gif"></a></td>
<td><img src="/images/mainmenu/end.gif"></td>
</tr>
</table>
</div>
11. plone/get_menu should be a python method to get a string that can be put in the left menu.
# get the section… contentPath = context.portal_url.getRelativeContentPath(context) obj = context parents = [] for i in range(0,len(contentPath) - 1): parents.append(obj.getParentNode()) obj = obj.getParentNode() parents.reverse() parents.append(context) print ‘<div id="left_menu">’ if len(parents) > 0: title = parents[0].get_short_title() print ‘'’<a class="level1″ href="%s">%s”’ % (parents[0].absolute_url_path(), title) for obj in parents[0].objectValues(): if (obj.id == ‘index_html’): continue if hasattr(context, ‘portal_workflow’): workflow = context.portal_workflow try: title = obj.get_short_title() if workflow.getInfoFor(obj,’review_state’)==’published’: print ‘'’<div class="level2″><a class="level2″ href="%s">%s</a> <div style="margin: 0px; padding: 0px;"><img src="/images/menu/TopLevelBar.gif"></div> ‘'’ % (obj.absolute_url_path(), title) for obj2 in obj.objectValues(): if (obj2.id == ‘index_html’): continue if workflow.getInfoFor(obj2,’review_state’)==’published’: print ‘'’<a class="level3″ href="%s">> %s”’ % (obj2.absolute_url_path(), obj2.get_short_title()) for obj3 in obj2.objectValues(): if (obj3.id == ‘index_html’): continue if workflow.getInfoFor(obj3,’review_state’)==’published’: print ‘'’<a class="level4″ href="%s">> %s”’ % (obj3.absolute_url_path(), obj3.get_short_title()) for obj4 in obj3.objectValues(): if (obj4.id == ‘index_html’): continue if workflow.getInfoFor(obj4,’review_state’)==’published’: print ‘'’<a class="level5″ href="%s">> %s”’ % (obj4.absolute_url_path(), obj4.get_short_title()) for obj5 in obj4.objectValues(): if (obj5.id == ‘index_html’): continue if workflow.getInfoFor(obj5,’review_state’)==’published’: print ‘'’<a class="level6″ href="%s">> %s”’ % (obj5.absolute_url_path(), obj5.get_short_title()) print ‘</div>’ except: pass print ‘</div>’ return printed
12. We’ll need a function like this:
plone/get_short_title
… because sometimes we’ll need a shorter title to go into the menu. NOTE: You’ll have to add this property short_title in the ZMI (just under the plone root):
try:
chain = context.aq_chain
except:
chain = [context]
if chain[0].hasProperty('short_title'):
return getattr(context, 'short_title')
else:
return context.title_or_id()
13. plone_templates/header
Eyeball this and see what is not necessary. We don’t need a minimum width requirement… we don’t need different stylesheets for small, medium, and large text…
14. plone_styles/ploneCustom.css
Here is where any left_menu styles should go.
15. plone_portlets/portlet_navigation (to change the look and features of the menu)
Here is where get_menu should be called, and I replaced the entire contents of portlet_navigation to the following (contents of the macro, not the file)
<div class="left_navigation" tal:content="structure here/get_menu"></div>
16. Allowing end-users to manage content on the homepage.
The homepage was built by just creating a template: index_html in the root. It has access to all methods, so it can basically copy header and main_document from the skins.
However, you want a user to be able to manage its contents.
Here is how you do that. You add something like this to the homepage, where homepage_content/left and …/right are valid content nodes in plone:
<div tal:content="structure here/homepage_content/left/CookedBody"
style="position: absolute; top: 247px; left: 0px;">
</div>
<div tal:content="structure here/homepage_content/right/CookedBody"
style="position: absolute; top: 420px; left: 490px;">
</div>
