migration refactor: account question banks import

test plan:
* course migration regressions (canvas cc, course copy)

* in addition:
 use the content migrations api to queue a content migration
 for an account ("/accounts/:account_id/content_migrations")
 to import a qti/cc package.
* confirm that the question banks from the package
 that would have been normally imported into a course are now
 imported into the account

closes #CNVS-12529

Change-Id: I2ef306341d2f7defe03c63a981679a3987f1aaa0
Reviewed-on: https://gerrit.instructure.com/34303
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Jeremy Stanley <jeremy@instructure.com>
QA-Review: Clare Strong <clare@instructure.com>
Product-Review: James Williams  <jamesw@instructure.com>
This commit is contained in:
James Williams 2014-05-05 06:23:55 -06:00
parent 9b28f26546
commit 2ba4531f73
58 changed files with 373 additions and 329 deletions

View File

@ -191,9 +191,7 @@ class ContentMigrationsController < ApplicationController
end
def migration_plugin_supported?(plugin)
# FIXME most migration types don't support Account either, but plugins that do would have to set additional_contexts
# in order to not be broken by this
@context.is_a?(Course) || @context.is_a?(Account) || Array(plugin.settings[:additional_contexts]).include?(@context.class.to_s)
Array(plugin.settings && plugin.settings[:valid_contexts]).include?(@context.class.to_s)
end
private :migration_plugin_supported?
@ -446,7 +444,7 @@ class ContentMigrationsController < ApplicationController
def find_migration_plugin(name)
if name =~ /context_external_tool/
plugin = Canvas::Plugin.new(name)
plugin.meta[:settings] = {requires_file_upload: true, worker: 'CCWorker'}.with_indifferent_access
plugin.meta[:settings] = {requires_file_upload: true, worker: 'CCWorker', valid_contexts: %w{Course}}.with_indifferent_access
plugin
else
Canvas::Plugin.find(name)

View File

@ -76,6 +76,8 @@ class Account < ActiveRecord::Base
has_many :roles
has_many :all_roles, :class_name => 'Role', :foreign_key => 'root_account_id'
has_many :progresses, :as => :context
has_many :content_migrations, :as => :context
def inherited_assessment_question_banks(include_self = false, *additional_contexts)
sql = []
conds = []
@ -1371,10 +1373,6 @@ class Account < ActiveRecord::Base
false
end
def import_from_migration(data, params, migration)
Importers::AccountContentImporter.import_content(self, data, params, migration)
end
def enable_fabulous_quizzes!
root_account.enable_feature! :draft_state
change_root_account_setting!(:enable_fabulous_quizzes, true)

View File

@ -263,13 +263,5 @@ class AssessmentQuestion < ActiveRecord::Base
dup
end
def self.process_migration(*args)
Importers::AssessmentQuestionImporter.process_migration(*args)
end
def self.import_from_migration(*args)
Importers::AssessmentQuestionImporter.import_from_migration(*args)
end
scope :active, where("assessment_questions.workflow_state<>'deleted'")
end

View File

@ -1632,14 +1632,6 @@ class Assignment < ActiveRecord::Base
end
protected :readable_submission_type
def self.process_migration(*args)
Importers::AssignmentImporter.process_migration(*args)
end
def self.import_from_migration(*args)
Importers::AssignmentImporter.import_from_migration(*args)
end
def expects_submission?
submission_types && submission_types.strip != "" && submission_types != "none" && submission_types != 'not_graded' && submission_types != "on_paper" && submission_types != 'external_tool'
end

View File

@ -169,14 +169,6 @@ class AssignmentGroup < ActiveRecord::Base
assignments.map(&:students).flatten
end
def self.process_migration(*args)
Importers::AssignmentGroupImporter.process_migration(*args)
end
def self.import_from_migration(*args)
Importers::AssignmentGroupImporter.import_from_migration(*args)
end
def self.add_never_drop_assignment(group, assignment)
rule = "never_drop:#{assignment.id}\n"
if group.rules

View File

@ -514,14 +514,6 @@ class CalendarEvent < ActiveRecord::Base
return CalendarEvent::IcalEvent.new(self).to_ics(in_own_calendar)
end
def self.process_migration(*args)
Importers::CalendarEventImporter.process_migration(*args)
end
def self.import_from_migration(*args)
Importers::CalendarEventImporter.import_from_migration(*args)
end
def self.max_visible_calendars
10
end

View File

@ -33,7 +33,7 @@ class ContentMigration < ActiveRecord::Base
DATE_FORMAT = "%m/%d/%Y"
attr_accessible :context, :migration_settings, :user, :source_course, :copy_options, :migration_type, :initiated_source
attr_accessor :outcome_to_id_map
attr_accessor :imported_migration_items, :outcome_to_id_map
EXPORTABLE_ATTRIBUTES = [
:id, :context_id, :user_id, :workflow_state, :migration_settings, :started_at, :finished_at, :created_at, :updated_at, :context_type,
@ -421,7 +421,7 @@ class ContentMigration < ActiveRecord::Base
migration_settings[:migration_ids_to_import] ||= {:copy=>{}}
self.context.import_from_migration(data, migration_settings[:migration_ids_to_import], self)
Importers.content_importer_for(self.context_type).import_content(self.context, data, migration_settings[:migration_ids_to_import], self)
if !self.import_immediately?
update_import_progress(100)
@ -609,4 +609,12 @@ class ContentMigration < ActiveRecord::Base
end
hash
end
def imported_migration_items
@imported_migration_items ||= []
end
def add_imported_item(item)
self.imported_migration_items << item unless self.imported_migration_items.include?(item)
end
end

View File

@ -160,7 +160,7 @@ class ContextExternalTool < ActiveRecord::Base
if tool_hash[:error]
@config_errors << [error_field, tool_hash[:error]]
else
Importers::ContextExternalToolImporter.import_from_migration(tool_hash, context, self)
Importers::ContextExternalToolImporter.import_from_migration(tool_hash, context, nil, self)
end
self.name = real_name unless real_name.blank?
rescue CC::Importer::BLTIConverter::CCImportError => e
@ -537,14 +537,6 @@ class ContextExternalTool < ActiveRecord::Base
end
end
def self.process_migration(*args)
Importers::ContextExternalToolImporter.process_migration(*args)
end
def self.import_from_migration(*args)
Importers::ContextExternalToolImporter.import_from_migration(*args)
end
def resource_selection_settings
settings[:resource_selection]
end

View File

@ -524,14 +524,6 @@ class ContextModule < ActiveRecord::Base
self.unlock_at && self.unlock_at > Time.now
end
def self.process_migration(*args)
Importers::ContextModuleImporter.process_migration(*args)
end
def self.import_from_migration(*args)
Importers::ContextModuleImporter.import_from_migration(*args)
end
def migration_position
@migration_position_counter ||= 0
@migration_position_counter = @migration_position_counter + 1

View File

@ -182,7 +182,7 @@ class Course < ActiveRecord::Base
has_many :page_views, :as => :context
has_many :asset_user_accesses, :as => :context
has_many :role_overrides, :as => :context
has_many :content_migrations, :foreign_key => :context_id
has_many :content_migrations, :as => :context
has_many :content_exports
has_many :course_imports
has_many :alerts, :as => :context, :include => :criteria
@ -1925,15 +1925,11 @@ class Course < ActiveRecord::Base
Canvas::Plugin.value_to_boolean(val)
end
def import_from_migration(data, params, migration)
Importers::CourseContentImporter.import_content(self, data, params, migration)
end
def add_migration_warning(message, exception='')
self.content_migration.add_warning(message, exception) if self.content_migration
end
attr_accessor :imported_migration_items, :full_migration_hash, :external_url_hash, :content_migration,
attr_accessor :full_migration_hash, :external_url_hash, :content_migration,
:folder_name_lookups, :attachment_path_id_lookup, :attachment_path_id_lookup_lower,
:assignment_group_no_drop_assignments, :migration_results

View File

@ -943,18 +943,6 @@ class DiscussionTopic < ActiveRecord::Base
Rails.cache.delete(root_topic.locked_cache_key(user)) if root_topic
end
def self.process_migration(data, migration)
# TODO: access Importers::DiscussionTopic directly
Importers::DiscussionTopicImporter.process_migration(data, migration)
end
def self.import_from_migration(*args)
# TODO: access Importers::DiscussionTopic directly
# this class method will eventually go away. leaving now
# for edge cases that may be using it.
Importers::DiscussionTopicImporter.import_from_migration(*args)
end
def self.podcast_elements(messages, context)
attachment_ids = []
media_object_ids = []

View File

@ -2,8 +2,12 @@ module Importers
class AccountContentImporter < Importer
self.item_class = Account
Importers.register_content_importer(self)
def self.import_content(account, data, params, migration)
migration.migration_settings[:import_quiz_questions_without_quiz] = true
Importers::AssessmentQuestionImporter.process_migration(data, migration)
Importers::LearningOutcomeImporter.process_migration(data, migration)
migration.progress = 100

View File

@ -53,7 +53,7 @@ module Importers
question[:question_bank_name] ||= migration.question_bank_name
question[:question_bank_name] ||= AssessmentQuestionBank.default_imported_title
end
if question[:assessment_question_migration_id]
if question[:assessment_question_migration_id] && !migration.migration_settings[:import_quiz_questions_without_quiz]
question_data[:qq_data][question['migration_id']] = question
next
end
@ -81,7 +81,7 @@ module Importers
end
begin
question = self.import_from_migration(question, migration.context, question_bank)
question = self.import_from_migration(question, migration.context, migration, question_bank)
# If the question appears to have links, we need to translate them so that file links point
# to the AssessmentQuestion. Ideally we would just do this before saving the question, but
@ -102,7 +102,7 @@ module Importers
question_data
end
def self.import_from_migration(hash, context, bank=nil, options={})
def self.import_from_migration(hash, context, migration=nil, bank=nil, options={})
hash = hash.with_indifferent_access
if !bank
hash[:question_bank_name] = nil if hash[:question_bank_name] == ''
@ -120,8 +120,10 @@ module Importers
end
end
hash.delete(:question_bank_migration_id) if hash.has_key?(:question_bank_migration_id)
context.imported_migration_items << bank if context.imported_migration_items && !context.imported_migration_items.include?(bank)
self.prep_for_import(hash, context)
migration.add_imported_item(bank) if migration
self.prep_for_import(hash, context, migration)
if id = hash['assessment_question_id']
AssessmentQuestion.where(id: id).update_all(name: hash[:question_name], question_data: hash.to_yaml,
workflow_state: 'active', created_at: Time.now.utc, updated_at: Time.now.utc,
@ -132,19 +134,19 @@ module Importers
VALUES (?,?,'active',?,?,?,?)
SQL
id = AssessmentQuestion.connection.insert(query, "#{name} Create",
AssessmentQuestion.primary_key, nil, AssessmentQuestion.sequence_name)
AssessmentQuestion.primary_key, nil, AssessmentQuestion.sequence_name)
hash['assessment_question_id'] = id
end
if context.respond_to?(:content_migration) && context.content_migration
if migration
hash[:missing_links].each do |field, missing_links|
context.content_migration.add_missing_content_links(:class => self.to_s,
:id => hash['assessment_question_id'], :field => field, :missing_links => missing_links,
:url => "/#{context.class.to_s.underscore.pluralize}/#{context.id}/question_banks/#{bank.id}#question_#{hash['assessment_question_id']}_question_text")
migration.add_missing_content_links(:class => self.to_s,
:id => hash['assessment_question_id'], :field => field, :missing_links => missing_links,
:url => "/#{context.class.to_s.underscore.pluralize}/#{context.id}/question_banks/#{bank.id}#question_#{hash['assessment_question_id']}_question_text")
end
if hash[:import_warnings]
hash[:import_warnings].each do |warning|
context.content_migration.add_warning(warning, {
:fix_issue_html_url => "/#{context.class.to_s.underscore.pluralize}/#{context.id}/question_banks/#{bank.id}#question_#{hash['assessment_question_id']}_question_text"
migration.add_warning(warning, {
:fix_issue_html_url => "/#{context.class.to_s.underscore.pluralize}/#{context.id}/question_banks/#{bank.id}#question_#{hash['assessment_question_id']}_question_text"
})
end
end
@ -153,11 +155,11 @@ module Importers
hash
end
def self.prep_for_import(hash, context)
def self.prep_for_import(hash, context, migration=nil)
hash[:missing_links] = {}
[:question_text, :correct_comments_html, :incorrect_comments_html, :neutral_comments_html, :more_comments_html].each do |field|
hash[:missing_links][field] = []
hash[field] = ImportedHtmlConverter.convert(hash[field], context, {:missing_links => hash[:missing_links][field], :remove_outer_nodes_if_one_child => true}) if hash[field].present?
hash[field] = ImportedHtmlConverter.convert(hash[field], context, migration, {:missing_links => hash[:missing_links][field], :remove_outer_nodes_if_one_child => true}) if hash[field].present?
end
[:correct_comments, :incorrect_comments, :neutral_comments, :more_comments].each do |field|
html_field = "#{field}_html".to_sym
@ -168,7 +170,7 @@ module Importers
hash[:answers].each_with_index do |answer, i|
[:html, :comments_html, :left_html].each do |field|
hash[:missing_links]["answer #{i} #{field}"] = []
answer[field] = ImportedHtmlConverter.convert(answer[field], context, {:missing_links => hash[:missing_links]["answer #{i} #{field}"], :remove_outer_nodes_if_one_child => true}) if answer[field].present?
answer[field] = ImportedHtmlConverter.convert(answer[field], context, migration, {:missing_links => hash[:missing_links]["answer #{i} #{field}"], :remove_outer_nodes_if_one_child => true}) if answer[field].present?
end
if answer[:comments].present? && answer[:comments] == answer[:comments_html]
answer.delete(:comments_html)

View File

@ -9,7 +9,7 @@ module Importers
groups.each do |group|
if migration.import_object?("assignment_groups", group['migration_id'])
begin
import_from_migration(group, migration.context)
import_from_migration(group, migration.context, migration)
rescue
migration.add_import_warning(t('#migration.assignment_group_type', "Assignment Group"), group[:title], $!)
end
@ -33,13 +33,13 @@ module Importers
end
end
def self.import_from_migration(hash, context, item=nil)
def self.import_from_migration(hash, context, migration=nil, item=nil)
hash = hash.with_indifferent_access
return nil if hash[:migration_id] && hash[:assignment_groups_to_import] && !hash[:assignment_groups_to_import][hash[:migration_id]]
item ||= AssignmentGroup.find_by_context_id_and_context_type_and_id(context.id, context.class.to_s, hash[:id])
item ||= AssignmentGroup.find_by_context_id_and_context_type_and_migration_id(context.id, context.class.to_s, hash[:migration_id]) if hash[:migration_id]
item ||= context.assignment_groups.new
context.imported_migration_items << item if context.imported_migration_items && item.new_record?
migration.add_imported_item(item) if migration && item.new_record?
item.migration_id = hash[:migration_id]
item.workflow_state = 'available' if item.deleted?
item.name = hash[:title]

View File

@ -9,7 +9,7 @@ module Importers
assignments.each do |assign|
if migration.import_object?("assignments", assign['migration_id'])
begin
import_from_migration(assign, migration.context)
import_from_migration(assign, migration.context, migration)
rescue
migration.add_import_warning(t('#migration.assignment_type', "Assignment"), assign[:title], $!)
end
@ -26,7 +26,7 @@ module Importers
end
end
def self.import_from_migration(hash, context, item=nil, quiz=nil)
def self.import_from_migration(hash, context, migration=nil, item=nil, quiz=nil)
hash = hash.with_indifferent_access
return nil if hash[:migration_id] && hash[:assignments_to_import] && !hash[:assignments_to_import][hash[:migration_id]]
item ||= Assignment.find_by_context_type_and_context_id_and_id(context.class.to_s, context.id, hash[:id])
@ -40,8 +40,8 @@ module Importers
end
hash[:missing_links] = {:description => [], :instructions => [], }
description = ""
description += hash[:instructions_in_html] == false ? ImportedHtmlConverter.convert_text(hash[:description] || "", context) : ImportedHtmlConverter.convert(hash[:description] || "", context, {:missing_links => hash[:missing_links][:description]})
description += hash[:instructions_in_html] == false ? ImportedHtmlConverter.convert_text(hash[:instructions] || "", context) : ImportedHtmlConverter.convert(hash[:instructions] || "", context, {:missing_links => hash[:missing_links][:instructions]})
description += hash[:instructions_in_html] == false ? ImportedHtmlConverter.convert_text(hash[:description] || "", context) : ImportedHtmlConverter.convert(hash[:description] || "", context, migration, {:missing_links => hash[:missing_links][:description]})
description += hash[:instructions_in_html] == false ? ImportedHtmlConverter.convert_text(hash[:instructions] || "", context) : ImportedHtmlConverter.convert(hash[:instructions] || "", context, migration, {:missing_links => hash[:missing_links][:instructions]})
description += Attachment.attachment_list_from_migration(context, hash[:attachment_ids])
item.description = description
@ -149,12 +149,12 @@ module Importers
item.send("#{prop}=", hash[prop]) unless hash[prop].nil?
end
context.imported_migration_items << item if context.imported_migration_items && new_record
migration.add_imported_item(item) if migration && new_record
item.save_without_broadcasting!
if context.respond_to?(:content_migration) && context.content_migration
if migration
hash[:missing_links].each do |field, missing_links|
context.content_migration.add_missing_content_links(:class => item.class.to_s,
migration.add_missing_content_links(:class => item.class.to_s,
:id => item.id, :field => field, :missing_links => missing_links,
:url => "/#{context.class.to_s.underscore.pluralize}/#{context.id}/#{item.class.to_s.demodulize.underscore.pluralize}/#{item.id}")
end

View File

@ -26,7 +26,7 @@ module Importers
attachments.values.each do |att|
if !att['is_folder'] && (migration.import_object?("attachments", att['migration_id']) || migration.import_object?("files", att['migration_id']))
begin
import_from_migration(att, migration.context)
import_from_migration(att, migration.context, migration)
rescue
migration.add_import_warning(I18n.t('#migration.file_type', "File"), (att[:display_name] || att[:path_name]), $!)
end
@ -56,7 +56,7 @@ module Importers
private
def self.import_from_migration(hash, context, item=nil)
def self.import_from_migration(hash, context, migration=nil, item=nil)
return nil if hash[:files_to_import] && !hash[:files_to_import][hash[:migration_id]]
item ||= Attachment.find_by_context_type_and_context_id_and_id(context.class.to_s, context.id, hash[:id])
item ||= Attachment.find_by_context_type_and_context_id_and_migration_id(context.class.to_s, context.id, hash[:migration_id]) # if hash[:migration_id]
@ -68,7 +68,7 @@ module Importers
item.file_state = 'hidden' if hash[:hidden]
item.display_name = hash[:display_name] if hash[:display_name]
item.save_without_broadcasting!
context.imported_migration_items << item if context.imported_migration_items
migration.add_imported_item(item) if migration
end
item
end

View File

@ -26,7 +26,7 @@ module Importers
events.each do |event|
if migration.import_object?("calendar_events", event['migration_id']) || migration.import_object?("events", event['migration_id'])
begin
import_from_migration(event, migration.context)
import_from_migration(event, migration.context, migration)
rescue
migration.add_import_warning(t('#migration.calendar_event_type', "Calendar Event"), event[:title], $!)
end
@ -34,7 +34,7 @@ module Importers
end
end
def self.import_from_migration(hash, context, item=nil)
def self.import_from_migration(hash, context, migration=nil, item=nil)
hash = hash.with_indifferent_access
return nil if hash[:migration_id] && hash[:events_to_import] && !hash[:events_to_import][hash[:migration_id]]
item ||= CalendarEvent.find_by_context_type_and_context_id_and_id(context.class.to_s, context.id, hash[:id])
@ -45,7 +45,7 @@ module Importers
item.workflow_state = 'active' if item.deleted?
item.title = hash[:title] || hash[:name]
hash[:missing_links] = []
item.description = ImportedHtmlConverter.convert(hash[:description] || "", context, {:missing_links => hash[:missing_links]})
item.description = ImportedHtmlConverter.convert(hash[:description] || "", context, migration, {:missing_links => hash[:missing_links]})
item.description += import_migration_attachment_suffix(hash, context)
item.start_at = Canvas::Migration::MigratorHelper.get_utc_time_from_timestamp(hash[:start_at] || hash[:start_date])
item.end_at = Canvas::Migration::MigratorHelper.get_utc_time_from_timestamp(hash[:end_at] || hash[:end_date])
@ -53,12 +53,12 @@ module Importers
item.imported = true
item.save_without_broadcasting!
if context.respond_to?(:content_migration) && context.content_migration
context.content_migration.add_missing_content_links(:class => item.class.to_s,
if migration
migration.add_missing_content_links(:class => item.class.to_s,
:id => item.id, :missing_links => hash[:missing_links],
:url => "/#{context.class.to_s.demodulize.underscore.pluralize}/#{context.id}/#{item.class.to_s.demodulize.underscore.pluralize}/#{item.id}")
end
context.imported_migration_items << item if context.imported_migration_items
migration.add_imported_item(item) if migration
if hash[:all_day]
item.all_day = hash[:all_day]
item.save

View File

@ -7,7 +7,7 @@ module Importers
tools = data['external_tools'] ? data['external_tools']: []
tools.each do |tool|
if migration.import_object?("context_external_tools", tool['migration_id']) || migration.import_object?("external_tools", tool['migration_id'])
item = import_from_migration(tool, migration.context)
item = import_from_migration(tool, migration.context, migration)
if item.consumer_key == 'fake' || item.shared_secret == 'fake'
migration.add_warning(t('external_tool_attention_needed', 'The security parameters for the external tool "%{tool_name}" need to be set in Course Settings.', :tool_name => item.name))
end
@ -15,7 +15,7 @@ module Importers
end
end
def self.import_from_migration(hash, context, item=nil)
def self.import_from_migration(hash, context, migration=nil, item=nil)
hash = hash.with_indifferent_access
return nil if hash[:migration_id] && hash[:external_tools_to_import] && !hash[:external_tools_to_import][hash[:migration_id]]
item ||= ContextExternalTool.find_by_context_id_and_context_type_and_migration_id(context.id, context.class.to_s, hash[:migration_id]) if hash[:migration_id]
@ -48,7 +48,7 @@ module Importers
end
item.save!
context.imported_migration_items << item if context.respond_to?(:imported_migration_items) && context.imported_migration_items && item.new_record?
migration.add_imported_item(item) if migration && item.new_record?
item
end
end

View File

@ -8,7 +8,7 @@ module Importers
modules.each do |mod|
if migration.import_object?("context_modules", mod['migration_id']) || migration.import_object?("modules", mod['migration_id'])
begin
self.import_from_migration(mod, migration.context)
self.import_from_migration(mod, migration.context, migration)
rescue
migration.add_import_warning(t('#migration.module_type', "Module"), mod[:title], $!)
end
@ -18,13 +18,13 @@ module Importers
migration.context.touch
end
def self.import_from_migration(hash, context, item=nil)
def self.import_from_migration(hash, context, migration=nil, item=nil)
hash = hash.with_indifferent_access
return nil if hash[:migration_id] && hash[:modules_to_import] && !hash[:modules_to_import][hash[:migration_id]]
item ||= ContextModule.find_by_context_type_and_context_id_and_id(context.class.to_s, context.id, hash[:id])
item ||= ContextModule.find_by_context_type_and_context_id_and_migration_id(context.class.to_s, context.id, hash[:migration_id]) if hash[:migration_id]
item ||= ContextModule.new(:context => context)
context.imported_migration_items << item if context.imported_migration_items && item.new_record?
migration.add_imported_item(item) if migration && item.new_record?
item.name = hash[:title] || hash[:description]
item.migration_id = hash[:migration_id]
if hash[:workflow_state] == 'unpublished'
@ -60,9 +60,9 @@ module Importers
@item_migration_position = item.content_tags.not_deleted.map(&:position).compact.max || 0
(hash[:items] || []).each do |tag_hash|
begin
self.add_module_item_from_migration(item, tag_hash, 0, context, item_map)
self.add_module_item_from_migration(item, tag_hash, 0, context, item_map, migration)
rescue
context.content_migration.add_import_warning(t(:migration_module_item_type, "Module Item"), tag_hash[:title], $!) if context.content_migration
migration.add_import_warning(t(:migration_module_item_type, "Module Item"), tag_hash[:title], $!) if migration
end
end
@ -85,7 +85,7 @@ module Importers
end
def self.add_module_item_from_migration(context_module, hash, level, context, item_map)
def self.add_module_item_from_migration(context_module, hash, level, context, item_map, migration=nil)
hash = hash.with_indifferent_access
hash[:migration_id] ||= hash[:item_migration_id]
hash[:migration_id] ||= Digest::MD5.hexdigest(hash[:title]) if hash[:title]
@ -97,7 +97,7 @@ module Importers
else
existing_item.workflow_state = 'active'
end
context.imported_migration_items << existing_item if context.imported_migration_items && existing_item.new_record?
migration.add_imported_item(existing_item) if migration && existing_item.new_record?
existing_item.migration_id = hash[:migration_id]
hash[:indent] = [hash[:indent] || 0, level].max
if hash[:linked_resource_type] =~ /wiki_type|wikipage/i
@ -203,7 +203,7 @@ module Importers
end
if hash[:sub_items]
hash[:sub_items].each do |tag_hash|
self.add_module_item_from_migration(context_module, tag_hash, level + 1, context, item_map)
self.add_module_item_from_migration(context_module, tag_hash, level + 1, context, item_map, migration)
end
end
item

View File

@ -2,6 +2,7 @@ module Importers
class CourseContentImporter < Importer
self.item_class = Course
Importers.register_content_importer(self)
def self.process_migration_files(course, data, migration)
return unless data['all_files_export'] && data['all_files_export']['file_path']
@ -69,14 +70,13 @@ module Importers
course.external_url_hash[link['link_id']] = link
end
ActiveRecord::Base.skip_touch_context
course.imported_migration_items = []
if !migration.for_course_copy?
# These only need to be processed once
Attachment.skip_media_object_creation do
self.process_migration_files(course, data, migration); migration.update_import_progress(18)
Importers::AttachmentImporter.process_migration(data, migration); migration.update_import_progress(20)
mo_attachments = course.imported_migration_items.find_all { |i| i.is_a?(Attachment) && i.media_entry_id.present? }
mo_attachments = migration.imported_migration_items.find_all { |i| i.is_a?(Attachment) && i.media_entry_id.present? }
begin
self.import_media_objects(mo_attachments, migration)
rescue => e
@ -138,7 +138,7 @@ module Importers
syllabus_should_be_added = everything_selected || migration.copy_options[:syllabus_body] || migration.copy_options[:all_syllabus_body]
if syllabus_should_be_added
syllabus_body = data[:course][:syllabus_body] if data[:course]
self.import_syllabus_from_migration(course, syllabus_body) if syllabus_body
self.import_syllabus_from_migration(course, syllabus_body, migration) if syllabus_body
end
migration.add_warnings_for_missing_content_links
@ -147,7 +147,7 @@ module Importers
#Adjust dates
if shift_options = migration.date_shift_options
shift_options = self.shift_date_options(course, shift_options)
course.imported_migration_items.each do |event|
migration.imported_migration_items.each do |event|
if event.is_a?(Assignment)
event.due_at = shift_date(event.due_at, shift_options)
event.lock_at = shift_date(event.lock_at, shift_options)
@ -183,7 +183,7 @@ module Importers
end
migration.progress=100
migration.migration_settings ||= {}
migration.migration_settings[:imported_assets] = course.imported_migration_items.map(&:asset_string)
migration.migration_settings[:imported_assets] = migration.imported_migration_items.map(&:asset_string)
migration.workflow_state = :imported
migration.save
ActiveRecord::Base.skip_touch_context(false)
@ -192,13 +192,14 @@ module Importers
else
course.touch
end
Auditors::Course.record_copied(migration.source_course, course, migration.user, source: migration.initiated_source)
course.imported_migration_items
migration.imported_migration_items
end
def self.import_syllabus_from_migration(course, syllabus_body)
def self.import_syllabus_from_migration(course, syllabus_body, migration=nil)
missing_links = []
course.syllabus_body = ImportedHtmlConverter.convert(syllabus_body, course, {:missing_links => missing_links})
course.syllabus_body = ImportedHtmlConverter.convert(syllabus_body, course, migration, {:missing_links => missing_links})
course.content_migration.add_missing_content_links(:class => course.class.to_s,
:id => course.id, :field => "syllabus", :missing_links => missing_links,
:url => "/#{course.class.to_s.underscore.pluralize}/#{course.id}/assignments/syllabus")

View File

@ -3,7 +3,7 @@ module Importers
self.item_class = DiscussionTopic
attr_accessor :options, :context, :item
attr_accessor :options, :context, :item, :migration
def self.process_migration(data, migration)
process_announcements_migration(Array(data['announcements']), migration)
@ -16,7 +16,7 @@ module Importers
event[:type] = 'announcement'
begin
self.import_from_migration(event, migration.context)
self.import_from_migration(event, migration.context, migration)
rescue
migration.add_import_warning(t('#migration.announcement_type', "Announcement"), event[:title], $!)
end
@ -32,7 +32,7 @@ module Importers
context ||= migration.context
next unless context && can_import_topic?(topic, migration)
begin
import_from_migration(topic.merge(topic_entries_to_import: topic_entries_to_import), context)
import_from_migration(topic.merge(topic_entries_to_import: topic_entries_to_import), context, migration)
rescue
migration.add_import_warning(t('#migration.discussion_topic_type', "Discussion Topic"), topic[:title], $!)
end
@ -46,14 +46,15 @@ module Importers
migration.import_object?('announcements', topic['migration_id']))
end
def self.import_from_migration(hash, context, item=nil)
importer = self.new(hash, context, item)
def self.import_from_migration(hash, context, migration=nil, item=nil)
importer = self.new(hash, context, migration, item)
importer.run
end
def initialize(hash, context, item)
def initialize(hash, context, migration, item)
self.options = DiscussionTopicOptions.new(hash)
self.context = context
self.migration = migration
self.item = find_or_create_topic(item)
end
@ -77,7 +78,7 @@ module Importers
:require_initial_post].each do |attr|
item.send("#{attr}=", options[attr])
end
item.message = options.message ? ImportedHtmlConverter.convert(options.message, context, missing_links: options[:missing_links]) : I18n.t('#discussion_topic.empty_message', 'No message')
item.message = options.message ? ImportedHtmlConverter.convert(options.message, context, migration, missing_links: options[:missing_links]) : I18n.t('#discussion_topic.empty_message', 'No message')
item.posted_at = Canvas::Migration::MigratorHelper.get_utc_time_from_timestamp(options[:posted_at])
item.delayed_post_at = Canvas::Migration::MigratorHelper.get_utc_time_from_timestamp(options.delayed_post_at)
item.last_reply_at = item.posted_at if item.new_record?
@ -102,25 +103,23 @@ module Importers
def fetch_assignment
return nil unless context.respond_to?(:assignments)
if options[:assignment]
Importers::AssignmentImporter.import_from_migration(options[:assignment], context)
Importers::AssignmentImporter.import_from_migration(options[:assignment], context, migration)
elsif options[:grading]
Importers::AssignmentImporter.import_from_migration({
grading: options[:grading], migration_id: options[:migration_id],
submission_format: 'discussion_topic', due_date: options.due_date,
title: options[:grading][:title]
}, context)
}, context, migration)
end
end
def import_migration_item
if context.respond_to?(:imported_migration_items)
Array(context.imported_migration_items) << item
end
migration.add_imported_item(item) if migration
end
def add_missing_content_links
if context.try_rescue(:content_migration)
context.content_migration.add_missing_content_links(class: item.class.to_s,
if migration
migration.add_missing_content_links(class: item.class.to_s,
id: item.id, missing_links: options[:missing_links],
url: "/#{context.class.to_s.underscore.pluralize}/#{context.id}/#{item.class.to_s.demodulize.underscore.pluralize}/#{item.id}")
end

View File

@ -9,7 +9,7 @@ module Importers
tools.each do |tool|
if tool['migration_id'] && (!to_import || to_import[tool['migration_id']])
begin
self.import_from_migration(tool, migration.context)
self.import_from_migration(tool, migration.context, migration)
rescue
migration.add_import_warning(t('#migration.external_feed_type', "External Feed"), tool[:title], $!)
end
@ -17,7 +17,7 @@ module Importers
end
end
def self.import_from_migration(hash, context, item=nil)
def self.import_from_migration(hash, context, migration=nil, item=nil)
hash = hash.with_indifferent_access
return nil if hash[:migration_id] && hash[:external_feeds_to_import] && !hash[:external_feeds_to_import][hash[:migration_id]]
item ||= ExternalFeed.find_by_context_id_and_context_type_and_migration_id(context.id, context.class.to_s, hash[:migration_id]) if hash[:migration_id]
@ -31,7 +31,7 @@ module Importers
item.header_match = hash[:header_match] unless hash[:header_match].blank?
item.save!
context.imported_migration_items << item if context.imported_migration_items && item.new_record?
migration.add_imported_item(item) if migration && item.new_record?
item
end
end

View File

@ -8,7 +8,7 @@ module Importers
standards.each do |standard|
if migration.import_object?('grading_standards', standard['migration_id'])
begin
self.import_from_migration(standard, migration.context)
self.import_from_migration(standard, migration.context, migration)
rescue
migration.add_import_warning(t('#migration.grading_standard_type', "Grading Standard"), standard[:title], $!)
end
@ -16,7 +16,7 @@ module Importers
end
end
def self.import_from_migration(hash, context, item=nil)
def self.import_from_migration(hash, context, migration=nil, item=nil)
hash = hash.with_indifferent_access
return nil if hash[:migration_id] && hash[:grading_standards_to_import] && !hash[:grading_standards_to_import][hash[:migration_id]]
item ||= GradingStandard.find_by_context_id_and_context_type_and_migration_id(context.id, context.class.to_s, hash[:migration_id]) if hash[:migration_id]
@ -31,7 +31,7 @@ module Importers
end
item.save!
context.imported_migration_items << item if context.imported_migration_items && item.new_record?
migration.add_imported_item(item) if migration && item.new_record?
item
end
end

View File

@ -8,7 +8,7 @@ module Importers
groups.each do |group|
if migration.import_object?("groups", group['migration_id'])
begin
self.import_from_migration(group, migration.context)
self.import_from_migration(group, migration.context, migration)
rescue
migration.add_import_warning(t('#migration.group_type', "Group"), group[:title], $!)
end
@ -16,13 +16,13 @@ module Importers
end
end
def self.import_from_migration(hash, context, item=nil)
def self.import_from_migration(hash, context, migration=nil, item=nil)
hash = hash.with_indifferent_access
return nil if hash[:migration_id] && hash[:groups_to_import] && !hash[:groups_to_import][hash[:migration_id]]
item ||= Group.find_by_context_id_and_context_type_and_id(context.id, context.class.to_s, hash[:id])
item ||= Group.find_by_context_id_and_context_type_and_migration_id(context.id, context.class.to_s, hash[:migration_id]) if hash[:migration_id]
item ||= context.groups.new
context.imported_migration_items << item if context.imported_migration_items && item.new_record?
migration.add_imported_item(item) if migration && item.new_record?
item.migration_id = hash[:migration_id]
item.name = hash[:title]
item.group_category = hash[:group_category].present? ?
@ -30,7 +30,7 @@ module Importers
GroupCategory.imported_for(context)
item.save!
context.imported_migration_items << item
migration.add_imported_item(item) if migration
item
end
end

View File

@ -1,4 +1,15 @@
module Importers
def self.register_content_importer(klass)
@content_importers ||= {}
@content_importers[klass.item_class.to_s] = klass
end
def self.content_importer_for(context_type)
klass = @content_importers[context_type]
raise "No content importer registered for #{context_type}" unless klass
klass
end
class Importer
class << self
attr_accessor :item_class

View File

@ -36,7 +36,7 @@ module Importers
root_outcome_group.adopt_outcome_group(item)
end
context.imported_migration_items << item if context && context.imported_migration_items && item.new_record?
migration.add_imported_item(item) if migration && item.new_record?
if hash[:outcomes]
hash[:outcomes].each do |child|

View File

@ -76,7 +76,7 @@ module Importers
end
item.save!
context.imported_migration_items << item if context && context.imported_migration_items && item.new_record?
migration.add_imported_item(item) if migration && item.new_record?
else
item = outcome
end

View File

@ -49,18 +49,18 @@ module Importers
if qq[:assessment_question_migration_id]
if aq = question_data[:aq_data][qq[:assessment_question_migration_id]]
qq['assessment_question_id'] = aq['assessment_question_id']
aq_hash = Importers::AssessmentQuestionImporter.prep_for_import(qq, context)
Quizzes::QuizQuestion.import_from_migration(aq_hash, context, quiz, item)
aq_hash = Importers::AssessmentQuestionImporter.prep_for_import(qq, context, migration)
Importers::QuizQuestionImporter.import_from_migration(aq_hash, context, migration, quiz, item)
else
aq_hash = Importers::AssessmentQuestionImporter.import_from_migration(qq, context)
aq_hash = Importers::AssessmentQuestionImporter.import_from_migration(qq, context, migration)
qq['assessment_question_id'] = aq_hash['assessment_question_id']
Quizzes::QuizQuestion.import_from_migration(aq_hash, context, quiz, item)
Importers::QuizQuestionImporter.import_from_migration(aq_hash, context, migration, quiz, item)
end
end
elsif aq = question_data[:aq_data][question[:migration_id]]
aq[:points_possible] = question[:points_possible] if question[:points_possible]
aq[:position] = i + 1
Quizzes::QuizQuestion.import_from_migration(aq, context, quiz, item)
Importers::QuizQuestionImporter.import_from_migration(aq, context, migration, quiz, item)
end
end

View File

@ -42,8 +42,7 @@ module Importers
end
end
begin
assessment[:migration] = migration
Importers::QuizImporter.import_from_migration(assessment, migration.context, question_data, nil, allow_update)
Importers::QuizImporter.import_from_migration(assessment, migration.context, migration, question_data, nil, allow_update)
rescue
migration.add_import_warning(t('#migration.quiz_type', "Quiz"), assessment[:title], $!)
end
@ -53,7 +52,7 @@ module Importers
# Import a quiz from a hash.
# It assumes that all the referenced questions are already in the database
def self.import_from_migration(hash, context, question_data, item=nil, allow_update = false)
def self.import_from_migration(hash, context, migration=nil, question_data=nil, item=nil, allow_update = false)
hash = hash.with_indifferent_access
# there might not be an import id if it's just a text-only type...
item ||= Quizzes::Quiz.find_by_context_type_and_context_id_and_id(context.class.to_s, context.id, hash[:id]) if hash[:id]
@ -75,7 +74,7 @@ module Importers
item.hide_correct_answers_at = Canvas::Migration::MigratorHelper.get_utc_time_from_timestamp(hash[:hide_correct_answers_at]) if hash[:hide_correct_answers_at]
item.scoring_policy = hash[:which_attempt_to_keep] if hash[:which_attempt_to_keep]
hash[:missing_links] = []
item.description = ImportedHtmlConverter.convert(hash[:description], context, {:missing_links => hash[:missing_links]})
item.description = ImportedHtmlConverter.convert(hash[:description], context, migration, {:missing_links => hash[:missing_links]})
%w[
@ -106,8 +105,8 @@ module Importers
item.save!
if context.respond_to?(:content_migration) && context.content_migration
context.content_migration.add_missing_content_links(
if migration
migration.add_missing_content_links(
:class => item.class.to_s,
:id => item.id, :missing_links => hash[:missing_links],
:url => "/#{context.class.to_s.demodulize.underscore.pluralize}/#{context.id}/#{item.class.to_s.demodulize.underscore.pluralize}/#{item.id}"
@ -143,21 +142,21 @@ module Importers
if qq[:assessment_question_migration_id]
if aq = question_data[:aq_data][qq[:assessment_question_migration_id]]
qq['assessment_question_id'] = aq['assessment_question_id']
aq_hash = ::Importers::AssessmentQuestionImporter.prep_for_import(qq, context)
Quizzes::QuizQuestion.import_from_migration(aq_hash, context, item)
aq_hash = ::Importers::AssessmentQuestionImporter.prep_for_import(qq, context, migration)
Importers::QuizQuestionImporter.import_from_migration(aq_hash, context, migration, item)
else
aq_hash = ::Importers::AssessmentQuestionImporter.import_from_migration(qq, context)
aq_hash = ::Importers::AssessmentQuestionImporter.import_from_migration(qq, context, migration)
qq['assessment_question_id'] = aq_hash['assessment_question_id']
Quizzes::QuizQuestion.import_from_migration(aq_hash, context, item)
Importers::QuizQuestionImporter.import_from_migration(aq_hash, context, migration, item)
end
end
elsif aq = question_data[:aq_data][question[:migration_id]]
aq[:position] = i + 1
aq[:points_possible] = question[:points_possible] if question[:points_possible]
Quizzes::QuizQuestion.import_from_migration(aq, context, item)
Importers::QuizQuestionImporter.import_from_migration(aq, context, migration, item)
end
when "question_group"
Importers::QuizGroupImporter.import_from_migration(question, context, item, question_data, i + 1, hash[:migration])
Importers::QuizGroupImporter.import_from_migration(question, context, item, question_data, i + 1, migration)
when "text_only_question"
qq = item.quiz_questions.new
qq.question_data = question
@ -175,7 +174,7 @@ module Importers
item.assignment = nil if item.assignment && item.assignment.quiz && item.assignment.quiz.id != item.id
item.assignment ||= context.assignments.new
item.assignment = ::Importers::AssignmentImporter.import_from_migration(hash[:assignment], context, item.assignment, item)
item.assignment = ::Importers::AssignmentImporter.import_from_migration(hash[:assignment], context, migration, item.assignment, item)
if !hash[:available]
item.workflow_state = 'unpublished'
@ -202,7 +201,7 @@ module Importers
item.save
item.assignment.save if item.assignment && item.assignment.changed?
context.imported_migration_items << item if context.imported_migration_items
migration.add_imported_item(item) if migration
item
end

View File

@ -0,0 +1,28 @@
module Importers
class QuizQuestionImporter < Importer
self.item_class = Quizzes::QuizQuestion
def self.import_from_migration(hash, context, migration=nil, quiz=nil, quiz_group=nil)
unless hash[:prepped_for_import]
Importers::AssessmentQuestionImporter.prep_for_import(hash, context, migration)
end
position = hash[:position] && hash[:position].to_i
if id = hash['quiz_question_id']
Quizzes::QuizQuestion.where(id: id).update_all(quiz_group_id: quiz_group,
assessment_question_id: hash['assessment_question_id'], question_data: hash.to_yaml,
created_at: Time.now.utc, updated_at: Time.now.utc, migration_id: hash[:migration_id],
position: position)
else
query = self.item_class.send(:sanitize_sql, [<<-SQL, quiz && quiz.id, quiz_group && quiz_group.id, hash['assessment_question_id'], hash.to_yaml, Time.now.utc, Time.now.utc, hash[:migration_id], position])
INSERT INTO quiz_questions (quiz_id, quiz_group_id, assessment_question_id, question_data, created_at, updated_at, migration_id, position)
VALUES (?,?,?,?,?,?,?,?)
SQL
self.item_class.connection.insert(query, "#{self.item_class.name} Create",
self.item_class.primary_key, nil, self.item_class.sequence_name)
end
hash
end
end
end

View File

@ -60,7 +60,7 @@ module Importers
end
end
context.imported_migration_items << item if context.imported_migration_items && item.new_record?
migration.add_imported_item(item) if migration && item.new_record?
item.save!
end

View File

@ -10,7 +10,7 @@ module Importers
outline['root_folder'] = true
begin
self.import_from_migration(outline.merge({:outline_folders_to_import => to_import}), migration.context)
self.import_from_migration(outline.merge({:outline_folders_to_import => to_import}), migration.context, migration)
rescue
migration.add_warning("Error importing the course outline.", $!)
end
@ -25,14 +25,14 @@ module Importers
end
next unless migration.import_object?("wiki_pages", wiki['migration_id']) || migration.import_object?("wikis", wiki['migration_id'])
begin
self.import_from_migration(wiki, migration.context) if wiki
self.import_from_migration(wiki, migration.context, migration) if wiki
rescue
migration.add_import_warning(t('#migration.wiki_page_type', "Wiki Page"), wiki[:title], $!)
end
end
end
def self.import_from_migration(hash, context, item=nil)
def self.import_from_migration(hash, context, migration=nil, item=nil)
hash = hash.with_indifferent_access
item ||= WikiPage.find_by_wiki_id_and_id(context.wiki.id, hash[:id])
item ||= WikiPage.find_by_wiki_id_and_migration_id(context.wiki.id, hash[:migration_id])
@ -61,14 +61,16 @@ module Importers
item.workflow_state = 'unpublished'
end
end
item.set_as_front_page! if !!hash[:front_page] && context.wiki.has_no_front_page
context.imported_migration_items << item if context.imported_migration_items && item.new_record?
migration.add_imported_item(item) if migration && item.new_record?
item.migration_id = hash[:migration_id]
(hash[:contents] || []).each do |sub_item|
next if sub_item[:type] == 'embedded_content'
Importers::WikiPageImporter.import_from_migration(sub_item.merge({
:outline_folders_to_import => hash[:outline_folders_to_import]
}), context)
}), context, migration)
end
return if hash[:type] && ['folder', 'FOLDER_TYPE'].member?(hash[:type]) && hash[:linked_resource_id]
hash[:missing_links] = {}
@ -80,10 +82,10 @@ module Importers
description = ""
if hash[:header]
hash[:missing_links][:field] = []
description += hash[:header][:is_html] ? ImportedHtmlConverter.convert(hash[:header][:body] || "", context, {:missing_links => hash[:missing_links][:header]}) : ImportedHtmlConverter.convert_text(hash[:header][:body] || [""], context)
description += hash[:header][:is_html] ? ImportedHtmlConverter.convert(hash[:header][:body] || "", context, migration, {:missing_links => hash[:missing_links][:header]}) : ImportedHtmlConverter.convert_text(hash[:header][:body] || [""], context)
end
hash[:missing_links][:description] = []
description += ImportedHtmlConverter.convert(hash[:description], context, {:missing_links => hash[:missing_links][:description]}) if hash[:description]
description += ImportedHtmlConverter.convert(hash[:description], context, migration, {:missing_links => hash[:missing_links][:description]}) if hash[:description]
contents = ""
allow_save = false if hash[:migration_id] && hash[:outline_folders_to_import] && !hash[:outline_folders_to_import][hash[:migration_id]]
hash[:contents].each do |sub_item|
@ -98,7 +100,7 @@ module Importers
end
description += "\n<h2>#{sub_item[:title]}</h2>\n" if sub_item[:title]
hash[:missing_links][:sub_item] = []
description += ImportedHtmlConverter.convert(sub_item[:description], context, {:missing_links => hash[:missing_links][:sub_item]}) if sub_item[:description]
description += ImportedHtmlConverter.convert(sub_item[:description], context, migration, {:missing_links => hash[:missing_links][:sub_item]}) if sub_item[:description]
elsif sub_item[:type] == 'linked_resource'
case sub_item[:linked_resource_type]
when 'TOC_TYPE'
@ -131,7 +133,7 @@ module Importers
description += "<ul>\n#{contents}\n</ul>" if contents && contents.length > 0
if hash[:footer]
hash[:missing_links][:footer] = []
description += hash[:footer][:is_html] ? ImportedHtmlConverter.convert(hash[:footer][:body] || "", context, {:missing_links => hash[:missing_links][:footer]}) : ImportedHtmlConverter.convert_text(hash[:footer][:body] || [""], context)
description += hash[:footer][:is_html] ? ImportedHtmlConverter.convert(hash[:footer][:body] || "", context, migration, {:missing_links => hash[:missing_links][:footer]}) : ImportedHtmlConverter.convert_text(hash[:footer][:body] || [""], context)
end
item.body = description
allow_save = false if !description || description.empty?
@ -145,7 +147,7 @@ module Importers
topic = Importers::DiscussionTopicImporter.import_from_migration(topic.merge({
:topics_to_import => hash[:topics_to_import],
:topic_entries_to_import => hash[:topic_entries_to_import]
}), context)
}), context, migration)
if topic
topic_count += 1
description += " <li><a href='/#{context.class.to_s.downcase.pluralize}/#{context.id}/discussion_topics/#{topic.id}'>#{topic.title}</a></li>\n"
@ -158,13 +160,13 @@ module Importers
#it's an actual wiki page
item.title = hash[:title].presence || item.url.presence || "unnamed page"
if item.title.length > WikiPage::TITLE_LENGTH
if context.respond_to?(:content_migration) && context.content_migration
context.content_migration.add_warning(t('warnings.truncated_wiki_title', "The title of the following wiki page was truncated: %{title}", :title => item.title))
if migration
migration.add_warning(t('warnings.truncated_wiki_title', "The title of the following wiki page was truncated: %{title}", :title => item.title))
end
item.title.splice!(0...WikiPage::TITLE_LENGTH) # truncate too-long titles
end
hash[:missing_links][:body] = []
item.body = ImportedHtmlConverter.convert(hash[:text] || "", context, {:missing_links => hash[:missing_links][:body]})
item.body = ImportedHtmlConverter.convert(hash[:text] || "", context, migration, {:missing_links => hash[:missing_links][:body]})
item.editing_roles = hash[:editing_roles] if hash[:editing_roles].present?
item.notify_of_update = hash[:notify_of_update] if !hash[:notify_of_update].nil?
else
@ -172,10 +174,10 @@ module Importers
end
if allow_save && hash[:migration_id]
item.save_without_broadcasting!
context.imported_migration_items << item if context.imported_migration_items
if context.respond_to?(:content_migration) && context.content_migration
migration.add_imported_item(item) if migration
if migration
hash[:missing_links].each do |field, missing_links|
context.content_migration.add_missing_content_links(:class => item.class.to_s,
migration.add_missing_content_links(:class => item.class.to_s,
:id => item.id, :field => field, :missing_links => missing_links,
:url => "/#{context.class.to_s.underscore.pluralize}/#{context.id}/wiki/#{item.url}")
end

View File

@ -213,11 +213,4 @@ class LearningOutcome < ActiveRecord::Base
scope :global, where(:context_id => nil)
def self.process_migration(*args)
Importers::LearningOutcomeImporter.process_migration(*args)
end
def self.import_from_migration(*args)
Importers::LearningOutcomeImporter.import_from_migration(*args)
end
end

View File

@ -178,28 +178,6 @@ class Quizzes::QuizQuestion < ActiveRecord::Base
where(:id => questions).update_all(set)
end
def self.import_from_migration(hash, context, quiz=nil, quiz_group=nil)
unless hash[:prepped_for_import]
Importers::AssessmentQuestionImporter.prep_for_import(hash, context)
end
position = hash[:position] && hash[:position].to_i
if id = hash['quiz_question_id']
Quizzes::QuizQuestion.where(id: id).update_all(quiz_group_id: quiz_group,
assessment_question_id: hash['assessment_question_id'], question_data: hash.to_yaml,
created_at: Time.now.utc, updated_at: Time.now.utc, migration_id: hash[:migration_id],
position: position)
else
query = sanitize_sql([<<-SQL, quiz && quiz.id, quiz_group && quiz_group.id, hash['assessment_question_id'], hash.to_yaml, Time.now.utc, Time.now.utc, hash[:migration_id], position])
INSERT INTO quiz_questions (quiz_id, quiz_group_id, assessment_question_id, question_data, created_at, updated_at, migration_id, position)
VALUES (?,?,?,?,?,?,?,?)
SQL
self.connection.insert(query, "#{name} Create",
primary_key, nil, sequence_name)
end
hash
end
def migrate_file_links
Quizzes::QuizQuestionLinkMigrator.migrate_file_links_in_question(self)
end

View File

@ -264,12 +264,4 @@ class Rubric < ActiveRecord::Base
def update_assessments_for_new_criteria(new_criteria)
criteria = self.data
end
def self.process_migration(*args)
Importers::RubricImporter.process_migration(*args)
end
def self.import_from_migration(*args)
Importers::RubricImporter.import_from_migration(*args)
end
end

View File

@ -433,12 +433,4 @@ class WikiPage < ActiveRecord::Base
self.workflow_state = 'active'
end
end
def self.process_migration(*args)
Importers::WikiPageImporter.process_migration(*args)
end
def self.import_from_migration(*args)
Importers::WikiPageImporter.import_from_migration(*args)
end
end

View File

@ -946,7 +946,7 @@ routes.draw do
end
scope(:controller => :content_migrations) do
%w(course group user).each do |context|
%w(account course group user).each do |context|
get "#{context.pluralize}/:#{context}_id/content_migrations/migrators", :action => :available_migrators, :path_name => "#{context}_content_migration_migrators_list"
get "#{context.pluralize}/:#{context}_id/content_migrations/:id", :action => :show, :path_name => "#{context}_content_migration"
get "#{context.pluralize}/:#{context}_id/content_migrations", :action => :index, :path_name => "#{context}_content_migration_list"
@ -957,7 +957,7 @@ routes.draw do
end
scope(:controller => :migration_issues) do
%w(course group user).each do |context|
%w(account course group user).each do |context|
get "#{context.pluralize}/:#{context}_id/content_migrations/:content_migration_id/migration_issues/:id", :action => :show, :path_name => "#{context}_content_migration_migration_issue"
get "#{context.pluralize}/:#{context}_id/content_migrations/:content_migration_id/migration_issues", :action => :index, :path_name => "#{context}_content_migration_migration_issue_list"
post "#{context.pluralize}/:#{context}_id/content_migrations/:content_migration_id/migration_issues", :action => :create, :path_name => "#{context}_content_migration_migration_issue_create"

View File

@ -118,7 +118,8 @@ Canvas::Plugin.register 'canvas_cartridge_importer', :export_system, {
:worker => 'CCWorker',
:migration_partial => 'canvas_config',
:requires_file_upload => true,
:provides =>{:canvas_cartridge => CC::Importer::Canvas::Converter}
:provides =>{:canvas_cartridge => CC::Importer::Canvas::Converter},
:valid_contexts => %w{Account Course}
},
}
require_dependency 'canvas/migration/worker/course_copy_worker'
@ -136,7 +137,8 @@ Canvas::Plugin.register 'course_copy_importer', :export_system, {
:requires_file_upload => false,
:skip_conversion_step => true,
:required_options_validator => Canvas::Migration::Validators::CourseCopyValidator,
:required_settings => [:source_course_id]
:required_settings => [:source_course_id],
:valid_contexts => %w{Course}
},
}
require_dependency 'canvas/migration/worker/zip_file_worker'
@ -155,7 +157,7 @@ Canvas::Plugin.register 'zip_file_importer', :export_system, {
:no_selective_import => true,
:required_options_validator => Canvas::Migration::Validators::ZipImporterValidator,
:required_settings => [:source_folder_id],
:additional_contexts => %w(User Group)
:valid_contexts => %w(Course Group User)
},
}
Canvas::Plugin.register 'common_cartridge_importer', :export_system, {
@ -174,6 +176,7 @@ Canvas::Plugin.register 'common_cartridge_importer', :export_system, {
:common_cartridge_1_0=>CC::Importer::Standard::Converter,
:common_cartridge_1_1=>CC::Importer::Standard::Converter,
:common_cartridge_1_2=>CC::Importer::Standard::Converter},
:valid_contexts => %w{Account Course}
},
}
Canvas::Plugin.register('grade_export', :sis, {

View File

@ -22,17 +22,17 @@ class ImportedHtmlConverter
CONTAINER_TYPES = ['div', 'p', 'body']
def self.convert(html, context, opts={})
def self.convert(html, context, migration=nil, opts={})
doc = Nokogiri::HTML(html || "")
attrs = ['rel', 'href', 'src', 'data', 'value']
course_path = "/#{context.class.to_s.underscore.pluralize}/#{context.id}"
for_course_copy = false
domain_substitution_map = {}
if context.respond_to?(:content_migration) && context.content_migration
for_course_copy = true if context.content_migration.for_course_copy?
if migration
for_course_copy = true if migration.for_course_copy?
if ds_map = context.content_migration.migration_settings[:domain_substitution_map]
if ds_map = migration.migration_settings[:domain_substitution_map]
ds_map.each{|k, v| domain_substitution_map[k.to_s] = v.to_s } # ensure strings
end
end

View File

@ -99,6 +99,25 @@ describe ContentMigrationsController, type: :request do
json.first['id'].should == @migration.id
end
end
context "Account" do
before do
@account = Account.create!(:name => 'name')
@account.add_user(@user)
@migration = @account.content_migrations.create
@migration.migration_type = 'qti_converter'
@migration.user = @user
@migration.save!
@migration_url = "/api/v1/accounts/#{@account.id}/content_migrations"
@params = @params.reject{ |k| k == :course_id }.merge(account_id: @account.id)
end
it "should return list" do
json = api_call(:get, @migration_url, @params)
json.length.should == 1
json.first['id'].should == @migration.id
end
end
end
describe 'show' do
@ -215,10 +234,26 @@ describe ContentMigrationsController, type: :request do
end
end
context "Account" do
before do
@account = Account.create!(:name => 'name')
@account.add_user(@user)
@migration = @account.content_migrations.create
@migration.migration_type = 'qti_converter'
@migration.user = @user
@migration.save!
@migration_url = "/api/v1/accounts/#{@account.id}/content_migrations/#{@migration.id}"
@params = @params.reject{ |k| k == :course_id }.merge(account_id: @account.id, id: @migration.to_param)
end
it "should return migration" do
json = api_call(:get, @migration_url, @params)
json['id'].should == @migration.id
end
end
end
describe 'create' do
before do
@params = {:controller => 'content_migrations', :format => 'json', :course_id => @course.id.to_param, :action => 'create'}
@post_params = {:migration_type => 'common_cartridge_importer', :pre_attachment => {:name => "test.zip"}}
@ -232,7 +267,7 @@ describe ContentMigrationsController, type: :request do
it "should queue a migration" do
@post_params.delete :pre_attachment
p = Canvas::Plugin.new("hi")
p.stubs(:settings).returns('worker' => 'CCWorker')
p.stubs(:settings).returns({'worker' => 'CCWorker', 'valid_contexts' => ['Course']}.with_indifferent_access)
Canvas::Plugin.stubs(:find).returns(p)
json = api_call(:post, @migration_url, @params, @post_params)
json["workflow_state"].should == 'running'
@ -243,7 +278,9 @@ describe ContentMigrationsController, type: :request do
it "should not queue a migration if do_not_run flag is set" do
@post_params.delete :pre_attachment
Canvas::Plugin.stubs(:find).returns(Canvas::Plugin.new("oi"))
p = Canvas::Plugin.new("hi")
p.stubs(:settings).returns({'worker' => 'CCWorker', 'valid_contexts' => ['Course']}.with_indifferent_access)
Canvas::Plugin.stubs(:find).returns(p)
json = api_call(:post, @migration_url, @params, @post_params.merge(:do_not_run => true))
json["workflow_state"].should == 'pre_processing'
migration = ContentMigration.find json['id']
@ -403,6 +440,23 @@ describe ContentMigrationsController, type: :request do
migration.context.should eql @group
end
end
context "Account" do
before do
@account = Account.create!(:name => 'migration account')
@account.add_user(@user)
@migration_url = "/api/v1/accounts/#{@account.id}/content_migrations"
@params = @params.reject{|k| k == :course_id}.merge(:account_id => @account.to_param)
end
it "should queue a migration" do
json = api_call(:post, @migration_url, @params,
{ :migration_type => 'qti_converter',
:settings => { :file_url => 'http://example.com/oi.zip' }})
migration = ContentMigration.find json['id']
migration.context.should eql @account
end
end
end
describe 'update' do

BIN
spec/fixtures/migration/quiz_qti.zip vendored Normal file

Binary file not shown.

View File

@ -63,7 +63,6 @@ end
def get_import_context(system=nil)
context = course_model
context.imported_migration_items = []
context.import_source == :webct if system == 'vista'
context

View File

@ -966,7 +966,7 @@ XML
ag.migration_id = "i713e960ab2685259505efeb08cd48a1d"
ag.save!
Importers::QuizImporter.import_from_migration(quiz_hash, @copy_to, {})
Importers::QuizImporter.import_from_migration(quiz_hash, @copy_to, nil, {})
q = @copy_to.quizzes.find_by_migration_id("ie3d8f8adfad423eb225229c539cdc450")
a = q.assignment
a.assignment_group.id.should == ag.id

View File

@ -417,6 +417,7 @@ describe "More Standard Common Cartridge importing" do
@migration.stubs(:to_import).returns(nil)
@migration.stubs(:context).returns(@copy_to)
@migration.stubs(:import_object?).returns(true)
@migration.stubs(:add_imported_item)
end
it "should properly handle top-level resource references" do

View File

@ -2411,4 +2411,73 @@ equation: <img class="equation_image" title="Log_216" src="/equation_images/Log_
worker.perform(cm)
end
context "account-level import" do
it "should import question banks from qti migrations" do
pending unless Qti.qti_enabled?
account = Account.create!(:name => 'account')
@user = user
account.add_user(@user)
cm = ContentMigration.new(:context => account, :user => @user)
cm.migration_type = 'qti_converter'
cm.migration_settings['import_immediately'] = true
qb_name = 'Import Unfiled Questions Into Me'
cm.migration_settings['question_bank_name'] = qb_name
cm.save!
package_path = File.join(File.dirname(__FILE__) + "/../fixtures/migration/cc_default_qb_test.zip")
attachment = Attachment.new
attachment.context = cm
attachment.uploaded_data = File.open(package_path, 'rb')
attachment.filename = 'file.zip'
attachment.save!
cm.attachment = attachment
cm.save!
cm.queue_migration
run_jobs
cm.migration_issues.should be_empty
account.assessment_question_banks.count.should == 1
bank = account.assessment_question_banks.first
bank.title.should == qb_name
bank.assessment_questions.count.should == 1
end
it "should import questions from quizzes into question banks" do
pending unless Qti.qti_enabled?
account = Account.create!(:name => 'account')
@user = user
account.add_user(@user)
cm = ContentMigration.new(:context => account, :user => @user)
cm.migration_type = 'qti_converter'
cm.migration_settings['import_immediately'] = true
cm.save!
package_path = File.join(File.dirname(__FILE__) + "/../fixtures/migration/quiz_qti.zip")
attachment = Attachment.new
attachment.context = cm
attachment.uploaded_data = File.open(package_path, 'rb')
attachment.filename = 'file.zip'
attachment.save!
cm.attachment = attachment
cm.save!
cm.queue_migration
run_jobs
cm.migration_issues.should be_empty
account.assessment_question_banks.count.should == 1
bank = account.assessment_question_banks.first
bank.title.should == "Unnamed Quiz"
bank.assessment_questions.count.should == 1
end
end
end

View File

@ -689,20 +689,4 @@ describe ContextExternalTool do
expect { ContextExternalTool.find_for("horseshoes", @course, :course_navigation) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
describe "import_from_migration" do
it "should work for course-level tools" do
course_model
tool = Importers::ContextExternalToolImporter.import_from_migration({:title => 'tool', :url => 'http://example.com'}, @course)
tool.should_not be_nil
tool.context.should == @course
end
it "should work for account-level tools" do
course_model
tool = Importers::ContextExternalToolImporter.import_from_migration({:title => 'tool', :url => 'http://example.com'}, @course.account)
tool.should_not be_nil
tool.context.should == @course.account
end
end
end

View File

@ -427,24 +427,6 @@ describe Group do
group.group_category.should == group_category
end
context "import_from_migration" do
it "should respect group_category from the hash" do
course_with_teacher
group = @course.groups.build
@course.imported_migration_items = []
Importers::GroupImporter.import_from_migration({:group_category => "random category"}, @course, group)
group.group_category.name.should == "random category"
end
it "should default group_category to imported if not in the hash" do
course_with_teacher
group = @course.groups.build
@course.imported_migration_items = []
Importers::GroupImporter.import_from_migration({}, @course, group)
group.group_category.should == GroupCategory.imported_for(@course)
end
end
it "as_json should include group_category" do
course()
gc = group_category(name: "Something")

View File

@ -32,7 +32,6 @@ describe "Assessment Question import from hash" do
it "should only import assessment question once" do
context = get_import_context
context.imported_migration_items ||= []
data = get_import_data '', 'single_question'
data = {'assessment_questions'=>{'assessment_questions'=>data}}
@ -49,7 +48,6 @@ describe "Assessment Question import from hash" do
it "should update assessment question on re-import" do
context = get_import_context
context.imported_migration_items ||= []
data = get_import_data '', 'single_question'
data = {'assessment_questions'=>{'assessment_questions'=>data}}
@ -138,7 +136,7 @@ describe "Assessment Question import from hash" do
question_data[question[:migration_id]] = context.assessment_questions.find_by_migration_id(question[:migration_id])
quiz = get_import_data 'cengage', 'quiz'
Importers::QuizImporter.import_from_migration(quiz, context, question_data)
Importers::QuizImporter.import_from_migration(quiz, context, nil, question_data)
quiz = context.quizzes.find_by_migration_id(quiz[:migration_id])
group = quiz.quiz_groups.first

View File

@ -30,7 +30,6 @@ module Importers
let(:attachment) { stub(:context= => true, :migration_id= => true, :save_without_broadcasting! => true) }
before :each do
course.imported_migration_items = []
course.stubs(:id).returns(course_id)
migration.stubs(:import_object?).with('attachments', migration_id).returns(true)
end
@ -56,7 +55,7 @@ module Importers
Importers::AttachmentImporter.process_migration(data, migration)
course.imported_migration_items.should == [attachment]
migration.imported_migration_items.should == [attachment]
end
it "imports attachments when the migration id is in the files_to_import hash" do
@ -136,26 +135,6 @@ module Importers
Importers::AttachmentImporter.process_migration(data, migration)
end
it "does not add to the imported_migration_items if imported_migration_items is nil" do
data = {
'file_map' => {
'a' => {
id: attachment_id,
migration_id: migration_id
}
}
}
course.imported_migration_items = nil
::Attachment.expects(:find_by_context_type_and_context_id_and_id).with("Course", course_id, attachment_id).returns(attachment)
migration.expects(:import_object?).with('attachments', migration_id).returns(true)
attachment.expects(:save_without_broadcasting!)
Importers::AttachmentImporter.process_migration(data, migration)
course.imported_migration_items.should == nil
end
it "does not import files that are not part of the migration" do
data = {
'file_map' => {

View File

@ -72,7 +72,7 @@ describe Importers::CalendarEventImporter do
it 'initializes a calendar event based on hash data' do
event = migration_course.calendar_events.build
hash = {
migration_id: 42,
migration_id: '42',
title: 'event title',
description: 'the event description',
start_at: Time.now,
@ -80,10 +80,10 @@ describe Importers::CalendarEventImporter do
attachment_type: 'external_url',
attachment_value: 'http://example.com'
}
Importers::CalendarEventImporter.import_from_migration(hash, migration_course, event)
Importers::CalendarEventImporter.import_from_migration(hash, migration_course, nil, event)
event.should_not be_new_record
event.imported.should be_true
event.migration_id.should == 42
event.migration_id.should == '42'
event.title.should == 'event title'
event.description.should match('the event description')
event.description.should match('example.com')

View File

@ -0,0 +1,17 @@
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
describe Importers::ContextExternalToolImporter do
it "should work for course-level tools" do
course_model
tool = Importers::ContextExternalToolImporter.import_from_migration({:title => 'tool', :url => 'http://example.com'}, @course)
tool.should_not be_nil
tool.context.should == @course
end
it "should work for account-level tools" do
course_model
tool = Importers::ContextExternalToolImporter.import_from_migration({:title => 'tool', :url => 'http://example.com'}, @course.account)
tool.should_not be_nil
tool.context.should == @course.account
end
end

View File

@ -308,7 +308,7 @@ describe Course do
Auditors::Course.expects(:record_copied).once.with(migration.source_course, @course, migration.user, source: migration.initiated_source)
@course.import_from_migration(data, params, migration)
Importers::CourseContentImporter.import_content(@course, data, params, migration)
end
end
end

View File

@ -62,4 +62,17 @@ describe "Importing Groups" do
group.discussion_topics.count.should == 1
end
it "should respect group_category from the hash" do
course_with_teacher
group = @course.groups.build
Importers::GroupImporter.import_from_migration({:group_category => "random category"}, @course, nil, group)
group.group_category.name.should == "random category"
end
it "should default group_category to imported if not in the hash" do
course_with_teacher
group = @course.groups.build
Importers::GroupImporter.import_from_migration({}, @course, nil, group)
group.group_category.should == GroupCategory.imported_for(@course)
end
end

View File

@ -27,6 +27,7 @@ describe "Importing Rubrics" do
context = get_import_context(system)
migration = stub()
migration.stubs(:context).returns(context)
migration.stubs(:add_imported_item)
data[:rubrics_to_import] = {}
Importers::RubricImporter.import_from_migration(data, migration).should be_nil

View File

@ -27,7 +27,7 @@ describe "Importers::QuizImporter" do
context = course_model
question_data = import_example_questions context
data = get_import_data ['vista', 'quiz'], 'simple_quiz_data'
Importers::QuizImporter.import_from_migration(data, context, question_data)
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
quiz = Quizzes::Quiz.find_by_migration_id data[:migration_id]
quiz.title.should == data[:title]
quiz.scoring_policy.should == data[:which_attempt_to_keep]
@ -42,7 +42,7 @@ describe "Importers::QuizImporter" do
context = course_model
question_data = import_example_questions context
data = get_import_data ['vista', 'quiz'], 'simple_quiz_data'
Importers::QuizImporter.import_from_migration(data, context, question_data)
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
quiz = Quizzes::Quiz.find_by_migration_id data[:migration_id]
quiz.quiz_questions.active.count.should == 1
# Check if the expected question name is in there
@ -53,7 +53,7 @@ describe "Importers::QuizImporter" do
context = get_import_context
question_data = import_example_questions context
data = get_import_data ['vista', 'quiz'], 'text_only_quiz_data'
Importers::QuizImporter.import_from_migration(data, context, question_data)
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
quiz = Quizzes::Quiz.find_by_migration_id data[:migration_id]
quiz.unpublished_question_count.should == 2
quiz.quiz_questions.active.count.should == 2
@ -66,7 +66,7 @@ describe "Importers::QuizImporter" do
context = get_import_context
question_data = import_example_questions context
data = get_import_data ['vista', 'quiz'], 'group_quiz_data'
Importers::QuizImporter.import_from_migration(data, context, question_data)
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
quiz = Quizzes::Quiz.find_by_migration_id data[:migration_id]
quiz.quiz_groups.count.should == 1
quiz.quiz_groups.first.quiz_questions.active.count.should == 3
@ -78,8 +78,8 @@ describe "Importers::QuizImporter" do
context = get_import_context
question_data = import_example_questions context
data = get_import_data ['vista', 'quiz'], 'text_only_quiz_data'
Importers::QuizImporter.import_from_migration(data, context, question_data)
Importers::QuizImporter.import_from_migration(data, context, question_data)
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
Quizzes::Quiz.count.should == 1
quiz = Quizzes::Quiz.find_by_migration_id data[:migration_id]
quiz.assignment.should be_nil
@ -89,8 +89,8 @@ describe "Importers::QuizImporter" do
context = get_import_context
question_data = import_example_questions context
data = get_import_data ['vista', 'quiz'], 'simple_quiz_data'
Importers::QuizImporter.import_from_migration(data, context, question_data)
Importers::QuizImporter.import_from_migration(data, context, question_data)
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
Assignment.count.should == 0
Quizzes::Quiz.count.should == 1
@ -105,8 +105,8 @@ describe "Importers::QuizImporter" do
context.root_account.enable_feature!(:draft_state)
question_data = import_example_questions context
data = get_import_data ['vista', 'quiz'], 'simple_quiz_data'
Importers::QuizImporter.import_from_migration(data, context, question_data)
Importers::QuizImporter.import_from_migration(data, context, question_data)
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
Assignment.count.should == 1
Quizzes::Quiz.count.should == 1
@ -129,13 +129,13 @@ describe "Importers::QuizImporter" do
context = get_import_context
question_data = import_example_questions context
data = get_import_data ['vista', 'quiz'], 'simple_quiz_data'
Importers::QuizImporter.import_from_migration(data, context, question_data)
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
quiz = Quizzes::Quiz.find_by_migration_id data[:migration_id]
quiz.quiz_questions.active.first.question_data[:question_name].should == "Rocket Bee!"
question_data[:aq_data][data['questions'].first[:migration_id]]['question_name'] = "Not Rocket Bee?"
Importers::QuizImporter.import_from_migration(data, context, question_data)
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
quiz.quiz_questions.active.first.question_data[:question_name].should == "Not Rocket Bee?"
end

View File

@ -13,7 +13,8 @@ Rails.configuration.to_prepare do
:common_core_guid => AcademicBenchmark::Converter::COMMON_CORE_GUID,
:worker => 'CCWorker',
:converter_class => AcademicBenchmark::Converter,
:provides => {:academic_benchmark => AcademicBenchmark::Converter}
:provides => {:academic_benchmark => AcademicBenchmark::Converter},
:valid_contexts => %w{Account}
}
}
end

View File

@ -9,7 +9,8 @@ Rails.configuration.to_prepare do
:settings => {
:migration_partial => 'moodle_config',
:worker=> 'CCWorker',
:provides =>{:moodle_1_9=>Moodle::Converter, :moodle_2=>Moodle::Converter}
:provides =>{:moodle_1_9=>Moodle::Converter, :moodle_2=>Moodle::Converter},
:valid_contexts => %w{Account Course}
}
}
end

View File

@ -16,7 +16,8 @@ Rails.configuration.to_prepare do
:requires_file_upload => true,
:provides =>{:qti=>Qti::Converter,
:webct=>Qti::Converter, # It can import WebCT Quizzes
}
},
:valid_contexts => %w{Account Course}
},
:validator => 'QtiPluginValidator'
}