Posted Feb 11, 2009
Managing Layer-Specific Resources in Plone
While working on GloWorm, I decided that I needed to provide extra JavaScript, KSS, and CSS to GloWorm's @@inspect view. Traditionally, I'd add the files to my skins folder, list them in Plone's resource registries – portal_javascript, portal_kss, and portal_css respectively – and try to create some sort of crazy rule checking to make sure that they were to be inserted into that particular page.
The component architecture allows for browser resources. These get a special ++resource++ url and have the option to be assigned to one particular browser layer. "Great", I thought, "My view implements its own layer, IGlowormLayer. I can use that."
But wait... Sticking my layer-constricted "++resource++gloworm.css" into portal_css makes Plone very unhappy.
So here's the workaround... Create a new viewlet, assigning it to plone.htmlhead (which, as the name intimates, contains content inserted into the HTML <head> tag of the page). I'll also specify that it belongs to our specific view layer, in this case, IGlowormLayer.
<browser:viewlet
name="glowormHtmlHeadIncludes"
manager="plone.app.layout.viewlets.intetorfaces.IHtmlHead"
class=".viewlets.GlowormHtmlHeadIncludes"
layer=".interfaces.IGlowormLayer"
permission="cmf.ManagePortal"
/>
I wanted to make sure that it was inserted after the content coming from portal_css, it should appear after the plone.resourceregistries viewlet.
<?xml version="1.0"?>
<object>
<order manager="plone.htmlhead" skinname="*">
<viewlet name="plone.resourceregistries" />
<viewlet name="glowormHtmlHeadIncludes" />
</order>
</object>
(see my previous post about viewlet ordering for more information about why I'm including the already-existing plone.resourceregistries viewlet in here.)
Next, I define my viewlet class. The template will need the site's base URL to call the resource files, so I'll define that in the update method.
class GlowormHtmlHeadIncludes(ViewletBase):
index = ViewPageTemplateFile('glowormHtmlHeadIncludes.pt')
def update(self):
portal_state = getMultiAdapter((self.context, self.request), name='plone_portal_state')
self.baseurl = portal_state.portal_url()
Now, the template...
<style type="text/css"
media="screen"
tal:content="structure string:<!-- @import url(${view/baseurl}/++resource++gloworm.css); -->">
</style>
<link rel="kinetic-stylesheet"
type="text/css"
tal:attributes="href string:${view/baseurl}/++resource++gloworm.kss" />
And that'll do it. My resources will show up in the HTML <head> only for views implementing the IGlowormLayer browser layer.
The main disadvantage of this method is that I lose out on ResourceRegistries' minifying and merging capabilities. In the case of GloWorm, these resources aren't used for any public views, so I felt it was not something warranting much concern.

Keep 'em coming ...
I'm really liking these tidbits that have shown up in my RSS feeds the last 2 days. These will likely be very helpful at some point in my near future. Thanks!
joe deluca