Merge lp:~danilo/launchpad/bugtask-index-portlet-setup into lp:launchpad
- bugtask-index-portlet-setup
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Graham Binns |
Approved revision: | no longer in the source branch. |
Merged at revision: | 12391 |
Proposed branch: | lp:~danilo/launchpad/bugtask-index-portlet-setup |
Merge into: | lp:launchpad |
Diff against target: |
2350 lines (+1161/-1113) 4 files modified
lib/lp/bugs/javascript/bugtask_index.js (+4/-1109) lib/lp/bugs/javascript/bugtask_index_portlets.js (+1152/-0) lib/lp/bugs/templates/bug-portlet-subscribers.pt (+3/-3) lib/lp/bugs/templates/bugtask-index.pt (+2/-1) |
To merge this branch: | bzr merge lp:~danilo/launchpad/bugtask-index-portlet-setup |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Graham Binns (community) | code | Approve | |
Review via email: mp+49818@code.launchpad.net |
Commit message
[r=gmb][no-qa] Split portlet setup for subscriber widgets from bugtask-index.js.
Description of the change
= Split bugtask-index.js into two files =
Warning: this is a branch with a huge diff, but it's a move of ~1100 lines of JS from bugtask_index.js to a new bugtask_
At the moment, bugtask-index.js is a behemoth of >2k lines of JS, doing many unrelated things:
- handling duplication
- handling branch linking
- handling "me too" functionality
- handling subscription portlets
The idea is to eventually split it all up by functionality, but a first step is to start with setup_portlet_
Future branches will split duplication from subscription stuff.
== Pre-implementation notes ==
Talked this over with Graham who suggested starting with setup_portlet_
== Implementation details ==
Only two functions are namespaced using lp.bugs.
Rest of it is a simple code move from bugtask-index.js to bugtask-
Fixes in other parts of the tree are due to failing windmill tests.
I am not sure how to best figure out what is the minimum list of required modules for a certain module (I just copied the list over from bugtask_index.js), so if a reviewer has any suggestions, I'd be happy to hear them out.
== Tests ==
bin/test -cvvm lp.bugs --layer=
== Demo and Q/A ==
Go to eg. https:/
= Launchpad lint =
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/
lib/lp/
lib/lp/
Preview Diff
1 | === modified file 'lib/lp/bugs/javascript/bugtask_index.js' | |||
2 | --- lib/lp/bugs/javascript/bugtask_index.js 2011-02-10 09:52:06 +0000 | |||
3 | +++ lib/lp/bugs/javascript/bugtask_index.js 2011-02-15 18:19:47 +0000 | |||
4 | @@ -39,125 +39,9 @@ | |||
5 | 39 | var privacy_spinner; | 39 | var privacy_spinner; |
6 | 40 | var link_branch_link; | 40 | var link_branch_link; |
7 | 41 | 41 | ||
8 | 42 | // The set of subscriber CSS IDs as a JSON struct. | ||
9 | 43 | var subscriber_ids; | ||
10 | 44 | |||
11 | 45 | // A boolean telling us whether advanced subscription features are to be | ||
12 | 46 | // used or not. | ||
13 | 47 | // XXX 2011-01-14 gmb bug=702859: | ||
14 | 48 | // We need to expose feature flags via the API to avoid this kind of | ||
15 | 49 | // thing. | ||
16 | 50 | var use_advanced_subscriptions = false; | ||
17 | 51 | var subscription_labels = Y.lp.bugs.subscriber.subscription_labels; | ||
18 | 52 | |||
19 | 53 | /* | ||
20 | 54 | * An object representing the bugtask subscribers portlet. | ||
21 | 55 | * | ||
22 | 56 | * Since the portlet loads via XHR and inline subscribing | ||
23 | 57 | * depends on that portlet being loaded, setup a custom | ||
24 | 58 | * event object, to provide a hook for initializing subscription | ||
25 | 59 | * link callbacks after custom events. | ||
26 | 60 | */ | ||
27 | 61 | var PortletTarget = function() {}; | ||
28 | 62 | Y.augment(PortletTarget, Y.Event.Target); | ||
29 | 63 | namespace.portlet = new PortletTarget(); | ||
30 | 64 | |||
31 | 65 | function setup_portlet_handlers() { | ||
32 | 66 | namespace.portlet.subscribe('bugs:portletloaded', function() { | ||
33 | 67 | load_subscriber_ids(); | ||
34 | 68 | }); | ||
35 | 69 | namespace.portlet.subscribe('bugs:dupeportletloaded', function() { | ||
36 | 70 | setup_unsubscribe_icon_handlers(); | ||
37 | 71 | }); | ||
38 | 72 | /* | ||
39 | 73 | * If the subscribers portlet fails to load, clear any | ||
40 | 74 | * click handlers, so the normal subscribe page can be reached. | ||
41 | 75 | */ | ||
42 | 76 | namespace.portlet.subscribe('bugs:portletloadfailed', function(handlers) { | ||
43 | 77 | if (Y.Lang.isArray(handlers)) { | ||
44 | 78 | var click_handler = handlers[0]; | ||
45 | 79 | click_handler.detach(); | ||
46 | 80 | } | ||
47 | 81 | }); | ||
48 | 82 | /* If the dupe subscribers portlet fails to load, | ||
49 | 83 | * be sure to try to handle any unsub icons that may | ||
50 | 84 | * exist for others. | ||
51 | 85 | */ | ||
52 | 86 | namespace.portlet.subscribe( | ||
53 | 87 | 'bugs:dupeportletloadfailed', | ||
54 | 88 | function(handlers) { | ||
55 | 89 | setup_unsubscribe_icon_handlers(); | ||
56 | 90 | }); | ||
57 | 91 | |||
58 | 92 | /* If loading the subscriber IDs JSON has succeeded, set up the | ||
59 | 93 | * subscription link handlers and load the subscribers from dupes. | ||
60 | 94 | */ | ||
61 | 95 | namespace.portlet.subscribe( | ||
62 | 96 | 'bugs:portletsubscriberidsloaded', | ||
63 | 97 | function() { | ||
64 | 98 | setup_subscription_link_handlers(); | ||
65 | 99 | load_subscribers_from_duplicates(); | ||
66 | 100 | }); | ||
67 | 101 | |||
68 | 102 | /* If loading the subscriber IDs JSON fails we still need to load the | ||
69 | 103 | * subscribers from duplicates but we don't set up the subscription link | ||
70 | 104 | * handlers. | ||
71 | 105 | */ | ||
72 | 106 | namespace.portlet.subscribe( | ||
73 | 107 | 'bugs:portletsubscriberidsfailed', | ||
74 | 108 | function() { | ||
75 | 109 | load_subscribers_from_duplicates(); | ||
76 | 110 | }); | ||
77 | 111 | |||
78 | 112 | /* | ||
79 | 113 | * Subscribing someone else requires loading a grayed out | ||
80 | 114 | * username into the DOM until the subscribe action completes. | ||
81 | 115 | * There are a couple XHR requests in check_can_be_unsubscribed | ||
82 | 116 | * before the subscribe work can be done, so fire a custom event | ||
83 | 117 | * bugs:nameloaded and do the work here when the event fires. | ||
84 | 118 | */ | ||
85 | 119 | namespace.portlet.subscribe('bugs:nameloaded', function(subscription) { | ||
86 | 120 | var error_handler = new LP.client.ErrorHandler(); | ||
87 | 121 | error_handler.clearProgressUI = function() { | ||
88 | 122 | var temp_link = Y.one('#temp-username'); | ||
89 | 123 | if (temp_link) { | ||
90 | 124 | var temp_parent = temp_link.get('parentNode'); | ||
91 | 125 | temp_parent.removeChild(temp_link); | ||
92 | 126 | } | ||
93 | 127 | }; | ||
94 | 128 | error_handler.showError = function(error_msg) { | ||
95 | 129 | Y.lp.app.errors.display_error( | ||
96 | 130 | Y.one('.menu-link-addsubscriber'), error_msg); | ||
97 | 131 | }; | ||
98 | 132 | |||
99 | 133 | var config = { | ||
100 | 134 | on: { | ||
101 | 135 | success: function() { | ||
102 | 136 | var temp_link = Y.one('#temp-username'); | ||
103 | 137 | var temp_spinner = Y.one('#temp-name-spinner'); | ||
104 | 138 | temp_link.removeChild(temp_spinner); | ||
105 | 139 | var anim = Y.lazr.anim.green_flash({ node: temp_link }); | ||
106 | 140 | anim.on('end', function() { | ||
107 | 141 | add_user_name_link(subscription); | ||
108 | 142 | var temp_parent = temp_link.get('parentNode'); | ||
109 | 143 | temp_parent.removeChild(temp_link); | ||
110 | 144 | }); | ||
111 | 145 | anim.run(); | ||
112 | 146 | }, | ||
113 | 147 | failure: error_handler.getFailureHandler() | ||
114 | 148 | }, | ||
115 | 149 | parameters: { | ||
116 | 150 | person: LP.client.get_absolute_uri( | ||
117 | 151 | subscription.get('person').get('escaped_uri')), | ||
118 | 152 | suppress_notify: false | ||
119 | 153 | } | ||
120 | 154 | }; | ||
121 | 155 | lp_client.named_post(bug_repr.self_link, 'subscribe', config); | ||
122 | 156 | }); | ||
123 | 157 | } | ||
124 | 158 | |||
125 | 159 | namespace.setup_bugtask_index = function() { | 42 | namespace.setup_bugtask_index = function() { |
127 | 160 | setup_portlet_handlers(); | 43 | Y.lp.bugs.bugtask_index.portlets.setup_portlet_handlers(); |
128 | 44 | |||
129 | 161 | /* | 45 | /* |
130 | 162 | * Check the page for links related to overlay forms and request the HTML | 46 | * Check the page for links related to overlay forms and request the HTML |
131 | 163 | * for these forms. | 47 | * for these forms. |
132 | @@ -275,203 +159,6 @@ | |||
133 | 275 | 159 | ||
134 | 276 | 160 | ||
135 | 277 | /* | 161 | /* |
136 | 278 | * Initialize click handler for the subscribe someone else link. | ||
137 | 279 | * | ||
138 | 280 | * @method setup_subscribe_someone_else_handler | ||
139 | 281 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
140 | 282 | */ | ||
141 | 283 | function setup_subscribe_someone_else_handler(subscription) { | ||
142 | 284 | var config = { | ||
143 | 285 | header: 'Subscribe someone else', | ||
144 | 286 | step_title: 'Search', | ||
145 | 287 | picker_activator: '.menu-link-addsubscriber' | ||
146 | 288 | }; | ||
147 | 289 | |||
148 | 290 | config.save = function(result) { | ||
149 | 291 | subscribe_someone_else(result, subscription); | ||
150 | 292 | }; | ||
151 | 293 | var picker = Y.lp.app.picker.create('ValidPersonOrTeam', config); | ||
152 | 294 | } | ||
153 | 295 | |||
154 | 296 | |||
155 | 297 | /* | ||
156 | 298 | * Handle the advanced_subscription_overlay's form submissions. | ||
157 | 299 | * | ||
158 | 300 | * @method handle_advanced_subscription_overlay | ||
159 | 301 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
160 | 302 | * @param form_data {Object} The data from the submitted form. | ||
161 | 303 | */ | ||
162 | 304 | function handle_advanced_subscription_overlay(subscription, form_data) { | ||
163 | 305 | var link = subscription.get('link'); | ||
164 | 306 | var link_parent = link.get('parentNode'); | ||
165 | 307 | if (link_parent.hasClass('subscribed-false') && | ||
166 | 308 | link_parent.hasClass('dup-subscribed-false')) { | ||
167 | 309 | // The user isn't subscribed, so subscribe them. | ||
168 | 310 | subscription.set( | ||
169 | 311 | 'bug_notification_level', | ||
170 | 312 | form_data['field.bug_notification_level']); | ||
171 | 313 | subscribe_current_user(subscription); | ||
172 | 314 | } else if ( | ||
173 | 315 | form_data['field.subscription'] == 'update-subscription') { | ||
174 | 316 | // The user is already subscribed and wants to update their | ||
175 | 317 | // subscription. | ||
176 | 318 | setup_client_and_bug(); | ||
177 | 319 | var person_name = subscription.get('person').get('name'); | ||
178 | 320 | var subscription_url = | ||
179 | 321 | lp_bug_entry.get('self_link') + '/+subscription/' + | ||
180 | 322 | person_name; | ||
181 | 323 | config = { | ||
182 | 324 | on: { | ||
183 | 325 | success: function(lp_subscription) { | ||
184 | 326 | subscription.enable_spinner('Updating subscription...'); | ||
185 | 327 | lp_subscription.set( | ||
186 | 328 | 'bug_notification_level', | ||
187 | 329 | form_data['field.bug_notification_level'][0]) | ||
188 | 330 | save_config = { | ||
189 | 331 | on: { | ||
190 | 332 | success: function(e) { | ||
191 | 333 | subscription.disable_spinner( | ||
192 | 334 | 'Edit subscription'); | ||
193 | 335 | var anim = Y.lazr.anim.green_flash({ | ||
194 | 336 | node: link_parent | ||
195 | 337 | }); | ||
196 | 338 | anim.run(); | ||
197 | 339 | }, | ||
198 | 340 | failure: function(e) { | ||
199 | 341 | subscription.disable_spinner( | ||
200 | 342 | 'Edit subscription'); | ||
201 | 343 | var anim = Y.lazr.anim.red_flash({ | ||
202 | 344 | node: link_parent | ||
203 | 345 | }); | ||
204 | 346 | anim.run(); | ||
205 | 347 | } | ||
206 | 348 | } | ||
207 | 349 | } | ||
208 | 350 | lp_subscription.lp_save(save_config); | ||
209 | 351 | } | ||
210 | 352 | } | ||
211 | 353 | } | ||
212 | 354 | lp_client.get(subscription_url, config); | ||
213 | 355 | } else { | ||
214 | 356 | // The user is already subscribed and wants to unsubscribe. | ||
215 | 357 | unsubscribe_current_user(subscription); | ||
216 | 358 | } | ||
217 | 359 | } | ||
218 | 360 | |||
219 | 361 | |||
220 | 362 | /* | ||
221 | 363 | * Create and return a FormOverlay for advanced subscription | ||
222 | 364 | * interactions. | ||
223 | 365 | * | ||
224 | 366 | * @method setup_advanced_subscription_overlay | ||
225 | 367 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
226 | 368 | */ | ||
227 | 369 | function setup_advanced_subscription_overlay(subscription) { | ||
228 | 370 | var subscription_overlay = new Y.lazr.FormOverlay({ | ||
229 | 371 | headerContent: '<h2>Subscribe to bug</h2>', | ||
230 | 372 | form_submit_button: | ||
231 | 373 | Y.Node.create(submit_button_html), | ||
232 | 374 | form_cancel_button: | ||
233 | 375 | Y.Node.create(cancel_button_html), | ||
234 | 376 | centered: true, | ||
235 | 377 | visible: false | ||
236 | 378 | }); | ||
237 | 379 | subscription_overlay.set( | ||
238 | 380 | 'form_submit_callback', function(form_data) { | ||
239 | 381 | handle_advanced_subscription_overlay(subscription, form_data); | ||
240 | 382 | subscription_overlay.hide(); | ||
241 | 383 | }); | ||
242 | 384 | |||
243 | 385 | var subscription_link_url = subscription.get( | ||
244 | 386 | 'link').get('href') + '/++form++'; | ||
245 | 387 | subscription_overlay.loadFormContentAndRender( | ||
246 | 388 | subscription_link_url); | ||
247 | 389 | subscription_overlay.render('#privacy-form-container'); | ||
248 | 390 | return subscription_overlay | ||
249 | 391 | } | ||
250 | 392 | |||
251 | 393 | |||
252 | 394 | /* | ||
253 | 395 | * Initialize callbacks for subscribe/unsubscribe links. | ||
254 | 396 | * | ||
255 | 397 | * @method setup_subscription_link_handlers | ||
256 | 398 | */ | ||
257 | 399 | function setup_subscription_link_handlers() { | ||
258 | 400 | if (LP.client.links.me === undefined) { | ||
259 | 401 | return; | ||
260 | 402 | } | ||
261 | 403 | |||
262 | 404 | setup_client_and_bug(); | ||
263 | 405 | var subscription = new Y.lp.bugs.subscriber.Subscription({ | ||
264 | 406 | link: Y.one('.menu-link-subscription'), | ||
265 | 407 | spinner: Y.one('#sub-unsub-spinner'), | ||
266 | 408 | subscriber: new Y.lp.bugs.subscriber.Subscriber({ | ||
267 | 409 | uri: LP.client.links.me, | ||
268 | 410 | subscriber_ids: subscriber_ids | ||
269 | 411 | }) | ||
270 | 412 | }); | ||
271 | 413 | |||
272 | 414 | var is_direct = subscription.get( | ||
273 | 415 | 'link').get('parentNode').hasClass('subscribed-true'); | ||
274 | 416 | var has_dupes = subscription.get( | ||
275 | 417 | 'link').get('parentNode').hasClass('dup-subscribed-true'); | ||
276 | 418 | subscription.set('is_direct', is_direct); | ||
277 | 419 | subscription.set('has_dupes', has_dupes); | ||
278 | 420 | |||
279 | 421 | if (subscription.is_node()) { | ||
280 | 422 | subscription.get('link').on('click', function(e) { | ||
281 | 423 | e.halt(); | ||
282 | 424 | subscription.set('can_be_unsubscribed', true); | ||
283 | 425 | subscription.set('person', subscription.get('subscriber')); | ||
284 | 426 | subscription.set('is_team', false); | ||
285 | 427 | var parent = e.target.get('parentNode'); | ||
286 | 428 | if (namespace.use_advanced_subscriptions) { | ||
287 | 429 | var subscription_overlay = | ||
288 | 430 | setup_advanced_subscription_overlay(subscription); | ||
289 | 431 | subscription_overlay.show(); | ||
290 | 432 | } else { | ||
291 | 433 | // Look for the false conditions of subscription, which | ||
292 | 434 | // is_direct_subscription, etc. don't report correctly, | ||
293 | 435 | // to make sure we only use subscribe_current_user for | ||
294 | 436 | // the current user. | ||
295 | 437 | if (parent.hasClass('subscribed-false') && | ||
296 | 438 | parent.hasClass('dup-subscribed-false')) { | ||
297 | 439 | subscribe_current_user(subscription); | ||
298 | 440 | } | ||
299 | 441 | else { | ||
300 | 442 | unsubscribe_current_user(subscription); | ||
301 | 443 | } | ||
302 | 444 | } | ||
303 | 445 | }); | ||
304 | 446 | subscription.get('link').addClass('js-action'); | ||
305 | 447 | } | ||
306 | 448 | |||
307 | 449 | setup_subscribe_someone_else_handler(subscription); | ||
308 | 450 | } | ||
309 | 451 | |||
310 | 452 | /* | ||
311 | 453 | * Set click handlers for unsubscribe remove icons. | ||
312 | 454 | * | ||
313 | 455 | * @method setup_unsubscribe_icon_handlers | ||
314 | 456 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
315 | 457 | */ | ||
316 | 458 | function setup_unsubscribe_icon_handlers() { | ||
317 | 459 | var subscription = new Y.lp.bugs.subscriber.Subscription({ | ||
318 | 460 | link: Y.one('.menu-link-subscription'), | ||
319 | 461 | spinner: Y.one('#sub-unsub-spinner'), | ||
320 | 462 | subscriber: new Y.lp.bugs.subscriber.Subscriber({ | ||
321 | 463 | uri: LP.client.links.me, | ||
322 | 464 | subscriber_ids: subscriber_ids | ||
323 | 465 | }) | ||
324 | 466 | }); | ||
325 | 467 | |||
326 | 468 | Y.on('click', function(e) { | ||
327 | 469 | e.halt(); | ||
328 | 470 | unsubscribe_user_via_icon(e.target, subscription); | ||
329 | 471 | }, '.unsub-icon'); | ||
330 | 472 | } | ||
331 | 473 | |||
332 | 474 | /* | ||
333 | 475 | * Create the lp client and bug entry if we haven't done so already. | 162 | * Create the lp client and bug entry if we haven't done so already. |
334 | 476 | * | 163 | * |
335 | 477 | * @method setup_client_and_bug | 164 | * @method setup_client_and_bug |
336 | @@ -850,594 +537,6 @@ | |||
337 | 850 | Y.fire('lp:branch-linked', bug_branch_node); | 537 | Y.fire('lp:branch-linked', bug_branch_node); |
338 | 851 | } | 538 | } |
339 | 852 | 539 | ||
340 | 853 | /* | ||
341 | 854 | * Traverse the DOM of a given remove icon to find | ||
342 | 855 | * the user's link. Returns a URI of the form "/~username". | ||
343 | 856 | * | ||
344 | 857 | * @method get_user_uri_from_icon | ||
345 | 858 | * @param icon {Node} The node representing a remove icon. | ||
346 | 859 | * @return user_uri {String} The user's uri, without the hostname. | ||
347 | 860 | */ | ||
348 | 861 | function get_user_uri_from_icon(icon) { | ||
349 | 862 | var parent_div = icon.get('parentNode').get('parentNode'); | ||
350 | 863 | // This should be parent_div.firstChild, but because of #text | ||
351 | 864 | // and cross-browser issues, using the YUI query syntax is | ||
352 | 865 | // safer here. | ||
353 | 866 | var user_uri = parent_div.one('a').getAttribute('href'); | ||
354 | 867 | |||
355 | 868 | // Strip the domain off. We just want a path. | ||
356 | 869 | var host_start = user_uri.indexOf('//'); | ||
357 | 870 | if (host_start != -1) { | ||
358 | 871 | var host_end = user_uri.indexOf('/', host_start+2); | ||
359 | 872 | return user_uri.substring(host_end, user_uri.length); | ||
360 | 873 | } | ||
361 | 874 | |||
362 | 875 | return user_uri; | ||
363 | 876 | } | ||
364 | 877 | |||
365 | 878 | |||
366 | 879 | /* | ||
367 | 880 | * Build the HTML for a user link for the subscribers list. | ||
368 | 881 | * | ||
369 | 882 | * @method build_user_link_html | ||
370 | 883 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
371 | 884 | * @return html {String} The HTML used for creating a subscriber link. | ||
372 | 885 | */ | ||
373 | 886 | function build_user_link_html(subscription) { | ||
374 | 887 | var name = subscription.get('person').get('name'); | ||
375 | 888 | var css_name = subscription.get('person').get('css_name'); | ||
376 | 889 | var full_name = subscription.get('person').get('full_display_name'); | ||
377 | 890 | // Be paranoid about display_name, since timeouts or other errors | ||
378 | 891 | // could mean display_name wasn't set on initialization. | ||
379 | 892 | if (subscription.get('person').get('display_name') === '') { | ||
380 | 893 | subscription.get('person').set_display_name(); | ||
381 | 894 | } | ||
382 | 895 | var display_name = subscription.get('person').get('display_name'); | ||
383 | 896 | var terms = { | ||
384 | 897 | name: name, | ||
385 | 898 | css_name: css_name, | ||
386 | 899 | display_name: display_name, | ||
387 | 900 | full_name: full_name | ||
388 | 901 | }; | ||
389 | 902 | |||
390 | 903 | if (subscription.is_current_user_subscribing()) { | ||
391 | 904 | terms.subscribed_by = 'themselves'; | ||
392 | 905 | } else { | ||
393 | 906 | terms.subscribed_by = 'by ' + full_name; | ||
394 | 907 | } | ||
395 | 908 | |||
396 | 909 | var html = Y.Node.create('<div><a></a></div>'); | ||
397 | 910 | html.addClass(terms.css_name); | ||
398 | 911 | |||
399 | 912 | if (subscription.is_direct_subscription()) { | ||
400 | 913 | html.set('id', 'direct-' + terms.css_name); | ||
401 | 914 | } else { | ||
402 | 915 | html.set('id', 'dupe-' + terms.css_name); | ||
403 | 916 | } | ||
404 | 917 | |||
405 | 918 | html.one('a') | ||
406 | 919 | .set('href', '/~' + terms.name) | ||
407 | 920 | .set('name', terms.full_name) | ||
408 | 921 | .set('title', 'Subscribed ' + terms.subscribed_by); | ||
409 | 922 | |||
410 | 923 | var span; | ||
411 | 924 | if (subscription.is_team()) { | ||
412 | 925 | span = '<span class="sprite team"></span>'; | ||
413 | 926 | } else { | ||
414 | 927 | span = '<span class="sprite person"></span>'; | ||
415 | 928 | } | ||
416 | 929 | |||
417 | 930 | html.one('a') | ||
418 | 931 | .appendChild(Y.Node.create(span)) | ||
419 | 932 | .appendChild(document.createTextNode(terms.display_name)); | ||
420 | 933 | |||
421 | 934 | // Add remove icon if the current user can unsubscribe the subscriber. | ||
422 | 935 | if (subscription.can_be_unsubscribed_by_user()) { | ||
423 | 936 | var icon_html = Y.Node.create( | ||
424 | 937 | '<a href="+subscribe">' + | ||
425 | 938 | '<img class="unsub-icon" src="/@@/remove" alt="Remove" /></a>'); | ||
426 | 939 | icon_html | ||
427 | 940 | .set('id', 'unsubscribe-' + terms.css_name) | ||
428 | 941 | .set('title', 'Unsubscribe ' + terms.full_name); | ||
429 | 942 | icon_html.one('img') | ||
430 | 943 | .set('id', 'unsubscribe-icon-' + terms.css_name); | ||
431 | 944 | html.appendChild(icon_html); | ||
432 | 945 | } | ||
433 | 946 | |||
434 | 947 | return html; | ||
435 | 948 | } | ||
436 | 949 | |||
437 | 950 | /* | ||
438 | 951 | * Used to remove the user's name from the subscriber's list. | ||
439 | 952 | * | ||
440 | 953 | * @method remove_user_name_link | ||
441 | 954 | * @param user_node {Node} Node representing the user name link. | ||
442 | 955 | */ | ||
443 | 956 | function remove_user_name_link(user_node) { | ||
444 | 957 | var parent = user_node.get('parentNode'); | ||
445 | 958 | parent.removeChild(user_node); | ||
446 | 959 | } | ||
447 | 960 | |||
448 | 961 | /* | ||
449 | 962 | * Returns the next node in alphabetical order after the subscriber | ||
450 | 963 | * node now being added. No node is returned to append to end of list. | ||
451 | 964 | * | ||
452 | 965 | * The name can appear in one of two different lists. 1) The list of | ||
453 | 966 | * subscribers that can be unsubscribed by the current user, and | ||
454 | 967 | * 2) the list of subscribers that cannont be unsubscribed. | ||
455 | 968 | * | ||
456 | 969 | * @method get_next_subscriber_node | ||
457 | 970 | * @param subscription_link {Node} The sub/unsub link. | ||
458 | 971 | * @return {Node} The node appearing next in the subscriber list or | ||
459 | 972 | * undefined if no node is next. | ||
460 | 973 | */ | ||
461 | 974 | function get_next_subscriber_node(subscription) { | ||
462 | 975 | var full_name = subscription.get('person').get('full_display_name'); | ||
463 | 976 | var can_be_unsubscribed = subscription.can_be_unsubscribed_by_user(); | ||
464 | 977 | var nodes_by_name = {}; | ||
465 | 978 | var unsubscribables = []; | ||
466 | 979 | var not_unsubscribables = []; | ||
467 | 980 | |||
468 | 981 | // Use the list of subscribers pulled from the DOM to have sortable | ||
469 | 982 | // lists of unsubscribable vs. not unsubscribale person links. | ||
470 | 983 | var all_subscribers = Y.all('#subscribers-links div'); | ||
471 | 984 | if (all_subscribers.size() > 0) { | ||
472 | 985 | all_subscribers.each(function(sub_link) { | ||
473 | 986 | if (sub_link.getAttribute('id') != 'temp-username') { | ||
474 | 987 | // User's displayname is found via the link's "name" | ||
475 | 988 | // attribute. | ||
476 | 989 | var sub_link_name = sub_link.one('a').getAttribute('name'); | ||
477 | 990 | nodes_by_name[sub_link_name] = sub_link; | ||
478 | 991 | if (sub_link.one('img.unsub-icon')) { | ||
479 | 992 | unsubscribables.push(sub_link_name); | ||
480 | 993 | } else { | ||
481 | 994 | not_unsubscribables.push(sub_link_name); | ||
482 | 995 | } | ||
483 | 996 | } | ||
484 | 997 | }); | ||
485 | 998 | |||
486 | 999 | // Add the current subscription. | ||
487 | 1000 | if (can_be_unsubscribed) { | ||
488 | 1001 | unsubscribables.push(full_name); | ||
489 | 1002 | } else { | ||
490 | 1003 | not_unsubscribables.push(full_name); | ||
491 | 1004 | } | ||
492 | 1005 | unsubscribables.sort(); | ||
493 | 1006 | not_unsubscribables.sort(); | ||
494 | 1007 | } else { | ||
495 | 1008 | // If there is no all_subscribers, then we're dealing with | ||
496 | 1009 | // the printed None, so return. | ||
497 | 1010 | return; | ||
498 | 1011 | } | ||
499 | 1012 | |||
500 | 1013 | var i; | ||
501 | 1014 | if ((!unsubscribables && !not_unsubscribables) || | ||
502 | 1015 | // If A) neither list exists, B) the user belongs in the second | ||
503 | 1016 | // list but the second list doesn't exist, or C) user belongs in the | ||
504 | 1017 | // first list and the second doesn't exist, return no node to append. | ||
505 | 1018 | (!can_be_unsubscribed && !not_unsubscribables) || | ||
506 | 1019 | (can_be_unsubscribed && unsubscribables && !not_unsubscribables)) { | ||
507 | 1020 | return; | ||
508 | 1021 | } else if ( | ||
509 | 1022 | // If the user belongs in the first list, and the first list | ||
510 | 1023 | // doesn't exist, but the second one does, return the first node | ||
511 | 1024 | // in the second list. | ||
512 | 1025 | can_be_unsubscribed && !unsubscribables && not_unsubscribables) { | ||
513 | 1026 | return nodes_by_name[not_unsubscribables[0]]; | ||
514 | 1027 | } else if (can_be_unsubscribed) { | ||
515 | 1028 | // If the user belongs in the first list, loop the list for position. | ||
516 | 1029 | for (i=0; i<unsubscribables.length; i++) { | ||
517 | 1030 | if (unsubscribables[i] == full_name) { | ||
518 | 1031 | if (i+1 < unsubscribables.length) { | ||
519 | 1032 | return nodes_by_name[unsubscribables[i+1]]; | ||
520 | 1033 | // If the current link should go at the end of the first | ||
521 | 1034 | // list and we're at the end of that list, return the | ||
522 | 1035 | // first node of the second list. Due to earlier checks | ||
523 | 1036 | // we can be sure this list exists. | ||
524 | 1037 | } else if (i+1 >= unsubscribables.length) { | ||
525 | 1038 | return nodes_by_name[not_unsubscribables[0]]; | ||
526 | 1039 | } | ||
527 | 1040 | } | ||
528 | 1041 | } | ||
529 | 1042 | } else if (!can_be_unsubscribed) { | ||
530 | 1043 | // If user belongs in the second list, loop the list for position. | ||
531 | 1044 | for (i=0; i<not_unsubscribables.length; i++) { | ||
532 | 1045 | if (not_unsubscribables[i] == full_name) { | ||
533 | 1046 | if (i+1 < not_unsubscribables.length) { | ||
534 | 1047 | return nodes_by_name[not_unsubscribables[i+1]]; | ||
535 | 1048 | } else { | ||
536 | 1049 | return; | ||
537 | 1050 | } | ||
538 | 1051 | } | ||
539 | 1052 | } | ||
540 | 1053 | } | ||
541 | 1054 | } | ||
542 | 1055 | |||
543 | 1056 | /* | ||
544 | 1057 | * Add the user name to the subscriber's list. | ||
545 | 1058 | * | ||
546 | 1059 | * @method add_user_name_link | ||
547 | 1060 | */ | ||
548 | 1061 | function add_user_name_link(subscription) { | ||
549 | 1062 | var person = subscription.get('person'); | ||
550 | 1063 | var link_node = build_user_link_html(subscription); | ||
551 | 1064 | var subscribers = Y.one('#subscribers-links'); | ||
552 | 1065 | if (subscription.is_current_user_subscribing()) { | ||
553 | 1066 | // If this is the current user, then top post the name and be done. | ||
554 | 1067 | subscribers.insertBefore(link_node, subscribers.get('firstChild')); | ||
555 | 1068 | } else { | ||
556 | 1069 | var next = get_next_subscriber_node(subscription); | ||
557 | 1070 | if (next) { | ||
558 | 1071 | subscribers.insertBefore(link_node, next); | ||
559 | 1072 | } else { | ||
560 | 1073 | // Handle the case of the displayed "None". | ||
561 | 1074 | var none_subscribers = Y.one('#none-subscribers'); | ||
562 | 1075 | if (none_subscribers) { | ||
563 | 1076 | var none_parent = none_subscribers.get('parentNode'); | ||
564 | 1077 | none_parent.removeChild(none_subscribers); | ||
565 | 1078 | } | ||
566 | 1079 | subscribers.appendChild(link_node); | ||
567 | 1080 | } | ||
568 | 1081 | } | ||
569 | 1082 | |||
570 | 1083 | // Set the click handler if adding a remove icon. | ||
571 | 1084 | if (subscription.can_be_unsubscribed_by_user()) { | ||
572 | 1085 | var remove_icon = | ||
573 | 1086 | Y.one('#unsubscribe-icon-' + person.get('css_name')); | ||
574 | 1087 | remove_icon.on('click', function(e) { | ||
575 | 1088 | e.halt(); | ||
576 | 1089 | unsubscribe_user_via_icon(e.target, subscription); | ||
577 | 1090 | }); | ||
578 | 1091 | } | ||
579 | 1092 | } | ||
580 | 1093 | |||
581 | 1094 | /* | ||
582 | 1095 | * Add a grayed out, temporary user name when subscribing | ||
583 | 1096 | * someone else. | ||
584 | 1097 | * | ||
585 | 1098 | * @method add_temp_user_name | ||
586 | 1099 | * @param subscription_link {Node} The sub/unsub link. | ||
587 | 1100 | */ | ||
588 | 1101 | function add_temp_user_name(subscription) { | ||
589 | 1102 | // Be paranoid about display_name, since timeouts or other errors | ||
590 | 1103 | // could mean display_name wasn't set on initialization. | ||
591 | 1104 | if (subscription.get('person').get('display_name') === '') { | ||
592 | 1105 | subscription.get('person').set_display_name(); | ||
593 | 1106 | } | ||
594 | 1107 | var display_name = subscription.get('person').get('display_name'); | ||
595 | 1108 | var img_src; | ||
596 | 1109 | if (subscription.is_team()) { | ||
597 | 1110 | img_src = '/@@/teamgray'; | ||
598 | 1111 | } else { | ||
599 | 1112 | img_src = '/@@/persongray'; | ||
600 | 1113 | } | ||
601 | 1114 | |||
602 | 1115 | // The <span>...</span> below must *not* be <span/>. On FF (maybe | ||
603 | 1116 | // others, but at least on FF 3.0.11) will then not notice any | ||
604 | 1117 | // following sibling nodes, like the spinner image. | ||
605 | 1118 | var link_node = Y.Node.create([ | ||
606 | 1119 | '<div id="temp-username"> ', | ||
607 | 1120 | ' <img alt="" width="14" height="14" />', | ||
608 | 1121 | ' <span>Other Display Name</span>', | ||
609 | 1122 | ' <img id="temp-name-spinner" src="/@@/spinner" alt="" ', | ||
610 | 1123 | ' style="position:absolute;right:8px" /></div>'].join('')); | ||
611 | 1124 | link_node.one('img').set('src', img_src); | ||
612 | 1125 | link_node.replaceChild( | ||
613 | 1126 | document.createTextNode(display_name), | ||
614 | 1127 | link_node.one('span')); | ||
615 | 1128 | |||
616 | 1129 | var subscribers = Y.one('#subscribers-links'); | ||
617 | 1130 | var next = get_next_subscriber_node(subscription); | ||
618 | 1131 | if (next) { | ||
619 | 1132 | subscribers.insertBefore(link_node, next); | ||
620 | 1133 | } else { | ||
621 | 1134 | // Handle the case of the displayed "None". | ||
622 | 1135 | var none_subscribers = Y.one('#none-subscribers'); | ||
623 | 1136 | if (none_subscribers) { | ||
624 | 1137 | var none_parent = none_subscribers.get('parentNode'); | ||
625 | 1138 | none_parent.removeChild(none_subscribers); | ||
626 | 1139 | } | ||
627 | 1140 | subscribers.appendChild(link_node); | ||
628 | 1141 | } | ||
629 | 1142 | |||
630 | 1143 | // Fire a custom event to know it's safe to begin | ||
631 | 1144 | // any actual subscribing work. | ||
632 | 1145 | namespace.portlet.fire('bugs:nameloaded', subscription); | ||
633 | 1146 | } | ||
634 | 1147 | |||
635 | 1148 | /* | ||
636 | 1149 | * Add the "None" div to the subscribers list if | ||
637 | 1150 | * there aren't any subscribers left. | ||
638 | 1151 | * | ||
639 | 1152 | * @method set_none_for_empty_subscribers | ||
640 | 1153 | */ | ||
641 | 1154 | function set_none_for_empty_subscribers() { | ||
642 | 1155 | var subscriber_list = Y.one('#subscribers-links'); | ||
643 | 1156 | // Assume if subscriber_list has no child divs | ||
644 | 1157 | // then the list of subscribers is empty. | ||
645 | 1158 | if (!Y.Lang.isValue(subscriber_list.one('div')) && | ||
646 | 1159 | !Y.Lang.isValue(Y.one('#none-subscribers'))) { | ||
647 | 1160 | var none_div = Y.Node.create('<div id="none-subscribers">None</div>'); | ||
648 | 1161 | subscriber_list.appendChild(none_div); | ||
649 | 1162 | } | ||
650 | 1163 | |||
651 | 1164 | // Clear the empty duplicate subscribers list if it exists. | ||
652 | 1165 | var dup_list = Y.one('#subscribers-from-duplicates'); | ||
653 | 1166 | if (Y.Lang.isValue(dup_list) && | ||
654 | 1167 | !Y.Lang.isValue(dup_list.one('div'))) { | ||
655 | 1168 | var parent = dup_list.get('parentNode'); | ||
656 | 1169 | parent.removeChild(dup_list); | ||
657 | 1170 | } | ||
658 | 1171 | } | ||
659 | 1172 | |||
660 | 1173 | /* | ||
661 | 1174 | * Set the class on subscription link's parentNode. | ||
662 | 1175 | * | ||
663 | 1176 | * This is used to reset the class used by the | ||
664 | 1177 | * click handler to know which link was clicked. | ||
665 | 1178 | * | ||
666 | 1179 | * @method set_subscription_link_parent_class | ||
667 | 1180 | * @param subscription_link {Node} The sub/unsub link. | ||
668 | 1181 | * @param subscribed {Boolean} The sub/unsub'ed flag for the class. | ||
669 | 1182 | * @param dupe_subscribed {Boolean} The sub/unsub'ed flag for dupes | ||
670 | 1183 | * on the class. | ||
671 | 1184 | */ | ||
672 | 1185 | function set_subscription_link_parent_class( | ||
673 | 1186 | user_link, subscribed, dupe_subscribed) { | ||
674 | 1187 | |||
675 | 1188 | var parent = user_link.get('parentNode'); | ||
676 | 1189 | if (subscribed) { | ||
677 | 1190 | parent.removeClass('subscribed-false'); | ||
678 | 1191 | parent.addClass('subscribed-true'); | ||
679 | 1192 | } else { | ||
680 | 1193 | parent.removeClass('subscribed-true'); | ||
681 | 1194 | parent.addClass('subscribed-false'); | ||
682 | 1195 | } | ||
683 | 1196 | |||
684 | 1197 | if (dupe_subscribed) { | ||
685 | 1198 | parent.removeClass('dup-subscribed-false'); | ||
686 | 1199 | parent.addClass('dup-subscribed-true'); | ||
687 | 1200 | } else { | ||
688 | 1201 | parent.removeClass('dup-subscribed-true'); | ||
689 | 1202 | parent.addClass('dup-subscribed-false'); | ||
690 | 1203 | } | ||
691 | 1204 | } | ||
692 | 1205 | |||
693 | 1206 | /* | ||
694 | 1207 | * Unsubscribe a user from this bugtask when a remove icon is clicked. | ||
695 | 1208 | * | ||
696 | 1209 | * @method unsubscribe_user_via_icon | ||
697 | 1210 | * @param icon {Node} The remove icon that was clicked. | ||
698 | 1211 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
699 | 1212 | */ | ||
700 | 1213 | function unsubscribe_user_via_icon(icon, subscription) { | ||
701 | 1214 | icon.set('src', '/@@/spinner'); | ||
702 | 1215 | var icon_parent = icon.get('parentNode'); | ||
703 | 1216 | |||
704 | 1217 | var user_uri = get_user_uri_from_icon(icon); | ||
705 | 1218 | var person = new Y.lp.bugs.subscriber.Subscriber({ | ||
706 | 1219 | uri: user_uri, | ||
707 | 1220 | subscriber_ids: subscriber_ids | ||
708 | 1221 | }); | ||
709 | 1222 | subscription.set('person', person); | ||
710 | 1223 | |||
711 | 1224 | // Determine if this is a dupe. | ||
712 | 1225 | var is_dupe; | ||
713 | 1226 | var icon_parent_div = icon_parent.get('parentNode'); | ||
714 | 1227 | var dupe_id = 'dupe-' + person.get('css_name'); | ||
715 | 1228 | if (icon_parent_div.get('id') == dupe_id) { | ||
716 | 1229 | is_dupe = true; | ||
717 | 1230 | } else { | ||
718 | 1231 | is_dupe = false; | ||
719 | 1232 | } | ||
720 | 1233 | |||
721 | 1234 | var error_handler = new LP.client.ErrorHandler(); | ||
722 | 1235 | error_handler.clearProgressUI = function () { | ||
723 | 1236 | icon.set('src', '/@@/remove'); | ||
724 | 1237 | // Grab the icon again to reset to click handler. | ||
725 | 1238 | var unsubscribe_icon = Y.one( | ||
726 | 1239 | '#unsubscribe-icon-' + person.get('css_name')); | ||
727 | 1240 | unsubscribe_icon.on('click', function(e) { | ||
728 | 1241 | e.halt(); | ||
729 | 1242 | unsubscribe_user_via_icon(e.target, subscription); | ||
730 | 1243 | }); | ||
731 | 1244 | |||
732 | 1245 | }; | ||
733 | 1246 | error_handler.showError = function (error_msg) { | ||
734 | 1247 | var flash_node = Y.one('.' + person.get('css_name')); | ||
735 | 1248 | Y.lp.app.errors.display_error(flash_node, error_msg); | ||
736 | 1249 | |||
737 | 1250 | }; | ||
738 | 1251 | |||
739 | 1252 | var subscription_link = subscription.get('link'); | ||
740 | 1253 | var config = { | ||
741 | 1254 | on: { | ||
742 | 1255 | success: function(client) { | ||
743 | 1256 | icon_parent.removeChild(icon); | ||
744 | 1257 | var anim = Y.lazr.anim.green_flash({ node: icon_parent_div }); | ||
745 | 1258 | anim.on('end', function(e) { | ||
746 | 1259 | remove_user_name_link(icon_parent_div); | ||
747 | 1260 | set_none_for_empty_subscribers(); | ||
748 | 1261 | var person_link = Y.one('.' + person.get('css_name')); | ||
749 | 1262 | if (Y.Lang.isNull(person_link) && | ||
750 | 1263 | subscription.is_current_user_subscribing()) { | ||
751 | 1264 | // Current user has been completely unsubscribed. | ||
752 | 1265 | subscription.disable_spinner( | ||
753 | 1266 | subscription_labels.SUBSCRIBE); | ||
754 | 1267 | set_subscription_link_parent_class( | ||
755 | 1268 | subscription_link, false, false); | ||
756 | 1269 | subscription.set('is_direct', false); | ||
757 | 1270 | subscription.set('has_dupes', false); | ||
758 | 1271 | } else { | ||
759 | 1272 | if (is_dupe) { | ||
760 | 1273 | // A direct subscription remains. | ||
761 | 1274 | set_subscription_link_parent_class( | ||
762 | 1275 | subscription_link, true, false); | ||
763 | 1276 | subscription.set('is_direct', true); | ||
764 | 1277 | subscription.set('has_dupes', false); | ||
765 | 1278 | } else { | ||
766 | 1279 | // A dupe subscription remains. | ||
767 | 1280 | set_subscription_link_parent_class( | ||
768 | 1281 | subscription_link, false, true); | ||
769 | 1282 | subscription.set('is_direct', false); | ||
770 | 1283 | subscription.set('has_dupes', true); | ||
771 | 1284 | } | ||
772 | 1285 | } | ||
773 | 1286 | }); | ||
774 | 1287 | anim.run(); | ||
775 | 1288 | }, | ||
776 | 1289 | |||
777 | 1290 | failure: error_handler.getFailureHandler() | ||
778 | 1291 | } | ||
779 | 1292 | }; | ||
780 | 1293 | |||
781 | 1294 | if (!subscription.is_current_user_subscribing()) { | ||
782 | 1295 | config.parameters = { | ||
783 | 1296 | person: LP.client.get_absolute_uri(user_uri) | ||
784 | 1297 | }; | ||
785 | 1298 | } | ||
786 | 1299 | |||
787 | 1300 | if (is_dupe) { | ||
788 | 1301 | lp_client.named_post( | ||
789 | 1302 | bug_repr.self_link, 'unsubscribeFromDupes', config); | ||
790 | 1303 | } else { | ||
791 | 1304 | lp_client.named_post(bug_repr.self_link, 'unsubscribe', config); | ||
792 | 1305 | } | ||
793 | 1306 | } | ||
794 | 1307 | |||
795 | 1308 | /* | ||
796 | 1309 | * Subscribe the current user via the LP API. | ||
797 | 1310 | * | ||
798 | 1311 | * @method subscribe_current_user | ||
799 | 1312 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
800 | 1313 | */ | ||
801 | 1314 | function subscribe_current_user(subscription) { | ||
802 | 1315 | subscription.enable_spinner('Subscribing...'); | ||
803 | 1316 | var subscription_link = subscription.get('link'); | ||
804 | 1317 | var subscriber = subscription.get('subscriber'); | ||
805 | 1318 | var bug_notification_level = subscription.get('bug_notification_level'); | ||
806 | 1319 | |||
807 | 1320 | var error_handler = new LP.client.ErrorHandler(); | ||
808 | 1321 | error_handler.clearProgressUI = function () { | ||
809 | 1322 | subscription.disable_spinner(); | ||
810 | 1323 | }; | ||
811 | 1324 | error_handler.showError = function (error_msg) { | ||
812 | 1325 | Y.lp.app.errors.display_error(subscription_link, error_msg); | ||
813 | 1326 | }; | ||
814 | 1327 | |||
815 | 1328 | var config = { | ||
816 | 1329 | on: { | ||
817 | 1330 | success: function(client) { | ||
818 | 1331 | if (namespace.use_advanced_subscriptions) { | ||
819 | 1332 | subscription.disable_spinner( | ||
820 | 1333 | subscription_labels.EDIT); | ||
821 | 1334 | } else { | ||
822 | 1335 | subscription.disable_spinner( | ||
823 | 1336 | subscription_labels.UNSUBSCRIBE); | ||
824 | 1337 | } | ||
825 | 1338 | |||
826 | 1339 | if (subscription.has_duplicate_subscriptions()) { | ||
827 | 1340 | set_subscription_link_parent_class( | ||
828 | 1341 | subscription_link, true, true); | ||
829 | 1342 | } else { | ||
830 | 1343 | set_subscription_link_parent_class( | ||
831 | 1344 | subscription_link, true, false); | ||
832 | 1345 | } | ||
833 | 1346 | |||
834 | 1347 | // Handle the case where the subscriber's list displays | ||
835 | 1348 | // "None". | ||
836 | 1349 | var empty_subscribers = Y.one("#none-subscribers"); | ||
837 | 1350 | if (empty_subscribers) { | ||
838 | 1351 | var parent = empty_subscribers.get('parentNode'); | ||
839 | 1352 | parent.removeChild(empty_subscribers); | ||
840 | 1353 | } | ||
841 | 1354 | |||
842 | 1355 | add_user_name_link(subscription); | ||
843 | 1356 | |||
844 | 1357 | var flash_node = Y.one('.' + subscriber.get('css_name')); | ||
845 | 1358 | var anim = Y.lazr.anim.green_flash({ node: flash_node }); | ||
846 | 1359 | anim.run(); | ||
847 | 1360 | }, | ||
848 | 1361 | |||
849 | 1362 | failure: error_handler.getFailureHandler() | ||
850 | 1363 | }, | ||
851 | 1364 | |||
852 | 1365 | parameters: { | ||
853 | 1366 | person: LP.client.get_absolute_uri(subscriber.get('escaped_uri')), | ||
854 | 1367 | suppress_notify: false, | ||
855 | 1368 | level: bug_notification_level | ||
856 | 1369 | } | ||
857 | 1370 | }; | ||
858 | 1371 | lp_client.named_post(bug_repr.self_link, 'subscribe', config); | ||
859 | 1372 | } | ||
860 | 1373 | |||
861 | 1374 | /* | ||
862 | 1375 | * Unsubscribe the current user via the LP API. | ||
863 | 1376 | * | ||
864 | 1377 | * @method unsubscribe_current_user | ||
865 | 1378 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
866 | 1379 | */ | ||
867 | 1380 | function unsubscribe_current_user(subscription) { | ||
868 | 1381 | subscription.enable_spinner('Unsubscribing...'); | ||
869 | 1382 | var subscription_link = subscription.get('link'); | ||
870 | 1383 | var subscriber = subscription.get('subscriber'); | ||
871 | 1384 | |||
872 | 1385 | var error_handler = new LP.client.ErrorHandler(); | ||
873 | 1386 | error_handler.clearProgressUI = function () { | ||
874 | 1387 | subscription.disable_spinner(); | ||
875 | 1388 | }; | ||
876 | 1389 | error_handler.showError = function (error_msg) { | ||
877 | 1390 | Y.lp.app.errors.display_error(subscription_link, error_msg); | ||
878 | 1391 | }; | ||
879 | 1392 | |||
880 | 1393 | var config = { | ||
881 | 1394 | on: { | ||
882 | 1395 | success: function(client) { | ||
883 | 1396 | if (subscription.is_direct_subscription() && | ||
884 | 1397 | subscription.has_duplicate_subscriptions()) { | ||
885 | 1398 | // Don't change the 'Unsubscribe' text if | ||
886 | 1399 | // dupe subscriptions remain. | ||
887 | 1400 | subscription.disable_spinner(); | ||
888 | 1401 | set_subscription_link_parent_class( | ||
889 | 1402 | subscription_link, false, true); | ||
890 | 1403 | subscription.set('is_direct', false); | ||
891 | 1404 | } else if (subscription.is_direct_subscription() && | ||
892 | 1405 | !subscription.has_duplicate_subscriptions()) { | ||
893 | 1406 | // Only unsub'ing a direct subscriber here. | ||
894 | 1407 | subscription.disable_spinner( | ||
895 | 1408 | subscription_labels.SUBSCRIBE); | ||
896 | 1409 | set_subscription_link_parent_class( | ||
897 | 1410 | subscription_link, false, false); | ||
898 | 1411 | subscription.set('is_direct', false); | ||
899 | 1412 | } else { | ||
900 | 1413 | // Only unsub'ing dupes here. | ||
901 | 1414 | subscription.disable_spinner( | ||
902 | 1415 | subscription_labels.SUBSCRIBE); | ||
903 | 1416 | set_subscription_link_parent_class( | ||
904 | 1417 | subscription_link, false, false); | ||
905 | 1418 | subscription.set('has_dupes', false); | ||
906 | 1419 | } | ||
907 | 1420 | |||
908 | 1421 | var flash_node = Y.one('.' + subscriber.get('css_name')); | ||
909 | 1422 | var anim = Y.lazr.anim.green_flash({ node: flash_node }); | ||
910 | 1423 | anim.on('end', function(e) { | ||
911 | 1424 | remove_user_name_link(flash_node); | ||
912 | 1425 | set_none_for_empty_subscribers(); | ||
913 | 1426 | }); | ||
914 | 1427 | anim.run(); | ||
915 | 1428 | }, | ||
916 | 1429 | |||
917 | 1430 | failure: error_handler.getFailureHandler() | ||
918 | 1431 | } | ||
919 | 1432 | }; | ||
920 | 1433 | if (subscription.is_direct_subscription()) { | ||
921 | 1434 | lp_client.named_post(bug_repr.self_link, 'unsubscribe', config); | ||
922 | 1435 | } else { | ||
923 | 1436 | lp_client.named_post( | ||
924 | 1437 | bug_repr.self_link, 'unsubscribeFromDupes', config); | ||
925 | 1438 | } | ||
926 | 1439 | } | ||
927 | 1440 | |||
928 | 1441 | 540 | ||
929 | 1442 | /** | 541 | /** |
930 | 1443 | * Set up a bug task table row. | 542 | * Set up a bug task table row. |
931 | @@ -1815,106 +914,6 @@ | |||
932 | 1815 | LP.client.cache.bug.self_link, 'markUserAffected', config); | 914 | LP.client.cache.bug.self_link, 'markUserAffected', config); |
933 | 1816 | } | 915 | } |
934 | 1817 | }); | 916 | }); |
935 | 1818 | |||
936 | 1819 | /* | ||
937 | 1820 | * Check if the current user can unsubscribe the person | ||
938 | 1821 | * being subscribed. | ||
939 | 1822 | * | ||
940 | 1823 | * This must be done in JavaScript, since the subscription | ||
941 | 1824 | * hasn't completed yet, and so, can_be_unsubscribed_by_user | ||
942 | 1825 | * cannot be used. | ||
943 | 1826 | * | ||
944 | 1827 | * @method check_can_be_unsubscribed | ||
945 | 1828 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
946 | 1829 | */ | ||
947 | 1830 | function check_can_be_unsubscribed(subscription) { | ||
948 | 1831 | var error_handler = new LP.client.ErrorHandler(); | ||
949 | 1832 | error_handler.showError = function (error_msg) { | ||
950 | 1833 | Y.lp.app.errors.display_error( | ||
951 | 1834 | Y.one('.menu-link-addsubscriber'), error_msg); | ||
952 | 1835 | }; | ||
953 | 1836 | |||
954 | 1837 | var config = { | ||
955 | 1838 | on: { | ||
956 | 1839 | success: function(result) { | ||
957 | 1840 | var is_team = result.get('is_team'); | ||
958 | 1841 | subscription.set('is_team', is_team); | ||
959 | 1842 | var final_config = { | ||
960 | 1843 | on: { | ||
961 | 1844 | success: function(result) { | ||
962 | 1845 | var team_member = false; | ||
963 | 1846 | for (var i=0; i<result.entries.length; i++) { | ||
964 | 1847 | if (result.entries[i].member_link == | ||
965 | 1848 | LP.client.get_absolute_uri( | ||
966 | 1849 | subscription.get( | ||
967 | 1850 | 'subscriber').get('uri'))) { | ||
968 | 1851 | team_member = true; | ||
969 | 1852 | } | ||
970 | 1853 | } | ||
971 | 1854 | |||
972 | 1855 | if (team_member) { | ||
973 | 1856 | subscription.set('can_be_unsubscribed', true); | ||
974 | 1857 | add_temp_user_name(subscription); | ||
975 | 1858 | } else { | ||
976 | 1859 | subscription.set( | ||
977 | 1860 | 'can_be_unsubscribed', false); | ||
978 | 1861 | add_temp_user_name(subscription); | ||
979 | 1862 | } | ||
980 | 1863 | }, | ||
981 | 1864 | |||
982 | 1865 | failure: error_handler.getFailureHandler() | ||
983 | 1866 | } | ||
984 | 1867 | }; | ||
985 | 1868 | |||
986 | 1869 | if (is_team) { | ||
987 | 1870 | // Get a list of members to see if current user | ||
988 | 1871 | // is a team member. | ||
989 | 1872 | var members = result.get( | ||
990 | 1873 | 'members_details_collection_link'); | ||
991 | 1874 | lp_client.get(members, final_config); | ||
992 | 1875 | } else { | ||
993 | 1876 | subscription.set('can_be_unsubscribed', false); | ||
994 | 1877 | add_temp_user_name(subscription); | ||
995 | 1878 | } | ||
996 | 1879 | }, | ||
997 | 1880 | |||
998 | 1881 | failure: error_handler.getFailureHandler() | ||
999 | 1882 | } | ||
1000 | 1883 | }; | ||
1001 | 1884 | lp_client.get(LP.client.get_absolute_uri( | ||
1002 | 1885 | subscription.get('person').get('escaped_uri')), config); | ||
1003 | 1886 | } | ||
1004 | 1887 | |||
1005 | 1888 | /* | ||
1006 | 1889 | * Subscribe a person or team other than the current user. | ||
1007 | 1890 | * This is a callback for the subscribe someone else picker. | ||
1008 | 1891 | * | ||
1009 | 1892 | * @method subscribe_someone_else | ||
1010 | 1893 | * @result {Object} The object representing a person returned by the API. | ||
1011 | 1894 | */ | ||
1012 | 1895 | function subscribe_someone_else(result, subscription) { | ||
1013 | 1896 | var person = new Y.lp.bugs.subscriber.Subscriber({ | ||
1014 | 1897 | uri: result.api_uri, | ||
1015 | 1898 | display_name: result.title, | ||
1016 | 1899 | subscriber_ids: subscriber_ids | ||
1017 | 1900 | }); | ||
1018 | 1901 | subscription.set('person', person); | ||
1019 | 1902 | |||
1020 | 1903 | var error_handler = new LP.client.ErrorHandler(); | ||
1021 | 1904 | error_handler.showError = function(error_msg) { | ||
1022 | 1905 | Y.lp.app.errors.display_error( | ||
1023 | 1906 | Y.one('.menu-link-addsubscriber'), error_msg); | ||
1024 | 1907 | }; | ||
1025 | 1908 | |||
1026 | 1909 | if (subscription.is_already_subscribed()) { | ||
1027 | 1910 | error_handler.showError( | ||
1028 | 1911 | subscription.get('person').get('full_display_name') + | ||
1029 | 1912 | ' has already been subscribed'); | ||
1030 | 1913 | } else { | ||
1031 | 1914 | check_can_be_unsubscribed(subscription); | ||
1032 | 1915 | } | ||
1033 | 1916 | } | ||
1034 | 1917 | |||
1035 | 1918 | /* | 917 | /* |
1036 | 1919 | * Click handling to pass comment text to the attachment | 918 | * Click handling to pass comment text to the attachment |
1037 | 1920 | * page if there is a comment. | 919 | * page if there is a comment. |
1038 | @@ -1935,114 +934,10 @@ | |||
1039 | 1935 | }); | 934 | }); |
1040 | 1936 | } | 935 | } |
1041 | 1937 | 936 | ||
1042 | 1938 | function load_subscribers_from_duplicates() { | ||
1043 | 1939 | if (Y.UA.ie) { | ||
1044 | 1940 | return null; | ||
1045 | 1941 | } | ||
1046 | 1942 | |||
1047 | 1943 | Y.one('#subscribers-portlet-dupe-spinner').setStyle( | ||
1048 | 1944 | 'display', 'block'); | ||
1049 | 1945 | |||
1050 | 1946 | function hide_spinner() { | ||
1051 | 1947 | Y.one('#subscribers-portlet-dupe-spinner').setStyle( | ||
1052 | 1948 | 'display', 'none'); | ||
1053 | 1949 | // Fire a custom event to signal failure, so that | ||
1054 | 1950 | // any remaining unsub icons can be hooked up. | ||
1055 | 1951 | namespace.portlet.fire('bugs:dupeportletloadfailed'); | ||
1056 | 1952 | } | ||
1057 | 1953 | |||
1058 | 1954 | function on_success(transactionid, response, args) { | ||
1059 | 1955 | hide_spinner(); | ||
1060 | 1956 | |||
1061 | 1957 | var dupe_subscribers_container = Y.one( | ||
1062 | 1958 | '#subscribers-from-duplicates-container'); | ||
1063 | 1959 | dupe_subscribers_container.set( | ||
1064 | 1960 | 'innerHTML', | ||
1065 | 1961 | dupe_subscribers_container.get('innerHTML') + | ||
1066 | 1962 | response.responseText); | ||
1067 | 1963 | |||
1068 | 1964 | // Fire a custom portlet loaded event to notify when | ||
1069 | 1965 | // it's safe to setup dupe subscriber link callbacks. | ||
1070 | 1966 | namespace.portlet.fire('bugs:dupeportletloaded'); | ||
1071 | 1967 | } | ||
1072 | 1968 | |||
1073 | 1969 | var config = {on: {success: on_success, | ||
1074 | 1970 | failure: hide_spinner}}; | ||
1075 | 1971 | var url = Y.one( | ||
1076 | 1972 | '#subscribers-from-dupes-content-link').getAttribute( | ||
1077 | 1973 | 'href').replace('bugs.', ''); | ||
1078 | 1974 | Y.io(url, config); | ||
1079 | 1975 | } | ||
1080 | 1976 | |||
1081 | 1977 | namespace.load_subscribers_portlet = function( | ||
1082 | 1978 | subscription_link, subscription_link_handler) { | ||
1083 | 1979 | if (Y.UA.ie) { | ||
1084 | 1980 | return null; | ||
1085 | 1981 | } | ||
1086 | 1982 | |||
1087 | 1983 | Y.one('#subscribers-portlet-spinner').setStyle('display', 'block'); | ||
1088 | 1984 | |||
1089 | 1985 | function hide_spinner() { | ||
1090 | 1986 | Y.one('#subscribers-portlet-spinner').setStyle('display', 'none'); | ||
1091 | 1987 | // Fire a custom event to notify that the initial click | ||
1092 | 1988 | // handler on subscription_link set above should be | ||
1093 | 1989 | // cleared. | ||
1094 | 1990 | if (namespace) { | ||
1095 | 1991 | namespace.portlet.fire( | ||
1096 | 1992 | 'bugs:portletloadfailed', subscription_link_handler); | ||
1097 | 1993 | } | ||
1098 | 1994 | } | ||
1099 | 1995 | |||
1100 | 1996 | function setup_portlet(transactionid, response, args) { | ||
1101 | 1997 | hide_spinner(); | ||
1102 | 1998 | var portlet = Y.one('#portlet-subscribers'); | ||
1103 | 1999 | portlet.set('innerHTML', | ||
1104 | 2000 | portlet.get('innerHTML') + response.responseText); | ||
1105 | 2001 | |||
1106 | 2002 | // Fire a custom portlet loaded event to notify when | ||
1107 | 2003 | // it's safe to setup subscriber link callbacks. | ||
1108 | 2004 | namespace.portlet.fire('bugs:portletloaded'); | ||
1109 | 2005 | } | ||
1110 | 2006 | |||
1111 | 2007 | var config = {on: {success: setup_portlet, | ||
1112 | 2008 | failure: hide_spinner}}; | ||
1113 | 2009 | var url = Y.one( | ||
1114 | 2010 | '#subscribers-content-link').getAttribute('href').replace( | ||
1115 | 2011 | 'bugs.', ''); | ||
1116 | 2012 | Y.io(url, config); | ||
1117 | 2013 | }; | ||
1118 | 2014 | |||
1119 | 2015 | function load_subscriber_ids() { | ||
1120 | 2016 | function on_success(transactionid, response, args) { | ||
1121 | 2017 | try { | ||
1122 | 2018 | subscriber_ids = Y.JSON.parse(response.responseText); | ||
1123 | 2019 | |||
1124 | 2020 | // Fire a custom event to trigger the setting-up of the | ||
1125 | 2021 | // subscription handlers. | ||
1126 | 2022 | namespace.portlet.fire('bugs:portletsubscriberidsloaded'); | ||
1127 | 2023 | } catch (e) { | ||
1128 | 2024 | // Fire an event to signal failure. This ensures that the | ||
1129 | 2025 | // subscribers-from-dupes still get loaded into the portlet. | ||
1130 | 2026 | namespace.portlet.fire('bugs:portletsubscriberidsfailed'); | ||
1131 | 2027 | } | ||
1132 | 2028 | } | ||
1133 | 2029 | |||
1134 | 2030 | function on_failure() { | ||
1135 | 2031 | // Fire an event to signal failure. This ensures that the | ||
1136 | 2032 | // subscribers-from-dupes still get loaded into the portlet. | ||
1137 | 2033 | namespace.portlet.fire('bugs:portletsubscriberidsfailed'); | ||
1138 | 2034 | } | ||
1139 | 2035 | |||
1140 | 2036 | var config = {on: {success: on_success, | ||
1141 | 2037 | failure: on_failure}}; | ||
1142 | 2038 | var url = Y.one( | ||
1143 | 2039 | '#subscribers-ids-link').getAttribute('href'); | ||
1144 | 2040 | Y.io(url, config); | ||
1145 | 2041 | } | ||
1146 | 2042 | 937 | ||
1147 | 2043 | }, "0.1", {"requires": ["base", "oop", "node", "event", "io-base", | 938 | }, "0.1", {"requires": ["base", "oop", "node", "event", "io-base", |
1148 | 2044 | "json-parse", "substitute", "widget-position-ext", | 939 | "json-parse", "substitute", "widget-position-ext", |
1149 | 2045 | "lazr.formoverlay", "lazr.anim", "lazr.base", | 940 | "lazr.formoverlay", "lazr.anim", "lazr.base", |
1150 | 2046 | "lazr.overlay", "lazr.choiceedit", "lp.app.picker", | 941 | "lazr.overlay", "lazr.choiceedit", "lp.app.picker", |
1153 | 2047 | "lp.client.plugins", "lp.bugs.subscriber", | 942 | "lp.client.plugins", "lp.bugs.bugtask_index.portlets", |
1154 | 2048 | "lp.app.errors"]}); | 943 | "lp.bugs.subscriber", "lp.app.errors"]}); |
1155 | 2049 | 944 | ||
1156 | === added file 'lib/lp/bugs/javascript/bugtask_index_portlets.js' | |||
1157 | --- lib/lp/bugs/javascript/bugtask_index_portlets.js 1970-01-01 00:00:00 +0000 | |||
1158 | +++ lib/lp/bugs/javascript/bugtask_index_portlets.js 2011-02-15 18:19:47 +0000 | |||
1159 | @@ -0,0 +1,1152 @@ | |||
1160 | 1 | /* Copyright 2011 Canonical Ltd. This software is licensed under the | ||
1161 | 2 | * GNU Affero General Public License version 3 (see the file LICENSE). | ||
1162 | 3 | * | ||
1163 | 4 | * Form overlay widgets and subscriber handling for bug pages. | ||
1164 | 5 | * | ||
1165 | 6 | * @module bugs | ||
1166 | 7 | * @submodule bugtask_index.portlets | ||
1167 | 8 | */ | ||
1168 | 9 | |||
1169 | 10 | YUI.add('lp.bugs.bugtask_index.portlets', function(Y) { | ||
1170 | 11 | |||
1171 | 12 | var namespace = Y.namespace('lp.bugs.bugtask_index.portlets'); | ||
1172 | 13 | |||
1173 | 14 | // The launchpad js client used. | ||
1174 | 15 | var lp_client; | ||
1175 | 16 | |||
1176 | 17 | // The launchpad client entry for the current bug. | ||
1177 | 18 | var lp_bug_entry; | ||
1178 | 19 | |||
1179 | 20 | // The bug itself, taken from cache. | ||
1180 | 21 | var bug_repr; | ||
1181 | 22 | |||
1182 | 23 | // A boolean telling us whether advanced subscription features are to be | ||
1183 | 24 | // used or not. | ||
1184 | 25 | var use_advanced_subscriptions = false; | ||
1185 | 26 | |||
1186 | 27 | var subscription_labels = Y.lp.bugs.subscriber.subscription_labels; | ||
1187 | 28 | |||
1188 | 29 | submit_button_html = | ||
1189 | 30 | '<button type="submit" name="field.actions.change" ' + | ||
1190 | 31 | 'value="Change" class="lazr-pos lazr-btn" >OK</button>'; | ||
1191 | 32 | cancel_button_html = | ||
1192 | 33 | '<button type="button" name="field.actions.cancel" ' + | ||
1193 | 34 | 'class="lazr-neg lazr-btn" >Cancel</button>'; | ||
1194 | 35 | |||
1195 | 36 | // The set of subscriber CSS IDs as a JSON struct. | ||
1196 | 37 | var subscriber_ids; | ||
1197 | 38 | |||
1198 | 39 | /* | ||
1199 | 40 | * An object representing the bugtask subscribers portlet. | ||
1200 | 41 | * | ||
1201 | 42 | * Since the portlet loads via XHR and inline subscribing | ||
1202 | 43 | * depends on that portlet being loaded, setup a custom | ||
1203 | 44 | * event object, to provide a hook for initializing subscription | ||
1204 | 45 | * link callbacks after custom events. | ||
1205 | 46 | */ | ||
1206 | 47 | var PortletTarget = function() {}; | ||
1207 | 48 | Y.augment(PortletTarget, Y.Event.Target); | ||
1208 | 49 | namespace.portlet = new PortletTarget(); | ||
1209 | 50 | |||
1210 | 51 | /* | ||
1211 | 52 | * Create the lp client and bug entry if we haven't done so already. | ||
1212 | 53 | * | ||
1213 | 54 | * @method setup_client_and_bug | ||
1214 | 55 | */ | ||
1215 | 56 | function setup_client_and_bug() { | ||
1216 | 57 | lp_client = new LP.client.Launchpad(); | ||
1217 | 58 | |||
1218 | 59 | if (bug_repr === undefined) { | ||
1219 | 60 | bug_repr = LP.client.cache.bug; | ||
1220 | 61 | lp_bug_entry = new LP.client.Entry( | ||
1221 | 62 | lp_client, bug_repr, bug_repr.self_link); | ||
1222 | 63 | } | ||
1223 | 64 | } | ||
1224 | 65 | |||
1225 | 66 | namespace.load_subscribers_portlet = function( | ||
1226 | 67 | subscription_link, subscription_link_handler) { | ||
1227 | 68 | if (Y.UA.ie) { | ||
1228 | 69 | return null; | ||
1229 | 70 | } | ||
1230 | 71 | |||
1231 | 72 | Y.one('#subscribers-portlet-spinner').setStyle('display', 'block'); | ||
1232 | 73 | |||
1233 | 74 | function hide_spinner() { | ||
1234 | 75 | Y.one('#subscribers-portlet-spinner').setStyle('display', 'none'); | ||
1235 | 76 | // Fire a custom event to notify that the initial click | ||
1236 | 77 | // handler on subscription_link set above should be | ||
1237 | 78 | // cleared. | ||
1238 | 79 | if (namespace) { | ||
1239 | 80 | namespace.portlet.fire( | ||
1240 | 81 | 'bugs:portletloadfailed', subscription_link_handler); | ||
1241 | 82 | } | ||
1242 | 83 | } | ||
1243 | 84 | |||
1244 | 85 | function setup_portlet(transactionid, response, args) { | ||
1245 | 86 | hide_spinner(); | ||
1246 | 87 | var portlet = Y.one('#portlet-subscribers'); | ||
1247 | 88 | portlet.set('innerHTML', | ||
1248 | 89 | portlet.get('innerHTML') + response.responseText); | ||
1249 | 90 | |||
1250 | 91 | // Fire a custom portlet loaded event to notify when | ||
1251 | 92 | // it's safe to setup subscriber link callbacks. | ||
1252 | 93 | namespace.portlet.fire('bugs:portletloaded'); | ||
1253 | 94 | } | ||
1254 | 95 | |||
1255 | 96 | var config = {on: {success: setup_portlet, | ||
1256 | 97 | failure: hide_spinner}}; | ||
1257 | 98 | var url = Y.one( | ||
1258 | 99 | '#subscribers-content-link').getAttribute('href').replace( | ||
1259 | 100 | 'bugs.', ''); | ||
1260 | 101 | Y.io(url, config); | ||
1261 | 102 | }; | ||
1262 | 103 | |||
1263 | 104 | |||
1264 | 105 | namespace.setup_portlet_handlers = function() { | ||
1265 | 106 | namespace.portlet.subscribe('bugs:portletloaded', function() { | ||
1266 | 107 | load_subscriber_ids(); | ||
1267 | 108 | }); | ||
1268 | 109 | namespace.portlet.subscribe('bugs:dupeportletloaded', function() { | ||
1269 | 110 | setup_unsubscribe_icon_handlers(); | ||
1270 | 111 | }); | ||
1271 | 112 | /* | ||
1272 | 113 | * If the subscribers portlet fails to load, clear any | ||
1273 | 114 | * click handlers, so the normal subscribe page can be reached. | ||
1274 | 115 | */ | ||
1275 | 116 | namespace.portlet.subscribe('bugs:portletloadfailed', function(handlers) { | ||
1276 | 117 | if (Y.Lang.isArray(handlers)) { | ||
1277 | 118 | var click_handler = handlers[0]; | ||
1278 | 119 | click_handler.detach(); | ||
1279 | 120 | } | ||
1280 | 121 | }); | ||
1281 | 122 | /* If the dupe subscribers portlet fails to load, | ||
1282 | 123 | * be sure to try to handle any unsub icons that may | ||
1283 | 124 | * exist for others. | ||
1284 | 125 | */ | ||
1285 | 126 | namespace.portlet.subscribe( | ||
1286 | 127 | 'bugs:dupeportletloadfailed', | ||
1287 | 128 | function(handlers) { | ||
1288 | 129 | setup_unsubscribe_icon_handlers(); | ||
1289 | 130 | }); | ||
1290 | 131 | |||
1291 | 132 | /* If loading the subscriber IDs JSON has succeeded, set up the | ||
1292 | 133 | * subscription link handlers and load the subscribers from dupes. | ||
1293 | 134 | */ | ||
1294 | 135 | namespace.portlet.subscribe( | ||
1295 | 136 | 'bugs:portletsubscriberidsloaded', | ||
1296 | 137 | function() { | ||
1297 | 138 | setup_subscription_link_handlers(); | ||
1298 | 139 | load_subscribers_from_duplicates(); | ||
1299 | 140 | }); | ||
1300 | 141 | |||
1301 | 142 | /* If loading the subscriber IDs JSON fails we still need to load the | ||
1302 | 143 | * subscribers from duplicates but we don't set up the subscription link | ||
1303 | 144 | * handlers. | ||
1304 | 145 | */ | ||
1305 | 146 | namespace.portlet.subscribe( | ||
1306 | 147 | 'bugs:portletsubscriberidsfailed', | ||
1307 | 148 | function() { | ||
1308 | 149 | load_subscribers_from_duplicates(); | ||
1309 | 150 | }); | ||
1310 | 151 | |||
1311 | 152 | /* | ||
1312 | 153 | * Subscribing someone else requires loading a grayed out | ||
1313 | 154 | * username into the DOM until the subscribe action completes. | ||
1314 | 155 | * There are a couple XHR requests in check_can_be_unsubscribed | ||
1315 | 156 | * before the subscribe work can be done, so fire a custom event | ||
1316 | 157 | * bugs:nameloaded and do the work here when the event fires. | ||
1317 | 158 | */ | ||
1318 | 159 | namespace.portlet.subscribe('bugs:nameloaded', function(subscription) { | ||
1319 | 160 | var error_handler = new LP.client.ErrorHandler(); | ||
1320 | 161 | error_handler.clearProgressUI = function() { | ||
1321 | 162 | var temp_link = Y.one('#temp-username'); | ||
1322 | 163 | if (temp_link) { | ||
1323 | 164 | var temp_parent = temp_link.get('parentNode'); | ||
1324 | 165 | temp_parent.removeChild(temp_link); | ||
1325 | 166 | } | ||
1326 | 167 | }; | ||
1327 | 168 | error_handler.showError = function(error_msg) { | ||
1328 | 169 | Y.lp.app.errors.display_error( | ||
1329 | 170 | Y.one('.menu-link-addsubscriber'), error_msg); | ||
1330 | 171 | }; | ||
1331 | 172 | |||
1332 | 173 | var config = { | ||
1333 | 174 | on: { | ||
1334 | 175 | success: function() { | ||
1335 | 176 | var temp_link = Y.one('#temp-username'); | ||
1336 | 177 | var temp_spinner = Y.one('#temp-name-spinner'); | ||
1337 | 178 | temp_link.removeChild(temp_spinner); | ||
1338 | 179 | var anim = Y.lazr.anim.green_flash({ node: temp_link }); | ||
1339 | 180 | anim.on('end', function() { | ||
1340 | 181 | add_user_name_link(subscription); | ||
1341 | 182 | var temp_parent = temp_link.get('parentNode'); | ||
1342 | 183 | temp_parent.removeChild(temp_link); | ||
1343 | 184 | }); | ||
1344 | 185 | anim.run(); | ||
1345 | 186 | }, | ||
1346 | 187 | failure: error_handler.getFailureHandler() | ||
1347 | 188 | }, | ||
1348 | 189 | parameters: { | ||
1349 | 190 | person: LP.client.get_absolute_uri( | ||
1350 | 191 | subscription.get('person').get('escaped_uri')), | ||
1351 | 192 | suppress_notify: false | ||
1352 | 193 | } | ||
1353 | 194 | }; | ||
1354 | 195 | lp_client.named_post(bug_repr.self_link, 'subscribe', config); | ||
1355 | 196 | }); | ||
1356 | 197 | } | ||
1357 | 198 | |||
1358 | 199 | function load_subscriber_ids() { | ||
1359 | 200 | function on_success(transactionid, response, args) { | ||
1360 | 201 | try { | ||
1361 | 202 | subscriber_ids = Y.JSON.parse(response.responseText); | ||
1362 | 203 | |||
1363 | 204 | // Fire a custom event to trigger the setting-up of the | ||
1364 | 205 | // subscription handlers. | ||
1365 | 206 | namespace.portlet.fire('bugs:portletsubscriberidsloaded'); | ||
1366 | 207 | } catch (e) { | ||
1367 | 208 | // Fire an event to signal failure. This ensures that the | ||
1368 | 209 | // subscribers-from-dupes still get loaded into the portlet. | ||
1369 | 210 | namespace.portlet.fire('bugs:portletsubscriberidsfailed'); | ||
1370 | 211 | } | ||
1371 | 212 | } | ||
1372 | 213 | |||
1373 | 214 | function on_failure() { | ||
1374 | 215 | // Fire an event to signal failure. This ensures that the | ||
1375 | 216 | // subscribers-from-dupes still get loaded into the portlet. | ||
1376 | 217 | namespace.portlet.fire('bugs:portletsubscriberidsfailed'); | ||
1377 | 218 | } | ||
1378 | 219 | |||
1379 | 220 | var config = {on: {success: on_success, | ||
1380 | 221 | failure: on_failure}}; | ||
1381 | 222 | var url = Y.one( | ||
1382 | 223 | '#subscribers-ids-link').getAttribute('href'); | ||
1383 | 224 | Y.io(url, config); | ||
1384 | 225 | } | ||
1385 | 226 | |||
1386 | 227 | /* | ||
1387 | 228 | * Set click handlers for unsubscribe remove icons. | ||
1388 | 229 | * | ||
1389 | 230 | * @method setup_unsubscribe_icon_handlers | ||
1390 | 231 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
1391 | 232 | */ | ||
1392 | 233 | function setup_unsubscribe_icon_handlers() { | ||
1393 | 234 | var subscription = new Y.lp.bugs.subscriber.Subscription({ | ||
1394 | 235 | link: Y.one('.menu-link-subscription'), | ||
1395 | 236 | spinner: Y.one('#sub-unsub-spinner'), | ||
1396 | 237 | subscriber: new Y.lp.bugs.subscriber.Subscriber({ | ||
1397 | 238 | uri: LP.client.links.me, | ||
1398 | 239 | subscriber_ids: subscriber_ids | ||
1399 | 240 | }) | ||
1400 | 241 | }); | ||
1401 | 242 | |||
1402 | 243 | Y.on('click', function(e) { | ||
1403 | 244 | e.halt(); | ||
1404 | 245 | unsubscribe_user_via_icon(e.target, subscription); | ||
1405 | 246 | }, '.unsub-icon'); | ||
1406 | 247 | } | ||
1407 | 248 | |||
1408 | 249 | /* | ||
1409 | 250 | * Initialize callbacks for subscribe/unsubscribe links. | ||
1410 | 251 | * | ||
1411 | 252 | * @method setup_subscription_link_handlers | ||
1412 | 253 | */ | ||
1413 | 254 | function setup_subscription_link_handlers() { | ||
1414 | 255 | if (LP.client.links.me === undefined) { | ||
1415 | 256 | return; | ||
1416 | 257 | } | ||
1417 | 258 | |||
1418 | 259 | setup_client_and_bug(); | ||
1419 | 260 | var subscription = new Y.lp.bugs.subscriber.Subscription({ | ||
1420 | 261 | link: Y.one('.menu-link-subscription'), | ||
1421 | 262 | spinner: Y.one('#sub-unsub-spinner'), | ||
1422 | 263 | subscriber: new Y.lp.bugs.subscriber.Subscriber({ | ||
1423 | 264 | uri: LP.client.links.me, | ||
1424 | 265 | subscriber_ids: subscriber_ids | ||
1425 | 266 | }) | ||
1426 | 267 | }); | ||
1427 | 268 | |||
1428 | 269 | var is_direct = subscription.get( | ||
1429 | 270 | 'link').get('parentNode').hasClass('subscribed-true'); | ||
1430 | 271 | var has_dupes = subscription.get( | ||
1431 | 272 | 'link').get('parentNode').hasClass('dup-subscribed-true'); | ||
1432 | 273 | subscription.set('is_direct', is_direct); | ||
1433 | 274 | subscription.set('has_dupes', has_dupes); | ||
1434 | 275 | |||
1435 | 276 | if (subscription.is_node()) { | ||
1436 | 277 | subscription.get('link').on('click', function(e) { | ||
1437 | 278 | e.halt(); | ||
1438 | 279 | subscription.set('can_be_unsubscribed', true); | ||
1439 | 280 | subscription.set('person', subscription.get('subscriber')); | ||
1440 | 281 | subscription.set('is_team', false); | ||
1441 | 282 | var parent = e.target.get('parentNode'); | ||
1442 | 283 | if (namespace.use_advanced_subscriptions) { | ||
1443 | 284 | var subscription_overlay = | ||
1444 | 285 | setup_advanced_subscription_overlay(subscription); | ||
1445 | 286 | subscription_overlay.show(); | ||
1446 | 287 | } else { | ||
1447 | 288 | // Look for the false conditions of subscription, which | ||
1448 | 289 | // is_direct_subscription, etc. don't report correctly, | ||
1449 | 290 | // to make sure we only use subscribe_current_user for | ||
1450 | 291 | // the current user. | ||
1451 | 292 | if (parent.hasClass('subscribed-false') && | ||
1452 | 293 | parent.hasClass('dup-subscribed-false')) { | ||
1453 | 294 | subscribe_current_user(subscription); | ||
1454 | 295 | } | ||
1455 | 296 | else { | ||
1456 | 297 | unsubscribe_current_user(subscription); | ||
1457 | 298 | } | ||
1458 | 299 | } | ||
1459 | 300 | }); | ||
1460 | 301 | subscription.get('link').addClass('js-action'); | ||
1461 | 302 | } | ||
1462 | 303 | |||
1463 | 304 | setup_subscribe_someone_else_handler(subscription); | ||
1464 | 305 | } | ||
1465 | 306 | |||
1466 | 307 | function load_subscribers_from_duplicates() { | ||
1467 | 308 | if (Y.UA.ie) { | ||
1468 | 309 | return null; | ||
1469 | 310 | } | ||
1470 | 311 | |||
1471 | 312 | Y.one('#subscribers-portlet-dupe-spinner').setStyle( | ||
1472 | 313 | 'display', 'block'); | ||
1473 | 314 | |||
1474 | 315 | function hide_spinner() { | ||
1475 | 316 | Y.one('#subscribers-portlet-dupe-spinner').setStyle( | ||
1476 | 317 | 'display', 'none'); | ||
1477 | 318 | // Fire a custom event to signal failure, so that | ||
1478 | 319 | // any remaining unsub icons can be hooked up. | ||
1479 | 320 | namespace.portlet.fire('bugs:dupeportletloadfailed'); | ||
1480 | 321 | } | ||
1481 | 322 | |||
1482 | 323 | function on_success(transactionid, response, args) { | ||
1483 | 324 | hide_spinner(); | ||
1484 | 325 | |||
1485 | 326 | var dupe_subscribers_container = Y.one( | ||
1486 | 327 | '#subscribers-from-duplicates-container'); | ||
1487 | 328 | dupe_subscribers_container.set( | ||
1488 | 329 | 'innerHTML', | ||
1489 | 330 | dupe_subscribers_container.get('innerHTML') + | ||
1490 | 331 | response.responseText); | ||
1491 | 332 | |||
1492 | 333 | // Fire a custom portlet loaded event to notify when | ||
1493 | 334 | // it's safe to setup dupe subscriber link callbacks. | ||
1494 | 335 | namespace.portlet.fire('bugs:dupeportletloaded'); | ||
1495 | 336 | } | ||
1496 | 337 | |||
1497 | 338 | var config = {on: {success: on_success, | ||
1498 | 339 | failure: hide_spinner}}; | ||
1499 | 340 | var url = Y.one( | ||
1500 | 341 | '#subscribers-from-dupes-content-link').getAttribute( | ||
1501 | 342 | 'href').replace('bugs.', ''); | ||
1502 | 343 | Y.io(url, config); | ||
1503 | 344 | } | ||
1504 | 345 | |||
1505 | 346 | /* | ||
1506 | 347 | * Add the user name to the subscriber's list. | ||
1507 | 348 | * | ||
1508 | 349 | * @method add_user_name_link | ||
1509 | 350 | */ | ||
1510 | 351 | function add_user_name_link(subscription) { | ||
1511 | 352 | var person = subscription.get('person'); | ||
1512 | 353 | var link_node = build_user_link_html(subscription); | ||
1513 | 354 | var subscribers = Y.one('#subscribers-links'); | ||
1514 | 355 | if (subscription.is_current_user_subscribing()) { | ||
1515 | 356 | // If this is the current user, then top post the name and be done. | ||
1516 | 357 | subscribers.insertBefore(link_node, subscribers.get('firstChild')); | ||
1517 | 358 | } else { | ||
1518 | 359 | var next = get_next_subscriber_node(subscription); | ||
1519 | 360 | if (next) { | ||
1520 | 361 | subscribers.insertBefore(link_node, next); | ||
1521 | 362 | } else { | ||
1522 | 363 | // Handle the case of the displayed "None". | ||
1523 | 364 | var none_subscribers = Y.one('#none-subscribers'); | ||
1524 | 365 | if (none_subscribers) { | ||
1525 | 366 | var none_parent = none_subscribers.get('parentNode'); | ||
1526 | 367 | none_parent.removeChild(none_subscribers); | ||
1527 | 368 | } | ||
1528 | 369 | subscribers.appendChild(link_node); | ||
1529 | 370 | } | ||
1530 | 371 | } | ||
1531 | 372 | |||
1532 | 373 | // Set the click handler if adding a remove icon. | ||
1533 | 374 | if (subscription.can_be_unsubscribed_by_user()) { | ||
1534 | 375 | var remove_icon = | ||
1535 | 376 | Y.one('#unsubscribe-icon-' + person.get('css_name')); | ||
1536 | 377 | remove_icon.on('click', function(e) { | ||
1537 | 378 | e.halt(); | ||
1538 | 379 | unsubscribe_user_via_icon(e.target, subscription); | ||
1539 | 380 | }); | ||
1540 | 381 | } | ||
1541 | 382 | } | ||
1542 | 383 | |||
1543 | 384 | /* | ||
1544 | 385 | * Unsubscribe a user from this bugtask when a remove icon is clicked. | ||
1545 | 386 | * | ||
1546 | 387 | * @method unsubscribe_user_via_icon | ||
1547 | 388 | * @param icon {Node} The remove icon that was clicked. | ||
1548 | 389 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
1549 | 390 | */ | ||
1550 | 391 | function unsubscribe_user_via_icon(icon, subscription) { | ||
1551 | 392 | icon.set('src', '/@@/spinner'); | ||
1552 | 393 | var icon_parent = icon.get('parentNode'); | ||
1553 | 394 | |||
1554 | 395 | var user_uri = get_user_uri_from_icon(icon); | ||
1555 | 396 | var person = new Y.lp.bugs.subscriber.Subscriber({ | ||
1556 | 397 | uri: user_uri, | ||
1557 | 398 | subscriber_ids: subscriber_ids | ||
1558 | 399 | }); | ||
1559 | 400 | subscription.set('person', person); | ||
1560 | 401 | |||
1561 | 402 | // Determine if this is a dupe. | ||
1562 | 403 | var is_dupe; | ||
1563 | 404 | var icon_parent_div = icon_parent.get('parentNode'); | ||
1564 | 405 | var dupe_id = 'dupe-' + person.get('css_name'); | ||
1565 | 406 | if (icon_parent_div.get('id') == dupe_id) { | ||
1566 | 407 | is_dupe = true; | ||
1567 | 408 | } else { | ||
1568 | 409 | is_dupe = false; | ||
1569 | 410 | } | ||
1570 | 411 | |||
1571 | 412 | var error_handler = new LP.client.ErrorHandler(); | ||
1572 | 413 | error_handler.clearProgressUI = function () { | ||
1573 | 414 | icon.set('src', '/@@/remove'); | ||
1574 | 415 | // Grab the icon again to reset to click handler. | ||
1575 | 416 | var unsubscribe_icon = Y.one( | ||
1576 | 417 | '#unsubscribe-icon-' + person.get('css_name')); | ||
1577 | 418 | unsubscribe_icon.on('click', function(e) { | ||
1578 | 419 | e.halt(); | ||
1579 | 420 | unsubscribe_user_via_icon(e.target, subscription); | ||
1580 | 421 | }); | ||
1581 | 422 | |||
1582 | 423 | }; | ||
1583 | 424 | error_handler.showError = function (error_msg) { | ||
1584 | 425 | var flash_node = Y.one('.' + person.get('css_name')); | ||
1585 | 426 | Y.lp.app.errors.display_error(flash_node, error_msg); | ||
1586 | 427 | |||
1587 | 428 | }; | ||
1588 | 429 | |||
1589 | 430 | var subscription_link = subscription.get('link'); | ||
1590 | 431 | var config = { | ||
1591 | 432 | on: { | ||
1592 | 433 | success: function(client) { | ||
1593 | 434 | icon_parent.removeChild(icon); | ||
1594 | 435 | var anim = Y.lazr.anim.green_flash({ node: icon_parent_div }); | ||
1595 | 436 | anim.on('end', function(e) { | ||
1596 | 437 | remove_user_name_link(icon_parent_div); | ||
1597 | 438 | set_none_for_empty_subscribers(); | ||
1598 | 439 | var person_link = Y.one('.' + person.get('css_name')); | ||
1599 | 440 | if (Y.Lang.isNull(person_link) && | ||
1600 | 441 | subscription.is_current_user_subscribing()) { | ||
1601 | 442 | // Current user has been completely unsubscribed. | ||
1602 | 443 | subscription.disable_spinner( | ||
1603 | 444 | subscription_labels.SUBSCRIBE); | ||
1604 | 445 | set_subscription_link_parent_class( | ||
1605 | 446 | subscription_link, false, false); | ||
1606 | 447 | subscription.set('is_direct', false); | ||
1607 | 448 | subscription.set('has_dupes', false); | ||
1608 | 449 | } else { | ||
1609 | 450 | if (is_dupe) { | ||
1610 | 451 | // A direct subscription remains. | ||
1611 | 452 | set_subscription_link_parent_class( | ||
1612 | 453 | subscription_link, true, false); | ||
1613 | 454 | subscription.set('is_direct', true); | ||
1614 | 455 | subscription.set('has_dupes', false); | ||
1615 | 456 | } else { | ||
1616 | 457 | // A dupe subscription remains. | ||
1617 | 458 | set_subscription_link_parent_class( | ||
1618 | 459 | subscription_link, false, true); | ||
1619 | 460 | subscription.set('is_direct', false); | ||
1620 | 461 | subscription.set('has_dupes', true); | ||
1621 | 462 | } | ||
1622 | 463 | } | ||
1623 | 464 | }); | ||
1624 | 465 | anim.run(); | ||
1625 | 466 | }, | ||
1626 | 467 | |||
1627 | 468 | failure: error_handler.getFailureHandler() | ||
1628 | 469 | } | ||
1629 | 470 | }; | ||
1630 | 471 | |||
1631 | 472 | if (!subscription.is_current_user_subscribing()) { | ||
1632 | 473 | config.parameters = { | ||
1633 | 474 | person: LP.client.get_absolute_uri(user_uri) | ||
1634 | 475 | }; | ||
1635 | 476 | } | ||
1636 | 477 | |||
1637 | 478 | if (is_dupe) { | ||
1638 | 479 | lp_client.named_post( | ||
1639 | 480 | bug_repr.self_link, 'unsubscribeFromDupes', config); | ||
1640 | 481 | } else { | ||
1641 | 482 | lp_client.named_post(bug_repr.self_link, 'unsubscribe', config); | ||
1642 | 483 | } | ||
1643 | 484 | } | ||
1644 | 485 | |||
1645 | 486 | /* | ||
1646 | 487 | * Create and return a FormOverlay for advanced subscription | ||
1647 | 488 | * interactions. | ||
1648 | 489 | * | ||
1649 | 490 | * @method setup_advanced_subscription_overlay | ||
1650 | 491 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
1651 | 492 | */ | ||
1652 | 493 | function setup_advanced_subscription_overlay(subscription) { | ||
1653 | 494 | var subscription_overlay = new Y.lazr.FormOverlay({ | ||
1654 | 495 | headerContent: '<h2>Subscribe to bug</h2>', | ||
1655 | 496 | form_submit_button: | ||
1656 | 497 | Y.Node.create(submit_button_html), | ||
1657 | 498 | form_cancel_button: | ||
1658 | 499 | Y.Node.create(cancel_button_html), | ||
1659 | 500 | centered: true, | ||
1660 | 501 | visible: false | ||
1661 | 502 | }); | ||
1662 | 503 | subscription_overlay.set( | ||
1663 | 504 | 'form_submit_callback', function(form_data) { | ||
1664 | 505 | handle_advanced_subscription_overlay(subscription, form_data); | ||
1665 | 506 | subscription_overlay.hide(); | ||
1666 | 507 | }); | ||
1667 | 508 | |||
1668 | 509 | var subscription_link_url = subscription.get( | ||
1669 | 510 | 'link').get('href') + '/++form++'; | ||
1670 | 511 | subscription_overlay.loadFormContentAndRender( | ||
1671 | 512 | subscription_link_url); | ||
1672 | 513 | subscription_overlay.render('#privacy-form-container'); | ||
1673 | 514 | return subscription_overlay | ||
1674 | 515 | } | ||
1675 | 516 | |||
1676 | 517 | /* | ||
1677 | 518 | * Subscribe the current user via the LP API. | ||
1678 | 519 | * | ||
1679 | 520 | * @method subscribe_current_user | ||
1680 | 521 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
1681 | 522 | */ | ||
1682 | 523 | function subscribe_current_user(subscription) { | ||
1683 | 524 | subscription.enable_spinner('Subscribing...'); | ||
1684 | 525 | var subscription_link = subscription.get('link'); | ||
1685 | 526 | var subscriber = subscription.get('subscriber'); | ||
1686 | 527 | var bug_notification_level = subscription.get('bug_notification_level'); | ||
1687 | 528 | |||
1688 | 529 | var error_handler = new LP.client.ErrorHandler(); | ||
1689 | 530 | error_handler.clearProgressUI = function () { | ||
1690 | 531 | subscription.disable_spinner(); | ||
1691 | 532 | }; | ||
1692 | 533 | error_handler.showError = function (error_msg) { | ||
1693 | 534 | Y.lp.app.errors.display_error(subscription_link, error_msg); | ||
1694 | 535 | }; | ||
1695 | 536 | |||
1696 | 537 | var config = { | ||
1697 | 538 | on: { | ||
1698 | 539 | success: function(client) { | ||
1699 | 540 | if (namespace.use_advanced_subscriptions) { | ||
1700 | 541 | subscription.disable_spinner( | ||
1701 | 542 | subscription_labels.EDIT); | ||
1702 | 543 | } else { | ||
1703 | 544 | subscription.disable_spinner( | ||
1704 | 545 | subscription_labels.UNSUBSCRIBE); | ||
1705 | 546 | } | ||
1706 | 547 | |||
1707 | 548 | if (subscription.has_duplicate_subscriptions()) { | ||
1708 | 549 | set_subscription_link_parent_class( | ||
1709 | 550 | subscription_link, true, true); | ||
1710 | 551 | } else { | ||
1711 | 552 | set_subscription_link_parent_class( | ||
1712 | 553 | subscription_link, true, false); | ||
1713 | 554 | } | ||
1714 | 555 | |||
1715 | 556 | // Handle the case where the subscriber's list displays | ||
1716 | 557 | // "None". | ||
1717 | 558 | var empty_subscribers = Y.one("#none-subscribers"); | ||
1718 | 559 | if (empty_subscribers) { | ||
1719 | 560 | var parent = empty_subscribers.get('parentNode'); | ||
1720 | 561 | parent.removeChild(empty_subscribers); | ||
1721 | 562 | } | ||
1722 | 563 | |||
1723 | 564 | add_user_name_link(subscription); | ||
1724 | 565 | |||
1725 | 566 | var flash_node = Y.one('.' + subscriber.get('css_name')); | ||
1726 | 567 | var anim = Y.lazr.anim.green_flash({ node: flash_node }); | ||
1727 | 568 | anim.run(); | ||
1728 | 569 | }, | ||
1729 | 570 | |||
1730 | 571 | failure: error_handler.getFailureHandler() | ||
1731 | 572 | }, | ||
1732 | 573 | |||
1733 | 574 | parameters: { | ||
1734 | 575 | person: LP.client.get_absolute_uri(subscriber.get('escaped_uri')), | ||
1735 | 576 | suppress_notify: false, | ||
1736 | 577 | level: bug_notification_level | ||
1737 | 578 | } | ||
1738 | 579 | }; | ||
1739 | 580 | lp_client.named_post(bug_repr.self_link, 'subscribe', config); | ||
1740 | 581 | } | ||
1741 | 582 | |||
1742 | 583 | /* | ||
1743 | 584 | * Unsubscribe the current user via the LP API. | ||
1744 | 585 | * | ||
1745 | 586 | * @method unsubscribe_current_user | ||
1746 | 587 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
1747 | 588 | */ | ||
1748 | 589 | function unsubscribe_current_user(subscription) { | ||
1749 | 590 | subscription.enable_spinner('Unsubscribing...'); | ||
1750 | 591 | var subscription_link = subscription.get('link'); | ||
1751 | 592 | var subscriber = subscription.get('subscriber'); | ||
1752 | 593 | |||
1753 | 594 | var error_handler = new LP.client.ErrorHandler(); | ||
1754 | 595 | error_handler.clearProgressUI = function () { | ||
1755 | 596 | subscription.disable_spinner(); | ||
1756 | 597 | }; | ||
1757 | 598 | error_handler.showError = function (error_msg) { | ||
1758 | 599 | Y.lp.app.errors.display_error(subscription_link, error_msg); | ||
1759 | 600 | }; | ||
1760 | 601 | |||
1761 | 602 | var config = { | ||
1762 | 603 | on: { | ||
1763 | 604 | success: function(client) { | ||
1764 | 605 | if (subscription.is_direct_subscription() && | ||
1765 | 606 | subscription.has_duplicate_subscriptions()) { | ||
1766 | 607 | // Don't change the 'Unsubscribe' text if | ||
1767 | 608 | // dupe subscriptions remain. | ||
1768 | 609 | subscription.disable_spinner(); | ||
1769 | 610 | set_subscription_link_parent_class( | ||
1770 | 611 | subscription_link, false, true); | ||
1771 | 612 | subscription.set('is_direct', false); | ||
1772 | 613 | } else if (subscription.is_direct_subscription() && | ||
1773 | 614 | !subscription.has_duplicate_subscriptions()) { | ||
1774 | 615 | // Only unsub'ing a direct subscriber here. | ||
1775 | 616 | subscription.disable_spinner( | ||
1776 | 617 | subscription_labels.SUBSCRIBE); | ||
1777 | 618 | set_subscription_link_parent_class( | ||
1778 | 619 | subscription_link, false, false); | ||
1779 | 620 | subscription.set('is_direct', false); | ||
1780 | 621 | } else { | ||
1781 | 622 | // Only unsub'ing dupes here. | ||
1782 | 623 | subscription.disable_spinner( | ||
1783 | 624 | subscription_labels.SUBSCRIBE); | ||
1784 | 625 | set_subscription_link_parent_class( | ||
1785 | 626 | subscription_link, false, false); | ||
1786 | 627 | subscription.set('has_dupes', false); | ||
1787 | 628 | } | ||
1788 | 629 | |||
1789 | 630 | var flash_node = Y.one('.' + subscriber.get('css_name')); | ||
1790 | 631 | var anim = Y.lazr.anim.green_flash({ node: flash_node }); | ||
1791 | 632 | anim.on('end', function(e) { | ||
1792 | 633 | remove_user_name_link(flash_node); | ||
1793 | 634 | set_none_for_empty_subscribers(); | ||
1794 | 635 | }); | ||
1795 | 636 | anim.run(); | ||
1796 | 637 | }, | ||
1797 | 638 | |||
1798 | 639 | failure: error_handler.getFailureHandler() | ||
1799 | 640 | } | ||
1800 | 641 | }; | ||
1801 | 642 | if (subscription.is_direct_subscription()) { | ||
1802 | 643 | lp_client.named_post(bug_repr.self_link, 'unsubscribe', config); | ||
1803 | 644 | } else { | ||
1804 | 645 | lp_client.named_post( | ||
1805 | 646 | bug_repr.self_link, 'unsubscribeFromDupes', config); | ||
1806 | 647 | } | ||
1807 | 648 | } | ||
1808 | 649 | |||
1809 | 650 | /* | ||
1810 | 651 | * Initialize click handler for the subscribe someone else link. | ||
1811 | 652 | * | ||
1812 | 653 | * @method setup_subscribe_someone_else_handler | ||
1813 | 654 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
1814 | 655 | */ | ||
1815 | 656 | function setup_subscribe_someone_else_handler(subscription) { | ||
1816 | 657 | var config = { | ||
1817 | 658 | header: 'Subscribe someone else', | ||
1818 | 659 | step_title: 'Search', | ||
1819 | 660 | picker_activator: '.menu-link-addsubscriber' | ||
1820 | 661 | }; | ||
1821 | 662 | |||
1822 | 663 | config.save = function(result) { | ||
1823 | 664 | subscribe_someone_else(result, subscription); | ||
1824 | 665 | }; | ||
1825 | 666 | var picker = Y.lp.app.picker.create('ValidPersonOrTeam', config); | ||
1826 | 667 | } | ||
1827 | 668 | |||
1828 | 669 | /* | ||
1829 | 670 | * Build the HTML for a user link for the subscribers list. | ||
1830 | 671 | * | ||
1831 | 672 | * @method build_user_link_html | ||
1832 | 673 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
1833 | 674 | * @return html {String} The HTML used for creating a subscriber link. | ||
1834 | 675 | */ | ||
1835 | 676 | function build_user_link_html(subscription) { | ||
1836 | 677 | var name = subscription.get('person').get('name'); | ||
1837 | 678 | var css_name = subscription.get('person').get('css_name'); | ||
1838 | 679 | var full_name = subscription.get('person').get('full_display_name'); | ||
1839 | 680 | // Be paranoid about display_name, since timeouts or other errors | ||
1840 | 681 | // could mean display_name wasn't set on initialization. | ||
1841 | 682 | if (subscription.get('person').get('display_name') === '') { | ||
1842 | 683 | subscription.get('person').set_display_name(); | ||
1843 | 684 | } | ||
1844 | 685 | var display_name = subscription.get('person').get('display_name'); | ||
1845 | 686 | var terms = { | ||
1846 | 687 | name: name, | ||
1847 | 688 | css_name: css_name, | ||
1848 | 689 | display_name: display_name, | ||
1849 | 690 | full_name: full_name | ||
1850 | 691 | }; | ||
1851 | 692 | |||
1852 | 693 | if (subscription.is_current_user_subscribing()) { | ||
1853 | 694 | terms.subscribed_by = 'themselves'; | ||
1854 | 695 | } else { | ||
1855 | 696 | terms.subscribed_by = 'by ' + full_name; | ||
1856 | 697 | } | ||
1857 | 698 | |||
1858 | 699 | var html = Y.Node.create('<div><a></a></div>'); | ||
1859 | 700 | html.addClass(terms.css_name); | ||
1860 | 701 | |||
1861 | 702 | if (subscription.is_direct_subscription()) { | ||
1862 | 703 | html.set('id', 'direct-' + terms.css_name); | ||
1863 | 704 | } else { | ||
1864 | 705 | html.set('id', 'dupe-' + terms.css_name); | ||
1865 | 706 | } | ||
1866 | 707 | |||
1867 | 708 | html.one('a') | ||
1868 | 709 | .set('href', '/~' + terms.name) | ||
1869 | 710 | .set('name', terms.full_name) | ||
1870 | 711 | .set('title', 'Subscribed ' + terms.subscribed_by); | ||
1871 | 712 | |||
1872 | 713 | var span; | ||
1873 | 714 | if (subscription.is_team()) { | ||
1874 | 715 | span = '<span class="sprite team"></span>'; | ||
1875 | 716 | } else { | ||
1876 | 717 | span = '<span class="sprite person"></span>'; | ||
1877 | 718 | } | ||
1878 | 719 | |||
1879 | 720 | html.one('a') | ||
1880 | 721 | .appendChild(Y.Node.create(span)) | ||
1881 | 722 | .appendChild(document.createTextNode(terms.display_name)); | ||
1882 | 723 | |||
1883 | 724 | // Add remove icon if the current user can unsubscribe the subscriber. | ||
1884 | 725 | if (subscription.can_be_unsubscribed_by_user()) { | ||
1885 | 726 | var icon_html = Y.Node.create( | ||
1886 | 727 | '<a href="+subscribe">' + | ||
1887 | 728 | '<img class="unsub-icon" src="/@@/remove" alt="Remove" /></a>'); | ||
1888 | 729 | icon_html | ||
1889 | 730 | .set('id', 'unsubscribe-' + terms.css_name) | ||
1890 | 731 | .set('title', 'Unsubscribe ' + terms.full_name); | ||
1891 | 732 | icon_html.one('img') | ||
1892 | 733 | .set('id', 'unsubscribe-icon-' + terms.css_name); | ||
1893 | 734 | html.appendChild(icon_html); | ||
1894 | 735 | } | ||
1895 | 736 | |||
1896 | 737 | return html; | ||
1897 | 738 | } | ||
1898 | 739 | |||
1899 | 740 | /* | ||
1900 | 741 | * Returns the next node in alphabetical order after the subscriber | ||
1901 | 742 | * node now being added. No node is returned to append to end of list. | ||
1902 | 743 | * | ||
1903 | 744 | * The name can appear in one of two different lists. 1) The list of | ||
1904 | 745 | * subscribers that can be unsubscribed by the current user, and | ||
1905 | 746 | * 2) the list of subscribers that cannot be unsubscribed. | ||
1906 | 747 | * | ||
1907 | 748 | * @method get_next_subscriber_node | ||
1908 | 749 | * @param subscription_link {Node} The sub/unsub link. | ||
1909 | 750 | * @return {Node} The node appearing next in the subscriber list or | ||
1910 | 751 | * undefined if no node is next. | ||
1911 | 752 | */ | ||
1912 | 753 | function get_next_subscriber_node(subscription) { | ||
1913 | 754 | var full_name = subscription.get('person').get('full_display_name'); | ||
1914 | 755 | var can_be_unsubscribed = subscription.can_be_unsubscribed_by_user(); | ||
1915 | 756 | var nodes_by_name = {}; | ||
1916 | 757 | var unsubscribables = []; | ||
1917 | 758 | var not_unsubscribables = []; | ||
1918 | 759 | |||
1919 | 760 | // Use the list of subscribers pulled from the DOM to have sortable | ||
1920 | 761 | // lists of unsubscribable vs. not unsubscribale person links. | ||
1921 | 762 | var all_subscribers = Y.all('#subscribers-links div'); | ||
1922 | 763 | if (all_subscribers.size() > 0) { | ||
1923 | 764 | all_subscribers.each(function(sub_link) { | ||
1924 | 765 | if (sub_link.getAttribute('id') != 'temp-username') { | ||
1925 | 766 | // User's displayname is found via the link's "name" | ||
1926 | 767 | // attribute. | ||
1927 | 768 | var sub_link_name = sub_link.one('a').getAttribute('name'); | ||
1928 | 769 | nodes_by_name[sub_link_name] = sub_link; | ||
1929 | 770 | if (sub_link.one('img.unsub-icon')) { | ||
1930 | 771 | unsubscribables.push(sub_link_name); | ||
1931 | 772 | } else { | ||
1932 | 773 | not_unsubscribables.push(sub_link_name); | ||
1933 | 774 | } | ||
1934 | 775 | } | ||
1935 | 776 | }); | ||
1936 | 777 | |||
1937 | 778 | // Add the current subscription. | ||
1938 | 779 | if (can_be_unsubscribed) { | ||
1939 | 780 | unsubscribables.push(full_name); | ||
1940 | 781 | } else { | ||
1941 | 782 | not_unsubscribables.push(full_name); | ||
1942 | 783 | } | ||
1943 | 784 | unsubscribables.sort(); | ||
1944 | 785 | not_unsubscribables.sort(); | ||
1945 | 786 | } else { | ||
1946 | 787 | // If there is no all_subscribers, then we're dealing with | ||
1947 | 788 | // the printed None, so return. | ||
1948 | 789 | return; | ||
1949 | 790 | } | ||
1950 | 791 | |||
1951 | 792 | var i; | ||
1952 | 793 | if ((!unsubscribables && !not_unsubscribables) || | ||
1953 | 794 | // If A) neither list exists, B) the user belongs in the second | ||
1954 | 795 | // list but the second list doesn't exist, or C) user belongs in the | ||
1955 | 796 | // first list and the second doesn't exist, return no node to append. | ||
1956 | 797 | (!can_be_unsubscribed && !not_unsubscribables) || | ||
1957 | 798 | (can_be_unsubscribed && unsubscribables && !not_unsubscribables)) { | ||
1958 | 799 | return; | ||
1959 | 800 | } else if ( | ||
1960 | 801 | // If the user belongs in the first list, and the first list | ||
1961 | 802 | // doesn't exist, but the second one does, return the first node | ||
1962 | 803 | // in the second list. | ||
1963 | 804 | can_be_unsubscribed && !unsubscribables && not_unsubscribables) { | ||
1964 | 805 | return nodes_by_name[not_unsubscribables[0]]; | ||
1965 | 806 | } else if (can_be_unsubscribed) { | ||
1966 | 807 | // If the user belongs in the first list, loop the list for position. | ||
1967 | 808 | for (i=0; i<unsubscribables.length; i++) { | ||
1968 | 809 | if (unsubscribables[i] == full_name) { | ||
1969 | 810 | if (i+1 < unsubscribables.length) { | ||
1970 | 811 | return nodes_by_name[unsubscribables[i+1]]; | ||
1971 | 812 | // If the current link should go at the end of the first | ||
1972 | 813 | // list and we're at the end of that list, return the | ||
1973 | 814 | // first node of the second list. Due to earlier checks | ||
1974 | 815 | // we can be sure this list exists. | ||
1975 | 816 | } else if (i+1 >= unsubscribables.length) { | ||
1976 | 817 | return nodes_by_name[not_unsubscribables[0]]; | ||
1977 | 818 | } | ||
1978 | 819 | } | ||
1979 | 820 | } | ||
1980 | 821 | } else if (!can_be_unsubscribed) { | ||
1981 | 822 | // If user belongs in the second list, loop the list for position. | ||
1982 | 823 | for (i=0; i<not_unsubscribables.length; i++) { | ||
1983 | 824 | if (not_unsubscribables[i] == full_name) { | ||
1984 | 825 | if (i+1 < not_unsubscribables.length) { | ||
1985 | 826 | return nodes_by_name[not_unsubscribables[i+1]]; | ||
1986 | 827 | } else { | ||
1987 | 828 | return; | ||
1988 | 829 | } | ||
1989 | 830 | } | ||
1990 | 831 | } | ||
1991 | 832 | } | ||
1992 | 833 | } | ||
1993 | 834 | |||
1994 | 835 | /* | ||
1995 | 836 | * Traverse the DOM of a given remove icon to find | ||
1996 | 837 | * the user's link. Returns a URI of the form "/~username". | ||
1997 | 838 | * | ||
1998 | 839 | * @method get_user_uri_from_icon | ||
1999 | 840 | * @param icon {Node} The node representing a remove icon. | ||
2000 | 841 | * @return user_uri {String} The user's uri, without the hostname. | ||
2001 | 842 | */ | ||
2002 | 843 | function get_user_uri_from_icon(icon) { | ||
2003 | 844 | var parent_div = icon.get('parentNode').get('parentNode'); | ||
2004 | 845 | // This should be parent_div.firstChild, but because of #text | ||
2005 | 846 | // and cross-browser issues, using the YUI query syntax is | ||
2006 | 847 | // safer here. | ||
2007 | 848 | var user_uri = parent_div.one('a').getAttribute('href'); | ||
2008 | 849 | |||
2009 | 850 | // Strip the domain off. We just want a path. | ||
2010 | 851 | var host_start = user_uri.indexOf('//'); | ||
2011 | 852 | if (host_start != -1) { | ||
2012 | 853 | var host_end = user_uri.indexOf('/', host_start+2); | ||
2013 | 854 | return user_uri.substring(host_end, user_uri.length); | ||
2014 | 855 | } | ||
2015 | 856 | |||
2016 | 857 | return user_uri; | ||
2017 | 858 | } | ||
2018 | 859 | |||
2019 | 860 | /* | ||
2020 | 861 | * Used to remove the user's name from the subscriber's list. | ||
2021 | 862 | * | ||
2022 | 863 | * @method remove_user_name_link | ||
2023 | 864 | * @param user_node {Node} Node representing the user name link. | ||
2024 | 865 | */ | ||
2025 | 866 | function remove_user_name_link(user_node) { | ||
2026 | 867 | var parent = user_node.get('parentNode'); | ||
2027 | 868 | parent.removeChild(user_node); | ||
2028 | 869 | } | ||
2029 | 870 | |||
2030 | 871 | /* | ||
2031 | 872 | * Add the "None" div to the subscribers list if | ||
2032 | 873 | * there aren't any subscribers left. | ||
2033 | 874 | * | ||
2034 | 875 | * @method set_none_for_empty_subscribers | ||
2035 | 876 | */ | ||
2036 | 877 | function set_none_for_empty_subscribers() { | ||
2037 | 878 | var subscriber_list = Y.one('#subscribers-links'); | ||
2038 | 879 | // Assume if subscriber_list has no child divs | ||
2039 | 880 | // then the list of subscribers is empty. | ||
2040 | 881 | if (!Y.Lang.isValue(subscriber_list.one('div')) && | ||
2041 | 882 | !Y.Lang.isValue(Y.one('#none-subscribers'))) { | ||
2042 | 883 | var none_div = Y.Node.create('<div id="none-subscribers">None</div>'); | ||
2043 | 884 | subscriber_list.appendChild(none_div); | ||
2044 | 885 | } | ||
2045 | 886 | |||
2046 | 887 | // Clear the empty duplicate subscribers list if it exists. | ||
2047 | 888 | var dup_list = Y.one('#subscribers-from-duplicates'); | ||
2048 | 889 | if (Y.Lang.isValue(dup_list) && | ||
2049 | 890 | !Y.Lang.isValue(dup_list.one('div'))) { | ||
2050 | 891 | var parent = dup_list.get('parentNode'); | ||
2051 | 892 | parent.removeChild(dup_list); | ||
2052 | 893 | } | ||
2053 | 894 | } | ||
2054 | 895 | |||
2055 | 896 | /* | ||
2056 | 897 | * Set the class on subscription link's parentNode. | ||
2057 | 898 | * | ||
2058 | 899 | * This is used to reset the class used by the | ||
2059 | 900 | * click handler to know which link was clicked. | ||
2060 | 901 | * | ||
2061 | 902 | * @method set_subscription_link_parent_class | ||
2062 | 903 | * @param subscription_link {Node} The sub/unsub link. | ||
2063 | 904 | * @param subscribed {Boolean} The sub/unsub'ed flag for the class. | ||
2064 | 905 | * @param dupe_subscribed {Boolean} The sub/unsub'ed flag for dupes | ||
2065 | 906 | * on the class. | ||
2066 | 907 | */ | ||
2067 | 908 | function set_subscription_link_parent_class( | ||
2068 | 909 | user_link, subscribed, dupe_subscribed) { | ||
2069 | 910 | |||
2070 | 911 | var parent = user_link.get('parentNode'); | ||
2071 | 912 | if (subscribed) { | ||
2072 | 913 | parent.removeClass('subscribed-false'); | ||
2073 | 914 | parent.addClass('subscribed-true'); | ||
2074 | 915 | } else { | ||
2075 | 916 | parent.removeClass('subscribed-true'); | ||
2076 | 917 | parent.addClass('subscribed-false'); | ||
2077 | 918 | } | ||
2078 | 919 | |||
2079 | 920 | if (dupe_subscribed) { | ||
2080 | 921 | parent.removeClass('dup-subscribed-false'); | ||
2081 | 922 | parent.addClass('dup-subscribed-true'); | ||
2082 | 923 | } else { | ||
2083 | 924 | parent.removeClass('dup-subscribed-true'); | ||
2084 | 925 | parent.addClass('dup-subscribed-false'); | ||
2085 | 926 | } | ||
2086 | 927 | } | ||
2087 | 928 | |||
2088 | 929 | |||
2089 | 930 | /* | ||
2090 | 931 | * Handle the advanced_subscription_overlay's form submissions. | ||
2091 | 932 | * | ||
2092 | 933 | * @method handle_advanced_subscription_overlay | ||
2093 | 934 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
2094 | 935 | * @param form_data {Object} The data from the submitted form. | ||
2095 | 936 | */ | ||
2096 | 937 | function handle_advanced_subscription_overlay(subscription, form_data) { | ||
2097 | 938 | var link = subscription.get('link'); | ||
2098 | 939 | var link_parent = link.get('parentNode'); | ||
2099 | 940 | if (link_parent.hasClass('subscribed-false') && | ||
2100 | 941 | link_parent.hasClass('dup-subscribed-false')) { | ||
2101 | 942 | // The user isn't subscribed, so subscribe them. | ||
2102 | 943 | subscription.set( | ||
2103 | 944 | 'bug_notification_level', | ||
2104 | 945 | form_data['field.bug_notification_level']); | ||
2105 | 946 | subscribe_current_user(subscription); | ||
2106 | 947 | } else if ( | ||
2107 | 948 | form_data['field.subscription'] == 'update-subscription') { | ||
2108 | 949 | // The user is already subscribed and wants to update their | ||
2109 | 950 | // subscription. | ||
2110 | 951 | setup_client_and_bug(); | ||
2111 | 952 | var person_name = subscription.get('person').get('name'); | ||
2112 | 953 | var subscription_url = | ||
2113 | 954 | lp_bug_entry.get('self_link') + '/+subscription/' + | ||
2114 | 955 | person_name; | ||
2115 | 956 | config = { | ||
2116 | 957 | on: { | ||
2117 | 958 | success: function(lp_subscription) { | ||
2118 | 959 | subscription.enable_spinner('Updating subscription...'); | ||
2119 | 960 | lp_subscription.set( | ||
2120 | 961 | 'bug_notification_level', | ||
2121 | 962 | form_data['field.bug_notification_level'][0]) | ||
2122 | 963 | save_config = { | ||
2123 | 964 | on: { | ||
2124 | 965 | success: function(e) { | ||
2125 | 966 | subscription.disable_spinner( | ||
2126 | 967 | 'Edit subscription'); | ||
2127 | 968 | var anim = Y.lazr.anim.green_flash({ | ||
2128 | 969 | node: link_parent | ||
2129 | 970 | }); | ||
2130 | 971 | anim.run(); | ||
2131 | 972 | }, | ||
2132 | 973 | failure: function(e) { | ||
2133 | 974 | subscription.disable_spinner( | ||
2134 | 975 | 'Edit subscription'); | ||
2135 | 976 | var anim = Y.lazr.anim.red_flash({ | ||
2136 | 977 | node: link_parent | ||
2137 | 978 | }); | ||
2138 | 979 | anim.run(); | ||
2139 | 980 | } | ||
2140 | 981 | } | ||
2141 | 982 | } | ||
2142 | 983 | lp_subscription.lp_save(save_config); | ||
2143 | 984 | } | ||
2144 | 985 | } | ||
2145 | 986 | } | ||
2146 | 987 | lp_client.get(subscription_url, config); | ||
2147 | 988 | } else { | ||
2148 | 989 | // The user is already subscribed and wants to unsubscribe. | ||
2149 | 990 | unsubscribe_current_user(subscription); | ||
2150 | 991 | } | ||
2151 | 992 | } | ||
2152 | 993 | |||
2153 | 994 | /* | ||
2154 | 995 | * Subscribe a person or team other than the current user. | ||
2155 | 996 | * This is a callback for the subscribe someone else picker. | ||
2156 | 997 | * | ||
2157 | 998 | * @method subscribe_someone_else | ||
2158 | 999 | * @result {Object} The object representing a person returned by the API. | ||
2159 | 1000 | */ | ||
2160 | 1001 | function subscribe_someone_else(result, subscription) { | ||
2161 | 1002 | var person = new Y.lp.bugs.subscriber.Subscriber({ | ||
2162 | 1003 | uri: result.api_uri, | ||
2163 | 1004 | display_name: result.title, | ||
2164 | 1005 | subscriber_ids: subscriber_ids | ||
2165 | 1006 | }); | ||
2166 | 1007 | subscription.set('person', person); | ||
2167 | 1008 | |||
2168 | 1009 | var error_handler = new LP.client.ErrorHandler(); | ||
2169 | 1010 | error_handler.showError = function(error_msg) { | ||
2170 | 1011 | Y.lp.app.errors.display_error( | ||
2171 | 1012 | Y.one('.menu-link-addsubscriber'), error_msg); | ||
2172 | 1013 | }; | ||
2173 | 1014 | |||
2174 | 1015 | if (subscription.is_already_subscribed()) { | ||
2175 | 1016 | error_handler.showError( | ||
2176 | 1017 | subscription.get('person').get('full_display_name') + | ||
2177 | 1018 | ' has already been subscribed'); | ||
2178 | 1019 | } else { | ||
2179 | 1020 | check_can_be_unsubscribed(subscription); | ||
2180 | 1021 | } | ||
2181 | 1022 | } | ||
2182 | 1023 | |||
2183 | 1024 | /* | ||
2184 | 1025 | * Check if the current user can unsubscribe the person | ||
2185 | 1026 | * being subscribed. | ||
2186 | 1027 | * | ||
2187 | 1028 | * This must be done in JavaScript, since the subscription | ||
2188 | 1029 | * hasn't completed yet, and so, can_be_unsubscribed_by_user | ||
2189 | 1030 | * cannot be used. | ||
2190 | 1031 | * | ||
2191 | 1032 | * @method check_can_be_unsubscribed | ||
2192 | 1033 | * @param subscription {Object} A Y.lp.bugs.subscriber.Subscription object. | ||
2193 | 1034 | */ | ||
2194 | 1035 | function check_can_be_unsubscribed(subscription) { | ||
2195 | 1036 | var error_handler = new LP.client.ErrorHandler(); | ||
2196 | 1037 | error_handler.showError = function (error_msg) { | ||
2197 | 1038 | Y.lp.app.errors.display_error( | ||
2198 | 1039 | Y.one('.menu-link-addsubscriber'), error_msg); | ||
2199 | 1040 | }; | ||
2200 | 1041 | |||
2201 | 1042 | var config = { | ||
2202 | 1043 | on: { | ||
2203 | 1044 | success: function(result) { | ||
2204 | 1045 | var is_team = result.get('is_team'); | ||
2205 | 1046 | subscription.set('is_team', is_team); | ||
2206 | 1047 | var final_config = { | ||
2207 | 1048 | on: { | ||
2208 | 1049 | success: function(result) { | ||
2209 | 1050 | var team_member = false; | ||
2210 | 1051 | for (var i=0; i<result.entries.length; i++) { | ||
2211 | 1052 | if (result.entries[i].member_link == | ||
2212 | 1053 | LP.client.get_absolute_uri( | ||
2213 | 1054 | subscription.get( | ||
2214 | 1055 | 'subscriber').get('uri'))) { | ||
2215 | 1056 | team_member = true; | ||
2216 | 1057 | } | ||
2217 | 1058 | } | ||
2218 | 1059 | |||
2219 | 1060 | if (team_member) { | ||
2220 | 1061 | subscription.set('can_be_unsubscribed', true); | ||
2221 | 1062 | add_temp_user_name(subscription); | ||
2222 | 1063 | } else { | ||
2223 | 1064 | subscription.set( | ||
2224 | 1065 | 'can_be_unsubscribed', false); | ||
2225 | 1066 | add_temp_user_name(subscription); | ||
2226 | 1067 | } | ||
2227 | 1068 | }, | ||
2228 | 1069 | |||
2229 | 1070 | failure: error_handler.getFailureHandler() | ||
2230 | 1071 | } | ||
2231 | 1072 | }; | ||
2232 | 1073 | |||
2233 | 1074 | if (is_team) { | ||
2234 | 1075 | // Get a list of members to see if current user | ||
2235 | 1076 | // is a team member. | ||
2236 | 1077 | var members = result.get( | ||
2237 | 1078 | 'members_details_collection_link'); | ||
2238 | 1079 | lp_client.get(members, final_config); | ||
2239 | 1080 | } else { | ||
2240 | 1081 | subscription.set('can_be_unsubscribed', false); | ||
2241 | 1082 | add_temp_user_name(subscription); | ||
2242 | 1083 | } | ||
2243 | 1084 | }, | ||
2244 | 1085 | |||
2245 | 1086 | failure: error_handler.getFailureHandler() | ||
2246 | 1087 | } | ||
2247 | 1088 | }; | ||
2248 | 1089 | lp_client.get(LP.client.get_absolute_uri( | ||
2249 | 1090 | subscription.get('person').get('escaped_uri')), config); | ||
2250 | 1091 | } | ||
2251 | 1092 | |||
2252 | 1093 | /* | ||
2253 | 1094 | * Add a grayed out, temporary user name when subscribing | ||
2254 | 1095 | * someone else. | ||
2255 | 1096 | * | ||
2256 | 1097 | * @method add_temp_user_name | ||
2257 | 1098 | * @param subscription_link {Node} The sub/unsub link. | ||
2258 | 1099 | */ | ||
2259 | 1100 | function add_temp_user_name(subscription) { | ||
2260 | 1101 | // Be paranoid about display_name, since timeouts or other errors | ||
2261 | 1102 | // could mean display_name wasn't set on initialization. | ||
2262 | 1103 | if (subscription.get('person').get('display_name') === '') { | ||
2263 | 1104 | subscription.get('person').set_display_name(); | ||
2264 | 1105 | } | ||
2265 | 1106 | var display_name = subscription.get('person').get('display_name'); | ||
2266 | 1107 | var img_src; | ||
2267 | 1108 | if (subscription.is_team()) { | ||
2268 | 1109 | img_src = '/@@/teamgray'; | ||
2269 | 1110 | } else { | ||
2270 | 1111 | img_src = '/@@/persongray'; | ||
2271 | 1112 | } | ||
2272 | 1113 | |||
2273 | 1114 | // The <span>...</span> below must *not* be <span/>. On FF (maybe | ||
2274 | 1115 | // others, but at least on FF 3.0.11) will then not notice any | ||
2275 | 1116 | // following sibling nodes, like the spinner image. | ||
2276 | 1117 | var link_node = Y.Node.create([ | ||
2277 | 1118 | '<div id="temp-username"> ', | ||
2278 | 1119 | ' <img alt="" width="14" height="14" />', | ||
2279 | 1120 | ' <span>Other Display Name</span>', | ||
2280 | 1121 | ' <img id="temp-name-spinner" src="/@@/spinner" alt="" ', | ||
2281 | 1122 | ' style="position:absolute;right:8px" /></div>'].join('')); | ||
2282 | 1123 | link_node.one('img').set('src', img_src); | ||
2283 | 1124 | link_node.replaceChild( | ||
2284 | 1125 | document.createTextNode(display_name), | ||
2285 | 1126 | link_node.one('span')); | ||
2286 | 1127 | |||
2287 | 1128 | var subscribers = Y.one('#subscribers-links'); | ||
2288 | 1129 | var next = get_next_subscriber_node(subscription); | ||
2289 | 1130 | if (next) { | ||
2290 | 1131 | subscribers.insertBefore(link_node, next); | ||
2291 | 1132 | } else { | ||
2292 | 1133 | // Handle the case of the displayed "None". | ||
2293 | 1134 | var none_subscribers = Y.one('#none-subscribers'); | ||
2294 | 1135 | if (none_subscribers) { | ||
2295 | 1136 | var none_parent = none_subscribers.get('parentNode'); | ||
2296 | 1137 | none_parent.removeChild(none_subscribers); | ||
2297 | 1138 | } | ||
2298 | 1139 | subscribers.appendChild(link_node); | ||
2299 | 1140 | } | ||
2300 | 1141 | |||
2301 | 1142 | // Fire a custom event to know it's safe to begin | ||
2302 | 1143 | // any actual subscribing work. | ||
2303 | 1144 | namespace.portlet.fire('bugs:nameloaded', subscription); | ||
2304 | 1145 | } | ||
2305 | 1146 | |||
2306 | 1147 | }, "0.1", {"requires": ["base", "oop", "node", "event", "io-base", | ||
2307 | 1148 | "json-parse", "substitute", "widget-position-ext", | ||
2308 | 1149 | "lazr.formoverlay", "lazr.anim", "lazr.base", | ||
2309 | 1150 | "lazr.overlay", "lazr.choiceedit", "lp.app.picker", | ||
2310 | 1151 | "lp.client.plugins", "lp.bugs.subscriber", | ||
2311 | 1152 | "lp.app.errors"]}); | ||
2312 | 0 | 1153 | ||
2313 | === modified file 'lib/lp/bugs/templates/bug-portlet-subscribers.pt' | |||
2314 | --- lib/lp/bugs/templates/bug-portlet-subscribers.pt 2010-06-15 13:51:55 +0000 | |||
2315 | +++ lib/lp/bugs/templates/bug-portlet-subscribers.pt 2011-02-15 18:19:47 +0000 | |||
2316 | @@ -25,7 +25,7 @@ | |||
2317 | 25 | <img src="/@@/spinner" /> | 25 | <img src="/@@/spinner" /> |
2318 | 26 | </div> | 26 | </div> |
2319 | 27 | <script type="text/javascript"> | 27 | <script type="text/javascript"> |
2321 | 28 | LPS.use('io-base', 'node', 'lp.bugs.bugtask_index', function(Y) { | 28 | LPS.use('io-base', 'node', 'lp.bugs.bugtask_index.portlets', function(Y) { |
2322 | 29 | // Must be done inline here to ensure the load event fires. | 29 | // Must be done inline here to ensure the load event fires. |
2323 | 30 | // This is a work around for a YUI3 issue with event handling. | 30 | // This is a work around for a YUI3 issue with event handling. |
2324 | 31 | var subscription_link = Y.one('.menu-link-subscription'); | 31 | var subscription_link = Y.one('.menu-link-subscription'); |
2325 | @@ -37,8 +37,8 @@ | |||
2326 | 37 | } | 37 | } |
2327 | 38 | 38 | ||
2328 | 39 | Y.on('domready', function() { | 39 | Y.on('domready', function() { |
2331 | 40 | if (Y.lp.bugs.bugtask_index) { | 40 | if (Y.lp.bugs.bugtask_index.portlets) { |
2332 | 41 | Y.lp.bugs.bugtask_index.load_subscribers_portlet( | 41 | Y.lp.bugs.bugtask_index.portlets.load_subscribers_portlet( |
2333 | 42 | subscription_link, subscription_link_handler); | 42 | subscription_link, subscription_link_handler); |
2334 | 43 | } | 43 | } |
2335 | 44 | }); | 44 | }); |
2336 | 45 | 45 | ||
2337 | === modified file 'lib/lp/bugs/templates/bugtask-index.pt' | |||
2338 | --- lib/lp/bugs/templates/bugtask-index.pt 2011-02-07 11:44:51 +0000 | |||
2339 | +++ lib/lp/bugs/templates/bugtask-index.pt 2011-02-15 18:19:47 +0000 | |||
2340 | @@ -23,8 +23,9 @@ | |||
2341 | 23 | </tal:subscriptions-js> | 23 | </tal:subscriptions-js> |
2342 | 24 | <script type="text/javascript"> | 24 | <script type="text/javascript"> |
2343 | 25 | LPS.use('base', 'node', 'oop', 'event', 'lp.bugs.bugtask_index', | 25 | LPS.use('base', 'node', 'oop', 'event', 'lp.bugs.bugtask_index', |
2344 | 26 | 'lp.bugs.bugtask_index.portlets', | ||
2345 | 26 | 'lp.code.branchmergeproposal.diff', function(Y) { | 27 | 'lp.code.branchmergeproposal.diff', function(Y) { |
2347 | 27 | Y.lp.bugs.bugtask_index.use_advanced_subscriptions = | 28 | Y.lp.bugs.bugtask_index.portlets.use_advanced_subscriptions = |
2348 | 28 | use_advanced_subscriptions; | 29 | use_advanced_subscriptions; |
2349 | 29 | Y.lp.bugs.bugtask_index.setup_bugtask_index(); | 30 | Y.lp.bugs.bugtask_index.setup_bugtask_index(); |
2350 | 30 | Y.on('load', function(e) { | 31 | Y.on('load', function(e) { |
1184 +var use_advanced_ subscriptions = false;
ha, so here's an interesting thing. that should be use_advanced_ subscriptions = false;`. i guess that it's use_advanced_ subscriptions being undefined. you can get rid of
`var namespace.
never been a problem because our code doesn't check for
namespace.
the xxx above this too since rob and i agreed it wasn't a bug after all.
you're also going to need to update the setting of bugtask_ index.use_ advanced_ subscriptions in bugtask-index.pt
y.lp.bugs.
to make sure it uses the new namespace, otherwise all of the advanced
subscriptions functionality is going to break.
You can double-check the new functionality by running the following query and then checking that the "Subscribe / Edit subscription" link behaves like it does for you on production:
`insert into featureflag (scope, priority, flag, value, date_modified) values ('default', 1, 'malone. advanced- subscriptions. enabled' , 'on', now() at time zone 'UTC');`