Merge lp:~thumper/launchpad/branch-subscription-subscribed-by into lp:launchpad/db-devel
- branch-subscription-subscribed-by
- Merge into db-devel
Status: | Merged |
---|---|
Approved by: | Curtis Hovey |
Approved revision: | no longer in the source branch. |
Merged at revision: | 9417 |
Proposed branch: | lp:~thumper/launchpad/branch-subscription-subscribed-by |
Merge into: | lp:launchpad/db-devel |
Diff against target: |
1649 lines (+395/-226) 50 files modified
database/sampledata/current-dev.sql (+4/-4) database/sampledata/current.sql (+4/-4) database/schema/patch-2207-60-0.sql (+18/-0) lib/canonical/launchpad/mail/commands.py (+3/-1) lib/canonical/launchpad/scripts/tests/test_garbo.py (+0/-2) lib/canonical/launchpad/security.py (+1/-23) lib/lp/app/errors.py (+18/-0) lib/lp/bugs/interfaces/bug.py (+0/-7) lib/lp/bugs/model/bug.py (+2/-1) lib/lp/code/browser/branch.py (+0/-12) lib/lp/code/browser/branchsubscription.py (+7/-24) lib/lp/code/browser/codeimport.py (+2/-1) lib/lp/code/configure.zcml (+1/-0) lib/lp/code/doc/branch-merge-proposal-notifications.txt (+4/-4) lib/lp/code/doc/branch-notifications.txt (+23/-39) lib/lp/code/doc/branch-visibility.txt (+1/-1) lib/lp/code/doc/branch.txt (+4/-4) lib/lp/code/doc/codeimport.txt (+1/-1) lib/lp/code/doc/codereviewcomment.txt (+1/-1) lib/lp/code/interfaces/branch.py (+11/-3) lib/lp/code/interfaces/branchsubscription.py (+12/-1) lib/lp/code/mail/tests/test_branch.py (+1/-1) lib/lp/code/mail/tests/test_branchmergeproposal.py (+3/-3) lib/lp/code/mail/tests/test_codehandler.py (+1/-1) lib/lp/code/mail/tests/test_codereviewcomment.py (+2/-2) lib/lp/code/model/branch.py (+14/-5) lib/lp/code/model/branchnamespace.py (+4/-2) lib/lp/code/model/branchsubscription.py (+12/-1) lib/lp/code/model/tests/test_branch.py (+14/-9) lib/lp/code/model/tests/test_branchcollection.py (+9/-5) lib/lp/code/model/tests/test_branchjob.py (+8/-4) lib/lp/code/model/tests/test_branchmergeproposals.py (+20/-16) lib/lp/code/model/tests/test_branchsubscription.py (+115/-0) lib/lp/code/scripts/tests/test_scan_branches.py (+2/-1) lib/lp/code/scripts/tests/test_sendbranchmail.py (+4/-2) lib/lp/code/security.py (+37/-0) lib/lp/code/stories/branches/xx-branch-edit.txt (+1/-1) lib/lp/code/stories/branches/xx-branchmergeproposals.txt (+2/-2) lib/lp/code/stories/branches/xx-person-branches.txt (+1/-1) lib/lp/code/stories/branches/xx-subscribing-branches.txt (+7/-24) lib/lp/code/stories/webservice/xx-branchsubscription.txt (+2/-0) lib/lp/code/tests/test_branch.py (+1/-1) lib/lp/codehosting/scanner/tests/test_bzrsync.py (+1/-1) lib/lp/codehosting/scanner/tests/test_email.py (+6/-3) lib/lp/codehosting/tests/test_jobs.py (+1/-1) lib/lp/registry/browser/tests/packaging-views.txt (+1/-1) lib/lp/registry/browser/tests/private-team-creation-views.txt (+1/-1) lib/lp/registry/doc/private-team-roles.txt (+2/-2) lib/lp/registry/model/distroseries.py (+1/-1) lib/lp/testing/factory.py (+5/-2) |
To merge this branch: | bzr merge lp:~thumper/launchpad/branch-subscription-subscribed-by |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Stuart Bishop (community) | db | Approve | |
Curtis Hovey (community) | release-critical code | Approve | |
Björn Tillenius | db | Pending | |
Review via email: mp+25937@code.launchpad.net |
Commit message
Allow people to subscribe teams they are not a member of, and record them as the user who subscribed the team.
Description of the change
Add a subscribed_by to the BranchSubscription table.
This work also changes the permissions and abilities for subscribing and unsubscribing from branches.
Work in progress for now.
Curtis Hovey (sinzui) wrote : | # |
Tim Penhey (thumper) wrote : | # |
On Fri, 28 May 2010 13:58:22 you wrote:
> Review: Needs Information release-critical
Thanks Curtis,
> > === modified file 'lib/lp/
> > --- lib/lp/
> > +++ lib/lp/
> > @@ -354,6 +354,7 @@
> >
> > def initialize(self):
> > self.notices = []
> >
> > + # TODO: FIXME - this is almost certainly not what we want.
> >
> > self._add_
>
> What do you want to do here?
Haha, that was my own personal reminder in that I *think* that the following
function call isn't needed, nor called, nor would do what is really wanted,
and it was a reminder to check.
> > === modified file 'lib/lp/
> > --- lib/lp/
> > +++ lib/lp/
> > @@ -41,6 +41,9 @@
> >
> > notNull=False, default=DEFAULT)
> >
> > review_level = EnumCol(
> >
> > notNull=True, default=DEFAULT)
> >
> > + subscribed_by = ForeignKey(
> > + dbName=
> > + storm_validator
> > notNull=True)
>
> I was suggested in a recent review to drop the dbName since it is identical
> to the column name. I did so, but now I ponder explicit is better than
> implicit again.
I'm relatively unconcerned about this point. I think at one stage the dbName
was required for ForeignKey but now may not be needed. I'm happy to remove
the dbName param if you think it best.
> > === modified file 'lib/lp/
> > --- lib/lp/
> > +0000 +++ lib/lp/
> > 04:47:25 +0000 @@ -350,5 +350,5 @@
> >
> > cnews
> > libstdc++
> > linux-source-2.6.15
> >
> > + hot
> >
> > thunderbird
> >
> > - hot
>
> What caused this? This look like me from last week?
Caused by someone.
> > === modified file 'lib/lp/
> > --- lib/lp/
> > +++ lib/lp/
> > @@ -332,7 +332,7 @@
> >
> > origin = SQL(joins)
> > condition = SQL(conditions + "AND packaging.id IS NULL")
> > results = IStore(
> >
> > - results = results.
> > + results = results.
> >
> > return [{
> >
> > 'package': SourcePackage(
> >
> > sourcepackagena
>
> This is me from last week.
Yes this was the source of it. It was failing locally for me consistently.
I'm back on to this branch now to add the extra tests and model validation.
Tim Penhey (thumper) wrote : | # |
On Fri, 28 May 2010 13:58:22 you wrote:
> Review: Needs Information release-critical
> > === modified file 'lib/lp/
> Wrap the code at 78 characters.
Done.
> > === modified file 'lib/lp/
> Wrap the code at 78 characters.
Done.
Also added the checking.
1 | === modified file 'lib/canonical/launchpad/security.py' | |||
2 | --- lib/canonical/launchpad/security.py 2010-05-27 04:44:39 +0000 | |||
3 | +++ lib/canonical/launchpad/security.py 2010-05-28 04:18:17 +0000 | |||
4 | @@ -24,8 +24,6 @@ | |||
5 | 24 | IBranch, user_has_special_branch_access) | 24 | IBranch, user_has_special_branch_access) |
6 | 25 | from lp.code.interfaces.branchmergeproposal import ( | 25 | from lp.code.interfaces.branchmergeproposal import ( |
7 | 26 | IBranchMergeProposal) | 26 | IBranchMergeProposal) |
8 | 27 | from lp.code.interfaces.branchsubscription import ( | ||
9 | 28 | IBranchSubscription) | ||
10 | 29 | from lp.code.interfaces.sourcepackagerecipe import ISourcePackageRecipe | 27 | from lp.code.interfaces.sourcepackagerecipe import ISourcePackageRecipe |
11 | 30 | from lp.code.interfaces.sourcepackagerecipebuild import ( | 28 | from lp.code.interfaces.sourcepackagerecipebuild import ( |
12 | 31 | ISourcePackageRecipeBuild) | 29 | ISourcePackageRecipeBuild) |
13 | @@ -61,8 +59,7 @@ | |||
14 | 61 | from lp.services.worlddata.interfaces.language import ILanguage, ILanguageSet | 59 | from lp.services.worlddata.interfaces.language import ILanguage, ILanguageSet |
15 | 62 | from lp.translations.interfaces.languagepack import ILanguagePack | 60 | from lp.translations.interfaces.languagepack import ILanguagePack |
16 | 63 | from canonical.launchpad.interfaces.launchpad import ( | 61 | from canonical.launchpad.interfaces.launchpad import ( |
19 | 64 | IBazaarApplication, IHasBug, IHasDrivers, ILaunchpadCelebrities, | 62 | IHasBug, IHasDrivers, ILaunchpadCelebrities, IPersonRoles) |
18 | 65 | IPersonRoles) | ||
20 | 66 | from lp.registry.interfaces.role import IHasOwner | 63 | from lp.registry.interfaces.role import IHasOwner |
21 | 67 | from lp.registry.interfaces.location import IPersonLocation | 64 | from lp.registry.interfaces.location import IPersonLocation |
22 | 68 | from lp.registry.interfaces.mailinglist import IMailingListSet | 65 | from lp.registry.interfaces.mailinglist import IMailingListSet |
23 | @@ -1733,26 +1730,6 @@ | |||
24 | 1733 | self.obj.distribution).checkAuthenticated(user)) | 1730 | self.obj.distribution).checkAuthenticated(user)) |
25 | 1734 | 1731 | ||
26 | 1735 | 1732 | ||
27 | 1736 | class BranchSubscriptionEdit(AuthorizationBase): | ||
28 | 1737 | permission = 'launchpad.Edit' | ||
29 | 1738 | usedfor = IBranchSubscription | ||
30 | 1739 | |||
31 | 1740 | def checkAuthenticated(self, user): | ||
32 | 1741 | """Is the user able to edit a branch subscription? | ||
33 | 1742 | |||
34 | 1743 | Any team member can edit a branch subscription for their team. | ||
35 | 1744 | Launchpad Admins can also edit any branch subscription. | ||
36 | 1745 | """ | ||
37 | 1746 | return (user.inTeam(self.obj.person) or | ||
38 | 1747 | user.inTeam(self.obj.subscribed_by) or | ||
39 | 1748 | user.in_admin or | ||
40 | 1749 | user.in_bazaar_experts) | ||
41 | 1750 | |||
42 | 1751 | |||
43 | 1752 | class BranchSubscriptionView(BranchSubscriptionEdit): | ||
44 | 1753 | permission = 'launchpad.View' | ||
45 | 1754 | |||
46 | 1755 | |||
47 | 1756 | class BranchMergeProposalView(AuthorizationBase): | 1733 | class BranchMergeProposalView(AuthorizationBase): |
48 | 1757 | permission = 'launchpad.View' | 1734 | permission = 'launchpad.View' |
49 | 1758 | usedfor = IBranchMergeProposal | 1735 | usedfor = IBranchMergeProposal |
50 | 1759 | 1736 | ||
51 | === modified file 'lib/lp/code/browser/branchsubscription.py' | |||
52 | --- lib/lp/code/browser/branchsubscription.py 2010-05-25 03:13:25 +0000 | |||
53 | +++ lib/lp/code/browser/branchsubscription.py 2010-05-28 09:31:03 +0000 | |||
54 | @@ -12,10 +12,9 @@ | |||
55 | 12 | 'BranchSubscriptionPrimaryContext', | 12 | 'BranchSubscriptionPrimaryContext', |
56 | 13 | ] | 13 | ] |
57 | 14 | 14 | ||
59 | 15 | from zope.component import getUtility | 15 | |
60 | 16 | from zope.interface import implements | 16 | from zope.interface import implements |
61 | 17 | 17 | ||
62 | 18 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities | ||
63 | 19 | from canonical.launchpad.webapp import ( | 18 | from canonical.launchpad.webapp import ( |
64 | 20 | action, canonical_url, LaunchpadEditFormView, LaunchpadFormView, | 19 | action, canonical_url, LaunchpadEditFormView, LaunchpadFormView, |
65 | 21 | LaunchpadView) | 20 | LaunchpadView) |
66 | @@ -210,7 +209,8 @@ | |||
67 | 210 | subscription = self.context.getSubscription(person) | 209 | subscription = self.context.getSubscription(person) |
68 | 211 | if subscription is None: | 210 | if subscription is None: |
69 | 212 | self.context.subscribe( | 211 | self.context.subscribe( |
71 | 213 | person, notification_level, max_diff_lines, review_level, self.user) | 212 | person, notification_level, max_diff_lines, review_level, |
72 | 213 | self.user) | ||
73 | 214 | 214 | ||
74 | 215 | self.add_notification_message( | 215 | self.add_notification_message( |
75 | 216 | '%s has been subscribed to this branch with: ' | 216 | '%s has been subscribed to this branch with: ' |
76 | 217 | 217 | ||
77 | === modified file 'lib/lp/code/configure.zcml' | |||
78 | --- lib/lp/code/configure.zcml 2010-05-07 04:53:47 +0000 | |||
79 | +++ lib/lp/code/configure.zcml 2010-05-28 04:18:17 +0000 | |||
80 | @@ -11,6 +11,7 @@ | |||
81 | 11 | xmlns:webservice="http://namespaces.canonical.com/webservice" | 11 | xmlns:webservice="http://namespaces.canonical.com/webservice" |
82 | 12 | i18n_domain="launchpad"> | 12 | i18n_domain="launchpad"> |
83 | 13 | <include package=".browser"/> | 13 | <include package=".browser"/> |
84 | 14 | <authorizations module="lp.code.security" /> | ||
85 | 14 | 15 | ||
86 | 15 | <!-- Branch Merge Queue --> | 16 | <!-- Branch Merge Queue --> |
87 | 16 | 17 | ||
88 | 17 | 18 | ||
89 | === modified file 'lib/lp/code/interfaces/branchsubscription.py' | |||
90 | --- lib/lp/code/interfaces/branchsubscription.py 2010-05-25 01:24:43 +0000 | |||
91 | +++ lib/lp/code/interfaces/branchsubscription.py 2010-05-28 04:18:17 +0000 | |||
92 | @@ -21,7 +21,8 @@ | |||
93 | 21 | from lp.code.interfaces.branch import IBranch | 21 | from lp.code.interfaces.branch import IBranch |
94 | 22 | from canonical.launchpad.fields import ParticipatingPersonChoice | 22 | from canonical.launchpad.fields import ParticipatingPersonChoice |
95 | 23 | from lazr.restful.declarations import ( | 23 | from lazr.restful.declarations import ( |
97 | 24 | export_as_webservice_entry, exported) | 24 | REQUEST_USER, call_with, export_as_webservice_entry, |
98 | 25 | export_read_operation, exported) | ||
99 | 25 | from lazr.restful.fields import Reference | 26 | from lazr.restful.fields import Reference |
100 | 26 | 27 | ||
101 | 27 | 28 | ||
102 | @@ -78,3 +79,8 @@ | |||
103 | 78 | title=_('Subscribed by'), required=True, | 79 | title=_('Subscribed by'), required=True, |
104 | 79 | vocabulary='ValidPersonOrTeam', readonly=True, | 80 | vocabulary='ValidPersonOrTeam', readonly=True, |
105 | 80 | description=_("The person who created this subscription."))) | 81 | description=_("The person who created this subscription."))) |
106 | 82 | |||
107 | 83 | @call_with(user=REQUEST_USER) | ||
108 | 84 | @export_read_operation() | ||
109 | 85 | def canBeUnsubscribedByUser(user): | ||
110 | 86 | """Can the user unsubscribe the subscriber from the branch?""" | ||
111 | 81 | 87 | ||
112 | === modified file 'lib/lp/code/model/branch.py' | |||
113 | --- lib/lp/code/model/branch.py 2010-05-25 04:37:11 +0000 | |||
114 | +++ lib/lp/code/model/branch.py 2010-05-28 09:04:16 +0000 | |||
115 | @@ -45,6 +45,7 @@ | |||
116 | 45 | from canonical.launchpad.webapp.interfaces import ( | 45 | from canonical.launchpad.webapp.interfaces import ( |
117 | 46 | IStoreSelector, MAIN_STORE, SLAVE_FLAVOR) | 46 | IStoreSelector, MAIN_STORE, SLAVE_FLAVOR) |
118 | 47 | 47 | ||
119 | 48 | from lp.app.errors import UserCannotUnsubscribePerson | ||
120 | 48 | from lp.code.bzr import ( | 49 | from lp.code.bzr import ( |
121 | 49 | BranchFormat, ControlFormat, CURRENT_BRANCH_FORMATS, | 50 | BranchFormat, ControlFormat, CURRENT_BRANCH_FORMATS, |
122 | 50 | CURRENT_REPOSITORY_FORMATS, RepositoryFormat) | 51 | CURRENT_REPOSITORY_FORMATS, RepositoryFormat) |
123 | @@ -705,9 +706,16 @@ | |||
124 | 705 | def unsubscribe(self, person, unsubscribed_by): | 706 | def unsubscribe(self, person, unsubscribed_by): |
125 | 706 | """See `IBranch`.""" | 707 | """See `IBranch`.""" |
126 | 707 | subscription = self.getSubscription(person) | 708 | subscription = self.getSubscription(person) |
127 | 709 | if subscription is None: | ||
128 | 710 | # Silent success seems order of the day (like bugs). | ||
129 | 711 | return | ||
130 | 712 | if not subscription.canBeUnsubscribedByUser(unsubscribed_by): | ||
131 | 713 | raise UserCannotUnsubscribePerson( | ||
132 | 714 | '%s does not have permission to unsubscribe %s.' % ( | ||
133 | 715 | unsubscribed_by.displayname, | ||
134 | 716 | person.displayname)) | ||
135 | 708 | store = Store.of(subscription) | 717 | store = Store.of(subscription) |
138 | 709 | assert subscription is not None, "User is not subscribed." | 718 | store.remove(subscription) |
137 | 710 | BranchSubscription.delete(subscription.id) | ||
139 | 711 | store.flush() | 719 | store.flush() |
140 | 712 | 720 | ||
141 | 713 | def getBranchRevision(self, sequence=None, revision=None, | 721 | def getBranchRevision(self, sequence=None, revision=None, |
142 | 714 | 722 | ||
143 | === modified file 'lib/lp/code/model/branchsubscription.py' | |||
144 | --- lib/lp/code/model/branchsubscription.py 2010-05-25 04:37:11 +0000 | |||
145 | +++ lib/lp/code/model/branchsubscription.py 2010-05-28 04:18:17 +0000 | |||
146 | @@ -13,13 +13,14 @@ | |||
147 | 13 | from canonical.database.constants import DEFAULT | 13 | from canonical.database.constants import DEFAULT |
148 | 14 | from canonical.database.sqlbase import SQLBase | 14 | from canonical.database.sqlbase import SQLBase |
149 | 15 | from canonical.database.enumcol import EnumCol | 15 | from canonical.database.enumcol import EnumCol |
151 | 16 | 16 | from canonical.launchpad.interfaces.launchpad import IPersonRoles | |
152 | 17 | from lp.code.enums import ( | 17 | from lp.code.enums import ( |
153 | 18 | BranchSubscriptionDiffSize, BranchSubscriptionNotificationLevel, | 18 | BranchSubscriptionDiffSize, BranchSubscriptionNotificationLevel, |
154 | 19 | CodeReviewNotificationLevel) | 19 | CodeReviewNotificationLevel) |
155 | 20 | from lp.code.interfaces.branchsubscription import IBranchSubscription | 20 | from lp.code.interfaces.branchsubscription import IBranchSubscription |
156 | 21 | from lp.code.interfaces.branch import IBranchNavigationMenu | 21 | from lp.code.interfaces.branch import IBranchNavigationMenu |
157 | 22 | from lp.code.interfaces.branchtarget import IHasBranchTarget | 22 | from lp.code.interfaces.branchtarget import IHasBranchTarget |
158 | 23 | from lp.code.security import BranchSubscriptionEdit | ||
159 | 23 | from lp.registry.interfaces.person import ( | 24 | from lp.registry.interfaces.person import ( |
160 | 24 | validate_person_not_private_membership) | 25 | validate_person_not_private_membership) |
161 | 25 | 26 | ||
162 | @@ -49,3 +50,10 @@ | |||
163 | 49 | def target(self): | 50 | def target(self): |
164 | 50 | """See `IHasBranchTarget`.""" | 51 | """See `IHasBranchTarget`.""" |
165 | 51 | return self.branch.target | 52 | return self.branch.target |
166 | 53 | |||
167 | 54 | def canBeUnsubscribedByUser(self, user): | ||
168 | 55 | """See `IBranchSubscription`.""" | ||
169 | 56 | if user is None: | ||
170 | 57 | return False | ||
171 | 58 | permission_check = BranchSubscriptionEdit(self) | ||
172 | 59 | return permission_check.checkAuthenticated(IPersonRoles(user)) | ||
173 | 52 | 60 | ||
174 | === added file 'lib/lp/code/model/tests/test_branchsubscription.py' | |||
175 | --- lib/lp/code/model/tests/test_branchsubscription.py 1970-01-01 00:00:00 +0000 | |||
176 | +++ lib/lp/code/model/tests/test_branchsubscription.py 2010-05-28 09:04:16 +0000 | |||
177 | @@ -0,0 +1,115 @@ | |||
178 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
179 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
180 | 3 | |||
181 | 4 | """Tests for the BranchSubscrptions model object..""" | ||
182 | 5 | |||
183 | 6 | from __future__ import with_statement | ||
184 | 7 | |||
185 | 8 | __metaclass__ = type | ||
186 | 9 | |||
187 | 10 | import unittest | ||
188 | 11 | |||
189 | 12 | from canonical.testing.layers import DatabaseFunctionalLayer | ||
190 | 13 | from lp.app.errors import UserCannotUnsubscribePerson | ||
191 | 14 | from lp.code.enums import ( | ||
192 | 15 | BranchSubscriptionNotificationLevel, CodeReviewNotificationLevel) | ||
193 | 16 | from lp.testing import TestCaseWithFactory, person_logged_in | ||
194 | 17 | |||
195 | 18 | |||
196 | 19 | class TestBranchSubscriptions(TestCaseWithFactory): | ||
197 | 20 | """Tests relating to branch subscriptions in general.""" | ||
198 | 21 | |||
199 | 22 | layer = DatabaseFunctionalLayer | ||
200 | 23 | |||
201 | 24 | def test_subscribed_by_set(self): | ||
202 | 25 | """The user subscribing is recorded along the subscriber.""" | ||
203 | 26 | subscriber = self.factory.makePerson() | ||
204 | 27 | subscribed_by = self.factory.makePerson() | ||
205 | 28 | branch = self.factory.makeAnyBranch() | ||
206 | 29 | subscription = branch.subscribe( | ||
207 | 30 | subscriber, BranchSubscriptionNotificationLevel.NOEMAIL, None, | ||
208 | 31 | CodeReviewNotificationLevel.NOEMAIL, subscribed_by) | ||
209 | 32 | self.assertEqual(subscriber, subscription.person) | ||
210 | 33 | self.assertEqual(subscribed_by, subscription.subscribed_by) | ||
211 | 34 | |||
212 | 35 | def test_unsubscribe(self): | ||
213 | 36 | """Test unsubscribing by the subscriber.""" | ||
214 | 37 | subscription = self.factory.makeBranchSubscription() | ||
215 | 38 | subscriber = subscription.person | ||
216 | 39 | branch = subscription.branch | ||
217 | 40 | branch.unsubscribe(subscriber, subscriber) | ||
218 | 41 | self.assertFalse(branch.hasSubscription(subscriber)) | ||
219 | 42 | |||
220 | 43 | def test_unsubscribe_by_subscriber(self): | ||
221 | 44 | """Test unsubscribing by the person who subscribed the user.""" | ||
222 | 45 | subscribed_by = self.factory.makePerson() | ||
223 | 46 | subscription = self.factory.makeBranchSubscription( | ||
224 | 47 | subscribed_by=subscribed_by) | ||
225 | 48 | subscriber = subscription.person | ||
226 | 49 | branch = subscription.branch | ||
227 | 50 | branch.unsubscribe(subscriber, subscribed_by) | ||
228 | 51 | self.assertFalse(branch.hasSubscription(subscriber)) | ||
229 | 52 | |||
230 | 53 | def test_unsubscribe_by_unauthorized(self): | ||
231 | 54 | """Test unsubscribing someone you shouldn't be able to.""" | ||
232 | 55 | subscription = self.factory.makeBranchSubscription() | ||
233 | 56 | branch = subscription.branch | ||
234 | 57 | self.assertRaises( | ||
235 | 58 | UserCannotUnsubscribePerson, | ||
236 | 59 | branch.unsubscribe, | ||
237 | 60 | subscription.person, | ||
238 | 61 | self.factory.makePerson()) | ||
239 | 62 | |||
240 | 63 | |||
241 | 64 | class TestBranchSubscriptionCanBeUnsubscribedbyUser(TestCaseWithFactory): | ||
242 | 65 | """Tests for BranchSubscription.canBeUnsubscribedByUser.""" | ||
243 | 66 | |||
244 | 67 | layer = DatabaseFunctionalLayer | ||
245 | 68 | |||
246 | 69 | def test_none(self): | ||
247 | 70 | """None for a user always returns False.""" | ||
248 | 71 | subscription = self.factory.makeBranchSubscription() | ||
249 | 72 | self.assertFalse(subscription.canBeUnsubscribedByUser(None)) | ||
250 | 73 | |||
251 | 74 | def test_self_subscriber(self): | ||
252 | 75 | """The subscriber has permission to unsubscribe.""" | ||
253 | 76 | subscription = self.factory.makeBranchSubscription() | ||
254 | 77 | self.assertTrue( | ||
255 | 78 | subscription.canBeUnsubscribedByUser(subscription.person)) | ||
256 | 79 | |||
257 | 80 | def test_non_subscriber_fails(self): | ||
258 | 81 | """An unrelated person can't unsubscribe a user.""" | ||
259 | 82 | subscription = self.factory.makeBranchSubscription() | ||
260 | 83 | editor = self.factory.makePerson() | ||
261 | 84 | self.assertFalse(subscription.canBeUnsubscribedByUser(editor)) | ||
262 | 85 | |||
263 | 86 | def test_subscribed_by(self): | ||
264 | 87 | """If a user subscribes someone else, the user can unsubscribe.""" | ||
265 | 88 | subscribed_by = self.factory.makePerson() | ||
266 | 89 | subscriber = self.factory.makePerson() | ||
267 | 90 | subscription = self.factory.makeBranchSubscription( | ||
268 | 91 | person=subscriber, subscribed_by=subscribed_by) | ||
269 | 92 | self.assertTrue(subscription.canBeUnsubscribedByUser(subscribed_by)) | ||
270 | 93 | |||
271 | 94 | def test_team_member_can_unsubscribe(self): | ||
272 | 95 | """Any team member can unsubscribe the team from a branch.""" | ||
273 | 96 | team = self.factory.makeTeam() | ||
274 | 97 | member = self.factory.makePerson() | ||
275 | 98 | with person_logged_in(team.teamowner): | ||
276 | 99 | team.addMember(member, team.teamowner) | ||
277 | 100 | subscription = self.factory.makeBranchSubscription( | ||
278 | 101 | person=team, subscribed_by=team.teamowner) | ||
279 | 102 | self.assertTrue(subscription.canBeUnsubscribedByUser(member)) | ||
280 | 103 | |||
281 | 104 | def test_team_subscriber_can_unsubscribe(self): | ||
282 | 105 | """A team can be unsubscribed by the subscriber even if they are not a | ||
283 | 106 | member.""" | ||
284 | 107 | team = self.factory.makeTeam() | ||
285 | 108 | subscribed_by = self.factory.makePerson() | ||
286 | 109 | subscription = self.factory.makeBranchSubscription( | ||
287 | 110 | person=team, subscribed_by=subscribed_by) | ||
288 | 111 | self.assertTrue(subscription.canBeUnsubscribedByUser(subscribed_by)) | ||
289 | 112 | |||
290 | 113 | |||
291 | 114 | def test_suite(): | ||
292 | 115 | return unittest.TestLoader().loadTestsFromName(__name__) | ||
293 | 0 | 116 | ||
294 | === added file 'lib/lp/code/security.py' | |||
295 | --- lib/lp/code/security.py 1970-01-01 00:00:00 +0000 | |||
296 | +++ lib/lp/code/security.py 2010-05-28 04:18:17 +0000 | |||
297 | @@ -0,0 +1,37 @@ | |||
298 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
299 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
300 | 3 | |||
301 | 4 | """Security adapters for the code module.""" | ||
302 | 5 | |||
303 | 6 | __metaclass__ = type | ||
304 | 7 | __all__ = [ | ||
305 | 8 | 'BranchSubscriptionEdit', | ||
306 | 9 | 'BranchSubscriptionView', | ||
307 | 10 | ] | ||
308 | 11 | |||
309 | 12 | from canonical.launchpad.security import AuthorizationBase | ||
310 | 13 | |||
311 | 14 | from lp.code.interfaces.branchsubscription import IBranchSubscription | ||
312 | 15 | |||
313 | 16 | |||
314 | 17 | |||
315 | 18 | class BranchSubscriptionEdit(AuthorizationBase): | ||
316 | 19 | permission = 'launchpad.Edit' | ||
317 | 20 | usedfor = IBranchSubscription | ||
318 | 21 | |||
319 | 22 | def checkAuthenticated(self, user): | ||
320 | 23 | """Is the user able to edit a branch subscription? | ||
321 | 24 | |||
322 | 25 | Any team member can edit a branch subscription for their team. | ||
323 | 26 | Launchpad Admins can also edit any branch subscription. | ||
324 | 27 | """ | ||
325 | 28 | return (user.inTeam(self.obj.person) or | ||
326 | 29 | user.inTeam(self.obj.subscribed_by) or | ||
327 | 30 | user.in_admin or | ||
328 | 31 | user.in_bazaar_experts) | ||
329 | 32 | |||
330 | 33 | |||
331 | 34 | class BranchSubscriptionView(BranchSubscriptionEdit): | ||
332 | 35 | permission = 'launchpad.View' | ||
333 | 36 | |||
334 | 37 | |||
335 | 0 | 38 | ||
336 | === modified file 'lib/lp/testing/factory.py' | |||
337 | --- lib/lp/testing/factory.py 2010-05-25 01:24:43 +0000 | |||
338 | +++ lib/lp/testing/factory.py 2010-05-28 09:31:03 +0000 | |||
339 | @@ -986,7 +986,8 @@ | |||
340 | 986 | 986 | ||
341 | 987 | return proposal | 987 | return proposal |
342 | 988 | 988 | ||
344 | 989 | def makeBranchSubscription(self, branch=None, person=None, subscribed_by=None): | 989 | def makeBranchSubscription(self, branch=None, person=None, |
345 | 990 | subscribed_by=None): | ||
346 | 990 | """Create a BranchSubscription. | 991 | """Create a BranchSubscription. |
347 | 991 | 992 | ||
348 | 992 | :param branch_title: The title to use for the created Branch | 993 | :param branch_title: The title to use for the created Branch |
Tim Penhey (thumper) wrote : | # |
On Fri, 28 May 2010 13:58:22 you wrote:
> Review: Needs Information release-critical
>
> > === modified file 'lib/lp/
> > --- lib/lp/
> > +++ lib/lp/
> > @@ -354,6 +354,7 @@
> >
> > def initialize(self):
> > self.notices = []
> >
> > + # TODO: FIXME - this is almost certainly not what we want.
> >
> > self._add_
>
> What do you want to do here?
Looks like it isn't used:
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -354,19 +354,6 @@
def initialize(self):
- # TODO: FIXME - this is almost certainly not what we want.
- self._add_
-
- def _add_subscripti
- """Add the appropriate notice after posting the subscription form."""
- if self.user and self.request.method == 'POST':
- newsub = self.request.
- if newsub == 'Subscribe':
- self.context.
- self.notices.
- elif newsub == 'Unsubscribe':
- self.context.
- self.notices.
branch.")
def user_is_
"""Is the current user subscribed to this branch?"""
Curtis Hovey (sinzui) wrote : | # |
Thanks for addressing this issue Tim. This branch is good to land.
Stuart Bishop (stub) wrote : | # |
Looks good. patch-2207-60-0.sql
Preview Diff
1 | === modified file 'database/sampledata/current-dev.sql' | |||
2 | --- database/sampledata/current-dev.sql 2010-05-27 22:18:16 +0000 | |||
3 | +++ database/sampledata/current-dev.sql 2010-05-29 09:01:05 +0000 | |||
4 | @@ -3131,10 +3131,10 @@ | |||
5 | 3131 | 3131 | ||
6 | 3132 | ALTER TABLE branchsubscription DISABLE TRIGGER ALL; | 3132 | ALTER TABLE branchsubscription DISABLE TRIGGER ALL; |
7 | 3133 | 3133 | ||
12 | 3134 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level) VALUES (1, 12, 20, '2006-10-16 18:31:43.079375', 1, NULL, 0); | 3134 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (1, 12, 20, '2006-10-16 18:31:43.079375', 1, NULL, 0, 12); |
13 | 3135 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level) VALUES (2, 12, 24, '2006-10-16 18:31:43.080236', 1, NULL, 0); | 3135 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (2, 12, 24, '2006-10-16 18:31:43.080236', 1, NULL, 0, 12); |
14 | 3136 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level) VALUES (4, 64, 29, '2007-05-28 02:41:07.938677', 1, NULL, 0); | 3136 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (4, 64, 29, '2007-05-28 02:41:07.938677', 1, NULL, 0, 64); |
15 | 3137 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level) VALUES (5, 64, 30, '2007-05-28 02:41:07.938677', 1, NULL, 0); | 3137 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (5, 64, 30, '2007-05-28 02:41:07.938677', 1, NULL, 0, 64); |
16 | 3138 | 3138 | ||
17 | 3139 | 3139 | ||
18 | 3140 | ALTER TABLE branchsubscription ENABLE TRIGGER ALL; | 3140 | ALTER TABLE branchsubscription ENABLE TRIGGER ALL; |
19 | 3141 | 3141 | ||
20 | === modified file 'database/sampledata/current.sql' | |||
21 | --- database/sampledata/current.sql 2010-05-27 22:18:16 +0000 | |||
22 | +++ database/sampledata/current.sql 2010-05-29 09:01:05 +0000 | |||
23 | @@ -3089,10 +3089,10 @@ | |||
24 | 3089 | 3089 | ||
25 | 3090 | ALTER TABLE branchsubscription DISABLE TRIGGER ALL; | 3090 | ALTER TABLE branchsubscription DISABLE TRIGGER ALL; |
26 | 3091 | 3091 | ||
31 | 3092 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level) VALUES (1, 12, 20, '2006-10-16 18:31:43.079375', 1, NULL, 0); | 3092 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (1, 12, 20, '2006-10-16 18:31:43.079375', 1, NULL, 0, 12); |
32 | 3093 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level) VALUES (2, 12, 24, '2006-10-16 18:31:43.080236', 1, NULL, 0); | 3093 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (2, 12, 24, '2006-10-16 18:31:43.080236', 1, NULL, 0, 12); |
33 | 3094 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level) VALUES (4, 64, 29, '2007-05-28 02:41:07.938677', 1, NULL, 0); | 3094 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (4, 64, 29, '2007-05-28 02:41:07.938677', 1, NULL, 0, 64); |
34 | 3095 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level) VALUES (5, 64, 30, '2007-05-28 02:41:07.938677', 1, NULL, 0); | 3095 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (5, 64, 30, '2007-05-28 02:41:07.938677', 1, NULL, 0, 64); |
35 | 3096 | 3096 | ||
36 | 3097 | 3097 | ||
37 | 3098 | ALTER TABLE branchsubscription ENABLE TRIGGER ALL; | 3098 | ALTER TABLE branchsubscription ENABLE TRIGGER ALL; |
38 | 3099 | 3099 | ||
39 | === added file 'database/schema/patch-2207-60-0.sql' | |||
40 | --- database/schema/patch-2207-60-0.sql 1970-01-01 00:00:00 +0000 | |||
41 | +++ database/schema/patch-2207-60-0.sql 2010-05-29 09:01:05 +0000 | |||
42 | @@ -0,0 +1,18 @@ | |||
43 | 1 | -- Copyright 2010 Canonical Ltd. This software is licensed under the | ||
44 | 2 | -- GNU Affero General Public License version 3 (see the file LICENSE). | ||
45 | 3 | |||
46 | 4 | SET client_min_messages=ERROR; | ||
47 | 5 | |||
48 | 6 | ALTER TABLE BranchSubscription | ||
49 | 7 | ADD COLUMN subscribed_by integer REFERENCES Person; | ||
50 | 8 | |||
51 | 9 | UPDATE BranchSubscription | ||
52 | 10 | SET subscribed_by = person; | ||
53 | 11 | |||
54 | 12 | ALTER TABLE BranchSubscription ALTER COLUMN subscribed_by SET NOT NULL; | ||
55 | 13 | |||
56 | 14 | -- Index needed for person merging. | ||
57 | 15 | CREATE INDEX branchsubscription__subscribed_by__idx | ||
58 | 16 | ON BranchSubscription(subscribed_by); | ||
59 | 17 | |||
60 | 18 | INSERT INTO LaunchpadDatabaseRevision VALUES (2207, 60, 0); | ||
61 | 0 | 19 | ||
62 | === modified file 'lib/canonical/launchpad/mail/commands.py' | |||
63 | --- lib/canonical/launchpad/mail/commands.py 2010-04-16 15:06:55 +0000 | |||
64 | +++ lib/canonical/launchpad/mail/commands.py 2010-05-29 09:01:05 +0000 | |||
65 | @@ -23,7 +23,7 @@ | |||
66 | 23 | IDistributionSourcePackage, EmailProcessingError, | 23 | IDistributionSourcePackage, EmailProcessingError, |
67 | 24 | NotFoundError, CreateBugParams, IPillarNameSet, | 24 | NotFoundError, CreateBugParams, IPillarNameSet, |
68 | 25 | BugTargetNotFound, IProjectGroup, ISourcePackage, IProductSeries, | 25 | BugTargetNotFound, IProjectGroup, ISourcePackage, IProductSeries, |
70 | 26 | BugTaskStatus, UserCannotUnsubscribePerson) | 26 | BugTaskStatus) |
71 | 27 | from lazr.lifecycle.event import ( | 27 | from lazr.lifecycle.event import ( |
72 | 28 | ObjectModifiedEvent, ObjectCreatedEvent) | 28 | ObjectModifiedEvent, ObjectCreatedEvent) |
73 | 29 | from lazr.lifecycle.interfaces import ( | 29 | from lazr.lifecycle.interfaces import ( |
74 | @@ -34,6 +34,8 @@ | |||
75 | 34 | from canonical.launchpad.validators.name import valid_name | 34 | from canonical.launchpad.validators.name import valid_name |
76 | 35 | from canonical.launchpad.webapp.authorization import check_permission | 35 | from canonical.launchpad.webapp.authorization import check_permission |
77 | 36 | 36 | ||
78 | 37 | from lp.app.errors import UserCannotUnsubscribePerson | ||
79 | 38 | |||
80 | 37 | 39 | ||
81 | 38 | def normalize_arguments(string_args): | 40 | def normalize_arguments(string_args): |
82 | 39 | """Normalizes the string arguments. | 41 | """Normalizes the string arguments. |
83 | 40 | 42 | ||
84 | === modified file 'lib/canonical/launchpad/scripts/tests/test_garbo.py' | |||
85 | --- lib/canonical/launchpad/scripts/tests/test_garbo.py 2010-04-23 08:33:14 +0000 | |||
86 | +++ lib/canonical/launchpad/scripts/tests/test_garbo.py 2010-05-29 09:01:05 +0000 | |||
87 | @@ -19,7 +19,6 @@ | |||
88 | 19 | 19 | ||
89 | 20 | from canonical.config import config | 20 | from canonical.config import config |
90 | 21 | from canonical.database.constants import THIRTY_DAYS_AGO, UTC_NOW | 21 | from canonical.database.constants import THIRTY_DAYS_AGO, UTC_NOW |
91 | 22 | from canonical.launchpad.database.emailaddress import EmailAddress | ||
92 | 23 | from canonical.launchpad.database.message import Message | 22 | from canonical.launchpad.database.message import Message |
93 | 24 | from canonical.launchpad.database.oauth import OAuthNonce | 23 | from canonical.launchpad.database.oauth import OAuthNonce |
94 | 25 | from canonical.launchpad.database.openidconsumer import OpenIDConsumerNonce | 24 | from canonical.launchpad.database.openidconsumer import OpenIDConsumerNonce |
95 | @@ -42,7 +41,6 @@ | |||
96 | 42 | from lp.code.model.branchjob import BranchJob, BranchUpgradeJob | 41 | from lp.code.model.branchjob import BranchJob, BranchUpgradeJob |
97 | 43 | from lp.code.model.codeimportresult import CodeImportResult | 42 | from lp.code.model.codeimportresult import CodeImportResult |
98 | 44 | from lp.registry.interfaces.person import IPersonSet, PersonCreationRationale | 43 | from lp.registry.interfaces.person import IPersonSet, PersonCreationRationale |
99 | 45 | from lp.registry.model.person import Person | ||
100 | 46 | from lp.services.job.model.job import Job | 44 | from lp.services.job.model.job import Job |
101 | 47 | 45 | ||
102 | 48 | 46 | ||
103 | 49 | 47 | ||
104 | === modified file 'lib/canonical/launchpad/security.py' | |||
105 | --- lib/canonical/launchpad/security.py 2010-05-26 08:54:27 +0000 | |||
106 | +++ lib/canonical/launchpad/security.py 2010-05-29 09:01:05 +0000 | |||
107 | @@ -24,8 +24,6 @@ | |||
108 | 24 | IBranch, user_has_special_branch_access) | 24 | IBranch, user_has_special_branch_access) |
109 | 25 | from lp.code.interfaces.branchmergeproposal import ( | 25 | from lp.code.interfaces.branchmergeproposal import ( |
110 | 26 | IBranchMergeProposal) | 26 | IBranchMergeProposal) |
111 | 27 | from lp.code.interfaces.branchsubscription import ( | ||
112 | 28 | IBranchSubscription) | ||
113 | 29 | from lp.code.interfaces.sourcepackagerecipe import ISourcePackageRecipe | 27 | from lp.code.interfaces.sourcepackagerecipe import ISourcePackageRecipe |
114 | 30 | from lp.code.interfaces.sourcepackagerecipebuild import ( | 28 | from lp.code.interfaces.sourcepackagerecipebuild import ( |
115 | 31 | ISourcePackageRecipeBuild) | 29 | ISourcePackageRecipeBuild) |
116 | @@ -63,8 +61,7 @@ | |||
117 | 63 | from lp.services.worlddata.interfaces.language import ILanguage, ILanguageSet | 61 | from lp.services.worlddata.interfaces.language import ILanguage, ILanguageSet |
118 | 64 | from lp.translations.interfaces.languagepack import ILanguagePack | 62 | from lp.translations.interfaces.languagepack import ILanguagePack |
119 | 65 | from canonical.launchpad.interfaces.launchpad import ( | 63 | from canonical.launchpad.interfaces.launchpad import ( |
122 | 66 | IBazaarApplication, IHasBug, IHasDrivers, ILaunchpadCelebrities, | 64 | IHasBug, IHasDrivers, ILaunchpadCelebrities, IPersonRoles) |
121 | 67 | IPersonRoles) | ||
123 | 68 | from lp.registry.interfaces.role import IHasOwner | 65 | from lp.registry.interfaces.role import IHasOwner |
124 | 69 | from lp.registry.interfaces.location import IPersonLocation | 66 | from lp.registry.interfaces.location import IPersonLocation |
125 | 70 | from lp.registry.interfaces.mailinglist import IMailingListSet | 67 | from lp.registry.interfaces.mailinglist import IMailingListSet |
126 | @@ -1749,25 +1746,6 @@ | |||
127 | 1749 | self.obj.distribution).checkAuthenticated(user)) | 1746 | self.obj.distribution).checkAuthenticated(user)) |
128 | 1750 | 1747 | ||
129 | 1751 | 1748 | ||
130 | 1752 | class BranchSubscriptionEdit(AuthorizationBase): | ||
131 | 1753 | permission = 'launchpad.Edit' | ||
132 | 1754 | usedfor = IBranchSubscription | ||
133 | 1755 | |||
134 | 1756 | def checkAuthenticated(self, user): | ||
135 | 1757 | """Is the user able to edit a branch subscription? | ||
136 | 1758 | |||
137 | 1759 | Any team member can edit a branch subscription for their team. | ||
138 | 1760 | Launchpad Admins can also edit any branch subscription. | ||
139 | 1761 | """ | ||
140 | 1762 | return (user.inTeam(self.obj.person) or | ||
141 | 1763 | user.in_admin or | ||
142 | 1764 | user.in_bazaar_experts) | ||
143 | 1765 | |||
144 | 1766 | |||
145 | 1767 | class BranchSubscriptionView(BranchSubscriptionEdit): | ||
146 | 1768 | permission = 'launchpad.View' | ||
147 | 1769 | |||
148 | 1770 | |||
149 | 1771 | class BranchMergeProposalView(AuthorizationBase): | 1749 | class BranchMergeProposalView(AuthorizationBase): |
150 | 1772 | permission = 'launchpad.View' | 1750 | permission = 'launchpad.View' |
151 | 1773 | usedfor = IBranchMergeProposal | 1751 | usedfor = IBranchMergeProposal |
152 | 1774 | 1752 | ||
153 | === added file 'lib/lp/app/errors.py' | |||
154 | --- lib/lp/app/errors.py 1970-01-01 00:00:00 +0000 | |||
155 | +++ lib/lp/app/errors.py 2010-05-29 09:01:05 +0000 | |||
156 | @@ -0,0 +1,18 @@ | |||
157 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
158 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
159 | 3 | |||
160 | 4 | """Cross application type errors for launchpad.""" | ||
161 | 5 | |||
162 | 6 | __metaclass__ = type | ||
163 | 7 | __all__ = [ | ||
164 | 8 | 'UserCannotUnsubscribePerson', | ||
165 | 9 | ] | ||
166 | 10 | |||
167 | 11 | from zope.security.interfaces import Unauthorized | ||
168 | 12 | |||
169 | 13 | from lazr.restful.declarations import webservice_error | ||
170 | 14 | |||
171 | 15 | |||
172 | 16 | class UserCannotUnsubscribePerson(Unauthorized): | ||
173 | 17 | """User does not have persmisson to unsubscribe person or team.""" | ||
174 | 18 | webservice_error(401) | ||
175 | 0 | 19 | ||
176 | === modified file 'lib/lp/bugs/interfaces/bug.py' | |||
177 | --- lib/lp/bugs/interfaces/bug.py 2010-04-29 17:49:19 +0000 | |||
178 | +++ lib/lp/bugs/interfaces/bug.py 2010-05-29 09:01:05 +0000 | |||
179 | @@ -20,7 +20,6 @@ | |||
180 | 20 | 'IProjectGroupBugAddForm', | 20 | 'IProjectGroupBugAddForm', |
181 | 21 | 'InvalidBugTargetType', | 21 | 'InvalidBugTargetType', |
182 | 22 | 'InvalidDuplicateValue', | 22 | 'InvalidDuplicateValue', |
183 | 23 | 'UserCannotUnsubscribePerson', | ||
184 | 24 | ] | 23 | ] |
185 | 25 | 24 | ||
186 | 26 | from zope.component import getUtility | 25 | from zope.component import getUtility |
187 | @@ -28,7 +27,6 @@ | |||
188 | 28 | from zope.schema import ( | 27 | from zope.schema import ( |
189 | 29 | Bool, Bytes, Choice, Datetime, Int, List, Object, Text, TextLine) | 28 | Bool, Bytes, Choice, Datetime, Int, List, Object, Text, TextLine) |
190 | 30 | from zope.schema.vocabulary import SimpleVocabulary | 29 | from zope.schema.vocabulary import SimpleVocabulary |
191 | 31 | from zope.security.interfaces import Unauthorized | ||
192 | 32 | 30 | ||
193 | 33 | from canonical.launchpad import _ | 31 | from canonical.launchpad import _ |
194 | 34 | from canonical.launchpad.fields import ( | 32 | from canonical.launchpad.fields import ( |
195 | @@ -805,11 +803,6 @@ | |||
196 | 805 | webservice_error(417) | 803 | webservice_error(417) |
197 | 806 | 804 | ||
198 | 807 | 805 | ||
199 | 808 | class UserCannotUnsubscribePerson(Unauthorized): | ||
200 | 809 | """User does not have persmisson to unsubscribe person or team.""" | ||
201 | 810 | webservice_error(401) | ||
202 | 811 | |||
203 | 812 | |||
204 | 813 | # We are forced to define these now to avoid circular import problems. | 806 | # We are forced to define these now to avoid circular import problems. |
205 | 814 | IBugAttachment['bug'].schema = IBug | 807 | IBugAttachment['bug'].schema = IBug |
206 | 815 | IBugWatch['bug'].schema = IBug | 808 | IBugWatch['bug'].schema = IBug |
207 | 816 | 809 | ||
208 | === modified file 'lib/lp/bugs/model/bug.py' | |||
209 | --- lib/lp/bugs/model/bug.py 2010-05-25 15:48:47 +0000 | |||
210 | +++ lib/lp/bugs/model/bug.py 2010-05-29 09:01:05 +0000 | |||
211 | @@ -64,12 +64,13 @@ | |||
212 | 64 | IStoreSelector, DEFAULT_FLAVOR, MAIN_STORE, NotFoundError) | 64 | IStoreSelector, DEFAULT_FLAVOR, MAIN_STORE, NotFoundError) |
213 | 65 | 65 | ||
214 | 66 | from lp.answers.interfaces.questiontarget import IQuestionTarget | 66 | from lp.answers.interfaces.questiontarget import IQuestionTarget |
215 | 67 | from lp.app.errors import UserCannotUnsubscribePerson | ||
216 | 67 | from lp.bugs.adapters.bugchange import ( | 68 | from lp.bugs.adapters.bugchange import ( |
217 | 68 | BranchLinkedToBug, BranchUnlinkedFromBug, BugConvertedToQuestion, | 69 | BranchLinkedToBug, BranchUnlinkedFromBug, BugConvertedToQuestion, |
218 | 69 | BugWatchAdded, BugWatchRemoved, SeriesNominated, UnsubscribedFromBug) | 70 | BugWatchAdded, BugWatchRemoved, SeriesNominated, UnsubscribedFromBug) |
219 | 70 | from lp.bugs.interfaces.bug import ( | 71 | from lp.bugs.interfaces.bug import ( |
220 | 71 | IBug, IBugBecameQuestionEvent, IBugSet, IFileBugData, | 72 | IBug, IBugBecameQuestionEvent, IBugSet, IFileBugData, |
222 | 72 | InvalidDuplicateValue, UserCannotUnsubscribePerson) | 73 | InvalidDuplicateValue) |
223 | 73 | from lp.bugs.interfaces.bugactivity import IBugActivitySet | 74 | from lp.bugs.interfaces.bugactivity import IBugActivitySet |
224 | 74 | from lp.bugs.interfaces.bugattachment import ( | 75 | from lp.bugs.interfaces.bugattachment import ( |
225 | 75 | BugAttachmentType, IBugAttachmentSet) | 76 | BugAttachmentType, IBugAttachmentSet) |
226 | 76 | 77 | ||
227 | === modified file 'lib/lp/code/browser/branch.py' | |||
228 | --- lib/lp/code/browser/branch.py 2010-05-27 01:46:06 +0000 | |||
229 | +++ lib/lp/code/browser/branch.py 2010-05-29 09:01:05 +0000 | |||
230 | @@ -356,18 +356,6 @@ | |||
231 | 356 | 356 | ||
232 | 357 | def initialize(self): | 357 | def initialize(self): |
233 | 358 | self.notices = [] | 358 | self.notices = [] |
234 | 359 | self._add_subscription_notice() | ||
235 | 360 | |||
236 | 361 | def _add_subscription_notice(self): | ||
237 | 362 | """Add the appropriate notice after posting the subscription form.""" | ||
238 | 363 | if self.user and self.request.method == 'POST': | ||
239 | 364 | newsub = self.request.form.get('subscribe', None) | ||
240 | 365 | if newsub == 'Subscribe': | ||
241 | 366 | self.context.subscribe(self.user) | ||
242 | 367 | self.notices.append("You have subscribed to this branch.") | ||
243 | 368 | elif newsub == 'Unsubscribe': | ||
244 | 369 | self.context.unsubscribe(self.user) | ||
245 | 370 | self.notices.append("You have unsubscribed from this branch.") | ||
246 | 371 | 359 | ||
247 | 372 | def user_is_subscribed(self): | 360 | def user_is_subscribed(self): |
248 | 373 | """Is the current user subscribed to this branch?""" | 361 | """Is the current user subscribed to this branch?""" |
249 | 374 | 362 | ||
250 | === modified file 'lib/lp/code/browser/branchsubscription.py' | |||
251 | --- lib/lp/code/browser/branchsubscription.py 2010-04-20 01:21:10 +0000 | |||
252 | +++ lib/lp/code/browser/branchsubscription.py 2010-05-29 09:01:05 +0000 | |||
253 | @@ -12,10 +12,9 @@ | |||
254 | 12 | 'BranchSubscriptionPrimaryContext', | 12 | 'BranchSubscriptionPrimaryContext', |
255 | 13 | ] | 13 | ] |
256 | 14 | 14 | ||
258 | 15 | from zope.component import getUtility | 15 | |
259 | 16 | from zope.interface import implements | 16 | from zope.interface import implements |
260 | 17 | 17 | ||
261 | 18 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities | ||
262 | 19 | from canonical.launchpad.webapp import ( | 18 | from canonical.launchpad.webapp import ( |
263 | 20 | action, canonical_url, LaunchpadEditFormView, LaunchpadFormView, | 19 | action, canonical_url, LaunchpadEditFormView, LaunchpadFormView, |
264 | 21 | LaunchpadView) | 20 | LaunchpadView) |
265 | @@ -118,7 +117,8 @@ | |||
266 | 118 | review_level = data['review_level'] | 117 | review_level = data['review_level'] |
267 | 119 | 118 | ||
268 | 120 | self.context.subscribe( | 119 | self.context.subscribe( |
270 | 121 | self.user, notification_level, max_diff_lines, review_level) | 120 | self.user, notification_level, max_diff_lines, review_level, |
271 | 121 | self.user) | ||
272 | 122 | 122 | ||
273 | 123 | self.add_notification_message( | 123 | self.add_notification_message( |
274 | 124 | 'You have subscribed to this branch with: ', | 124 | 'You have subscribed to this branch with: ', |
275 | @@ -171,7 +171,7 @@ | |||
276 | 171 | def unsubscribe(self, action, data): | 171 | def unsubscribe(self, action, data): |
277 | 172 | # Be proactive in the checking to catch the stale post problem. | 172 | # Be proactive in the checking to catch the stale post problem. |
278 | 173 | if self.context.hasSubscription(self.user): | 173 | if self.context.hasSubscription(self.user): |
280 | 174 | self.context.unsubscribe(self.user) | 174 | self.context.unsubscribe(self.user, self.user) |
281 | 175 | self.request.response.addNotification( | 175 | self.request.response.addNotification( |
282 | 176 | "You have unsubscribed from this branch.") | 176 | "You have unsubscribed from this branch.") |
283 | 177 | else: | 177 | else: |
284 | @@ -209,7 +209,8 @@ | |||
285 | 209 | subscription = self.context.getSubscription(person) | 209 | subscription = self.context.getSubscription(person) |
286 | 210 | if subscription is None: | 210 | if subscription is None: |
287 | 211 | self.context.subscribe( | 211 | self.context.subscribe( |
289 | 212 | person, notification_level, max_diff_lines, review_level) | 212 | person, notification_level, max_diff_lines, review_level, |
290 | 213 | self.user) | ||
291 | 213 | 214 | ||
292 | 214 | self.add_notification_message( | 215 | self.add_notification_message( |
293 | 215 | '%s has been subscribed to this branch with: ' | 216 | '%s has been subscribed to this branch with: ' |
294 | @@ -222,24 +223,6 @@ | |||
295 | 222 | subscription.notification_level, subscription.max_diff_lines, | 223 | subscription.notification_level, subscription.max_diff_lines, |
296 | 223 | review_level) | 224 | review_level) |
297 | 224 | 225 | ||
298 | 225 | def validate(self, data): | ||
299 | 226 | """Ensure that when a team is subscribed, the user is a member.""" | ||
300 | 227 | celebs = getUtility(ILaunchpadCelebrities) | ||
301 | 228 | # An admin or bzr expert can subscribe anyone. | ||
302 | 229 | if self.user.inTeam(celebs.admin) or ( | ||
303 | 230 | self.user.inTeam(celebs.bazaar_experts)): | ||
304 | 231 | return | ||
305 | 232 | |||
306 | 233 | person = data.get('person') | ||
307 | 234 | if (person is not None and | ||
308 | 235 | person.isTeam() and | ||
309 | 236 | not self.user.inTeam(person)): | ||
310 | 237 | # A person can only subscribe a team if they are members | ||
311 | 238 | # of that team. | ||
312 | 239 | self.setFieldError( | ||
313 | 240 | 'person', | ||
314 | 241 | "You can only subscribe teams that you are a member of.") | ||
315 | 242 | |||
316 | 243 | 226 | ||
317 | 244 | class BranchSubscriptionEditView(LaunchpadEditFormView): | 227 | class BranchSubscriptionEditView(LaunchpadEditFormView): |
318 | 245 | """The view for editing branch subscriptions. | 228 | """The view for editing branch subscriptions. |
319 | @@ -273,7 +256,7 @@ | |||
320 | 273 | @action("Unsubscribe", name="unsubscribe") | 256 | @action("Unsubscribe", name="unsubscribe") |
321 | 274 | def unsubscribe_action(self, action, data): | 257 | def unsubscribe_action(self, action, data): |
322 | 275 | """Unsubscribe the team from the branch.""" | 258 | """Unsubscribe the team from the branch.""" |
324 | 276 | self.branch.unsubscribe(self.person) | 259 | self.branch.unsubscribe(self.person, self.user) |
325 | 277 | self.request.response.addNotification( | 260 | self.request.response.addNotification( |
326 | 278 | "%s has been unsubscribed from this branch." | 261 | "%s has been unsubscribed from this branch." |
327 | 279 | % self.person.displayname) | 262 | % self.person.displayname) |
328 | 280 | 263 | ||
329 | === modified file 'lib/lp/code/browser/codeimport.py' | |||
330 | --- lib/lp/code/browser/codeimport.py 2010-04-28 02:49:58 +0000 | |||
331 | +++ lib/lp/code/browser/codeimport.py 2010-05-29 09:01:05 +0000 | |||
332 | @@ -392,7 +392,8 @@ | |||
333 | 392 | self.user, | 392 | self.user, |
334 | 393 | BranchSubscriptionNotificationLevel.FULL, | 393 | BranchSubscriptionNotificationLevel.FULL, |
335 | 394 | BranchSubscriptionDiffSize.NODIFF, | 394 | BranchSubscriptionDiffSize.NODIFF, |
337 | 395 | CodeReviewNotificationLevel.NOEMAIL) | 395 | CodeReviewNotificationLevel.NOEMAIL, |
338 | 396 | self.user) | ||
339 | 396 | 397 | ||
340 | 397 | self.next_url = canonical_url(code_import.branch) | 398 | self.next_url = canonical_url(code_import.branch) |
341 | 398 | 399 | ||
342 | 399 | 400 | ||
343 | === modified file 'lib/lp/code/configure.zcml' | |||
344 | --- lib/lp/code/configure.zcml 2010-05-19 15:39:52 +0000 | |||
345 | +++ lib/lp/code/configure.zcml 2010-05-29 09:01:05 +0000 | |||
346 | @@ -11,6 +11,7 @@ | |||
347 | 11 | xmlns:webservice="http://namespaces.canonical.com/webservice" | 11 | xmlns:webservice="http://namespaces.canonical.com/webservice" |
348 | 12 | i18n_domain="launchpad"> | 12 | i18n_domain="launchpad"> |
349 | 13 | <include package=".browser"/> | 13 | <include package=".browser"/> |
350 | 14 | <authorizations module="lp.code.security" /> | ||
351 | 14 | 15 | ||
352 | 15 | <!-- Branch Merge Queue --> | 16 | <!-- Branch Merge Queue --> |
353 | 16 | 17 | ||
354 | 17 | 18 | ||
355 | === modified file 'lib/lp/code/doc/branch-merge-proposal-notifications.txt' | |||
356 | --- lib/lp/code/doc/branch-merge-proposal-notifications.txt 2010-04-06 01:21:33 +0000 | |||
357 | +++ lib/lp/code/doc/branch-merge-proposal-notifications.txt 2010-05-29 09:01:05 +0000 | |||
358 | @@ -30,13 +30,13 @@ | |||
359 | 30 | >>> _unused = bmp.source_branch.subscribe(source_subscriber, | 30 | >>> _unused = bmp.source_branch.subscribe(source_subscriber, |
360 | 31 | ... BranchSubscriptionNotificationLevel.NOEMAIL, | 31 | ... BranchSubscriptionNotificationLevel.NOEMAIL, |
361 | 32 | ... BranchSubscriptionDiffSize.NODIFF, | 32 | ... BranchSubscriptionDiffSize.NODIFF, |
363 | 33 | ... CodeReviewNotificationLevel.STATUS) | 33 | ... CodeReviewNotificationLevel.STATUS, source_subscriber) |
364 | 34 | >>> target_subscriber = factory.makePerson( | 34 | >>> target_subscriber = factory.makePerson( |
365 | 35 | ... email='target@example.com', displayname='Target Subscriber') | 35 | ... email='target@example.com', displayname='Target Subscriber') |
366 | 36 | >>> target_subscription = bmp.target_branch.subscribe(target_subscriber, | 36 | >>> target_subscription = bmp.target_branch.subscribe(target_subscriber, |
367 | 37 | ... BranchSubscriptionNotificationLevel.NOEMAIL, | 37 | ... BranchSubscriptionNotificationLevel.NOEMAIL, |
368 | 38 | ... BranchSubscriptionDiffSize.NODIFF, | 38 | ... BranchSubscriptionDiffSize.NODIFF, |
370 | 39 | ... CodeReviewNotificationLevel.FULL) | 39 | ... CodeReviewNotificationLevel.FULL, target_subscriber) |
371 | 40 | 40 | ||
372 | 41 | The owners of the branches are subscribed when the branches are created. | 41 | The owners of the branches are subscribed when the branches are created. |
373 | 42 | 42 | ||
374 | @@ -69,8 +69,8 @@ | |||
375 | 69 | 69 | ||
376 | 70 | Now we will unsubscribe the branch owners to simplify the rest of the test. | 70 | Now we will unsubscribe the branch owners to simplify the rest of the test. |
377 | 71 | 71 | ||
380 | 72 | >>> bmp.source_branch.unsubscribe(source_owner) | 72 | >>> bmp.source_branch.unsubscribe(source_owner, source_owner) |
381 | 73 | >>> bmp.target_branch.unsubscribe(target_owner) | 73 | >>> bmp.target_branch.unsubscribe(target_owner, target_owner) |
382 | 74 | >>> recipients = bmp.getNotificationRecipients( | 74 | >>> recipients = bmp.getNotificationRecipients( |
383 | 75 | ... CodeReviewNotificationLevel.FULL) | 75 | ... CodeReviewNotificationLevel.FULL) |
384 | 76 | 76 | ||
385 | 77 | 77 | ||
386 | === modified file 'lib/lp/code/doc/branch-notifications.txt' | |||
387 | --- lib/lp/code/doc/branch-notifications.txt 2010-03-10 05:49:00 +0000 | |||
388 | +++ lib/lp/code/doc/branch-notifications.txt 2010-05-29 09:01:05 +0000 | |||
389 | @@ -40,7 +40,7 @@ | |||
390 | 40 | ... branch.owner, | 40 | ... branch.owner, |
391 | 41 | ... BranchSubscriptionNotificationLevel.FULL, | 41 | ... BranchSubscriptionNotificationLevel.FULL, |
392 | 42 | ... BranchSubscriptionDiffSize.WHOLEDIFF, | 42 | ... BranchSubscriptionDiffSize.WHOLEDIFF, |
394 | 43 | ... CodeReviewNotificationLevel.NOEMAIL) | 43 | ... CodeReviewNotificationLevel.NOEMAIL, branch.owner) |
395 | 44 | >>> BranchMailer.forRevision(branch, 1, 'foo@canonical.com', | 44 | >>> BranchMailer.forRevision(branch, 1, 'foo@canonical.com', |
396 | 45 | ... 'The contents.', None, 'Subject line').sendAll() | 45 | ... 'The contents.', None, 'Subject line').sendAll() |
397 | 46 | 46 | ||
398 | @@ -71,7 +71,7 @@ | |||
399 | 71 | You are subscribed to branch lp://dev/~name12/firefox/main. | 71 | You are subscribed to branch lp://dev/~name12/firefox/main. |
400 | 72 | To unsubscribe from this branch go to http://code.launchpad.dev/~name12/firefox/main/+edit-subscription | 72 | To unsubscribe from this branch go to http://code.launchpad.dev/~name12/firefox/main/+edit-subscription |
401 | 73 | <BLANKLINE> | 73 | <BLANKLINE> |
403 | 74 | >>> branch.unsubscribe(branch.owner) | 74 | >>> branch.unsubscribe(branch.owner, branch.owner) |
404 | 75 | 75 | ||
405 | 76 | 76 | ||
406 | 77 | == Subscriptions == | 77 | == Subscriptions == |
407 | @@ -98,63 +98,55 @@ | |||
408 | 98 | >>> from lp.code.interfaces.branch import IBranch, IBranchSet | 98 | >>> from lp.code.interfaces.branch import IBranch, IBranchSet |
409 | 99 | >>> personset = getUtility(IPersonSet) | 99 | >>> personset = getUtility(IPersonSet) |
410 | 100 | 100 | ||
413 | 101 | >>> branch.subscribe( | 101 | >>> def subscribe_user_by_email(branch, email, level, size, level2): |
414 | 102 | ... personset.getByEmail('no-priv@canonical.com'), | 102 | ... subscriber = personset.getByEmail(email) |
415 | 103 | ... branch.subscribe(subscriber, level, size, level2, subscriber) | ||
416 | 104 | |||
417 | 105 | >>> subscribe_user_by_email(branch, 'no-priv@canonical.com', | ||
418 | 103 | ... BranchSubscriptionNotificationLevel.NOEMAIL, | 106 | ... BranchSubscriptionNotificationLevel.NOEMAIL, |
419 | 104 | ... BranchSubscriptionDiffSize.NODIFF, | 107 | ... BranchSubscriptionDiffSize.NODIFF, |
420 | 105 | ... CodeReviewNotificationLevel.NOEMAIL) | 108 | ... CodeReviewNotificationLevel.NOEMAIL) |
421 | 106 | <BranchSubscription ... | ||
422 | 107 | 109 | ||
425 | 108 | >>> branch.subscribe( | 110 | >>> subscribe_user_by_email(branch, 'test@canonical.com', |
424 | 109 | ... personset.getByEmail('test@canonical.com'), | ||
426 | 110 | ... BranchSubscriptionNotificationLevel.ATTRIBUTEONLY, | 111 | ... BranchSubscriptionNotificationLevel.ATTRIBUTEONLY, |
427 | 111 | ... BranchSubscriptionDiffSize.NODIFF, | 112 | ... BranchSubscriptionDiffSize.NODIFF, |
428 | 112 | ... CodeReviewNotificationLevel.NOEMAIL) | 113 | ... CodeReviewNotificationLevel.NOEMAIL) |
429 | 113 | <BranchSubscription ... | ||
430 | 114 | 114 | ||
433 | 115 | >>> branch.subscribe( | 115 | >>> subscribe_user_by_email(branch, 'carlos@canonical.com', |
432 | 116 | ... personset.getByEmail('carlos@canonical.com'), | ||
434 | 117 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, | 116 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, |
435 | 118 | ... BranchSubscriptionDiffSize.NODIFF, | 117 | ... BranchSubscriptionDiffSize.NODIFF, |
436 | 119 | ... CodeReviewNotificationLevel.NOEMAIL) | 118 | ... CodeReviewNotificationLevel.NOEMAIL) |
437 | 120 | <BranchSubscription ... | ||
438 | 121 | 119 | ||
441 | 122 | >>> branch.subscribe( | 120 | >>> subscribe_user_by_email(branch, 'jeff.waugh@ubuntulinux.com', |
440 | 123 | ... personset.getByEmail('jeff.waugh@ubuntulinux.com'), | ||
442 | 124 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, | 121 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, |
443 | 125 | ... BranchSubscriptionDiffSize.HALFKLINES, | 122 | ... BranchSubscriptionDiffSize.HALFKLINES, |
444 | 126 | ... CodeReviewNotificationLevel.NOEMAIL) | 123 | ... CodeReviewNotificationLevel.NOEMAIL) |
445 | 127 | <BranchSubscription ... | ||
446 | 128 | 124 | ||
449 | 129 | >>> branch.subscribe( | 125 | >>> subscribe_user_by_email(branch, 'celso.providelo@canonical.com', |
448 | 130 | ... personset.getByEmail('celso.providelo@canonical.com'), | ||
450 | 131 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, | 126 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, |
451 | 132 | ... BranchSubscriptionDiffSize.ONEKLINES, | 127 | ... BranchSubscriptionDiffSize.ONEKLINES, |
452 | 133 | ... CodeReviewNotificationLevel.NOEMAIL) | 128 | ... CodeReviewNotificationLevel.NOEMAIL) |
453 | 134 | <BranchSubscription ... | ||
454 | 135 | 129 | ||
457 | 136 | >>> branch.subscribe( | 130 | >>> subscribe_user_by_email(branch, 'daf@canonical.com', |
456 | 137 | ... personset.getByEmail('daf@canonical.com'), | ||
458 | 138 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, | 131 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, |
459 | 139 | ... BranchSubscriptionDiffSize.FIVEKLINES, | 132 | ... BranchSubscriptionDiffSize.FIVEKLINES, |
460 | 140 | ... CodeReviewNotificationLevel.NOEMAIL) | 133 | ... CodeReviewNotificationLevel.NOEMAIL) |
461 | 141 | <BranchSubscription ... | ||
462 | 142 | 134 | ||
465 | 143 | >>> branch.subscribe( | 135 | >>> subscribe_user_by_email(branch, 'mark@example.com', |
464 | 144 | ... personset.getByEmail('mark@example.com'), | ||
466 | 145 | ... BranchSubscriptionNotificationLevel.FULL, | 136 | ... BranchSubscriptionNotificationLevel.FULL, |
467 | 146 | ... BranchSubscriptionDiffSize.WHOLEDIFF, | 137 | ... BranchSubscriptionDiffSize.WHOLEDIFF, |
468 | 147 | ... CodeReviewNotificationLevel.NOEMAIL) | 138 | ... CodeReviewNotificationLevel.NOEMAIL) |
469 | 148 | <BranchSubscription ... | ||
470 | 149 | 139 | ||
471 | 150 | Team are subscribed in the same way. | 140 | Team are subscribed in the same way. |
472 | 151 | 141 | ||
475 | 152 | >>> branch.subscribe( | 142 | >>> def subscribe_team_by_name(branch, name, level, size, level2): |
476 | 153 | ... personset.getByName('launchpad'), | 143 | ... team = personset.getByName(name) |
477 | 144 | ... branch.subscribe(team, level, size, level2, team.teamowner) | ||
478 | 145 | |||
479 | 146 | >>> subscribe_team_by_name(branch, 'launchpad', | ||
480 | 154 | ... BranchSubscriptionNotificationLevel.FULL, | 147 | ... BranchSubscriptionNotificationLevel.FULL, |
481 | 155 | ... BranchSubscriptionDiffSize.WHOLEDIFF, | 148 | ... BranchSubscriptionDiffSize.WHOLEDIFF, |
482 | 156 | ... CodeReviewNotificationLevel.NOEMAIL) | 149 | ... CodeReviewNotificationLevel.NOEMAIL) |
483 | 157 | <BranchSubscription ... | ||
484 | 158 | 150 | ||
485 | 159 | And to make sure we have them: | 151 | And to make sure we have them: |
486 | 160 | 152 | ||
487 | @@ -358,7 +350,7 @@ | |||
488 | 358 | Unsubscribe everybody. | 350 | Unsubscribe everybody. |
489 | 359 | 351 | ||
490 | 360 | >>> for subscription in branch.subscriptions: | 352 | >>> for subscription in branch.subscriptions: |
492 | 361 | ... branch.unsubscribe(subscription.person) | 353 | ... branch.unsubscribe(subscription.person, subscription.person) |
493 | 362 | >>> len(list(branch.subscriptions)) | 354 | >>> len(list(branch.subscriptions)) |
494 | 363 | 0 | 355 | 0 |
495 | 364 | 356 | ||
496 | @@ -373,29 +365,23 @@ | |||
497 | 373 | If a team is registered, and that team has an email address assigned, | 365 | If a team is registered, and that team has an email address assigned, |
498 | 374 | then that email address is used for the notifications. | 366 | then that email address is used for the notifications. |
499 | 375 | 367 | ||
502 | 376 | >>> branch.subscribe( | 368 | >>> subscribe_user_by_email(branch, 'david.allouche@canonical.com', |
501 | 377 | ... personset.getByEmail('david.allouche@canonical.com'), | ||
503 | 378 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, | 369 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, |
504 | 379 | ... BranchSubscriptionDiffSize.HALFKLINES, | 370 | ... BranchSubscriptionDiffSize.HALFKLINES, |
505 | 380 | ... CodeReviewNotificationLevel.NOEMAIL) | 371 | ... CodeReviewNotificationLevel.NOEMAIL) |
506 | 381 | <BranchSubscription ... | ||
507 | 382 | 372 | ||
510 | 383 | >>> branch.subscribe( | 373 | >>> subscribe_team_by_name(branch, 'vcs-imports', |
509 | 384 | ... personset.getByName('vcs-imports'), | ||
511 | 385 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, | 374 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, |
512 | 386 | ... BranchSubscriptionDiffSize.FIVEKLINES, | 375 | ... BranchSubscriptionDiffSize.FIVEKLINES, |
513 | 387 | ... CodeReviewNotificationLevel.NOEMAIL) | 376 | ... CodeReviewNotificationLevel.NOEMAIL) |
514 | 388 | <BranchSubscription ... | ||
515 | 389 | 377 | ||
516 | 390 | The ubuntu-team has an email address supplied (support@ubuntu.com), so | 378 | The ubuntu-team has an email address supplied (support@ubuntu.com), so |
517 | 391 | that is used rather than the email addresses of the seven members. | 379 | that is used rather than the email addresses of the seven members. |
518 | 392 | 380 | ||
521 | 393 | >>> branch.subscribe( | 381 | >>> subscribe_team_by_name(branch, 'ubuntu-team', |
520 | 394 | ... personset.getByName('ubuntu-team'), | ||
522 | 395 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, | 382 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, |
523 | 396 | ... BranchSubscriptionDiffSize.ONEKLINES, | 383 | ... BranchSubscriptionDiffSize.ONEKLINES, |
524 | 397 | ... CodeReviewNotificationLevel.NOEMAIL) | 384 | ... CodeReviewNotificationLevel.NOEMAIL) |
525 | 398 | <BranchSubscription ... | ||
526 | 399 | 385 | ||
527 | 400 | >>> recipients = branch.getNotificationRecipients() | 386 | >>> recipients = branch.getNotificationRecipients() |
528 | 401 | >>> for email in recipients.getEmails(): | 387 | >>> for email in recipients.getEmails(): |
529 | @@ -427,12 +413,10 @@ | |||
530 | 427 | 413 | ||
531 | 428 | Resubscribe our test user. | 414 | Resubscribe our test user. |
532 | 429 | 415 | ||
535 | 430 | >>> branch.subscribe( | 416 | >>> subscribe_user_by_email(branch, 'test@canonical.com', |
534 | 431 | ... personset.getByEmail('test@canonical.com'), | ||
536 | 432 | ... BranchSubscriptionNotificationLevel.ATTRIBUTEONLY, | 417 | ... BranchSubscriptionNotificationLevel.ATTRIBUTEONLY, |
537 | 433 | ... BranchSubscriptionDiffSize.NODIFF, | 418 | ... BranchSubscriptionDiffSize.NODIFF, |
538 | 434 | ... CodeReviewNotificationLevel.NOEMAIL) | 419 | ... CodeReviewNotificationLevel.NOEMAIL) |
539 | 435 | <BranchSubscription ... | ||
540 | 436 | 420 | ||
541 | 437 | >>> from zope.event import notify | 421 | >>> from zope.event import notify |
542 | 438 | >>> from lazr.lifecycle.event import ObjectModifiedEvent | 422 | >>> from lazr.lifecycle.event import ObjectModifiedEvent |
543 | 439 | 423 | ||
544 | === modified file 'lib/lp/code/doc/branch-visibility.txt' | |||
545 | --- lib/lp/code/doc/branch-visibility.txt 2010-01-12 14:52:41 +0000 | |||
546 | +++ lib/lp/code/doc/branch-visibility.txt 2010-05-29 09:01:05 +0000 | |||
547 | @@ -138,7 +138,7 @@ | |||
548 | 138 | >>> branch.subscribe(ubuntu_team, | 138 | >>> branch.subscribe(ubuntu_team, |
549 | 139 | ... BranchSubscriptionNotificationLevel.NOEMAIL, | 139 | ... BranchSubscriptionNotificationLevel.NOEMAIL, |
550 | 140 | ... BranchSubscriptionDiffSize.NODIFF, | 140 | ... BranchSubscriptionDiffSize.NODIFF, |
552 | 141 | ... CodeReviewNotificationLevel.NOEMAIL) | 141 | ... CodeReviewNotificationLevel.NOEMAIL, ubuntu_team.teamowner) |
553 | 142 | <BranchSubscription ...> | 142 | <BranchSubscription ...> |
554 | 143 | 143 | ||
555 | 144 | >>> access.checkAccountAuthenticated(jdub.account) | 144 | >>> access.checkAccountAuthenticated(jdub.account) |
556 | 145 | 145 | ||
557 | === modified file 'lib/lp/code/doc/branch.txt' | |||
558 | --- lib/lp/code/doc/branch.txt 2010-01-12 21:37:34 +0000 | |||
559 | +++ lib/lp/code/doc/branch.txt 2010-05-29 09:01:05 +0000 | |||
560 | @@ -310,7 +310,7 @@ | |||
561 | 310 | ... subscriber, | 310 | ... subscriber, |
562 | 311 | ... BranchSubscriptionNotificationLevel.FULL, | 311 | ... BranchSubscriptionNotificationLevel.FULL, |
563 | 312 | ... BranchSubscriptionDiffSize.FIVEKLINES, | 312 | ... BranchSubscriptionDiffSize.FIVEKLINES, |
565 | 313 | ... CodeReviewNotificationLevel.FULL) | 313 | ... CodeReviewNotificationLevel.FULL, subscriber) |
566 | 314 | >>> verifyObject(IBranchSubscription, subscription) | 314 | >>> verifyObject(IBranchSubscription, subscription) |
567 | 315 | True | 315 | True |
568 | 316 | >>> subscription.branch == branch and subscription.person == subscriber | 316 | >>> subscription.branch == branch and subscription.person == subscriber |
569 | @@ -338,7 +338,7 @@ | |||
570 | 338 | ... subscriber, | 338 | ... subscriber, |
571 | 339 | ... BranchSubscriptionNotificationLevel.FULL, | 339 | ... BranchSubscriptionNotificationLevel.FULL, |
572 | 340 | ... BranchSubscriptionDiffSize.FIVEKLINES, | 340 | ... BranchSubscriptionDiffSize.FIVEKLINES, |
574 | 341 | ... CodeReviewNotificationLevel.NOEMAIL) | 341 | ... CodeReviewNotificationLevel.NOEMAIL, subscriber) |
575 | 342 | >>> subscription == subscription2 | 342 | >>> subscription == subscription2 |
576 | 343 | True | 343 | True |
577 | 344 | >>> subscription2.review_level == CodeReviewNotificationLevel.NOEMAIL | 344 | >>> subscription2.review_level == CodeReviewNotificationLevel.NOEMAIL |
578 | @@ -346,7 +346,7 @@ | |||
579 | 346 | 346 | ||
580 | 347 | Unsubscribing is also supported. | 347 | Unsubscribing is also supported. |
581 | 348 | 348 | ||
583 | 349 | >>> branch.unsubscribe(subscriber) | 349 | >>> branch.unsubscribe(subscriber, subscriber) |
584 | 350 | >>> branch.subscribers.count() | 350 | >>> branch.subscribers.count() |
585 | 351 | 1 | 351 | 1 |
586 | 352 | 352 | ||
587 | @@ -365,7 +365,7 @@ | |||
588 | 365 | ... subscriber, | 365 | ... subscriber, |
589 | 366 | ... BranchSubscriptionNotificationLevel.FULL, | 366 | ... BranchSubscriptionNotificationLevel.FULL, |
590 | 367 | ... BranchSubscriptionDiffSize.FIVEKLINES, | 367 | ... BranchSubscriptionDiffSize.FIVEKLINES, |
592 | 368 | ... CodeReviewNotificationLevel.NOEMAIL) | 368 | ... CodeReviewNotificationLevel.NOEMAIL, subscriber) |
593 | 369 | 369 | ||
594 | 370 | >>> print_names(branch2.getSubscriptionsByLevel([ | 370 | >>> print_names(branch2.getSubscriptionsByLevel([ |
595 | 371 | ... BranchSubscriptionNotificationLevel.FULL])) | 371 | ... BranchSubscriptionNotificationLevel.FULL])) |
596 | 372 | 372 | ||
597 | === modified file 'lib/lp/code/doc/codeimport.txt' | |||
598 | --- lib/lp/code/doc/codeimport.txt 2010-03-18 22:18:49 +0000 | |||
599 | +++ lib/lp/code/doc/codeimport.txt 2010-05-29 09:01:05 +0000 | |||
600 | @@ -258,7 +258,7 @@ | |||
601 | 258 | ... nopriv, | 258 | ... nopriv, |
602 | 259 | ... BranchSubscriptionNotificationLevel.FULL, | 259 | ... BranchSubscriptionNotificationLevel.FULL, |
603 | 260 | ... BranchSubscriptionDiffSize.NODIFF, | 260 | ... BranchSubscriptionDiffSize.NODIFF, |
605 | 261 | ... CodeReviewNotificationLevel.FULL) | 261 | ... CodeReviewNotificationLevel.FULL, nopriv) |
606 | 262 | 262 | ||
607 | 263 | >>> from lp.testing.mail_helpers import ( | 263 | >>> from lp.testing.mail_helpers import ( |
608 | 264 | ... pop_notifications, print_emails) | 264 | ... pop_notifications, print_emails) |
609 | 265 | 265 | ||
610 | === modified file 'lib/lp/code/doc/codereviewcomment.txt' | |||
611 | --- lib/lp/code/doc/codereviewcomment.txt 2010-04-06 01:13:28 +0000 | |||
612 | +++ lib/lp/code/doc/codereviewcomment.txt 2010-05-29 09:01:05 +0000 | |||
613 | @@ -65,7 +65,7 @@ | |||
614 | 65 | >>> _unused = merge_proposal.source_branch.subscribe(source_subscriber, | 65 | >>> _unused = merge_proposal.source_branch.subscribe(source_subscriber, |
615 | 66 | ... BranchSubscriptionNotificationLevel.NOEMAIL, | 66 | ... BranchSubscriptionNotificationLevel.NOEMAIL, |
616 | 67 | ... BranchSubscriptionDiffSize.NODIFF, | 67 | ... BranchSubscriptionDiffSize.NODIFF, |
618 | 68 | ... CodeReviewNotificationLevel.FULL) | 68 | ... CodeReviewNotificationLevel.FULL, source_subscriber) |
619 | 69 | >>> from lp.testing.mail_helpers import ( | 69 | >>> from lp.testing.mail_helpers import ( |
620 | 70 | ... pop_notifications, print_emails) | 70 | ... pop_notifications, print_emails) |
621 | 71 | >>> _unused = pop_notifications() | 71 | >>> _unused = pop_notifications() |
622 | 72 | 72 | ||
623 | === modified file 'lib/lp/code/interfaces/branch.py' | |||
624 | --- lib/lp/code/interfaces/branch.py 2010-04-26 00:24:24 +0000 | |||
625 | +++ lib/lp/code/interfaces/branch.py 2010-05-29 09:01:05 +0000 | |||
626 | @@ -965,9 +965,10 @@ | |||
627 | 965 | title=_("The level of code review notification emails."), | 965 | title=_("The level of code review notification emails."), |
628 | 966 | vocabulary=CodeReviewNotificationLevel)) | 966 | vocabulary=CodeReviewNotificationLevel)) |
629 | 967 | @operation_returns_entry(Interface) # Really IBranchSubscription | 967 | @operation_returns_entry(Interface) # Really IBranchSubscription |
630 | 968 | @call_with(subscribed_by=REQUEST_USER) | ||
631 | 968 | @export_write_operation() | 969 | @export_write_operation() |
632 | 969 | def subscribe(person, notification_level, max_diff_lines, | 970 | def subscribe(person, notification_level, max_diff_lines, |
634 | 970 | code_review_level): | 971 | code_review_level, subscribed_by): |
635 | 971 | """Subscribe this person to the branch. | 972 | """Subscribe this person to the branch. |
636 | 972 | 973 | ||
637 | 973 | :param person: The `Person` to subscribe. | 974 | :param person: The `Person` to subscribe. |
638 | @@ -977,6 +978,8 @@ | |||
639 | 977 | appear in a notification. | 978 | appear in a notification. |
640 | 978 | :param code_review_level: The kinds of code review activity that cause | 979 | :param code_review_level: The kinds of code review activity that cause |
641 | 979 | notification. | 980 | notification. |
642 | 981 | :param subscribed_by: The person who is subscribing the subscriber. | ||
643 | 982 | Most often the subscriber themselves. | ||
644 | 980 | :return: new or existing BranchSubscription.""" | 983 | :return: new or existing BranchSubscription.""" |
645 | 981 | 984 | ||
646 | 982 | @operation_parameters( | 985 | @operation_parameters( |
647 | @@ -995,9 +998,14 @@ | |||
648 | 995 | person=Reference( | 998 | person=Reference( |
649 | 996 | title=_("The person to unsubscribe"), | 999 | title=_("The person to unsubscribe"), |
650 | 997 | schema=IPerson)) | 1000 | schema=IPerson)) |
651 | 1001 | @call_with(unsubscribed_by=REQUEST_USER) | ||
652 | 998 | @export_write_operation() | 1002 | @export_write_operation() |
655 | 999 | def unsubscribe(person): | 1003 | def unsubscribe(person, unsubscribed_by): |
656 | 1000 | """Remove the person's subscription to this branch.""" | 1004 | """Remove the person's subscription to this branch. |
657 | 1005 | |||
658 | 1006 | :param person: The person or team to unsubscribe from the branch. | ||
659 | 1007 | :param unsubscribed_by: The person doing the unsubscribing. | ||
660 | 1008 | """ | ||
661 | 1001 | 1009 | ||
662 | 1002 | def getSubscriptionsByLevel(notification_levels): | 1010 | def getSubscriptionsByLevel(notification_levels): |
663 | 1003 | """Return the subscriptions that are at the given notification levels. | 1011 | """Return the subscriptions that are at the given notification levels. |
664 | 1004 | 1012 | ||
665 | === modified file 'lib/lp/code/interfaces/branchsubscription.py' | |||
666 | --- lib/lp/code/interfaces/branchsubscription.py 2009-06-25 04:06:00 +0000 | |||
667 | +++ lib/lp/code/interfaces/branchsubscription.py 2010-05-29 09:01:05 +0000 | |||
668 | @@ -21,7 +21,8 @@ | |||
669 | 21 | from lp.code.interfaces.branch import IBranch | 21 | from lp.code.interfaces.branch import IBranch |
670 | 22 | from canonical.launchpad.fields import ParticipatingPersonChoice | 22 | from canonical.launchpad.fields import ParticipatingPersonChoice |
671 | 23 | from lazr.restful.declarations import ( | 23 | from lazr.restful.declarations import ( |
673 | 24 | export_as_webservice_entry, exported) | 24 | REQUEST_USER, call_with, export_as_webservice_entry, |
674 | 25 | export_read_operation, exported) | ||
675 | 25 | from lazr.restful.fields import Reference | 26 | from lazr.restful.fields import Reference |
676 | 26 | 27 | ||
677 | 27 | 28 | ||
678 | @@ -73,3 +74,13 @@ | |||
679 | 73 | 'Control the kind of review activity that triggers ' | 74 | 'Control the kind of review activity that triggers ' |
680 | 74 | 'notifications.' | 75 | 'notifications.' |
681 | 75 | ))) | 76 | ))) |
682 | 77 | |||
683 | 78 | subscribed_by = exported(ParticipatingPersonChoice( | ||
684 | 79 | title=_('Subscribed by'), required=True, | ||
685 | 80 | vocabulary='ValidPersonOrTeam', readonly=True, | ||
686 | 81 | description=_("The person who created this subscription."))) | ||
687 | 82 | |||
688 | 83 | @call_with(user=REQUEST_USER) | ||
689 | 84 | @export_read_operation() | ||
690 | 85 | def canBeUnsubscribedByUser(user): | ||
691 | 86 | """Can the user unsubscribe the subscriber from the branch?""" | ||
692 | 76 | 87 | ||
693 | === modified file 'lib/lp/code/mail/tests/test_branch.py' | |||
694 | --- lib/lp/code/mail/tests/test_branch.py 2010-04-26 00:22:16 +0000 | |||
695 | +++ lib/lp/code/mail/tests/test_branch.py 2010-05-29 09:01:05 +0000 | |||
696 | @@ -35,7 +35,7 @@ | |||
697 | 35 | source_branch.owner, target_branch) | 35 | source_branch.owner, target_branch) |
698 | 36 | subscription = merge_proposal.source_branch.subscribe( | 36 | subscription = merge_proposal.source_branch.subscribe( |
699 | 37 | subscriber, BranchSubscriptionNotificationLevel.NOEMAIL, None, | 37 | subscriber, BranchSubscriptionNotificationLevel.NOEMAIL, None, |
701 | 38 | CodeReviewNotificationLevel.FULL) | 38 | CodeReviewNotificationLevel.FULL, subscriber) |
702 | 39 | return merge_proposal, subscription | 39 | return merge_proposal, subscription |
703 | 40 | 40 | ||
704 | 41 | def test_forBranchSubscriber(self): | 41 | def test_forBranchSubscriber(self): |
705 | 42 | 42 | ||
706 | === modified file 'lib/lp/code/mail/tests/test_branchmergeproposal.py' | |||
707 | --- lib/lp/code/mail/tests/test_branchmergeproposal.py 2010-05-18 13:14:40 +0000 | |||
708 | +++ lib/lp/code/mail/tests/test_branchmergeproposal.py 2010-05-29 09:01:05 +0000 | |||
709 | @@ -67,7 +67,7 @@ | |||
710 | 67 | email='baz.quxx@example.com') | 67 | email='baz.quxx@example.com') |
711 | 68 | bmp.source_branch.subscribe(subscriber, | 68 | bmp.source_branch.subscribe(subscriber, |
712 | 69 | BranchSubscriptionNotificationLevel.NOEMAIL, None, | 69 | BranchSubscriptionNotificationLevel.NOEMAIL, None, |
714 | 70 | CodeReviewNotificationLevel.FULL) | 70 | CodeReviewNotificationLevel.FULL, subscriber) |
715 | 71 | bmp.source_branch.owner.name = 'bob' | 71 | bmp.source_branch.owner.name = 'bob' |
716 | 72 | bmp.source_branch.name = 'fix-foo-for-bar' | 72 | bmp.source_branch.name = 'fix-foo-for-bar' |
717 | 73 | bmp.target_branch.owner.name = 'mary' | 73 | bmp.target_branch.owner.name = 'mary' |
718 | @@ -176,7 +176,7 @@ | |||
719 | 176 | bmp = request.merge_proposal | 176 | bmp = request.merge_proposal |
720 | 177 | bmp.source_branch.subscribe( | 177 | bmp.source_branch.subscribe( |
721 | 178 | bmp.registrant, BranchSubscriptionNotificationLevel.NOEMAIL, None, | 178 | bmp.registrant, BranchSubscriptionNotificationLevel.NOEMAIL, None, |
723 | 179 | CodeReviewNotificationLevel.FULL) | 179 | CodeReviewNotificationLevel.FULL, bmp.registrant) |
724 | 180 | mailer = BMPMailer.forCreation(bmp, bmp.registrant) | 180 | mailer = BMPMailer.forCreation(bmp, bmp.registrant) |
725 | 181 | ctrl = mailer.generateEmail(bmp.registrant, | 181 | ctrl = mailer.generateEmail(bmp.registrant, |
726 | 182 | bmp.registrant.preferredemail.email) | 182 | bmp.registrant.preferredemail.email) |
727 | @@ -259,7 +259,7 @@ | |||
728 | 259 | diff_text=diff_text) | 259 | diff_text=diff_text) |
729 | 260 | bmp.source_branch.subscribe(subscriber, | 260 | bmp.source_branch.subscribe(subscriber, |
730 | 261 | BranchSubscriptionNotificationLevel.NOEMAIL, None, | 261 | BranchSubscriptionNotificationLevel.NOEMAIL, None, |
732 | 262 | CodeReviewNotificationLevel.STATUS) | 262 | CodeReviewNotificationLevel.STATUS, subscriber) |
733 | 263 | mailer = BMPMailer.forCreation(bmp, bmp.registrant) | 263 | mailer = BMPMailer.forCreation(bmp, bmp.registrant) |
734 | 264 | ctrl = mailer.generateEmail('baz.quxx@example.com', subscriber) | 264 | ctrl = mailer.generateEmail('baz.quxx@example.com', subscriber) |
735 | 265 | self.assertEqual(0, len(ctrl.attachments)) | 265 | self.assertEqual(0, len(ctrl.attachments)) |
736 | 266 | 266 | ||
737 | === modified file 'lib/lp/code/mail/tests/test_codehandler.py' | |||
738 | --- lib/lp/code/mail/tests/test_codehandler.py 2010-04-29 07:47:47 +0000 | |||
739 | +++ lib/lp/code/mail/tests/test_codehandler.py 2010-05-29 09:01:05 +0000 | |||
740 | @@ -346,7 +346,7 @@ | |||
741 | 346 | subscriber = self.factory.makePerson() | 346 | subscriber = self.factory.makePerson() |
742 | 347 | bmp.source_branch.subscribe( | 347 | bmp.source_branch.subscribe( |
743 | 348 | subscriber, BranchSubscriptionNotificationLevel.NOEMAIL, None, | 348 | subscriber, BranchSubscriptionNotificationLevel.NOEMAIL, None, |
745 | 349 | CodeReviewNotificationLevel.FULL) | 349 | CodeReviewNotificationLevel.FULL, subscriber) |
746 | 350 | email_addr = bmp.address | 350 | email_addr = bmp.address |
747 | 351 | self.switchDbUser(config.processmail.dbuser) | 351 | self.switchDbUser(config.processmail.dbuser) |
748 | 352 | self.code_handler.process(mail, email_addr, None) | 352 | self.code_handler.process(mail, email_addr, None) |
749 | 353 | 353 | ||
750 | === modified file 'lib/lp/code/mail/tests/test_codereviewcomment.py' | |||
751 | --- lib/lp/code/mail/tests/test_codereviewcomment.py 2010-05-07 17:16:44 +0000 | |||
752 | +++ lib/lp/code/mail/tests/test_codereviewcomment.py 2010-05-29 09:01:05 +0000 | |||
753 | @@ -48,7 +48,7 @@ | |||
754 | 48 | notification_level = CodeReviewNotificationLevel.FULL | 48 | notification_level = CodeReviewNotificationLevel.FULL |
755 | 49 | comment.branch_merge_proposal.source_branch.subscribe( | 49 | comment.branch_merge_proposal.source_branch.subscribe( |
756 | 50 | subscriber, BranchSubscriptionNotificationLevel.NOEMAIL, None, | 50 | subscriber, BranchSubscriptionNotificationLevel.NOEMAIL, None, |
758 | 51 | notification_level) | 51 | notification_level, subscriber) |
759 | 52 | # Email is not sent on construction, so fake a root message id on the | 52 | # Email is not sent on construction, so fake a root message id on the |
760 | 53 | # merge proposal. | 53 | # merge proposal. |
761 | 54 | login_person(comment.branch_merge_proposal.registrant) | 54 | login_person(comment.branch_merge_proposal.registrant) |
762 | @@ -265,7 +265,7 @@ | |||
763 | 265 | email='commenter@email.com', displayname='Commenter') | 265 | email='commenter@email.com', displayname='Commenter') |
764 | 266 | bmp.source_branch.subscribe(commenter, | 266 | bmp.source_branch.subscribe(commenter, |
765 | 267 | BranchSubscriptionNotificationLevel.NOEMAIL, None, | 267 | BranchSubscriptionNotificationLevel.NOEMAIL, None, |
767 | 268 | CodeReviewNotificationLevel.FULL) | 268 | CodeReviewNotificationLevel.FULL, commenter) |
768 | 269 | comment = bmp.createComment(commenter, 'hello') | 269 | comment = bmp.createComment(commenter, 'hello') |
769 | 270 | return comment | 270 | return comment |
770 | 271 | 271 | ||
771 | 272 | 272 | ||
772 | === modified file 'lib/lp/code/model/branch.py' | |||
773 | --- lib/lp/code/model/branch.py 2010-05-15 17:43:59 +0000 | |||
774 | +++ lib/lp/code/model/branch.py 2010-05-29 09:01:05 +0000 | |||
775 | @@ -45,6 +45,7 @@ | |||
776 | 45 | from canonical.launchpad.webapp.interfaces import ( | 45 | from canonical.launchpad.webapp.interfaces import ( |
777 | 46 | IStoreSelector, MAIN_STORE, SLAVE_FLAVOR) | 46 | IStoreSelector, MAIN_STORE, SLAVE_FLAVOR) |
778 | 47 | 47 | ||
779 | 48 | from lp.app.errors import UserCannotUnsubscribePerson | ||
780 | 48 | from lp.code.bzr import ( | 49 | from lp.code.bzr import ( |
781 | 49 | BranchFormat, ControlFormat, CURRENT_BRANCH_FORMATS, | 50 | BranchFormat, ControlFormat, CURRENT_BRANCH_FORMATS, |
782 | 50 | CURRENT_REPOSITORY_FORMATS, RepositoryFormat) | 51 | CURRENT_REPOSITORY_FORMATS, RepositoryFormat) |
783 | @@ -659,7 +660,7 @@ | |||
784 | 659 | 660 | ||
785 | 660 | # subscriptions | 661 | # subscriptions |
786 | 661 | def subscribe(self, person, notification_level, max_diff_lines, | 662 | def subscribe(self, person, notification_level, max_diff_lines, |
788 | 662 | code_review_level): | 663 | code_review_level, subscribed_by): |
789 | 663 | """See `IBranch`.""" | 664 | """See `IBranch`.""" |
790 | 664 | # If the person is already subscribed, update the subscription with | 665 | # If the person is already subscribed, update the subscription with |
791 | 665 | # the specified notification details. | 666 | # the specified notification details. |
792 | @@ -668,7 +669,8 @@ | |||
793 | 668 | subscription = BranchSubscription( | 669 | subscription = BranchSubscription( |
794 | 669 | branch=self, person=person, | 670 | branch=self, person=person, |
795 | 670 | notification_level=notification_level, | 671 | notification_level=notification_level, |
797 | 671 | max_diff_lines=max_diff_lines, review_level=code_review_level) | 672 | max_diff_lines=max_diff_lines, review_level=code_review_level, |
798 | 673 | subscribed_by=subscribed_by) | ||
799 | 672 | Store.of(subscription).flush() | 674 | Store.of(subscription).flush() |
800 | 673 | else: | 675 | else: |
801 | 674 | subscription.notification_level = notification_level | 676 | subscription.notification_level = notification_level |
802 | @@ -701,12 +703,19 @@ | |||
803 | 701 | """See `IBranch`.""" | 703 | """See `IBranch`.""" |
804 | 702 | return self.getSubscription(person) is not None | 704 | return self.getSubscription(person) is not None |
805 | 703 | 705 | ||
807 | 704 | def unsubscribe(self, person): | 706 | def unsubscribe(self, person, unsubscribed_by): |
808 | 705 | """See `IBranch`.""" | 707 | """See `IBranch`.""" |
809 | 706 | subscription = self.getSubscription(person) | 708 | subscription = self.getSubscription(person) |
810 | 709 | if subscription is None: | ||
811 | 710 | # Silent success seems order of the day (like bugs). | ||
812 | 711 | return | ||
813 | 712 | if not subscription.canBeUnsubscribedByUser(unsubscribed_by): | ||
814 | 713 | raise UserCannotUnsubscribePerson( | ||
815 | 714 | '%s does not have permission to unsubscribe %s.' % ( | ||
816 | 715 | unsubscribed_by.displayname, | ||
817 | 716 | person.displayname)) | ||
818 | 707 | store = Store.of(subscription) | 717 | store = Store.of(subscription) |
821 | 708 | assert subscription is not None, "User is not subscribed." | 718 | store.remove(subscription) |
820 | 709 | BranchSubscription.delete(subscription.id) | ||
822 | 710 | store.flush() | 719 | store.flush() |
823 | 711 | 720 | ||
824 | 712 | def getBranchRevision(self, sequence=None, revision=None, | 721 | def getBranchRevision(self, sequence=None, revision=None, |
825 | 713 | 722 | ||
826 | === modified file 'lib/lp/code/model/branchnamespace.py' | |||
827 | --- lib/lp/code/model/branchnamespace.py 2010-02-17 11:19:42 +0000 | |||
828 | +++ lib/lp/code/model/branchnamespace.py 2010-05-29 09:01:05 +0000 | |||
829 | @@ -106,7 +106,8 @@ | |||
830 | 106 | implicit_subscription, | 106 | implicit_subscription, |
831 | 107 | BranchSubscriptionNotificationLevel.NOEMAIL, | 107 | BranchSubscriptionNotificationLevel.NOEMAIL, |
832 | 108 | BranchSubscriptionDiffSize.NODIFF, | 108 | BranchSubscriptionDiffSize.NODIFF, |
834 | 109 | CodeReviewNotificationLevel.NOEMAIL) | 109 | CodeReviewNotificationLevel.NOEMAIL, |
835 | 110 | registrant) | ||
836 | 110 | 111 | ||
837 | 111 | # The registrant of the branch should also be automatically subscribed | 112 | # The registrant of the branch should also be automatically subscribed |
838 | 112 | # in order for them to get code review notifications. The implicit | 113 | # in order for them to get code review notifications. The implicit |
839 | @@ -116,7 +117,8 @@ | |||
840 | 116 | registrant, | 117 | registrant, |
841 | 117 | BranchSubscriptionNotificationLevel.NOEMAIL, | 118 | BranchSubscriptionNotificationLevel.NOEMAIL, |
842 | 118 | BranchSubscriptionDiffSize.NODIFF, | 119 | BranchSubscriptionDiffSize.NODIFF, |
844 | 119 | CodeReviewNotificationLevel.FULL) | 120 | CodeReviewNotificationLevel.FULL, |
845 | 121 | registrant) | ||
846 | 120 | 122 | ||
847 | 121 | notify(ObjectCreatedEvent(branch)) | 123 | notify(ObjectCreatedEvent(branch)) |
848 | 122 | return branch | 124 | return branch |
849 | 123 | 125 | ||
850 | === modified file 'lib/lp/code/model/branchsubscription.py' | |||
851 | --- lib/lp/code/model/branchsubscription.py 2009-06-25 04:06:00 +0000 | |||
852 | +++ lib/lp/code/model/branchsubscription.py 2010-05-29 09:01:05 +0000 | |||
853 | @@ -13,13 +13,14 @@ | |||
854 | 13 | from canonical.database.constants import DEFAULT | 13 | from canonical.database.constants import DEFAULT |
855 | 14 | from canonical.database.sqlbase import SQLBase | 14 | from canonical.database.sqlbase import SQLBase |
856 | 15 | from canonical.database.enumcol import EnumCol | 15 | from canonical.database.enumcol import EnumCol |
858 | 16 | 16 | from canonical.launchpad.interfaces.launchpad import IPersonRoles | |
859 | 17 | from lp.code.enums import ( | 17 | from lp.code.enums import ( |
860 | 18 | BranchSubscriptionDiffSize, BranchSubscriptionNotificationLevel, | 18 | BranchSubscriptionDiffSize, BranchSubscriptionNotificationLevel, |
861 | 19 | CodeReviewNotificationLevel) | 19 | CodeReviewNotificationLevel) |
862 | 20 | from lp.code.interfaces.branchsubscription import IBranchSubscription | 20 | from lp.code.interfaces.branchsubscription import IBranchSubscription |
863 | 21 | from lp.code.interfaces.branch import IBranchNavigationMenu | 21 | from lp.code.interfaces.branch import IBranchNavigationMenu |
864 | 22 | from lp.code.interfaces.branchtarget import IHasBranchTarget | 22 | from lp.code.interfaces.branchtarget import IHasBranchTarget |
865 | 23 | from lp.code.security import BranchSubscriptionEdit | ||
866 | 23 | from lp.registry.interfaces.person import ( | 24 | from lp.registry.interfaces.person import ( |
867 | 24 | validate_person_not_private_membership) | 25 | validate_person_not_private_membership) |
868 | 25 | 26 | ||
869 | @@ -41,8 +42,18 @@ | |||
870 | 41 | notNull=False, default=DEFAULT) | 42 | notNull=False, default=DEFAULT) |
871 | 42 | review_level = EnumCol(enum=CodeReviewNotificationLevel, | 43 | review_level = EnumCol(enum=CodeReviewNotificationLevel, |
872 | 43 | notNull=True, default=DEFAULT) | 44 | notNull=True, default=DEFAULT) |
873 | 45 | subscribed_by = ForeignKey( | ||
874 | 46 | dbName='subscribed_by', foreignKey='Person', | ||
875 | 47 | storm_validator=validate_person_not_private_membership, notNull=True) | ||
876 | 44 | 48 | ||
877 | 45 | @property | 49 | @property |
878 | 46 | def target(self): | 50 | def target(self): |
879 | 47 | """See `IHasBranchTarget`.""" | 51 | """See `IHasBranchTarget`.""" |
880 | 48 | return self.branch.target | 52 | return self.branch.target |
881 | 53 | |||
882 | 54 | def canBeUnsubscribedByUser(self, user): | ||
883 | 55 | """See `IBranchSubscription`.""" | ||
884 | 56 | if user is None: | ||
885 | 57 | return False | ||
886 | 58 | permission_check = BranchSubscriptionEdit(self) | ||
887 | 59 | return permission_check.checkAuthenticated(IPersonRoles(user)) | ||
888 | 49 | 60 | ||
889 | === modified file 'lib/lp/code/model/tests/test_branch.py' | |||
890 | --- lib/lp/code/model/tests/test_branch.py 2010-04-23 04:11:12 +0000 | |||
891 | +++ lib/lp/code/model/tests/test_branch.py 2010-05-29 09:01:05 +0000 | |||
892 | @@ -908,7 +908,7 @@ | |||
893 | 908 | # The owner of the branch is subscribed to the branch when it is | 908 | # The owner of the branch is subscribed to the branch when it is |
894 | 909 | # created. The tests here assume no initial connections, so | 909 | # created. The tests here assume no initial connections, so |
895 | 910 | # unsubscribe the branch owner here. | 910 | # unsubscribe the branch owner here. |
897 | 911 | self.branch.unsubscribe(self.branch.owner) | 911 | self.branch.unsubscribe(self.branch.owner, self.branch.owner) |
898 | 912 | 912 | ||
899 | 913 | def test_deletable(self): | 913 | def test_deletable(self): |
900 | 914 | """A newly created branch can be deleted without any problems.""" | 914 | """A newly created branch can be deleted without any problems.""" |
901 | @@ -930,7 +930,7 @@ | |||
902 | 930 | """A branch that has a subscription can be deleted.""" | 930 | """A branch that has a subscription can be deleted.""" |
903 | 931 | self.branch.subscribe( | 931 | self.branch.subscribe( |
904 | 932 | self.user, BranchSubscriptionNotificationLevel.NOEMAIL, None, | 932 | self.user, BranchSubscriptionNotificationLevel.NOEMAIL, None, |
906 | 933 | CodeReviewNotificationLevel.NOEMAIL) | 933 | CodeReviewNotificationLevel.NOEMAIL, self.user) |
907 | 934 | self.assertEqual(True, self.branch.canBeDeleted()) | 934 | self.assertEqual(True, self.branch.canBeDeleted()) |
908 | 935 | 935 | ||
909 | 936 | def test_codeImportCanStillBeDeleted(self): | 936 | def test_codeImportCanStillBeDeleted(self): |
910 | @@ -1068,7 +1068,7 @@ | |||
911 | 1068 | # The owner of the branch is subscribed to the branch when it is | 1068 | # The owner of the branch is subscribed to the branch when it is |
912 | 1069 | # created. The tests here assume no initial connections, so | 1069 | # created. The tests here assume no initial connections, so |
913 | 1070 | # unsubscribe the branch owner here. | 1070 | # unsubscribe the branch owner here. |
915 | 1071 | self.branch.unsubscribe(self.branch.owner) | 1071 | self.branch.unsubscribe(self.branch.owner, self.branch.owner) |
916 | 1072 | 1072 | ||
917 | 1073 | def test_plainBranch(self): | 1073 | def test_plainBranch(self): |
918 | 1074 | """Ensure that a fresh branch has no deletion requirements.""" | 1074 | """Ensure that a fresh branch has no deletion requirements.""" |
919 | @@ -1081,8 +1081,9 @@ | |||
920 | 1081 | prerequisite_branch = self.factory.makeProductBranch( | 1081 | prerequisite_branch = self.factory.makeProductBranch( |
921 | 1082 | product=self.branch.product) | 1082 | product=self.branch.product) |
922 | 1083 | # Remove the implicit subscriptions. | 1083 | # Remove the implicit subscriptions. |
925 | 1084 | target_branch.unsubscribe(target_branch.owner) | 1084 | target_branch.unsubscribe(target_branch.owner, target_branch.owner) |
926 | 1085 | prerequisite_branch.unsubscribe(prerequisite_branch.owner) | 1085 | prerequisite_branch.unsubscribe( |
927 | 1086 | prerequisite_branch.owner, prerequisite_branch.owner) | ||
928 | 1086 | merge_proposal1 = self.branch.addLandingTarget( | 1087 | merge_proposal1 = self.branch.addLandingTarget( |
929 | 1087 | self.branch.owner, target_branch, prerequisite_branch) | 1088 | self.branch.owner, target_branch, prerequisite_branch) |
930 | 1088 | # Disable this merge proposal, to allow creating a new identical one | 1089 | # Disable this merge proposal, to allow creating a new identical one |
931 | @@ -1261,7 +1262,8 @@ | |||
932 | 1261 | """Deletion requirements for a code import branch are right""" | 1262 | """Deletion requirements for a code import branch are right""" |
933 | 1262 | code_import = self.factory.makeCodeImport() | 1263 | code_import = self.factory.makeCodeImport() |
934 | 1263 | # Remove the implicit branch subscription first. | 1264 | # Remove the implicit branch subscription first. |
936 | 1264 | code_import.branch.unsubscribe(code_import.branch.owner) | 1265 | code_import.branch.unsubscribe( |
937 | 1266 | code_import.branch.owner, code_import.branch.owner) | ||
938 | 1265 | self.assertEqual({}, code_import.branch.deletionRequirements()) | 1267 | self.assertEqual({}, code_import.branch.deletionRequirements()) |
939 | 1266 | 1268 | ||
940 | 1267 | def test_branchWithCodeImportDeletion(self): | 1269 | def test_branchWithCodeImportDeletion(self): |
941 | @@ -2303,7 +2305,8 @@ | |||
942 | 2303 | # Revisions created before the start date are not returned. | 2305 | # Revisions created before the start date are not returned. |
943 | 2304 | branch = self.factory.makeAnyBranch() | 2306 | branch = self.factory.makeAnyBranch() |
944 | 2305 | epoch = datetime(2009, 9, 10, tzinfo=UTC) | 2307 | epoch = datetime(2009, 9, 10, tzinfo=UTC) |
946 | 2306 | old = add_revision_to_branch( | 2308 | # Add some revisions before the epoch. |
947 | 2309 | add_revision_to_branch( | ||
948 | 2307 | self.factory, branch, epoch - timedelta(days=1)) | 2310 | self.factory, branch, epoch - timedelta(days=1)) |
949 | 2308 | new = add_revision_to_branch( | 2311 | new = add_revision_to_branch( |
950 | 2309 | self.factory, branch, epoch + timedelta(days=1)) | 2312 | self.factory, branch, epoch + timedelta(days=1)) |
951 | @@ -2318,7 +2321,8 @@ | |||
952 | 2318 | end_date = epoch + timedelta(days=2) | 2321 | end_date = epoch + timedelta(days=2) |
953 | 2319 | in_range = add_revision_to_branch( | 2322 | in_range = add_revision_to_branch( |
954 | 2320 | self.factory, branch, end_date - timedelta(days=1)) | 2323 | self.factory, branch, end_date - timedelta(days=1)) |
956 | 2321 | too_new = add_revision_to_branch( | 2324 | # Add some revisions after the end_date. |
957 | 2325 | add_revision_to_branch( | ||
958 | 2322 | self.factory, branch, end_date + timedelta(days=1)) | 2326 | self.factory, branch, end_date + timedelta(days=1)) |
959 | 2323 | result = branch.getMainlineBranchRevisions(epoch, end_date) | 2327 | result = branch.getMainlineBranchRevisions(epoch, end_date) |
960 | 2324 | branch_revisions = [br for br, rev, ra in result] | 2328 | branch_revisions = [br for br, rev, ra in result] |
961 | @@ -2354,7 +2358,8 @@ | |||
962 | 2354 | epoch = datetime(2009, 9, 10, tzinfo=UTC) | 2358 | epoch = datetime(2009, 9, 10, tzinfo=UTC) |
963 | 2355 | old = add_revision_to_branch( | 2359 | old = add_revision_to_branch( |
964 | 2356 | self.factory, branch, epoch + timedelta(days=1)) | 2360 | self.factory, branch, epoch + timedelta(days=1)) |
966 | 2357 | merged = add_revision_to_branch( | 2361 | # Add some non mainline revision. |
967 | 2362 | add_revision_to_branch( | ||
968 | 2358 | self.factory, branch, epoch + timedelta(days=2), mainline=False) | 2363 | self.factory, branch, epoch + timedelta(days=2), mainline=False) |
969 | 2359 | new = add_revision_to_branch( | 2364 | new = add_revision_to_branch( |
970 | 2360 | self.factory, branch, epoch + timedelta(days=3)) | 2365 | self.factory, branch, epoch + timedelta(days=3)) |
971 | 2361 | 2366 | ||
972 | === modified file 'lib/lp/code/model/tests/test_branchcollection.py' | |||
973 | --- lib/lp/code/model/tests/test_branchcollection.py 2009-10-28 04:33:49 +0000 | |||
974 | +++ lib/lp/code/model/tests/test_branchcollection.py 2010-05-29 09:01:05 +0000 | |||
975 | @@ -364,7 +364,8 @@ | |||
976 | 364 | branch.subscribe( | 364 | branch.subscribe( |
977 | 365 | subscriber, BranchSubscriptionNotificationLevel.NOEMAIL, | 365 | subscriber, BranchSubscriptionNotificationLevel.NOEMAIL, |
978 | 366 | BranchSubscriptionDiffSize.NODIFF, | 366 | BranchSubscriptionDiffSize.NODIFF, |
980 | 367 | CodeReviewNotificationLevel.NOEMAIL) | 367 | CodeReviewNotificationLevel.NOEMAIL, |
981 | 368 | subscriber) | ||
982 | 368 | collection = self.all_branches.subscribedBy(subscriber) | 369 | collection = self.all_branches.subscribedBy(subscriber) |
983 | 369 | self.assertEqual([branch], list(collection.getBranches())) | 370 | self.assertEqual([branch], list(collection.getBranches())) |
984 | 370 | 371 | ||
985 | @@ -377,7 +378,7 @@ | |||
986 | 377 | owned_branch = self.factory.makeAnyBranch(owner=person) | 378 | owned_branch = self.factory.makeAnyBranch(owner=person) |
987 | 378 | # Unsubscribe the owner, to demonstrate that we show owned branches | 379 | # Unsubscribe the owner, to demonstrate that we show owned branches |
988 | 379 | # even if they aren't subscribed. | 380 | # even if they aren't subscribed. |
990 | 380 | owned_branch.unsubscribe(person) | 381 | owned_branch.unsubscribe(person, person) |
991 | 381 | # Subscribe two other people to the owned branch to make sure | 382 | # Subscribe two other people to the owned branch to make sure |
992 | 382 | # that the BranchSubscription join is doing it right. | 383 | # that the BranchSubscription join is doing it right. |
993 | 383 | self.factory.makeBranchSubscription(branch=owned_branch) | 384 | self.factory.makeBranchSubscription(branch=owned_branch) |
994 | @@ -389,7 +390,8 @@ | |||
995 | 389 | subscribed_branch.subscribe( | 390 | subscribed_branch.subscribe( |
996 | 390 | person, BranchSubscriptionNotificationLevel.NOEMAIL, | 391 | person, BranchSubscriptionNotificationLevel.NOEMAIL, |
997 | 391 | BranchSubscriptionDiffSize.NODIFF, | 392 | BranchSubscriptionDiffSize.NODIFF, |
999 | 392 | CodeReviewNotificationLevel.NOEMAIL) | 393 | CodeReviewNotificationLevel.NOEMAIL, |
1000 | 394 | person) | ||
1001 | 393 | related_branches = self.all_branches.relatedTo(person) | 395 | related_branches = self.all_branches.relatedTo(person) |
1002 | 394 | self.assertEqual( | 396 | self.assertEqual( |
1003 | 395 | sorted([owned_branch, registered_branch, subscribed_branch]), | 397 | sorted([owned_branch, registered_branch, subscribed_branch]), |
1004 | @@ -548,7 +550,8 @@ | |||
1005 | 548 | removeSecurityProxy(self.private_branch1).subscribe( | 550 | removeSecurityProxy(self.private_branch1).subscribe( |
1006 | 549 | subscriber, BranchSubscriptionNotificationLevel.NOEMAIL, | 551 | subscriber, BranchSubscriptionNotificationLevel.NOEMAIL, |
1007 | 550 | BranchSubscriptionDiffSize.NODIFF, | 552 | BranchSubscriptionDiffSize.NODIFF, |
1009 | 551 | CodeReviewNotificationLevel.NOEMAIL) | 553 | CodeReviewNotificationLevel.NOEMAIL, |
1010 | 554 | subscriber) | ||
1011 | 552 | branches = self.all_branches.visibleByUser(subscriber) | 555 | branches = self.all_branches.visibleByUser(subscriber) |
1012 | 553 | self.assertEqual( | 556 | self.assertEqual( |
1013 | 554 | sorted([self.public_branch, self.private_branch1]), | 557 | sorted([self.public_branch, self.private_branch1]), |
1014 | @@ -564,7 +567,8 @@ | |||
1015 | 564 | removeSecurityProxy(private_branch).subscribe( | 567 | removeSecurityProxy(private_branch).subscribe( |
1016 | 565 | team, BranchSubscriptionNotificationLevel.NOEMAIL, | 568 | team, BranchSubscriptionNotificationLevel.NOEMAIL, |
1017 | 566 | BranchSubscriptionDiffSize.NODIFF, | 569 | BranchSubscriptionDiffSize.NODIFF, |
1019 | 567 | CodeReviewNotificationLevel.NOEMAIL) | 570 | CodeReviewNotificationLevel.NOEMAIL, |
1020 | 571 | team_owner) | ||
1021 | 568 | # Members of the team can see the private branch that the team is | 572 | # Members of the team can see the private branch that the team is |
1022 | 569 | # subscribed to. | 573 | # subscribed to. |
1023 | 570 | branches = self.all_branches.visibleByUser(team_owner) | 574 | branches = self.all_branches.visibleByUser(team_owner) |
1024 | 571 | 575 | ||
1025 | === modified file 'lib/lp/code/model/tests/test_branchjob.py' | |||
1026 | --- lib/lp/code/model/tests/test_branchjob.py 2010-05-25 05:19:30 +0000 | |||
1027 | +++ lib/lp/code/model/tests/test_branchjob.py 2010-05-29 09:01:05 +0000 | |||
1028 | @@ -322,10 +322,12 @@ | |||
1029 | 322 | def test_run_sends_mail(self): | 322 | def test_run_sends_mail(self): |
1030 | 323 | """Ensure RevisionMailJob.run sends mail with correct values.""" | 323 | """Ensure RevisionMailJob.run sends mail with correct values.""" |
1031 | 324 | branch = self.factory.makeAnyBranch() | 324 | branch = self.factory.makeAnyBranch() |
1033 | 325 | branch.subscribe(branch.registrant, | 325 | branch.subscribe( |
1034 | 326 | branch.registrant, | ||
1035 | 326 | BranchSubscriptionNotificationLevel.FULL, | 327 | BranchSubscriptionNotificationLevel.FULL, |
1036 | 327 | BranchSubscriptionDiffSize.WHOLEDIFF, | 328 | BranchSubscriptionDiffSize.WHOLEDIFF, |
1038 | 328 | CodeReviewNotificationLevel.FULL) | 329 | CodeReviewNotificationLevel.FULL, |
1039 | 330 | branch.registrant) | ||
1040 | 329 | job = RevisionMailJob.create( | 331 | job = RevisionMailJob.create( |
1041 | 330 | branch, 0, 'from@example.com', 'hello', False, 'subject') | 332 | branch, 0, 'from@example.com', 'hello', False, 'subject') |
1042 | 331 | job.run() | 333 | job.run() |
1043 | @@ -515,10 +517,12 @@ | |||
1044 | 515 | product = self.factory.makeProduct(name='foo') | 517 | product = self.factory.makeProduct(name='foo') |
1045 | 516 | branch = self.factory.makeProductBranch( | 518 | branch = self.factory.makeProductBranch( |
1046 | 517 | name='bar', product=product, owner=jrandom) | 519 | name='bar', product=product, owner=jrandom) |
1048 | 518 | branch.subscribe(branch.registrant, | 520 | branch.subscribe( |
1049 | 521 | branch.registrant, | ||
1050 | 519 | BranchSubscriptionNotificationLevel.FULL, | 522 | BranchSubscriptionNotificationLevel.FULL, |
1051 | 520 | BranchSubscriptionDiffSize.WHOLEDIFF, | 523 | BranchSubscriptionDiffSize.WHOLEDIFF, |
1053 | 521 | CodeReviewNotificationLevel.FULL) | 524 | CodeReviewNotificationLevel.FULL, |
1054 | 525 | branch.registrant) | ||
1055 | 522 | branch, tree = self.create_branch_and_tree(db_branch=branch) | 526 | branch, tree = self.create_branch_and_tree(db_branch=branch) |
1056 | 523 | tree.branch.nick = 'nicholas' | 527 | tree.branch.nick = 'nicholas' |
1057 | 524 | tree.lock_write() | 528 | tree.lock_write() |
1058 | 525 | 529 | ||
1059 | === modified file 'lib/lp/code/model/tests/test_branchmergeproposals.py' | |||
1060 | --- lib/lp/code/model/tests/test_branchmergeproposals.py 2010-05-07 04:53:47 +0000 | |||
1061 | +++ lib/lp/code/model/tests/test_branchmergeproposals.py 2010-05-29 09:01:05 +0000 | |||
1062 | @@ -722,16 +722,20 @@ | |||
1063 | 722 | subscriber_set = set([source_owner, target_owner]) | 722 | subscriber_set = set([source_owner, target_owner]) |
1064 | 723 | self.assertEqual(subscriber_set, set(recipients.keys())) | 723 | self.assertEqual(subscriber_set, set(recipients.keys())) |
1065 | 724 | source_subscriber = self.factory.makePerson() | 724 | source_subscriber = self.factory.makePerson() |
1067 | 725 | bmp.source_branch.subscribe(source_subscriber, | 725 | bmp.source_branch.subscribe( |
1068 | 726 | source_subscriber, | ||
1069 | 726 | BranchSubscriptionNotificationLevel.NOEMAIL, None, | 727 | BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1071 | 727 | CodeReviewNotificationLevel.FULL) | 728 | CodeReviewNotificationLevel.FULL, |
1072 | 729 | source_subscriber) | ||
1073 | 728 | recipients = bmp.getNotificationRecipients( | 730 | recipients = bmp.getNotificationRecipients( |
1074 | 729 | CodeReviewNotificationLevel.STATUS) | 731 | CodeReviewNotificationLevel.STATUS) |
1075 | 730 | subscriber_set.add(source_subscriber) | 732 | subscriber_set.add(source_subscriber) |
1076 | 731 | self.assertEqual(subscriber_set, set(recipients.keys())) | 733 | self.assertEqual(subscriber_set, set(recipients.keys())) |
1078 | 732 | bmp.source_branch.subscribe(source_subscriber, | 734 | bmp.source_branch.subscribe( |
1079 | 735 | source_subscriber, | ||
1080 | 733 | BranchSubscriptionNotificationLevel.NOEMAIL, None, | 736 | BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1082 | 734 | CodeReviewNotificationLevel.NOEMAIL) | 737 | CodeReviewNotificationLevel.NOEMAIL, |
1083 | 738 | source_subscriber) | ||
1084 | 735 | # By specifying no email, they will no longer get email. | 739 | # By specifying no email, they will no longer get email. |
1085 | 736 | subscriber_set.remove(source_subscriber) | 740 | subscriber_set.remove(source_subscriber) |
1086 | 737 | recipients = bmp.getNotificationRecipients( | 741 | recipients = bmp.getNotificationRecipients( |
1087 | @@ -744,11 +748,11 @@ | |||
1088 | 744 | full_subscriber = self.factory.makePerson() | 748 | full_subscriber = self.factory.makePerson() |
1089 | 745 | bmp.source_branch.subscribe(full_subscriber, | 749 | bmp.source_branch.subscribe(full_subscriber, |
1090 | 746 | BranchSubscriptionNotificationLevel.NOEMAIL, None, | 750 | BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1092 | 747 | CodeReviewNotificationLevel.FULL) | 751 | CodeReviewNotificationLevel.FULL, full_subscriber) |
1093 | 748 | status_subscriber = self.factory.makePerson() | 752 | status_subscriber = self.factory.makePerson() |
1094 | 749 | bmp.source_branch.subscribe(status_subscriber, | 753 | bmp.source_branch.subscribe(status_subscriber, |
1095 | 750 | BranchSubscriptionNotificationLevel.NOEMAIL, None, | 754 | BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1097 | 751 | CodeReviewNotificationLevel.STATUS) | 755 | CodeReviewNotificationLevel.STATUS, status_subscriber) |
1098 | 752 | recipients = bmp.getNotificationRecipients( | 756 | recipients = bmp.getNotificationRecipients( |
1099 | 753 | CodeReviewNotificationLevel.STATUS) | 757 | CodeReviewNotificationLevel.STATUS) |
1100 | 754 | # Both of the branch owners are now subscribed to their own | 758 | # Both of the branch owners are now subscribed to their own |
1101 | @@ -778,15 +782,15 @@ | |||
1102 | 778 | source_subscriber = self.factory.makePerson() | 782 | source_subscriber = self.factory.makePerson() |
1103 | 779 | bmp.source_branch.subscribe(source_subscriber, | 783 | bmp.source_branch.subscribe(source_subscriber, |
1104 | 780 | BranchSubscriptionNotificationLevel.NOEMAIL, None, | 784 | BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1106 | 781 | CodeReviewNotificationLevel.FULL) | 785 | CodeReviewNotificationLevel.FULL, source_subscriber) |
1107 | 782 | target_subscriber = self.factory.makePerson() | 786 | target_subscriber = self.factory.makePerson() |
1108 | 783 | bmp.target_branch.subscribe(target_subscriber, | 787 | bmp.target_branch.subscribe(target_subscriber, |
1109 | 784 | BranchSubscriptionNotificationLevel.NOEMAIL, None, | 788 | BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1111 | 785 | CodeReviewNotificationLevel.FULL) | 789 | CodeReviewNotificationLevel.FULL, target_subscriber) |
1112 | 786 | prerequisite_subscriber = self.factory.makePerson() | 790 | prerequisite_subscriber = self.factory.makePerson() |
1113 | 787 | bmp.prerequisite_branch.subscribe(prerequisite_subscriber, | 791 | bmp.prerequisite_branch.subscribe(prerequisite_subscriber, |
1114 | 788 | BranchSubscriptionNotificationLevel.NOEMAIL, None, | 792 | BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1116 | 789 | CodeReviewNotificationLevel.FULL) | 793 | CodeReviewNotificationLevel.FULL, prerequisite_subscriber) |
1117 | 790 | recipients = bmp.getNotificationRecipients( | 794 | recipients = bmp.getNotificationRecipients( |
1118 | 791 | CodeReviewNotificationLevel.FULL) | 795 | CodeReviewNotificationLevel.FULL) |
1119 | 792 | self.assertEqual( | 796 | self.assertEqual( |
1120 | @@ -832,7 +836,7 @@ | |||
1121 | 832 | # Make sure that the registrant is subscribed. | 836 | # Make sure that the registrant is subscribed. |
1122 | 833 | bmp.source_branch.subscribe(registrant, | 837 | bmp.source_branch.subscribe(registrant, |
1123 | 834 | BranchSubscriptionNotificationLevel.NOEMAIL, None, | 838 | BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1125 | 835 | CodeReviewNotificationLevel.FULL) | 839 | CodeReviewNotificationLevel.FULL, registrant) |
1126 | 836 | recipients = bmp.getNotificationRecipients( | 840 | recipients = bmp.getNotificationRecipients( |
1127 | 837 | CodeReviewNotificationLevel.STATUS) | 841 | CodeReviewNotificationLevel.STATUS) |
1128 | 838 | reason = recipients[registrant] | 842 | reason = recipients[registrant] |
1129 | @@ -879,7 +883,7 @@ | |||
1130 | 879 | # we don't send them eamil. | 883 | # we don't send them eamil. |
1131 | 880 | bmp = self.factory.makeBranchMergeProposal() | 884 | bmp = self.factory.makeBranchMergeProposal() |
1132 | 881 | owner = bmp.source_branch.owner | 885 | owner = bmp.source_branch.owner |
1134 | 882 | bmp.source_branch.unsubscribe(owner) | 886 | bmp.source_branch.unsubscribe(owner, owner) |
1135 | 883 | recipients = bmp.getNotificationRecipients( | 887 | recipients = bmp.getNotificationRecipients( |
1136 | 884 | CodeReviewNotificationLevel.STATUS) | 888 | CodeReviewNotificationLevel.STATUS) |
1137 | 885 | self.assertFalse(owner in recipients) | 889 | self.assertFalse(owner in recipients) |
1138 | @@ -892,20 +896,20 @@ | |||
1139 | 892 | eric = self.factory.makePerson() | 896 | eric = self.factory.makePerson() |
1140 | 893 | bmp.source_branch.subscribe( | 897 | bmp.source_branch.subscribe( |
1141 | 894 | eric, BranchSubscriptionNotificationLevel.NOEMAIL, None, | 898 | eric, BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1143 | 895 | CodeReviewNotificationLevel.FULL) | 899 | CodeReviewNotificationLevel.FULL, eric) |
1144 | 896 | # Subscribe bob to the target branch only. | 900 | # Subscribe bob to the target branch only. |
1145 | 897 | bob = self.factory.makePerson() | 901 | bob = self.factory.makePerson() |
1146 | 898 | bmp.target_branch.subscribe( | 902 | bmp.target_branch.subscribe( |
1147 | 899 | bob, BranchSubscriptionNotificationLevel.NOEMAIL, None, | 903 | bob, BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1149 | 900 | CodeReviewNotificationLevel.FULL) | 904 | CodeReviewNotificationLevel.FULL, bob) |
1150 | 901 | # Subscribe charlie to both. | 905 | # Subscribe charlie to both. |
1151 | 902 | charlie = self.factory.makePerson() | 906 | charlie = self.factory.makePerson() |
1152 | 903 | bmp.source_branch.subscribe( | 907 | bmp.source_branch.subscribe( |
1153 | 904 | charlie, BranchSubscriptionNotificationLevel.NOEMAIL, None, | 908 | charlie, BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1155 | 905 | CodeReviewNotificationLevel.FULL) | 909 | CodeReviewNotificationLevel.FULL, charlie) |
1156 | 906 | bmp.target_branch.subscribe( | 910 | bmp.target_branch.subscribe( |
1157 | 907 | charlie, BranchSubscriptionNotificationLevel.NOEMAIL, None, | 911 | charlie, BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1159 | 908 | CodeReviewNotificationLevel.FULL) | 912 | CodeReviewNotificationLevel.FULL, charlie) |
1160 | 909 | # Make both branches private. | 913 | # Make both branches private. |
1161 | 910 | removeSecurityProxy(bmp.source_branch).private = True | 914 | removeSecurityProxy(bmp.source_branch).private = True |
1162 | 911 | removeSecurityProxy(bmp.target_branch).private = True | 915 | removeSecurityProxy(bmp.target_branch).private = True |
1163 | @@ -1157,7 +1161,7 @@ | |||
1164 | 1157 | proposal.source_branch.subscribe( | 1161 | proposal.source_branch.subscribe( |
1165 | 1158 | subscriber, | 1162 | subscriber, |
1166 | 1159 | BranchSubscriptionNotificationLevel.NOEMAIL, None, | 1163 | BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1168 | 1160 | CodeReviewNotificationLevel.NOEMAIL) | 1164 | CodeReviewNotificationLevel.NOEMAIL, subscriber) |
1169 | 1161 | self.assertEqual( | 1165 | self.assertEqual( |
1170 | 1162 | ['~albert/mike/work', '~albert/november/work'], | 1166 | ['~albert/mike/work', '~albert/november/work'], |
1171 | 1163 | self._get_merge_proposals(albert, visible_by_user=subscriber)) | 1167 | self._get_merge_proposals(albert, visible_by_user=subscriber)) |
1172 | 1164 | 1168 | ||
1173 | === added file 'lib/lp/code/model/tests/test_branchsubscription.py' | |||
1174 | --- lib/lp/code/model/tests/test_branchsubscription.py 1970-01-01 00:00:00 +0000 | |||
1175 | +++ lib/lp/code/model/tests/test_branchsubscription.py 2010-05-29 09:01:05 +0000 | |||
1176 | @@ -0,0 +1,115 @@ | |||
1177 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
1178 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
1179 | 3 | |||
1180 | 4 | """Tests for the BranchSubscrptions model object..""" | ||
1181 | 5 | |||
1182 | 6 | from __future__ import with_statement | ||
1183 | 7 | |||
1184 | 8 | __metaclass__ = type | ||
1185 | 9 | |||
1186 | 10 | import unittest | ||
1187 | 11 | |||
1188 | 12 | from canonical.testing.layers import DatabaseFunctionalLayer | ||
1189 | 13 | from lp.app.errors import UserCannotUnsubscribePerson | ||
1190 | 14 | from lp.code.enums import ( | ||
1191 | 15 | BranchSubscriptionNotificationLevel, CodeReviewNotificationLevel) | ||
1192 | 16 | from lp.testing import TestCaseWithFactory, person_logged_in | ||
1193 | 17 | |||
1194 | 18 | |||
1195 | 19 | class TestBranchSubscriptions(TestCaseWithFactory): | ||
1196 | 20 | """Tests relating to branch subscriptions in general.""" | ||
1197 | 21 | |||
1198 | 22 | layer = DatabaseFunctionalLayer | ||
1199 | 23 | |||
1200 | 24 | def test_subscribed_by_set(self): | ||
1201 | 25 | """The user subscribing is recorded along the subscriber.""" | ||
1202 | 26 | subscriber = self.factory.makePerson() | ||
1203 | 27 | subscribed_by = self.factory.makePerson() | ||
1204 | 28 | branch = self.factory.makeAnyBranch() | ||
1205 | 29 | subscription = branch.subscribe( | ||
1206 | 30 | subscriber, BranchSubscriptionNotificationLevel.NOEMAIL, None, | ||
1207 | 31 | CodeReviewNotificationLevel.NOEMAIL, subscribed_by) | ||
1208 | 32 | self.assertEqual(subscriber, subscription.person) | ||
1209 | 33 | self.assertEqual(subscribed_by, subscription.subscribed_by) | ||
1210 | 34 | |||
1211 | 35 | def test_unsubscribe(self): | ||
1212 | 36 | """Test unsubscribing by the subscriber.""" | ||
1213 | 37 | subscription = self.factory.makeBranchSubscription() | ||
1214 | 38 | subscriber = subscription.person | ||
1215 | 39 | branch = subscription.branch | ||
1216 | 40 | branch.unsubscribe(subscriber, subscriber) | ||
1217 | 41 | self.assertFalse(branch.hasSubscription(subscriber)) | ||
1218 | 42 | |||
1219 | 43 | def test_unsubscribe_by_subscriber(self): | ||
1220 | 44 | """Test unsubscribing by the person who subscribed the user.""" | ||
1221 | 45 | subscribed_by = self.factory.makePerson() | ||
1222 | 46 | subscription = self.factory.makeBranchSubscription( | ||
1223 | 47 | subscribed_by=subscribed_by) | ||
1224 | 48 | subscriber = subscription.person | ||
1225 | 49 | branch = subscription.branch | ||
1226 | 50 | branch.unsubscribe(subscriber, subscribed_by) | ||
1227 | 51 | self.assertFalse(branch.hasSubscription(subscriber)) | ||
1228 | 52 | |||
1229 | 53 | def test_unsubscribe_by_unauthorized(self): | ||
1230 | 54 | """Test unsubscribing someone you shouldn't be able to.""" | ||
1231 | 55 | subscription = self.factory.makeBranchSubscription() | ||
1232 | 56 | branch = subscription.branch | ||
1233 | 57 | self.assertRaises( | ||
1234 | 58 | UserCannotUnsubscribePerson, | ||
1235 | 59 | branch.unsubscribe, | ||
1236 | 60 | subscription.person, | ||
1237 | 61 | self.factory.makePerson()) | ||
1238 | 62 | |||
1239 | 63 | |||
1240 | 64 | class TestBranchSubscriptionCanBeUnsubscribedbyUser(TestCaseWithFactory): | ||
1241 | 65 | """Tests for BranchSubscription.canBeUnsubscribedByUser.""" | ||
1242 | 66 | |||
1243 | 67 | layer = DatabaseFunctionalLayer | ||
1244 | 68 | |||
1245 | 69 | def test_none(self): | ||
1246 | 70 | """None for a user always returns False.""" | ||
1247 | 71 | subscription = self.factory.makeBranchSubscription() | ||
1248 | 72 | self.assertFalse(subscription.canBeUnsubscribedByUser(None)) | ||
1249 | 73 | |||
1250 | 74 | def test_self_subscriber(self): | ||
1251 | 75 | """The subscriber has permission to unsubscribe.""" | ||
1252 | 76 | subscription = self.factory.makeBranchSubscription() | ||
1253 | 77 | self.assertTrue( | ||
1254 | 78 | subscription.canBeUnsubscribedByUser(subscription.person)) | ||
1255 | 79 | |||
1256 | 80 | def test_non_subscriber_fails(self): | ||
1257 | 81 | """An unrelated person can't unsubscribe a user.""" | ||
1258 | 82 | subscription = self.factory.makeBranchSubscription() | ||
1259 | 83 | editor = self.factory.makePerson() | ||
1260 | 84 | self.assertFalse(subscription.canBeUnsubscribedByUser(editor)) | ||
1261 | 85 | |||
1262 | 86 | def test_subscribed_by(self): | ||
1263 | 87 | """If a user subscribes someone else, the user can unsubscribe.""" | ||
1264 | 88 | subscribed_by = self.factory.makePerson() | ||
1265 | 89 | subscriber = self.factory.makePerson() | ||
1266 | 90 | subscription = self.factory.makeBranchSubscription( | ||
1267 | 91 | person=subscriber, subscribed_by=subscribed_by) | ||
1268 | 92 | self.assertTrue(subscription.canBeUnsubscribedByUser(subscribed_by)) | ||
1269 | 93 | |||
1270 | 94 | def test_team_member_can_unsubscribe(self): | ||
1271 | 95 | """Any team member can unsubscribe the team from a branch.""" | ||
1272 | 96 | team = self.factory.makeTeam() | ||
1273 | 97 | member = self.factory.makePerson() | ||
1274 | 98 | with person_logged_in(team.teamowner): | ||
1275 | 99 | team.addMember(member, team.teamowner) | ||
1276 | 100 | subscription = self.factory.makeBranchSubscription( | ||
1277 | 101 | person=team, subscribed_by=team.teamowner) | ||
1278 | 102 | self.assertTrue(subscription.canBeUnsubscribedByUser(member)) | ||
1279 | 103 | |||
1280 | 104 | def test_team_subscriber_can_unsubscribe(self): | ||
1281 | 105 | """A team can be unsubscribed by the subscriber even if they are not a | ||
1282 | 106 | member.""" | ||
1283 | 107 | team = self.factory.makeTeam() | ||
1284 | 108 | subscribed_by = self.factory.makePerson() | ||
1285 | 109 | subscription = self.factory.makeBranchSubscription( | ||
1286 | 110 | person=team, subscribed_by=subscribed_by) | ||
1287 | 111 | self.assertTrue(subscription.canBeUnsubscribedByUser(subscribed_by)) | ||
1288 | 112 | |||
1289 | 113 | |||
1290 | 114 | def test_suite(): | ||
1291 | 115 | return unittest.TestLoader().loadTestsFromName(__name__) | ||
1292 | 0 | 116 | ||
1293 | === modified file 'lib/lp/code/scripts/tests/test_scan_branches.py' | |||
1294 | --- lib/lp/code/scripts/tests/test_scan_branches.py 2010-04-28 05:40:02 +0000 | |||
1295 | +++ lib/lp/code/scripts/tests/test_scan_branches.py 2010-05-29 09:01:05 +0000 | |||
1296 | @@ -52,7 +52,8 @@ | |||
1297 | 52 | db_branch.registrant, | 52 | db_branch.registrant, |
1298 | 53 | BranchSubscriptionNotificationLevel.FULL, | 53 | BranchSubscriptionNotificationLevel.FULL, |
1299 | 54 | BranchSubscriptionDiffSize.WHOLEDIFF, | 54 | BranchSubscriptionDiffSize.WHOLEDIFF, |
1301 | 55 | CodeReviewNotificationLevel.FULL) | 55 | CodeReviewNotificationLevel.FULL, |
1302 | 56 | db_branch.registrant) | ||
1303 | 56 | transaction.commit() | 57 | transaction.commit() |
1304 | 57 | 58 | ||
1305 | 58 | self.run_script_and_assert_success() | 59 | self.run_script_and_assert_success() |
1306 | 59 | 60 | ||
1307 | === modified file 'lib/lp/code/scripts/tests/test_sendbranchmail.py' | |||
1308 | --- lib/lp/code/scripts/tests/test_sendbranchmail.py 2010-04-28 05:40:02 +0000 | |||
1309 | +++ lib/lp/code/scripts/tests/test_sendbranchmail.py 2010-05-29 09:01:05 +0000 | |||
1310 | @@ -24,10 +24,12 @@ | |||
1311 | 24 | 24 | ||
1312 | 25 | def createBranch(self): | 25 | def createBranch(self): |
1313 | 26 | branch, tree = self.create_branch_and_tree() | 26 | branch, tree = self.create_branch_and_tree() |
1315 | 27 | branch.subscribe(branch.registrant, | 27 | branch.subscribe( |
1316 | 28 | branch.registrant, | ||
1317 | 28 | BranchSubscriptionNotificationLevel.FULL, | 29 | BranchSubscriptionNotificationLevel.FULL, |
1318 | 29 | BranchSubscriptionDiffSize.WHOLEDIFF, | 30 | BranchSubscriptionDiffSize.WHOLEDIFF, |
1320 | 30 | CodeReviewNotificationLevel.FULL) | 31 | CodeReviewNotificationLevel.FULL, |
1321 | 32 | branch.registrant) | ||
1322 | 31 | transport = tree.bzrdir.root_transport | 33 | transport = tree.bzrdir.root_transport |
1323 | 32 | transport.put_bytes('foo', 'bar') | 34 | transport.put_bytes('foo', 'bar') |
1324 | 33 | tree.add('foo') | 35 | tree.add('foo') |
1325 | 34 | 36 | ||
1326 | === added file 'lib/lp/code/security.py' | |||
1327 | --- lib/lp/code/security.py 1970-01-01 00:00:00 +0000 | |||
1328 | +++ lib/lp/code/security.py 2010-05-29 09:01:05 +0000 | |||
1329 | @@ -0,0 +1,37 @@ | |||
1330 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
1331 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
1332 | 3 | |||
1333 | 4 | """Security adapters for the code module.""" | ||
1334 | 5 | |||
1335 | 6 | __metaclass__ = type | ||
1336 | 7 | __all__ = [ | ||
1337 | 8 | 'BranchSubscriptionEdit', | ||
1338 | 9 | 'BranchSubscriptionView', | ||
1339 | 10 | ] | ||
1340 | 11 | |||
1341 | 12 | from canonical.launchpad.security import AuthorizationBase | ||
1342 | 13 | |||
1343 | 14 | from lp.code.interfaces.branchsubscription import IBranchSubscription | ||
1344 | 15 | |||
1345 | 16 | |||
1346 | 17 | |||
1347 | 18 | class BranchSubscriptionEdit(AuthorizationBase): | ||
1348 | 19 | permission = 'launchpad.Edit' | ||
1349 | 20 | usedfor = IBranchSubscription | ||
1350 | 21 | |||
1351 | 22 | def checkAuthenticated(self, user): | ||
1352 | 23 | """Is the user able to edit a branch subscription? | ||
1353 | 24 | |||
1354 | 25 | Any team member can edit a branch subscription for their team. | ||
1355 | 26 | Launchpad Admins can also edit any branch subscription. | ||
1356 | 27 | """ | ||
1357 | 28 | return (user.inTeam(self.obj.person) or | ||
1358 | 29 | user.inTeam(self.obj.subscribed_by) or | ||
1359 | 30 | user.in_admin or | ||
1360 | 31 | user.in_bazaar_experts) | ||
1361 | 32 | |||
1362 | 33 | |||
1363 | 34 | class BranchSubscriptionView(BranchSubscriptionEdit): | ||
1364 | 35 | permission = 'launchpad.View' | ||
1365 | 36 | |||
1366 | 37 | |||
1367 | 0 | 38 | ||
1368 | === modified file 'lib/lp/code/stories/branches/xx-branch-edit.txt' | |||
1369 | --- lib/lp/code/stories/branches/xx-branch-edit.txt 2010-05-24 04:27:46 +0000 | |||
1370 | +++ lib/lp/code/stories/branches/xx-branch-edit.txt 2010-05-29 09:01:05 +0000 | |||
1371 | @@ -223,7 +223,7 @@ | |||
1372 | 223 | >>> _unused = foogoo_svn.subscribe(sample_person, | 223 | >>> _unused = foogoo_svn.subscribe(sample_person, |
1373 | 224 | ... BranchSubscriptionNotificationLevel.ATTRIBUTEONLY, | 224 | ... BranchSubscriptionNotificationLevel.ATTRIBUTEONLY, |
1374 | 225 | ... BranchSubscriptionDiffSize.NODIFF, | 225 | ... BranchSubscriptionDiffSize.NODIFF, |
1376 | 226 | ... CodeReviewNotificationLevel.NOEMAIL) | 226 | ... CodeReviewNotificationLevel.NOEMAIL, sample_person) |
1377 | 227 | >>> logout() | 227 | >>> logout() |
1378 | 228 | 228 | ||
1379 | 229 | >>> nopriv_browser = setupBrowser(auth='Basic no-priv@canonical.com:test') | 229 | >>> nopriv_browser = setupBrowser(auth='Basic no-priv@canonical.com:test') |
1380 | 230 | 230 | ||
1381 | === modified file 'lib/lp/code/stories/branches/xx-branchmergeproposals.txt' | |||
1382 | --- lib/lp/code/stories/branches/xx-branchmergeproposals.txt 2010-04-07 16:05:38 +0000 | |||
1383 | +++ lib/lp/code/stories/branches/xx-branchmergeproposals.txt 2010-05-29 09:01:05 +0000 | |||
1384 | @@ -19,7 +19,7 @@ | |||
1385 | 19 | ... '~name12/firefox/main') | 19 | ... '~name12/firefox/main') |
1386 | 20 | >>> _unused = branch.subscribe(subscriber, | 20 | >>> _unused = branch.subscribe(subscriber, |
1387 | 21 | ... BranchSubscriptionNotificationLevel.NOEMAIL, None, | 21 | ... BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1389 | 22 | ... CodeReviewNotificationLevel.FULL) | 22 | ... CodeReviewNotificationLevel.FULL, subscriber) |
1390 | 23 | 23 | ||
1391 | 24 | Also subscribe to gnome-terminal, since Firefox tests are mixed with | 24 | Also subscribe to gnome-terminal, since Firefox tests are mixed with |
1392 | 25 | Gnome Terminal tests. | 25 | Gnome Terminal tests. |
1393 | @@ -28,7 +28,7 @@ | |||
1394 | 28 | ... '~name12/gnome-terminal/main') | 28 | ... '~name12/gnome-terminal/main') |
1395 | 29 | >>> _unused = branch.subscribe(subscriber, | 29 | >>> _unused = branch.subscribe(subscriber, |
1396 | 30 | ... BranchSubscriptionNotificationLevel.NOEMAIL, None, | 30 | ... BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1398 | 31 | ... CodeReviewNotificationLevel.FULL) | 31 | ... CodeReviewNotificationLevel.FULL, subscriber) |
1399 | 32 | >>> from lp.code.tests.helpers import make_erics_fooix_project | 32 | >>> from lp.code.tests.helpers import make_erics_fooix_project |
1400 | 33 | >>> locals().update(make_erics_fooix_project(factory)) | 33 | >>> locals().update(make_erics_fooix_project(factory)) |
1401 | 34 | >>> logout() | 34 | >>> logout() |
1402 | 35 | 35 | ||
1403 | === modified file 'lib/lp/code/stories/branches/xx-person-branches.txt' | |||
1404 | --- lib/lp/code/stories/branches/xx-person-branches.txt 2010-05-13 16:22:19 +0000 | |||
1405 | +++ lib/lp/code/stories/branches/xx-person-branches.txt 2010-05-29 09:01:05 +0000 | |||
1406 | @@ -107,7 +107,7 @@ | |||
1407 | 107 | 107 | ||
1408 | 108 | >>> login(ANONYMOUS) | 108 | >>> login(ANONYMOUS) |
1409 | 109 | >>> b2 = factory.makeAnyBranch(owner=eric) | 109 | >>> b2 = factory.makeAnyBranch(owner=eric) |
1411 | 110 | >>> ignored = b2.unsubscribe(eric) | 110 | >>> ignored = b2.unsubscribe(eric, eric) |
1412 | 111 | >>> logout() | 111 | >>> logout() |
1413 | 112 | 112 | ||
1414 | 113 | >>> eric_browser.open('http://code.launchpad.dev/~eric') | 113 | >>> eric_browser.open('http://code.launchpad.dev/~eric') |
1415 | 114 | 114 | ||
1416 | === modified file 'lib/lp/code/stories/branches/xx-subscribing-branches.txt' | |||
1417 | --- lib/lp/code/stories/branches/xx-subscribing-branches.txt 2010-04-20 04:12:30 +0000 | |||
1418 | +++ lib/lp/code/stories/branches/xx-subscribing-branches.txt 2010-05-29 09:01:05 +0000 | |||
1419 | @@ -190,25 +190,8 @@ | |||
1420 | 190 | >>> browser.getControl('Person').value = 'landscape-developers' | 190 | >>> browser.getControl('Person').value = 'landscape-developers' |
1421 | 191 | >>> browser.getControl('Subscribe').click() | 191 | >>> browser.getControl('Subscribe').click() |
1422 | 192 | 192 | ||
1442 | 193 | The user can only subscribe teams that they are members of. | 193 | The user does not have to be in the team to subscribe them. |
1443 | 194 | 194 | ||
1425 | 195 | >>> for message in get_feedback_messages(browser.contents): | ||
1426 | 196 | ... print extract_text(message) | ||
1427 | 197 | There is 1 error. | ||
1428 | 198 | You can only subscribe teams that you are a member of. | ||
1429 | 199 | |||
1430 | 200 | Sample Person is a member of Landscape Developers. | ||
1431 | 201 | |||
1432 | 202 | >>> browser = setupBrowser(auth='Basic test@canonical.com:test') | ||
1433 | 203 | >>> browser.open( | ||
1434 | 204 | ... 'http://code.launchpad.dev/~name12/gnome-terminal/main') | ||
1435 | 205 | >>> browser.getLink('Subscribe someone else').click() | ||
1436 | 206 | >>> browser.getControl('Notification Level').displayValue = [ | ||
1437 | 207 | ... 'Branch attribute and revision notifications'] | ||
1438 | 208 | >>> browser.getControl('Generated Diff Size Limit').displayValue = [ | ||
1439 | 209 | ... '1000 lines'] | ||
1440 | 210 | >>> browser.getControl('Person').value = 'landscape-developers' | ||
1441 | 211 | >>> browser.getControl('Subscribe').click() | ||
1444 | 212 | >>> print_informational_message(browser.contents) | 195 | >>> print_informational_message(browser.contents) |
1445 | 213 | Landscape Developers has been subscribed to this branch with: | 196 | Landscape Developers has been subscribed to this branch with: |
1446 | 214 | Send notifications for both branch attribute updates | 197 | Send notifications for both branch attribute updates |
1447 | @@ -234,10 +217,10 @@ | |||
1448 | 234 | Editing a team subscription | 217 | Editing a team subscription |
1449 | 235 | =========================== | 218 | =========================== |
1450 | 236 | 219 | ||
1455 | 237 | In order to edit a team subscription the logged in user needs to be | 220 | In order to edit a team subscription the logged in user needs to be a member |
1456 | 238 | a member of the team that is subscribed. There is a link shown in | 221 | of the team that is subscribed, or must the the person who subscribed the team |
1457 | 239 | the subscriptions portlet to edit the subscription of a team that | 222 | to the branch. There is a link shown in the subscriptions portlet to edit the |
1458 | 240 | the logged in user is a member of. | 223 | subscription of a team that the logged in user is a member of. |
1459 | 241 | 224 | ||
1460 | 242 | XXX: thumper 2007-06-11 | 225 | XXX: thumper 2007-06-11 |
1461 | 243 | There should be a central user subscriptions page. This could then | 226 | There should be a central user subscriptions page. This could then |
1462 | @@ -293,7 +276,7 @@ | |||
1463 | 293 | ... CodeReviewNotificationLevel) | 276 | ... CodeReviewNotificationLevel) |
1464 | 294 | >>> ignored = branch.subscribe( | 277 | >>> ignored = branch.subscribe( |
1465 | 295 | ... private_team, BranchSubscriptionNotificationLevel.NOEMAIL, None, | 278 | ... private_team, BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1467 | 296 | ... CodeReviewNotificationLevel.NOEMAIL) | 279 | ... CodeReviewNotificationLevel.NOEMAIL, private_team.teamowner) |
1468 | 297 | >>> url = canonical_url(branch) | 280 | >>> url = canonical_url(branch) |
1469 | 298 | >>> logout() | 281 | >>> logout() |
1470 | 299 | 282 | ||
1471 | 300 | 283 | ||
1472 | === modified file 'lib/lp/code/stories/webservice/xx-branchsubscription.txt' | |||
1473 | --- lib/lp/code/stories/webservice/xx-branchsubscription.txt 2009-06-16 03:40:05 +0000 | |||
1474 | +++ lib/lp/code/stories/webservice/xx-branchsubscription.txt 2010-05-29 09:01:05 +0000 | |||
1475 | @@ -38,6 +38,7 @@ | |||
1476 | 38 | resource_type_link: u'http://.../#branch_subscription' | 38 | resource_type_link: u'http://.../#branch_subscription' |
1477 | 39 | review_level: u'No email' | 39 | review_level: u'No email' |
1478 | 40 | self_link: u'http://.../~farmer-bob/farm/corn/+subscription/farmer-joe' | 40 | self_link: u'http://.../~farmer-bob/farm/corn/+subscription/farmer-joe' |
1479 | 41 | subscribed_by_link: u'http://.../~salgado' | ||
1480 | 41 | 42 | ||
1481 | 42 | >>> def print_subscriber_count(branch): | 43 | >>> def print_subscriber_count(branch): |
1482 | 43 | ... subscribers = webservice.get( | 44 | ... subscribers = webservice.get( |
1483 | @@ -91,6 +92,7 @@ | |||
1484 | 91 | resource_type_link: u'http://.../#branch_subscription' | 92 | resource_type_link: u'http://.../#branch_subscription' |
1485 | 92 | review_level: u'Status changes only' | 93 | review_level: u'Status changes only' |
1486 | 93 | self_link: u'http://.../~farmer-bob/farm/corn/+subscription/farmer-joe' | 94 | self_link: u'http://.../~farmer-bob/farm/corn/+subscription/farmer-joe' |
1487 | 95 | subscribed_by_link: u'http://.../~salgado' | ||
1488 | 94 | 96 | ||
1489 | 95 | 97 | ||
1490 | 96 | We print the count, and even though subscribe was called again, there's still | 98 | We print the count, and even though subscribe was called again, there's still |
1491 | 97 | 99 | ||
1492 | === modified file 'lib/lp/code/tests/test_branch.py' | |||
1493 | --- lib/lp/code/tests/test_branch.py 2010-05-27 01:46:06 +0000 | |||
1494 | +++ lib/lp/code/tests/test_branch.py 2010-05-29 09:01:05 +0000 | |||
1495 | @@ -134,7 +134,7 @@ | |||
1496 | 134 | removeSecurityProxy(branch).subscribe( | 134 | removeSecurityProxy(branch).subscribe( |
1497 | 135 | person, BranchSubscriptionNotificationLevel.NOEMAIL, | 135 | person, BranchSubscriptionNotificationLevel.NOEMAIL, |
1498 | 136 | BranchSubscriptionDiffSize.NODIFF, | 136 | BranchSubscriptionDiffSize.NODIFF, |
1500 | 137 | CodeReviewNotificationLevel.NOEMAIL) | 137 | CodeReviewNotificationLevel.NOEMAIL, person) |
1501 | 138 | self.assertAuthenticatedView(branch, person, True) | 138 | self.assertAuthenticatedView(branch, person, True) |
1502 | 139 | 139 | ||
1503 | 140 | def test_privateBranchAnyoneElse(self): | 140 | def test_privateBranchAnyoneElse(self): |
1504 | 141 | 141 | ||
1505 | === modified file 'lib/lp/codehosting/scanner/tests/test_bzrsync.py' | |||
1506 | --- lib/lp/codehosting/scanner/tests/test_bzrsync.py 2010-05-03 09:31:06 +0000 | |||
1507 | +++ lib/lp/codehosting/scanner/tests/test_bzrsync.py 2010-05-29 09:01:05 +0000 | |||
1508 | @@ -85,7 +85,7 @@ | |||
1509 | 85 | LaunchpadZopelessLayer.txn.begin() | 85 | LaunchpadZopelessLayer.txn.begin() |
1510 | 86 | new_branch = self.factory.makeAnyBranch(*args, **kwargs) | 86 | new_branch = self.factory.makeAnyBranch(*args, **kwargs) |
1511 | 87 | # Unsubscribe the implicit owner subscription. | 87 | # Unsubscribe the implicit owner subscription. |
1513 | 88 | new_branch.unsubscribe(new_branch.owner) | 88 | new_branch.unsubscribe(new_branch.owner, new_branch.owner) |
1514 | 89 | LaunchpadZopelessLayer.txn.commit() | 89 | LaunchpadZopelessLayer.txn.commit() |
1515 | 90 | return new_branch | 90 | return new_branch |
1516 | 91 | 91 | ||
1517 | 92 | 92 | ||
1518 | === modified file 'lib/lp/codehosting/scanner/tests/test_email.py' | |||
1519 | --- lib/lp/codehosting/scanner/tests/test_email.py 2010-05-18 13:12:34 +0000 | |||
1520 | +++ lib/lp/codehosting/scanner/tests/test_email.py 2010-05-29 09:01:05 +0000 | |||
1521 | @@ -41,7 +41,8 @@ | |||
1522 | 41 | test_user, | 41 | test_user, |
1523 | 42 | BranchSubscriptionNotificationLevel.FULL, | 42 | BranchSubscriptionNotificationLevel.FULL, |
1524 | 43 | BranchSubscriptionDiffSize.FIVEKLINES, | 43 | BranchSubscriptionDiffSize.FIVEKLINES, |
1526 | 44 | CodeReviewNotificationLevel.NOEMAIL) | 44 | CodeReviewNotificationLevel.NOEMAIL, |
1527 | 45 | test_user) | ||
1528 | 45 | LaunchpadZopelessLayer.txn.commit() | 46 | LaunchpadZopelessLayer.txn.commit() |
1529 | 46 | return branch | 47 | return branch |
1530 | 47 | 48 | ||
1531 | @@ -151,7 +152,8 @@ | |||
1532 | 151 | db_branch.registrant, | 152 | db_branch.registrant, |
1533 | 152 | BranchSubscriptionNotificationLevel.FULL, | 153 | BranchSubscriptionNotificationLevel.FULL, |
1534 | 153 | BranchSubscriptionDiffSize.WHOLEDIFF, | 154 | BranchSubscriptionDiffSize.WHOLEDIFF, |
1536 | 154 | CodeReviewNotificationLevel.FULL) | 155 | CodeReviewNotificationLevel.FULL, |
1537 | 156 | db_branch.registrant) | ||
1538 | 155 | self.assertEqual(0, len(list(RevisionMailJob.iterReady()))) | 157 | self.assertEqual(0, len(list(RevisionMailJob.iterReady()))) |
1539 | 156 | notify(events.TipChanged(db_branch, tree.branch, True)) | 158 | notify(events.TipChanged(db_branch, tree.branch, True)) |
1540 | 157 | self.assertEqual(1, len(list(RevisionMailJob.iterReady()))) | 159 | self.assertEqual(1, len(list(RevisionMailJob.iterReady()))) |
1541 | @@ -164,7 +166,8 @@ | |||
1542 | 164 | db_branch.registrant, | 166 | db_branch.registrant, |
1543 | 165 | BranchSubscriptionNotificationLevel.FULL, | 167 | BranchSubscriptionNotificationLevel.FULL, |
1544 | 166 | BranchSubscriptionDiffSize.WHOLEDIFF, | 168 | BranchSubscriptionDiffSize.WHOLEDIFF, |
1546 | 167 | CodeReviewNotificationLevel.FULL) | 169 | CodeReviewNotificationLevel.FULL, |
1547 | 170 | db_branch.registrant) | ||
1548 | 168 | self.assertEqual(0, len(list(RevisionMailJob.iterReady()))) | 171 | self.assertEqual(0, len(list(RevisionMailJob.iterReady()))) |
1549 | 169 | notify(events.RevisionsRemoved(db_branch, tree.branch, ['x'])) | 172 | notify(events.RevisionsRemoved(db_branch, tree.branch, ['x'])) |
1550 | 170 | self.assertEqual(1, len(list(RevisionMailJob.iterReady()))) | 173 | self.assertEqual(1, len(list(RevisionMailJob.iterReady()))) |
1551 | 171 | 174 | ||
1552 | === modified file 'lib/lp/codehosting/tests/test_jobs.py' | |||
1553 | --- lib/lp/codehosting/tests/test_jobs.py 2010-04-23 05:49:08 +0000 | |||
1554 | +++ lib/lp/codehosting/tests/test_jobs.py 2010-05-29 09:01:05 +0000 | |||
1555 | @@ -30,7 +30,7 @@ | |||
1556 | 30 | branch.subscribe(branch.registrant, | 30 | branch.subscribe(branch.registrant, |
1557 | 31 | BranchSubscriptionNotificationLevel.FULL, | 31 | BranchSubscriptionNotificationLevel.FULL, |
1558 | 32 | BranchSubscriptionDiffSize.WHOLEDIFF, | 32 | BranchSubscriptionDiffSize.WHOLEDIFF, |
1560 | 33 | CodeReviewNotificationLevel.FULL) | 33 | CodeReviewNotificationLevel.FULL, branch.registrant) |
1561 | 34 | tree_transport = tree.bzrdir.root_transport | 34 | tree_transport = tree.bzrdir.root_transport |
1562 | 35 | tree_transport.put_bytes("hello.txt", "Hello World\n") | 35 | tree_transport.put_bytes("hello.txt", "Hello World\n") |
1563 | 36 | tree.add('hello.txt') | 36 | tree.add('hello.txt') |
1564 | 37 | 37 | ||
1565 | === modified file 'lib/lp/registry/browser/tests/packaging-views.txt' | |||
1566 | --- lib/lp/registry/browser/tests/packaging-views.txt 2010-04-16 18:00:31 +0000 | |||
1567 | +++ lib/lp/registry/browser/tests/packaging-views.txt 2010-05-29 09:01:05 +0000 | |||
1568 | @@ -350,5 +350,5 @@ | |||
1569 | 350 | cnews | 350 | cnews |
1570 | 351 | libstdc++ | 351 | libstdc++ |
1571 | 352 | linux-source-2.6.15 | 352 | linux-source-2.6.15 |
1572 | 353 | hot | ||
1573 | 353 | thunderbird | 354 | thunderbird |
1574 | 354 | hot | ||
1575 | 355 | 355 | ||
1576 | === modified file 'lib/lp/registry/browser/tests/private-team-creation-views.txt' | |||
1577 | --- lib/lp/registry/browser/tests/private-team-creation-views.txt 2009-08-03 21:46:09 +0000 | |||
1578 | +++ lib/lp/registry/browser/tests/private-team-creation-views.txt 2010-05-29 09:01:05 +0000 | |||
1579 | @@ -411,7 +411,7 @@ | |||
1580 | 411 | ... team, | 411 | ... team, |
1581 | 412 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, | 412 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, |
1582 | 413 | ... BranchSubscriptionDiffSize.WHOLEDIFF, | 413 | ... BranchSubscriptionDiffSize.WHOLEDIFF, |
1584 | 414 | ... CodeReviewNotificationLevel.STATUS) | 414 | ... CodeReviewNotificationLevel.STATUS, team_owner) |
1585 | 415 | ... # A branch visibility rule. | 415 | ... # A branch visibility rule. |
1586 | 416 | ... from lp.code.enums import BranchVisibilityRule | 416 | ... from lp.code.enums import BranchVisibilityRule |
1587 | 417 | ... from lp.registry.interfaces.product import IProductSet | 417 | ... from lp.registry.interfaces.product import IProductSet |
1588 | 418 | 418 | ||
1589 | === modified file 'lib/lp/registry/doc/private-team-roles.txt' | |||
1590 | --- lib/lp/registry/doc/private-team-roles.txt 2010-04-12 08:04:31 +0000 | |||
1591 | +++ lib/lp/registry/doc/private-team-roles.txt 2010-05-29 09:01:05 +0000 | |||
1592 | @@ -130,7 +130,7 @@ | |||
1593 | 130 | ... priv_team, | 130 | ... priv_team, |
1594 | 131 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, | 131 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, |
1595 | 132 | ... BranchSubscriptionDiffSize.WHOLEDIFF, | 132 | ... BranchSubscriptionDiffSize.WHOLEDIFF, |
1597 | 133 | ... CodeReviewNotificationLevel.STATUS) | 133 | ... CodeReviewNotificationLevel.STATUS, team_owner) |
1598 | 134 | >>> print subscription.person.name | 134 | >>> print subscription.person.name |
1599 | 135 | private-team | 135 | private-team |
1600 | 136 | 136 | ||
1601 | @@ -141,7 +141,7 @@ | |||
1602 | 141 | ... pm_team, | 141 | ... pm_team, |
1603 | 142 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, | 142 | ... BranchSubscriptionNotificationLevel.DIFFSONLY, |
1604 | 143 | ... BranchSubscriptionDiffSize.WHOLEDIFF, | 143 | ... BranchSubscriptionDiffSize.WHOLEDIFF, |
1606 | 144 | ... CodeReviewNotificationLevel.STATUS) | 144 | ... CodeReviewNotificationLevel.STATUS, team_owner) |
1607 | 145 | Traceback (most recent call last): | 145 | Traceback (most recent call last): |
1608 | 146 | ... | 146 | ... |
1609 | 147 | PrivatePersonLinkageError: Cannot link person | 147 | PrivatePersonLinkageError: Cannot link person |
1610 | 148 | 148 | ||
1611 | === modified file 'lib/lp/registry/model/distroseries.py' | |||
1612 | --- lib/lp/registry/model/distroseries.py 2010-05-19 15:39:52 +0000 | |||
1613 | +++ lib/lp/registry/model/distroseries.py 2010-05-29 09:01:05 +0000 | |||
1614 | @@ -332,7 +332,7 @@ | |||
1615 | 332 | origin = SQL(joins) | 332 | origin = SQL(joins) |
1616 | 333 | condition = SQL(conditions + "AND packaging.id IS NULL") | 333 | condition = SQL(conditions + "AND packaging.id IS NULL") |
1617 | 334 | results = IStore(self).using(origin).find(find_spec, condition) | 334 | results = IStore(self).using(origin).find(find_spec, condition) |
1619 | 335 | results = results.order_by('score DESC') | 335 | results = results.order_by('score DESC', SourcePackageName.name) |
1620 | 336 | return [{ | 336 | return [{ |
1621 | 337 | 'package': SourcePackage( | 337 | 'package': SourcePackage( |
1622 | 338 | sourcepackagename=spn, distroseries=self), | 338 | sourcepackagename=spn, distroseries=self), |
1623 | 339 | 339 | ||
1624 | === modified file 'lib/lp/testing/factory.py' | |||
1625 | --- lib/lp/testing/factory.py 2010-05-28 23:06:25 +0000 | |||
1626 | +++ lib/lp/testing/factory.py 2010-05-29 09:01:05 +0000 | |||
1627 | @@ -988,7 +988,8 @@ | |||
1628 | 988 | 988 | ||
1629 | 989 | return proposal | 989 | return proposal |
1630 | 990 | 990 | ||
1632 | 991 | def makeBranchSubscription(self, branch=None, person=None): | 991 | def makeBranchSubscription(self, branch=None, person=None, |
1633 | 992 | subscribed_by=None): | ||
1634 | 992 | """Create a BranchSubscription. | 993 | """Create a BranchSubscription. |
1635 | 993 | 994 | ||
1636 | 994 | :param branch_title: The title to use for the created Branch | 995 | :param branch_title: The title to use for the created Branch |
1637 | @@ -998,9 +999,11 @@ | |||
1638 | 998 | branch = self.makeBranch() | 999 | branch = self.makeBranch() |
1639 | 999 | if person is None: | 1000 | if person is None: |
1640 | 1000 | person = self.makePerson() | 1001 | person = self.makePerson() |
1641 | 1002 | if subscribed_by is None: | ||
1642 | 1003 | subscribed_by = person | ||
1643 | 1001 | return branch.subscribe(person, | 1004 | return branch.subscribe(person, |
1644 | 1002 | BranchSubscriptionNotificationLevel.NOEMAIL, None, | 1005 | BranchSubscriptionNotificationLevel.NOEMAIL, None, |
1646 | 1003 | CodeReviewNotificationLevel.NOEMAIL) | 1006 | CodeReviewNotificationLevel.NOEMAIL, subscribed_by) |
1647 | 1004 | 1007 | ||
1648 | 1005 | def makeDiff(self, diff_text=DIFF): | 1008 | def makeDiff(self, diff_text=DIFF): |
1649 | 1006 | return Diff.fromFile(StringIO(diff_text), len(diff_text)) | 1009 | return Diff.fromFile(StringIO(diff_text), len(diff_text)) |
> === modified file 'lib/lp/ code/browser/ branch. py' code/browser/ branch. py 2010-05-20 04:01:34 +0000 code/browser/ branch. py 2010-05-27 04:47:25 +0000 subscription_ notice( )
> --- lib/lp/
> +++ lib/lp/
> @@ -354,6 +354,7 @@
>
> def initialize(self):
> self.notices = []
> + # TODO: FIXME - this is almost certainly not what we want.
> self._add_
What do you want to do here?
> === modified file 'lib/lp/ code/browser/ branchsubscript ion.py' code/browser/ branchsubscript ion.py 2010-04-20 01:21:10 +0000 code/browser/ branchsubscript ion.py 2010-05-27 04:47:25 +0000 getSubscription (person) subscribe(
> --- lib/lp/
> +++ lib/lp/
> ...
>
> @@ -209,7 +210,7 @@
> subscription = self.context.
> if subscription is None:
> self.context.
> - person, notification_level, max_diff_lines, review_level)
> + person, notification_level, max_diff_lines, review_level, self.user)
Wrap the code at 78 characters.
> === modified file 'lib/lp/ code/model/ branchsubscript ion.py' code/model/ branchsubscript ion.py 2009-06-25 04:06:00 +0000 code/model/ branchsubscript ion.py 2010-05-27 04:47:25 +0000 enum=CodeReview NotificationLev el, 'subscribed_ by', foreignKey= 'Person' , =validate_ person_ not_private_ membership, notNull=True)
> --- lib/lp/
> +++ lib/lp/
> @@ -41,6 +41,9 @@
> notNull=False, default=DEFAULT)
> review_level = EnumCol(
> notNull=True, default=DEFAULT)
> + subscribed_by = ForeignKey(
> + dbName=
> + storm_validator
I was suggested in a recent review to drop the dbName since it is identical
to the column name. I did so, but now I ponder explicit is better than
implicit again.
> === modified file 'lib/lp/ registry/ browser/ tests/packaging -views. txt' registry/ browser/ tests/packaging -views. txt 2010-04-16 18:00:31 +0000 registry/ browser/ tests/packaging -views. txt 2010-05-27 04:47:25 +0000
> --- lib/lp/
> +++ lib/lp/
> @@ -350,5 +350,5 @@
> cnews
> libstdc++
> linux-source-2.6.15
> + hot
> thunderbird
> - hot
What caused this? This look like me from last week?
> === modified file 'lib/lp/ registry/ model/distroser ies.py' registry/ model/distroser ies.py 2010-05-12 23:23:19 +0000 registry/ model/distroser ies.py 2010-05-27 04:47:25 +0000 self).using( origin) .find(find_ spec, condition) order_by( 'score DESC') order_by( 'score DESC', SourcePackageNa me.name) me=spn, distroseries=self),
> --- lib/lp/
> +++ lib/lp/
> @@ -332,7 +332,7 @@
> origin = SQL(joins)
> condition = SQL(conditions + "AND packaging.id IS NULL")
> results = IStore(
> - results = results.
> + results = results.
> return [{
> 'package': SourcePackage(
> sourcepackagena
This is me from last week.
> === modified file 'lib/lp/ testing/ factory. py' testing/ factory. py 2010-05-25 13:13:02 +0000 testing/ factory. py 2010-05-27 04:47:25 +0000 ription( self, branch=None, person=None): ription( self, branch=None, person=None, subscri...
> --- lib/lp/
> +++ lib/lp/
> @@ -986,7 +986,7 @@
>
> return proposal
>
> - def makeBranchSubsc
> + def makeBranchSubsc