← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wallyworld/launchpad/commercial-expired-job-1047026 into lp:launchpad

 

Ian Booth has proposed merging lp:~wallyworld/launchpad/commercial-expired-job-1047026 into lp:launchpad.

Commit message:
When a commercial subscription expires, set the bug and branch sharing policies to forbidden, disallowing the creation of new bugs and branches.

Requested reviews:
  William Grant (wgrant)
Related bugs:
  Bug #1047026 in Launchpad itself: "Commercial subscription expiration does not reconfigure sharing polcies"
  https://bugs.launchpad.net/launchpad/+bug/1047026

For more details, see:
https://code.launchpad.net/~wallyworld/launchpad/commercial-expired-job-1047026/+merge/125384

== Implementation ==

Update the job's _deactivateCommercialFeatures() method to set bug and branch sharing policies to FORBIDDEN, a new enum introduced in this branch.

When a product has forbidden policies, then creating new bugs / branches is not allowed. This is reflected in the following ways:

- the branch privacy portlet on the listing page already displayed text when creating new branches is forbidden; extra text was added with a link to the sharing page to easily allow project maintainers to update the sharing policy to allow new branches.

- the file bug pages for products and project groups have been reworked to display a message that new bugs are not allowed and a link to the sharing page
- for project groups, the individual product text has been changed where appropriate

An existing issue was addressed also where the allowed information types for a bug needed to include the current information type even if not in the allowed list. This allows the info type popup to work properly.

== Tests ==

Update the test_deactivateCommercialFeatures_open_source test.
Add or modify tests for the branch and filebug functionality. This incuded unit tests and updating doctests.

== Lint ==

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/bugs/browser/bugtarget.py
  lib/lp/bugs/browser/tests/test_bugtarget_filebug.py
  lib/lp/bugs/interfaces/bugtarget.py
  lib/lp/bugs/model/bug.py
  lib/lp/bugs/model/tests/test_bug.py
  lib/lp/bugs/stories/guided-filebug/xx-project-guided-filebug.txt
  lib/lp/bugs/templates/bugtarget-filebug-search.pt
  lib/lp/bugs/templates/bugtarget-macros-filebug.pt
  lib/lp/code/model/branchnamespace.py
  lib/lp/code/model/tests/test_branchnamespace.py
  lib/lp/code/templates/product-branches.pt
  lib/lp/registry/enums.py
  lib/lp/registry/model/productjob.py
  lib/lp/registry/tests/test_productjob.py

-- 
https://code.launchpad.net/~wallyworld/launchpad/commercial-expired-job-1047026/+merge/125384
Your team Launchpad code reviewers is subscribed to branch lp:launchpad.
=== modified file 'lib/lp/bugs/browser/bugtarget.py'
--- lib/lp/bugs/browser/bugtarget.py	2012-09-14 00:59:44 +0000
+++ lib/lp/bugs/browser/bugtarget.py	2012-09-20 01:01:27 +0000
@@ -482,6 +482,10 @@
         bug_tracking_usage = self.getMainContext().bug_tracking_usage
         return bug_tracking_usage == ServiceUsage.LAUNCHPAD
 
+    def contextAllowsNewBugs(self):
+        return (self.contextUsesMalone() and
+                self.getMainContext().getAllowedBugInformationTypes())
+
     def shouldSelectPackageName(self):
         """Should the radio button to select a package be selected?"""
         return (
@@ -1062,6 +1066,10 @@
     def contextUsesMalone(self):
         return self.default_product is not None
 
+    def contextAllowsNewBugs(self):
+        product = self.default_product
+        return product is not None and product.getAllowedBugInformationTypes()
+
     def contextIsProduct(self):
         return False
 

=== modified file 'lib/lp/bugs/browser/tests/test_bugtarget_filebug.py'
--- lib/lp/bugs/browser/tests/test_bugtarget_filebug.py	2012-09-13 16:36:54 +0000
+++ lib/lp/bugs/browser/tests/test_bugtarget_filebug.py	2012-09-20 01:01:27 +0000
@@ -27,7 +27,6 @@
 from lp.registry.enums import (
     BugSharingPolicy,
     InformationType,
-    PRIVATE_INFORMATION_TYPES,
     PUBLIC_INFORMATION_TYPES,
     )
 from lp.registry.interfaces.projectgroup import IProjectGroup
@@ -478,6 +477,19 @@
         self.assertIsNotNone(
             soup.find('input', attrs={'name': 'field.information_type'}))
 
+    def test_filebug_when_no_bugs_allowed(self):
+        # Attempting to file a bug against a project with sharing policy
+        # forbidden results in a message saying it's not allowed.
+        product = self.factory.makeProduct(
+            official_malone=True,
+            bug_sharing_policy=BugSharingPolicy.FORBIDDEN)
+        with person_logged_in(product.owner):
+            view = create_initialized_view(
+                product, '+filebug', principal=product.owner)
+            html = view.render()
+        self.assertIn("You can't create new bugs for", html)
+        self.assertIn("Sharing policies may be changed", html)
+
 
 class TestFileBugForNonBugSupervisors(TestCaseWithFactory):
 

=== modified file 'lib/lp/bugs/interfaces/bugtarget.py'
--- lib/lp/bugs/interfaces/bugtarget.py	2012-08-31 02:24:18 +0000
+++ lib/lp/bugs/interfaces/bugtarget.py	2012-09-20 01:01:27 +0000
@@ -210,6 +210,7 @@
     BugSharingPolicy.PUBLIC_OR_PROPRIETARY: NON_EMBARGOED_INFORMATION_TYPES,
     BugSharingPolicy.PROPRIETARY_OR_PUBLIC: NON_EMBARGOED_INFORMATION_TYPES,
     BugSharingPolicy.PROPRIETARY: (InformationType.PROPRIETARY,),
+    BugSharingPolicy.FORBIDDEN: [],
     }
 
 BUG_POLICY_DEFAULT_TYPES = {
@@ -217,6 +218,7 @@
     BugSharingPolicy.PUBLIC_OR_PROPRIETARY: InformationType.PUBLIC,
     BugSharingPolicy.PROPRIETARY_OR_PUBLIC: InformationType.PROPRIETARY,
     BugSharingPolicy.PROPRIETARY: InformationType.PROPRIETARY,
+    BugSharingPolicy.FORBIDDEN: None,
     }
 
 

=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py	2012-09-18 21:56:29 +0000
+++ lib/lp/bugs/model/bug.py	2012-09-20 01:01:27 +0000
@@ -1706,6 +1706,7 @@
         for pillar in self.affected_pillars:
             types.intersection_update(
                 set(pillar.getAllowedBugInformationTypes()))
+        types.add(self.information_type)
         return types
 
     def transitionToInformationType(self, information_type, who):

=== modified file 'lib/lp/bugs/model/tests/test_bug.py'
--- lib/lp/bugs/model/tests/test_bug.py	2012-09-18 16:07:51 +0000
+++ lib/lp/bugs/model/tests/test_bug.py	2012-09-20 01:01:27 +0000
@@ -886,6 +886,18 @@
              InformationType.PRIVATESECURITY, InformationType.USERDATA],
             self.factory.makeBug().getAllowedInformationTypes(None))
 
+    def test_getAllowedInformationTypesIncludesCurrent(self):
+        # A bug's allowed information types must include its current
+        # information type even if said type is not in the allowed types.
+        product = self.factory.makeProduct()
+        bug = self.factory.makeBug(
+            target=product, information_type=InformationType.PUBLICSECURITY)
+        removeSecurityProxy(product).bug_sharing_policy = (
+            BugSharingPolicy.FORBIDDEN)
+        self.assertContentEqual(
+            [InformationType.PUBLICSECURITY],
+            bug.getAllowedInformationTypes(None))
+
     def test_transitionToInformationType_respects_allowed_proprietary(self):
         # transitionToInformationType rejects types that aren't allowed
         # for the bug.

=== modified file 'lib/lp/bugs/stories/guided-filebug/xx-project-guided-filebug.txt'
--- lib/lp/bugs/stories/guided-filebug/xx-project-guided-filebug.txt	2012-09-07 21:00:46 +0000
+++ lib/lp/bugs/stories/guided-filebug/xx-project-guided-filebug.txt	2012-09-20 01:01:27 +0000
@@ -73,8 +73,8 @@
     >>> for message in find_tags_by_class(user_browser.contents,
     ...     'informational message'):
     ...     print message.renderContents()
-    There are no projects registered for Test Group that use Launchpad to
-    track bugs.
+    There are no projects registered for Test Group that either use Launchpad
+    to track bugs or allow new bugs to be filed.
 
 If the project maintainer visits the filebug page they will see an
 additional message:
@@ -121,8 +121,8 @@
     >>> for message in find_tags_by_class(user_browser.contents,
     ...     'informational message'):
     ...     print message.renderContents()
-    There are no projects registered for Test Group that use Launchpad to
-    track bugs.
+    There are no projects registered for Test Group that either use Launchpad
+    to track bugs or allow new bugs to be filed.
 
 Testy Product doesn't use define an external bug tracker and isn't
 registered in any source packages and the user is told this.
@@ -130,7 +130,7 @@
     >>> print extract_text(find_tag_by_id(user_browser.contents,
     ...     'product-list-summary'))
     There is 1 project registered as part of Test Group but it does not use
-    Launchpad as its bug tracker.
+    Launchpad as its bug tracker or does not allow new bugs to be filed.
 
     >>> for tag in find_tags_by_class(
     ...     user_browser.contents, 'product-bug-options'):
@@ -147,10 +147,42 @@
     >>> user_browser.getLink('Testy Product').url
     'http://launchpad.dev/testy'
 
+If Testy Product has a bug sharing policy of forbidden, then it is not possible
+to file new bugs and this will be reflected on Test Group's filebug page.
+
+    >>> from lp.registry.interfaces.product import IProductSet
+    >>> from zope.component import getUtility
+    >>> from zope.security.proxy import removeSecurityProxy
+    >>> from lp.registry.enums import BugSharingPolicy
+    >>> login('admin@xxxxxxxxxxxxx')
+    >>> testy = getUtility(IProductSet).getByName('testy')
+    >>> removeSecurityProxy(testy).bug_sharing_policy = (
+    ...     BugSharingPolicy.FORBIDDEN)
+    >>> logout()
+
+    >>> user_browser.reload()
+    >>> print extract_text(find_tag_by_id(user_browser.contents,
+    ...     'product-list-summary'))
+    There is 1 project registered as part of Test Group but it does not use
+    Launchpad as its bug tracker or does not allow new bugs to be filed.
+
+    >>> for tag in find_tags_by_class(
+    ...     user_browser.contents, 'product-bug-options'):
+    ...     print extract_text(tag)
+    Testy Product
+    You can't create new bugs for Testy Product.
+
+Reset the bug sharing policy back to public.
+
+    >>> login('admin@xxxxxxxxxxxxx')
+    >>> testy = getUtility(IProductSet).getByName('testy')
+    >>> removeSecurityProxy(testy).bug_sharing_policy = (
+    ...     BugSharingPolicy.PUBLIC)
+    >>> logout()
+
 If we set up an external bug tracker for Testy Product, this will be
 reflected in the Test Group's filebug page.
 
-    >>> from zope.component import getUtility
     >>> from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 
     >>> login('admin@xxxxxxxxxxxxx')
@@ -173,7 +205,7 @@
     >>> print extract_text(find_tag_by_id(user_browser.contents,
     ...     'product-list-summary'))
     There is 1 project registered as part of Test Group but it does not use
-    Launchpad as its bug tracker.
+    Launchpad as its bug tracker or does not allow new bugs to be filed.
 
     >>> for tag in find_tags_by_class(
     ...     user_browser.contents, 'product-bug-options'):
@@ -196,7 +228,7 @@
     >>> print extract_text(find_tag_by_id(user_browser.contents,
     ...     'product-list-summary'))
     There is 1 project registered as part of Test Group but it does not use
-    Launchpad as its bug tracker.
+    Launchpad as its bug tracker or does not allow new bugs to be filed.
 
     >>> for tag in find_tags_by_class(
     ...     user_browser.contents, 'product-bug-options'):

=== modified file 'lib/lp/bugs/templates/bugtarget-filebug-search.pt'
--- lib/lp/bugs/templates/bugtarget-filebug-search.pt	2012-08-02 07:02:18 +0000
+++ lib/lp/bugs/templates/bugtarget-filebug-search.pt	2012-09-20 01:01:27 +0000
@@ -32,6 +32,16 @@
       </tal:does-not-use-malone>
 
       <tal:uses-malone tal:condition="view/contextUsesMalone">
+      <tal:no-new-bugs tal:condition="not: view/contextAllowsNewBugs">
+          You can't create new bugs for
+          <tal:name replace="context/displayname"/>.
+          <tal:sharing-link condition="context/required:launchpad.Edit">
+          <br>Sharing policies may be changed on the
+          <a tal:attributes="href string:${context/fmt:url:mainsite}/+sharing">sharing page</a>.
+          </tal:sharing-link>
+      </tal:no-new-bugs>
+
+      <tal:new-bugs tal:condition="view/contextAllowsNewBugs">
         <div class="informational message"
             tal:condition="view/extra_data_to_process"
           >Please wait while bug data is processed. This page will
@@ -139,6 +149,7 @@
           <a id="duplicate-search-url"
               tal:attributes="href view/duplicate_search_url"></a>
         </p>
+      </tal:new-bugs>
       </tal:uses-malone>
     </div>
 

=== modified file 'lib/lp/bugs/templates/bugtarget-macros-filebug.pt'
--- lib/lp/bugs/templates/bugtarget-macros-filebug.pt	2012-08-01 06:59:20 +0000
+++ lib/lp/bugs/templates/bugtarget-macros-filebug.pt	2012-09-20 01:01:27 +0000
@@ -240,7 +240,7 @@
          condition="not: context/required:launchpad.Edit">
         There are no projects registered for <span
         tal:replace="context/displayname">project displayname</span>
-        that use Launchpad to track bugs.
+        that either use Launchpad to track bugs or allow new bugs to be filed.
       </p>
       <tal:not-has-products tal:condition="not: context/products">
         <tal:admin-warning condition="context/required:launchpad.Edit">
@@ -261,12 +261,15 @@
           <p id="product-list-summary">
             There are <tal:count replace="context/products/count" /> projects
             registered as part of <tal:project replace="context/displayname" />
-            but none of them use Launchpad as their bug tracker.
+            but either none of them use Launchpad as their bug tracker or none
+            allow new bugs to be filed.
           </p>
           <p>
             However, it may be possible for you to file a bug against a
             specific project in a source package with which it is registered
-            or in an external bug tracker.
+            or in an external bug tracker. Project maintainers may also
+            update the project's bug sharing policy to allow new bugs to be
+            filed.
           </p>
           <p>
             The projects that are part of
@@ -282,7 +285,8 @@
           <p id="product-list-summary">
             There is 1 project registered as part of
             <tal:project replace="context/displayname" /> but it
-            does not use Launchpad as its bug tracker.
+            does not use Launchpad as its bug tracker or does not allow new
+            bugs to be filed.
           </p>
           <p>
             The details of the project are shown below, along with any
@@ -291,12 +295,26 @@
             appropriate place to file your bug, please contact the
             administrator of <tal:project
             replace="context/displayname">Project</tal:project>.
+            The project maintainer may also update the project's bug sharing
+            policy to allow new bugs to be filed.
           </p>
         </tal:singular>
         <ul class="product-bug-options" tal:repeat="product context/products">
           <li condition="product/bug_tracking_usage/enumvalue:LAUNCHPAD">
             <tal:link replace="structure product/fmt:link" />
             <ul class="bulleted">
+              <tal:new-bugs-forbidden condition="not: product/getAllowedBugInformationTypes">
+                <li>
+                  You can't create new bugs for
+                  <tal:name replace="product/displayname"/>.
+                  <tal:sharing-link condition="product/required:launchpad.Edit">
+                  <br>Sharing policies may be changed on the
+                  <a tal:attributes="href string:${product/fmt:url:mainsite}/+sharing"
+                      >sharing page</a>.
+                  </tal:sharing-link>
+                </li>
+              </tal:new-bugs-forbidden>
+              <tal:new-bugs-allowed condition="product/getAllowedBugInformationTypes">
               <tal:external-tracker
                   define="bugtracker product/getExternalBugTracker">
                 <li tal:condition="bugtracker">
@@ -336,6 +354,7 @@
                   them for us.</a>
                 </li>
               </tal:packages>
+              </tal:new-bugs-allowed>
             </ul>
           </li>
         </ul>

=== modified file 'lib/lp/code/model/branchnamespace.py'
--- lib/lp/code/model/branchnamespace.py	2012-09-06 00:01:38 +0000
+++ lib/lp/code/model/branchnamespace.py	2012-09-20 01:01:27 +0000
@@ -93,6 +93,7 @@
     BranchSharingPolicy.PROPRIETARY: [InformationType.PROPRIETARY],
     BranchSharingPolicy.EMBARGOED_OR_PROPRIETARY:
         [InformationType.PROPRIETARY, InformationType.EMBARGOED],
+    BranchSharingPolicy.FORBIDDEN: [],
     }
 
 BRANCH_POLICY_DEFAULT_TYPES = {
@@ -101,6 +102,7 @@
     BranchSharingPolicy.PROPRIETARY_OR_PUBLIC: InformationType.PROPRIETARY,
     BranchSharingPolicy.PROPRIETARY: InformationType.PROPRIETARY,
     BranchSharingPolicy.EMBARGOED_OR_PROPRIETARY: InformationType.EMBARGOED,
+    BranchSharingPolicy.FORBIDDEN: None,
     }
 
 BRANCH_POLICY_REQUIRED_GRANTS = {
@@ -109,6 +111,7 @@
     BranchSharingPolicy.PROPRIETARY_OR_PUBLIC: InformationType.PROPRIETARY,
     BranchSharingPolicy.PROPRIETARY: InformationType.PROPRIETARY,
     BranchSharingPolicy.EMBARGOED_OR_PROPRIETARY: InformationType.PROPRIETARY,
+    BranchSharingPolicy.FORBIDDEN: None,
     }
 
 
@@ -143,6 +146,9 @@
             sourcepackagename = sourcepackage.sourcepackagename
 
         information_type = self.getDefaultInformationType()
+        if information_type is None:
+            raise ValueError(
+                'Branches cannot be created without an information type')
 
         branch = Branch(
             registrant=registrant, name=name, owner=self.owner,

=== modified file 'lib/lp/code/model/tests/test_branchnamespace.py'
--- lib/lp/code/model/tests/test_branchnamespace.py	2012-09-06 00:01:38 +0000
+++ lib/lp/code/model/tests/test_branchnamespace.py	2012-09-20 01:01:27 +0000
@@ -473,6 +473,12 @@
         self.assertEqual(
             InformationType.PUBLIC, namespace.getDefaultInformationType())
 
+    def test_forbidden_anyone(self):
+        namespace = self.makeProductNamespace(
+            BranchSharingPolicy.FORBIDDEN)
+        self.assertContentEqual([], namespace.getAllowedInformationTypes())
+        self.assertEqual(None, namespace.getDefaultInformationType())
+
     def test_public_or_proprietary_anyone(self):
         namespace = self.makeProductNamespace(
             BranchSharingPolicy.PUBLIC_OR_PROPRIETARY)

=== modified file 'lib/lp/code/templates/product-branches.pt'
--- lib/lp/code/templates/product-branches.pt	2012-07-12 04:51:38 +0000
+++ lib/lp/code/templates/product-branches.pt	2012-09-20 01:01:27 +0000
@@ -25,6 +25,10 @@
            id="privacy-text">
           You can't create new branches for
           <tal:name replace="context/displayname"/>.
+          <tal:sharing-link condition="context/required:launchpad.Edit">
+          <br>Sharing policies may be changed on the
+          <a tal:attributes="href string:${context/fmt:url:mainsite}/+sharing">sharing page</a>.
+          </tal:sharing-link>
         </span>
 
         <span tal:condition="view/default_information_type"

=== modified file 'lib/lp/registry/enums.py'
--- lib/lp/registry/enums.py	2012-09-16 12:49:11 +0000
+++ lib/lp/registry/enums.py	2012-09-20 01:01:27 +0000
@@ -120,11 +120,13 @@
             'description': term.description,
             'name': term.title,
             'order': order.index(term.name),
-            'is_private': (term not in PUBLIC_INFORMATION_TYPES), 'description_css_class': 'choice-description',
+            'is_private': (term not in PUBLIC_INFORMATION_TYPES),
+            'description_css_class': 'choice-description',
         }
 
     cache.objects['information_type_data'] = dump
 
+
 class SharingPermission(DBEnumeratedType):
     """Sharing permission.
 
@@ -188,6 +190,13 @@
         new branches.
         """)
 
+    FORBIDDEN = DBItem(6, """
+        New branches are forbidden
+
+        No new branches may be created but existing branches may still be
+        updated.
+        """)
+
 
 class BugSharingPolicy(DBEnumeratedType):
 
@@ -216,6 +225,12 @@
         Bugs are always proprietary.
         """)
 
+    FORBIDDEN = DBItem(5, """
+        New bugs are forbidden
+
+        No new bugs may be filed but existing bugs may still be updated.
+        """)
+
 
 class SpecificationSharingPolicy(DBEnumeratedType):
 

=== modified file 'lib/lp/registry/model/productjob.py'
--- lib/lp/registry/model/productjob.py	2012-06-18 20:33:55 +0000
+++ lib/lp/registry/model/productjob.py	2012-09-20 01:01:27 +0000
@@ -38,7 +38,11 @@
 from zope.security.proxy import removeSecurityProxy
 
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
-from lp.registry.enums import ProductJobType
+from lp.registry.enums import (
+    BranchSharingPolicy,
+    BugSharingPolicy,
+    ProductJobType,
+    )
 from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.product import (
     IProduct,
@@ -468,7 +472,10 @@
         if self._is_proprietary:
             self.product.active = False
         else:
-            removeSecurityProxy(self.product).private_bugs = False
+            naked_product = removeSecurityProxy(self.product)
+            naked_product.private_bugs = False
+            naked_product.setBranchSharingPolicy(BranchSharingPolicy.FORBIDDEN)
+            naked_product.setBugSharingPolicy(BugSharingPolicy.FORBIDDEN)
             for series in self.product.series:
                 if series.branch is not None and series.branch.private:
                     removeSecurityProxy(series).branch = None

=== modified file 'lib/lp/registry/tests/test_productjob.py'
--- lib/lp/registry/tests/test_productjob.py	2012-08-13 19:34:10 +0000
+++ lib/lp/registry/tests/test_productjob.py	2012-09-20 01:01:27 +0000
@@ -23,6 +23,8 @@
 
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.registry.enums import (
+    BugSharingPolicy,
+    BranchSharingPolicy,
     InformationType,
     ProductJobType,
     )
@@ -723,6 +725,10 @@
         clear_property_cache(product)
         self.assertIs(True, product.active)
         self.assertIs(False, product.private_bugs)
+        self.assertEqual(
+            BranchSharingPolicy.FORBIDDEN, product.branch_sharing_policy)
+        self.assertEqual(
+            BugSharingPolicy.FORBIDDEN, product.bug_sharing_policy)
         self.assertEqual(public_branch, public_series.branch)
         self.assertIs(None, private_series.branch)
         self.assertIs(None, product.commercial_subscription)


Follow ups