← Back to team overview

launchpad-reviewers team mailing list archive

[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."""