Merge lp:~mwhudson/launchpad/apoca-publish-code into lp:launchpad

Proposed by Michael Hudson-Doyle
Status: Merged
Approved by: Curtis Hovey
Approved revision: no longer in the source branch.
Merged at revision: 11202
Proposed branch: lp:~mwhudson/launchpad/apoca-publish-code
Merge into: lp:launchpad
Prerequisite: lp:~mwhudson/launchpad/code-publisher-in-lp.code
Diff against target: 2578 lines (+590/-364)
40 files modified
lib/canonical/configure.zcml (+8/-8)
lib/canonical/launchpad/doc/webapp-publication.txt (+3/-31)
lib/canonical/launchpad/layers.py (+0/-18)
lib/canonical/launchpad/webapp/meta.zcml (+1/-1)
lib/canonical/launchpad/webapp/metazcml.py (+29/-35)
lib/canonical/launchpad/webapp/servers.py (+7/-58)
lib/canonical/launchpad/webapp/tests/test_servers.py (+27/-42)
lib/canonical/launchpad/zcml/marketing.zcml (+12/-12)
lib/lp/answers/browser/configure.zcml (+2/-2)
lib/lp/answers/browser/tests/views.txt (+1/-1)
lib/lp/answers/configure.zcml (+5/-1)
lib/lp/answers/publisher.py (+41/-0)
lib/lp/answers/tests/test_publisher.py (+49/-0)
lib/lp/app/tests/test_help.py (+7/-5)
lib/lp/blueprints/browser/configure.zcml (+2/-2)
lib/lp/blueprints/browser/tests/test_specificationtarget.py (+3/-3)
lib/lp/blueprints/configure.zcml (+5/-1)
lib/lp/blueprints/publisher.py (+34/-0)
lib/lp/blueprints/tests/test_publisher.py (+39/-0)
lib/lp/bugs/browser/malone.py (+2/-2)
lib/lp/bugs/browser/tests/test_configure_bugtracker_links.py (+2/-1)
lib/lp/bugs/configure.zcml (+5/-1)
lib/lp/bugs/publisher.py (+34/-0)
lib/lp/bugs/tests/test_publisher.py (+39/-0)
lib/lp/code/tests/test_publisher.py (+0/-6)
lib/lp/registry/browser/configure.zcml (+19/-19)
lib/lp/translations/browser/configure.zcml (+101/-101)
lib/lp/translations/browser/tests/language-views.txt (+1/-1)
lib/lp/translations/browser/tests/poexport-request-views.txt (+1/-1)
lib/lp/translations/browser/tests/pofile-views.txt (+1/-1)
lib/lp/translations/browser/tests/potemplate-views.txt (+1/-1)
lib/lp/translations/browser/tests/test_translationimportqueueentry.py (+2/-1)
lib/lp/translations/browser/tests/translationimportqueue-views.txt (+2/-1)
lib/lp/translations/browser/tests/translationmessage-views.txt (+1/-1)
lib/lp/translations/browser/tests/translator-views.txt (+3/-3)
lib/lp/translations/browser/translations.py (+3/-2)
lib/lp/translations/configure.zcml (+5/-1)
lib/lp/translations/doc/rosetta-karma.txt (+1/-1)
lib/lp/translations/publisher.py (+42/-0)
lib/lp/translations/tests/test_publisher.py (+50/-0)
To merge this branch: bzr merge lp:~mwhudson/launchpad/apoca-publish-code
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code Approve
Review via email: mp+30490@code.launchpad.net

Commit message

Move application specific publishing code into lp.$application.publisher modules

Description of the change

Following on from lp:~mwhudson/launchpad/code-publisher-in-lp.code, this branch moves all application specific publication code into lp.$application.publisher modules.

I'm afraid I expect it's a bit of a beast to review -- a mix of a load of literally mechanical import fixups and a little bit of deep stuff. Let me know if you want me to prepare this branch in another, easier to review, way.

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

well for starters, please don't add

> +def test_suite():
> + return unittest.TestLoader().loadTestsFromName(__name__)

:P

-Rob

Revision history for this message
Curtis Hovey (sinzui) wrote :

This looks fine to land after you very that make lint is happy. I think the imports of unittest are no longer needed in a few modules.

/me is reminded that if he could set the alias to /blueprint, he would fix the project name.

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/configure.zcml'
2--- lib/canonical/configure.zcml 2010-07-22 01:48:51 +0000
3+++ lib/canonical/configure.zcml 2010-07-22 20:59:01 +0000
4@@ -85,14 +85,14 @@
5 <browser:defaultView
6 for="canonical.launchpad.interfaces.ILaunchpadRoot"
7 name="specs"
8- layer="canonical.launchpad.layers.BlueprintsLayer"
9+ layer="lp.blueprints.publisher.BlueprintsLayer"
10 />
11 <browser:page
12 name=""
13 for="canonical.launchpad.interfaces.ILaunchpadRoot"
14 class="canonical.launchpad.browser.launchpad.LaunchpadImageFolder"
15 permission="zope.Public"
16- layer="canonical.launchpad.layers.BlueprintsLayer"
17+ layer="lp.blueprints.publisher.BlueprintsLayer"
18 />
19
20 <!-- virtual host: code -->
21@@ -113,42 +113,42 @@
22 <browser:defaultView
23 for="canonical.launchpad.interfaces.ILaunchpadRoot"
24 name="translations"
25- layer="canonical.launchpad.layers.TranslationsLayer"
26+ layer="lp.translations.publisher.TranslationsLayer"
27 />
28 <browser:page
29 name=""
30 for="canonical.launchpad.interfaces.ILaunchpadRoot"
31 class="canonical.launchpad.browser.launchpad.LaunchpadImageFolder"
32 permission="zope.Public"
33- layer="canonical.launchpad.layers.TranslationsLayer"
34+ layer="lp.translations.publisher.TranslationsLayer"
35 />
36
37 <!-- virtual host: bugs -->
38 <browser:defaultView
39 for="canonical.launchpad.interfaces.ILaunchpadRoot"
40 name="bugs"
41- layer="canonical.launchpad.layers.BugsLayer"
42+ layer="lp.bugs.publisher.BugsLayer"
43 />
44 <browser:page
45 name=""
46 for="canonical.launchpad.interfaces.ILaunchpadRoot"
47 class="canonical.launchpad.browser.launchpad.LaunchpadImageFolder"
48 permission="zope.Public"
49- layer="canonical.launchpad.layers.BugsLayer"
50+ layer="lp.bugs.publisher.BugsLayer"
51 />
52
53 <!-- virtual host: answers -->
54 <browser:defaultView
55 for="canonical.launchpad.interfaces.ILaunchpadRoot"
56 name="questions"
57- layer="canonical.launchpad.layers.AnswersLayer"
58+ layer="lp.answers.publisher.AnswersLayer"
59 />
60 <browser:page
61 name=""
62 for="canonical.launchpad.interfaces.ILaunchpadRoot"
63 class="canonical.launchpad.browser.launchpad.LaunchpadImageFolder"
64 permission="zope.Public"
65- layer="canonical.launchpad.layers.AnswersLayer"
66+ layer="lp.answers.publisher.AnswersLayer"
67 />
68
69 <include package="canonical.widgets" />
70
71=== modified file 'lib/canonical/launchpad/doc/webapp-publication.txt'
72--- lib/canonical/launchpad/doc/webapp-publication.txt 2010-07-20 10:21:01 +0000
73+++ lib/canonical/launchpad/doc/webapp-publication.txt 2010-07-22 20:59:01 +0000
74@@ -356,30 +356,14 @@
75 ProtocolErrorPublication: status=405
76 Allow: GET HEAD POST
77
78- >>> print_request_and_publication('answers.launchpad.dev')
79- AnswersBrowserRequest
80- AnswersPublication
81-
82 >>> print_request_and_publication('api.launchpad.dev')
83 WebServiceClientRequest
84 WebServicePublication
85
86- >>> print_request_and_publication('blueprints.launchpad.dev')
87- BlueprintBrowserRequest
88- BlueprintPublication
89-
90- >>> print_request_and_publication('bugs.launchpad.dev')
91- BugsBrowserRequest
92- BugsPublication
93-
94 >>> print_request_and_publication('feeds.launchpad.dev')
95 FeedsBrowserRequest
96 FeedsPublication
97
98- >>> print_request_and_publication('translations.launchpad.dev')
99- TranslationsBrowserRequest
100- TranslationsPublication
101-
102 The web service RequestPublicationFactory responds to the six most
103 common HTTP methods, but it will only accept a MIME type of
104 application/json.
105@@ -404,27 +388,15 @@
106 WebServiceClientRequest
107 WebServicePublication
108
109-When a request for '/api' is made to one of the application virtualhost,
110-it is also handled by the web service request and publication:
111+When a request for '/api' is made to one of the application
112+virtualhosts, such as the application root, it is also handled by the
113+web service request and publication:
114
115 >>> print_request_and_publication(
116 ... 'launchpad.dev', method='GET',
117 ... extra_environment={'PATH_INFO': '/api'})
118 WebServiceClientRequest
119 WebServicePublication
120- >>> for subdomain in [
121- ... 'answers', 'blueprints', 'bugs', 'translations']:
122- ... print_request_and_publication(
123- ... '%s.launchpad.dev' % subdomain, method='GET',
124- ... extra_environment={'PATH_INFO': '/api'})
125- WebServiceClientRequest
126- WebServicePublication
127- WebServiceClientRequest
128- WebServicePublication
129- WebServiceClientRequest
130- WebServicePublication
131- WebServiceClientRequest
132- WebServicePublication
133
134 Requests for '/api' on other hosts like feeds are handled like
135 other requests on these hosts:
136
137=== modified file 'lib/canonical/launchpad/layers.py'
138--- lib/canonical/launchpad/layers.py 2010-07-16 15:35:45 +0000
139+++ lib/canonical/launchpad/layers.py 2010-07-22 20:59:01 +0000
140@@ -28,23 +28,6 @@
141 """The `LaunchpadLayer` layer."""
142
143
144-class TranslationsLayer(LaunchpadLayer):
145- """The `TranslationsLayer` layer."""
146-
147-
148-class BugsLayer(LaunchpadLayer):
149- """The `BugsLayer` layer."""
150-
151-
152-class BlueprintLayer(LaunchpadLayer):
153- """The `BlueprintLayer` layer."""
154-BlueprintsLayer = BlueprintLayer
155-
156-
157-class AnswersLayer(LaunchpadLayer):
158- """The `AnswersLayer` layer."""
159-
160-
161 class DebugLayer(Interface):
162 """The `DebugLayer` layer.
163
164@@ -83,4 +66,3 @@
165
166 class WebServiceLayer(IWebServiceLayer, LaunchpadLayer):
167 """The layer for web service requests."""
168-
169
170=== modified file 'lib/canonical/launchpad/webapp/meta.zcml'
171--- lib/canonical/launchpad/webapp/meta.zcml 2010-07-16 15:13:50 +0000
172+++ lib/canonical/launchpad/webapp/meta.zcml 2010-07-22 20:59:01 +0000
173@@ -72,7 +72,7 @@
174 <directive
175 namespace="http://namespaces.zope.org/zope"
176 name="publisher"
177- schema="canonical.launchpad.webapp.metazcml.ILaunchpadPublicationDirective"
178+ schema="zope.app.publication.metadirectives.IRequestPublicationDirective"
179 handler="canonical.launchpad.webapp.metazcml.launchpadPublisher"
180 />
181
182
183=== modified file 'lib/canonical/launchpad/webapp/metazcml.py'
184--- lib/canonical/launchpad/webapp/metazcml.py 2010-07-16 15:35:45 +0000
185+++ lib/canonical/launchpad/webapp/metazcml.py 2010-07-22 20:59:01 +0000
186@@ -11,7 +11,6 @@
187 from zope.app.file.image import Image
188 from zope.app.pagetemplate.engine import TrustedEngine
189 from zope.app.publication.metaconfigure import publisher
190-from zope.app.publication.metadirectives import IRequestPublicationDirective
191 from zope.component import getUtility
192 from zope.component.security import PublicPermission
193 from zope.component.zcml import adapter, handler, utility, view
194@@ -21,7 +20,7 @@
195 from zope.publisher.interfaces.browser import (
196 IBrowserPublisher, IBrowserRequest, IDefaultBrowserLayer)
197 from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest
198-from zope.schema import Int, TextLine
199+from zope.schema import TextLine
200 from zope.security.checker import Checker, CheckerPublic
201 from zope.security.interfaces import IPermission
202 from zope.security.permission import Permission
203@@ -35,7 +34,6 @@
204 from z3c.ptcompat.zcml import page_directive as original_page
205 from z3c.ptcompat.zcml import pages_directive as original_pages
206
207-from canonical.config import config
208 from canonical.launchpad.layers import FeedsLayer
209 from canonical.launchpad.webapp.interfaces import (
210 IApplicationMenu, IAuthorization, ICanonicalUrlData, IContextMenu,
211@@ -48,10 +46,12 @@
212
213 module = GlobalObject(title=u'module', required=True)
214
215+
216 def _isAuthorization(module_member):
217 return (type(module_member) is type and
218 IAuthorization.implementedBy(module_member))
219
220+
221 def authorizations(_context, module):
222 if not inspect.ismodule(module):
223 raise TypeError("module attribute must be a module: %s, %s" %
224@@ -100,6 +100,7 @@
225 raise RuntimeError(
226 "unrecognised discriminator name", name)
227
228+
229 class SecuredUtilityDirective:
230
231 def __init__(self, _context, provides, class_=None, component=None,
232@@ -131,11 +132,10 @@
233 # of the zcml.
234 checker = Checker(
235 self.permission_collector.get_permissions,
236- self.permission_collector.set_permissions
237- )
238+ self.permission_collector.set_permissions)
239 component = ProxyFactory(self.component, checker=checker)
240- utility(self._context, self.provides, component=component,
241- name=self.name)
242+ utility(
243+ self._context, self.provides, component=component, name=self.name)
244 return ()
245
246
247@@ -144,29 +144,24 @@
248
249 for_ = GlobalObject(
250 title=u"Specification of the object that has this canonical url",
251- required=True
252- )
253+ required=True)
254
255 urldata = GlobalObject(
256 title=u"Adapter to ICanonicalUrlData for this object.",
257- required=False
258- )
259+ required=False)
260
261 path_expression = TextLine(
262 title=u"TALES expression that evaluates to the path"
263 " relative to the parent object.",
264- required=False
265- )
266+ required=False)
267
268 attribute_to_parent = PythonIdentifier(
269 title=u"Name of the attribute that gets you to the parent object",
270- required=False
271- )
272+ required=False)
273
274 parent_utility = GlobalObject(
275 title=u"Interface of the utility that is the parent of the object",
276- required=False
277- )
278+ required=False)
279
280 rootsite = PythonIdentifier(
281 title=u"Name of the site this URL has as its root."
282@@ -182,14 +177,12 @@
283 generic mechanism, what that 'something' is isn't important.
284 """
285 module = GlobalObject(
286- title=u"Module in which the classes are found."
287- )
288+ title=u"Module in which the classes are found.")
289
290 classes = Tokens(
291 value_type=PythonIdentifier(),
292 title=u"Space separated list of classes to register.",
293- required=True
294- )
295+ required=True)
296
297
298 class IMenusDirective(IGlueDirective):
299@@ -208,13 +201,11 @@
300
301 for_ = GlobalObject(
302 title=u"Specification of the object that has this favicon",
303- required=True
304- )
305+ required=True)
306
307 file = Path(
308 title=u"Path to the image file",
309- required=True
310- )
311+ required=True)
312
313
314 def menus(_context, module, classes):
315@@ -307,6 +298,7 @@
316 class InterfaceInstanceDispatcher:
317 """Dispatch getitem on names that appear in the interface to the instance.
318 """
319+
320 def __init__(self, interface, instance):
321 self.interface = interface
322 self.instance = instance
323@@ -379,6 +371,7 @@
324 _for = for_
325 _compiled_path_expression = compiled_path_expression
326 rootsite = rootsite_
327+
328 @property
329 def inside(self):
330 return getattr(self.context, attribute_to_parent)
331@@ -387,6 +380,7 @@
332 _for = for_
333 _compiled_path_expression = compiled_path_expression
334 rootsite = rootsite_
335+
336 @property
337 def inside(self):
338 return getUtility(parent_utility)
339@@ -397,17 +391,18 @@
340
341
342 class FaviconRendererBase:
343-
344 # subclasses must provide a 'fileobj' member that has 'contentType'
345 # and 'data' attributes.
346
347 def __call__(self):
348- self.request.response.setHeader('Content-type',
349- self.file.contentType)
350+ self.request.response.setHeader(
351+ 'Content-type', self.file.contentType)
352 return self.file.data
353
354+
355 def favicon(_context, for_, file):
356 fileobj = Image(open(file, 'rb').read())
357+
358 class Favicon(FaviconRendererBase):
359 file = fileobj
360
361@@ -434,8 +429,7 @@
362 layer=IDefaultBrowserLayer, template=None, class_=None,
363 allowed_interface=None, allowed_attributes=None,
364 attribute='__call__', menu=None, title=None,
365- facet=None
366- ):
367+ facet=None):
368 """Like the standard 'page' directive, but with an added 'facet' optional
369 argument.
370
371@@ -503,7 +497,7 @@
372
373 for_ = GlobalObject(
374 title=u"Specification of the object that has the renamed page",
375- required=True )
376+ required=True)
377
378 layer = GlobalInterface(
379 title=u"The layer the renamed page is in.",
380@@ -532,6 +526,7 @@
381 def renamed_page(_context, for_, name, new_name, layer=IDefaultBrowserLayer,
382 rootsite=None):
383 """Will provide a `RedirectView` that will redirect to the new_name."""
384+
385 def renamed_factory(context, request):
386 return RenamedView(
387 context, request, new_name=new_name, rootsite=rootsite)
388@@ -656,15 +651,14 @@
389 utility(_context, ILaunchpadPermission, permission, name=id)
390
391
392-class ILaunchpadPublicationDirective(IRequestPublicationDirective):
393-
394- priorty = Int(required=False)
395-
396 _arbitrary_priority = 12
397
398
399 def launchpadPublisher(_context, name, factory, methods=['*'],
400 mimetypes=['*'], priority=None):
401+ # This overrides zope's definition of the <publisher> directive to supply
402+ # an arbitrary unique priority if none is explicitly supplied -- we don't
403+ # care about the priority in Launchpad but it needs to be unique.
404 global _arbitrary_priority
405 if priority is None:
406 _arbitrary_priority += 1
407
408=== modified file 'lib/canonical/launchpad/webapp/servers.py'
409--- lib/canonical/launchpad/webapp/servers.py 2010-07-22 01:48:51 +0000
410+++ lib/canonical/launchpad/webapp/servers.py 2010-07-22 20:59:01 +0000
411@@ -352,7 +352,7 @@
412 else:
413 request_factory = ProtocolErrorRequest
414 publication_factory = ProtocolErrorPublicationFactory(
415- 405, headers={'Allow':" ".join(self.methods)})
416+ 405, headers={'Allow': " ".join(self.methods)})
417 factories = (request_factory, publication_factory)
418
419 return factories
420@@ -614,6 +614,7 @@
421 """See `ISynchronizer`."""
422 pass
423
424+
425 class BrowserFormNG:
426 """Wrapper that provides IBrowserFormNG around a regular form dict."""
427
428@@ -680,6 +681,7 @@
429 def install(cls):
430 """Install the monkey patch."""
431 assert not cls.installed, "Monkey patch is already installed."
432+
433 def _getFormInput_single(self):
434 """Return the submitted form value.
435
436@@ -975,9 +977,7 @@
437 userid,
438 pageid,
439 referer,
440- user_agent
441- )
442- )
443+ user_agent))
444
445
446 http = wsgi.ServerType(
447@@ -1015,52 +1015,6 @@
448 class MainLaunchpadPublication(LaunchpadBrowserPublication):
449 """The publication used for the main Launchpad site."""
450
451-# ---- blueprint
452-
453-class BlueprintBrowserRequest(LaunchpadBrowserRequest):
454- implements(canonical.launchpad.layers.BlueprintLayer)
455-
456-class BlueprintPublication(LaunchpadBrowserPublication):
457- """The publication used for the Blueprint site."""
458-
459-# ---- translations
460-
461-class TranslationsPublication(LaunchpadBrowserPublication):
462- """The publication used for the Translations site."""
463-
464-class TranslationsBrowserRequest(LaunchpadBrowserRequest):
465- implements(canonical.launchpad.layers.TranslationsLayer)
466-
467- def __init__(self, body_instream, environ, response=None):
468- super(TranslationsBrowserRequest, self).__init__(
469- body_instream, environ, response)
470- # Some of the responses from translations vary based on language.
471- self.response.setHeader(
472- 'Vary', 'Cookie, Authorization, Accept-Language')
473-
474-# ---- bugs
475-
476-class BugsPublication(LaunchpadBrowserPublication):
477- """The publication used for the Bugs site."""
478-
479-class BugsBrowserRequest(LaunchpadBrowserRequest):
480- implements(canonical.launchpad.layers.BugsLayer)
481-
482-# ---- answers
483-
484-class AnswersPublication(LaunchpadBrowserPublication):
485- """The publication used for the Answers site."""
486-
487-class AnswersBrowserRequest(LaunchpadBrowserRequest):
488- implements(canonical.launchpad.layers.AnswersLayer)
489-
490- def __init__(self, body_instream, environ, response=None):
491- super(AnswersBrowserRequest, self).__init__(
492- body_instream, environ, response)
493- # Many of the responses from Answers vary based on language.
494- self.response.setHeader(
495- 'Vary', 'Cookie, Authorization, Accept-Language')
496-
497
498 class AccountPrincipalMixin:
499 """Mixin for publication that works with person-less accounts."""
500@@ -1326,10 +1280,10 @@
501
502 class PublicXMLRPCPublication(LaunchpadBrowserPublication):
503 """The publication used for public XML-RPC requests."""
504+
505 def handleException(self, object, request, exc_info, retry_allowed=True):
506 LaunchpadBrowserPublication.handleException(
507- self, object, request, exc_info, retry_allowed
508- )
509+ self, object, request, exc_info, retry_allowed)
510 OpStats.stats['xml-rpc faults'] += 1
511
512 def endRequest(self, request, object):
513@@ -1475,16 +1429,11 @@
514 factories = [
515 VWSHRP('mainsite', LaunchpadBrowserRequest, MainLaunchpadPublication,
516 handle_default_host=True),
517- VWSHRP('blueprints', BlueprintBrowserRequest, BlueprintPublication),
518- VWSHRP('translations', TranslationsBrowserRequest,
519- TranslationsPublication),
520- VWSHRP('bugs', BugsBrowserRequest, BugsPublication),
521- VWSHRP('answers', AnswersBrowserRequest, AnswersPublication),
522 VHRP('feeds', FeedsBrowserRequest, FeedsPublication),
523 WebServiceRequestPublicationFactory(
524 'api', WebServiceClientRequest, WebServicePublication),
525 XMLRPCRequestPublicationFactory(
526- 'xmlrpc', PublicXMLRPCRequest, PublicXMLRPCPublication)
527+ 'xmlrpc', PublicXMLRPCRequest, PublicXMLRPCPublication),
528 ]
529
530 if config.launchpad.enable_test_openid_provider:
531
532=== modified file 'lib/canonical/launchpad/webapp/tests/test_servers.py'
533--- lib/canonical/launchpad/webapp/tests/test_servers.py 2010-07-14 14:11:15 +0000
534+++ lib/canonical/launchpad/webapp/tests/test_servers.py 2010-07-22 20:59:01 +0000
535@@ -21,9 +21,8 @@
536 from lp.testing import TestCase
537
538 from canonical.launchpad.webapp.servers import (
539- AnswersBrowserRequest, ApplicationServerSettingRequestFactory,
540- BugsBrowserRequest, BugsPublication, LaunchpadBrowserRequest,
541- TranslationsBrowserRequest, VHostWebServiceRequestPublicationFactory,
542+ ApplicationServerSettingRequestFactory, LaunchpadBrowserRequest,
543+ VHostWebServiceRequestPublicationFactory,
544 VirtualHostRequestPublicationFactory, WebServiceRequestPublicationFactory,
545 WebServiceClientRequest, WebServicePublication, WebServiceTestRequest)
546
547@@ -99,35 +98,42 @@
548
549 class TestVhostWebserviceFactory(WebServiceTestCase):
550
551+ class VHostTestBrowserRequest(LaunchpadBrowserRequest):
552+ pass
553+
554+ class VHostTestPublication(LaunchpadBrowserRequest):
555+ pass
556+
557 def setUp(self):
558 super(TestVhostWebserviceFactory, self).setUp()
559+ # XXX We have to use a real hostname.
560 self.factory = VHostWebServiceRequestPublicationFactory(
561- 'bugs', BugsBrowserRequest, BugsPublication)
562+ 'bugs', self.VHostTestBrowserRequest, self.VHostTestPublication)
563
564 def wsgi_env(self, path, method='GET'):
565 """Simulate a WSGI application environment."""
566 return {
567 'PATH_INFO': path,
568 'HTTP_HOST': 'bugs.launchpad.dev',
569- 'REQUEST_METHOD': method
570+ 'REQUEST_METHOD': method,
571 }
572
573 @property
574- def working_api_path(self):
575- """A path to the webservice API that should work every time."""
576+ def api_path(self):
577+ """Requests to this path should be treated as webservice requests."""
578 return '/' + getUtility(IWebServiceConfiguration).path_override
579
580 @property
581- def failing_api_path(self):
582- """A path that should not work with the webservice API."""
583+ def non_api_path(self):
584+ """Requests to this path should not be treated as webservice requests.
585+ """
586 return '/foo'
587
588 def test_factory_produces_webservice_objects(self):
589 """The factory should produce WebService request and publication
590 objects for requests to the /api root URL.
591 """
592- env = self.wsgi_env(
593- '/' + getUtility(IWebServiceConfiguration).path_override)
594+ env = self.wsgi_env(self.api_path)
595
596 # Necessary preamble and sanity check. We need to call
597 # the factory's canHandle() method with an appropriate
598@@ -153,7 +159,7 @@
599 specified in it's constructor if the request is not bound for the
600 web service.
601 """
602- env = self.wsgi_env('/foo')
603+ env = self.wsgi_env(self.non_api_path)
604 self.assert_(self.factory.canHandle(env),
605 "Sanity check: The factory should be able to handle requests.")
606
607@@ -162,12 +168,12 @@
608 # We need to unwrap the real request factory.
609 request_factory = wrapped_factory.requestfactory
610
611- self.assertEqual(request_factory, BugsBrowserRequest,
612- "Requests to normal paths should return a Bugs "
613+ self.assertEqual(request_factory, self.VHostTestBrowserRequest,
614+ "Requests to normal paths should return a VHostTest "
615 "request object.")
616 self.assertEqual(
617- publication_factory, BugsPublication,
618- "Requests to normal paths should return a Bugs "
619+ publication_factory, self.VHostTestPublication,
620+ "Requests to normal paths should return a VHostTest "
621 "publication object.")
622
623 def test_factory_processes_webservice_http_methods(self):
624@@ -177,7 +183,7 @@
625 allowed_methods = WebServiceRequestPublicationFactory.default_methods
626
627 for method in allowed_methods:
628- env = self.wsgi_env(self.working_api_path, method)
629+ env = self.wsgi_env(self.api_path, method)
630 self.assert_(self.factory.canHandle(env),
631 "Sanity check")
632 # Returns a tuple of (request_factory, publication_factory).
633@@ -198,7 +204,7 @@
634 denied_methods = set(ws_methods) - set(vhost_methods)
635
636 for method in denied_methods:
637- env = self.wsgi_env(self.failing_api_path, method)
638+ env = self.wsgi_env(self.non_api_path, method)
639 self.assert_(self.factory.canHandle(env),
640 "Sanity check")
641 # Returns a tuple of (request_factory, publication_factory).
642@@ -221,9 +227,6 @@
643 self.assert_(
644 self.factory.isWebServicePath('/api'),
645 "The factory should handle URLs that start with /api.")
646- self.assert_(
647- self.factory.isWebServicePath('/api/'),
648- "The factory should handle URLs that start with /api.")
649
650 self.assert_(
651 self.factory.isWebServicePath('/api/foo'),
652@@ -263,8 +266,10 @@
653 implements(IGenericCollection)
654
655 class MyRootResource(RootResource):
656+
657 def _build_top_level_objects(self):
658- return ({'foo' : (IGenericEntry, GenericCollection())}, {})
659+ return ({'foo': (IGenericEntry, GenericCollection())}, {})
660+
661 getGlobalSiteManager().registerUtility(
662 MyRootResource(), IServiceRootResource)
663
664@@ -385,26 +390,6 @@
665 self.assertEquals(self.request.getNearest(IThing), (None, None))
666
667
668-class TestAnswersBrowserRequest(TestCase):
669- """Tests for the Answers request class."""
670-
671- def test_response_should_vary_based_on_language(self):
672- request = AnswersBrowserRequest(StringIO.StringIO(''), {})
673- self.assertEquals(
674- request.response.getHeader('Vary'),
675- 'Cookie, Authorization, Accept-Language')
676-
677-
678-class TestTranslationsBrowserRequest(TestCase):
679- """Tests for the Translations request class."""
680-
681- def test_response_should_vary_based_on_language(self):
682- request = TranslationsBrowserRequest(StringIO.StringIO(''), {})
683- self.assertEquals(
684- request.response.getHeader('Vary'),
685- 'Cookie, Authorization, Accept-Language')
686-
687-
688 class TestLaunchpadBrowserRequest(TestCase):
689
690 def prepareRequest(self, form):
691
692=== modified file 'lib/canonical/launchpad/zcml/marketing.zcml'
693--- lib/canonical/launchpad/zcml/marketing.zcml 2010-07-16 15:40:14 +0000
694+++ lib/canonical/launchpad/zcml/marketing.zcml 2010-07-22 20:59:01 +0000
695@@ -26,14 +26,14 @@
696 <!-- Marketing material for Answers. -->
697 <browser:renamed-page
698 for="canonical.launchpad.webapp.interfaces.ILaunchpadApplication"
699- layer="canonical.launchpad.layers.AnswersLayer"
700+ layer="lp.answers.publisher.AnswersLayer"
701 name="+about"
702 new_name="+tour/community-support"
703 rootsite="mainsite"
704 />
705 <browser:renamed-page
706 for="canonical.launchpad.webapp.interfaces.ILaunchpadApplication"
707- layer="canonical.launchpad.layers.AnswersLayer"
708+ layer="lp.answers.publisher.AnswersLayer"
709 name="+faq"
710 new_name="+tour/community-support"
711 rootsite="mainsite"
712@@ -43,20 +43,20 @@
713 new_name="+tour/community-support"
714 rootsite="mainsite"
715 for="canonical.launchpad.webapp.interfaces.ILaunchpadApplication"
716- layer="canonical.launchpad.layers.AnswersLayer"
717+ layer="lp.answers.publisher.AnswersLayer"
718 />
719
720 <!-- Marketing material for Blueprints. -->
721 <browser:renamed-page
722 for="canonical.launchpad.webapp.interfaces.ILaunchpadApplication"
723- layer="canonical.launchpad.layers.BlueprintsLayer"
724+ layer="lp.blueprints.publisher.BlueprintsLayer"
725 name="+about"
726 new_name="+tour/feature-tracking"
727 rootsite="mainsite"
728 />
729 <browser:renamed-page
730 for="canonical.launchpad.webapp.interfaces.ILaunchpadApplication"
731- layer="canonical.launchpad.layers.BlueprintsLayer"
732+ layer="lp.blueprints.publisher.BlueprintsLayer"
733 name="+faq"
734 new_name="+tour/feature-tracking"
735 rootsite="mainsite"
736@@ -64,7 +64,7 @@
737 <browser:renamed-page
738 name="+tour"
739 for="canonical.launchpad.webapp.interfaces.ILaunchpadApplication"
740- layer="canonical.launchpad.layers.BlueprintsLayer"
741+ layer="lp.blueprints.publisher.BlueprintsLayer"
742 new_name="+tour/feature-tracking"
743 rootsite="mainsite"
744 />
745@@ -72,14 +72,14 @@
746 <!-- Marketing material for Bugs. -->
747 <browser:renamed-page
748 for="canonical.launchpad.webapp.interfaces.ILaunchpadApplication"
749- layer="canonical.launchpad.layers.BugsLayer"
750+ layer="lp.bugs.publisher.BugsLayer"
751 name="+about"
752 new_name="+tour/bugs"
753 rootsite="mainsite"
754 />
755 <browser:renamed-page
756 for="canonical.launchpad.webapp.interfaces.ILaunchpadApplication"
757- layer="canonical.launchpad.layers.BugsLayer"
758+ layer="lp.bugs.publisher.BugsLayer"
759 name="+faq"
760 new_name="+tour/bugs"
761 rootsite="mainsite"
762@@ -87,7 +87,7 @@
763 <browser:renamed-page
764 name="+tour"
765 for="canonical.launchpad.webapp.interfaces.ILaunchpadApplication"
766- layer="canonical.launchpad.layers.BugsLayer"
767+ layer="lp.bugs.publisher.BugsLayer"
768 new_name="+tour/bugs"
769 rootsite="mainsite"
770 />
771@@ -119,14 +119,14 @@
772 <!-- Marketing material for Translations. -->
773 <browser:renamed-page
774 for="canonical.launchpad.webapp.interfaces.ILaunchpadApplication"
775- layer="canonical.launchpad.layers.TranslationsLayer"
776+ layer="lp.translations.publisher.TranslationsLayer"
777 name="+about"
778 new_name="+tour/translation"
779 rootsite="mainsite"
780 />
781 <browser:renamed-page
782 for="canonical.launchpad.webapp.interfaces.ILaunchpadApplication"
783- layer="canonical.launchpad.layers.TranslationsLayer"
784+ layer="lp.translations.publisher.TranslationsLayer"
785 name="+faq"
786 new_name="+tour/translation"
787 rootsite="mainsite"
788@@ -134,7 +134,7 @@
789 <browser:renamed-page
790 name="+tour"
791 for="canonical.launchpad.webapp.interfaces.ILaunchpadApplication"
792- layer="canonical.launchpad.layers.TranslationsLayer"
793+ layer="lp.translations.publisher.TranslationsLayer"
794 new_name="+tour/translation"
795 rootsite="mainsite"
796 />
797
798=== modified file 'lib/lp/answers/browser/configure.zcml'
799--- lib/lp/answers/browser/configure.zcml 2009-11-22 17:28:49 +0000
800+++ lib/lp/answers/browser/configure.zcml 2010-07-22 20:59:01 +0000
801@@ -14,7 +14,7 @@
802 IQuestionCollection. -->
803 <browser:defaultView
804 for="lp.answers.interfaces.questioncollection.IQuestionCollection"
805- layer="canonical.launchpad.layers.AnswersLayer"
806+ layer="lp.answers.publisher.AnswersLayer"
807 name="+tickets"
808 />
809 <browser:page
810@@ -351,7 +351,7 @@
811
812 <browser:defaultView
813 for="canonical.launchpad.interfaces.IPerson"
814- layer="canonical.launchpad.layers.AnswersLayer"
815+ layer="lp.answers.publisher.AnswersLayer"
816 name="+questions"
817 />
818
819
820=== modified file 'lib/lp/answers/browser/tests/views.txt'
821--- lib/lp/answers/browser/tests/views.txt 2010-04-16 15:06:55 +0000
822+++ lib/lp/answers/browser/tests/views.txt 2010-07-22 20:59:01 +0000
823@@ -860,7 +860,7 @@
824 the question listing table. Products display the default columns of
825 Summary, Created, Submitter, Assignee, and Status.
826
827- >>> from canonical.launchpad.layers import AnswersLayer
828+ >>> from lp.answers.publisher import AnswersLayer
829 >>> from canonical.launchpad.testing.pages import (
830 ... extract_text, find_tag_by_id)
831
832
833=== modified file 'lib/lp/answers/configure.zcml'
834--- lib/lp/answers/configure.zcml 2010-02-17 11:19:42 +0000
835+++ lib/lp/answers/configure.zcml 2010-07-22 20:59:01 +0000
836@@ -10,6 +10,10 @@
837
838 <include package=".browser" />
839
840+ <publisher
841+ name="answers"
842+ factory="lp.answers.publisher.answers_request_publication_factory"/>
843+
844 <subscriber
845 for=".interfaces.question.IQuestion
846 lazr.lifecycle.interfaces.IObjectCreatedEvent"
847@@ -235,7 +239,7 @@
848
849
850 <lp:help-folder
851- folder="help" type="canonical.launchpad.layers.AnswersLayer" />
852+ folder="help" type="lp.answers.publisher.AnswersLayer" />
853 <adapter
854 name="answers"
855 provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
856
857=== added file 'lib/lp/answers/publisher.py'
858--- lib/lp/answers/publisher.py 1970-01-01 00:00:00 +0000
859+++ lib/lp/answers/publisher.py 2010-07-22 20:59:01 +0000
860@@ -0,0 +1,41 @@
861+# Copyright 2010 Canonical Ltd. This software is licensed under the
862+# GNU Affero General Public License version 3 (see the file LICENSE).
863+
864+"""Answers's custom publication."""
865+
866+__metaclass__ = type
867+__all__ = [
868+ 'AnswersBrowserRequest',
869+ 'AnswersLayer',
870+ 'answers_request_publication_factory',
871+ ]
872+
873+
874+from zope.interface import implements
875+from zope.publisher.interfaces.browser import (
876+ IBrowserRequest, IDefaultBrowserLayer)
877+
878+from canonical.launchpad.webapp.publication import LaunchpadBrowserPublication
879+from canonical.launchpad.webapp.servers import (
880+ LaunchpadBrowserRequest, VHostWebServiceRequestPublicationFactory)
881+
882+
883+class AnswersLayer(IBrowserRequest, IDefaultBrowserLayer):
884+ """The Answers layer."""
885+
886+
887+class AnswersBrowserRequest(LaunchpadBrowserRequest):
888+ """Instances of AnswersBrowserRequest provide `AnswersLayer`."""
889+ implements(AnswersLayer)
890+
891+ def __init__(self, body_instream, environ, response=None):
892+ super(AnswersBrowserRequest, self).__init__(
893+ body_instream, environ, response)
894+ # Many of the responses from Answers vary based on language.
895+ self.response.setHeader(
896+ 'Vary', 'Cookie, Authorization, Accept-Language')
897+
898+
899+def answers_request_publication_factory():
900+ return VHostWebServiceRequestPublicationFactory(
901+ 'answers', AnswersBrowserRequest, LaunchpadBrowserPublication)
902
903=== added file 'lib/lp/answers/tests/test_publisher.py'
904--- lib/lp/answers/tests/test_publisher.py 1970-01-01 00:00:00 +0000
905+++ lib/lp/answers/tests/test_publisher.py 2010-07-22 20:59:01 +0000
906@@ -0,0 +1,49 @@
907+# Copyright 2010 Canonical Ltd. This software is licensed under the
908+# GNU Affero General Public License version 3 (see the file LICENSE).
909+
910+"""Tests for answers's custom publications."""
911+
912+__metaclass__ = type
913+
914+import StringIO
915+
916+from canonical.config import config
917+from canonical.launchpad.layers import WebServiceLayer
918+from canonical.testing.layers import FunctionalLayer
919+
920+from lp.testing import TestCase
921+from lp.testing.publication import get_request_and_publication
922+
923+from lp.answers.publisher import AnswersLayer, AnswersBrowserRequest
924+
925+
926+class TestRegistration(TestCase):
927+ """Answers' publication customizations are installed correctly."""
928+
929+ layer = FunctionalLayer
930+
931+ def test_answers_request_provides_answers_layer(self):
932+ # The request constructed for requests to the answers hostname
933+ # provides AnswersLayer.
934+ request, publication = get_request_and_publication(
935+ host=config.vhost.answers.hostname)
936+ self.assertProvides(request, AnswersLayer)
937+
938+ def test_answers_host_has_api(self):
939+ # Requests to /api on the answers domain are treated as web service
940+ # requests.
941+ request, publication = get_request_and_publication(
942+ host=config.vhost.answers.hostname,
943+ extra_environment={'PATH_INFO': '/api/1.0'})
944+ # XXX MichaelHudson, 2010-07-20, bug=607664: WebServiceLayer only
945+ # actually provides WebServiceLayer in the sense of verifyObject after
946+ # traversal has started.
947+ self.assertTrue(WebServiceLayer.providedBy(request))
948+
949+ def test_response_should_vary_based_on_language(self):
950+ # Responses to requests to answers pages have the 'Vary' header set to
951+ # include Accept-Language.
952+ request = AnswersBrowserRequest(StringIO.StringIO(''), {})
953+ self.assertEquals(
954+ request.response.getHeader('Vary'),
955+ 'Cookie, Authorization, Accept-Language')
956
957=== modified file 'lib/lp/app/tests/test_help.py'
958--- lib/lp/app/tests/test_help.py 2010-07-22 01:48:51 +0000
959+++ lib/lp/app/tests/test_help.py 2010-07-22 20:59:01 +0000
960@@ -8,17 +8,19 @@
961
962 from zope.component import getUtility
963
964-
965-from canonical.testing.layers import FunctionalLayer
966-from canonical.launchpad.layers import (
967- AnswersLayer, BlueprintsLayer, BugsLayer, LaunchpadLayer,
968- TranslationsLayer)
969 from canonical.launchpad.testing.systemdocs import create_view
970 from canonical.launchpad.webapp.interfaces import ILaunchpadApplication
971+from canonical.launchpad.layers import LaunchpadLayer
972+from canonical.testing.layers import FunctionalLayer
973
974 from canonical.lazr.folder import ExportedFolder
975
976+from lp.answers.publisher import AnswersLayer
977+from lp.blueprints.publisher import BlueprintsLayer
978+from lp.bugs.publisher import BugsLayer
979 from lp.code.publisher import CodeLayer
980+from lp.translations.publisher import TranslationsLayer
981+
982
983 # The root of the tree
984 ROOT = os.path.realpath(
985
986=== modified file 'lib/lp/blueprints/browser/configure.zcml'
987--- lib/lp/blueprints/browser/configure.zcml 2010-02-17 11:19:42 +0000
988+++ lib/lp/blueprints/browser/configure.zcml 2010-07-22 20:59:01 +0000
989@@ -18,7 +18,7 @@
990 <browser:defaultView
991 for="lp.blueprints.interfaces.sprint.ISprint"
992 name="+specs"
993- layer="canonical.launchpad.layers.BlueprintLayer"/>
994+ layer="lp.blueprints.publisher.BlueprintsLayer"/>
995 <browser:url
996 for="lp.blueprints.interfaces.sprint.ISprint"
997 path_expression="name"
998@@ -562,7 +562,7 @@
999 <browser:defaultView
1000 for="lp.registry.interfaces.distroseries.IDistroSeries"
1001 name="+specs"
1002- layer="canonical.launchpad.layers.BlueprintLayer"/>
1003+ layer="lp.blueprints.publisher.BlueprintsLayer"/>
1004 <browser:page
1005 name="+addspec"
1006 for="lp.registry.interfaces.distroseries.IDistroSeries"
1007
1008=== modified file 'lib/lp/blueprints/browser/tests/test_specificationtarget.py'
1009--- lib/lp/blueprints/browser/tests/test_specificationtarget.py 2009-11-08 19:34:52 +0000
1010+++ lib/lp/blueprints/browser/tests/test_specificationtarget.py 2010-07-22 20:59:01 +0000
1011@@ -5,11 +5,11 @@
1012
1013 import unittest
1014
1015-from canonical.launchpad.layers import BlueprintLayer
1016 from canonical.testing.layers import DatabaseFunctionalLayer
1017
1018 from lp.blueprints.interfaces.specificationtarget import (
1019 IHasSpecifications, ISpecificationTarget)
1020+from lp.blueprints.publisher import BlueprintsLayer
1021 from lp.testing import login_person, TestCaseWithFactory
1022 from lp.testing.views import create_view
1023
1024@@ -58,7 +58,7 @@
1025 def verify_involvment(self, context):
1026 self.assertTrue(IHasSpecifications.providedBy(context))
1027 view = create_view(
1028- context, '+specs', layer=BlueprintLayer, principal=self.user)
1029+ context, '+specs', layer=BlueprintsLayer, principal=self.user)
1030 self.assertTrue(
1031 '<div id="involvement" class="portlet involvement">' in view())
1032
1033@@ -78,7 +78,7 @@
1034 context = self.factory.makePerson(name='pistachio')
1035 self.assertTrue(IHasSpecifications.providedBy(context))
1036 view = create_view(
1037- context, '+specs', layer=BlueprintLayer, principal=self.user)
1038+ context, '+specs', layer=BlueprintsLayer, principal=self.user)
1039 self.assertFalse(
1040 '<div id="involvement" class="portlet involvement">' in view())
1041
1042
1043=== modified file 'lib/lp/blueprints/configure.zcml'
1044--- lib/lp/blueprints/configure.zcml 2009-09-23 03:36:55 +0000
1045+++ lib/lp/blueprints/configure.zcml 2010-07-22 20:59:01 +0000
1046@@ -12,8 +12,12 @@
1047 <include
1048 package=".browser"/>
1049
1050+ <publisher
1051+ name="blueprints"
1052+ factory="lp.blueprints.publisher.blueprints_request_publication_factory"/>
1053+
1054 <lp:help-folder
1055- folder="help" type="canonical.launchpad.layers.BlueprintsLayer" />
1056+ folder="help" type="lp.blueprints.publisher.BlueprintsLayer" />
1057
1058 <!-- Sprint -->
1059
1060
1061=== added file 'lib/lp/blueprints/publisher.py'
1062--- lib/lp/blueprints/publisher.py 1970-01-01 00:00:00 +0000
1063+++ lib/lp/blueprints/publisher.py 2010-07-22 20:59:01 +0000
1064@@ -0,0 +1,34 @@
1065+# Copyright 2010 Canonical Ltd. This software is licensed under the
1066+# GNU Affero General Public License version 3 (see the file LICENSE).
1067+
1068+"""Blueprints' custom publication."""
1069+
1070+__metaclass__ = type
1071+__all__ = [
1072+ 'BlueprintsBrowserRequest',
1073+ 'BlueprintsLayer',
1074+ 'blueprints_request_publication_factory',
1075+ ]
1076+
1077+
1078+from zope.interface import implements
1079+from zope.publisher.interfaces.browser import (
1080+ IBrowserRequest, IDefaultBrowserLayer)
1081+
1082+from canonical.launchpad.webapp.publication import LaunchpadBrowserPublication
1083+from canonical.launchpad.webapp.servers import (
1084+ LaunchpadBrowserRequest, VHostWebServiceRequestPublicationFactory)
1085+
1086+
1087+class BlueprintsLayer(IBrowserRequest, IDefaultBrowserLayer):
1088+ """The Blueprints layer."""
1089+
1090+
1091+class BlueprintsBrowserRequest(LaunchpadBrowserRequest):
1092+ """Instances of BlueprintsBrowserRequest provide `BlueprintsLayer`."""
1093+ implements(BlueprintsLayer)
1094+
1095+
1096+def blueprints_request_publication_factory():
1097+ return VHostWebServiceRequestPublicationFactory(
1098+ 'blueprints', BlueprintsBrowserRequest, LaunchpadBrowserPublication)
1099
1100=== added file 'lib/lp/blueprints/tests/test_publisher.py'
1101--- lib/lp/blueprints/tests/test_publisher.py 1970-01-01 00:00:00 +0000
1102+++ lib/lp/blueprints/tests/test_publisher.py 2010-07-22 20:59:01 +0000
1103@@ -0,0 +1,39 @@
1104+# Copyright 2010 Canonical Ltd. This software is licensed under the
1105+# GNU Affero General Public License version 3 (see the file LICENSE).
1106+
1107+"""Tests for blueprints' custom publications."""
1108+
1109+__metaclass__ = type
1110+
1111+from canonical.config import config
1112+from canonical.launchpad.layers import WebServiceLayer
1113+from canonical.testing.layers import FunctionalLayer
1114+
1115+from lp.testing import TestCase
1116+from lp.testing.publication import get_request_and_publication
1117+
1118+from lp.blueprints.publisher import BlueprintsLayer
1119+
1120+
1121+class TestRegistration(TestCase):
1122+ """Blueprints's publication customizations are installed correctly."""
1123+
1124+ layer = FunctionalLayer
1125+
1126+ def test_blueprints_request_provides_blueprints_layer(self):
1127+ # The request constructed for requests to the blueprints hostname
1128+ # provides BlueprintsLayer.
1129+ request, publication = get_request_and_publication(
1130+ host=config.vhost.blueprints.hostname)
1131+ self.assertProvides(request, BlueprintsLayer)
1132+
1133+ def test_blueprints_host_has_api(self):
1134+ # Requests to /api on the blueprints domain are treated as web service
1135+ # requests.
1136+ request, publication = get_request_and_publication(
1137+ host=config.vhost.blueprints.hostname,
1138+ extra_environment={'PATH_INFO': '/api/1.0'})
1139+ # XXX MichaelHudson, 2010-07-20, bug=607664: WebServiceLayer only
1140+ # actually provides WebServiceLayer in the sense of verifyObject after
1141+ # traversal has started.
1142+ self.assertTrue(WebServiceLayer.providedBy(request))
1143
1144=== modified file 'lib/lp/bugs/browser/malone.py'
1145--- lib/lp/bugs/browser/malone.py 2009-09-04 15:32:52 +0000
1146+++ lib/lp/bugs/browser/malone.py 2010-07-22 20:59:01 +0000
1147@@ -13,7 +13,6 @@
1148 from zope.component import getUtility
1149 from zope.security.interfaces import Unauthorized
1150
1151-import canonical.launchpad.layers
1152
1153 from canonical.launchpad.webapp import (
1154 Link, Navigation, canonical_url, stepto)
1155@@ -25,6 +24,7 @@
1156 from lp.bugs.interfaces.bugtracker import IBugTrackerSet
1157 from lp.bugs.interfaces.cve import ICveSet
1158 from lp.bugs.interfaces.malone import IMaloneApplication
1159+from lp.bugs.publisher import BugsLayer
1160 from lp.registry.interfaces.distribution import IDistributionSet
1161 from lp.registry.interfaces.product import IProductSet
1162
1163@@ -33,7 +33,7 @@
1164
1165 usedfor = IMaloneApplication
1166
1167- newlayer = canonical.launchpad.layers.BugsLayer
1168+ newlayer = BugsLayer
1169
1170 @stepto('bugs')
1171 def bugs(self):
1172
1173=== modified file 'lib/lp/bugs/browser/tests/test_configure_bugtracker_links.py'
1174--- lib/lp/bugs/browser/tests/test_configure_bugtracker_links.py 2010-06-17 15:26:05 +0000
1175+++ lib/lp/bugs/browser/tests/test_configure_bugtracker_links.py 2010-07-22 20:59:01 +0000
1176@@ -8,9 +8,10 @@
1177 import unittest
1178
1179 from canonical.launchpad.ftests import ANONYMOUS, login
1180-from canonical.launchpad.layers import BugsLayer
1181 from canonical.testing import LaunchpadFunctionalLayer
1182
1183+from lp.bugs.publisher import BugsLayer
1184+
1185 from lp.testing import login_person, TestCaseWithFactory
1186 from lp.testing.views import create_initialized_view
1187
1188
1189=== modified file 'lib/lp/bugs/configure.zcml'
1190--- lib/lp/bugs/configure.zcml 2010-07-22 20:30:26 +0000
1191+++ lib/lp/bugs/configure.zcml 2010-07-22 20:59:01 +0000
1192@@ -12,8 +12,12 @@
1193 <include
1194 package=".browser"/>
1195
1196+ <publisher
1197+ name="bugs"
1198+ factory="lp.bugs.publisher.bugs_request_publication_factory"/>
1199+
1200 <lp:help-folder
1201- folder="help" type="canonical.launchpad.layers.BugsLayer" />
1202+ folder="help" type="lp.bugs.publisher.BugsLayer" />
1203
1204 <class
1205 class="canonical.launchpad.database.BugActivity">
1206
1207=== added file 'lib/lp/bugs/publisher.py'
1208--- lib/lp/bugs/publisher.py 1970-01-01 00:00:00 +0000
1209+++ lib/lp/bugs/publisher.py 2010-07-22 20:59:01 +0000
1210@@ -0,0 +1,34 @@
1211+# Copyright 2010 Canonical Ltd. This software is licensed under the
1212+# GNU Affero General Public License version 3 (see the file LICENSE).
1213+
1214+"""Bugs' custom publication."""
1215+
1216+__metaclass__ = type
1217+__all__ = [
1218+ 'BugsBrowserRequest',
1219+ 'BugsLayer',
1220+ 'bugs_request_publication_factory',
1221+ ]
1222+
1223+
1224+from zope.interface import implements
1225+from zope.publisher.interfaces.browser import (
1226+ IBrowserRequest, IDefaultBrowserLayer)
1227+
1228+from canonical.launchpad.webapp.publication import LaunchpadBrowserPublication
1229+from canonical.launchpad.webapp.servers import (
1230+ LaunchpadBrowserRequest, VHostWebServiceRequestPublicationFactory)
1231+
1232+
1233+class BugsLayer(IBrowserRequest, IDefaultBrowserLayer):
1234+ """The Bugs layer."""
1235+
1236+
1237+class BugsBrowserRequest(LaunchpadBrowserRequest):
1238+ """Instances of BugBrowserRequest provide `BugsLayer`."""
1239+ implements(BugsLayer)
1240+
1241+
1242+def bugs_request_publication_factory():
1243+ return VHostWebServiceRequestPublicationFactory(
1244+ 'bugs', BugsBrowserRequest, LaunchpadBrowserPublication)
1245
1246=== added file 'lib/lp/bugs/tests/test_publisher.py'
1247--- lib/lp/bugs/tests/test_publisher.py 1970-01-01 00:00:00 +0000
1248+++ lib/lp/bugs/tests/test_publisher.py 2010-07-22 20:59:01 +0000
1249@@ -0,0 +1,39 @@
1250+# Copyright 2010 Canonical Ltd. This software is licensed under the
1251+# GNU Affero General Public License version 3 (see the file LICENSE).
1252+
1253+"""Tests for bugs' custom publications."""
1254+
1255+__metaclass__ = type
1256+
1257+from canonical.config import config
1258+from canonical.launchpad.layers import WebServiceLayer
1259+from canonical.testing.layers import FunctionalLayer
1260+
1261+from lp.testing import TestCase
1262+from lp.testing.publication import get_request_and_publication
1263+
1264+from lp.bugs.publisher import BugsLayer
1265+
1266+
1267+class TestRegistration(TestCase):
1268+ """Bugs publication customizations are installed correctly."""
1269+
1270+ layer = FunctionalLayer
1271+
1272+ def test_bugs_request_provides_bugs_layer(self):
1273+ # The request constructed for requests to the bugs hostname provides
1274+ # BugsLayer.
1275+ request, publication = get_request_and_publication(
1276+ host=config.vhost.bugs.hostname)
1277+ self.assertProvides(request, BugsLayer)
1278+
1279+ def test_bugs_host_has_api(self):
1280+ # Requests to /api on the bugs domain are treated as web service
1281+ # requests.
1282+ request, publication = get_request_and_publication(
1283+ host=config.vhost.bugs.hostname,
1284+ extra_environment={'PATH_INFO': '/api/1.0'})
1285+ # XXX MichaelHudson, 2010-07-20, bug=607664: WebServiceLayer only
1286+ # actually provides WebServiceLayer in the sense of verifyObject after
1287+ # traversal has started.
1288+ self.assertTrue(WebServiceLayer.providedBy(request))
1289
1290=== modified file 'lib/lp/code/tests/test_publisher.py'
1291--- lib/lp/code/tests/test_publisher.py 2010-07-20 23:53:33 +0000
1292+++ lib/lp/code/tests/test_publisher.py 2010-07-22 20:59:01 +0000
1293@@ -5,8 +5,6 @@
1294
1295 __metaclass__ = type
1296
1297-import unittest
1298-
1299 from canonical.config import config
1300 from canonical.launchpad.layers import WebServiceLayer
1301 from canonical.testing.layers import FunctionalLayer
1302@@ -39,7 +37,3 @@
1303 # actually provides WebServiceLayer in the sense of verifyObject after
1304 # traversal has started.
1305 self.assertTrue(WebServiceLayer.providedBy(request))
1306-
1307-
1308-def test_suite():
1309- return unittest.TestLoader().loadTestsFromName(__name__)
1310
1311=== modified file 'lib/lp/registry/browser/configure.zcml'
1312--- lib/lp/registry/browser/configure.zcml 2010-07-16 15:40:14 +0000
1313+++ lib/lp/registry/browser/configure.zcml 2010-07-22 20:59:01 +0000
1314@@ -57,7 +57,7 @@
1315 name="+index"/>
1316 <browser:defaultView
1317 for="lp.registry.interfaces.distroseries.IDistroSeries"
1318- layer="canonical.launchpad.layers.BugsLayer"
1319+ layer="lp.bugs.publisher.BugsLayer"
1320 name="+bugs-index"/>
1321 <browser:page
1322 for="lp.registry.interfaces.distroseries.IDistroSeries"
1323@@ -294,15 +294,15 @@
1324 <browser:defaultView
1325 for="lp.registry.interfaces.projectgroup.IProjectGroup"
1326 name="+bugs"
1327- layer="canonical.launchpad.layers.BugsLayer"/>
1328+ layer="lp.bugs.publisher.BugsLayer"/>
1329 <browser:defaultView
1330 for="lp.registry.interfaces.projectgroup.IProjectGroup"
1331 name="+specs"
1332- layer="canonical.launchpad.layers.BlueprintLayer"/>
1333+ layer="lp.blueprints.publisher.BlueprintsLayer"/>
1334 <browser:defaultView
1335 for="lp.registry.interfaces.projectgroup.IProjectGroup"
1336 name="+questions"
1337- layer="canonical.launchpad.layers.AnswersLayer"/>
1338+ layer="lp.answers.publisher.AnswersLayer"/>
1339 <browser:pages
1340 for="lp.registry.interfaces.projectgroup.IProjectGroup"
1341 class="lp.registry.browser.project.ProjectView"
1342@@ -408,7 +408,7 @@
1343 <browser:defaultView
1344 for="lp.registry.interfaces.projectgroup.IProjectGroupSeries"
1345 name="+specs"
1346- layer="canonical.launchpad.layers.BlueprintLayer"/>
1347+ layer="lp.blueprints.publisher.BlueprintsLayer"/>
1348 <browser:menus
1349 classes="
1350 ProjectActionMenu
1351@@ -428,11 +428,11 @@
1352 name="+index"/>
1353 <browser:defaultView
1354 for="lp.registry.interfaces.distributionsourcepackage.IDistributionSourcePackage"
1355- layer="canonical.launchpad.layers.AnswersLayer"
1356+ layer="lp.answers.publisher.AnswersLayer"
1357 name="+questions"/>
1358 <browser:defaultView
1359 for="lp.registry.interfaces.distributionsourcepackage.IDistributionSourcePackage"
1360- layer="canonical.launchpad.layers.BugsLayer"
1361+ layer="lp.bugs.publisher.BugsLayer"
1362 name="+bugs"/>
1363 <browser:url
1364 for="lp.registry.interfaces.distributionsourcepackage.IDistributionSourcePackage"
1365@@ -762,11 +762,11 @@
1366 name="+index"/>
1367 <browser:defaultView
1368 for="lp.registry.interfaces.person.IPerson"
1369- layer="canonical.launchpad.layers.BlueprintLayer"
1370+ layer="lp.blueprints.publisher.BlueprintsLayer"
1371 name="+specs"/>
1372 <browser:defaultView
1373 for="lp.registry.interfaces.person.IPerson"
1374- layer="canonical.launchpad.layers.BugsLayer"
1375+ layer="lp.bugs.publisher.BugsLayer"
1376 name="+bugs"/>
1377 <adapter
1378 factory="lp.registry.browser.person.PersonXHTMLRepresentation"
1379@@ -1353,18 +1353,18 @@
1380 <browser:defaultView
1381 for="lp.registry.interfaces.product.IProduct"
1382 name="+specs"
1383- layer="canonical.launchpad.layers.BlueprintLayer"/>
1384+ layer="lp.blueprints.publisher.BlueprintsLayer"/>
1385 <browser:defaultView
1386 for="lp.registry.interfaces.product.IProduct"
1387 name="+code-index"
1388 layer="lp.code.publisher.CodeLayer"/>
1389 <browser:defaultView
1390 for="lp.registry.interfaces.product.IProduct"
1391- layer="canonical.launchpad.layers.AnswersLayer"
1392+ layer="lp.answers.publisher.AnswersLayer"
1393 name="+questions"/>
1394 <browser:defaultView
1395 for="lp.registry.interfaces.product.IProduct"
1396- layer="canonical.launchpad.layers.BugsLayer"
1397+ layer="lp.bugs.publisher.BugsLayer"
1398 name="+bugs-index"/>
1399 <browser:navigation
1400 module="lp.registry.browser.product"
1401@@ -1596,10 +1596,10 @@
1402 <browser:defaultView
1403 for="lp.registry.interfaces.productseries.IProductSeries"
1404 name="+specs"
1405- layer="canonical.launchpad.layers.BlueprintLayer"/>
1406+ layer="lp.blueprints.publisher.BlueprintsLayer"/>
1407 <browser:defaultView
1408 for="lp.registry.interfaces.productseries.IProductSeries"
1409- layer="canonical.launchpad.layers.BugsLayer"
1410+ layer="lp.bugs.publisher.BugsLayer"
1411 name="+bugs-index"/>
1412 <browser:page
1413 name="+get-involved"
1414@@ -1739,14 +1739,14 @@
1415 <browser:defaultView
1416 for="lp.registry.interfaces.distribution.IDistribution"
1417 name="+specs"
1418- layer="canonical.launchpad.layers.BlueprintLayer"/>
1419+ layer="lp.blueprints.publisher.BlueprintsLayer"/>
1420 <browser:defaultView
1421 for="lp.registry.interfaces.distribution.IDistribution"
1422- layer="canonical.launchpad.layers.BugsLayer"
1423+ layer="lp.bugs.publisher.BugsLayer"
1424 name="+bugs-index"/>
1425 <browser:defaultView
1426 for="lp.registry.interfaces.distribution.IDistribution"
1427- layer="canonical.launchpad.layers.AnswersLayer"
1428+ layer="lp.answers.publisher.AnswersLayer"
1429 name="+questions"/>
1430 <browser:navigation
1431 module="lp.registry.browser.distribution"
1432@@ -1946,11 +1946,11 @@
1433 name="+index"/>
1434 <browser:defaultView
1435 for="lp.registry.interfaces.sourcepackage.ISourcePackage"
1436- layer="canonical.launchpad.layers.BugsLayer"
1437+ layer="lp.bugs.publisher.BugsLayer"
1438 name="+bugs"/>
1439 <browser:defaultView
1440 for="lp.registry.interfaces.sourcepackage.ISourcePackage"
1441- layer="canonical.launchpad.layers.AnswersLayer"
1442+ layer="lp.answers.publisher.AnswersLayer"
1443 name="+questions"/>
1444 <browser:navigation
1445 module="lp.registry.browser.sourcepackage"
1446
1447=== modified file 'lib/lp/translations/browser/configure.zcml'
1448--- lib/lp/translations/browser/configure.zcml 2010-04-19 08:11:52 +0000
1449+++ lib/lp/translations/browser/configure.zcml 2010-07-22 20:59:01 +0000
1450@@ -15,7 +15,7 @@
1451 <browser:defaultView
1452 for="canonical.launchpad.interfaces.IRosettaApplication"
1453 name="+index"
1454- layer="canonical.launchpad.layers.TranslationsLayer"/>
1455+ layer="lp.translations.publisher.TranslationsLayer"/>
1456 <browser:navigation
1457 module="lp.translations.browser.translations"
1458 classes="
1459@@ -25,7 +25,7 @@
1460 facet="translations"
1461 permission="zope.Public"
1462 class="lp.translations.browser.translations.RosettaApplicationView"
1463- layer="canonical.launchpad.layers.TranslationsLayer">
1464+ layer="lp.translations.publisher.TranslationsLayer">
1465 <browser:page
1466 name="+index"
1467 template="../templates/rosetta-index.pt"/>
1468@@ -44,7 +44,7 @@
1469 facet="translations"
1470 permission="zope.Public"
1471 class="lp.translations.browser.translations.TranslatableProductsView"
1472- layer="canonical.launchpad.layers.TranslationsLayer"
1473+ layer="lp.translations.publisher.TranslationsLayer"
1474 name="+products-with-translations"
1475 template="../templates/rosetta-products.pt"/>
1476 <browser:pages
1477@@ -66,48 +66,48 @@
1478 permission="zope.Public"
1479 name="+barchart"
1480 template="../templates/rosettastats-barchart.pt"
1481- layer="canonical.launchpad.layers.TranslationsLayer"/>
1482+ layer="lp.translations.publisher.TranslationsLayer"/>
1483 <browser:page
1484 for="*"
1485 name="+translations-macros"
1486 facet="translations"
1487 permission="zope.Public"
1488 template="../templates/translations-macros.pt"
1489- layer="canonical.launchpad.layers.TranslationsLayer"/>
1490+ layer="lp.translations.publisher.TranslationsLayer"/>
1491 <browser:page
1492 for="lp.translations.interfaces.translationgroup.ITranslationPolicy"
1493 facet="translations"
1494 permission="zope.Public"
1495 name="+portlet-translation-groups-and-permission"
1496 template="../templates/hastranslationgroup-portlet-translation-groups-and-permission.pt"
1497- layer="canonical.launchpad.layers.TranslationsLayer"/>
1498+ layer="lp.translations.publisher.TranslationsLayer"/>
1499 <facet
1500 facet="translations">
1501 <browser:defaultView
1502 for="lp.translations.interfaces.translator.ITranslator"
1503 name="+admin"
1504- layer="canonical.launchpad.layers.TranslationsLayer"/>
1505+ layer="lp.translations.publisher.TranslationsLayer"/>
1506 <browser:page
1507 name="+admin"
1508 for="lp.translations.interfaces.translator.ITranslator"
1509 permission="launchpad.Admin"
1510 class="lp.translations.browser.translator.TranslatorAdminView"
1511 template="../../app/templates/generic-edit.pt"
1512- layer="canonical.launchpad.layers.TranslationsLayer"/>
1513+ layer="lp.translations.publisher.TranslationsLayer"/>
1514 <browser:page
1515 name="+edit"
1516 for="lp.translations.interfaces.translator.IEditTranslator"
1517 permission="launchpad.Edit"
1518 class="lp.translations.browser.translator.TranslatorEditView"
1519 template="../../app/templates/generic-edit.pt"
1520- layer="canonical.launchpad.layers.TranslationsLayer"/>
1521+ layer="lp.translations.publisher.TranslationsLayer"/>
1522 <browser:page
1523 name="+remove"
1524 for="lp.translations.interfaces.translator.ITranslator"
1525 permission="launchpad.Edit"
1526 class="lp.translations.browser.translator.TranslatorRemoveView"
1527 template="../../app/templates/generic-edit.pt"
1528- layer="canonical.launchpad.layers.TranslationsLayer"/>
1529+ layer="lp.translations.publisher.TranslationsLayer"/>
1530 </facet>
1531 <facet
1532 facet="translations">
1533@@ -119,7 +119,7 @@
1534 <browser:defaultView
1535 for="lp.translations.interfaces.translationimportqueue.ITranslationImportQueueEntry"
1536 name="+index"
1537- layer="canonical.launchpad.layers.TranslationsLayer"/>
1538+ layer="lp.translations.publisher.TranslationsLayer"/>
1539 <browser:url
1540 for="lp.translations.interfaces.translationimportqueue.ITranslationImportQueueEntry"
1541 path_expression="string:${id}"
1542@@ -130,12 +130,12 @@
1543 class="lp.translations.browser.translationimportqueue.TranslationImportQueueEntryView"
1544 permission="launchpad.Admin"
1545 template="../templates/translationimportqueueentry-index.pt"
1546- layer="canonical.launchpad.layers.TranslationsLayer"/>
1547+ layer="lp.translations.publisher.TranslationsLayer"/>
1548 <browser:page
1549 for="lp.translations.interfaces.translationimportqueue.ITranslationImportQueueEntry"
1550 facet="translations"
1551 permission="zope.Public"
1552- layer="canonical.launchpad.layers.TranslationsLayer"
1553+ layer="lp.translations.publisher.TranslationsLayer"
1554 name="+portlet-details"
1555 class="lp.translations.browser.translationimportqueue.TranslationImportQueueEntryView"
1556 template="../templates/translationimportqueueentry-portlet-details.pt"/>
1557@@ -146,7 +146,7 @@
1558 <browser:defaultView
1559 for="lp.translations.interfaces.translationimportqueue.ITranslationImportQueue"
1560 name="+index"
1561- layer="canonical.launchpad.layers.TranslationsLayer"/>
1562+ layer="lp.translations.publisher.TranslationsLayer"/>
1563 <browser:page
1564 for="lp.translations.interfaces.translationimportqueue.ITranslationImportQueue"
1565 name="+index"
1566@@ -154,7 +154,7 @@
1567 facet="translations"
1568 permission="zope.Public"
1569 template="../templates/translationimportqueue-index.pt"
1570- layer="canonical.launchpad.layers.TranslationsLayer"/>
1571+ layer="lp.translations.publisher.TranslationsLayer"/>
1572 </facet>
1573 <browser:url
1574 for="lp.translations.interfaces.distroserieslanguage.IDistroSeriesLanguage"
1575@@ -167,12 +167,12 @@
1576 <browser:defaultView
1577 for="lp.translations.interfaces.distroserieslanguage.IDistroSeriesLanguage"
1578 name="+index"
1579- layer="canonical.launchpad.layers.TranslationsLayer"/>
1580+ layer="lp.translations.publisher.TranslationsLayer"/>
1581 <browser:pages
1582 for="lp.translations.interfaces.distroserieslanguage.IDistroSeriesLanguage"
1583 permission="zope.Public"
1584 facet="translations"
1585- layer="canonical.launchpad.layers.TranslationsLayer">
1586+ layer="lp.translations.publisher.TranslationsLayer">
1587 <browser:page
1588 name="+rosetta-status-legend"
1589 template="../templates/rosetta-status-legend.pt"/>
1590@@ -184,7 +184,7 @@
1591 template="../templates/serieslanguage-index.pt"
1592 class="lp.translations.browser.serieslanguage.DistroSeriesLanguageView"
1593 facet="translations"
1594- layer="canonical.launchpad.layers.TranslationsLayer"/>
1595+ layer="lp.translations.publisher.TranslationsLayer"/>
1596 <facet
1597 facet="translations">
1598 <browser:navigation
1599@@ -204,19 +204,19 @@
1600 <browser:defaultView
1601 for="lp.translations.interfaces.pofile.IPOFile"
1602 name="+translate"
1603- layer="canonical.launchpad.layers.TranslationsLayer"/>
1604+ layer="lp.translations.publisher.TranslationsLayer"/>
1605 <browser:page
1606 for="lp.translations.interfaces.pofile.IPOFile"
1607 name="+details"
1608 permission="zope.Public"
1609 class="lp.translations.browser.pofile.POFileDetailsView"
1610- layer="canonical.launchpad.layers.TranslationsLayer"
1611+ layer="lp.translations.publisher.TranslationsLayer"
1612 template="../templates/pofile-details.pt"/>
1613 <browser:pages
1614 for="lp.translations.interfaces.pofile.IPOFile"
1615 permission="zope.Public"
1616 class="lp.translations.browser.pofile.POFileView"
1617- layer="canonical.launchpad.layers.TranslationsLayer">
1618+ layer="lp.translations.publisher.TranslationsLayer">
1619 <!-- POFile Portlets -->
1620 <browser:page
1621 name="+contributors"
1622@@ -237,7 +237,7 @@
1623 class="lp.translations.browser.pofile.POFileUploadView"
1624 for="lp.translations.interfaces.pofile.IPOFile"
1625 template="../templates/pofile-upload.pt"
1626- layer="canonical.launchpad.layers.TranslationsLayer"/>
1627+ layer="lp.translations.publisher.TranslationsLayer"/>
1628 <browser:page
1629 name="+translate"
1630 for="lp.translations.interfaces.pofile.IPOFile"
1631@@ -250,21 +250,21 @@
1632 permission="zope.Public"
1633 template="../templates/pofile-translate.pt"
1634 class="lp.translations.browser.pofile.POFileTranslateView"
1635- layer="canonical.launchpad.layers.TranslationsLayer"/>
1636+ layer="lp.translations.publisher.TranslationsLayer"/>
1637 <browser:page
1638 name="+export"
1639 for="lp.translations.interfaces.pofile.IPOFile"
1640 permission="launchpad.AnyPerson"
1641 template="../templates/pofile-export.pt"
1642 class="lp.translations.browser.pofile.POExportView"
1643- layer="canonical.launchpad.layers.TranslationsLayer"/>
1644+ layer="lp.translations.publisher.TranslationsLayer"/>
1645 <browser:page
1646 name="+filter"
1647 for="lp.translations.interfaces.pofile.IPOFile"
1648 permission="zope.Public"
1649 template="../templates/pofile-filter.pt"
1650 class="lp.translations.browser.pofile.POFileFilteredView"
1651- layer="canonical.launchpad.layers.TranslationsLayer"/>
1652+ layer="lp.translations.publisher.TranslationsLayer"/>
1653 </facet>
1654 <browser:url
1655 for="lp.translations.interfaces.productserieslanguage.IProductSeriesLanguage"
1656@@ -282,7 +282,7 @@
1657 for="lp.translations.interfaces.productserieslanguage.IProductSeriesLanguage"
1658 permission="zope.Public"
1659 facet="translations"
1660- layer="canonical.launchpad.layers.TranslationsLayer">
1661+ layer="lp.translations.publisher.TranslationsLayer">
1662 <browser:page
1663 name="+rosetta-status-legend"
1664 template="../templates/rosetta-status-legend.pt"/>
1665@@ -294,7 +294,7 @@
1666 template="../templates/serieslanguage-index.pt"
1667 class="lp.translations.browser.serieslanguage.ProductSeriesLanguageView"
1668 facet="translations"
1669- layer="canonical.launchpad.layers.TranslationsLayer"/>
1670+ layer="lp.translations.publisher.TranslationsLayer"/>
1671 <facet
1672 facet="translations">
1673 <browser:navigation
1674@@ -304,7 +304,7 @@
1675 <browser:defaultView
1676 for="canonical.launchpad.interfaces.ILanguage"
1677 name="+index"
1678- layer="canonical.launchpad.layers.TranslationsLayer"/>
1679+ layer="lp.translations.publisher.TranslationsLayer"/>
1680 <browser:menus
1681 module="lp.translations.browser.language"
1682 classes="
1683@@ -321,19 +321,19 @@
1684 permission="zope.Public"
1685 template="../templates/language-index.pt"
1686 name="+index"
1687- layer="canonical.launchpad.layers.TranslationsLayer"/>
1688+ layer="lp.translations.publisher.TranslationsLayer"/>
1689 <browser:page
1690 for="canonical.launchpad.interfaces.ILanguage"
1691 class="lp.translations.browser.language.LanguageAdminView"
1692 permission="launchpad.Admin"
1693 template="../../app/templates/generic-edit.pt"
1694 name="+admin"
1695- layer="canonical.launchpad.layers.TranslationsLayer"/>
1696+ layer="lp.translations.publisher.TranslationsLayer"/>
1697 <browser:pages
1698 for="canonical.launchpad.interfaces.ILanguage"
1699 class="lp.translations.browser.language.LanguageView"
1700 permission="zope.Public"
1701- layer="canonical.launchpad.layers.TranslationsLayer">
1702+ layer="lp.translations.publisher.TranslationsLayer">
1703 <browser:page
1704 name="+portlet-details"
1705 template="../templates/language-portlet-details.pt"/>
1706@@ -344,7 +344,7 @@
1707 <browser:defaultView
1708 for="canonical.launchpad.interfaces.ILanguageSet"
1709 name="+index"
1710- layer="canonical.launchpad.layers.TranslationsLayer"/>
1711+ layer="lp.translations.publisher.TranslationsLayer"/>
1712 <browser:url
1713 for="canonical.launchpad.interfaces.ILanguageSet"
1714 path_expression="string:+languages"
1715@@ -356,14 +356,14 @@
1716 permission="zope.Public"
1717 template="../templates/languageset-index.pt"
1718 name="+index"
1719- layer="canonical.launchpad.layers.TranslationsLayer"/>
1720+ layer="lp.translations.publisher.TranslationsLayer"/>
1721 <browser:page
1722 for="canonical.launchpad.interfaces.ILanguageSet"
1723 class="lp.translations.browser.language.LanguageAddView"
1724 permission="launchpad.Admin"
1725 template="../../app/templates/generic-edit.pt"
1726 name="+add"
1727- layer="canonical.launchpad.layers.TranslationsLayer"/>
1728+ layer="lp.translations.publisher.TranslationsLayer"/>
1729 <browser:navigation
1730 module="lp.translations.browser.potemplate"
1731 classes="
1732@@ -373,19 +373,19 @@
1733 <browser:defaultView
1734 for="lp.translations.interfaces.potemplate.IPOTemplate"
1735 name="+index"
1736- layer="canonical.launchpad.layers.TranslationsLayer"/>
1737+ layer="lp.translations.publisher.TranslationsLayer"/>
1738 <browser:page
1739 name="+preferred-chart"
1740 for="lp.translations.interfaces.potemplate.IPOTemplate"
1741 class="lp.translations.browser.potemplate.POTemplateViewPreferred"
1742 permission="zope.Public"
1743 template="../templates/potemplate-chart.pt"
1744- layer="canonical.launchpad.layers.TranslationsLayer"/>
1745+ layer="lp.translations.publisher.TranslationsLayer"/>
1746 <browser:pages
1747 for="lp.translations.interfaces.potemplate.IPOTemplate"
1748 class="lp.translations.browser.potemplate.POTemplateView"
1749 permission="zope.Public"
1750- layer="canonical.launchpad.layers.TranslationsLayer">
1751+ layer="lp.translations.publisher.TranslationsLayer">
1752 <browser:page
1753 name="+index"
1754 template="../templates/potemplate-index.pt"/>
1755@@ -408,28 +408,28 @@
1756 permission="launchpad.Edit"
1757 template="../templates/potemplate-upload.pt"
1758 class="lp.translations.browser.potemplate.POTemplateUploadView"
1759- layer="canonical.launchpad.layers.TranslationsLayer"/>
1760+ layer="lp.translations.publisher.TranslationsLayer"/>
1761 <browser:page
1762 name="+edit"
1763 for="lp.translations.interfaces.potemplate.IPOTemplate"
1764 class="lp.translations.browser.potemplate.POTemplateEditView"
1765 permission="launchpad.Edit"
1766 template="../../app/templates/generic-edit.pt"
1767- layer="canonical.launchpad.layers.TranslationsLayer"/>
1768+ layer="lp.translations.publisher.TranslationsLayer"/>
1769 <browser:page
1770 name="+admin"
1771 for="lp.translations.interfaces.potemplate.IPOTemplate"
1772 class="lp.translations.browser.potemplate.POTemplateAdminView"
1773 permission="launchpad.TranslationsAdmin"
1774 template="../../app/templates/generic-edit.pt"
1775- layer="canonical.launchpad.layers.TranslationsLayer"/>
1776+ layer="lp.translations.publisher.TranslationsLayer"/>
1777 <browser:page
1778 name="+export"
1779 for="lp.translations.interfaces.potemplate.IPOTemplate"
1780 permission="launchpad.AnyPerson"
1781 template="../templates/potemplate-export.pt"
1782 class="lp.translations.browser.potemplate.POTemplateExportView"
1783- layer="canonical.launchpad.layers.TranslationsLayer"/>
1784+ layer="lp.translations.publisher.TranslationsLayer"/>
1785 <browser:menus
1786 module="lp.translations.browser.potemplate"
1787 classes="
1788@@ -444,14 +444,14 @@
1789 <browser:defaultView
1790 for="lp.translations.interfaces.potemplate.IPOTemplateSubset"
1791 name="+index"
1792- layer="canonical.launchpad.layers.TranslationsLayer"/>
1793+ layer="lp.translations.publisher.TranslationsLayer"/>
1794 <browser:page
1795 name="+index"
1796 for="lp.translations.interfaces.potemplate.IPOTemplateSubset"
1797 permission="zope.Public"
1798 class="lp.translations.browser.potemplate.POTemplateSubsetView"
1799 attribute="__call__"
1800- layer="canonical.launchpad.layers.TranslationsLayer"/>
1801+ layer="lp.translations.publisher.TranslationsLayer"/>
1802 <browser:navigation
1803 module="lp.translations.browser.translationgroup"
1804 classes="
1805@@ -460,7 +460,7 @@
1806 <browser:defaultView
1807 for="lp.translations.interfaces.translationgroup.ITranslationGroup"
1808 name="+index"
1809- layer="canonical.launchpad.layers.TranslationsLayer"/>
1810+ layer="lp.translations.publisher.TranslationsLayer"/>
1811 <browser:url
1812 for="lp.translations.interfaces.translationgroup.ITranslationGroup"
1813 path_expression="name"
1814@@ -470,7 +470,7 @@
1815 for="lp.translations.interfaces.translationgroup.ITranslationGroup"
1816 class="lp.translations.browser.translationgroup.TranslationGroupView"
1817 permission="zope.Public"
1818- layer="canonical.launchpad.layers.TranslationsLayer">
1819+ layer="lp.translations.publisher.TranslationsLayer">
1820 <browser:page
1821 template="../templates/translationgroup-index.pt"
1822 name="+index"/>
1823@@ -487,27 +487,27 @@
1824 permission="launchpad.Edit"
1825 class="lp.translations.browser.translationgroup.TranslationGroupEditView"
1826 template="../../app/templates/generic-edit.pt"
1827- layer="canonical.launchpad.layers.TranslationsLayer"/>
1828+ layer="lp.translations.publisher.TranslationsLayer"/>
1829 <browser:page
1830 name="+appoint"
1831 for="lp.translations.interfaces.translationgroup.ITranslationGroup"
1832 permission="launchpad.Edit"
1833 class="lp.translations.browser.translationgroup.TranslationGroupAddTranslatorView"
1834 template="../../app/templates/generic-edit.pt"
1835- layer="canonical.launchpad.layers.TranslationsLayer"/>
1836+ layer="lp.translations.publisher.TranslationsLayer"/>
1837 <browser:page
1838 name="+reassign"
1839 for="lp.translations.interfaces.translationgroup.ITranslationGroup"
1840 permission="launchpad.Edit"
1841 class="lp.translations.browser.translationgroup.TranslationGroupReassignmentView"
1842 template="../templates/translationgroup-reassignment.pt"
1843- layer="canonical.launchpad.layers.TranslationsLayer"/>
1844+ layer="lp.translations.publisher.TranslationsLayer"/>
1845 <browser:page
1846 for="*"
1847 permission="launchpad.Edit"
1848 name="+object-reassignment"
1849 template="../../../canonical/launchpad/templates/object-reassignment.pt"
1850- layer="canonical.launchpad.layers.TranslationsLayer"/>
1851+ layer="lp.translations.publisher.TranslationsLayer"/>
1852 <browser:url
1853 for="lp.translations.interfaces.translationgroup.ITranslationGroupSet"
1854 path_expression="string:+groups"
1855@@ -516,21 +516,21 @@
1856 <browser:defaultView
1857 for="lp.translations.interfaces.translationgroup.ITranslationGroupSet"
1858 name="+index"
1859- layer="canonical.launchpad.layers.TranslationsLayer"/>
1860+ layer="lp.translations.publisher.TranslationsLayer"/>
1861 <browser:page
1862 name="+new"
1863 for="lp.translations.interfaces.translationgroup.ITranslationGroupSet"
1864 class="lp.translations.browser.translationgroup.TranslationGroupAddView"
1865 permission="launchpad.Admin"
1866 template="../../app/templates/generic-edit.pt"
1867- layer="canonical.launchpad.layers.TranslationsLayer"/>
1868+ layer="lp.translations.publisher.TranslationsLayer"/>
1869 <browser:page
1870 for="lp.translations.interfaces.translationgroup.ITranslationGroupSet"
1871 class="lp.translations.browser.translationgroup.TranslationGroupSetView"
1872 permission="zope.Public"
1873 template="../templates/translationgroups-index.pt"
1874 name="+index"
1875- layer="canonical.launchpad.layers.TranslationsLayer"/>
1876+ layer="lp.translations.publisher.TranslationsLayer"/>
1877 <browser:url
1878 for="lp.translations.interfaces.translationmessage.ITranslationMessage"
1879 path_expression="string:${potmsgset/sequence}"
1880@@ -544,13 +544,13 @@
1881 <browser:defaultView
1882 for="lp.translations.interfaces.translationmessage.ITranslationMessage"
1883 name="+index"
1884- layer="canonical.launchpad.layers.TranslationsLayer"/>
1885+ layer="lp.translations.publisher.TranslationsLayer"/>
1886 <browser:page
1887 for="lp.translations.interfaces.translationmessage.ITranslationMessage"
1888 class="lp.translations.browser.translationmessage.CurrentTranslationMessageIndexView"
1889 permission="zope.Public"
1890 name="+index"
1891- layer="canonical.launchpad.layers.TranslationsLayer"/>
1892+ layer="lp.translations.publisher.TranslationsLayer"/>
1893 <browser:page
1894 name="+translate"
1895 for="lp.translations.interfaces.translationmessage.ITranslationMessage"
1896@@ -562,20 +562,20 @@
1897 permission="zope.Public"
1898 template="../templates/translationmessage-translate.pt"
1899 class="lp.translations.browser.translationmessage.CurrentTranslationMessagePageView"
1900- layer="canonical.launchpad.layers.TranslationsLayer"/>
1901+ layer="lp.translations.publisher.TranslationsLayer"/>
1902 <browser:page
1903 name="+display"
1904 for="lp.translations.interfaces.translationmessage.ITranslationMessageSuggestions"
1905 permission="launchpad.AnyPerson"
1906 template="../templates/translationmessage-suggestions.pt"
1907- layer="canonical.launchpad.layers.TranslationsLayer"/>
1908+ layer="lp.translations.publisher.TranslationsLayer"/>
1909
1910 <!-- SourcePackage translation pages -->
1911
1912 <browser:defaultView
1913 for="lp.registry.interfaces.sourcepackage.ISourcePackage"
1914 name="+translations"
1915- layer="canonical.launchpad.layers.TranslationsLayer"/>
1916+ layer="lp.translations.publisher.TranslationsLayer"/>
1917 <browser:menus
1918 classes="SourcePackageTranslationsMenu"
1919 module="lp.translations.browser.sourcepackage"/>
1920@@ -586,11 +586,11 @@
1921 class="lp.translations.browser.hastranslationimports.HasTranslationImportsView"
1922 permission="zope.Public"
1923 template="../templates/hastranslationimports-index.pt"
1924- layer="canonical.launchpad.layers.TranslationsLayer"/>
1925+ layer="lp.translations.publisher.TranslationsLayer"/>
1926 <browser:page
1927 for="lp.registry.interfaces.sourcepackage.ISourcePackage"
1928 name="+export"
1929- layer="canonical.launchpad.layers.TranslationsLayer"
1930+ layer="lp.translations.publisher.TranslationsLayer"
1931 permission="launchpad.ExpensiveRequest"
1932 template="../templates/translations-export.pt"
1933 class="
1934@@ -609,7 +609,7 @@
1935 for="lp.registry.interfaces.sourcepackage.ISourcePackage"
1936 permission="zope.Public"
1937 class="lp.translations.browser.sourcepackage.SourcePackageTranslationsView"
1938- layer="canonical.launchpad.layers.TranslationsLayer">
1939+ layer="lp.translations.publisher.TranslationsLayer">
1940 <browser:page
1941 name="+potlist"
1942 template="../templates/object-pots.pt"/>
1943@@ -623,7 +623,7 @@
1944 <browser:defaultView
1945 for="lp.registry.interfaces.productseries.IProductSeries"
1946 name="+translations"
1947- layer="canonical.launchpad.layers.TranslationsLayer"/>
1948+ layer="lp.translations.publisher.TranslationsLayer"/>
1949 <browser:menus
1950 classes="ProductSeriesTranslationsMenu"
1951 module="lp.translations.browser.productseries"/>
1952@@ -636,7 +636,7 @@
1953 for="lp.registry.interfaces.productseries.IProductSeries"
1954 class="lp.translations.browser.productseries.ProductSeriesView"
1955 permission="zope.Public"
1956- layer="canonical.launchpad.layers.TranslationsLayer" >
1957+ layer="lp.translations.publisher.TranslationsLayer" >
1958 <browser:page
1959 name="+translations"
1960 template="../templates/productseries-translations.pt"
1961@@ -650,7 +650,7 @@
1962 for="lp.registry.interfaces.productseries.IProductSeries"
1963 class="lp.translations.browser.productseries.ProductSeriesUploadView"
1964 permission="launchpad.Edit"
1965- layer="canonical.launchpad.layers.TranslationsLayer">
1966+ layer="lp.translations.publisher.TranslationsLayer">
1967 <browser:page
1968 name="+translations-upload"
1969 template="../templates/productseries-translations-upload.pt"/>
1970@@ -661,38 +661,38 @@
1971 class="lp.translations.browser.hastranslationimports.HasTranslationImportsView"
1972 permission="zope.Public"
1973 template="../templates/hastranslationimports-index.pt"
1974- layer="canonical.launchpad.layers.TranslationsLayer"/>
1975+ layer="lp.translations.publisher.TranslationsLayer"/>
1976 <browser:page
1977 for="lp.registry.interfaces.productseries.IProductSeries"
1978 name="+translations-settings"
1979 class="lp.translations.browser.productseries.ProductSeriesTranslationsSettingsView"
1980 permission="launchpad.Edit"
1981 template="../templates/productseries-translations-settings.pt"
1982- layer="canonical.launchpad.layers.TranslationsLayer"/>
1983+ layer="lp.translations.publisher.TranslationsLayer"/>
1984 <browser:page
1985 for="lp.registry.interfaces.productseries.IProductSeries"
1986 name="+templates"
1987 class="lp.translations.browser.productseries.ProductSeriesTemplatesView"
1988 permission="zope.Public"
1989 template="../templates/object-templates.pt"
1990- layer="canonical.launchpad.layers.TranslationsLayer"/>
1991+ layer="lp.translations.publisher.TranslationsLayer"/>
1992 <browser:page
1993 for="lp.registry.interfaces.productseries.IProductSeries"
1994 name="+request-bzr-import"
1995 class="lp.translations.browser.productseries.ProductSeriesTranslationsBzrImportView"
1996 permission="launchpad.Edit"
1997 template="../templates/productseries-translations-bzr-import.pt"
1998- layer="canonical.launchpad.layers.TranslationsLayer"/>
1999+ layer="lp.translations.publisher.TranslationsLayer"/>
2000 <browser:page
2001 name="+export"
2002- layer="canonical.launchpad.layers.TranslationsLayer"
2003+ layer="lp.translations.publisher.TranslationsLayer"
2004 for="lp.registry.interfaces.productseries.IProductSeries"
2005 permission="launchpad.AnyPerson"
2006 template="../templates/translations-export.pt"
2007 class="lp.translations.browser.productseries.ProductSeriesTranslationsExportView"/>
2008 <browser:page
2009 name="+link-translations-branch"
2010- layer="canonical.launchpad.layers.TranslationsLayer"
2011+ layer="lp.translations.publisher.TranslationsLayer"
2012 for="lp.registry.interfaces.productseries.IProductSeries"
2013 permission="launchpad.Edit"
2014 template="../../app/templates/generic-edit.pt"
2015@@ -703,7 +703,7 @@
2016 <browser:defaultView
2017 for="lp.registry.interfaces.person.IPerson"
2018 name="+translations"
2019- layer="canonical.launchpad.layers.TranslationsLayer"/>
2020+ layer="lp.translations.publisher.TranslationsLayer"/>
2021 <browser:menus
2022 module="lp.translations.browser.person"
2023 classes="
2024@@ -720,7 +720,7 @@
2025 class="lp.translations.browser.person.PersonTranslationView"
2026 permission="zope.Public"
2027 template="../templates/person-translations.pt"
2028- layer="canonical.launchpad.layers.TranslationsLayer"/>
2029+ layer="lp.translations.publisher.TranslationsLayer"/>
2030 <browser:page
2031 for="lp.registry.interfaces.person.IPerson"
2032 name="+licensing"
2033@@ -728,14 +728,14 @@
2034 lp.translations.browser.person.PersonTranslationRelicensingView"
2035 permission="launchpad.Edit"
2036 template="../templates/person-translations-relicensing.pt"
2037- layer="canonical.launchpad.layers.TranslationsLayer"/>
2038+ layer="lp.translations.publisher.TranslationsLayer"/>
2039 <browser:page
2040 for="lp.registry.interfaces.person.IPerson"
2041 name="+imports"
2042 class="lp.translations.browser.hastranslationimports.HasTranslationImportsView"
2043 permission="zope.Public"
2044 template="../templates/hastranslationimports-index.pt"
2045- layer="canonical.launchpad.layers.TranslationsLayer"/>
2046+ layer="lp.translations.publisher.TranslationsLayer"/>
2047 <browser:page
2048 for="lp.registry.interfaces.person.IPerson"
2049 class="lp.translations.browser.person.PersonTranslationView"
2050@@ -748,35 +748,35 @@
2051 class="lp.translations.browser.person.TranslationActivityView"
2052 permission="zope.Public"
2053 template="../templates/person-translation-activity.pt"
2054- layer="canonical.launchpad.layers.TranslationsLayer"/>
2055+ layer="lp.translations.publisher.TranslationsLayer"/>
2056 <browser:page
2057 for="lp.registry.interfaces.person.IPerson"
2058 name="+translations-to-review"
2059 class="lp.translations.browser.person.PersonTranslationReviewView"
2060 permission="zope.Public"
2061 template="../templates/person-translations-to-review.pt"
2062- layer="canonical.launchpad.layers.TranslationsLayer"/>
2063+ layer="lp.translations.publisher.TranslationsLayer"/>
2064 <browser:page
2065 for="lp.registry.interfaces.person.IPerson"
2066 name="+translations-to-review-table"
2067 class="lp.translations.browser.person.PersonTranslationView"
2068 permission="zope.Public"
2069 template="../templates/person-translations-to-review-table.pt"
2070- layer="canonical.launchpad.layers.TranslationsLayer"/>
2071+ layer="lp.translations.publisher.TranslationsLayer"/>
2072 <browser:page
2073 for="lp.registry.interfaces.person.IPerson"
2074 name="+translations-to-complete-table"
2075 class="lp.translations.browser.person.PersonTranslationView"
2076 permission="zope.Public"
2077 template="../templates/person-translations-to-complete-table.pt"
2078- layer="canonical.launchpad.layers.TranslationsLayer"/>
2079+ layer="lp.translations.publisher.TranslationsLayer"/>
2080
2081
2082 <!-- Product views -->
2083
2084 <browser:defaultView
2085 for="lp.registry.interfaces.product.IProduct"
2086- layer="canonical.launchpad.layers.TranslationsLayer"
2087+ layer="lp.translations.publisher.TranslationsLayer"
2088 name="+translations"/>
2089 <browser:menus
2090 classes="
2091@@ -798,19 +798,19 @@
2092 class="lp.translations.browser.hastranslationimports.HasTranslationImportsView"
2093 permission="zope.Public"
2094 template="../templates/hastranslationimports-index.pt"
2095- layer="canonical.launchpad.layers.TranslationsLayer"/>
2096+ layer="lp.translations.publisher.TranslationsLayer"/>
2097 <browser:page
2098 name="+settings"
2099 for="lp.registry.interfaces.product.IProduct"
2100 class="lp.translations.browser.product.ProductSettingsView"
2101 permission="launchpad.TranslationsAdmin"
2102 template="../templates/set-translators.pt"
2103- layer="canonical.launchpad.layers.TranslationsLayer"/>
2104+ layer="lp.translations.publisher.TranslationsLayer"/>
2105 <browser:pages
2106 for="lp.registry.interfaces.product.IProduct"
2107 permission="zope.Public"
2108 class="lp.translations.browser.product.ProductView"
2109- layer="canonical.launchpad.layers.TranslationsLayer">
2110+ layer="lp.translations.publisher.TranslationsLayer">
2111 <browser:page
2112 name="+translations"
2113 template="../templates/product-translations.pt"/>
2114@@ -830,7 +830,7 @@
2115 <browser:defaultView
2116 for="lp.registry.interfaces.projectgroup.IProjectGroup"
2117 name="+translations"
2118- layer="canonical.launchpad.layers.TranslationsLayer"/>
2119+ layer="lp.translations.publisher.TranslationsLayer"/>
2120 <browser:menus
2121 classes="
2122 ProjectTranslationsMenu"
2123@@ -846,7 +846,7 @@
2124 name="+translations"
2125 class="lp.translations.browser.project.ProjectView"
2126 template="../templates/project-translations.pt"
2127- layer="canonical.launchpad.layers.TranslationsLayer"/>
2128+ layer="lp.translations.publisher.TranslationsLayer"/>
2129 <browser:page
2130 name="+help-translate-button"
2131 for="lp.registry.interfaces.projectgroup.IProjectGroup"
2132@@ -858,14 +858,14 @@
2133 class="lp.translations.browser.project.ProjectSettingsView"
2134 permission="launchpad.TranslationsAdmin"
2135 template="../templates/set-translators.pt"
2136- layer="canonical.launchpad.layers.TranslationsLayer"/>
2137+ layer="lp.translations.publisher.TranslationsLayer"/>
2138
2139 <!-- Distribution -->
2140
2141 <browser:defaultView
2142 for="lp.registry.interfaces.distribution.IDistribution"
2143 name="+translations"
2144- layer="canonical.launchpad.layers.TranslationsLayer"/>
2145+ layer="lp.translations.publisher.TranslationsLayer"/>
2146 <browser:menus
2147 classes="
2148 DistributionTranslationsMenu"
2149@@ -881,7 +881,7 @@
2150 permission="zope.Public"
2151 name="+translations"
2152 template="../templates/distribution-translations.pt"
2153- layer="canonical.launchpad.layers.TranslationsLayer"/>
2154+ layer="lp.translations.publisher.TranslationsLayer"/>
2155 <browser:page
2156 name="+help-translate-button"
2157 for="lp.registry.interfaces.distribution.IDistribution"
2158@@ -893,35 +893,35 @@
2159 class="lp.translations.browser.distribution.DistributionSettingsView"
2160 permission="launchpad.TranslationsAdmin"
2161 template="../templates/set-translators.pt"
2162- layer="canonical.launchpad.layers.TranslationsLayer"/>
2163+ layer="lp.translations.publisher.TranslationsLayer"/>
2164 <browser:page
2165 for="lp.registry.interfaces.distribution.IDistribution"
2166 name="+imports"
2167 class="lp.translations.browser.hastranslationimports.HasTranslationImportsView"
2168 permission="zope.Public"
2169 template="../templates/hastranslationimports-index.pt"
2170- layer="canonical.launchpad.layers.TranslationsLayer"/>
2171+ layer="lp.translations.publisher.TranslationsLayer"/>
2172 <browser:page
2173 for="lp.registry.interfaces.distribution.IDistribution"
2174 name="+select-language-pack-admin"
2175 class="lp.translations.browser.distribution.DistributionLanguagePackAdminView"
2176 permission="launchpad.TranslationsAdmin"
2177 template="../../app/templates/generic-edit.pt"
2178- layer="canonical.launchpad.layers.TranslationsLayer"/>
2179+ layer="lp.translations.publisher.TranslationsLayer"/>
2180 <!-- Language pack admin portlet -->
2181 <browser:page
2182 name="+language-pack-admin-info"
2183 for="lp.registry.interfaces.distribution.IDistribution"
2184 permission="launchpad.View"
2185 template="../templates/distribution-language-pack-admin-info.pt"
2186- layer="canonical.launchpad.layers.TranslationsLayer"/>
2187+ layer="lp.translations.publisher.TranslationsLayer"/>
2188
2189 <!-- DistroSeries -->
2190
2191 <browser:defaultView
2192 for="lp.registry.interfaces.distroseries.IDistroSeries"
2193 name="+translations"
2194- layer="canonical.launchpad.layers.TranslationsLayer"/>
2195+ layer="lp.translations.publisher.TranslationsLayer"/>
2196 <browser:menus
2197 classes="
2198 DistroSeriesTranslationsMenu"
2199@@ -936,7 +936,7 @@
2200 for="lp.registry.interfaces.distroseries.IDistroSeries"
2201 class="lp.translations.browser.distroseries.DistroSeriesView"
2202 permission="zope.Public"
2203- layer="canonical.launchpad.layers.TranslationsLayer">
2204+ layer="lp.translations.publisher.TranslationsLayer">
2205 <browser:page
2206 name="+translations"
2207 template="../templates/distroseries-translations.pt"/>
2208@@ -948,7 +948,7 @@
2209 for="lp.registry.interfaces.distroseries.IDistroSeries"
2210 class="lp.translations.browser.distroseries.DistroSeriesTemplatesView"
2211 permission="zope.Public"
2212- layer="canonical.launchpad.layers.TranslationsLayer"
2213+ layer="lp.translations.publisher.TranslationsLayer"
2214 name="+templates"
2215 template="../templates/object-templates.pt" />
2216 <browser:page
2217@@ -962,19 +962,19 @@
2218 class="lp.translations.browser.distroseries.DistroSeriesTranslationsAdminView"
2219 name="+admin"
2220 template="../../app/templates/generic-edit.pt"
2221- layer="canonical.launchpad.layers.TranslationsLayer"/>
2222+ layer="lp.translations.publisher.TranslationsLayer"/>
2223 <browser:page
2224 for="lp.registry.interfaces.distroseries.IDistroSeries"
2225 name="+imports"
2226 class="lp.translations.browser.hastranslationimports.HasTranslationImportsView"
2227 permission="zope.Public"
2228 template="../templates/hastranslationimports-index.pt"
2229- layer="canonical.launchpad.layers.TranslationsLayer"/>
2230+ layer="lp.translations.publisher.TranslationsLayer"/>
2231 <browser:page
2232 for="lp.registry.interfaces.distroseries.IDistroSeries"
2233 class="lp.translations.browser.distroseries.DistroSeriesLanguagePackView"
2234 permission="zope.Public"
2235- layer="canonical.launchpad.layers.TranslationsLayer"
2236+ layer="lp.translations.publisher.TranslationsLayer"
2237 name="+language-packs"
2238 template="../templates/distroseries-language-packs.pt"/>
2239
2240@@ -984,7 +984,7 @@
2241 <browser:defaultView
2242 for="lp.translations.interfaces.customlanguagecode.ICustomLanguageCode"
2243 name="+index"
2244- layer="canonical.launchpad.layers.TranslationsLayer"/>
2245+ layer="lp.translations.publisher.TranslationsLayer"/>
2246 <browser:url
2247 for="lp.translations.interfaces.customlanguagecode.ICustomLanguageCode"
2248 path_expression="string:+customcode/${language_code}"
2249@@ -996,7 +996,7 @@
2250 permission="zope.Public"
2251 class="lp.translations.browser.customlanguagecode.CustomLanguageCodeView"
2252 template="../templates/customlanguagecode-index.pt"
2253- layer="canonical.launchpad.layers.TranslationsLayer"/>
2254+ layer="lp.translations.publisher.TranslationsLayer"/>
2255
2256 <browser:page
2257 name="+remove"
2258@@ -1004,21 +1004,21 @@
2259 permission="launchpad.Admin"
2260 class="lp.translations.browser.customlanguagecode.CustomLanguageCodeRemoveView"
2261 template="../../app/templates/generic-edit.pt"
2262- layer="canonical.launchpad.layers.TranslationsLayer"/>
2263+ layer="lp.translations.publisher.TranslationsLayer"/>
2264
2265 <!-- IHasCustomLanguageCodes -->
2266
2267 <browser:page
2268 name="+custom-language-codes"
2269 for="lp.translations.interfaces.customlanguagecode.IHasCustomLanguageCodes"
2270- layer="canonical.launchpad.layers.TranslationsLayer"
2271+ layer="lp.translations.publisher.TranslationsLayer"
2272 class="lp.translations.browser.customlanguagecode.CustomLanguageCodesIndexView"
2273 template="../templates/customlanguagecodes-index.pt"
2274 permission="zope.Public"/>
2275 <browser:page
2276 name="+add-custom-language-code"
2277 for="lp.translations.interfaces.customlanguagecode.IHasCustomLanguageCodes"
2278- layer="canonical.launchpad.layers.TranslationsLayer"
2279+ layer="lp.translations.publisher.TranslationsLayer"
2280 class="lp.translations.browser.customlanguagecode.CustomLanguageCodeAddView"
2281 template="../templates/customlanguagecode-add.pt"
2282 permission="launchpad.Admin"/>
2283
2284=== modified file 'lib/lp/translations/browser/tests/language-views.txt'
2285--- lib/lp/translations/browser/tests/language-views.txt 2010-04-21 13:08:48 +0000
2286+++ lib/lp/translations/browser/tests/language-views.txt 2010-07-22 20:59:01 +0000
2287@@ -6,7 +6,7 @@
2288
2289 >>> from zope.component import getUtility
2290 >>> from lp.services.worlddata.interfaces.language import ILanguageSet
2291- >>> from canonical.launchpad.layers import TranslationsLayer
2292+ >>> from lp.translations.publisher import TranslationsLayer
2293
2294 >>> language_set = getUtility(ILanguageSet)
2295 >>> portuguese = language_set.getLanguageByCode('pt_BR')
2296
2297=== modified file 'lib/lp/translations/browser/tests/poexport-request-views.txt'
2298--- lib/lp/translations/browser/tests/poexport-request-views.txt 2009-07-02 17:16:50 +0000
2299+++ lib/lp/translations/browser/tests/poexport-request-views.txt 2010-07-22 20:59:01 +0000
2300@@ -1,7 +1,7 @@
2301 PO Export Request Browser Views
2302 -------------------------------
2303
2304- >>> from canonical.launchpad.layers import TranslationsLayer
2305+ >>> from lp.translations.publisher import TranslationsLayer
2306
2307 Check there are no requests in the database to start with.
2308
2309
2310=== modified file 'lib/lp/translations/browser/tests/pofile-views.txt'
2311--- lib/lp/translations/browser/tests/pofile-views.txt 2010-01-20 22:41:54 +0000
2312+++ lib/lp/translations/browser/tests/pofile-views.txt 2010-07-22 20:59:01 +0000
2313@@ -15,7 +15,7 @@
2314 >>> from lp.registry.interfaces.distribution import IDistributionSet
2315 >>> from lp.registry.interfaces.sourcepackagename import (
2316 ... ISourcePackageNameSet)
2317- >>> from canonical.launchpad.layers import TranslationsLayer
2318+ >>> from lp.translations.publisher import TranslationsLayer
2319
2320 All the tests will be submitted as coming from the No Privilege person.
2321
2322
2323=== modified file 'lib/lp/translations/browser/tests/potemplate-views.txt'
2324--- lib/lp/translations/browser/tests/potemplate-views.txt 2009-07-02 17:16:50 +0000
2325+++ lib/lp/translations/browser/tests/potemplate-views.txt 2010-07-22 20:59:01 +0000
2326@@ -13,7 +13,7 @@
2327 >>> from lp.registry.interfaces.distribution import IDistributionSet
2328 >>> from lp.registry.interfaces.sourcepackagename import (
2329 ... ISourcePackageNameSet)
2330- >>> from canonical.launchpad.layers import TranslationsLayer
2331+ >>> from lp.translations.publisher import TranslationsLayer
2332 >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest
2333
2334 All the tests will be submitted as comming from the No Privilege person.
2335
2336=== modified file 'lib/lp/translations/browser/tests/test_translationimportqueueentry.py'
2337--- lib/lp/translations/browser/tests/test_translationimportqueueentry.py 2010-04-19 15:24:29 +0000
2338+++ lib/lp/translations/browser/tests/test_translationimportqueueentry.py 2010-07-22 20:59:01 +0000
2339@@ -12,12 +12,13 @@
2340
2341 from canonical.testing import LaunchpadFunctionalLayer
2342
2343-from canonical.launchpad.layers import TranslationsLayer, setFirstLayer
2344+from canonical.launchpad.layers import setFirstLayer
2345 from canonical.launchpad.webapp import canonical_url
2346 from canonical.launchpad.webapp.servers import LaunchpadTestRequest
2347 from lp.testing import TestCaseWithFactory
2348 from lp.translations.interfaces.translationimportqueue import (
2349 ITranslationImportQueue)
2350+from lp.translations.publisher import TranslationsLayer
2351
2352
2353 class TestTranslationImportQueueEntryView(TestCaseWithFactory):
2354
2355=== modified file 'lib/lp/translations/browser/tests/translationimportqueue-views.txt'
2356--- lib/lp/translations/browser/tests/translationimportqueue-views.txt 2009-07-02 17:16:50 +0000
2357+++ lib/lp/translations/browser/tests/translationimportqueue-views.txt 2010-07-22 20:59:01 +0000
2358@@ -5,11 +5,12 @@
2359
2360 >>> from zope.component import getUtility, getMultiAdapter
2361 >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest
2362+ >>> from canonical.launchpad.layers import setFirstLayer
2363 >>> from lp.translations.interfaces.translationimportqueue import (
2364 ... ITranslationImportQueue)
2365 >>> from lp.translations.browser.translationimportqueue import (
2366 ... TranslationImportQueueEntryView)
2367- >>> from canonical.launchpad.layers import TranslationsLayer, setFirstLayer
2368+ >>> from lp.translations.publisher import TranslationsLayer
2369
2370 This view is only accessible for administrators.
2371
2372
2373=== modified file 'lib/lp/translations/browser/tests/translationmessage-views.txt'
2374--- lib/lp/translations/browser/tests/translationmessage-views.txt 2010-03-22 17:18:28 +0000
2375+++ lib/lp/translations/browser/tests/translationmessage-views.txt 2010-07-22 20:59:01 +0000
2376@@ -7,7 +7,7 @@
2377 >>> from lp.translations.model.pofile import POFile
2378 >>> from lp.translations.model.translationmessage import (
2379 ... TranslationMessage)
2380- >>> from canonical.launchpad.layers import TranslationsLayer
2381+ >>> from lp.translations.publisher import TranslationsLayer
2382 >>> from canonical.launchpad.webapp import canonical_url
2383
2384 All the tests will be submitted as comming from Kurem, an editor for the POFile
2385
2386=== modified file 'lib/lp/translations/browser/tests/translator-views.txt'
2387--- lib/lp/translations/browser/tests/translator-views.txt 2009-09-17 11:28:13 +0000
2388+++ lib/lp/translations/browser/tests/translator-views.txt 2010-07-22 20:59:01 +0000
2389@@ -23,7 +23,7 @@
2390
2391 Translator +admin view provides a nice page title and a form label.
2392
2393- >>> from canonical.launchpad.layers import TranslationsLayer
2394+ >>> from lp.translations.publisher import TranslationsLayer
2395 >>> view = create_initialized_view(translator, '+admin',
2396 ... layer=TranslationsLayer)
2397 >>> print view.label
2398@@ -45,7 +45,7 @@
2399 for a language in a TranslationGroup, and page title and form label
2400 describe that appropriately.
2401
2402- >>> from canonical.launchpad.layers import TranslationsLayer
2403+ >>> from lp.translations.publisher import TranslationsLayer
2404 >>> view = create_initialized_view(translator, '+edit',
2405 ... layer=TranslationsLayer)
2406 >>> print view.label
2407@@ -66,7 +66,7 @@
2408 Translator +edit view allows one to only set translation guidelines
2409 for a language in a TranslationGroup.
2410
2411- >>> from canonical.launchpad.layers import TranslationsLayer
2412+ >>> from lp.translations.publisher import TranslationsLayer
2413 >>> view = create_initialized_view(translator, '+remove',
2414 ... layer=TranslationsLayer)
2415 >>> print view.label
2416
2417=== modified file 'lib/lp/translations/browser/translations.py'
2418--- lib/lp/translations/browser/translations.py 2010-02-01 21:20:57 +0000
2419+++ lib/lp/translations/browser/translations.py 2010-07-22 20:59:01 +0000
2420@@ -27,7 +27,7 @@
2421 from lp.registry.interfaces.product import IProductSet
2422 from lp.services.worlddata.interfaces.country import ICountry
2423 from lp.registry.interfaces.person import IPersonSet
2424-from canonical.launchpad.layers import TranslationsLayer
2425+from lp.translations.publisher import TranslationsLayer
2426 from canonical.launchpad.webapp import (
2427 LaunchpadView, Navigation, stepto, canonical_url)
2428 from canonical.launchpad.webapp.batching import BatchNavigator
2429@@ -183,7 +183,7 @@
2430 self.request.response.redirect(
2431 '/'.join([
2432 canonical_url(self.context, rootsite='translations'),
2433- self.page
2434+ self.page,
2435 ]), status=301)
2436
2437
2438@@ -208,6 +208,7 @@
2439
2440 class TranslationsLanguageBreadcrumb(Breadcrumb):
2441 """Breadcrumb for objects with language."""
2442+
2443 @property
2444 def text(self):
2445 return self.context.language.displayname
2446
2447=== modified file 'lib/lp/translations/configure.zcml'
2448--- lib/lp/translations/configure.zcml 2010-07-19 13:30:29 +0000
2449+++ lib/lp/translations/configure.zcml 2010-07-22 20:59:01 +0000
2450@@ -13,8 +13,12 @@
2451 <include
2452 package=".browser"/>
2453
2454+ <publisher
2455+ name="translations"
2456+ factory="lp.translations.publisher.translations_request_publication_factory"/>
2457+
2458 <lp:help-folder
2459- folder="help" type="canonical.launchpad.layers.TranslationsLayer" />
2460+ folder="help" type="lp.translations.publisher.TranslationsLayer" />
2461
2462 <class
2463 class="lp.translations.model.vpotexport.VPOTExport">
2464
2465=== modified file 'lib/lp/translations/doc/rosetta-karma.txt'
2466--- lib/lp/translations/doc/rosetta-karma.txt 2010-02-16 20:36:48 +0000
2467+++ lib/lp/translations/doc/rosetta-karma.txt 2010-07-22 20:59:01 +0000
2468@@ -364,7 +364,7 @@
2469 product from where the IPOTemplate is and he has rights to change the
2470 description.
2471
2472- >>> from canonical.launchpad.layers import TranslationsLayer
2473+ >>> from lp.translations.publisher import TranslationsLayer
2474 >>> sample_person = personset.getByEmail('test@canonical.com')
2475 >>> login('test@canonical.com')
2476 >>> form = {
2477
2478=== added file 'lib/lp/translations/publisher.py'
2479--- lib/lp/translations/publisher.py 1970-01-01 00:00:00 +0000
2480+++ lib/lp/translations/publisher.py 2010-07-22 20:59:01 +0000
2481@@ -0,0 +1,42 @@
2482+# Copyright 2010 Canonical Ltd. This software is licensed under the
2483+# GNU Affero General Public License version 3 (see the file LICENSE).
2484+
2485+"""Translations's custom publication."""
2486+
2487+__metaclass__ = type
2488+__all__ = [
2489+ 'TranslationsBrowserRequest',
2490+ 'TranslationsLayer',
2491+ 'translations_request_publication_factory',
2492+ ]
2493+
2494+
2495+from zope.interface import implements
2496+from zope.publisher.interfaces.browser import (
2497+ IBrowserRequest, IDefaultBrowserLayer)
2498+
2499+from canonical.launchpad.webapp.publication import LaunchpadBrowserPublication
2500+from canonical.launchpad.webapp.servers import (
2501+ LaunchpadBrowserRequest, VHostWebServiceRequestPublicationFactory)
2502+
2503+
2504+class TranslationsLayer(IBrowserRequest, IDefaultBrowserLayer):
2505+ """The Translations layer."""
2506+
2507+
2508+class TranslationsBrowserRequest(LaunchpadBrowserRequest):
2509+ """Instances of TranslationsBrowserRequest provide `TranslationsLayer`."""
2510+ implements(TranslationsLayer)
2511+
2512+ def __init__(self, body_instream, environ, response=None):
2513+ super(TranslationsBrowserRequest, self).__init__(
2514+ body_instream, environ, response)
2515+ # Many of the responses from Translations vary based on language.
2516+ self.response.setHeader(
2517+ 'Vary', 'Cookie, Authorization, Accept-Language')
2518+
2519+
2520+def translations_request_publication_factory():
2521+ return VHostWebServiceRequestPublicationFactory(
2522+ 'translations', TranslationsBrowserRequest,
2523+ LaunchpadBrowserPublication)
2524
2525=== added file 'lib/lp/translations/tests/test_publisher.py'
2526--- lib/lp/translations/tests/test_publisher.py 1970-01-01 00:00:00 +0000
2527+++ lib/lp/translations/tests/test_publisher.py 2010-07-22 20:59:01 +0000
2528@@ -0,0 +1,50 @@
2529+# Copyright 2010 Canonical Ltd. This software is licensed under the
2530+# GNU Affero General Public License version 3 (see the file LICENSE).
2531+
2532+"""Tests for translations's custom publications."""
2533+
2534+__metaclass__ = type
2535+
2536+import StringIO
2537+
2538+from canonical.config import config
2539+from canonical.launchpad.layers import WebServiceLayer
2540+from canonical.testing.layers import FunctionalLayer
2541+
2542+from lp.testing import TestCase
2543+from lp.testing.publication import get_request_and_publication
2544+
2545+from lp.translations.publisher import (
2546+ TranslationsLayer, TranslationsBrowserRequest)
2547+
2548+
2549+class TestRegistration(TestCase):
2550+ """Translations's publication customizations are installed correctly."""
2551+
2552+ layer = FunctionalLayer
2553+
2554+ def test_translations_request_provides_translations_layer(self):
2555+ # The request constructed for requests to the translations hostname
2556+ # provides TranslationsLayer.
2557+ request, publication = get_request_and_publication(
2558+ host=config.vhost.translations.hostname)
2559+ self.assertProvides(request, TranslationsLayer)
2560+
2561+ def test_translations_host_has_api(self):
2562+ # Requests to /api on the translations domain are treated as web
2563+ # service requests.
2564+ request, publication = get_request_and_publication(
2565+ host=config.vhost.translations.hostname,
2566+ extra_environment={'PATH_INFO': '/api/1.0'})
2567+ # XXX MichaelHudson, 2010-07-20, bug=607664: WebServiceLayer only
2568+ # actually provides WebServiceLayer in the sense of verifyObject after
2569+ # traversal has started.
2570+ self.assertTrue(WebServiceLayer.providedBy(request))
2571+
2572+ def test_response_should_vary_based_on_language(self):
2573+ # Responses to requests to translations pages have the 'Vary' header
2574+ # set to include Accept-Language.
2575+ request = TranslationsBrowserRequest(StringIO.StringIO(''), {})
2576+ self.assertEquals(
2577+ request.response.getHeader('Vary'),
2578+ 'Cookie, Authorization, Accept-Language')