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
1=== modified file 'lib/lp/bugs/browser/bug.py'
2--- lib/lp/bugs/browser/bug.py 2011-03-09 11:53:54 +0000
3+++ lib/lp/bugs/browser/bug.py 2011-03-15 11:01:24 +0000
4@@ -511,6 +511,15 @@
5 else:
6 return 'subscribed-false %s' % dup_class
7
8+ @property
9+ def current_user_mute_class(self):
10+ bug = self.context
11+ subscription_class = self.current_user_subscription_class
12+ if bug.isMuted(self.user):
13+ return 'muted-true %s' % subscription_class
14+ else:
15+ return 'muted-false %s' % subscription_class
16+
17 @cachedproperty
18 def _bug_attachments(self):
19 """Get a dict of attachment type -> attachments list."""
20
21=== modified file 'lib/lp/bugs/browser/bugsubscription.py'
22--- lib/lp/bugs/browser/bugsubscription.py 2011-03-07 21:21:21 +0000
23+++ lib/lp/bugs/browser/bugsubscription.py 2011-03-15 11:01:24 +0000
24@@ -497,6 +497,9 @@
25 for subscription in direct_subscriptions:
26 if not check_permission('launchpad.View', subscription.person):
27 continue
28+ if (subscription.bug_notification_level ==
29+ BugNotificationLevel.NOTHING):
30+ continue
31 if subscription.person == self.user:
32 can_unsubscribe = [subscription] + can_unsubscribe
33 elif subscription.canBeUnsubscribedByUser(self.user):
34
35=== modified file 'lib/lp/bugs/browser/tests/test_bugsubscription_views.py'
36--- lib/lp/bugs/browser/tests/test_bugsubscription_views.py 2011-03-03 10:49:12 +0000
37+++ lib/lp/bugs/browser/tests/test_bugsubscription_views.py 2011-03-15 11:01:24 +0000
38@@ -317,3 +317,31 @@
39 self.bug.default_bugtask, BugSubscriptionListView)
40 self.assertEqual(
41 list(harness.view.structural_subscriptions), [sub])
42+
43+
44+class BugPortletSubscribersContentsTestCase(TestCaseWithFactory):
45+ """Tests for the BugPortletSubscribersContents view."""
46+
47+ layer = LaunchpadFunctionalLayer
48+
49+ def setUp(self):
50+ super(BugPortletSubscribersContentsTestCase, self).setUp()
51+ self.bug = self.factory.makeBug()
52+ self.subscriber = self.factory.makePerson()
53+
54+ def test_sorted_direct_subscriptions_doesnt_show_mutes(self):
55+ # BugPortletSubscribersContents.sorted_direct_subscriptions does
56+ # not return muted subscriptions.
57+ with person_logged_in(self.subscriber):
58+ subscription = self.bug.subscribe(
59+ self.subscriber, self.subscriber,
60+ level=BugNotificationLevel.NOTHING)
61+ view = create_initialized_view(
62+ self.bug, name="+bug-portlet-subscribers-content")
63+ # Loop over the results of sorted_direct_subscriptions to
64+ # extract the subscriptions from their
65+ # SubscriptionAttrDecorator intances.
66+ sorted_subscriptions = [
67+ decorator.subscription for decorator in
68+ view.sorted_direct_subscriptions]
69+ self.assertFalse(subscription in sorted_subscriptions)
70
71=== modified file 'lib/lp/bugs/javascript/bugtask_index_portlets.js'
72--- lib/lp/bugs/javascript/bugtask_index_portlets.js 2011-03-09 15:42:42 +0000
73+++ lib/lp/bugs/javascript/bugtask_index_portlets.js 2011-03-15 11:01:24 +0000
74@@ -256,8 +256,6 @@
75
76 setup_client_and_bug();
77 var mute_link = Y.one('.menu-link-mute_subscription');
78- var parent_node = mute_link.get('parentNode');
79- var is_subscribed = !parent_node.hasClass('subscribed-false');
80 var mute_subscription = new Y.lp.bugs.subscriber.Subscription({
81 link: mute_link,
82 spinner: Y.one('#mute-unmute-spinner'),
83@@ -271,29 +269,65 @@
84 mute_link.addClass('js-action');
85 mute_link.on('click', function(e) {
86 e.halt();
87+ var parent_node = mute_link.get('parentNode');
88+ var is_subscribed = !parent_node.hasClass('subscribed-false');
89+ var is_muted = parent_node.hasClass('muted-true');
90 mute_subscription.enable_spinner('Muting...');
91- if (! is_subscribed) {
92+ if (! is_muted) {
93 mute_subscription.set(
94 'bug_notification_level', "Nothing");
95- mute_current_user(mute_subscription);
96+ success_callback = function() {
97+ parent_node.removeClass('muted-false');
98+ parent_node.addClass('muted-true');
99+ mute_subscription.set('is_muted', true);
100+ update_subscription_after_mute_or_unmute(
101+ mute_subscription);
102+ }
103+ mute_current_user(mute_subscription, success_callback);
104 } else {
105- unmute_current_user(mute_subscription);
106+ success_callback = function() {
107+ parent_node.removeClass('muted-true');
108+ parent_node.addClass('muted-false');
109+ mute_subscription.set('is_muted', false);
110+ update_subscription_after_mute_or_unmute(
111+ mute_subscription);
112+ }
113+ unmute_current_user(mute_subscription, success_callback);
114 }
115 mute_subscription.disable_spinner();
116 });
117 }
118
119-
120 /*
121- * Initialize callbacks for subscribe/unsubscribe links.
122+ * Update the subscription links after the mute button has been clicked.
123 *
124- * @method setup_subscription_link_handlers
125+ * @param mute_subscription {Object} A Y.lp.bugs.subscriber.Subscription
126+ * object.
127 */
128-function setup_subscription_link_handlers() {
129- if (LP.links.me === undefined) {
130- return;
131+function update_subscription_after_mute_or_unmute(mute_subscription) {
132+ var subscription = get_subscribe_self_subscription();
133+ var subscription_link = subscription.get('link');
134+
135+ subscription.enable_spinner('Updating...');
136+ if (mute_subscription.get('is_muted')) {
137+ subscription.disable_spinner(subscription_labels.SUBSCRIBE);
138+ if (subscription.has_duplicate_subscriptions()) {
139+ set_subscription_link_parent_class(
140+ subscription_link, false, true);
141+ } else {
142+ set_subscription_link_parent_class(
143+ subscription_link, false, false);
144+ }
145+ } else {
146+ subscription.disable_spinner(subscription_labels.SUBSCRIBE);
147 }
148+}
149
150+/*
151+ * Set up and return a Subscription object for the direct subscription
152+ * link.
153+ */
154+function get_subscribe_self_subscription() {
155 setup_client_and_bug();
156 var subscription = new Y.lp.bugs.subscriber.Subscription({
157 link: Y.one('.menu-link-subscription'),
158@@ -310,7 +344,20 @@
159 'link').get('parentNode').hasClass('dup-subscribed-true');
160 subscription.set('is_direct', is_direct);
161 subscription.set('has_dupes', has_dupes);
162-
163+ return subscription;
164+}
165+
166+/*
167+ * Initialize callbacks for subscribe/unsubscribe links.
168+ *
169+ * @method setup_subscription_link_handlers
170+ */
171+function setup_subscription_link_handlers() {
172+ if (LP.links.me === undefined) {
173+ return;
174+ }
175+
176+ var subscription = get_subscribe_self_subscription();
177 var subscription_overlay;
178 if (namespace.use_advanced_subscriptions) {
179 subscription_overlay = setup_advanced_subscription_overlay(
180@@ -346,9 +393,7 @@
181
182 setup_subscribe_someone_else_handler(subscription);
183 if (namespace.use_advanced_subscriptions) {
184- // This is currently disabled since the mute link is a work in
185- // progress.
186- // setup_mute_link_handlers();
187+ setup_mute_link_handlers();
188 }
189 }
190
191@@ -739,8 +784,9 @@
192 *
193 * @method mute_current_user
194 * @param subscription {Object} A Y.lp.bugs.subscribe.Subscription object.
195+ * @param success_callback {Object} A function to be called on success.
196 */
197-function mute_current_user(subscription) {
198+function mute_current_user(subscription, success_callback) {
199 subscription.enable_spinner('Muting...');
200 var subscription_link = subscription.get('link');
201 var subscriber = subscription.get('subscriber');
202@@ -761,6 +807,9 @@
203 var flash_node = subscription_link.get('parentNode');
204 var anim = Y.lazr.anim.green_flash({ node: flash_node });
205 anim.run();
206+ if (Y.Lang.isValue(success_callback)) {
207+ success_callback();
208+ }
209 },
210
211 failure: error_handler.getFailureHandler()
212@@ -781,8 +830,9 @@
213 *
214 * @method unmute_current_user
215 * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object.
216+ * @param success_callback {Object} A function to be called on success.
217 */
218-function unmute_current_user(subscription) {
219+function unmute_current_user(subscription, success_callback) {
220 subscription.enable_spinner('Unmuting...');
221 var subscription_link = subscription.get('link');
222 var subscriber = subscription.get('subscriber');
223@@ -802,6 +852,9 @@
224 var flash_node = subscription_link.get('parentNode');
225 var anim = Y.lazr.anim.green_flash({ node: flash_node });
226 anim.run();
227+ if (Y.Lang.isValue(success_callback)) {
228+ success_callback();
229+ }
230 },
231
232 failure: error_handler.getFailureHandler()
233
234=== modified file 'lib/lp/bugs/templates/bug-portlet-subscribers.pt'
235--- lib/lp/bugs/templates/bug-portlet-subscribers.pt 2011-03-09 15:42:42 +0000
236+++ lib/lp/bugs/templates/bug-portlet-subscribers.pt 2011-03-15 11:01:24 +0000
237@@ -13,17 +13,13 @@
238 tal:content="structure context_menu/subscription/render" />
239 <div id="sub-unsub-spinner">Subscribing...</div>
240 <div tal:content="structure context_menu/addsubscriber/render" />
241- <tal:comment replace="nothing">
242- <!-- This section enables the mute link; it's disabled since
243- that's a work-in-progress. -->
244- <tal:show-mute
245- condition="request/features/malone.advanced-subscriptions.enabled">
246- <div
247- tal:attributes="class view/current_user_subscription_class"
248- tal:content="structure context_menu/mute_subscription/render" />
249- <div id="mute-unmute-spinner">Unmuting...</div>
250- </tal:show-mute>
251- </tal:comment>
252+ <tal:show-mute
253+ condition="request/features/malone.advanced-subscriptions.enabled">
254+ <div
255+ tal:attributes="class view/current_user_mute_class"
256+ tal:content="structure context_menu/mute_subscription/render" />
257+ <div id="mute-unmute-spinner">Unmuting...</div>
258+ </tal:show-mute>
259 </div>
260 <a id="subscribers-ids-link"
261 tal:define="bug context/bug|context"