879 lines
36 KiB
JavaScript
879 lines
36 KiB
JavaScript
/**
|
|
* Copyright (C) 2011 Instructure, Inc.
|
|
*
|
|
* This file is part of Canvas.
|
|
*
|
|
* Canvas is free software: you can redistribute it and/or modify it under
|
|
* the terms of the GNU Affero General Public License as published by the Free
|
|
* Software Foundation, version 3 of the License.
|
|
*
|
|
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
define([
|
|
'i18n!groups',
|
|
'jquery' /* $ */,
|
|
'underscore',
|
|
'compiled/fn/preventDefault',
|
|
'compiled/views/MessageStudentsDialog',
|
|
'jqueryui/draggable' /* /\.draggable/ */,
|
|
'jquery.ajaxJSON' /* ajaxJSON */,
|
|
'jquery.instructure_forms' /* formSubmit, fillFormData, formErrors */,
|
|
'jqueryui/dialog',
|
|
'jquery.instructure_misc_helpers' /* replaceTags */,
|
|
'jquery.instructure_misc_plugins' /* confirmDelete, showIf */,
|
|
'jquery.loadingImg' /* loadingImage */,
|
|
'compiled/jquery.rails_flash_notifications',
|
|
'jquery.templateData' /* fillTemplateData, getTemplateData */,
|
|
'vendor/jquery.scrollTo' /* /\.scrollTo/ */,
|
|
'jqueryui/droppable' /* /\.droppable/ */,
|
|
'jqueryui/tabs' /* /\.tabs/ */
|
|
], function(I18n, $, _, preventDefault, MessageStudentsDialog) {
|
|
|
|
window.contextGroups = {
|
|
autoLoadGroupThreshold: 15,
|
|
paginationSize: 15,
|
|
|
|
populateUserElement: function($user, data) {
|
|
if (data.sections) {
|
|
data.section_id = _(data.sections).pluck('section_id').join(",");
|
|
data.section_code = _(data.sections).pluck('section_code').join(", ");
|
|
}
|
|
|
|
$user.removeClass('user_template');
|
|
$user.addClass('user_id_' + data.user_id);
|
|
$user.fillTemplateData({ data: data });
|
|
},
|
|
|
|
loadMembersForGroup: function($group) {
|
|
var url = ENV.list_users_url;
|
|
var id = $group.getTemplateData({textValues: ['group_id']}).group_id;
|
|
url = $.replaceTags(url, "id", id)
|
|
|
|
$group.find(".load_members_link").hide();
|
|
$group.find(".loading_members").show();
|
|
|
|
$.ajaxJSON(url, "GET", null, function(data) {
|
|
// remove existing students from group (this should have returned everyone)
|
|
$group.find(".student").remove();
|
|
|
|
// create new entries for everyone we got back, and insert them into the list.
|
|
// TODO: we should presort the list and give an "append" option to insertIntoGroup.
|
|
// Right now it takes O(n^2) to insert students, which can be painful in a large list.
|
|
var $user_template = $(".user_template");
|
|
for (var i = 0; i < data.length; i++) {
|
|
var $user = $user_template.clone();
|
|
contextGroups.populateUserElement($user, data[i]);
|
|
contextGroups.insertIntoGroup($user, $group);
|
|
|
|
$group.find(".user_count_hidden").text("0");
|
|
$(window).triggerHandler('resize');
|
|
contextGroups.updateCategoryCounts($group.parents(".group_category"));
|
|
}
|
|
$group.find(".loading_members").hide();
|
|
});
|
|
},
|
|
|
|
// 0 means to reload the current page, whatever it is
|
|
// < 0 means to only load that page if no other page is loaded
|
|
loadUnassignedMembersPage: function($group, page) {
|
|
if (page < 0 && $group.data("page_loaded")) { return; }
|
|
page = Math.abs(page);
|
|
|
|
if (page == 0) {
|
|
page = $group.data("page_loaded") || 1;
|
|
}
|
|
|
|
var students_visible = $group.find(".student_list .student").length;
|
|
var students_hidden = parseInt($group.find(".user_count_hidden").text());
|
|
var total_students = students_visible + students_hidden;
|
|
|
|
// ensure we don't try to go to a page that won't have any students on it
|
|
if (total_students > 0) {
|
|
page = Math.min(page, Math.floor((total_students - 1) / contextGroups.paginationSize) + 1);
|
|
}
|
|
|
|
// This is lots of duplicated code from above, with tweaks. TODO: Refactor
|
|
var category_id = $group.closest(".group_category").data('category_id');
|
|
var url = ENV.list_unassigned_users_url;
|
|
url += "?category_id=" + category_id + "&page=" + page;
|
|
|
|
$group.find(".load_members_link").hide();
|
|
$group.find(".loading_members").show();
|
|
|
|
$.ajaxJSON(url, "GET", null, function(data) {
|
|
$group.data("page_loaded", page);
|
|
$group.find(".user_count").text(I18n.t('category.student', 'student', {count: data['total_entries']}));
|
|
$group.find(".user_count_hidden").text(data['total_entries'] - data['users'].length);
|
|
$group.find(".group_user_count").show();
|
|
$group.find(".student").remove();
|
|
$group.find(".student_links").showIf(data['total_entries'] > 0);
|
|
|
|
var $user_template = $(".user_template");
|
|
var users = data['users'];
|
|
for (var i = 0; i < users.length; i++) {
|
|
var $user = $user_template.clone();
|
|
contextGroups.populateUserElement($user, users[i]);
|
|
contextGroups.insertIntoGroup($user, $group);
|
|
}
|
|
|
|
// xsslint safeString.property pagination_html
|
|
$group.find(".loading_members").html(data.pagination_html);
|
|
$group.find(".unassigned_members_pagination a").click(function(event) {
|
|
event.preventDefault();
|
|
var page_regex = /page=(\d+)/
|
|
match = page_regex.exec($(this).attr('href'))
|
|
if (match.length >= 1) {
|
|
var link_page = match[1];
|
|
contextGroups.loadUnassignedMembersPage($(this).closest(".group_blank").first(), link_page)
|
|
}
|
|
});
|
|
|
|
$(window).triggerHandler('resize');
|
|
})
|
|
},
|
|
|
|
moveToGroup: function($student, $group) {
|
|
if ($student.parents(".group")[0] == $group[0]) { return; }
|
|
var id = $group.getTemplateData({textValues: ['group_id']}).group_id;
|
|
var user_id = $student.getTemplateData({textValues: ['user_id']}).user_id;
|
|
var $original_group = $student.parents(".group");
|
|
var url = ENV.add_user_url;
|
|
var data = {};
|
|
var method;
|
|
if (id) {
|
|
method = "POST";
|
|
data.user_id = user_id;
|
|
} else {
|
|
method = "DELETE";
|
|
url = $.replaceTags(ENV.remove_user_url, "user_id", user_id);
|
|
id = $original_group.getTemplateData({textValues: ['group_id']}).group_id;
|
|
}
|
|
url = $.replaceTags(url, "id", id);
|
|
$student.remove();
|
|
|
|
var $student_instances = $student;
|
|
contextGroups.insertIntoGroup($student, $group);
|
|
$category = $group.parents(".group_category")
|
|
if($category.hasClass('student_organized') && !$group.hasClass('group_blank')) {
|
|
var $s = $student.clone();
|
|
$student_instances = $student_instances.add($s);
|
|
contextGroups.insertIntoGroup($s, $original_group);
|
|
}
|
|
$student = $student_instances;
|
|
$student.addClass('event_pending');
|
|
contextGroups.updateCategoryCounts($group.parents(".group_category"));
|
|
$.ajaxJSON(url, method, data, function(data) {
|
|
var category_id = $group.parents(".group_category").data('category_id');
|
|
$(".student.user_" + user_id).each(function() {
|
|
var $span = $(this).find(".category_" + category_id + "_group_id");
|
|
if(!$span || $span.length == 0) {
|
|
$span = $(document.createElement('span'));
|
|
$span.addClass('category_' + category_id + '_group_id');
|
|
$(this).find(".data").append($span);
|
|
}
|
|
$span.text(data.group_membership.group_id || "");
|
|
});
|
|
|
|
$student.removeClass('event_pending');
|
|
contextGroups.updateCategoryCounts($group.parents(".group_category"));
|
|
|
|
var groups = $($original_group);
|
|
groups = groups.add($group);
|
|
contextGroups.updateCategoryHeterogeneity($group.parents(".group_category"), groups);
|
|
|
|
var unassigned_group = $group.parents(".group_category").find(".group_blank");
|
|
var students_visible = unassigned_group.find(".student_list .student").length;
|
|
var students_hidden = parseInt(unassigned_group.find(".user_count_hidden").text());
|
|
|
|
if (students_visible <= 5 && students_hidden > 0) {
|
|
contextGroups.loadUnassignedMembersPage(unassigned_group, 0);
|
|
}
|
|
},
|
|
function(data) {
|
|
// move failed, undo it
|
|
$student.remove();
|
|
$student = $student.first();
|
|
contextGroups.insertIntoGroup($student, $original_group);
|
|
$student.removeClass('event_pending');
|
|
contextGroups.updateCategoryCounts($category);
|
|
|
|
var message;
|
|
if (data.errors && data.errors.user_id) {
|
|
// attempted group membership claims the user was unacceptable for
|
|
// some reason (probably section).
|
|
message = data.errors.user_id[0].message;
|
|
} else if (data.errors && data.errors.group_id) {
|
|
message = data.errors.group_id[0].message;
|
|
} else {
|
|
message = I18n.t('errors.unknown', 'An unexpected error occurred.');
|
|
}
|
|
$.flashError(message);
|
|
});
|
|
},
|
|
|
|
insertIntoGroup: function($student, $group) {
|
|
var $before = null;
|
|
var data = $student.getTemplateData({textValues: ['name', 'user_id']})
|
|
var student_name = data.name;
|
|
|
|
// don't insert users into a group they're already in
|
|
if ($group.find(".student_list .user_id_" + data.user_id).length > 0) { return; }
|
|
|
|
$group.find(".student.user_" + data.user_id).remove();
|
|
$group.find(".student_list .student").each(function() {
|
|
var compare_name = $(this).getTemplateData({textValues: ['name']}).name;
|
|
if(compare_name > student_name) {
|
|
$before = $(this);
|
|
return false;
|
|
}
|
|
});
|
|
if(!$before) {
|
|
$group.find(".student_list").append($student);
|
|
} else {
|
|
$before.before($student);
|
|
}
|
|
$student.draggable({
|
|
helper: function() {
|
|
var $helper = $(this).clone().css('cursor', 'pointer');//.css({position: 'absolute'}).appendTo($(this).parents(".group_category"));
|
|
$helper.addClass('dragging');
|
|
$helper.width($(this).width());
|
|
$(this).parents(".group_category").append($helper);
|
|
return $helper;
|
|
}
|
|
});
|
|
},
|
|
|
|
updateCategoryCounts: function($category) {
|
|
$category.find(".group").each(function() {
|
|
var $this = $(this);
|
|
var userCount = $this.find(".student_list .student").length + parseInt($this.find(".user_count_hidden").text());
|
|
$this.find(".user_count").text(I18n.t('category.student', 'student', {count: userCount}));
|
|
$this.find(".student_links").showIf(userCount > 0);
|
|
});
|
|
|
|
var groupCount = $category.find(".group:not(.group_blank)").length;
|
|
$category.find(".group_count").text(I18n.t('group', 'Group', {count: groupCount}));
|
|
|
|
$category.find(".group").each(function() {
|
|
$(this).find(".expand_collapse_links").showIf($(this).find(".student_list li").length > 0);
|
|
});
|
|
},
|
|
|
|
updateCategoryHeterogeneity: function($category, groups) {
|
|
// check if any of the provided groups are heterogenous now (skipping the
|
|
// "unassigned" group should it be involved).
|
|
var heterogenous = false;
|
|
groups.each(function() {
|
|
var section_id_counts = {};
|
|
$section_ids = $(this).find(".student_list .student .section_id");
|
|
if (!$(this).hasClass('group_blank') && $section_ids.length > 0 && $section_ids.text()) {
|
|
$section_ids.each(function() {
|
|
var section_ids = $(this).text().split(",");
|
|
for (var i = 0; i < section_ids.length; i++) {
|
|
if (!section_id_counts[section_ids[i]]) {
|
|
section_id_counts[section_ids[i]] = 0;
|
|
}
|
|
section_id_counts[section_ids[i]] += 1;
|
|
}
|
|
});
|
|
|
|
var found = true;
|
|
for (var section_id in section_id_counts) {
|
|
if (section_id_counts[section_id] === $section_ids.length) {
|
|
found = false;
|
|
}
|
|
}
|
|
heterogenous = heterogenous || found;
|
|
}
|
|
});
|
|
$category.find('.heterogenous').text(heterogenous ? 'true' : 'false');
|
|
},
|
|
|
|
populateCategory: function(panel) {
|
|
var $category = $(panel);
|
|
|
|
// Start loading groups that have < X members automatically
|
|
$category.find(".group").each(function() {
|
|
var members = parseInt($(this).find(".user_count_hidden").text());
|
|
if (members < contextGroups.autoLoadGroupThreshold && members > 0) {
|
|
contextGroups.loadMembersForGroup($(this));
|
|
}
|
|
})
|
|
|
|
// -1 means to load page 1, but only if there aren't any other pages loaded (for switching between tabs)
|
|
if ($category.length) {
|
|
contextGroups.loadUnassignedMembersPage($category.find(".group_blank"), -1);
|
|
}
|
|
},
|
|
|
|
updateCategory: function($category, category) {
|
|
// update name in $category, tab, and sidebar
|
|
$category.find('.category_name').text(category.name);
|
|
$($category.data('tab_link')).text(category.name);
|
|
$('#sidebar_category_' + category.id + ' .category').text(category.name);
|
|
|
|
// put self_signup value in $category template data, and toggle
|
|
// appropriate self signup text
|
|
$category.find('.self_signup').text(category.self_signup || '');
|
|
$category.find('.self_signup_text').showIf(category.self_signup);
|
|
$category.find('.restricted_self_signup_text').showIf(category.self_signup === 'restricted');
|
|
$category.find('.assign_students_link').showIf(category.self_signup !== 'restricted');
|
|
$category.find('.group_limit_blurb').showIf(category.group_limit);
|
|
$category.find('.group_limit, .group_limit_text').text(category.group_limit || '');
|
|
$category.find('.students_link_separator').showIf(category.self_signup && category.self_signup !== 'restricted');
|
|
$category.find('.message_students_link').showIf(category.self_signup);
|
|
},
|
|
|
|
addGroupToSidebar: function(group) {
|
|
if($("#sidebar_group_" + group.id).length > 0) {
|
|
return;
|
|
}
|
|
var $category = $("#sidebar_category_" + group.group_category_id);
|
|
if($category.length == 0) {
|
|
$category = $("#sidebar_category_blank").clone(true);
|
|
$category.find("ul").empty();
|
|
$category.fillTemplateData({
|
|
data: group,
|
|
id: 'sidebar_category_' + group.group_category_id
|
|
});
|
|
$(".sidebar_category:last").after($category.show());
|
|
}
|
|
var $group = $("#sidebar_group_blank").clone(true);
|
|
$group.fillTemplateData({
|
|
data: group,
|
|
hrefValues: ['id'],
|
|
id: 'sidebar_group_' + group.id
|
|
});
|
|
$category.find("ul").append($group.show());
|
|
$("#category_header").show();
|
|
},
|
|
|
|
droppable_options: {
|
|
accept: '.student',
|
|
hoverClass: 'hover',
|
|
tolerance: 'pointer',
|
|
drop: function(event, ui) {
|
|
$(".group_category > .student").remove();
|
|
contextGroups.moveToGroup($(ui.draggable), $(this));
|
|
}
|
|
}
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
$("li.student").live('mousedown', function(event) { event.preventDefault(); return false; });
|
|
$("#group_tabs").tabs();
|
|
$("#group_tabs").bind('tabsselect', function(event, ui) {
|
|
contextGroups.populateCategory(ui.panel);
|
|
});
|
|
$(".group").filter(function(){ return $(this).parents("#category_template").length == 0;}).droppable(contextGroups.droppable_options);
|
|
$(".add_group_link").click(function(event) {
|
|
event.preventDefault();
|
|
var $category = $(this).parents(".group_category");
|
|
var $group = $("#category_template").find(".group_blank").clone(true);
|
|
$group.removeAttr('id');
|
|
$group.find(".student_links").remove();
|
|
$group.find(".student_list").empty();
|
|
$group.removeClass('group_blank');
|
|
$group.find(".load-more").hide();
|
|
$group.find(".name").text(I18n.t('group_name', "Group Name"));
|
|
$category.find(".clearer").before($group.show());
|
|
$group.find(".edit_group_link").click();
|
|
});
|
|
$("#edit_group_form").formSubmit({
|
|
object_name: "group",
|
|
processData: function(data) {
|
|
data['group[group_category_id]'] = $(this).parents(".group_category").data('category_id');
|
|
return data;
|
|
},
|
|
beforeSubmit: function(data) {
|
|
var $group = $(this).parents(".group");
|
|
$(this).remove();
|
|
$group.removeClass('editing');
|
|
$group.loadingImage();
|
|
$group.fillTemplateData({
|
|
data: data,
|
|
avoid: '.student_list'
|
|
});
|
|
return $group;
|
|
},
|
|
success: function(data, $group) {
|
|
$group.loadingImage('remove');
|
|
$group.droppable(contextGroups.droppable_options);
|
|
$group.find(".name.blank_name").hide().end()
|
|
.find(".group_name").show();
|
|
$group.find(".group_user_count").show();
|
|
data.group_id = data.id;
|
|
$group.fillTemplateData({
|
|
id: 'group_' + data.id,
|
|
data: data,
|
|
avoid: '.student_list',
|
|
hrefValues: ['id']
|
|
});
|
|
contextGroups.addGroupToSidebar(data)
|
|
contextGroups.updateCategoryCounts($group.parents(".group_category"));
|
|
}
|
|
});
|
|
$(".edit_group_link").click(function(event) {
|
|
event.preventDefault();
|
|
var $group = $(this).parents(".group");
|
|
var data = $group.getTemplateData({textValues: ['name', 'max_membership']});
|
|
var $form = $("#edit_group_form").clone(true);
|
|
$form.fillFormData(data, {
|
|
object_name: 'group'
|
|
});
|
|
$group.addClass('editing');
|
|
$group.prepend($form.show());
|
|
$form.find(":input:visible:first").focus().select();
|
|
if($group.attr('id')) {
|
|
$form.attr('method', 'PUT').attr('action', $group.find(".edit_group_link").attr('href'));
|
|
} else {
|
|
$form.attr('method', 'POST').attr('action', ENV.add_group_url);
|
|
}
|
|
if($group.length > 0) {
|
|
$group.parents(".group_category").scrollTo($group);
|
|
}
|
|
});
|
|
$("#edit_group_form .cancel_button").click(function() {
|
|
var $group = $(this).parents(".group");
|
|
$(this).parents(".group").removeClass('editing');
|
|
$(this).parents("form").remove();
|
|
if(!$group.attr('id')) {
|
|
$group.remove();
|
|
}
|
|
});
|
|
$("#edit_category_form").formSubmit({
|
|
object_name: "category",
|
|
property_validations: {
|
|
'name': function(val, data) {
|
|
var $category = $(this).parents('.group_category');
|
|
var original_name = $category.find('.category_name:first').text();
|
|
if (original_name.toLowerCase() == val.toLowerCase()) {
|
|
return;
|
|
}
|
|
var found = false;
|
|
$("#category_list .category").each(function() {
|
|
if($(this).text().toLowerCase() == val.toLowerCase()) {
|
|
found = true;
|
|
return false;
|
|
}
|
|
});
|
|
if(found) {
|
|
return I18n.t('errors.category_in_use', "\"%{category_name}\" is already in use", {category_name: val});
|
|
}
|
|
},
|
|
'group_limit': function(val, data) {
|
|
if (parseInt(val) <= 1) {
|
|
return I18n.t('errors.group_limit', 'Group limit must be blank or greater than 1')
|
|
}
|
|
return false;
|
|
}
|
|
},
|
|
beforeSubmit: function(data) {
|
|
var $category = $(this).parents(".group_category");
|
|
var tab_index = $("#group_tabs").tabs('option', 'selected');
|
|
var tab_link = $($('#category_list .category')[tab_index]).find('a');
|
|
$category.data('tab_link', tab_link);
|
|
$(this).find("button").attr('disabled', true);
|
|
$(this).find(".submit_button").text(I18n.t('status.updating', "Updating..."));
|
|
$(this).loadingImage();
|
|
return $category;
|
|
},
|
|
success: function(data, $category) {
|
|
contextGroups.updateCategory($category, data.group_category);
|
|
$(this).loadingImage('remove');
|
|
$(this).remove();
|
|
$category.removeClass('editing');
|
|
},
|
|
error: function(data) {
|
|
$(this).loadingImage('remove');
|
|
$(this).find("button").attr('disabled', false);
|
|
$(this).find(".submit_button").text(I18n.t('errors.update_failed', "Update Failed, Try Again"));
|
|
$(this).formErrors(data);
|
|
}
|
|
});
|
|
$(".edit_category_link").click(function(event) {
|
|
event.preventDefault();
|
|
var $category = $(this).parents(".group_category");
|
|
var $form = $("#edit_category_form").clone(true);
|
|
|
|
// fill out form given the current category values
|
|
var data = $category.getTemplateData({textValues: ['category_name', 'self_signup', 'heterogenous', 'group_limit']});
|
|
var form_data = {
|
|
name: data.category_name,
|
|
enable_self_signup: data.self_signup !== null && data.self_signup !== '',
|
|
restrict_self_signup: data.self_signup == 'restricted',
|
|
group_limit: data.group_limit
|
|
};
|
|
$form.fillFormData(form_data, { object_name: 'category' });
|
|
$form.find("#category_restrict_self_signup").prop('disabled', !form_data.enable_self_signup || data.heterogenous == 'true');
|
|
$form.find("#group_structure_self_signup_subcontainer").showIf( $form.find('#category_enable_self_signup').is(':checked') );
|
|
|
|
$category.addClass('editing');
|
|
$category.prepend($form.show());
|
|
$form.find(":input:visible:first").focus().select();
|
|
$form.attr('method', 'PUT').attr('action', $category.find(".edit_category_link").attr('href'));
|
|
$category.scrollTo($form.find(":input:visible:first"));
|
|
});
|
|
$("#edit_category_form .cancel_button").click(function() {
|
|
var $category = $(this).parents(".group_category");
|
|
$category.removeClass('editing');
|
|
$(this).parents("form").remove();
|
|
});
|
|
$(".delete_category_link").click(function(event) {
|
|
event.preventDefault();
|
|
var index = $("#group_tabs").tabs('option', 'selected')
|
|
if(index == -1) {
|
|
index = $("#category_list li").index($("#category_list li.ui-tabs-selected"));
|
|
}
|
|
var $category = $(this).parents(".group_category");
|
|
var url = $.replaceTags($(this).attr('href'), "category_id", $category.data('category_id'))
|
|
$category.confirmDelete({
|
|
url: url,
|
|
message: I18n.t('confirm.remove_category', "Are you sure you want to remove this set of groups?"),
|
|
success: function() {
|
|
categories_remaining = $("#category_list li").length;
|
|
$("#group_tabs").tabs('remove', index);
|
|
$("#group_tabs").showIf(categories_remaining > 0);
|
|
var category_id = $(this).data('category_id');
|
|
if (categories_remaining > 0) {
|
|
if (index > categories_remaining) {
|
|
index = categories_remaining;
|
|
}
|
|
$("#group_tabs").tabs('select', index);
|
|
}
|
|
$("#sidebar_category_" + category_id).slideUp(function() {
|
|
$(this).remove();
|
|
});
|
|
$("#no_groups_message").showIf(categories_remaining == 0);
|
|
}
|
|
});
|
|
});
|
|
$(".add_category_link").click(function(event) {
|
|
event.preventDefault();
|
|
addGroupCategory(function(data) {
|
|
$("#group_tabs").show();
|
|
$("#no_groups_message").hide();
|
|
var $category = $("#category_template").clone(true).removeAttr('id');
|
|
var $group_template = $("#category_template").find(".group_blank");
|
|
$category.find(".group").droppable(contextGroups.droppable_options);
|
|
var group_category = data[0].group_category;
|
|
var groups = data[1];
|
|
for(var idx in groups) {
|
|
var group = groups[idx].group || groups[idx].course_assigned_group;
|
|
|
|
group.group_id = group.id;
|
|
$group = $group_template.clone(true).removeClass('group_blank');
|
|
$group.attr('id', 'group_' + group.id);
|
|
if (!group.users || group.users.length == 0) {
|
|
$group.find(".load-more").hide();
|
|
}
|
|
$group.droppable(contextGroups.droppable_options);
|
|
group.user_count = group.users.length;
|
|
group.user_count_hidden = group.users.length;
|
|
$group.fillTemplateData({
|
|
id: 'group_' + group.id,
|
|
data: group,
|
|
avoid: '.student_list',
|
|
hrefValues: ['id']
|
|
});
|
|
$category.find(".clearer").before($group);
|
|
contextGroups.addGroupToSidebar(group)
|
|
|
|
if (group.users) {
|
|
for(var jdx in group.users) {
|
|
var user = group.users[jdx].user;
|
|
var $span = $(document.createElement('span')).addClass('category_' + group_category.id + "_group_id");
|
|
$span.text(group.id);
|
|
$(".student.user_" + user.id).find(".data").append($span);
|
|
}
|
|
}
|
|
|
|
$group.find(".name.blank_name").hide().end()
|
|
.find(".group_name").show();
|
|
$group.find(".group_user_count").show();
|
|
}
|
|
$category.fillTemplateData({
|
|
data: {
|
|
category_id: group_category.id,
|
|
category_name: group_category.name,
|
|
self_signup: group_category.self_signup
|
|
},
|
|
hrefValues: ['category_id']
|
|
});
|
|
$category.attr('id', 'category_' + group_category.id);
|
|
$category.data('category_id', group_category.id);
|
|
$category.find('.self_signup_text').showIf(group_category.self_signup);
|
|
$category.find('.restricted_self_signup_text').showIf(group_category.self_signup == 'restricted');
|
|
$category.find('.assign_students_link').showIf(group_category.self_signup !== 'restricted');
|
|
$category.find('.students_link_separator').showIf(group_category.self_signup && group_category.self_signup !== 'restricted');
|
|
$category.find('.message_students_link').showIf(group_category.self_signup);
|
|
|
|
var newIndex = $("#group_tabs").tabs('length');
|
|
if ($("li.category").last().hasClass('student_organized')) {
|
|
newIndex -= 1;
|
|
}
|
|
$("#group_tabs").append($category);
|
|
$("#group_tabs").tabs('add', '#category_' + group_category.id, group_category.name, newIndex);
|
|
$("#group_tabs").tabs('select', newIndex);
|
|
contextGroups.populateCategory($category);
|
|
contextGroups.updateCategory($category, group_category);
|
|
contextGroups.updateCategoryCounts($category);
|
|
$(window).triggerHandler('resize');
|
|
});
|
|
});
|
|
$("#add_category_form").formSubmit({
|
|
property_validations: {
|
|
'category[name]': function(val, data) {
|
|
var found = false;
|
|
$("#category_list .category").each(function() {
|
|
if($(this).text().toLowerCase() == val.toLowerCase()) {
|
|
found = true;
|
|
return false;
|
|
}
|
|
});
|
|
if(found) {
|
|
return I18n.t('errors.category_in_use', "\"%{category_name}\" is already in use", {category_name: val});
|
|
}
|
|
},
|
|
'category[group_limit]': function(val, data) {
|
|
if (parseInt(val) <= 1) {
|
|
return I18n.t('errors.group_limit', 'Group limit must be blank or greater than 1')
|
|
}
|
|
return false;
|
|
}
|
|
},
|
|
beforeSubmit: function(data) {
|
|
$(this).loadingImage();
|
|
$(this).data('category_name', data['category[name]']);
|
|
$(this).find("button").attr('disabled', true);
|
|
$(this).find(".submit_button").text(I18n.t('status.creating_groups', "Creating Category..."));
|
|
},
|
|
success: function(data) {
|
|
$(this).loadingImage('remove');
|
|
var callbacks = $(this).data('callbacks') || [];
|
|
for(var idx in callbacks) {
|
|
var callback = callbacks[idx];
|
|
if(callback && $.isFunction(callback)) {
|
|
callback.call(this, data);
|
|
}
|
|
}
|
|
$(this).data('callbacks', [])
|
|
$(this).find("button").attr('disabled', false);
|
|
$(this).find(".submit_button").text(I18n.t('button.create_category', "Create Category"));
|
|
$(this).dialog('close');
|
|
},
|
|
error: function(data) {
|
|
$(this).loadingImage('remove');
|
|
$(this).find("button").attr('disabled', false);
|
|
$(this).find(".submit_button").text(I18n.t('errors.creating_category_failed', "Category Creation Failed, Try Again"));
|
|
$(this).formErrors(data);
|
|
}
|
|
});
|
|
$("#add_category_form .cancel_button").click(function() {
|
|
$("#add_category_form").dialog('close');
|
|
});
|
|
$("#add_category_form #category_enable_self_signup").change(function() {
|
|
var self_signup = $(this).prop('checked')
|
|
$("#add_category_form #category_restrict_self_signup").prop('disabled', !self_signup);
|
|
$("#add_category_form #group_structure_standard_subcontainer").showIf(!self_signup);
|
|
$("#add_category_form #group_structure_self_signup_subcontainer").showIf(self_signup);
|
|
if (!self_signup) {
|
|
$("#add_category_form #category_restrict_self_signup").prop('checked', false);
|
|
}
|
|
});
|
|
$("#edit_category_form #category_enable_self_signup").change(function() {
|
|
var self_signup = $(this).prop('checked');
|
|
var heterogenous = $(this).parents('.group_category').find('.heterogenous').text() == 'true';
|
|
var disable_restrict = !self_signup || heterogenous;
|
|
$("#edit_category_form #category_restrict_self_signup").prop('disabled', disable_restrict);
|
|
$("#edit_category_form #group_structure_self_signup_subcontainer").showIf(self_signup);
|
|
if (disable_restrict) {
|
|
$("#edit_category_form #category_restrict_self_signup").prop('checked', false);
|
|
}
|
|
});
|
|
$(".load_members_link").click(function(event) {
|
|
event.preventDefault();
|
|
contextGroups.loadMembersForGroup($(this).parents(".group"));
|
|
});
|
|
$(".delete_group_link").click(function(event) {
|
|
event.preventDefault();
|
|
$(this).parents(".group").confirmDelete({
|
|
message: I18n.t('confirm.delete_group', "Are you sure you want to delete this group?"),
|
|
url: $(this).attr('href'),
|
|
success: function() {
|
|
var $blank_category = $(this).parents(".group_category").find(".group_blank");
|
|
var group_id = $(this).getTemplateData({textValues: ['group_id']}).group_id;
|
|
$("#sidebar_group_" + group_id).slideUp(function() {
|
|
$(this).remove();
|
|
});
|
|
$(this).slideUp(function() {
|
|
var $category = $(this).parents(".group_category");
|
|
if (!$category.hasClass('student_organized')) {
|
|
$(this).find(".student").each(function() {
|
|
contextGroups.insertIntoGroup($(this), $blank_category);
|
|
});
|
|
}
|
|
$(this).remove();
|
|
contextGroups.updateCategoryCounts($category);
|
|
});
|
|
}
|
|
});
|
|
});
|
|
$(".assign_students_link").click(function(event) {
|
|
event.preventDefault();
|
|
|
|
// confirm before proceeding
|
|
var result = confirm(I18n.t('confirm.assign_students', "This will randomly assign all unassigned students as evenly as possible among the existing student groups"));
|
|
if (!result) { return; }
|
|
|
|
// indicate 'working' in visual state
|
|
var $category = $(this).parents(".group_category");
|
|
var $unassigned = $category.find(".group_blank");
|
|
$unassigned.find(".assign_students_link").hide();
|
|
$unassigned.find(".loading_members").text(I18n.t('status.assigning_students', "Assigning Students..."));
|
|
$unassigned.find(".loading_members").show();
|
|
|
|
// perform ajax request to do the assignment server side
|
|
var url = ENV.assign_unassigned_users_url;
|
|
url = $.replaceTags(url, "category_id", $category.data('category_id'));
|
|
$.ajaxJSON(url, "POST", {'sync': true}, function(data) {
|
|
if (!data.length) {
|
|
// reset visual state
|
|
$unassigned.find(".assign_students_link").show();
|
|
$unassigned.find(".loading_members").hide();
|
|
$(window).triggerHandler('resize');
|
|
$.flashError(I18n.t('notices.no_students_assigned', "Nothing to do."));
|
|
return;
|
|
}
|
|
var user_template = $(".user_template");
|
|
for (var i = 0; i < data.length; i++) {
|
|
var group = data[i];
|
|
var $group = $category.find('#group_' + group.id);
|
|
for (var j = 0; j < group.new_members.length; j++) {
|
|
var user = group.new_members[j];
|
|
|
|
// remove existing user element, if any
|
|
$category.find('.user_id_' + user.user_id).remove();
|
|
|
|
// create new user element and place it in the right group
|
|
var $user = user_template.clone();
|
|
contextGroups.populateUserElement($user, user);
|
|
contextGroups.insertIntoGroup($user, $group);
|
|
}
|
|
}
|
|
|
|
// update visual state
|
|
contextGroups.updateCategoryCounts($category);
|
|
$unassigned.find(".assign_students_link").show();
|
|
$unassigned.find(".loading_members").hide();
|
|
$(window).triggerHandler('resize');
|
|
$.flashMessage(I18n.t('notices.students_assigned', "Students assigned to groups."));
|
|
}, function(data) {
|
|
// reset visual state
|
|
$unassigned.find(".assign_students_link").show();
|
|
$unassigned.find(".loading_members").hide();
|
|
$(window).triggerHandler('resize');
|
|
});
|
|
});
|
|
$(".self_signup_help_link").click(function(event) {
|
|
event.preventDefault();
|
|
$("#self_signup_help_dialog").dialog({
|
|
title: I18n.t('titles.self_signup_help', "Self Sign-Up Groups"),
|
|
width: 400
|
|
});
|
|
});
|
|
$("#category_split_groups").on('click', function() {
|
|
$("#category_split_group_count").val("1");
|
|
});
|
|
$("#category_no_groups").on('click', function() {
|
|
$("#category_split_group_count").val("");
|
|
});
|
|
contextGroups.populateCategory($("#group_tabs .group_category:first"));
|
|
$(window).resize(function() {
|
|
if($(".group_category:visible:first").length == 0) { return; }
|
|
var top = $(".group_category:visible:first").offset().top;
|
|
var height = $(window).height();
|
|
$(".group_category").height(height - top - 40);
|
|
var $cat = $(".group_category:visible:first");
|
|
var catTop = $cat.offset().top;
|
|
var leftTop = $cat.find(".left_side").offset().top;
|
|
$(".group_category").find(".right_side,.left_side").height(height - top - 40 - (leftTop - catTop) + 10);
|
|
}).triggerHandler('resize');
|
|
$(".group_category .group").find(".expand_group_link").click(function(event) {
|
|
event.preventDefault();
|
|
$(this).hide()
|
|
.parents(".group").find(".student_list").slideDown().end()
|
|
.find(".load-more").slideDown().end()
|
|
.find(".collapse_group_link").show();
|
|
}).end().find(".collapse_group_link").click(function(event) {
|
|
event.preventDefault();
|
|
$(this).hide()
|
|
.parents(".group").find(".student_list").slideUp().end()
|
|
.find(".load-more").slideUp().end()
|
|
.find(".expand_group_link").show();
|
|
});
|
|
$(".group_category").find(".expand_groups_link").click(function(event) {
|
|
event.preventDefault();
|
|
$(this).parents(".group_category").find(".expand_group_link").click();
|
|
});
|
|
$(".group_category").find(".collapse_groups_link").click(function(event) {
|
|
event.preventDefault();
|
|
$(this).parents(".group_category").find(".collapse_group_link").click();
|
|
});
|
|
$(".group").hover(function() {
|
|
$(this).addClass('group-hover');
|
|
}, function() {
|
|
$(this).removeClass('group-hover');
|
|
});
|
|
$(".group_category").each(function() {
|
|
contextGroups.updateCategoryCounts($(this));
|
|
});
|
|
$("#tabs_loading_wrapper").show();
|
|
|
|
|
|
function loadUnassignedStudentsFor(categoryId) {
|
|
var $studentsDfrd = $.Deferred();
|
|
var students = [];
|
|
var baseUrl = ENV.list_unassigned_users_url + "?no_html=1&category_id=" + categoryId + "&per_page=100&page=";
|
|
|
|
var fetch = function(url) {
|
|
$.ajaxJSON(url, 'GET', null, function(data) {
|
|
_.each(data.users, function(user) {
|
|
students.push({id: user.user_id, short_name: user.display_name});
|
|
});
|
|
if (data.next_page)
|
|
fetch(baseUrl + data.next_page);
|
|
else
|
|
$studentsDfrd.resolve(students);
|
|
}, function() { $studentsDfrd.reject(); });
|
|
};
|
|
|
|
fetch(baseUrl + "1");
|
|
return $studentsDfrd;
|
|
}
|
|
|
|
$(".message_students_link").click(preventDefault(function() {
|
|
// jQuery sadness until we rewrite public/javascripts/manage_groups.js :(
|
|
var $category = $(this).closest('.group_category');
|
|
var categoryName = $category.find('.category_name').first().text();
|
|
var categoryId = $category.data('category_id');
|
|
|
|
loadUnassignedStudentsFor(categoryId).then(function(students) {
|
|
var dialog = new MessageStudentsDialog({
|
|
context: categoryName,
|
|
recipientGroups: [
|
|
{name: I18n.t('students_who_have_not_joined_a_group', 'Students who have not joined a group'), recipients: students}
|
|
]});
|
|
dialog.open();
|
|
});
|
|
}));
|
|
});
|
|
});
|
|
|