~launchpad-pqm/launchpad/devel

12293.1.11 by Curtis Hovey
Updated copyright.
1
# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
8687.15.18 by Karl Fogel
Add the copyright header block to files under lib/canonical/.
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
1936 by Canonical.com Patch Queue Manager
Fix various bugs to do with login links, and use a view with page template fragment and a view class to do the algorithmic stuff. r=spiv.
4
"""Browser code for the launchpad application."""
5
6
__metaclass__ = type
1716.1.190 by Christian Reis
Merge from RF, again, this time for real
7
__all__ = [
3842.2.9 by jml at canonical
Add AppFrontPageSearchView to __all__ so browser/specification.py can import it.
8
    'AppFrontPageSearchView',
7675.117.1 by Francis J. Lacoste
+login is now a 404 on the SSO and Unauthorized are really 500
9
    'DoesNotExistView',
6476.13.1 by Matthew Paul Thomas
more changes
10
    'Hierarchy',
6574.1.2 by Francis J. Lacoste
Moved ShipIt resources to their icing folder.
11
    'IcingFolder',
14382.1.5 by Aaron Bentley
view_name is included in BugTaskSearchListingView RequestCache.
12
    'iter_view_registrations',
9322.10.16 by Guilherme Salgado
Re-add a line I accidentally removed
13
    'LaunchpadImageFolder',
9474.2.1 by Gary Poster
convert three templates to 3.0; also correct some oddities in table layout in +graphics
14
    'LaunchpadGraphics',
6574.1.2 by Francis J. Lacoste
Moved ShipIt resources to their icing folder.
15
    'LaunchpadRootNavigation',
5530.1.21 by Carlos Perello Marin
Applied again my changes to allow a way to test disabled links
16
    'LinkView',
1716.1.190 by Christian Reis
Merge from RF, again, this time for real
17
    'LoginStatus',
13678.1.2 by Benji York
checkpoint
18
    'Macro',
1716.1.190 by Christian Reis
Merge from RF, again, this time for real
19
    'MaintenanceMessage',
5863.17.18 by Carlos Perello Marin
Applied review comments
20
    'NavigationMenuTabs',
3489.1.1 by Bjorn Tillenius
add test for soft timeouts and make sure they do get logged.
21
    'SoftTimeoutView',
14382.1.8 by Aaron Bentley
Cleanup
22
    'get_launchpad_views',
1716.1.190 by Christian Reis
Merge from RF, again, this time for real
23
    ]
1936 by Canonical.com Patch Queue Manager
Fix various bugs to do with login links, and use a view with page template fragment and a view class to do the algorithmic stuff. r=spiv.
24
6061.2.20 by Curtis Hovey
Resolved merge conflicts. Added an XXX and condition to getTopLevelPublications.
25
2389 by Canonical.com Patch Queue Manager
[r=sabdfl] improvements to logout pages
26
import cgi
14550.1.1 by Steve Kowalik
Run format-imports over lib/lp and lib/canonical/launchpad
27
from datetime import timedelta
5472.2.13 by Carlos Perello Marin
Sorted menu entries by ILink.text
28
import operator
3731 by Steve Alexander
merge from mpt resolving conflicts
29
import os
8296.2.1 by Brad Crittenden
Optimize redirection for lp.net/bugs/xxx URLs.
30
import re
3489.1.2 by Bjorn Tillenius
review commments.
31
import time
8296.2.1 by Brad Crittenden
Optimize redirection for lp.net/bugs/xxx URLs.
32
import urllib
2389 by Canonical.com Patch Queue Manager
[r=sabdfl] improvements to logout pages
33
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
34
from lazr.uri import URI
9550.4.1 by Guilherme Salgado
Fix it
35
from zope import i18n
9271.6.2 by Guilherme Salgado
Do not append a page breadcrumb when we're looking at an object's default page, plus a couple fixes.
36
from zope.app import zapi
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
37
from zope.component import (
14382.1.5 by Aaron Bentley
view_name is included in BugTaskSearchListingView RequestCache.
38
    getGlobalSiteManager,
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
39
    getUtility,
40
    queryAdapter,
41
    )
42
from zope.datetime import (
43
    DateTimeError,
44
    parseDatetimetz,
45
    )
46
from zope.i18nmessageid import Message
3722 by Steve Alexander
merge from main ui branch, resolving conflict
47
from zope.interface import implements
6753.5.1 by Diogo Matsubara
merge the twozero tour again
48
from zope.publisher.interfaces import NotFound
7675.117.1 by Francis J. Lacoste
+login is now a 404 on the SSO and Unauthorized are really 500
49
from zope.publisher.interfaces.browser import IBrowserPublisher
3691.267.37 by Stuart Bishop
Bazaar needs old XML-RPC URL to remain functional
50
from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest
3489.1.1 by Bjorn Tillenius
add test for soft timeouts and make sure they do get logged.
51
from zope.security.interfaces import Unauthorized
13678.1.2 by Benji York
checkpoint
52
from zope.traversing.interfaces import ITraversable
1716.1.192 by Christian Reis
Fix missing import. Man..
53
3489.1.1 by Bjorn Tillenius
add test for soft timeouts and make sure they do get logged.
54
from canonical.config import config
55
from canonical.launchpad.helpers import intOrZero
7675.560.2 by Graham Binns
More api work. Still doesn't work.
56
from canonical.launchpad.interfaces.launchpad import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
57
    IAppFrontPageSearchForm,
58
    IBazaarApplication,
59
    IRosettaApplication,
60
    )
14557.1.5 by Curtis Hovey
Simplified names.
61
from lp.services.statistics.interfaces.statistic import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
62
    ILaunchpadStatisticSet,
63
    )
14557.1.9 by Curtis Hovey
Moved logintoken to lp.verification.
64
from lp.services.verification.interfaces.logintoken import ILoginTokenSet
14557.1.12 by Curtis Hovey
Moved temporaryblobstorage to lp.services.
65
from lp.services.temporaryblobstorage.interfaces import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
66
    ITemporaryStorageManager,
67
    )
68
from canonical.launchpad.layers import WebServiceLayer
7675.560.7 by Graham Binns
Tidied up imports in canonical.launchpad.browswer.launchpad.
69
from canonical.launchpad.webapp import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
70
    canonical_name,
71
    canonical_url,
72
    LaunchpadView,
73
    Link,
74
    Navigation,
75
    StandardLaunchpadFacets,
76
    stepto,
77
    )
78
from canonical.launchpad.webapp.authorization import check_permission
7675.560.7 by Graham Binns
Tidied up imports in canonical.launchpad.browswer.launchpad.
79
from canonical.launchpad.webapp.breadcrumb import Breadcrumb
80
from canonical.launchpad.webapp.interfaces import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
81
    IBreadcrumb,
82
    ILaunchBag,
83
    ILaunchpadRoot,
84
    INavigationMenu,
85
    )
7675.560.7 by Graham Binns
Tidied up imports in canonical.launchpad.browswer.launchpad.
86
from canonical.launchpad.webapp.publisher import RedirectionView
11848.2.10 by Edwin Grubbs
Formatted imports.
87
from canonical.launchpad.webapp.url import urlappend
88
from canonical.launchpad.webapp.vhosts import allvhosts
89
from canonical.lazr import (
90
    ExportedFolder,
91
    ExportedImageFolder,
92
    )
93
from lp.answers.interfaces.questioncollection import IQuestionSet
11929.9.1 by Tim Penhey
Move launchpadform into lp.app.browser.
94
from lp.app.browser.launchpadform import (
95
    custom_widget,
96
    LaunchpadFormView,
97
    )
11626.3.2 by Curtis Hovey
Move tales gto lp.app.
98
from lp.app.browser.tales import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
99
    DurationFormatterAPI,
100
    MenuAPI,
101
    )
102
from lp.app.errors import (
103
    GoneError,
104
    NotFoundError,
105
    POSTToNonCanonicalURL,
106
    )
9511.2.7 by Michael Nelson
Fixed bug 433852
107
from lp.app.interfaces.headings import IMajorHeadingView
13130.1.13 by Curtis Hovey
Fixed import errors.
108
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
12293.1.10 by Curtis Hovey
Formatted imports.
109
from lp.app.widgets.project import ProjectScopeWidget
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
110
from lp.blueprints.interfaces.specification import ISpecificationSet
111
from lp.blueprints.interfaces.sprint import ISprintSet
7675.560.7 by Graham Binns
Tidied up imports in canonical.launchpad.browswer.launchpad.
112
from lp.bugs.interfaces.bug import IBugSet
113
from lp.bugs.interfaces.malone import IMaloneApplication
114
from lp.buildmaster.interfaces.builder import IBuilderSet
11270.2.11 by Tim Penhey
Move InvalidNamespace from lp.code.interfaces.branchnamespace to lp.code.errors
115
from lp.code.errors import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
116
    CannotHaveLinkedBranch,
117
    InvalidNamespace,
118
    NoLinkedBranch,
119
    )
8777.4.7 by Jonathan Lange
Update the other old uses of IBranches.
120
from lp.code.interfaces.branch import IBranchSet
11515.5.10 by Ian Booth
Lint fixes
121
from lp.code.interfaces.branchlookup import IBranchLookup
7675.560.7 by Graham Binns
Tidied up imports in canonical.launchpad.browswer.launchpad.
122
from lp.code.interfaces.codeimport import ICodeImportSet
123
from lp.hardwaredb.interfaces.hwdb import IHWDBApplication
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
124
from lp.registry.interfaces.announcement import IAnnouncementSet
7675.110.3 by Curtis Hovey
Ran the migration script to move registry code to lp.registry.
125
from lp.registry.interfaces.codeofconduct import ICodeOfConductSet
126
from lp.registry.interfaces.distribution import IDistributionSet
127
from lp.registry.interfaces.karma import IKarmaActionSet
11848.2.4 by Edwin Grubbs
Added browser classes and permissions.
128
from lp.registry.interfaces.nameblacklist import INameBlacklistSet
7675.110.3 by Curtis Hovey
Ran the migration script to move registry code to lp.registry.
129
from lp.registry.interfaces.person import IPersonSet
130
from lp.registry.interfaces.pillar import IPillarNameSet
131
from lp.registry.interfaces.product import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
132
    InvalidProductName,
133
    IProductSet,
134
    )
10326.1.2 by Henning Eggers
Renamed project interfaces module to projectgroup.
135
from lp.registry.interfaces.projectgroup import IProjectGroupSet
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
136
from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
14550.1.1 by Steve Kowalik
Run format-imports over lib/lp and lib/canonical/launchpad
137
from lp.services.identity.interfaces.account import AccountStatus
11382.6.34 by Gavin Panella
Reformat imports in all files touched so far.
138
from lp.services.propertycache import cachedproperty
13333.5.3 by Jonathan Lange
Use the new utc_now()
139
from lp.services.utils import utc_now
10348.3.1 by Jonathan Davies
Put in place the foundations for exporting Country's over the API.
140
from lp.services.worlddata.interfaces.country import ICountrySet
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
141
from lp.services.worlddata.interfaces.language import ILanguageSet
142
from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet
143
from lp.soyuz.interfaces.packageset import IPackagesetSet
13387.1.10 by Francis J. Lacoste
URL to processor is now of the form /+processors/<name>
144
from lp.soyuz.interfaces.processor import (
145
    IProcessorFamilySet,
146
    IProcessorSet,
147
    )
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
148
from lp.testopenid.interfaces.server import ITestOpenIDApplication
149
from lp.translations.interfaces.translationgroup import ITranslationGroupSet
8751.1.1 by Danilo Å egan
Store migration changes so far.
150
from lp.translations.interfaces.translationimportqueue import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
151
    ITranslationImportQueue,
152
    )
7944.3.3 by Francis J. Lacoste
Moved interfaces.
153
2519 by Canonical.com Patch Queue Manager
r=BjornT, more actions portlets converted to menus, introduction of LaunchpadView.
154
5863.17.9 by Carlos Perello Marin
Applied most changes discussed with Francis
155
class NavigationMenuTabs(LaunchpadView):
156
    """View class that helps its template render the navigation menu tabs.
157
5863.17.18 by Carlos Perello Marin
Applied review comments
158
    Nothing at all is rendered if there are no navigation menu items.
5863.17.9 by Carlos Perello Marin
Applied most changes discussed with Francis
159
    """
160
161
    def initialize(self):
6574.3.1 by Curtis Hovey
Updated NavigationMenuTabs.initialize to MenuAPI.
162
        menuapi = MenuAPI(self.context)
5863.17.11 by Carlos Perello Marin
Reverted the changes to menubox so we don't include Navigation links there, we need to duplicate that information until the migration is complete
163
        self.links = sorted([
9061.2.15 by Edwin Grubbs
Circumvent confusing AttributeErrors.
164
            link for link in menuapi.navigation.values()
6574.3.1 by Curtis Hovey
Updated NavigationMenuTabs.initialize to MenuAPI.
165
            if (link.enabled or config.devmode)],
5863.17.11 by Carlos Perello Marin
Reverted the changes to menubox so we don't include Navigation links there, we need to duplicate that information until the migration is complete
166
            key=operator.attrgetter('sort_key'))
6574.3.1 by Curtis Hovey
Updated NavigationMenuTabs.initialize to MenuAPI.
167
        self.title = None
168
        if len(self.links) > 0:
169
            facet = menuapi.selectedfacetname()
170
            menu = queryAdapter(self.context, INavigationMenu, name=facet)
171
            if menu is not None:
172
                self.title = menu.title
7675.295.8 by Curtis Hovey
Revised the layout to hide holes created by optional content.
173
        self.enabled_links = [link for link in self.links if link.enabled]
5863.17.9 by Carlos Perello Marin
Applied most changes discussed with Francis
174
175
    def render(self):
176
        if not self.links:
2519 by Canonical.com Patch Queue Manager
r=BjornT, more actions portlets converted to menus, introduction of LaunchpadView.
177
            return ''
178
        else:
179
            return self.template()
2494 by Canonical.com Patch Queue Manager
r=stub add 'launchpad (or shipit) is going down for maintenance' facility.
180
181
5530.1.21 by Carlos Perello Marin
Applied again my changes to allow a way to test disabled links
182
class LinkView(LaunchpadView):
183
    """View class that helps its template render a menu link.
184
185
    The link is not rendered if it's not enabled and we are not in development
186
    mode.
187
    """
7675.285.9 by Curtis Hovey
Revised the fmt:link rule to use 'sprite modify' when rendering links for the mofidy icons.
188
    MODIFY_ICONS = ('edit', 'remove', 'trash-icon')
8868.1.1 by Curtis Hovey
Simplify the fmt:link options and ensure edit, remove and trash icons are after link text.
189
190
    @property
191
    def sprite_class(self):
192
        """Return the class used to display the link's icon."""
7675.285.9 by Curtis Hovey
Revised the fmt:link rule to use 'sprite modify' when rendering links for the mofidy icons.
193
        if self.context.icon in self.MODIFY_ICONS:
194
            # The 3.0 UI design says these are displayed like other icons
195
            # But they do not have the same use so we want to keep this rule
196
            # separate.
197
            return 'sprite modify'
8868.1.1 by Curtis Hovey
Simplify the fmt:link options and ensure edit, remove and trash icons are after link text.
198
        else:
199
            return 'sprite'
5530.1.21 by Carlos Perello Marin
Applied again my changes to allow a way to test disabled links
200
201
    def render(self):
202
        """Render the menu link if it's enabled or we're in dev mode."""
203
        if self.context.enabled or config.devmode:
6912.5.19 by Curtis Hovey
Revised XXX comment metadata to remove anonmalies from the XXX report.
204
            # XXX: Tom Berger 2008-04-16 bug=218706:
6087.4.2 by Tom Berger
remove the actions menu from the bug target bugs index page
205
            # We strip the result of the template rendering
6087.4.4 by Tom Berger
typo
206
            # since ZPT seems to always insert a line break
6087.4.2 by Tom Berger
remove the actions menu from the bug target bugs index page
207
            # at the end of an embedded template.
208
            return self.template().strip()
5530.1.21 by Carlos Perello Marin
Applied again my changes to allow a way to test disabled links
209
        else:
210
            return ''
211
12611.2.5 by Brad Crittenden
Simplified the launchpad-inline page template by moving logic into the view. Removed unnecessary styling.
212
    @property
213
    def css_class(self):
214
        """Return the CSS class."""
215
        value = ["menu-link-%s" % self.context.name]
12611.2.7 by Brad Crittenden
Fixed failing tests
216
        if not self.context.linked:
217
            value.append('nolink')
12611.2.5 by Brad Crittenden
Simplified the launchpad-inline page template by moving logic into the view. Removed unnecessary styling.
218
        if self.context.icon:
219
            value.append(self.sprite_class)
220
            value.append(self.context.icon)
221
        if self.context.hidden:
222
            value.append('invisible-link')
223
        return " ".join(value)
224
225
    @property
226
    def url(self):
227
        """Return the url if linked."""
228
        if self.context.linked:
229
            return self.context.url
230
        return ''
231
232
    @property
233
    def summary(self):
234
        """Return the summary if linked."""
235
        if self.context.linked:
236
            return self.context.summary
237
        return ''
238
5530.1.21 by Carlos Perello Marin
Applied again my changes to allow a way to test disabled links
239
6476.13.1 by Matthew Paul Thomas
more changes
240
class Hierarchy(LaunchpadView):
241
    """The hierarchy part of the location bar on each page."""
242
7675.618.54 by Paul Hummer
Patched Hierarchy to optionally leave off the vhost request
243
    vhost_breadcrumb = True
244
9087.4.3 by Guilherme Salgado
Finally, something that works with all examples
245
    @property
246
    def objects(self):
247
        """The objects for which we want breadcrumbs."""
9322.10.15 by Guilherme Salgado
Fix a bunch of things
248
        return self.request.traversed_objects
9087.4.3 by Guilherme Salgado
Finally, something that works with all examples
249
9225.1.3 by Michael Nelson
New styled breadcrumbs.
250
    @cachedproperty
6767.5.8 by Maris Fogels
Added the Hierarchy.items() method.
251
    def items(self):
6767.5.40 by Maris Fogels
Rework based on reviewer feedback.
252
        """Return a list of `IBreadcrumb` objects visible in the hierarchy.
6767.5.8 by Maris Fogels
Added the Hierarchy.items() method.
253
254
        The list starts with the breadcrumb closest to the hierarchy root.
255
        """
9087.4.9 by Guilherme Salgado
Fix a couple existing tests and tweak my implementation to not break others
256
        breadcrumbs = []
9087.4.3 by Guilherme Salgado
Finally, something that works with all examples
257
        for obj in self.objects:
7675.618.60 by Paul Hummer
Fixed some more breadcrumbs assery and now it's NIIIICE</borat>
258
            breadcrumb = IBreadcrumb(obj, None)
9209.4.1 by Guilherme Salgado
Get rid of BreadcrumbBuilder as it's not necessary anymore
259
            if breadcrumb is not None:
260
                breadcrumbs.append(breadcrumb)
9087.4.3 by Guilherme Salgado
Finally, something that works with all examples
261
262
        host = URI(self.request.getURL()).host
9271.6.1 by Guilherme Salgado
Breadcrumbs for leaf pages
263
        mainhost = allvhosts.configs['mainsite'].hostname
7675.618.54 by Paul Hummer
Patched Hierarchy to optionally leave off the vhost request
264
        if (len(breadcrumbs) != 0 and
265
            host != mainhost and
266
            self.vhost_breadcrumb):
9271.6.1 by Guilherme Salgado
Breadcrumbs for leaf pages
267
            # We have breadcrumbs and we're not on the mainsite, so we'll
268
            # sneak an extra breadcrumb for the vhost we're on.
269
            vhost = host.split('.')[0]
270
271
            # Iterate over the context of our breadcrumbs in reverse order and
272
            # for the first one we find an adapter named after the vhost we're
273
            # on, generate an extra breadcrumb and insert it in our list.
274
            for idx, breadcrumb in reversed(list(enumerate(breadcrumbs))):
275
                extra_breadcrumb = queryAdapter(
276
                    breadcrumb.context, IBreadcrumb, name=vhost)
277
                if extra_breadcrumb is not None:
278
                    breadcrumbs.insert(idx + 1, extra_breadcrumb)
279
                    break
9469.1.1 by Barry Warsaw
Redesign ~person/+editemails for UI 3.0. Along the way, fix bug 180349 by linking the team name to the canonical url for the team.
280
        if len(breadcrumbs) > 0:
9271.6.3 by Guilherme Salgado
Better names
281
            page_crumb = self.makeBreadcrumbForRequestedPage()
9271.6.2 by Guilherme Salgado
Do not append a page breadcrumb when we're looking at an object's default page, plus a couple fixes.
282
            if page_crumb:
283
                breadcrumbs.append(page_crumb)
9209.4.1 by Guilherme Salgado
Get rid of BreadcrumbBuilder as it's not necessary anymore
284
        return breadcrumbs
6767.5.21 by Maris Fogels
Changed the hierarchy interface a bit, so that breadcrumb construction is handed off to the class. This ensures consistency in presentation.
285
9511.2.7 by Michael Nelson
Fixed bug 433852
286
    @property
9511.2.10 by Michael Nelson
Small style changes from barry.
287
    def _naked_context_view(self):
9511.2.7 by Michael Nelson
Fixed bug 433852
288
        """Return the unproxied view for the context of the hierarchy."""
289
        from zope.security.proxy import removeSecurityProxy
9511.2.11 by Michael Nelson
Fix for test failures during ec2test run. Ensure traversed objects is not empty before trying to get the last traversed object.
290
        if len(self.request.traversed_objects) > 0:
291
            return removeSecurityProxy(self.request.traversed_objects[-1])
292
        else:
293
            return None
9511.2.7 by Michael Nelson
Fixed bug 433852
294
9271.6.3 by Guilherme Salgado
Better names
295
    def makeBreadcrumbForRequestedPage(self):
9271.6.2 by Guilherme Salgado
Do not append a page breadcrumb when we're looking at an object's default page, plus a couple fixes.
296
        """Return an `IBreadcrumb` for the requested page.
297
9271.6.3 by Guilherme Salgado
Better names
298
        The `IBreadcrumb` for the requested page is created using the current
299
        URL and the page's name (i.e. the last path segment of the URL).
300
9271.6.2 by Guilherme Salgado
Do not append a page breadcrumb when we're looking at an object's default page, plus a couple fixes.
301
        If the requested page (as specified in self.request) is the default
9322.10.11 by Guilherme Salgado
Do not rely on Navigation for having objects added to request.traversed_objects.
302
        one for our parent view's context, return None.
9271.6.2 by Guilherme Salgado
Do not append a page breadcrumb when we're looking at an object's default page, plus a couple fixes.
303
        """
9271.6.1 by Guilherme Salgado
Breadcrumbs for leaf pages
304
        url = self.request.getURL()
9322.10.15 by Guilherme Salgado
Fix a bunch of things
305
        obj = self.request.traversed_objects[-2]
9322.10.1 by Guilherme Salgado
First attempt at using page titles for breadcrumbs. Get the page using getMultiAdapter() and check for a page_title attribute, falling back to getting the title from pagetitles.py
306
        default_view_name = zapi.getDefaultViewName(obj, self.request)
9511.2.10 by Michael Nelson
Small style changes from barry.
307
        view = self._naked_context_view
9322.10.15 by Guilherme Salgado
Fix a bunch of things
308
        if view.__name__ != default_view_name:
9322.10.1 by Guilherme Salgado
First attempt at using page titles for breadcrumbs. Get the page using getMultiAdapter() and check for a page_title attribute, falling back to getting the title from pagetitles.py
309
            title = getattr(view, 'page_title', None)
310
            if title is None:
9322.10.22 by Guilherme Salgado
Fix a bunch of tests and change makeBreadcrumbForRequestedPage() to use view.label before trying to look up a title in pagetitles.py
311
                title = getattr(view, 'label', None)
9550.4.1 by Guilherme Salgado
Fix it
312
            if isinstance(title, Message):
313
                title = i18n.translate(title, context=self.request)
9271.6.1 by Guilherme Salgado
Breadcrumbs for leaf pages
314
            breadcrumb = Breadcrumb(None)
315
            breadcrumb._url = url
9322.10.1 by Guilherme Salgado
First attempt at using page titles for breadcrumbs. Get the page using getMultiAdapter() and check for a page_title attribute, falling back to getting the title from pagetitles.py
316
            breadcrumb.text = title
9271.6.1 by Guilherme Salgado
Breadcrumbs for leaf pages
317
            return breadcrumb
9271.6.3 by Guilherme Salgado
Better names
318
        else:
319
            return None
9271.6.1 by Guilherme Salgado
Breadcrumbs for leaf pages
320
9225.1.3 by Michael Nelson
New styled breadcrumbs.
321
    @property
322
    def display_breadcrumbs(self):
323
        """Return whether the breadcrumbs should be displayed."""
324
        # If there is only one breadcrumb then it does not make sense
325
        # to display it as it will simply repeat the context.title.
9511.2.7 by Michael Nelson
Fixed bug 433852
326
        # If the view is an IMajorHeadingView then we do not want
327
        # to display breadcrumbs either.
9511.2.10 by Michael Nelson
Small style changes from barry.
328
        has_major_heading = IMajorHeadingView.providedBy(
329
            self._naked_context_view)
330
        return len(self.items) > 1 and not has_major_heading
6476.12.2 by Matthew Paul Thomas
more changes
331
9297.2.5 by Barry Warsaw
cleanup
332
13678.1.4 by Benji York
move the Macro class up so it can be subclassed
333
class Macro:
334
    """Keeps templates that are registered as pages from being URL accessable.
335
336
    The standard pattern in LP is to register templates that contain macros as
337
    views on all objects:
338
339
    <browser:page
340
        for="*"
341
        name="+main-template-macros"
342
        template="../templates/base-layout-macros.pt"
343
        permission="zope.Public"
344
        />
345
13678.1.9 by Benji York
improve wording using suggestion from review
346
    Without this class, that pattern would make the template URL traversable
347
    from any object.  Therefore requests like these would all "work":
13678.1.4 by Benji York
move the Macro class up so it can be subclassed
348
349
        http://launchpad.net/+main-template-macros
350
        http://launchpad.net/ubuntu/+main-template-macros
351
        http://launchpad.net/ubuntu/+main-template-macros
352
        https://blueprints.launchpad.dev/ubuntu/hoary/+main-template-macros
353
354
    Obviously, those requests wouldn't do anything useful and would instead
355
    generate an OOPS.
356
357
    It would be nice to use a different pattern for macros instead, but we've
358
    grown dependent on some of the peculiatrities of registering macro
359
    templates in this way.
360
361
    This class was created in order to prevent macro templates from being
362
    accessable via URL without having to make nontrivial changes to the many,
363
    many templates that use macros.  To use the class add a "class" parameter
364
    to macro template registrations:
365
366
    <browser:page
367
        for="*"
368
        name="+main-template-macros"
369
        template="../templates/base-layout-macros.pt"
370
        class="lp.app.browser.launchpad.Macro"
371
        permission="zope.Public"
372
        />
373
    """
374
    implements(IBrowserPublisher, ITraversable)
375
376
    def __init__(self, context, request):
377
        self.context = context
378
379
    def traverse(self, name, furtherPath):
380
        return self.index.macros[name]
381
382
    def browserDefault(self, request):
383
        return self, ()
384
385
    def publishTraverse(self, request, name):
386
        raise NotFound(self.context, self.__name__)
387
388
    def __call__(self):
389
        raise NotFound(self.context, self.__name__)
390
391
13678.1.10 by Benji York
fix failures found in EC2
392
class MaintenanceMessage:
2494 by Canonical.com Patch Queue Manager
r=stub add 'launchpad (or shipit) is going down for maintenance' facility.
393
    """Display a maintenance message if the control file is present and
394
    it contains a valid iso format time.
395
396
    The maintenance message shows the approximate time before launchpad will
397
    be taken offline for maintenance.
398
399
    The control file is +maintenancetime.txt in the launchpad root.
400
401
    If there is no maintenance message, an empty string is returned.
402
403
    If the maintenance time is too far in the future, then an empty string
404
    is returned.
405
406
    If the maintenance time is in the past, then the maintenance message says
407
    that Launchpad will go offline "very very soon".
408
409
    If the text in the maintenance message is poorly formatted, then an
410
    empty string is returned, and a warning should be logged.
411
    """
412
413
    timelefttext = None
414
415
    notmuchtime = timedelta(seconds=30)
416
    toomuchtime = timedelta(seconds=1800)  # 30 minutes
417
418
    def __call__(self):
419
        if os.path.exists('+maintenancetime.txt'):
420
            message = file('+maintenancetime.txt').read()
421
            try:
422
                maintenancetime = parseDatetimetz(message)
423
            except DateTimeError:
4664.1.1 by Curtis Hovey
Normalized comments for bug 3732.
424
                # XXX SteveAlexander 2005-09-22: log a warning here.
2494 by Canonical.com Patch Queue Manager
r=stub add 'launchpad (or shipit) is going down for maintenance' facility.
425
                return ''
13333.5.3 by Jonathan Lange
Use the new utc_now()
426
            timeleft = maintenancetime - utc_now()
2494 by Canonical.com Patch Queue Manager
r=stub add 'launchpad (or shipit) is going down for maintenance' facility.
427
            if timeleft > self.toomuchtime:
428
                return ''
429
            elif timeleft < self.notmuchtime:
430
                self.timelefttext = 'very very soon'
431
            else:
432
                self.timelefttext = 'in %s' % (
433
                    DurationFormatterAPI(timeleft).approximateduration())
434
            return self.index()
435
        return ''
1970 by Canonical.com Patch Queue Manager
Daf's menus branch. r=SteveA.
436
437
438
class LaunchpadRootFacets(StandardLaunchpadFacets):
2622 by Canonical.com Patch Queue Manager
[trivial] remove all uses of browser:suburl and nuke the suburl directive code.
439
1970 by Canonical.com Patch Queue Manager
Daf's menus branch. r=SteveA.
440
    usedfor = ILaunchpadRoot
441
3691.398.22 by Francis J. Lacoste
Rename support facet to answers.
442
    enable_only = ['overview', 'bugs', 'answers', 'specifications',
3691.147.3 by Matthew Paul Thomas
Removes Calendar facet links, and begins renaming 'Specifications' to 'Features' and 'Branches' to 'Code'.
443
                   'translations', 'branches']
2738 by Canonical.com Patch Queue Manager
[trivial] various bugs fixed
444
1970 by Canonical.com Patch Queue Manager
Daf's menus branch. r=SteveA.
445
    def overview(self):
446
        target = ''
6713.2.1 by Matthew Paul Thomas
Adds application tabs to the application front pages.
447
        text = 'Launchpad Home'
3691.31.31 by Steve Alexander
extend facet menu hook to choose appropriate site for facet menus by name.
448
        return Link(target, text)
1970 by Canonical.com Patch Queue Manager
Daf's menus branch. r=SteveA.
449
450
    def translations(self):
3618.1.50 by Steve Alexander
implement all vhosts, update main template presentation of vhosts
451
        target = ''
1970 by Canonical.com Patch Queue Manager
Daf's menus branch. r=SteveA.
452
        text = 'Translations'
3691.31.31 by Steve Alexander
extend facet menu hook to choose appropriate site for facet menus by name.
453
        return Link(target, text)
1970 by Canonical.com Patch Queue Manager
Daf's menus branch. r=SteveA.
454
455
    def bugs(self):
3618.1.50 by Steve Alexander
implement all vhosts, update main template presentation of vhosts
456
        target = ''
1970 by Canonical.com Patch Queue Manager
Daf's menus branch. r=SteveA.
457
        text = 'Bugs'
3691.31.31 by Steve Alexander
extend facet menu hook to choose appropriate site for facet menus by name.
458
        return Link(target, text)
1970 by Canonical.com Patch Queue Manager
Daf's menus branch. r=SteveA.
459
3691.398.22 by Francis J. Lacoste
Rename support facet to answers.
460
    def answers(self):
3618.1.50 by Steve Alexander
implement all vhosts, update main template presentation of vhosts
461
        target = ''
462
        text = 'Answers'
3691.398.22 by Francis J. Lacoste
Rename support facet to answers.
463
        summary = 'Launchpad Answer Tracker'
3691.31.31 by Steve Alexander
extend facet menu hook to choose appropriate site for facet menus by name.
464
        return Link(target, text, summary)
2396 by Canonical.com Patch Queue Manager
[r=spiv] launchpad support tracker
465
2423 by Canonical.com Patch Queue Manager
r=bjornt plus some [trivial] extra refactorings. more consistent facet menus across the whole of launchpad. improvements in the API.
466
    def specifications(self):
3691.31.28 by Steve Alexander
kinda hacky implementation of binding facets to particular sites
467
        target = ''
3618.1.49 by Steve Alexander
various small refactorings, change features.launchpad.dev to blueprint.launchpad.dev, implement code.launchpad.dev
468
        text = 'Blueprints'
2396 by Canonical.com Patch Queue Manager
[r=spiv] launchpad support tracker
469
        summary = 'Launchpad feature specification tracker.'
3691.31.31 by Steve Alexander
extend facet menu hook to choose appropriate site for facet menus by name.
470
        return Link(target, text, summary)
2396 by Canonical.com Patch Queue Manager
[r=spiv] launchpad support tracker
471
3311.1.1 by Mark Shuttleworth
Polish up the Bazaar front page
472
    def branches(self):
3618.1.49 by Steve Alexander
various small refactorings, change features.launchpad.dev to blueprint.launchpad.dev, implement code.launchpad.dev
473
        target = ''
10842.1.1 by Paul Hummer
Changed 'Branches' to 'Code' and prepping to run tests
474
        text = 'Code'
3311.1.1 by Mark Shuttleworth
Polish up the Bazaar front page
475
        summary = 'The Code Bazaar'
3691.31.31 by Steve Alexander
extend facet menu hook to choose appropriate site for facet menus by name.
476
        return Link(target, text, summary)
3311.1.1 by Mark Shuttleworth
Polish up the Bazaar front page
477
1970 by Canonical.com Patch Queue Manager
Daf's menus branch. r=SteveA.
478
1936 by Canonical.com Patch Queue Manager
Fix various bugs to do with login links, and use a view with page template fragment and a view class to do the algorithmic stuff. r=spiv.
479
class LoginStatus:
480
481
    def __init__(self, context, request):
482
        self.context = context
483
        self.request = request
484
        self.user = getUtility(ILaunchBag).user
485
486
    @property
487
    def login_shown(self):
488
        return (self.user is None and
489
                '+login' not in self.request['PATH_INFO'])
490
491
    @property
492
    def logged_in(self):
493
        return self.user is not None
494
495
    @property
496
    def login_url(self):
497
        query_string = self.request.get('QUERY_STRING', '')
2389 by Canonical.com Patch Queue Manager
[r=sabdfl] improvements to logout pages
498
499
        # If we have a query string, remove some things we don't want, and
500
        # keep it around.
1936 by Canonical.com Patch Queue Manager
Fix various bugs to do with login links, and use a view with page template fragment and a view class to do the algorithmic stuff. r=spiv.
501
        if query_string:
2389 by Canonical.com Patch Queue Manager
[r=sabdfl] improvements to logout pages
502
            query_dict = cgi.parse_qs(query_string, keep_blank_values=True)
503
            query_dict.pop('loggingout', None)
504
            query_string = urllib.urlencode(
505
                sorted(query_dict.items()), doseq=True)
506
            # If we still have a query_string after things we don't want
507
            # have been removed, add it onto the url.
508
            if query_string:
509
                query_string = '?' + query_string
1966 by Canonical.com Patch Queue Manager
[trivial] really fix regression this time. new approach, combining request.getApplicationURL() with PATH_INFO with its virtual hosting info removed, to get the URL. This is not ideal, but it will work more reliably.
510
511
        # The approach we're taking is to combine the application url with
512
        # the path_info, taking out path steps that are to do with virtual
513
        # hosting.  This is not exactly correct, as the application url
514
        # can have other path steps in it.  We're not using the feature of
515
        # having other path steps in the application url, so this will work
516
        # for us, assuming we don't need that in the future.
517
518
        # The application_url is typically like 'http://thing:port'. No
519
        # trailing slash.
520
        application_url = self.request.getApplicationURL()
1965 by Canonical.com Patch Queue Manager
[trivial] fixed virtual hosting regression for login links, with tests.
521
522
        # We're going to use PATH_INFO to remove any spurious '+index' at the
523
        # end of the URL.  But, PATH_INFO will contain virtual hosting
524
        # configuration, if there is any.
1936 by Canonical.com Patch Queue Manager
Fix various bugs to do with login links, and use a view with page template fragment and a view class to do the algorithmic stuff. r=spiv.
525
        path_info = self.request['PATH_INFO']
1966 by Canonical.com Patch Queue Manager
[trivial] really fix regression this time. new approach, combining request.getApplicationURL() with PATH_INFO with its virtual hosting info removed, to get the URL. This is not ideal, but it will work more reliably.
526
1965 by Canonical.com Patch Queue Manager
[trivial] fixed virtual hosting regression for login links, with tests.
527
        # Remove any virtual hosting segments.
528
        path_steps = []
529
        in_virtual_hosting_section = False
530
        for step in path_info.split('/'):
531
            if step.startswith('++vh++'):
532
                in_virtual_hosting_section = True
533
                continue
534
            if step == '++':
535
                in_virtual_hosting_section = False
536
                continue
537
            if not in_virtual_hosting_section:
538
                path_steps.append(step)
539
        path = '/'.join(path_steps)
1966 by Canonical.com Patch Queue Manager
[trivial] really fix regression this time. new approach, combining request.getApplicationURL() with PATH_INFO with its virtual hosting info removed, to get the URL. This is not ideal, but it will work more reliably.
540
1936 by Canonical.com Patch Queue Manager
Fix various bugs to do with login links, and use a view with page template fragment and a view class to do the algorithmic stuff. r=spiv.
541
        # Make the URL stop at the end of path_info so that we don't get
542
        # spurious '+index' at the end.
1966 by Canonical.com Patch Queue Manager
[trivial] really fix regression this time. new approach, combining request.getApplicationURL() with PATH_INFO with its virtual hosting info removed, to get the URL. This is not ideal, but it will work more reliably.
543
        full_url = '%s%s' % (application_url, path)
1936 by Canonical.com Patch Queue Manager
Fix various bugs to do with login links, and use a view with page template fragment and a view class to do the algorithmic stuff. r=spiv.
544
        if full_url.endswith('/'):
545
            full_url = full_url[:-1]
546
        logout_url_end = '/+logout'
10420.3.1 by Guilherme Salgado
Fix the bug by not including a +openid-callback path element on the link to the +login page.
547
        openid_callback_url_end = '/+openid-callback'
1936 by Canonical.com Patch Queue Manager
Fix various bugs to do with login links, and use a view with page template fragment and a view class to do the algorithmic stuff. r=spiv.
548
        if full_url.endswith(logout_url_end):
549
            full_url = full_url[:-len(logout_url_end)]
10420.3.1 by Guilherme Salgado
Fix the bug by not including a +openid-callback path element on the link to the +login page.
550
        elif full_url.endswith(openid_callback_url_end):
551
            full_url = full_url[:-len(openid_callback_url_end)]
552
        else:
553
            # No need to remove anything from full_url.
554
            pass
1936 by Canonical.com Patch Queue Manager
Fix various bugs to do with login links, and use a view with page template fragment and a view class to do the algorithmic stuff. r=spiv.
555
        return '%s/+login%s' % (full_url, query_string)
2622 by Canonical.com Patch Queue Manager
[trivial] remove all uses of browser:suburl and nuke the suburl directive code.
556
557
558
class LaunchpadRootNavigation(Navigation):
559
560
    usedfor = ILaunchpadRoot
561
3859.4.3 by Francis J. Lacoste
Rename URLs of the Answer Tracker containging ticket or support-contact.
562
    @stepto('support')
563
    def redirect_support(self):
11626.4.1 by Curtis Hovey
Save spike for switch from feedback@ to /support. There are open questions about the criteria for showing suppot--anonymous or cannot login. Should /support not be reused.
564
        """Redirect /support to launchpad Answers site."""
5576.1.9 by Barry Warsaw
lint fixes
565
        target_url = canonical_url(
11626.4.1 by Curtis Hovey
Save spike for switch from feedback@ to /support. There are open questions about the criteria for showing suppot--anonymous or cannot login. Should /support not be reused.
566
            getUtility(ILaunchpadCelebrities).launchpad, rootsite='answers')
567
        return self.redirectSubTree(target_url, status=301)
3859.4.3 by Francis J. Lacoste
Rename URLs of the Answer Tracker containging ticket or support-contact.
568
4906.1.1 by Diogo Matsubara
Fix bug 141057 (Move Legal items from /legal to help.lp.net) by redirecting lp.net/legal to the wiki h.l.net/Legal
569
    @stepto('legal')
570
    def redirect_legal(self):
571
        """Redirect /legal to help.launchpad.net/Legal site."""
572
        return self.redirectSubTree(
573
            'https://help.launchpad.net/Legal', status=301)
574
4934.3.24 by Elliot Murphy
Restore code from a bad merge from trunk.
575
    @stepto('faq')
576
    def redirect_faq(self):
7178.1.1 by Diogo Matsubara
[r=kiko, release-critical=kiko] Fixes bug 273612 (The FAQs on the help wiki should use Launchpad's FAQ system), bug 257444 (Tour spelling polish for branch hosting), bug 282969 (dogfood is displaying traceback to any user) and [release-critical=joey] update what's new for 2.1.10
577
        """Redirect /faq to launchpad-project/+faqs."""
4934.3.24 by Elliot Murphy
Restore code from a bad merge from trunk.
578
        return self.redirectSubTree(
7178.1.1 by Diogo Matsubara
[r=kiko, release-critical=kiko] Fixes bug 273612 (The FAQs on the help wiki should use Launchpad's FAQ system), bug 257444 (Tour spelling polish for branch hosting), bug 282969 (dogfood is displaying traceback to any user) and [release-critical=joey] update what's new for 2.1.10
579
            'https://answers.launchpad.net/launchpad-project/+faqs',
580
            status=301)
4934.3.24 by Elliot Murphy
Restore code from a bad merge from trunk.
581
582
    @stepto('feedback')
583
    def redirect_feedback(self):
584
        """Redirect /feedback to help.launchpad.net/Feedback site."""
585
        return self.redirectSubTree(
586
            'https://help.launchpad.net/Feedback', status=301)
587
7876.1.1 by Jonathan Lange
Basic branch redirection.
588
    @stepto('+branch')
589
    def redirect_branch(self):
7876.1.3 by Jonathan Lange
Add trailing path support
590
        """Redirect /+branch/<foo> to the branch named 'foo'.
591
592
        'foo' can be the unique name of the branch, or any of the aliases for
593
        the branch.
11515.5.1 by Ian Booth
redirect_branch changes
594
        If 'foo' resolves to an ICanHasLinkedBranch instance but the linked
11515.5.7 by Ian Booth
Code review fixes
595
        branch is not yet set, redirect back to the referring page with a
596
        suitable notification message.
11515.5.1 by Ian Booth
redirect_branch changes
597
        If 'foo' is completely invalid, redirect back to the referring page
598
        with a suitable error message.
7876.1.3 by Jonathan Lange
Add trailing path support
599
        """
11515.5.7 by Ian Booth
Code review fixes
600
11582.3.2 by Ian Booth
Add new test for untested code path
601
        # The default target url to go to will be back to the referring page
602
        # (in the case that there is an error resolving the branch url).
603
        # Note: the http referer may be None if someone has hacked a url
604
        # directly rather than following a /+branch/<foo> link.
605
        target_url = self.request.getHeader('referer')
7876.1.1 by Jonathan Lange
Basic branch redirection.
606
        path = '/'.join(self.request.stepstogo)
7876.1.2 by Jonathan Lange
Support 'product' names at the end of +branch.
607
        try:
11769.1.1 by Tim Penhey
Raise NotFoundError not the original error in the case where there is no referrer.
608
            branch_data = getUtility(IBranchLookup).getByLPPath(path)
609
            branch, trailing = branch_data
610
            target_url = canonical_url(branch)
611
            if trailing is not None:
612
                target_url = urlappend(target_url, trailing)
613
        except (NoLinkedBranch), e:
614
            # A valid ICanHasLinkedBranch target exists but there's no
615
            # branch or it's not visible.
616
617
            # If are aren't arriving at this invalid branch URL from
618
            # another page then we just raise a NotFoundError to generate
619
            # a 404, otherwise we end up in a bad recursion loop. The
620
            # target url will be None in that case.
621
            if target_url is None:
622
                raise NotFoundError
623
            self.request.response.addNotification(
624
                "The target %s does not have a linked branch." % path)
11515.5.1 by Ian Booth
redirect_branch changes
625
        except (CannotHaveLinkedBranch, InvalidNamespace,
11582.3.3 by Ian Booth
Fix 2.6 syntax
626
                InvalidProductName, NotFoundError), e:
11582.3.1 by Ian Booth
Initial coding
627
            # If are aren't arriving at this invalid branch URL from another
11769.1.1 by Tim Penhey
Raise NotFoundError not the original error in the case where there is no referrer.
628
            # page then we just raise a NotFoundError to generate a 404,
629
            # otherwise we end up in a bad recursion loop. The target url will
630
            # be None in that case.
11582.3.2 by Ian Booth
Add new test for untested code path
631
            if target_url is None:
11769.1.1 by Tim Penhey
Raise NotFoundError not the original error in the case where there is no referrer.
632
                raise NotFoundError
11515.5.8 by Ian Booth
Improve error messages
633
            error_msg = str(e)
11515.5.12 by Ian Booth
Small fixes as per code review
634
            if error_msg == '':
11515.5.8 by Ian Booth
Improve error messages
635
                error_msg = "Invalid branch lp:%s." % path
636
            self.request.response.addErrorNotification(error_msg)
11515.5.7 by Ian Booth
Code review fixes
637
11582.3.2 by Ian Booth
Add new test for untested code path
638
        return self.redirectSubTree(target_url)
7876.1.1 by Jonathan Lange
Basic branch redirection.
639
8003.1.2 by Celso Providelo
Change traversal to IBuilderSet (buildfarm) to not use '+' since it is not a path prefix. Now the buildfarm page is available at '/builders', but '/+builds' will permanently redirects to the new URL.
640
    @stepto('+builds')
641
    def redirect_buildfarm(self):
642
        """Redirect old /+builds requests to new URL, /builders."""
643
        new_url = '/builders'
644
        return self.redirectSubTree(
645
            urlappend(new_url, '/'.join(self.request.stepstogo)))
646
8003.1.4 by Celso Providelo
applying review comments, r=intellectronica.
647
    # XXX cprov 2009-03-19 bug=345877: path segments starting with '+'
648
    # should never correspond to a valid traversal, they confuse the
649
    # hierarchical navigation model.
2622 by Canonical.com Patch Queue Manager
[trivial] remove all uses of browser:suburl and nuke the suburl directive code.
650
    stepto_utilities = {
5151.1.14 by Mark Shuttleworth
Publish single Atom feed with all announcements hosted in LP
651
        '+announcements': IAnnouncementSet,
3691.436.8 by Mark Shuttleworth
Create overview of all mentoring activity
652
        'binarypackagenames': IBinaryPackageNameSet,
8777.4.7 by Jonathan Lange
Update the other old uses of IBranches.
653
        'branches': IBranchSet,
3691.436.8 by Mark Shuttleworth
Create overview of all mentoring activity
654
        'bugs': IMaloneApplication,
8003.1.2 by Celso Providelo
Change traversal to IBuilderSet (buildfarm) to not use '+' since it is not a path prefix. Now the buildfarm page is available at '/builders', but '/+builds' will permanently redirects to the new URL.
655
        'builders': IBuilderSet,
3691.436.8 by Mark Shuttleworth
Create overview of all mentoring activity
656
        '+code': IBazaarApplication,
4236.3.10 by Michael Hudson
do the z3 plumbing in what looks to me like a more sensible way
657
        '+code-imports': ICodeImportSet,
3691.436.8 by Mark Shuttleworth
Create overview of all mentoring activity
658
        'codeofconduct': ICodeOfConductSet,
10348.3.1 by Jonathan Davies
Put in place the foundations for exporting Country's over the API.
659
        '+countries': ICountrySet,
2622 by Canonical.com Patch Queue Manager
[trivial] remove all uses of browser:suburl and nuke the suburl directive code.
660
        'distros': IDistributionSet,
4974.3.1 by Abel Deuring
renamed the first URL component of the HWDB from hwdb to +hwdb
661
        '+hwdb': IHWDBApplication,
2622 by Canonical.com Patch Queue Manager
[trivial] remove all uses of browser:suburl and nuke the suburl directive code.
662
        'karmaaction': IKarmaActionSet,
4268.3.23 by Carlos Perello Marin
Ported old import queue page to use the new infrastructure
663
        '+imports': ITranslationImportQueue,
3691.436.57 by Mark Shuttleworth
Merge BETA
664
        '+languages': ILanguageSet,
11848.2.9 by Edwin Grubbs
Added tests. Changed +nameblacklists to +nameblacklist.
665
        '+nameblacklist': INameBlacklistSet,
8485.3.1 by Muharem Hrnjadovic
first stab at exposing package sets at the LP API
666
        'package-sets': IPackagesetSet,
3691.436.8 by Mark Shuttleworth
Create overview of all mentoring activity
667
        'people': IPersonSet,
6989.2.1 by Leonard Richardson
Initial attempt at implementation.
668
        'pillars': IPillarNameSet,
13240.2.3 by Brad Crittenden
Broken to hell
669
        '+processor-families': IProcessorFamilySet,
13387.1.10 by Francis J. Lacoste
URL to processor is now of the form /+processors/<name>
670
        '+processors': IProcessorSet,
4011.2.7 by Bjorn Tillenius
move /products to /projects
671
        'projects': IProductSet,
10326.1.1 by Henning Eggers
Mechanically renamed IProject* to IProjectGroup*.
672
        'projectgroups': IProjectGroupSet,
3691.436.8 by Mark Shuttleworth
Create overview of all mentoring activity
673
        'sourcepackagenames': ISourcePackageNameSet,
2622 by Canonical.com Patch Queue Manager
[trivial] remove all uses of browser:suburl and nuke the suburl directive code.
674
        'specs': ISpecificationSet,
675
        'sprints': ISprintSet,
3691.436.76 by Mark Shuttleworth
Make LP stats available to admins
676
        '+statistics': ILaunchpadStatisticSet,
3691.436.26 by Mark Shuttleworth
Make sure offers of mentoring are only displayed when appropriate.
677
        'token': ILoginTokenSet,
3691.8.96 by Carlos Perello Marin
Changed +translation-groups with +groups as agreed with mpt
678
        '+groups': ITranslationGroupSet,
3691.267.29 by Stuart Bishop
Fix more page tests
679
        'translations': IRosettaApplication,
10065.2.3 by Guilherme Salgado
Move the test openid views under a newly created testopenid vhost.
680
        'testopenid': ITestOpenIDApplication,
3859.4.3 by Francis J. Lacoste
Rename URLs of the Answer Tracker containging ticket or support-contact.
681
        'questions': IQuestionSet,
7675.560.10 by Graham Binns
Merged Gavin's changes.
682
        'temporary-blobs': ITemporaryStorageManager,
3691.267.7 by Stuart Bishop
URL changees
683
        # These three have been renamed, and no redirects done, as the old
684
        # urls now point to the product pages.
685
        #'bazaar': IBazaarApplication,
686
        #'malone': IMaloneApplication,
687
        #'rosetta': IRosettaApplication,
2622 by Canonical.com Patch Queue Manager
[trivial] remove all uses of browser:suburl and nuke the suburl directive code.
688
        }
689
4011.2.7 by Bjorn Tillenius
move /products to /projects
690
    @stepto('products')
691
    def products(self):
4011.2.10 by Bjorn Tillenius
fix all test failures related to /products
692
        return self.redirectSubTree(
693
            canonical_url(getUtility(IProductSet)), status=301)
4011.2.7 by Bjorn Tillenius
move /products to /projects
694
2622 by Canonical.com Patch Queue Manager
[trivial] remove all uses of browser:suburl and nuke the suburl directive code.
695
    def traverse(self, name):
696
        if name in self.stepto_utilities:
4823.6.7 by Barry Warsaw
Much simplification and improvement in the way private ports are done.
697
            return getUtility(self.stepto_utilities[name])
3691.267.1 by Stuart Bishop
Work in progress
698
13303.4.1 by mbp at canonical
launchpad.net/~ now redirects you to your user page
699
        if name == '~':
700
            person = getUtility(ILaunchBag).user
701
            if person is None:
702
                raise Unauthorized()
703
            # Keep the context and the subtree so that
704
            # bugs.l.n/~/+assignedbugs goes to the person's canonical
705
            # assigned list.
706
            return self.redirectSubTree(
707
                canonical_url(self.context) + "~"
708
                + canonical_name(person.name),
709
                status=302)
710
        elif name.startswith('~'):  # Allow traversal to ~foo for People
4934.3.24 by Elliot Murphy
Restore code from a bad merge from trunk.
711
            if canonical_name(name) != name:
13303.4.1 by mbp at canonical
launchpad.net/~ now redirects you to your user page
712
                # (for instance, uppercase username?)
4934.3.24 by Elliot Murphy
Restore code from a bad merge from trunk.
713
                if self.request.method == 'POST':
714
                    raise POSTToNonCanonicalURL
715
                return self.redirectSubTree(
716
                    canonical_url(self.context) + canonical_name(name),
717
                    status=301)
718
            else:
719
                person = getUtility(IPersonSet).getByName(name[1:])
10067.3.1 by Curtis Hovey
Raise a GoneError (HTTP 410) when traversing suspended user
720
                if person is None:
721
                    return person
7510.4.6 by Barry Warsaw
Respond to reviews. Move the check code into the LaunchpadRootNavigation
722
                # Check to see if this is a team, and if so, whether the
723
                # logged in user is allowed to view the team, by virtue of
724
                # team membership or Launchpad administration.
14494.4.2 by Curtis Hovey
Patched the private team traversal rules back into the tree because merge3 and diff3 suck.
725
                if (person.is_team and
726
                    not check_permission('launchpad.LimitedView', person)):
10067.3.1 by Curtis Hovey
Raise a GoneError (HTTP 410) when traversing suspended user
727
                    raise NotFound(self.context, name)
728
                # Only admins are permitted to see suspended users.
729
                if person.account_status == AccountStatus.SUSPENDED:
12552.1.1 by j.c.sackett
Changed the permission check.
730
                    if not check_permission('launchpad.Moderate', person):
10067.3.1 by Curtis Hovey
Raise a GoneError (HTTP 410) when traversing suspended user
731
                        raise GoneError(
732
                            'User is suspended: %s' % name)
733
                return person
3691.267.39 by Stuart Bishop
Trivial optimization to traverse()
734
3691.267.37 by Stuart Bishop
Bazaar needs old XML-RPC URL to remain functional
735
        # Dapper and Edgy shipped with https://launchpad.net/bazaar hard coded
736
        # into the Bazaar Launchpad plugin (part of Bazaar core). So in theory
737
        # we need to support this URL until 2011 (although I suspect the API
738
        # will break much sooner than that) or updates sent to
739
        # {dapper,edgy}-updates. Probably all irrelevant, as I suspect the
740
        # number of people using the plugin in edgy and dapper is 0.
3691.267.39 by Stuart Bishop
Trivial optimization to traverse()
741
        if name == 'bazaar' and IXMLRPCRequest.providedBy(self.request):
3691.267.37 by Stuart Bishop
Bazaar needs old XML-RPC URL to remain functional
742
            return getUtility(IBazaarApplication)
743
5088.1.1 by Christian Robottom Reis
Fix for bug #156271, Allow administrators to reactivate disabled projects.
744
        # account for common typing mistakes
745
        if canonical_name(name) != name:
746
            if self.request.method == 'POST':
747
                raise POSTToNonCanonicalURL
748
            return self.redirectSubTree(
9080.2.2 by Brad Crittenden
Formatting changes per review.
749
                (canonical_url(self.context, request=self.request) +
750
                 canonical_name(name)),
5088.1.1 by Christian Robottom Reis
Fix for bug #156271, Allow administrators to reactivate disabled projects.
751
                status=301)
752
753
        pillar = getUtility(IPillarNameSet).getByName(
6080.9.8 by Edwin Grubbs
Added IPillar interface and security
754
            name, ignore_inactive=False)
755
        if pillar is not None and check_permission('launchpad.View', pillar):
7332.1.1 by Guilherme Salgado
Make it possible to traverse to pillars using any of their aliases. Also rewrite I{Product,Distribution,Project}.getByName() using IPillarNameSet.getByName()
756
            if pillar.name != name:
757
                # This pillar was accessed through one of its aliases, so we
758
                # must redirect to its canonical URL.
12338.4.3 by j.c.sackett
Rewrapped a line.
759
                return self.redirectSubTree(
12338.4.6 by j.c.sackett
Updated use of canonical_url when finding the url of a pillar found by alias to pass in the request.
760
                    canonical_url(pillar, self.request), status=301)
6080.9.8 by Edwin Grubbs
Added IPillar interface and security
761
            return pillar
762
        return None
2622 by Canonical.com Patch Queue Manager
[trivial] remove all uses of browser:suburl and nuke the suburl directive code.
763
3691.412.16 by James Henstridge
resurrect redirection code that carlos reverted in carlos.perello@canonical.com-20070226121532-guyv8nodkvhs12nx
764
    def _getBetaRedirectionView(self):
5622.2.5 by Maris Fogels
Rework. Fixed some style issues, and clarified a comment string.
765
        # If the inhibit_beta_redirect cookie is set, don't redirect.
3691.412.16 by James Henstridge
resurrect redirection code that carlos reverted in carlos.perello@canonical.com-20070226121532-guyv8nodkvhs12nx
766
        if self.request.cookies.get('inhibit_beta_redirect', '0') == '1':
767
            return None
768
5622.2.5 by Maris Fogels
Rework. Fixed some style issues, and clarified a comment string.
769
        # If we are looking at the front page, don't redirect.
3691.412.16 by James Henstridge
resurrect redirection code that carlos reverted in carlos.perello@canonical.com-20070226121532-guyv8nodkvhs12nx
770
        if self.request['PATH_INFO'] == '/':
771
            return None
3973.1.29 by Steve Alexander
remove unused root Launchpad breadcrumb
772
5622.2.5 by Maris Fogels
Rework. Fixed some style issues, and clarified a comment string.
773
        # If this is a HTTP POST, we don't want to issue a redirect.
774
        # Doing so would go against the HTTP standard.
5622.2.2 by Maris Fogels
Implemented the fix for beta redirection on POST. Fixed the pagetest output to match the working test.
775
        if self.request.method == 'POST':
776
            return None
777
8920.1.2 by Francis J. Lacoste
Disable beta team redirection on the webservice.
778
        # If this is a web service request, don't redirect.
779
        if WebServiceLayer.providedBy(self.request):
780
            return None
781
8296.2.2 by Brad Crittenden
Untabified file.
782
        # If the request is for a bug then redirect straight to that bug.
783
        bug_match = re.match("/bugs/(\d+)$", self.request['PATH_INFO'])
784
        if bug_match:
785
            bug_number = bug_match.group(1)
786
            bug_set = getUtility(IBugSet)
787
            try:
788
                bug = bug_set.get(bug_number)
11929.9.1 by Tim Penhey
Move launchpadform into lp.app.browser.
789
            except NotFoundError:
8296.2.1 by Brad Crittenden
Optimize redirection for lp.net/bugs/xxx URLs.
790
                raise NotFound(self.context, bug_number)
8296.2.3 by Brad Crittenden
Don't leak bugtasks for private bugs and avoid blowing up when if the launchpad_beta_tester celebrity is missing.
791
            if not check_permission("launchpad.View", bug):
12599.4.2 by Leonard Richardson
Merge from trunk.
792
                return None
11815.1.2 by Robert Collins
Drop beta site redirect support.
793
            # Empty the traversal stack, since we're redirecting.
794
            self.request.setTraversalStack([])
795
            # And perform a temporary redirect.
11815.1.3 by Robert Collins
Tweak from thumper.
796
            return RedirectionView(canonical_url(bug.default_bugtask),
797
                self.request, status=303)
11815.1.2 by Robert Collins
Drop beta site redirect support.
798
        # Explicit catchall - do not redirect.
799
        return None
3691.412.16 by James Henstridge
resurrect redirection code that carlos reverted in carlos.perello@canonical.com-20070226121532-guyv8nodkvhs12nx
800
801
    def publishTraverse(self, request, name):
802
        beta_redirection_view = self._getBetaRedirectionView()
803
        if beta_redirection_view is not None:
804
            return beta_redirection_view
805
        return Navigation.publishTraverse(self, request, name)
806
2622 by Canonical.com Patch Queue Manager
[trivial] remove all uses of browser:suburl and nuke the suburl directive code.
807
3489.1.1 by Bjorn Tillenius
add test for soft timeouts and make sure they do get logged.
808
class SoftTimeoutView(LaunchpadView):
809
810
    def __call__(self):
811
        """Generate a soft timeout by sleeping enough time."""
3489.1.2 by Bjorn Tillenius
review commments.
812
        start_time = time.time()
3489.1.1 by Bjorn Tillenius
add test for soft timeouts and make sure they do get logged.
813
        celebrities = getUtility(ILaunchpadCelebrities)
814
        if (self.user is None or
815
            not self.user.inTeam(celebrities.launchpad_developers)):
816
            raise Unauthorized
817
818
        self.request.response.setHeader('content-type', 'text/plain')
5855.5.6 by Curtis Hovey
Revisions to remove the timeout keys from the launchpad config section and the
819
        soft_timeout = intOrZero(config.database.soft_request_timeout)
3489.1.1 by Bjorn Tillenius
add test for soft timeouts and make sure they do get logged.
820
        if soft_timeout == 0:
821
            return 'No soft timeout threshold is set.'
822
13130.1.6 by Curtis Hovey
Move ILaunchpadCelebrity to lp.app.
823
        time.sleep(soft_timeout / 1000.0)
3489.1.2 by Bjorn Tillenius
review commments.
824
        time_to_generate_page = (time.time() - start_time) * 1000
825
        # In case we didn't sleep enogh time, sleep a while longer to
826
        # pass the soft timeout threshold.
3489.1.1 by Bjorn Tillenius
add test for soft timeouts and make sure they do get logged.
827
        while time_to_generate_page < soft_timeout:
3489.1.2 by Bjorn Tillenius
review commments.
828
            time.sleep(0.1)
829
            time_to_generate_page = (time.time() - start_time) * 1000
3489.1.1 by Bjorn Tillenius
add test for soft timeouts and make sure they do get logged.
830
        return (
831
            'Soft timeout threshold is set to %s ms. This page took'
832
            ' %s ms to render.' % (soft_timeout, time_to_generate_page))
3691.270.4 by Guilherme Salgado
New page (at /+search) which lets people search across products, projects and distributions all at once
833
834
5369.1.1 by Mark Shuttleworth
Add PopCalXP in date and datetime picker configurations
835
class IcingFolder(ExportedFolder):
836
    """Export the Launchpad icing."""
3712 by Steve Alexander
add IcingFolder
837
7459.4.3 by Francis J. Lacoste
Export lazr-js under icing.
838
    export_subdirectories = True
839
5369.1.16 by Mark Shuttleworth
Changes based on review from JamesH
840
    folder = os.path.join(
13130.1.23 by Curtis Hovey
Fixed paths to directories.
841
        config.root, 'lib/canonical/launchpad/icing/')
3712 by Steve Alexander
add IcingFolder
842
3717 by Steve Alexander
rough draft of structural object presentation
843
6574.1.5 by Francis J. Lacoste
Replace zope resources by LaunchpadImageFolder.
844
class LaunchpadImageFolder(ExportedImageFolder):
845
    """Export the Launchpad images - supporting retrieval without extension.
846
    """
847
848
    folder = os.path.join(
13130.1.23 by Curtis Hovey
Fixed paths to directories.
849
        config.root, 'lib/canonical/launchpad/images/')
6574.1.5 by Francis J. Lacoste
Replace zope resources by LaunchpadImageFolder.
850
851
6753.5.1 by Diogo Matsubara
merge the twozero tour again
852
class LaunchpadTourFolder(ExportedFolder):
853
    """Export a launchpad tour folder.
854
855
    This exported folder supports traversing to subfolders.
856
    """
857
858
    folder = os.path.join(
859
        os.path.dirname(os.path.realpath(__file__)), '../tour/')
860
861
    export_subdirectories = True
862
863
    def publishTraverse(self, request, name):
864
        """Hide the source directory.
865
866
        The source directory contains source material that we don't want
867
        published over the web.
868
        """
869
        if name == 'source':
870
            raise NotFound(request, name)
871
        return super(LaunchpadTourFolder, self).publishTraverse(request, name)
872
873
    def browserDefault(self, request):
874
        """Redirect to index.html if the directory itself is requested."""
875
        if len(self.names) == 0:
876
            return RedirectionView(
6721.2.12 by Christian Reis
Drop the .html in tour pages, yuck
877
                "%s+tour/index" % canonical_url(self.context),
6753.5.1 by Diogo Matsubara
merge the twozero tour again
878
                self.request, status=302), ()
879
        else:
880
            return self, ()
881
882
6770.2.14 by Francis J. Lacoste
Server API documentation from +apidoc.
883
class LaunchpadAPIDocFolder(ExportedFolder):
884
    """Export the API documentation."""
885
886
    folder = os.path.join(
13130.1.23 by Curtis Hovey
Fixed paths to directories.
887
        config.root, 'lib/canonical/launchpad/apidoc/')
6770.2.14 by Francis J. Lacoste
Server API documentation from +apidoc.
888
889
    def browserDefault(self, request):
890
        """Traverse to index.html if the directory itself is requested."""
891
        if len(self.names) == 0:
892
            return self, ('index.html', )
893
        else:
894
            return self, ()
895
896
3847.2.40 by Mark Shuttleworth
Implement blueprint app home page search
897
class AppFrontPageSearchView(LaunchpadFormView):
898
899
    schema = IAppFrontPageSearchForm
900
    custom_widget('scope', ProjectScopeWidget)
901
902
    @property
903
    def scope_css_class(self):
904
        """The CSS class for used in the scope widget."""
905
        if self.scope_error:
906
            return 'error'
907
        else:
908
            return None
909
910
    @property
911
    def scope_error(self):
912
        """The error message for the scope widget."""
5243.1.1 by Jonathan Knowles
Renaming method getWidgetError (in LaunchpadFormView) to getFieldError.
913
        return self.getFieldError('scope')
3847.2.40 by Mark Shuttleworth
Implement blueprint app home page search
914
915
9474.2.1 by Gary Poster
convert three templates to 3.0; also correct some oddities in table layout in +graphics
916
class LaunchpadGraphics(LaunchpadView):
917
    label = page_title = 'Overview of Launchpad graphics and icons'
918
919
7841.5.5 by Curtis Hovey
Add a control to show or hide small maps. Uses a reimplentation of the portlet
920
def get_launchpad_views(cookies):
7841.5.15 by Curtis Hovey
Changes per review.
921
    """The state of optional page elements the user may choose to view.
7841.5.5 by Curtis Hovey
Add a control to show or hide small maps. Uses a reimplentation of the portlet
922
923
    :param cookies: The request.cookies object that contains launchpad_views.
924
    :return: A dict of all the view states.
925
    """
926
    views = {
927
        'small_maps': True,
928
        }
929
    cookie = cookies.get('launchpad_views', '')
930
    if len(cookie) > 0:
931
        pairs = cookie.split('&')
932
        for pair in pairs:
7841.5.15 by Curtis Hovey
Changes per review.
933
            parts = pair.split('=')
934
            if len(parts) != 2:
935
                # The cookie is malformed, possibly hacked.
936
                continue
937
            key, value = parts
7841.5.5 by Curtis Hovey
Add a control to show or hide small maps. Uses a reimplentation of the portlet
938
            if not key in views:
7841.5.15 by Curtis Hovey
Changes per review.
939
                # The cookie may be hacked.
7841.5.5 by Curtis Hovey
Add a control to show or hide small maps. Uses a reimplentation of the portlet
940
                continue
941
            # 'false' is the value that the browser script sets to disable a
942
            # part of a page. Any other value is considered to be 'true'.
943
            views[key] = value != 'false'
944
    return views
7675.117.1 by Francis J. Lacoste
+login is now a 404 on the SSO and Unauthorized are really 500
945
946
14382.1.5 by Aaron Bentley
view_name is included in BugTaskSearchListingView RequestCache.
947
def iter_view_registrations(cls):
14382.1.11 by Aaron Bentley
Fix text.
948
    """Iterate through the AdapterRegistrations of a view.
14382.1.5 by Aaron Bentley
view_name is included in BugTaskSearchListingView RequestCache.
949
14382.1.7 by Aaron Bentley
Simplify iter_view_registrations, fix tests.
950
    The input must be the final registered form of the class, which is
951
    typically a SimpleViewClass variant.
14382.1.5 by Aaron Bentley
view_name is included in BugTaskSearchListingView RequestCache.
952
    """
953
    for registration in getGlobalSiteManager().registeredAdapters():
14382.1.6 by Aaron Bentley
Actually use view_name in ListingNavigator.
954
        if registration.factory == cls:
14382.1.7 by Aaron Bentley
Simplify iter_view_registrations, fix tests.
955
            yield registration
14382.1.5 by Aaron Bentley
view_name is included in BugTaskSearchListingView RequestCache.
956
957
7675.117.1 by Francis J. Lacoste
+login is now a 404 on the SSO and Unauthorized are really 500
958
class DoesNotExistView:
7675.117.9 by Francis J. Lacoste
Typo.
959
    """A view that simply raises NotFound when rendered.
7675.117.1 by Francis J. Lacoste
+login is now a 404 on the SSO and Unauthorized are really 500
960
961
    Useful to register as a view that shouldn't appear on a particular
962
    virtual host.
963
    """
964
    implements(IBrowserPublisher)
965
966
    def __init__(self, context, request):
967
        self.context = context
968
969
    def publishTraverse(self, request, name):
970
        """See `IBrowserPublisher`."""
971
        return self
972
973
    def browserDefault(self, request):
974
        """See `IBrowserPublisher`."""
975
        return self, ()
976
977
    def __call__(self):
978
        raise NotFound(self.context, self.__name__)