Merge lp:~danilo/launchpad/bug-772754-other-subscribers-remove-cruft into lp:launchpad
- bug-772754-other-subscribers-remove-cruft
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | 13243 |
Proposed branch: | lp:~danilo/launchpad/bug-772754-other-subscribers-remove-cruft |
Merge into: | lp:launchpad |
Prerequisite: | lp:~danilo/launchpad/bug-772754-other-subscribers-actions |
Diff against target: |
3223 lines (+122/-2659) 25 files modified
lib/lp/app/javascript/picker.js (+5/-3) lib/lp/bugs/browser/bug.py (+0/-18) lib/lp/bugs/browser/bugsubscription.py (+6/-59) lib/lp/bugs/browser/configure.zcml (+0/-17) lib/lp/bugs/browser/tests/bug-portlet-subscribers-content.txt (+0/-25) lib/lp/bugs/browser/tests/bug-subscription-views.txt (+0/-40) lib/lp/bugs/browser/tests/test_bug_views.py (+0/-11) lib/lp/bugs/browser/tests/test_bugsubscription.py (+0/-23) lib/lp/bugs/browser/tests/test_bugsubscription_views.py (+0/-53) lib/lp/bugs/javascript/bugtask_index.js (+2/-4) lib/lp/bugs/javascript/bugtask_index_portlets.js (+0/-897) lib/lp/bugs/javascript/subscriber.js (+0/-390) lib/lp/bugs/javascript/subscribers_list.js (+0/-66) lib/lp/bugs/javascript/tests/test_subscriber.html (+0/-104) lib/lp/bugs/javascript/tests/test_subscriber.js (+0/-288) lib/lp/bugs/javascript/tests/test_subscribers_list.js (+1/-318) lib/lp/bugs/stories/bug-privacy/05-set-bug-private-as-admin.txt (+7/-13) lib/lp/bugs/stories/bugs/bug-add-subscriber.txt (+29/-32) lib/lp/bugs/stories/bugs/xx-bug-personal-subscriptions.txt (+45/-125) lib/lp/bugs/templates/bug-portlet-dupe-subscribers-content.pt (+0/-49) lib/lp/bugs/templates/bug-portlet-subscribers-content.pt (+0/-74) lib/lp/bugs/templates/bug-portlet-subscribers.pt (+0/-10) lib/lp/bugs/templates/bug-portlet-subscription.pt (+0/-3) lib/lp/bugs/templates/bugtask-index.pt (+1/-14) lib/lp/bugs/tests/bug.py (+26/-23) |
To merge this branch: | bzr merge lp:~danilo/launchpad/bug-772754-other-subscribers-remove-cruft |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Данило Шеган (community) | Approve | ||
Review via email: mp+64188@code.launchpad.net |
Commit message
Description of the change
= Bug 772754: Other subscribers list, part 7 =
NOTE: Not much review will be needed (this is just removal of all the unneeded bits and pieces). Final step, all the tests are passing, and I'll be landing this branch when all the others have been reviewed.
This is a final part of ongoing work for providing the "other subscribers" list as indicated in mockup https:/
== Tests ==
bin/test -m lp.bugs
== Demo and Q/A ==
N/A
Lint is clean.
Gary Poster (gary) wrote : | # |
Данило Шеган (danilo) wrote : | # |
Hi Gary, thanks for looking at this branch.
The first one should have already been fixed (there are even tests to prove it, but there was a bug in the ordering logic that allowed it to sometimes be misordered; both tests and code have been improved to catch this case). If it's not in this branch yet, sorry about it (that's one thing I wish pipelines made easier: pushing _all_ branches up).
As for the second, I omitted it on purpose: long text tends to not be read. As far as the help link, I'd like that, but I'll have to get help from Matt (or someone else) on the text, so I am leaving that for a next branch.
As for the third thing, the code is structured in a way that it should be easy to add another "action" (of which unsubscribe is only one option offered atm) which allows one to change the level of the subscription, but since we don't have that for teams yet anyway, I didn't want to implement it here. IOW, adding the icon/action should be easy, making it do the right thing is out of scope.
Данило Шеган (danilo) wrote : | # |
Marking this one as approved, it's just a removal of all the unused stuff. All tests are passing in ec2.
Preview Diff
1 | === modified file 'lib/lp/app/javascript/picker.js' |
2 | --- lib/lp/app/javascript/picker.js 2011-06-09 20:57:53 +0000 |
3 | +++ lib/lp/app/javascript/picker.js 2011-06-15 11:10:20 +0000 |
4 | @@ -318,11 +318,13 @@ |
5 | * @param {Node} text_input The input field to copy the selected match too. |
6 | */ |
7 | namespace.connect_select_menu = function (select_menu, text_input) { |
8 | - var copy_selected_value = function(e) { |
9 | - text_input.value = select_menu.value; |
10 | + if (Y.Lang.isValue(select_menu)) { |
11 | + var copy_selected_value = function(e) { |
12 | + text_input.value = select_menu.value; |
13 | }; |
14 | - Y.on('change', copy_selected_value, select_menu); |
15 | + Y.on('change', copy_selected_value, select_menu); |
16 | }; |
17 | +}; |
18 | |
19 | /** |
20 | * Creates a picker widget that has already been rendered and hidden. |
21 | |
22 | === modified file 'lib/lp/bugs/browser/bug.py' |
23 | --- lib/lp/bugs/browser/bug.py 2011-06-15 11:09:59 +0000 |
24 | +++ lib/lp/bugs/browser/bug.py 2011-06-15 11:10:20 +0000 |
25 | @@ -477,24 +477,6 @@ |
26 | """ |
27 | return self.subscription_info.duplicate_subscriptions.subscribers |
28 | |
29 | - @cachedproperty |
30 | - def subscriber_ids(self): |
31 | - """Return a dictionary mapping a css_name to user name.""" |
32 | - subscribers = set().union( |
33 | - self.direct_subscribers, |
34 | - self.duplicate_subscribers) |
35 | - |
36 | - # The current user has to be in subscribers_id so |
37 | - # in case the id is needed for a new subscription. |
38 | - user = getUtility(ILaunchBag).user |
39 | - if user is not None: |
40 | - subscribers.add(user) |
41 | - |
42 | - ids = {} |
43 | - for sub in subscribers: |
44 | - ids[sub.name] = 'subscriber-%s' % sub.id |
45 | - return ids |
46 | - |
47 | def getSubscriptionClassForUser(self, subscribed_person): |
48 | """Return a set of CSS class names based on subscription status. |
49 | |
50 | |
51 | === modified file 'lib/lp/bugs/browser/bugsubscription.py' |
52 | --- lib/lp/bugs/browser/bugsubscription.py 2011-06-15 11:09:59 +0000 |
53 | +++ lib/lp/bugs/browser/bugsubscription.py 2011-06-15 11:10:20 +0000 |
54 | @@ -7,8 +7,7 @@ |
55 | __all__ = [ |
56 | 'AdvancedSubscriptionMixin', |
57 | 'BugMuteSelfView', |
58 | - 'BugPortletDuplicateSubcribersContents', |
59 | - 'BugPortletSubcribersContents', |
60 | + 'BugPortletSubscribersWithDetails', |
61 | 'BugSubscriptionAddView', |
62 | 'BugSubscriptionListView', |
63 | ] |
64 | @@ -546,46 +545,6 @@ |
65 | 'dupe_links_string': dupe_links_string}) |
66 | |
67 | |
68 | -class BugPortletSubcribersContents(LaunchpadView, BugViewMixin): |
69 | - """View for the contents for the subscribers portlet.""" |
70 | - |
71 | - @property |
72 | - def sorted_direct_subscriptions(self): |
73 | - """Get the list of direct subscriptions to the bug. |
74 | - |
75 | - The list is sorted such that subscriptions you can unsubscribe appear |
76 | - before all other subscriptions. |
77 | - """ |
78 | - direct_subscriptions = [ |
79 | - SubscriptionAttrDecorator(subscription) |
80 | - for subscription in self.context.getDirectSubscriptions().sorted] |
81 | - can_unsubscribe = [] |
82 | - cannot_unsubscribe = [] |
83 | - for subscription in direct_subscriptions: |
84 | - if not check_permission('launchpad.View', subscription.person): |
85 | - continue |
86 | - if subscription.person == self.user: |
87 | - can_unsubscribe = [subscription] + can_unsubscribe |
88 | - elif subscription.canBeUnsubscribedByUser(self.user): |
89 | - can_unsubscribe.append(subscription) |
90 | - else: |
91 | - cannot_unsubscribe.append(subscription) |
92 | - return can_unsubscribe + cannot_unsubscribe |
93 | - |
94 | - |
95 | -class BugPortletDuplicateSubcribersContents(LaunchpadView, BugViewMixin): |
96 | - """View for the contents for the subscribers-from-dupes portlet block.""" |
97 | - |
98 | - @property |
99 | - def sorted_subscriptions_from_dupes(self): |
100 | - """Get the list of subscriptions to duplicates of this bug.""" |
101 | - return [ |
102 | - SubscriptionAttrDecorator(subscription) |
103 | - for subscription in sorted( |
104 | - self.context.getSubscriptionsFromDuplicates(), |
105 | - key=(lambda subscription: subscription.person.displayname))] |
106 | - |
107 | - |
108 | class BugPortletSubscribersWithDetails(LaunchpadView): |
109 | """A view that returns a JSON dump of the subscriber details for a bug.""" |
110 | |
111 | @@ -596,10 +555,12 @@ |
112 | details = list(self.context.getDirectSubscribersWithDetails()) |
113 | api_request = IWebServiceClientRequest(self.request) |
114 | for person, subscription in details: |
115 | - if person == self.user: |
116 | - # Skip the current user viewing the page. |
117 | - continue |
118 | can_edit = self.user is not None and self.user.inTeam(person) |
119 | + if person == self.user or (person.private and not can_edit): |
120 | + # Skip the current user viewing the page, |
121 | + # and private teams user is not a member of. |
122 | + continue |
123 | + |
124 | subscriber = { |
125 | 'name': person.name, |
126 | 'display_name': person.displayname, |
127 | @@ -641,20 +602,6 @@ |
128 | return self.subscriber_data_js |
129 | |
130 | |
131 | -class BugPortletSubcribersIds(LaunchpadView, BugViewMixin): |
132 | - """A view that returns a JSON dump of the subscriber IDs for a bug.""" |
133 | - |
134 | - @property |
135 | - def subscriber_ids_js(self): |
136 | - """Return subscriber_ids in a form suitable for JavaScript use.""" |
137 | - return dumps(self.subscriber_ids) |
138 | - |
139 | - def render(self): |
140 | - """Override the default render() to return only JSON.""" |
141 | - self.request.response.setHeader('content-type', 'application/json') |
142 | - return self.subscriber_ids_js |
143 | - |
144 | - |
145 | class SubscriptionAttrDecorator: |
146 | """A BugSubscription with added attributes for HTML/JS.""" |
147 | delegates(IBugSubscription, 'subscription') |
148 | |
149 | === modified file 'lib/lp/bugs/browser/configure.zcml' |
150 | --- lib/lp/bugs/browser/configure.zcml 2011-06-15 11:09:59 +0000 |
151 | +++ lib/lp/bugs/browser/configure.zcml 2011-06-15 11:10:20 +0000 |
152 | @@ -1085,23 +1085,6 @@ |
153 | </browser:pages> |
154 | <browser:page |
155 | for="lp.bugs.interfaces.bug.IBug" |
156 | - name="+bug-portlet-subscribers-content" |
157 | - class="lp.bugs.browser.bugsubscription.BugPortletSubcribersContents" |
158 | - template="../templates/bug-portlet-subscribers-content.pt" |
159 | - permission="zope.Public"/> |
160 | - <browser:page |
161 | - for="lp.bugs.interfaces.bug.IBug" |
162 | - name="+bug-portlet-dupe-subscribers-content" |
163 | - class="lp.bugs.browser.bugsubscription.BugPortletDuplicateSubcribersContents" |
164 | - template="../templates/bug-portlet-dupe-subscribers-content.pt" |
165 | - permission="zope.Public"/> |
166 | - <browser:page |
167 | - for="lp.bugs.interfaces.bug.IBug" |
168 | - name="+bug-portlet-subscribers-ids" |
169 | - class="lp.bugs.browser.bugsubscription.BugPortletSubcribersIds" |
170 | - permission="zope.Public"/> |
171 | - <browser:page |
172 | - for="lp.bugs.interfaces.bug.IBug" |
173 | name="+bug-portlet-subscribers-details" |
174 | class=" |
175 | lp.bugs.browser.bugsubscription.BugPortletSubscribersWithDetails" |
176 | |
177 | === removed file 'lib/lp/bugs/browser/tests/bug-portlet-subscribers-content.txt' |
178 | --- lib/lp/bugs/browser/tests/bug-portlet-subscribers-content.txt 2010-09-14 15:32:53 +0000 |
179 | +++ lib/lp/bugs/browser/tests/bug-portlet-subscribers-content.txt 1970-01-01 00:00:00 +0000 |
180 | @@ -1,25 +0,0 @@ |
181 | -= Subscribers portlet content view = |
182 | - |
183 | -The view for the subscribers portlet exposes methods for getting the |
184 | -subscription data ready for display. The list of subscribers is reordered so |
185 | -that subscriptions that the current viewer can unsubscriber appear at the top |
186 | -of the list. |
187 | - |
188 | - >>> from lp.bugs.browser.bugsubscription import ( |
189 | - ... BugPortletSubcribersContents) |
190 | - >>> login('foo.bar@canonical.com') |
191 | - >>> reporter = factory.makePerson(displayname='Bug Reporter') |
192 | - >>> bug = factory.makeBug(owner=reporter) |
193 | - >>> view = BugPortletSubcribersContents(bug, None) |
194 | - >>> bug.subscribe(view.user, view.user) |
195 | - <lp.bugs.model.bugsubscription.BugSubscription ...> |
196 | - >>> bug.subscribe(view.user.teams_participated_in[0], view.user) |
197 | - <lp.bugs.model.bugsubscription.BugSubscription ...> |
198 | - >>> for subscription in view.sorted_direct_subscriptions: |
199 | - ... print '%s %s' % ( |
200 | - ... subscription.person.displayname, |
201 | - ... subscription.canBeUnsubscribedByUser(view.user)) |
202 | - Foo Bar True |
203 | - Canonical Partner Developers True |
204 | - Bug Reporter False |
205 | - |
206 | |
207 | === removed file 'lib/lp/bugs/browser/tests/bug-subscription-views.txt' |
208 | --- lib/lp/bugs/browser/tests/bug-subscription-views.txt 2010-08-02 17:48:13 +0000 |
209 | +++ lib/lp/bugs/browser/tests/bug-subscription-views.txt 1970-01-01 00:00:00 +0000 |
210 | @@ -1,40 +0,0 @@ |
211 | -Bug subscription views |
212 | -====================== |
213 | - |
214 | -Getting subscriber CSS IDs |
215 | --------------------------- |
216 | - |
217 | -It's possible to get a mapping of bug subscriber names to CSS IDs using |
218 | -the +bug-portlet-subscribers-ids view. |
219 | - |
220 | - >>> from lp.bugs.interfaces.bug import IBugSet |
221 | - >>> bug_15 = getUtility(IBugSet).get(15) |
222 | - |
223 | - >>> subscriber_ids_view = create_initialized_view( |
224 | - ... bug_15, '+bug-portlet-subscribers-ids') |
225 | - |
226 | -The view's subscriber_ids_js property returns a JSON struct of the |
227 | -person name to CSS ID mappings for the bug's subscribers. |
228 | - |
229 | - >>> print subscriber_ids_view.subscriber_ids_js |
230 | - {"name16": "subscriber-16"} |
231 | - |
232 | -If bug 15 is marked as a duplicate of another bug, its subscribers will |
233 | -be included in that bugs subscriber_ids_js mapping. |
234 | - |
235 | - >>> bug_13 = getUtility(IBugSet).get(13) |
236 | - >>> subscriber_ids_view = create_initialized_view( |
237 | - ... bug_13, '+bug-portlet-subscribers-ids') |
238 | - |
239 | - >>> print subscriber_ids_view.subscriber_ids_js |
240 | - {"name12": "subscriber-12"} |
241 | - |
242 | - >>> login('foo.bar@canonical.com') |
243 | - >>> bug_15.markAsDuplicate(bug_13) |
244 | - >>> bug_13 = getUtility(IBugSet).get(13) |
245 | - |
246 | - >>> subscriber_ids_view = create_initialized_view( |
247 | - ... bug_13, '+bug-portlet-subscribers-ids') |
248 | - |
249 | - >>> print subscriber_ids_view.subscriber_ids_js |
250 | - {"name12": "subscriber-12", "name16": "subscriber-16"} |
251 | |
252 | === modified file 'lib/lp/bugs/browser/tests/test_bug_views.py' |
253 | --- lib/lp/bugs/browser/tests/test_bug_views.py 2011-06-15 11:09:59 +0000 |
254 | +++ lib/lp/bugs/browser/tests/test_bug_views.py 2011-06-15 11:10:20 +0000 |
255 | @@ -109,17 +109,6 @@ |
256 | self.assertTrue('menu-link-editsubscriptions' not in html) |
257 | self.assertTrue('/+subscriptions' not in html) |
258 | |
259 | - def test_mute_subscription_link_not_shown_with_no_feature_flag(self): |
260 | - # Mute link is not shown when the feature flag is off. |
261 | - person = self.factory.makePerson() |
262 | - with person_logged_in(person): |
263 | - with FeatureFixture({self.feature_flag_1: None}): |
264 | - view = create_initialized_view( |
265 | - self.bug, name="+portlet-subscription") |
266 | - self.assertFalse(view.user_should_see_mute_link) |
267 | - html = view.render() |
268 | - self.assertFalse('mute_subscription' in html) |
269 | - |
270 | def _hasCSSClass(self, html, element_id, css_class): |
271 | # Return True if element with ID `element_id` in `html` has |
272 | # a CSS class `css_class`. |
273 | |
274 | === removed file 'lib/lp/bugs/browser/tests/test_bugsubscription.py' |
275 | --- lib/lp/bugs/browser/tests/test_bugsubscription.py 2010-10-04 19:50:45 +0000 |
276 | +++ lib/lp/bugs/browser/tests/test_bugsubscription.py 1970-01-01 00:00:00 +0000 |
277 | @@ -1,23 +0,0 @@ |
278 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
279 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
280 | - |
281 | -import unittest |
282 | - |
283 | -from canonical.launchpad.testing.systemdocs import ( |
284 | - LayeredDocFileSuite, |
285 | - setUp, |
286 | - tearDown, |
287 | - ) |
288 | -from canonical.testing.layers import LaunchpadFunctionalLayer |
289 | - |
290 | - |
291 | -def test_suite(): |
292 | - suite = unittest.TestSuite() |
293 | - suite.addTest(LayeredDocFileSuite( |
294 | - 'bug-portlet-subscribers-content.txt', setUp=setUp, tearDown=tearDown, |
295 | - layer=LaunchpadFunctionalLayer)) |
296 | - return suite |
297 | - |
298 | - |
299 | -if __name__ == '__main__': |
300 | - unittest.TextTestRunner().run(test_suite()) |
301 | |
302 | === modified file 'lib/lp/bugs/browser/tests/test_bugsubscription_views.py' |
303 | --- lib/lp/bugs/browser/tests/test_bugsubscription_views.py 2011-06-15 11:09:59 +0000 |
304 | +++ lib/lp/bugs/browser/tests/test_bugsubscription_views.py 2011-06-15 11:10:20 +0000 |
305 | @@ -14,7 +14,6 @@ |
306 | from canonical.testing.layers import LaunchpadFunctionalLayer |
307 | from lazr.restful.interfaces import IWebServiceClientRequest |
308 | from lp.bugs.browser.bugsubscription import ( |
309 | - BugPortletSubcribersIds, |
310 | BugPortletSubscribersWithDetails, |
311 | BugSubscriptionListView, |
312 | BugSubscriptionSubscribeSelfView, |
313 | @@ -343,58 +342,6 @@ |
314 | 'bug-notification-level-field', widget_class) |
315 | |
316 | |
317 | -class BugSubscriptionAdvancedFeaturesPortletTestCase(TestCaseWithFactory): |
318 | - |
319 | - layer = LaunchpadFunctionalLayer |
320 | - feature_flag = 'malone.advanced-subscriptions.enabled' |
321 | - |
322 | - def setUp(self): |
323 | - super(BugSubscriptionAdvancedFeaturesPortletTestCase, self).setUp() |
324 | - self.bug = self.factory.makeBug() |
325 | - self.person = self.factory.makePerson() |
326 | - self.target = self.bug.default_bugtask.target |
327 | - subscriber = self.factory.makePerson() |
328 | - with person_logged_in(self.person): |
329 | - self.target.addBugSubscription(subscriber, subscriber) |
330 | - |
331 | - def get_contents(self, flag): |
332 | - with person_logged_in(self.person): |
333 | - with FeatureFixture({self.feature_flag: flag}): |
334 | - bug_view = create_initialized_view( |
335 | - self.bug, name="+bug-portlet-subscribers-content") |
336 | - return bug_view.render() |
337 | - |
338 | - def test_also_notified_suppressed(self): |
339 | - # If the advanced-subscription.enabled feature flag is on then the |
340 | - # "Also notified" portion of the portlet is suppressed. |
341 | - contents = self.get_contents(ON) |
342 | - self.assertFalse('Also notified' in contents) |
343 | - |
344 | - def test_also_notified_not_suppressed(self): |
345 | - # If the advanced-subscription.enabled feature flag is off then the |
346 | - # "Also notified" portion of the portlet is shown. |
347 | - contents = self.get_contents(OFF) |
348 | - self.assertTrue('Also notified' in contents) |
349 | - |
350 | - |
351 | -class BugPortletSubcribersIdsTests(TestCaseWithFactory): |
352 | - |
353 | - layer = LaunchpadFunctionalLayer |
354 | - |
355 | - def test_content_type(self): |
356 | - bug = self.factory.makeBug() |
357 | - |
358 | - person = self.factory.makePerson() |
359 | - with person_logged_in(person): |
360 | - harness = LaunchpadFormHarness( |
361 | - bug.default_bugtask, BugPortletSubcribersIds) |
362 | - harness.view.render() |
363 | - |
364 | - self.assertEqual( |
365 | - harness.request.response.getHeader('content-type'), |
366 | - 'application/json') |
367 | - |
368 | - |
369 | class BugSubscriptionsListViewTestCase(TestCaseWithFactory): |
370 | """Tests for the BugSubscriptionsListView.""" |
371 | |
372 | |
373 | === modified file 'lib/lp/bugs/javascript/bugtask_index.js' |
374 | --- lib/lp/bugs/javascript/bugtask_index.js 2011-06-08 01:38:11 +0000 |
375 | +++ lib/lp/bugs/javascript/bugtask_index.js 2011-06-15 11:10:20 +0000 |
376 | @@ -40,8 +40,6 @@ |
377 | var link_branch_link; |
378 | |
379 | namespace.setup_bugtask_index = function() { |
380 | - Y.lp.bugs.bugtask_index.portlets.setup_portlet_handlers(); |
381 | - |
382 | /* |
383 | * Display the privacy notification if the bug is private |
384 | */ |
385 | @@ -1115,5 +1113,5 @@ |
386 | "lazr.formoverlay", "lazr.anim", "lazr.base", |
387 | "lazr.overlay", "lazr.choiceedit", "lp.app.picker", |
388 | "lp.client", "escape", |
389 | - "lp.client.plugins", "lp.bugs.bugtask_index.portlets", |
390 | - "lp.bugs.subscriber", "lp.app.errors"]}); |
391 | + "lp.client.plugins", "lp.bugs.subscriber", |
392 | + "lp.app.errors"]}); |
393 | |
394 | === removed file 'lib/lp/bugs/javascript/bugtask_index_portlets.js' |
395 | --- lib/lp/bugs/javascript/bugtask_index_portlets.js 2011-06-15 11:09:59 +0000 |
396 | +++ lib/lp/bugs/javascript/bugtask_index_portlets.js 1970-01-01 00:00:00 +0000 |
397 | @@ -1,897 +0,0 @@ |
398 | -/* Copyright 2011 Canonical Ltd. This software is licensed under the |
399 | - * GNU Affero General Public License version 3 (see the file LICENSE). |
400 | - * |
401 | - * Form overlay widgets and subscriber handling for bug pages. |
402 | - * |
403 | - * @module bugs |
404 | - * @submodule bugtask_index.portlets |
405 | - */ |
406 | - |
407 | -YUI.add('lp.bugs.bugtask_index.portlets', function(Y) { |
408 | - |
409 | -var namespace = Y.namespace('lp.bugs.bugtask_index.portlets'); |
410 | - |
411 | -// The launchpad js client used. |
412 | -var lp_client; |
413 | - |
414 | -// The launchpad client entry for the current bug. |
415 | -var lp_bug_entry; |
416 | - |
417 | -// The bug itself, taken from cache. |
418 | -var bug_repr; |
419 | - |
420 | -// A boolean telling us whether advanced subscription features are to be |
421 | -// used or not. |
422 | -var use_advanced_subscriptions = false; |
423 | - |
424 | -var subscription_labels = Y.lp.bugs.subscriber.subscription_labels; |
425 | - |
426 | -submit_button_html = |
427 | - '<button type="submit" name="field.actions.change" ' + |
428 | - 'value="Change" class="lazr-pos lazr-btn" >OK</button>'; |
429 | -cancel_button_html = |
430 | - '<button type="button" name="field.actions.cancel" ' + |
431 | - 'class="lazr-neg lazr-btn" >Cancel</button>'; |
432 | - |
433 | -// The set of subscriber CSS IDs as a JSON struct. |
434 | -var subscriber_ids; |
435 | - |
436 | -/* |
437 | - * An object representing the bugtask subscribers portlet. |
438 | - * |
439 | - * Since the portlet loads via XHR and inline subscribing |
440 | - * depends on that portlet being loaded, setup a custom |
441 | - * event object, to provide a hook for initializing subscription |
442 | - * link callbacks after custom events. |
443 | - */ |
444 | -var PortletTarget = function() {}; |
445 | -Y.augment(PortletTarget, Y.Event.Target); |
446 | -namespace.portlet = new PortletTarget(); |
447 | - |
448 | -/* |
449 | - * Create the lp client and bug entry if we haven't done so already. |
450 | - * |
451 | - * @method setup_client_and_bug |
452 | - */ |
453 | -function setup_client_and_bug() { |
454 | - lp_client = new Y.lp.client.Launchpad(); |
455 | - |
456 | - if (bug_repr === undefined) { |
457 | - bug_repr = LP.cache.bug; |
458 | - lp_bug_entry = new Y.lp.client.Entry( |
459 | - lp_client, bug_repr, bug_repr.self_link); |
460 | - } |
461 | -} |
462 | - |
463 | -namespace.load_subscribers_portlet = function( |
464 | - subscription_link, subscription_link_handler) { |
465 | - if (Y.UA.ie) { |
466 | - return null; |
467 | - } |
468 | - |
469 | - Y.one('#subscribers-portlet-spinner').setStyle('display', 'block'); |
470 | - |
471 | - function hide_spinner() { |
472 | - Y.one('#subscribers-portlet-spinner').setStyle('display', 'none'); |
473 | - // Fire a custom event to notify that the initial click |
474 | - // handler on subscription_link set above should be |
475 | - // cleared. |
476 | - if (namespace) { |
477 | - namespace.portlet.fire( |
478 | - 'bugs:portletloadfailed', subscription_link_handler); |
479 | - } |
480 | - } |
481 | - |
482 | - function setup_portlet(transactionid, response, args) { |
483 | - hide_spinner(); |
484 | - Y.one('#portlet-subscribers') |
485 | - .appendChild(Y.Node.create(response.responseText)); |
486 | - |
487 | - // Fire a custom portlet loaded event to notify when |
488 | - // it's safe to setup subscriber link callbacks. |
489 | - namespace.portlet.fire('bugs:portletloaded'); |
490 | - } |
491 | - |
492 | - var config = {on: {success: setup_portlet, |
493 | - failure: hide_spinner}}; |
494 | - var url = Y.one( |
495 | - '#subscribers-content-link').getAttribute('href').replace( |
496 | - 'bugs.', ''); |
497 | - Y.io(url, config); |
498 | -}; |
499 | - |
500 | - |
501 | -namespace.setup_portlet_handlers = function() { |
502 | - namespace.portlet.subscribe('bugs:portletloaded', function() { |
503 | - load_subscriber_ids(); |
504 | - }); |
505 | - /* |
506 | - * If the subscribers portlet fails to load, clear any |
507 | - * click handlers, so the normal subscribe page can be reached. |
508 | - */ |
509 | - namespace.portlet.subscribe('bugs:portletloadfailed', function(handler) { |
510 | - handler.detach(); |
511 | - }); |
512 | - namespace.portlet.subscribe('bugs:dupeportletloaded', function() { |
513 | - setup_subscription_link_handlers(); |
514 | - setup_unsubscribe_icon_handlers(); |
515 | - }); |
516 | - /* If the dupe subscribers portlet fails to load, |
517 | - * be sure to try to handle any unsub icons that may |
518 | - * exist for others. |
519 | - */ |
520 | - namespace.portlet.subscribe( |
521 | - 'bugs:dupeportletloadfailed', |
522 | - function(handlers) { |
523 | - setup_subscription_link_handlers(); |
524 | - setup_unsubscribe_icon_handlers(); |
525 | - }); |
526 | - |
527 | - /* If loading the subscriber IDs JSON has succeeded, set up the |
528 | - * subscription link handlers and load the subscribers from dupes. |
529 | - */ |
530 | - namespace.portlet.subscribe( |
531 | - 'bugs:portletsubscriberidsloaded', |
532 | - function() { |
533 | - load_subscribers_from_duplicates(); |
534 | - }); |
535 | - |
536 | - /* If loading the subscriber IDs JSON fails we still need to load the |
537 | - * subscribers from duplicates but we don't set up the subscription link |
538 | - * handlers. |
539 | - */ |
540 | - namespace.portlet.subscribe( |
541 | - 'bugs:portletsubscriberidsfailed', |
542 | - function() { |
543 | - load_subscribers_from_duplicates(); |
544 | - }); |
545 | - |
546 | - /* |
547 | - * Subscribing someone else requires loading a grayed out |
548 | - * username into the DOM until the subscribe action completes. |
549 | - * There are a couple XHR requests in check_can_be_unsubscribed |
550 | - * before the subscribe work can be done, so fire a custom event |
551 | - * bugs:nameloaded and do the work here when the event fires. |
552 | - */ |
553 | - namespace.portlet.subscribe('bugs:nameloaded', function(subscription) { |
554 | - var error_handler = new Y.lp.client.ErrorHandler(); |
555 | - error_handler.clearProgressUI = function() { |
556 | - var temp_link = Y.one('#temp-username'); |
557 | - if (temp_link) { |
558 | - var temp_parent = temp_link.get('parentNode'); |
559 | - temp_parent.removeChild(temp_link); |
560 | - } |
561 | - }; |
562 | - error_handler.showError = function(error_msg) { |
563 | - Y.lp.app.errors.display_error( |
564 | - Y.one('.menu-link-addsubscriber'), error_msg); |
565 | - }; |
566 | - |
567 | - var config = { |
568 | - on: { |
569 | - success: function() { |
570 | - var temp_link = Y.one('#temp-username'); |
571 | - var temp_spinner = Y.one('#temp-name-spinner'); |
572 | - temp_link.removeChild(temp_spinner); |
573 | - var anim = Y.lazr.anim.green_flash({ node: temp_link }); |
574 | - anim.on('end', function() { |
575 | - add_user_name_link(subscription); |
576 | - var temp_parent = temp_link.get('parentNode'); |
577 | - temp_parent.removeChild(temp_link); |
578 | - }); |
579 | - anim.run(); |
580 | - }, |
581 | - failure: error_handler.getFailureHandler() |
582 | - }, |
583 | - parameters: { |
584 | - person: Y.lp.client.get_absolute_uri( |
585 | - subscription.get('person').get('escaped_uri')), |
586 | - suppress_notify: false |
587 | - } |
588 | - }; |
589 | - lp_client.named_post(bug_repr.self_link, 'subscribe', config); |
590 | - }); |
591 | -}; |
592 | - |
593 | -function load_subscriber_ids() { |
594 | - function on_success(transactionid, response, args) { |
595 | - try { |
596 | - subscriber_ids = Y.JSON.parse(response.responseText); |
597 | - |
598 | - // Fire a custom event to trigger the setting-up of the |
599 | - // subscription handlers. |
600 | - namespace.portlet.fire('bugs:portletsubscriberidsloaded'); |
601 | - } catch (e) { |
602 | - // Fire an event to signal failure. This ensures that the |
603 | - // subscribers-from-dupes still get loaded into the portlet. |
604 | - namespace.portlet.fire('bugs:portletsubscriberidsfailed'); |
605 | - } |
606 | - } |
607 | - |
608 | - function on_failure() { |
609 | - // Fire an event to signal failure. This ensures that the |
610 | - // subscribers-from-dupes still get loaded into the portlet. |
611 | - namespace.portlet.fire('bugs:portletsubscriberidsfailed'); |
612 | - } |
613 | - |
614 | - var config = {on: {success: on_success, |
615 | - failure: on_failure}}; |
616 | - var url = Y.one( |
617 | - '#subscribers-ids-link').getAttribute('href'); |
618 | - Y.io(url, config); |
619 | -} |
620 | - |
621 | -/* |
622 | - * Set click handlers for unsubscribe remove icons. |
623 | - * |
624 | - * @method setup_unsubscribe_icon_handlers |
625 | - * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. |
626 | - */ |
627 | -function setup_unsubscribe_icon_handlers() { |
628 | - var subscription = new Y.lp.bugs.subscriber.Subscription({ |
629 | - link: Y.one('.menu-link-subscription'), |
630 | - spinner: Y.one('#sub-unsub-spinner'), |
631 | - subscriber: new Y.lp.bugs.subscriber.Subscriber({ |
632 | - uri: LP.links.me, |
633 | - subscriber_ids: subscriber_ids |
634 | - }) |
635 | - }); |
636 | - |
637 | - Y.on('click', function(e) { |
638 | - e.halt(); |
639 | - unsubscribe_user_via_icon(e.target, subscription); |
640 | - }, '.unsub-icon'); |
641 | -} |
642 | - |
643 | -/* |
644 | - * Set up and return a Subscription object for the direct subscription |
645 | - * link. |
646 | - */ |
647 | -function get_subscribe_self_subscription() { |
648 | - setup_client_and_bug(); |
649 | - var subscription = new Y.lp.bugs.subscriber.Subscription({ |
650 | - link: Y.one('.menu-link-subscription'), |
651 | - spinner: Y.one('#sub-unsub-spinner'), |
652 | - subscriber: new Y.lp.bugs.subscriber.Subscriber({ |
653 | - uri: LP.links.me, |
654 | - subscriber_ids: subscriber_ids |
655 | - }) |
656 | - }); |
657 | - |
658 | - subscription.set('can_be_unsubscribed', true); |
659 | - subscription.set('person', subscription.get('subscriber')); |
660 | - subscription.set('is_team', false); |
661 | - var css_name = subscription.get('person').get('css_name'); |
662 | - var direct_css_name = '#direct-' + css_name; |
663 | - var direct_node = Y.one(direct_css_name); |
664 | - var is_direct = direct_node !== null; |
665 | - subscription.set('is_direct', is_direct); |
666 | - var dupe_css_name = '#dupe-' + css_name; |
667 | - var dupe_node = Y.one(dupe_css_name); |
668 | - var has_dupes = dupe_node !== null; |
669 | - subscription.set('has_dupes', has_dupes); |
670 | - return subscription; |
671 | -} |
672 | - |
673 | - |
674 | -/* |
675 | - * Set up and return a Subscription object for the team subscription |
676 | - * link. |
677 | - */ |
678 | -function get_team_subscription(team_uri) { |
679 | - setup_client_and_bug(); |
680 | - var subscription = new Y.lp.bugs.subscriber.Subscription({ |
681 | - link: Y.one('.menu-link-subscription'), |
682 | - spinner: Y.one('#sub-unsub-spinner'), |
683 | - subscriber: new Y.lp.bugs.subscriber.Subscriber({ |
684 | - uri: team_uri, |
685 | - subscriber_ids: subscriber_ids |
686 | - }) |
687 | - }); |
688 | - |
689 | - subscription.set('is_direct', true); |
690 | - subscription.set('has_dupes', false); |
691 | - subscription.set('can_be_unsubscribed', true); |
692 | - subscription.set('person', subscription.get('subscriber')); |
693 | - subscription.set('is_team', true); |
694 | - return subscription; |
695 | -} |
696 | - |
697 | -/* |
698 | - * Initialize callbacks for subscribe/unsubscribe links. |
699 | - * |
700 | - * @method setup_subscription_link_handlers |
701 | - */ |
702 | -function setup_subscription_link_handlers() { |
703 | - if (LP.links.me === undefined) { |
704 | - return; |
705 | - } |
706 | - |
707 | - var subscription = get_subscribe_self_subscription(); |
708 | - |
709 | -} |
710 | - |
711 | -function load_subscribers_from_duplicates() { |
712 | - if (Y.UA.ie) { |
713 | - return null; |
714 | - } |
715 | - |
716 | - Y.one('#subscribers-portlet-dupe-spinner').setStyle( |
717 | - 'display', 'block'); |
718 | - |
719 | - function hide_spinner() { |
720 | - Y.one('#subscribers-portlet-dupe-spinner').setStyle( |
721 | - 'display', 'none'); |
722 | - } |
723 | - |
724 | - function on_failure(transactionid, response, args) { |
725 | - hide_spinner(); |
726 | - // Fire a custom event to signal failure, so that |
727 | - // any remaining unsub icons can be hooked up. |
728 | - namespace.portlet.fire('bugs:dupeportletloadfailed'); |
729 | - } |
730 | - |
731 | - function on_success(transactionid, response, args) { |
732 | - hide_spinner(); |
733 | - |
734 | - var dupe_subscribers_container = Y.one( |
735 | - '#subscribers-from-duplicates-container'); |
736 | - dupe_subscribers_container.set( |
737 | - 'innerHTML', |
738 | - dupe_subscribers_container.get('innerHTML') + |
739 | - response.responseText); |
740 | - |
741 | - // Fire a custom portlet loaded event to notify when |
742 | - // it's safe to setup dupe subscriber link callbacks. |
743 | - namespace.portlet.fire('bugs:dupeportletloaded'); |
744 | - } |
745 | - |
746 | - var config = {on: {success: on_success, |
747 | - failure: on_failure}}; |
748 | - var url = Y.one( |
749 | - '#subscribers-from-dupes-content-link').getAttribute( |
750 | - 'href').replace('bugs.', ''); |
751 | - Y.io(url, config); |
752 | -} |
753 | - |
754 | -/* |
755 | - * Add the user name to the subscriber's list. |
756 | - * |
757 | - * @method add_user_name_link |
758 | - */ |
759 | -function add_user_name_link(subscription) { |
760 | - // Be paranoid about display_name, since timeouts or other errors |
761 | - // could mean display_name wasn't set on initialization. |
762 | - subscription.get('person').set_display_name(function () { |
763 | - _add_user_name_link(subscription); |
764 | - }); |
765 | -} |
766 | - |
767 | -function _add_user_name_link(subscription) { |
768 | - var person = subscription.get('person'); |
769 | - var link_node = build_user_link_html(subscription); |
770 | - var subscribers = Y.one('#subscribers-links'); |
771 | - if (subscription.is_current_user_subscribing()) { |
772 | - // If this is the current user, then top post the name and be done. |
773 | - subscribers.insertBefore(link_node, subscribers.get('firstChild')); |
774 | - } else { |
775 | - var next = get_next_subscriber_node(subscription); |
776 | - if (next) { |
777 | - subscribers.insertBefore(link_node, next); |
778 | - } else { |
779 | - subscribers.appendChild(link_node); |
780 | - } |
781 | - } |
782 | - // Handle the case of no previous subscribers. |
783 | - var none_subscribers = Y.one('#none-subscribers'); |
784 | - if (none_subscribers) { |
785 | - var none_parent = none_subscribers.get('parentNode'); |
786 | - none_parent.removeChild(none_subscribers); |
787 | - } |
788 | - // Highlight the new addition with a green flash. |
789 | - Y.lazr.anim.green_flash({ node: link_node }).run(); |
790 | - // Set the click handler if adding a remove icon. |
791 | - if (subscription.can_be_unsubscribed_by_user()) { |
792 | - var remove_icon = |
793 | - Y.one('#unsubscribe-icon-' + person.get('css_name')); |
794 | - remove_icon.on('click', function(e) { |
795 | - e.halt(); |
796 | - unsubscribe_user_via_icon(e.target, subscription); |
797 | - }); |
798 | - } |
799 | -} |
800 | - |
801 | -/* |
802 | - * Unsubscribe a user from this bugtask when a remove icon is clicked. |
803 | - * |
804 | - * @method unsubscribe_user_via_icon |
805 | - * @param icon {Node} The remove icon that was clicked. |
806 | - * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. |
807 | -*/ |
808 | -function unsubscribe_user_via_icon(icon, subscription) { |
809 | - icon.set('src', '/@@/spinner'); |
810 | - var icon_parent = icon.get('parentNode'); |
811 | - |
812 | - var user_uri = get_user_uri_from_icon(icon); |
813 | - var person = new Y.lp.bugs.subscriber.Subscriber({ |
814 | - uri: user_uri, |
815 | - subscriber_ids: subscriber_ids |
816 | - }); |
817 | - subscription.set('person', person); |
818 | - |
819 | - // Determine if this is a dupe. |
820 | - var is_dupe; |
821 | - var icon_parent_div = icon_parent.get('parentNode'); |
822 | - var dupe_id = 'dupe-' + person.get('css_name'); |
823 | - if (icon_parent_div.get('id') === dupe_id) { |
824 | - is_dupe = true; |
825 | - } else { |
826 | - is_dupe = false; |
827 | - } |
828 | - |
829 | - var error_handler = new Y.lp.client.ErrorHandler(); |
830 | - error_handler.clearProgressUI = function () { |
831 | - icon.set('src', '/@@/remove'); |
832 | - // Grab the icon again to reset to click handler. |
833 | - var unsubscribe_icon = Y.one( |
834 | - '#unsubscribe-icon-' + person.get('css_name')); |
835 | - unsubscribe_icon.on('click', function(e) { |
836 | - e.halt(); |
837 | - unsubscribe_user_via_icon(e.target, subscription); |
838 | - }); |
839 | - |
840 | - }; |
841 | - error_handler.showError = function (error_msg) { |
842 | - var flash_node = Y.one('.' + person.get('css_name')); |
843 | - Y.lp.app.errors.display_error(flash_node, error_msg); |
844 | - |
845 | - }; |
846 | - |
847 | - var subscription_link = subscription.get('link'); |
848 | - var config = { |
849 | - on: { |
850 | - success: function(client) { |
851 | - var num_person_links = Y.all( |
852 | - '.' + person.get('css_name')).size(); |
853 | - Y.lp.bugs.subscribers_list.remove_user_link(person, is_dupe); |
854 | - var has_direct, has_dupes; |
855 | - if (num_person_links === 1 && |
856 | - subscription.is_current_user_subscribing()) { |
857 | - // Current user has been completely unsubscribed. |
858 | - subscription.disable_spinner( |
859 | - subscription_labels.SUBSCRIBE); |
860 | - has_direct = false; |
861 | - has_dupes = false; |
862 | - } else { |
863 | - // If we removed the duplicate subscription, |
864 | - // we are left with the direct one, and vice versa. |
865 | - has_direct = is_dupe; |
866 | - has_dupes = !is_dupe; |
867 | - } |
868 | - subscription.set('is_direct', has_direct); |
869 | - subscription.set('has_dupes', has_dupes); |
870 | - set_subscription_link_parent_class( |
871 | - subscription_link, has_direct, has_dupes); |
872 | - }, |
873 | - |
874 | - failure: error_handler.getFailureHandler() |
875 | - } |
876 | - }; |
877 | - |
878 | - if (!subscription.is_current_user_subscribing()) { |
879 | - config.parameters = { |
880 | - person: Y.lp.client.get_absolute_uri(user_uri) |
881 | - }; |
882 | - } |
883 | - |
884 | - if (is_dupe) { |
885 | - lp_client.named_post( |
886 | - bug_repr.self_link, 'unsubscribeFromDupes', config); |
887 | - } else { |
888 | - lp_client.named_post(bug_repr.self_link, 'unsubscribe', config); |
889 | - } |
890 | -} |
891 | - |
892 | -/* |
893 | - * Initialize click handler for the subscribe someone else link. |
894 | - * |
895 | - * @method setup_subscribe_someone_else_handler |
896 | - * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. |
897 | - */ |
898 | -function setup_subscribe_someone_else_handler(subscription) { |
899 | - var config = { |
900 | - header: 'Subscribe someone else', |
901 | - step_title: 'Search', |
902 | - picker_activator: '.menu-link-addsubscriber' |
903 | - }; |
904 | - |
905 | - config.save = function(result) { |
906 | - subscribe_someone_else(result, subscription); |
907 | - }; |
908 | - var picker = Y.lp.app.picker.create('ValidPersonOrTeam', config); |
909 | -} |
910 | - |
911 | -/* |
912 | - * Build the HTML for a user link for the subscribers list. |
913 | - * |
914 | - * @method build_user_link_html |
915 | - * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. |
916 | - * @return html {String} The HTML used for creating a subscriber link. |
917 | - */ |
918 | -function build_user_link_html(subscription) { |
919 | - var name = subscription.get('person').get('name'); |
920 | - var css_name = subscription.get('person').get('css_name'); |
921 | - var full_name = subscription.get('person').get('full_display_name'); |
922 | - var display_name = subscription.get('person').get('display_name'); |
923 | - var terms = { |
924 | - name: name, |
925 | - css_name: css_name, |
926 | - display_name: display_name, |
927 | - full_name: full_name |
928 | - }; |
929 | - |
930 | - if (subscription.is_current_user_subscribing()) { |
931 | - terms.subscribed_by = 'themselves'; |
932 | - } else { |
933 | - terms.subscribed_by = 'by ' + full_name; |
934 | - } |
935 | - |
936 | - var html = Y.Node.create('<div><a></a></div>'); |
937 | - html.addClass(terms.css_name); |
938 | - |
939 | - if (subscription.has_duplicate_subscriptions()) { |
940 | - html.set('id', 'dupe-' + terms.css_name); |
941 | - } else { |
942 | - html.set('id', 'direct-' + terms.css_name); |
943 | - } |
944 | - |
945 | - html.one('a') |
946 | - .set('href', '/~' + terms.name) |
947 | - .set('name', terms.full_name) |
948 | - .set('title', 'Subscribed ' + terms.subscribed_by); |
949 | - |
950 | - var span; |
951 | - if (subscription.is_team()) { |
952 | - span = '<span class="sprite team"></span>'; |
953 | - } else { |
954 | - span = '<span class="sprite person"></span>'; |
955 | - } |
956 | - |
957 | - html.one('a') |
958 | - .appendChild(Y.Node.create(span)) |
959 | - .appendChild(document.createTextNode(terms.display_name)); |
960 | - |
961 | - // Add remove icon if the current user can unsubscribe the subscriber. |
962 | - if (subscription.can_be_unsubscribed_by_user()) { |
963 | - var icon_html = Y.Node.create( |
964 | - '<a href="+subscribe">' + |
965 | - '<img class="unsub-icon" src="/@@/remove" alt="Remove" /></a>'); |
966 | - icon_html |
967 | - .set('id', 'unsubscribe-' + terms.css_name) |
968 | - .set('title', 'Unsubscribe ' + terms.full_name); |
969 | - icon_html.one('img') |
970 | - .set('id', 'unsubscribe-icon-' + terms.css_name); |
971 | - html.appendChild(icon_html); |
972 | - } |
973 | - |
974 | - return html; |
975 | -} |
976 | - |
977 | -/* |
978 | - * Returns the next node in alphabetical order after the subscriber |
979 | - * node now being added. No node is returned to append to end of list. |
980 | - * |
981 | - * The name can appear in one of two different lists. 1) The list of |
982 | - * subscribers that can be unsubscribed by the current user, and |
983 | - * 2) the list of subscribers that cannot be unsubscribed. |
984 | - * |
985 | - * @method get_next_subscriber_node |
986 | - * @param subscription_link {Node} The sub/unsub link. |
987 | - * @return {Node} The node appearing next in the subscriber list or |
988 | - * undefined if no node is next. |
989 | - */ |
990 | -function get_next_subscriber_node(subscription) { |
991 | - var full_name = subscription.get('person').get('full_display_name'); |
992 | - var can_be_unsubscribed = subscription.can_be_unsubscribed_by_user(); |
993 | - var nodes_by_name = {}; |
994 | - var unsubscribables = []; |
995 | - var not_unsubscribables = []; |
996 | - |
997 | - // Use the list of subscribers pulled from the DOM to have sortable |
998 | - // lists of unsubscribable vs. not unsubscribable person links. |
999 | - var all_subscribers = Y.all('#subscribers-links div'); |
1000 | - if (all_subscribers.size() > 0) { |
1001 | - all_subscribers.each(function(sub_link) { |
1002 | - if (sub_link.getAttribute('id') !== 'temp-username') { |
1003 | - // User's displayname is found via the link's "name" |
1004 | - // attribute. |
1005 | - var sub_link_name = sub_link.one('a').getAttribute('name'); |
1006 | - nodes_by_name[sub_link_name] = sub_link; |
1007 | - if (sub_link.one('img.unsub-icon')) { |
1008 | - unsubscribables.push(sub_link_name); |
1009 | - } else { |
1010 | - not_unsubscribables.push(sub_link_name); |
1011 | - } |
1012 | - } |
1013 | - }); |
1014 | - |
1015 | - // Add the current subscription. |
1016 | - if (can_be_unsubscribed) { |
1017 | - unsubscribables.push(full_name); |
1018 | - } else { |
1019 | - not_unsubscribables.push(full_name); |
1020 | - } |
1021 | - unsubscribables.sort(); |
1022 | - not_unsubscribables.sort(); |
1023 | - } else { |
1024 | - // If there is no all_subscribers, then we're dealing with |
1025 | - // the printed None, so return. |
1026 | - return; |
1027 | - } |
1028 | - |
1029 | - var i; |
1030 | - if ((!unsubscribables && !not_unsubscribables) || |
1031 | - // If A) neither list exists, B) the user belongs in the second |
1032 | - // list but the second list doesn't exist, or C) user belongs in the |
1033 | - // first list and the second doesn't exist, return no node to append. |
1034 | - (!can_be_unsubscribed && !not_unsubscribables) || |
1035 | - (can_be_unsubscribed && unsubscribables && !not_unsubscribables)) { |
1036 | - return; |
1037 | - } else if ( |
1038 | - // If the user belongs in the first list, and the first list |
1039 | - // doesn't exist, but the second one does, return the first node |
1040 | - // in the second list. |
1041 | - can_be_unsubscribed && !unsubscribables && not_unsubscribables) { |
1042 | - return nodes_by_name[not_unsubscribables[0]]; |
1043 | - } else if (can_be_unsubscribed) { |
1044 | - // If the user belongs in the first list, loop the list for position. |
1045 | - for (i=0; i<unsubscribables.length; i++) { |
1046 | - if (unsubscribables[i] === full_name) { |
1047 | - if (i+1 < unsubscribables.length) { |
1048 | - return nodes_by_name[unsubscribables[i+1]]; |
1049 | - // If the current link should go at the end of the first |
1050 | - // list and we're at the end of that list, return the |
1051 | - // first node of the second list. Due to earlier checks |
1052 | - // we can be sure this list exists. |
1053 | - } else if (i+1 >= unsubscribables.length) { |
1054 | - return nodes_by_name[not_unsubscribables[0]]; |
1055 | - } |
1056 | - } |
1057 | - } |
1058 | - } else if (!can_be_unsubscribed) { |
1059 | - // If user belongs in the second list, loop the list for position. |
1060 | - for (i=0; i<not_unsubscribables.length; i++) { |
1061 | - if (not_unsubscribables[i] === full_name) { |
1062 | - if (i+1 < not_unsubscribables.length) { |
1063 | - return nodes_by_name[not_unsubscribables[i+1]]; |
1064 | - } else { |
1065 | - return; |
1066 | - } |
1067 | - } |
1068 | - } |
1069 | - } |
1070 | -} |
1071 | - |
1072 | -/* |
1073 | - * Traverse the DOM of a given remove icon to find |
1074 | - * the user's link. Returns a URI of the form "/~username". |
1075 | - * |
1076 | - * @method get_user_uri_from_icon |
1077 | - * @param icon {Node} The node representing a remove icon. |
1078 | - * @return user_uri {String} The user's uri, without the hostname. |
1079 | - */ |
1080 | -function get_user_uri_from_icon(icon) { |
1081 | - var parent_div = icon.get('parentNode').get('parentNode'); |
1082 | - // This should be parent_div.firstChild, but because of #text |
1083 | - // and cross-browser issues, using the YUI query syntax is |
1084 | - // safer here. |
1085 | - var user_uri = parent_div.one('a').getAttribute('href'); |
1086 | - |
1087 | - // Strip the domain off. We just want a path. |
1088 | - var host_start = user_uri.indexOf('//'); |
1089 | - if (host_start !== -1) { |
1090 | - var host_end = user_uri.indexOf('/', host_start+2); |
1091 | - return user_uri.substring(host_end, user_uri.length); |
1092 | - } |
1093 | - |
1094 | - return user_uri; |
1095 | -} |
1096 | - |
1097 | -/* |
1098 | - * Set the class on subscription link's parentNode. |
1099 | - * |
1100 | - * This is used to reset the class used by the |
1101 | - * click handler to know which link was clicked. |
1102 | - * |
1103 | - * @method set_subscription_link_parent_class |
1104 | - * @param subscription_link {Node} The sub/unsub link. |
1105 | - * @param subscribed {Boolean} The sub/unsub'ed flag for the class. |
1106 | - * @param dupe_subscribed {Boolean} The sub/unsub'ed flag for dupes |
1107 | - * on the class. |
1108 | - */ |
1109 | -function set_subscription_link_parent_class( |
1110 | - user_link, subscribed, dupe_subscribed) { |
1111 | - |
1112 | - var parent = user_link.get('parentNode'); |
1113 | - if (subscribed) { |
1114 | - parent.removeClass('subscribed-false'); |
1115 | - parent.addClass('subscribed-true'); |
1116 | - } else { |
1117 | - parent.removeClass('subscribed-true'); |
1118 | - parent.addClass('subscribed-false'); |
1119 | - } |
1120 | - |
1121 | - if (dupe_subscribed) { |
1122 | - parent.removeClass('dup-subscribed-false'); |
1123 | - parent.addClass('dup-subscribed-true'); |
1124 | - } else { |
1125 | - parent.removeClass('dup-subscribed-true'); |
1126 | - parent.addClass('dup-subscribed-false'); |
1127 | - } |
1128 | -} |
1129 | - |
1130 | -/* |
1131 | - * Subscribe a person or team other than the current user. |
1132 | - * This is a callback for the subscribe someone else picker. |
1133 | - * |
1134 | - * @method subscribe_someone_else |
1135 | - * @result {Object} The object representing a person returned by the API. |
1136 | - */ |
1137 | -function subscribe_someone_else(result, subscription) { |
1138 | - var person = new Y.lp.bugs.subscriber.Subscriber({ |
1139 | - uri: result.api_uri, |
1140 | - display_name: result.title, |
1141 | - subscriber_ids: subscriber_ids |
1142 | - }); |
1143 | - subscription.set('person', person); |
1144 | - |
1145 | - var error_handler = new Y.lp.client.ErrorHandler(); |
1146 | - error_handler.showError = function(error_msg) { |
1147 | - Y.lp.app.errors.display_error( |
1148 | - Y.one('.menu-link-addsubscriber'), error_msg); |
1149 | - }; |
1150 | - |
1151 | - if (subscription.is_already_subscribed()) { |
1152 | - error_handler.showError( |
1153 | - subscription.get('person').get('full_display_name') + |
1154 | - ' has already been subscribed'); |
1155 | - } else { |
1156 | - check_can_be_unsubscribed(subscription); |
1157 | - } |
1158 | -} |
1159 | - |
1160 | -/* |
1161 | - * Check if the current user can unsubscribe the person |
1162 | - * being subscribed. |
1163 | - * |
1164 | - * This must be done in JavaScript, since the subscription |
1165 | - * hasn't completed yet, and so, can_be_unsubscribed_by_user |
1166 | - * cannot be used. |
1167 | - * |
1168 | - * @method check_can_be_unsubscribed |
1169 | - * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. |
1170 | - */ |
1171 | -function check_can_be_unsubscribed(subscription) { |
1172 | - var error_handler = new Y.lp.client.ErrorHandler(); |
1173 | - error_handler.showError = function (error_msg) { |
1174 | - Y.lp.app.errors.display_error( |
1175 | - Y.one('.menu-link-addsubscriber'), error_msg); |
1176 | - }; |
1177 | - |
1178 | - var config = { |
1179 | - on: { |
1180 | - success: function(result) { |
1181 | - var is_team = result.get('is_team'); |
1182 | - subscription.set('is_team', is_team); |
1183 | - var final_config = { |
1184 | - on: { |
1185 | - success: function(result) { |
1186 | - var team_member = false; |
1187 | - var i; |
1188 | - for (i=0; i<result.entries.length; i++) { |
1189 | - if (result.entries[i].get('member_link') === |
1190 | - Y.lp.client.get_absolute_uri( |
1191 | - subscription.get( |
1192 | - 'subscriber').get('uri'))) { |
1193 | - team_member = true; |
1194 | - } |
1195 | - } |
1196 | - |
1197 | - if (team_member) { |
1198 | - subscription.set('can_be_unsubscribed', true); |
1199 | - add_temp_user_name(subscription); |
1200 | - } else { |
1201 | - subscription.set( |
1202 | - 'can_be_unsubscribed', false); |
1203 | - add_temp_user_name(subscription); |
1204 | - } |
1205 | - }, |
1206 | - |
1207 | - failure: error_handler.getFailureHandler() |
1208 | - } |
1209 | - }; |
1210 | - |
1211 | - if (is_team) { |
1212 | - // Get a list of members to see if current user |
1213 | - // is a team member. |
1214 | - var members = result.get( |
1215 | - 'members_details_collection_link'); |
1216 | - lp_client.get(members, final_config); |
1217 | - } else { |
1218 | - subscription.set('can_be_unsubscribed', false); |
1219 | - add_temp_user_name(subscription); |
1220 | - } |
1221 | - }, |
1222 | - |
1223 | - failure: error_handler.getFailureHandler() |
1224 | - } |
1225 | - }; |
1226 | - lp_client.get(Y.lp.client.get_absolute_uri( |
1227 | - subscription.get('person').get('escaped_uri')), config); |
1228 | -} |
1229 | - |
1230 | -/* |
1231 | - * Add a grayed out, temporary user name when subscribing |
1232 | - * someone else. |
1233 | - * |
1234 | - * @method add_temp_user_name |
1235 | - * @param subscription_link {Node} The sub/unsub link. |
1236 | - */ |
1237 | -function add_temp_user_name(subscription) { |
1238 | - // Be paranoid about display_name, since timeouts or other errors |
1239 | - // could mean display_name wasn't set on initialization. |
1240 | - subscription.get('person').set_display_name(function () { |
1241 | - _add_temp_user_name(subscription); |
1242 | - }); |
1243 | -} |
1244 | - |
1245 | -function _add_temp_user_name(subscription) { |
1246 | - var display_name = subscription.get('person').get('display_name'); |
1247 | - var img_src; |
1248 | - if (subscription.is_team()) { |
1249 | - img_src = '/@@/teamgray'; |
1250 | - } else { |
1251 | - img_src = '/@@/persongray'; |
1252 | - } |
1253 | - |
1254 | - // The <span>...</span> below must *not* be <span/>. On FF (maybe |
1255 | - // others, but at least on FF 3.0.11) will then not notice any |
1256 | - // following sibling nodes, like the spinner image. |
1257 | - var link_node = Y.Node.create([ |
1258 | - '<div id="temp-username"> ', |
1259 | - ' <img alt="" width="14" height="14" />', |
1260 | - ' <span>Other Display Name</span>', |
1261 | - ' <img id="temp-name-spinner" src="/@@/spinner" alt="" ', |
1262 | - ' style="position:absolute;right:8px" /></div>'].join('')); |
1263 | - link_node.one('img').set('src', img_src); |
1264 | - link_node.replaceChild( |
1265 | - document.createTextNode(display_name), |
1266 | - link_node.one('span')); |
1267 | - |
1268 | - var subscribers = Y.one('#subscribers-links'); |
1269 | - var next = get_next_subscriber_node(subscription); |
1270 | - if (next) { |
1271 | - subscribers.insertBefore(link_node, next); |
1272 | - } else { |
1273 | - // Handle the case of no subscribers. |
1274 | - var none_subscribers = Y.one('#none-subscribers'); |
1275 | - if (none_subscribers) { |
1276 | - var none_parent = none_subscribers.get('parentNode'); |
1277 | - none_parent.removeChild(none_subscribers); |
1278 | - } |
1279 | - subscribers.appendChild(link_node); |
1280 | - } |
1281 | - |
1282 | - // Fire a custom event to know it's safe to begin |
1283 | - // any actual subscribing work. |
1284 | - namespace.portlet.fire('bugs:nameloaded', subscription); |
1285 | -} |
1286 | - |
1287 | -}, "0.1", {"requires": ["base", "oop", "node", "event", "io-base", |
1288 | - "json-parse", "substitute", "widget-position-ext", |
1289 | - "lazr.formoverlay", "lazr.anim", "lazr.base", |
1290 | - "lazr.overlay", "lazr.choiceedit", "lp.app.picker", |
1291 | - "lp.client", |
1292 | - "lp.client.plugins", "lp.bugs.subscriber", |
1293 | - "lp.bugs.subscribers_list", |
1294 | - "lp.bugs.bug_notification_level", "lp.app.errors"]}); |
1295 | |
1296 | === removed file 'lib/lp/bugs/javascript/subscriber.js' |
1297 | --- lib/lp/bugs/javascript/subscriber.js 2011-05-18 21:36:46 +0000 |
1298 | +++ lib/lp/bugs/javascript/subscriber.js 1970-01-01 00:00:00 +0000 |
1299 | @@ -1,390 +0,0 @@ |
1300 | -/** Copyright (c) 2009, Canonical Ltd. All rights reserved. |
1301 | - * |
1302 | - * Objects for subscription handling. |
1303 | - * |
1304 | - * @module bugs |
1305 | - * @submodule subscriber |
1306 | - */ |
1307 | - |
1308 | -YUI.add('lp.bugs.subscriber', function(Y) { |
1309 | - |
1310 | -var namespace = Y.namespace('lp.bugs.subscriber'); |
1311 | -namespace.subscription_labels = { |
1312 | - 'EDIT': 'Edit subscription', |
1313 | - 'SUBSCRIBE': 'Subscribe', |
1314 | - 'UNSUBSCRIBE': 'Unsubscribe' |
1315 | -}; |
1316 | - |
1317 | - |
1318 | -/** |
1319 | - * A Subscription object which represents the subscription |
1320 | - * being attempted. |
1321 | - * |
1322 | - * @class Subscription |
1323 | - * @namespace lp |
1324 | - */ |
1325 | -function Subscription(config) { |
1326 | - Subscription.superclass.constructor.apply(this, arguments); |
1327 | -} |
1328 | - |
1329 | -Subscription.ATTRS = { |
1330 | - 'link': { |
1331 | - value: null |
1332 | - }, |
1333 | - |
1334 | - 'can_be_unsubscribed': { |
1335 | - value: false |
1336 | - }, |
1337 | - |
1338 | - 'is_direct': { |
1339 | - value: true |
1340 | - }, |
1341 | - |
1342 | - 'has_dupes': { |
1343 | - value: false |
1344 | - }, |
1345 | - |
1346 | - 'person': { |
1347 | - value: null |
1348 | - }, |
1349 | - |
1350 | - 'is_team': { |
1351 | - value: false |
1352 | - }, |
1353 | - |
1354 | - 'subscriber': { |
1355 | - value: null |
1356 | - }, |
1357 | - |
1358 | - 'spinner': { |
1359 | - value: null |
1360 | - }, |
1361 | - |
1362 | - 'bug_notification_level': { |
1363 | - value: 'Discussion' |
1364 | - } |
1365 | -}; |
1366 | - |
1367 | -Y.extend(Subscription, Y.Base, { |
1368 | - |
1369 | - /** |
1370 | - * Is the current subscription link a node? |
1371 | - * Useful in checking that the link is defined. |
1372 | - * |
1373 | - * @method is_node |
1374 | - * @return {Boolean} |
1375 | - */ |
1376 | - 'is_node': function() { |
1377 | - return this.get('link') instanceof Y.Node; |
1378 | - }, |
1379 | - |
1380 | - /** |
1381 | - * Is the person being subscribed already subscribed? |
1382 | - * |
1383 | - * @method is_already_subscribed |
1384 | - * @return {Boolean} |
1385 | - */ |
1386 | - 'is_already_subscribed': function() { |
1387 | - var display_name = this.get('person').get('full_display_name'); |
1388 | - var already_subscribed = false; |
1389 | - |
1390 | - Y.all('#subscribers-links div').each(function(link) { |
1391 | - var name = link.one('a').getAttribute('name'); |
1392 | - if (name === display_name) { |
1393 | - already_subscribed = true; |
1394 | - } |
1395 | - }); |
1396 | - |
1397 | - return already_subscribed; |
1398 | - }, |
1399 | - |
1400 | - /** |
1401 | - * Can this subscriber being unsubscribed by the current |
1402 | - * user? |
1403 | - * |
1404 | - * @method can_be_unsubscribed_by_user |
1405 | - * @return {Boolean} |
1406 | - */ |
1407 | - 'can_be_unsubscribed_by_user': function() { |
1408 | - return this.get('can_be_unsubscribed'); |
1409 | - }, |
1410 | - |
1411 | - /** |
1412 | - * Is this the current user subscribing him/herself? |
1413 | - * |
1414 | - * @method is_current_user_subscribing |
1415 | - * @return {Boolean} |
1416 | - */ |
1417 | - 'is_current_user_subscribing': function() { |
1418 | - return ( |
1419 | - this.get('subscriber').get('name') === |
1420 | - this.get('person').get('name') |
1421 | - ); |
1422 | - }, |
1423 | - |
1424 | - /** |
1425 | - * Is the current subscription a direct subscription? |
1426 | - * |
1427 | - * @method is_direct_subscription |
1428 | - * @return {Boolean} |
1429 | - */ |
1430 | - 'is_direct_subscription': function() { |
1431 | - return this.get('is_direct'); |
1432 | - }, |
1433 | - |
1434 | - /** |
1435 | - * Does this subscription have dupes? |
1436 | - * |
1437 | - * @method has_duplicate_subscriptions |
1438 | - * @return {Boolean} |
1439 | - */ |
1440 | - 'has_duplicate_subscriptions': function() { |
1441 | - return this.get('has_dupes'); |
1442 | - }, |
1443 | - |
1444 | - /** |
1445 | - * Is this subscriber a team? |
1446 | - * |
1447 | - * @method is_team |
1448 | - * @return {Boolean} |
1449 | - */ |
1450 | - 'is_team': function() { |
1451 | - return this.get('is_team'); |
1452 | - }, |
1453 | - |
1454 | - /** |
1455 | - * Turn on the progess spinner. |
1456 | - * |
1457 | - * @method enable_spinner |
1458 | - */ |
1459 | - 'enable_spinner': function(text) { |
1460 | - if (Y.Lang.isValue(text)) { |
1461 | - this.get('spinner').set('innerHTML', text); |
1462 | - } |
1463 | - this.get('link').setStyle('display', 'none'); |
1464 | - this.get('spinner').setStyle('display', 'block'); |
1465 | - }, |
1466 | - |
1467 | - /** |
1468 | - * Turn off the progress spinner. |
1469 | - * |
1470 | - * @method disable_spinner |
1471 | - */ |
1472 | - 'disable_spinner': function(text) { |
1473 | - if (Y.Lang.isValue(text)) { |
1474 | - var link = this.get('link'); |
1475 | - link.set('innerHTML', text); |
1476 | - if (text === namespace.subscription_labels.SUBSCRIBE) { |
1477 | - link.removeClass('modify remove'); |
1478 | - link.removeClass('modify edit'); |
1479 | - link.addClass('add'); |
1480 | - } else if (text === namespace.subscription_labels.EDIT) { |
1481 | - link.removeClass('add'); |
1482 | - link.addClass('modify edit'); |
1483 | - } else { |
1484 | - link.removeClass('add'); |
1485 | - link.addClass('modify remove'); |
1486 | - } |
1487 | - } |
1488 | - this.get('spinner').setStyle('display', 'none'); |
1489 | - this.get('link').setStyle('display', 'inline'); |
1490 | - } |
1491 | -}); |
1492 | - |
1493 | -namespace.Subscription = Subscription; |
1494 | - |
1495 | -/** A Subscriber object which can represent the subscribing person or |
1496 | - * the person being subscribed. |
1497 | - * |
1498 | - * @class Subscriber |
1499 | - * @namespace lp |
1500 | - */ |
1501 | -function Subscriber(config) { |
1502 | - Subscriber.superclass.constructor.apply(this, arguments); |
1503 | -} |
1504 | - |
1505 | -Subscriber.NAME = 'Subscriber'; |
1506 | -Subscriber.ATTRS = { |
1507 | - uri: { |
1508 | - value: '' |
1509 | - }, |
1510 | - |
1511 | - name: { |
1512 | - value: '' |
1513 | - }, |
1514 | - |
1515 | - css_name: { |
1516 | - value: '' |
1517 | - }, |
1518 | - |
1519 | - escaped_uri: { |
1520 | - value: '' |
1521 | - }, |
1522 | - |
1523 | - user_node: { |
1524 | - value: null |
1525 | - }, |
1526 | - |
1527 | - display_name: { |
1528 | - value: '' |
1529 | - }, |
1530 | - |
1531 | - full_display_name: { |
1532 | - value: '' |
1533 | - }, |
1534 | - |
1535 | - subscriber_ids: { |
1536 | - value: null |
1537 | - } |
1538 | -}; |
1539 | - |
1540 | -Y.extend(Subscriber, Y.Base, { |
1541 | - |
1542 | - /** |
1543 | - * Subscriber can take as little as a Person uri and work |
1544 | - * out most of the person's name attributes from that. |
1545 | - * |
1546 | - * The display_name is the tricky part and has to be worked |
1547 | - * out either from the DOM or via the LP API. The object can |
1548 | - * be passed a DOM node in the config, but the object tries |
1549 | - * to work out the DOM on the fly if not and falls back to |
1550 | - * the API if LP is available. (See the included display_name |
1551 | - * methods for more.) |
1552 | - * |
1553 | - * @method initializer |
1554 | - */ |
1555 | - initializer: function(config) { |
1556 | - if (this.get('uri') !== '') { |
1557 | - this.set('name', this.get('uri').substring(2)); |
1558 | - var name = this.get('name'); |
1559 | - |
1560 | - // If we have a subscriber_ids object and that object |
1561 | - // has an entry for this Subscriber, then set css_name. |
1562 | - // Otherwise, create a css_name with a guid and update |
1563 | - // subscriber_ids to include it. |
1564 | - var subscriber_ids = this.get('subscriber_ids'); |
1565 | - if (Y.Lang.isValue(subscriber_ids)) { |
1566 | - var css_name = this.get('subscriber_ids')[name]; |
1567 | - if (Y.Lang.isValue(css_name)) { |
1568 | - this.set('css_name', css_name); |
1569 | - } else { |
1570 | - css_name = 'subscriber-' + Y.guid(); |
1571 | - subscriber_ids[name] = css_name; |
1572 | - this.set('subscriber_ids', subscriber_ids); |
1573 | - this.set('css_name', css_name); |
1574 | - } |
1575 | - } |
1576 | - |
1577 | - // Handle the case of plus signs in user names. |
1578 | - var escaped_uri; |
1579 | - if (name.indexOf('+') > 0) { |
1580 | - escaped_uri = name.replace('+', '%2B'); |
1581 | - } else { |
1582 | - escaped_uri = name; |
1583 | - } |
1584 | - this.set('escaped_uri', '/~' + escaped_uri); |
1585 | - } |
1586 | - |
1587 | - this.set_display_name(); |
1588 | - this.set_truncated_display_name(); |
1589 | - }, |
1590 | - |
1591 | - /** |
1592 | - * Finds the display name using the LP API. |
1593 | - * |
1594 | - * @method get_display_name_from_api |
1595 | - * @param client {Object} An LP API client. |
1596 | - * @param on_done {Object} A function to call when the display name has |
1597 | - * been loaded. |
1598 | - */ |
1599 | - get_display_name_from_api: function(client, on_done) { |
1600 | - var self = this; |
1601 | - var cfg = { |
1602 | - on: { |
1603 | - success: function(person) { |
1604 | - var display_name = person.lookup_value('display_name'); |
1605 | - self.set('display_name', display_name); |
1606 | - self.set_truncated_display_name(); |
1607 | - if (Y.Lang.isFunction(on_done)) { |
1608 | - on_done(); |
1609 | - } |
1610 | - } |
1611 | - } |
1612 | - }; |
1613 | - client.get(this.get('escaped_uri'), cfg); |
1614 | - }, |
1615 | - |
1616 | - /** Finds the display name in a DOM node. |
1617 | - * |
1618 | - * This method can use a DOM node supplied in the config but |
1619 | - * will also try the standard class name for a subscriber's |
1620 | - * node. |
1621 | - * |
1622 | - * @method get_display_name_from_node |
1623 | - */ |
1624 | - get_display_name_from_node: function() { |
1625 | - var user_node; |
1626 | - if (Y.Lang.isValue(this.get('user_node'))) { |
1627 | - user_node = this.get('user_node'); |
1628 | - } else { |
1629 | - user_node = Y.one('.subscriber-' + this.get('name')); |
1630 | - } |
1631 | - |
1632 | - if (Y.Lang.isValue(user_node)) { |
1633 | - this.set('user_node', user_node); |
1634 | - var anchor = this.get('user_node').one('a'); |
1635 | - var display_name = anchor.get('name'); |
1636 | - return display_name; |
1637 | - } else { |
1638 | - return ''; |
1639 | - } |
1640 | - }, |
1641 | - |
1642 | - /** |
1643 | - * A wrapper around the other getDisplayNameXXX functions to |
1644 | - * work out if setting the display_name is possible. Calling |
1645 | - * this is the safest way to ensure display_name is set |
1646 | - * correctly. |
1647 | - * |
1648 | - * @method set_display_name |
1649 | - * @param on_done {Object} A function to call when the display name has |
1650 | - * been loaded. |
1651 | - */ |
1652 | - set_display_name: function(on_done) { |
1653 | - var display_name = this.get_display_name_from_node(); |
1654 | - if (display_name !== '') { |
1655 | - this.set('display_name', display_name); |
1656 | - this.set_truncated_display_name(); |
1657 | - } else { |
1658 | - if (window.LP !== undefined && |
1659 | - window.LP.links.me !== undefined) { |
1660 | - var client = new Y.lp.client.Launchpad(); |
1661 | - this.get_display_name_from_api(client, on_done); |
1662 | - } |
1663 | - } |
1664 | - }, |
1665 | - |
1666 | - /** |
1667 | - * Sets the truncated version of the display_name. |
1668 | - * |
1669 | - * @method set_truncated_display_name |
1670 | - */ |
1671 | - set_truncated_display_name: function() { |
1672 | - var display_name = this.get('display_name'); |
1673 | - if (display_name !== '') { |
1674 | - var truncated_name; |
1675 | - if (display_name.length > 20) { |
1676 | - truncated_name = display_name.substring(0, 17) + '...'; |
1677 | - } else { |
1678 | - truncated_name = display_name; |
1679 | - } |
1680 | - this.set('display_name', truncated_name); |
1681 | - this.set('full_display_name', display_name); |
1682 | - } |
1683 | - } |
1684 | - |
1685 | -}); |
1686 | - |
1687 | -namespace.Subscriber = Subscriber; |
1688 | - |
1689 | - }, "0.1", {"requires": ["base", "node", "lp.client"]}); |
1690 | |
1691 | === modified file 'lib/lp/bugs/javascript/subscribers_list.js' |
1692 | --- lib/lp/bugs/javascript/subscribers_list.js 2011-06-15 11:09:59 +0000 |
1693 | +++ lib/lp/bugs/javascript/subscribers_list.js 2011-06-15 11:10:20 +0000 |
1694 | @@ -27,72 +27,6 @@ |
1695 | |
1696 | var namespace = Y.namespace('lp.bugs.subscribers_list'); |
1697 | |
1698 | -/** |
1699 | - * Reset the subscribers list if needed. |
1700 | - * |
1701 | - * Adds the "None" div to the subscribers list if |
1702 | - * there aren't any subscribers left, and clears up |
1703 | - * the duplicate subscribers list if empty. |
1704 | - * |
1705 | - * @method reset |
1706 | - */ |
1707 | -function reset() { |
1708 | - var subscriber_list = Y.one('#subscribers-links'); |
1709 | - // Assume if subscriber_list has no child divs |
1710 | - // then the list of subscribers is empty. |
1711 | - if (!Y.Lang.isValue(subscriber_list.one('div')) && |
1712 | - !Y.Lang.isValue(Y.one('#none-subscribers'))) { |
1713 | - var none_div = Y.Node.create( |
1714 | - '<div id="none-subscribers">No subscribers.</div>'); |
1715 | - var subscribers = subscriber_list.get('parentNode'); |
1716 | - subscribers.appendChild(none_div); |
1717 | - } |
1718 | - |
1719 | - // Clear the empty duplicate subscribers list if it exists. |
1720 | - var dup_list = Y.one('#subscribers-from-duplicates'); |
1721 | - if (Y.Lang.isValue(dup_list) && |
1722 | - !Y.Lang.isValue(dup_list.one('div'))) { |
1723 | - dup_list.remove(); |
1724 | - } |
1725 | -} |
1726 | -namespace._reset = reset; |
1727 | - |
1728 | -/** |
1729 | - * Remove the user's name from the subscribers list. |
1730 | - * It uses the green-flash animation to indicate successful removal. |
1731 | - * |
1732 | - * @method remove_user_link |
1733 | - * @param subscriber {Subscriber} Subscriber that you want to remove. |
1734 | - * @param is_dupe {Boolean} Uses subscription link from the duplicates |
1735 | - * instead. |
1736 | - */ |
1737 | -function remove_user_link(subscriber, is_dupe) { |
1738 | - var user_node_id; |
1739 | - var user_name = subscriber.get('css_name'); |
1740 | - if (is_dupe === true) { |
1741 | - user_node_id = '#dupe-' + user_name; |
1742 | - } else { |
1743 | - user_node_id = '#direct-' + user_name; |
1744 | - } |
1745 | - var user_node = Y.one(user_node_id); |
1746 | - if (Y.Lang.isValue(user_node)) { |
1747 | - // If there's an icon, we remove it prior to animation |
1748 | - // so animation looks better. |
1749 | - var unsub_icon = user_node.one('#unsubscribe-icon-' + user_name); |
1750 | - if (Y.Lang.isValue(unsub_icon)) { |
1751 | - unsub_icon.remove(); |
1752 | - } |
1753 | - var anim = Y.lazr.anim.green_flash({ node: user_node }); |
1754 | - anim.on('end', function() { |
1755 | - user_node.remove(); |
1756 | - reset(); |
1757 | - }); |
1758 | - anim.run(); |
1759 | - } |
1760 | -} |
1761 | -namespace.remove_user_link = remove_user_link; |
1762 | - |
1763 | - |
1764 | var CSS_CLASSES = { |
1765 | section : 'subscribers-section', |
1766 | list: 'subscribers-list', |
1767 | |
1768 | === removed file 'lib/lp/bugs/javascript/tests/test_subscriber.html' |
1769 | --- lib/lp/bugs/javascript/tests/test_subscriber.html 2011-02-28 00:54:30 +0000 |
1770 | +++ lib/lp/bugs/javascript/tests/test_subscriber.html 1970-01-01 00:00:00 +0000 |
1771 | @@ -1,104 +0,0 @@ |
1772 | -<html> |
1773 | - <head> |
1774 | - <title>Launchpad subscriber</title> |
1775 | - |
1776 | - <!-- YUI 3.0 Setup --> |
1777 | - <script type="text/javascript" |
1778 | - src="../../../../canonical/launchpad/icing/yui/yui/yui.js"></script> |
1779 | - <script type="text/javascript" |
1780 | - src="../../../../canonical/launchpad/icing/lazr/build/lazr.js"></script> |
1781 | - <link rel="stylesheet" |
1782 | - href="../../../../canonical/launchpad/icing/yui/cssreset/reset.css"/> |
1783 | - <link rel="stylesheet" |
1784 | - href="../../../../canonical/launchpad/icing/yui/cssfonts/fonts.css"/> |
1785 | - <link rel="stylesheet" |
1786 | - href="../../../../canonical/launchpad/icing/yui/cssbase/base.css"/> |
1787 | - <link rel="stylesheet" |
1788 | - href="../../../../canonical/launchpad/javascript/test.css" /> |
1789 | - |
1790 | - <script type="text/javascript" |
1791 | - src="../../../app/javascript/client.js"></script> |
1792 | - |
1793 | - <!-- The module under test --> |
1794 | - <script type="text/javascript" |
1795 | - src="../subscriber.js"></script> |
1796 | - |
1797 | - <!-- The test suite --> |
1798 | - <script type="text/javascript" |
1799 | - src="test_subscriber.js"></script> |
1800 | - |
1801 | - <!-- Pretty up the sample html --> |
1802 | - <style type="text/css"> |
1803 | - div#sample {margin:15px; width:200px; border:1px solid #999; padding:10px;} |
1804 | - </style> |
1805 | - </head> |
1806 | - <body class="yui3-skin-sam"> |
1807 | - <!-- Example markup required by test suite --> |
1808 | - <div id="sample"> |
1809 | - <div class="section"> |
1810 | - <div class="subscribed-true dup-subscribed-false"> |
1811 | - <a href="+subscribe" |
1812 | - class="menu-link-subscription sprite remove js-action">Unsubscribe |
1813 | - </a> |
1814 | - </div> |
1815 | - <div> |
1816 | - <a href="+addsubscriber" |
1817 | - class="menu-link-addsubscriber sprite add js-action">Subscribe |
1818 | - someone else |
1819 | - </a> |
1820 | - </div> |
1821 | - </div> |
1822 | - |
1823 | - <div class="section" id="subscribers-direct"> |
1824 | - <h2>Subscribers</h2> |
1825 | - <div id="subscribers-links"> |
1826 | - <div class="subscriber-tester"> |
1827 | - <a href="/~tester" name="JS Test User" |
1828 | - title="Subscribed by Launchpad Janitor"> |
1829 | - <span class="sprite person"></span> |
1830 | - JS Test User |
1831 | - </a> |
1832 | - |
1833 | - <a href="+subscribe" |
1834 | - class="subscribed-true dup-subscribed-false" |
1835 | - id="unsubscribe-tester" title="Unsubscribe JS Test User"> |
1836 | - <img class="unsub-icon" |
1837 | - src="../../../../canonical/launchpad/images/remove.png" |
1838 | - id="unsubscribe-icon-tester"> |
1839 | - </a> |
1840 | - </div> |
1841 | - |
1842 | - <div class="subscriber-some-team"> |
1843 | - <a href="/~some-team" name="Some Team" |
1844 | - title="Subscribed by Launchpad Janitor"> |
1845 | - <span class="sprite team"></span> |
1846 | - Some Team |
1847 | - </a> |
1848 | - |
1849 | - <a href="+subscribe" |
1850 | - class="subscribed-true dup-subscribed-false" |
1851 | - id="unsubscribe-some-team" title="Unsubscribe Some Team"> |
1852 | - <img class="unsub-icon" |
1853 | - src="../../../../canonical/launchpad/images/remove.png" |
1854 | - id="unsubscribe-icon-some-team"> |
1855 | - </a> |
1856 | - </div> |
1857 | - </div> |
1858 | - </div> |
1859 | - |
1860 | - <div id="subscribers-from-duplicates" class="section"> |
1861 | - <h2>From duplicates</h2> |
1862 | - <div class="subscriber-duper" id="dupe-subscriber-duper"> |
1863 | - <a href="/~duper" name="Duper Dude" |
1864 | - title="Subscribed by JS Test User"> |
1865 | - <span class="sprite person"></span> |
1866 | - Duper Dude |
1867 | - </a> |
1868 | - </div> |
1869 | - </div> |
1870 | - </div> |
1871 | - |
1872 | - <!-- The test output --> |
1873 | - <div id="log"></div> |
1874 | - </body> |
1875 | -</html> |
1876 | |
1877 | === removed file 'lib/lp/bugs/javascript/tests/test_subscriber.js' |
1878 | --- lib/lp/bugs/javascript/tests/test_subscriber.js 2011-06-07 16:42:11 +0000 |
1879 | +++ lib/lp/bugs/javascript/tests/test_subscriber.js 1970-01-01 00:00:00 +0000 |
1880 | @@ -1,288 +0,0 @@ |
1881 | -YUI({ |
1882 | - base: '../../../../canonical/launchpad/icing/yui/', |
1883 | - filter: 'raw', combine: false, fetchCSS: false |
1884 | - }).use('test', 'console', 'lp.bugs.subscriber', function(Y) { |
1885 | - |
1886 | -var suite = new Y.Test.Suite("lp.bugs.subscriber Tests"); |
1887 | - |
1888 | -/* |
1889 | - * Test that all the parts of the user name |
1890 | - * are set when given just a URI. |
1891 | - */ |
1892 | -suite.add(new Y.Test.Case({ |
1893 | - name: 'Subscriber From Simple Config', |
1894 | - |
1895 | - setUp: function() { |
1896 | - this.config = { |
1897 | - uri: '/~deryck' |
1898 | - }; |
1899 | - this.subscriber = new Y.lp.bugs.subscriber.Subscriber(this.config); |
1900 | - }, |
1901 | - |
1902 | - tearDown: function() { |
1903 | - delete this.config; |
1904 | - delete this.subscriber; |
1905 | - }, |
1906 | - |
1907 | - test_uri_config: function() { |
1908 | - Y.Assert.areEqual( |
1909 | - '/~deryck', |
1910 | - this.subscriber.get('uri'), |
1911 | - 'User URI should be /~deryck'); |
1912 | - Y.Assert.areEqual( |
1913 | - 'deryck', |
1914 | - this.subscriber.get('name'), |
1915 | - 'User name should be deryck'); |
1916 | - Y.Assert.areEqual( |
1917 | - this.subscriber.get('uri'), |
1918 | - this.subscriber.get('escaped_uri'), |
1919 | - 'The escaped user uri should be the same as the unescaped uri.'); |
1920 | - Y.Assert.isNull( |
1921 | - this.subscriber.get('user_node'), |
1922 | - 'User node should not be known and be null at this point.'); |
1923 | - Y.Assert.areSame( |
1924 | - '', |
1925 | - this.subscriber.get('css_name'), |
1926 | - 'Without subscriber_ids object, css_name should not be set yet.'); |
1927 | - Y.Assert.areSame( |
1928 | - '', |
1929 | - this.subscriber.get('display_name'), |
1930 | - 'Without user node or client, the display name should be empty.'); |
1931 | - } |
1932 | -})); |
1933 | - |
1934 | -/* |
1935 | - * Test that all the parts of the user name |
1936 | - * are set correctly when a name needs escaping. |
1937 | - */ |
1938 | -suite.add(new Y.Test.Case({ |
1939 | - name: 'Escaping Subscriber From Simple Config', |
1940 | - |
1941 | - setUp: function() { |
1942 | - this.config = { |
1943 | - uri: '/~foo+bar', |
1944 | - subscriber_ids: {'foo+bar': 'subscriber-16'} |
1945 | - }; |
1946 | - this.subscriber = new Y.lp.bugs.subscriber.Subscriber(this.config); |
1947 | - }, |
1948 | - |
1949 | - tearDown: function() { |
1950 | - delete this.config; |
1951 | - delete this.subscriber; |
1952 | - }, |
1953 | - |
1954 | - test_escaping_uri_config: function() { |
1955 | - Y.Assert.areEqual( |
1956 | - '/~foo+bar', |
1957 | - this.subscriber.get('uri'), |
1958 | - 'User URI should be /~foo+bar'); |
1959 | - Y.Assert.areEqual( |
1960 | - 'foo+bar', |
1961 | - this.subscriber.get('name'), |
1962 | - 'User name should be foo+bar'); |
1963 | - Y.Assert.areEqual( |
1964 | - '/~foo%2Bbar', |
1965 | - this.subscriber.get('escaped_uri'), |
1966 | - 'Escaped user URI should be /~foo%2Bbar'); |
1967 | - Y.Assert.areEqual( |
1968 | - 'subscriber-16', |
1969 | - this.subscriber.get('css_name'), |
1970 | - 'css_name for user should be subscriber-16'); |
1971 | - } |
1972 | -})); |
1973 | - |
1974 | -/* |
1975 | - * Test that the display_name is correctly worked out |
1976 | - * when passed a Node. |
1977 | - */ |
1978 | -suite.add(new Y.Test.Case({ |
1979 | - name: 'Subscriber Name When Passed Node', |
1980 | - |
1981 | - setUp: function() { |
1982 | - var node = Y.one('.subscriber-tester'); |
1983 | - this.config = { |
1984 | - uri: '/~tester', |
1985 | - user_node: node |
1986 | - }; |
1987 | - this.subscriber = new Y.lp.bugs.subscriber.Subscriber(this.config); |
1988 | - }, |
1989 | - |
1990 | - tearDown: function() { |
1991 | - delete this.config; |
1992 | - delete this.subscriber; |
1993 | - }, |
1994 | - |
1995 | - test_display_name: function() { |
1996 | - Y.Assert.areEqual( |
1997 | - 'JS Test User', |
1998 | - this.subscriber.get('display_name'), |
1999 | - 'The user name should be JS Test User.'); |
2000 | - } |
2001 | -})); |
2002 | - |
2003 | -/* |
2004 | - * Test that display_name is correctly worked out from |
2005 | - * the DOM when not passed a Node. |
2006 | - */ |
2007 | -suite.add(new Y.Test.Case({ |
2008 | - name: 'Subscriber Name When Not Passed Node', |
2009 | - |
2010 | - setUp: function() { |
2011 | - this.config = { |
2012 | - uri: '/~tester' |
2013 | - }; |
2014 | - this.subscriber = new Y.lp.bugs.subscriber.Subscriber(this.config); |
2015 | - }, |
2016 | - |
2017 | - tearDown: function() { |
2018 | - delete this.config; |
2019 | - delete this.subscriber; |
2020 | - }, |
2021 | - |
2022 | - test_display_name_from_dom: function() { |
2023 | - Y.Assert.areEqual( |
2024 | - 'JS Test User', |
2025 | - this.subscriber.get('display_name'), |
2026 | - 'The user name should be JS Test User.'); |
2027 | - } |
2028 | -})); |
2029 | - |
2030 | -/* |
2031 | - * Subscriber class that stubs out API calls. |
2032 | - */ |
2033 | -function APIStubSubscriber(config) { |
2034 | - APIStubSubscriber.superclass.constructor.apply(this, arguments); |
2035 | -} |
2036 | -Y.extend(APIStubSubscriber, Y.lp.bugs.subscriber.Subscriber, { |
2037 | - get_display_name_from_api: function(client) { |
2038 | - this.set('display_name', 'From API'); |
2039 | - this.set_truncated_display_name(); |
2040 | - } |
2041 | -}); |
2042 | - |
2043 | -/* |
2044 | - * Test that the API is consulted when the display_name cannot be |
2045 | - * worked out from a given Node or the DOM. |
2046 | - */ |
2047 | -suite.add(new Y.Test.Case({ |
2048 | - name: 'Subscriber Name From API', |
2049 | - |
2050 | - setUp: function() { |
2051 | - // LP is global. |
2052 | - window.LP = { |
2053 | - cache: {}, |
2054 | - links: {} |
2055 | - }; |
2056 | - Y.lp.client.Launchpad = function() {}; |
2057 | - }, |
2058 | - |
2059 | - tearDown: function() { |
2060 | - delete window.LP; |
2061 | - }, |
2062 | - |
2063 | - test_display_name_from_api: function() { |
2064 | - // The API should be consulted when the user is logged in. Set |
2065 | - // the link to "me" to something other than undefined to |
2066 | - // indicate that there is a logged-in user. |
2067 | - LP.links.me = 'not-undefined'; |
2068 | - var subscriber = new APIStubSubscriber({}); |
2069 | - Y.Assert.areEqual( |
2070 | - 'From API', subscriber.get('display_name'), |
2071 | - 'The display name should be "From API".'); |
2072 | - }, |
2073 | - |
2074 | - test_display_name_when_not_logged_in: function() { |
2075 | - // The API should not be consulted when no user is logged in. |
2076 | - var subscriber = new APIStubSubscriber({}); |
2077 | - Y.Assert.areEqual( |
2078 | - '', subscriber.get('display_name'), |
2079 | - 'The display name should be the empty string.'); |
2080 | - } |
2081 | -})); |
2082 | - |
2083 | -/* |
2084 | - * Test that a Subscription is properly initialized from |
2085 | - * a simple config and that the basic methods work. |
2086 | - */ |
2087 | -suite.add(new Y.Test.Case({ |
2088 | - name: 'Subscription Test', |
2089 | - |
2090 | - setUp: function() { |
2091 | - this.config = { |
2092 | - can_be_unsubscribed: false, |
2093 | - is_direct: true, |
2094 | - is_team: true |
2095 | - }; |
2096 | - this.subscription = new Y.lp.bugs.subscriber.Subscription( |
2097 | - this.config); |
2098 | - }, |
2099 | - |
2100 | - tearDown: function() { |
2101 | - delete this.config; |
2102 | - delete this.subscription; |
2103 | - }, |
2104 | - |
2105 | - test_subscription_config: function() { |
2106 | - Y.Assert.isFalse( |
2107 | - this.subscription.can_be_unsubscribed_by_user(), |
2108 | - 'The user should not be able to unsubscribed this subscription.'); |
2109 | - Y.Assert.isTrue( |
2110 | - this.subscription.is_team(), |
2111 | - 'This subscription should be for a team.'); |
2112 | - Y.Assert.isTrue( |
2113 | - this.subscription.is_direct_subscription(), |
2114 | - 'This should be a direct subscription.'); |
2115 | - // Also check that the defaults were set. |
2116 | - Y.Assert.isNull( |
2117 | - this.subscription.get('person'), |
2118 | - 'The subscription should not be setup for a person.'); |
2119 | - Y.Assert.isNull( |
2120 | - this.subscription.get('subscriber'), |
2121 | - 'The subscription should not be setup for a subscriber.'); |
2122 | - }, |
2123 | - |
2124 | - test_subscription_is_node: function() { |
2125 | - Y.Assert.isFalse( |
2126 | - this.subscription.is_node(), |
2127 | - 'Initially, no node should be supplied to the config.'); |
2128 | - var link = Y.one('.menu-link-subscription'); |
2129 | - this.subscription.set('link', link); |
2130 | - Y.Assert.isTrue( |
2131 | - this.subscription.is_node(), |
2132 | - 'This subscription should have a node for subscription link.'); |
2133 | - }, |
2134 | - |
2135 | - test_already_subscribed: function() { |
2136 | - var person = new Y.lp.bugs.subscriber.Subscriber({uri: '/~tester'}); |
2137 | - this.subscription.set('person', person); |
2138 | - Y.Assert.isTrue( |
2139 | - this.subscription.is_already_subscribed(), |
2140 | - 'The JS Test User should be already subscribed.'); |
2141 | - }, |
2142 | - |
2143 | - test_is_current_user_subscribing: function() { |
2144 | - var person = new Y.lp.bugs.subscriber.Subscriber({uri: '/~tester'}); |
2145 | - this.subscription.set('person', person); |
2146 | - var subscriber = this.subscription.get('person'); |
2147 | - this.subscription.set('subscriber', subscriber); |
2148 | - Y.Assert.isTrue( |
2149 | - this.subscription.is_current_user_subscribing(), |
2150 | - 'Current user should be the same person being subscribed.'); |
2151 | - } |
2152 | -})); |
2153 | - |
2154 | - |
2155 | -var handle_complete = function(data) { |
2156 | - window.status = '::::' + JSON.stringify(data); |
2157 | - }; |
2158 | -Y.Test.Runner.on('complete', handle_complete); |
2159 | -Y.Test.Runner.add(suite); |
2160 | - |
2161 | -var console = new Y.Console({newestOnTop: false}); |
2162 | -console.render('#log'); |
2163 | - |
2164 | -Y.on('domready', function() { |
2165 | - Y.Test.Runner.run(); |
2166 | -}); |
2167 | -}); |
2168 | - |
2169 | |
2170 | === modified file 'lib/lp/bugs/javascript/tests/test_subscribers_list.js' |
2171 | --- lib/lp/bugs/javascript/tests/test_subscribers_list.js 2011-06-15 11:09:59 +0000 |
2172 | +++ lib/lp/bugs/javascript/tests/test_subscribers_list.js 2011-06-15 11:10:20 +0000 |
2173 | @@ -12,324 +12,6 @@ |
2174 | /** |
2175 | * Set-up all the nodes required for subscribers list testing. |
2176 | */ |
2177 | -function setUpOldSubscribersList(root_node, with_dupes) { |
2178 | - // Set-up subscribers list. |
2179 | - var direct_links = Y.Node.create('<div></div>') |
2180 | - .set('id', 'subscribers-links'); |
2181 | - var direct_list = Y.Node.create('<div></div>') |
2182 | - .set('id', 'subscribers-direct'); |
2183 | - direct_list.appendChild(direct_links); |
2184 | - root_node.appendChild(direct_list); |
2185 | - |
2186 | - if (with_dupes === true) { |
2187 | - var dupe_links = Y.Node.create('<div></div>') |
2188 | - .set('id', 'subscribers-from-duplicates'); |
2189 | - var dupe_list = Y.Node.create('<div></div>') |
2190 | - .set('id', 'subscribers-from-duplicates-container'); |
2191 | - dupe_list.appendChild(dupe_links); |
2192 | - root_node.appendChild(dupe_list); |
2193 | - } |
2194 | - return direct_list; |
2195 | -} |
2196 | - |
2197 | -/** |
2198 | - * Test resetting of the subscribers list. |
2199 | - */ |
2200 | -suite.add(new Y.Test.Case({ |
2201 | - name: 'Resetting subscribers list', |
2202 | - |
2203 | - setUp: function() { |
2204 | - this.root = Y.Node.create('<div></div>'); |
2205 | - Y.one('body').appendChild(this.root); |
2206 | - }, |
2207 | - |
2208 | - tearDown: function() { |
2209 | - this.root.remove(); |
2210 | - }, |
2211 | - |
2212 | - test_no_subscribers: function() { |
2213 | - // There are no subscribers left in the subscribers_list |
2214 | - // (iow, subscribers_links is empty). |
2215 | - var subscribers_list = setUpOldSubscribersList(this.root); |
2216 | - |
2217 | - // Resetting the list adds a 'None' div to the |
2218 | - // subscribers_list (and not to the subscriber_links). |
2219 | - module._reset(); |
2220 | - var none_node = subscribers_list.one('#none-subscribers'); |
2221 | - Y.Assert.isNotNull(none_node); |
2222 | - Y.Assert.areEqual('No subscribers.', none_node.get('innerHTML')); |
2223 | - Y.Assert.areEqual(subscribers_list, |
2224 | - none_node.get('parentNode')); |
2225 | - |
2226 | - }, |
2227 | - |
2228 | - test_subscribers: function() { |
2229 | - // When there is at least one subscriber, nothing |
2230 | - // happens when reset() is called. |
2231 | - var subscribers_list = setUpOldSubscribersList(this.root); |
2232 | - var subscribers_links = subscribers_list.one('#subscribers-links'); |
2233 | - subscribers_links.appendChild( |
2234 | - Y.Node.create('<div>Subscriber</div>')); |
2235 | - |
2236 | - // Resetting the list is a no-op. |
2237 | - module._reset(); |
2238 | - var none_node = subscribers_list.one('#none-subscribers'); |
2239 | - Y.Assert.isNull(none_node); |
2240 | - }, |
2241 | - |
2242 | - |
2243 | - test_empty_duplicates: function() { |
2244 | - // There are no subscribers among the duplicate subscribers. |
2245 | - var subscribers_list = setUpOldSubscribersList(this.root, true); |
2246 | - var dupe_subscribers = this.root.one('#subscribers-from-duplicates'); |
2247 | - |
2248 | - // Resetting the list removes the entire duplicate subscribers node. |
2249 | - module._reset(); |
2250 | - Y.Assert.isNull(Y.one('#subscribers-from-duplicates')); |
2251 | - |
2252 | - }, |
2253 | - |
2254 | - test_duplicates: function() { |
2255 | - // There are subscribers among the duplicate subscribers, |
2256 | - // and nothing changes. |
2257 | - var subscribers_list = setUpOldSubscribersList(this.root, true); |
2258 | - var dupe_subscribers = this.root.one('#subscribers-from-duplicates'); |
2259 | - dupe_subscribers.appendChild(Y.Node.create('<div>Subscriber</div>')); |
2260 | - |
2261 | - // Resetting the list does nothing. |
2262 | - module._reset(); |
2263 | - |
2264 | - // The list is still there. |
2265 | - var dupes_node = this.root.one('#subscribers-from-duplicates'); |
2266 | - Y.Assert.isNotNull(dupes_node); |
2267 | - Y.Assert.areEqual(1, dupes_node.all('div').size()); |
2268 | - } |
2269 | -})); |
2270 | - |
2271 | - |
2272 | -/** |
2273 | - * Test removal of a single person link from the subscribers list. |
2274 | - */ |
2275 | -suite.add(new Y.Test.Case({ |
2276 | - name: 'Removal of a subscriber link', |
2277 | - |
2278 | - addSubscriber: function(root_node, subscriber, through_dupe) { |
2279 | - var css_class = subscriber.get('css_name'); |
2280 | - var id; |
2281 | - if (through_dupe === true) { |
2282 | - links = root_node.one('#subscribers-from-duplicates'); |
2283 | - id = 'dupe-' + css_class; |
2284 | - } else { |
2285 | - links = root_node.one('#subscribers-links'); |
2286 | - id = 'direct-' + css_class; |
2287 | - } |
2288 | - return links.appendChild( |
2289 | - Y.Node.create('<div></div>') |
2290 | - .addClass(css_class) |
2291 | - .set('id', id) |
2292 | - .set('text', subscriber.get('uri'))); |
2293 | - }, |
2294 | - |
2295 | - setUp: function() { |
2296 | - this.root = Y.Node.create('<div></div>'); |
2297 | - Y.one('body').appendChild(this.root); |
2298 | - this.subscriber_ids = {}; |
2299 | - }, |
2300 | - |
2301 | - tearDown: function() { |
2302 | - this.root.remove(); |
2303 | - delete this.subscriber_ids; |
2304 | - }, |
2305 | - |
2306 | - test_no_matching_subscriber: function() { |
2307 | - // If there is no matching subscriber, removal silently passes. |
2308 | - |
2309 | - // Set-up subscribers list. |
2310 | - setUpOldSubscribersList(this.root); |
2311 | - |
2312 | - var person = new Y.lp.bugs.subscriber.Subscriber({ |
2313 | - uri: 'myself', |
2314 | - subscriber_ids: this.subscriber_ids |
2315 | - }); |
2316 | - var other_person = new Y.lp.bugs.subscriber.Subscriber({ |
2317 | - uri: 'someone', |
2318 | - subscriber_ids: this.subscriber_ids |
2319 | - }); |
2320 | - this.addSubscriber(this.root, other_person); |
2321 | - |
2322 | - module.remove_user_link(person); |
2323 | - |
2324 | - // `other_person` is not removed. |
2325 | - Y.Assert.isNotNull( |
2326 | - this.root.one('.' + other_person.get('css_name'))); |
2327 | - }, |
2328 | - |
2329 | - test_unsubscribe_icon_removal: function() { |
2330 | - // If there is an unsubscribe icon, it gets removed |
2331 | - // before animation starts. |
2332 | - |
2333 | - // Set-up subscribers list. |
2334 | - setUpOldSubscribersList(this.root); |
2335 | - |
2336 | - var person = new Y.lp.bugs.subscriber.Subscriber({ |
2337 | - uri: 'myself', |
2338 | - subscriber_ids: this.subscriber_ids |
2339 | - }); |
2340 | - this.addSubscriber(this.root, person); |
2341 | - var css_name = person.get('css_name'); |
2342 | - this.root.one('.' + css_name) |
2343 | - .appendChild('<div></div>') |
2344 | - .appendChild('<img></img>') |
2345 | - .set('id', 'unsubscribe-icon-' + css_name); |
2346 | - |
2347 | - module.remove_user_link(person); |
2348 | - |
2349 | - // Unsubscribe icon is removed immediatelly. |
2350 | - Y.Assert.isNull(this.root.one('#unsubscribe-icon-' + css_name)); |
2351 | - }, |
2352 | - |
2353 | - test_direct_subscriber: function() { |
2354 | - // If there is a direct subscriber, removal works fine. |
2355 | - |
2356 | - // Set-up subscribers list. |
2357 | - setUpOldSubscribersList(this.root); |
2358 | - |
2359 | - var person = new Y.lp.bugs.subscriber.Subscriber({ |
2360 | - uri: 'myself', |
2361 | - subscriber_ids: this.subscriber_ids |
2362 | - }); |
2363 | - this.addSubscriber(this.root, person); |
2364 | - |
2365 | - module.remove_user_link(person); |
2366 | - |
2367 | - this.wait(function() { |
2368 | - // There is no subscriber link anymore. |
2369 | - Y.Assert.isNull(this.root.one('.' + person.get('css_name'))); |
2370 | - // And the reset() call adds the "No subscribers" node. |
2371 | - Y.Assert.isNotNull(this.root.one('#none-subscribers')); |
2372 | - }, 1100); |
2373 | - }, |
2374 | - |
2375 | - test_direct_subscriber_remove_dupe: function() { |
2376 | - // If there is only a direct subscriber, attempting removal of |
2377 | - // a duplicate subscription link does nothing. |
2378 | - |
2379 | - // Set-up subscribers list. |
2380 | - setUpOldSubscribersList(this.root); |
2381 | - |
2382 | - var person = new Y.lp.bugs.subscriber.Subscriber({ |
2383 | - uri: 'myself', |
2384 | - subscriber_ids: this.subscriber_ids |
2385 | - }); |
2386 | - this.addSubscriber(this.root, person); |
2387 | - |
2388 | - module.remove_user_link(person, true); |
2389 | - |
2390 | - this.wait(function() { |
2391 | - // There is no subscriber link anymore. |
2392 | - Y.Assert.isNotNull(this.root.one('.' + person.get('css_name'))); |
2393 | - }, 1100); |
2394 | - }, |
2395 | - |
2396 | - test_dupe_subscriber: function() { |
2397 | - // If there is a duplicate subscriber, removal works fine. |
2398 | - |
2399 | - // Set-up subscribers list. |
2400 | - setUpOldSubscribersList(this.root, true); |
2401 | - |
2402 | - var person = new Y.lp.bugs.subscriber.Subscriber({ |
2403 | - uri: 'myself', |
2404 | - subscriber_ids: this.subscriber_ids |
2405 | - }); |
2406 | - this.addSubscriber(this.root, person, true); |
2407 | - |
2408 | - module.remove_user_link(person, true); |
2409 | - |
2410 | - this.wait(function() { |
2411 | - // There is no subscriber link anymore. |
2412 | - Y.Assert.isNull(this.root.one('.' + person.get('css_name'))); |
2413 | - // And the reset() call cleans up the entire duplicate section. |
2414 | - Y.Assert.isNull(this.root.one('#subscribers-from-duplicates')); |
2415 | - }, 1100); |
2416 | - }, |
2417 | - |
2418 | - test_dupe_subscriber_remove_direct: function() { |
2419 | - // If there is a duplicate subscriber, trying to remove the |
2420 | - // direct subscription user link doesn't do anything. |
2421 | - |
2422 | - // Set-up subscribers list. |
2423 | - setUpOldSubscribersList(this.root, true); |
2424 | - |
2425 | - var person = new Y.lp.bugs.subscriber.Subscriber({ |
2426 | - uri: 'myself', |
2427 | - subscriber_ids: this.subscriber_ids |
2428 | - }); |
2429 | - this.addSubscriber(this.root, person, true); |
2430 | - |
2431 | - module.remove_user_link(person); |
2432 | - |
2433 | - this.wait(function() { |
2434 | - // There is no subscriber link anymore. |
2435 | - Y.Assert.isNotNull(this.root.one('.' + person.get('css_name'))); |
2436 | - }, 1100); |
2437 | - }, |
2438 | - |
2439 | - test_direct_and_dupe_subscriber_remove_dupe: function() { |
2440 | - // If there a subscriber is both directly subscribed and |
2441 | - // subscribed through duplicate, removal removes only one link. |
2442 | - |
2443 | - // Set-up subscribers list. |
2444 | - setUpOldSubscribersList(this.root, true); |
2445 | - |
2446 | - var person = new Y.lp.bugs.subscriber.Subscriber({ |
2447 | - uri: 'myself', |
2448 | - subscriber_ids: this.subscriber_ids |
2449 | - }); |
2450 | - this.addSubscriber(this.root, person); |
2451 | - this.addSubscriber(this.root, person, true); |
2452 | - |
2453 | - // Remove the duplicate subscription link. |
2454 | - module.remove_user_link(person, true); |
2455 | - |
2456 | - this.wait(function() { |
2457 | - // Remaining entry is the direct subscription one. |
2458 | - var nodes = this.root.all('.' + person.get('css_name')); |
2459 | - Y.Assert.areEqual(1, nodes.size()); |
2460 | - Y.Assert.areEqual('direct-' + person.get('css_name'), |
2461 | - nodes.pop().get('id')); |
2462 | - }, 1100); |
2463 | - }, |
2464 | - |
2465 | - test_direct_and_dupe_subscriber_remove_direct: function() { |
2466 | - // If there a subscriber is both directly subscribed and |
2467 | - // subscribed through duplicate, removal removes only one link. |
2468 | - |
2469 | - // Set-up subscribers list. |
2470 | - setUpOldSubscribersList(this.root, true); |
2471 | - |
2472 | - var person = new Y.lp.bugs.subscriber.Subscriber({ |
2473 | - uri: 'myself', |
2474 | - subscriber_ids: this.subscriber_ids |
2475 | - }); |
2476 | - this.addSubscriber(this.root, person); |
2477 | - this.addSubscriber(this.root, person, true); |
2478 | - |
2479 | - // Remove the direct subscription link. |
2480 | - module.remove_user_link(person); |
2481 | - |
2482 | - this.wait(function() { |
2483 | - // Remaining entry is the duplicate subscription one. |
2484 | - var nodes = this.root.all('.' + person.get('css_name')); |
2485 | - Y.Assert.areEqual(1, nodes.size()); |
2486 | - Y.Assert.areEqual('dupe-' + person.get('css_name'), |
2487 | - nodes.pop().get('id')); |
2488 | - }, 1100); |
2489 | - } |
2490 | -})); |
2491 | - |
2492 | -/** |
2493 | - * Set-up all the nodes required for subscribers list testing. |
2494 | - */ |
2495 | function setUpSubscribersList(root_node) { |
2496 | // Set-up subscribers list. |
2497 | var node = Y.Node.create('<div />') |
2498 | @@ -341,6 +23,7 @@ |
2499 | return new module.SubscribersList(config); |
2500 | } |
2501 | |
2502 | + |
2503 | /** |
2504 | * Test resetting of the no subscribers indication. |
2505 | */ |
2506 | |
2507 | === modified file 'lib/lp/bugs/stories/bug-privacy/05-set-bug-private-as-admin.txt' |
2508 | --- lib/lp/bugs/stories/bug-privacy/05-set-bug-private-as-admin.txt 2010-12-24 10:37:15 +0000 |
2509 | +++ lib/lp/bugs/stories/bug-privacy/05-set-bug-private-as-admin.txt 2011-06-15 11:10:20 +0000 |
2510 | @@ -9,17 +9,13 @@ |
2511 | explicit. There are two implicit subscribers. |
2512 | |
2513 | >>> from lp.bugs.tests.bug import ( |
2514 | - ... print_also_notified, print_direct_subscribers, |
2515 | - ... print_subscribers_from_duplicates) |
2516 | + ... print_also_notified, print_direct_subscribers) |
2517 | |
2518 | >>> browser.open( |
2519 | - ... "http://launchpad.dev/bugs/2/+bug-portlet-subscribers-content") |
2520 | + ... "http://launchpad.dev/bugs/2/+bug-portlet-subscribers-details") |
2521 | |
2522 | >>> print_direct_subscribers(browser.contents) |
2523 | - Steve Alexander (Subscribed by Launchpad Janitor) |
2524 | - |
2525 | - >>> print_subscribers_from_duplicates(browser.contents) |
2526 | - From duplicates: |
2527 | + Steve Alexander |
2528 | >>> print_also_notified(browser.contents) |
2529 | Also notified: |
2530 | Sample Person |
2531 | @@ -40,14 +36,12 @@ |
2532 | All the implicit subscribers have been made explicit. |
2533 | |
2534 | >>> browser.open( |
2535 | - ... "http://launchpad.dev/bugs/2/+bug-portlet-subscribers-content") |
2536 | + ... "http://launchpad.dev/bugs/2/+bug-portlet-subscribers-details") |
2537 | >>> print_direct_subscribers(browser.contents) |
2538 | - Ubuntu Team (Subscribed by Foo Bar) (Unsubscribe Ubuntu Team) |
2539 | - Sample Person (Subscribed by Foo Bar) |
2540 | - Steve Alexander (Subscribed by Launchpad Janitor) |
2541 | + Sample Person |
2542 | + Steve Alexander |
2543 | + Ubuntu Team (Unsubscribe) |
2544 | |
2545 | - >>> print_subscribers_from_duplicates(browser.contents) |
2546 | - From duplicates: |
2547 | >>> print_also_notified(browser.contents) |
2548 | Also notified: |
2549 | |
2550 | |
2551 | === modified file 'lib/lp/bugs/stories/bugs/bug-add-subscriber.txt' |
2552 | --- lib/lp/bugs/stories/bugs/bug-add-subscriber.txt 2010-04-29 17:49:19 +0000 |
2553 | +++ lib/lp/bugs/stories/bugs/bug-add-subscriber.txt 2011-06-15 11:10:20 +0000 |
2554 | @@ -32,17 +32,14 @@ |
2555 | currently subscribed to the bug: |
2556 | |
2557 | >>> from lp.bugs.tests.bug import ( |
2558 | - ... print_also_notified, print_direct_subscribers, |
2559 | - ... print_subscribers_from_duplicates) |
2560 | + ... print_also_notified, print_direct_subscribers) |
2561 | |
2562 | >>> user_browser.open( |
2563 | ... 'http://bugs.launchpad.dev/bugs/1/' |
2564 | - ... '+bug-portlet-subscribers-content') |
2565 | + ... '+bug-portlet-subscribers-details') |
2566 | >>> print_direct_subscribers(user_browser.contents) |
2567 | - Sample Person (Subscribed by Launchpad Janitor) |
2568 | - Steve Alexander (Subscribed by Launchpad Janitor) |
2569 | - >>> print_subscribers_from_duplicates(user_browser.contents) |
2570 | - From duplicates: |
2571 | + Sample Person |
2572 | + Steve Alexander |
2573 | >>> print_also_notified(user_browser.contents) |
2574 | Also notified: |
2575 | Foo Bar |
2576 | @@ -68,11 +65,11 @@ |
2577 | |
2578 | >>> user_browser.open( |
2579 | ... 'http://bugs.launchpad.dev/bugs/1/' |
2580 | - ... '+bug-portlet-subscribers-content') |
2581 | + ... '+bug-portlet-subscribers-details') |
2582 | >>> print_direct_subscribers(user_browser.contents) |
2583 | - David Allouche (Subscribed by No Privileges Person) |
2584 | - Sample Person (Subscribed by Launchpad Janitor) |
2585 | - Steve Alexander (Subscribed by Launchpad Janitor) |
2586 | + David Allouche |
2587 | + Sample Person |
2588 | + Steve Alexander |
2589 | |
2590 | David got a notification, saying that he was subscribed to the bug. |
2591 | |
2592 | @@ -111,12 +108,12 @@ |
2593 | |
2594 | >>> user_browser.open( |
2595 | ... 'http://bugs.launchpad.dev/bugs/1/' |
2596 | - ... '+bug-portlet-subscribers-content') |
2597 | + ... '+bug-portlet-subscribers-details') |
2598 | >>> print_direct_subscribers(user_browser.contents) |
2599 | - David Allouche (Subscribed by No Privileges Person) |
2600 | - Landscape Developers (Subscribed by No Privileges Person) |
2601 | - Sample Person (Subscribed by Launchpad Janitor) |
2602 | - Steve Alexander (Subscribed by Launchpad Janitor) |
2603 | + David Allouche |
2604 | + Landscape Developers |
2605 | + Sample Person |
2606 | + Steve Alexander |
2607 | |
2608 | |
2609 | Subscription of private teams |
2610 | @@ -145,37 +142,37 @@ |
2611 | 'http://bugs.launchpad.dev/firefox/+bug/1' |
2612 | >>> foobar_browser.open( |
2613 | ... 'http://bugs.launchpad.dev/bugs/1/' |
2614 | - ... '+bug-portlet-subscribers-content') |
2615 | + ... '+bug-portlet-subscribers-details') |
2616 | >>> print_direct_subscribers(foobar_browser.contents) |
2617 | - Private Team (Subscribed by Foo Bar) (Unsubscribe Private Team) |
2618 | - David Allouche (Subscribed by No Privileges Person) |
2619 | - Landscape Developers (Subscribed by No Privileges Person) |
2620 | - Sample Person (Subscribed by Launchpad Janitor) |
2621 | - Steve Alexander (Subscribed by Launchpad Janitor) |
2622 | + David Allouche |
2623 | + Landscape Developers |
2624 | + Private Team (Unsubscribe) |
2625 | + Sample Person |
2626 | + Steve Alexander |
2627 | |
2628 | Someone not in the team will not see the private team in the |
2629 | subscribers list. |
2630 | |
2631 | >>> user_browser.open( |
2632 | ... 'http://bugs.launchpad.dev/bugs/1/' |
2633 | - ... '+bug-portlet-subscribers-content') |
2634 | + ... '+bug-portlet-subscribers-details') |
2635 | >>> print_direct_subscribers(user_browser.contents) |
2636 | - David Allouche (Subscribed by No Privileges Person) |
2637 | - Landscape Developers (Subscribed by No Privileges Person) |
2638 | - Sample Person (Subscribed by Launchpad Janitor) |
2639 | - Steve Alexander (Subscribed by Launchpad Janitor) |
2640 | + David Allouche |
2641 | + Landscape Developers |
2642 | + Sample Person |
2643 | + Steve Alexander |
2644 | |
2645 | An anonymous user will not be shown the private team in the subscribers |
2646 | list. |
2647 | |
2648 | >>> anon_browser.open( |
2649 | ... 'http://bugs.launchpad.dev/bugs/1/' |
2650 | - ... '+bug-portlet-subscribers-content') |
2651 | + ... '+bug-portlet-subscribers-details') |
2652 | >>> print_direct_subscribers(anon_browser.contents) |
2653 | - David Allouche (Subscribed by No Privileges Person) |
2654 | - Landscape Developers (Subscribed by No Privileges Person) |
2655 | - Sample Person (Subscribed by Launchpad Janitor) |
2656 | - Steve Alexander (Subscribed by Launchpad Janitor) |
2657 | + David Allouche |
2658 | + Landscape Developers |
2659 | + Sample Person |
2660 | + Steve Alexander |
2661 | |
2662 | The activity log also does not show subscribed private teams. If we |
2663 | look at the activity log for the bug used above, we'll see that David Allouche |
2664 | |
2665 | === modified file 'lib/lp/bugs/stories/bugs/xx-bug-personal-subscriptions.txt' |
2666 | --- lib/lp/bugs/stories/bugs/xx-bug-personal-subscriptions.txt 2011-06-15 11:09:59 +0000 |
2667 | +++ lib/lp/bugs/stories/bugs/xx-bug-personal-subscriptions.txt 2011-06-15 11:10:20 +0000 |
2668 | @@ -5,11 +5,11 @@ |
2669 | in the actions portlet. |
2670 | |
2671 | >>> from lp.bugs.tests.bug import ( |
2672 | - ... print_direct_subscribers, print_also_notified, |
2673 | - ... print_subscribers_from_duplicates) |
2674 | + ... print_direct_subscribers, print_also_notified) |
2675 | |
2676 | >>> browser = setupBrowser(auth='Basic foo.bar@canonical.com:test') |
2677 | - >>> browser.open('http://bugs.launchpad.dev/firefox/+bug/1') |
2678 | + >>> bug_1 = 'http://bugs.launchpad.dev/bugs/1/' |
2679 | + >>> browser.open(bug_1) |
2680 | >>> browser.url |
2681 | 'http://bugs.launchpad.dev/firefox/+bug/1' |
2682 | |
2683 | @@ -37,21 +37,7 @@ |
2684 | ... print tag.renderContents() |
2685 | You have subscribed to this bug report. |
2686 | |
2687 | -There's also now a link to unsubscribe the user next to the name. It's a |
2688 | -relative URL to +subscribe, so it will only work when the portlet is |
2689 | -in the context of the bug page. |
2690 | - |
2691 | - >>> bug_1 = 'http://bugs.launchpad.dev/bugs/1/' |
2692 | - >>> browser.open(bug_1 + '+bug-portlet-subscribers-content') |
2693 | - >>> print_direct_subscribers(browser.contents) |
2694 | - Foo Bar (Self-subscribed) (Unsubscribe Foo Bar) |
2695 | - Sample Person (Subscribed by Launchpad Janitor) |
2696 | - Steve Alexander (Subscribed by Launchpad Janitor) |
2697 | - |
2698 | - >>> browser.open(bug_1 + '+bug-portlet-subscribers-content') |
2699 | - >>> link = browser.getLink(id='unsubscribe-subscriber-16') |
2700 | - >>> print link.mech_link.url |
2701 | - +subscribe |
2702 | +Reloading the page shows the link for editing one's subscription. |
2703 | |
2704 | >>> browser.open(bug_1) |
2705 | >>> browser.getLink( |
2706 | @@ -92,21 +78,15 @@ |
2707 | |
2708 | There's an unsubscribe link next to the team name. |
2709 | |
2710 | - >>> browser.open(bug_1 + '+bug-portlet-subscribers-content') |
2711 | + >>> browser.open(bug_1 + '+bug-portlet-subscribers-details') |
2712 | >>> print_direct_subscribers(browser.contents) |
2713 | - Launchpad Developers (Subscribed by Foo Bar) (Unsubscribe Launchpad |
2714 | - Developers) |
2715 | - Sample Person (Subscribed by Launchpad Janitor) |
2716 | - Steve Alexander (Subscribed by Launchpad Janitor) |
2717 | + Launchpad Developers (Unsubscribe) |
2718 | + Sample Person |
2719 | + Steve Alexander |
2720 | |
2721 | -Clicking either the subscribe link (for subscribing the user to the bug) |
2722 | -or the unsubscribe link for the team gives us the option of both |
2723 | +Clicking either the subscribe link gives us the option of both |
2724 | subscribing Foo Bar, and unsubscribing the Launchpad team. |
2725 | |
2726 | - >>> browser.open(bug_1 + '+bug-portlet-subscribers-content') |
2727 | - >>> browser.getLink(id='unsubscribe-subscriber-57').mech_link.url |
2728 | - '+subscribe' |
2729 | - |
2730 | >>> browser.open(bug_1) |
2731 | >>> browser.getLink( |
2732 | ... "not directly subscribed to this bug's notifications.").click() |
2733 | @@ -130,10 +110,10 @@ |
2734 | ... print tag.renderContents() |
2735 | Launchpad Developers has been unsubscribed from bug 1. |
2736 | |
2737 | - >>> browser.open(bug_1 + '+bug-portlet-subscribers-content') |
2738 | + >>> browser.open(bug_1 + '+bug-portlet-subscribers-details') |
2739 | >>> print_direct_subscribers(browser.contents) |
2740 | - Sample Person (Subscribed by Launchpad Janitor) |
2741 | - Steve Alexander (Subscribed by Launchpad Janitor) |
2742 | + Sample Person |
2743 | + Steve Alexander |
2744 | |
2745 | On the subscribe page there's a Cancel link as well, that will return |
2746 | the browser to the bug page. |
2747 | @@ -155,25 +135,19 @@ |
2748 | |
2749 | >>> len(find_tags_by_class(browser.contents, 'informational message')) |
2750 | 0 |
2751 | - >>> browser.open(bug_1 + '+bug-portlet-subscribers-content') |
2752 | - >>> print_direct_subscribers(browser.contents) |
2753 | - Sample Person (Subscribed by Launchpad Janitor) |
2754 | - Steve Alexander (Subscribed by Launchpad Janitor) |
2755 | |
2756 | -Subscribers which the current user may unsubscribe (the current user and teams |
2757 | -they are a member of) display first in the list, before all other |
2758 | -subscriptions. |
2759 | +Subscribers which the current user may unsubscribe have an indication |
2760 | +that shows the unsubscribe link in the list of subscribers. |
2761 | |
2762 | >>> browser.open( |
2763 | ... 'http://bugs.launchpad.dev/firefox/+bug/1/+addsubscriber') |
2764 | >>> browser.getControl('Person').value = 'testing-spanish-team' |
2765 | >>> browser.getControl('Subscribe user').click() |
2766 | - >>> browser.open(bug_1 + '+bug-portlet-subscribers-content') |
2767 | + >>> browser.open(bug_1 + '+bug-portlet-subscribers-details') |
2768 | >>> print_direct_subscribers(browser.contents) |
2769 | - testing Spanish team (Subscribed by Foo Bar) |
2770 | - (Unsubscribe testing Spanish team) |
2771 | - Sample Person (Subscribed by Launchpad Janitor) |
2772 | - Steve Alexander (Subscribed by Launchpad Janitor) |
2773 | + Sample Person |
2774 | + Steve Alexander |
2775 | + testing Spanish team (Unsubscribe) |
2776 | |
2777 | Subscriptions and Duplicate Bugs |
2778 | -------------------------------- |
2779 | @@ -188,17 +162,12 @@ |
2780 | >>> stevea_browser = setupBrowser( |
2781 | ... auth="Basic steve.alexander@ubuntulinux.com:test") |
2782 | >>> bug_3 = 'http://launchpad.dev/bugs/3/' |
2783 | - >>> stevea_browser.open(bug_3 + "+bug-portlet-dupe-subscribers-content") |
2784 | - >>> print_subscribers_from_duplicates(stevea_browser.contents) |
2785 | - From duplicates: |
2786 | - |
2787 | - >>> stevea_browser.open(bug_3 + "+bug-portlet-subscribers-content") |
2788 | + >>> stevea_browser.open(bug_3 + "+bug-portlet-subscribers-details") |
2789 | >>> print_also_notified(stevea_browser.contents) |
2790 | Also notified: |
2791 | |
2792 | But if bug #2, a bug that Steve is directly subscribed to, is marked as |
2793 | -a dupe of bug #3, then Steve gets indirectly subscribed to bug #3, and |
2794 | -is presented with the Unsubscribe link instead. |
2795 | +a dupe of bug #3, then Steve gets indirectly subscribed to bug #3. |
2796 | |
2797 | >>> bug_2 = 'http://launchpad.dev/tomcat/+bug/2/' |
2798 | >>> stevea_browser.open(bug_2 + "+duplicate") |
2799 | @@ -206,14 +175,18 @@ |
2800 | >>> stevea_browser.getControl("Duplicate Of").value = "3" |
2801 | >>> stevea_browser.getControl("Change").click() |
2802 | |
2803 | - >>> stevea_browser.open(bug_3 + "+bug-portlet-dupe-subscribers-content") |
2804 | - >>> print_subscribers_from_duplicates(stevea_browser.contents) |
2805 | - From duplicates: |
2806 | - Steve Alexander (Subscribed to bug 2 by Launchpad Janitor) |
2807 | - (Unsubscribe Steve Alexander) |
2808 | - |
2809 | - >>> stevea_browser.getLink(id='unsubscribe-subscriber-11').mech_link.url |
2810 | - '+subscribe' |
2811 | +A logged-in user doesn't show up in the subscribers list. |
2812 | + |
2813 | + >>> stevea_browser.open(bug_3 + "+bug-portlet-subscribers-details") |
2814 | + >>> print_also_notified(stevea_browser.contents) |
2815 | + Also notified: |
2816 | + |
2817 | +However, an anonymous user will see them listed. |
2818 | + |
2819 | + >>> anon_browser.open(bug_3 + "+bug-portlet-subscribers-details") |
2820 | + >>> print_also_notified(anon_browser.contents) |
2821 | + Also notified: |
2822 | + Steve Alexander |
2823 | |
2824 | When he chooses to unsubscribe, he will be unsubscribed from bug #2, the |
2825 | dupe of bug #3, so he'll no longer get mail from bug #3. |
2826 | @@ -223,24 +196,16 @@ |
2827 | ... "not directly subscribed to this bug's notifications.").click() |
2828 | >>> stevea_browser.getControl("Continue").click() |
2829 | |
2830 | -# XXX: Brad Bollenbach 2006-09-27 bug=62634: Printing the tag here, |
2831 | -# instead of tag.string. |
2832 | - |
2833 | >>> for tag in find_tags_by_class( |
2834 | ... stevea_browser.contents, 'informational message'): |
2835 | ... print tag.renderContents() |
2836 | You have been unsubscribed from bug 3 and 1 duplicate (<a...#2</a>)... |
2837 | |
2838 | -(Except for Mark, who has a structural subscription to the target, |
2839 | -there are no longer any indirect subscribers, because Steve was |
2840 | +There are no longer any indirect subscribers, because Steve was |
2841 | unsubscribed from the dupes and thus is no longer indirectly subscribed |
2842 | -to bug #3.) |
2843 | - |
2844 | - >>> stevea_browser.open(bug_3 + "+bug-portlet-dupe-subscribers-content") |
2845 | - >>> print_subscribers_from_duplicates(stevea_browser.contents) |
2846 | - From duplicates: |
2847 | - |
2848 | - >>> stevea_browser.open(bug_3 + "+bug-portlet-subscribers-content") |
2849 | +to bug #3. |
2850 | + |
2851 | + >>> stevea_browser.open(bug_3 + "+bug-portlet-subscribers-details") |
2852 | >>> print_also_notified(stevea_browser.contents) |
2853 | Also notified: |
2854 | |
2855 | @@ -260,16 +225,11 @@ |
2856 | >>> stevea_browser.getControl('Person').value = 'stevea' |
2857 | >>> stevea_browser.getControl('Subscribe user').click() |
2858 | |
2859 | - >>> stevea_browser.open(bug_3 + "+bug-portlet-dupe-subscribers-content") |
2860 | - >>> print_subscribers_from_duplicates(stevea_browser.contents) |
2861 | - From duplicates: |
2862 | - Sample Person (Subscribed ...) |
2863 | - Steve Alexander (Subscribed ...) (Unsubscribe Steve Alexander) |
2864 | - testing Spanish team (Subscribed to bug 1 by Foo Bar) |
2865 | - |
2866 | - >>> stevea_browser.open(bug_3 + "+bug-portlet-subscribers-content") |
2867 | + >>> stevea_browser.open(bug_3 + "+bug-portlet-subscribers-details") |
2868 | >>> print_also_notified(stevea_browser.contents) |
2869 | Also notified: |
2870 | + Sample Person |
2871 | + testing Spanish team |
2872 | |
2873 | >>> stevea_browser.open("http://launchpad.dev/bugs/3") |
2874 | >>> stevea_browser.getLink( |
2875 | @@ -289,11 +249,9 @@ |
2876 | >>> stevea_browser.getControl("Duplicate Of").value = "" |
2877 | >>> stevea_browser.getControl("Change").click() |
2878 | |
2879 | -This unsubscribe behaviour is team-aware too, so you can unsubscribe |
2880 | -your teams from a bug, even when the team's subscription comes from a |
2881 | -duplicate. For example, let's login as Foo Bar, subscribe ubuntu-team to |
2882 | -bug #2, and notice how bug #3's indirect subscriptions are update to |
2883 | -include that team. |
2884 | +This unsubscribe behaviour is team-aware: if we login as Foo Bar, |
2885 | +subscribe ubuntu-team to bug #2, bug #3's indirect subscriptions |
2886 | +are updated to include that team. |
2887 | |
2888 | >>> foobar_browser = setupBrowser(auth="Basic foo.bar@canonical.com:test") |
2889 | >>> foobar_browser.open("http://launchpad.dev/bugs/2") |
2890 | @@ -301,17 +259,10 @@ |
2891 | >>> foobar_browser.getControl("Person").value = "ubuntu-team" |
2892 | >>> foobar_browser.getControl("Subscribe user").click() |
2893 | |
2894 | - >>> foobar_browser.open(bug_3 + "+bug-portlet-dupe-subscribers-content") |
2895 | - |
2896 | - >>> print_subscribers_from_duplicates(foobar_browser.contents) |
2897 | - From duplicates: |
2898 | - Ubuntu Team (Subscribed to bug 2 by Foo Bar) (Unsubscribe Ubuntu Team) |
2899 | - |
2900 | - >>> foobar_browser.open( |
2901 | - ... "http://launchpad.dev/bugs/3/+bug-portlet-subscribers-content") |
2902 | - |
2903 | + >>> foobar_browser.open(bug_3 + "+bug-portlet-subscribers-details") |
2904 | >>> print_also_notified(foobar_browser.contents) |
2905 | Also notified: |
2906 | + Ubuntu Team |
2907 | |
2908 | The subscribe link for Foo Bar still says "Subscribe", because |
2909 | Foo Bar can subscribe himself directly to this bug. For unsubscribing |
2910 | @@ -337,38 +288,7 @@ |
2911 | |
2912 | (ubuntu-team is no longer an indirect subscriber.) |
2913 | |
2914 | - >>> foobar_browser.open(bug_3 + "+bug-portlet-dupe-subscribers-content") |
2915 | - >>> print_subscribers_from_duplicates(foobar_browser.contents) |
2916 | - From duplicates: |
2917 | - |
2918 | >>> foobar_browser.open( |
2919 | - ... "http://launchpad.dev/bugs/3/+bug-portlet-subscribers-content") |
2920 | + ... "http://launchpad.dev/bugs/3/+bug-portlet-subscribers-details") |
2921 | >>> print_also_notified(foobar_browser.contents) |
2922 | Also notified: |
2923 | - |
2924 | - |
2925 | -Displaying subscribers |
2926 | ----------------------- |
2927 | - |
2928 | -The display names of subscribers are escaped in the subscribers list, they are |
2929 | -also trimmed to 20 characters, so that they fit alongside the unsubscribe |
2930 | -icon. |
2931 | - |
2932 | - >>> login(ANONYMOUS) |
2933 | - >>> abuser = factory.makePerson( |
2934 | - ... name='abuser', |
2935 | - ... displayname='<script>javascript:alert("YO")</script>') |
2936 | - >>> logout() |
2937 | - >>> browser.open( |
2938 | - ... 'http://bugs.launchpad.dev/firefox/+bug/1/+addsubscriber') |
2939 | - >>> browser.getControl('Person').value = 'abuser' |
2940 | - >>> browser.getControl('Subscribe user').click() |
2941 | - >>> bug_1 = 'http://bugs.launchpad.dev/bugs/1/' |
2942 | - >>> browser.open(bug_1 + '+bug-portlet-subscribers-content') |
2943 | - >>> subscriber_list = find_tag_by_id( |
2944 | - ... browser.contents, 'subscribers-direct') |
2945 | - >>> for subscriber in subscriber_list.findAll('div'): |
2946 | - ... if '~abuser' in subscriber.a['href']: |
2947 | - ... print subscriber.a.contents[2].strip() |
2948 | - <script>javascrip... |
2949 | - |
2950 | |
2951 | === removed file 'lib/lp/bugs/templates/bug-portlet-dupe-subscribers-content.pt' |
2952 | --- lib/lp/bugs/templates/bug-portlet-dupe-subscribers-content.pt 2011-02-03 05:14:54 +0000 |
2953 | +++ lib/lp/bugs/templates/bug-portlet-dupe-subscribers-content.pt 1970-01-01 00:00:00 +0000 |
2954 | @@ -1,49 +0,0 @@ |
2955 | -<div |
2956 | - tal:omit-tag="" |
2957 | - xmlns:tal="http://xml.zope.org/namespaces/tal" |
2958 | - xmlns:metal="http://xml.zope.org/namespaces/metal" |
2959 | - xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
2960 | - tal:define=" |
2961 | - bug context/bug|context; |
2962 | - from_dupes_subscriptions view/sorted_subscriptions_from_dupes; |
2963 | - "> |
2964 | - <div |
2965 | - tal:condition="from_dupes_subscriptions" |
2966 | - id="subscribers-from-duplicates" |
2967 | - class="section" |
2968 | - > |
2969 | - <h2>From duplicates</h2> |
2970 | - <div |
2971 | - tal:repeat="subscription from_dupes_subscriptions" |
2972 | - tal:attributes=" |
2973 | - class subscription/css_name; |
2974 | - id string:dupe-${subscription/css_name}; |
2975 | - " |
2976 | - > |
2977 | - |
2978 | - <a |
2979 | - tal:condition="subscription/person/name|nothing" |
2980 | - tal:attributes=" |
2981 | - href subscription/person/fmt:url; |
2982 | - title subscription/display_duplicate_subscribed_by; |
2983 | - name subscription/person/fmt:displayname |
2984 | - " |
2985 | - > |
2986 | - <tal:block replace="structure subscription/person/fmt:icon" /> |
2987 | - <tal:block replace="subscription/person/fmt:displayname/fmt:shorten/20" /> |
2988 | - </a> |
2989 | - |
2990 | - <a tal:condition="python: subscription.canBeUnsubscribedByUser(view.user)" |
2991 | - href="+subscribe" |
2992 | - tal:attributes=" |
2993 | - title string:Unsubscribe ${subscription/person/fmt:displayname}; |
2994 | - id string:unsubscribe-${subscription/css_name}; |
2995 | - class python: view.getSubscriptionClassForUser(subscription.person) |
2996 | - " |
2997 | - > |
2998 | - <img class="unsub-icon" src="/@@/remove" alt="Remove" |
2999 | - tal:attributes="id string:unsubscribe-icon-${subscription/css_name}" /> |
3000 | - </a> |
3001 | - </div> |
3002 | - </div> |
3003 | -</div> |
3004 | |
3005 | === removed file 'lib/lp/bugs/templates/bug-portlet-subscribers-content.pt' |
3006 | --- lib/lp/bugs/templates/bug-portlet-subscribers-content.pt 2011-05-20 21:09:40 +0000 |
3007 | +++ lib/lp/bugs/templates/bug-portlet-subscribers-content.pt 1970-01-01 00:00:00 +0000 |
3008 | @@ -1,74 +0,0 @@ |
3009 | -<div |
3010 | - tal:omit-tag="" |
3011 | - xmlns:tal="http://xml.zope.org/namespaces/tal" |
3012 | - xmlns:metal="http://xml.zope.org/namespaces/metal" |
3013 | - xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
3014 | - tal:define=" |
3015 | - bug context/bug|context; |
3016 | - direct_subscriptions view/sorted_direct_subscriptions; |
3017 | - "> |
3018 | - <div class="section" id="subscribers-direct"> |
3019 | - <h3>Directly subscribed</h3> |
3020 | - <div id="subscribers-links"> |
3021 | - <div |
3022 | - tal:condition="direct_subscriptions" |
3023 | - tal:repeat="subscription direct_subscriptions" |
3024 | - tal:attributes=" |
3025 | - class subscription/css_name; |
3026 | - id string:direct-${subscription/css_name}"> |
3027 | - <metal:subscriber metal:define-macro="subscriber-row"> |
3028 | - |
3029 | - <a |
3030 | - tal:condition="subscription/person/name|nothing" |
3031 | - tal:attributes=" |
3032 | - href subscription/person/fmt:url; |
3033 | - title subscription/display_subscribed_by; |
3034 | - name subscription/person/fmt:displayname |
3035 | - " |
3036 | - > |
3037 | - <tal:block replace="structure subscription/person/fmt:icon" /> |
3038 | - <tal:block replace="subscription/person/fmt:displayname/fmt:shorten/20" /> |
3039 | - </a> |
3040 | - |
3041 | - <a tal:condition="python: subscription.canBeUnsubscribedByUser(view.user)" |
3042 | - href="+subscribe" |
3043 | - tal:attributes=" |
3044 | - title string:Unsubscribe ${subscription/person/fmt:displayname}; |
3045 | - id string:unsubscribe-${subscription/css_name}; |
3046 | - class python: view.getSubscriptionClassForUser(subscription.person) |
3047 | - " |
3048 | - > |
3049 | - <img class="unsub-icon" src="/@@/remove" alt="Remove" |
3050 | - tal:attributes="id string:unsubscribe-icon-${subscription/css_name}" /> |
3051 | - </a> |
3052 | - |
3053 | - </metal:subscriber> |
3054 | - </div> |
3055 | - </div> |
3056 | - <div id="none-subscribers" |
3057 | - tal:condition="not:direct_subscriptions"> |
3058 | - No subscribers.</div> |
3059 | - </div> |
3060 | - <div id="subscribers-from-duplicates-container"> |
3061 | - <a id="subscribers-from-dupes-content-link" |
3062 | - tal:attributes="href bug/fmt:url/+bug-portlet-dupe-subscribers-content"></a> |
3063 | - <div id="subscribers-portlet-dupe-spinner" |
3064 | - style="text-align: center; display: none"> |
3065 | - <img src="/@@/spinner" /> |
3066 | - </div> |
3067 | - </div> |
3068 | - <tal:also_notified condition="not: request/features/malone.advanced-subscriptions.enabled"> |
3069 | - <div |
3070 | - tal:define="also_notified_subscribers bug/getAlsoNotifiedSubscribers" |
3071 | - tal:condition="also_notified_subscribers" |
3072 | - id="subscribers-indirect" |
3073 | - class="Section" |
3074 | - > |
3075 | - <h3>Also notified</h3> |
3076 | - <div |
3077 | - tal:repeat="subscriber also_notified_subscribers" |
3078 | - tal:content="structure subscriber/fmt:link" |
3079 | - /> |
3080 | - </div> |
3081 | - </tal:also_notified> |
3082 | -</div> |
3083 | |
3084 | === modified file 'lib/lp/bugs/templates/bug-portlet-subscribers.pt' |
3085 | --- lib/lp/bugs/templates/bug-portlet-subscribers.pt 2011-06-15 11:09:59 +0000 |
3086 | +++ lib/lp/bugs/templates/bug-portlet-subscribers.pt 2011-06-15 11:10:20 +0000 |
3087 | @@ -11,14 +11,4 @@ |
3088 | <div tal:content="structure context_menu/addsubscriber/render" /> |
3089 | </div> |
3090 | <div id="other-bug-subscribers"></div> |
3091 | - <a id="subscribers-ids-link" |
3092 | - tal:define="bug context/bug|context" |
3093 | - tal:attributes="href bug/fmt:url/+bug-portlet-subscribers-ids"></a> |
3094 | - <a id="subscribers-content-link" |
3095 | - tal:define="bug context/bug|context" |
3096 | - tal:attributes="href bug/fmt:url/+bug-portlet-subscribers-content"></a> |
3097 | - <div id="subscribers-portlet-spinner" |
3098 | - style="text-align: center; display: none"> |
3099 | - <img src="/@@/spinner" /> |
3100 | - </div> |
3101 | </div> |
3102 | |
3103 | === modified file 'lib/lp/bugs/templates/bug-portlet-subscription.pt' |
3104 | --- lib/lp/bugs/templates/bug-portlet-subscription.pt 2011-06-15 11:09:59 +0000 |
3105 | +++ lib/lp/bugs/templates/bug-portlet-subscription.pt 2011-06-15 11:10:20 +0000 |
3106 | @@ -51,7 +51,6 @@ |
3107 | </div> |
3108 | <script type="text/javascript"> |
3109 | LPS.use('io-base', 'node', |
3110 | - 'lp.bugs.bugtask_index.portlets', |
3111 | 'lp.bugs.bugtask_index.portlets.subscription', function(Y) { |
3112 | // Must be done inline here to ensure the load event fires. |
3113 | // This is a work around for a YUI3 issue with event handling. |
3114 | @@ -64,8 +63,6 @@ |
3115 | |
3116 | Y.on('domready', function() { |
3117 | Y.lp.bugs.bugtask_index.portlets.subscription.initialize(); |
3118 | - Y.lp.bugs.bugtask_index.portlets.load_subscribers_portlet( |
3119 | - subscription_link, subscription_link_handler); |
3120 | }); |
3121 | }); |
3122 | </script> |
3123 | |
3124 | === modified file 'lib/lp/bugs/templates/bugtask-index.pt' |
3125 | --- lib/lp/bugs/templates/bugtask-index.pt 2011-06-15 11:09:59 +0000 |
3126 | +++ lib/lp/bugs/templates/bugtask-index.pt 2011-06-15 11:10:20 +0000 |
3127 | @@ -10,17 +10,6 @@ |
3128 | <script type='text/javascript' tal:content="string:var yui_base='${yui}';" /> |
3129 | <script type='text/javascript' id='available-official-tags-js' |
3130 | tal:content="view/available_official_tags_js" /> |
3131 | - <tal:subscriptions-js |
3132 | - define="enabled features/malone.advanced-subscriptions.enabled"> |
3133 | - <script type="text/javascript" id="use-advanced-subscriptions" |
3134 | - tal:condition="enabled"> |
3135 | - use_advanced_subscriptions = true; |
3136 | - </script> |
3137 | - <script type="text/javascript" id="use-advanced-subscriptions" |
3138 | - tal:condition="not:enabled"> |
3139 | - use_advanced_subscriptions = false; |
3140 | - </script> |
3141 | - </tal:subscriptions-js> |
3142 | <tal:if condition="context/bug/private"> |
3143 | <script type="text/javascript"> |
3144 | var bug_private = true; |
3145 | @@ -43,11 +32,9 @@ |
3146 | </tal:if> |
3147 | <script type="text/javascript"> |
3148 | LPS.use('base', 'node', 'oop', 'event', 'lp.bugs.bugtask_index', |
3149 | - 'lp.bugs.bugtask_index.portlets', 'lp.bugs.subscribers_list', |
3150 | + 'lp.bugs.subscribers_list', |
3151 | 'lp.code.branchmergeproposal.diff', 'lp.comments.hide', |
3152 | function(Y) { |
3153 | - Y.lp.bugs.bugtask_index.portlets.use_advanced_subscriptions = |
3154 | - use_advanced_subscriptions; |
3155 | Y.lp.bugs.bugtask_index.setup_bugtask_index(); |
3156 | Y.on('load', function(e) { |
3157 | Y.lp.code.branchmergeproposal.diff.connect_diff_links(); |
3158 | |
3159 | === modified file 'lib/lp/bugs/tests/bug.py' |
3160 | --- lib/lp/bugs/tests/bug.py 2011-05-27 21:12:25 +0000 |
3161 | +++ lib/lp/bugs/tests/bug.py 2011-06-15 11:10:20 +0000 |
3162 | @@ -43,35 +43,38 @@ |
3163 | |
3164 | def print_direct_subscribers(bug_page): |
3165 | """Print the direct subscribers listed in a portlet.""" |
3166 | - print_subscribers(bug_page, 'subscribers-direct') |
3167 | + print_subscribers(bug_page, 'Maybe', reverse=True) |
3168 | |
3169 | |
3170 | def print_also_notified(bug_page): |
3171 | - """Print the structural subscribers listed in a portlet.""" |
3172 | + """Print the structural and duplicate subscribers listed in a portlet.""" |
3173 | print 'Also notified:' |
3174 | - print_subscribers(bug_page, 'subscribers-indirect') |
3175 | - |
3176 | - |
3177 | -def print_subscribers_from_duplicates(bug_page): |
3178 | - """Print the subscribers from duplicates listed in a portlet.""" |
3179 | - print 'From duplicates:' |
3180 | - print_subscribers(bug_page, 'subscribers-from-duplicates') |
3181 | - |
3182 | - |
3183 | -def print_subscribers(bug_page, subscriber_list_id): |
3184 | - """Print the subscribers listed in the subscriber portlet.""" |
3185 | - subscriber_list = find_tag_by_id(bug_page, subscriber_list_id) |
3186 | - if subscriber_list is None: |
3187 | - # No list with this ID (as can happen if there are |
3188 | - # no indirect subscribers), so just print an empty string. |
3189 | + print_subscribers(bug_page, 'Maybe') |
3190 | + |
3191 | + |
3192 | +def print_subscribers(bug_page, subscription_level=None, reverse=False): |
3193 | + """Print the subscribers listed in the subscribers JSON portlet.""" |
3194 | + from simplejson import loads |
3195 | + details = loads(bug_page) |
3196 | + |
3197 | + if details is None: |
3198 | + # No subscribers at all. |
3199 | print "" |
3200 | else: |
3201 | - anchors = subscriber_list.findAll('a') |
3202 | - for anchor in anchors: |
3203 | - sub_display = extract_text(anchor) |
3204 | - if anchor.has_key('title'): |
3205 | - sub_display += (' (%s)' % anchor['title']) |
3206 | - print sub_display |
3207 | + lines = [] |
3208 | + for subscription in details: |
3209 | + level_matches = ( |
3210 | + (not reverse and |
3211 | + subscription['subscription_level'] == subscription_level) or |
3212 | + (reverse and |
3213 | + subscription['subscription_level'] != subscription_level)) |
3214 | + if subscription_level is None or level_matches: |
3215 | + subscriber = subscription['subscriber'] |
3216 | + line = subscriber['display_name'] |
3217 | + if subscriber['can_edit']: |
3218 | + line += " (Unsubscribe)" |
3219 | + lines.append(line) |
3220 | + print "\n".join(sorted(lines)) |
3221 | |
3222 | |
3223 | def print_bug_affects_table(content, highlighted_only=False): |
Hi Danilo. I'm using this branch for a UX review, so I'm going to make comments here that can probably be addressed in earlier branches if you like.
It looks good!
In the mockup, "Maybe notified" goes last in the portlet, while in your branch, at least in https:/ /bugs.launchpad .dev/firefox/ +bug/1 , it comes before "Notified of all changes". I still think that we should show categories from the most connected to the least--so "Notified of all changes" should come first, followed by METADATA, followed by LIFECYCLE, followed by "Maybe notified". If the change you made from the mockup was intentional, so be it--I won't be around for you to convince me :-) .
In the mockup, there is a lot of text describing what "Maybe notified" means. I do prefer to not have that text around, as you have done, but I am inclined to say that we should have a help link with that information. If you disagree, again, since I'll be absent, I'm a pushover.
I'd prefer the above two changes. This next comment is about a change that you made that I think is for the best. In the mockup, I showed pencil icons for teams that the user could modify. However, as you've noted in the past, we do not let teams change their subscription levels, so including that here would be scope creep. So, +1 on omitting that.
That's it. Thank you!