1
# Copyright 2009 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
11
'ProductBranchTarget',
14
from operator import attrgetter
16
from zope.component import getUtility
17
from zope.interface import implements
18
from zope.security.proxy import isinstance as zope_isinstance
20
from lp.code.errors import NoLinkedBranch
21
from lp.code.interfaces.branchcollection import IAllBranches
22
from lp.code.interfaces.branchtarget import (
23
check_default_stacked_on,
26
from lp.code.interfaces.codeimport import ICodeImportSet
27
from lp.code.interfaces.linkedbranch import get_linked_to_branch
28
from lp.registry.interfaces.pocket import PackagePublishingPocket
29
from lp.registry.interfaces.series import SeriesStatus
30
from lp.services.webapp.authorization import check_permission
31
from lp.services.webapp.interfaces import ICanonicalUrlData
32
from lp.services.webapp.sorting import sorted_version_numbers
35
def branch_to_target(branch):
36
"""Adapt an IBranch to an IBranchTarget."""
40
class _BaseBranchTarget:
42
def __eq__(self, other):
43
return self.context == other.context
45
def __ne__(self, other):
46
return self.context != other.context
48
def newCodeImport(self, registrant, branch_name, rcs_type, url=None,
49
cvs_root=None, cvs_module=None, owner=None):
50
"""See `IBranchTarget`."""
51
return getUtility(ICodeImportSet).new(
52
registrant, self, branch_name, rcs_type, url=url,
53
cvs_root=cvs_root, cvs_module=cvs_module, owner=owner)
55
def getRelatedSeriesBranchInfo(self, parent_branch, limit_results=None):
56
"""See `IBranchTarget`."""
59
def getRelatedPackageBranchInfo(self, parent_branch, limit_results=None):
60
"""See `IBranchTarget`."""
64
class PackageBranchTarget(_BaseBranchTarget):
65
implements(IBranchTarget)
67
def __init__(self, sourcepackage):
68
self.sourcepackage = sourcepackage
72
"""See `IBranchTarget`."""
73
return self.sourcepackage.path
77
"""See `IBranchTarget`."""
79
self.sourcepackage.distribution,
80
self.sourcepackage.distroseries,
86
"""See `IBranchTarget`."""
87
return self.sourcepackage
89
def getNamespace(self, owner):
90
"""See `IBranchTarget`."""
91
from lp.code.model.branchnamespace import (
93
return PackageNamespace(owner, self.sourcepackage)
97
"""See `IBranchTarget`."""
98
return getUtility(IAllBranches).inSourcePackage(self.sourcepackage)
101
def default_stacked_on_branch(self):
102
"""See `IBranchTarget`."""
103
return check_default_stacked_on(
104
self.sourcepackage.development_version.getBranch(
105
PackagePublishingPocket.RELEASE))
108
def default_merge_target(self):
109
"""See `IBranchTarget`."""
110
return self.sourcepackage.getBranch(PackagePublishingPocket.RELEASE)
113
def displayname(self):
114
"""See `IBranchTarget`."""
115
return self.sourcepackage.displayname
118
def supports_merge_proposals(self):
119
"""See `IBranchTarget`."""
123
def supports_short_identites(self):
124
"""See `IBranchTarget`."""
128
def supports_code_imports(self):
129
"""See `IBranchTarget`."""
132
def areBranchesMergeable(self, other_target):
133
"""See `IBranchTarget`."""
134
# Branches are mergable into a PackageTarget if the source package
135
# name is the same, or the branch is associated with the linked
137
if zope_isinstance(other_target, PackageBranchTarget):
138
my_sourcepackagename = self.context.sourcepackagename
139
other_sourcepackagename = other_target.context.sourcepackagename
140
return my_sourcepackagename == other_sourcepackagename
141
elif zope_isinstance(other_target, ProductBranchTarget):
142
# If the sourcepackage has a related product, then branches of
143
# that product are mergeable.
144
product_series = self.sourcepackage.productseries
145
if product_series is None:
148
return other_target.context == product_series.product
152
def assignKarma(self, person, action_name, date_created=None):
153
"""See `IBranchTarget`."""
154
return person.assignKarma(
156
distribution=self.context.distribution,
157
sourcepackagename=self.context.sourcepackagename,
158
datecreated=date_created)
160
def getBugTask(self, bug):
161
"""See `IBranchTarget`."""
162
# XXX: rockstar - See bug 397251. Basically, source packages may have
163
# specific bug tasks. This should return those specific bugtasks in
165
return bug.default_bugtask
167
def _retargetBranch(self, branch):
168
"""Set the branch target to refer to this target.
170
This only updates the target related attributes of the branch, and
171
expects a branch without a security proxy as a parameter.
173
branch.product = None
174
branch.distroseries = self.sourcepackage.distroseries
175
branch.sourcepackagename = self.sourcepackage.sourcepackagename
177
def getRelatedPackageBranchInfo(self, parent_branch, limit_results=None):
178
"""See `IBranchTarget`."""
180
linked_branches = self.sourcepackage.linkedBranches()
181
distroseries = self.sourcepackage.distroseries
183
[(branch, distroseries)
184
for branch in linked_branches.values()
185
if (check_permission('launchpad.View', branch) and
186
branch != parent_branch and
187
distroseries.status != SeriesStatus.OBSOLETE)])
189
result = sorted_version_numbers(result, key=lambda branch_info: (
190
getattr(branch_info[1], 'name')))
192
if limit_results is not None:
193
# We only want the most recent branches
194
result = result[:limit_results]
198
class PersonBranchTarget(_BaseBranchTarget):
199
implements(IBranchTarget)
202
default_stacked_on_branch = None
203
default_merge_target = None
205
def __init__(self, person):
209
def components(self):
210
"""See `IBranchTarget`."""
215
"""See `IBranchTarget`."""
219
def displayname(self):
220
"""See `IBranchTarget`."""
221
return "~%s/+junk" % self.person.name
223
def getNamespace(self, owner):
224
"""See `IBranchTarget`."""
225
from lp.code.model.branchnamespace import (
227
return PersonalNamespace(owner)
230
def collection(self):
231
"""See `IBranchTarget`."""
232
return getUtility(IAllBranches).ownedBy(self.person).isJunk()
235
def supports_merge_proposals(self):
236
"""See `IBranchTarget`."""
240
def supports_short_identites(self):
241
"""See `IBranchTarget`."""
245
def supports_code_imports(self):
246
"""See `IBranchTarget`."""
249
def areBranchesMergeable(self, other_target):
250
"""See `IBranchTarget`."""
253
def assignKarma(self, person, action_name, date_created=None):
254
"""See `IBranchTarget`."""
255
# Does nothing. No karma for +junk.
258
def getBugTask(self, bug):
259
"""See `IBranchTarget`."""
260
return bug.default_bugtask
262
def _retargetBranch(self, branch):
263
"""Set the branch target to refer to this target.
265
This only updates the target related attributes of the branch, and
266
expects a branch without a security proxy as a parameter.
268
branch.product = None
269
branch.distroseries = None
270
branch.sourcepackagename = None
273
class ProductBranchTarget(_BaseBranchTarget):
274
implements(IBranchTarget)
276
def __init__(self, product):
277
self.product = product
280
def components(self):
281
"""See `IBranchTarget`."""
282
return [self.product]
286
"""See `IBranchTarget`."""
290
def displayname(self):
291
"""See `IBranchTarget`."""
292
return self.product.displayname
296
"""See `IBranchTarget`."""
297
return self.product.name
300
def default_stacked_on_branch(self):
301
"""See `IBranchTarget`."""
302
return check_default_stacked_on(self.product.development_focus.branch)
305
def default_merge_target(self):
306
"""See `IBranchTarget`."""
307
return self.product.development_focus.branch
309
def getNamespace(self, owner):
310
"""See `IBranchTarget`."""
311
from lp.code.model.branchnamespace import (
313
return ProductNamespace(owner, self.product)
316
def collection(self):
317
"""See `IBranchTarget`."""
318
return getUtility(IAllBranches).inProduct(self.product)
321
def supports_merge_proposals(self):
322
"""See `IBranchTarget`."""
326
def supports_short_identites(self):
327
"""See `IBranchTarget`."""
331
def supports_code_imports(self):
332
"""See `IBranchTarget`."""
335
def areBranchesMergeable(self, other_target):
336
"""See `IBranchTarget`."""
337
# Branches are mergable into a PackageTarget if the source package
338
# name is the same, or the branch is associated with the linked
340
if zope_isinstance(other_target, ProductBranchTarget):
341
return self.product == other_target.context
342
elif zope_isinstance(other_target, PackageBranchTarget):
343
# If the sourcepackage has a related product, and that product is
344
# the same as ours, then the branches are mergeable.
345
product_series = other_target.context.productseries
346
if product_series is None:
349
return self.product == product_series.product
353
def assignKarma(self, person, action_name, date_created=None):
354
"""See `IBranchTarget`."""
355
return person.assignKarma(
356
action_name, product=self.product, datecreated=date_created)
358
def getBugTask(self, bug):
359
"""See `IBranchTarget`."""
360
task = bug.getBugTask(self.product)
362
# Just choose the first task for the bug.
363
task = bug.bugtasks[0]
366
def _retargetBranch(self, branch):
367
"""Set the branch target to refer to this target.
369
This only updates the target related attributes of the branch, and
370
expects a branch without a security proxy as a parameter.
372
branch.product = self.product
373
branch.distroseries = None
374
branch.sourcepackagename = None
376
def getRelatedSeriesBranchInfo(self, parent_branch, limit_results=None):
377
"""See `IBranchTarget`."""
379
for series in self.product.series:
380
if (series.status != SeriesStatus.OBSOLETE
381
and series != self.product.development_focus):
382
sorted_series.append(series)
383
# Now sort the list by name with newer versions before older.
384
sorted_series = sorted_version_numbers(sorted_series,
385
key=attrgetter('name'))
386
# Add the development focus first.
387
sorted_series.insert(0, self.product.development_focus)
390
for series in sorted_series:
392
branch = get_linked_to_branch(series).branch
393
if (branch not in result and branch != parent_branch and
394
check_permission('launchpad.View', branch)):
395
result.append((branch, series))
396
except NoLinkedBranch:
397
# If there's no branch for a particular series, we don't care.
400
if limit_results is not None:
401
# We only want the most recent branches
402
result = result[:limit_results]
405
def getRelatedPackageBranchInfo(self, parent_branch, limit_results=None):
406
"""See `IBranchTarget`."""
408
for distrosourcepackage in self.product.distrosourcepackages:
410
branch = get_linked_to_branch(distrosourcepackage).branch
411
series = distrosourcepackage.distribution.currentseries
412
if (branch != parent_branch and series is not None and
413
check_permission('launchpad.View', branch)):
414
result.append((branch, series))
415
except NoLinkedBranch:
416
# If there's no branch for a particular source package,
420
result = sorted_version_numbers(result, key=lambda branch_info: (
421
getattr(branch_info[1], 'name')))
423
if limit_results is not None:
424
# We only want the most recent branches
425
result = result[:limit_results]
429
def get_canonical_url_data_for_target(branch_target):
430
"""Return the `ICanonicalUrlData` for an `IBranchTarget`."""
431
return ICanonicalUrlData(branch_target.context)
434
def product_series_to_branch_target(product_series):
435
"""The Product itself is the branch target given a ProductSeries."""
436
return ProductBranchTarget(product_series.product)
439
def distribution_sourcepackage_to_branch_target(distro_sourcepackage):
440
"""The development version of the distro sourcepackage is the target."""
441
dev_version = distro_sourcepackage.development_version
442
# It is possible for distributions to not have any series, and if that is
443
# the case, the dev_version is None.
444
if dev_version is None:
446
return PackageBranchTarget(dev_version)