release mastery paths content after disabling feature on course
also don't restrict content if imported into a course with mastery paths disabled test plan: * create a course with mastery paths enabled and content set to be released on completion of a trigger assignment (including other assignments, ungraded quizzes, and wiki pages) * copy the course into another one that does not have mastery paths enabled * all content should be visible by students in the copied course * in the original course, disable the mastery paths feature * it should give a warning (that actually works to not turn the feature flag off if you cancel the dialog) that says that the content will have to reconfigured if re-enabled * all content previously hidden from students should now be available closes #LS-1459 Change-Id: I47c8dc773ecdc60b5921853c266959f3e8335f0b Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/248869 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com> QA-Review: Jeremy Stanley <jeremy@instructure.com> Product-Review: Jeremy Stanley <jeremy@instructure.com>
This commit is contained in:
parent
8bef98717c
commit
7a80ec7e92
|
@ -71,7 +71,7 @@ export default class WikiPage extends Backbone.Model
|
|||
parse: (response, options) ->
|
||||
if response.wiki_page
|
||||
response = _.extend _.omit(response, 'wiki_page'), response.wiki_page
|
||||
response.set_assignment = response.assignment?
|
||||
response.set_assignment = response.assignment? && response.assignment.only_visible_to_overrides
|
||||
assign_attributes = response.assignment || {}
|
||||
response.assignment = @createAssignment(assign_attributes)
|
||||
response
|
||||
|
|
|
@ -65,10 +65,12 @@ export default class FeatureFlagView extends Backbone.View {
|
|||
|
||||
applyAction(action) {
|
||||
$.when(this.canUpdate(action)).then(
|
||||
$.when(this.checkSiteAdmin()).then(
|
||||
() => this.model.updateState(action),
|
||||
() => this.render() // undo UI change if user cancels
|
||||
)
|
||||
() =>
|
||||
$.when(this.checkSiteAdmin()).then(
|
||||
() => this.model.updateState(action),
|
||||
() => this.render() // undo UI change if user cancels
|
||||
),
|
||||
() => this.render() // ditto
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -88,6 +88,28 @@ module ConditionalRelease
|
|||
rules_data
|
||||
end
|
||||
|
||||
def self.release_mastery_paths_content_in_course(course)
|
||||
overrides_scope = AssignmentOverride.where(:set_type => AssignmentOverride::SET_TYPE_NOOP, :set_id => AssignmentOverride::NOOP_MASTERY_PATHS).active
|
||||
assignment_ids = overrides_scope.where.not(:assignment_id => nil).pluck(:assignment_id)
|
||||
assignment_ids.sort.each_slice(100) do |sliced_ids|
|
||||
course.assignments.active.where(:id => sliced_ids).where(:only_visible_to_overrides => true).where.not(submission_types: 'wiki_page').to_a.each do |assignment|
|
||||
assignment.update_attribute(:only_visible_to_overrides, false)
|
||||
end
|
||||
end
|
||||
wp_assignment_ids = course.wiki_pages.not_deleted.where.not(:assignment_id => nil).pluck(:assignment_id)
|
||||
wp_assignment_ids.sort.each_slice(100) do |sliced_ids|
|
||||
course.assignments.active.where(:id => sliced_ids).where(:only_visible_to_overrides => true, :submission_types => 'wiki_page').each do |wp_assignment|
|
||||
wp_assignment.update_attribute(:only_visible_to_overrides, false)
|
||||
end
|
||||
end
|
||||
quiz_ids = overrides_scope.where(:assignment_id => nil).where.not(:quiz_id => nil).pluck(:quiz_id)
|
||||
quiz_ids.sort.each_slice(100) do |sliced_ids|
|
||||
course.quizzes.active.where(:id => sliced_ids).where(:only_visible_to_overrides => true).to_a.each do |quiz|
|
||||
quiz.update_attribute(:only_visible_to_overrides, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
private
|
||||
|
||||
|
|
|
@ -158,11 +158,13 @@ module Importers
|
|||
elsif ['external_tool'].include?(hash[:submission_format])
|
||||
item.submission_types = "external_tool"
|
||||
end
|
||||
if item.submission_types == "online_quiz"
|
||||
case item.submission_types
|
||||
when "online_quiz"
|
||||
item.saved_by = :quiz
|
||||
end
|
||||
if item.submission_types == "discussion_topic"
|
||||
when "discussion_topic"
|
||||
item.saved_by = :discussion_topic
|
||||
when "wiki_page"
|
||||
item.saved_by = :wiki_page
|
||||
end
|
||||
|
||||
if hash[:grading_type]
|
||||
|
@ -230,7 +232,11 @@ module Importers
|
|||
end
|
||||
|
||||
if hash[:assignment_overrides]
|
||||
added_overrides = false
|
||||
hash[:assignment_overrides].each do |o|
|
||||
next if o[:set_id].to_i == AssignmentOverride::NOOP_MASTERY_PATHS &&
|
||||
o[:set_type] == AssignmentOverride::SET_TYPE_NOOP &&
|
||||
!context.feature_enabled?(:conditional_release)
|
||||
override = item.assignment_overrides.where(o.slice(:set_type, :set_id)).first
|
||||
override ||= item.assignment_overrides.build
|
||||
override.set_type = o[:set_type]
|
||||
|
@ -241,10 +247,12 @@ module Importers
|
|||
override.send "override_#{field}", Canvas::Migration::MigratorHelper.get_utc_time_from_timestamp(o[field])
|
||||
end
|
||||
override.save!
|
||||
added_overrides = true
|
||||
migration.add_imported_item(override,
|
||||
key: [item.migration_id, override.set_type, override.set_id].join('/'))
|
||||
end
|
||||
if hash.has_key?(:only_visible_to_overrides)
|
||||
can_restrict = added_overrides || (item.submission_types == "wiki_page" && context.feature_enabled?(:conditional_release))
|
||||
if hash.has_key?(:only_visible_to_overrides) && can_restrict
|
||||
item.only_visible_to_overrides = hash[:only_visible_to_overrides]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -245,7 +245,11 @@ module Importers
|
|||
end
|
||||
|
||||
if hash[:assignment_overrides]
|
||||
added_overrides = false
|
||||
hash[:assignment_overrides].each do |o|
|
||||
next if o[:set_id].to_i == AssignmentOverride::NOOP_MASTERY_PATHS &&
|
||||
o[:set_type] == AssignmentOverride::SET_TYPE_NOOP &&
|
||||
!context.feature_enabled?(:conditional_release)
|
||||
override = item.assignment_overrides.where(o.slice(:set_type, :set_id)).first
|
||||
override ||= item.assignment_overrides.build
|
||||
override.set_type = o[:set_type]
|
||||
|
@ -256,10 +260,11 @@ module Importers
|
|||
override.send "override_#{field}", Canvas::Migration::MigratorHelper.get_utc_time_from_timestamp(o[field])
|
||||
end
|
||||
override.save!
|
||||
added_overrides = true
|
||||
migration.add_imported_item(override,
|
||||
key: [item.migration_id, override.set_type, override.set_id].join('/'))
|
||||
end
|
||||
if hash.has_key?(:only_visible_to_overrides)
|
||||
if hash.has_key?(:only_visible_to_overrides) && added_overrides
|
||||
item.only_visible_to_overrides = hash[:only_visible_to_overrides]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -240,7 +240,7 @@ module Importers
|
|||
allow_save = false
|
||||
end
|
||||
if allow_save && hash[:migration_id]
|
||||
if hash[:assignment].present?
|
||||
if hash[:assignment].present? && context.feature_enabled?(:conditional_release)
|
||||
hash[:assignment][:title] ||= item.title
|
||||
item.assignment = Importers::AssignmentImporter.import_from_migration(
|
||||
hash[:assignment], context, migration)
|
||||
|
|
|
@ -252,6 +252,8 @@ conditional_release:
|
|||
results.
|
||||
applies_to: Course
|
||||
root_opt_in: true
|
||||
custom_transition_proc: conditional_release_transition_hook
|
||||
after_state_change_proc: conditional_release_after_change_hook
|
||||
wrap_calendar_event_titles:
|
||||
state: allowed
|
||||
display_name: Wrap event titles in Calendar month view
|
||||
|
|
|
@ -52,6 +52,7 @@ module CC::Importer::Canvas
|
|||
wiki[:assignment] = {
|
||||
migration_id: asg_id,
|
||||
assignment_overrides: [],
|
||||
submission_types: 'wiki_page',
|
||||
only_visible_to_overrides: meta['only_visible_to_overrides'] == 'true'
|
||||
}
|
||||
end
|
||||
|
|
|
@ -80,5 +80,24 @@ module FeatureFlags
|
|||
I18n.t("Disabling the Elementary Theme will change the font in the Canvas interface for all users in your course.")
|
||||
transitions['off']['reload_page'] = true
|
||||
end
|
||||
|
||||
def self.conditional_release_transition_hook(_user, context, _from_state, transitions)
|
||||
if context.is_a?(Course)
|
||||
transitions['off'] ||= {}
|
||||
transitions['off']['message'] =
|
||||
I18n.t("Disabling the Mastery Paths feature will release configured assignments and content to all students.
|
||||
If the feature is re-enabled, these assignments will need to be configured for Mastery Paths again.")
|
||||
end
|
||||
end
|
||||
|
||||
def self.conditional_release_after_change_hook(_user, context, _old_state, new_state)
|
||||
if context.is_a?(Course) && new_state == "off"
|
||||
ConditionalRelease::Service.send_later_if_production_enqueue_args(
|
||||
:release_mastery_paths_content_in_course,
|
||||
{:priority => Delayed::LOW_PRIORITY, :n_strand => ["conditional_release_unassignment", context.global_root_account_id]},
|
||||
context
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -216,5 +216,51 @@ describe ConditionalRelease::Service do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "releasing content after disabling feature flag" do
|
||||
before :once do
|
||||
Account.default.allow_feature!(:conditional_release)
|
||||
course_with_student(:active_all => true)
|
||||
@course.enable_feature!(:conditional_release)
|
||||
@module = @course.context_modules.create!(:workflow_state => "active")
|
||||
end
|
||||
|
||||
def release_content
|
||||
Feature.definitions['conditional_release'].after_state_change_proc.call(@teacher, @course, 'on', 'off')
|
||||
end
|
||||
|
||||
it "should release mastery paths assigned assignments" do
|
||||
assmt = assignment_model(course: @course, workflow_state: 'published', only_visible_to_overrides: true)
|
||||
assignment_override_model(assignment: assmt,
|
||||
set_type: AssignmentOverride::SET_TYPE_NOOP,
|
||||
set_id: AssignmentOverride::NOOP_MASTERY_PATHS)
|
||||
tag = @module.add_item(:id => assmt.id, :type => "assignment")
|
||||
expect(@course.module_items_visible_to(@student).to_a).to eq []
|
||||
|
||||
release_content
|
||||
expect(@course.module_items_visible_to(@student).to_a).to eq [tag]
|
||||
end
|
||||
|
||||
it "should release mastery paths assigned ungraded quizzes" do
|
||||
quiz = quiz_model(course: @course, quiz_type: "survey", only_visible_to_overrides: true)
|
||||
assignment_override_model(quiz: quiz,
|
||||
set_type: AssignmentOverride::SET_TYPE_NOOP,
|
||||
set_id: AssignmentOverride::NOOP_MASTERY_PATHS)
|
||||
tag = @module.add_item(:id => quiz.id, :type => "quiz")
|
||||
expect(@course.module_items_visible_to(@student).to_a).to eq []
|
||||
|
||||
release_content
|
||||
expect(@course.module_items_visible_to(@student).to_a).to eq [tag]
|
||||
end
|
||||
|
||||
it "should release mastery paths assigned wiki pages" do
|
||||
wiki_page_assignment_model(course: @course, only_visible_to_overrides: true)
|
||||
tag = @module.add_item(:id => @page.id, :type => "wiki_page")
|
||||
expect(@course.module_items_visible_to(@student).to_a).to eq []
|
||||
|
||||
release_content
|
||||
expect(@course.module_items_visible_to(@student).to_a).to eq [tag]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -685,10 +685,11 @@ describe ContentMigration do
|
|||
end
|
||||
|
||||
it "should copy only noop overrides" do
|
||||
Account.default.enable_feature!(:conditional_release)
|
||||
assignment_override_model(assignment: @assignment, set_type: 'ADHOC')
|
||||
assignment_override_model(assignment: @assignment, set_type: 'Noop',
|
||||
set_id: 1, title: 'Tag 1')
|
||||
assignment_override_model(assignment: @assignment, set_type: 'Noop',
|
||||
assignment_override_model(assignment: @assignment, set_type: AssignmentOverride::SET_TYPE_NOOP,
|
||||
set_id: AssignmentOverride::NOOP_MASTERY_PATHS, title: 'Tag 1')
|
||||
assignment_override_model(assignment: @assignment, set_type: AssignmentOverride::SET_TYPE_NOOP,
|
||||
set_id: nil, title: 'Tag 2')
|
||||
@assignment.only_visible_to_overrides = true
|
||||
@assignment.save!
|
||||
|
@ -700,7 +701,21 @@ describe ContentMigration do
|
|||
expect(to_assignment.assignment_overrides.detect{ |o| o.set_id.nil? }.title).to eq 'Tag 2'
|
||||
end
|
||||
|
||||
it "should ignore conditional release noop overrides if feature is not enabled in destination" do
|
||||
assignment_override_model(assignment: @assignment,
|
||||
set_type: AssignmentOverride::SET_TYPE_NOOP,
|
||||
set_id: AssignmentOverride::NOOP_MASTERY_PATHS)
|
||||
@assignment.only_visible_to_overrides = true
|
||||
@assignment.save!
|
||||
|
||||
run_course_copy
|
||||
to_assignment = @copy_to.assignments.first
|
||||
expect(to_assignment.only_visible_to_overrides).to be_falsey
|
||||
expect(to_assignment.assignment_overrides.length).to eq 0
|
||||
end
|
||||
|
||||
it "should copy dates" do
|
||||
Account.default.enable_feature!(:conditional_release)
|
||||
due_at = 1.hour.from_now.round
|
||||
assignment_override_model(assignment: @assignment, set_type: 'Noop',
|
||||
set_id: 1, title: 'Tag 1', due_at: due_at)
|
||||
|
@ -713,6 +728,7 @@ describe ContentMigration do
|
|||
end
|
||||
|
||||
it "preserves only_visible_to_overrides for page assignments" do
|
||||
Account.default.enable_feature!(:conditional_release)
|
||||
a1 = assignment_model(context: @copy_from, title: 'a1', submission_types: 'wiki_page', only_visible_to_overrides: true)
|
||||
a1.build_wiki_page(title: a1.title, context: a1.context).save!
|
||||
a2 = assignment_model(context: @copy_from, title: 'a2', submission_types: 'wiki_page', only_visible_to_overrides: false)
|
||||
|
@ -723,6 +739,15 @@ describe ContentMigration do
|
|||
a2_to = @copy_to.assignments.where(migration_id: mig_id(a2)).take
|
||||
expect(a2_to.only_visible_to_overrides).to eq false
|
||||
end
|
||||
|
||||
it "ignores page assignments if mastery paths is not enabled in destination" do
|
||||
a1 = assignment_model(context: @copy_from, title: 'a1', submission_types: 'wiki_page', only_visible_to_overrides: true)
|
||||
a1.build_wiki_page(title: a1.title, context: a1.context).save!
|
||||
run_course_copy
|
||||
page_to = @copy_to.wiki_pages.where(migration_id: mig_id(a1.wiki_page)).take
|
||||
expect(page_to.assignment).to eq nil
|
||||
expect(@copy_to.assignments.where(migration_id: mig_id(a1)).exists?).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
it "should copy the thing" do
|
||||
|
|
|
@ -1129,6 +1129,7 @@ equation: <img class="equation_image" title="Log_216" src="/equation_images/Log_
|
|||
end
|
||||
|
||||
it "should copy only noop overrides" do
|
||||
Account.default.enable_feature!(:conditional_release)
|
||||
due_at = 1.hour.from_now.round
|
||||
assignment_override_model(quiz: @quiz_plain, set_type: 'Noop', set_id: 1, title: 'Tag 3')
|
||||
assignment_override_model(quiz: @quiz_assigned, set_type: 'Noop', set_id: 1, title: 'Tag 4', due_at: due_at)
|
||||
|
@ -1139,6 +1140,22 @@ equation: <img class="equation_image" title="Log_216" src="/equation_images/Log_
|
|||
expect(to_quiz_assigned.assignment_overrides.pluck(:title)).to eq ['Tag 4']
|
||||
expect(to_quiz_assigned.assignment_overrides.first.due_at).to eq due_at
|
||||
end
|
||||
|
||||
it "should ignore conditional release noop overrides if feature is not enabled in destination" do
|
||||
assignment_override_model(quiz: @quiz_assigned, set_type: 'Noop', set_id: 1, title: 'ignore me')
|
||||
@quiz_assigned.update_attribute(:only_visible_to_overrides, true)
|
||||
|
||||
assignment_override_model(quiz: @quiz_plain, set_type: 'Noop', set_id: 9001, title: 'should keep this')
|
||||
@quiz_plain.update_attribute(:only_visible_to_overrides, true)
|
||||
|
||||
run_course_copy
|
||||
to_quiz_plain = @copy_to.quizzes.where(migration_id: mig_id(@quiz_plain)).first
|
||||
to_quiz_assigned = @copy_to.quizzes.where(migration_id: mig_id(@quiz_assigned)).first
|
||||
expect(to_quiz_assigned.assignment_overrides.count).to eq 0
|
||||
expect(to_quiz_assigned.only_visible_to_overrides).to eq false
|
||||
expect(to_quiz_plain.assignment_overrides.count).to eq 1
|
||||
expect(to_quiz_plain.only_visible_to_overrides).to eq true
|
||||
end
|
||||
end
|
||||
|
||||
it "should not destroy assessment questions when copying twice" do
|
||||
|
|
|
@ -81,6 +81,7 @@ describe ContentMigration do
|
|||
end
|
||||
|
||||
it "should keep assignment relationship" do
|
||||
@copy_to.enable_feature!(:conditional_release)
|
||||
vanilla_page_from = @copy_from.wiki_pages.create!(title: "Everyone Sees This Page")
|
||||
title = "conditional page"
|
||||
wiki_page_assignment_model(course: @copy_from, title: title)
|
||||
|
|
Loading…
Reference in New Issue