Custom traversal and page templates
Question
Using Marius Gedminas's excellent blog post, I have created a custom traverser for a folder in my site.
This allows me to show: http://foo.com/folder/random_id
Instead of: http://foo.com/folder/object.html?id=random_id
The configuration side works great, I can catch the random_ids and search through my messages for the correct one, ready to display.
My problem is that I'm unsure how to then display the data via my usual page templates - at the TODO point in his original code ;)
if name == 'mycalendar':
mycalendar = ... # TODO: do something to get the appropriate object
return mycalendar
Usually I'd use something similar to:
class Test(BrowserPage):
template = ViewPageTemplateFile('atest.pt')
def __call__(self):
return self.template()
But I can't work out how to do this correctly in the context of the custom traversal.
UPDATE: To be clear I want to avoid adding anything else to the url (No: http://foo.com/folder/random_id/read).
I don't need the view to be available via any other address (No: http://foo.com/folder/read)
The ZCML for the view I'd like to use is:
<browser:page
for="foo.interfaces.IFooFolderContainer"
name="read"
template="read.pt"
permission="zope.ManageContent"
/>
I'm guessing (on further advice), something along the lines of:
return getMultiAdapter((mycalendar, self.request), IPageTemplate, name=u'read')
Or even a default view for the object type (a dict in this case) that's being returned:
<browser:page
for="dict"
name="read"
template="read.pt"
permission="zope.ManageContent"
/>
Solution
It would be easier to answer your question if you showed what your custom traverser is doing.
Essentially, you want something like this:
def publishTraverse(self, request, name):
if name in self.context:
return MyMessageView(self.context[name], request)
# fall back to views such as index.html
view = queryMultiAdapter((self.context, request), name=name)
if view is not None:
return view
# give up and return a 404 Not Found error page
raise NotFound(self.context, name, request)
where MyMessageView can be something as simple as
class MyMessageView(BrowserPage):
__call__ = ViewPageTemplateFile('read.pt')
Disclaimer: I'm not sure if the view you instantiate directly will be protected by a security wrapper; make sure your functional tests ensure anonymous users can't view messages if that's what you want.
OTHER TIPS
If you end up at a proper object with your custom traverser, you can just tack on the template name and user "context" in that template. So http://foo.com/folder/random_id/my_template
and in the template do the normal <h1 tal:content="context/title" />
stuff.
IIUC, what you want is to render the 'read' view when somebody requests /folder/random_id. If that's the case, all you need to do is make your traversal return an object (IFolderContent, maybe) representing a random_id and specify the 'view' page as the defaultView for IFolderContent.
The defaultView is needed because there's no view specified for the random_id object in your URL.