~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.17 by Karl Fogel
Add the copyright header block to the rest of the files under lib/lp/.
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
4
"""View classes for `IProductSeries`."""
2068 by Canonical.com Patch Queue Manager
[r=SteveA/trivial] import fascism, kill translationeffort, kill sshkey browser code, add __all__s to interfaces, other cleanings-up
5
6
__metaclass__ = type
7
5481.2.6 by Tim Penhey
Fixes following review
8
__all__ = [
9
    'get_series_branch_error',
9209.4.5 by Guilherme Salgado
Update all existing breadcrumb adapters to use Breadcrumb rather than BreadcrumbBuilder. Also rename them all
10
    'ProductSeriesBreadcrumb',
5481.2.6 by Tim Penhey
Fixes following review
11
    'ProductSeriesBugsMenu',
8255.11.2 by Curtis Hovey
Product series owners and drivers can delete mis-created series.
12
    'ProductSeriesDeleteView',
11320.1.1 by Edwin Grubbs
Converted productseries detailed-display from a macro to its own template.
13
    'ProductSeriesDetailedDisplayView',
5481.2.6 by Tim Penhey
Fixes following review
14
    'ProductSeriesEditView',
15
    'ProductSeriesFacets',
16
    'ProductSeriesFileBugRedirect',
7675.662.1 by Brad Crittenden
Correct ProductInvolvedMenu to reference the product not the product series.
17
    'ProductSeriesInvolvedMenu',
10648.1.7 by Curtis Hovey
Removed the provideAdapter call from pillar and moved the Product and ProductSeries
18
    'ProductSeriesInvolvementView',
5481.2.6 by Tim Penhey
Fixes following review
19
    'ProductSeriesLinkBranchView',
20
    'ProductSeriesLinkBranchFromCodeView',
21
    'ProductSeriesNavigation',
22
    'ProductSeriesOverviewMenu',
7709.3.2 by Curtis Hovey
Deleting a release now deletes the files attached to a release too. Updated the
23
    'ProductSeriesOverviewNavigationMenu',
5481.2.6 by Tim Penhey
Fixes following review
24
    'ProductSeriesRdfView',
25
    'ProductSeriesReviewView',
10456.2.1 by Brad Crittenden
checkpoint
26
    'ProductSeriesSetBranchView',
5481.2.6 by Tim Penhey
Fixes following review
27
    'ProductSeriesSpecificationsMenu',
9081.2.8 by Curtis Hovey
Updated the productseries ubuntu packaging page to UI 3.0.
28
    'ProductSeriesUbuntuPackagingView',
5481.2.6 by Tim Penhey
Fixes following review
29
    'ProductSeriesView',
30
    ]
615 by Canonical.com Patch Queue Manager
productseries now core-complete for add, view, edit
31
3691.125.12 by James Henstridge
check the (series,branch) constraint when updating a series branch
32
import cgi
8805.7.3 by Edwin Grubbs
Optimized specification_status_counts.
33
from operator import attrgetter
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
34
35
from bzrlib.revision import NULL_REVISION
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
36
from lazr.restful.interface import (
37
    copy_field,
38
    use_template,
39
    )
40
from z3c.ptcompat import ViewPageTemplateFile
41
from zope.app.form.browser import (
42
    TextAreaWidget,
43
    TextWidget,
44
    )
1648 by Canonical.com Patch Queue Manager
land marks NukeSourceSource patch
45
from zope.component import getUtility
10023.3.4 by Curtis Hovey
Revise the Ubuntu packaging view to allow the user to select any ubuntu series.
46
from zope.formlib import form
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
47
from zope.interface import (
48
    implements,
49
    Interface,
50
    )
10023.3.4 by Curtis Hovey
Revise the Ubuntu packaging view to allow the user to select any ubuntu series.
51
from zope.schema import Choice
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
52
from zope.schema.vocabulary import (
53
    SimpleTerm,
54
    SimpleVocabulary,
55
    )
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
56
14600.1.12 by Curtis Hovey
Move i18n to lp.
57
from lp import _
14593.2.14 by Curtis Hovey
Extracted language helpers to lp.services.worlddata.helpers.
58
from lp.services.worlddata.helpers import browser_languages
14600.2.2 by Curtis Hovey
Moved webapp to lp.services.
59
from lp.services.webapp import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
60
    ApplicationMenu,
61
    canonical_url,
62
    enabled_with_permission,
63
    LaunchpadView,
64
    Link,
65
    Navigation,
66
    NavigationMenu,
67
    StandardLaunchpadFacets,
68
    stepthrough,
69
    stepto,
70
    )
14600.2.2 by Curtis Hovey
Moved webapp to lp.services.
71
from lp.services.webapp.authorization import check_permission
72
from lp.services.webapp.batching import BatchNavigator
73
from lp.services.webapp.breadcrumb import Breadcrumb
74
from lp.services.webapp.menu import structured
11929.9.1 by Tim Penhey
Move launchpadform into lp.app.browser.
75
from lp.app.browser.launchpadform import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
76
    action,
77
    custom_widget,
78
    LaunchpadEditFormView,
79
    LaunchpadFormView,
11929.11.14 by Tim Penhey
Move the method to render a part of the widget into the launchpadform module.
80
    render_radio_widget_part,
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
81
    ReturnToReferrerMixin,
82
    )
11626.3.2 by Curtis Hovey
Move tales gto lp.app.
83
from lp.app.browser.tales import MenuAPI
11452.1.6 by j.c.sackett
Converted productseries views and updated tests.
84
from lp.app.enums import ServiceUsage
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
85
from lp.app.errors import (
86
    NotFoundError,
87
    UnexpectedFormData,
88
    )
13130.1.12 by Curtis Hovey
Sorted imports.
89
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
12293.1.10 by Curtis Hovey
Formatted imports.
90
from lp.app.widgets.itemswidgets import LaunchpadRadioWidget
91
from lp.app.widgets.textwidgets import StrippedTextWidget
9559.1.2 by Henning Eggers
Converted hasspecifications-specs.pt to 3.0 layout. Refactored all specification menus into a Mixin. Fixed specfications vhost breadcrumb. Fixed some labels and page_titles.
92
from lp.blueprints.browser.specificationtarget import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
93
    HasSpecificationsMenuMixin,
94
    )
11824.1.5 by Tim Penhey
Fix imports for SpecificationImplementationStatus.
95
from lp.blueprints.enums import SpecificationImplementationStatus
96
from lp.blueprints.interfaces.specification import ISpecificationSet
8523.3.1 by Gavin Panella
Bugs tree reorg after automated migration.
97
from lp.bugs.browser.bugtask import BugTargetTraversalMixin
13130.1.12 by Curtis Hovey
Sorted imports.
98
from lp.bugs.browser.structuralsubscription import (
99
    expose_structural_subscription_data_to_js,
100
    StructuralSubscriptionMenuMixin,
101
    StructuralSubscriptionTargetTraversalMixin,
102
    )
14157.3.1 by Jeroen Vermeulen
Lint.
103
from lp.bugs.interfaces.bugtask import IBugTaskSet
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
104
from lp.code.browser.branch import BranchNameValidationMixin
10456.2.1 by Brad Crittenden
checkpoint
105
from lp.code.browser.branchref import BranchRef
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
106
from lp.code.enums import (
107
    BranchType,
108
    RevisionControlSystems,
109
    )
110
from lp.code.errors import (
111
    BranchCreationForbidden,
112
    BranchExists,
113
    )
11270.2.3 by Tim Penhey
Fix more imports.
114
from lp.code.interfaces.branch import IBranch
8777.6.2 by Danilo Šegan
Move productseries translation views out of registry and into translations.
115
from lp.code.interfaces.branchjob import IRosettaUploadJobSource
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
116
from lp.code.interfaces.branchtarget import IBranchTarget
10456.2.34 by Brad Crittenden
Fix lint and merge issues.
117
from lp.code.interfaces.codeimport import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
118
    ICodeImport,
119
    ICodeImportSet,
120
    )
121
from lp.registry.browser import (
12688.1.5 by Brad Crittenden
Fixed issues from review. Created add_subscribe_link.
122
    add_subscribe_link,
11594.3.3 by Curtis Hovey
Removed duplicate an unused code.
123
    BaseRdfView,
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
124
    MilestoneOverlayMixin,
125
    RegistryDeleteViewMixin,
126
    StatusCount,
127
    )
128
from lp.registry.browser.pillar import (
129
    InvolvedMenu,
130
    PillarView,
131
    )
10193.2.1 by Edwin Grubbs
Removed PackagingAddView.
132
from lp.registry.interfaces.packaging import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
133
    IPackaging,
134
    IPackagingUtil,
135
    )
136
from lp.registry.interfaces.productseries import IProductSeries
137
from lp.registry.interfaces.series import SeriesStatus
138
from lp.services.fields import URIField
11382.6.34 by Gavin Panella
Reformat imports in all files touched so far.
139
from lp.services.propertycache import cachedproperty
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
140
from lp.services.worlddata.interfaces.country import ICountry
141
from lp.services.worlddata.interfaces.language import ILanguageSet
8751.1.1 by Danilo Šegan
Store migration changes so far.
142
from lp.translations.interfaces.potemplate import IPOTemplateSet
143
from lp.translations.interfaces.productserieslanguage import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
144
    IProductSeriesLanguageSet,
145
    )
10456.2.1 by Brad Crittenden
checkpoint
146
9963.2.1 by Curtis Hovey
Do not allow users to delete series that are linked to packages.
147
3691.154.7 by David Allouche
improve error message for cvs/svn details in +source
148
def quote(text):
9963.2.4 by Curtis Hovey
Changes per review.
149
    """Escape and quote text."""
3691.154.7 by David Allouche
improve error message for cvs/svn details in +source
150
    return cgi.escape(text, quote=True)
151
152
9206.2.1 by William Grant
Add a URL and traversal mixin for IStructuralSubscription. Mix it into all targets.
153
class ProductSeriesNavigation(Navigation, BugTargetTraversalMixin,
9206.1.5 by William Grant
Style fixes.
154
    StructuralSubscriptionTargetTraversalMixin):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
155
    """A class to navigate `IProductSeries` URLs."""
2630 by Canonical.com Patch Queue Manager
[trivial] lots of tidying up. converting all database classes to use NotFoundError consistently, and to import it from launchpad.interfaces in preparation for the move to a new zope3. Also, introduced a NameNotAvailable error. removed browser:traverse rdirective. commented out shipit test that fails sometimes.
156
    usedfor = IProductSeries
157
3691.160.1 by James Henstridge
Add support for Bazaar branch references to branch, product and product series pages
158
    @stepto('.bzr')
159
    def dotbzr(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
160
        """Return the series branch."""
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
161
        if self.context.branch:
162
            return BranchRef(self.context.branch)
3691.160.1 by James Henstridge
Add support for Bazaar branch references to branch, product and product series pages
163
        else:
164
            return None
165
2630 by Canonical.com Patch Queue Manager
[trivial] lots of tidying up. converting all database classes to use NotFoundError consistently, and to import it from launchpad.interfaces in preparation for the move to a new zope3. Also, introduced a NameNotAvailable error. removed browser:traverse rdirective. commented out shipit test that fails sometimes.
166
    @stepto('+pots')
167
    def pots(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
168
        """Return the series templates."""
2630 by Canonical.com Patch Queue Manager
[trivial] lots of tidying up. converting all database classes to use NotFoundError consistently, and to import it from launchpad.interfaces in preparation for the move to a new zope3. Also, introduced a NameNotAvailable error. removed browser:traverse rdirective. commented out shipit test that fails sometimes.
169
        potemplateset = getUtility(IPOTemplateSet)
170
        return potemplateset.getSubset(productseries=self.context)
171
8485.7.1 by Danilo Šegan
Merge with productserieslanguage page implementation.
172
    @stepthrough('+lang')
173
    def traverse_lang(self, langcode):
174
        """Retrieve the ProductSeriesLanguage or a dummy if it is None."""
8485.7.10 by Danilo Šegan
A bunch of fixes after review from jtv.
175
        # We do not want users to see the 'en' pofile because
8485.7.1 by Danilo Šegan
Merge with productserieslanguage page implementation.
176
        # we store the messages we want to translate as English.
177
        if langcode == 'en':
178
            raise NotFoundError(langcode)
179
180
        langset = getUtility(ILanguageSet)
181
        try:
182
            lang = langset[langcode]
183
        except IndexError:
184
            # Unknown language code.
185
            raise NotFoundError
186
        psl_set = getUtility(IProductSeriesLanguageSet)
8485.7.10 by Danilo Šegan
A bunch of fixes after review from jtv.
187
        psl = psl_set.getProductSeriesLanguage(self.context, lang)
8485.7.1 by Danilo Šegan
Merge with productserieslanguage page implementation.
188
189
        return psl
190
2630 by Canonical.com Patch Queue Manager
[trivial] lots of tidying up. converting all database classes to use NotFoundError consistently, and to import it from launchpad.interfaces in preparation for the move to a new zope3. Also, introduced a NameNotAvailable error. removed browser:traverse rdirective. commented out shipit test that fails sometimes.
191
    def traverse(self, name):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
192
        """See `INavigation`."""
13045.8.4 by Curtis Hovey
Reverted unneeded change because the change needed to be lower.
193
        return self.context.getRelease(name)
2630 by Canonical.com Patch Queue Manager
[trivial] lots of tidying up. converting all database classes to use NotFoundError consistently, and to import it from launchpad.interfaces in preparation for the move to a new zope3. Also, introduced a NameNotAvailable error. removed browser:traverse rdirective. commented out shipit test that fails sometimes.
194
195
9209.4.5 by Guilherme Salgado
Update all existing breadcrumb adapters to use Breadcrumb rather than BreadcrumbBuilder. Also rename them all
196
class ProductSeriesBreadcrumb(Breadcrumb):
6767.6.26 by Maris Fogels
Rework based on reviewer feedback.
197
    """Builds a breadcrumb for an `IProductSeries`."""
9963.2.1 by Curtis Hovey
Do not allow users to delete series that are linked to packages.
198
6767.6.6 by Maris Fogels
Migrated the ProductSeries breadcrumb to the new system interface.
199
    @property
200
    def text(self):
9209.4.5 by Guilherme Salgado
Update all existing breadcrumb adapters to use Breadcrumb rather than BreadcrumbBuilder. Also rename them all
201
        """See `IBreadcrumb`."""
6767.6.6 by Maris Fogels
Migrated the ProductSeries breadcrumb to the new system interface.
202
        return 'Series ' + self.context.name
203
204
3203.1.3 by Mark Shuttleworth
Substantial cleanup of spec listings and spec targeting
205
class ProductSeriesFacets(StandardLaunchpadFacets):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
206
    """A class that provides the series facets."""
3203.1.3 by Mark Shuttleworth
Substantial cleanup of spec listings and spec targeting
207
    usedfor = IProductSeries
5481.2.6 by Tim Penhey
Fixes following review
208
    enable_only = [
209
        'overview', 'branches', 'bugs', 'specifications', 'translations']
210
211
    def branches(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
212
        """Return a link to view the branches related to this series."""
5481.2.6 by Tim Penhey
Fixes following review
213
        # Override to go to the branches for the product.
10842.1.1 by Paul Hummer
Changed 'Branches' to 'Code' and prepping to run tests
214
        text = 'Code'
5481.2.6 by Tim Penhey
Fixes following review
215
        summary = 'View related branches of code'
216
        link = canonical_url(self.context.product, rootsite='code')
217
        return Link(link, text, summary=summary)
3203.1.3 by Mark Shuttleworth
Substantial cleanup of spec listings and spec targeting
218
219
10648.1.4 by Curtis Hovey
Commit was appears to be sane code. Navigation links are broken and this looks like
220
class IProductSeriesInvolved(Interface):
221
    """A marker interface for getting involved."""
222
223
224
class ProductSeriesInvolvedMenu(InvolvedMenu):
225
    """The get involved menu."""
226
    usedfor = IProductSeriesInvolved
227
    links = [
228
        'report_bug', 'help_translate', 'submit_code', 'register_blueprint']
229
7675.662.1 by Brad Crittenden
Correct ProductInvolvedMenu to reference the product not the product series.
230
    @property
231
    def view(self):
232
        return self.context
233
234
    @property
235
    def pillar(self):
236
        return self.view.context.product
237
10648.1.4 by Curtis Hovey
Commit was appears to be sane code. Navigation links are broken and this looks like
238
    def submit_code(self):
10648.1.5 by Curtis Hovey
Switch to ZCML adapter registration to avoid multiple registrations from python
239
        target = canonical_url(
7675.662.1 by Brad Crittenden
Correct ProductInvolvedMenu to reference the product not the product series.
240
            self.pillar, view_name='+addbranch', rootsite='code')
11452.1.6 by j.c.sackett
Converted productseries views and updated tests.
241
        enabled = self.view.codehosting_usage == ServiceUsage.LAUNCHPAD
10648.1.4 by Curtis Hovey
Commit was appears to be sane code. Navigation links are broken and this looks like
242
        return Link(
10648.1.5 by Curtis Hovey
Switch to ZCML adapter registration to avoid multiple registrations from python
243
            target, 'Submit code', icon='code', enabled=enabled)
10648.1.4 by Curtis Hovey
Commit was appears to be sane code. Navigation links are broken and this looks like
244
245
10648.1.7 by Curtis Hovey
Removed the provideAdapter call from pillar and moved the Product and ProductSeries
246
class ProductSeriesInvolvementView(PillarView):
247
    """Encourage configuration of involvement links for project series."""
248
10648.1.4 by Curtis Hovey
Commit was appears to be sane code. Navigation links are broken and this looks like
249
    implements(IProductSeriesInvolved)
10648.1.7 by Curtis Hovey
Removed the provideAdapter call from pillar and moved the Product and ProductSeries
250
    has_involvement = True
251
    visible_disabled_link_names = ['submit_code']
252
253
    def __init__(self, context, request):
254
        super(ProductSeriesInvolvementView, self).__init__(context, request)
11483.3.2 by j.c.sackett
Update callsites for official_answers
255
        self.answers_usage = ServiceUsage.NOT_APPLICABLE
11452.1.6 by j.c.sackett
Converted productseries views and updated tests.
256
        if self.context.branch is not None:
257
            self.codehosting_usage = ServiceUsage.LAUNCHPAD
258
        else:
259
            self.codehosting_usage = ServiceUsage.UNKNOWN
10648.1.7 by Curtis Hovey
Removed the provideAdapter call from pillar and moved the Product and ProductSeries
260
261
    @property
262
    def configuration_links(self):
263
        """The enabled involvement links."""
264
        series_menu = MenuAPI(self.context).overview
265
        set_branch = series_menu['set_branch']
266
        set_branch.text = 'Configure series branch'
11452.1.6 by j.c.sackett
Converted productseries views and updated tests.
267
        if self.codehosting_usage == ServiceUsage.LAUNCHPAD:
268
            configured = True
269
        else:
270
            configured = False
11452.1.3 by j.c.sackett
Reverted some changes and added some comments.
271
        return [dict(link=set_branch,
11452.1.6 by j.c.sackett
Converted productseries views and updated tests.
272
                     configured=configured)]
11452.1.3 by j.c.sackett
Reverted some changes and added some comments.
273
10648.1.4 by Curtis Hovey
Commit was appears to be sane code. Navigation links are broken and this looks like
274
9660.4.1 by Tom Berger
Use a different icon and text for the strucutal subscription menu link depensing on whether the user is already subscribed.
275
class ProductSeriesOverviewMenu(
276
    ApplicationMenu, StructuralSubscriptionMenuMixin):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
277
    """The overview menu."""
3203.1.3 by Mark Shuttleworth
Substantial cleanup of spec listings and spec targeting
278
    usedfor = IProductSeries
279
    facet = 'overview'
12393.30.9 by Brad Crittenden
Support for product series. Some tests failing for unknown reasons.
280
281
    @cachedproperty
282
    def links(self):
283
        links = [
284
            'configure_bugtracker',
285
            'create_milestone',
286
            'create_release',
287
            'delete',
288
            'driver',
289
            'edit',
290
            'link_branch',
291
            'rdf',
292
            'set_branch',
293
            ]
12688.1.5 by Brad Crittenden
Fixed issues from review. Created add_subscribe_link.
294
        add_subscribe_link(links)
12393.30.9 by Brad Crittenden
Support for product series. Some tests failing for unknown reasons.
295
        links.append('ubuntupkg')
296
        return links
2548 by Canonical.com Patch Queue Manager
r=bjornt, plus some [trivial], lots of menus work, special error page for request timeouts.
297
3691.169.5 by James Henstridge
Allow entering Bazaar branch details on +source form, add sourceadmin buttons
298
    @enabled_with_permission('launchpad.Edit')
11462.2.12 by Edwin Grubbs
Made changes for UI reviews.
299
    def configure_bugtracker(self):
300
        text = 'Configure bug tracker'
301
        summary = 'Specify where bugs are tracked for this project'
302
        return Link(
11462.2.13 by Edwin Grubbs
Fixed tests.
303
            canonical_url(self.context.product,
304
                          view_name='+configure-bugtracker'),
11462.2.12 by Edwin Grubbs
Made changes for UI reviews.
305
            text, summary, icon='edit')
306
307
    @enabled_with_permission('launchpad.Edit')
2548 by Canonical.com Patch Queue Manager
r=bjornt, plus some [trivial], lots of menus work, special error page for request timeouts.
308
    def edit(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
309
        """Return a link to edit this series."""
3847.2.15 by Mark Shuttleworth
Consistency in menu text and capitalisation
310
        text = 'Change details'
8445.2.1 by Curtis Hovey
First draft of the series page.
311
        summary = 'Edit this series'
312
        return Link('+edit', text, summary, icon='edit')
2548 by Canonical.com Patch Queue Manager
r=bjornt, plus some [trivial], lots of menus work, special error page for request timeouts.
313
3691.169.5 by James Henstridge
Allow entering Bazaar branch details on +source form, add sourceadmin buttons
314
    @enabled_with_permission('launchpad.Edit')
8255.11.2 by Curtis Hovey
Product series owners and drivers can delete mis-created series.
315
    def delete(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
316
        """Return a link to delete this series."""
8255.11.2 by Curtis Hovey
Product series owners and drivers can delete mis-created series.
317
        text = 'Delete series'
318
        summary = "Delete this series and all it's dependent items."
319
        return Link('+delete', text, summary, icon='trash-icon')
320
321
    @enabled_with_permission('launchpad.Edit')
3348.1.21 by Mark Shuttleworth
Enable setting and proper use of the "drivers" details.
322
    def driver(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
323
        """Return a link to set the release manager."""
8566.2.1 by Curtis Hovey
Use the term 'Release manager' to distinguish the series driver from the project drivers.
324
        text = 'Appoint release manager'
3348.1.21 by Mark Shuttleworth
Enable setting and proper use of the "drivers" details.
325
        summary = 'Someone with permission to set goals this series'
326
        return Link('+driver', text, summary, icon='edit')
327
5481.2.2 by Tim Penhey
Added a new page just to set the bazaar branch.
328
    @enabled_with_permission('launchpad.Edit')
329
    def link_branch(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
330
        """Return a link to set the bazaar branch for this series."""
8445.2.9 by Curtis Hovey
series +linkbranch link text changes Link to branch => Change branch when
331
        if self.context.branch is None:
332
            text = 'Link to branch'
8615.11.3 by Curtis Hovey
Change branch now uses an edit icon.
333
            icon = 'add'
9691.3.5 by Tim Penhey
New story for setting the development focus branch.
334
            summary = 'Set the branch for this series'
8445.2.9 by Curtis Hovey
series +linkbranch link text changes Link to branch => Change branch when
335
        else:
336
            text = "Change branch"
8615.11.3 by Curtis Hovey
Change branch now uses an edit icon.
337
            icon = 'edit'
9691.3.5 by Tim Penhey
New story for setting the development focus branch.
338
            summary = 'Change the branch for this series'
8615.11.3 by Curtis Hovey
Change branch now uses an edit icon.
339
        return Link('+linkbranch', text, summary, icon=icon)
5481.2.2 by Tim Penhey
Added a new page just to set the bazaar branch.
340
10469.1.21 by Edwin Grubbs
Make involvement portlet use +setbranch instead of +linkbranch.
341
    @enabled_with_permission('launchpad.Edit')
342
    def set_branch(self):
343
        """Return a link to set the bazaar branch for this series."""
344
        # Once +setbranch has been beta tested thoroughly, it should
345
        # replace the +linkbranch page.
346
        if self.context.branch is None:
347
            text = 'Link to branch'
348
            icon = 'add'
349
            summary = 'Set the branch for this series'
350
        else:
351
            text = "Change branch"
352
            icon = 'edit'
353
            summary = 'Change the branch for this series'
354
        return Link('+setbranch', text, summary, icon=icon)
355
10248.2.5 by Jamal Fanaian
Fixed security problem in test xx-product-package-pages with ubuntupkg thanks to help from bac
356
    @enabled_with_permission('launchpad.AnyPerson')
2548 by Canonical.com Patch Queue Manager
r=bjornt, plus some [trivial], lots of menus work, special error page for request timeouts.
357
    def ubuntupkg(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
358
        """Return a link to link this series to an ubuntu sourcepackage."""
3847.2.15 by Mark Shuttleworth
Consistency in menu text and capitalisation
359
        text = 'Link to Ubuntu package'
8445.2.1 by Curtis Hovey
First draft of the series page.
360
        return Link('+ubuntupkg', text, icon='add')
2548 by Canonical.com Patch Queue Manager
r=bjornt, plus some [trivial], lots of menus work, special error page for request timeouts.
361
3472.2.1 by Mark Shuttleworth
Hang milestones mostly off series.
362
    @enabled_with_permission('launchpad.Edit')
7675.118.1 by Edwin Grubbs
Working form for creating a product release off of the series.
363
    def create_milestone(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
364
        """Return a link to create a milestone."""
7675.118.1 by Edwin Grubbs
Working form for creating a product release off of the series.
365
        text = 'Create milestone'
3472.2.1 by Mark Shuttleworth
Hang milestones mostly off series.
366
        summary = 'Register a new milestone for this series'
367
        return Link('+addmilestone', text, summary, icon='add')
368
7675.118.1 by Edwin Grubbs
Working form for creating a product release off of the series.
369
    @enabled_with_permission('launchpad.Edit')
370
    def create_release(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
371
        """Return a link to create a release."""
7675.118.1 by Edwin Grubbs
Working form for creating a product release off of the series.
372
        text = 'Create release'
373
        return Link('+addrelease', text, icon='add')
374
3348.1.25 by Mark Shuttleworth
Test fixes and productseries page cleanup.
375
    def rdf(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
376
        """Return a link to download the series RDF data."""
3847.2.15 by Mark Shuttleworth
Consistency in menu text and capitalisation
377
        text = 'Download RDF metadata'
2548 by Canonical.com Patch Queue Manager
r=bjornt, plus some [trivial], lots of menus work, special error page for request timeouts.
378
        return Link('+rdf', text, icon='download')
379
9660.4.1 by Tom Berger
Use a different icon and text for the strucutal subscription menu link depensing on whether the user is already subscribed.
380
381
class ProductSeriesBugsMenu(ApplicationMenu, StructuralSubscriptionMenuMixin):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
382
    """The bugs menu."""
4310.1.21 by Bjorn Tillenius
add a +nominations page for a product series.
383
    usedfor = IProductSeries
384
    facet = 'bugs'
12393.30.9 by Brad Crittenden
Support for product series. Some tests failing for unknown reasons.
385
386
    @cachedproperty
387
    def links(self):
388
        links = ['new', 'nominations']
12688.1.5 by Brad Crittenden
Fixed issues from review. Created add_subscribe_link.
389
        add_subscribe_link(links)
12393.30.9 by Brad Crittenden
Support for product series. Some tests failing for unknown reasons.
390
        return links
4310.1.21 by Bjorn Tillenius
add a +nominations page for a product series.
391
392
    def new(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
393
        """Return a link to report a bug in this series."""
4310.1.21 by Bjorn Tillenius
add a +nominations page for a product series.
394
        return Link('+filebug', 'Report a bug', icon='add')
395
396
    def nominations(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
397
        """Return a link to review bugs nominated for this series."""
4310.1.21 by Bjorn Tillenius
add a +nominations page for a product series.
398
        return Link('+nominations', 'Review nominations', icon='bug')
399
400
9559.1.2 by Henning Eggers
Converted hasspecifications-specs.pt to 3.0 layout. Refactored all specification menus into a Mixin. Fixed specfications vhost breadcrumb. Fixed some labels and page_titles.
401
class ProductSeriesSpecificationsMenu(NavigationMenu,
402
                                      HasSpecificationsMenuMixin):
3203.1.17 by Steve Alexander
Various improvements and cleanups from code review.
403
    """Specs menu for ProductSeries.
404
405
    This menu needs to keep track of whether we are showing all the
3203.1.6 by Mark Shuttleworth
Improve the Specifications menu for ProductSeries and DistroRelease
406
    specs, or just those that are approved/declined/proposed. It should
407
    allow you to change the set your are showing while keeping the basic
408
    view intact.
409
    """
3203.1.3 by Mark Shuttleworth
Substantial cleanup of spec listings and spec targeting
410
411
    usedfor = IProductSeries
412
    facet = 'specifications'
9838.2.19 by Curtis Hovey
Added register a meeting link to hasspecifications pillars and series.
413
    links = [
414
        'listall', 'assignments', 'setgoals', 'listdeclined',
415
        'new', 'register_sprint']
4621.2.63 by Jonathan Knowles
Adds the textual "Register a blueprint" link to blueprint listing pages for product series.
416
3203.1.3 by Mark Shuttleworth
Substantial cleanup of spec listings and spec targeting
417
7709.3.2 by Curtis Hovey
Deleting a release now deletes the files attached to a release too. Updated the
418
class ProductSeriesOverviewNavigationMenu(NavigationMenu):
419
    """Overview navigation menus for `IProductSeries` objects."""
420
    # Suppress the ProductOverviewNavigationMenu from showing on series,
421
    # release, and milestone pages.
422
    usedfor = IProductSeries
423
    facet = 'overview'
424
    links = ()
425
426
3691.125.14 by James Henstridge
fixes suggested by Bjorn in review
427
def get_series_branch_error(product, branch):
428
    """Check if the given branch is suitable for the given product.
3691.125.12 by James Henstridge
check the (series,branch) constraint when updating a series branch
429
430
    Returns an HTML error message on error, and None otherwise.
431
    """
432
    if branch.product != product:
5653.2.6 by Maris Fogels
Fixed a number of broken pagetests.
433
        return structured(
434
            '<a href="%s">%s</a> is not a branch of <a href="%s">%s</a>.',
435
            canonical_url(branch),
436
            branch.unique_name,
437
            canonical_url(product),
438
            product.displayname)
3691.125.12 by James Henstridge
check the (series,branch) constraint when updating a series branch
439
    return None
440
2344 by Canonical.com Patch Queue Manager
[not r=kiko] specification tracker
441
8777.6.2 by Danilo Šegan
Move productseries translation views out of registry and into translations.
442
class ProductSeriesView(LaunchpadView, MilestoneOverlayMixin):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
443
    """A view to show a series with translations."""
9731.2.1 by Curtis Hovey
Extracted +ubuntupkg code to its own view.
444
12393.30.9 by Brad Crittenden
Support for product series. Some tests failing for unknown reasons.
445
    def initialize(self):
12393.30.17 by Brad Crittenden
Merge from yellow + use expose_structural_subscription_data_to_js instead of new JSMixin
446
        super(ProductSeriesView, self).initialize()
12393.30.9 by Brad Crittenden
Support for product series. Some tests failing for unknown reasons.
447
        expose_structural_subscription_data_to_js(
448
            self.context, self.request, self.user)
449
9731.2.1 by Curtis Hovey
Extracted +ubuntupkg code to its own view.
450
    @property
451
    def page_title(self):
452
        """Return the HTML page title."""
453
        return self.context.title
454
455
    def requestCountry(self):
456
        """The country associated with the IP of the request."""
457
        return ICountry(self.request, None)
458
459
    def browserLanguages(self):
460
        """The languages the user's browser requested."""
14593.2.14 by Curtis Hovey
Extracted language helpers to lp.services.worlddata.helpers.
461
        return browser_languages(self.request)
9731.2.1 by Curtis Hovey
Extracted +ubuntupkg code to its own view.
462
463
    @property
464
    def request_import_link(self):
465
        """A link to the page for requesting a new code import."""
11318.8.19 by Tim Penhey
Being explicit about code.
466
        return canonical_url(
467
            self.context.product, view_name='+new-import', rootsite='code')
9731.2.1 by Curtis Hovey
Extracted +ubuntupkg code to its own view.
468
469
    @property
470
    def user_branch_visible(self):
471
        """Can the logged in user see the user branch."""
472
        branch = self.context.branch
473
        return (branch is not None and
474
                check_permission('launchpad.View', branch))
475
476
    @property
477
    def is_obsolete(self):
9742.7.7 by Edwin Grubbs
Fixed docstrings.
478
        """Return True if the series is OBSOLETE.
9731.2.1 by Curtis Hovey
Extracted +ubuntupkg code to its own view.
479
480
        Obsolete series do not need to display as much information as other
481
        series. Accessing private bugs is an expensive operation and showing
482
        them for obsolete series can be a problem if many series are being
483
        displayed.
484
        """
10054.26.1 by Adi Roiban
Refactor DistroSeriesStatus to SeriesStatus; Don't prompt for setting up translations for obsolete product series.
485
        return self.context.status == SeriesStatus.OBSOLETE
9731.2.1 by Curtis Hovey
Extracted +ubuntupkg code to its own view.
486
487
    @cachedproperty
488
    def bugtask_status_counts(self):
489
        """A list StatusCounts summarising the targeted bugtasks."""
490
        bugtaskset = getUtility(IBugTaskSet)
14124.2.12 by Gavin Panella
Update the ProductSeries:+status view to reflect the new behaviour of getStatusCountsForProductSeries().
491
        status_counts = bugtaskset.getStatusCountsForProductSeries(
9731.2.1 by Curtis Hovey
Extracted +ubuntupkg code to its own view.
492
            self.user, self.context)
14124.2.12 by Gavin Panella
Update the ProductSeries:+status view to reflect the new behaviour of getStatusCountsForProductSeries().
493
        # We sort by value before sortkey because the statuses returned can be
494
        # from different (though related) enums.
495
        statuses = sorted(status_counts, key=attrgetter('value', 'sortkey'))
496
        return [
497
            StatusCount(status, status_counts[status])
498
            for status in statuses]
9731.2.1 by Curtis Hovey
Extracted +ubuntupkg code to its own view.
499
500
    @cachedproperty
501
    def specification_status_counts(self):
502
        """A list StatusCounts summarising the targeted specification."""
503
        specification_set = getUtility(ISpecificationSet)
504
        status_id_counts = specification_set.getStatusCountsForProductSeries(
505
            self.context)
506
        SpecStatus = SpecificationImplementationStatus
507
        status_counts = dict([(SpecStatus.items[status_id], count)
508
                              for status_id, count in status_id_counts])
509
        return [StatusCount(status, status_counts[status])
510
                for status in sorted(status_counts,
511
                                     key=attrgetter('sortkey'))]
512
7675.803.1 by Edwin Grubbs
Batch milestones.
513
    @cachedproperty
9731.2.1 by Curtis Hovey
Extracted +ubuntupkg code to its own view.
514
    def latest_release_with_download_files(self):
515
        for release in self.context.releases:
516
            if len(list(release.files)) > 0:
517
                return release
518
        return None
519
7675.803.1 by Edwin Grubbs
Batch milestones.
520
    @cachedproperty
521
    def milestone_batch_navigator(self):
522
        return BatchNavigator(self.context.all_milestones, self.request)
523
9731.2.1 by Curtis Hovey
Extracted +ubuntupkg code to its own view.
524
11320.1.1 by Edwin Grubbs
Converted productseries detailed-display from a macro to its own template.
525
class ProductSeriesDetailedDisplayView(ProductSeriesView):
526
527
    @cachedproperty
528
    def latest_milestones(self):
11320.1.6 by Edwin Grubbs
Addressed review comments. Fixed _milestone_order.
529
        # Convert to list to avoid the query being run multiple times.
530
        return list(self.context.milestones[:12])
11320.1.1 by Edwin Grubbs
Converted productseries detailed-display from a macro to its own template.
531
532
    @cachedproperty
533
    def latest_releases(self):
11320.1.6 by Edwin Grubbs
Addressed review comments. Fixed _milestone_order.
534
        # Convert to list to avoid the query being run multiple times.
535
        return list(self.context.releases[:12])
11320.1.1 by Edwin Grubbs
Converted productseries detailed-display from a macro to its own template.
536
537
10193.2.1 by Edwin Grubbs
Removed PackagingAddView.
538
class ProductSeriesUbuntuPackagingView(LaunchpadFormView):
9731.2.4 by Curtis Hovey
Refactored the ProductSeriesUbuntuPackagingView to decend from PackagingAddView.
539
10193.2.1 by Edwin Grubbs
Removed PackagingAddView.
540
    schema = IPackaging
10023.3.4 by Curtis Hovey
Revise the Ubuntu packaging view to allow the user to select any ubuntu series.
541
    field_names = ['sourcepackagename', 'distroseries']
9731.2.4 by Curtis Hovey
Refactored the ProductSeriesUbuntuPackagingView to decend from PackagingAddView.
542
    page_title = 'Ubuntu source packaging'
543
    label = page_title
544
545
    def __init__(self, context, request):
9742.7.7 by Edwin Grubbs
Fixed docstrings.
546
        """Set the static packaging information for this series."""
9731.2.4 by Curtis Hovey
Refactored the ProductSeriesUbuntuPackagingView to decend from PackagingAddView.
547
        super(ProductSeriesUbuntuPackagingView, self).__init__(
548
            context, request)
10023.3.4 by Curtis Hovey
Revise the Ubuntu packaging view to allow the user to select any ubuntu series.
549
        self._ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
550
        self._ubuntu_series = self._ubuntu.currentseries
9710.5.12 by Curtis Hovey
Reverted 9721:
551
        try:
9731.2.4 by Curtis Hovey
Refactored the ProductSeriesUbuntuPackagingView to decend from PackagingAddView.
552
            package = self.context.getPackage(self._ubuntu_series)
553
            self.default_sourcepackagename = package.sourcepackagename
9710.5.12 by Curtis Hovey
Reverted 9721:
554
        except NotFoundError:
9731.2.4 by Curtis Hovey
Refactored the ProductSeriesUbuntuPackagingView to decend from PackagingAddView.
555
            # The package has never been set.
556
            self.default_sourcepackagename = None
557
10193.2.1 by Edwin Grubbs
Removed PackagingAddView.
558
    @property
559
    def next_url(self):
560
        """See `LaunchpadFormView`."""
561
        return canonical_url(self.context)
562
563
    cancel_url = next_url
564
10023.3.4 by Curtis Hovey
Revise the Ubuntu packaging view to allow the user to select any ubuntu series.
565
    def setUpFields(self):
566
        """See `LaunchpadFormView`.
567
568
        The packaging is restricted to ubuntu series and the default value
569
        is the current development series.
570
        """
10023.3.12 by Curtis Hovey
Fixed super() call to ensure the class works if the super class adds a setUpFields().
571
        super(ProductSeriesUbuntuPackagingView, self).setUpFields()
10023.3.4 by Curtis Hovey
Revise the Ubuntu packaging view to allow the user to select any ubuntu series.
572
        series_vocabulary = SimpleVocabulary(
573
            [SimpleTerm(series, series.name, series.named_version)
574
             for series in self._ubuntu.series])
575
        choice = Choice(__name__='distroseries',
576
            title=_('Series'),
577
            default=self._ubuntu_series,
578
            vocabulary=series_vocabulary,
579
            description=_(
10023.3.14 by Curtis Hovey
Updated text per review.
580
                "Series where this package is published. The current series "
581
                "is most important to the Ubuntu community."),
10023.3.4 by Curtis Hovey
Revise the Ubuntu packaging view to allow the user to select any ubuntu series.
582
            required=True)
583
        field = form.Fields(choice, render_context=self.render_context)
584
        self.form_fields = self.form_fields.omit(choice.__name__) + field
585
10722.2.3 by Curtis Hovey
Use initial_values instead of setUpWidgets().
586
    @property
587
    def initial_values(self):
588
        """See `LaunchpadFormView`."""
9731.2.4 by Curtis Hovey
Refactored the ProductSeriesUbuntuPackagingView to decend from PackagingAddView.
589
        if self.default_sourcepackagename is not None:
10722.2.3 by Curtis Hovey
Use initial_values instead of setUpWidgets().
590
            return {'sourcepackagename': self.default_sourcepackagename}
591
        else:
592
            return {}
9731.2.4 by Curtis Hovey
Refactored the ProductSeriesUbuntuPackagingView to decend from PackagingAddView.
593
594
    @property
595
    def default_distroseries(self):
596
        """The current Ubuntu distroseries"""
597
        return self._ubuntu_series
598
599
    @property
600
    def ubuntu_history(self):
601
        return self.context.getPackagingInDistribution(
602
            self.default_distroseries.distribution)
603
10722.2.2 by Curtis Hovey
Added a method to replace duplicate code.
604
    def _getSubmittedSeries(self, data):
605
        """Return the submitted or default series."""
606
        return data.get('distroseries', self.default_distroseries)
607
9731.2.4 by Curtis Hovey
Refactored the ProductSeriesUbuntuPackagingView to decend from PackagingAddView.
608
    def validate(self, data):
10193.2.1 by Edwin Grubbs
Removed PackagingAddView.
609
        productseries = self.context
9731.2.4 by Curtis Hovey
Refactored the ProductSeriesUbuntuPackagingView to decend from PackagingAddView.
610
        sourcepackagename = data.get('sourcepackagename', None)
10722.2.2 by Curtis Hovey
Added a method to replace duplicate code.
611
        distroseries = self._getSubmittedSeries(data)
10193.2.1 by Edwin Grubbs
Removed PackagingAddView.
612
11099.2.1 by Edwin Grubbs
Fix unique constraint error due to validate() not catching some errors.
613
        packaging_util = getUtility(IPackagingUtil)
614
        if packaging_util.packagingEntryExists(
615
            productseries=productseries,
616
            sourcepackagename=sourcepackagename,
617
            distroseries=distroseries):
618
            # The package already exists. Don't display an error. The
619
            # action method will let this go by.
9731.2.4 by Curtis Hovey
Refactored the ProductSeriesUbuntuPackagingView to decend from PackagingAddView.
620
            return
10193.2.1 by Edwin Grubbs
Removed PackagingAddView.
621
10561.1.1 by Edwin Grubbs
Added restriction in IProductSeries.setPackaging().
622
        # Do not allow users to create links to unpublished Ubuntu packages.
11099.2.1 by Edwin Grubbs
Fix unique constraint error due to validate() not catching some errors.
623
        if (sourcepackagename is not None
624
            and distroseries.distribution.full_functionality):
10193.2.1 by Edwin Grubbs
Removed PackagingAddView.
625
            source_package = distroseries.getSourcePackage(sourcepackagename)
626
            if source_package.currentrelease is None:
627
                message = ("The source package is not published in %s." %
628
                    distroseries.displayname)
629
                self.setFieldError('sourcepackagename', message)
11099.2.1 by Edwin Grubbs
Fix unique constraint error due to validate() not catching some errors.
630
10193.2.1 by Edwin Grubbs
Removed PackagingAddView.
631
        if packaging_util.packagingEntryExists(
632
            sourcepackagename=sourcepackagename,
633
            distroseries=distroseries):
634
            # The series package conflicts with another series.
635
            sourcepackage = distroseries.getSourcePackage(
636
                sourcepackagename.name)
637
            message = structured(
638
                'The <a href="%s">%s</a> package in %s is already linked to '
639
                'another series.' %
640
                (canonical_url(sourcepackage),
641
                 sourcepackagename.name,
642
                 distroseries.displayname))
643
            self.setFieldError('sourcepackagename', message)
644
9731.2.4 by Curtis Hovey
Refactored the ProductSeriesUbuntuPackagingView to decend from PackagingAddView.
645
    @action('Update', name='continue')
646
    def continue_action(self, action, data):
9710.5.12 by Curtis Hovey
Reverted 9721:
647
        # set the packaging record for this productseries in the current
648
        # ubuntu series. if none exists, one will be created
10722.2.2 by Curtis Hovey
Added a method to replace duplicate code.
649
        distroseries = self._getSubmittedSeries(data)
9731.2.4 by Curtis Hovey
Refactored the ProductSeriesUbuntuPackagingView to decend from PackagingAddView.
650
        sourcepackagename = data['sourcepackagename']
10722.2.1 by Curtis Hovey
Updated +ubuntupkg view to used the submitted distroseries.
651
        if getUtility(IPackagingUtil).packagingEntryExists(
652
            sourcepackagename, distroseries, productseries=self.context):
9731.2.4 by Curtis Hovey
Refactored the ProductSeriesUbuntuPackagingView to decend from PackagingAddView.
653
            # There is no change.
654
            return
655
        self.context.setPackaging(
10722.2.1 by Curtis Hovey
Updated +ubuntupkg view to used the submitted distroseries.
656
            distroseries, sourcepackagename, self.user)
9710.5.12 by Curtis Hovey
Reverted 9721:
657
9081.2.8 by Curtis Hovey
Updated the productseries ubuntu packaging page to UI 3.0.
658
3691.125.6 by James Henstridge
add UI for setting the branch for a product series
659
class ProductSeriesEditView(LaunchpadEditFormView):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
660
    """A View to edit the attributes of a series."""
3691.125.6 by James Henstridge
add UI for setting the branch for a product series
661
    schema = IProductSeries
6414.2.3 by Guilherme Salgado
UI for changing a product series' status and displaying it.
662
    field_names = [
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
663
        'name', 'summary', 'status', 'branch', 'releasefileglob']
3691.125.6 by James Henstridge
add UI for setting the branch for a product series
664
    custom_widget('summary', TextAreaWidget, height=7, width=62)
3691.169.13 by James Henstridge
move the releaseroot/releasefileglob entries to the edit product form
665
    custom_widget('releasefileglob', StrippedTextWidget, displayWidth=40)
3691.125.6 by James Henstridge
add UI for setting the branch for a product series
666
9081.2.2 by Curtis Hovey
Update the productseries edit page to UI 3.0
667
    @property
668
    def label(self):
669
        """The form label."""
9081.2.10 by Curtis Hovey
Fixed series page title that was broken in a previous commit.
670
        return 'Edit %s %s series' % (
9081.2.2 by Curtis Hovey
Update the productseries edit page to UI 3.0
671
            self.context.product.displayname, self.context.name)
672
673
    @property
674
    def page_title(self):
675
        """The page title."""
676
        return self.label
677
3691.125.7 by James Henstridge
allow setting the series branch when creating a product series, and validate that the branch comes from the same product
678
    def validate(self, data):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
679
        """See `LaunchpadFormView`."""
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
680
        branch = data.get('branch')
3691.125.12 by James Henstridge
check the (series,branch) constraint when updating a series branch
681
        if branch is not None:
3691.125.14 by James Henstridge
fixes suggested by Bjorn in review
682
            message = get_series_branch_error(self.context.product, branch)
3691.125.12 by James Henstridge
check the (series,branch) constraint when updating a series branch
683
            if message:
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
684
                self.setFieldError('branch', message)
3691.125.7 by James Henstridge
allow setting the series branch when creating a product series, and validate that the branch comes from the same product
685
3691.125.6 by James Henstridge
add UI for setting the branch for a product series
686
    @action(_('Change'), name='change')
687
    def change_action(self, action, data):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
688
        """Update the series."""
3691.125.6 by James Henstridge
add UI for setting the branch for a product series
689
        self.updateContextFromData(data)
690
691
    @property
692
    def next_url(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
693
        """See `LaunchpadFormView`."""
3691.125.6 by James Henstridge
add UI for setting the branch for a product series
694
        return canonical_url(self.context)
695
9081.2.2 by Curtis Hovey
Update the productseries edit page to UI 3.0
696
    @property
697
    def cancel_url(self):
698
        """See `LaunchpadFormView`."""
699
        return canonical_url(self.context)
700
3691.125.6 by James Henstridge
add UI for setting the branch for a product series
701
8255.11.2 by Curtis Hovey
Product series owners and drivers can delete mis-created series.
702
class ProductSeriesDeleteView(RegistryDeleteViewMixin, LaunchpadEditFormView):
703
    """A view to remove a productseries from a product."""
704
    schema = IProductSeries
705
    field_names = []
706
707
    @property
708
    def label(self):
709
        """The form label."""
9081.2.10 by Curtis Hovey
Fixed series page title that was broken in a previous commit.
710
        return 'Delete %s %s series' % (
8255.11.2 by Curtis Hovey
Product series owners and drivers can delete mis-created series.
711
            self.context.product.displayname, self.context.name)
712
9081.2.1 by Curtis Hovey
Updated productseries delete to ui 3.0.
713
    @property
714
    def page_title(self):
715
        """The page title."""
716
        return self.label
717
8255.11.2 by Curtis Hovey
Product series owners and drivers can delete mis-created series.
718
    @cachedproperty
719
    def milestones(self):
720
        """A list of all the series `IMilestone`s."""
721
        return self.context.all_milestones
722
723
    @cachedproperty
724
    def bugtasks(self):
725
        """A list of all `IBugTask`s targeted to this series."""
9571.1.5 by Curtis Hovey
Optimised code to keep it DRY.
726
        all_bugtasks = self._getBugtasks(self.context)
8255.11.2 by Curtis Hovey
Product series owners and drivers can delete mis-created series.
727
        for milestone in self.milestones:
728
            all_bugtasks.extend(self._getBugtasks(milestone))
729
        return all_bugtasks
730
731
    @cachedproperty
732
    def specifications(self):
733
        """A list of all `ISpecification`s targeted to this series."""
9571.1.5 by Curtis Hovey
Optimised code to keep it DRY.
734
        all_specifications = self._getSpecifications(self.context)
8255.11.2 by Curtis Hovey
Product series owners and drivers can delete mis-created series.
735
        for milestone in self.milestones:
736
            all_specifications.extend(self._getSpecifications(milestone))
737
        return all_specifications
738
739
    @cachedproperty
740
    def has_bugtasks_and_specifications(self):
741
        """Does the series have any targeted bugtasks or specifications."""
742
        return len(self.bugtasks) > 0 or len(self.specifications) > 0
743
9571.1.6 by Curtis Hovey
Updated the UI to have consistent list indentation. Add a rule to show which branch will be unlinked if the series is deleted.
744
    @property
745
    def has_linked_branch(self):
746
        """Is the series linked to a branch."""
747
        return self.context.branch is not None
748
8255.11.2 by Curtis Hovey
Product series owners and drivers can delete mis-created series.
749
    @cachedproperty
750
    def product_release_files(self):
751
        """A list of all `IProductReleaseFile`s that belong to this series."""
752
        all_files = []
753
        for milestone in self.milestones:
754
            all_files.extend(self._getProductReleaseFiles(milestone))
755
        return all_files
756
9963.2.2 by Curtis Hovey
Do not delete a series if it has translations.
757
    @cachedproperty
9963.2.1 by Curtis Hovey
Do not allow users to delete series that are linked to packages.
758
    def has_linked_packages(self):
759
        """Is the series linked to source packages."""
760
        return self.context.packagings.count() > 0
761
9963.2.2 by Curtis Hovey
Do not delete a series if it has translations.
762
    @cachedproperty
9963.2.1 by Curtis Hovey
Do not allow users to delete series that are linked to packages.
763
    def linked_packages_message(self):
764
        url = canonical_url(self.context.product, view_name="+packages")
765
        return (
766
            "You cannot delete a series that is linked to packages in "
767
            "distributions. You can remove the links from the "
768
            '<a href="%s">project packaging</a> page.' % url)
769
770
    development_focus_message = _(
771
        "You cannot delete a series that is the focus of "
772
        "development. Make another series the focus of development "
773
        "before deleting this one.")
774
8255.11.2 by Curtis Hovey
Product series owners and drivers can delete mis-created series.
775
    @cachedproperty
9963.2.2 by Curtis Hovey
Do not delete a series if it has translations.
776
    def has_translations(self):
777
        """Does the series have translations?"""
778
        return self.context.potemplate_count > 0
779
780
    translations_message = (
781
        "This series cannot be deleted because it has translations.")
782
783
    @cachedproperty
8255.11.2 by Curtis Hovey
Product series owners and drivers can delete mis-created series.
784
    def can_delete(self):
785
        """Can this series be delete."""
9963.2.1 by Curtis Hovey
Do not allow users to delete series that are linked to packages.
786
        return not (
9963.2.2 by Curtis Hovey
Do not delete a series if it has translations.
787
            self.context.is_development_focus
788
            or self.has_linked_packages or self.has_translations)
8255.11.2 by Curtis Hovey
Product series owners and drivers can delete mis-created series.
789
790
    def canDeleteAction(self, action):
791
        """Is the delete action available."""
9963.2.1 by Curtis Hovey
Do not allow users to delete series that are linked to packages.
792
        if self.context.is_development_focus:
793
            self.addError(self.development_focus_message)
9963.2.4 by Curtis Hovey
Changes per review.
794
        if self.has_linked_packages:
9963.2.1 by Curtis Hovey
Do not allow users to delete series that are linked to packages.
795
            self.addError(structured(self.linked_packages_message))
9963.2.4 by Curtis Hovey
Changes per review.
796
        if self.has_translations:
9963.2.2 by Curtis Hovey
Do not delete a series if it has translations.
797
            self.addError(self.translations_message)
8255.11.2 by Curtis Hovey
Product series owners and drivers can delete mis-created series.
798
        return self.can_delete
799
800
    @action('Delete this Series', name='delete', condition=canDeleteAction)
801
    def delete_action(self, action, data):
802
        """Detach and delete associated objects and remove the series."""
803
        product = self.context.product
804
        name = self.context.name
8255.11.4 by Curtis Hovey
Changes per review. Moved the delete productseries rule to the RegistryDeleteMixin. Added support for BeautifulSoup PageElements as content to find_tag_by_id(). Added has_available_actions to LaunchpadFormView and made the 'or' before the cancel link conditional upon it.
805
        self._deleteProductSeries(self.context)
8255.11.2 by Curtis Hovey
Product series owners and drivers can delete mis-created series.
806
        self.request.response.addInfoNotification(
807
            "Series %s deleted." % name)
808
        self.next_url = canonical_url(product)
809
810
10456.2.11 by Brad Crittenden
Added more browser functionality for +setbranch
811
LINK_LP_BZR = 'link-lp-bzr'
812
CREATE_NEW = 'create-new'
813
IMPORT_EXTERNAL = 'import-external'
814
815
10456.2.17 by Brad Crittenden
Made branch type vocabulary a constant
816
BRANCH_TYPE_VOCABULARY = SimpleVocabulary((
817
    SimpleTerm(LINK_LP_BZR, LINK_LP_BZR,
818
               _("Link to a Bazaar branch already on Launchpad")),
819
    SimpleTerm(CREATE_NEW, CREATE_NEW,
820
               _("Create a new, empty branch in Launchpad and "
821
                 "link to this series")),
822
    SimpleTerm(IMPORT_EXTERNAL, IMPORT_EXTERNAL,
823
               _("Import a branch hosted somewhere else")),
824
    ))
10456.2.1 by Brad Crittenden
checkpoint
825
826
827
class SetBranchForm(Interface):
828
    """The fields presented on the form for setting a branch."""
829
830
    use_template(
831
        ICodeImport,
832
        ['cvs_module'])
833
834
    rcs_type = Choice(title=_("Type of RCS"),
13756.4.1 by Jelmer Vernooij
Re-import bzr code imports.
835
        required=False, vocabulary=RevisionControlSystems,
10456.2.1 by Brad Crittenden
checkpoint
836
        description=_(
837
            "The version control system to import from. "))
838
839
    repo_url = URIField(
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
840
        title=_("Branch URL"), required=True,
10456.2.1 by Brad Crittenden
checkpoint
841
        description=_("The URL of the branch."),
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
842
        allowed_schemes=["http", "https"],
10456.2.1 by Brad Crittenden
checkpoint
843
        allow_userinfo=False,
844
        allow_port=True,
845
        allow_query=False,
846
        allow_fragment=False,
847
        trailing_slash=False)
848
849
    branch_location = copy_field(
850
        IProductSeries['branch'],
851
        __name__='branch_location',
10456.2.8 by Brad Crittenden
Added branch name and owner fields to the interface and template.
852
        title=_('Branch'),
10456.2.1 by Brad Crittenden
checkpoint
853
        description=_(
854
            "The Bazaar branch for this series in Launchpad, "
855
            "if one exists."),
856
        )
857
858
    branch_type = Choice(
859
        title=_('Import type'),
10456.2.17 by Brad Crittenden
Made branch type vocabulary a constant
860
        vocabulary=BRANCH_TYPE_VOCABULARY,
10456.2.1 by Brad Crittenden
checkpoint
861
        description=_("The type of import"),
862
        required=True)
863
10456.2.8 by Brad Crittenden
Added branch name and owner fields to the interface and template.
864
    branch_name = copy_field(
865
        IBranch['name'],
866
        __name__='branch_name',
867
        title=_('Branch name'),
868
        description=_(''),
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
869
        required=True,
10456.2.8 by Brad Crittenden
Added branch name and owner fields to the interface and template.
870
        )
871
872
    branch_owner = copy_field(
873
        IBranch['owner'],
874
        __name__='branch_owner',
875
        title=_('Branch owner'),
876
        description=_(''),
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
877
        required=True,
10456.2.8 by Brad Crittenden
Added branch name and owner fields to the interface and template.
878
        )
879
10456.2.1 by Brad Crittenden
checkpoint
880
10469.1.21 by Edwin Grubbs
Make involvement portlet use +setbranch instead of +linkbranch.
881
class ProductSeriesSetBranchView(ReturnToReferrerMixin, LaunchpadFormView,
882
                                 ProductSeriesView,
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
883
                                 BranchNameValidationMixin):
10456.2.1 by Brad Crittenden
checkpoint
884
    """The view to set a branch for the ProductSeries."""
885
886
    schema = SetBranchForm
887
    # Set for_input to True to ensure fields marked read-only will be editable
888
    # upon creation.
889
    for_input = True
890
891
    custom_widget('rcs_type', LaunchpadRadioWidget)
892
    custom_widget('branch_type', LaunchpadRadioWidget)
893
10667.3.8 by Brad Crittenden
Create mechanism to override next_url when there are errors.
894
    errors_in_action = False
895
896
    @property
10714.1.3 by Brad Crittenden
Updated the way initial_values are handled to make more sense.
897
    def initial_values(self):
898
        return dict(
13756.4.1 by Jelmer Vernooij
Re-import bzr code imports.
899
            rcs_type=RevisionControlSystems.BZR,
10714.1.3 by Brad Crittenden
Updated the way initial_values are handled to make more sense.
900
            branch_type=LINK_LP_BZR,
901
            branch_location=self.context.branch)
902
903
    @property
10667.3.8 by Brad Crittenden
Create mechanism to override next_url when there are errors.
904
    def next_url(self):
905
        """Return the next_url.
906
907
        Use the value from `ReturnToReferrerMixin` or None if there
908
        are errors.
909
        """
910
        if self.errors_in_action:
911
            return None
912
        return super(ProductSeriesSetBranchView, self).next_url
913
10456.2.18 by Brad Crittenden
Change widget generation based on review
914
    def setUpWidgets(self):
10456.2.29 by Brad Crittenden
Cleaned up render method. Other changes from review.
915
        """See `LaunchpadFormView`."""
10456.2.18 by Brad Crittenden
Change widget generation based on review
916
        super(ProductSeriesSetBranchView, self).setUpWidgets()
917
        widget = self.widgets['rcs_type']
918
        vocab = widget.vocabulary
10456.2.29 by Brad Crittenden
Cleaned up render method. Other changes from review.
919
        current_value = widget._getFormValue()
11929.11.14 by Tim Penhey
Move the method to render a part of the widget into the launchpadform module.
920
        self.rcs_type_cvs = render_radio_widget_part(
921
            widget, vocab.CVS, current_value, 'CVS')
922
        self.rcs_type_svn = render_radio_widget_part(
923
            widget, vocab.BZR_SVN, current_value, 'SVN')
924
        self.rcs_type_git = render_radio_widget_part(
925
            widget, vocab.GIT, current_value)
926
        self.rcs_type_hg = render_radio_widget_part(
927
            widget, vocab.HG, current_value)
928
        self.rcs_type_bzr = render_radio_widget_part(
929
            widget, vocab.BZR, current_value)
10456.2.18 by Brad Crittenden
Change widget generation based on review
930
        self.rcs_type_emptymarker = widget._emptyMarker()
931
932
        widget = self.widgets['branch_type']
933
        current_value = widget._getFormValue()
934
        vocab = widget.vocabulary
935
936
        (self.branch_type_link,
937
         self.branch_type_create,
938
         self.branch_type_import) = [
11929.11.14 by Tim Penhey
Move the method to render a part of the widget into the launchpadform module.
939
            render_radio_widget_part(widget, value, current_value)
10456.2.18 by Brad Crittenden
Change widget generation based on review
940
            for value in (LINK_LP_BZR, CREATE_NEW, IMPORT_EXTERNAL)]
941
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
942
    def _validateLinkLpBzr(self, data):
10456.2.12 by Brad Crittenden
Added more tests and functionality to ProductSeriesSetBranchView
943
        """Validate data for link-lp-bzr case."""
944
        if 'branch_location' not in data:
945
            self.setFieldError(
946
                'branch_location',
947
                'The branch location must be set.')
948
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
949
    def _validateCreateNew(self, data):
10456.2.12 by Brad Crittenden
Added more tests and functionality to ProductSeriesSetBranchView
950
        """Validate data for create new case."""
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
951
        self._validateBranch(data)
10456.2.12 by Brad Crittenden
Added more tests and functionality to ProductSeriesSetBranchView
952
10456.2.21 by Brad Crittenden
Give widget errors high priority
953
    def _validateImportExternal(self, data):
10456.2.12 by Brad Crittenden
Added more tests and functionality to ProductSeriesSetBranchView
954
        """Validate data for import external case."""
10456.2.11 by Brad Crittenden
Added more browser functionality for +setbranch
955
        rcs_type = data.get('rcs_type')
956
        repo_url = data.get('repo_url')
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
957
958
        if repo_url is None:
959
            self.setFieldError('repo_url',
960
                               'You must set the external repository URL.')
10456.2.22 by Brad Crittenden
Do not allow duplicate code imports for the same URL
961
        else:
962
            # Ensure this URL has not been imported before.
963
            code_import = getUtility(ICodeImportSet).getByURL(repo_url)
964
            if code_import is not None:
965
                self.setFieldError(
966
                    'repo_url',
967
                    structured("""
968
                    This foreign branch URL is already specified for
969
                    the imported branch <a href="%s">%s</a>.""",
970
                               canonical_url(code_import.branch),
971
                               code_import.branch.unique_name))
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
972
10456.2.12 by Brad Crittenden
Added more tests and functionality to ProductSeriesSetBranchView
973
        # RCS type is mandatory.
974
        # This condition should never happen since an initial value is set.
975
        if rcs_type is None:
976
            # The error shows but does not identify the widget.
977
            self.setFieldError(
978
                'rcs_type',
979
                'You must specify the type of RCS for the remote host.')
13756.4.1 by Jelmer Vernooij
Re-import bzr code imports.
980
        elif rcs_type == RevisionControlSystems.CVS:
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
981
            if 'cvs_module' not in data:
982
                self.setFieldError(
983
                    'cvs_module',
984
                    'The CVS module must be set.')
985
        self._validateBranch(data)
10456.2.12 by Brad Crittenden
Added more tests and functionality to ProductSeriesSetBranchView
986
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
987
    def _validateBranch(self, data):
10456.2.12 by Brad Crittenden
Added more tests and functionality to ProductSeriesSetBranchView
988
        """Validate that branch name and owner are set."""
989
        if 'branch_name' not in data:
990
            self.setFieldError(
991
                'branch_name',
992
                'The branch name must be set.')
993
        if 'branch_owner' not in data:
994
            self.setFieldError(
995
                'branch_owner',
996
                'The branch owner must be set.')
997
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
998
    def _setRequired(self, names, value):
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
999
        """Mark the widget field as optional."""
1000
        for name in names:
1001
            widget = self.widgets[name]
1002
            # The 'required' property on the widget context is set to False.
1003
            # The widget also has a 'required' property but it isn't used
1004
            # during validation.
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
1005
            widget.context.required = value
1006
1007
    def _validSchemes(self, rcs_type):
1008
        """Return the valid schemes for the repository URL."""
1009
        schemes = set(['http', 'https'])
1010
        # Extend the allowed schemes for the repository URL based on
1011
        # rcs_type.
1012
        extra_schemes = {
13756.4.1 by Jelmer Vernooij
Re-import bzr code imports.
1013
            RevisionControlSystems.BZR_SVN: ['svn'],
1014
            RevisionControlSystems.GIT: ['git'],
1015
            RevisionControlSystems.BZR: ['bzr'],
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
1016
            }
1017
        schemes.update(extra_schemes.get(rcs_type, []))
1018
        return schemes
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
1019
1020
    def validate_widgets(self, data, names=None):
1021
        """See `LaunchpadFormView`."""
1022
        names = ['branch_type', 'rcs_type']
1023
        super(ProductSeriesSetBranchView, self).validate_widgets(data, names)
1024
        branch_type = data.get('branch_type')
1025
        if branch_type == LINK_LP_BZR:
1026
            # Mark other widgets as non-required.
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
1027
            self._setRequired(['rcs_type', 'repo_url', 'cvs_module',
1028
                               'branch_name', 'branch_owner'], False)
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
1029
        elif branch_type == CREATE_NEW:
10456.2.25 by Brad Crittenden
Make repo_url optional for creating a new branch. Set the registrant properly for an import
1030
            self._setRequired(
1031
                ['branch_location', 'repo_url', 'rcs_type', 'cvs_module'],
1032
                False)
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
1033
        elif branch_type == IMPORT_EXTERNAL:
1034
            rcs_type = data.get('rcs_type')
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
1035
1036
            # Set the valid schemes based on rcs_type.
1037
            self.widgets['repo_url'].field.allowed_schemes = (
1038
                self._validSchemes(rcs_type))
1039
            # The branch location is not required for validation.
1040
            self._setRequired(['branch_location'], False)
1041
            # The cvs_module is required if it is a CVS import.
13756.4.1 by Jelmer Vernooij
Re-import bzr code imports.
1042
            if rcs_type == RevisionControlSystems.CVS:
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
1043
                self._setRequired(['cvs_module'], True)
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
1044
        else:
1045
            raise AssertionError("Unknown branch type %s" % branch_type)
1046
        # Perform full validation now.
1047
        super(ProductSeriesSetBranchView, self).validate_widgets(data)
1048
10456.2.12 by Brad Crittenden
Added more tests and functionality to ProductSeriesSetBranchView
1049
    def validate(self, data):
1050
        """See `LaunchpadFormView`."""
10456.2.21 by Brad Crittenden
Give widget errors high priority
1051
        # If widget validation returned errors then there is no need to
1052
        # continue as we'd likely just override the errors reported there.
1053
        if len(self.errors) > 0:
1054
            return
10456.2.11 by Brad Crittenden
Added more browser functionality for +setbranch
1055
        branch_type = data['branch_type']
1056
        if branch_type == IMPORT_EXTERNAL:
10456.2.21 by Brad Crittenden
Give widget errors high priority
1057
            self._validateImportExternal(data)
10456.2.11 by Brad Crittenden
Added more browser functionality for +setbranch
1058
        elif branch_type == LINK_LP_BZR:
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
1059
            self._validateLinkLpBzr(data)
10456.2.12 by Brad Crittenden
Added more tests and functionality to ProductSeriesSetBranchView
1060
        elif branch_type == CREATE_NEW:
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
1061
            self._validateCreateNew(data)
10456.2.12 by Brad Crittenden
Added more tests and functionality to ProductSeriesSetBranchView
1062
        else:
1063
            raise AssertionError("Unknown branch type %s" % branch_type)
10456.2.3 by Brad Crittenden
Working JS interface. No backend support.
1064
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
1065
    @property
1066
    def target(self):
1067
        """The branch target for the context."""
10648.1.9 by Curtis Hovey
Move ProductSeries branch_add menu link to Product to avoid crafting links.
1068
        return IBranchTarget(self.context.product)
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
1069
10456.2.3 by Brad Crittenden
Working JS interface. No backend support.
1070
    @action(_('Update'), name='update')
1071
    def update_action(self, action, data):
1072
        branch_type = data.get('branch_type')
10456.2.11 by Brad Crittenden
Added more browser functionality for +setbranch
1073
        if branch_type == LINK_LP_BZR:
10456.2.3 by Brad Crittenden
Working JS interface. No backend support.
1074
            branch_location = data.get('branch_location')
10456.2.12 by Brad Crittenden
Added more tests and functionality to ProductSeriesSetBranchView
1075
            if branch_location != self.context.branch:
1076
                self.context.branch = branch_location
1077
                # Request an initial upload of translation files.
1078
                getUtility(IRosettaUploadJobSource).create(
1079
                    self.context.branch, NULL_REVISION)
10456.2.3 by Brad Crittenden
Working JS interface. No backend support.
1080
            else:
10456.2.12 by Brad Crittenden
Added more tests and functionality to ProductSeriesSetBranchView
1081
                self.context.branch = branch_location
1082
            self.request.response.addInfoNotification(
1083
                'Series code location updated.')
10456.2.3 by Brad Crittenden
Working JS interface. No backend support.
1084
        else:
10456.2.11 by Brad Crittenden
Added more browser functionality for +setbranch
1085
            branch_name = data.get('branch_name')
1086
            branch_owner = data.get('branch_owner')
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
1087
1088
            # Create a new branch.
10456.2.11 by Brad Crittenden
Added more browser functionality for +setbranch
1089
            if branch_type == CREATE_NEW:
13756.5.1 by Jelmer Vernooij
Reimport bzr code import support UI patch.
1090
                branch = self._createBzrBranch(branch_name, branch_owner)
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
1091
                if branch is not None:
1092
                    self.context.branch = branch
1093
                    self.request.response.addInfoNotification(
1094
                        'New branch created and linked to the series.')
1095
1096
            # Import or mirror an external branch.
10456.2.11 by Brad Crittenden
Added more browser functionality for +setbranch
1097
            elif branch_type == IMPORT_EXTERNAL:
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
1098
                # Either create an externally hosted bzr branch
1099
                # (a.k.a. 'mirrored') or create a new code import.
1100
                rcs_type = data.get('rcs_type')
13756.5.1 by Jelmer Vernooij
Reimport bzr code import support UI patch.
1101
                # We need to create an import request.
1102
                if rcs_type == RevisionControlSystems.CVS:
1103
                    cvs_root = data.get('repo_url')
1104
                    cvs_module = data.get('cvs_module')
1105
                    url = None
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
1106
                else:
13756.5.1 by Jelmer Vernooij
Reimport bzr code import support UI patch.
1107
                    cvs_root = None
1108
                    cvs_module = None
1109
                    url = data.get('repo_url')
1110
                rcs_item = RevisionControlSystems.items[rcs_type.name]
1111
                try:
1112
                    code_import = getUtility(ICodeImportSet).new(
1113
                        registrant=branch_owner,
1114
                        target=IBranchTarget(self.context.product),
1115
                        branch_name=branch_name,
1116
                        rcs_type=rcs_item,
1117
                        url=url,
1118
                        cvs_root=cvs_root,
1119
                        cvs_module=cvs_module)
1120
                except BranchExists, e:
1121
                    self._setBranchExists(e.existing_branch,
1122
                                          'branch_name')
1123
                    self.errors_in_action = True
1124
                    # Abort transaction. This is normally handled
1125
                    # by LaunchpadFormView, but we are already in
1126
                    # the success handler.
1127
                    self._abort()
1128
                    return
1129
                self.context.branch = code_import.branch
1130
                self.request.response.addInfoNotification(
1131
                    'Code import created and branch linked to the '
1132
                    'series.')
10456.2.11 by Brad Crittenden
Added more browser functionality for +setbranch
1133
            else:
1134
                raise UnexpectedFormData(branch_type)
10456.2.3 by Brad Crittenden
Working JS interface. No backend support.
1135
13756.5.1 by Jelmer Vernooij
Reimport bzr code import support UI patch.
1136
    def _createBzrBranch(self, branch_name, branch_owner, repo_url=None):
13756.5.14 by Jelmer Vernooij
Work on fixing code import UI.
1137
        """Create a new hosted Bazaar branch.
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
1138
1139
        Return the branch on success or None.
1140
        """
1141
        branch = None
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
1142
        try:
1143
            namespace = self.target.getNamespace(branch_owner)
13756.5.1 by Jelmer Vernooij
Reimport bzr code import support UI patch.
1144
            branch = namespace.createBranch(branch_type=BranchType.HOSTED,
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
1145
                                            name=branch_name,
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
1146
                                            registrant=self.user,
1147
                                            url=repo_url)
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
1148
        except BranchCreationForbidden:
1149
            self.addError(
1150
                "You are not allowed to create branches in %s." %
1151
                self.context.displayname)
1152
        except BranchExists, e:
1153
            self._setBranchExists(e.existing_branch, 'branch_name')
10820.1.1 by Edwin Grubbs
Fixed error messages. Enable submit code link for product series once a branch has been set for the series.
1154
        if branch is None:
1155
            self.errors_in_action = True
1156
            # Abort transaction. This is normally handled by
1157
            # LaunchpadFormView, but we are already in the success handler.
1158
            self._abort()
10456.2.16 by Brad Crittenden
Fixed form validation errors and added tests.
1159
        return branch
10456.2.13 by Brad Crittenden
Added browser support for other +setbranch options
1160
10456.2.1 by Brad Crittenden
checkpoint
1161
10469.1.13 by Edwin Grubbs
Handled reviewer comments.
1162
class ProductSeriesLinkBranchView(ReturnToReferrerMixin,
10469.1.20 by Edwin Grubbs
Merged in ~bac/launchpad/bug-524302
1163
                                  ProductSeriesView,
10469.1.13 by Edwin Grubbs
Handled reviewer comments.
1164
                                  LaunchpadEditFormView):
5481.2.2 by Tim Penhey
Added a new page just to set the bazaar branch.
1165
    """View to set the bazaar branch for a product series."""
1166
1167
    schema = IProductSeries
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
1168
    field_names = ['branch']
5481.2.2 by Tim Penhey
Added a new page just to set the bazaar branch.
1169
1170
    @property
9081.2.6 by Curtis Hovey
Updated the product linkbranch page to UI 3.0.
1171
    def label(self):
1172
        """The form label."""
9081.2.10 by Curtis Hovey
Fixed series page title that was broken in a previous commit.
1173
        return 'Link an existing branch to %s %s series' % (
9081.2.6 by Curtis Hovey
Updated the product linkbranch page to UI 3.0.
1174
            self.context.product.displayname, self.context.name)
1175
1176
    @property
1177
    def page_title(self):
1178
        """The page title."""
1179
        return self.label
1180
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
1181
    @action(_('Update'), name='update')
5481.2.2 by Tim Penhey
Added a new page just to set the bazaar branch.
1182
    def update_action(self, action, data):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
1183
        """Update the branch attribute."""
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
1184
        if data['branch'] != self.context.branch:
1185
            self.updateContextFromData(data)
1186
            # Request an initial upload of translation files.
1187
            getUtility(IRosettaUploadJobSource).create(
1188
                self.context.branch, NULL_REVISION)
1189
        else:
1190
            self.updateContextFromData(data)
5481.2.2 by Tim Penhey
Added a new page just to set the bazaar branch.
1191
        self.request.response.addInfoNotification(
1192
            'Series code location updated.')
1193
1194
5481.2.6 by Tim Penhey
Fixes following review
1195
class ProductSeriesLinkBranchFromCodeView(ProductSeriesLinkBranchView):
1196
    """Set the branch link from the code overview page."""
1197
1198
    @property
1199
    def next_url(self):
1200
        """Take the user back to the code overview page."""
1201
        return canonical_url(self.context.product, rootsite="code")
1202
1203
7182.6.3 by Guilherme Salgado
Convert ProductSeriesReviewView into a LFV
1204
class ProductSeriesReviewView(LaunchpadEditFormView):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
1205
    """A view to review and change the series `IProduct` and name."""
7182.6.3 by Guilherme Salgado
Convert ProductSeriesReviewView into a LFV
1206
    schema = IProductSeries
1207
    field_names = ['product', 'name']
1208
    custom_widget('name', TextWidget, width=20)
1209
9081.2.5 by Curtis Hovey
Updated the product review page to UI 3.0.
1210
    @property
1211
    def label(self):
1212
        """The form label."""
9081.2.10 by Curtis Hovey
Fixed series page title that was broken in a previous commit.
1213
        return 'Administer %s %s series' % (
9081.2.5 by Curtis Hovey
Updated the product review page to UI 3.0.
1214
            self.context.product.displayname, self.context.name)
1215
1216
    @property
1217
    def page_title(self):
1218
        """The page title."""
1219
        return self.label
1220
1221
    @property
1222
    def cancel_url(self):
1223
        """See `LaunchpadFormView`."""
1224
        return canonical_url(self.context)
1225
7182.6.3 by Guilherme Salgado
Convert ProductSeriesReviewView into a LFV
1226
    @action(_('Change'), name='change')
1227
    def change_action(self, action, data):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
1228
        """Update the series."""
7182.6.3 by Guilherme Salgado
Convert ProductSeriesReviewView into a LFV
1229
        self.updateContextFromData(data)
3203.1.17 by Steve Alexander
Various improvements and cleanups from code review.
1230
        self.request.response.addInfoNotification(
1231
            _('This Series has been changed'))
7182.6.3 by Guilherme Salgado
Convert ProductSeriesReviewView into a LFV
1232
        self.next_url = canonical_url(self.context)
3203.1.3 by Mark Shuttleworth
Substantial cleanup of spec listings and spec targeting
1233
1234
11594.3.3 by Curtis Hovey
Removed duplicate an unused code.
1235
class ProductSeriesRdfView(BaseRdfView):
2005 by Canonical.com Patch Queue Manager
[r=spiv] HatcheryRDFPublishing
1236
    """A view that sets its mime-type to application/rdf+xml"""
2276 by Canonical.com Patch Queue Manager
r=jamesh Fix Bug 1603: RDF fails when a name has unicode chars
1237
1238
    template = ViewPageTemplateFile(
1239
        '../templates/productseries-rdf.pt')
1240
11594.3.3 by Curtis Hovey
Removed duplicate an unused code.
1241
    @property
1242
    def filename(self):
1243
        return '%s-%s' % (self.context.product.name, self.context.name)
2005 by Canonical.com Patch Queue Manager
[r=spiv] HatcheryRDFPublishing
1244
1245
4092.3.1 by Bjorn Tillenius
redirect a series' +filebug to it's product's one.
1246
class ProductSeriesFileBugRedirect(LaunchpadView):
1247
    """Redirect to the product's +filebug page."""
1248
1249
    def initialize(self):
8566.2.4 by Curtis Hovey
Fixed indentation to quiet lint. Added missing docstrings.
1250
        """See `LaunchpadFormView`."""
4092.3.1 by Bjorn Tillenius
redirect a series' +filebug to it's product's one.
1251
        filebug_url = "%s/+filebug" % canonical_url(self.context.product)
1252
        self.request.response.redirect(filebug_url)