From 2a1126bdd40aa210ba3fd6c3e991ba9132a4a559 Mon Sep 17 00:00:00 2001 From: Joseph Ditton Date: Mon, 9 Nov 2020 13:16:58 -0700 Subject: [PATCH] Adds functionality to support shortcuts for adding all members of a group or section to a conference. This change adds checkboxes for groups and sections to the members list section of the dialog for creating / editing a conference. These checkboxes are "shortcuts" that allow the user to quickly add all members of a specific section or group to a conference. These shortcuts are implemented client side and the model for conferences is unchanged. Test Plan: - Make sure the conferences feature is enabled - In a course open the dialog to create a new conference - Uncheck the "Invite All" box - Verify the following in the member list: - A checkbox for each section should appear in the "Sections" partition (only if there is more than one section in the course) - A checkbox for each group should appear in the "Groups" partition (only if there is at least one section in the course) - The headers for "Sections" and "Groups" should only appear if there is atleast item in the respective partition - The header for "Users" should only appear if one or more of the other partitions is present - Checking the box for section / group also checks the boxes for each of the users in that section / group - Unchecking the box for section / group also unchecks the boxes for each of the users in that section / group provided that another section / group that contains that user isn't also checked - If a user is added to the conference via a section or group you should not be able to uncheck the checkbox for that user. --- .../conferences/EditConferenceView.coffee | 73 +++++ app/controllers/conferences_controller.rb | 18 ++ .../conferences/editConferenceForm.handlebars | 35 +++ .../jsx/conferences/EditConferenceViewSpec.js | 272 +++++++++++++++++- 4 files changed, 397 insertions(+), 1 deletion(-) diff --git a/app/coffeescripts/views/conferences/EditConferenceView.coffee b/app/coffeescripts/views/conferences/EditConferenceView.coffee index 932b9fc14492f..27e841efe715b 100644 --- a/app/coffeescripts/views/conferences/EditConferenceView.coffee +++ b/app/coffeescripts/views/conferences/EditConferenceView.coffee @@ -45,7 +45,9 @@ export default class EditConferenceView extends DialogBaseView @delegateEvents() @toggleAllUsers() @markInvitedUsers() + @markInvitedSectionsAndGroups() @renderConferenceFormUserSettings() + @setupGroupAndSectionEventListeners() @$('form').formSubmit( object_name: 'web_conference' beforeSubmit: (data) => @@ -105,14 +107,23 @@ export default class EditConferenceView extends DialogBaseView if numberHelper.validate(conferenceData.duration) conferenceData.duration = I18n.n(conferenceData.duration) + hide_groups = !ENV.groups || ENV.groups.length == 0 + hide_sections = !ENV.sections || ENV.sections.length <= 1 + hide_user_header = hide_groups && hide_sections + json = settings: is_editing: is_editing is_adding: is_adding disable_duration_changes: ((conferenceData['long_running'] || is_editing) && conferenceData['started_at']) auth_token: authenticity_token() + hide_sections: hide_sections + hide_groups: hide_groups + hide_user_header: hide_user_header conferenceData: conferenceData users: ENV.users + sections: ENV.sections + groups: ENV.groups context_is_group: ENV.context_asset_string.split("_")[0] == "group" conferenceTypes: ENV.conference_type_details.map((type) -> {name: type.name, type: type.type, selected: (conferenceData.conference_type == type.type)} @@ -203,9 +214,71 @@ export default class EditConferenceView extends DialogBaseView el.attr('disabled', true) ) + markInvitedSectionsAndGroups: -> + _.each(ENV.sections, (section) => + section_user_ids = ENV.section_user_ids_map[section.id] + intersection = _.intersection(section_user_ids, @model.get("user_ids")) + if (intersection.length == section_user_ids.length) + el = $("#members_list .member.section_" + section.id).find(":checkbox") + el.attr('checked', true) + el.attr('disabled', true) + ) + + _.each(ENV.groups, (group) => + group_user_ids = ENV.group_user_ids_map[group.id] + intersection = _.intersection(group_user_ids, @model.get("user_ids")) + if (intersection.length == group_user_ids.length) + el = $("#members_list .member.group_" + group.id).find(":checkbox") + el.attr('checked', true) + el.attr('disabled', true) + ) + + changeLongRunning: (e) -> if ($(e.currentTarget).is(':checked')) $('#web_conference_duration').prop('disabled', true).val('') else # use restore time from data attribute $('#web_conference_duration').prop('disabled', false).val($('#web_conference_duration').data('restore-value')) + + setupGroupAndSectionEventListeners: () -> + selectedBySection = [] + selectedByGroup = [] + toggleMember = (id, checked) -> + memberEl = $("#members_list .member.user_" + id).find(":checkbox") + memberEl.attr('checked', checked) + memberEl.attr('disabled', checked) + + _.each(ENV.groups, (group) => + el = $("#members_list .member.group_" + group.id) + el.on("change", (e) => + _.each( + ENV.group_user_ids_map[group.id], + (id) => + if (e.target.checked) + selectedByGroup.push(id) + toggleMember(id, e.target.checked) + else + selectedByGroup = _.without(selectedByGroup, id) + if (!_.contains(selectedBySection, id) && !_.contains(@model.get("user_ids"), id)) + toggleMember(id, e.target.checked) + ) + ) + ) + + _.each(ENV.sections, (section) => + el = $("#members_list .member.section_" + section.id) + el.on("change", (e) => + _.each( + ENV.section_user_ids_map[section.id], + (id) => + if (e.target.checked) + selectedBySection.push(id) + toggleMember(id, e.target.checked) + else + selectedBySection = _.without(selectedBySection, id) + if (!_.contains(selectedByGroup, id) && !_.contains(@model.get("user_ids"), id)) + toggleMember(id, e.target.checked) + ) + ) + ) diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb index 3f5e847cf4444..6fd74fb1c329c 100644 --- a/app/controllers/conferences_controller.rb +++ b/app/controllers/conferences_controller.rb @@ -276,6 +276,18 @@ def web_index(conferences) @render_alternatives = WebConference.conference_types(@context).all? { |ct| ct[:replace_with_alternatives] } case @context when Course + @sections = @context.course_sections + @groups = @context.active_groups + + @group_user_ids_map = @groups.to_a.reduce({}) do |acc, group| + acc[group.id] = group.participating_users_in_context.map{|u| u.id.to_s} + acc + end + + @section_user_ids_map = @sections.to_a.reduce({}) do |acc, section| + acc[section.id] = section.participants.map{|u| u.id.to_s} + acc + end @users = User.where(:id => @context.current_enrollments.not_fake.active_by_date.where.not(:user_id => @current_user).select(:user_id)). order(User.sortable_name_order_by_clause).to_a @render_alternatives ||= @context.settings[:show_conference_alternatives].present? @@ -286,6 +298,8 @@ def web_index(conferences) @users = @context.users.where("users.id<>?", @current_user).order(User.sortable_name_order_by_clause).to_a.uniq end end + @groups ||= [] + @sections ||= [] # exposing the initial data as json embedded on page. js_env( @@ -294,6 +308,10 @@ def web_index(conferences) default_conference: default_conference_json(@context, @current_user, session), conference_type_details: conference_types_json(WebConference.conference_types(@context)), users: @users.map { |u| {:id => u.id, :name => u.last_name_first} }, + groups: @groups.map { |g| {:id => g.id, :name => g.full_name} }, + sections: @sections.map { |s| {:id => s.id, :name => s.display_name} }, + group_user_ids_map: @group_user_ids_map, + section_user_ids_map: @section_user_ids_map, can_create_conferences: @context.grants_right?(@current_user, session, :create_conferences), render_alternatives: @render_alternatives ) diff --git a/app/views/jst/conferences/editConferenceForm.handlebars b/app/views/jst/conferences/editConferenceForm.handlebars index cad1506c48346..1364392c520d1 100644 --- a/app/views/jst/conferences/editConferenceForm.handlebars +++ b/app/views/jst/conferences/editConferenceForm.handlebars @@ -74,6 +74,41 @@ {{/unless}}