Guard ungraded discussions with new student_visibilities
refs LF-1051 flag=differentiated_modules Test Plan 1. Check the following places for ungraded discussion 1a: Graphql discussion_type should be propertly guarded 1b. Discussion index page should be correctly guarded 1c. Discussion show page should be correctly guarded 1d. Discussion API should be correctly guarded 1e. Create an ungraded discussion that will unlock in the future and verify that the correct message appears when opening it as a student Change-Id: I54f7a3667312ae06e0181e68859156774e7ebe21 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/346014 Reviewed-by: Jackson Howe <jackson.howe@instructure.com> QA-Review: Jackson Howe <jackson.howe@instructure.com> Product-Review: Jason Gillett <jason.gillett@instructure.com> Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
This commit is contained in:
parent
75c00a176b
commit
337c8fb2c2
|
@ -741,9 +741,23 @@ class DiscussionTopic < ActiveRecord::Base
|
|||
topic_participant
|
||||
end
|
||||
|
||||
scope :joins_ungraded_discussion_student_visibilities, lambda { |user_ids, course_ids|
|
||||
joins(:ungraded_discussion_student_visibilities)
|
||||
.where(ungraded_discussion_student_visibilities: { user_id: user_ids, course_id: course_ids })
|
||||
}
|
||||
|
||||
scope :visible_to_students_in_course_with_da, lambda { |user_ids, course_ids|
|
||||
without_assignment_in_course(course_ids)
|
||||
.union(joins_assignment_student_visibilities(user_ids, course_ids))
|
||||
if Account.site_admin.feature_enabled?(:differentiated_modules)
|
||||
where.not(assignment_id: nil)
|
||||
.joins_assignment_student_visibilities(user_ids, course_ids)
|
||||
.union(
|
||||
where(assignment_id: nil)
|
||||
.joins_ungraded_discussion_student_visibilities(user_ids, course_ids)
|
||||
)
|
||||
else
|
||||
without_assignment_in_course(course_ids)
|
||||
.union(joins_assignment_student_visibilities(user_ids, course_ids))
|
||||
end
|
||||
}
|
||||
|
||||
scope :not_ignored_by, lambda { |user, purpose|
|
||||
|
@ -1620,14 +1634,27 @@ class DiscussionTopic < ActiveRecord::Base
|
|||
next false unless section_visibilities.intersect?(course_specific_sections)
|
||||
end
|
||||
end
|
||||
# Verify that section limited teachers/ta's are properly restricted when differentiated_modules is enabled
|
||||
if context.is_a?(Course) && (Account.site_admin.feature_enabled?(:differentiated_modules) && !visible_to_everyone && context.user_is_instructor?(user))
|
||||
section_overrides = assignment_overrides.active.where(set_type: "CourseSection").pluck(:set_id)
|
||||
visible_sections_for_user = context.course_section_visibility(user)
|
||||
next false if visible_sections_for_user == :none
|
||||
|
||||
# If there are no section_overrides, then no check for section_specific instructor roles is needed
|
||||
if visible_sections_for_user != :all && section_overrides.any?
|
||||
course_specific_sections = course_sections.pluck(:id)
|
||||
next false unless visible_sections_for_user.intersect?(course_specific_sections)
|
||||
end
|
||||
end
|
||||
# user is an admin in the context (teacher/ta/designer) OR
|
||||
# user is an account admin with appropriate permission
|
||||
next true if context.grants_any_right?(user, :manage, :read_course_content)
|
||||
|
||||
# assignment exists and isn't assigned to user (differentiated assignments)
|
||||
if for_assignment? && !assignment.visible_to_user?(user)
|
||||
next false
|
||||
if for_assignment?
|
||||
next false unless assignment.visible_to_user?(user)
|
||||
elsif Account.site_admin.feature_enabled?(:differentiated_modules)
|
||||
next false unless visible_to_user?(user)
|
||||
end
|
||||
|
||||
# topic is not published
|
||||
|
|
|
@ -53,6 +53,8 @@ module DifferentiableAssignment
|
|||
ModuleStudentVisibility
|
||||
when "WikiPage"
|
||||
WikiPageStudentVisibility
|
||||
when "DiscussionTopic"
|
||||
UngradedDiscussionStudentVisibility
|
||||
else
|
||||
Quizzes::QuizStudentVisibility
|
||||
end
|
||||
|
@ -66,6 +68,8 @@ module DifferentiableAssignment
|
|||
:context_module_id
|
||||
when "WikiPage"
|
||||
:wiki_page_id
|
||||
when "DiscussionTopic"
|
||||
:discussion_topic_id
|
||||
else
|
||||
:quiz_id
|
||||
end
|
||||
|
|
|
@ -1163,6 +1163,88 @@ describe DiscussionTopicsController, type: :request do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "differentiated modules" do
|
||||
before do
|
||||
Account.site_admin.enable_feature! :differentiated_modules
|
||||
end
|
||||
|
||||
context "ungraded discussions" do
|
||||
before do
|
||||
course_factory(active_all: true)
|
||||
@course_section = @course.course_sections.create
|
||||
|
||||
@student1, @student2 = create_users(2, return_type: :record)
|
||||
@course.enroll_student(@student1, enrollment_state: "active")
|
||||
@course.enroll_student(@student2, enrollment_state: "active")
|
||||
student_in_section(@course.course_sections.first, user: @student1)
|
||||
student_in_section(@course.course_sections.second, user: @student2)
|
||||
|
||||
@teacher = teacher_in_course(course: @course, active_enrollment: true).user
|
||||
@topic_visible_to_everyone = discussion_topic_model(user: @teacher, context: @course)
|
||||
@topic = discussion_topic_model(user: @teacher, context: @course)
|
||||
@topic.update!(only_visible_to_overrides: true)
|
||||
end
|
||||
|
||||
it "shows only the visible topics" do
|
||||
override = @topic.assignment_overrides.create!
|
||||
override.assignment_override_students.create!(user: @student1)
|
||||
|
||||
@user = @student2
|
||||
|
||||
json = api_call(:get,
|
||||
"/api/v1/courses/#{@course.id}/discussion_topics.json",
|
||||
{ controller: "discussion_topics", action: "index", format: "json", course_id: @course.id.to_s })
|
||||
expect(json.size).to eq 1
|
||||
|
||||
@user = @student1
|
||||
|
||||
json = api_call(:get,
|
||||
"/api/v1/courses/#{@course.id}/discussion_topics.json",
|
||||
{ controller: "discussion_topics", action: "index", format: "json", course_id: @course.id.to_s })
|
||||
expect(json.size).to eq 2
|
||||
end
|
||||
|
||||
it "is visible only to users who can access the assigned section" do
|
||||
@topic.assignment_overrides.create!(set: @course_section)
|
||||
|
||||
@user = @student2
|
||||
json = api_call(:get,
|
||||
"/api/v1/courses/#{@course.id}/discussion_topics.json",
|
||||
{ controller: "discussion_topics", action: "index", format: "json", course_id: @course.id.to_s })
|
||||
expect(json.size).to eq 2
|
||||
|
||||
@user = @student1
|
||||
json = api_call(:get,
|
||||
"/api/v1/courses/#{@course.id}/discussion_topics.json",
|
||||
{ controller: "discussion_topics", action: "index", format: "json", course_id: @course.id.to_s })
|
||||
expect(json.size).to eq 1
|
||||
end
|
||||
|
||||
it "is visible only to students in module override section" do
|
||||
context_module = @course.context_modules.create!(name: "module")
|
||||
context_module.content_tags.create!(content: @topic, context: @course)
|
||||
|
||||
override2 = @topic.assignment_overrides.create!(unlock_at: "2022-02-01T01:00:00Z",
|
||||
unlock_at_overridden: true,
|
||||
lock_at: "2022-02-02T01:00:00Z",
|
||||
lock_at_overridden: true)
|
||||
override2.assignment_override_students.create!(user: @student2)
|
||||
|
||||
@user = @student2
|
||||
json = api_call(:get,
|
||||
"/api/v1/courses/#{@course.id}/discussion_topics.json",
|
||||
{ controller: "discussion_topics", action: "index", format: "json", course_id: @course.id.to_s })
|
||||
expect(json.size).to eq 2
|
||||
|
||||
@user = @student1
|
||||
json = api_call(:get,
|
||||
"/api/v1/courses/#{@course.id}/discussion_topics.json",
|
||||
{ controller: "discussion_topics", action: "index", format: "json", course_id: @course.id.to_s })
|
||||
expect(json.size).to eq 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET 'show'" do
|
||||
|
|
|
@ -368,6 +368,99 @@ describe DiscussionTopicsController do
|
|||
expect(InstStatsd::Statsd).to have_received(:count).with("discussion_topic.index.visit.closed_for_comments", 0).at_least(:once)
|
||||
end
|
||||
end
|
||||
|
||||
describe "differentiated modules" do
|
||||
before do
|
||||
Account.site_admin.enable_feature! :differentiated_modules
|
||||
end
|
||||
|
||||
context "ungraded discussions" do
|
||||
before do
|
||||
course_factory(active_all: true)
|
||||
@course_section = @course.course_sections.create
|
||||
|
||||
@student1, @student2 = create_users(2, return_type: :record)
|
||||
@course.enroll_student(@student1, enrollment_state: "active")
|
||||
@course.enroll_student(@student2, enrollment_state: "active")
|
||||
student_in_section(@course.course_sections.first, user: @student1)
|
||||
student_in_section(@course.course_sections.second, user: @student2)
|
||||
|
||||
@teacher = teacher_in_course(course: @course, active_enrollment: true).user
|
||||
@topic_visible_to_everyone = discussion_topic_model(user: @teacher, context: @course)
|
||||
@topic = discussion_topic_model(user: @teacher, context: @course)
|
||||
@topic.update!(only_visible_to_overrides: true)
|
||||
end
|
||||
|
||||
it "shows only assigned topics" do
|
||||
override = @topic.assignment_overrides.create!
|
||||
override.assignment_override_students.create!(user: @student1)
|
||||
|
||||
user_session(@student2)
|
||||
get "index", params: { course_id: @course.id }, format: :json
|
||||
parsed_json = json_parse(response.body)
|
||||
visible_ids_to_student_2 = parsed_json.pluck("id")
|
||||
|
||||
expect(assigns["topics"]).not_to include(@topic)
|
||||
expect(visible_ids_to_student_2).to include(@topic_visible_to_everyone.id)
|
||||
expect(visible_ids_to_student_2).not_to include(@topic.id)
|
||||
|
||||
user_session(@student1)
|
||||
get "index", params: { course_id: @course.id }, format: :json
|
||||
parsed_json = json_parse(response.body)
|
||||
visible_ids_to_student_1 = parsed_json.pluck("id")
|
||||
|
||||
expect(visible_ids_to_student_1).to include(@topic_visible_to_everyone.id)
|
||||
expect(visible_ids_to_student_1).to include(@topic.id)
|
||||
end
|
||||
|
||||
it "is visible only to users who can access the assigned section" do
|
||||
@topic.assignment_overrides.create!(set: @course_section)
|
||||
|
||||
user_session(@student2)
|
||||
get "index", params: { course_id: @course.id }, format: :json
|
||||
parsed_json = json_parse(response.body)
|
||||
visible_ids_to_student_2 = parsed_json.pluck("id")
|
||||
|
||||
expect(visible_ids_to_student_2).to include(@topic_visible_to_everyone.id)
|
||||
expect(visible_ids_to_student_2).to include(@topic.id)
|
||||
|
||||
user_session(@student1)
|
||||
get "index", params: { course_id: @course.id }, format: :json
|
||||
parsed_json = json_parse(response.body)
|
||||
visible_ids_to_student_1 = parsed_json.pluck("id")
|
||||
|
||||
expect(visible_ids_to_student_1).to include(@topic_visible_to_everyone.id)
|
||||
expect(visible_ids_to_student_1).not_to include(@topic.id)
|
||||
end
|
||||
|
||||
it "is visible only to students in module override section" do
|
||||
context_module = @course.context_modules.create!(name: "module")
|
||||
context_module.content_tags.create!(content: @topic, context: @course)
|
||||
|
||||
override2 = @topic.assignment_overrides.create!(unlock_at: "2022-02-01T01:00:00Z",
|
||||
unlock_at_overridden: true,
|
||||
lock_at: "2022-02-02T01:00:00Z",
|
||||
lock_at_overridden: true)
|
||||
override2.assignment_override_students.create!(user: @student2)
|
||||
|
||||
user_session(@student2)
|
||||
get "index", params: { course_id: @course.id }, format: :json
|
||||
parsed_json = json_parse(response.body)
|
||||
visible_ids_to_student_2 = parsed_json.pluck("id")
|
||||
|
||||
expect(visible_ids_to_student_2).to include(@topic_visible_to_everyone.id)
|
||||
expect(visible_ids_to_student_2).to include(@topic.id)
|
||||
|
||||
user_session(@student1)
|
||||
get "index", params: { course_id: @course.id }, format: :json
|
||||
parsed_json = json_parse(response.body)
|
||||
visible_ids_to_student_1 = parsed_json.pluck("id")
|
||||
|
||||
expect(visible_ids_to_student_1).to include(@topic_visible_to_everyone.id)
|
||||
expect(visible_ids_to_student_1).not_to include(@topic.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET 'show'" do
|
||||
|
|
|
@ -905,4 +905,58 @@ describe Types::DiscussionType do
|
|||
expect(discussion_type.resolve("delayedPostAt")).to eq discussion.delayed_post_at&.iso8601
|
||||
end
|
||||
end
|
||||
|
||||
context "differentiated modules" do
|
||||
before do
|
||||
Account.site_admin.enable_feature! :differentiated_modules
|
||||
end
|
||||
|
||||
context "ungraded discussions" do
|
||||
before do
|
||||
course_factory(active_all: true)
|
||||
@topic = discussion_topic_model(user: @teacher, context: @course)
|
||||
@topic.update!(only_visible_to_overrides: true)
|
||||
@course_section = @course.course_sections.create
|
||||
@student1 = student_in_course(course: @course, active_enrollment: true).user
|
||||
@student2 = student_in_course(course: @course, active_enrollment: true, section: @course_section).user
|
||||
@teacher1 = teacher_in_course(course: @course, active_enrollment: true).user
|
||||
|
||||
@student1_type = GraphQLTypeTester.new(@topic, current_user: @student1)
|
||||
@student2_type = GraphQLTypeTester.new(@topic, current_user: @student2)
|
||||
@teacher1_type = GraphQLTypeTester.new(@topic, current_user: @teacher1)
|
||||
end
|
||||
|
||||
it "is visible only to the assigned student" do
|
||||
override = @topic.assignment_overrides.create!
|
||||
override.assignment_override_students.create!(user: @student1)
|
||||
|
||||
expect(@student1_type.resolve("_id")).to be_truthy
|
||||
expect(@student2_type.resolve("_id")).to be_nil
|
||||
expect(@teacher1_type.resolve("_id")).to be_truthy
|
||||
end
|
||||
|
||||
it "is visible only to users who can access the assigned section" do
|
||||
@topic.assignment_overrides.create!(set: @course_section)
|
||||
|
||||
expect(@student1_type.resolve("_id")).to be_nil
|
||||
expect(@student2_type.resolve("_id")).to be_truthy
|
||||
expect(@teacher1_type.resolve("_id")).to be_truthy
|
||||
end
|
||||
|
||||
it "is visible only to students in module override section" do
|
||||
context_module = @course.context_modules.create!(name: "module")
|
||||
context_module.content_tags.create!(content: @topic, context: @course)
|
||||
|
||||
override2 = @topic.assignment_overrides.create!(unlock_at: "2022-02-01T01:00:00Z",
|
||||
unlock_at_overridden: true,
|
||||
lock_at: "2022-02-02T01:00:00Z",
|
||||
lock_at_overridden: true)
|
||||
override2.assignment_override_students.create!(user: @student1)
|
||||
|
||||
expect(@student1_type.resolve("_id")).to be_truthy
|
||||
expect(@student2_type.resolve("_id")).to be_nil
|
||||
expect(@teacher1_type.resolve("_id")).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -529,6 +529,61 @@ describe DiscussionTopic do
|
|||
expect(@announcement.active_participants_include_tas_and_teachers.include?(user)).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context "differentiated modules" do
|
||||
before do
|
||||
Account.site_admin.enable_feature! :differentiated_modules
|
||||
end
|
||||
|
||||
context "ungraded discussions" do
|
||||
before do
|
||||
@topic = discussion_topic_model(user: @teacher, context: @course)
|
||||
@topic.update!(only_visible_to_overrides: true)
|
||||
@course_section = @course.course_sections.create
|
||||
@student1 = student_in_course(course: @course, active_enrollment: true).user
|
||||
@student2 = student_in_course(course: @course, active_enrollment: true, section: @course_section).user
|
||||
@teacher1 = teacher_in_course(course: @course, active_enrollment: true).user
|
||||
@teacher2_limited_to_section = teacher_in_course(course: @course, active_enrollment: true).user
|
||||
Enrollment.limit_privileges_to_course_section!(@course, @teacher2_limited_to_section, true)
|
||||
end
|
||||
|
||||
it "is visible only to the assigned student" do
|
||||
override = @topic.assignment_overrides.create!
|
||||
override.assignment_override_students.create!(user: @student1)
|
||||
expect(@topic.visible_for?(@student1)).to be_truthy
|
||||
expect(@topic.visible_for?(@student2)).to be_falsey
|
||||
|
||||
expect(@topic.visible_for?(@teacher1)).to be_truthy
|
||||
expect(@topic.visible_for?(@teacher2_limited_to_section)).to be_truthy
|
||||
end
|
||||
|
||||
it "is visible only to users who can access the assigned section" do
|
||||
@topic.assignment_overrides.create!(set: @course_section)
|
||||
expect(@topic.visible_for?(@student1)).to be_falsey
|
||||
expect(@topic.visible_for?(@student2)).to be_truthy
|
||||
|
||||
expect(@topic.visible_for?(@teacher1)).to be_truthy
|
||||
expect(@topic.visible_for?(@teacher2_limited_to_section)).to be_falsey
|
||||
end
|
||||
|
||||
it "is visible only to students in module override section" do
|
||||
context_module = @course.context_modules.create!(name: "module")
|
||||
context_module.content_tags.create!(content: @topic, context: @course)
|
||||
|
||||
override2 = @topic.assignment_overrides.create!(unlock_at: "2022-02-01T01:00:00Z",
|
||||
unlock_at_overridden: true,
|
||||
lock_at: "2022-02-02T01:00:00Z",
|
||||
lock_at_overridden: true)
|
||||
override2.assignment_override_students.create!(user: @student1)
|
||||
|
||||
expect(@topic.visible_for?(@student1)).to be_truthy
|
||||
expect(@topic.visible_for?(@student2)).to be_falsey
|
||||
|
||||
expect(@topic.visible_for?(@teacher1)).to be_truthy
|
||||
expect(@topic.visible_for?(@teacher2_limited_to_section)).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "differentiated assignements" do
|
||||
|
@ -1554,6 +1609,53 @@ describe DiscussionTopic do
|
|||
it "does not return discussions that have an assignment and no visibility" do
|
||||
expect(DiscussionTopic.visible_to_students_in_course_with_da([@student.id], [@course.id])).not_to include(@topic)
|
||||
end
|
||||
|
||||
context "differentiated modules" do
|
||||
before do
|
||||
Account.site_admin.enable_feature! :differentiated_modules
|
||||
end
|
||||
|
||||
context "ungraded discussions" do
|
||||
before do
|
||||
@topic = discussion_topic_model(user: @teacher, context: @course)
|
||||
@topic.update!(only_visible_to_overrides: true)
|
||||
@course_section = @course.course_sections.create
|
||||
@student1 = student_in_course(course: @course, active_enrollment: true).user
|
||||
@student2 = student_in_course(course: @course, active_enrollment: true, section: @course_section).user
|
||||
@teacher1 = teacher_in_course(course: @course, active_enrollment: true).user
|
||||
@teacher2_limited_to_section = teacher_in_course(course: @course, active_enrollment: true).user
|
||||
Enrollment.limit_privileges_to_course_section!(@course, @teacher2_limited_to_section, true)
|
||||
end
|
||||
|
||||
it "is visible only to the assigned student" do
|
||||
override = @topic.assignment_overrides.create!
|
||||
override.assignment_override_students.create!(user: @student1)
|
||||
|
||||
expect(DiscussionTopic.visible_to_students_in_course_with_da([@student1.id], [@course.id])).to include(@topic)
|
||||
expect(DiscussionTopic.visible_to_students_in_course_with_da([@student2.id], [@course.id])).not_to include(@topic)
|
||||
end
|
||||
|
||||
it "is visible only to users who can access the assigned section" do
|
||||
@topic.assignment_overrides.create!(set: @course_section)
|
||||
expect(DiscussionTopic.visible_to_students_in_course_with_da([@student1.id], [@course.id])).not_to include(@topic)
|
||||
expect(DiscussionTopic.visible_to_students_in_course_with_da([@student2.id], [@course.id])).to include(@topic)
|
||||
end
|
||||
|
||||
it "is visible only to students in module override section" do
|
||||
context_module = @course.context_modules.create!(name: "module")
|
||||
context_module.content_tags.create!(content: @topic, context: @course)
|
||||
|
||||
override2 = @topic.assignment_overrides.create!(unlock_at: "2022-02-01T01:00:00Z",
|
||||
unlock_at_overridden: true,
|
||||
lock_at: "2022-02-02T01:00:00Z",
|
||||
lock_at_overridden: true)
|
||||
override2.assignment_override_students.create!(user: @student1)
|
||||
|
||||
expect(DiscussionTopic.visible_to_students_in_course_with_da([@student1.id], [@course.id])).to include(@topic)
|
||||
expect(DiscussionTopic.visible_to_students_in_course_with_da([@student2.id], [@course.id])).not_to include(@topic)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "posters" do
|
||||
|
|
Loading…
Reference in New Issue