95
95
ADMIN_TEAM_SUBSCRIBED_TO_DUPLICATE: (
96
96
_ADMIN_BECAUSE_TEAM_IS + _SUBSCRIBED_TO_DUPLICATE),
97
97
ADMIN_TEAM_SUBSCRIBED_TO_DUPLICATES: (
98
_ADMIN_BECAUSE_TEAM_IS + _SUBSCRIBED_TO_DUPLICATES),
98
_ADMIN_BECAUSE_TEAM_IS + _SUBSCRIBED_TO_DUPLICATES)
101
101
/* These are the owner variations. */
103
"the owner of the {pillar_type} " +
104
"{pillar}, which has no bug supervisor.");
103
"the owner of {pillar}, which has no bug supervisor.");
105
104
/* These are the actual strings to use. */
107
106
YOU_OWNER: _BECAUSE_YOU_ARE + _OWNER,
108
107
TEAM_OWNER: _BECAUSE_TEAM_IS + _OWNER,
109
ADMIN_TEAM_OWNER: _ADMIN_BECAUSE_TEAM_IS + _OWNER,
108
ADMIN_TEAM_OWNER: _ADMIN_BECAUSE_TEAM_IS + _OWNER
111
/* These are the actions */
114
* This takes an array of ObjectLinks and a url_suffix, and returns a new
115
* array of new ObjectLinks based on the input array, but with the suffix
116
* appended to each original ObjectLink's url.
118
function add_url_element_to_links(links, url_suffix) {
121
for (index = 0; index < links.length; index++) {
122
var original = links[index];
123
result.push(ObjectLink(
124
original.self, original.title, original.url + url_suffix));
128
namespace._add_url_element_to_links = add_url_element_to_links;
130
function lp_client() {
131
// This is a hook point for tests.
132
if (!Y.Lang.isValue(namespace._lp_client)) {
133
namespace._lp_client = new Y.lp.client.Launchpad();
135
return namespace._lp_client;
139
* Helper to find the appropriate link text and get a list of
140
* subscriptions that need to be unsubscribed for duplicate
141
* subscriptions for a person/team.
143
function get_unsubscribe_duplicates_text_and_subscriptions(args) {
145
var subscriptions = [];
147
if (Y.Lang.isValue(args.teams)) {
150
// There should never be more than one team.
151
if (args.teams.length !== 1) {
152
Y.error('We can only unsubscribe a single team from ' +
153
'multiple duplicate bugs.');
155
// Collect all pairs of (team, dupe-bug) that need to be
157
for (index = 0; index < args.bugs.length; index++) {
159
subscriber: args.teams[0].self.self_link,
160
bug: args.bugs[index].self.self_link
163
text = choose_by_number(
165
'Unsubscribe this team from the duplicate',
166
'Unsubscribe this team from all duplicates');
168
// Unsubscribe person.
170
// Collect all pairs of (team, dupe-bug) that need to be
172
for (index = 0; index < args.bugs.length; index++) {
174
subscriber: LP.links.me,
175
bug: args.bugs[index].self.self_link
178
text = choose_by_number(
180
'Unsubscribe yourself from the duplicate',
181
'Unsubscribe yourself from all duplicates');
185
subscriptions: subscriptions
188
namespace._get_unsubscribe_duplicates_text_and_subscriptions =
189
get_unsubscribe_duplicates_text_and_subscriptions;
192
* Helper to find the appropriate link text and get a list of
193
* subscriptions that need to be unsubscribed for team subscriptions.
195
function get_team_unsubscribe_text_and_subscriptions(args) {
196
var subscriptions = [];
198
var text = choose_by_number(args.teams.length,
199
'Unsubscribe this team',
200
'Unsubscribe all of these teams');
201
for (index = 0; index < args.teams.length; index++) {
203
subscriber: args.teams[index].self.self_link,
204
bug: LP.cache.context.bug_link
209
subscriptions: subscriptions
212
namespace._get_team_unsubscribe_text_and_subscriptions =
213
get_team_unsubscribe_text_and_subscriptions;
216
* Returns a link node with on-click handler that unsubscribes all
217
* subscriptions listed in `subscriptions` and link text set to `text`.
219
function get_node_for_unsubscribing(text, subscriptions) {
220
var node = Y.Node.create(
221
'<a href="#" class="sprite modify remove js-action"></a>');
222
var client = lp_client();
223
var handler = new Y.lp.client.ErrorHandler();
225
node.set('text', text);
227
handler.showError = function(error_msg) {
228
Y.lp.app.errors.display_error(node, error_msg);
230
handler.clearProgressUI = function () {
231
node.replaceClass('spinner', 'remove');
234
node.on('click', function (e) {
237
callback = function () {
238
if (subscriptions.length > 0) {
239
// Fire off another unsubscribe call.
240
var subscription = subscriptions.pop();
242
on: {success: callback,
243
failure: handler.getFailureHandler()},
244
parameters: {person: subscription.subscriber}
251
// We are done. Remove the parent node.
252
node.replaceClass('spinner', 'remove');
253
var container = node.ancestor(
254
'.subscription-description');
255
var anim = Y.lazr.effects.slide_in(container);
256
anim.on('end', function () {
262
node.replaceClass('remove', 'spinner');
269
namespace._get_node_for_unsubscribing = get_node_for_unsubscribing;
272
CHANGE_ASSIGNEES: function () {
273
return Y.Node.create('<a>Change assignees for this bug</a>')
274
.set('href', LP.cache.context.web_link);
276
UNSUBSCRIBE_DUPLICATES: function (args) {
277
var data = get_unsubscribe_duplicates_text_and_subscriptions(args);
278
return get_node_for_unsubscribing(data.text, data.subscriptions);
280
CHANGE_TEAM_SUBSCRIPTIONS: function (args) {
281
// TODO: add the ability to change notification level.
282
var data = get_team_unsubscribe_text_and_subscriptions(args);
283
return get_node_for_unsubscribing(data.text, data.subscriptions);
285
SET_BUG_SUPERVISOR: function (args) {
286
return Y.Node.create('<a></a>')
287
.set('text', 'Set the bug supervisor for ' + args.pillar.title)
288
.set('href', args.pillar.web_link + '/+bugsupervisor');
290
CONTACT_TEAMS: function (args) {
291
var node = Y.Node.create('<span></span>');
294
safely_render_description(
295
{reason: 'Contact {teams} to request the administrators '+
298
teams: add_url_element_to_links(
299
args.teams, '/+contactuser')}}));
303
namespace._actions = actions;
114
306
* Return appropriate object based on the number.
205
401
* than one, variable `teams` is set containing all the teams.
207
403
function gather_subscriptions_by_role(
208
category, team_singular, team_plural,
209
admin_team_singular, admin_team_plural) {
210
var subscriptions = [];
211
if (category.as_team_member.length > 0) {
213
for (var index in category.as_team_member) {
214
var team_subscription = category.as_team_member[index];
215
teams.push(get_link_data(team_subscription.principal));
217
var sub = choose_by_number(
218
category.as_team_member.length,
219
{ reason: team_singular,
222
{ reason: team_plural,
225
subscriptions.push(sub);
228
if (category.as_team_admin.length > 0) {
230
for (var index in category.as_team_admin) {
231
var team_subscription = category.as_team_admin[index];
232
teams.push(get_link_data(team_subscription.principal));
234
var sub = choose_by_number(
235
category.as_team_admin.length,
236
{ reason: admin_team_singular,
239
{ reason: admin_team_plural,
242
subscriptions.push(sub);
245
return subscriptions;
404
category, team_config, admin_team_config) {
408
work = [{subscriptions: category.as_team_member,
409
config: team_config},
410
{subscriptions: category.as_team_admin,
411
config: admin_team_config}];
412
for (work_index = 0; work_index < work.length; work_index++) {
413
var subscriptions = work[work_index].subscriptions;
414
var config = work[work_index].config;
415
if (subscriptions.length > 0) {
418
for (index = 0; index < subscriptions.length; index++) {
419
var team_subscription = subscriptions[index],
420
team = team_subscription.principal,
422
key = Y.Lang.isValue(key) ? key : team; // For tests.
423
if (!Y.Lang.isValue(team_map[key])) {
424
var link_data = get_link_data(team);
425
team_map[team.web_link] = link_data;
426
teams.push(link_data);
429
var sub = choose_by_number(
430
subscriptions.length,
431
{ reason: config.singular,
434
{ reason: config.plural,
437
sub.action = config.action;
438
sub.args = {teams: teams};
255
453
if (category.personal.length > 0) {
256
454
subscriptions.push(
257
455
{ reason: reasons.YOU_ASSIGNED,
457
action: actions.CHANGE_ASSIGNEES });
261
460
// We add all the team assignments grouped by roles in the team.
262
461
return subscriptions.concat(
263
462
gather_subscriptions_by_role(
264
category, reasons.TEAM_ASSIGNED, reasons.TEAMS_ASSIGNED,
265
reasons.ADMIN_TEAM_ASSIGNED, reasons.ADMIN_TEAMS_ASSIGNED));
464
{singular: reasons.TEAM_ASSIGNED,
465
plural: reasons.TEAMS_ASSIGNED,
466
action: actions.CONTACT_TEAMS},
467
{singular: reasons.ADMIN_TEAM_ASSIGNED,
468
plural: reasons.ADMIN_TEAMS_ASSIGNED,
469
action: actions.CHANGE_ASSIGNEES}));
267
471
namespace._gather_subscriptions_as_assignee =
268
472
gather_subscriptions_as_assignee;
475
* Adds a `subscription` to `subscriptions` if it's not in the list already.
476
* Compares reason, action and all the `vars` from existing subscription.
478
function add_subscription_to_set(subscriptions, subscription) {
480
for (index = 0; index < subscriptions.length; index++) {
481
sub = subscriptions[index];
482
if (sub.reason === subscription.reason &&
483
sub.action === subscription.action) {
484
var are_vars_same = true;
486
for (param in sub.vars) {
487
if (sub.vars.hasOwnProperty(param)) {
488
// We only check vars from the existing subscription.
489
// Theoretically, there could be a var on `subscription`
490
// not present on `sub`, but we're guarding against that
491
// with reason/action checks.
492
if (sub.vars[param].self
493
!== subscription.vars[param].self) {
494
are_vars_same = false;
504
// We haven't found matching subscriptions, add it.
505
subscriptions.push(subscription);
271
509
* Gather subscription information for implicit bug supervisor.
273
511
function gather_subscriptions_as_supervisor(category) {
274
512
var subscriptions = [];
275
513
var reasons = namespace._reasons;
514
var index, team_subscription, team_link;
277
for (var index in category.personal) {
516
for (index = 0; index < category.personal.length; index++) {
278
517
var subscription = category.personal[index];
518
add_subscription_to_set(subscriptions, {
280
519
reason: reasons.YOU_OWNER,
282
521
pillar: get_link_data(subscription.pillar)
523
action: actions.SET_BUG_SUPERVISOR,
524
args: {pillar: subscription.pillar}
287
for (var index in category.as_team_member) {
288
var team_subscription = category.as_team_member[index];
528
for (index = 0; index < category.as_team_member.length; index++) {
529
team_subscription = category.as_team_member[index];
530
team_link = get_link_data(team_subscription.principal);
531
add_subscription_to_set(subscriptions, {
290
532
reason: reasons.TEAM_OWNER,
292
team: get_link_data(team_subscription.principal),
293
535
pillar: get_link_data(team_subscription.pillar)
537
action: actions.CONTACT_TEAMS,
538
args: {teams: [team_link]}
298
for (var index in category.as_team_admin) {
299
var team_subscription = category.as_team_admin[index];
542
for (index = 0; index < category.as_team_admin.length; index++) {
543
team_subscription = category.as_team_admin[index];
544
add_subscription_to_set(subscriptions, {
301
545
reason: reasons.ADMIN_TEAM_OWNER,
303
547
team: get_link_data(team_subscription.principal),
304
548
pillar: get_link_data(team_subscription.pillar)
550
action: actions.SET_BUG_SUPERVISOR,
551
args: {pillar: team_subscription.pillar}
585
860
function get_other_descriptions_node(info, extra_data) {
586
861
var subs = gather_nondirect_subscriptions(info);
587
if (subs.length > 0) {
588
var other_node = Y.Node.create('<div></div>')
863
if (subs.length > 0 || has_structural_subscriptions()) {
864
var node = Y.Node.create('<div></div>')
589
865
.set('id', 'other-subscriptions');
591
for (var index in subs) {
592
other_node.appendChild(
866
var header = Y.Node.create('<div></div>')
867
.set('id', 'other-subscriptions-header');
868
var header_link = Y.Node.create('<a></a>')
870
.set('text', 'Other subscriptions');
871
header.appendChild(header_link);
872
node.appendChild(header);
873
var list = Y.Node.create('<div></div>')
874
.set('id', 'other-subscriptions-list');
875
node.appendChild(list);
877
setup_slider(list, header_link);
879
for (index = 0; index < subs.length; index++) {
593
881
get_single_description_node(subs[index], extra_data));
598
886
return undefined;
601
889
namespace._get_other_descriptions_node = get_other_descriptions_node;
892
* Are there any structural subscriptions that need to be rendered.
894
function has_structural_subscriptions() {
895
return (LP.cache.subscription_info &&
896
LP.cache.subscription_info.length > 0);
900
* Sets up a slider that slides the `body` in and out when `header`
903
function setup_slider(body, header) {
904
// Hide the widget body contents.
905
body.addClass('lazr-closed');
906
body.setStyle('display', 'none');
908
// Ensure that the widget header uses the correct sprite icon
909
// and gets the styling for javascript actions applied.
910
header.addClass('sprite');
911
header.addClass('treeCollapsed');
912
header.addClass('js-action');
915
function toggle_body_visibility(e) {
918
slide = Y.lazr.effects.slide_out(body);
919
header.replaceClass('treeCollapsed', 'treeExpanded');
921
slide.set('reverse', !slide.get('reverse'));
922
header.toggleClass('treeExpanded');
923
header.toggleClass('treeCollapsed');
928
header.on('click', toggle_body_visibility);
605
932
* Add descriptions for all non-structural subscriptions to the page.