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