Merge lp:~gmb/launchpad/mute-button-cleanup-bug-204980 into lp:launchpad

Proposed by Graham Binns
Status: Merged
Approved by: Leonard Richardson
Approved revision: no longer in the source branch.
Merged at revision: 12602
Proposed branch: lp:~gmb/launchpad/mute-button-cleanup-bug-204980
Merge into: lp:launchpad
Diff against target: 261 lines (+117/-28)
5 files modified
lib/lp/bugs/browser/bug.py (+9/-0)
lib/lp/bugs/browser/bugsubscription.py (+3/-0)
lib/lp/bugs/browser/tests/test_bugsubscription_views.py (+28/-0)
lib/lp/bugs/javascript/bugtask_index_portlets.js (+70/-17)
lib/lp/bugs/templates/bug-portlet-subscribers.pt (+7/-11)
To merge this branch: bzr merge lp:~gmb/launchpad/mute-button-cleanup-bug-204980
Reviewer Review Type Date Requested Status
Leonard Richardson (community) Approve
Review via email: mp+53401@code.launchpad.net

Commit message

[r=leonardr][bug=204980] A mute button has been added to all bug pages for members of the Malone Alpha team.

Description of the change

This branch does a bunch of clean-up work for the bug mail mute button
story and activates the mute link for malone-alpha members.

The main aim of this branch was to make the mute button behave sensibly
and to not have other parts of the UI doing things which could confuse
the user. To this end, I've made the following tweaks:

 - Muted users no longer appear in the subscriber list
 - The subscribe link doesn't change when a user is muted; it will now
   always display "Subscribe me" for muted subscriptions.

I've made the following changes:

== lib/lp/bugs/browser/bug.py ==

 - I've added a current_user_mute_class property. This is used when
   rendering the subscribers portlet so that the JavaScript can pick up
   the user's mute status based on the CSS class of the Mute link's
   parent (this might not be the most elegant way to do things but it's
   consistent with how the Subscribe link works and I can't think of a
   better way ATM).

== lib/lp/bugs/browser/bugsubscription.py ==

 - I've updated the BugPortletSubscribersContents so that the
   sorted_direct_subscriptions property doesn't return muted
   subscriptions.

== lib/lp/bugs/browser/tests/test_bugsubscription_views.py ==

 - I've added a test case to cover the change to
   BugPortletSubscribersContents.sorted_direct_subscriptions.

== lib/lp/bugs/javascript/bugtask_index_portlets.js ==

 - I've refactored the existing JS and added code to make the Mute link
   behave as it should. It also now updates the Subscribe link properly.

== lib/lp/bugs/templates/bug-portlet-subscribers.pt ==

 - I've enabled the mute link for malone-alpha users via the
   malone.advanced-subscriptions.enabled flag.

To post a comment you must log in.
Revision history for this message
Leonard Richardson (leonardr) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/bugs/browser/bug.py'
--- lib/lp/bugs/browser/bug.py 2011-03-09 11:53:54 +0000
+++ lib/lp/bugs/browser/bug.py 2011-03-15 11:01:24 +0000
@@ -511,6 +511,15 @@
511 else:511 else:
512 return 'subscribed-false %s' % dup_class512 return 'subscribed-false %s' % dup_class
513513
514 @property
515 def current_user_mute_class(self):
516 bug = self.context
517 subscription_class = self.current_user_subscription_class
518 if bug.isMuted(self.user):
519 return 'muted-true %s' % subscription_class
520 else:
521 return 'muted-false %s' % subscription_class
522
514 @cachedproperty523 @cachedproperty
515 def _bug_attachments(self):524 def _bug_attachments(self):
516 """Get a dict of attachment type -> attachments list."""525 """Get a dict of attachment type -> attachments list."""
517526
=== modified file 'lib/lp/bugs/browser/bugsubscription.py'
--- lib/lp/bugs/browser/bugsubscription.py 2011-03-07 21:21:21 +0000
+++ lib/lp/bugs/browser/bugsubscription.py 2011-03-15 11:01:24 +0000
@@ -497,6 +497,9 @@
497 for subscription in direct_subscriptions:497 for subscription in direct_subscriptions:
498 if not check_permission('launchpad.View', subscription.person):498 if not check_permission('launchpad.View', subscription.person):
499 continue499 continue
500 if (subscription.bug_notification_level ==
501 BugNotificationLevel.NOTHING):
502 continue
500 if subscription.person == self.user:503 if subscription.person == self.user:
501 can_unsubscribe = [subscription] + can_unsubscribe504 can_unsubscribe = [subscription] + can_unsubscribe
502 elif subscription.canBeUnsubscribedByUser(self.user):505 elif subscription.canBeUnsubscribedByUser(self.user):
503506
=== modified file 'lib/lp/bugs/browser/tests/test_bugsubscription_views.py'
--- lib/lp/bugs/browser/tests/test_bugsubscription_views.py 2011-03-03 10:49:12 +0000
+++ lib/lp/bugs/browser/tests/test_bugsubscription_views.py 2011-03-15 11:01:24 +0000
@@ -317,3 +317,31 @@
317 self.bug.default_bugtask, BugSubscriptionListView)317 self.bug.default_bugtask, BugSubscriptionListView)
318 self.assertEqual(318 self.assertEqual(
319 list(harness.view.structural_subscriptions), [sub])319 list(harness.view.structural_subscriptions), [sub])
320
321
322class BugPortletSubscribersContentsTestCase(TestCaseWithFactory):
323 """Tests for the BugPortletSubscribersContents view."""
324
325 layer = LaunchpadFunctionalLayer
326
327 def setUp(self):
328 super(BugPortletSubscribersContentsTestCase, self).setUp()
329 self.bug = self.factory.makeBug()
330 self.subscriber = self.factory.makePerson()
331
332 def test_sorted_direct_subscriptions_doesnt_show_mutes(self):
333 # BugPortletSubscribersContents.sorted_direct_subscriptions does
334 # not return muted subscriptions.
335 with person_logged_in(self.subscriber):
336 subscription = self.bug.subscribe(
337 self.subscriber, self.subscriber,
338 level=BugNotificationLevel.NOTHING)
339 view = create_initialized_view(
340 self.bug, name="+bug-portlet-subscribers-content")
341 # Loop over the results of sorted_direct_subscriptions to
342 # extract the subscriptions from their
343 # SubscriptionAttrDecorator intances.
344 sorted_subscriptions = [
345 decorator.subscription for decorator in
346 view.sorted_direct_subscriptions]
347 self.assertFalse(subscription in sorted_subscriptions)
320348
=== modified file 'lib/lp/bugs/javascript/bugtask_index_portlets.js'
--- lib/lp/bugs/javascript/bugtask_index_portlets.js 2011-03-09 15:42:42 +0000
+++ lib/lp/bugs/javascript/bugtask_index_portlets.js 2011-03-15 11:01:24 +0000
@@ -256,8 +256,6 @@
256256
257 setup_client_and_bug();257 setup_client_and_bug();
258 var mute_link = Y.one('.menu-link-mute_subscription');258 var mute_link = Y.one('.menu-link-mute_subscription');
259 var parent_node = mute_link.get('parentNode');
260 var is_subscribed = !parent_node.hasClass('subscribed-false');
261 var mute_subscription = new Y.lp.bugs.subscriber.Subscription({259 var mute_subscription = new Y.lp.bugs.subscriber.Subscription({
262 link: mute_link,260 link: mute_link,
263 spinner: Y.one('#mute-unmute-spinner'),261 spinner: Y.one('#mute-unmute-spinner'),
@@ -271,29 +269,65 @@
271 mute_link.addClass('js-action');269 mute_link.addClass('js-action');
272 mute_link.on('click', function(e) {270 mute_link.on('click', function(e) {
273 e.halt();271 e.halt();
272 var parent_node = mute_link.get('parentNode');
273 var is_subscribed = !parent_node.hasClass('subscribed-false');
274 var is_muted = parent_node.hasClass('muted-true');
274 mute_subscription.enable_spinner('Muting...');275 mute_subscription.enable_spinner('Muting...');
275 if (! is_subscribed) {276 if (! is_muted) {
276 mute_subscription.set(277 mute_subscription.set(
277 'bug_notification_level', "Nothing");278 'bug_notification_level', "Nothing");
278 mute_current_user(mute_subscription);279 success_callback = function() {
280 parent_node.removeClass('muted-false');
281 parent_node.addClass('muted-true');
282 mute_subscription.set('is_muted', true);
283 update_subscription_after_mute_or_unmute(
284 mute_subscription);
285 }
286 mute_current_user(mute_subscription, success_callback);
279 } else {287 } else {
280 unmute_current_user(mute_subscription);288 success_callback = function() {
289 parent_node.removeClass('muted-true');
290 parent_node.addClass('muted-false');
291 mute_subscription.set('is_muted', false);
292 update_subscription_after_mute_or_unmute(
293 mute_subscription);
294 }
295 unmute_current_user(mute_subscription, success_callback);
281 }296 }
282 mute_subscription.disable_spinner();297 mute_subscription.disable_spinner();
283 });298 });
284}299}
285300
286
287/*301/*
288 * Initialize callbacks for subscribe/unsubscribe links.302 * Update the subscription links after the mute button has been clicked.
289 *303 *
290 * @method setup_subscription_link_handlers304 * @param mute_subscription {Object} A Y.lp.bugs.subscriber.Subscription
305 * object.
291 */306 */
292function setup_subscription_link_handlers() {307function update_subscription_after_mute_or_unmute(mute_subscription) {
293 if (LP.links.me === undefined) {308 var subscription = get_subscribe_self_subscription();
294 return;309 var subscription_link = subscription.get('link');
310
311 subscription.enable_spinner('Updating...');
312 if (mute_subscription.get('is_muted')) {
313 subscription.disable_spinner(subscription_labels.SUBSCRIBE);
314 if (subscription.has_duplicate_subscriptions()) {
315 set_subscription_link_parent_class(
316 subscription_link, false, true);
317 } else {
318 set_subscription_link_parent_class(
319 subscription_link, false, false);
320 }
321 } else {
322 subscription.disable_spinner(subscription_labels.SUBSCRIBE);
295 }323 }
324}
296325
326/*
327 * Set up and return a Subscription object for the direct subscription
328 * link.
329 */
330function get_subscribe_self_subscription() {
297 setup_client_and_bug();331 setup_client_and_bug();
298 var subscription = new Y.lp.bugs.subscriber.Subscription({332 var subscription = new Y.lp.bugs.subscriber.Subscription({
299 link: Y.one('.menu-link-subscription'),333 link: Y.one('.menu-link-subscription'),
@@ -310,7 +344,20 @@
310 'link').get('parentNode').hasClass('dup-subscribed-true');344 'link').get('parentNode').hasClass('dup-subscribed-true');
311 subscription.set('is_direct', is_direct);345 subscription.set('is_direct', is_direct);
312 subscription.set('has_dupes', has_dupes);346 subscription.set('has_dupes', has_dupes);
313347 return subscription;
348}
349
350/*
351 * Initialize callbacks for subscribe/unsubscribe links.
352 *
353 * @method setup_subscription_link_handlers
354 */
355function setup_subscription_link_handlers() {
356 if (LP.links.me === undefined) {
357 return;
358 }
359
360 var subscription = get_subscribe_self_subscription();
314 var subscription_overlay;361 var subscription_overlay;
315 if (namespace.use_advanced_subscriptions) {362 if (namespace.use_advanced_subscriptions) {
316 subscription_overlay = setup_advanced_subscription_overlay(363 subscription_overlay = setup_advanced_subscription_overlay(
@@ -346,9 +393,7 @@
346393
347 setup_subscribe_someone_else_handler(subscription);394 setup_subscribe_someone_else_handler(subscription);
348 if (namespace.use_advanced_subscriptions) {395 if (namespace.use_advanced_subscriptions) {
349 // This is currently disabled since the mute link is a work in396 setup_mute_link_handlers();
350 // progress.
351 // setup_mute_link_handlers();
352 }397 }
353}398}
354399
@@ -739,8 +784,9 @@
739 *784 *
740 * @method mute_current_user785 * @method mute_current_user
741 * @param subscription {Object} A Y.lp.bugs.subscribe.Subscription object.786 * @param subscription {Object} A Y.lp.bugs.subscribe.Subscription object.
787 * @param success_callback {Object} A function to be called on success.
742 */788 */
743function mute_current_user(subscription) {789function mute_current_user(subscription, success_callback) {
744 subscription.enable_spinner('Muting...');790 subscription.enable_spinner('Muting...');
745 var subscription_link = subscription.get('link');791 var subscription_link = subscription.get('link');
746 var subscriber = subscription.get('subscriber');792 var subscriber = subscription.get('subscriber');
@@ -761,6 +807,9 @@
761 var flash_node = subscription_link.get('parentNode');807 var flash_node = subscription_link.get('parentNode');
762 var anim = Y.lazr.anim.green_flash({ node: flash_node });808 var anim = Y.lazr.anim.green_flash({ node: flash_node });
763 anim.run();809 anim.run();
810 if (Y.Lang.isValue(success_callback)) {
811 success_callback();
812 }
764 },813 },
765814
766 failure: error_handler.getFailureHandler()815 failure: error_handler.getFailureHandler()
@@ -781,8 +830,9 @@
781 *830 *
782 * @method unmute_current_user831 * @method unmute_current_user
783 * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object.832 * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object.
833 * @param success_callback {Object} A function to be called on success.
784 */834 */
785function unmute_current_user(subscription) {835function unmute_current_user(subscription, success_callback) {
786 subscription.enable_spinner('Unmuting...');836 subscription.enable_spinner('Unmuting...');
787 var subscription_link = subscription.get('link');837 var subscription_link = subscription.get('link');
788 var subscriber = subscription.get('subscriber');838 var subscriber = subscription.get('subscriber');
@@ -802,6 +852,9 @@
802 var flash_node = subscription_link.get('parentNode');852 var flash_node = subscription_link.get('parentNode');
803 var anim = Y.lazr.anim.green_flash({ node: flash_node });853 var anim = Y.lazr.anim.green_flash({ node: flash_node });
804 anim.run();854 anim.run();
855 if (Y.Lang.isValue(success_callback)) {
856 success_callback();
857 }
805 },858 },
806859
807 failure: error_handler.getFailureHandler()860 failure: error_handler.getFailureHandler()
808861
=== modified file 'lib/lp/bugs/templates/bug-portlet-subscribers.pt'
--- lib/lp/bugs/templates/bug-portlet-subscribers.pt 2011-03-09 15:42:42 +0000
+++ lib/lp/bugs/templates/bug-portlet-subscribers.pt 2011-03-15 11:01:24 +0000
@@ -13,17 +13,13 @@
13 tal:content="structure context_menu/subscription/render" />13 tal:content="structure context_menu/subscription/render" />
14 <div id="sub-unsub-spinner">Subscribing...</div>14 <div id="sub-unsub-spinner">Subscribing...</div>
15 <div tal:content="structure context_menu/addsubscriber/render" />15 <div tal:content="structure context_menu/addsubscriber/render" />
16 <tal:comment replace="nothing">16 <tal:show-mute
17 <!-- This section enables the mute link; it's disabled since17 condition="request/features/malone.advanced-subscriptions.enabled">
18 that's a work-in-progress. -->18 <div
19 <tal:show-mute19 tal:attributes="class view/current_user_mute_class"
20 condition="request/features/malone.advanced-subscriptions.enabled">20 tal:content="structure context_menu/mute_subscription/render" />
21 <div21 <div id="mute-unmute-spinner">Unmuting...</div>
22 tal:attributes="class view/current_user_subscription_class"22 </tal:show-mute>
23 tal:content="structure context_menu/mute_subscription/render" />
24 <div id="mute-unmute-spinner">Unmuting...</div>
25 </tal:show-mute>
26 </tal:comment>
27 </div>23 </div>
28 <a id="subscribers-ids-link"24 <a id="subscribers-ids-link"
29 tal:define="bug context/bug|context"25 tal:define="bug context/bug|context"