Merge lp:~stevenk/launchpad/filter-spec-lists into lp:launchpad

Proposed by Steve Kowalik
Status: Merged
Merged at revision: 16505
Proposed branch: lp:~stevenk/launchpad/filter-spec-lists
Merge into: lp:launchpad
Diff against target: 368 lines (+103/-41)
11 files modified
lib/lp/bugs/browser/bug.py (+5/-1)
lib/lp/bugs/interfaces/bug.py (+4/-2)
lib/lp/bugs/model/bug.py (+14/-5)
lib/lp/bugs/model/tests/test_bugtask.py (+33/-10)
lib/lp/bugs/templates/bug-portlet-specs.pt (+2/-2)
lib/lp/code/browser/branch.py (+6/-2)
lib/lp/code/browser/branchmergeproposal.py (+6/-3)
lib/lp/code/interfaces/branch.py (+4/-1)
lib/lp/code/model/branch.py (+22/-8)
lib/lp/code/model/tests/test_branch.py (+5/-5)
lib/lp/code/templates/branch-macros.pt (+2/-2)
To merge this branch: bzr merge lp:~stevenk/launchpad/filter-spec-lists
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+149748@code.launchpad.net

Description of the change

Change IBug.specifications so that it returns a resultset of the linked specifications that the user can view.

Add a new method, IBranch.getSpecificationLinks() that will return a resultset of the specification links themselves (since that's what IBranch.spec_links does) that the user can view. IBranch.spec_links continues to live since it's exported via the API.

Clean up some whitespace that caught my eye.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

I wouldn't use an explicit join in getSpecifications (particularly since you have the join condition and where clause around the wrong way), but otherwise fine.

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/bug.py'
2--- lib/lp/bugs/browser/bug.py 2012-09-28 01:25:36 +0000
3+++ lib/lp/bugs/browser/bug.py 2013-02-21 07:22:22 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
6+# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 """IBug related view classes."""
10@@ -536,6 +536,10 @@
11 """
12 return getUtility(ILaunchBag).bugtask
13
14+ @property
15+ def specifications(self):
16+ return self.context.getSpecifications(self.user)
17+
18
19 class BugInformationTypePortletView(InformationTypePortletMixin,
20 LaunchpadView):
21
22=== modified file 'lib/lp/bugs/interfaces/bug.py'
23--- lib/lp/bugs/interfaces/bug.py 2013-02-06 04:22:43 +0000
24+++ lib/lp/bugs/interfaces/bug.py 2013-02-21 07:22:22 +0000
25@@ -1,4 +1,4 @@
26-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
27+# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
28 # GNU Affero General Public License version 3 (see the file LICENSE).
29
30 """Interfaces related to bugs."""
31@@ -313,7 +313,6 @@
32 initial_message = Attribute(
33 "The message that was specified when creating the bug")
34 questions = Attribute("List of questions related to this bug.")
35- specifications = Attribute("List of related specifications.")
36 tags = exported(List(
37 title=_("Tags"),
38 description=_("Space-separated keywords for classifying "
39@@ -383,6 +382,9 @@
40 value_type=Reference(schema=IMessage)),
41 exported_as='messages'))
42
43+ def getSpecifications(user):
44+ """List of related specifications that the user can view."""
45+
46 def _indexed_messages(include_content=False, include_parents=False):
47 """Low level query for getting bug messages.
48
49
50=== modified file 'lib/lp/bugs/model/bug.py'
51--- lib/lp/bugs/model/bug.py 2013-02-06 04:56:33 +0000
52+++ lib/lp/bugs/model/bug.py 2013-02-21 07:22:22 +0000
53@@ -98,6 +98,11 @@
54 from lp.app.interfaces.services import IService
55 from lp.app.model.launchpad import InformationTypeMixin
56 from lp.app.validators import LaunchpadValidationError
57+from lp.blueprints.model.specification import Specification
58+from lp.blueprints.model.specificationbug import SpecificationBug
59+from lp.blueprints.model.specificationsearch import (
60+ get_specification_privacy_filter,
61+ )
62 from lp.bugs.adapters.bug import convert_to_information_type
63 from lp.bugs.adapters.bugchange import (
64 BranchLinkedToBug,
65@@ -360,11 +365,7 @@
66 cves = SQLRelatedJoin('Cve', intermediateTable='BugCve',
67 orderBy='sequence', joinColumn='bug', otherColumn='cve')
68 cve_links = SQLMultipleJoin('BugCve', joinColumn='bug', orderBy='id')
69- duplicates = SQLMultipleJoin(
70- 'Bug', joinColumn='duplicateof', orderBy='id')
71- specifications = SQLRelatedJoin('Specification', joinColumn='bug',
72- otherColumn='specification', intermediateTable='SpecificationBug',
73- orderBy='-datecreated')
74+ duplicates = SQLMultipleJoin('Bug', joinColumn='duplicateof', orderBy='id')
75 questions = SQLRelatedJoin('Question', joinColumn='bug',
76 otherColumn='question', intermediateTable='QuestionBug',
77 orderBy='-datecreated')
78@@ -379,6 +380,14 @@
79 heat_last_updated = UtcDateTimeCol(default=None)
80 latest_patch_uploaded = UtcDateTimeCol(default=None)
81
82+ def getSpecifications(self, user):
83+ """See `IBug`."""
84+ return IStore(SpecificationBug).find(
85+ Specification,
86+ SpecificationBug.bugID == self.id,
87+ SpecificationBug.specificationID == Specification.id,
88+ *get_specification_privacy_filter(user))
89+
90 @property
91 def security_related(self):
92 return self.information_type in SECURITY_INFORMATION_TYPES
93
94=== modified file 'lib/lp/bugs/model/tests/test_bugtask.py'
95--- lib/lp/bugs/model/tests/test_bugtask.py 2012-10-18 14:43:24 +0000
96+++ lib/lp/bugs/model/tests/test_bugtask.py 2013-02-21 07:22:22 +0000
97@@ -1,4 +1,4 @@
98-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
99+# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
100 # GNU Affero General Public License version 3 (see the file LICENSE).
101
102 __metaclass__ = type
103@@ -528,7 +528,7 @@
104 ])
105
106
107-class TestBugTaskPrivacy(TestCase):
108+class TestBugTaskPrivacy(TestCaseWithFactory):
109 """Verify that the bug is either private or public.
110
111 XXX: rharding 2012-05-14 bug=999298: These tests are ported from doctests
112@@ -553,14 +553,15 @@
113 ubuntu_team = getUtility(IPersonSet).getByEmail('support@ubuntu.com')
114 bug_upstream_firefox_crashes.bug.subscribe(ubuntu_team, ubuntu_team)
115
116- old_state = Snapshot(bug_upstream_firefox_crashes.bug,
117- providing=IBug)
118- self.assertTrue(bug_upstream_firefox_crashes.bug.setPrivate(True,
119- foobar))
120-
121- bug_set_private = ObjectModifiedEvent(bug_upstream_firefox_crashes.bug,
122- old_state,
123- ["id", "title", "private"])
124+ old_state = Snapshot(
125+ bug_upstream_firefox_crashes.bug, providing=IBug)
126+ self.assertTrue(
127+ bug_upstream_firefox_crashes.bug.setPrivate(True, foobar))
128+
129+ bug_set_private = ObjectModifiedEvent(
130+ bug_upstream_firefox_crashes.bug, old_state,
131+ ["id", "title", "private"])
132+
133 notify(bug_set_private)
134 flush_database_updates()
135
136@@ -671,6 +672,28 @@
137 bug_upstream_firefox_crashes.transitionToStatus(
138 BugTaskStatus.NEW, getUtility(ILaunchBag).user)
139
140+ def _createBugAndSpecification(self):
141+ bug = self.factory.makeBug()
142+ spec = self.factory.makeSpecification(
143+ information_type=InformationType.PROPRIETARY)
144+ with person_logged_in(spec.product.owner):
145+ spec.linkBug(bug)
146+ return spec, bug
147+
148+ def test_bug_specifications_is_filtered_for_anonymous(self):
149+ spec, bug = self._createBugAndSpecification()
150+ self.assertContentEqual([], bug.getSpecifications(None))
151+
152+ def test_bug_specifications_is_filtered_for_unknown_user(self):
153+ spec, bug = self._createBugAndSpecification()
154+ self.assertContentEqual(
155+ [], bug.getSpecifications(self.factory.makePerson()))
156+
157+ def test_bug_specifications_for_authorised_user(self):
158+ spec, bug = self._createBugAndSpecification()
159+ self.assertContentEqual(
160+ [spec], bug.getSpecifications(spec.product.owner))
161+
162
163 class TestBugTaskDelta(TestCaseWithFactory):
164
165
166=== modified file 'lib/lp/bugs/templates/bug-portlet-specs.pt'
167--- lib/lp/bugs/templates/bug-portlet-specs.pt 2009-10-29 21:39:12 +0000
168+++ lib/lp/bugs/templates/bug-portlet-specs.pt 2013-02-21 07:22:22 +0000
169@@ -3,10 +3,10 @@
170 xmlns:metal="http://xml.zope.org/namespaces/metal"
171 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
172 class="portlet vertical" id="portlet-blueprints"
173- tal:condition="context/specifications">
174+ tal:condition="view/specifications">
175 <h2>Related blueprints</h2>
176 <ul>
177- <li tal:repeat="spec context/specifications"
178+ <li tal:repeat="spec view/specifications"
179 tal:content="structure spec/fmt:link" />
180 </ul>
181 </div>
182
183=== modified file 'lib/lp/code/browser/branch.py'
184--- lib/lp/code/browser/branch.py 2012-12-12 04:59:52 +0000
185+++ lib/lp/code/browser/branch.py 2013-02-21 07:22:22 +0000
186@@ -1,4 +1,4 @@
187-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
188+# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
189 # GNU Affero General Public License version 3 (see the file LICENSE).
190
191 """Branch views."""
192@@ -354,7 +354,7 @@
193 return Link('+linkbug', text, icon='add')
194
195 def link_blueprint(self):
196- if self.context.spec_links:
197+ if list(self.context.getSpecificationLinks(self.user)):
198 text = 'Link to another blueprint'
199 else:
200 text = 'Link to a blueprint'
201@@ -689,6 +689,10 @@
202 self.context.branch, IBranch['lifecycle_status'],
203 header='Change status to', css_class_prefix='branchstatus')
204
205+ @property
206+ def spec_links(self):
207+ return self.context.getSpecificationLinks(self.user)
208+
209
210 class BranchInProductView(BranchView):
211
212
213=== modified file 'lib/lp/code/browser/branchmergeproposal.py'
214--- lib/lp/code/browser/branchmergeproposal.py 2013-01-07 02:40:55 +0000
215+++ lib/lp/code/browser/branchmergeproposal.py 2013-02-21 07:22:22 +0000
216@@ -1,4 +1,4 @@
217-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
218+# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
219 # GNU Affero General Public License version 3 (see the file LICENSE).
220
221 """Views, navigation and actions for BranchMergeProposals."""
222@@ -719,8 +719,11 @@
223 def has_bug_or_spec(self):
224 """Return whether or not the merge proposal has a linked bug or spec.
225 """
226- branch = self.context.source_branch
227- return self.linked_bugtasks or branch.spec_links
228+ return self.linked_bugtasks or self.spec_links
229+
230+ @property
231+ def spec_links(self):
232+ return self.context.source_branch.getSpecificationLinks(self.user)
233
234 @cachedproperty
235 def linked_bugtasks(self):
236
237=== modified file 'lib/lp/code/interfaces/branch.py'
238--- lib/lp/code/interfaces/branch.py 2013-01-16 06:41:43 +0000
239+++ lib/lp/code/interfaces/branch.py 2013-02-21 07:22:22 +0000
240@@ -1,4 +1,4 @@
241-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
242+# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
243 # GNU Affero General Public License version 3 (see the file LICENSE).
244
245 """Branch interfaces."""
246@@ -482,6 +482,9 @@
247 value_type=Reference(Interface)), # Really ISpecificationBranch
248 as_of="beta")
249
250+ def getSpecificationLinks(user):
251+ """Fetch the `ISpecificationBranch`'s that the user can view."""
252+
253 @call_with(registrant=REQUEST_USER)
254 @operation_parameters(
255 spec=Reference(schema=Interface)) # Really ISpecification
256
257=== modified file 'lib/lp/code/model/branch.py'
258--- lib/lp/code/model/branch.py 2013-02-14 05:36:37 +0000
259+++ lib/lp/code/model/branch.py 2013-02-21 07:22:22 +0000
260@@ -1,4 +1,4 @@
261-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
262+# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
263 # GNU Affero General Public License version 3 (see the file LICENSE).
264
265 __metaclass__ = type
266@@ -66,6 +66,11 @@
267 IPrivacy,
268 )
269 from lp.app.interfaces.services import IService
270+from lp.blueprints.model.specification import Specification
271+from lp.blueprints.model.specificationbranch import SpecificationBranch
272+from lp.blueprints.model.specificationsearch import (
273+ get_specification_privacy_filter,
274+ )
275 from lp.bugs.interfaces.bugtask import IBugTaskSet
276 from lp.bugs.interfaces.bugtaskfilter import filter_bugtasks_by_context
277 from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams
278@@ -438,9 +443,20 @@
279 """See `IBranch`."""
280 return bug.unlinkBranch(self, user)
281
282- spec_links = SQLMultipleJoin('SpecificationBranch',
283- joinColumn='branch',
284- orderBy='id')
285+ spec_links = SQLMultipleJoin(
286+ 'SpecificationBranch', joinColumn='branch', orderBy='id')
287+
288+ def getSpecificationLinks(self, user):
289+ """See `IBranch`."""
290+ tables = [
291+ SpecificationBranch,
292+ Join(
293+ Specification,
294+ SpecificationBranch.specificationID == Specification.id)]
295+ return Store.of(self).using(*tables).find(
296+ SpecificationBranch,
297+ SpecificationBranch.branchID == self.id,
298+ *get_specification_privacy_filter(user))
299
300 def linkSpecification(self, spec, registrant):
301 """See `IBranch`."""
302@@ -459,8 +475,7 @@
303 @property
304 def active_landing_targets(self):
305 """Merge proposals not in final states where this branch is source."""
306- store = Store.of(self)
307- return store.find(
308+ return Store.of(self).find(
309 BranchMergeProposal, BranchMergeProposal.source_branch == self,
310 Not(BranchMergeProposal.queue_status.is_in(
311 BRANCH_MERGE_PROPOSAL_FINAL_STATES)))
312@@ -615,8 +630,7 @@
313
314 def getStackedBranches(self):
315 """See `IBranch`."""
316- store = Store.of(self)
317- return store.find(Branch, Branch.stacked_on == self)
318+ return Store.of(self).find(Branch, Branch.stacked_on == self)
319
320 def getStackedOnBranches(self):
321 """See `IBranch`."""
322
323=== modified file 'lib/lp/code/model/tests/test_branch.py'
324--- lib/lp/code/model/tests/test_branch.py 2013-01-07 02:40:55 +0000
325+++ lib/lp/code/model/tests/test_branch.py 2013-02-21 07:22:22 +0000
326@@ -1,4 +1,4 @@
327-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
328+# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
329 # GNU Affero General Public License version 3 (see the file LICENSE).
330
331 """Tests for Branches."""
332@@ -1573,10 +1573,10 @@
333 spec2.linkBranch(self.branch, self.branch.owner)
334 spec2_branch_id = self.branch.spec_links[1].id
335 self.branch.destroySelf(break_references=True)
336- self.assertRaises(SQLObjectNotFound, SpecificationBranch.get,
337- spec1_branch_id)
338- self.assertRaises(SQLObjectNotFound, SpecificationBranch.get,
339- spec2_branch_id)
340+ self.assertRaises(
341+ SQLObjectNotFound, SpecificationBranch.get, spec1_branch_id)
342+ self.assertRaises(
343+ SQLObjectNotFound, SpecificationBranch.get, spec2_branch_id)
344
345 def test_branchWithSeriesRequirements(self):
346 """Deletion requirements for a series' branch are right."""
347
348=== modified file 'lib/lp/code/templates/branch-macros.pt'
349--- lib/lp/code/templates/branch-macros.pt 2012-08-07 07:07:02 +0000
350+++ lib/lp/code/templates/branch-macros.pt 2013-02-21 07:22:22 +0000
351@@ -76,7 +76,7 @@
352 <metal:branch-link use-macro="branch/@@+macros/bug-branch-links"/>
353 </tal:branch-link>
354
355- <div tal:repeat="spec_link mergeproposal/source_branch/spec_links">
356+ <div tal:repeat="spec_link view/spec_links">
357 <img src="/@@/blueprint"
358 tal:replace="structure spec_link/specification/image:icon" />
359 <a tal:attributes="href spec_link/specification/fmt:url:blueprints"
360@@ -140,7 +140,7 @@
361 show_edit - show the edit form
362 </tal:comment>
363
364- <tal:spec-tasks repeat="spec_branch branch/spec_links">
365+ <tal:spec-tasks repeat="spec_branch view/spec_links">
366 <div tal:define="has_edit_permission spec_branch/required:launchpad.AnyPerson;
367 show_edit show_edit|nothing;
368 show_edit python: show_edit and has_edit_permission;