Merge lp:~salgado/launchpad/layer-specific-navigation into lp:launchpad

Proposed by Guilherme Salgado
Status: Merged
Approved by: Paul Hummer
Approved revision: no longer in the source branch.
Merged at revision: 11352
Proposed branch: lp:~salgado/launchpad/layer-specific-navigation
Merge into: lp:launchpad
Diff against target: 208 lines (+127/-11)
4 files modified
lib/canonical/launchpad/configure.zcml (+6/-0)
lib/canonical/launchpad/doc/navigation.txt (+9/-1)
lib/canonical/launchpad/webapp/metazcml.py (+5/-10)
lib/canonical/launchpad/webapp/tests/test_navigation.py (+107/-0)
To merge this branch: bzr merge lp:~salgado/launchpad/layer-specific-navigation
Reviewer Review Type Date Requested Status
Paul Hummer (community) code Approve
Review via email: mp+32358@code.launchpad.net

Commit message

Make it possible to register navigation classes for a specific layer.

To post a comment you must log in.
Revision history for this message
Paul Hummer (rockstar) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/configure.zcml'
2--- lib/canonical/launchpad/configure.zcml 2010-07-14 09:58:11 +0000
3+++ lib/canonical/launchpad/configure.zcml 2010-08-13 15:29:47 +0000
4@@ -96,6 +96,12 @@
5 classes="LaunchpadRootNavigation"
6 />
7
8+ <browser:navigation
9+ module="canonical.launchpad.browser"
10+ classes="LaunchpadRootNavigation"
11+ layer="zope.publisher.interfaces.xmlrpc.IXMLRPCRequest"
12+ />
13+
14 <!-- Register a handler to fix things up just before the application
15 starts (and after zcml has been processed). -->
16 <subscriber handler=".webapp.initialization.handle_process_start" />
17
18=== modified file 'lib/canonical/launchpad/doc/navigation.txt'
19--- lib/canonical/launchpad/doc/navigation.txt 2010-08-02 02:13:52 +0000
20+++ lib/canonical/launchpad/doc/navigation.txt 2010-08-13 15:29:47 +0000
21@@ -234,7 +234,7 @@
22
23 == ZCML for browser:navigation ==
24
25-The zcml processor `browser:navigation` processes navigation classes.
26+The zcml processor `browser:navigation` registers navigation classes.
27
28 >>> class ThingSetView:
29 ...
30@@ -266,6 +266,14 @@
31 ... </configure>
32 ... """)
33
34+Once registered, we can look the navigation up using getMultiAdapter().
35+
36+ >>> from zope.component import getMultiAdapter
37+ >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest
38+ >>> from zope.publisher.interfaces.browser import IBrowserPublisher
39+ >>> navigation = getMultiAdapter(
40+ ... (thingset, LaunchpadTestRequest()), IBrowserPublisher, name='')
41+
42 This time, we get the view object for the page that was registered.
43
44 >>> navigation.publishTraverse(request, 'thingview')
45
46=== modified file 'lib/canonical/launchpad/webapp/metazcml.py'
47--- lib/canonical/launchpad/webapp/metazcml.py 2010-08-06 18:13:44 +0000
48+++ lib/canonical/launchpad/webapp/metazcml.py 2010-08-13 15:29:47 +0000
49@@ -19,7 +19,6 @@
50 from zope.interface import Interface, implements
51 from zope.publisher.interfaces.browser import (
52 IBrowserPublisher, IBrowserRequest, IDefaultBrowserLayer)
53-from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest
54 from zope.schema import TextLine
55 from zope.security.checker import Checker, CheckerPublic
56 from zope.security.interfaces import IPermission
57@@ -193,6 +192,10 @@
58 class INavigationDirective(IGlueDirective):
59 """Hook up traversal etc."""
60
61+ layer = GlobalInterface(
62+ title=u"The layer where this navigation is going to be available.",
63+ required=False)
64+
65
66 class IFeedsDirective(IGlueDirective):
67 """Hook up feeds."""
68@@ -267,7 +270,7 @@
69 layer=layer, class_=feedclass)
70
71
72-def navigation(_context, module, classes):
73+def navigation(_context, module, classes, layer=IDefaultBrowserLayer):
74 """Handler for the `INavigationDirective`."""
75 if not inspect.ismodule(module):
76 raise TypeError("module attribute must be a module: %s, %s" %
77@@ -281,19 +284,11 @@
78 for_ = [navclass.usedfor]
79
80 # Register the navigation as the traversal component.
81- layer = IDefaultBrowserLayer
82 provides = IBrowserPublisher
83 name = ''
84 view(_context, factory, layer, name, for_,
85 permission=PublicPermission, provides=provides,
86 allowed_interface=[IBrowserPublisher])
87- #view(_context, factory, layer, name, for_,
88- # permission=PublicPermission, provides=provides)
89-
90- # Also register the navigation as a traversal component for XMLRPC.
91- xmlrpc_layer = IXMLRPCRequest
92- view(_context, factory, xmlrpc_layer, name, for_,
93- permission=PublicPermission, provides=provides)
94
95
96 class InterfaceInstanceDispatcher:
97
98=== added file 'lib/canonical/launchpad/webapp/tests/test_navigation.py'
99--- lib/canonical/launchpad/webapp/tests/test_navigation.py 1970-01-01 00:00:00 +0000
100+++ lib/canonical/launchpad/webapp/tests/test_navigation.py 2010-08-13 15:29:47 +0000
101@@ -0,0 +1,107 @@
102+# Copyright 2010 Canonical Ltd. This software is licensed under the
103+# GNU Affero General Public License version 3 (see the file LICENSE).
104+
105+__metaclass__ = type
106+
107+from zope.component import ComponentLookupError, getMultiAdapter
108+from zope.configuration import xmlconfig
109+from zope.interface import implements, Interface
110+from zope.publisher.interfaces.browser import (
111+ IBrowserPublisher, IDefaultBrowserLayer)
112+from zope.testing.cleanup import cleanUp
113+
114+from canonical.launchpad.webapp import Navigation
115+
116+from lp.testing import TestCase
117+
118+
119+class TestNavigationDirective(TestCase):
120+
121+ def test_default_layer(self):
122+ # By default all navigation classes are registered for
123+ # IDefaultBrowserLayer.
124+ directive = """
125+ <browser:navigation
126+ module="%(this)s" classes="ThingNavigation"/>
127+ """ % dict(this=this)
128+ xmlconfig.string(zcml_configure % directive)
129+ navigation = getMultiAdapter(
130+ (Thing(), DefaultBrowserLayer()), IBrowserPublisher, name='')
131+ self.assertIsInstance(navigation, ThingNavigation)
132+
133+ def test_specific_layer(self):
134+ # If we specify a layer when registering a navigation class, it will
135+ # only be available on that layer.
136+ directive = """
137+ <browser:navigation
138+ module="%(this)s" classes="OtherThingNavigation"
139+ layer="%(this)s.IOtherLayer" />
140+ """ % dict(this=this)
141+ xmlconfig.string(zcml_configure % directive)
142+ self.assertRaises(
143+ ComponentLookupError,
144+ getMultiAdapter,
145+ (Thing(), DefaultBrowserLayer()), IBrowserPublisher, name='')
146+
147+ navigation = getMultiAdapter(
148+ (Thing(), OtherLayer()), IBrowserPublisher, name='')
149+ self.assertIsInstance(navigation, OtherThingNavigation)
150+
151+ def test_multiple_navigations_for_single_context(self):
152+ # It is possible to have multiple navigation classes for a given
153+ # context class as long as they are registered for different layers.
154+ directive = """
155+ <browser:navigation
156+ module="%(this)s" classes="ThingNavigation"/>
157+ <browser:navigation
158+ module="%(this)s" classes="OtherThingNavigation"
159+ layer="%(this)s.IOtherLayer" />
160+ """ % dict(this=this)
161+ xmlconfig.string(zcml_configure % directive)
162+
163+ navigation = getMultiAdapter(
164+ (Thing(), DefaultBrowserLayer()), IBrowserPublisher, name='')
165+ other_navigation = getMultiAdapter(
166+ (Thing(), OtherLayer()), IBrowserPublisher, name='')
167+ self.assertNotEqual(navigation, other_navigation)
168+
169+ def tearDown(self):
170+ TestCase.tearDown(self)
171+ cleanUp()
172+
173+
174+class DefaultBrowserLayer:
175+ implements(IDefaultBrowserLayer)
176+
177+
178+class IThing(Interface):
179+ pass
180+
181+
182+class Thing(object):
183+ implements(IThing)
184+
185+
186+class ThingNavigation(Navigation):
187+ usedfor = IThing
188+
189+
190+class OtherThingNavigation(Navigation):
191+ usedfor = IThing
192+
193+
194+class IOtherLayer(Interface):
195+ pass
196+
197+
198+class OtherLayer:
199+ implements(IOtherLayer)
200+
201+
202+this = "canonical.launchpad.webapp.tests.test_navigation"
203+zcml_configure = """
204+ <configure xmlns:browser="http://namespaces.zope.org/browser">
205+ <include package="canonical.launchpad.webapp" file="meta.zcml" />
206+ %s
207+ </configure>
208+ """