message students who haven't joined a group, fixes CNVS-5408
test plan: 1. allow self signup on a group category 2. click the "message students..." link that appears in the Unassigned section 3. you should be able to send a message to those students who have not joined a group yet Change-Id: I1766c8ade8233226a16dfd88b53524ae2e3d2f7e Reviewed-on: https://gerrit.instructure.com/20338 Reviewed-by: Jon Jensen <jon@instructure.com> Product-Review: Jon Jensen <jon@instructure.com> QA-Review: Jon Jensen <jon@instructure.com> Tested-by: Jenkins <jenkins@instructure.com>
This commit is contained in:
parent
88ca0e82da
commit
853e4a163e
|
@ -116,24 +116,28 @@ class GroupsController < ApplicationController
|
|||
category = @context.group_categories.find_by_id(params[:category_id])
|
||||
return render :json => {}, :status => :not_found unless category
|
||||
page = (params[:page] || 1).to_i rescue 1
|
||||
per_page = [[(params[:per_page] || 15).to_i, 1].max, 100].min
|
||||
if category && !category.student_organized?
|
||||
groups = category.groups.active
|
||||
else
|
||||
groups = []
|
||||
end
|
||||
users = @context.paginate_users_not_in_groups(groups, page)
|
||||
users = @context.paginate_users_not_in_groups(groups, page, per_page)
|
||||
|
||||
if authorized_action(@context, @current_user, :manage)
|
||||
respond_to do |format|
|
||||
format.json { render :json => {
|
||||
:pages => users.total_pages,
|
||||
:current_page => users.current_page,
|
||||
:next_page => users.next_page,
|
||||
:previous_page => users.previous_page,
|
||||
:total_entries => users.total_entries,
|
||||
:pagination_html => render_to_string(:partial => 'user_pagination', :locals => { :users => users }),
|
||||
:users => users.map { |u| u.group_member_json(@context) }
|
||||
} }
|
||||
format.json {
|
||||
json = {
|
||||
:pages => users.total_pages,
|
||||
:current_page => users.current_page,
|
||||
:next_page => users.next_page,
|
||||
:previous_page => users.previous_page,
|
||||
:total_entries => users.total_entries,
|
||||
:users => users.map { |u| u.group_member_json(@context) }
|
||||
}
|
||||
json[:pagination_html] = render_to_string(:partial => 'user_pagination', :locals => { :users => users }) unless params[:no_html]
|
||||
render :json => json
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
.message-students-dialog {
|
||||
textarea {
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
|
|
|
@ -62,11 +62,15 @@
|
|||
<h3 class="name blank_name"><%= t :group_category_unassigned, "Unassigned" %></h3>
|
||||
<% end %>
|
||||
<% unless @context.is_a?(Account) || protected_category %>
|
||||
<a href="#" class="assign_students_link no-hover"
|
||||
title="<%= t :random_distribution_explanation, "Distribute Unassigned Students Equally Among Groups" %>"
|
||||
style="<%= hidden if category && category.restricted_self_signup? %>">
|
||||
<%= t :randomly_distribute_students, "randomly assign students" %>
|
||||
</a>
|
||||
<span class="student_links">
|
||||
<a href="#" class="assign_students_link no-hover"
|
||||
title="<%= t :random_distribution_explanation, "Distribute Unassigned Students Equally Among Groups" %>"
|
||||
style="<%= hidden if !category || category.restricted_self_signup? %>">
|
||||
<%= t :randomly_distribute_students, "randomly assign students" %>
|
||||
</a>
|
||||
<span style="<%= hidden if !category || !category.self_signup? || category.restricted_self_signup? %>" class="students_link_separator"> | </span>
|
||||
<a href="#" style="<%= hidden if !category || !category.self_signup? %>" class="message_students_link"><%= t "message_students", "message students..." %></a>
|
||||
</span>
|
||||
<% end %>
|
||||
<div style="display: none;">
|
||||
<span class="category_name"> </span>
|
||||
|
|
|
@ -165,15 +165,13 @@ a.load_members_link, .loading_members {
|
|||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div style="display: none;" id="manage_group_urls">
|
||||
<a href="<%= group_add_user_url("{{ id }}") %>" class="add_user_url"> </a>
|
||||
<a href="<%= group_remove_user_url("{{ id }}") %>" class="remove_user_url"> </a>
|
||||
<a href="<%= group_members_url("{{ id }}") %>" class="list_users_url"> </a>
|
||||
<a href="<%= context_url(@context, :context_group_unassigned_members_url) %>" class="list_unassigned_users_url"> </a>
|
||||
<a href="<%= context_url(@context, :context_group_assign_unassigned_members_url, :category_id => "{{ category_id }}") %>" class="assign_unassigned_users_url"> </a>
|
||||
<a href="<%= context_url(@context, :context_groups_url) %>" class="add_group_url" %> </a>
|
||||
</div>
|
||||
|
||||
<% js_env :add_user_url => group_add_user_url("{{ id }}"),
|
||||
:remove_user_url => group_remove_user_url("{{ id }}"),
|
||||
:list_users_url => group_members_url("{{ id }}") ,
|
||||
:list_unassigned_users_url => context_url(@context, :context_group_unassigned_members_url),
|
||||
:assign_unassigned_users_url => context_url(@context, :context_group_assign_unassigned_members_url, :category_id => "{{ category_id }}"),
|
||||
:add_group_url => context_url(@context, :context_groups_url) %>
|
||||
|
||||
<div id="tabs_loading_wrapper" style="display: none;">
|
||||
<div style="text-align: left; <%= hidden unless @categories.empty? %>" id="no_groups_message">
|
||||
<% if @context.is_a?(Account) %>
|
||||
|
|
|
@ -19,6 +19,8 @@ define([
|
|||
'i18n!groups',
|
||||
'jquery' /* $ */,
|
||||
'underscore',
|
||||
'compiled/fn/preventDefault',
|
||||
'compiled/views/MessageStudentsDialog',
|
||||
'jqueryui/draggable' /* /\.draggable/ */,
|
||||
'jquery.ajaxJSON' /* ajaxJSON */,
|
||||
'jquery.instructure_forms' /* formSubmit, fillFormData, formErrors */,
|
||||
|
@ -31,7 +33,7 @@ define([
|
|||
'vendor/jquery.scrollTo' /* /\.scrollTo/ */,
|
||||
'jqueryui/droppable' /* /\.droppable/ */,
|
||||
'jqueryui/tabs' /* /\.tabs/ */
|
||||
], function(I18n, $, _) {
|
||||
], function(I18n, $, _, preventDefault, MessageStudentsDialog) {
|
||||
|
||||
window.contextGroups = {
|
||||
autoLoadGroupThreshold: 15,
|
||||
|
@ -49,7 +51,7 @@ define([
|
|||
},
|
||||
|
||||
loadMembersForGroup: function($group) {
|
||||
var url = $("#manage_group_urls .list_users_url").attr('href');
|
||||
var url = ENV.list_users_url;
|
||||
var id = $group.getTemplateData({textValues: ['group_id']}).group_id;
|
||||
url = $.replaceTags(url, "id", id)
|
||||
|
||||
|
@ -98,7 +100,7 @@ define([
|
|||
|
||||
// This is lots of duplicated code from above, with tweaks. TODO: Refactor
|
||||
var category_id = $group.closest(".group_category").data('category_id');
|
||||
var url = $("#manage_group_urls .list_unassigned_users_url").attr('href');
|
||||
var url = ENV.list_unassigned_users_url;
|
||||
url += "?category_id=" + category_id + "&page=" + page;
|
||||
|
||||
$group.find(".load_members_link").hide();
|
||||
|
@ -110,6 +112,7 @@ define([
|
|||
$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'];
|
||||
|
@ -139,10 +142,10 @@ define([
|
|||
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 = $("#manage_group_urls .add_user_url").attr('href');
|
||||
var url = ENV.add_user_url;
|
||||
method = "POST";
|
||||
if(!id || id.length == 0) {
|
||||
url = $("#manage_group_urls .remove_user_url").attr('href');
|
||||
url = ENV.remove_user_url;
|
||||
method = "DELETE";
|
||||
id = $original_group.getTemplateData({textValues: ['group_id']}).group_id;
|
||||
}
|
||||
|
@ -246,8 +249,10 @@ define([
|
|||
|
||||
updateCategoryCounts: function($category) {
|
||||
$category.find(".group").each(function() {
|
||||
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}));
|
||||
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;
|
||||
|
@ -319,6 +324,8 @@ define([
|
|||
$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) {
|
||||
|
@ -368,6 +375,7 @@ define([
|
|||
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();
|
||||
|
@ -423,7 +431,7 @@ define([
|
|||
if($group.attr('id')) {
|
||||
$form.attr('method', 'PUT').attr('action', $group.find(".edit_group_link").attr('href'));
|
||||
} else {
|
||||
$form.attr('method', 'POST').attr('action', $("#manage_group_urls .add_group_url").attr('href'));
|
||||
$form.attr('method', 'POST').attr('action', ENV.add_group_url);
|
||||
}
|
||||
if($group.length > 0) {
|
||||
$group.parents(".group_category").scrollTo($group);
|
||||
|
@ -601,6 +609,8 @@ define([
|
|||
$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')) {
|
||||
|
@ -728,7 +738,7 @@ define([
|
|||
$unassigned.find(".loading_members").show();
|
||||
|
||||
// perform ajax request to do the assignment server side
|
||||
var url = $("#manage_group_urls .assign_unassigned_users_url").attr('href');
|
||||
var url = ENV.assign_unassigned_users_url;
|
||||
url = $.replaceTags(url, "category_id", $category.data('category_id'));
|
||||
$.ajaxJSON(url, "POST", null, function(data) {
|
||||
if (!data.length) {
|
||||
|
@ -823,6 +833,44 @@ define([
|
|||
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();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../common')
|
|||
keep_trying_until { find_with_jquery("#add_category_form:visible").should be_nil }
|
||||
category = course.group_categories.find_by_name(name)
|
||||
category.should_not be_nil
|
||||
keep_trying_until { fj("#category_#{category.id} .student_links:visible") }
|
||||
category
|
||||
end
|
||||
|
||||
|
@ -105,4 +106,4 @@ require File.expand_path(File.dirname(__FILE__) + '/../common')
|
|||
$('#{from_group} .user_id_#{user_id}'),
|
||||
$('#{to_group}'))
|
||||
SCRIPT
|
||||
end
|
||||
end
|
||||
|
|
|
@ -171,6 +171,29 @@ describe "manage groups" do
|
|||
f('#no_students_message').should be_nil
|
||||
end
|
||||
|
||||
it "should let you message students not in a group" do
|
||||
groups_student_enrollment 3
|
||||
group_category1 = @course.group_categories.create(:name => "Project Groups")
|
||||
group_category2 = @course.group_categories.create(:name => "Self Signup Groups")
|
||||
group_category2.configure_self_signup(true, false)
|
||||
group_category2.save
|
||||
get "/courses/#{@course.id}/groups"
|
||||
wait_for_ajaximations
|
||||
|
||||
ff(".group_category").size.should == 3
|
||||
keep_trying_until { !f("#category_#{group_category1.id} .right_side .loading_members").displayed? }
|
||||
f('.group_category .student_links').should be_displayed
|
||||
f('.group_category .message_students_link').should_not be_displayed # only self signup can do it
|
||||
ff('.ui-tabs-anchor')[1].click
|
||||
|
||||
keep_trying_until { !f("#category_#{group_category2.id} .right_side .loading_members").displayed? }
|
||||
message_students_link = ff('.group_category .message_students_link')[1]
|
||||
message_students_link.should be_displayed
|
||||
message_students_link.click
|
||||
|
||||
keep_trying_until{ f('.message-students-dialog').should be_displayed }
|
||||
end
|
||||
|
||||
context "data validation" do
|
||||
before (:each) do
|
||||
student_in_course
|
||||
|
|
|
@ -369,21 +369,21 @@ describe "manage groups students" do
|
|||
|
||||
it "should give Assigning Students... visual feedback" do
|
||||
#pending "causes whatever spec follows this to fail even in different files"
|
||||
assign_students = fj("#category_#{@category.id} .assign_students_link:visible")
|
||||
assign_students.should_not be_nil
|
||||
assign_students.click
|
||||
# Do some magic to make sure the next ajax request doesn't complete until we're ready for it to
|
||||
lock = Mutex.new
|
||||
lock.lock
|
||||
GroupsController.before_filter { lock.lock; lock.unlock; true }
|
||||
confirm_dialog = driver.switch_to.alert
|
||||
confirm_dialog.accept
|
||||
loading = fj("#category_#{@category.id} .group_blank .loading_members:visible")
|
||||
loading.text.should == 'Assigning Students...'
|
||||
lock.unlock
|
||||
GroupsController.filter_chain.pop
|
||||
# make sure we wait before moving on
|
||||
wait_for_ajax_requests
|
||||
end
|
||||
assign_students = fj("#category_#{@category.id} .assign_students_link:visible")
|
||||
assign_students.should_not be_nil
|
||||
assign_students.click
|
||||
# Do some magic to make sure the next ajax request doesn't complete until we're ready for it to
|
||||
lock = Mutex.new
|
||||
lock.lock
|
||||
GroupsController.before_filter { lock.lock; lock.unlock; true }
|
||||
confirm_dialog = driver.switch_to.alert
|
||||
confirm_dialog.accept
|
||||
loading = fj("#category_#{@category.id} .group_blank .loading_members:visible")
|
||||
loading.text.should == 'Assigning Students...'
|
||||
lock.unlock
|
||||
GroupsController.filter_chain.pop
|
||||
# make sure we wait before moving on
|
||||
wait_for_ajax_requests
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue