launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #14770
[Merge] lp:~jcsackett/launchpad/no-private-releases into lp:launchpad
j.c.sackett has proposed merging lp:~jcsackett/launchpad/no-private-releases into lp:launchpad.
Commit message:
Blocks making releases for private products and adds warning about public release for embargoed ones.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1086002 in Launchpad itself: "private projects can create a release "
https://bugs.launchpad.net/launchpad/+bug/1086002
For more details, see:
https://code.launchpad.net/~jcsackett/launchpad/no-private-releases/+merge/139092
Summary
=======
Releases must be blocked for proprietary products as releases must be public.
For embargoed products, where releases may be part of unembargoing a project,
they are allowed but warn the user.
Preimp
======
Spoke with Deryck.
Implementation
==============
A check is made to forbid release creation on private products at the model
level, to prevent it happening over the API.
The AddRelease views gain a new method to check if releases are allowed; if
they are not, the template renders a message saying they are not rather than
rendering the form.
The AddRelease views, on initialize, check if the product is EMBARGOED. If so,
they add a warning notification explaining that the release will be public.
Tests
=====
bin/test -vvct NonPublicProductReleaseViewTestCase
QA
==
Attempt creating releases for EMBARGOED and PROPRIETARY products. On
EMBARGOED, you should get a warning, but be able to create the release. On
PROPRIETARY, you should not get the form.
LoC
===
Part of private projects.
Lint
====
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/registry/templates/productrelease-add.pt
lib/lp/registry/browser/productrelease.py
lib/lp/registry/tests/test_milestone.py
lib/lp/registry/templates/productrelease-add-from-series.pt
lib/lp/registry/browser/tests/test_productrelease.py
lib/lp/registry/model/milestone.py
--
https://code.launchpad.net/~jcsackett/launchpad/no-private-releases/+merge/139092
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jcsackett/launchpad/no-private-releases into lp:launchpad.
=== modified file 'lib/lp/registry/browser/productrelease.py'
--- lib/lp/registry/browser/productrelease.py 2012-11-19 23:09:58 +0000
+++ lib/lp/registry/browser/productrelease.py 2012-12-10 20:52:24 +0000
@@ -39,6 +39,7 @@
LaunchpadEditFormView,
LaunchpadFormView,
)
+from lp.app.enums import InformationType
from lp.app.widgets.date import DateTimeWidget
from lp.registry.browser import (
BaseRdfView,
@@ -135,6 +136,11 @@
notify(ObjectCreatedEvent(newrelease))
@property
+ def releases_allowed(self):
+ return (self.context.product.information_type !=
+ InformationType.PROPRIETARY)
+
+ @property
def label(self):
"""The form label."""
return smartquote('Create a new release for %s' %
@@ -160,6 +166,11 @@
]
def initialize(self):
+ if (self.context.product.information_type ==
+ InformationType.EMBARGOED):
+ self.request.response.addWarningNotification(
+ _("Any releases added for %s will be PUBLIC." %
+ self.context.product.displayname))
if self.context.product_release is not None:
self.request.response.addErrorNotification(
_("A project release already exists for this milestone."))
@@ -191,6 +202,14 @@
'changelog',
]
+ def initialize(self):
+ if (self.context.product.information_type ==
+ InformationType.EMBARGOED):
+ self.request.response.addWarningNotification(
+ _("Any releases added for %s will be PUBLIC." %
+ self.context.displayname))
+ super(ProductReleaseFromSeriesAddView, self).initialize()
+
def setUpFields(self):
super(ProductReleaseFromSeriesAddView, self).setUpFields()
self._prependKeepMilestoneActiveField()
=== modified file 'lib/lp/registry/browser/tests/test_productrelease.py'
--- lib/lp/registry/browser/tests/test_productrelease.py 2012-12-10 13:43:47 +0000
+++ lib/lp/registry/browser/tests/test_productrelease.py 2012-12-10 20:52:24 +0000
@@ -6,8 +6,10 @@
__metaclass__ = type
+from lp.app.enums import InformationType
from lp.services.webapp.escaping import html_escape
from lp.testing import (
+ BrowserTestCase,
person_logged_in,
TestCaseWithFactory,
)
@@ -15,6 +17,60 @@
from lp.testing.views import create_initialized_view
+class NonPublicProductReleaseViewTestCase(BrowserTestCase):
+
+ layer = LaunchpadFunctionalLayer
+
+ def test_proprietary_add_milestone(self):
+ owner = self.factory.makePerson()
+ product = self.factory.makeProduct(name='fnord',
+ owner=owner, information_type=InformationType.PROPRIETARY)
+ milestone = self.factory.makeMilestone(product=product)
+ with person_logged_in(owner):
+ browser = self.getViewBrowser(
+ milestone, view_name="+addrelease", user=owner)
+ msg = 'Fnord is PROPRIETARY. It cannot have any releases.'
+ self.assertTrue(html_escape(msg) in browser.contents)
+
+ def test_proprietary_add_series(self):
+ owner = self.factory.makePerson()
+ product = self.factory.makeProduct(name='fnord',
+ owner=owner, information_type=InformationType.PROPRIETARY)
+ series = self.factory.makeProductSeries(product=product, name='bnord')
+ with person_logged_in(owner):
+ browser = self.getViewBrowser(
+ series, view_name="+addrelease", user=owner)
+ msg = ('The bnord series of Fnord is PROPRIETARY.'
+ ' It cannot have any releases.')
+ self.assertTrue(html_escape(msg) in browser.contents)
+
+ def test_embargoed_add_milestone(self):
+ owner = self.factory.makePerson()
+ product = self.factory.makeProduct(name='fnord',
+ owner=owner, information_type=InformationType.EMBARGOED)
+ milestone = self.factory.makeMilestone(product=product)
+ with person_logged_in(owner):
+ view = create_initialized_view(milestone, name="+addrelease")
+ notifications = [
+ nm.message for nm in view.request.response.notifications]
+ self.assertEqual(
+ [html_escape("Any releases added for Fnord will be PUBLIC.")],
+ notifications)
+
+ def test_embargoed_add_series(self):
+ owner = self.factory.makePerson()
+ product = self.factory.makeProduct(name='fnord',
+ owner=owner, information_type=InformationType.EMBARGOED)
+ series = self.factory.makeProductSeries(product=product, name='bnord')
+ with person_logged_in(owner):
+ view = create_initialized_view(series, name="+addrelease")
+ notifications = [
+ nm.message for nm in view.request.response.notifications]
+ self.assertEqual(
+ [html_escape("Any releases added for bnord will be PUBLIC.")],
+ notifications)
+
+
class ProductReleaseAddDownloadFileViewTestCase(TestCaseWithFactory):
layer = LaunchpadFunctionalLayer
=== modified file 'lib/lp/registry/model/milestone.py'
--- lib/lp/registry/model/milestone.py 2012-12-06 13:33:46 +0000
+++ lib/lp/registry/model/milestone.py 2012-12-10 20:52:24 +0000
@@ -38,6 +38,7 @@
from zope.interface import implements
from lp.app.errors import NotFoundError
+from lp.app.enums import InformationType
from lp.blueprints.model.specification import (
Specification,
visible_specification_query,
@@ -55,6 +56,7 @@
from lp.bugs.model.structuralsubscription import (
StructuralSubscriptionTargetMixin,
)
+from lp.registry.errors import ProprietaryProduct
from lp.registry.interfaces.milestone import (
IHasMilestones,
IMilestone,
@@ -274,6 +276,10 @@
def createProductRelease(self, owner, datereleased,
changelog=None, release_notes=None):
"""See `IMilestone`."""
+ info_type = self.product.information_type
+ if info_type == InformationType.PROPRIETARY:
+ raise ProprietaryProduct(
+ "Proprietary products cannot have releases.")
if self.product_release is not None:
raise MultipleProductReleases()
release = ProductRelease(
=== modified file 'lib/lp/registry/templates/productrelease-add-from-series.pt'
--- lib/lp/registry/templates/productrelease-add-from-series.pt 2012-05-11 21:40:31 +0000
+++ lib/lp/registry/templates/productrelease-add-from-series.pt 2012-12-10 20:52:24 +0000
@@ -64,6 +64,7 @@
</metal:block>
<div metal:fill-slot="main">
+ <tal:releases-allowed condition="view/releases_allowed">
<div metal:use-macro="context/@@launchpad_form/form">
<div metal:fill-slot="extra_info" tal:condition="context/releases"
style="border-bottom: solid 1px black">
@@ -77,6 +78,12 @@
</ul>
</div>
</div>
+ </tal:releases-allowed>
+ <tal:releases-forbidden condition="not: view/releases_allowed">
+ The <tal:seriesname replace="context/displayname"
+ /> series of <tal:productname replace="context/product/displayname"
+ /> is PROPRIETARY. It cannot have any releases.
+ </tal:releases-forbidden>
</div>
</body>
=== modified file 'lib/lp/registry/templates/productrelease-add.pt'
--- lib/lp/registry/templates/productrelease-add.pt 2010-10-10 21:54:16 +0000
+++ lib/lp/registry/templates/productrelease-add.pt 2012-12-10 20:52:24 +0000
@@ -14,6 +14,7 @@
</metal:block>
<div metal:fill-slot="main">
+ <tal:releases-allowed condition="view/releases_allowed">
<div metal:use-macro="context/@@launchpad_form/form">
<div id="other-releases"
metal:fill-slot="extra_info"
@@ -29,6 +30,12 @@
</ul>
</div>
</div>
+ </tal:releases-allowed>
+ <tal:releases-forbidden condition="not: view/releases_allowed">
+ <tal:productname replace="context/product/displayname"
+ /> is PROPRIETARY. It cannot have any releases.
+ </tal:releases-forbidden>
+
</div>
</body>
=== modified file 'lib/lp/registry/tests/test_milestone.py'
--- lib/lp/registry/tests/test_milestone.py 2012-12-06 16:09:31 +0000
+++ lib/lp/registry/tests/test_milestone.py 2012-12-10 20:52:24 +0000
@@ -5,8 +5,8 @@
__metaclass__ = type
+import datetime
from operator import attrgetter
-import unittest
from storm.exceptions import NoneError
from zope.component import getUtility
@@ -29,6 +29,7 @@
IHasMilestones,
IMilestoneSet,
)
+from lp.registry.errors import ProprietaryProduct
from lp.registry.interfaces.product import IProductSet
from lp.testing import (
ANONYMOUS,
@@ -44,16 +45,18 @@
from lp.testing.matchers import DoesNotSnapshot
-class MilestoneTest(unittest.TestCase):
+class MilestoneTest(TestCaseWithFactory):
"""Milestone tests."""
layer = LaunchpadFunctionalLayer
def setUp(self):
+ super(MilestoneTest, self).setUp()
login(ANONYMOUS)
def tearDown(self):
logout()
+ super(MilestoneTest, self).tearDown()
def testMilestoneSetIterator(self):
"""Test of MilestoneSet.__iter__()."""
@@ -113,6 +116,15 @@
all_visible_milestones_ids,
[1, 2, 3])
+ def test_proprietary_product_milestones_cannot_have_releases(self):
+ owner = self.factory.makePerson()
+ product = self.factory.makeProduct(
+ owner=owner, information_type=InformationType.PROPRIETARY)
+ milestone = self.factory.makeMilestone(product=product)
+ with person_logged_in(owner):
+ self.assertRaises(ProprietaryProduct,
+ milestone.createProductRelease, owner, datetime.date.today())
+
class MilestoneSecurityAdaperTestCase(TestCaseWithFactory):
"""A TestCase for the security adapter of milestones."""