tags:

views:

133

answers:

3

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%5Fid/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"
/>
A: 

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.

Reinout van Rees
I want to set the template via Python, avoiding adding anything to the path if I can.
Jon Hadley
A: 

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.

Guilherme Salgado
That would have been the 'proper' way to do it - the object should have had an interface from the start - but adding one now would involve some fairly nasty generations.
Jon Hadley
+1  A: 

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.

Marius Gedminas
Yup, this worked as you suggested - although I'd ended up using the following before you replied, which does similar - but is messier/hackier: return getMultiAdapter((self.context[name], self.request), name=u'read') <browser:defaultView for="persistent.dict.PersistentDict" name="read" /> <browser:page for="persistent.dict.PersistentDict" name="read" template="read.pt" permission="zope.ManageContent" />
Jon Hadley