Breadcrumbs *********** We use an adapter approach to get a flexible system of creating and retrieving breadcrumbs for certain views. How to get existing breadcrumbs =============================== We create a company to check the breadcrumb functionality. >>> from waeup.ikoba.app import Company >>> root = getRootFolder() >>> root['app'] = Company() >>> app = root['app'] The quick way (if you have a viewname) -------------------------------------- We can get an ordered list of breadcrumbs for a given context and viewname: >>> from waeup.ikoba.browser.breadcrumbs import getBreadcrumbList >>> blist1 = getBreadcrumbList(app, 'index') >>> blist1 [] A slightly more extensive list for the datacenter: >>> blist2 = getBreadcrumbList(app['datacenter'], 'index') >>> from pprint import pprint >>> pprint(blist2) [, , ] We get a breadcrumb for company, administration area and data center in that order. Each breadcrumb provides a ``title``, ``context``, and ``viewname`` attribute, so that views and page templates can easily create markup out of it: >>> pprint([(x.title) for x in blist2]) [u'Home', u'Administration', u'Data Center'] >>> pprint([(x.context, x.viewname) for x in blist2]) [(, 'index'), (, 'administration'), (, 'index')] The administration area breadcrumb might be a surprise, as there is no equivalent object in the ZODB. In fact the administration area is only a certain view (the 'administration' view) on the company object. We will show below, how you can define breadcrumbs this way. As a rule of thumb you should know, that every breadcrumb is not only attached for a certain kind of object, but for a tuple of context object and a viewname. This way we can make sure, that there are different breadcrumb lists generated for instance for the administration view of Company instances and the index view. While the first should look something like:: Home -> Administration we expect for the latter only:: Home Another quick way (if you have a complete view) ----------------------------------------------- Another quick approach for use in viewlets, views and similar contexts, where you usually have a view available but not a viewname, is to use ``getBreadcrumbListForView``. We create a view (a page in this case, but pages are views) on the user administration index page: >>> from zope.publisher.browser import TestRequest >>> from zope.component import getMultiAdapter >>> page = getMultiAdapter((app['users'], TestRequest()), name='index') Now we can get the breadcrumbs for this view: >>> from waeup.ikoba.browser.breadcrumbs import getBreadcrumbListForView >>> blist3 = getBreadcrumbListForView(page) >>> [x.title for x in blist3] [u'Home', u'Administration', u'Officers'] ..note:: This works only for views created with grokcore.component (i.e. Grok Views, Pages, etc.) but not for simple Zope3 views, as the viewname can not easily be extracted from the view for the latter ones. Type-safe retrieval of breadcrumbs for a view --------------------------------------------- If you want to make sure, that a view for which you want to get breadcrumbs is really a Grok view, then you can use the IBreadcrumbContainer adapter. This one adapts only grok views. The returned breadcrumb container supports iteration: >>> from waeup.ikoba.browser.interfaces import IBreadcrumbContainer >>> mybccontainer = IBreadcrumbContainer(page) >>> [x.title for x in mybccontainer] [u'Home', u'Administration', u'Officers'] It is, however, not a real Python list but only an iterable container. You can also get the real list of breadcrumbs: >>> pprint(mybccontainer.getList()) [<...breadcrumbs.CompanyBreadcrumb object at 0x...>, <...breadcrumbs.AdministrationBreadcrumb object at 0x...>, <...breadcrumbs.UsersContainerBreadcrumb object at 0x...>] The general details ------------------- Now we can get breadcrumbs for contexts and view names. For example a breadcrumb for the 'index' view of our Company object: >>> from zope.component import getAdapter >>> from waeup.ikoba.browser.interfaces import IBreadcrumb >>> b1 = getAdapter(app, IBreadcrumb, 'index') >>> b1 Breadcrumb objects provide a title: >>> b1.title u'Home' a parent (None for the app object): >>> b1.parent is None True and a viewname: >>> b1.viewname 'index' Using a context and a viewname, one can easily compute a complete URL in views, viewlets, etc. >>> b2 = getAdapter(app['datacenter'], IBreadcrumb, 'index') >>> b2 >>> b2.title u'Data Center' >>> b2.viewname 'index' As the data center is not root of the hierarchy, it provides a non-trivial parent: >>> b2.parent (, 'administration') This result denotes a new context object (the Company instance we created above) and a view name ('administration'). Normally, the view name would be 'index', but as some administrative components should look to the user as if they were grouped in a common subarea of the root (the administration area), we can also set a different view than the default as target. When we like to receive the breadcrumb for this parent, we can do so as above: >>> context, viewname = b2.parent >>> b3 = getAdapter(context, IBreadcrumb, viewname) >>> b3 As you can see, we get an AdministrationBreadcrumb, although the context object, for which the breadcrumb was created is also the Company instance as above: >>> b3.context is b1.context True This administration area now has still a parent, the front-page: >>> context, viewname = b3.parent >>> context, viewname (, 'index') We create last breadcrumb: >>> b4 = getAdapter(context, IBreadcrumb, viewname) This is finally the last piece, the breadcrumb which links to the front page. It has no parent: >>> b4.parent is None True How to create new breadcrumbs ============================= Breadcrumbs are looked up as named adapters. This means, you must create a named adapter with the following specifications: * Derive your breadcrumb class from ``Breadcrumb`` class. This will save you lots of work. * The adapter context (``grok.context``) must be the object type you want to provide a breadcrumb for. So, if you want to write a Breadcrumb adapter for objects providing ``IMyFunnyType``, write:: grok.context(IMyFunnyType) * The adapter name (``grok.name``) must be the same as the name of the view, for which the breadcrumb component should be registered. If you want to write a breadcrumb component for the 'index' view of some content type and derive your class from Breadcrumb, then you can skip this step as 'index' is the default name. * Set a ``title`` attribute to give the text that should be displayed. Use something like:: @property def title(self): return self.some_computed_title() if you want computed titles.