imported link refactoring
test plan: * regression test links within copied content * should also fix a pre-existing bug where links to content within assessment/quiz questions were not being copied closes #CNVS-20890 #CNVS-20740 Change-Id: I1d1b89faba468690eaddaa1e29cd2936c807cc60 Reviewed-on: https://gerrit.instructure.com/55830 Tested-by: Jenkins Reviewed-by: Jeremy Stanley <jeremy@instructure.com> QA-Review: Michael Hargiss <mhargiss@instructure.com> Product-Review: James Williams <jamesw@instructure.com>
This commit is contained in:
parent
3c44f4f6e7
commit
91646b65eb
|
@ -229,7 +229,7 @@ class ApplicationController < ActionController::Base
|
||||||
# time or resources that are not well represented by simple time/cpu
|
# time or resources that are not well represented by simple time/cpu
|
||||||
# benchmarks, so you can use this method to increase the perceived cost
|
# benchmarks, so you can use this method to increase the perceived cost
|
||||||
# of a request by an arbitrary amount. For an anchor, rate limiting
|
# of a request by an arbitrary amount. For an anchor, rate limiting
|
||||||
# kicks in when a user has exceeded 600 arbitrary units of cost (it's
|
# kicks in when a user has exceeded 600 arbitrary units of cost (it's
|
||||||
# a leaky bucket, go see Canvas::RequestThrottle), so using an 'amount'
|
# a leaky bucket, go see Canvas::RequestThrottle), so using an 'amount'
|
||||||
# param of 600, for example, would max out the bucket immediately
|
# param of 600, for example, would max out the bucket immediately
|
||||||
def increment_request_cost(amount)
|
def increment_request_cost(amount)
|
||||||
|
|
|
@ -79,14 +79,55 @@ class AssessmentQuestion < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def translate_link_regex
|
||||||
|
@regex ||= Regexp.new(%{/#{context_type.downcase.pluralize}/#{context_id}/(?:files/(\\d+)/(?:download|preview)|file_contents/(course%20files/[^'"?]*))(?:\\?([^'"]*))?})
|
||||||
|
end
|
||||||
|
|
||||||
|
def file_substitutions
|
||||||
|
@file_substitutions ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def translate_file_link(link, match_data=nil)
|
||||||
|
match_data ||= link.match(translate_link_regex)
|
||||||
|
return link unless match_data
|
||||||
|
|
||||||
|
id = match_data[1]
|
||||||
|
path = match_data[2]
|
||||||
|
id_or_path = id || path
|
||||||
|
|
||||||
|
if !file_substitutions[id_or_path]
|
||||||
|
if id
|
||||||
|
file = Attachment.where(context_type: context_type, context_id: context_id, id: id_or_path).first
|
||||||
|
elsif path
|
||||||
|
path = URI.unescape(id_or_path)
|
||||||
|
file = Folder.find_attachment_in_context_with_path(assessment_question_bank.context, path)
|
||||||
|
end
|
||||||
|
begin
|
||||||
|
new_file = file.clone_for(self)
|
||||||
|
rescue => e
|
||||||
|
new_file = nil
|
||||||
|
er_id = Canvas::Errors.capture_exception(:file_clone_during_translate_links, e)[:error_report]
|
||||||
|
logger.error("Error while cloning attachment during"\
|
||||||
|
" AssessmentQuestion#translate_links: "\
|
||||||
|
"id: #{self.id} error_report: #{er_id}")
|
||||||
|
end
|
||||||
|
new_file.save if new_file
|
||||||
|
file_substitutions[id_or_path] = new_file
|
||||||
|
end
|
||||||
|
if sub = file_substitutions[id_or_path]
|
||||||
|
query_rest = match_data[3] ? "&#{match_data[3]}" : ''
|
||||||
|
"/assessment_questions/#{self.id}/files/#{sub.id}/download?verifier=#{sub.uuid}#{query_rest}"
|
||||||
|
else
|
||||||
|
link
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def translate_links
|
def translate_links
|
||||||
# we can't translate links unless this question has a context (through a bank)
|
# we can't translate links unless this question has a context (through a bank)
|
||||||
return unless assessment_question_bank && assessment_question_bank.context
|
return unless assessment_question_bank && assessment_question_bank.context
|
||||||
|
|
||||||
# This either matches the id from a url like: /courses/15395/files/11454/download
|
# This either matches the id from a url like: /courses/15395/files/11454/download
|
||||||
# or gets the relative path at the end of one like: /courses/15395/file_contents/course%20files/unfiled/test.jpg
|
# or gets the relative path at the end of one like: /courses/15395/file_contents/course%20files/unfiled/test.jpg
|
||||||
regex = Regexp.new(%{/#{context_type.downcase.pluralize}/#{context_id}/(?:files/(\\d+)/(?:download|preview)|file_contents/(course%20files/[^'"?]*))(?:\\?([^'"]*))?})
|
|
||||||
file_substitutions = {}
|
|
||||||
|
|
||||||
deep_translate = lambda do |obj|
|
deep_translate = lambda do |obj|
|
||||||
if obj.is_a?(Hash)
|
if obj.is_a?(Hash)
|
||||||
|
@ -94,33 +135,8 @@ class AssessmentQuestion < ActiveRecord::Base
|
||||||
elsif obj.is_a?(Array)
|
elsif obj.is_a?(Array)
|
||||||
obj.map {|v| deep_translate.call(v) }
|
obj.map {|v| deep_translate.call(v) }
|
||||||
elsif obj.is_a?(String)
|
elsif obj.is_a?(String)
|
||||||
obj.gsub(regex) do |match|
|
obj.gsub(translate_link_regex) do |match|
|
||||||
id_or_path = $1 || $2
|
translate_file_link(match, $~)
|
||||||
if !file_substitutions[id_or_path]
|
|
||||||
if $1
|
|
||||||
file = Attachment.where(context_type: context_type, context_id: context_id, id: id_or_path).first
|
|
||||||
elsif $2
|
|
||||||
path = URI.unescape(id_or_path)
|
|
||||||
file = Folder.find_attachment_in_context_with_path(assessment_question_bank.context, path)
|
|
||||||
end
|
|
||||||
begin
|
|
||||||
new_file = file.clone_for(self)
|
|
||||||
rescue => e
|
|
||||||
new_file = nil
|
|
||||||
er_id = Canvas::Errors.capture_exception(:file_clone_during_translate_links, e)[:error_report]
|
|
||||||
logger.error("Error while cloning attachment during"\
|
|
||||||
" AssessmentQuestion#translate_links: "\
|
|
||||||
"id: #{self.id} error_report: #{er_id}")
|
|
||||||
end
|
|
||||||
new_file.save if new_file
|
|
||||||
file_substitutions[id_or_path] = new_file
|
|
||||||
end
|
|
||||||
if sub = file_substitutions[id_or_path]
|
|
||||||
query_rest = $3 ? "&#{$3}" : ''
|
|
||||||
"/assessment_questions/#{self.id}/files/#{sub.id}/download?verifier=#{sub.uuid}#{query_rest}"
|
|
||||||
else
|
|
||||||
match
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
obj
|
obj
|
||||||
|
|
|
@ -36,7 +36,7 @@ class ContentMigration < ActiveRecord::Base
|
||||||
DATE_FORMAT = "%m/%d/%Y"
|
DATE_FORMAT = "%m/%d/%Y"
|
||||||
|
|
||||||
attr_accessible :context, :migration_settings, :user, :source_course, :copy_options, :migration_type, :initiated_source
|
attr_accessible :context, :migration_settings, :user, :source_course, :copy_options, :migration_type, :initiated_source
|
||||||
attr_accessor :imported_migration_items, :outcome_to_id_map
|
attr_accessor :imported_migration_items, :outcome_to_id_map, :attachment_path_id_lookup, :attachment_path_id_lookup_lower
|
||||||
|
|
||||||
workflow do
|
workflow do
|
||||||
state :created
|
state :created
|
||||||
|
@ -588,27 +588,27 @@ class ContentMigration < ActiveRecord::Base
|
||||||
ContentMigration.where(:id => self).update_all(:progress=>val)
|
ContentMigration.where(:id => self).update_all(:progress=>val)
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_missing_content_links(item)
|
def html_converter
|
||||||
@missing_content_links ||= {}
|
@html_converter ||= ImportedHtmlConverter.new(self)
|
||||||
item[:field] ||= :text
|
|
||||||
key = "#{item[:class]}_#{item[:id]}_#{item[:field]}"
|
|
||||||
if item[:missing_links].present?
|
|
||||||
@missing_content_links[key] = item
|
|
||||||
else
|
|
||||||
@missing_content_links.delete(key)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_warnings_for_missing_content_links
|
def convert_html(*args)
|
||||||
return unless @missing_content_links
|
html_converter.convert(*args)
|
||||||
@missing_content_links.each_value do |item|
|
end
|
||||||
if item[:missing_links].any?
|
|
||||||
add_warning(t(:missing_content_links_title, "Missing links found in imported content") + " - #{item[:class]} #{item[:field]}",
|
def convert_text(*args)
|
||||||
{:error_message => "#{item[:class]} #{item[:field]} - " + t(:missing_content_links_message,
|
html_converter.convert_text(*args)
|
||||||
"The following references could not be resolved:") + " " + item[:missing_links].join(', '),
|
end
|
||||||
:fix_issue_html_url => item[:url]})
|
|
||||||
end
|
def resolve_content_links!
|
||||||
end
|
html_converter.resolve_content_links!
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_warning_for_missing_content_links(type, field, missing_links, fix_issue_url)
|
||||||
|
add_warning(t(:missing_content_links_title, "Missing links found in imported content") + " - #{type} #{field}",
|
||||||
|
{:error_message => "#{type} #{field} - " + t(:missing_content_links_message,
|
||||||
|
"The following references could not be resolved:") + " " + missing_links.join(', '),
|
||||||
|
:fix_issue_html_url => fix_issue_url})
|
||||||
end
|
end
|
||||||
|
|
||||||
UPLOAD_TIMEOUT = 1.hour
|
UPLOAD_TIMEOUT = 1.hour
|
||||||
|
@ -700,17 +700,31 @@ class ContentMigration < ActiveRecord::Base
|
||||||
|
|
||||||
def imported_migration_items
|
def imported_migration_items
|
||||||
@imported_migration_items_hash ||= {}
|
@imported_migration_items_hash ||= {}
|
||||||
@imported_migration_items_hash.values.flatten
|
@imported_migration_items_hash.values.map(&:values).flatten
|
||||||
|
end
|
||||||
|
|
||||||
|
def imported_migration_items_hash(klass)
|
||||||
|
@imported_migration_items_hash ||= {}
|
||||||
|
@imported_migration_items_hash[klass.name] ||= {}
|
||||||
end
|
end
|
||||||
|
|
||||||
def imported_migration_items_by_class(klass)
|
def imported_migration_items_by_class(klass)
|
||||||
@imported_migration_items_hash ||= {}
|
imported_migration_items_hash(klass).values
|
||||||
@imported_migration_items_hash[klass.name] ||= []
|
end
|
||||||
|
|
||||||
|
def find_imported_migration_item(klass, migration_id)
|
||||||
|
imported_migration_items_hash(klass)[migration_id]
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_imported_item(item)
|
def add_imported_item(item)
|
||||||
arr = imported_migration_items_by_class(item.class)
|
imported_migration_items_hash(item.class)[item.migration_id] = item
|
||||||
arr << item unless arr.include?(item)
|
end
|
||||||
|
|
||||||
|
def add_attachment_path(path, migration_id)
|
||||||
|
self.attachment_path_id_lookup ||= {}
|
||||||
|
self.attachment_path_id_lookup_lower ||= {}
|
||||||
|
self.attachment_path_id_lookup[path] = migration_id
|
||||||
|
self.attachment_path_id_lookup_lower[path.downcase] = migration_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_external_tool_translation(migration_id, target_tool, custom_fields)
|
def add_external_tool_translation(migration_id, target_tool, custom_fields)
|
||||||
|
|
|
@ -184,6 +184,7 @@ class Course < ActiveRecord::Base
|
||||||
has_many :context_external_tools, :as => :context, :dependent => :destroy, :order => 'name'
|
has_many :context_external_tools, :as => :context, :dependent => :destroy, :order => 'name'
|
||||||
belongs_to :wiki
|
belongs_to :wiki
|
||||||
has_many :quizzes, :class_name => 'Quizzes::Quiz', :as => :context, :dependent => :destroy, :order => 'lock_at, title'
|
has_many :quizzes, :class_name => 'Quizzes::Quiz', :as => :context, :dependent => :destroy, :order => 'lock_at, title'
|
||||||
|
has_many :quiz_questions, :class_name => 'Quizzes::QuizQuestion', :through => :quizzes
|
||||||
has_many :active_quizzes, :class_name => 'Quizzes::Quiz', :as => :context, :include => :assignment, :conditions => ['quizzes.workflow_state != ?', 'deleted'], :order => 'created_at'
|
has_many :active_quizzes, :class_name => 'Quizzes::Quiz', :as => :context, :include => :assignment, :conditions => ['quizzes.workflow_state != ?', 'deleted'], :order => 'created_at'
|
||||||
has_many :assessment_questions, :through => :assessment_question_banks
|
has_many :assessment_questions, :through => :assessment_question_banks
|
||||||
has_many :assessment_question_banks, :as => :context, :include => [:assessment_questions, :assessment_question_bank_users]
|
has_many :assessment_question_banks, :as => :context, :include => [:assessment_questions, :assessment_question_bank_users]
|
||||||
|
@ -1967,8 +1968,7 @@ class Course < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_accessor :full_migration_hash, :external_url_hash,
|
attr_accessor :full_migration_hash, :external_url_hash,
|
||||||
:folder_name_lookups, :attachment_path_id_lookup, :attachment_path_id_lookup_lower,
|
:folder_name_lookups, :assignment_group_no_drop_assignments, :migration_results
|
||||||
:assignment_group_no_drop_assignments, :migration_results
|
|
||||||
|
|
||||||
|
|
||||||
def backup_to_json
|
def backup_to_json
|
||||||
|
@ -2009,7 +2009,6 @@ class Course < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def copy_attachments_from_course(course, options={})
|
def copy_attachments_from_course(course, options={})
|
||||||
self.attachment_path_id_lookup = {}
|
|
||||||
root_folder = Folder.root_folders(self).first
|
root_folder = Folder.root_folders(self).first
|
||||||
root_folder_name = root_folder.name + '/'
|
root_folder_name = root_folder.name + '/'
|
||||||
ce = options[:content_export]
|
ce = options[:content_export]
|
||||||
|
@ -2025,7 +2024,7 @@ class Course < ActiveRecord::Base
|
||||||
if !ce || ce.export_object?(file)
|
if !ce || ce.export_object?(file)
|
||||||
begin
|
begin
|
||||||
new_file = file.clone_for(self, nil, :overwrite => true)
|
new_file = file.clone_for(self, nil, :overwrite => true)
|
||||||
self.attachment_path_id_lookup[file.full_display_path.gsub(/\A#{root_folder_name}/, '')] = new_file.migration_id
|
cm.add_attachment_path(file.full_display_path.gsub(/\A#{root_folder_name}/, ''), new_file.migration_id)
|
||||||
new_folder_id = merge_mapped_id(file.folder)
|
new_folder_id = merge_mapped_id(file.folder)
|
||||||
|
|
||||||
if file.folder && file.folder.parent_folder_id.nil?
|
if file.folder && file.folder.parent_folder_id.nil?
|
||||||
|
|
|
@ -30,4 +30,4 @@ module Importers
|
||||||
end
|
end
|
||||||
|
|
||||||
require_dependency 'importers/account_content_importer'
|
require_dependency 'importers/account_content_importer'
|
||||||
require_dependency 'importers/course_content_importer'
|
require_dependency 'importers/course_content_importer'
|
||||||
|
|
|
@ -10,6 +10,8 @@ module Importers
|
||||||
Importers::AssessmentQuestionImporter.process_migration(data, migration)
|
Importers::AssessmentQuestionImporter.process_migration(data, migration)
|
||||||
Importers::LearningOutcomeImporter.process_migration(data, migration)
|
Importers::LearningOutcomeImporter.process_migration(data, migration)
|
||||||
|
|
||||||
|
migration.resolve_content_links!
|
||||||
|
|
||||||
migration.progress = 100
|
migration.progress = 100
|
||||||
migration.workflow_state = :imported
|
migration.workflow_state = :imported
|
||||||
migration.save
|
migration.save
|
||||||
|
|
|
@ -67,16 +67,6 @@ module Importers
|
||||||
|
|
||||||
begin
|
begin
|
||||||
question = self.import_from_migration(question, migration.context, migration, 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
|
|
||||||
# the link needs to include the id of the AQ, which we don't have until it's saved. This will
|
|
||||||
# be a problem as long as we use the question as a context for its attachments. (We're turning this
|
|
||||||
# hash into a string so we can quickly check if anywhere in the hash might have a URL.)
|
|
||||||
if question.to_s =~ %r{/files/\d+/(download|preview)}
|
|
||||||
AssessmentQuestion.find(question[:assessment_question_id]).translate_links
|
|
||||||
end
|
|
||||||
|
|
||||||
question_data[:aq_data][question['migration_id']] = question
|
question_data[:aq_data][question['migration_id']] = question
|
||||||
rescue
|
rescue
|
||||||
migration.add_import_warning(t('#migration.quiz_question_type', "Quiz Question"), question[:question_name], $!)
|
migration.add_import_warning(t('#migration.quiz_question_type', "Quiz Question"), question[:question_name], $!)
|
||||||
|
@ -90,9 +80,8 @@ module Importers
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
hash.delete(:question_bank_migration_id) if hash.has_key?(:question_bank_migration_id)
|
hash.delete(:question_bank_migration_id) if hash.has_key?(:question_bank_migration_id)
|
||||||
|
|
||||||
self.prep_for_import(hash, context, migration)
|
self.prep_for_import(hash, migration, :assessment_question)
|
||||||
|
|
||||||
missing_links = hash.delete(:missing_links) || {}
|
|
||||||
import_warnings = hash.delete(:import_warnings) || []
|
import_warnings = hash.delete(:import_warnings) || []
|
||||||
if error = hash.delete(:import_error)
|
if error = hash.delete(:import_error)
|
||||||
import_warnings << error
|
import_warnings << error
|
||||||
|
@ -115,54 +104,49 @@ module Importers
|
||||||
hash['assessment_question_id'] = id
|
hash['assessment_question_id'] = id
|
||||||
end
|
end
|
||||||
|
|
||||||
if migration
|
if import_warnings
|
||||||
missing_links.each do |field, links|
|
import_warnings.each do |warning|
|
||||||
migration.add_missing_content_links(:class => self.to_s,
|
migration.add_warning(warning, {
|
||||||
:id => hash['assessment_question_id'], :field => field, :missing_links => links,
|
:fix_issue_html_url => "/#{context.class.to_s.underscore.pluralize}/#{context.id}/question_banks/#{bank.id}#question_#{hash['assessment_question_id']}_question_text"
|
||||||
:url => "/#{context.class.to_s.underscore.pluralize}/#{context.id}/question_banks/#{bank.id}#question_#{hash['assessment_question_id']}_question_text")
|
})
|
||||||
end
|
|
||||||
if import_warnings
|
|
||||||
import_warnings.each do |warning|
|
|
||||||
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
|
end
|
||||||
end
|
end
|
||||||
hash
|
hash
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.prep_for_import(hash, context, migration=nil)
|
def self.prep_for_import(hash, migration, item_type)
|
||||||
return hash if hash[:prepped_for_import]
|
return hash if hash[:prepped_for_import]
|
||||||
hash[:missing_links] = {}
|
|
||||||
[:question_text, :correct_comments_html, :incorrect_comments_html, :neutral_comments_html, :more_comments_html].each do |field|
|
[:question_text, :correct_comments_html, :incorrect_comments_html, :neutral_comments_html, :more_comments_html].each do |field|
|
||||||
hash[:missing_links][field] = []
|
|
||||||
if hash[field].present?
|
if hash[field].present?
|
||||||
hash[field] = ImportedHtmlConverter.convert(hash[field], context, migration, {:remove_outer_nodes_if_one_child => true}) do |warn, link|
|
hash[field] = migration.convert_html(
|
||||||
hash[:missing_links][field] << link if warn == :missing_link
|
hash[field], item_type, hash[:migration_id], field, {:remove_outer_nodes_if_one_child => true}
|
||||||
end
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
[:correct_comments, :incorrect_comments, :neutral_comments, :more_comments].each do |field|
|
[:correct_comments, :incorrect_comments, :neutral_comments, :more_comments].each do |field|
|
||||||
html_field = "#{field}_html".to_sym
|
html_field = "#{field}_html".to_sym
|
||||||
if hash[field].present? && hash[field] == hash[html_field]
|
if hash[field].present? && hash[field] == hash[html_field]
|
||||||
hash.delete(html_field)
|
hash.delete(html_field)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
hash[:answers].each_with_index do |answer, i|
|
hash[:answers].each_with_index do |answer, i|
|
||||||
[:html, :comments_html, :left_html].each do |field|
|
[:html, :comments_html, :left_html].each do |field|
|
||||||
key = "answer #{i} #{field}"
|
key = "answer #{i} #{field}"
|
||||||
hash[:missing_links][key] = []
|
|
||||||
if answer[field].present?
|
if answer[field].present?
|
||||||
answer[field] = ImportedHtmlConverter.convert(answer[field], context, migration, {:remove_outer_nodes_if_one_child => true}) do |warn, link|
|
answer[field] = migration.convert_html(
|
||||||
hash[:missing_links][key] << link if warn == :missing_link
|
answer[field], item_type, hash[:migration_id], key, {:remove_outer_nodes_if_one_child => true}
|
||||||
end
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if answer[:comments].present? && answer[:comments] == answer[:comments_html]
|
if answer[:comments].present? && answer[:comments] == answer[:comments_html]
|
||||||
answer.delete(:comments_html)
|
answer.delete(:comments_html)
|
||||||
end
|
end
|
||||||
end if hash[:answers]
|
end if hash[:answers]
|
||||||
|
|
||||||
hash[:prepped_for_import] = true
|
hash[:prepped_for_import] = true
|
||||||
hash
|
hash
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,14 +33,14 @@ module Importers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.import_from_migration(hash, context, migration=nil, item=nil)
|
def self.import_from_migration(hash, context, migration, item=nil)
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
return nil if hash[:migration_id] && hash[:assignment_groups_to_import] && !hash[:assignment_groups_to_import][hash[:migration_id]]
|
return nil if hash[:migration_id] && hash[:assignment_groups_to_import] && !hash[:assignment_groups_to_import][hash[:migration_id]]
|
||||||
item ||= AssignmentGroup.where(context_id: context, context_type: context.class.to_s, id: hash[:id]).first
|
item ||= AssignmentGroup.where(context_id: context, context_type: context.class.to_s, id: hash[:id]).first
|
||||||
item ||= AssignmentGroup.where(context_id: context, context_type: context.class.to_s, migration_id: hash[:migration_id]).first if hash[:migration_id]
|
item ||= AssignmentGroup.where(context_id: context, context_type: context.class.to_s, migration_id: hash[:migration_id]).first if hash[:migration_id]
|
||||||
item ||= context.assignment_groups.where(name: hash[:title], migration_id: nil).first
|
item ||= context.assignment_groups.where(name: hash[:title], migration_id: nil).first
|
||||||
item ||= context.assignment_groups.new
|
item ||= context.assignment_groups.new
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
item.migration_id = hash[:migration_id]
|
item.migration_id = hash[:migration_id]
|
||||||
item.workflow_state = 'available' if item.deleted?
|
item.workflow_state = 'available' if item.deleted?
|
||||||
item.name = hash[:title]
|
item.name = hash[:title]
|
||||||
|
|
|
@ -26,7 +26,7 @@ module Importers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.import_from_migration(hash, context, migration=nil, item=nil, quiz=nil)
|
def self.import_from_migration(hash, context, migration, item=nil, quiz=nil)
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
return nil if hash[:migration_id] && hash[:assignments_to_import] && !hash[:assignments_to_import][hash[:migration_id]]
|
return nil if hash[:migration_id] && hash[:assignments_to_import] && !hash[:assignments_to_import][hash[:migration_id]]
|
||||||
item ||= Assignment.where(context_type: context.class.to_s, context_id: context, id: hash[:id]).first
|
item ||= Assignment.where(context_type: context.class.to_s, context_id: context, id: hash[:id]).first
|
||||||
|
@ -46,20 +46,14 @@ module Importers
|
||||||
self.extend TextHelper
|
self.extend TextHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
missing_links = {:description => [], :instructions => []}
|
|
||||||
description = ""
|
description = ""
|
||||||
if hash[:instructions_in_html] == false
|
if hash[:instructions_in_html] == false
|
||||||
description += ImportedHtmlConverter.convert_text(hash[:description] || "", context)
|
description += migration.convert_text(hash[:description] || "")
|
||||||
description += ImportedHtmlConverter.convert_text(hash[:instructions] || "", context)
|
description += migration.convert_text(hash[:instructions] || "")
|
||||||
else
|
else
|
||||||
description += ImportedHtmlConverter.convert(hash[:description] || "", context, migration) do |warn, link|
|
description += migration.convert_html(hash[:description] || "", :assignment, hash[:migration_id], :description)
|
||||||
missing_links[:description] << link if warn == :missing_link
|
description += migration.convert_html(hash[:instructions] || "", :assignment, hash[:migration_id], :description)
|
||||||
end
|
|
||||||
description += ImportedHtmlConverter.convert(hash[:instructions] || "", context, migration) do |warn, link|
|
|
||||||
missing_links[:instructions] << link if warn == :missing_link
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
description += Attachment.attachment_list_from_migration(context, hash[:attachment_ids])
|
description += Attachment.attachment_list_from_migration(context, hash[:attachment_ids])
|
||||||
item.description = description
|
item.description = description
|
||||||
|
|
||||||
|
@ -107,11 +101,12 @@ module Importers
|
||||||
item.submission_types = 'not_graded'
|
item.submission_types = 'not_graded'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if hash[:assignment_group_migration_id]
|
||||||
|
item.assignment_group = context.assignment_groups.where(migration_id: hash[:assignment_group_migration_id]).first
|
||||||
|
end
|
||||||
|
item.assignment_group ||= context.assignment_groups.where(name: t(:imported_assignments_group, "Imported Assignments")).first_or_create
|
||||||
|
|
||||||
# Associating with a rubric or a quiz might cause item to get saved, no longer indicating
|
item.save_without_broadcasting!
|
||||||
# that it is a new record. We need to know that below, where we add to the list of
|
|
||||||
# imported items
|
|
||||||
new_record = item.new_record?
|
|
||||||
|
|
||||||
rubric = nil
|
rubric = nil
|
||||||
rubric = context.rubrics.where(migration_id: hash[:rubric_migration_id]).first if hash[:rubric_migration_id]
|
rubric = context.rubrics.where(migration_id: hash[:rubric_migration_id]).first if hash[:rubric_migration_id]
|
||||||
|
@ -156,10 +151,6 @@ module Importers
|
||||||
item.submission_types = 'online_quiz'
|
item.submission_types = 'online_quiz'
|
||||||
item.saved_by = :quiz
|
item.saved_by = :quiz
|
||||||
end
|
end
|
||||||
if hash[:assignment_group_migration_id]
|
|
||||||
item.assignment_group = context.assignment_groups.where(migration_id: hash[:assignment_group_migration_id]).first
|
|
||||||
end
|
|
||||||
item.assignment_group ||= context.assignment_groups.where(name: t(:imported_assignments_group, "Imported Assignments")).first_or_create
|
|
||||||
|
|
||||||
hash[:due_at] ||= hash[:due_date]
|
hash[:due_at] ||= hash[:due_date]
|
||||||
[:due_at, :lock_at, :unlock_at, :peer_reviews_due_at].each do |key|
|
[:due_at, :lock_at, :unlock_at, :peer_reviews_due_at].each do |key|
|
||||||
|
@ -178,9 +169,9 @@ module Importers
|
||||||
item.send("#{prop}=", hash[prop]) unless hash[prop].nil?
|
item.send("#{prop}=", hash[prop]) unless hash[prop].nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
|
|
||||||
if migration && migration.date_shift_options
|
if migration.date_shift_options
|
||||||
# Unfortunately, we save the assignment here, and then shift dates and
|
# Unfortunately, we save the assignment here, and then shift dates and
|
||||||
# save the assignment again later in the course migration. Saving here
|
# save the assignment again later in the course migration. Saving here
|
||||||
# would normally schedule the auto peer reviews job with the
|
# would normally schedule the auto peer reviews job with the
|
||||||
|
@ -193,14 +184,6 @@ module Importers
|
||||||
item.save_without_broadcasting!
|
item.save_without_broadcasting!
|
||||||
item.skip_schedule_peer_reviews = nil
|
item.skip_schedule_peer_reviews = nil
|
||||||
|
|
||||||
if migration
|
|
||||||
missing_links.each do |field, missing_links|
|
|
||||||
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
|
|
||||||
end
|
|
||||||
|
|
||||||
if item.submission_types == 'external_tool'
|
if item.submission_types == 'external_tool'
|
||||||
tag = item.create_external_tool_tag(:url => hash[:external_tool_url], :new_tab => hash[:external_tool_new_tab])
|
tag = item.create_external_tool_tag(:url => hash[:external_tool_url], :new_tab => hash[:external_tool_new_tab])
|
||||||
if hash[:external_tool_id] && migration && !migration.cross_institution?
|
if hash[:external_tool_id] && migration && !migration.cross_institution?
|
||||||
|
@ -212,7 +195,11 @@ module Importers
|
||||||
end
|
end
|
||||||
tag.content_type = 'ContextExternalTool'
|
tag.content_type = 'ContextExternalTool'
|
||||||
if !tag.save
|
if !tag.save
|
||||||
migration.add_warning(t('errors.import.external_tool_url', "The url for the external tool assignment \"%{assignment_name}\" wasn't valid.", :assignment_name => item.title)) if migration && tag.errors["url"]
|
if tag.errors["url"]
|
||||||
|
migration.add_warning(t('errors.import.external_tool_url',
|
||||||
|
"The url for the external tool assignment \"%{assignment_name}\" wasn't valid.",
|
||||||
|
:assignment_name => item.title))
|
||||||
|
end
|
||||||
item.association(:external_tool_tag).target = nil # otherwise it will trigger destroy on the tag
|
item.association(:external_tool_tag).target = nil # otherwise it will trigger destroy on the tag
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -57,7 +57,7 @@ module Importers
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def self.import_from_migration(hash, context, migration=nil, item=nil, created_usage_rights_map={})
|
def self.import_from_migration(hash, context, migration, item=nil, created_usage_rights_map={})
|
||||||
return nil if hash[:files_to_import] && !hash[:files_to_import][hash[:migration_id]]
|
return nil if hash[:files_to_import] && !hash[:files_to_import][hash[:migration_id]]
|
||||||
item ||= Attachment.where(context_type: context.class.to_s, context_id: context, id: hash[:id]).first
|
item ||= Attachment.where(context_type: context.class.to_s, context_id: context, id: hash[:id]).first
|
||||||
item ||= Attachment.where(context_type: context.class.to_s, context_id: context, migration_id: hash[:migration_id]).first # if hash[:migration_id]
|
item ||= Attachment.where(context_type: context.class.to_s, context_id: context, migration_id: hash[:migration_id]).first # if hash[:migration_id]
|
||||||
|
@ -71,7 +71,7 @@ module Importers
|
||||||
item.usage_rights_id = find_or_create_usage_rights(context, hash[:usage_rights], created_usage_rights_map) if hash[:usage_rights]
|
item.usage_rights_id = find_or_create_usage_rights(context, hash[:usage_rights], created_usage_rights_map) if hash[:usage_rights]
|
||||||
item.set_publish_state_for_usage_rights unless hash[:locked]
|
item.set_publish_state_for_usage_rights unless hash[:locked]
|
||||||
item.save_without_broadcasting!
|
item.save_without_broadcasting!
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
end
|
end
|
||||||
item
|
item
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,7 +34,7 @@ module Importers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.import_from_migration(hash, context, migration=nil, item=nil)
|
def self.import_from_migration(hash, context, migration, item=nil)
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
return nil if hash[:migration_id] && hash[:events_to_import] && !hash[:events_to_import][hash[:migration_id]]
|
return nil if hash[:migration_id] && hash[:events_to_import] && !hash[:events_to_import][hash[:migration_id]]
|
||||||
item ||= CalendarEvent.where(context_type: context.class.to_s, context_id: context, id: hash[:id]).first
|
item ||= CalendarEvent.where(context_type: context.class.to_s, context_id: context, id: hash[:id]).first
|
||||||
|
@ -44,10 +44,8 @@ module Importers
|
||||||
item.migration_id = hash[:migration_id]
|
item.migration_id = hash[:migration_id]
|
||||||
item.workflow_state = 'active' if item.deleted?
|
item.workflow_state = 'active' if item.deleted?
|
||||||
item.title = hash[:title] || hash[:name]
|
item.title = hash[:title] || hash[:name]
|
||||||
missing_links = []
|
|
||||||
item.description = ImportedHtmlConverter.convert(hash[:description] || "", context, migration) do |warn, link|
|
item.description = migration.convert_html(hash[:description] || "", :calendar_event, hash[:migration_id], :description)
|
||||||
missing_links << link if warn == :missing_link
|
|
||||||
end
|
|
||||||
item.description += import_migration_attachment_suffix(hash, context)
|
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.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])
|
item.end_at = Canvas::Migration::MigratorHelper.get_utc_time_from_timestamp(hash[:end_at] || hash[:end_date])
|
||||||
|
@ -55,12 +53,7 @@ module Importers
|
||||||
item.imported = true
|
item.imported = true
|
||||||
|
|
||||||
item.save_without_broadcasting!
|
item.save_without_broadcasting!
|
||||||
if migration
|
migration.add_imported_item(item)
|
||||||
migration.add_missing_content_links(:class => item.class.to_s,
|
|
||||||
:id => item.id, :missing_links => missing_links,
|
|
||||||
:url => "/#{context.class.to_s.demodulize.underscore.pluralize}/#{context.id}/#{item.class.to_s.demodulize.underscore.pluralize}/#{item.id}")
|
|
||||||
end
|
|
||||||
migration.add_imported_item(item) if migration
|
|
||||||
if hash[:all_day]
|
if hash[:all_day]
|
||||||
item.all_day = hash[:all_day]
|
item.all_day = hash[:all_day]
|
||||||
item.save
|
item.save
|
||||||
|
|
|
@ -21,7 +21,7 @@ module Importers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.import_from_migration(hash, context, migration=nil, item=nil)
|
def self.import_from_migration(hash, context, migration, item=nil)
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
return nil if hash[:migration_id] && hash[:external_tools_to_import] && !hash[:external_tools_to_import][hash[:migration_id]]
|
return nil if hash[:migration_id] && hash[:external_tools_to_import] && !hash[:external_tools_to_import][hash[:migration_id]]
|
||||||
|
|
||||||
|
|
|
@ -50,13 +50,13 @@ module Importers
|
||||||
migration.context.touch
|
migration.context.touch
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.import_from_migration(hash, context, migration=nil, item=nil)
|
def self.import_from_migration(hash, context, migration, item=nil)
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
return nil if hash[:migration_id] && hash[:modules_to_import] && !hash[:modules_to_import][hash[:migration_id]]
|
return nil if hash[:migration_id] && hash[:modules_to_import] && !hash[:modules_to_import][hash[:migration_id]]
|
||||||
item ||= ContextModule.where(context_type: context.class.to_s, context_id: context, id: hash[:id]).first
|
item ||= ContextModule.where(context_type: context.class.to_s, context_id: context, id: hash[:id]).first
|
||||||
item ||= ContextModule.where(context_type: context.class.to_s, context_id: context, migration_id: hash[:migration_id]).first if hash[:migration_id]
|
item ||= ContextModule.where(context_type: context.class.to_s, context_id: context, migration_id: hash[:migration_id]).first if hash[:migration_id]
|
||||||
item ||= ContextModule.new(:context => context)
|
item ||= ContextModule.new(:context => context)
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
item.name = hash[:title] || hash[:description]
|
item.name = hash[:title] || hash[:description]
|
||||||
item.migration_id = hash[:migration_id]
|
item.migration_id = hash[:migration_id]
|
||||||
if hash[:workflow_state] == 'unpublished'
|
if hash[:workflow_state] == 'unpublished'
|
||||||
|
@ -94,7 +94,7 @@ module Importers
|
||||||
begin
|
begin
|
||||||
self.add_module_item_from_migration(item, tag_hash, 0, context, item_map, migration)
|
self.add_module_item_from_migration(item, tag_hash, 0, context, item_map, migration)
|
||||||
rescue
|
rescue
|
||||||
migration.add_import_warning(t(:migration_module_item_type, "Module Item"), tag_hash[:title], $!) if migration
|
migration.add_import_warning(t(:migration_module_item_type, "Module Item"), tag_hash[:title], $!)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ module Importers
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def self.add_module_item_from_migration(context_module, hash, level, context, item_map, migration=nil)
|
def self.add_module_item_from_migration(context_module, hash, level, context, item_map, migration)
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
hash[:migration_id] ||= hash[:item_migration_id]
|
hash[:migration_id] ||= hash[:item_migration_id]
|
||||||
hash[:migration_id] ||= Digest::MD5.hexdigest(hash[:title]) if hash[:title]
|
hash[:migration_id] ||= Digest::MD5.hexdigest(hash[:title]) if hash[:title]
|
||||||
|
@ -129,7 +129,7 @@ module Importers
|
||||||
else
|
else
|
||||||
existing_item.workflow_state = 'active'
|
existing_item.workflow_state = 'active'
|
||||||
end
|
end
|
||||||
migration.add_imported_item(existing_item) if migration
|
migration.add_imported_item(existing_item)
|
||||||
existing_item.migration_id = hash[:migration_id]
|
existing_item.migration_id = hash[:migration_id]
|
||||||
hash[:indent] = [hash[:indent] || 0, level].max
|
hash[:indent] = [hash[:indent] || 0, level].max
|
||||||
resource_class = linked_resource_type_class(hash[:linked_resource_type])
|
resource_class = linked_resource_type_class(hash[:linked_resource_type])
|
||||||
|
@ -137,7 +137,7 @@ module Importers
|
||||||
wiki = context_module.context.wiki.wiki_pages.where(migration_id: hash[:linked_resource_id]).first if hash[:linked_resource_id]
|
wiki = context_module.context.wiki.wiki_pages.where(migration_id: hash[:linked_resource_id]).first if hash[:linked_resource_id]
|
||||||
if wiki
|
if wiki
|
||||||
item = context_module.add_item({
|
item = context_module.add_item({
|
||||||
:title => hash[:title] || hash[:linked_resource_title],
|
:title => wiki.title.presence || hash[:title] || hash[:linked_resource_title],
|
||||||
:type => 'wiki_page',
|
:type => 'wiki_page',
|
||||||
:id => wiki.id,
|
:id => wiki.id,
|
||||||
:indent => hash[:indent].to_i
|
:indent => hash[:indent].to_i
|
||||||
|
@ -158,7 +158,7 @@ module Importers
|
||||||
ass = context_module.context.assignments.where(migration_id: hash[:linked_resource_id]).first if hash[:linked_resource_id]
|
ass = context_module.context.assignments.where(migration_id: hash[:linked_resource_id]).first if hash[:linked_resource_id]
|
||||||
if ass
|
if ass
|
||||||
item = context_module.add_item({
|
item = context_module.add_item({
|
||||||
:title => hash[:title] || hash[:linked_resource_title],
|
:title => ass.title.presence || hash[:title] || hash[:linked_resource_title],
|
||||||
:type => 'assignment',
|
:type => 'assignment',
|
||||||
:id => ass.id,
|
:id => ass.id,
|
||||||
:indent => hash[:indent].to_i
|
:indent => hash[:indent].to_i
|
||||||
|
@ -175,7 +175,7 @@ module Importers
|
||||||
# external url
|
# external url
|
||||||
if url = hash[:url]
|
if url = hash[:url]
|
||||||
if (CanvasHttp.validate_url(hash[:url]) rescue nil)
|
if (CanvasHttp.validate_url(hash[:url]) rescue nil)
|
||||||
url = migration.process_domain_substitutions(url) if migration
|
url = migration.process_domain_substitutions(url)
|
||||||
|
|
||||||
item = context_module.add_item({
|
item = context_module.add_item({
|
||||||
:title => hash[:title] || hash[:linked_resource_title] || hash['description'],
|
:title => hash[:title] || hash[:linked_resource_title] || hash['description'],
|
||||||
|
@ -184,7 +184,7 @@ module Importers
|
||||||
:url => url
|
:url => url
|
||||||
}, existing_item, :position => context_module.migration_position)
|
}, existing_item, :position => context_module.migration_position)
|
||||||
else
|
else
|
||||||
migration.add_import_warning(t(:migration_module_item_type, "Module Item"), hash[:title], "#{hash[:url]} is not a valid URL") if migration
|
migration.add_import_warning(t(:migration_module_item_type, "Module Item"), hash[:title], "#{hash[:url]} is not a valid URL")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elsif resource_class == ContextExternalTool
|
elsif resource_class == ContextExternalTool
|
||||||
|
@ -194,7 +194,7 @@ module Importers
|
||||||
|
|
||||||
if hash[:linked_resource_global_id] && (!migration || !migration.cross_institution?)
|
if hash[:linked_resource_global_id] && (!migration || !migration.cross_institution?)
|
||||||
external_tool_id = hash[:linked_resource_global_id]
|
external_tool_id = hash[:linked_resource_global_id]
|
||||||
elsif migration && arr = migration.find_external_tool_translation(hash[:linked_resource_id])
|
elsif arr = migration.find_external_tool_translation(hash[:linked_resource_id])
|
||||||
external_tool_id = arr[0]
|
external_tool_id = arr[0]
|
||||||
custom_fields = arr[1]
|
custom_fields = arr[1]
|
||||||
if custom_fields.present?
|
if custom_fields.present?
|
||||||
|
@ -206,14 +206,14 @@ module Importers
|
||||||
|
|
||||||
if external_tool_url
|
if external_tool_url
|
||||||
title = hash[:title] || hash[:linked_resource_title] || hash['description']
|
title = hash[:title] || hash[:linked_resource_title] || hash['description']
|
||||||
if migration
|
|
||||||
external_tool_url = migration.process_domain_substitutions(external_tool_url)
|
external_tool_url = migration.process_domain_substitutions(external_tool_url)
|
||||||
if external_tool_id.nil?
|
if external_tool_id.nil?
|
||||||
migration.add_warning(t(:foreign_lti_tool,
|
migration.add_warning(t(:foreign_lti_tool,
|
||||||
%q{The account External Tool for module item "%{title}" must be configured before the item can be launched},
|
%q{The account External Tool for module item "%{title}" must be configured before the item can be launched},
|
||||||
:title => title))
|
:title => title))
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
item = context_module.add_item({
|
item = context_module.add_item({
|
||||||
:title => title,
|
:title => title,
|
||||||
:type => 'context_external_tool',
|
:type => 'context_external_tool',
|
||||||
|
@ -226,7 +226,7 @@ module Importers
|
||||||
quiz = context_module.context.quizzes.where(migration_id: hash[:linked_resource_id]).first if hash[:linked_resource_id]
|
quiz = context_module.context.quizzes.where(migration_id: hash[:linked_resource_id]).first if hash[:linked_resource_id]
|
||||||
if quiz
|
if quiz
|
||||||
item = context_module.add_item({
|
item = context_module.add_item({
|
||||||
:title => hash[:title] || hash[:linked_resource_title],
|
:title => quiz.title.presence || hash[:title] || hash[:linked_resource_title],
|
||||||
:type => 'quiz',
|
:type => 'quiz',
|
||||||
:indent => hash[:indent].to_i,
|
:indent => hash[:indent].to_i,
|
||||||
:id => quiz.id
|
:id => quiz.id
|
||||||
|
@ -236,7 +236,7 @@ module Importers
|
||||||
topic = context_module.context.discussion_topics.where(migration_id: hash[:linked_resource_id]).first if hash[:linked_resource_id]
|
topic = context_module.context.discussion_topics.where(migration_id: hash[:linked_resource_id]).first if hash[:linked_resource_id]
|
||||||
if topic
|
if topic
|
||||||
item = context_module.add_item({
|
item = context_module.add_item({
|
||||||
:title => hash[:title] || hash[:linked_resource_title],
|
:title => topic.title.presence || hash[:title] || hash[:linked_resource_title],
|
||||||
:type => 'discussion_topic',
|
:type => 'discussion_topic',
|
||||||
:indent => hash[:indent].to_i,
|
:indent => hash[:indent].to_i,
|
||||||
:id => topic.id
|
:id => topic.id
|
||||||
|
|
|
@ -9,14 +9,14 @@ module Importers
|
||||||
data['all_files_export']['file_path'] ||= data['all_files_zip']
|
data['all_files_export']['file_path'] ||= data['all_files_zip']
|
||||||
return unless data['all_files_export']['file_path'] && File.exist?(data['all_files_export']['file_path'])
|
return unless data['all_files_export']['file_path'] && File.exist?(data['all_files_export']['file_path'])
|
||||||
|
|
||||||
course.attachment_path_id_lookup ||= {}
|
migration.attachment_path_id_lookup ||= {}
|
||||||
course.attachment_path_id_lookup_lower ||= {}
|
migration.attachment_path_id_lookup_lower ||= {}
|
||||||
|
|
||||||
params = migration.migration_settings[:migration_ids_to_import]
|
params = migration.migration_settings[:migration_ids_to_import]
|
||||||
valid_paths = []
|
valid_paths = []
|
||||||
(data['file_map'] || {}).each do |id, file|
|
(data['file_map'] || {}).each do |id, file|
|
||||||
path = file['path_name'].starts_with?('/') ? file['path_name'][1..-1] : file['path_name']
|
path = file['path_name'].starts_with?('/') ? file['path_name'][1..-1] : file['path_name']
|
||||||
course.attachment_path_id_lookup[path] = file['migration_id']
|
migration.add_attachment_path(path, file['migration_id'])
|
||||||
course.attachment_path_id_lookup_lower[path.downcase] = file['migration_id']
|
|
||||||
if migration.import_object?("attachments", file['migration_id']) || migration.import_object?("files", file['migration_id'])
|
if migration.import_object?("attachments", file['migration_id']) || migration.import_object?("files", file['migration_id'])
|
||||||
if file['errored']
|
if file['errored']
|
||||||
migration.add_warning(t(:file_import_warning, "File %{file} could not be found", :file => File.basename(file['path_name'])))
|
migration.add_warning(t(:file_import_warning, "File %{file} could not be found", :file => File.basename(file['path_name'])))
|
||||||
|
@ -45,7 +45,7 @@ module Importers
|
||||||
:callback => callback,
|
:callback => callback,
|
||||||
:logger => logger,
|
:logger => logger,
|
||||||
:rename_files => migration.migration_settings[:files_import_allow_rename],
|
:rename_files => migration.migration_settings[:files_import_allow_rename],
|
||||||
:migration_id_map => course.attachment_path_id_lookup,
|
:migration_id_map => migration.attachment_path_id_lookup,
|
||||||
}
|
}
|
||||||
if root_path = migration.migration_settings[:files_import_root_path]
|
if root_path = migration.migration_settings[:files_import_root_path]
|
||||||
unzip_opts[:root_directory] = Folder.assert_path(
|
unzip_opts[:root_directory] = Folder.assert_path(
|
||||||
|
@ -104,38 +104,29 @@ module Importers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
migration.update_import_progress(31)
|
migration.update_import_progress(35)
|
||||||
question_data = Importers::AssessmentQuestionImporter.process_migration(data, migration); migration.update_import_progress(35)
|
question_data = Importers::AssessmentQuestionImporter.process_migration(data, migration); migration.update_import_progress(45)
|
||||||
Importers::GroupImporter.process_migration(data, migration); migration.update_import_progress(36)
|
Importers::GroupImporter.process_migration(data, migration); migration.update_import_progress(48)
|
||||||
Importers::LearningOutcomeImporter.process_migration(data, migration); migration.update_import_progress(37)
|
Importers::LearningOutcomeImporter.process_migration(data, migration); migration.update_import_progress(50)
|
||||||
Importers::RubricImporter.process_migration(data, migration); migration.update_import_progress(38)
|
Importers::RubricImporter.process_migration(data, migration); migration.update_import_progress(52)
|
||||||
course.assignment_group_no_drop_assignments = {}
|
course.assignment_group_no_drop_assignments = {}
|
||||||
Importers::AssignmentGroupImporter.process_migration(data, migration); migration.update_import_progress(39)
|
Importers::AssignmentGroupImporter.process_migration(data, migration); migration.update_import_progress(54)
|
||||||
Importers::ExternalFeedImporter.process_migration(data, migration); migration.update_import_progress(39.5)
|
Importers::ExternalFeedImporter.process_migration(data, migration); migration.update_import_progress(56)
|
||||||
Importers::GradingStandardImporter.process_migration(data, migration); migration.update_import_progress(40)
|
Importers::GradingStandardImporter.process_migration(data, migration); migration.update_import_progress(58)
|
||||||
Importers::ContextExternalToolImporter.process_migration(data, migration); migration.update_import_progress(45)
|
Importers::ContextExternalToolImporter.process_migration(data, migration); migration.update_import_progress(60)
|
||||||
|
Importers::QuizImporter.process_migration(data, migration, question_data); migration.update_import_progress(65)
|
||||||
#These need to be ran twice because they can reference each other
|
Importers::DiscussionTopicImporter.process_migration(data, migration); migration.update_import_progress(70)
|
||||||
Importers::QuizImporter.process_migration(data, migration, question_data); migration.update_import_progress(50)
|
Importers::WikiPageImporter.process_migration(data, migration); migration.update_import_progress(75)
|
||||||
Importers::DiscussionTopicImporter.process_migration(data, migration);migration.update_import_progress(55)
|
Importers::AssignmentImporter.process_migration(data, migration); migration.update_import_progress(80)
|
||||||
Importers::WikiPageImporter.process_migration(data, migration);migration.update_import_progress(60)
|
Importers::ContextModuleImporter.process_migration(data, migration); migration.update_import_progress(85)
|
||||||
Importers::AssignmentImporter.process_migration(data, migration);migration.update_import_progress(65)
|
Importers::WikiPageImporter.process_migration_course_outline(data, migration)
|
||||||
|
Importers::CalendarEventImporter.process_migration(data, migration)
|
||||||
# and second time...
|
|
||||||
Importers::ContextModuleImporter.process_migration(data, migration);migration.update_import_progress(70)
|
|
||||||
Importers::QuizImporter.process_migration(data, migration, question_data); migration.update_import_progress(72)
|
|
||||||
Importers::DiscussionTopicImporter.process_migration(data, migration);migration.update_import_progress(75)
|
|
||||||
Importers::WikiPageImporter.process_migration(data, migration);migration.update_import_progress(80)
|
|
||||||
Importers::AssignmentImporter.process_migration(data, migration);migration.update_import_progress(85)
|
|
||||||
|
|
||||||
#These aren't referenced by anything, but reference other things
|
|
||||||
Importers::CalendarEventImporter.process_migration(data, migration);migration.update_import_progress(90)
|
|
||||||
Importers::WikiPageImporter.process_migration_course_outline(data, migration);migration.update_import_progress(95)
|
|
||||||
|
|
||||||
everything_selected = !migration.copy_options || migration.is_set?(migration.copy_options[:everything])
|
everything_selected = !migration.copy_options || migration.is_set?(migration.copy_options[:everything])
|
||||||
if everything_selected || migration.is_set?(migration.copy_options[:all_course_settings])
|
if everything_selected || migration.is_set?(migration.copy_options[:all_course_settings])
|
||||||
self.import_settings_from_migration(course, data, migration); migration.update_import_progress(96)
|
self.import_settings_from_migration(course, data, migration)
|
||||||
end
|
end
|
||||||
|
migration.update_import_progress(90)
|
||||||
|
|
||||||
# be very explicit about draft state courses, but be liberal toward legacy courses
|
# be very explicit about draft state courses, but be liberal toward legacy courses
|
||||||
if course.wiki.has_no_front_page
|
if course.wiki.has_no_front_page
|
||||||
|
@ -155,7 +146,8 @@ module Importers
|
||||||
self.import_syllabus_from_migration(course, syllabus_body, migration) if syllabus_body
|
self.import_syllabus_from_migration(course, syllabus_body, migration) if syllabus_body
|
||||||
end
|
end
|
||||||
|
|
||||||
migration.add_warnings_for_missing_content_links
|
migration.resolve_content_links!
|
||||||
|
migration.update_import_progress(95)
|
||||||
|
|
||||||
begin
|
begin
|
||||||
#Adjust dates
|
#Adjust dates
|
||||||
|
@ -226,13 +218,7 @@ module Importers
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.import_syllabus_from_migration(course, syllabus_body, migration)
|
def self.import_syllabus_from_migration(course, syllabus_body, migration)
|
||||||
missing_links = []
|
course.syllabus_body = migration.convert_html(syllabus_body, :syllabus, nil, :syllabus)
|
||||||
course.syllabus_body = ImportedHtmlConverter.convert(syllabus_body, course, migration) do |warn, link|
|
|
||||||
missing_links << link if warn == :missing_link
|
|
||||||
end
|
|
||||||
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")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.import_settings_from_migration(course, data, migration)
|
def self.import_settings_from_migration(course, data, migration)
|
||||||
|
|
|
@ -46,7 +46,7 @@ module Importers
|
||||||
migration.import_object?('announcements', topic['migration_id']))
|
migration.import_object?('announcements', topic['migration_id']))
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.import_from_migration(hash, context, migration=nil, item=nil)
|
def self.import_from_migration(hash, context, migration, item=nil)
|
||||||
importer = self.new(hash, context, migration, item)
|
importer = self.new(hash, context, migration, item)
|
||||||
importer.run
|
importer.run
|
||||||
end
|
end
|
||||||
|
@ -79,11 +79,10 @@ module Importers
|
||||||
:require_initial_post].each do |attr|
|
:require_initial_post].each do |attr|
|
||||||
item.send("#{attr}=", options[attr])
|
item.send("#{attr}=", options[attr])
|
||||||
end
|
end
|
||||||
missing_links = []
|
|
||||||
|
type = item.is_a?(Announcement) ? :announcement : :discussion_topic
|
||||||
if options.message
|
if options.message
|
||||||
item.message = ImportedHtmlConverter.convert(options.message, context, migration) do |warn, link|
|
item.message = migration.convert_html(options.message, type, options[:migration_id], :message)
|
||||||
missing_links << link if warn == :missing_link
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
item.message = I18n.t('#discussion_topic.empty_message', 'No message')
|
item.message = I18n.t('#discussion_topic.empty_message', 'No message')
|
||||||
end
|
end
|
||||||
|
@ -115,7 +114,6 @@ module Importers
|
||||||
|
|
||||||
item.save_without_broadcasting!
|
item.save_without_broadcasting!
|
||||||
import_migration_item
|
import_migration_item
|
||||||
add_missing_content_links(missing_links)
|
|
||||||
item.saved_by = nil
|
item.saved_by = nil
|
||||||
item
|
item
|
||||||
end
|
end
|
||||||
|
@ -136,15 +134,7 @@ module Importers
|
||||||
end
|
end
|
||||||
|
|
||||||
def import_migration_item
|
def import_migration_item
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
end
|
|
||||||
|
|
||||||
def add_missing_content_links(missing_links)
|
|
||||||
if migration
|
|
||||||
migration.add_missing_content_links(class: item.class.to_s,
|
|
||||||
id: item.id, missing_links: missing_links,
|
|
||||||
url: "/#{context.class.to_s.underscore.pluralize}/#{context.id}/#{item.class.to_s.demodulize.underscore.pluralize}/#{item.id}")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class DiscussionTopicOptions
|
class DiscussionTopicOptions
|
||||||
|
|
|
@ -17,7 +17,7 @@ module Importers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.import_from_migration(hash, context, migration=nil, item=nil)
|
def self.import_from_migration(hash, context, migration, item=nil)
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
return nil if hash[:migration_id] && hash[:external_feeds_to_import] && !hash[:external_feeds_to_import][hash[:migration_id]]
|
return nil if hash[:migration_id] && hash[:external_feeds_to_import] && !hash[:external_feeds_to_import][hash[:migration_id]]
|
||||||
item ||= find_or_initialize_from_migration(hash, context)
|
item ||= find_or_initialize_from_migration(hash, context)
|
||||||
|
@ -28,7 +28,7 @@ module Importers
|
||||||
item.header_match = hash[:header_match] unless hash[:header_match].blank?
|
item.header_match = hash[:header_match] unless hash[:header_match].blank?
|
||||||
|
|
||||||
item.save!
|
item.save!
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
item
|
item
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ module Importers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.import_from_migration(hash, context, migration=nil, item=nil)
|
def self.import_from_migration(hash, context, migration, item=nil)
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
return nil if hash[:migration_id] && hash[:grading_standards_to_import] && !hash[:grading_standards_to_import][hash[:migration_id]]
|
return nil if hash[:migration_id] && hash[:grading_standards_to_import] && !hash[:grading_standards_to_import][hash[:migration_id]]
|
||||||
item ||= GradingStandard.where(context_id: context, context_type: context.class.to_s, migration_id: hash[:migration_id]).first if hash[:migration_id]
|
item ||= GradingStandard.where(context_id: context, context_type: context.class.to_s, migration_id: hash[:migration_id]).first if hash[:migration_id]
|
||||||
|
@ -38,7 +38,7 @@ module Importers
|
||||||
end
|
end
|
||||||
|
|
||||||
item.save!
|
item.save!
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
item
|
item
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,13 +16,13 @@ module Importers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.import_from_migration(hash, context, migration=nil, item=nil)
|
def self.import_from_migration(hash, context, migration, item=nil)
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
return nil if hash[:migration_id] && hash[:groups_to_import] && !hash[:groups_to_import][hash[:migration_id]]
|
return nil if hash[:migration_id] && hash[:groups_to_import] && !hash[:groups_to_import][hash[:migration_id]]
|
||||||
item ||= Group.where(context_id: context, context_type: context.class.to_s, id: hash[:id]).first
|
item ||= Group.where(context_id: context, context_type: context.class.to_s, id: hash[:id]).first
|
||||||
item ||= Group.where(context_id: context, context_type: context.class.to_s, migration_id: hash[:migration_id]).first if hash[:migration_id]
|
item ||= Group.where(context_id: context, context_type: context.class.to_s, migration_id: hash[:migration_id]).first if hash[:migration_id]
|
||||||
item ||= context.groups.new
|
item ||= context.groups.new
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
item.migration_id = hash[:migration_id]
|
item.migration_id = hash[:migration_id]
|
||||||
item.name = hash[:title]
|
item.name = hash[:title]
|
||||||
item.group_category = hash[:group_category].present? ?
|
item.group_category = hash[:group_category].present? ?
|
||||||
|
@ -30,7 +30,7 @@ module Importers
|
||||||
GroupCategory.imported_for(context)
|
GroupCategory.imported_for(context)
|
||||||
|
|
||||||
item.save!
|
item.save!
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
item
|
item
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -36,7 +36,7 @@ module Importers
|
||||||
root_outcome_group.adopt_outcome_group(item)
|
root_outcome_group.adopt_outcome_group(item)
|
||||||
end
|
end
|
||||||
|
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
|
|
||||||
if hash[:outcomes]
|
if hash[:outcomes]
|
||||||
hash[:outcomes].each do |child|
|
hash[:outcomes].each do |child|
|
||||||
|
|
|
@ -80,7 +80,7 @@ module Importers
|
||||||
|
|
||||||
item.save!
|
item.save!
|
||||||
|
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
else
|
else
|
||||||
item = outcome
|
item = outcome
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
module Importers
|
||||||
|
class LinkParser
|
||||||
|
module Helpers
|
||||||
|
def context
|
||||||
|
@context ||= @migration.context
|
||||||
|
end
|
||||||
|
|
||||||
|
def context_path
|
||||||
|
@context_path ||= "/#{context.class.to_s.underscore.pluralize}/#{context.id}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def relative_url?(url)
|
||||||
|
ImportedHtmlConverter.relative_url?(url)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
include Helpers
|
||||||
|
|
||||||
|
REFERENCE_KEYWORDS = %w{CANVAS_COURSE_REFERENCE CANVAS_OBJECT_REFERENCE WIKI_REFERENCE IMS_CC_FILEBASE IMS-CC-FILEBASE}
|
||||||
|
LINK_PLACEHOLDER = "LINK.PLACEHOLDER"
|
||||||
|
|
||||||
|
attr_reader :unresolved_link_map
|
||||||
|
|
||||||
|
def initialize(migration)
|
||||||
|
@migration = migration
|
||||||
|
reset!
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset!
|
||||||
|
@unresolved_link_map = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_unresolved_link(link, item_type, mig_id, field)
|
||||||
|
key = {:type => item_type, :migration_id => mig_id}
|
||||||
|
@unresolved_link_map[key] ||= {}
|
||||||
|
@unresolved_link_map[key][field] ||= []
|
||||||
|
@unresolved_link_map[key][field] << link
|
||||||
|
end
|
||||||
|
|
||||||
|
def placeholder(old_value)
|
||||||
|
"#{LINK_PLACEHOLDER}_#{Digest::MD5.hexdigest(old_value)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_link(node, attr, item_type, mig_id, field)
|
||||||
|
return unless node[attr].present?
|
||||||
|
|
||||||
|
if attr == 'value'
|
||||||
|
return unless node['name'] && node['name'] == 'src'
|
||||||
|
end
|
||||||
|
|
||||||
|
url = node[attr].dup
|
||||||
|
REFERENCE_KEYWORDS.each do |ref|
|
||||||
|
url.gsub!("%24#{ref}%24", "$#{ref}$")
|
||||||
|
end
|
||||||
|
|
||||||
|
result = parse_url(url, node, attr)
|
||||||
|
if result[:resolved]
|
||||||
|
# resolved, just replace and carry on
|
||||||
|
new_url = result[:new_url] || url
|
||||||
|
if @migration && !relative_url?(new_url) && processed_url = @migration.process_domain_substitutions(new_url)
|
||||||
|
new_url = processed_url
|
||||||
|
end
|
||||||
|
node[attr] = new_url
|
||||||
|
else
|
||||||
|
result.delete(:resolved)
|
||||||
|
if result[:link_type] == :media_object
|
||||||
|
# because we may actually change the media comment node itself
|
||||||
|
# (rather than just replacing a value), we're going to
|
||||||
|
# replace the entire node with a placeholder
|
||||||
|
result[:old_value] = node.to_xml
|
||||||
|
result[:placeholder] = placeholder(result[:old_value])
|
||||||
|
placeholder_node = Nokogiri::HTML::DocumentFragment.parse(result[:placeholder])
|
||||||
|
|
||||||
|
node.replace(placeholder_node)
|
||||||
|
else
|
||||||
|
result[:old_value] = node[attr]
|
||||||
|
result[:placeholder] = placeholder(result[:old_value])
|
||||||
|
node[attr] = result[:placeholder]
|
||||||
|
end
|
||||||
|
add_unresolved_link(result, item_type, mig_id, field)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unresolved(type, data={})
|
||||||
|
{:resolved => false, :link_type => type}.merge(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolved(new_url=nil)
|
||||||
|
{:resolved => true, :new_url => new_url}
|
||||||
|
end
|
||||||
|
|
||||||
|
# returns a hash with resolution status and data to hold onto if unresolved
|
||||||
|
def parse_url(url, node, attr)
|
||||||
|
if url =~ /wiki_page_migration_id=(.*)/
|
||||||
|
unresolved(:wiki_page, :migration_id => $1)
|
||||||
|
elsif url =~ /discussion_topic_migration_id=(.*)/
|
||||||
|
unresolved(:discussion_topic, :migration_id => $1)
|
||||||
|
elsif url =~ %r{\$CANVAS_COURSE_REFERENCE\$/modules/items/(.*)}
|
||||||
|
unresolved(:module_item, :migration_id => $1)
|
||||||
|
elsif url =~ %r{(?:\$CANVAS_OBJECT_REFERENCE\$|\$WIKI_REFERENCE\$)/([^/]*)/(.*)}
|
||||||
|
unresolved(:object, :type => $1, :migration_id => $2)
|
||||||
|
|
||||||
|
elsif url =~ %r{\$CANVAS_COURSE_REFERENCE\$/(.*)}
|
||||||
|
resolved("#{context_path}/#{$1}")
|
||||||
|
|
||||||
|
elsif url =~ %r{\$IMS(?:-|_)CC(?:-|_)FILEBASE\$/(.*)}
|
||||||
|
rel_path = URI.unescape($1)
|
||||||
|
if attr == 'href' && node['class'] && node['class'] =~ /instructure_inline_media_comment/
|
||||||
|
unresolved(:media_object, :rel_path => rel_path)
|
||||||
|
else
|
||||||
|
unresolved(:file, :rel_path => rel_path)
|
||||||
|
end
|
||||||
|
elsif attr == 'href' && node['class'] && node['class'] =~ /instructure_inline_media_comment/
|
||||||
|
# Course copy media reference, leave it alone
|
||||||
|
resolved
|
||||||
|
elsif attr == 'src' && node['class'] && node['class'] =~ /equation_image/
|
||||||
|
# Equation image, leave it alone
|
||||||
|
resolved
|
||||||
|
elsif url =~ %r{\A/assessment_questions/\d+/files/\d+}
|
||||||
|
# The file is in the context of an AQ, leave the link alone
|
||||||
|
resolved
|
||||||
|
elsif url =~ %r{\A/courses/\d+/files/\d+}
|
||||||
|
# This points to a specific file already, leave it alone
|
||||||
|
resolved
|
||||||
|
elsif @migration && @migration.for_course_copy?
|
||||||
|
# For course copies don't try to fix relative urls. Any url we can
|
||||||
|
# correctly alter was changed during the 'export' step
|
||||||
|
resolved
|
||||||
|
elsif url.start_with?('#')
|
||||||
|
# It's just a link to an anchor, leave it alone
|
||||||
|
resolved
|
||||||
|
elsif relative_url?(url)
|
||||||
|
unresolved(:file, :rel_path => URI.unescape(url))
|
||||||
|
else
|
||||||
|
resolved
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,198 @@
|
||||||
|
module Importers
|
||||||
|
class LinkReplacer
|
||||||
|
LINK_TYPE_TO_CLASS = {
|
||||||
|
:announcement => Announcement,
|
||||||
|
:assessment_question => AssessmentQuestion,
|
||||||
|
:assignment => Assignment,
|
||||||
|
:calendar_event => CalendarEvent,
|
||||||
|
:discussion_topic => DiscussionTopic,
|
||||||
|
:quiz => Quizzes::Quiz,
|
||||||
|
:wiki_page => WikiPage
|
||||||
|
}
|
||||||
|
|
||||||
|
include LinkParser::Helpers
|
||||||
|
|
||||||
|
def initialize(migration)
|
||||||
|
@migration = migration
|
||||||
|
end
|
||||||
|
|
||||||
|
def replace_placeholders!(link_map)
|
||||||
|
load_questions!(link_map)
|
||||||
|
|
||||||
|
link_map.each do |item_key, field_links|
|
||||||
|
begin
|
||||||
|
item_key[:item] ||= retrieve_item(item_key)
|
||||||
|
|
||||||
|
add_missing_link_warnings!(item_key, field_links)
|
||||||
|
|
||||||
|
replace_item_placeholders!(item_key, field_links)
|
||||||
|
rescue
|
||||||
|
@migration.add_warning("An error occurred while translating content links", $!)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# these don't get added to the list of imported migration items
|
||||||
|
def load_questions!(link_map)
|
||||||
|
aq_item_keys = link_map.keys.select{|item_key| item_key[:type] == :assessment_question}
|
||||||
|
aq_item_keys.each_slice(100) do |item_keys|
|
||||||
|
context.assessment_questions.where(:migration_id => item_keys.map{|ikey| ikey[:migration_id]}).each do |aq|
|
||||||
|
item_keys.detect{|ikey| ikey[:migration_id] == aq.migration_id}[:item] = aq
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
qq_item_keys = link_map.keys.select{|item_key| item_key[:type] == :quiz_question}
|
||||||
|
qq_item_keys.each_slice(100) do |item_keys|
|
||||||
|
context.quiz_questions.where(:migration_id => item_keys.map{|ikey| ikey[:migration_id]}).each do |qq|
|
||||||
|
item_keys.detect{|ikey| ikey[:migration_id] == qq.migration_id}[:item] = qq
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def retrieve_item(item_key)
|
||||||
|
klass = LINK_TYPE_TO_CLASS[item_key[:type]]
|
||||||
|
return unless klass
|
||||||
|
item = @migration.find_imported_migration_item(klass, item_key[:migration_id])
|
||||||
|
raise "item not found" unless item
|
||||||
|
item
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_missing_link_warnings!(item_key, field_links)
|
||||||
|
fix_issue_url = nil
|
||||||
|
field_links.each do |field, links|
|
||||||
|
missing_links = links.select{|link| link[:missing_url] || !link[:new_value]}
|
||||||
|
if missing_links.any?
|
||||||
|
fix_issue_url ||= fix_issue_url(item_key)
|
||||||
|
type = item_key[:type].to_s.humanize.titleize
|
||||||
|
@migration.add_warning_for_missing_content_links(type, field, missing_links, fix_issue_url)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def fix_issue_url(item_key)
|
||||||
|
item = item_key[:item]
|
||||||
|
|
||||||
|
case item_key[:type]
|
||||||
|
when :assessment_question
|
||||||
|
"#{context_path}/question_banks/#{item.assessment_question_bank_id}#question_#{item.id}_question_text"
|
||||||
|
when :syllabus
|
||||||
|
"#{context_path}/assignments/syllabus"
|
||||||
|
when :wiki_page
|
||||||
|
"#{context_path}/pages/#{item.url}"
|
||||||
|
else
|
||||||
|
"#{context_path}/#{item.class.to_s.demodulize.underscore.pluralize}/#{item.id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def replace_item_placeholders!(item_key, field_links, skip_associations=false)
|
||||||
|
case item_key[:type]
|
||||||
|
when :syllabus
|
||||||
|
syllabus = context.syllabus_body
|
||||||
|
if sub_placeholders!(syllabus, field_links.values.flatten)
|
||||||
|
context.class.where(:id => context.id).update_all(:syllabus_body => syllabus)
|
||||||
|
end
|
||||||
|
when :assessment_question
|
||||||
|
process_assessment_question!(item_key[:item], field_links.values.flatten)
|
||||||
|
when :quiz_question
|
||||||
|
process_quiz_question!(item_key[:item], field_links.values.flatten)
|
||||||
|
else
|
||||||
|
item = item_key[:item]
|
||||||
|
item_updates = {}
|
||||||
|
field_links.each do |field, links|
|
||||||
|
html = item.read_attribute(field)
|
||||||
|
if sub_placeholders!(html, links)
|
||||||
|
item_updates[field] = html
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if item_updates.present?
|
||||||
|
item.class.where(:id => item.id).update_all(item_updates)
|
||||||
|
end
|
||||||
|
|
||||||
|
unless skip_associations
|
||||||
|
process_assignment_types!(item, field_links.values.flatten)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# returns false if no substitutions were made
|
||||||
|
def sub_placeholders!(html, links)
|
||||||
|
subbed = false
|
||||||
|
links.each do |link|
|
||||||
|
new_value = link[:new_value] || link[:old_value]
|
||||||
|
if html.sub!(link[:placeholder], new_value)
|
||||||
|
subbed = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
subbed
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_assignment_types!(item, links)
|
||||||
|
case item
|
||||||
|
when Assignment
|
||||||
|
if item.discussion_topic
|
||||||
|
replace_item_placeholders!({:item => item.discussion_topic}, {:message => links}, true)
|
||||||
|
end
|
||||||
|
if item.quiz
|
||||||
|
replace_item_placeholders!({:item => item.quiz}, {:description => links}, true)
|
||||||
|
end
|
||||||
|
when DiscussionTopic
|
||||||
|
if item.assignment
|
||||||
|
replace_item_placeholders!({:item => item.assignment}, {:description => links}, true)
|
||||||
|
end
|
||||||
|
when Quizzes::Quiz
|
||||||
|
if item.assignment
|
||||||
|
replace_item_placeholders!({:item => item.assignment}, {:description => links}, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_assessment_question!(aq, links)
|
||||||
|
# we have to do a little bit more here because the question_data can get copied all over
|
||||||
|
quiz_ids = []
|
||||||
|
Quizzes::QuizQuestion.where(:assessment_question_id => aq.id).find_each do |qq|
|
||||||
|
qq_yaml = qq['question_data'].to_yaml
|
||||||
|
if sub_placeholders!(qq_yaml, links)
|
||||||
|
Quizzes::QuizQuestion.where(:id => qq.id).update_all(:question_data => qq_yaml)
|
||||||
|
quiz_ids << qq.quiz_id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if quiz_ids.any?
|
||||||
|
Quizzes::Quiz.where(:id => quiz_ids.uniq).where("quiz_data IS NOT NULL").find_each do |quiz|
|
||||||
|
quiz_yaml = quiz['quiz_data'].to_yaml
|
||||||
|
if sub_placeholders!(quiz_yaml, links)
|
||||||
|
Quizzes::Quiz.where(:id => quiz.id).update_all(:quiz_data => quiz_yaml)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# we have to do some special link translations for files in assessment questions
|
||||||
|
# because we stopped doing them in the regular importer
|
||||||
|
# basically just moving them to the question context
|
||||||
|
links.each do |link|
|
||||||
|
next unless link[:new_value]
|
||||||
|
link[:new_value] = aq.translate_file_link(link[:new_value])
|
||||||
|
end
|
||||||
|
|
||||||
|
aq_yaml = aq['question_data'].to_yaml
|
||||||
|
if sub_placeholders!(aq_yaml, links)
|
||||||
|
AssessmentQuestion.where(:id => aq.id).update_all(:question_data => aq_yaml)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_quiz_question!(qq, links)
|
||||||
|
qq_yaml = qq['question_data'].to_yaml
|
||||||
|
if sub_placeholders!(qq_yaml, links)
|
||||||
|
Quizzes::QuizQuestion.where(:id => qq.id).update_all(:question_data => qq_yaml)
|
||||||
|
end
|
||||||
|
|
||||||
|
quiz = Quizzes::Quiz.where(:id => qq.quiz_id).where("quiz_data IS NOT NULL").first
|
||||||
|
if quiz
|
||||||
|
quiz_yaml = quiz['quiz_data'].to_yaml
|
||||||
|
if sub_placeholders!(quiz_yaml, links)
|
||||||
|
Quizzes::Quiz.where(:id => quiz.id).update_all(:quiz_data => quiz_yaml)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,165 @@
|
||||||
|
module Importers
|
||||||
|
class LinkResolver
|
||||||
|
include LinkParser::Helpers
|
||||||
|
|
||||||
|
def initialize(migration)
|
||||||
|
@migration = migration
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_links!(link_map)
|
||||||
|
link_map.each do |item_key, field_links|
|
||||||
|
field_links.each do |_field, links|
|
||||||
|
links.each do |link|
|
||||||
|
resolve_link!(link)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# finds the :new_value to use to replace the placeholder
|
||||||
|
def resolve_link!(link)
|
||||||
|
case link[:link_type]
|
||||||
|
when :wiki_page
|
||||||
|
if linked_wiki = context.wiki.wiki_pages.where(migration_id: link[:migration_id]).select('url').first
|
||||||
|
link[:new_value] = "#{context_path}/pages/#{linked_wiki.url}"
|
||||||
|
end
|
||||||
|
when :discussion_topic
|
||||||
|
if linked_topic = context.discussion_topics.where(migration_id: link[:migration_id]).select('id').first
|
||||||
|
link[:new_value] = "#{context_path}/discussion_topics/#{linked_topic.id}"
|
||||||
|
end
|
||||||
|
when :module_item
|
||||||
|
if tag = context.context_module_tags.where(:migration_id => link[:migration_id]).select('id').first
|
||||||
|
link[:new_value] = "#{context_path}/modules/items/#{tag.id}"
|
||||||
|
end
|
||||||
|
when :object
|
||||||
|
type = link[:type]
|
||||||
|
migration_id = link[:migration_id]
|
||||||
|
|
||||||
|
type_for_url = type
|
||||||
|
type = 'context_modules' if type == 'modules'
|
||||||
|
type = 'pages' if type == 'wiki'
|
||||||
|
if type == 'pages'
|
||||||
|
link[:new_value] = "#{context_path}/pages/#{migration_id}"
|
||||||
|
elsif type == 'attachments'
|
||||||
|
if att = context.attachments.where(migration_id: migration_id).first
|
||||||
|
link[:new_value] = "#{context_path}/files/#{att.id}/preview"
|
||||||
|
end
|
||||||
|
elsif context.respond_to?(type) && context.send(type).respond_to?(:where)
|
||||||
|
if object = context.send(type).where(migration_id: migration_id).first
|
||||||
|
link[:new_value] = "#{context_path}/#{type_for_url}/#{object.id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
when :media_object
|
||||||
|
# because we actually might change the node itself
|
||||||
|
# this part is a little trickier
|
||||||
|
# tl;dr we've replaced the entire node with the placeholder
|
||||||
|
# see LinkParser for details
|
||||||
|
|
||||||
|
rel_path = link[:rel_path]
|
||||||
|
node = Nokogiri::HTML::DocumentFragment.parse(link[:old_value]).children.first
|
||||||
|
new_url = resolve_media_comment_data(node, rel_path)
|
||||||
|
new_url ||= resolve_relative_file_url(rel_path)
|
||||||
|
|
||||||
|
unless new_url
|
||||||
|
new_url ||= missing_relative_file_url(rel_path)
|
||||||
|
link[:missing_url] = new_url
|
||||||
|
end
|
||||||
|
node['href'] = new_url
|
||||||
|
link[:new_value] = node.to_xml
|
||||||
|
when :file
|
||||||
|
rel_path = link[:rel_path]
|
||||||
|
new_url = resolve_relative_file_url(rel_path)
|
||||||
|
unless new_url
|
||||||
|
new_url = missing_relative_file_url(rel_path)
|
||||||
|
link[:missing_url] = new_url
|
||||||
|
end
|
||||||
|
link[:new_value] = new_url
|
||||||
|
else
|
||||||
|
raise "unrecognized link_type in unresolved link"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def missing_relative_file_url(rel_path)
|
||||||
|
# the rel_path should already be escaped
|
||||||
|
File.join(URI::escape("#{context_path}/file_contents/#{Folder.root_folders(context).first.name}"), rel_path.gsub(" ", "%20"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_file_in_context(rel_path)
|
||||||
|
mig_id = nil
|
||||||
|
# This is for backward-compatibility: canvas attachment filenames are escaped
|
||||||
|
# with '+' for spaces and older exports have files with that instead of %20
|
||||||
|
alt_rel_path = rel_path.gsub('+', ' ')
|
||||||
|
if @migration.attachment_path_id_lookup
|
||||||
|
mig_id ||= @migration.attachment_path_id_lookup[rel_path]
|
||||||
|
mig_id ||= @migration.attachment_path_id_lookup[alt_rel_path]
|
||||||
|
end
|
||||||
|
if !mig_id && @migration.attachment_path_id_lookup_lower
|
||||||
|
mig_id ||= @migration.attachment_path_id_lookup_lower[rel_path.downcase]
|
||||||
|
mig_id ||= @migration.attachment_path_id_lookup_lower[alt_rel_path.downcase]
|
||||||
|
end
|
||||||
|
|
||||||
|
mig_id && context.attachments.where(migration_id: mig_id).first
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_relative_file_url(rel_path)
|
||||||
|
new_url = nil
|
||||||
|
split = rel_path.split('?')
|
||||||
|
qs = split.pop if split.length > 1
|
||||||
|
rel_path = split.join('?')
|
||||||
|
|
||||||
|
rel_path_parts = Pathname.new(rel_path).each_filename.to_a
|
||||||
|
|
||||||
|
# e.g. start with "a/b/c.txt" then try "b/c.txt" then try "c.txt"
|
||||||
|
while new_url.nil? && rel_path_parts.length > 0
|
||||||
|
sub_path = File.join(rel_path_parts)
|
||||||
|
if file = find_file_in_context(sub_path)
|
||||||
|
new_url = "#{context_path}/files/#{file.id}"
|
||||||
|
# support other params in the query string, that were exported from the
|
||||||
|
# original path components and query string. see
|
||||||
|
# CCHelper::file_query_string
|
||||||
|
params = Rack::Utils.parse_nested_query(qs.presence || "")
|
||||||
|
qs = []
|
||||||
|
new_action = ""
|
||||||
|
params.each do |k,v|
|
||||||
|
case k
|
||||||
|
when /canvas_qs_(.*)/
|
||||||
|
qs << "#{Rack::Utils.escape($1)}=#{Rack::Utils.escape(v)}"
|
||||||
|
when /canvas_(.*)/
|
||||||
|
new_action += "/#{$1}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if new_action.present?
|
||||||
|
new_url += new_action
|
||||||
|
else
|
||||||
|
new_url += "/preview"
|
||||||
|
end
|
||||||
|
new_url += "?#{qs.join("&")}" if qs.present?
|
||||||
|
end
|
||||||
|
rel_path_parts.shift
|
||||||
|
end
|
||||||
|
new_url
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_media_comment_data(node, rel_path)
|
||||||
|
if context.respond_to?(:attachment_path_id_lookup) &&
|
||||||
|
context.attachment_path_id_lookup &&
|
||||||
|
context.attachment_path_id_lookup[rel_path]
|
||||||
|
file = context.attachments.where(migration_id: context.attachment_path_id_lookup[rel_path]).first
|
||||||
|
if file && file.media_object
|
||||||
|
media_id = file.media_object.media_id
|
||||||
|
node['id'] = "media_comment_#{media_id}"
|
||||||
|
return "/media_objects/#{media_id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if node['id'] && node['id'] =~ /\Amedia_comment_(.+)\z/
|
||||||
|
return "/media_objects/#{$1}"
|
||||||
|
else
|
||||||
|
node.delete('class')
|
||||||
|
node.delete('id')
|
||||||
|
node.delete('style')
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -160,7 +160,7 @@ module Importers
|
||||||
|
|
||||||
# Import a quiz from a hash.
|
# Import a quiz from a hash.
|
||||||
# It assumes that all the referenced questions are already in the database
|
# It assumes that all the referenced questions are already in the database
|
||||||
def self.import_from_migration(hash, context, migration=nil, question_data=nil, item=nil, allow_update = false)
|
def self.import_from_migration(hash, context, migration, question_data=nil, item=nil, allow_update = false)
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
# there might not be an import id if it's just a text-only type...
|
# there might not be an import id if it's just a text-only type...
|
||||||
item ||= Quizzes::Quiz.where(context_type: context.class.to_s, context_id: context, id: hash[:id]).first if hash[:id]
|
item ||= Quizzes::Quiz.where(context_type: context.class.to_s, context_id: context, id: hash[:id]).first if hash[:id]
|
||||||
|
@ -184,10 +184,7 @@ module Importers
|
||||||
item.scoring_policy = hash[:which_attempt_to_keep] if hash[:which_attempt_to_keep]
|
item.scoring_policy = hash[:which_attempt_to_keep] if hash[:which_attempt_to_keep]
|
||||||
|
|
||||||
missing_links = []
|
missing_links = []
|
||||||
item.description = ImportedHtmlConverter.convert(hash[:description], context, migration) do |warn, link|
|
item.description = migration.convert_html(hash[:description], :quiz, hash[:migration_id], :description)
|
||||||
missing_links << link if warn == :missing_link
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
%w[
|
%w[
|
||||||
migration_id
|
migration_id
|
||||||
|
@ -220,14 +217,6 @@ module Importers
|
||||||
item.saved_by = :migration
|
item.saved_by = :migration
|
||||||
item.save!
|
item.save!
|
||||||
|
|
||||||
if migration
|
|
||||||
migration.add_missing_content_links(
|
|
||||||
:class => item.class.to_s,
|
|
||||||
:id => item.id, :missing_links => missing_links,
|
|
||||||
:url => "/#{context.class.to_s.demodulize.underscore.pluralize}/#{context.id}/#{item.class.to_s.demodulize.underscore.pluralize}/#{item.id}"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
if question_data
|
if question_data
|
||||||
question_data[:qq_ids] ||= {}
|
question_data[:qq_ids] ||= {}
|
||||||
hash[:questions] ||= []
|
hash[:questions] ||= []
|
||||||
|
@ -295,7 +284,7 @@ module Importers
|
||||||
item.save
|
item.save
|
||||||
item.assignment.save if item.assignment && item.assignment.changed?
|
item.assignment.save if item.assignment && item.assignment.changed?
|
||||||
|
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
item.saved_by = nil
|
item.saved_by = nil
|
||||||
item
|
item
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,9 +3,9 @@ module Importers
|
||||||
|
|
||||||
self.item_class = Quizzes::QuizQuestion
|
self.item_class = Quizzes::QuizQuestion
|
||||||
|
|
||||||
def self.import_from_migration(aq_hash, qq_hash, position, qq_ids, context, migration=nil, quiz=nil, quiz_group=nil)
|
def self.import_from_migration(aq_hash, qq_hash, position, qq_ids, context, migration, quiz=nil, quiz_group=nil)
|
||||||
unless aq_hash[:prepped_for_import]
|
unless aq_hash[:prepped_for_import]
|
||||||
Importers::AssessmentQuestionImporter.prep_for_import(aq_hash, context, migration)
|
Importers::AssessmentQuestionImporter.prep_for_import(aq_hash, migration, :quiz_question)
|
||||||
end
|
end
|
||||||
|
|
||||||
hash = aq_hash.dup
|
hash = aq_hash.dup
|
||||||
|
@ -14,16 +14,17 @@ module Importers
|
||||||
hash[:points_possible] = 0 if hash[:points_possible].to_f < 0
|
hash[:points_possible] = 0 if hash[:points_possible].to_f < 0
|
||||||
|
|
||||||
mig_id = qq_hash['quiz_question_migration_id'] || qq_hash['migration_id']
|
mig_id = qq_hash['quiz_question_migration_id'] || qq_hash['migration_id']
|
||||||
|
|
||||||
if id = qq_ids[mig_id]
|
if id = qq_ids[mig_id]
|
||||||
Quizzes::QuizQuestion.where(id: id).update_all(quiz_group_id: quiz_group,
|
Quizzes::QuizQuestion.where(id: id).update_all(quiz_group_id: quiz_group,
|
||||||
assessment_question_id: hash['assessment_question_id'], question_data: hash.to_yaml,
|
assessment_question_id: hash['assessment_question_id'], question_data: hash.to_yaml,
|
||||||
created_at: Time.now.utc, updated_at: Time.now.utc, migration_id: mig_id,
|
created_at: Time.now.utc, updated_at: Time.now.utc, migration_id: mig_id,
|
||||||
position: position)
|
position: position)
|
||||||
else
|
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])
|
args = [quiz && quiz.id, quiz_group && quiz_group.id, hash['assessment_question_id'],
|
||||||
INSERT INTO quiz_questions (quiz_id, quiz_group_id, assessment_question_id, question_data, created_at, updated_at, migration_id, position)
|
hash.to_yaml, Time.now.utc, Time.now.utc, mig_id, position]
|
||||||
VALUES (?,?,?,?,?,?,?,?)
|
query = self.item_class.send(:sanitize_sql, [<<-SQL, *args])
|
||||||
|
INSERT INTO quiz_questions (quiz_id, quiz_group_id, assessment_question_id, question_data, created_at, updated_at, migration_id, position)
|
||||||
|
VALUES (?,?,?,?,?,?,?,?)
|
||||||
SQL
|
SQL
|
||||||
qq_ids[mig_id] = self.item_class.connection.insert(query, "#{self.item_class.name} Create",
|
qq_ids[mig_id] = self.item_class.connection.insert(query, "#{self.item_class.name} Create",
|
||||||
self.item_class.primary_key, nil, self.item_class.sequence_name)
|
self.item_class.primary_key, nil, self.item_class.sequence_name)
|
||||||
|
|
|
@ -60,7 +60,7 @@ module Importers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
item.save!
|
item.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ module Importers
|
||||||
end
|
end
|
||||||
private_class_method :wiki_page_migration?
|
private_class_method :wiki_page_migration?
|
||||||
|
|
||||||
def self.import_from_migration(hash, context, migration=nil, item=nil)
|
def self.import_from_migration(hash, context, migration, item=nil)
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
item ||= WikiPage.where(wiki_id: context.wiki, id: hash[:id]).first
|
item ||= WikiPage.where(wiki_id: context.wiki, id: hash[:id]).first
|
||||||
item ||= WikiPage.where(wiki_id: context.wiki, migration_id: hash[:migration_id]).first
|
item ||= WikiPage.where(wiki_id: context.wiki, migration_id: hash[:migration_id]).first
|
||||||
|
@ -71,7 +71,7 @@ module Importers
|
||||||
end
|
end
|
||||||
|
|
||||||
item.set_as_front_page! if !!hash[:front_page] && context.wiki.has_no_front_page
|
item.set_as_front_page! if !!hash[:front_page] && context.wiki.has_no_front_page
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
|
|
||||||
item.migration_id = hash[:migration_id]
|
item.migration_id = hash[:migration_id]
|
||||||
(hash[:contents] || []).each do |sub_item|
|
(hash[:contents] || []).each do |sub_item|
|
||||||
|
@ -81,7 +81,6 @@ module Importers
|
||||||
}), context, migration)
|
}), context, migration)
|
||||||
end
|
end
|
||||||
return if hash[:type] && ['folder', 'FOLDER_TYPE'].member?(hash[:type]) && hash[:linked_resource_id]
|
return if hash[:type] && ['folder', 'FOLDER_TYPE'].member?(hash[:type]) && hash[:linked_resource_id]
|
||||||
missing_links = {}
|
|
||||||
allow_save = true
|
allow_save = true
|
||||||
if hash[:type] == 'linked_resource' || hash[:type] == "URL_TYPE"
|
if hash[:type] == 'linked_resource' || hash[:type] == "URL_TYPE"
|
||||||
allow_save = false
|
allow_save = false
|
||||||
|
@ -89,21 +88,15 @@ module Importers
|
||||||
item.title = hash[:title] unless hash[:root_folder]
|
item.title = hash[:title] unless hash[:root_folder]
|
||||||
description = ""
|
description = ""
|
||||||
if hash[:header]
|
if hash[:header]
|
||||||
missing_links[:header] = []
|
|
||||||
if hash[:header][:is_html]
|
if hash[:header][:is_html]
|
||||||
description += ImportedHtmlConverter.convert(hash[:header][:body] || "", context, migration) do |warn, link|
|
description += migration.convert_html(hash[:header][:body], :wiki_page, hash[:migration_id], :body)
|
||||||
missing_links[:header] << link if warn == :missing_link
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
description += ImportedHtmlConverter.convert_text(hash[:header][:body] || [""], context)
|
description += migration.convert_text(hash[:header][:body] || [""])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
missing_links[:description] = []
|
|
||||||
if hash[:description]
|
if hash[:description]
|
||||||
description += ImportedHtmlConverter.convert(hash[:description], context, migration) do |warn, link|
|
description += migration.convert_html(hash[:description], :wiki_page, hash[:migration_id], :body)
|
||||||
missing_links[:description] << link if warn == :missing_link
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
contents = ""
|
contents = ""
|
||||||
|
@ -120,11 +113,8 @@ module Importers
|
||||||
end
|
end
|
||||||
description += "\n<h2>#{sub_item[:title]}</h2>\n" if sub_item[:title]
|
description += "\n<h2>#{sub_item[:title]}</h2>\n" if sub_item[:title]
|
||||||
|
|
||||||
missing_links[:sub_item] = []
|
|
||||||
if sub_item[:description]
|
if sub_item[:description]
|
||||||
description += ImportedHtmlConverter.convert(sub_item[:description], context, migration) do |warn, link|
|
description += migration.convert_html(sub_item[:description], :wiki_page, hash[:migration_id], :body)
|
||||||
missing_links[:sub_item] << link if warn == :missing_link
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
elsif sub_item[:type] == 'linked_resource'
|
elsif sub_item[:type] == 'linked_resource'
|
||||||
|
@ -159,13 +149,10 @@ module Importers
|
||||||
description += "<ul>\n#{contents}\n</ul>" if contents && contents.length > 0
|
description += "<ul>\n#{contents}\n</ul>" if contents && contents.length > 0
|
||||||
|
|
||||||
if hash[:footer]
|
if hash[:footer]
|
||||||
missing_links[:footer] = []
|
|
||||||
if hash[:footer][:is_html]
|
if hash[:footer][:is_html]
|
||||||
description += ImportedHtmlConverter.convert(hash[:footer][:body] || "", context, migration) do |warn, link|
|
description += migration.convert_html(hash[:footer][:body], :wiki_page, hash[:migration_id], :body)
|
||||||
missing_links[:footer] << link if warn == :missing_link
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
description += ImportedHtmlConverter.convert_text(hash[:footer][:body] || [""], context)
|
description += migration.convert_text(hash[:footer][:body] || "")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -194,16 +181,12 @@ module Importers
|
||||||
#it's an actual wiki page
|
#it's an actual wiki page
|
||||||
item.title = hash[:title].presence || item.url.presence || "unnamed page"
|
item.title = hash[:title].presence || item.url.presence || "unnamed page"
|
||||||
if item.title.length > WikiPage::TITLE_LENGTH
|
if item.title.length > WikiPage::TITLE_LENGTH
|
||||||
if migration
|
migration.add_warning(t('warnings.truncated_wiki_title',
|
||||||
migration.add_warning(t('warnings.truncated_wiki_title', "The title of the following wiki page was truncated: %{title}", :title => item.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
|
item.title.splice!(0...WikiPage::TITLE_LENGTH) # truncate too-long titles
|
||||||
end
|
end
|
||||||
|
|
||||||
missing_links[:body] = []
|
item.body = migration.convert_html(hash[:text], :wiki_page, hash[:migration_id], :body)
|
||||||
item.body = ImportedHtmlConverter.convert(hash[:text] || "", context, migration) do |warn, link|
|
|
||||||
missing_links[:body] << link if warn == :missing_link
|
|
||||||
end
|
|
||||||
|
|
||||||
item.editing_roles = hash[:editing_roles] if hash[:editing_roles].present?
|
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?
|
item.notify_of_update = hash[:notify_of_update] if !hash[:notify_of_update].nil?
|
||||||
|
@ -215,14 +198,7 @@ module Importers
|
||||||
item.user = nil
|
item.user = nil
|
||||||
end
|
end
|
||||||
item.save_without_broadcasting!
|
item.save_without_broadcasting!
|
||||||
migration.add_imported_item(item) if migration
|
migration.add_imported_item(item)
|
||||||
if migration
|
|
||||||
missing_links.each do |field, missing_links|
|
|
||||||
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}/pages/#{item.url}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return item
|
return item
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -81,8 +81,8 @@ describe "Converting Blackboard Vista qti" do
|
||||||
hash[:answers].each { |a| a.delete(:id) }
|
hash[:answers].each { |a| a.delete(:id) }
|
||||||
expect(hash.reject{|k,v| KEYS_TO_IGNORE.include?(k.to_s)}).to eq VistaExpected::TRUE_FALSE2
|
expect(hash.reject{|k,v| KEYS_TO_IGNORE.include?(k.to_s)}).to eq VistaExpected::TRUE_FALSE2
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should convert image reference" do
|
it "should convert image reference" do
|
||||||
hash = get_question_hash(vista_question_dir, 'mc', delete_answer_ids=true, opts={})
|
hash = get_question_hash(vista_question_dir, 'mc', delete_answer_ids=true, opts={})
|
||||||
expect(hash[:question_text]).to match %r{\$CANVAS_OBJECT_REFERENCE\$/attachments/67320753001}
|
expect(hash[:question_text]).to match %r{\$CANVAS_OBJECT_REFERENCE\$/attachments/67320753001}
|
||||||
end
|
end
|
||||||
|
@ -98,7 +98,7 @@ describe "Converting Blackboard Vista qti" do
|
||||||
hash = get_question("ID_4609865577341")
|
hash = get_question("ID_4609865577341")
|
||||||
expect(hash.reject{|k,v| KEYS_TO_IGNORE.include?(k.to_s)}).to eq VistaExpected::TRUE_FALSE
|
expect(hash.reject{|k,v| KEYS_TO_IGNORE.include?(k.to_s)}).to eq VistaExpected::TRUE_FALSE
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should convert multiple choice questions with multiple correct answers (multiple answer)" do
|
it "should convert multiple choice questions with multiple correct answers (multiple answer)" do
|
||||||
hash = get_question("ID_4609865392341")
|
hash = get_question("ID_4609865392341")
|
||||||
expect(hash.reject{|k,v| KEYS_TO_IGNORE.include?(k.to_s)}).to eq VistaExpected::MULTIPLE_ANSWER
|
expect(hash.reject{|k,v| KEYS_TO_IGNORE.include?(k.to_s)}).to eq VistaExpected::MULTIPLE_ANSWER
|
||||||
|
@ -147,7 +147,7 @@ describe "Converting Blackboard Vista qti" do
|
||||||
hash = get_question("ID_4609885376341")
|
hash = get_question("ID_4609885376341")
|
||||||
expect(hash.reject{|k,v| KEYS_TO_IGNORE.include?(k.to_s)}).to eq VistaExpected::COMBINATION
|
expect(hash.reject{|k,v| KEYS_TO_IGNORE.include?(k.to_s)}).to eq VistaExpected::COMBINATION
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should convert fill in multiple blanks questions" do
|
it "should convert fill in multiple blanks questions" do
|
||||||
hash = get_question("ID_4609842630341")
|
hash = get_question("ID_4609842630341")
|
||||||
expect(hash.reject{|k,v| KEYS_TO_IGNORE.include?(k.to_s)}).to eq VistaExpected::FILL_IN_MULTIPLE_BLANKS
|
expect(hash.reject{|k,v| KEYS_TO_IGNORE.include?(k.to_s)}).to eq VistaExpected::FILL_IN_MULTIPLE_BLANKS
|
||||||
|
@ -209,7 +209,7 @@ module VistaExpected
|
||||||
{:text=>"False", :weight=>0, :migration_id=>"false"}],
|
{:text=>"False", :weight=>0, :migration_id=>"false"}],
|
||||||
:question_type=>"true_false_question",
|
:question_type=>"true_false_question",
|
||||||
:migration_id=>"ID_4609865577341"}.with_indifferent_access
|
:migration_id=>"ID_4609865577341"}.with_indifferent_access
|
||||||
|
|
||||||
TRUE_FALSE2 = {:correct_comments=>"",
|
TRUE_FALSE2 = {:correct_comments=>"",
|
||||||
:points_possible=>1,
|
:points_possible=>1,
|
||||||
:question_name=>"True/False",
|
:question_name=>"True/False",
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
class Canvas::Migration::Worker::CourseCopyWorker < Struct.new(:migration_id)
|
class Canvas::Migration::Worker::CourseCopyWorker < Struct.new(:migration_id)
|
||||||
def perform(cm=nil)
|
def perform(cm=nil)
|
||||||
cm ||= ContentMigration.find migration_id
|
cm ||= ContentMigration.find migration_id
|
||||||
|
|
||||||
cm.workflow_state = :pre_processing
|
cm.workflow_state = :pre_processing
|
||||||
cm.reset_job_progress
|
cm.reset_job_progress
|
||||||
cm.migration_settings[:skip_import_notification] = true
|
cm.migration_settings[:skip_import_notification] = true
|
||||||
|
|
|
@ -29,7 +29,7 @@ module CC::Importer::Standard
|
||||||
|
|
||||||
MANIFEST_FILE = "imsmanifest.xml"
|
MANIFEST_FILE = "imsmanifest.xml"
|
||||||
SUPPORTED_TYPES = /assessment\z|\Aassignment|\Aimswl|\Aimsbasiclti|\Aimsdt|webcontent|learning-application-resource\z/
|
SUPPORTED_TYPES = /assessment\z|\Aassignment|\Aimswl|\Aimsbasiclti|\Aimsdt|webcontent|learning-application-resource\z/
|
||||||
|
|
||||||
attr_accessor :resources
|
attr_accessor :resources
|
||||||
|
|
||||||
# settings will use these keys: :course_name, :base_download_dir
|
# settings will use these keys: :course_name, :base_download_dir
|
||||||
|
@ -92,7 +92,7 @@ module CC::Importer::Standard
|
||||||
@file_path_migration_id[path] || @file_path_migration_id[path.gsub(%r{\$[^$]*\$|\.\./}, '')] ||
|
@file_path_migration_id[path] || @file_path_migration_id[path.gsub(%r{\$[^$]*\$|\.\./}, '')] ||
|
||||||
@file_path_migration_id[path.gsub(%r{\$[^$]*\$|\.\./}, '').sub(WEB_RESOURCES_FOLDER + '/', '')]
|
@file_path_migration_id[path.gsub(%r{\$[^$]*\$|\.\./}, '').sub(WEB_RESOURCES_FOLDER + '/', '')]
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_canvas_att_replacement_url(path, resource_dir=nil)
|
def get_canvas_att_replacement_url(path, resource_dir=nil)
|
||||||
if path.start_with?('../')
|
if path.start_with?('../')
|
||||||
if url = get_canvas_att_replacement_url(path.sub('../', ''), resource_dir)
|
if url = get_canvas_att_replacement_url(path.sub('../', ''), resource_dir)
|
||||||
|
@ -132,7 +132,7 @@ module CC::Importer::Standard
|
||||||
@file_path_migration_id[file[:path_name]] = file[:migration_id]
|
@file_path_migration_id[file[:path_name]] = file[:migration_id]
|
||||||
add_file(file)
|
add_file(file)
|
||||||
end
|
end
|
||||||
|
|
||||||
FILEBASE_REGEX = /\$IMS[-_]CC[-_]FILEBASE\$/
|
FILEBASE_REGEX = /\$IMS[-_]CC[-_]FILEBASE\$/
|
||||||
def replace_urls(html, resource_dir=nil)
|
def replace_urls(html, resource_dir=nil)
|
||||||
return "" if html.blank?
|
return "" if html.blank?
|
||||||
|
@ -182,6 +182,6 @@ module CC::Importer::Standard
|
||||||
|
|
||||||
tools
|
tools
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,141 +23,27 @@ class ImportedHtmlConverter
|
||||||
include HtmlTextHelper
|
include HtmlTextHelper
|
||||||
|
|
||||||
CONTAINER_TYPES = ['div', 'p', 'body']
|
CONTAINER_TYPES = ['div', 'p', 'body']
|
||||||
REFERENCE_KEYWORDS = %w{CANVAS_COURSE_REFERENCE CANVAS_OBJECT_REFERENCE WIKI_REFERENCE IMS_CC_FILEBASE IMS-CC-FILEBASE}
|
LINK_ATTRS = ['rel', 'href', 'src', 'data', 'value']
|
||||||
# yields warnings
|
|
||||||
def self.convert(html, context, migration=nil, opts={})
|
attr_reader :link_parser, :link_resolver, :link_replacer
|
||||||
|
|
||||||
|
def initialize(migration)
|
||||||
|
@migration = migration
|
||||||
|
@link_parser = Importers::LinkParser.new(migration)
|
||||||
|
@link_resolver = Importers::LinkResolver.new(migration)
|
||||||
|
@link_replacer = Importers::LinkReplacer.new(migration)
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert(html, item_type, mig_id, field, opts={})
|
||||||
doc = Nokogiri::HTML(html || "")
|
doc = Nokogiri::HTML(html || "")
|
||||||
attrs = ['rel', 'href', 'src', 'data', 'value']
|
|
||||||
course_path = "/#{context.class.to_s.underscore.pluralize}/#{context.id}"
|
|
||||||
|
|
||||||
for_course_copy = false
|
|
||||||
if migration
|
|
||||||
for_course_copy = true if migration.for_course_copy?
|
|
||||||
end
|
|
||||||
|
|
||||||
doc.search("*").each do |node|
|
doc.search("*").each do |node|
|
||||||
attrs.each do |attr|
|
LINK_ATTRS.each do |attr|
|
||||||
if node[attr].present?
|
@link_parser.convert_link(node, attr, item_type, mig_id, field)
|
||||||
if attr == 'value'
|
|
||||||
next unless node['name'] && node['name'] == 'src'
|
|
||||||
end
|
|
||||||
|
|
||||||
new_url = nil
|
|
||||||
missing_relative_url = nil
|
|
||||||
|
|
||||||
val = node[attr].dup
|
|
||||||
REFERENCE_KEYWORDS.each do |ref|
|
|
||||||
val.gsub!("%24#{ref}%24", "$#{ref}$")
|
|
||||||
end
|
|
||||||
|
|
||||||
if val =~ /wiki_page_migration_id=(.*)/
|
|
||||||
# This would be from a BB9 migration.
|
|
||||||
#todo: refactor migration systems to use new $CANVAS...$ flags
|
|
||||||
#todo: FLAG UNFOUND REFERENCES TO re-attempt in second loop?
|
|
||||||
if wiki_migration_id = $1
|
|
||||||
if linked_wiki = context.wiki.wiki_pages.where(migration_id: wiki_migration_id).first
|
|
||||||
new_url = "#{course_path}/pages/#{linked_wiki.url}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elsif val =~ /discussion_topic_migration_id=(.*)/
|
|
||||||
if topic_migration_id = $1
|
|
||||||
if linked_topic = context.discussion_topics.where(migration_id: topic_migration_id).first
|
|
||||||
new_url = "#{course_path}/discussion_topics/#{linked_topic.id}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elsif val =~ %r{\$CANVAS_COURSE_REFERENCE\$/modules/items/(.*)}
|
|
||||||
if tag = context.context_module_tags.where(:migration_id => $1).select('id').first
|
|
||||||
new_url = "#{course_path}/modules/items/#{tag.id}"
|
|
||||||
end
|
|
||||||
elsif val =~ %r{(?:\$CANVAS_OBJECT_REFERENCE\$|\$WIKI_REFERENCE\$)/([^/]*)/(.*)}
|
|
||||||
type = $1
|
|
||||||
migration_id = $2
|
|
||||||
type_for_url = type
|
|
||||||
type = 'context_modules' if type == 'modules'
|
|
||||||
type = 'pages' if type == 'wiki'
|
|
||||||
if type == 'pages'
|
|
||||||
new_url = "#{course_path}/pages/#{migration_id}"
|
|
||||||
elsif type == 'attachments'
|
|
||||||
if att = context.attachments.where(migration_id: migration_id).first
|
|
||||||
new_url = "#{course_path}/files/#{att.id}/preview"
|
|
||||||
end
|
|
||||||
elsif context.respond_to?(type) && context.send(type).respond_to?(:where)
|
|
||||||
if object = context.send(type).where(migration_id: migration_id).first
|
|
||||||
new_url = "#{course_path}/#{type_for_url}/#{object.id}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elsif val =~ %r{\$CANVAS_COURSE_REFERENCE\$/(.*)}
|
|
||||||
section = $1
|
|
||||||
new_url = "#{course_path}/#{section}"
|
|
||||||
elsif val =~ %r{\$IMS(?:-|_)CC(?:-|_)FILEBASE\$/(.*)}
|
|
||||||
rel_path = URI.unescape($1)
|
|
||||||
if attr == 'href' && node['class'] && node['class'] =~ /instructure_inline_media_comment/
|
|
||||||
new_url = replace_media_comment_data(node, rel_path, context, opts) {|warning, data| yield warning, data if block_given?}
|
|
||||||
unless new_url
|
|
||||||
unless new_url = replace_relative_file_url(rel_path, context)
|
|
||||||
missing_relative_url = rel_path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
unless new_url = replace_relative_file_url(rel_path, context)
|
|
||||||
missing_relative_url = rel_path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elsif attr == 'href' && node['class'] && node['class'] =~ /instructure_inline_media_comment/
|
|
||||||
# Course copy media reference, leave it alone
|
|
||||||
new_url = node[attr]
|
|
||||||
elsif attr == 'src' && node['class'] && node['class'] =~ /equation_image/
|
|
||||||
# Equation image, leave it alone
|
|
||||||
new_url = node[attr]
|
|
||||||
elsif val =~ %r{\A/assessment_questions/\d+/files/\d+}
|
|
||||||
# The file is in the context of an AQ, leave the link alone
|
|
||||||
new_url = node[attr]
|
|
||||||
elsif val =~ %r{\A/courses/\d+/files/\d+}
|
|
||||||
# This points to a specific file already, leave it alone
|
|
||||||
new_url = node[attr]
|
|
||||||
elsif for_course_copy
|
|
||||||
# For course copies don't try to fix relative urls. Any url we can
|
|
||||||
# correctly alter was changed during the 'export' step
|
|
||||||
new_url = node[attr]
|
|
||||||
elsif val.start_with?('#')
|
|
||||||
# It's just a link to an anchor, leave it alone
|
|
||||||
new_url = node[attr]
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
if relative_url?(node[attr])
|
|
||||||
unescaped = URI.unescape(val)
|
|
||||||
unless new_url = replace_relative_file_url(unescaped, context)
|
|
||||||
missing_relative_url = unescaped
|
|
||||||
end
|
|
||||||
else
|
|
||||||
new_url = node[attr]
|
|
||||||
end
|
|
||||||
rescue URI::InvalidURIError
|
|
||||||
Rails.logger.warn "attempting to translate invalid url: #{node[attr]}"
|
|
||||||
# leave the url as it was
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if missing_relative_url
|
|
||||||
node[attr] = replace_missing_relative_url(missing_relative_url, context, course_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
if migration && converted_url = migration.process_domain_substitutions(new_url || val)
|
|
||||||
if converted_url != (new_url || val)
|
|
||||||
new_url = converted_url
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if new_url
|
|
||||||
node[attr] = new_url
|
|
||||||
else
|
|
||||||
yield :missing_link, node[attr] if block_given?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
node = doc.at_css('body')
|
node = doc.at_css('body')
|
||||||
|
return "" unless node
|
||||||
if opts[:remove_outer_nodes_if_one_child]
|
if opts[:remove_outer_nodes_if_one_child]
|
||||||
while node.children.size == 1 && node.child.child
|
while node.children.size == 1 && node.child.child
|
||||||
break unless CONTAINER_TYPES.member? node.child.name
|
break unless CONTAINER_TYPES.member? node.child.name
|
||||||
|
@ -166,105 +52,29 @@ class ImportedHtmlConverter
|
||||||
end
|
end
|
||||||
|
|
||||||
node.inner_html
|
node.inner_html
|
||||||
rescue
|
rescue Nokogiri::SyntaxError
|
||||||
""
|
""
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find_file_in_context(rel_path, context)
|
def convert_text(text)
|
||||||
mig_id = nil
|
format_message(text || "")[0]
|
||||||
# This is for backward-compatibility: canvas attachment filenames are escaped
|
|
||||||
# with '+' for spaces and older exports have files with that instead of %20
|
|
||||||
alt_rel_path = rel_path.gsub('+', ' ')
|
|
||||||
if context.respond_to?(:attachment_path_id_lookup) && context.attachment_path_id_lookup
|
|
||||||
mig_id ||= context.attachment_path_id_lookup[rel_path]
|
|
||||||
mig_id ||= context.attachment_path_id_lookup[alt_rel_path]
|
|
||||||
end
|
|
||||||
if !mig_id && context.respond_to?(:attachment_path_id_lookup_lower) && context.attachment_path_id_lookup_lower
|
|
||||||
mig_id ||= context.attachment_path_id_lookup_lower[rel_path.downcase]
|
|
||||||
mig_id ||= context.attachment_path_id_lookup_lower[alt_rel_path.downcase]
|
|
||||||
end
|
|
||||||
|
|
||||||
mig_id && context.attachments.where(migration_id: mig_id).first
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.replace_relative_file_url(rel_path, context)
|
def resolve_content_links!
|
||||||
new_url = nil
|
link_map = @link_parser.unresolved_link_map
|
||||||
split = rel_path.split('?')
|
return unless link_map.present?
|
||||||
qs = split.pop if split.length > 1
|
|
||||||
rel_path = split.join('?')
|
|
||||||
|
|
||||||
rel_path_parts = Pathname.new(rel_path).each_filename.to_a
|
@link_resolver.resolve_links!(link_map)
|
||||||
|
@link_replacer.replace_placeholders!(link_map)
|
||||||
# e.g. start with "a/b/c.txt" then try "b/c.txt" then try "c.txt"
|
@link_parser.reset!
|
||||||
while new_url.nil? && rel_path_parts.length > 0
|
|
||||||
sub_path = File.join(rel_path_parts)
|
|
||||||
if file = find_file_in_context(sub_path, context)
|
|
||||||
new_url = "/courses/#{context.id}/files/#{file.id}"
|
|
||||||
# support other params in the query string, that were exported from the
|
|
||||||
# original path components and query string. see
|
|
||||||
# CCHelper::file_query_string
|
|
||||||
params = Rack::Utils.parse_nested_query(qs.presence || "")
|
|
||||||
qs = []
|
|
||||||
new_action = ""
|
|
||||||
params.each do |k,v|
|
|
||||||
case k
|
|
||||||
when /canvas_qs_(.*)/
|
|
||||||
qs << "#{Rack::Utils.escape($1)}=#{Rack::Utils.escape(v)}"
|
|
||||||
when /canvas_(.*)/
|
|
||||||
new_action += "/#{$1}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if new_action.present?
|
|
||||||
new_url += new_action
|
|
||||||
else
|
|
||||||
new_url += "/preview"
|
|
||||||
end
|
|
||||||
new_url += "?#{qs.join("&")}" if qs.present?
|
|
||||||
end
|
|
||||||
rel_path_parts.shift
|
|
||||||
end
|
|
||||||
new_url
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.replace_missing_relative_url(rel_path, context, course_path)
|
|
||||||
# the rel_path should already be escaped
|
|
||||||
File.join(URI::escape("#{course_path}/file_contents/#{Folder.root_folders(context).first.name}"), rel_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.replace_media_comment_data(node, rel_path, context, opts={})
|
|
||||||
if context.respond_to?(:attachment_path_id_lookup) &&
|
|
||||||
context.attachment_path_id_lookup &&
|
|
||||||
context.attachment_path_id_lookup[rel_path]
|
|
||||||
file = context.attachments.where(migration_id: context.attachment_path_id_lookup[rel_path]).first
|
|
||||||
if file && file.media_object
|
|
||||||
media_id = file.media_object.media_id
|
|
||||||
node['id'] = "media_comment_#{media_id}"
|
|
||||||
return "/media_objects/#{media_id}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if node['id'] && node['id'] =~ /\Amedia_comment_(.+)\z/
|
|
||||||
link = "/media_objects/#{$1}"
|
|
||||||
yield :missing_link, link
|
|
||||||
return link
|
|
||||||
else
|
|
||||||
node.delete('class')
|
|
||||||
node.delete('id')
|
|
||||||
node.delete('style')
|
|
||||||
yield :missing_link, rel_path
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.relative_url?(url)
|
def self.relative_url?(url)
|
||||||
URI.parse(url).relative? && !url.to_s.start_with?("//")
|
URI.parse(url).relative? && !url.to_s.start_with?("//")
|
||||||
|
rescue URI::InvalidURIError
|
||||||
|
# leave the url as it was
|
||||||
|
Rails.logger.warn "attempting to translate invalid url: #{url}"
|
||||||
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.convert_text(text, context, import_source=:webct)
|
end
|
||||||
instance.format_message(text || "")[0]
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.instance
|
|
||||||
@@instance ||= self.new
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -50,7 +50,6 @@ def get_import_data(sub_folder, hash_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def import_example_questions(context)
|
def import_example_questions(context)
|
||||||
migration = context.content_migrations.new
|
|
||||||
questions = []
|
questions = []
|
||||||
QUESTIONS.each do |question|
|
QUESTIONS.each do |question|
|
||||||
if import_data_exists?(['vista', 'quiz'], question[0])
|
if import_data_exists?(['vista', 'quiz'], question[0])
|
||||||
|
@ -59,12 +58,12 @@ def import_example_questions(context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
hash = {'assessment_questions' => {'assessment_questions' => questions}}
|
hash = {'assessment_questions' => {'assessment_questions' => questions}}
|
||||||
Importers::AssessmentQuestionImporter.process_migration(hash, migration)
|
Importers::AssessmentQuestionImporter.process_migration(hash, @migration)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_import_context(system=nil)
|
def get_import_context(system=nil)
|
||||||
context = course_model
|
context = course_model
|
||||||
context.import_source == :webct if system == 'vista'
|
context.import_source == :webct if system == 'vista'
|
||||||
|
|
||||||
context
|
context
|
||||||
end
|
end
|
||||||
|
|
|
@ -96,7 +96,7 @@ describe "Canvas Cartridge importing" do
|
||||||
hash = {:migration_id=>CC::CCHelper.create_key(a),
|
hash = {:migration_id=>CC::CCHelper.create_key(a),
|
||||||
:title=>a.title,
|
:title=>a.title,
|
||||||
:assignment_group_migration_id=>CC::CCHelper.create_key(ag2)}
|
:assignment_group_migration_id=>CC::CCHelper.create_key(ag2)}
|
||||||
Importers::AssignmentImporter.import_from_migration(hash, @copy_to)
|
Importers::AssignmentImporter.import_from_migration(hash, @copy_to, @migration)
|
||||||
|
|
||||||
ag2_2.reload
|
ag2_2.reload
|
||||||
expect(ag2_2.assignments.count).to eq 1
|
expect(ag2_2.assignments.count).to eq 1
|
||||||
|
@ -567,8 +567,9 @@ describe "Canvas Cartridge importing" do
|
||||||
hash = @converter.convert_wiki(doc, 'some-page')
|
hash = @converter.convert_wiki(doc, 'some-page')
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
#import into new course
|
#import into new course
|
||||||
@copy_to.attachment_path_id_lookup = { 'unfiled/ohai there.txt' => attachment_import.migration_id }
|
@migration.attachment_path_id_lookup = { 'unfiled/ohai there.txt' => attachment_import.migration_id }
|
||||||
Importers::WikiPageImporter.import_from_migration(hash, @copy_to)
|
Importers::WikiPageImporter.import_from_migration(hash, @copy_to, @migration)
|
||||||
|
@migration.resolve_content_links!
|
||||||
|
|
||||||
page_2 = @copy_to.wiki.wiki_pages.where(migration_id: migration_id).first
|
page_2 = @copy_to.wiki.wiki_pages.where(migration_id: migration_id).first
|
||||||
expect(page_2.title).to eq page.title
|
expect(page_2.title).to eq page.title
|
||||||
|
@ -595,7 +596,7 @@ describe "Canvas Cartridge importing" do
|
||||||
to_att.migration_id = CC::CCHelper.create_key(from_att)
|
to_att.migration_id = CC::CCHelper.create_key(from_att)
|
||||||
to_att.save
|
to_att.save
|
||||||
path = to_att.full_display_path.gsub('course files/', '')
|
path = to_att.full_display_path.gsub('course files/', '')
|
||||||
@copy_to.attachment_path_id_lookup = {path => to_att.migration_id}
|
@migration.attachment_path_id_lookup = {path => to_att.migration_id}
|
||||||
|
|
||||||
body_with_link = %{<p>Watup? <strong>eh?</strong>
|
body_with_link = %{<p>Watup? <strong>eh?</strong>
|
||||||
<a href=\"/courses/%s/assignments\">Assignments</a>
|
<a href=\"/courses/%s/assignments\">Assignments</a>
|
||||||
|
@ -625,6 +626,7 @@ describe "Canvas Cartridge importing" do
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
#import into new course
|
#import into new course
|
||||||
Importers::WikiPageImporter.process_migration({'wikis' => [hash, nil]}, @migration)
|
Importers::WikiPageImporter.process_migration({'wikis' => [hash, nil]}, @migration)
|
||||||
|
@migration.resolve_content_links!
|
||||||
|
|
||||||
expect(ErrorReport.last.message).to match /nil wiki/
|
expect(ErrorReport.last.message).to match /nil wiki/
|
||||||
|
|
||||||
|
@ -650,7 +652,7 @@ describe "Canvas Cartridge importing" do
|
||||||
hash = @converter.convert_wiki(doc, 'blti-link')
|
hash = @converter.convert_wiki(doc, 'blti-link')
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
#import into new course
|
#import into new course
|
||||||
Importers::WikiPageImporter.import_from_migration(hash, @copy_to)
|
Importers::WikiPageImporter.import_from_migration(hash, @copy_to, @migration)
|
||||||
|
|
||||||
page_2 = @copy_to.wiki.wiki_pages.where(migration_id: migration_id).first
|
page_2 = @copy_to.wiki.wiki_pages.where(migration_id: migration_id).first
|
||||||
expect(page_2.title).to eq page.title
|
expect(page_2.title).to eq page.title
|
||||||
|
@ -703,7 +705,7 @@ describe "Canvas Cartridge importing" do
|
||||||
hash = @converter.parse_canvas_assignment_data(meta_doc, html_doc)
|
hash = @converter.parse_canvas_assignment_data(meta_doc, html_doc)
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
#import
|
#import
|
||||||
Importers::AssignmentImporter.import_from_migration(hash, @copy_to)
|
Importers::AssignmentImporter.import_from_migration(hash, @copy_to, @migration)
|
||||||
|
|
||||||
asmnt_2 = @copy_to.assignments.where(migration_id: migration_id).first
|
asmnt_2 = @copy_to.assignments.where(migration_id: migration_id).first
|
||||||
expect(asmnt_2.title).to eq asmnt.title
|
expect(asmnt_2.title).to eq asmnt.title
|
||||||
|
@ -742,7 +744,7 @@ describe "Canvas Cartridge importing" do
|
||||||
hash = @converter.parse_canvas_assignment_data(meta_doc, html_doc)
|
hash = @converter.parse_canvas_assignment_data(meta_doc, html_doc)
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
#import
|
#import
|
||||||
Importers::AssignmentImporter.import_from_migration(hash, @copy_to)
|
Importers::AssignmentImporter.import_from_migration(hash, @copy_to, @migration)
|
||||||
|
|
||||||
asmnt_2 = @copy_to.assignments.where(migration_id: migration_id).first
|
asmnt_2 = @copy_to.assignments.where(migration_id: migration_id).first
|
||||||
expect(asmnt_2.submission_types).to eq "external_tool"
|
expect(asmnt_2.submission_types).to eq "external_tool"
|
||||||
|
@ -803,7 +805,7 @@ XML
|
||||||
hash = @converter.convert_topic(cc_doc, meta_doc)
|
hash = @converter.convert_topic(cc_doc, meta_doc)
|
||||||
hash = hash.with_indifferent_access
|
hash = hash.with_indifferent_access
|
||||||
#import
|
#import
|
||||||
Importers::DiscussionTopicImporter.import_from_migration(hash, @copy_to)
|
Importers::DiscussionTopicImporter.import_from_migration(hash, @copy_to, @migration)
|
||||||
|
|
||||||
dt_2 = @copy_to.discussion_topics.where(migration_id: migration_id).first
|
dt_2 = @copy_to.discussion_topics.where(migration_id: migration_id).first
|
||||||
expect(dt_2.title).to eq dt.title
|
expect(dt_2.title).to eq dt.title
|
||||||
|
@ -851,7 +853,7 @@ XML
|
||||||
ag1.migration_id = CC::CCHelper.create_key(assignment.assignment_group)
|
ag1.migration_id = CC::CCHelper.create_key(assignment.assignment_group)
|
||||||
ag1.save!
|
ag1.save!
|
||||||
#import
|
#import
|
||||||
Importers::DiscussionTopicImporter.import_from_migration(hash, @copy_to)
|
Importers::DiscussionTopicImporter.import_from_migration(hash, @copy_to, @migration)
|
||||||
|
|
||||||
dt_2 = @copy_to.discussion_topics.where(migration_id: migration_id).first
|
dt_2 = @copy_to.discussion_topics.where(migration_id: migration_id).first
|
||||||
expect(dt_2.title).to eq dt.title
|
expect(dt_2.title).to eq dt.title
|
||||||
|
@ -961,7 +963,7 @@ XML
|
||||||
ag.migration_id = "i713e960ab2685259505efeb08cd48a1d"
|
ag.migration_id = "i713e960ab2685259505efeb08cd48a1d"
|
||||||
ag.save!
|
ag.save!
|
||||||
|
|
||||||
Importers::QuizImporter.import_from_migration(quiz_hash, @copy_to, nil, {})
|
Importers::QuizImporter.import_from_migration(quiz_hash, @copy_to, @migration, {})
|
||||||
q = @copy_to.quizzes.where(migration_id: "ie3d8f8adfad423eb225229c539cdc450").first
|
q = @copy_to.quizzes.where(migration_id: "ie3d8f8adfad423eb225229c539cdc450").first
|
||||||
a = q.assignment
|
a = q.assignment
|
||||||
expect(a.assignment_group.id).to eq ag.id
|
expect(a.assignment_group.id).to eq ag.id
|
||||||
|
|
|
@ -19,43 +19,57 @@
|
||||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||||
|
|
||||||
describe ImportedHtmlConverter do
|
describe ImportedHtmlConverter do
|
||||||
|
# tests link_parser and link_resolver
|
||||||
|
|
||||||
context ".convert" do
|
context ".convert" do
|
||||||
before(:each) do
|
before :once do
|
||||||
course
|
course
|
||||||
@path = "/courses/#{@course.id}/"
|
@path = "/courses/#{@course.id}/"
|
||||||
|
@migration = @course.content_migrations.create!
|
||||||
|
@converter = @migration.html_converter
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def convert_and_replace(test_string)
|
||||||
|
html = @migration.convert_html(test_string, 'sometype', 'somemigid', 'somefield')
|
||||||
|
link_map = @converter.link_parser.unresolved_link_map
|
||||||
|
|
||||||
|
@converter.link_resolver.resolve_links!(link_map)
|
||||||
|
if link_map.present?
|
||||||
|
@converter.link_replacer.sub_placeholders!(html, link_map.values.map(&:values).flatten)
|
||||||
|
end
|
||||||
|
html
|
||||||
|
end
|
||||||
|
|
||||||
it "should convert a wiki reference" do
|
it "should convert a wiki reference" do
|
||||||
test_string = %{<a href="%24WIKI_REFERENCE%24/wiki/test-wiki-page">Test Wiki Page</a>}
|
test_string = %{<a href="%24WIKI_REFERENCE%24/wiki/test-wiki-page">Test Wiki Page</a>}
|
||||||
@course.wiki.wiki_pages.create!(:title => "Test Wiki Page", :body => "stuff")
|
@course.wiki.wiki_pages.create!(:title => "Test Wiki Page", :body => "stuff")
|
||||||
|
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<a href="#{@path}pages/test-wiki-page">Test Wiki Page</a>}
|
expect(convert_and_replace(test_string)).to eq %{<a href="#{@path}pages/test-wiki-page">Test Wiki Page</a>}
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should convert a wiki reference without $ escaped" do
|
it "should convert a wiki reference without $ escaped" do
|
||||||
test_string = %{<a href="$WIKI_REFERENCE$/wiki/test-wiki-page">Test Wiki Page</a>}
|
test_string = %{<a href="$WIKI_REFERENCE$/wiki/test-wiki-page">Test Wiki Page</a>}
|
||||||
@course.wiki.wiki_pages.create!(:title => "Test Wiki Page", :body => "stuff")
|
@course.wiki.wiki_pages.create!(:title => "Test Wiki Page", :body => "stuff")
|
||||||
|
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<a href="#{@path}pages/test-wiki-page">Test Wiki Page</a>}
|
expect(convert_and_replace(test_string)).to eq %{<a href="#{@path}pages/test-wiki-page">Test Wiki Page</a>}
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should convert a wiki reference by migration id" do
|
it "should convert a wiki reference by migration id" do
|
||||||
test_string = %{<a href="wiki_page_migration_id=123456677788">Test Wiki Page</a>}
|
test_string = %{<a href="wiki_page_migration_id=123456677788">Test Wiki Page</a>}
|
||||||
wiki = @course.wiki.wiki_pages.create(:title => "Test Wiki Page", :body => "stuff")
|
wiki = @course.wiki.wiki_pages.create(:title => "Test Wiki Page", :body => "stuff")
|
||||||
wiki.migration_id = "123456677788"
|
wiki.migration_id = "123456677788"
|
||||||
wiki.save!
|
wiki.save!
|
||||||
|
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<a href="#{@path}pages/test-wiki-page">Test Wiki Page</a>}
|
expect(convert_and_replace(test_string)).to eq %{<a href="#{@path}pages/test-wiki-page">Test Wiki Page</a>}
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should convert a discussion reference by migration id" do
|
it "should convert a discussion reference by migration id" do
|
||||||
test_string = %{<a href="discussion_topic_migration_id=123456677788">Test topic</a>}
|
test_string = %{<a href="discussion_topic_migration_id=123456677788">Test topic</a>}
|
||||||
topic = @course.discussion_topics.create(:title => "Test discussion")
|
topic = @course.discussion_topics.create(:title => "Test discussion")
|
||||||
topic.migration_id = "123456677788"
|
topic.migration_id = "123456677788"
|
||||||
topic.save!
|
topic.save!
|
||||||
|
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<a href="#{@path}discussion_topics/#{topic.id}">Test topic</a>}
|
expect(convert_and_replace(test_string)).to eq %{<a href="#{@path}discussion_topics/#{topic.id}">Test topic</a>}
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_test_att
|
def make_test_att
|
||||||
|
@ -67,84 +81,84 @@ describe ImportedHtmlConverter do
|
||||||
|
|
||||||
it "should find an attachment by migration id" do
|
it "should find an attachment by migration id" do
|
||||||
att = make_test_att()
|
att = make_test_att()
|
||||||
|
|
||||||
test_string = %{<p>This is an image: <br /><img src="%24CANVAS_OBJECT_REFERENCE%24/attachments/1768525836051" alt=":(" /></p>}
|
test_string = %{<p>This is an image: <br /><img src="%24CANVAS_OBJECT_REFERENCE%24/attachments/1768525836051" alt=":(" /></p>}
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<p>This is an image: <br><img src="#{@path}files/#{att.id}/preview" alt=":("></p>}
|
expect(convert_and_replace(test_string)).to eq %{<p>This is an image: <br><img src="#{@path}files/#{att.id}/preview" alt=":("></p>}
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should find an attachment by path" do
|
it "should find an attachment by path" do
|
||||||
att = make_test_att()
|
att = make_test_att()
|
||||||
|
|
||||||
test_string = %{<p>This is an image: <br /><img src="%24IMS_CC_FILEBASE%24/test.png" alt=":(" /></p>}
|
test_string = %{<p>This is an image: <br /><img src="%24IMS_CC_FILEBASE%24/test.png" alt=":(" /></p>}
|
||||||
|
|
||||||
# if there isn't a path->migration id map it'll be a relative course file path
|
# if there isn't a path->migration id map it'll be a relative course file path
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<p>This is an image: <br><img src="#{@path}file_contents/course%20files/test.png" alt=":("></p>}
|
expect(convert_and_replace(test_string)).to eq %{<p>This is an image: <br><img src="#{@path}file_contents/course%20files/test.png" alt=":("></p>}
|
||||||
|
|
||||||
@course.attachment_path_id_lookup = {"test.png" => att.migration_id}
|
@migration.attachment_path_id_lookup = {"test.png" => att.migration_id}
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<p>This is an image: <br><img src="#{@path}files/#{att.id}/preview" alt=":("></p>}
|
expect(convert_and_replace(test_string)).to eq %{<p>This is an image: <br><img src="#{@path}files/#{att.id}/preview" alt=":("></p>}
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should find an attachment by a path with a space" do
|
it "should find an attachment by a path with a space" do
|
||||||
att = make_test_att()
|
att = make_test_att()
|
||||||
@course.attachment_path_id_lookup = {"subfolder/with a space/test.png" => att.migration_id}
|
@migration.attachment_path_id_lookup = {"subfolder/with a space/test.png" => att.migration_id}
|
||||||
|
|
||||||
test_string = %{<img src="subfolder/with%20a%20space/test.png" alt="nope" />}
|
test_string = %{<img src="subfolder/with%20a%20space/test.png" alt="nope" />}
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<img src="#{@path}files/#{att.id}/preview" alt="nope">}
|
expect(convert_and_replace(test_string)).to eq %{<img src="#{@path}files/#{att.id}/preview" alt="nope">}
|
||||||
|
|
||||||
test_string = %{<img src="subfolder/with+a+space/test.png" alt="nope" />}
|
test_string = %{<img src="subfolder/with+a+space/test.png" alt="nope" />}
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<img src="#{@path}files/#{att.id}/preview" alt="nope">}
|
expect(convert_and_replace(test_string)).to eq %{<img src="#{@path}files/#{att.id}/preview" alt="nope">}
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should find an attachment even if the link has an extraneous folder" do
|
it "should find an attachment even if the link has an extraneous folder" do
|
||||||
att = make_test_att()
|
att = make_test_att()
|
||||||
@course.attachment_path_id_lookup = {"subfolder/test.png" => att.migration_id}
|
@migration.attachment_path_id_lookup = {"subfolder/test.png" => att.migration_id}
|
||||||
|
|
||||||
test_string = %{<img src="anotherfolder/subfolder/test.png" alt="nope" />}
|
test_string = %{<img src="anotherfolder/subfolder/test.png" alt="nope" />}
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<img src="#{@path}files/#{att.id}/preview" alt="nope">}
|
expect(convert_and_replace(test_string)).to eq %{<img src="#{@path}files/#{att.id}/preview" alt="nope">}
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should find an attachment by path if capitalization is different" do
|
it "should find an attachment by path if capitalization is different" do
|
||||||
att = make_test_att()
|
att = make_test_att()
|
||||||
@course.attachment_path_id_lookup = {"subfolder/withCapital/test.png" => "wrong!"}
|
@migration.attachment_path_id_lookup = {"subfolder/withCapital/test.png" => "wrong!"}
|
||||||
@course.attachment_path_id_lookup_lower = {"subfolder/withcapital/test.png" => att.migration_id}
|
@migration.attachment_path_id_lookup_lower = {"subfolder/withcapital/test.png" => att.migration_id}
|
||||||
|
|
||||||
test_string = %{<img src="subfolder/WithCapital/TEST.png" alt="nope" />}
|
test_string = %{<img src="subfolder/WithCapital/TEST.png" alt="nope" />}
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<img src="#{@path}files/#{att.id}/preview" alt="nope">}
|
expect(convert_and_replace(test_string)).to eq %{<img src="#{@path}files/#{att.id}/preview" alt="nope">}
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should find an attachment with query params" do
|
it "should find an attachment with query params" do
|
||||||
att = make_test_att()
|
att = make_test_att()
|
||||||
@course.attachment_path_id_lookup = {"test.png" => att.migration_id}
|
@migration.attachment_path_id_lookup = {"test.png" => att.migration_id}
|
||||||
|
|
||||||
test_string = %{<img src="%24IMS_CC_FILEBASE%24/test.png?canvas_customaction=1&canvas_qs_customparam=1" alt="nope" />}
|
test_string = %{<img src="%24IMS_CC_FILEBASE%24/test.png?canvas_customaction=1&canvas_qs_customparam=1" alt="nope" />}
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<img src="#{@path}files/#{att.id}/customaction?customparam=1" alt="nope">}
|
expect(convert_and_replace(test_string)).to eq %{<img src="#{@path}files/#{att.id}/customaction?customparam=1" alt="nope">}
|
||||||
|
|
||||||
test_string = %{<img src="%24IMS_CC_FILEBASE%24/test.png?canvas_qs_customparam2=3" alt="nope" />}
|
test_string = %{<img src="%24IMS_CC_FILEBASE%24/test.png?canvas_qs_customparam2=3" alt="nope" />}
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<img src="#{@path}files/#{att.id}/preview?customparam2=3" alt="nope">}
|
expect(convert_and_replace(test_string)).to eq %{<img src="#{@path}files/#{att.id}/preview?customparam2=3" alt="nope">}
|
||||||
|
|
||||||
test_string = %{<img src="%24IMS_CC_FILEBASE%24/test.png?notarelevantparam" alt="nope" />}
|
test_string = %{<img src="%24IMS_CC_FILEBASE%24/test.png?notarelevantparam" alt="nope" />}
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<img src="#{@path}files/#{att.id}/preview" alt="nope">}
|
expect(convert_and_replace(test_string)).to eq %{<img src="#{@path}files/#{att.id}/preview" alt="nope">}
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should convert course section urls" do
|
it "should convert course section urls" do
|
||||||
test_string = %{<a href="%24CANVAS_COURSE_REFERENCE%24/discussion_topics">discussions</a>}
|
test_string = %{<a href="%24CANVAS_COURSE_REFERENCE%24/discussion_topics">discussions</a>}
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<a href="#{@path}discussion_topics">discussions</a>}
|
expect(convert_and_replace(test_string)).to eq %{<a href="#{@path}discussion_topics">discussions</a>}
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should leave invalid and absolute urls alone" do
|
it "should leave invalid and absolute urls alone" do
|
||||||
test_string = %{<a href="stupid &^%$ url">Linkage</a><br><a href="http://www.example.com/poop">Linkage</a>}
|
test_string = %{<a href="stupid &^%$ url">Linkage</a><br><a href="http://www.example.com/poop">Linkage</a>}
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<a href="stupid%20&%5E%%24%20url">Linkage</a><br><a href="http://www.example.com/poop">Linkage</a>}
|
expect(convert_and_replace(test_string)).to eq %{<a href="stupid%20&%5E%%24%20url">Linkage</a><br><a href="http://www.example.com/poop">Linkage</a>}
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should prepend course files for unrecognized relative urls" do
|
it "should prepend course files for unrecognized relative urls" do
|
||||||
test_string = %{<a href="/relative/path/to/file">Linkage</a>}
|
test_string = %{<a href="/relative/path/to/file">Linkage</a>}
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<a href="#{@path}file_contents/course%20files/relative/path/to/file">Linkage</a>}
|
expect(convert_and_replace(test_string)).to eq %{<a href="#{@path}file_contents/course%20files/relative/path/to/file">Linkage</a>}
|
||||||
test_string = %{<a href="relative/path/to/file">Linkage</a>}
|
test_string = %{<a href="relative/path/to/file">Linkage</a>}
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<a href="#{@path}file_contents/course%20files/relative/path/to/file">Linkage</a>}
|
expect(convert_and_replace(test_string)).to eq %{<a href="#{@path}file_contents/course%20files/relative/path/to/file">Linkage</a>}
|
||||||
test_string = %{<a href="relative/path/to/file%20with%20space.html">Linkage</a>}
|
test_string = %{<a href="relative/path/to/file%20with%20space.html">Linkage</a>}
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<a href="#{@path}file_contents/course%20files/relative/path/to/file%20with%20space.html">Linkage</a>}
|
expect(convert_and_replace(test_string)).to eq %{<a href="#{@path}file_contents/course%20files/relative/path/to/file%20with%20space.html">Linkage</a>}
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should preserve media comment links" do
|
it "should preserve media comment links" do
|
||||||
test_string = <<-HTML.strip
|
test_string = <<-HTML.strip
|
||||||
<p>
|
<p>
|
||||||
|
@ -153,13 +167,13 @@ describe ImportedHtmlConverter do
|
||||||
</p>
|
</p>
|
||||||
HTML
|
HTML
|
||||||
|
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq test_string.gsub("/courses/#{course.id}/file_contents/course%20files",'')
|
expect(convert_and_replace(test_string)).to eq test_string
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should handle and repair half broken media links" do
|
it "should handle and repair half broken media links" do
|
||||||
test_string = %{<p><a href="/courses/#{@course.id}/file_contents/%24IMS_CC_FILEBASE%24/#" class="instructure_inline_media_comment video_comment" id="media_comment_0_l4l5n0wt">this is a media comment</a><br><br></p>}
|
test_string = %{<p><a href="/courses/#{@course.id}/file_contents/%24IMS_CC_FILEBASE%24/#" class="instructure_inline_media_comment video_comment" id="media_comment_0_l4l5n0wt">this is a media comment</a><br><br></p>}
|
||||||
|
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq %{<p><a href="/media_objects/0_l4l5n0wt" class="instructure_inline_media_comment video_comment" id="media_comment_0_l4l5n0wt">this is a media comment</a><br><br></p>}
|
expect(convert_and_replace(test_string)).to eq %{<p><a href="/media_objects/0_l4l5n0wt" class="instructure_inline_media_comment video_comment" id="media_comment_0_l4l5n0wt">this is a media comment</a><br><br></p>}
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should only convert url params" do
|
it "should only convert url params" do
|
||||||
|
@ -175,7 +189,7 @@ describe ImportedHtmlConverter do
|
||||||
</object>
|
</object>
|
||||||
HTML
|
HTML
|
||||||
|
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to match_ignoring_whitespace(<<-HTML.strip)
|
expect(convert_and_replace(test_string)).to match_ignoring_whitespace(<<-HTML.strip)
|
||||||
<object>
|
<object>
|
||||||
<param name="controls" value="CONSOLE">
|
<param name="controls" value="CONSOLE">
|
||||||
<param name="controller" value="true">
|
<param name="controller" value="true">
|
||||||
|
@ -188,25 +202,25 @@ describe ImportedHtmlConverter do
|
||||||
|
|
||||||
it "should leave an anchor tag alone" do
|
it "should leave an anchor tag alone" do
|
||||||
test_string = '<p><a href="#anchor_ref">ref</a></p>'
|
test_string = '<p><a href="#anchor_ref">ref</a></p>'
|
||||||
expect(ImportedHtmlConverter.convert(test_string, @course)).to eq test_string
|
expect(convert_and_replace(test_string)).to eq test_string
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context ".relative_url?" do
|
context ".relative_url?" do
|
||||||
it "should recognize an absolute url" do
|
it "should recognize an absolute url" do
|
||||||
expect(ImportedHtmlConverter.relative_url?("http://example.com")).to eq false
|
expect(ImportedHtmlConverter.relative_url?("http://example.com")).to eq false
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should recognize relative urls" do
|
it "should recognize relative urls" do
|
||||||
expect(ImportedHtmlConverter.relative_url?("/relative/eh")).to eq true
|
expect(ImportedHtmlConverter.relative_url?("/relative/eh")).to eq true
|
||||||
expect(ImportedHtmlConverter.relative_url?("also/relative")).to eq true
|
expect(ImportedHtmlConverter.relative_url?("also/relative")).to eq true
|
||||||
expect(ImportedHtmlConverter.relative_url?("watup/nothing.html#anchoritbaby")).to eq true
|
expect(ImportedHtmlConverter.relative_url?("watup/nothing.html#anchoritbaby")).to eq true
|
||||||
expect(ImportedHtmlConverter.relative_url?("watup/nothing?absolutely=1")).to eq true
|
expect(ImportedHtmlConverter.relative_url?("watup/nothing?absolutely=1")).to eq true
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should error on invalid urls" do
|
it "should not error on invalid urls" do
|
||||||
expect { ImportedHtmlConverter.relative_url?("stupid &^%$ url") }.to raise_error(URI::InvalidURIError)
|
expect(ImportedHtmlConverter.relative_url?("stupid &^%$ url")).to be_falsey
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -838,5 +838,69 @@ equation: <img class="equation_image" title="Log_216" src="/equation_images/Log_
|
||||||
expect(quiz_to.assignment).to eq a_to
|
expect(quiz_to.assignment).to eq a_to
|
||||||
expect(a_to).to be_published
|
expect(a_to).to be_published
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should correctly copy links to quizzes inside assessment questions" do
|
||||||
|
link_quiz = @copy_from.quizzes.create!(:title => "linked quiz")
|
||||||
|
|
||||||
|
html = "<a href=\"/courses/%s/quizzes/%s\">linky</a>"
|
||||||
|
|
||||||
|
bank = @copy_from.assessment_question_banks.create!(:title => 'bank')
|
||||||
|
data = {'question_name' => 'test question', 'question_type' => 'essay_question',
|
||||||
|
'question_text' => (html % [@copy_from.id, link_quiz.id])}
|
||||||
|
aq = bank.assessment_questions.create!(:question_data => data)
|
||||||
|
|
||||||
|
other_quiz = @copy_from.quizzes.create!(:title => "other quiz")
|
||||||
|
qq = other_quiz.quiz_questions.create!(:question_data => data)
|
||||||
|
other_quiz.generate_quiz_data
|
||||||
|
other_quiz.published_at = Time.now
|
||||||
|
other_quiz.workflow_state = 'available'
|
||||||
|
other_quiz.save!
|
||||||
|
|
||||||
|
run_course_copy
|
||||||
|
|
||||||
|
link_quiz2 = @copy_to.quizzes.where(migration_id: mig_id(link_quiz)).first
|
||||||
|
expected_html = (html % [@copy_to.id, link_quiz2.id])
|
||||||
|
|
||||||
|
other_quiz2 = @copy_to.quizzes.where(migration_id: mig_id(other_quiz)).first
|
||||||
|
aq2 = @copy_to.assessment_questions.where(migration_id: mig_id(aq)).first
|
||||||
|
qq2 = other_quiz2.quiz_questions.first
|
||||||
|
|
||||||
|
expect(aq2.question_data['question_text']).to eq expected_html
|
||||||
|
expect(qq2.question_data['question_text']).to eq expected_html
|
||||||
|
expect(other_quiz2.quiz_data.first['question_text']).to eq expected_html
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should correctly copy links to quizzes inside standalone quiz questions" do
|
||||||
|
# i.e. quiz questions imported independently from their original assessment question
|
||||||
|
link_quiz = @copy_from.quizzes.create!(:title => "linked quiz")
|
||||||
|
|
||||||
|
html = "<a href=\"/courses/%s/quizzes/%s\">linky</a>"
|
||||||
|
|
||||||
|
bank = @copy_from.assessment_question_banks.create!(:title => 'bank')
|
||||||
|
data = {'question_name' => 'test question', 'question_type' => 'essay_question',
|
||||||
|
'question_text' => (html % [@copy_from.id, link_quiz.id])}
|
||||||
|
aq = bank.assessment_questions.create!(:question_data => data)
|
||||||
|
|
||||||
|
other_quiz = @copy_from.quizzes.create!(:title => "other quiz")
|
||||||
|
qq = other_quiz.quiz_questions.create!(:question_data => data)
|
||||||
|
other_quiz.generate_quiz_data
|
||||||
|
other_quiz.published_at = Time.now
|
||||||
|
other_quiz.workflow_state = 'available'
|
||||||
|
other_quiz.save!
|
||||||
|
|
||||||
|
@cm.copy_options = {
|
||||||
|
:quizzes => {mig_id(link_quiz) => "1", mig_id(other_quiz) => "1"}
|
||||||
|
}
|
||||||
|
run_course_copy
|
||||||
|
|
||||||
|
link_quiz2 = @copy_to.quizzes.where(migration_id: mig_id(link_quiz)).first
|
||||||
|
expected_html = (html % [@copy_to.id, link_quiz2.id])
|
||||||
|
|
||||||
|
other_quiz2 = @copy_to.quizzes.where(migration_id: mig_id(other_quiz)).first
|
||||||
|
qq2 = other_quiz2.quiz_questions.first
|
||||||
|
|
||||||
|
expect(qq2.question_data['question_text']).to eq expected_html
|
||||||
|
expect(other_quiz2.quiz_data.first['question_text']).to eq expected_html
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
require File.expand_path(File.dirname(__FILE__) + '../../../import_helper')
|
require File.expand_path(File.dirname(__FILE__) + '../../../import_helper')
|
||||||
|
|
||||||
describe "Assessment Question import from hash" do
|
describe "Assessment Question import from hash" do
|
||||||
|
|
||||||
SYSTEMS.each do |system|
|
SYSTEMS.each do |system|
|
||||||
QUESTIONS.each do |q|
|
QUESTIONS.each do |q|
|
||||||
if import_data_exists? [system, 'quiz'], q[0]
|
if import_data_exists? [system, 'quiz'], q[0]
|
||||||
|
@ -68,7 +68,7 @@ describe "Assessment Question import from hash" do
|
||||||
data = {'assessment_questions' => {'assessment_questions' => [q]}}
|
data = {'assessment_questions' => {'assessment_questions' => [q]}}
|
||||||
migration = ContentMigration.create!(:context => context)
|
migration = ContentMigration.create!(:context => context)
|
||||||
Importers::AssessmentQuestionImporter.process_migration(data, migration)
|
Importers::AssessmentQuestionImporter.process_migration(data, migration)
|
||||||
|
|
||||||
bank = AssessmentQuestionBank.where(context_type: context.class.to_s, context_id: context, title: q[:question_bank_name]).first
|
bank = AssessmentQuestionBank.where(context_type: context.class.to_s, context_id: context, title: q[:question_bank_name]).first
|
||||||
expect(bank.assessment_questions.count).to eq 1
|
expect(bank.assessment_questions.count).to eq 1
|
||||||
expect(bank.assessment_questions.first.migration_id).to eq q[:migration_id]
|
expect(bank.assessment_questions.first.migration_id).to eq q[:migration_id]
|
||||||
|
@ -139,7 +139,7 @@ describe "Assessment Question import from hash" do
|
||||||
question_data[:aq_data][question[:migration_id]] = context.assessment_questions.where(migration_id: question[:migration_id]).first
|
question_data[:aq_data][question[:migration_id]] = context.assessment_questions.where(migration_id: question[:migration_id]).first
|
||||||
|
|
||||||
quiz = get_import_data 'cengage', 'quiz'
|
quiz = get_import_data 'cengage', 'quiz'
|
||||||
Importers::QuizImporter.import_from_migration(quiz, context, nil, question_data)
|
Importers::QuizImporter.import_from_migration(quiz, context, migration, question_data)
|
||||||
quiz = context.quizzes.where(migration_id: quiz[:migration_id]).first
|
quiz = context.quizzes.where(migration_id: quiz[:migration_id]).first
|
||||||
|
|
||||||
group = quiz.quiz_groups.first
|
group = quiz.quiz_groups.first
|
||||||
|
@ -150,7 +150,7 @@ end
|
||||||
|
|
||||||
def test_question_import(hash_name, system, question_type=nil)
|
def test_question_import(hash_name, system, question_type=nil)
|
||||||
question_type ||= "#{hash_name}_question"
|
question_type ||= "#{hash_name}_question"
|
||||||
q = get_import_data [system, 'quiz'], hash_name
|
q = get_import_data [system, 'quiz'], hash_name
|
||||||
# q[:question_type].should == question_type
|
# q[:question_type].should == question_type
|
||||||
context = get_import_context(system)
|
context = get_import_context(system)
|
||||||
data = {'assessment_questions' => {'assessment_questions' => [q]}}
|
data = {'assessment_questions' => {'assessment_questions' => [q]}}
|
||||||
|
|
|
@ -25,16 +25,17 @@ describe "Importing Assignment Groups" do
|
||||||
it "should import from #{system}" do
|
it "should import from #{system}" do
|
||||||
data = get_import_data(system, 'assignment_group')
|
data = get_import_data(system, 'assignment_group')
|
||||||
context = get_import_context(system)
|
context = get_import_context(system)
|
||||||
|
migration = context.content_migrations.create!
|
||||||
|
|
||||||
data[:assignment_groups_to_import] = {}
|
data[:assignment_groups_to_import] = {}
|
||||||
expect {
|
expect {
|
||||||
expect(Importers::AssignmentGroupImporter.import_from_migration(data, context)).to be_nil
|
expect(Importers::AssignmentGroupImporter.import_from_migration(data, context, migration)).to be_nil
|
||||||
}.to change(AssignmentGroup, :count).by(0)
|
}.to change(AssignmentGroup, :count).by(0)
|
||||||
|
|
||||||
data[:assignment_groups_to_import][data[:migration_id]] = true
|
data[:assignment_groups_to_import][data[:migration_id]] = true
|
||||||
expect {
|
expect {
|
||||||
Importers::AssignmentGroupImporter.import_from_migration(data, context)
|
Importers::AssignmentGroupImporter.import_from_migration(data, context, migration)
|
||||||
Importers::AssignmentGroupImporter.import_from_migration(data, context)
|
Importers::AssignmentGroupImporter.import_from_migration(data, context, migration)
|
||||||
}.to change(AssignmentGroup, :count).by(1)
|
}.to change(AssignmentGroup, :count).by(1)
|
||||||
g = AssignmentGroup.where(migration_id: data[:migration_id]).first
|
g = AssignmentGroup.where(migration_id: data[:migration_id]).first
|
||||||
|
|
||||||
|
@ -45,20 +46,22 @@ describe "Importing Assignment Groups" do
|
||||||
|
|
||||||
it "should reuse existing empty assignment groups with the same name" do
|
it "should reuse existing empty assignment groups with the same name" do
|
||||||
course_model
|
course_model
|
||||||
|
migration = @course.content_migrations.create!
|
||||||
assignment_group = @course.assignment_groups.create! name: 'teh group'
|
assignment_group = @course.assignment_groups.create! name: 'teh group'
|
||||||
assignment_group_json = { 'title' => 'teh group', 'migration_id' => '123' }
|
assignment_group_json = { 'title' => 'teh group', 'migration_id' => '123' }
|
||||||
Importers::AssignmentGroupImporter.import_from_migration(assignment_group_json, @course)
|
Importers::AssignmentGroupImporter.import_from_migration(assignment_group_json, @course, migration)
|
||||||
expect(assignment_group.reload.migration_id).to eq('123')
|
expect(assignment_group.reload.migration_id).to eq('123')
|
||||||
expect(@course.assignment_groups.count).to eq 1
|
expect(@course.assignment_groups.count).to eq 1
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should not match assignment groups with migration ids by name" do
|
it "should not match assignment groups with migration ids by name" do
|
||||||
course_model
|
course_model
|
||||||
|
migration = @course.content_migrations.create!
|
||||||
assignment_group = @course.assignment_groups.create name: 'teh group'
|
assignment_group = @course.assignment_groups.create name: 'teh group'
|
||||||
assignment_group.migration_id = '456'
|
assignment_group.migration_id = '456'
|
||||||
assignment_group.save!
|
assignment_group.save!
|
||||||
assignment_group_json = { 'title' => 'teh group', 'migration_id' => '123' }
|
assignment_group_json = { 'title' => 'teh group', 'migration_id' => '123' }
|
||||||
Importers::AssignmentGroupImporter.import_from_migration(assignment_group_json, @course)
|
Importers::AssignmentGroupImporter.import_from_migration(assignment_group_json, @course, migration)
|
||||||
expect(assignment_group.reload.migration_id).to eq('456')
|
expect(assignment_group.reload.migration_id).to eq('456')
|
||||||
expect(@course.assignment_groups.count).to eq 2
|
expect(@course.assignment_groups.count).to eq 2
|
||||||
end
|
end
|
||||||
|
@ -66,12 +69,13 @@ describe "Importing Assignment Groups" do
|
||||||
it "should get attached to an assignment" do
|
it "should get attached to an assignment" do
|
||||||
data = get_import_data('bb8', 'assignment_group')
|
data = get_import_data('bb8', 'assignment_group')
|
||||||
context = get_import_context('bb8')
|
context = get_import_context('bb8')
|
||||||
|
migration = context.content_migrations.create!
|
||||||
expect {
|
expect {
|
||||||
Importers::AssignmentGroupImporter.import_from_migration(data, context)
|
Importers::AssignmentGroupImporter.import_from_migration(data, context, migration)
|
||||||
}.to change(AssignmentGroup, :count).by(1)
|
}.to change(AssignmentGroup, :count).by(1)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
ass = Importers::AssignmentImporter.import_from_migration(get_import_data('bb8', 'assignment'), context)
|
ass = Importers::AssignmentImporter.import_from_migration(get_import_data('bb8', 'assignment'), context, migration)
|
||||||
expect(ass.assignment_group.name).to eq data[:title]
|
expect(ass.assignment_group.name).to eq data[:title]
|
||||||
}.to change(AssignmentGroup, :count).by(0)
|
}.to change(AssignmentGroup, :count).by(0)
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,19 +25,20 @@ describe "Importing assignments" do
|
||||||
it "should import assignments for #{system}" do
|
it "should import assignments for #{system}" do
|
||||||
data = get_import_data(system, 'assignment')
|
data = get_import_data(system, 'assignment')
|
||||||
context = get_import_context(system)
|
context = get_import_context(system)
|
||||||
|
migration = context.content_migrations.create!
|
||||||
|
|
||||||
data[:assignments_to_import] = {}
|
data[:assignments_to_import] = {}
|
||||||
expect {
|
expect {
|
||||||
expect(Importers::AssignmentImporter.import_from_migration(data, context)).to be_nil
|
expect(Importers::AssignmentImporter.import_from_migration(data, context, migration)).to be_nil
|
||||||
}.to change(Assignment, :count).by(0)
|
}.to change(Assignment, :count).by(0)
|
||||||
|
|
||||||
data[:assignments_to_import][data[:migration_id]] = true
|
data[:assignments_to_import][data[:migration_id]] = true
|
||||||
expect {
|
expect {
|
||||||
Importers::AssignmentImporter.import_from_migration(data, context)
|
Importers::AssignmentImporter.import_from_migration(data, context, migration)
|
||||||
Importers::AssignmentImporter.import_from_migration(data, context)
|
Importers::AssignmentImporter.import_from_migration(data, context, migration)
|
||||||
}.to change(Assignment, :count).by(1)
|
}.to change(Assignment, :count).by(1)
|
||||||
a = Assignment.where(migration_id: data[:migration_id]).first
|
a = Assignment.where(migration_id: data[:migration_id]).first
|
||||||
|
|
||||||
expect(a.title).to eq data[:title]
|
expect(a.title).to eq data[:title]
|
||||||
expect(a.description).to include(data[:instructions]) if data[:instructions]
|
expect(a.description).to include(data[:instructions]) if data[:instructions]
|
||||||
expect(a.description).to include(data[:description]) if data[:description]
|
expect(a.description).to include(data[:description]) if data[:description]
|
||||||
|
@ -50,6 +51,7 @@ describe "Importing assignments" do
|
||||||
it "should import grading information when rubric is included" do
|
it "should import grading information when rubric is included" do
|
||||||
file_data = get_import_data('', 'assignment')
|
file_data = get_import_data('', 'assignment')
|
||||||
context = get_import_context('')
|
context = get_import_context('')
|
||||||
|
migration = context.content_migrations.create!
|
||||||
|
|
||||||
assignment_hash = file_data.find{|h| h['migration_id'] == '4469882339231'}.with_indifferent_access
|
assignment_hash = file_data.find{|h| h['migration_id'] == '4469882339231'}.with_indifferent_access
|
||||||
|
|
||||||
|
@ -58,13 +60,14 @@ describe "Importing assignments" do
|
||||||
rubric.points_possible = 42
|
rubric.points_possible = 42
|
||||||
rubric.save!
|
rubric.save!
|
||||||
|
|
||||||
Importers::AssignmentImporter.import_from_migration(assignment_hash, context)
|
Importers::AssignmentImporter.import_from_migration(assignment_hash, context, migration)
|
||||||
a = Assignment.where(migration_id: assignment_hash[:migration_id]).first
|
a = Assignment.where(migration_id: assignment_hash[:migration_id]).first
|
||||||
expect(a.points_possible).to eq rubric.points_possible
|
expect(a.points_possible).to eq rubric.points_possible
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should infer the default name when importing a nameless assignment" do
|
it "should infer the default name when importing a nameless assignment" do
|
||||||
course_model
|
course_model
|
||||||
|
migration = @course.content_migrations.create!
|
||||||
nameless_assignment_hash = {
|
nameless_assignment_hash = {
|
||||||
"migration_id" => "ib4834d160d180e2e91572e8b9e3b1bc6",
|
"migration_id" => "ib4834d160d180e2e91572e8b9e3b1bc6",
|
||||||
"assignment_group_migration_id" => "i2bc4b8ea8fac88f1899e5e95d76f3004",
|
"assignment_group_migration_id" => "i2bc4b8ea8fac88f1899e5e95d76f3004",
|
||||||
|
@ -84,13 +87,14 @@ describe "Importing assignments" do
|
||||||
"position" => 6,
|
"position" => 6,
|
||||||
"peer_review_count" => 0
|
"peer_review_count" => 0
|
||||||
}
|
}
|
||||||
Importers::AssignmentImporter.import_from_migration(nameless_assignment_hash, @course)
|
Importers::AssignmentImporter.import_from_migration(nameless_assignment_hash, @course, migration)
|
||||||
assignment = @course.assignments.where(migration_id: 'ib4834d160d180e2e91572e8b9e3b1bc6').first
|
assignment = @course.assignments.where(migration_id: 'ib4834d160d180e2e91572e8b9e3b1bc6').first
|
||||||
expect(assignment.title).to eq 'untitled assignment'
|
expect(assignment.title).to eq 'untitled assignment'
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should schedule auto peer reviews if dates are not shifted " do
|
it "should schedule auto peer reviews if dates are not shifted " do
|
||||||
course_model
|
course_model
|
||||||
|
migration = @course.content_migrations.create!
|
||||||
assign_hash = {
|
assign_hash = {
|
||||||
"migration_id" => "ib4834d160d180e2e91572e8b9e3b1bc6",
|
"migration_id" => "ib4834d160d180e2e91572e8b9e3b1bc6",
|
||||||
"assignment_group_migration_id" => "i2bc4b8ea8fac88f1899e5e95d76f3004",
|
"assignment_group_migration_id" => "i2bc4b8ea8fac88f1899e5e95d76f3004",
|
||||||
|
@ -104,7 +108,7 @@ describe "Importing assignments" do
|
||||||
"peer_reviews_due_at" => 1401947999000
|
"peer_reviews_due_at" => 1401947999000
|
||||||
}
|
}
|
||||||
expects_job_with_tag('Assignment#do_auto_peer_review') {
|
expects_job_with_tag('Assignment#do_auto_peer_review') {
|
||||||
Importers::AssignmentImporter.import_from_migration(assign_hash, @course)
|
Importers::AssignmentImporter.import_from_migration(assign_hash, @course, migration)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -122,14 +126,11 @@ describe "Importing assignments" do
|
||||||
"due_at" => 1401947999000,
|
"due_at" => 1401947999000,
|
||||||
"peer_reviews_due_at" => 1401947999000
|
"peer_reviews_due_at" => 1401947999000
|
||||||
}
|
}
|
||||||
migration = mock()
|
migration = @course.content_migrations.create!
|
||||||
migration.stubs(:for_course_copy?)
|
|
||||||
migration.stubs(:add_missing_content_links)
|
|
||||||
migration.stubs(:add_imported_item)
|
|
||||||
migration.stubs(:date_shift_options).returns(true)
|
migration.stubs(:date_shift_options).returns(true)
|
||||||
expects_job_with_tag('Assignment#do_auto_peer_review', 0) {
|
expects_job_with_tag('Assignment#do_auto_peer_review', 0) {
|
||||||
Importers::AssignmentImporter.import_from_migration(assign_hash, @course, migration)
|
Importers::AssignmentImporter.import_from_migration(assign_hash, @course, migration)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,6 +30,7 @@ module Importers
|
||||||
let(:attachment) do
|
let(:attachment) do
|
||||||
stub(:context= => true,
|
stub(:context= => true,
|
||||||
:migration_id= => true,
|
:migration_id= => true,
|
||||||
|
:migration_id => migration_id,
|
||||||
:save_without_broadcasting! => true,
|
:save_without_broadcasting! => true,
|
||||||
:set_publish_state_for_usage_rights => nil)
|
:set_publish_state_for_usage_rights => nil)
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,6 +23,8 @@ describe Importers::CalendarEventImporter do
|
||||||
|
|
||||||
let_once(:migration_course) { course(active_all: true) }
|
let_once(:migration_course) { course(active_all: true) }
|
||||||
|
|
||||||
|
let(:migration) { migration_course.content_migrations.create! }
|
||||||
|
|
||||||
let(:migration_assignment) do
|
let(:migration_assignment) do
|
||||||
assignment = migration_course.assignments.build(title: 'migration assignment')
|
assignment = migration_course.assignments.build(title: 'migration assignment')
|
||||||
assignment.workflow_state = 'active'
|
assignment.workflow_state = 'active'
|
||||||
|
@ -80,7 +82,7 @@ describe Importers::CalendarEventImporter do
|
||||||
attachment_type: 'external_url',
|
attachment_type: 'external_url',
|
||||||
attachment_value: 'http://example.com'
|
attachment_value: 'http://example.com'
|
||||||
}
|
}
|
||||||
Importers::CalendarEventImporter.import_from_migration(hash, migration_course, nil, event)
|
Importers::CalendarEventImporter.import_from_migration(hash, migration_course, migration, event)
|
||||||
expect(event).not_to be_new_record
|
expect(event).not_to be_new_record
|
||||||
expect(event.imported).to be_truthy
|
expect(event.imported).to be_truthy
|
||||||
expect(event.migration_id).to eq '42'
|
expect(event.migration_id).to eq '42'
|
||||||
|
@ -143,14 +145,15 @@ describe Importers::CalendarEventImporter do
|
||||||
it "should import calendar events for #{system}" do
|
it "should import calendar events for #{system}" do
|
||||||
data = get_import_data(system, 'calendar_event')
|
data = get_import_data(system, 'calendar_event')
|
||||||
context = get_import_context(system)
|
context = get_import_context(system)
|
||||||
|
migration = context.content_migrations.create!
|
||||||
|
|
||||||
data[:events_to_import] = {}
|
data[:events_to_import] = {}
|
||||||
expect(Importers::CalendarEventImporter.import_from_migration(data, context)).to be_nil
|
expect(Importers::CalendarEventImporter.import_from_migration(data, context, migration)).to be_nil
|
||||||
expect(context.calendar_events.count).to eq 0
|
expect(context.calendar_events.count).to eq 0
|
||||||
|
|
||||||
data[:events_to_import][data[:migration_id]] = true
|
data[:events_to_import][data[:migration_id]] = true
|
||||||
Importers::CalendarEventImporter.import_from_migration(data, context)
|
Importers::CalendarEventImporter.import_from_migration(data, context, migration)
|
||||||
Importers::CalendarEventImporter.import_from_migration(data, context)
|
Importers::CalendarEventImporter.import_from_migration(data, context, migration)
|
||||||
expect(context.calendar_events.count).to eq 1
|
expect(context.calendar_events.count).to eq 1
|
||||||
|
|
||||||
event = CalendarEvent.where(migration_id: data[:migration_id]).first
|
event = CalendarEvent.where(migration_id: data[:migration_id]).first
|
||||||
|
|
|
@ -3,14 +3,16 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
|
||||||
describe Importers::ContextExternalToolImporter do
|
describe Importers::ContextExternalToolImporter do
|
||||||
it "should work for course-level tools" do
|
it "should work for course-level tools" do
|
||||||
course_model
|
course_model
|
||||||
tool = Importers::ContextExternalToolImporter.import_from_migration({:title => 'tool', :url => 'http://example.com'}, @course)
|
migration = @course.content_migrations.create!
|
||||||
|
tool = Importers::ContextExternalToolImporter.import_from_migration({:title => 'tool', :url => 'http://example.com'}, @course, migration)
|
||||||
expect(tool).not_to be_nil
|
expect(tool).not_to be_nil
|
||||||
expect(tool.context).to eq @course
|
expect(tool.context).to eq @course
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should work for account-level tools" do
|
it "should work for account-level tools" do
|
||||||
course_model
|
course_model
|
||||||
tool = Importers::ContextExternalToolImporter.import_from_migration({:title => 'tool', :url => 'http://example.com'}, @course.account)
|
migration = @course.account.content_migrations.create!
|
||||||
|
tool = Importers::ContextExternalToolImporter.import_from_migration({:title => 'tool', :url => 'http://example.com'}, @course.account, migration)
|
||||||
expect(tool).not_to be_nil
|
expect(tool).not_to be_nil
|
||||||
expect(tool.context).to eq @course.account
|
expect(tool.context).to eq @course.account
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,13 +25,14 @@ describe "Importing modules" do
|
||||||
it "should import from #{system}" do
|
it "should import from #{system}" do
|
||||||
data = get_import_data(system, 'module')
|
data = get_import_data(system, 'module')
|
||||||
context = get_import_context(system)
|
context = get_import_context(system)
|
||||||
|
migration = context.content_migrations.create!
|
||||||
data[:modules_to_import] = {}
|
data[:modules_to_import] = {}
|
||||||
expect(Importers::ContextModuleImporter.import_from_migration(data, context)).to be_nil
|
expect(Importers::ContextModuleImporter.import_from_migration(data, context, migration)).to be_nil
|
||||||
expect(context.context_modules.count).to eq 0
|
expect(context.context_modules.count).to eq 0
|
||||||
|
|
||||||
data[:modules_to_import][data[:migration_id]] = true
|
data[:modules_to_import][data[:migration_id]] = true
|
||||||
Importers::ContextModuleImporter.import_from_migration(data, context)
|
Importers::ContextModuleImporter.import_from_migration(data, context, migration)
|
||||||
Importers::ContextModuleImporter.import_from_migration(data, context)
|
Importers::ContextModuleImporter.import_from_migration(data, context, migration)
|
||||||
expect(context.context_modules.count).to eq 1
|
expect(context.context_modules.count).to eq 1
|
||||||
|
|
||||||
mod = ContextModule.where(migration_id: data[:migration_id]).first
|
mod = ContextModule.where(migration_id: data[:migration_id]).first
|
||||||
|
@ -40,30 +41,32 @@ describe "Importing modules" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should link to url objects" do
|
it "should link to url objects" do
|
||||||
data = get_import_data('vista', 'module')
|
data = get_import_data('vista', 'module')
|
||||||
context = get_import_context('vista')
|
context = get_import_context('vista')
|
||||||
|
migration = context.content_migrations.create!
|
||||||
context.external_url_hash = {}
|
context.external_url_hash = {}
|
||||||
|
|
||||||
topic = Importers::ContextModuleImporter.import_from_migration(data, context)
|
topic = Importers::ContextModuleImporter.import_from_migration(data, context, migration)
|
||||||
expect(topic.content_tags.count).to eq 2
|
expect(topic.content_tags.count).to eq 2
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should link to objects on the second pass" do
|
it "should link to objects on the second pass" do
|
||||||
data = get_import_data('bb8', 'module')
|
data = get_import_data('bb8', 'module')
|
||||||
context = get_import_context('bb8')
|
context = get_import_context('bb8')
|
||||||
|
migration = context.content_migrations.create!
|
||||||
context.external_url_hash = {}
|
context.external_url_hash = {}
|
||||||
|
|
||||||
|
|
||||||
topic = Importers::ContextModuleImporter.import_from_migration(data, context)
|
topic = Importers::ContextModuleImporter.import_from_migration(data, context, migration)
|
||||||
expect(topic.content_tags.count).to eq 0
|
expect(topic.content_tags.count).to eq 0
|
||||||
|
|
||||||
ass = get_import_data('bb8', 'assignment')
|
ass = get_import_data('bb8', 'assignment')
|
||||||
Importers::AssignmentImporter.import_from_migration(ass, context)
|
Importers::AssignmentImporter.import_from_migration(ass, context, migration)
|
||||||
expect(context.assignments.count).to eq 1
|
expect(context.assignments.count).to eq 1
|
||||||
|
|
||||||
topic = Importers::ContextModuleImporter.import_from_migration(data, context)
|
topic = Importers::ContextModuleImporter.import_from_migration(data, context, migration)
|
||||||
expect(topic.content_tags.count).to eq 1
|
expect(topic.content_tags.count).to eq 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -30,14 +30,15 @@ describe Importers::DiscussionTopicImporter do
|
||||||
data = data.with_indifferent_access
|
data = data.with_indifferent_access
|
||||||
|
|
||||||
context = get_import_context(system)
|
context = get_import_context(system)
|
||||||
|
migration = context.content_migrations.create!
|
||||||
|
|
||||||
data[:topics_to_import] = {}
|
data[:topics_to_import] = {}
|
||||||
expect(Importers::DiscussionTopicImporter.import_from_migration(data, context)).to be_nil
|
expect(Importers::DiscussionTopicImporter.import_from_migration(data, context, migration)).to be_nil
|
||||||
expect(context.discussion_topics.count).to eq 0
|
expect(context.discussion_topics.count).to eq 0
|
||||||
|
|
||||||
data[:topics_to_import][data[:migration_id]] = true
|
data[:topics_to_import][data[:migration_id]] = true
|
||||||
Importers::DiscussionTopicImporter.import_from_migration(data, context)
|
Importers::DiscussionTopicImporter.import_from_migration(data, context, migration)
|
||||||
Importers::DiscussionTopicImporter.import_from_migration(data, context)
|
Importers::DiscussionTopicImporter.import_from_migration(data, context, migration)
|
||||||
expect(context.discussion_topics.count).to eq 1
|
expect(context.discussion_topics.count).to eq 1
|
||||||
|
|
||||||
topic = DiscussionTopic.where(migration_id: data[:migration_id]).first
|
topic = DiscussionTopic.where(migration_id: data[:migration_id]).first
|
||||||
|
@ -62,13 +63,14 @@ describe Importers::DiscussionTopicImporter do
|
||||||
it "should import assignments for #{system}" do
|
it "should import assignments for #{system}" do
|
||||||
data = get_import_data(system, 'announcements')
|
data = get_import_data(system, 'announcements')
|
||||||
context = get_import_context(system)
|
context = get_import_context(system)
|
||||||
|
migration = context.content_migrations.create!
|
||||||
data[:topics_to_import] = {}
|
data[:topics_to_import] = {}
|
||||||
expect(Importers::DiscussionTopicImporter.import_from_migration(data, context)).to be_nil
|
expect(Importers::DiscussionTopicImporter.import_from_migration(data, context, migration)).to be_nil
|
||||||
expect(context.discussion_topics.count).to eq 0
|
expect(context.discussion_topics.count).to eq 0
|
||||||
|
|
||||||
data[:topics_to_import][data[:migration_id]] = true
|
data[:topics_to_import][data[:migration_id]] = true
|
||||||
Importers::DiscussionTopicImporter.import_from_migration(data, context)
|
Importers::DiscussionTopicImporter.import_from_migration(data, context, migration)
|
||||||
Importers::DiscussionTopicImporter.import_from_migration(data, context)
|
Importers::DiscussionTopicImporter.import_from_migration(data, context, migration)
|
||||||
expect(context.discussion_topics.count).to eq 1
|
expect(context.discussion_topics.count).to eq 1
|
||||||
|
|
||||||
topic = DiscussionTopic.where(migration_id: data[:migration_id]).first
|
topic = DiscussionTopic.where(migration_id: data[:migration_id]).first
|
||||||
|
@ -82,12 +84,13 @@ describe Importers::DiscussionTopicImporter do
|
||||||
it "should not attach files when no attachment_migration_id is specified" do
|
it "should not attach files when no attachment_migration_id is specified" do
|
||||||
data = get_import_data('bb8', 'discussion_topic').first.with_indifferent_access
|
data = get_import_data('bb8', 'discussion_topic').first.with_indifferent_access
|
||||||
context = get_import_context('bb8')
|
context = get_import_context('bb8')
|
||||||
|
migration = context.content_migrations.create!
|
||||||
|
|
||||||
data[:attachment_migration_id] = nil
|
data[:attachment_migration_id] = nil
|
||||||
attachment_model(:context => context) # create a file with no migration id
|
attachment_model(:context => context) # create a file with no migration id
|
||||||
|
|
||||||
data[:topics_to_import] = {data[:migration_id] => true}
|
data[:topics_to_import] = {data[:migration_id] => true}
|
||||||
Importers::DiscussionTopicImporter.import_from_migration(data, context)
|
Importers::DiscussionTopicImporter.import_from_migration(data, context, migration)
|
||||||
|
|
||||||
topic = DiscussionTopic.where(migration_id: data[:migration_id]).first
|
topic = DiscussionTopic.where(migration_id: data[:migration_id]).first
|
||||||
expect(topic.attachment).to be_nil
|
expect(topic.attachment).to be_nil
|
||||||
|
|
|
@ -23,13 +23,14 @@ describe Importers::ExternalFeedImporter do
|
||||||
context ".import_from_migration" do
|
context ".import_from_migration" do
|
||||||
it "creates a feed from the provided hash" do
|
it "creates a feed from the provided hash" do
|
||||||
@course = course
|
@course = course
|
||||||
|
migration = @course.content_migrations.create!
|
||||||
data = {
|
data = {
|
||||||
url: 'http://www.example.com/feed',
|
url: 'http://www.example.com/feed',
|
||||||
title: 'test feed',
|
title: 'test feed',
|
||||||
verbosity: 'link_only',
|
verbosity: 'link_only',
|
||||||
header_match: ''
|
header_match: ''
|
||||||
}
|
}
|
||||||
feed = Importers::ExternalFeedImporter.import_from_migration(data, @course)
|
feed = Importers::ExternalFeedImporter.import_from_migration(data, @course, migration)
|
||||||
expect(feed.url).to eq data[:url]
|
expect(feed.url).to eq data[:url]
|
||||||
expect(feed.title).to eq data[:title]
|
expect(feed.title).to eq data[:title]
|
||||||
expect(feed.verbosity).to eq data[:verbosity]
|
expect(feed.verbosity).to eq data[:verbosity]
|
||||||
|
|
|
@ -25,14 +25,15 @@ describe "Importing Groups" do
|
||||||
it "should import from #{system}" do
|
it "should import from #{system}" do
|
||||||
data = get_import_data(system, 'group')
|
data = get_import_data(system, 'group')
|
||||||
context = get_import_context(system)
|
context = get_import_context(system)
|
||||||
|
migration = context.content_migrations.create!
|
||||||
|
|
||||||
data[:groups_to_import] = {}
|
data[:groups_to_import] = {}
|
||||||
expect(Importers::GroupImporter.import_from_migration(data, context)).to be_nil
|
expect(Importers::GroupImporter.import_from_migration(data, context, migration)).to be_nil
|
||||||
expect(context.groups.count).to eq 0
|
expect(context.groups.count).to eq 0
|
||||||
|
|
||||||
data[:groups_to_import][data[:migration_id]] = true
|
data[:groups_to_import][data[:migration_id]] = true
|
||||||
Importers::GroupImporter.import_from_migration(data, context)
|
Importers::GroupImporter.import_from_migration(data, context, migration)
|
||||||
Importers::GroupImporter.import_from_migration(data, context)
|
Importers::GroupImporter.import_from_migration(data, context, migration)
|
||||||
expect(context.groups.count).to eq 1
|
expect(context.groups.count).to eq 1
|
||||||
g = Group.where(migration_id: data[:migration_id]).first
|
g = Group.where(migration_id: data[:migration_id]).first
|
||||||
|
|
||||||
|
@ -44,8 +45,9 @@ describe "Importing Groups" do
|
||||||
it "should attach to a discussion" do
|
it "should attach to a discussion" do
|
||||||
data = get_import_data('bb8', 'group')
|
data = get_import_data('bb8', 'group')
|
||||||
context = get_import_context('bb8')
|
context = get_import_context('bb8')
|
||||||
|
migration = context.content_migrations.create!
|
||||||
|
|
||||||
Importers::GroupImporter.import_from_migration(data, context)
|
Importers::GroupImporter.import_from_migration(data, context, migration)
|
||||||
expect(context.groups.count).to eq 1
|
expect(context.groups.count).to eq 1
|
||||||
|
|
||||||
category = get_import_data('bb8', 'group_discussion')
|
category = get_import_data('bb8', 'group_discussion')
|
||||||
|
@ -54,7 +56,7 @@ describe "Importing Groups" do
|
||||||
topic['group_id'] = category['group_id']
|
topic['group_id'] = category['group_id']
|
||||||
group = Group.where(context_id: context, context_type: context.class.to_s, migration_id: topic['group_id']).first
|
group = Group.where(context_id: context, context_type: context.class.to_s, migration_id: topic['group_id']).first
|
||||||
if group
|
if group
|
||||||
Importers::DiscussionTopicImporter.import_from_migration(topic, group)
|
Importers::DiscussionTopicImporter.import_from_migration(topic, group, migration)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -64,15 +66,17 @@ describe "Importing Groups" do
|
||||||
|
|
||||||
it "should respect group_category from the hash" do
|
it "should respect group_category from the hash" do
|
||||||
course_with_teacher
|
course_with_teacher
|
||||||
|
migration = @course.content_migrations.create!
|
||||||
group = @course.groups.build
|
group = @course.groups.build
|
||||||
Importers::GroupImporter.import_from_migration({:group_category => "random category"}, @course, nil, group)
|
Importers::GroupImporter.import_from_migration({:group_category => "random category"}, @course, migration, group)
|
||||||
expect(group.group_category.name).to eq "random category"
|
expect(group.group_category.name).to eq "random category"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should default group_category to imported if not in the hash" do
|
it "should default group_category to imported if not in the hash" do
|
||||||
course_with_teacher
|
course_with_teacher
|
||||||
|
migration = @course.content_migrations.create!
|
||||||
group = @course.groups.build
|
group = @course.groups.build
|
||||||
Importers::GroupImporter.import_from_migration({}, @course, nil, group)
|
Importers::GroupImporter.import_from_migration({}, @course, migration, group)
|
||||||
expect(group.group_category).to eq GroupCategory.imported_for(@course)
|
expect(group.group_category).to eq GroupCategory.imported_for(@course)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,13 +21,14 @@ require File.expand_path(File.dirname(__FILE__) + '../../../import_helper')
|
||||||
describe "Importers::QuizImporter" do
|
describe "Importers::QuizImporter" do
|
||||||
before(:once) do
|
before(:once) do
|
||||||
course_model
|
course_model
|
||||||
|
@migration = @course.content_migrations.create!
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should get the quiz properties" do
|
it "should get the quiz properties" do
|
||||||
context = course_model
|
context = course_model
|
||||||
question_data = import_example_questions context
|
question_data = import_example_questions context
|
||||||
data = get_import_data ['vista', 'quiz'], 'simple_quiz_data'
|
data = get_import_data ['vista', 'quiz'], 'simple_quiz_data'
|
||||||
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
|
Importers::QuizImporter.import_from_migration(data, context, @migration, question_data)
|
||||||
quiz = Quizzes::Quiz.where(migration_id: data[:migration_id]).first
|
quiz = Quizzes::Quiz.where(migration_id: data[:migration_id]).first
|
||||||
expect(quiz.title).to eq data[:title]
|
expect(quiz.title).to eq data[:title]
|
||||||
expect(quiz.scoring_policy).to eq data[:which_attempt_to_keep]
|
expect(quiz.scoring_policy).to eq data[:which_attempt_to_keep]
|
||||||
|
@ -37,23 +38,23 @@ describe "Importers::QuizImporter" do
|
||||||
expect(quiz.shuffle_answers).to eq data[:shuffle_answers]
|
expect(quiz.shuffle_answers).to eq data[:shuffle_answers]
|
||||||
expect(quiz.show_correct_answers).to eq data[:show_correct_answers]
|
expect(quiz.show_correct_answers).to eq data[:show_correct_answers]
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should complete a quiz question reference" do
|
it "should complete a quiz question reference" do
|
||||||
context = course_model
|
context = course_model
|
||||||
question_data = import_example_questions context
|
question_data = import_example_questions context
|
||||||
data = get_import_data ['vista', 'quiz'], 'simple_quiz_data'
|
data = get_import_data ['vista', 'quiz'], 'simple_quiz_data'
|
||||||
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
|
Importers::QuizImporter.import_from_migration(data, context, @migration, question_data)
|
||||||
quiz = Quizzes::Quiz.where(migration_id: data[:migration_id]).first
|
quiz = Quizzes::Quiz.where(migration_id: data[:migration_id]).first
|
||||||
expect(quiz.quiz_questions.active.count).to eq 1
|
expect(quiz.quiz_questions.active.count).to eq 1
|
||||||
# Check if the expected question name is in there
|
# Check if the expected question name is in there
|
||||||
expect(quiz.quiz_questions.active.first.question_data[:question_name]).to eq "Rocket Bee!"
|
expect(quiz.quiz_questions.active.first.question_data[:question_name]).to eq "Rocket Bee!"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should import a text only question" do
|
it "should import a text only question" do
|
||||||
context = get_import_context
|
context = get_import_context
|
||||||
question_data = import_example_questions context
|
question_data = import_example_questions context
|
||||||
data = get_import_data ['vista', 'quiz'], 'text_only_quiz_data'
|
data = get_import_data ['vista', 'quiz'], 'text_only_quiz_data'
|
||||||
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
|
Importers::QuizImporter.import_from_migration(data, context, @migration, question_data)
|
||||||
quiz = Quizzes::Quiz.where(migration_id: data[:migration_id]).first
|
quiz = Quizzes::Quiz.where(migration_id: data[:migration_id]).first
|
||||||
expect(quiz.unpublished_question_count).to eq 2
|
expect(quiz.unpublished_question_count).to eq 2
|
||||||
expect(quiz.quiz_questions.active.count).to eq 2
|
expect(quiz.quiz_questions.active.count).to eq 2
|
||||||
|
@ -61,12 +62,12 @@ describe "Importers::QuizImporter" do
|
||||||
expect(sorted_questions.first.question_data[:question_text]).to eq data[:questions].first[:question_text]
|
expect(sorted_questions.first.question_data[:question_text]).to eq data[:questions].first[:question_text]
|
||||||
expect(sorted_questions.first.question_data[:question_type]).to eq 'text_only_question'
|
expect(sorted_questions.first.question_data[:question_type]).to eq 'text_only_question'
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should import a question group" do
|
it "should import a question group" do
|
||||||
context = get_import_context
|
context = get_import_context
|
||||||
question_data = import_example_questions context
|
question_data = import_example_questions context
|
||||||
data = get_import_data ['vista', 'quiz'], 'group_quiz_data'
|
data = get_import_data ['vista', 'quiz'], 'group_quiz_data'
|
||||||
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
|
Importers::QuizImporter.import_from_migration(data, context, @migration, question_data)
|
||||||
quiz = Quizzes::Quiz.where(migration_id: data[:migration_id]).first
|
quiz = Quizzes::Quiz.where(migration_id: data[:migration_id]).first
|
||||||
expect(quiz.quiz_groups.count).to eq 1
|
expect(quiz.quiz_groups.count).to eq 1
|
||||||
expect(quiz.quiz_groups.first.quiz_questions.active.count).to eq 3
|
expect(quiz.quiz_groups.first.quiz_questions.active.count).to eq 3
|
||||||
|
@ -78,8 +79,8 @@ describe "Importers::QuizImporter" do
|
||||||
context = get_import_context
|
context = get_import_context
|
||||||
question_data = import_example_questions context
|
question_data = import_example_questions context
|
||||||
data = get_import_data ['vista', 'quiz'], 'text_only_quiz_data'
|
data = get_import_data ['vista', 'quiz'], 'text_only_quiz_data'
|
||||||
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
|
Importers::QuizImporter.import_from_migration(data, context, @migration, question_data)
|
||||||
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
|
Importers::QuizImporter.import_from_migration(data, context, @migration, question_data)
|
||||||
expect(Quizzes::Quiz.count).to eq 1
|
expect(Quizzes::Quiz.count).to eq 1
|
||||||
quiz = Quizzes::Quiz.where(migration_id: data[:migration_id]).first
|
quiz = Quizzes::Quiz.where(migration_id: data[:migration_id]).first
|
||||||
expect(quiz.assignment).to be_nil
|
expect(quiz.assignment).to be_nil
|
||||||
|
@ -91,7 +92,7 @@ describe "Importers::QuizImporter" do
|
||||||
quiz_hash = get_import_data ['vista', 'quiz'], 'simple_quiz_data'
|
quiz_hash = get_import_data ['vista', 'quiz'], 'simple_quiz_data'
|
||||||
data = {'assessments' => {'assessments' => [quiz_hash]}}
|
data = {'assessments' => {'assessments' => [quiz_hash]}}
|
||||||
migration = context.content_migrations.create!
|
migration = context.content_migrations.create!
|
||||||
Importers::CourseContentImporter.import_content(context, data, nil, migration)
|
Importers::CourseContentImporter.import_content(context, data, @migration, migration)
|
||||||
|
|
||||||
expect(Assignment.count).to eq 0
|
expect(Assignment.count).to eq 0
|
||||||
expect(Quizzes::Quiz.count).to eq 1
|
expect(Quizzes::Quiz.count).to eq 1
|
||||||
|
@ -111,7 +112,7 @@ describe "Importers::QuizImporter" do
|
||||||
data = {'assessments' => {'assessments' => [quiz_hash]}, 'assignments' => [assignment_hash]}
|
data = {'assessments' => {'assessments' => [quiz_hash]}, 'assignments' => [assignment_hash]}
|
||||||
|
|
||||||
migration = context.content_migrations.create!
|
migration = context.content_migrations.create!
|
||||||
Importers::CourseContentImporter.import_content(context, data, nil, migration)
|
Importers::CourseContentImporter.import_content(context, data, @migration, migration)
|
||||||
|
|
||||||
expect(Assignment.count).to eq 1
|
expect(Assignment.count).to eq 1
|
||||||
expect(Quizzes::Quiz.count).to eq 1
|
expect(Quizzes::Quiz.count).to eq 1
|
||||||
|
@ -121,10 +122,12 @@ describe "Importers::QuizImporter" do
|
||||||
expect(quiz.assignment).not_to be_nil
|
expect(quiz.assignment).not_to be_nil
|
||||||
expect(quiz.quiz_type).to eq 'assignment'
|
expect(quiz.quiz_type).to eq 'assignment'
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should convert relative file references to course-relative file references" do
|
it "should convert relative file references to course-relative file references" do
|
||||||
context = course_model
|
context = @course
|
||||||
import_example_questions context
|
import_example_questions context
|
||||||
|
@migration.resolve_content_links!
|
||||||
|
|
||||||
question = AssessmentQuestion.where(migration_id: '4393906433391').first
|
question = AssessmentQuestion.where(migration_id: '4393906433391').first
|
||||||
expect(question.data[:question_text]).to eq "Why does that bee/rocket ship company suck? <img src=\"/courses/#{context.id}/file_contents/course%20files/rocket.png\">"
|
expect(question.data[:question_text]).to eq "Why does that bee/rocket ship company suck? <img src=\"/courses/#{context.id}/file_contents/course%20files/rocket.png\">"
|
||||||
question = AssessmentQuestion.where(migration_id: 'URN-X-WEBCT-VISTA_V2-790EA1350E1A681DE0440003BA07D9B4').first
|
question = AssessmentQuestion.where(migration_id: 'URN-X-WEBCT-VISTA_V2-790EA1350E1A681DE0440003BA07D9B4').first
|
||||||
|
@ -135,13 +138,13 @@ describe "Importers::QuizImporter" do
|
||||||
context = get_import_context
|
context = get_import_context
|
||||||
question_data = import_example_questions context
|
question_data = import_example_questions context
|
||||||
data = get_import_data ['vista', 'quiz'], 'simple_quiz_data'
|
data = get_import_data ['vista', 'quiz'], 'simple_quiz_data'
|
||||||
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
|
Importers::QuizImporter.import_from_migration(data, context, @migration, question_data)
|
||||||
quiz = Quizzes::Quiz.where(migration_id: data[:migration_id]).first
|
quiz = Quizzes::Quiz.where(migration_id: data[:migration_id]).first
|
||||||
|
|
||||||
expect(quiz.quiz_questions.active.first.question_data[:question_name]).to eq "Rocket Bee!"
|
expect(quiz.quiz_questions.active.first.question_data[:question_name]).to eq "Rocket Bee!"
|
||||||
|
|
||||||
question_data[:aq_data][data['questions'].first[:migration_id]]['question_name'] = "Not Rocket Bee?"
|
question_data[:aq_data][data['questions'].first[:migration_id]]['question_name'] = "Not Rocket Bee?"
|
||||||
Importers::QuizImporter.import_from_migration(data, context, nil, question_data)
|
Importers::QuizImporter.import_from_migration(data, context, @migration, question_data)
|
||||||
|
|
||||||
expect(quiz.quiz_questions.active.first.question_data[:question_name]).to eq "Not Rocket Bee?"
|
expect(quiz.quiz_questions.active.first.question_data[:question_name]).to eq "Not Rocket Bee?"
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,9 +25,10 @@ describe "Importing wikis" do
|
||||||
it "should import for #{system}" do
|
it "should import for #{system}" do
|
||||||
data = get_import_data(system, 'wiki')
|
data = get_import_data(system, 'wiki')
|
||||||
context = get_import_context(system)
|
context = get_import_context(system)
|
||||||
|
migration = context.content_migrations.create!
|
||||||
|
|
||||||
Importers::WikiPageImporter.import_from_migration(data, context)
|
Importers::WikiPageImporter.import_from_migration(data, context, migration)
|
||||||
Importers::WikiPageImporter.import_from_migration(data, context)
|
Importers::WikiPageImporter.import_from_migration(data, context, migration)
|
||||||
expect(context.wiki.wiki_pages.count).to eq 1
|
expect(context.wiki.wiki_pages.count).to eq 1
|
||||||
|
|
||||||
wiki = WikiPage.where(migration_id: data[:migration_id]).first
|
wiki = WikiPage.where(migration_id: data[:migration_id]).first
|
||||||
|
@ -39,12 +40,14 @@ describe "Importing wikis" do
|
||||||
it "should update BB9 wiki page links to the correct url" do
|
it "should update BB9 wiki page links to the correct url" do
|
||||||
data = get_import_data('bb9', 'wikis')
|
data = get_import_data('bb9', 'wikis')
|
||||||
context = get_import_context('bb9')
|
context = get_import_context('bb9')
|
||||||
|
migration = context.content_migrations.create!
|
||||||
2.times do
|
2.times do
|
||||||
data.each do |wiki|
|
data.each do |wiki|
|
||||||
Importers::WikiPageImporter.import_from_migration(wiki, context)
|
Importers::WikiPageImporter.import_from_migration(wiki, context, migration)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
migration.resolve_content_links!
|
||||||
|
|
||||||
# The wiki references should resolve to course urls
|
# The wiki references should resolve to course urls
|
||||||
expect(context.wiki.wiki_pages.count).to eq 18
|
expect(context.wiki.wiki_pages.count).to eq 18
|
||||||
wiki = WikiPage.where(migration_id: 'res00146').first
|
wiki = WikiPage.where(migration_id: 'res00146').first
|
||||||
|
|
Loading…
Reference in New Issue