Merge lp:~gary/launchpad/add-target-subscription-page into lp:launchpad

Proposed by Gary Poster
Status: Merged
Approved by: Gary Poster
Approved revision: no longer in the source branch.
Merged at revision: 12587
Proposed branch: lp:~gary/launchpad/add-target-subscription-page
Merge into: lp:launchpad
Diff against target: 298 lines (+162/-13)
6 files modified
lib/lp/bugs/browser/bugtarget.py (+19/-4)
lib/lp/bugs/browser/configure.zcml (+6/-0)
lib/lp/bugs/browser/tests/test_bugtarget_subscription.py (+37/-0)
lib/lp/bugs/model/structuralsubscription.py (+23/-8)
lib/lp/bugs/templates/bugtarget-subscription-list.pt (+25/-0)
lib/lp/bugs/tests/test_structuralsubscriptiontarget.py (+52/-1)
To merge this branch: bzr merge lp:~gary/launchpad/add-target-subscription-page
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+53095@code.launchpad.net

Commit message

[r=bac][no-qa] add a page on which JS can be built to address bug 674422.

Description of the change

This branch is an incremental branch that adds a page on which JS can be built to address bug 674422. It is very similar at this time to lp.bugs.browser.bugsubscription.BugSubscriptionListView, which exists for the same reasons as part of the same effort.

make lint is happy.

You could kind of say I had a pre-imp with Benji about this; I am supporting an effort that he is leading.

Thank you

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

Nice branch Gary. Thanks for explaining ProxyFactory to me.

As I mentioned on IRC, your new file should have (C) 2011.

You've also got some import sorting issues. It'd be easier to run our neato tool than for me to identify them. Fix them only if you want. (Look its the lovable non-pedantic me.)

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/browser/bugtarget.py'
2--- lib/lp/bugs/browser/bugtarget.py 2011-02-24 15:30:54 +0000
3+++ lib/lp/bugs/browser/bugtarget.py 2011-03-11 22:20:38 +0000
4@@ -24,10 +24,7 @@
5 import cgi
6 from cStringIO import StringIO
7 from datetime import datetime
8-from operator import (
9- attrgetter,
10- itemgetter,
11- )
12+from operator import itemgetter
13 import urllib
14
15 from lazr.restful.interface import copy_field
16@@ -129,6 +126,9 @@
17 from lp.bugs.interfaces.malone import IMaloneApplication
18 from lp.bugs.interfaces.securitycontact import IHasSecurityContact
19 from lp.bugs.model.bugtask import BugTask
20+from lp.bugs.model.structuralsubscription import (
21+ get_all_structural_subscriptions_for_target,
22+ )
23 from lp.bugs.utilities.filebugdataparser import FileBugData
24 from lp.hardwaredb.interfaces.hwdb import IHWSubmissionSet
25 from lp.registry.browser.product import ProductConfigureBase
26@@ -1560,3 +1560,18 @@
27 def proxiedUrlForLibraryFile(self, patch):
28 """Return the proxied download URL for a Librarian file."""
29 return ProxiedLibraryFileAlias(patch.libraryfile, patch).http_url
30+
31+
32+class TargetSubscriptionView(LaunchpadView):
33+ """A view to show all a person's structural subscriptions to a target."""
34+
35+ @property
36+ def label(self):
37+ return "Your subscriptions to %s" % (self.context.displayname,)
38+
39+ page_title = label
40+
41+ @property
42+ def structural_subscriptions(self):
43+ return get_all_structural_subscriptions_for_target(
44+ self.context, self.user)
45
46=== modified file 'lib/lp/bugs/browser/configure.zcml'
47--- lib/lp/bugs/browser/configure.zcml 2011-02-11 11:12:18 +0000
48+++ lib/lp/bugs/browser/configure.zcml 2011-03-11 22:20:38 +0000
49@@ -1200,6 +1200,12 @@
50 permission="zope.Public"
51 class="lp.bugs.browser.structuralsubscription.StructuralSubscribersPortletView"
52 template="../templates/structural-subscription-target-portlet-subscribers.pt"/>
53+ <browser:page
54+ for="lp.bugs.interfaces.structuralsubscription.IStructuralSubscriptionTarget"
55+ name="+subscriptions"
56+ class="lp.bugs.browser.bugtarget.TargetSubscriptionView"
57+ permission="launchpad.AnyPerson"
58+ template="../templates/bugtarget-subscription-list.pt"/>
59 </facet>
60
61 <browser:url
62
63=== added file 'lib/lp/bugs/browser/tests/test_bugtarget_subscription.py'
64--- lib/lp/bugs/browser/tests/test_bugtarget_subscription.py 1970-01-01 00:00:00 +0000
65+++ lib/lp/bugs/browser/tests/test_bugtarget_subscription.py 2011-03-11 22:20:38 +0000
66@@ -0,0 +1,37 @@
67+# Copyright 2011 Canonical Ltd. This software is licensed under the
68+# GNU Affero General Public License version 3 (see the file LICENSE).
69+
70+"""Tests for TestSubscriptionView."""
71+
72+__metaclass__ = type
73+
74+from canonical.launchpad.ftests import LaunchpadFormHarness
75+from canonical.testing.layers import LaunchpadFunctionalLayer
76+from lp.bugs.browser.bugtarget import TargetSubscriptionView
77+from lp.testing import (
78+ person_logged_in,
79+ TestCaseWithFactory,
80+ )
81+
82+
83+class TargetSubscriptionViewTestCase(TestCaseWithFactory):
84+ """Tests for the TargetSubscriptionView."""
85+
86+ layer = LaunchpadFunctionalLayer
87+
88+ def setUp(self):
89+ super(TargetSubscriptionViewTestCase, self).setUp()
90+ self.product = self.factory.makeProduct(
91+ name='widgetsrus', displayname='Widgets R Us')
92+ self.subscriber = self.factory.makePerson()
93+
94+ def test_identify_structural_subscriptions(self):
95+ # This shows simply that we can identify the structural
96+ # subscriptions for the page. The content will come later.
97+ with person_logged_in(self.subscriber):
98+ sub = self.product.addBugSubscription(
99+ self.subscriber, self.subscriber)
100+ harness = LaunchpadFormHarness(
101+ self.product, TargetSubscriptionView)
102+ self.assertEqual(
103+ list(harness.view.structural_subscriptions), [sub])
104
105=== modified file 'lib/lp/bugs/model/structuralsubscription.py'
106--- lib/lp/bugs/model/structuralsubscription.py 2011-03-07 21:05:12 +0000
107+++ lib/lp/bugs/model/structuralsubscription.py 2011-03-11 22:20:38 +0000
108@@ -4,6 +4,7 @@
109 __metaclass__ = type
110 __all__ = [
111 'get_all_structural_subscriptions',
112+ 'get_all_structural_subscriptions_for_target',
113 'get_structural_subscribers',
114 'get_structural_subscription_targets',
115 'StructuralSubscription',
116@@ -11,13 +12,6 @@
117 ]
118
119 import pytz
120-
121-from storm.locals import (
122- DateTime,
123- Int,
124- Reference,
125- )
126-
127 from storm.base import Storm
128 from storm.expr import (
129 And,
130@@ -33,15 +27,21 @@
131 SQL,
132 Union,
133 )
134+from storm.locals import (
135+ DateTime,
136+ Int,
137+ Reference,
138+ )
139 from storm.store import (
140+ EmptyResultSet,
141 Store,
142- EmptyResultSet,
143 )
144 from zope.component import (
145 adapts,
146 getUtility,
147 )
148 from zope.interface import implements
149+from zope.security.proxy import ProxyFactory
150
151 from canonical.database.constants import UTC_NOW
152 from canonical.database.sqlbase import quote
153@@ -81,6 +81,7 @@
154 from lp.registry.interfaces.productseries import IProductSeries
155 from lp.registry.interfaces.projectgroup import IProjectGroup
156 from lp.registry.interfaces.sourcepackage import ISourcePackage
157+from lp.registry.model.teammembership import TeamParticipation
158 from lp.services.propertycache import cachedproperty
159
160
161@@ -504,6 +505,20 @@
162 yield (bugtask, bugtask.milestone)
163
164
165+@ProxyFactory
166+def get_all_structural_subscriptions_for_target(target, person):
167+ """Find the personal and team structural subscriptions to the target.
168+ """
169+ # This is here because of a circular import.
170+ from lp.registry.model.person import Person
171+ return IStore(StructuralSubscription).find(
172+ StructuralSubscription,
173+ IStructuralSubscriptionTargetHelper(target).join,
174+ StructuralSubscription.subscriber == Person.id,
175+ TeamParticipation.personID == person.id,
176+ TeamParticipation.teamID == Person.id)
177+
178+
179 def _get_all_structural_subscriptions(find, targets, *conditions):
180 """Find the structural subscriptions for the given targets.
181
182
183=== added file 'lib/lp/bugs/templates/bugtarget-subscription-list.pt'
184--- lib/lp/bugs/templates/bugtarget-subscription-list.pt 1970-01-01 00:00:00 +0000
185+++ lib/lp/bugs/templates/bugtarget-subscription-list.pt 2011-03-11 22:20:38 +0000
186@@ -0,0 +1,25 @@
187+<html
188+ xmlns="http://www.w3.org/1999/xhtml"
189+ xmlns:tal="http://xml.zope.org/namespaces/tal"
190+ xmlns:metal="http://xml.zope.org/namespaces/metal"
191+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
192+ xml:lang="en"
193+ lang="en"
194+ dir="ltr"
195+ metal:use-macro="view/macro:page/main_only"
196+ i18n:domain="malone"
197+>
198+
199+<body>
200+ <div metal:fill-slot="main">
201+
202+ <div id="maincontent">
203+ <div id="nonportlets" class="readable">
204+
205+ </div>
206+ </div>
207+
208+ </div>
209+
210+</body>
211+</html>
212
213=== modified file 'lib/lp/bugs/tests/test_structuralsubscriptiontarget.py'
214--- lib/lp/bugs/tests/test_structuralsubscriptiontarget.py 2011-03-07 15:59:50 +0000
215+++ lib/lp/bugs/tests/test_structuralsubscriptiontarget.py 2011-03-11 22:20:38 +0000
216@@ -24,6 +24,7 @@
217 from canonical.launchpad.webapp.interfaces import ILaunchBag
218 from canonical.launchpad.webapp.testing import verifyObject
219 from canonical.testing.layers import (
220+ DatabaseFunctionalLayer,
221 LaunchpadFunctionalLayer,
222 LaunchpadZopelessLayer,
223 )
224@@ -32,7 +33,10 @@
225 IStructuralSubscriptionTarget,
226 IStructuralSubscriptionTargetHelper,
227 )
228-from lp.bugs.model.structuralsubscription import StructuralSubscription
229+from lp.bugs.model.structuralsubscription import (
230+ get_all_structural_subscriptions_for_target,
231+ StructuralSubscription,
232+ )
233 from lp.bugs.tests.test_bugtarget import bugtarget_filebug
234 from lp.registry.errors import (
235 DeleteSubscriptionError,
236@@ -46,8 +50,10 @@
237 login,
238 login_celebrity,
239 login_person,
240+ person_logged_in,
241 TestCaseWithFactory,
242 )
243+from lp.testing.factory import is_security_proxied_or_harmless
244 from lp.testing.matchers import Provides
245
246
247@@ -426,6 +432,51 @@
248 compile_storm(helper.join))
249
250
251+class TestGetAllStructuralSubscriptionsForTarget(TestCaseWithFactory):
252+
253+ layer = DatabaseFunctionalLayer
254+
255+ def setUp(self):
256+ super(TestGetAllStructuralSubscriptionsForTarget, self).setUp()
257+ self.subscriber = self.factory.makePerson()
258+ self.team = self.factory.makeTeam(members=[self.subscriber])
259+ login_person(self.subscriber)
260+ self.product = self.factory.makeProduct()
261+ self.milestone = self.factory.makeMilestone(product=self.product)
262+
263+ def getSubscriptions(self):
264+ subscriptions = get_all_structural_subscriptions_for_target(
265+ self.product, self.subscriber)
266+ self.assertTrue(is_security_proxied_or_harmless(subscriptions))
267+ return subscriptions
268+
269+ def test_no_subscriptions(self):
270+ subscriptions = self.getSubscriptions()
271+ self.assertEqual([], list(subscriptions))
272+
273+ def test_self_subscription(self):
274+ sub = self.product.addBugSubscription(
275+ self.subscriber, self.subscriber)
276+ subscriptions = self.getSubscriptions()
277+ self.assertEqual([sub], list(subscriptions))
278+
279+ def test_team_subscription(self):
280+ with person_logged_in(self.team.teamowner):
281+ sub = self.product.addBugSubscription(
282+ self.team, self.team.teamowner)
283+ subscriptions = self.getSubscriptions()
284+ self.assertEqual([sub], list(subscriptions))
285+
286+ def test_both_subscriptions(self):
287+ self_sub = self.product.addBugSubscription(
288+ self.subscriber, self.subscriber)
289+ with person_logged_in(self.team.teamowner):
290+ team_sub = self.product.addBugSubscription(
291+ self.team, self.team.teamowner)
292+ subscriptions = self.getSubscriptions()
293+ self.assertEqual(set([self_sub, team_sub]), set(subscriptions))
294+
295+
296 def distributionSourcePackageSetUp(test):
297 setUp(test)
298 ubuntu = getUtility(IDistributionSet).getByName('ubuntu')