Merge lp:~benji/launchpad/bug-777786 into lp:launchpad
- bug-777786
- Merge into devel
Proposed by
Benji York
Status: | Merged |
---|---|
Approved by: | Benji York |
Approved revision: | no longer in the source branch. |
Merged at revision: | 13079 |
Proposed branch: | lp:~benji/launchpad/bug-777786 |
Merge into: | lp:launchpad |
Diff against target: |
471 lines (+267/-45) 5 files modified
lib/lp/bugs/browser/structuralsubscription.py (+22/-6) lib/lp/bugs/model/bugsubscriptionfilter.py (+8/-2) lib/lp/registry/interfaces/mailinglist.py (+6/-8) lib/lp/registry/javascript/structural-subscription.js (+77/-29) lib/lp/registry/javascript/tests/test_structural_subscription.js (+154/-0) |
To merge this branch: | bzr merge lp:~benji/launchpad/bug-777786 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Graham Binns (community) | code | Approve | |
Review via email:
|
Commit message
[r=gmb][bug=777786] instead of an ineffectual "stop email" link, give users the option to contact the subscribing team
Description of the change
Bug 777786 notes that it's somewhat rude to show users a "Stop my emails
from this subscription" for subscriptions via teams that have a contact
address because it won't actually stop the email. This branch replaces
that link with a link to contact the team with a pre-populated message
that the user can edit.
I also fixed a bit of lint in lib/lp/
The make lint report is clean.
The tests are in lib/lp/
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Graham Binns (gmb) : | # |
review:
Approve
(code)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/bugs/browser/structuralsubscription.py' | |||
2 | --- lib/lp/bugs/browser/structuralsubscription.py 2011-05-10 15:16:05 +0000 | |||
3 | +++ lib/lp/bugs/browser/structuralsubscription.py 2011-05-18 14:32:15 +0000 | |||
4 | @@ -14,7 +14,10 @@ | |||
5 | 14 | 'StructuralSubscribersPortletView', | 14 | 'StructuralSubscribersPortletView', |
6 | 15 | ] | 15 | ] |
7 | 16 | 16 | ||
9 | 17 | from operator import attrgetter | 17 | from operator import ( |
10 | 18 | attrgetter, | ||
11 | 19 | itemgetter, | ||
12 | 20 | ) | ||
13 | 18 | 21 | ||
14 | 19 | from lazr.restful.interfaces import ( | 22 | from lazr.restful.interfaces import ( |
15 | 20 | IJSONRequestCache, | 23 | IJSONRequestCache, |
16 | @@ -491,20 +494,33 @@ | |||
17 | 491 | subscriber = subscription.subscriber | 494 | subscriber = subscription.subscriber |
18 | 492 | for filter in subscription.bug_filters: | 495 | for filter in subscription.bug_filters: |
19 | 493 | is_team = subscriber.isTeam() | 496 | is_team = subscriber.isTeam() |
22 | 494 | user_is_team_admin = (is_team and | 497 | user_is_team_admin = ( |
23 | 495 | subscriber in administered_teams) | 498 | is_team and subscriber in administered_teams) |
24 | 499 | team_has_contact_address = ( | ||
25 | 500 | is_team and subscriber.preferredemail is not None) | ||
26 | 501 | mailing_list = subscriber.mailing_list | ||
27 | 502 | user_is_on_team_mailing_list = ( | ||
28 | 503 | team_has_contact_address and | ||
29 | 504 | mailing_list is not None and | ||
30 | 505 | mailing_list.is_usable and | ||
31 | 506 | mailing_list.getSubscription(subscriber) is not None) | ||
32 | 496 | record['filters'].append(dict( | 507 | record['filters'].append(dict( |
33 | 497 | filter=filter, | 508 | filter=filter, |
34 | 498 | subscriber_link=absoluteURL(subscriber, api_request), | 509 | subscriber_link=absoluteURL(subscriber, api_request), |
36 | 499 | subscriber_url = canonical_url( | 510 | subscriber_url=canonical_url( |
37 | 500 | subscriber, rootsite='mainsite'), | 511 | subscriber, rootsite='mainsite'), |
38 | 512 | target_bugs_url=canonical_url( | ||
39 | 513 | target, rootsite='bugs'), | ||
40 | 501 | subscriber_title=subscriber.title, | 514 | subscriber_title=subscriber.title, |
41 | 502 | subscriber_is_team=is_team, | 515 | subscriber_is_team=is_team, |
42 | 503 | user_is_team_admin=user_is_team_admin, | 516 | user_is_team_admin=user_is_team_admin, |
43 | 517 | team_has_contact_address=team_has_contact_address, | ||
44 | 518 | user_is_on_team_mailing_list=user_is_on_team_mailing_list, | ||
45 | 504 | can_mute=filter.isMuteAllowed(user), | 519 | can_mute=filter.isMuteAllowed(user), |
47 | 505 | is_muted=filter.muted(user) is not None)) | 520 | is_muted=filter.muted(user) is not None, |
48 | 521 | target_title=target.title)) | ||
49 | 506 | info = info.values() | 522 | info = info.values() |
51 | 507 | info.sort(key=lambda item: item['target_url']) | 523 | info.sort(key=itemgetter('target_url')) |
52 | 508 | IJSONRequestCache(request).objects['subscription_info'] = info | 524 | IJSONRequestCache(request).objects['subscription_info'] = info |
53 | 509 | 525 | ||
54 | 510 | 526 | ||
55 | 511 | 527 | ||
56 | === modified file 'lib/lp/bugs/model/bugsubscriptionfilter.py' | |||
57 | --- lib/lp/bugs/model/bugsubscriptionfilter.py 2011-04-05 22:34:35 +0000 | |||
58 | +++ lib/lp/bugs/model/bugsubscriptionfilter.py 2011-05-18 14:32:15 +0000 | |||
59 | @@ -255,9 +255,15 @@ | |||
60 | 255 | 255 | ||
61 | 256 | def isMuteAllowed(self, person): | 256 | def isMuteAllowed(self, person): |
62 | 257 | """See `IBugSubscriptionFilter`.""" | 257 | """See `IBugSubscriptionFilter`.""" |
63 | 258 | subscriber = self.structural_subscription.subscriber | ||
64 | 259 | # The person can mute the Subscription if the subscription is via a | ||
65 | 260 | # team of which they are a member and the team doesn't have a contact | ||
66 | 261 | # address (because if the team does, then the mute would be | ||
67 | 262 | # ineffectual). | ||
68 | 258 | return ( | 263 | return ( |
71 | 259 | self.structural_subscription.subscriber.isTeam() and | 264 | subscriber.isTeam() and |
72 | 260 | person.inTeam(self.structural_subscription.subscriber)) | 265 | person.inTeam(subscriber) and |
73 | 266 | subscriber.preferredemail is None) | ||
74 | 261 | 267 | ||
75 | 262 | def muted(self, person): | 268 | def muted(self, person): |
76 | 263 | store = Store.of(self) | 269 | store = Store.of(self) |
77 | 264 | 270 | ||
78 | === modified file 'lib/lp/registry/interfaces/mailinglist.py' | |||
79 | --- lib/lp/registry/interfaces/mailinglist.py 2011-05-12 21:33:10 +0000 | |||
80 | +++ lib/lp/registry/interfaces/mailinglist.py 2011-05-18 14:32:15 +0000 | |||
81 | @@ -110,7 +110,7 @@ | |||
82 | 110 | INACTIVE = DBItem(7, """ | 110 | INACTIVE = DBItem(7, """ |
83 | 111 | Inactive | 111 | Inactive |
84 | 112 | 112 | ||
86 | 113 | A previously active mailing lit has been made inactive by its team | 113 | A previously active mailing list has been made inactive by its team |
87 | 114 | owner. | 114 | owner. |
88 | 115 | """) | 115 | """) |
89 | 116 | 116 | ||
90 | @@ -132,7 +132,7 @@ | |||
91 | 132 | 132 | ||
92 | 133 | The mailing list has been flagged for deactivation by the team owner. | 133 | The mailing list has been flagged for deactivation by the team owner. |
93 | 134 | Mailman will be informed of this and will take the necessary actions | 134 | Mailman will be informed of this and will take the necessary actions |
95 | 135 | to deactive the list. | 135 | to deactivate the list. |
96 | 136 | """) | 136 | """) |
97 | 137 | 137 | ||
98 | 138 | MOD_FAILED = DBItem(11, """ | 138 | MOD_FAILED = DBItem(11, """ |
99 | @@ -246,8 +246,7 @@ | |||
100 | 246 | title=_('Review date'), | 246 | title=_('Review date'), |
101 | 247 | description=_('The date on which this mailing list registration was ' | 247 | description=_('The date on which this mailing list registration was ' |
102 | 248 | 'reviewed, or None if the registration has not yet ' | 248 | 'reviewed, or None if the registration has not yet ' |
105 | 249 | 'been reviewed.') | 249 | 'been reviewed.')) |
104 | 250 | ) | ||
106 | 251 | 250 | ||
107 | 252 | date_activated = Datetime( | 251 | date_activated = Datetime( |
108 | 253 | title=_('Activation date'), | 252 | title=_('Activation date'), |
109 | @@ -255,8 +254,7 @@ | |||
110 | 255 | 'meaning that the Mailman process has successfully ' | 254 | 'meaning that the Mailman process has successfully ' |
111 | 256 | 'created it. This may be None if the mailing list ' | 255 | 'created it. This may be None if the mailing list ' |
112 | 257 | 'has not yet been activated, or that its activation ' | 256 | 'has not yet been activated, or that its activation ' |
115 | 258 | 'has failed.') | 257 | 'has failed.')) |
114 | 259 | ) | ||
116 | 260 | 258 | ||
117 | 261 | status = Choice( | 259 | status = Choice( |
118 | 262 | title=_('Status'), | 260 | title=_('Status'), |
119 | @@ -434,8 +432,8 @@ | |||
120 | 434 | def getReviewableMessages(message_id_filter=None): | 432 | def getReviewableMessages(message_id_filter=None): |
121 | 435 | """Return the set of all held messages for this list requiring review. | 433 | """Return the set of all held messages for this list requiring review. |
122 | 436 | 434 | ||
125 | 437 | :param message_id_filter: If supplied only messages with message ids in | 435 | :param message_id_filter: If supplied only messages with message ids |
126 | 438 | the filter are returned. | 436 | in the filter are returned. |
127 | 439 | :return: A sequence of `IMessageApproval`s for this mailing list, | 437 | :return: A sequence of `IMessageApproval`s for this mailing list, |
128 | 440 | where the status is `PostedMessageStatus.NEW`. The returned set | 438 | where the status is `PostedMessageStatus.NEW`. The returned set |
129 | 441 | is ordered first by the date the message was posted, then by | 439 | is ordered first by the date the message was posted, then by |
130 | 442 | 440 | ||
131 | === modified file 'lib/lp/registry/javascript/structural-subscription.js' | |||
132 | --- lib/lp/registry/javascript/structural-subscription.js 2011-05-11 21:01:58 +0000 | |||
133 | +++ lib/lp/registry/javascript/structural-subscription.js 2011-05-18 14:32:15 +0000 | |||
134 | @@ -1239,7 +1239,7 @@ | |||
135 | 1239 | } else { | 1239 | } else { |
136 | 1240 | filter_info.is_muted = false; | 1240 | filter_info.is_muted = false; |
137 | 1241 | } | 1241 | } |
139 | 1242 | handle_mute(node, filter_info.is_muted); | 1242 | handle_mute(node, filter_info); |
140 | 1243 | }, | 1243 | }, |
141 | 1244 | failure: error_handler.getFailureHandler() | 1244 | failure: error_handler.getFailureHandler() |
142 | 1245 | } | 1245 | } |
143 | @@ -1304,11 +1304,11 @@ | |||
144 | 1304 | /** | 1304 | /** |
145 | 1305 | * For a given filter node, set it up properly based on mute state. | 1305 | * For a given filter node, set it up properly based on mute state. |
146 | 1306 | */ | 1306 | */ |
148 | 1307 | function handle_mute(node, muted) { | 1307 | function handle_mute(node, filter_info) { |
149 | 1308 | var control = node.one('a.mute-subscription'); | 1308 | var control = node.one('a.mute-subscription'); |
150 | 1309 | var label = node.one('em.mute-label'); | 1309 | var label = node.one('em.mute-label'); |
151 | 1310 | var description = node.one('.filter-description'); | 1310 | var description = node.one('.filter-description'); |
153 | 1311 | if (muted) { | 1311 | if (filter_info.is_muted) { |
154 | 1312 | control.set('text', 'Send my emails for this subscription'); | 1312 | control.set('text', 'Send my emails for this subscription'); |
155 | 1313 | control.replaceClass(MUTE_ICON_CLASS, UNMUTE_ICON_CLASS); | 1313 | control.replaceClass(MUTE_ICON_CLASS, UNMUTE_ICON_CLASS); |
156 | 1314 | label.setStyle('display', null); | 1314 | label.setStyle('display', null); |
157 | @@ -1335,23 +1335,31 @@ | |||
158 | 1335 | '<strong class="filter-name"></strong>')); | 1335 | '<strong class="filter-name"></strong>')); |
159 | 1336 | 1336 | ||
160 | 1337 | if (filter_info.can_mute) { | 1337 | if (filter_info.can_mute) { |
164 | 1338 | filter_node.appendChild(Y.Node.create( | 1338 | filter_node.append(Y.Node.create('<em/>') |
165 | 1339 | '<em class="mute-label" style="padding-left: 1em;">You '+ | 1339 | .set('text', 'You do not receive emails from this subscription.') |
166 | 1340 | 'do not receive emails from this subscription.</em>')); | 1340 | .addClass('mute-label') |
167 | 1341 | .setStyle('paddingLeft', '1em')); | ||
168 | 1341 | } | 1342 | } |
169 | 1342 | 1343 | ||
170 | 1343 | var control = filter_node.appendChild( | 1344 | var control = filter_node.appendChild( |
171 | 1344 | Y.Node.create('<span style="float: right"></span>')); | 1345 | Y.Node.create('<span style="float: right"></span>')); |
172 | 1345 | 1346 | ||
173 | 1346 | if (filter_info.can_mute) { | 1347 | if (filter_info.can_mute) { |
182 | 1347 | var link = control.appendChild(Y.Node.create( | 1348 | var link = control.appendChild(Y.Node.create('<a/>') |
183 | 1348 | '<a href="#" class="sprite js-action mute-subscription"></a>')); | 1349 | .set('href', '#') |
184 | 1349 | var help = control.appendChild(Y.Node.create( | 1350 | .addClass('sprite') |
185 | 1350 | '<a target="help" class="sprite maybe mute-help"'+ | 1351 | .addClass('js-action') |
186 | 1351 | ' style="visibility: hidden;"'+ | 1352 | .addClass('mute-subscription')); |
187 | 1352 | ' href="/+help/structural-subscription-mute.html">'+ | 1353 | var help = control.appendChild(Y.Node.create('<a/>') |
188 | 1353 | ' <span class="invisible-link">Delivery help</span>'+ | 1354 | .set('href', '/+help/structural-subscription-mute.html') |
189 | 1354 | '</a>')); | 1355 | .set('target', 'help') |
190 | 1356 | .addClass('sprite') | ||
191 | 1357 | .addClass('maybe') | ||
192 | 1358 | .addClass('mute-help') | ||
193 | 1359 | .setStyle('visibility', 'hidden') | ||
194 | 1360 | .append(Y.Node.create('<span/>') | ||
195 | 1361 | .addClass('invisible-link') | ||
196 | 1362 | .set('text', 'Delivery help'))); | ||
197 | 1355 | // For some reason the help link will not appear in Chrome unless | 1363 | // For some reason the help link will not appear in Chrome unless |
198 | 1356 | // there is a non-empty element immediately after the help node. | 1364 | // there is a non-empty element immediately after the help node. |
199 | 1357 | control.append(Y.Node.create('<span> </span>')); | 1365 | control.append(Y.Node.create('<span> </span>')); |
200 | @@ -1362,7 +1370,7 @@ | |||
201 | 1362 | help.setStyle('visibility', 'visible'); | 1370 | help.setStyle('visibility', 'visible'); |
202 | 1363 | // Every time we trigger the display of the help link we need to | 1371 | // Every time we trigger the display of the help link we need to |
203 | 1364 | // cancel any pending hiding of the help link so it doesn't | 1372 | // cancel any pending hiding of the help link so it doesn't |
205 | 1365 | // dissapear on us. If there isn't one pending, this is a NOP. | 1373 | // disappear on us. If there isn't one pending, this is a NOP. |
206 | 1366 | clearTimeout(hide_help_timeout); | 1374 | clearTimeout(hide_help_timeout); |
207 | 1367 | }; | 1375 | }; |
208 | 1368 | var hide_help = function () { | 1376 | var hide_help = function () { |
209 | @@ -1376,20 +1384,56 @@ | |||
210 | 1376 | 1384 | ||
211 | 1377 | if (can_edit(filter_info)) { | 1385 | if (can_edit(filter_info)) { |
212 | 1378 | // User can edit the subscription. | 1386 | // User can edit the subscription. |
224 | 1379 | control.append(Y.Node.create( | 1387 | control.append(Y.Node.create('<a/>') |
225 | 1380 | '<a href="#" style="margin-right: 2em;"'+ | 1388 | .set('href', '#') |
226 | 1381 | ' class="sprite modify edit js-action edit-subscription">'+ | 1389 | .set('text', 'Edit this subscription') |
227 | 1382 | ' Edit this subscription</a>'+ | 1390 | .setStyle('marginRight', '2em') |
228 | 1383 | '<a href="#" class="sprite modify remove js-action '+ | 1391 | .addClass('sprite') |
229 | 1384 | ' delete-subscription">Unsubscribe</a>')); | 1392 | .addClass('modify') |
230 | 1385 | } | 1393 | .addClass('edit') |
231 | 1386 | 1394 | .addClass('js-action') | |
232 | 1387 | filter_node.appendChild( | 1395 | .addClass('edit-subscription')); |
233 | 1388 | Y.Node.create('<div style="padding-left: 1em" '+ | 1396 | control.append(Y.Node.create('<a/>') |
234 | 1389 | 'class="filter-description"></div>')); | 1397 | .set('href', '#') |
235 | 1398 | .set('text', 'Unsubscribe') | ||
236 | 1399 | .addClass('sprite') | ||
237 | 1400 | .addClass('modify') | ||
238 | 1401 | .addClass('remove') | ||
239 | 1402 | .addClass('js-action') | ||
240 | 1403 | .addClass('delete-subscription')); | ||
241 | 1404 | } | ||
242 | 1405 | |||
243 | 1406 | if (filter_info.team_has_contact_address | ||
244 | 1407 | && !filter_info.user_is_team_admin) { | ||
245 | 1408 | var subject = urlEncode('Team contact address and subscriptions'); | ||
246 | 1409 | var user_participation; | ||
247 | 1410 | if (filter_info.user_is_on_team_mailing_list) { | ||
248 | 1411 | user_participation = 'subscribe to the team\'s mailing list'; | ||
249 | 1412 | } else { | ||
250 | 1413 | user_participation = 'be a part of the team'; | ||
251 | 1414 | } | ||
252 | 1415 | var message = urlEncode( | ||
253 | 1416 | 'Hello. I receive email notifications about bugs in '+ | ||
254 | 1417 | filter_info.target_title+' because of a team subscription for '+ | ||
255 | 1418 | filter_info.subscriber_title+'. I would like to continue to '+ | ||
256 | 1419 | user_participation+', but I would like to receive less email '+ | ||
257 | 1420 | 'from this subscription. Could you remove the team contact '+ | ||
258 | 1421 | 'email address so that the team members can manage their own '+ | ||
259 | 1422 | 'subscriptions (see '+filter_info.subscriber_url+'), or delete '+ | ||
260 | 1423 | 'or reduce level of the subscription itself (see '+ | ||
261 | 1424 | filter_info.target_bugs_url+'/+subscriptions)?\n\nThank you.'); | ||
262 | 1425 | control.append(Y.Node.create('<a/>') | ||
263 | 1426 | .set('href', filter_info.subscriber_url+'/+contactuser'+ | ||
264 | 1427 | '?field.message='+message+'&field.subject='+subject) | ||
265 | 1428 | .set('text', 'Request team administrators change')); | ||
266 | 1429 | } | ||
267 | 1430 | |||
268 | 1431 | filter_node.append(Y.Node.create('<div/>') | ||
269 | 1432 | .setStyle('paddingLeft', '1em') | ||
270 | 1433 | .addClass('filter-description')); | ||
271 | 1390 | 1434 | ||
272 | 1391 | if (filter_info.can_mute) { | 1435 | if (filter_info.can_mute) { |
274 | 1392 | handle_mute(filter_node, filter_info.is_muted); | 1436 | handle_mute(filter_node, filter_info); |
275 | 1393 | } | 1437 | } |
276 | 1394 | 1438 | ||
277 | 1395 | fill_filter_description(filter_node, filter_info, filter); | 1439 | fill_filter_description(filter_node, filter_info, filter); |
278 | @@ -1397,6 +1441,10 @@ | |||
279 | 1397 | return filter_node; | 1441 | return filter_node; |
280 | 1398 | } | 1442 | } |
281 | 1399 | 1443 | ||
282 | 1444 | // Expose in the namespace for testing purposes. | ||
283 | 1445 | namespace._create_filter_node = create_filter_node; | ||
284 | 1446 | |||
285 | 1447 | |||
286 | 1400 | /** | 1448 | /** |
287 | 1401 | * Create a node with subscription description. | 1449 | * Create a node with subscription description. |
288 | 1402 | */ | 1450 | */ |
289 | @@ -1424,7 +1472,7 @@ | |||
290 | 1424 | // and see the information you expect to see. | 1472 | // and see the information you expect to see. |
291 | 1425 | LP.cache['structural-subscription-filter-'+filter_id.toString()] = | 1473 | LP.cache['structural-subscription-filter-'+filter_id.toString()] = |
292 | 1426 | filter; | 1474 | filter; |
294 | 1427 | node.appendChild(create_filter_node(filter_id, filter_info, filter)); | 1475 | node.append(create_filter_node(filter_id, filter_info, filter)); |
295 | 1428 | filter_id += 1; | 1476 | filter_id += 1; |
296 | 1429 | } | 1477 | } |
297 | 1430 | return node; | 1478 | return node; |
298 | @@ -1641,7 +1689,7 @@ | |||
299 | 1641 | var team_info = get_team_info(form_data); | 1689 | var team_info = get_team_info(form_data); |
300 | 1642 | 1690 | ||
301 | 1643 | var filter_info = { | 1691 | var filter_info = { |
303 | 1644 | filter : filter, | 1692 | filter: filter, |
304 | 1645 | subscriber_is_team: team_info.is_team, | 1693 | subscriber_is_team: team_info.is_team, |
305 | 1646 | user_is_team_admin: team_info.is_team, | 1694 | user_is_team_admin: team_info.is_team, |
306 | 1647 | can_mute: team_info.is_team, | 1695 | can_mute: team_info.is_team, |
307 | 1648 | 1696 | ||
308 | === modified file 'lib/lp/registry/javascript/tests/test_structural_subscription.js' | |||
309 | --- lib/lp/registry/javascript/tests/test_structural_subscription.js 2011-05-11 21:05:17 +0000 | |||
310 | +++ lib/lp/registry/javascript/tests/test_structural_subscription.js 2011-05-18 14:32:15 +0000 | |||
311 | @@ -716,6 +716,160 @@ | |||
312 | 716 | suite.add(test_case); | 716 | suite.add(test_case); |
313 | 717 | 717 | ||
314 | 718 | suite.add(new Y.Test.Case({ | 718 | suite.add(new Y.Test.Case({ |
315 | 719 | name: 'Structural Subscription team contact', | ||
316 | 720 | |||
317 | 721 | _should: {error: {}}, | ||
318 | 722 | |||
319 | 723 | setUp: function() { | ||
320 | 724 | // Monkeypatch LP to avoid network traffic and to allow | ||
321 | 725 | // insertion of test data. | ||
322 | 726 | this.original_lp = monkeypatch_LP(); | ||
323 | 727 | |||
324 | 728 | LP.cache.subscription_info = [{ | ||
325 | 729 | target_url: 'http://example.com', | ||
326 | 730 | target_title:'Example project', | ||
327 | 731 | filters: [{ | ||
328 | 732 | filter: { | ||
329 | 733 | description: 'DESCRIPTION', | ||
330 | 734 | statuses: [], | ||
331 | 735 | importances: [], | ||
332 | 736 | tags: [], | ||
333 | 737 | find_all_tags: true, | ||
334 | 738 | bug_notification_level: 'Discussion', | ||
335 | 739 | self_link: 'http://example.com/a_filter' | ||
336 | 740 | }, | ||
337 | 741 | can_mute: true, | ||
338 | 742 | is_muted: false, | ||
339 | 743 | team_has_contact_address: true, | ||
340 | 744 | user_is_team_admin: false, | ||
341 | 745 | subscriber_is_team: true, | ||
342 | 746 | subscriber_url: 'http://example.com/subscriber', | ||
343 | 747 | subscriber_title: 'Thidwick' | ||
344 | 748 | }] | ||
345 | 749 | }]; | ||
346 | 750 | this.configuration = { | ||
347 | 751 | content_box: content_box_id, | ||
348 | 752 | lp_client: make_lp_client_stub() | ||
349 | 753 | }; | ||
350 | 754 | |||
351 | 755 | this.content_node = create_test_node(); | ||
352 | 756 | Y.one('body').appendChild(this.content_node); | ||
353 | 757 | }, | ||
354 | 758 | |||
355 | 759 | tearDown: function() { | ||
356 | 760 | window.LP = this.original_lp; | ||
357 | 761 | remove_test_node(); | ||
358 | 762 | delete this.content_node; | ||
359 | 763 | }, | ||
360 | 764 | |||
361 | 765 | test_administrative_change_link: function() { | ||
362 | 766 | var filter_info = { | ||
363 | 767 | filter: { | ||
364 | 768 | description: 'DESCRIPTION', | ||
365 | 769 | statuses: [], | ||
366 | 770 | importances: [], | ||
367 | 771 | tags: [], | ||
368 | 772 | find_all_tags: true, | ||
369 | 773 | bug_notification_level: 'Discussion', | ||
370 | 774 | self_link: 'http://example.com/a_filter' | ||
371 | 775 | }, | ||
372 | 776 | can_mute: true, | ||
373 | 777 | is_muted: false, | ||
374 | 778 | team_has_contact_address: true, | ||
375 | 779 | user_is_team_admin: false, | ||
376 | 780 | user_is_on_team_mailing_list: true, | ||
377 | 781 | subscriber_is_team: true, | ||
378 | 782 | subscriber_url: 'http://example.com/subscriber', | ||
379 | 783 | subscriber_title: 'Thidwick' | ||
380 | 784 | }; | ||
381 | 785 | var node = module._create_filter_node( | ||
382 | 786 | 'ID', filter_info, filter_info.filter); | ||
383 | 787 | var content = node.getContent(); | ||
384 | 788 | // If a subscription is via a team and the user isn't a team | ||
385 | 789 | // admin and the team has a contact address, the user gets a link | ||
386 | 790 | // to request the administrators change the subscription or drop | ||
387 | 791 | // the contact address. | ||
388 | 792 | Assert.areNotEqual( | ||
389 | 793 | -1, | ||
390 | 794 | content.search(/Request team administrators change/)); | ||
391 | 795 | // If the team's contact address is to a (launchpad-managed) | ||
392 | 796 | // mailing list, then the pre-filled in email message is phrased | ||
393 | 797 | // accordingly. | ||
394 | 798 | Assert.areNotEqual( | ||
395 | 799 | -1, | ||
396 | 800 | content.search(/subscribe%20to%20the%20team/)); | ||
397 | 801 | }, | ||
398 | 802 | |||
399 | 803 | test_administrative_change_link_no_mailing_list: function() { | ||
400 | 804 | var filter_info = { | ||
401 | 805 | filter: { | ||
402 | 806 | description: 'DESCRIPTION', | ||
403 | 807 | statuses: [], | ||
404 | 808 | importances: [], | ||
405 | 809 | tags: [], | ||
406 | 810 | find_all_tags: true, | ||
407 | 811 | bug_notification_level: 'Discussion', | ||
408 | 812 | self_link: 'http://example.com/a_filter' | ||
409 | 813 | }, | ||
410 | 814 | can_mute: true, | ||
411 | 815 | is_muted: false, | ||
412 | 816 | team_has_contact_address: true, | ||
413 | 817 | user_is_team_admin: false, | ||
414 | 818 | user_is_on_team_mailing_list: false, | ||
415 | 819 | subscriber_is_team: true, | ||
416 | 820 | subscriber_url: 'http://example.com/subscriber', | ||
417 | 821 | subscriber_title: 'Thidwick' | ||
418 | 822 | }; | ||
419 | 823 | var node = module._create_filter_node( | ||
420 | 824 | 'ID', filter_info, filter_info.filter); | ||
421 | 825 | var content = node.getContent(); | ||
422 | 826 | // If a subscription is via a team and the user isn't a team | ||
423 | 827 | // admin and the team has a contact address, the user gets a link | ||
424 | 828 | // to request the administrators change the subscription or drop | ||
425 | 829 | // the contact address. | ||
426 | 830 | Assert.areNotEqual( | ||
427 | 831 | -1, | ||
428 | 832 | content.search(/Request team administrators change/)); | ||
429 | 833 | // If the team's contact address is not a (launchpad-managed) | ||
430 | 834 | // mailing list, then the pre-filled in email message is phrased | ||
431 | 835 | // accordingly. | ||
432 | 836 | Assert.areNotEqual( | ||
433 | 837 | -1, | ||
434 | 838 | content.search(/be%20a%20part%20of%20the%20team/)); | ||
435 | 839 | }, | ||
436 | 840 | |||
437 | 841 | test_mute_not_shown_when_ineffectual: function() { | ||
438 | 842 | // If muting the subscription in question won't have an effect, | ||
439 | 843 | // then the mute link isn't shown. | ||
440 | 844 | var filter_info = { | ||
441 | 845 | filter: { | ||
442 | 846 | description: 'DESCRIPTION', | ||
443 | 847 | statuses: [], | ||
444 | 848 | importances: [], | ||
445 | 849 | tags: [], | ||
446 | 850 | find_all_tags: true, | ||
447 | 851 | bug_notification_level: 'Discussion', | ||
448 | 852 | self_link: 'http://example.com/a_filter' | ||
449 | 853 | }, | ||
450 | 854 | can_mute: false, | ||
451 | 855 | is_muted: false, | ||
452 | 856 | team_has_contact_address: true, | ||
453 | 857 | user_is_team_admin: false, | ||
454 | 858 | user_is_on_team_mailing_list: true, | ||
455 | 859 | subscriber_is_team: true, | ||
456 | 860 | subscriber_url: 'http://example.com/subscriber', | ||
457 | 861 | subscriber_title: 'Thidwick' | ||
458 | 862 | }; | ||
459 | 863 | var node = module._create_filter_node( | ||
460 | 864 | 'ID', filter_info, filter_info.filter); | ||
461 | 865 | var content = node.getContent(); | ||
462 | 866 | Assert.areEqual( | ||
463 | 867 | -1, | ||
464 | 868 | content.search(/Stop my emails/)); | ||
465 | 869 | } | ||
466 | 870 | })); | ||
467 | 871 | |||
468 | 872 | suite.add(new Y.Test.Case({ | ||
469 | 719 | name: 'Structural Subscription: deleting failed filters', | 873 | name: 'Structural Subscription: deleting failed filters', |
470 | 720 | 874 | ||
471 | 721 | _should: {error: {}}, | 875 | _should: {error: {}}, |