Merge lp:~jcsackett/launchpad/filter-milestone-vocabulary-for-projectgroups into lp:launchpad

Proposed by j.c.sackett
Status: Merged
Approved by: j.c.sackett
Approved revision: no longer in the source branch.
Merged at revision: 16285
Proposed branch: lp:~jcsackett/launchpad/filter-milestone-vocabulary-for-projectgroups
Merge into: lp:launchpad
Diff against target: 83 lines (+35/-5)
2 files modified
lib/lp/registry/model/projectgroup.py (+6/-2)
lib/lp/registry/tests/test_milestone_vocabularies.py (+29/-3)
To merge this branch: bzr merge lp:~jcsackett/launchpad/filter-milestone-vocabulary-for-projectgroups
Reviewer Review Type Date Requested Status
Abel Deuring (community) code Approve
Review via email: mp+134667@code.launchpad.net

Commit message

Updates project group milestone filtering to not display milestones from associated private products if the user shouldn't see them.

Description of the change

Summary
=======
As it currently stands, when milestones are fetched for a projectgroup, the
milestones from all associated products are fetched, regardless of whether or
not they belong to a private product the current user cannot see.

Projectgroup must be updated to not show milestones the user can't see, using
our existing filtering clause.

Preimp
======
None.

Implementation
==============
The method `ProjectGroup`.`_getMilestoneCondition`, which creates the query to
fetch milestones, is updated to pull the current user from the launchbag and
create the privacy filtering clause from that. It then adds that clause to the
existing query.

Tests have also been updated for this case.

Tests
=====
bin/test -vvct TestMilestoneVocabulary

QA
==
Ensure the milestones listed for a project does not include anything from
associated private products.

LoC
===
Part of private projects.

Lint
====

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/registry/model/projectgroup.py
  lib/lp/registry/tests/test_milestone_vocabularies.py

To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :

Looks good.

But I am wondering if we need to change the expression returned by ProductSet.getProductPrivacyFilter():

Users may have an artifact grant for a bug/branch/blueprint of a private product but not a policy grant for the product itself; I suspect that we should add a clause like APGF.artifact == None to address this case.

But this affects of course other callsites of getProductPrivacyFilter(), so we should probably talk about it in our standup call on Monday.

review: Approve (code)
Revision history for this message
j.c.sackett (jcsackett) wrote :

> Users may have an artifact grant for a bug/branch/blueprint of a private
> product but not a policy grant for the product itself; I suspect that we
> should add a clause like APGF.artifact == None to address this case.
>
> But this affects of course other callsites of getProductPrivacyFilter(), so we
> should probably talk about it in our standup call on Monday.

I agree, especially after dicsussion on IRC. Let's bring this up in the next standup.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/registry/model/projectgroup.py'
--- lib/lp/registry/model/projectgroup.py 2012-10-19 03:01:32 +0000
+++ lib/lp/registry/model/projectgroup.py 2012-11-16 20:43:23 +0000
@@ -407,8 +407,12 @@
407407
408 def _getMilestoneCondition(self):408 def _getMilestoneCondition(self):
409 """See `HasMilestonesMixin`."""409 """See `HasMilestonesMixin`."""
410 return And(Milestone.productID == Product.id,410 user = getUtility(ILaunchBag).user
411 Product.projectID == self.id)411 privacy_filter = ProductSet.getProductPrivacyFilter(user)
412 return And(
413 Milestone.productID == Product.id,
414 Product.projectID == self.id,
415 privacy_filter)
412416
413 def _getMilestones(self, user, only_active):417 def _getMilestones(self, user, only_active):
414 """Return a list of milestones for this project group.418 """Return a list of milestones for this project group.
415419
=== modified file 'lib/lp/registry/tests/test_milestone_vocabularies.py'
--- lib/lp/registry/tests/test_milestone_vocabularies.py 2012-10-11 04:57:59 +0000
+++ lib/lp/registry/tests/test_milestone_vocabularies.py 2012-11-16 20:43:23 +0000
@@ -5,10 +5,9 @@
55
6__metaclass__ = type6__metaclass__ = type
77
8from unittest import TestCase
9
10from zope.component import getUtility8from zope.component import getUtility
119
10from lp.app.enums import InformationType
12from lp.blueprints.interfaces.specification import ISpecificationSet11from lp.blueprints.interfaces.specification import ISpecificationSet
13from lp.registry.interfaces.distribution import IDistributionSet12from lp.registry.interfaces.distribution import IDistributionSet
14from lp.registry.interfaces.person import IPersonSet13from lp.registry.interfaces.person import IPersonSet
@@ -18,20 +17,47 @@
18from lp.testing import (17from lp.testing import (
19 login,18 login,
20 logout,19 logout,
20 person_logged_in,
21 TestCaseWithFactory,
21 )22 )
22from lp.testing.layers import DatabaseFunctionalLayer23from lp.testing.layers import DatabaseFunctionalLayer
2324
2425
25class TestMilestoneVocabulary(TestCase):26class TestMilestoneVocabulary(TestCaseWithFactory):
26 """Test that the MilestoneVocabulary behaves as expected."""27 """Test that the MilestoneVocabulary behaves as expected."""
28
27 layer = DatabaseFunctionalLayer29 layer = DatabaseFunctionalLayer
2830
29 def setUp(self):31 def setUp(self):
32 super(TestMilestoneVocabulary, self).setUp()
30 login('test@canonical.com')33 login('test@canonical.com')
3134
32 def tearDown(self):35 def tearDown(self):
36 super(TestMilestoneVocabulary, self).tearDown()
33 logout()37 logout()
3438
39 def test_project_group_does_not_show_nonpublic_products(self):
40 # Milestones for a projectgroup should not include those on an
41 # associated private product.
42 owner = self.factory.makePerson()
43 group = self.factory.makeProject(owner=owner)
44 public = self.factory.makeProduct(project=group, owner=owner)
45 private = self.factory.makeProduct(project=group, owner=owner,
46 information_type=InformationType.PROPRIETARY)
47 with person_logged_in(owner):
48 m1 = self.factory.makeMilestone(name='public', product=public)
49 m2 = self.factory.makeMilestone(name='private', product=private)
50 vocabulary = MilestoneVocabulary(group)
51 expected = [m1.title]
52 listing = [term.title for term in vocabulary]
53 self.assertEqual(expected, listing)
54
55 with person_logged_in(owner):
56 vocabulary = MilestoneVocabulary(group)
57 expected = [m1.title, m2.title]
58 listing = [term.title for term in vocabulary]
59 self.assertEqual(expected, listing)
60
35 def testProductMilestoneVocabulary(self):61 def testProductMilestoneVocabulary(self):
36 """Test of MilestoneVocabulary for a product."""62 """Test of MilestoneVocabulary for a product."""
37 firefox = getUtility(IProductSet).getByName('firefox')63 firefox = getUtility(IProductSet).getByName('firefox')