allow cross-shard course copy
test plan: * before checking this commit out, copy some content into a course * after checking it out, make sure re-copying the course overwrites the content from the earlier copy (tl;dr migration identifiers are being changed but not for old exported/copied courses) * make sure course copies/imports/exports still work closes #CORE-2718 #CORE-2720 Change-Id: I7515c7ec2943afaaf502432f1510d3e580f13ced Reviewed-on: https://gerrit.instructure.com/188371 Tested-by: Jenkins QA-Review: Rob Orton <rob@instructure.com> Product-Review: Rob Orton <rob@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com> Reviewed-by: Rob Orton <rob@instructure.com>
This commit is contained in:
parent
7ded5716d8
commit
97fbbcaad3
|
@ -153,7 +153,13 @@ class ContentExportsApiController < ApplicationController
|
|||
export.settings[:skip_notifications] = true if value_to_boolean(params[:skip_notifications])
|
||||
|
||||
# ZipExporter accepts unhashed asset strings, to avoid having to instantiate all the files and folders
|
||||
selected_content = ContentMigration.process_copy_params(params[:select]&.to_unsafe_h, true, params[:export_type] == ContentExport::ZIP) if params[:select]
|
||||
if params[:select]
|
||||
selected_content = ContentMigration.process_copy_params(params[:select]&.to_unsafe_h,
|
||||
for_content_export: true,
|
||||
return_asset_strings: params[:export_type] == ContentExport::ZIP,
|
||||
global_identifiers: export.can_use_global_identifiers?)
|
||||
end
|
||||
|
||||
case params[:export_type]
|
||||
when 'qti'
|
||||
export.export_type = ContentExport::QTI
|
||||
|
|
|
@ -500,7 +500,8 @@ class ContentMigrationsController < ApplicationController
|
|||
params[:do_not_run] = true
|
||||
end
|
||||
elsif params[:copy]
|
||||
copy_options = ContentMigration.process_copy_params(params[:copy]&.to_unsafe_h)
|
||||
copy_options = ContentMigration.process_copy_params(params[:copy]&.to_unsafe_h,
|
||||
global_identifiers: @content_migration.content_export&.global_identifiers?)
|
||||
@content_migration.migration_settings[:migration_ids_to_import] ||= {}
|
||||
@content_migration.migration_settings[:migration_ids_to_import][:copy] = copy_options
|
||||
@content_migration.copy_options = copy_options
|
||||
|
|
|
@ -34,7 +34,7 @@ module ContentImportsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def mig_id(obj)
|
||||
CC::CCHelper.create_key(obj)
|
||||
def mig_id(obj, global: false)
|
||||
CC::CCHelper.create_key(obj, global: global)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -307,13 +307,21 @@ class Attachment < ActiveRecord::Base
|
|||
excluded_atts = EXCLUDED_COPY_ATTRIBUTES
|
||||
excluded_atts += ["locked", "hidden"] if dup == existing && !options[:migration]&.for_master_course_import?
|
||||
dup.assign_attributes(self.attributes.except(*excluded_atts))
|
||||
|
||||
dup.context = context
|
||||
# avoid cycles (a -> b -> a) and self-references (a -> a) in root_attachment_id pointers
|
||||
if dup.new_record? || ![self.id, self.root_attachment_id].include?(dup.id)
|
||||
dup.root_attachment_id = self.root_attachment_id || self.id
|
||||
if self.shard == dup.shard
|
||||
dup.root_attachment_id = self.root_attachment_id || self.id
|
||||
else
|
||||
if existing_attachment = dup.find_existing_attachment_for_md5
|
||||
dup.root_attachment = existing_attachment
|
||||
else
|
||||
dup.write_attribute(:filename, self.filename)
|
||||
Attachments::Storage.store_for_attachment(dup, self.open)
|
||||
end
|
||||
end
|
||||
end
|
||||
dup.write_attribute(:filename, self.filename) unless dup.root_attachment_id?
|
||||
dup.context = context
|
||||
dup.write_attribute(:filename, self.filename) unless dup.read_attribute(:filename) || dup.root_attachment_id?
|
||||
dup.migration_id = options[:migration_id] || CC::CCHelper.create_key(self)
|
||||
dup.mark_as_importing!(options[:migration]) if options[:migration]
|
||||
if context.respond_to?(:log_merge_result)
|
||||
|
|
|
@ -33,6 +33,8 @@ class ContentExport < ActiveRecord::Base
|
|||
|
||||
has_one :job_progress, :class_name => 'Progress', :as => :context, :inverse_of => :context
|
||||
|
||||
before_create :set_global_identifiers
|
||||
|
||||
# export types
|
||||
COMMON_CARTRIDGE = 'common_cartridge'.freeze
|
||||
COURSE_COPY = 'course_copy'.freeze
|
||||
|
@ -41,6 +43,7 @@ class ContentExport < ActiveRecord::Base
|
|||
USER_DATA = 'user_data'.freeze
|
||||
ZIP = 'zip'.freeze
|
||||
QUIZZES2 = 'quizzes2'.freeze
|
||||
CC_EXPORT_TYPES = [COMMON_CARTRIDGE, COURSE_COPY, MASTER_COURSE_COPY, QTI, QUIZZES2].freeze
|
||||
|
||||
workflow do
|
||||
state :created
|
||||
|
@ -91,18 +94,31 @@ class ContentExport < ActiveRecord::Base
|
|||
can :create
|
||||
end
|
||||
|
||||
def set_global_identifiers
|
||||
self.global_identifiers = can_use_global_identifiers? if CC_EXPORT_TYPES.include?(self.export_type)
|
||||
end
|
||||
|
||||
def can_use_global_identifiers?
|
||||
# use global identifiers if no other cc export from this course has used local identifiers
|
||||
# i.e. all exports from now on should try to use global identifiers
|
||||
# unless there's a risk of not matching up with a previous export
|
||||
!self.context.content_exports.where(:export_type => CC_EXPORT_TYPES, :global_identifiers => false).exists?
|
||||
end
|
||||
|
||||
def export(opts={})
|
||||
opts = opts.with_indifferent_access
|
||||
case export_type
|
||||
when ZIP
|
||||
export_zip(opts)
|
||||
when USER_DATA
|
||||
export_user_data(opts)
|
||||
when QUIZZES2
|
||||
return unless context.feature_enabled?(:quizzes_next)
|
||||
export_quizzes2
|
||||
else
|
||||
export_course(opts)
|
||||
self.shard.activate do
|
||||
opts = opts.with_indifferent_access
|
||||
case export_type
|
||||
when ZIP
|
||||
export_zip(opts)
|
||||
when USER_DATA
|
||||
export_user_data(opts)
|
||||
when QUIZZES2
|
||||
return unless context.feature_enabled?(:quizzes_next)
|
||||
export_quizzes2
|
||||
else
|
||||
export_course(opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
handle_asynchronously :export, :priority => Delayed::LOW_PRIORITY, :max_attempts => 1
|
||||
|
@ -293,15 +309,17 @@ class ContentExport < ActiveRecord::Base
|
|||
if zip_export?
|
||||
obj.asset_string
|
||||
else
|
||||
CC::CCHelper.create_key(obj)
|
||||
create_key(obj)
|
||||
end
|
||||
end
|
||||
|
||||
def create_key(obj, prepend="")
|
||||
if for_master_migration? && !is_external_object?(obj)
|
||||
master_migration.master_template.migration_id_for(obj, prepend) # because i'm too scared to use normal migration ids
|
||||
else
|
||||
CC::CCHelper.create_key(obj, prepend)
|
||||
self.shard.activate do
|
||||
if for_master_migration? && !is_external_object?(obj)
|
||||
master_migration.master_template.migration_id_for(obj, prepend) # because i'm too scared to use normal migration ids
|
||||
else
|
||||
CC::CCHelper.create_key(obj, prepend, global: self.global_identifiers?)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -111,6 +111,13 @@ class ContentMigration < ActiveRecord::Base
|
|||
!!migration_settings[:import_immediately]
|
||||
end
|
||||
|
||||
def content_export
|
||||
if !association(:content_export).loaded? && source_course_id && Shard.shard_for(source_course_id) != self.shard
|
||||
association(:content_export).target = Shard.shard_for(source_course_id).activate { ContentExport.where(:content_migration_id => self).first }
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
def converter_class=(c_class)
|
||||
migration_settings[:converter_class] = c_class
|
||||
end
|
||||
|
@ -845,12 +852,12 @@ class ContentMigration < ActiveRecord::Base
|
|||
|
||||
# strips out the "id_" prepending the migration ids in the form
|
||||
# also converts arrays of migration ids (or real ids for course exports) into the old hash format
|
||||
def self.process_copy_params(hash, for_content_export=false, return_asset_strings=false)
|
||||
def self.process_copy_params(hash, for_content_export: false, return_asset_strings: false, global_identifiers: false)
|
||||
return {} if hash.blank?
|
||||
process_key = if return_asset_strings
|
||||
->(asset_string) { asset_string }
|
||||
else
|
||||
->(asset_string) { CC::CCHelper.create_key(asset_string) }
|
||||
->(asset_string) { CC::CCHelper.create_key(asset_string, global: global_identifiers) }
|
||||
end
|
||||
new_hash = {}
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ module Importers
|
|||
# be very explicit about draft state courses, but be liberal toward legacy courses
|
||||
if course.wiki.has_no_front_page
|
||||
if migration.for_course_copy? && (source = migration.source_course || Course.where(id: migration.migration_settings[:source_course_id]).first)
|
||||
mig_id = CC::CCHelper.create_key(source.wiki.front_page)
|
||||
mig_id = migration.content_export.create_key(source.wiki.front_page)
|
||||
if new_front_page = course.wiki_pages.where(migration_id: mig_id).first
|
||||
course.wiki.set_front_page_url!(new_front_page.url)
|
||||
end
|
||||
|
@ -335,7 +335,8 @@ module Importers
|
|||
course.context_external_tools.having_setting('course_navigation') :
|
||||
ContextExternalTool.find_all_for(course, :course_navigation)
|
||||
if tool = (all_tools.detect{|t| t.migration_id == tool_mig_id} ||
|
||||
all_tools.detect{|t| CC::CCHelper.create_key(t) == tool_mig_id})
|
||||
all_tools.detect{|t| CC::CCHelper.create_key(t) == tool_mig_id ||
|
||||
CC::CCHelper.create_key(t, global: true) == tool_mig_id})
|
||||
# translate the migration_id to a real id
|
||||
tab['id'] = "context_external_tool_#{tool.id}"
|
||||
tab_config << tab
|
||||
|
|
|
@ -21,12 +21,13 @@
|
|||
<div style="overflow: auto; max-height: 200px">
|
||||
<%= check_box :copy, :all_quizzes, :class => "copy_all", :checked => true %><%= label :copy, :all_quizzes, t('labels.all_quizzes', "All Quizzes") %>
|
||||
<ul class="unstyled_list" style="margin-<%= direction('left') %>: 20px;">
|
||||
<%= global_ids = @context.content_exports.temp_record.can_use_global_identifiers? %>
|
||||
<% @context.quizzes.active.each do |quiz| %>
|
||||
<li>
|
||||
<%= check_box "copy[quizzes]", mig_id(quiz), :class => 'quiz_item', :checked => true %>
|
||||
<%= label "copy[quizzes]", mig_id(quiz), quiz.title %>
|
||||
<%= check_box "copy[quizzes]", mig_id(quiz, global: global_ids), :class => 'quiz_item', :checked => true %>
|
||||
<%= label "copy[quizzes]", mig_id(quiz, global: global_ids), quiz.title %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
</fieldset>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
class AddForeignKeys5 < ActiveRecord::Migration[4.2]
|
||||
disable_ddl_transaction!
|
||||
tag :postdeploy
|
||||
tag :predeploy
|
||||
|
||||
def self.up
|
||||
add_foreign_key_if_not_exists :favorites, :users, :delay_validation => true
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
class AddForeignKeys12 < ActiveRecord::Migration[4.2]
|
||||
tag :postdeploy
|
||||
tag :predeploy
|
||||
disable_ddl_transaction!
|
||||
|
||||
def self.up
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
class RemoveCourseCopyForeignKeys < ActiveRecord::Migration[5.1]
|
||||
tag :predeploy
|
||||
|
||||
def up
|
||||
remove_foreign_key :content_migrations, :column => :source_course_id
|
||||
remove_foreign_key_if_exists :content_migrations, :attachments
|
||||
remove_foreign_key :content_exports, :content_migrations
|
||||
remove_foreign_key :folders, :column => :cloned_item_id
|
||||
end
|
||||
|
||||
def down
|
||||
add_foreign_key_if_not_exists :content_migrations, :courses, :column => :source_course_id, :delay_validation => true
|
||||
add_foreign_key_if_not_exists :content_migrations, :attachments, :delay_validation => true
|
||||
add_foreign_key_if_not_exists :content_exports, :content_migrations, :delay_validation => true
|
||||
add_foreign_key_if_not_exists :folders, :cloned_items, :delay_validation => true
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
class AddGlobalIdentifiersToContentExports < ActiveRecord::Migration[5.1]
|
||||
tag :predeploy
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_column :content_exports, :global_identifiers, :boolean
|
||||
change_column_default(:content_exports, :global_identifiers, false)
|
||||
DataFixup::BackfillNulls.run(ContentExport, :global_identifiers, default_value: false)
|
||||
change_column_null(:content_exports, :global_identifiers, false)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :content_exports, :global_identifiers
|
||||
end
|
||||
end
|
|
@ -93,7 +93,7 @@ module Canvas::Migration::ExternalContent
|
|||
obj = obj_class.where(obj_class.primary_key => canvas_id).first
|
||||
obj ? content_export.create_key(obj) : NOT_FOUND
|
||||
else
|
||||
CC::CCHelper.create_key("#{obj_class.reflection_type_name}_#{canvas_id}")
|
||||
(content_export || CC::CCHelper).create_key("#{obj_class.reflection_type_name}_#{canvas_id}")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -316,7 +316,7 @@ module Canvas::Migration::Helpers
|
|||
|
||||
hash = {type: type, title: title}
|
||||
if @migration
|
||||
mig_id = CC::CCHelper.create_key(item)
|
||||
mig_id = (@migration.content_export || CC::CCHelper).create_key(item)
|
||||
hash[:migration_id] = mig_id
|
||||
hash[:property] = "#{property_prefix}[#{type}][id_#{mig_id}]"
|
||||
else
|
||||
|
|
|
@ -30,6 +30,7 @@ class Canvas::Migration::Worker::CourseCopyWorker < Canvas::Migration::Worker::B
|
|||
begin
|
||||
source = cm.source_course || Course.find(cm.migration_settings[:source_course_id])
|
||||
ce = ContentExport.new
|
||||
ce.shard = source.shard
|
||||
ce.context = source
|
||||
ce.content_migration = cm
|
||||
ce.selected_content = cm.copy_options
|
||||
|
@ -38,7 +39,9 @@ class Canvas::Migration::Worker::CourseCopyWorker < Canvas::Migration::Worker::B
|
|||
ce.save!
|
||||
cm.content_export = ce
|
||||
|
||||
ce.export_without_send_later
|
||||
source.shard.activate do
|
||||
ce.export_without_send_later
|
||||
end
|
||||
|
||||
if ce.workflow_state == 'exported_for_course_copy'
|
||||
# use the exported attachment as the import archive
|
||||
|
|
|
@ -104,13 +104,14 @@ module CCHelper
|
|||
CCHelper.ims_datetime(date, default)
|
||||
end
|
||||
|
||||
def self.create_key(object, prepend="")
|
||||
def self.create_key(object, prepend="", global: false)
|
||||
if object.is_a? ActiveRecord::Base
|
||||
key = object.asset_string
|
||||
key = global ? object.global_asset_string : object.asset_string
|
||||
else
|
||||
key = object.to_s
|
||||
end
|
||||
"i" + Digest::MD5.hexdigest(prepend + key)
|
||||
# make it obvious if we're using new identifiers now
|
||||
(global ? "g" : "i") + Digest::MD5.hexdigest(prepend + key)
|
||||
end
|
||||
|
||||
def self.ims_date(date=nil,default=Time.now)
|
||||
|
|
|
@ -40,12 +40,13 @@ module CC::Exporter::Epub
|
|||
"WikiPage" => :pages
|
||||
}.freeze
|
||||
|
||||
def initialize(cartridge, sort_by_content=false, export_type=:epub)
|
||||
def initialize(cartridge, sort_by_content=false, export_type=:epub, global_identifiers: false)
|
||||
@cartridge = cartridge
|
||||
@export_type = export_type
|
||||
@sort_by_content = sort_by_content || cartridge_json[:modules].empty?
|
||||
@global_identifiers = global_identifiers
|
||||
end
|
||||
attr_reader :cartridge, :sort_by_content
|
||||
attr_reader :cartridge, :sort_by_content, :global_identifiers
|
||||
delegate :unsupported_files, to: :cartridge_converter, allow_nil: true
|
||||
|
||||
def cartridge_json
|
||||
|
|
|
@ -26,7 +26,8 @@ module CC::Exporter::WebZip
|
|||
end
|
||||
|
||||
def convert_to_offline_web_zip(progress_key)
|
||||
exporter = CC::Exporter::WebZip::Exporter.new(content_cartridge.open, false, :web_zip)
|
||||
exporter = CC::Exporter::WebZip::Exporter.new(content_cartridge.open, false, :web_zip,
|
||||
global_identifiers: self.content_export.global_identifiers?)
|
||||
zip = create_zip(exporter, progress_key)
|
||||
file_path = zip.create
|
||||
|
||||
|
@ -36,4 +37,4 @@ module CC::Exporter::WebZip
|
|||
file_path
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,6 +19,7 @@ module CC::Exporter::WebZip
|
|||
class ZipPackage < CC::Exporter::Epub::FilesDirectory
|
||||
|
||||
def initialize(exporter, course, user, progress_key)
|
||||
@global_identifiers = exporter.global_identifiers
|
||||
@files = exporter.unsupported_files + exporter.cartridge_json[:files]
|
||||
@pages = exporter.cartridge_json[:pages]
|
||||
@assignments = exporter.cartridge_json[:assignments]
|
||||
|
@ -33,7 +34,7 @@ module CC::Exporter::WebZip
|
|||
@user = user
|
||||
@current_progress = Rails.cache.fetch(progress_key)
|
||||
@current_progress ||= MustViewModuleProgressor.new(user, course).current_progress
|
||||
@html_converter = CC::CCHelper::HtmlContentExporter.new(course, user, for_epub_export: true)
|
||||
@html_converter = CC::CCHelper::HtmlContentExporter.new(course, user, for_epub_export: true, key_generator: self)
|
||||
@canvas_object_export_hash = map_canvas_objects_to_export_ids
|
||||
end
|
||||
attr_reader :files, :course, :user, :current_progress
|
||||
|
@ -43,6 +44,10 @@ module CC::Exporter::WebZip
|
|||
CONTENT_TYPES = [*ASSIGNMENT_TYPES, 'WikiPage'].freeze
|
||||
CONTENT_TOKENS = [CC::CCHelper::OBJECT_TOKEN, CC::CCHelper::COURSE_TOKEN, CC::CCHelper::WIKI_TOKEN].freeze
|
||||
|
||||
def create_key(obj)
|
||||
CC::CCHelper.create_key(obj, global: @global_identifiers)
|
||||
end
|
||||
|
||||
def force_timezone(time)
|
||||
time&.in_time_zone(user.time_zone)&.iso8601
|
||||
end
|
||||
|
@ -234,7 +239,7 @@ module CC::Exporter::WebZip
|
|||
prereqs: mod.prerequisites.map{|pre| pre[:id]}.select{|id| active_module_ids.include?(id)},
|
||||
requirement: requirement_type(mod),
|
||||
sequential: mod.require_sequential_progress || false,
|
||||
exportId: CC::CCHelper.create_key(mod),
|
||||
exportId: create_key(mod),
|
||||
items: parse_module_item_data(mod)
|
||||
}
|
||||
end
|
||||
|
@ -291,7 +296,7 @@ module CC::Exporter::WebZip
|
|||
def find_export_id(item)
|
||||
case item.content_type
|
||||
when 'Assignment', 'DiscussionTopic', 'Quizzes::Quiz'
|
||||
CC::CCHelper.create_key(item.content)
|
||||
create_key(item.content)
|
||||
when 'WikiPage'
|
||||
item.content&.url
|
||||
end
|
||||
|
@ -303,7 +308,7 @@ module CC::Exporter::WebZip
|
|||
item_hash[:submissionTypes] = item_content.readable_submission_types
|
||||
item_hash[:graded] = item_content.grading_type != 'not_graded'
|
||||
when Quizzes::Quiz
|
||||
item_hash[:assignmentExportId] = CC::CCHelper.create_key(item_content.assignment)
|
||||
item_hash[:assignmentExportId] = create_key(item_content.assignment)
|
||||
item_hash[:questionCount] = item_content.question_count
|
||||
item_hash[:timeLimit] = item_content.time_limit
|
||||
item_hash[:attempts] = item_content.allowed_attempts
|
||||
|
@ -312,7 +317,7 @@ module CC::Exporter::WebZip
|
|||
item_hash[:lockAt] = force_timezone(item_content.lock_at)
|
||||
item_hash[:unlockAt] = force_timezone(item_content.unlock_at)
|
||||
item_content = item_content.assignment
|
||||
item_hash[:assignmentExportId] = CC::CCHelper.create_key(item_content) if item_content.present?
|
||||
item_hash[:assignmentExportId] = create_key(item_content) if item_content.present?
|
||||
item_hash[:graded] = item_content.present?
|
||||
end
|
||||
return unless item_content
|
||||
|
@ -375,10 +380,10 @@ module CC::Exporter::WebZip
|
|||
type_export_hash = {}
|
||||
assignment_export_hash = {}
|
||||
course.send(type).each do |item|
|
||||
tag = (type == :wiki_pages ? item.url : CC::CCHelper.create_key(item))
|
||||
tag = (type == :wiki_pages ? item.url : create_key(item))
|
||||
type_export_hash[tag] = item
|
||||
next unless (type == :discussion_topics || type == :quizzes) && item.assignment
|
||||
assignment_id = CC::CCHelper.create_key(item.assignment)
|
||||
assignment_id = create_key(item.assignment)
|
||||
assignment_export_hash[assignment_id] = item
|
||||
@discussion_quiz_export_id_map[assignment_id] = tag
|
||||
end
|
||||
|
|
|
@ -284,7 +284,7 @@ describe ContentExportsApiController, type: :request do
|
|||
expect(export.workflow_state).to eql 'created'
|
||||
expect(export.export_type).to eql 'common_cartridge'
|
||||
expect(export.user_id).to eql t_teacher.id
|
||||
expect(export.settings['selected_content']['context_modules']).to eq({CC::CCHelper.create_key(mod) => "1"})
|
||||
expect(export.settings['selected_content']['context_modules']).to eq({export.create_key(mod) => "1"})
|
||||
expect(export.job_progress).to be_queued
|
||||
|
||||
run_jobs
|
||||
|
@ -304,10 +304,10 @@ describe ContentExportsApiController, type: :request do
|
|||
|
||||
run_jobs
|
||||
|
||||
expect(@course.context_modules.where(migration_id: CC::CCHelper.create_key(mod))).to be_exists
|
||||
copied_page = @course.wiki_pages.where(migration_id: CC::CCHelper.create_key(page_to_copy)).first
|
||||
expect(@course.context_modules.where(migration_id: export.create_key(mod))).to be_exists
|
||||
copied_page = @course.wiki_pages.where(migration_id: export.create_key(page_to_copy)).first
|
||||
expect(copied_page).not_to be_nil
|
||||
expect(@course.wiki_pages.where(migration_id: CC::CCHelper.create_key(page_to_not_copy))).not_to be_exists
|
||||
expect(@course.wiki_pages.where(migration_id: export.create_key(page_to_not_copy))).not_to be_exists
|
||||
|
||||
copied_att = @course.attachments.where(filename: att_to_copy.filename).first
|
||||
expect(copied_att).not_to be_nil
|
||||
|
@ -324,7 +324,7 @@ describe ContentExportsApiController, type: :request do
|
|||
expect(export.workflow_state).to eql 'created'
|
||||
expect(export.export_type).to eql 'common_cartridge'
|
||||
expect(export.user_id).to eql t_teacher.id
|
||||
expect(export.settings['selected_content']['context_modules']).to eq({CC::CCHelper.create_key(mod) => "1"})
|
||||
expect(export.settings['selected_content']['context_modules']).to eq({export.create_key(mod) => "1"})
|
||||
expect(export.job_progress).to be_queued
|
||||
|
||||
run_jobs
|
||||
|
@ -344,10 +344,10 @@ describe ContentExportsApiController, type: :request do
|
|||
|
||||
run_jobs
|
||||
|
||||
expect(@course.context_modules.where(migration_id: CC::CCHelper.create_key(mod))).to be_exists
|
||||
copied_page = @course.wiki_pages.where(migration_id: CC::CCHelper.create_key(page_to_copy)).first
|
||||
expect(@course.context_modules.where(migration_id: export.create_key(mod))).to be_exists
|
||||
copied_page = @course.wiki_pages.where(migration_id: export.create_key(page_to_copy)).first
|
||||
expect(copied_page).not_to be_nil
|
||||
expect(@course.wiki_pages.where(migration_id: CC::CCHelper.create_key(page_to_not_copy))).not_to be_exists
|
||||
expect(@course.wiki_pages.where(migration_id: export.create_key(page_to_not_copy))).not_to be_exists
|
||||
|
||||
copied_att = @course.attachments.where(filename: att_to_copy.filename).first
|
||||
expect(copied_att).not_to be_nil
|
||||
|
@ -360,7 +360,7 @@ describe ContentExportsApiController, type: :request do
|
|||
{ controller: 'content_exports_api', action: 'create', format: 'json', course_id: t_course.to_param, export_type: 'common_cartridge'},
|
||||
{ :select => {:quizzes => [quiz_to_copy.id]} })
|
||||
export = t_course.content_exports.where(id: json['id']).first
|
||||
expect(export.settings['selected_content']['quizzes']).to eq({CC::CCHelper.create_key(quiz_to_copy) => "1"})
|
||||
expect(export.settings['selected_content']['quizzes']).to eq({export.create_key(quiz_to_copy) => "1"})
|
||||
expect(export.export_object?(quiz_to_copy)).to be_truthy
|
||||
end
|
||||
|
||||
|
@ -369,7 +369,7 @@ describe ContentExportsApiController, type: :request do
|
|||
{ controller: 'content_exports_api', action: 'create', format: 'json', course_id: t_course.to_param, export_type: 'common_cartridge'},
|
||||
{ :select => {:announcements => [announcement.id]} })
|
||||
export = t_course.content_exports.where(id: json['id']).first
|
||||
expect(export.settings['selected_content']['announcements']).to eq({CC::CCHelper.create_key(announcement) => "1"})
|
||||
expect(export.settings['selected_content']['announcements']).to eq({export.create_key(announcement) => "1"})
|
||||
expect(export.export_object?(announcement)).to be_truthy
|
||||
end
|
||||
|
||||
|
@ -379,7 +379,7 @@ describe ContentExportsApiController, type: :request do
|
|||
{ :select => {:discussion_topics => [announcement.id]} })
|
||||
|
||||
export = t_course.content_exports.where(id: json['id']).first
|
||||
expect(export.settings['selected_content']['discussion_topics']).to eq({CC::CCHelper.create_key(announcement) => "1"})
|
||||
expect(export.settings['selected_content']['discussion_topics']).to eq({export.create_key(announcement) => "1"})
|
||||
expect(export.export_object?(announcement)).to be_truthy
|
||||
|
||||
run_jobs
|
||||
|
@ -395,7 +395,7 @@ describe ContentExportsApiController, type: :request do
|
|||
|
||||
run_jobs
|
||||
|
||||
copied_ann = @course.announcements.where(migration_id: CC::CCHelper.create_key(announcement)).first
|
||||
copied_ann = @course.announcements.where(migration_id: export.create_key(announcement)).first
|
||||
expect(copied_ann).to be_present
|
||||
end
|
||||
|
||||
|
@ -454,9 +454,9 @@ describe ContentExportsApiController, type: :request do
|
|||
|
||||
run_jobs
|
||||
|
||||
copied_page = @course.wiki_pages.where(migration_id: CC::CCHelper.create_key(page_to_copy)).first
|
||||
copied_page = @course.wiki_pages.where(migration_id: export.create_key(page_to_copy)).first
|
||||
expect(copied_page).not_to be_nil
|
||||
expect(@course.wiki_pages.where(migration_id: CC::CCHelper.create_key(page_to_not_copy))).not_to be_exists
|
||||
expect(@course.wiki_pages.where(migration_id: export.create_key(page_to_not_copy))).not_to be_exists
|
||||
end
|
||||
|
||||
it "should export rubrics attached to discussions" do
|
||||
|
@ -486,7 +486,7 @@ describe ContentExportsApiController, type: :request do
|
|||
|
||||
to_assign = @course.assignments.first
|
||||
to_outcomes = to_assign.rubric.learning_outcome_alignments.map(&:learning_outcome).map(&:migration_id)
|
||||
expect(to_outcomes).to eql [CC::CCHelper.create_key(@outcome)]
|
||||
expect(to_outcomes).to eql [export.create_key(@outcome)]
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -191,6 +191,8 @@ describe Canvas::Migration::Helpers::SelectiveContentFormatter do
|
|||
@migration = double()
|
||||
allow(@migration).to receive(:migration_type).and_return('course_copy_importer')
|
||||
allow(@migration).to receive(:source_course).and_return(@course)
|
||||
export = @course.content_exports.create!(:export_type => ContentExport::COURSE_COPY)
|
||||
allow(@migration).to receive(:content_export).and_return(export)
|
||||
@course_outcome = outcome_model
|
||||
@account_outcome = outcome_model(:outcome_context => @course.account)
|
||||
end
|
||||
|
|
|
@ -72,7 +72,7 @@ describe "Common Cartridge exporting" do
|
|||
end
|
||||
|
||||
def mig_id(obj)
|
||||
CC::CCHelper.create_key(obj)
|
||||
CC::CCHelper.create_key(obj, global: true)
|
||||
end
|
||||
|
||||
def check_resource_node(obj, type, selected=true)
|
||||
|
@ -224,10 +224,10 @@ describe "Common Cartridge exporting" do
|
|||
check_resource_node(@q1, CC::CCHelper::QTI_ASSESSMENT_TYPE)
|
||||
check_resource_node(@q2, CC::CCHelper::QTI_ASSESSMENT_TYPE)
|
||||
|
||||
alt_mig_id1 = CC::CCHelper.create_key(@q1, 'canvas_')
|
||||
alt_mig_id1 = CC::CCHelper.create_key(@q1, 'canvas_', global: true)
|
||||
expect(@manifest_doc.at_css("resource[identifier=#{alt_mig_id1}][type=\"#{CC::CCHelper::LOR}\"]")).not_to be_nil
|
||||
|
||||
alt_mig_id2 = CC::CCHelper.create_key(@q2, 'canvas_')
|
||||
alt_mig_id2 = CC::CCHelper.create_key(@q2, 'canvas_', global: true)
|
||||
expect(@manifest_doc.at_css("resource[identifier=#{alt_mig_id2}][type=\"#{CC::CCHelper::LOR}\"]")).not_to be_nil
|
||||
end
|
||||
|
||||
|
@ -571,11 +571,12 @@ describe "Common Cartridge exporting" do
|
|||
@ce.export_type = ContentExport::COMMON_CARTRIDGE
|
||||
@ce.save!
|
||||
run_export
|
||||
file_node = @manifest_doc.at_css("resource[identifier='id4164d7d594985594573e63f8ca15975'] file[href$='/blah.flv.tlh.subtitles']")
|
||||
key = CC::CCHelper.create_key(track.content, global: true)
|
||||
file_node = @manifest_doc.at_css("resource[identifier='#{key}'] file[href$='/blah.flv.tlh.subtitles']")
|
||||
expect(file_node).to be_present
|
||||
expect(@zip_file.read(file_node['href'])).to eql(track.content)
|
||||
track_doc = Nokogiri::XML(@zip_file.read('course_settings/media_tracks.xml'))
|
||||
expect(track_doc.at_css('media_tracks media track[locale=tlh][kind=subtitles][identifierref=id4164d7d594985594573e63f8ca15975]')).to be_present
|
||||
expect(track_doc.at_css("media_tracks media track[locale=tlh][kind=subtitles][identifierref=#{key}]")).to be_present
|
||||
expect(ccc_schema.validate(track_doc)).to be_empty
|
||||
end
|
||||
|
||||
|
|
|
@ -76,6 +76,10 @@ describe "Exportable" do
|
|||
def create_zip(exporter, progress_key)
|
||||
ZipPackageTest.new(exporter, @course, @user, progress_key)
|
||||
end
|
||||
|
||||
def content_export
|
||||
@export ||= @course.content_exports.create!(:export_type => ContentExport::COURSE_COPY)
|
||||
end
|
||||
end
|
||||
|
||||
context "#convert_to_web_zip" do
|
||||
|
|
|
@ -27,6 +27,10 @@ describe "ZipPackage" do
|
|||
end
|
||||
end
|
||||
|
||||
def create_key(obj)
|
||||
CC::CCHelper.create_key(obj)
|
||||
end
|
||||
|
||||
before :once do
|
||||
course_with_student(active_all: true)
|
||||
@cartridge_path = 'spec/fixtures/migration/unicode-filename-test-export.imscc'
|
||||
|
@ -44,7 +48,7 @@ describe "ZipPackage" do
|
|||
module_data = zip_package.parse_module_data
|
||||
expect(module_data).to eq [{id: @module.id, name: 'first_module', status: 'completed',
|
||||
unlockDate: nil, prereqs: [], requirement: nil, sequential: false,
|
||||
exportId: CC::CCHelper.create_key(@module), items: []}]
|
||||
exportId: create_key(@module), items: []}]
|
||||
end
|
||||
|
||||
it "should show modules locked by prerequisites with status of locked" do
|
||||
|
@ -317,8 +321,8 @@ describe "ZipPackage" do
|
|||
zip_package = CC::Exporter::WebZip::ZipPackage.new(@exporter, @course, @student, @cache_key)
|
||||
module_item_data = zip_package.parse_module_item_data(@module)
|
||||
|
||||
expect(module_item_data[0][:assignmentExportId]).to eq CC::CCHelper.create_key(graded_discussion_assignment)
|
||||
expect(module_item_data[1][:assignmentExportId]).to eq CC::CCHelper.create_key(quiz.assignment)
|
||||
expect(module_item_data[0][:assignmentExportId]).to eq create_key(graded_discussion_assignment)
|
||||
expect(module_item_data[1][:assignmentExportId]).to eq create_key(quiz.assignment)
|
||||
end
|
||||
|
||||
it "should parse graded status for not graded assignments, quizzes and discussions" do
|
||||
|
@ -419,7 +423,7 @@ describe "ZipPackage" do
|
|||
|
||||
zip_package = CC::Exporter::WebZip::ZipPackage.new(@exporter, @course, @student, @cache_key)
|
||||
module_item_data = zip_package.parse_module_item_data(@module)
|
||||
expect(module_item_data[0][:exportId]).to eq CC::CCHelper.create_key(assign)
|
||||
expect(module_item_data[0][:exportId]).to eq create_key(assign)
|
||||
expect(module_item_data[1][:exportId]).to eq 'wiki-page-1'
|
||||
end
|
||||
|
||||
|
@ -575,10 +579,14 @@ describe "ZipPackage" do
|
|||
export.user = @student
|
||||
export.save
|
||||
export.export_course
|
||||
exporter = CC::Exporter::WebZip::Exporter.new(export.attachment.open, false, :web_zip)
|
||||
exporter = CC::Exporter::WebZip::Exporter.new(export.attachment.open, false, :web_zip, global_identifiers: true)
|
||||
CC::Exporter::WebZip::ZipPackage.new(exporter, @course, @student, @cache_key)
|
||||
end
|
||||
|
||||
def create_key(obj)
|
||||
CC::CCHelper.create_key(obj, global: true)
|
||||
end
|
||||
|
||||
context "with course navigation tabs enabled" do
|
||||
before :once do
|
||||
@course.tab_configuration = [
|
||||
|
@ -600,7 +608,7 @@ describe "ZipPackage" do
|
|||
zip_package = create_zip_package
|
||||
assignment_data = zip_package.parse_non_module_items(:assignments)
|
||||
expect(assignment_data).to eq [{
|
||||
exportId: CC::CCHelper.create_key(assign), title: 'Assignment 1', type: 'Assignment',
|
||||
exportId: create_key(assign), title: 'Assignment 1', type: 'Assignment',
|
||||
content: '<p>Hi</p>', submissionTypes: "a text entry box or a file upload", graded: true,
|
||||
pointsPossible: 10.0, dueAt: due.in_time_zone(@student.time_zone).iso8601,
|
||||
lockAt: lock.in_time_zone(@student.time_zone).iso8601,
|
||||
|
@ -613,7 +621,7 @@ describe "ZipPackage" do
|
|||
zip_package = create_zip_package
|
||||
disc_data = zip_package.parse_non_module_items(:discussion_topics)
|
||||
expect(disc_data).to eq [{
|
||||
exportId: CC::CCHelper.create_key(disc), title: 'Discussion 1', type: 'DiscussionTopic',
|
||||
exportId: create_key(disc), title: 'Discussion 1', type: 'DiscussionTopic',
|
||||
graded: false, content: "<h1>Discussion</h1>", lockAt: nil, unlockAt: nil
|
||||
}]
|
||||
end
|
||||
|
@ -624,9 +632,9 @@ describe "ZipPackage" do
|
|||
zip_package = create_zip_package
|
||||
quiz_data = zip_package.parse_non_module_items(:quizzes)
|
||||
expect(quiz_data).to eq [{
|
||||
exportId: CC::CCHelper.create_key(quiz), title: 'Quiz 1', type: 'Quizzes::Quiz', questionCount: 0,
|
||||
exportId: create_key(quiz), title: 'Quiz 1', type: 'Quizzes::Quiz', questionCount: 0,
|
||||
timeLimit: 5, attempts: 2, graded: true, pointsPossible: 0.0, dueAt: nil, lockAt: nil,
|
||||
unlockAt: nil, content: nil, assignmentExportId: CC::CCHelper.create_key(quiz.assignment)
|
||||
unlockAt: nil, content: nil, assignmentExportId: create_key(quiz.assignment)
|
||||
}]
|
||||
end
|
||||
|
||||
|
@ -693,7 +701,7 @@ describe "ZipPackage" do
|
|||
zip_package = create_zip_package
|
||||
course_data = zip_package.parse_course_data
|
||||
expect(course_data[:assignments]).to eq [{
|
||||
exportId: CC::CCHelper.create_key(assign), title: 'Assignment 1', type: 'Assignment',
|
||||
exportId: create_key(assign), title: 'Assignment 1', type: 'Assignment',
|
||||
content: '<p>Hi</p>', submissionTypes: nil, graded: true,
|
||||
pointsPossible: nil, dueAt: nil, lockAt: nil, unlockAt: nil
|
||||
}]
|
||||
|
@ -707,12 +715,12 @@ describe "ZipPackage" do
|
|||
quiz.publish!
|
||||
@module.content_tags.create!(content: quiz, context: @course, indent: 0)
|
||||
course_data = create_zip_package.parse_course_data
|
||||
expect(course_data[:assignments][0][:exportId]).to eq CC::CCHelper.create_key(assign)
|
||||
expect(course_data[:assignments][0][:exportId]).to eq create_key(assign)
|
||||
expect(course_data[:assignments].length).to eq 1
|
||||
expect(course_data[:quizzes][0][:exportId]).to eq CC::CCHelper.create_key(quiz)
|
||||
expected = "<a href=\"assignments/#{CC::CCHelper.create_key(assign)}\">Link</a>"
|
||||
expect(course_data[:quizzes][0][:exportId]).to eq create_key(quiz)
|
||||
expected = "<a href=\"assignments/#{create_key(assign)}\">Link</a>"
|
||||
expect(course_data[:quizzes][0][:content]).to eq expected
|
||||
expect(course_data[:quizzes][0][:assignmentExportId]).to eq CC::CCHelper.create_key(quiz.assignment)
|
||||
expect(course_data[:quizzes][0][:assignmentExportId]).to eq create_key(quiz.assignment)
|
||||
end
|
||||
|
||||
it "should export quizzes linked from module items" do
|
||||
|
@ -722,13 +730,13 @@ describe "ZipPackage" do
|
|||
description: "<a href=\"/courses/#{@course.id}/quizzes/#{quiz.id}\">Link</a>")
|
||||
@module.content_tags.create!(content: assign, context: @course, indent: 0)
|
||||
course_data = create_zip_package.parse_course_data
|
||||
quiz_key = CC::CCHelper.create_key(quiz)
|
||||
expect(course_data[:assignments][0][:exportId]).to eq CC::CCHelper.create_key(assign)
|
||||
quiz_key = create_key(quiz)
|
||||
expect(course_data[:assignments][0][:exportId]).to eq create_key(assign)
|
||||
expect(course_data[:assignments][0][:content]).to eq "<a href=\"quizzes/#{quiz_key}\">Link</a>"
|
||||
expect(course_data[:assignments].length).to eq 1
|
||||
expect(course_data[:quizzes][0][:exportId]).to eq quiz_key
|
||||
expect(course_data[:quizzes].length).to eq 1
|
||||
expect(course_data[:quizzes][0][:assignmentExportId]).to eq CC::CCHelper.create_key(quiz.assignment)
|
||||
expect(course_data[:quizzes][0][:assignmentExportId]).to eq create_key(quiz.assignment)
|
||||
end
|
||||
|
||||
it "should export pages linked from module items" do
|
||||
|
@ -737,7 +745,7 @@ describe "ZipPackage" do
|
|||
description: "<a href=\"/courses/#{@course.id}/pages/#{page.id}\">Link</a>")
|
||||
@module.content_tags.create!(content: assign, context: @course, indent: 0)
|
||||
course_data = create_zip_package.parse_course_data
|
||||
expect(course_data[:assignments][0][:exportId]).to eq CC::CCHelper.create_key(assign)
|
||||
expect(course_data[:assignments][0][:exportId]).to eq create_key(assign)
|
||||
expect(course_data[:assignments][0][:content]).to eq "<a href=\"pages/#{page.url}\">Link</a>"
|
||||
expect(course_data[:assignments].length).to eq 1
|
||||
expect(course_data[:pages][0][:exportId]).to eq page.url
|
||||
|
@ -750,11 +758,11 @@ describe "ZipPackage" do
|
|||
description: "<a href=\"/courses/#{@course.id}/discussion_topics/#{discussion.id}\">Link</a>")
|
||||
@module.content_tags.create!(content: assign, context: @course, indent: 0)
|
||||
course_data = create_zip_package.parse_course_data
|
||||
expect(course_data[:assignments][0][:exportId]).to eq CC::CCHelper.create_key(assign)
|
||||
expected = "<a href=\"discussion_topics/#{CC::CCHelper.create_key(discussion)}\">Link</a>"
|
||||
expect(course_data[:assignments][0][:exportId]).to eq create_key(assign)
|
||||
expected = "<a href=\"discussion_topics/#{create_key(discussion)}\">Link</a>"
|
||||
expect(course_data[:assignments][0][:content]).to eq expected
|
||||
expect(course_data[:assignments].length).to eq 1
|
||||
expect(course_data[:discussion_topics][0][:exportId]).to eq CC::CCHelper.create_key(discussion)
|
||||
expect(course_data[:discussion_topics][0][:exportId]).to eq create_key(discussion)
|
||||
expect(course_data[:discussion_topics].length).to eq 1
|
||||
end
|
||||
|
||||
|
@ -764,7 +772,7 @@ describe "ZipPackage" do
|
|||
description: "<a href=\"/courses/#{@course.id}/files/#{file.id}/download?wrap=1\">Link</a>")
|
||||
@module.content_tags.create!(content: assign, context: @course, indent: 0)
|
||||
course_data = create_zip_package.parse_course_data
|
||||
expect(course_data[:assignments][0][:exportId]).to eq CC::CCHelper.create_key(assign)
|
||||
expect(course_data[:assignments][0][:exportId]).to eq create_key(assign)
|
||||
expected = "<a href=\"viewer/files/amazing_file.txt?canvas_download=1&canvas_qs_wrap=1\">Link</a>"
|
||||
expect(course_data[:assignments][0][:content]).to eq expected
|
||||
expect(course_data[:assignments].length).to eq 1
|
||||
|
@ -779,12 +787,12 @@ describe "ZipPackage" do
|
|||
body: "<a href=\"/courses/#{@course.id}/assignments/#{assign.id}\">Link</a>")
|
||||
@module.content_tags.create!(content: page, context: @course, indent: 0)
|
||||
course_data = create_zip_package.parse_course_data
|
||||
expect(course_data[:assignments][0][:exportId]).to eq CC::CCHelper.create_key(assign)
|
||||
expect(course_data[:assignments][0][:exportId]).to eq create_key(assign)
|
||||
expect(course_data[:assignments][0][:content]).to eq "<a href=\"viewer/files/amazing_file.txt\">Link</a>"
|
||||
expect(course_data[:assignments].length).to eq 1
|
||||
page_data = course_data[:pages][0]
|
||||
expect(page_data[:exportId]).to eq page.url
|
||||
expect(page_data[:content]).to eq "<a href=\"assignments/#{CC::CCHelper.create_key(assign)}\">Link</a>"
|
||||
expect(page_data[:content]).to eq "<a href=\"assignments/#{create_key(assign)}\">Link</a>"
|
||||
expect(course_data[:pages].length).to eq 1
|
||||
expect(course_data[:files]).to eq [{type: "file", name: "amazing_file.txt", size: 26, files: nil}]
|
||||
end
|
||||
|
@ -797,12 +805,12 @@ describe "ZipPackage" do
|
|||
@module.content_tags.create!(content: page, context: @course, indent: 0)
|
||||
course_data = create_zip_package.parse_course_data
|
||||
expect(course_data[:assignments]).to eq [{
|
||||
exportId: CC::CCHelper.create_key(assign), title: 'Assignment 1', type: 'Assignment',
|
||||
exportId: create_key(assign), title: 'Assignment 1', type: 'Assignment',
|
||||
content: "<a href=\"pages/page-1\">Link</a>", submissionTypes: nil, graded: true,
|
||||
pointsPossible: nil, dueAt: nil, lockAt: nil, unlockAt: nil
|
||||
}]
|
||||
expect(course_data[:pages]).to eq [{exportId: 'page-1', title: 'Page 1', type: 'WikiPage',
|
||||
content: "<a href=\"assignments/#{CC::CCHelper.create_key(assign)}\">Link</a>", frontPage: false}]
|
||||
content: "<a href=\"assignments/#{create_key(assign)}\">Link</a>", frontPage: false}]
|
||||
end
|
||||
|
||||
it "should export items linked as images" do
|
||||
|
@ -814,9 +822,9 @@ describe "ZipPackage" do
|
|||
@module.content_tags.create!(content: survey, context: @course, indent: 0)
|
||||
course_data = create_zip_package.parse_course_data
|
||||
expect(course_data[:quizzes]).to eq [{
|
||||
exportId: CC::CCHelper.create_key(survey), title: 'Survey 1', type: 'Quizzes::Quiz',
|
||||
exportId: create_key(survey), title: 'Survey 1', type: 'Quizzes::Quiz',
|
||||
content: "<img src=\"viewer/files/cn_image.jpg\">",
|
||||
assignmentExportId: CC::CCHelper.create_key(survey.assignment), questionCount: 0, timeLimit: nil,
|
||||
assignmentExportId: create_key(survey.assignment), questionCount: 0, timeLimit: nil,
|
||||
attempts: 1, graded: false, dueAt: due_at.iso8601, lockAt: nil, unlockAt: nil
|
||||
}]
|
||||
expect(course_data[:files]).to eq [{type: "file", name: "cn_image.jpg", size: 30339, files: nil}]
|
||||
|
@ -830,13 +838,13 @@ describe "ZipPackage" do
|
|||
@module.content_tags.create!(content: assign, context: @course, indent: 0)
|
||||
course_data = create_zip_package.parse_course_data
|
||||
expect(course_data[:assignments]).to eq [{
|
||||
exportId: CC::CCHelper.create_key(assign), title: 'Assignment 1', type: 'Assignment',
|
||||
content: "<a href=\"assignments/#{CC::CCHelper.create_key(quiz.assignment)}\">Link</a>",
|
||||
exportId: create_key(assign), title: 'Assignment 1', type: 'Assignment',
|
||||
content: "<a href=\"assignments/#{create_key(quiz.assignment)}\">Link</a>",
|
||||
submissionTypes: nil, graded: true, pointsPossible: nil, dueAt: nil, lockAt: nil, unlockAt: nil
|
||||
}]
|
||||
expect(course_data[:quizzes]).to eq [{
|
||||
exportId: CC::CCHelper.create_key(quiz), title: 'Quiz 1', type: 'Quizzes::Quiz',
|
||||
content: nil, assignmentExportId: CC::CCHelper.create_key(quiz.assignment), questionCount: 0,
|
||||
exportId: create_key(quiz), title: 'Quiz 1', type: 'Quizzes::Quiz',
|
||||
content: nil, assignmentExportId: create_key(quiz.assignment), questionCount: 0,
|
||||
timeLimit: nil, attempts: 1, graded: true, pointsPossible: 0.0, dueAt: nil, lockAt: nil, unlockAt: nil
|
||||
}]
|
||||
end
|
||||
|
@ -857,7 +865,7 @@ describe "ZipPackage" do
|
|||
@module.content_tags.create!(content: disc, context: @course, indent: 0)
|
||||
course_data = create_zip_package.parse_course_data
|
||||
expect(course_data[:discussion_topics]).to eq [{
|
||||
exportId: CC::CCHelper.create_key(disc), title: 'Discussion 1', type: 'DiscussionTopic',
|
||||
exportId: create_key(disc), title: 'Discussion 1', type: 'DiscussionTopic',
|
||||
content: "<img src=\"viewer/files/folder%231/cn_image.jpg\">",
|
||||
lockAt: nil, unlockAt: nil, graded: false
|
||||
}]
|
||||
|
@ -911,8 +919,8 @@ describe "ZipPackage" do
|
|||
course_data = create_zip_package.parse_course_data
|
||||
expect(course_data[:assignments]).to eq []
|
||||
expect(course_data[:quizzes]).to eq [{
|
||||
exportId: CC::CCHelper.create_key(quiz), title: 'Quiz 1', type: 'Quizzes::Quiz',
|
||||
content: nil, assignmentExportId: CC::CCHelper.create_key(quiz.assignment), questionCount: 0,
|
||||
exportId: create_key(quiz), title: 'Quiz 1', type: 'Quizzes::Quiz',
|
||||
content: nil, assignmentExportId: create_key(quiz.assignment), questionCount: 0,
|
||||
timeLimit: nil, attempts: 1, graded: true, pointsPossible: 0.0, dueAt: nil, lockAt: nil, unlockAt: nil
|
||||
}]
|
||||
end
|
||||
|
@ -938,7 +946,7 @@ describe "ZipPackage" do
|
|||
assign = @course.assignments.create!(title: 'Assignment 1', points_possible: 10, description: '<p>Hi</p>')
|
||||
html = %(<a title="Assignment 1" href="/courses/#{@course.id}/assignments/#{assign.id}") +
|
||||
%( data-api-returntype="Assignment">Assignment 1</a>)
|
||||
expected_html = %(<a title="Assignment 1" href="assignments/#{CC::CCHelper.create_key(assign)}") +
|
||||
expected_html = %(<a title="Assignment 1" href="assignments/#{create_key(assign)}") +
|
||||
%( data-api-returntype="Assignment">Assignment 1</a>)
|
||||
converted_html = @zip_package.convert_html_to_local(html)
|
||||
expect(converted_html).to eq expected_html
|
||||
|
@ -949,7 +957,7 @@ describe "ZipPackage" do
|
|||
description: '<p>Hi</p>', unlock_at: 5.days.from_now)
|
||||
html = %(<a title="Assignment 1" href="/courses/#{@course.id}/assignments/#{assign.id}") +
|
||||
%( data-api-returntype="Assignment">Assignment 1</a>)
|
||||
expected_html = %(<a title="Assignment 1" href="assignments/#{CC::CCHelper.create_key(assign)}") +
|
||||
expected_html = %(<a title="Assignment 1" href="assignments/#{create_key(assign)}") +
|
||||
%( data-api-returntype="Assignment">Assignment 1</a>)
|
||||
converted_html = @zip_package.convert_html_to_local(html)
|
||||
expect(converted_html).to eq expected_html
|
||||
|
|
|
@ -175,7 +175,7 @@ describe ContentExport do
|
|||
communication_channel_model.confirm!
|
||||
|
||||
['created', 'exporting', 'exported_for_course_copy', 'deleted'].each do |workflow|
|
||||
@ce.workflow_state = workflow
|
||||
@ce.workflow_state = workflow
|
||||
expect { @ce.save! }.to change(DelayedMessage, :count).by 0
|
||||
expect(@ce.messages_sent['Content Export Finished']).to be_blank
|
||||
expect(@ce.messages_sent['Content Export Failed']).to be_blank
|
||||
|
@ -238,4 +238,33 @@ describe ContentExport do
|
|||
expect(ContentExport.expired.pluck(:id)).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context "global_identifiers" do
|
||||
it "should be automatically set to true" do
|
||||
cc_export = @course.content_exports.create!(:export_type => ContentExport::COURSE_COPY)
|
||||
expect(cc_export.global_identifiers).to eq true
|
||||
end
|
||||
|
||||
it "should not set if there are any other exports in the context that weren't set" do
|
||||
prev_export = @course.content_exports.create!(:export_type => ContentExport::COURSE_COPY)
|
||||
prev_export.update_attribute(:global_identifiers, false)
|
||||
cc_export = @course.content_exports.create!(:export_type => ContentExport::COURSE_COPY)
|
||||
expect(cc_export.global_identifiers).to eq false
|
||||
end
|
||||
|
||||
it "should use global asset strings for keys if set" do
|
||||
export = @course.content_exports.create!(:export_type => ContentExport::COURSE_COPY)
|
||||
a = @course.assignments.create!
|
||||
expect(a).to receive(:global_asset_string).once.and_call_original
|
||||
export.create_key(a)
|
||||
end
|
||||
|
||||
it "should use local asset strings for keys if not set" do
|
||||
export = @course.content_exports.create!(:export_type => ContentExport::COURSE_COPY)
|
||||
export.update_attribute(:global_identifiers, false)
|
||||
a = @course.assignments.create!
|
||||
expect(a).to receive(:asset_string).once.and_call_original
|
||||
export.create_key(a)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
require File.expand_path(File.dirname(__FILE__) + '/../../sharding_spec_helper.rb')
|
||||
require File.expand_path(File.dirname(__FILE__) + '/course_copy_helper.rb')
|
||||
|
||||
describe ContentMigration do
|
||||
context "course copy across shards" do
|
||||
specs_require_sharding
|
||||
include_examples "course copy"
|
||||
|
||||
before :once do
|
||||
@shard1.activate do
|
||||
@other_account = Account.create
|
||||
@copy_from = @other_account.courses.create!
|
||||
@copy_from.enroll_user(@user, "TeacherEnrollment", :enrollment_state => "active")
|
||||
end
|
||||
@cm.update_attribute(:source_course, @copy_from)
|
||||
end
|
||||
|
||||
it "should copy everything" do
|
||||
dt1 = @copy_from.discussion_topics.create!(:message => "hi", :title => "discussion title")
|
||||
cm = @copy_from.context_modules.create!(:name => "some module")
|
||||
att = @copy_from.attachments.create!(:filename => 'first.txt', :uploaded_data => StringIO.new('ohai'), :folder => Folder.unfiled_folder(@copy_from))
|
||||
wiki = @copy_from.wiki_pages.create!(:title => "wiki", :body => "ohai")
|
||||
quiz = @copy_from.quizzes.create! if Qti.qti_enabled?
|
||||
ag = @copy_from.assignment_groups.create!(:name => 'empty group')
|
||||
asmnt = @copy_from.assignments.create!(:title => "some assignment")
|
||||
cal = @copy_from.calendar_events.create!(:title => "haha", :description => "oi")
|
||||
tool = @copy_from.context_external_tools.create!(:name => "new tool", :consumer_key => "key", :shared_secret => "secret",
|
||||
:domain => 'example.com', :custom_fields => {'a' => '1', 'b' => '2'}, :workflow_state => 'public')
|
||||
data = [{:points => 3,:description => "Outcome row",:id => 1,:ratings => [{:points => 3,:description => "Rockin'",:criterion_id => 1,:id => 2}]}]
|
||||
rub1 = @copy_from.rubrics.create!(:title => "rub1", :data => data)
|
||||
rub1.associate_with(@copy_from, @copy_from)
|
||||
default = @copy_from.root_outcome_group
|
||||
lo = @copy_from.created_learning_outcomes.create!(:context => @copy_from, :short_description => "outcome1", :workflow_state => 'active',
|
||||
:data => {:rubric_criterion=>{:mastery_points=>2, :ratings=>[{:description=>"e", :points=>50}, {:description=>"me", :points=>2},
|
||||
{:description=>"Does Not Meet Expectations", :points=>0.5}], :description=>"First outcome", :points_possible=>5}})
|
||||
default.add_outcome(lo)
|
||||
gs = @copy_from.grading_standards.create!(:title => "Standard eh", :data => [["A", 0.93], ["A-", 0.89], ["F", 0]])
|
||||
|
||||
run_course_copy
|
||||
|
||||
dt1_to = @copy_to.discussion_topics.first
|
||||
expect(dt1_to.migration_id).to eq CC::CCHelper.create_key("discussion_topic_#{dt1.global_id}", global: true) # use global identifier
|
||||
expect(dt1_to.workflow_state).to eq 'active'
|
||||
expect(@copy_to.context_modules.where(migration_id: mig_id(cm)).first.workflow_state).to eq 'active'
|
||||
expect(@copy_to.attachments.count).to eq 1
|
||||
att_to = @copy_to.attachments.where(migration_id: mig_id(att)).first
|
||||
expect(att_to.file_state).to eq 'available'
|
||||
expect(att_to.root_attachment_id).to eq nil # should be root
|
||||
expect(att_to.open.read).to eq "ohai" # should have copied content over
|
||||
expect(att_to.namespace).to eq "account_#{@copy_to.root_account.id}"
|
||||
expect(@copy_to.wiki_pages.where(migration_id: mig_id(wiki)).first.workflow_state).to eq 'active'
|
||||
rub2 = @copy_to.rubrics.where(migration_id: mig_id(rub1)).first
|
||||
expect(rub2.workflow_state).to eq 'active'
|
||||
expect(rub2.rubric_associations.first.bookmarked).to eq true
|
||||
expect(@copy_to.created_learning_outcomes.where(migration_id: mig_id(lo)).first.workflow_state).to eq 'active'
|
||||
expect(@copy_to.quizzes.where(migration_id: mig_id(quiz)).first.workflow_state).to eq 'unpublished' if Qti.qti_enabled?
|
||||
expect(@copy_to.context_external_tools.where(migration_id: mig_id(tool)).first.workflow_state).to eq 'public'
|
||||
expect(@copy_to.assignment_groups.where(migration_id: mig_id(ag)).first.workflow_state).to eq 'available'
|
||||
expect(@copy_to.assignments.where(migration_id: mig_id(asmnt)).first.workflow_state).to eq asmnt.workflow_state
|
||||
expect(@copy_to.grading_standards.where(migration_id: mig_id(gs)).first.workflow_state).to eq 'active'
|
||||
expect(@copy_to.calendar_events.where(migration_id: mig_id(cal)).first.workflow_state).to eq 'active'
|
||||
end
|
||||
|
||||
it "should use local ids if we're possibly re-importing a previously copied course" do
|
||||
prev_export = @copy_from.content_exports.create!(:export_type => ContentExport::COURSE_COPY)
|
||||
prev_export.update_attribute(:global_identifiers, false)
|
||||
|
||||
dt = @copy_from.discussion_topics.create!(:message => "hi", :title => "discussion title")
|
||||
|
||||
run_course_copy
|
||||
|
||||
expect(@copy_from.content_exports.last).to eq @cm.content_export
|
||||
expect(@cm.content_export.global_identifiers?).to eq false
|
||||
|
||||
dt_to = @copy_to.discussion_topics.first
|
||||
expect(dt_to.migration_id).to eq CC::CCHelper.create_key("discussion_topic_#{dt.local_id}", global: false)
|
||||
end
|
||||
|
||||
it "should try to find existing root attachments on destination account" do
|
||||
att = @copy_from.attachments.create!(:filename => 'first.txt', :uploaded_data => StringIO.new('ohai'), :folder => Folder.unfiled_folder(@copy_from))
|
||||
dest_root = @copy_to.attachments.create!(:filename => 'totallydifferentname.txt', :uploaded_data => StringIO.new('ohai'), :folder => Folder.unfiled_folder(@copy_to))
|
||||
|
||||
run_course_copy
|
||||
|
||||
att_to = @copy_to.attachments.where(migration_id: mig_id(att)).first
|
||||
expect(att_to.root_attachment).to eq dest_root
|
||||
end
|
||||
end
|
||||
end
|
|
@ -110,7 +110,7 @@ describe ContentMigration do
|
|||
|
||||
run_course_copy
|
||||
|
||||
tool = @copy_to.context_external_tools.where(migration_id: CC::CCHelper.create_key(@tool_from)).first
|
||||
tool = @copy_to.context_external_tools.where(migration_id: mig_id(@tool_from)).first
|
||||
expect(tool.settings[:vendor_extensions]).to eq [{'platform'=>"my.lms.com", 'custom_fields'=>{"key"=>"value"}}]
|
||||
end
|
||||
|
||||
|
@ -124,7 +124,7 @@ describe ContentMigration do
|
|||
|
||||
run_course_copy
|
||||
|
||||
tool = @copy_to.context_external_tools.where(migration_id: CC::CCHelper.create_key(@tool_from)).first
|
||||
tool = @copy_to.context_external_tools.where(migration_id: mig_id(@tool_from)).first
|
||||
expect(tool.course_navigation).not_to be_nil
|
||||
expect(tool.course_navigation).to eq @tool_from.course_navigation
|
||||
expect(tool.editor_button).not_to be_nil
|
||||
|
@ -143,7 +143,7 @@ describe ContentMigration do
|
|||
:url => "https://www.example.com/launch"
|
||||
run_course_copy
|
||||
|
||||
tool_copy = @copy_to.context_external_tools.where(migration_id: CC::CCHelper.create_key(@tool_from)).first
|
||||
tool_copy = @copy_to.context_external_tools.where(migration_id: mig_id(@tool_from)).first
|
||||
tag = @copy_to.context_modules.first.content_tags.first
|
||||
expect(tag.content_type).to eq 'ContextExternalTool'
|
||||
expect(tag.content_id).to eq tool_copy.id
|
||||
|
@ -198,7 +198,7 @@ describe ContentMigration do
|
|||
|
||||
run_course_copy
|
||||
|
||||
tool = @copy_to.context_external_tools.where(migration_id: CC::CCHelper.create_key(@tool_from)).first
|
||||
tool = @copy_to.context_external_tools.where(migration_id: mig_id(@tool_from)).first
|
||||
expect(tool.settings[:selection_width]).to eq 5000
|
||||
expect(tool.course_settings_sub_navigation[:message_type]).to eq "ContentItemSelectionResponse"
|
||||
end
|
||||
|
|
|
@ -85,7 +85,7 @@ shared_examples_for "course copy" do
|
|||
end
|
||||
|
||||
def mig_id(obj)
|
||||
CC::CCHelper.create_key(obj)
|
||||
CC::CCHelper.create_key(obj, global: true)
|
||||
end
|
||||
|
||||
def create_outcome(context, group=nil)
|
||||
|
|
|
@ -76,7 +76,7 @@ describe ContentMigration do
|
|||
@cm.save!
|
||||
run_course_copy
|
||||
|
||||
new_topic = @copy_to.discussion_topics.where(migration_id: CC::CCHelper.create_key(topic)).first
|
||||
new_topic = @copy_to.discussion_topics.where(migration_id: mig_id(topic)).first
|
||||
expect(new_topic).not_to be_nil
|
||||
expect(new_topic.message).to eq topic.message
|
||||
expect(@copy_to.syllabus_body).to match(/\/courses\/#{@copy_to.id}\/discussion_topics\/#{new_topic.id}/)
|
||||
|
@ -124,10 +124,10 @@ describe ContentMigration do
|
|||
|
||||
run_course_copy
|
||||
|
||||
new_att = @copy_to.attachments.where(migration_id: CC::CCHelper.create_key(att)).first
|
||||
new_att = @copy_to.attachments.where(migration_id: mig_id(att)).first
|
||||
expect(new_att).not_to be_nil
|
||||
|
||||
new_topic = @copy_to.discussion_topics.where(migration_id: CC::CCHelper.create_key(topic)).first
|
||||
new_topic = @copy_to.discussion_topics.where(migration_id: mig_id(topic)).first
|
||||
expect(new_topic).not_to be_nil
|
||||
expect(new_topic.message).to match(Regexp.new("/courses/#{@copy_to.id}/files/#{new_att.id}/preview"))
|
||||
end
|
||||
|
@ -140,10 +140,10 @@ describe ContentMigration do
|
|||
|
||||
run_course_copy
|
||||
|
||||
new_att = @copy_to.attachments.where(migration_id: CC::CCHelper.create_key(att)).first
|
||||
new_att = @copy_to.attachments.where(migration_id: mig_id(att)).first
|
||||
expect(new_att).not_to be_nil
|
||||
|
||||
new_topic = @copy_to.discussion_topics.where(migration_id: CC::CCHelper.create_key(topic)).first
|
||||
new_topic = @copy_to.discussion_topics.where(migration_id: mig_id(topic)).first
|
||||
expect(new_topic).not_to be_nil
|
||||
|
||||
expect(new_topic.message).to match(Regexp.new("/courses/#{@copy_to.id}/files/#{new_att.id}/preview"))
|
||||
|
@ -158,7 +158,7 @@ describe ContentMigration do
|
|||
run_course_copy
|
||||
|
||||
@copy_to.enroll_student(student)
|
||||
new_att = @copy_to.attachments.where(migration_id: CC::CCHelper.create_key(att)).first
|
||||
new_att = @copy_to.attachments.where(migration_id: mig_id(att)).first
|
||||
expect(new_att).to be_present
|
||||
|
||||
expect(new_att.grants_right?(student, :download)).to be_falsey
|
||||
|
@ -671,7 +671,7 @@ describe ContentMigration do
|
|||
run_course_copy
|
||||
|
||||
expect(@copy_to.calendar_events.count).to eq 2
|
||||
cal_2 = @copy_to.calendar_events.where(migration_id: CC::CCHelper.create_key(cal)).first
|
||||
cal_2 = @copy_to.calendar_events.where(migration_id: mig_id(cal)).first
|
||||
expect(cal_2.title).to eq cal.title
|
||||
expect(cal_2.start_at.to_i).to eq cal.start_at.to_i
|
||||
expect(cal_2.end_at.to_i).to eq cal.end_at.to_i
|
||||
|
@ -679,7 +679,7 @@ describe ContentMigration do
|
|||
expect(cal_2.all_day_date).to eq cal.all_day_date
|
||||
cal_2.description = body_with_link % @copy_to.id
|
||||
|
||||
cal2_2 = @copy_to.calendar_events.where(migration_id: CC::CCHelper.create_key(cal2)).first
|
||||
cal2_2 = @copy_to.calendar_events.where(migration_id: mig_id(cal2)).first
|
||||
expect(cal2_2.title).to eq cal2.title
|
||||
expect(cal2_2.start_at.to_i).to eq cal2.start_at.to_i
|
||||
expect(cal2_2.end_at.to_i).to eq cal2.end_at.to_i
|
||||
|
@ -697,10 +697,10 @@ describe ContentMigration do
|
|||
run_course_copy
|
||||
}.to raise_error(ArgumentError)
|
||||
|
||||
new_att = @copy_to.attachments.where(migration_id: CC::CCHelper.create_key(att)).first
|
||||
new_att = @copy_to.attachments.where(migration_id: mig_id(att)).first
|
||||
expect(new_att).not_to be_nil
|
||||
|
||||
new_topic = @copy_to.discussion_topics.where(migration_id: CC::CCHelper.create_key(topic)).first
|
||||
new_topic = @copy_to.discussion_topics.where(migration_id: mig_id(topic)).first
|
||||
expect(new_topic).not_to be_nil
|
||||
expect(new_topic.message).to match(Regexp.new("/courses/#{@copy_to.id}/files/#{new_att.id}/preview"))
|
||||
end
|
||||
|
|
|
@ -56,7 +56,7 @@ describe "content exports" do
|
|||
|
||||
run_export do
|
||||
f("input[value=qti]").click
|
||||
f(%{.quiz_item[name="copy[quizzes][#{CC::CCHelper.create_key(q2)}]"]}).click
|
||||
f(%{.quiz_item[name="copy[quizzes][#{CC::CCHelper.create_key(q2, global: true)}]"]}).click
|
||||
end
|
||||
|
||||
expect(@export.export_type).to eq 'qti'
|
||||
|
@ -65,8 +65,8 @@ describe "content exports" do
|
|||
zip_file = Zip::File.open(file_handle.path)
|
||||
manifest_doc = Nokogiri::XML.parse(zip_file.read("imsmanifest.xml"))
|
||||
|
||||
expect(manifest_doc.at_css("resource[identifier=#{CC::CCHelper.create_key(q1)}]")).not_to be_nil
|
||||
expect(manifest_doc.at_css("resource[identifier=#{CC::CCHelper.create_key(q2)}]")).to be_nil
|
||||
expect(manifest_doc.at_css("resource[identifier=#{CC::CCHelper.create_key(q1, global: true)}]")).not_to be_nil
|
||||
expect(manifest_doc.at_css("resource[identifier=#{CC::CCHelper.create_key(q2, global: true)}]")).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue