don't let students message the whole class by default, fixes CNVS-3536
adds new "Send messages to the entire class" permission, which defaults to off for students. controls whether or not the "Select All" checkbox and the checkboxes next to Everybody/Teachers/Students/etc. are available in the recipient finder. test plan: 1. As a teacher, confirm that the Everybody/Teachers/Students/Select All checkboxes are available in any courses you teach 2. As a student, confirm that the Everybody/Teachers/Students/Select All checkboxes are not available in any courses where you are just a student 3. In course permissions, let students "Send messages to the entire class" 4. Confirm that students now see all those checkboxes Change-Id: I5bca82414dc6e1e2f19abdd2e3257ca935d6f2c4 Reviewed-on: https://gerrit.instructure.com/17519 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Joe Tanner <joe@instructure.com> QA-Review: Cam Theriault <cam@instructure.com>
This commit is contained in:
parent
b290e23d31
commit
d1434fffb3
|
@ -543,6 +543,8 @@ define [
|
|||
delete data.avatar_url # since it's the wrong size and possibly a blank image
|
||||
currentData = @userCache[data.id] ? {}
|
||||
@userCache[data.id] = $.extend(currentData, data)
|
||||
canToggle: (data) ->
|
||||
data.type is 'user' or data.permissions?.send_messages_all
|
||||
selector:
|
||||
limiter: (options) =>
|
||||
if options.level > 0 then -1 else 5
|
||||
|
@ -550,25 +552,30 @@ define [
|
|||
preparer: (postData, data, parent) =>
|
||||
context = postData.context
|
||||
if not postData.search and context and data.length > 1
|
||||
parentData = parent.data('user_data')
|
||||
if context.match(/^(course|section)_\d+$/)
|
||||
# i.e. we are listing synthetic contexts under a course or section
|
||||
data.unshift
|
||||
id: "#{context}_all"
|
||||
name: everyoneText
|
||||
user_count: parent.data('user_data').user_count
|
||||
user_count: parentData.user_count
|
||||
type: 'context'
|
||||
avatar_url: parent.data('user_data').avatar_url
|
||||
selectAll: true
|
||||
else if context.match(/^((course|section)_\d+_.*|group_\d+)$/) and not context.match(/^course_\d+_(groups|sections)$/)
|
||||
avatar_url: parentData.avatar_url
|
||||
permissions: parentData.permissions
|
||||
selectAll: parentData.permissions.send_messages_all
|
||||
else if context.match(/^((course|section)_\d+_.*|group_\d+)$/) and not context.match(/^course_\d+_(groups|sections)$/) and parentData.permissions.send_messages_all
|
||||
# i.e. we are listing all users in a group or synthetic context
|
||||
data.unshift
|
||||
id: context
|
||||
name: selectAllText
|
||||
user_count: parent.data('user_data').user_count
|
||||
user_count: parentData.user_count
|
||||
type: 'context'
|
||||
avatar_url: parent.data('user_data').avatar_url
|
||||
avatar_url: parentData.avatar_url
|
||||
permissions: parentData.permissions
|
||||
selectAll: true
|
||||
noExpand: true # just a magic select-all checkbox, you can't drill into it
|
||||
baseData:
|
||||
permissions: ["send_messages_all"]
|
||||
|
||||
return if $scope
|
||||
|
||||
|
|
|
@ -45,10 +45,13 @@ define [
|
|||
options = $.extend true, {}, @defaults(), options
|
||||
@prefixUserIds = options.prefixUserIds
|
||||
@contexts = options.contexts
|
||||
@canToggle = options.canToggle if options.canToggle
|
||||
super $node, options
|
||||
|
||||
populator: (selector, $node, data, options={}) =>
|
||||
data.id = "#{data.id}"
|
||||
data.type ?= 'user'
|
||||
|
||||
if data.avatar_url
|
||||
$img = $('<img class="avatar" />')
|
||||
$img.attr('src', data.avatar_url)
|
||||
|
@ -61,7 +64,7 @@ define [
|
|||
$span = $('<span />', class: 'details')
|
||||
if data.common_courses?
|
||||
$span.html(@contextList(courses: data.common_courses, groups: data.common_groups))
|
||||
else if data.type and data.user_count?
|
||||
else if data.user_count?
|
||||
$span.text(I18n.t('people_count', 'person', {count: data.user_count}))
|
||||
else if data.item_count?
|
||||
if data.id.match(/_groups$/)
|
||||
|
@ -82,14 +85,17 @@ define [
|
|||
$node.data('id', if data.type is 'context' or not @prefixUserIds then data.id else "user_#{data.id}")
|
||||
data.rootId = options.ancestors[0]
|
||||
$node.data('user_data', data)
|
||||
$node.addClass(if data.type then data.type else 'user')
|
||||
$node.addClass(data.type)
|
||||
if options.level > 0 and selector.options.showToggles
|
||||
$node.prepend('<a class="toggle"><i></i></a>')
|
||||
$node.addClass('toggleable') unless data.item_count # can't toggle certain synthetic contexts, e.g. "Student Groups"
|
||||
if data.type == 'context' and not data.noExpand
|
||||
$node.addClass('toggleable') if @canToggle(data)
|
||||
if data.type is 'context' and not data.noExpand
|
||||
$node.prepend('<a class="expand"><i></i></a>')
|
||||
$node.addClass('expandable')
|
||||
|
||||
canToggle: (data) ->
|
||||
not data.item_count # can't toggle certain synthetic contexts, e.g. "Student Groups"
|
||||
|
||||
buildContextInfo: (data) =>
|
||||
match = data.id.match(/^(course|section)_(\d+)$/)
|
||||
termInfo = @contexts["#{match[1]}s"][match[2]] if match
|
||||
|
|
|
@ -31,7 +31,6 @@ define [
|
|||
@fetchListAjaxRequests = []
|
||||
@queryCache = {}
|
||||
@$container = $('<div />').addClass('autocomplete_menu')
|
||||
@$container.addClass('with-toggles') if @options.showToggles
|
||||
@$menu = $('<div />').append(@$list = @newList())
|
||||
@$container.append($('<div />').append(@$menu))
|
||||
@$container.css('top', 0).css('left', 0)
|
||||
|
@ -92,7 +91,9 @@ define [
|
|||
@clear()
|
||||
@close()
|
||||
@input.focus()
|
||||
$list.body = $list.find('ul').last()
|
||||
$uls = $list.find('ul')
|
||||
$list.heading = $uls.first()
|
||||
$list.body = $uls.last()
|
||||
$list
|
||||
|
||||
captureKeyDown: (e) ->
|
||||
|
@ -367,6 +368,9 @@ define [
|
|||
@$list.disableWhileLoading(deferred)
|
||||
deferred
|
||||
|
||||
toggleableItems: (data) ->
|
||||
return false unless data.length
|
||||
|
||||
renderList: (data, options={}, postData={}) ->
|
||||
@open()
|
||||
|
||||
|
@ -377,10 +381,8 @@ define [
|
|||
$list.selectAll = null
|
||||
|
||||
@selection = null
|
||||
$uls = $list.find('ul')
|
||||
$uls.html('')
|
||||
$heading = $uls.first()
|
||||
$body = $uls.last()
|
||||
$list.heading.html('')
|
||||
$list.body.html('')
|
||||
if data.length
|
||||
parent = if @stack.length then @stack[@stack.length - 1][0] else null
|
||||
ancestors = if @stack.length then (ancestor[0].data('id') for ancestor in @stack) else []
|
||||
|
@ -393,19 +395,20 @@ define [
|
|||
@populateRow($li, row, level: @stack.length, first: (i is 0), last: (i is data.length - 1), parent: parent, ancestors: ancestors)
|
||||
$list.selectAll = $li if row.selectAll
|
||||
$li.addClass('on') if $li.hasClass('toggleable') and @input.hasToken($li.data('id'))
|
||||
$body.append($li)
|
||||
$list.body.append($li)
|
||||
$list.body.find('li.toggleable').addClass('on') if $list.selectAll?.hasClass?('on') or @stack.length and @stack[@stack.length - 1][0].hasClass?('on')
|
||||
else
|
||||
$message = $('<li class="message first last"></li>')
|
||||
$message.text(@options.messages?.noResults ? '')
|
||||
$body.append($message)
|
||||
$list.body.append($message)
|
||||
$list.toggleClass('with-toggles', @options.showToggles and $list.body.find('li.toggleable').length > 0)
|
||||
|
||||
if @listExpanded()
|
||||
$li = @stack[@stack.length - 1][0].clone()
|
||||
$li.addClass('expanded').removeClass('active first last')
|
||||
$heading.append($li).show()
|
||||
$list.heading.append($li).show()
|
||||
else
|
||||
$heading.hide()
|
||||
$list.heading.hide()
|
||||
|
||||
if options.expand
|
||||
$list.insertAfter(@$list)
|
||||
|
|
|
@ -239,7 +239,8 @@ class RoleOverridesController < ApplicationController
|
|||
# read_reports -- [sTAD ] View usage reports for the course
|
||||
# read_roster -- [STADo] See the list of users
|
||||
# read_sis -- [sTa ] Read SIS data
|
||||
# send_messages -- [STADo] Send messages to course members
|
||||
# send_messages -- [STADo] Send messages to individual course members
|
||||
# send_messages_all -- [sTADo] Send messages to the entire class
|
||||
# view_all_grades -- [ TAd ] View all grades
|
||||
# view_group_pages -- [sTADo] View the group pages of all student groups
|
||||
#
|
||||
|
|
|
@ -55,6 +55,10 @@ class SearchController < ApplicationController
|
|||
# @argument type ["user"|"context"] Limit the search just to users or contexts (groups/courses).
|
||||
# @argument user_id [Integer] Search for a specific user id. This ignores the other above parameters, and will never return more than one result.
|
||||
# @argument from_conversation_id [Integer] When searching by user_id, only users that could be normally messaged by this user will be returned. This parameter allows you to specify a conversation that will be referenced for a shared context -- if both the current user and the searched user are in the conversation, the user will be returned. This is used to start new side conversations.
|
||||
# @argument permissions[] Array of permission strings to be checked for each
|
||||
# matched context (e.g. "send_messages"). This argument determines which
|
||||
# permissions may be returned in the response; it won't prevent contexts
|
||||
# from being returned if they don't grant the permission(s)
|
||||
#
|
||||
# @example_response
|
||||
# [
|
||||
|
@ -77,12 +81,16 @@ class SearchController < ApplicationController
|
|||
# enrollment types for each course to show what they share with this user
|
||||
# @response_field common_groups Only set for users. Hash of group ids and
|
||||
# enrollment types for each group to show what they share with this user
|
||||
# @response_field permissions[] Only set for contexts. Mapping of requested
|
||||
# permissions that the context grants the current user, e.g.
|
||||
# {send_messages: true}
|
||||
def recipients
|
||||
|
||||
# admins may not be able to see the course listed at the top level (since
|
||||
# they aren't enrolled in it), but if they search within it, we want
|
||||
# things to work, so we set everything up here
|
||||
load_all_contexts :context => get_admin_search_context(params[:context])
|
||||
load_all_contexts :context => get_admin_search_context(params[:context]),
|
||||
:permissions => params[:permissions]
|
||||
|
||||
types = (params[:types] || [] + [params[:type]]).compact
|
||||
types |= [:course, :section, :group] if types.delete('context')
|
||||
|
@ -179,9 +187,8 @@ class SearchController < ApplicationController
|
|||
end
|
||||
elsif options[:synthetic_contexts]
|
||||
if context_name =~ /\Acourse_(\d+)(_(groups|sections))?\z/ && (course = @contexts[:courses][$1.to_i]) && messageable_context_states[course[:state]]
|
||||
course = Course.find_by_id(course[:id])
|
||||
sections = @contexts[:sections].values.select{ |section| section[:parent] == {:course => course.id} }
|
||||
groups = @contexts[:groups].values.select{ |group| group[:parent] == {:course => course.id} }
|
||||
sections = @contexts[:sections].values.select{ |section| section[:parent] == {:course => course[:id]} }
|
||||
groups = @contexts[:groups].values.select{ |group| group[:parent] == {:course => course[:id]} }
|
||||
case context_name
|
||||
when /\Acourse_\d+\z/
|
||||
if terms.present? || options[:search_all_contexts] # search all groups and sections (and users)
|
||||
|
@ -203,8 +210,7 @@ class SearchController < ApplicationController
|
|||
if terms.present? # we'll just search the users
|
||||
result = []
|
||||
else
|
||||
section = CourseSection.find_by_id(section[:id])
|
||||
return synthetic_contexts_for(section.course, context_name)
|
||||
return synthetic_contexts_for(course_for_section(section), context_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -227,8 +233,16 @@ class SearchController < ApplicationController
|
|||
:name => context[:name],
|
||||
:avatar_url => avatar_url,
|
||||
:type => :context,
|
||||
:user_count => user_counts[context[:type]][context[:id]]
|
||||
:user_count => user_counts[context[:type]][context[:id]],
|
||||
:permissions => context[:permissions] || {}
|
||||
}
|
||||
if context[:type] == :section
|
||||
# TODO: have load_all_contexts actually return section-level
|
||||
# permissions. but before we do that, sections will need to grant many
|
||||
# more permission (possibly inherited from the course, like
|
||||
# :send_messages_all)
|
||||
ret[:permissions] = course_for_section(context)[:permissions]
|
||||
end
|
||||
ret[:context_name] = context[:context_name] if context[:context_name] && context_name.nil?
|
||||
ret
|
||||
}
|
||||
|
@ -240,6 +254,10 @@ class SearchController < ApplicationController
|
|||
options[:limit] ? result[offset, offset + options[:limit]] : result
|
||||
end
|
||||
|
||||
def course_for_section(section)
|
||||
@contexts[:courses][section[:parent][:course]]
|
||||
end
|
||||
|
||||
def synthetic_contexts_for(course, context)
|
||||
@skip_users = true
|
||||
# TODO: move the aggregation entirely into the DB. we only select a little
|
||||
|
@ -247,17 +265,18 @@ class SearchController < ApplicationController
|
|||
users = @current_user.messageable_users(:context => context)
|
||||
enrollment_counts = {:all => users.size}
|
||||
users.each do |user|
|
||||
user.common_courses[course.id].uniq.each do |role|
|
||||
user.common_courses[course[:id]].uniq.each do |role|
|
||||
enrollment_counts[role] ||= 0
|
||||
enrollment_counts[role] += 1
|
||||
end
|
||||
end
|
||||
avatar_url = avatar_url_for_group(blank_fallback)
|
||||
result = []
|
||||
result << {:id => "#{context}_teachers", :name => t(:enrollments_teachers, "Teachers"), :user_count => enrollment_counts['TeacherEnrollment'], :avatar_url => avatar_url, :type => :context} if enrollment_counts['TeacherEnrollment'].to_i > 0
|
||||
result << {:id => "#{context}_tas", :name => t(:enrollments_tas, "Teaching Assistants"), :user_count => enrollment_counts['TaEnrollment'], :avatar_url => avatar_url, :type => :context} if enrollment_counts['TaEnrollment'].to_i > 0
|
||||
result << {:id => "#{context}_students", :name => t(:enrollments_students, "Students"), :user_count => enrollment_counts['StudentEnrollment'], :avatar_url => avatar_url, :type => :context} if enrollment_counts['StudentEnrollment'].to_i > 0
|
||||
result << {:id => "#{context}_observers", :name => t(:enrollments_observers, "Observers"), :user_count => enrollment_counts['ObserverEnrollment'], :avatar_url => avatar_url, :type => :context} if enrollment_counts['ObserverEnrollment'].to_i > 0
|
||||
synthetic_context = {:avatar_url => avatar_url, :type => :context, :permissions => course[:permissions]}
|
||||
result << synthetic_context.merge({:id => "#{context}_teachers", :name => t(:enrollments_teachers, "Teachers"), :user_count => enrollment_counts['TeacherEnrollment']}) if enrollment_counts['TeacherEnrollment'].to_i > 0
|
||||
result << synthetic_context.merge({:id => "#{context}_tas", :name => t(:enrollments_tas, "Teaching Assistants"), :user_count => enrollment_counts['TaEnrollment']}) if enrollment_counts['TaEnrollment'].to_i > 0
|
||||
result << synthetic_context.merge({:id => "#{context}_students", :name => t(:enrollments_students, "Students"), :user_count => enrollment_counts['StudentEnrollment']}) if enrollment_counts['StudentEnrollment'].to_i > 0
|
||||
result << synthetic_context.merge({:id => "#{context}_observers", :name => t(:enrollments_observers, "Observers"), :user_count => enrollment_counts['ObserverEnrollment']}) if enrollment_counts['ObserverEnrollment'].to_i > 0
|
||||
result
|
||||
end
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ module SearchHelper
|
|||
permissions = options[:permissions] || []
|
||||
@contexts.each do |type, contexts|
|
||||
contexts.each do |id, context|
|
||||
context[:permissions] ||= {}
|
||||
context[:permissions] = HashWithIndifferentAccess.new(context[:permissions] || {})
|
||||
context[:permissions].slice!(*permissions) unless permissions == :all
|
||||
end
|
||||
end
|
||||
|
|
|
@ -319,6 +319,7 @@ class Group < ActiveRecord::Base
|
|||
can :read and
|
||||
can :read_roster and
|
||||
can :send_messages and
|
||||
can :send_messages_all and
|
||||
can :follow
|
||||
|
||||
# if I am a member of this group and I can moderate_forum in the group's context
|
||||
|
|
|
@ -168,7 +168,7 @@ class RoleOverride < ActiveRecord::Base
|
|||
]
|
||||
},
|
||||
:send_messages => {
|
||||
:label => lambda { t('permissions.send_messages', "Send messages to course members") },
|
||||
:label => lambda { t('permissions.send_messages', "Send messages to individual course members") },
|
||||
:available_to => [
|
||||
'StudentEnrollment',
|
||||
'TaEnrollment',
|
||||
|
@ -187,6 +187,25 @@ class RoleOverride < ActiveRecord::Base
|
|||
'AccountAdmin'
|
||||
]
|
||||
},
|
||||
:send_messages_all => {
|
||||
:label => lambda { t('permissions.send_messages_all', "Send messages to the entire class") },
|
||||
:available_to => [
|
||||
'StudentEnrollment',
|
||||
'TaEnrollment',
|
||||
'DesignerEnrollment',
|
||||
'TeacherEnrollment',
|
||||
'TeacherlessStudentEnrollment',
|
||||
'ObserverEnrollment',
|
||||
'AccountAdmin',
|
||||
'AccountMembership'
|
||||
],
|
||||
:true_for => [
|
||||
'TaEnrollment',
|
||||
'DesignerEnrollment',
|
||||
'TeacherEnrollment',
|
||||
'AccountAdmin'
|
||||
]
|
||||
},
|
||||
:manage_outcomes => {
|
||||
:label => lambda { t('permissions.manage_outcomes', "Manage learning outcomes") },
|
||||
:available_to => [
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
i
|
||||
background: transparent url(/images/messages/expand-context.png) no-repeat 10px -23px
|
||||
a.toggle
|
||||
display: none
|
||||
float: left
|
||||
margin: 0 14px 0 6px
|
||||
i
|
||||
|
@ -87,13 +88,15 @@
|
|||
i
|
||||
background-position: 10px -55px
|
||||
a.toggle
|
||||
display: none
|
||||
display: none !important
|
||||
li.active.expanded
|
||||
a.expand:hover
|
||||
cursor: pointer
|
||||
background: #fff
|
||||
border-color: #e4ebef
|
||||
&.with-toggles
|
||||
div.with-toggles
|
||||
a.toggle
|
||||
display: block
|
||||
li.expanded, li.active.expanded
|
||||
padding-left: 11px
|
||||
img.avatar
|
||||
|
|
|
@ -970,10 +970,10 @@ describe ConversationsController, :type => :integration do
|
|||
{ :controller => 'search', :action => 'recipients', :format => 'json', :search => 'o' })
|
||||
json.each { |c| c.delete("avatar_url") }
|
||||
json.should eql [
|
||||
{"id" => "course_#{@course.id}", "name" => "the course", "type" => "context", "user_count" => 6},
|
||||
{"id" => "section_#{@other_section.id}", "name" => "the other section", "type" => "context", "user_count" => 1, "context_name" => "the course"},
|
||||
{"id" => "section_#{@course.default_section.id}", "name" => "the section", "type" => "context", "user_count" => 5, "context_name" => "the course"},
|
||||
{"id" => "group_#{@group.id}", "name" => "the group", "type" => "context", "user_count" => 3, "context_name" => "the course"},
|
||||
{"id" => "course_#{@course.id}", "name" => "the course", "type" => "context", "user_count" => 6, "permissions" => {}},
|
||||
{"id" => "section_#{@other_section.id}", "name" => "the other section", "type" => "context", "user_count" => 1, "context_name" => "the course", "permissions" => {}},
|
||||
{"id" => "section_#{@course.default_section.id}", "name" => "the section", "type" => "context", "user_count" => 5, "context_name" => "the course", "permissions" => {}},
|
||||
{"id" => "group_#{@group.id}", "name" => "the group", "type" => "context", "user_count" => 3, "context_name" => "the course", "permissions" => {}},
|
||||
{"id" => @bob.id, "name" => "bob", "common_courses" => {@course.id.to_s => ["StudentEnrollment"]}, "common_groups" => {@group.id.to_s => ["Member"]}},
|
||||
{"id" => @joe.id, "name" => "joe", "common_courses" => {@course.id.to_s => ["StudentEnrollment"]}, "common_groups" => {@group.id.to_s => ["Member"]}},
|
||||
{"id" => @me.id, "name" => @me.name, "common_courses" => {@course.id.to_s => ["TeacherEnrollment"]}, "common_groups" => {@group.id.to_s => ["Member"]}},
|
||||
|
|
|
@ -35,10 +35,10 @@ describe SearchController, :type => :integration do
|
|||
{ :controller => 'search', :action => 'recipients', :format => 'json', :search => 'o' })
|
||||
json.each { |c| c.delete("avatar_url") }
|
||||
json.should eql [
|
||||
{"id" => "course_#{@course.id}", "name" => "the course", "type" => "context", "user_count" => 6},
|
||||
{"id" => "section_#{@other_section.id}", "name" => "the other section", "type" => "context", "user_count" => 1, "context_name" => "the course"},
|
||||
{"id" => "section_#{@course.default_section.id}", "name" => "the section", "type" => "context", "user_count" => 5, "context_name" => "the course"},
|
||||
{"id" => "group_#{@group.id}", "name" => "the group", "type" => "context", "user_count" => 3, "context_name" => "the course"},
|
||||
{"id" => "course_#{@course.id}", "name" => "the course", "type" => "context", "user_count" => 6, "permissions" => {}},
|
||||
{"id" => "section_#{@other_section.id}", "name" => "the other section", "type" => "context", "user_count" => 1, "context_name" => "the course", "permissions" => {}},
|
||||
{"id" => "section_#{@course.default_section.id}", "name" => "the section", "type" => "context", "user_count" => 5, "context_name" => "the course", "permissions" => {}},
|
||||
{"id" => "group_#{@group.id}", "name" => "the group", "type" => "context", "user_count" => 3, "context_name" => "the course", "permissions" => {}},
|
||||
{"id" => @bob.id, "name" => "bob", "common_courses" => {@course.id.to_s => ["StudentEnrollment"]}, "common_groups" => {@group.id.to_s => ["Member"]}},
|
||||
{"id" => @joe.id, "name" => "joe", "common_courses" => {@course.id.to_s => ["StudentEnrollment"]}, "common_groups" => {@group.id.to_s => ["Member"]}},
|
||||
{"id" => @me.id, "name" => @me.name, "common_courses" => {@course.id.to_s => ["TeacherEnrollment"]}, "common_groups" => {@group.id.to_s => ["Member"]}},
|
||||
|
@ -210,8 +210,8 @@ describe SearchController, :type => :integration do
|
|||
{ :controller => 'search', :action => 'recipients', :format => 'json', :context => "course_#{@course.id}", :synthetic_contexts => "1" })
|
||||
json.each { |c| c.delete("avatar_url") }
|
||||
json.should eql [
|
||||
{"id" => "course_#{@course.id}_teachers", "name" => "Teachers", "type" => "context", "user_count" => 1},
|
||||
{"id" => "course_#{@course.id}_students", "name" => "Students", "type" => "context", "user_count" => 5},
|
||||
{"id" => "course_#{@course.id}_teachers", "name" => "Teachers", "type" => "context", "user_count" => 1, "permissions" => {}},
|
||||
{"id" => "course_#{@course.id}_students", "name" => "Students", "type" => "context", "user_count" => 5, "permissions" => {}},
|
||||
{"id" => "course_#{@course.id}_sections", "name" => "Course Sections", "type" => "context", "item_count" => 2},
|
||||
{"id" => "course_#{@course.id}_groups", "name" => "Student Groups", "type" => "context", "item_count" => 1}
|
||||
]
|
||||
|
@ -222,8 +222,8 @@ describe SearchController, :type => :integration do
|
|||
{ :controller => 'search', :action => 'recipients', :format => 'json', :context => "section_#{@course.default_section.id}", :synthetic_contexts => "1" })
|
||||
json.each { |c| c.delete("avatar_url") }
|
||||
json.should eql [
|
||||
{"id" => "section_#{@course.default_section.id}_teachers", "name" => "Teachers", "type" => "context", "user_count" => 1},
|
||||
{"id" => "section_#{@course.default_section.id}_students", "name" => "Students", "type" => "context", "user_count" => 4}
|
||||
{"id" => "section_#{@course.default_section.id}_teachers", "name" => "Teachers", "type" => "context", "user_count" => 1, "permissions" => {}},
|
||||
{"id" => "section_#{@course.default_section.id}_students", "name" => "Students", "type" => "context", "user_count" => 4, "permissions" => {}}
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -232,7 +232,7 @@ describe SearchController, :type => :integration do
|
|||
{ :controller => 'search', :action => 'recipients', :format => 'json', :context => "course_#{@course.id}_groups", :synthetic_contexts => "1" })
|
||||
json.each { |c| c.delete("avatar_url") }
|
||||
json.should eql [
|
||||
{"id" => "group_#{@group.id}", "name" => "the group", "type" => "context", "user_count" => 3}
|
||||
{"id" => "group_#{@group.id}", "name" => "the group", "type" => "context", "user_count" => 3, "permissions" => {}}
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -241,8 +241,34 @@ describe SearchController, :type => :integration do
|
|||
{ :controller => 'search', :action => 'recipients', :format => 'json', :context => "course_#{@course.id}_sections", :synthetic_contexts => "1" })
|
||||
json.each { |c| c.delete("avatar_url") }
|
||||
json.should eql [
|
||||
{"id" => "section_#{@other_section.id}", "name" => @other_section.name, "type" => "context", "user_count" => 1},
|
||||
{"id" => "section_#{@course.default_section.id}", "name" => @course.default_section.name, "type" => "context", "user_count" => 5}
|
||||
{"id" => "section_#{@other_section.id}", "name" => @other_section.name, "type" => "context", "user_count" => 1, "permissions" => {}},
|
||||
{"id" => "section_#{@course.default_section.id}", "name" => @course.default_section.name, "type" => "context", "user_count" => 5, "permissions" => {}}
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
context "permissions" do
|
||||
it "should return valid permission data" do
|
||||
json = api_call(:get, "/api/v1/search/recipients.json?search=the%20o&permissions[]=send_messages_all",
|
||||
{ :controller => 'search', :action => 'recipients', :format => 'json', :search => 'the o', :permissions => ["send_messages_all"] })
|
||||
json.each { |c| c.delete("avatar_url") }
|
||||
json.should eql [
|
||||
{"id" => "course_#{@course.id}", "name" => "the course", "type" => "context", "user_count" => 6, "permissions" => {"send_messages_all" => true}},
|
||||
{"id" => "section_#{@other_section.id}", "name" => "the other section", "type" => "context", "user_count" => 1, "context_name" => "the course", "permissions" => {"send_messages_all" => true}},
|
||||
{"id" => "section_#{@course.default_section.id}", "name" => "the section", "type" => "context", "user_count" => 5, "context_name" => "the course", "permissions" => {"send_messages_all" => true}},
|
||||
{"id" => "group_#{@group.id}", "name" => "the group", "type" => "context", "user_count" => 3, "context_name" => "the course", "permissions" => {"send_messages_all" => true}}
|
||||
]
|
||||
end
|
||||
|
||||
it "should not return invalid permission data" do
|
||||
json = api_call(:get, "/api/v1/search/recipients.json?search=the%20o&permissions[]=foo_bar",
|
||||
{ :controller => 'search', :action => 'recipients', :format => 'json', :search => 'the o', :permissions => ["foo_bar"] })
|
||||
json.each { |c| c.delete("avatar_url") }
|
||||
json.should eql [
|
||||
{"id" => "course_#{@course.id}", "name" => "the course", "type" => "context", "user_count" => 6, "permissions" => {}},
|
||||
{"id" => "section_#{@other_section.id}", "name" => "the other section", "type" => "context", "user_count" => 1, "context_name" => "the course", "permissions" => {}},
|
||||
{"id" => "section_#{@course.default_section.id}", "name" => "the section", "type" => "context", "user_count" => 5, "context_name" => "the course", "permissions" => {}},
|
||||
{"id" => "group_#{@group.id}", "name" => "the group", "type" => "context", "user_count" => 3, "context_name" => "the course", "permissions" => {}}
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,7 +31,7 @@ describe SearchHelper do
|
|||
@contexts[:courses][@course.id][:permissions].should be_empty
|
||||
|
||||
load_all_contexts(:permissions => [:manage_assignments])
|
||||
@contexts[:courses][@course.id][:permissions].should eql({:manage_assignments => true})
|
||||
@contexts[:courses][@course.id][:permissions][:manage_assignments].should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,6 +26,7 @@ describe "conversations recipient finder" do
|
|||
menu.should == ["the course", "the group"]
|
||||
browse "the course" do
|
||||
menu.should == ["Everyone", "Teachers", "Students", "Course Sections", "Student Groups"]
|
||||
toggleable.should == ["Everyone", "Teachers", "Students"]
|
||||
browse("Everyone") { menu.should == ["Select All", "nobody@example.com", "student 1", "student 2"] }
|
||||
browse("Teachers") { menu.should == ["nobody@example.com"] }
|
||||
browse("Students") { menu.should == ["Select All", "student 1", "student 2"] }
|
||||
|
@ -50,6 +51,40 @@ describe "conversations recipient finder" do
|
|||
browse("the group") { menu.should == ["Select All", "nobody@example.com", "student 1"] }
|
||||
end
|
||||
|
||||
it "should respect permissions" do
|
||||
# only affects courses/sections, not groups
|
||||
RoleOverride.create!(:context => Account.default, :permission => 'send_messages_all', :enrollment_type => 'TeacherEnrollment', :enabled => false)
|
||||
|
||||
browse_menu
|
||||
|
||||
menu.should == ["the course", "the group"]
|
||||
browse "the course" do
|
||||
menu.should == ["Everyone", "Teachers", "Students", "Course Sections", "Student Groups"]
|
||||
toggleable.should == []
|
||||
browse("Everyone") { menu.should == ["nobody@example.com", "student 1", "student 2"] }
|
||||
browse("Teachers") { menu.should == ["nobody@example.com"] }
|
||||
browse("Students") { menu.should == ["student 1", "student 2"] }
|
||||
browse "Course Sections" do
|
||||
menu.should == ["the other section", "the section"]
|
||||
browse "the other section" do
|
||||
menu.should == ["Students"]
|
||||
browse("Students") { menu.should == ["student 2"] }
|
||||
end
|
||||
browse "the section" do
|
||||
menu.should == ["Everyone", "Teachers", "Students"]
|
||||
browse("Everyone") { menu.should == ["nobody@example.com", "student 1"] }
|
||||
browse("Teachers") { menu.should == ["nobody@example.com"] }
|
||||
browse("Students") { menu.should == ["student 1"] }
|
||||
end
|
||||
end
|
||||
browse "Student Groups" do
|
||||
menu.should == ["the group"]
|
||||
browse("the group") { menu.should == ["Select All", "nobody@example.com", "student 1"] }
|
||||
end
|
||||
end
|
||||
browse("the group") { menu.should == ["Select All", "nobody@example.com", "student 1"] }
|
||||
end
|
||||
|
||||
it "should return recently concluded courses" do
|
||||
@course.complete!
|
||||
|
||||
|
|
|
@ -77,8 +77,16 @@ shared_examples_for "conversations selenium tests" do
|
|||
elements.map(&:last)
|
||||
end
|
||||
|
||||
def toggleable
|
||||
with_class("toggleable")
|
||||
end
|
||||
|
||||
def toggled
|
||||
elements.select { |e| e.first.attribute('class') =~ /(^| )on($| )/ }.map(&:last)
|
||||
with_class("on")
|
||||
end
|
||||
|
||||
def with_class(klass)
|
||||
elements.select { |e| e.first.attribute('class') =~ /(\A| )#{klass}(\z| )/ }.map(&:last)
|
||||
end
|
||||
|
||||
def click(name)
|
||||
|
|
Loading…
Reference in New Issue