selective content export api
test plan: * test the undocumented content_list api endpoint: api/v1/courses/:course_id/content_list which returns the same content list that would be shown by the selective_content endpoint of a pending course copy content migration with no 'type' parameter, it should return a list of exportable categories and urls for each expandable type with a type parameter (e.g. content_list?type=context_modules), it should send a list of items with their titles and ids (asset strings) these ids can be used to select the content for the export * use the content_exports_api create endpoint to create a common_cartridge export, but add an additional argument, "select" (equivalent to the "copy" parameters for the content_migration api) e.g. api/v1/courses/:course_id/content_exports? select[all_wiki_pages]=1& select[context_modules][context_module_42]=1 (replacing :course_id and :module_id) * also try using arrays of ids as a parameter e.g. api/v1/courses/:course_id/content_exports? select[context_modules][]=42& select[context_modules][]=43 this should create a package that only contains the specified content, and any course files referenced in html content (confirm by re-importing) closes #CNVS-13502 Change-Id: I5bdcdc8255b30c5f78585dc335d25eb19bd5a7c0 Reviewed-on: https://gerrit.instructure.com/36655 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Clare Strong <clare@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com> Product-Review: Bracken Mosbacker <bracken@instructure.com>
This commit is contained in:
parent
5bd46c8f39
commit
5b2d0cb97f
|
@ -131,14 +131,17 @@ class ContentExportsApiController < ApplicationController
|
|||
export.course = @context
|
||||
export.user = @current_user
|
||||
export.workflow_state = 'created'
|
||||
|
||||
selected_content = ContentMigration.process_copy_params(params[:select], true) if params[:select]
|
||||
if params[:export_type] == 'qti'
|
||||
export.export_type = ContentExport::QTI
|
||||
export.selected_content = { all_quizzes: true }
|
||||
export.selected_content = selected_content || { all_quizzes: true }
|
||||
else
|
||||
export.export_type = ContentExport::COMMON_CARTRIDGE
|
||||
export.selected_content = { everything: true }
|
||||
export.selected_content = selected_content || { everything: true }
|
||||
end
|
||||
opts = params.slice(:version)
|
||||
|
||||
export.progress = 0
|
||||
if export.save
|
||||
export.queue_api_job(opts)
|
||||
|
@ -149,4 +152,16 @@ class ContentExportsApiController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def content_list
|
||||
if authorized_action(@context, @current_user, :read_as_admin)
|
||||
base_url = api_v1_course_content_list_url(@context)
|
||||
formatter = Canvas::Migration::Helpers::SelectiveContentFormatter.new(nil, base_url)
|
||||
|
||||
unless formatter.valid_type?(params[:type])
|
||||
return render :json => {:message => "unsupported migration type"}, :status => :bad_request
|
||||
end
|
||||
|
||||
render :json => formatter.get_content_list(params[:type], @context)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -428,7 +428,12 @@ class ContentMigrationsController < ApplicationController
|
|||
def content_list
|
||||
@content_migration = @context.content_migrations.find(params[:id])
|
||||
base_url = api_v1_course_content_migration_selective_data_url(@context, @content_migration)
|
||||
render :json => @content_migration.get_content_list(params[:type], base_url)
|
||||
formatter = Canvas::Migration::Helpers::SelectiveContentFormatter.new(@content_migration, base_url)
|
||||
|
||||
unless formatter.valid_type?(params[:type])
|
||||
return render :json => {:message => "unsupported migration type"}, :status => :bad_request
|
||||
end
|
||||
render :json => formatter.get_content_list(params[:type])
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -164,12 +164,12 @@ class ContentExport < ActiveRecord::Base
|
|||
is_set?(selected_content[symbol]) || is_set?(selected_content[:everything])
|
||||
end
|
||||
|
||||
def add_item_to_export(obj)
|
||||
return unless obj && obj.class.respond_to?(:table_name)
|
||||
def add_item_to_export(obj, type=nil)
|
||||
return unless obj && (type || obj.class.respond_to?(:table_name))
|
||||
return if selected_content.empty?
|
||||
return if is_set?(selected_content[:everything])
|
||||
|
||||
asset_type = obj.class.table_name
|
||||
asset_type = type || obj.class.table_name
|
||||
selected_content[asset_type] ||= {}
|
||||
selected_content[asset_type][CC::CCHelper.create_key(obj)] = true
|
||||
end
|
||||
|
|
|
@ -571,12 +571,6 @@ class ContentMigration < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
# returns a list of content for selective content migrations
|
||||
# If no section is specified the top-level areas with content are returned
|
||||
def get_content_list(type=nil, base_url=nil)
|
||||
Canvas::Migration::Helpers::SelectiveContentFormatter.new(self, base_url).get_content_list(type)
|
||||
end
|
||||
|
||||
UPLOAD_TIMEOUT = 1.hour
|
||||
def check_for_pre_processing_timeout
|
||||
if self.pre_processing? && (self.updated_at.utc + UPLOAD_TIMEOUT) < Time.now.utc
|
||||
|
@ -588,20 +582,47 @@ class ContentMigration < ActiveRecord::Base
|
|||
end
|
||||
|
||||
# strips out the "id_" prepending the migration ids in the form
|
||||
def self.process_copy_params(hash)
|
||||
return {} if hash.blank? || !hash.is_a?(Hash)
|
||||
hash.values.each do |sub_hash|
|
||||
next unless sub_hash.is_a?(Hash) # e.g. second level in :copy => {:context_modules => {:id_100 => true, etc}}
|
||||
# also converts arrays of migration ids (or real ids for course exports) into the old hash format
|
||||
def self.process_copy_params(hash, for_content_export=false)
|
||||
return {} if hash.blank?
|
||||
new_hash = {}
|
||||
|
||||
clean_hash = {}
|
||||
sub_hash.keys.each do |k|
|
||||
if k.is_a?(String) && k.start_with?("id_")
|
||||
clean_hash[k.sub("id_", "")] = sub_hash.delete(k)
|
||||
hash.each do |key, value|
|
||||
case value
|
||||
when Hash # e.g. second level in :copy => {:context_modules => {:id_100 => true, etc}}
|
||||
new_sub_hash = {}
|
||||
|
||||
value.each do |sub_key, sub_value|
|
||||
if for_content_export
|
||||
new_sub_hash[CC::CCHelper.create_key(sub_key)] = sub_value
|
||||
elsif sub_key.is_a?(String) && sub_key.start_with?("id_")
|
||||
new_sub_hash[sub_key.sub("id_", "")] = sub_value
|
||||
else
|
||||
new_sub_hash[sub_key] = sub_value
|
||||
end
|
||||
end
|
||||
|
||||
new_hash[key] = new_sub_hash
|
||||
when Array
|
||||
# e.g. :select => {:context_modules => [100, 101]} for content exports
|
||||
# or :select => {:context_modules => [blahblahblah, blahblahblah2]} for normal migration ids
|
||||
sub_hash = {}
|
||||
if for_content_export
|
||||
asset_type = key.to_s.singularize
|
||||
value.each do |id|
|
||||
sub_hash[CC::CCHelper.create_key("#{asset_type}_#{id}")] = '1'
|
||||
end
|
||||
else
|
||||
value.each do |id|
|
||||
sub_hash[id] = '1'
|
||||
end
|
||||
end
|
||||
new_hash[key] = sub_hash
|
||||
else
|
||||
new_hash[key] = value
|
||||
end
|
||||
sub_hash.merge!(clean_hash)
|
||||
end
|
||||
hash
|
||||
new_hash
|
||||
end
|
||||
|
||||
def imported_migration_items
|
||||
|
|
|
@ -843,6 +843,8 @@ routes.draw do
|
|||
get 'courses/:course_id/folders/:id', :controller => :folders, :action => :show, :path_name => 'course_folder'
|
||||
put 'accounts/:account_id/courses', :action => :batch_update
|
||||
post 'courses/:course_id/ping', :action => :ping, :path_name => 'course_ping'
|
||||
|
||||
get "courses/:course_id/content_list", :controller => :content_exports_api, :action => :content_list, :path_name => "course_content_list"
|
||||
end
|
||||
|
||||
scope(:controller => :tabs) do
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
module Canvas::Migration::Helpers
|
||||
class SelectiveContentFormatter
|
||||
COURSE_SETTING_TYPE = -> { I18n.t('lib.canvas.migration.course_settings', 'Course Settings') }
|
||||
COURSE_SYLLABUS_TYPE = -> { I18n.t('lib.canvas.migration.syllabus_body', 'Syllabus Body') }
|
||||
SELECTIVE_CONTENT_TYPES = [
|
||||
['course_settings', -> { I18n.t('lib.canvas.migration.course_settings', 'Course Settings') }],
|
||||
['syllabus_body', -> { I18n.t('lib.canvas.migration.syllabus_body', 'Syllabus Body') }],
|
||||
['context_modules', -> { I18n.t('lib.canvas.migration.context_modules', 'Modules') }],
|
||||
['assignments', -> { I18n.t('lib.canvas.migration.assignments', 'Assignments') }],
|
||||
['quizzes', -> { I18n.t('lib.canvas.migration.quizzes', 'Quizzes') }],
|
||||
|
@ -18,16 +18,20 @@ module Canvas::Migration::Helpers
|
|||
['attachments', -> { I18n.t('lib.canvas.migration.attachments', 'Files') }],
|
||||
]
|
||||
|
||||
def initialize(migration, base_url=nil)
|
||||
def initialize(migration=nil, base_url=nil)
|
||||
@migration = migration
|
||||
@base_url = base_url
|
||||
end
|
||||
|
||||
def get_content_list(type=nil)
|
||||
raise "unsupported migration type" if type && !SELECTIVE_CONTENT_TYPES.any?{|t|t[0] == type}
|
||||
def valid_type?(type=nil)
|
||||
type.nil? || SELECTIVE_CONTENT_TYPES.any?{|t|t[0] == type}
|
||||
end
|
||||
|
||||
if @migration.migration_type == 'course_copy_importer'
|
||||
get_content_from_course(type)
|
||||
def get_content_list(type=nil, source=nil)
|
||||
raise "unsupported migration type" unless valid_type?(type)
|
||||
|
||||
if !@migration || @migration.migration_type == 'course_copy_importer'
|
||||
get_content_from_course(type, source)
|
||||
elsif @migration.overview_attachment
|
||||
get_content_from_overview(type)
|
||||
else
|
||||
|
@ -37,6 +41,10 @@ module Canvas::Migration::Helpers
|
|||
|
||||
private
|
||||
|
||||
def property_prefix
|
||||
@migration ? "copy" : "select"
|
||||
end
|
||||
|
||||
# pulls the available items from the overview attachment on the content migration
|
||||
def get_content_from_overview(type=nil)
|
||||
course_data = Rails.cache.fetch(['migration_selective_cache', @migration.shard, @migration].cache_key, :expires_in => 5.minutes) do
|
||||
|
@ -69,15 +77,14 @@ module Canvas::Migration::Helpers
|
|||
end
|
||||
else
|
||||
if course_data['course']
|
||||
content_list << {type: 'course_settings', property: "copy[all_course_settings]", title: SELECTIVE_CONTENT_TYPES[0].last.call}
|
||||
content_list << {type: 'course_settings', property: "#{property_prefix}[all_course_settings]", title: COURSE_SETTING_TYPE.call}
|
||||
if course_data['course']['syllabus_body']
|
||||
content_list << {type: 'syllabus_body', property: "copy[all_syllabus_body]", title: SELECTIVE_CONTENT_TYPES[1].last.call}
|
||||
|
||||
content_list << {type: 'syllabus_body', property: "#{property_prefix}[all_syllabus_body]", title: COURSE_SYLLABUS_TYPE.call}
|
||||
end
|
||||
end
|
||||
SELECTIVE_CONTENT_TYPES.each do |type, title|
|
||||
if course_data[type] && course_data[type].count > 0
|
||||
hash = {type: type, property: "copy[all_#{type}]", title: title.call, count: course_data[type].count}
|
||||
hash = {type: type, property: "#{property_prefix}[all_#{type}]", title: title.call, count: course_data[type].count}
|
||||
add_url!(hash, type)
|
||||
content_list << hash
|
||||
end
|
||||
|
@ -121,7 +128,7 @@ module Canvas::Migration::Helpers
|
|||
content_list << item_hash('attachments', atts[0])
|
||||
else
|
||||
mig_id = Digest::MD5.hexdigest(folder_name)
|
||||
folder = {type: 'folders', property: "copy[folders][id_#{mig_id}]", title: folder_name, migration_id: mig_id, sub_items: []}
|
||||
folder = {type: 'folders', property: "#{property_prefix}[folders][id_#{mig_id}]", title: folder_name, migration_id: mig_id, sub_items: []}
|
||||
content_list << folder
|
||||
atts.each {|att| folder[:sub_items] << item_hash('attachments', att)}
|
||||
end
|
||||
|
@ -131,7 +138,7 @@ module Canvas::Migration::Helpers
|
|||
def item_hash(type, item)
|
||||
hash = {
|
||||
type: type,
|
||||
property: "copy[#{type}][id_#{item['migration_id']}]",
|
||||
property: "#{property_prefix}[#{type}][id_#{item['migration_id']}]",
|
||||
title: item['title'],
|
||||
migration_id: item['migration_id']
|
||||
}
|
||||
|
@ -139,7 +146,7 @@ module Canvas::Migration::Helpers
|
|||
hash[:path] = item['path_name']
|
||||
hash[:title] = item['file_name']
|
||||
elsif type == 'assessment_question_banks'
|
||||
if hash[:title].blank? && @migration.context.respond_to?(:assessment_question_banks)
|
||||
if hash[:title].blank? && @migration && @migration.context.respond_to?(:assessment_question_banks)
|
||||
if hash[:migration_id] && bank = @migration.context.assessment_question_banks.find_by_migration_id(hash[:migration_id])
|
||||
hash[:title] = bank.title
|
||||
elsif @migration.question_bank_id && default_bank = @migration.context.assessment_question_banks.find_by_id(@migration.question_bank_id)
|
||||
|
@ -170,9 +177,10 @@ module Canvas::Migration::Helpers
|
|||
end
|
||||
|
||||
# returns lists of available content from a source course
|
||||
def get_content_from_course(type=nil)
|
||||
def get_content_from_course(type=nil, source=nil)
|
||||
content_list = []
|
||||
if source = @migration.source_course || Course.find(@migration.migration_settings[:source_course_id])
|
||||
source ||= @migration.source_course || Course.find(@migration.migration_settings[:source_course_id]) if @migration
|
||||
if source
|
||||
if type
|
||||
case type
|
||||
when 'assignments'
|
||||
|
@ -214,14 +222,14 @@ module Canvas::Migration::Helpers
|
|||
end
|
||||
end
|
||||
else
|
||||
content_list << {type: 'course_settings', property: "#{property_prefix}[all_course_settings]", title: COURSE_SETTING_TYPE.call}
|
||||
content_list << {type: 'syllabus_body', property: "#{property_prefix}[all_syllabus_body]", title: COURSE_SYLLABUS_TYPE.call}
|
||||
|
||||
SELECTIVE_CONTENT_TYPES.each do |type, title|
|
||||
next if type == 'groups'
|
||||
|
||||
count = 0
|
||||
if type == 'course_settings' || type == 'syllabus_body'
|
||||
content_list << {type: type, property: "copy[all_#{type}]", title: title.call}
|
||||
next
|
||||
elsif type == 'wiki_pages'
|
||||
if type == 'wiki_pages'
|
||||
count = source.wiki.wiki_pages.not_deleted.count
|
||||
elsif type == 'discussion_topics'
|
||||
count = source.discussion_topics.active.only_discussion_topics.count
|
||||
|
@ -236,7 +244,7 @@ module Canvas::Migration::Helpers
|
|||
end
|
||||
|
||||
next if count == 0
|
||||
hash = {type: type, property: "copy[all_#{type}]", title: title.call, count: count}
|
||||
hash = {type: type, property: "#{property_prefix}[all_#{type}]", title: title.call, count: count}
|
||||
add_url!(hash, type)
|
||||
content_list << hash
|
||||
end
|
||||
|
@ -253,8 +261,7 @@ module Canvas::Migration::Helpers
|
|||
end
|
||||
end
|
||||
|
||||
def course_item_hash(type, item)
|
||||
mig_id = CC::CCHelper.create_key(item)
|
||||
def course_item_hash(type, item, include_linked_resource=true)
|
||||
title = nil
|
||||
title ||= item.title if item.respond_to?(:title)
|
||||
title ||= item.full_name if item.respond_to?(:full_name)
|
||||
|
@ -263,38 +270,39 @@ module Canvas::Migration::Helpers
|
|||
title ||= item.short_description if item.respond_to?(:short_description)
|
||||
title ||= ''
|
||||
|
||||
hash = {type: type, property: "copy[#{type}][id_#{mig_id}]", title: title, migration_id: mig_id}
|
||||
hash = course_linked_resource(item, hash)
|
||||
hash = {type: type, title: title}
|
||||
if @migration
|
||||
mig_id = CC::CCHelper.create_key(item)
|
||||
hash[:migration_id] = mig_id
|
||||
hash[:property] = "#{property_prefix}[#{type}][id_#{mig_id}]"
|
||||
else
|
||||
hash[:id] = item.asset_string
|
||||
end
|
||||
hash = course_linked_resource(item, hash) if include_linked_resource
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
def course_linked_resource(item, hash)
|
||||
lr = nil
|
||||
if item.is_a?(Assignment)
|
||||
if item.quiz
|
||||
mig_id = CC::CCHelper.create_key(item.quiz)
|
||||
hash[:linked_resource] = {:type => 'quizzes', :migration_id => mig_id,
|
||||
:message => I18n.t('linked_quiz_message', "linked with Quiz '%{title}'", :title => item.quiz.title)
|
||||
}
|
||||
lr = course_item_hash('quizzes', item.quiz, false)
|
||||
lr[:message] = I18n.t('linked_quiz_message', "linked with Quiz '%{title}'",
|
||||
:title => item.quiz.title)
|
||||
elsif item.discussion_topic
|
||||
mig_id = CC::CCHelper.create_key(item.discussion_topic)
|
||||
hash[:linked_resource] = {:type => 'discussion_topics', :migration_id => mig_id,
|
||||
:message => I18n.t('linked_discussion_topic_message', "linked with Discussion Topic '%{title}'",
|
||||
:title => item.discussion_topic.title)
|
||||
}
|
||||
lr = course_item_hash('discussion_topics', item.discussion_topic, false)
|
||||
lr[:message] = I18n.t('linked_discussion_topic_message', "linked with Discussion Topic '%{title}'",
|
||||
:title => item.discussion_topic.title)
|
||||
end
|
||||
elsif item.is_a?(DiscussionTopic) && item.assignment
|
||||
mig_id = CC::CCHelper.create_key(item.assignment)
|
||||
hash[:linked_resource] = {:type => 'assignments', :migration_id => mig_id,
|
||||
:message => I18n.t('linked_assignment_message', "linked with Assignment '%{title}'",
|
||||
elsif (item.is_a?(DiscussionTopic) || item.is_a?(Quizzes::Quiz)) && item.assignment
|
||||
lr = course_item_hash('assignments', item.assignment, false)
|
||||
lr[:message] = I18n.t('linked_assignment_message', "linked with Assignment '%{title}'",
|
||||
:title => item.assignment.title)
|
||||
}
|
||||
elsif item.is_a?(Quizzes::Quiz) && item.assignment
|
||||
mig_id = CC::CCHelper.create_key(item.assignment)
|
||||
hash[:linked_resource] = {:type => 'assignments', :migration_id => mig_id,
|
||||
:message => I18n.t('linked_assignment_message', "linked with Assignment '%{title}'",
|
||||
:title => item.assignment.title)
|
||||
}
|
||||
end
|
||||
if lr
|
||||
lr.delete(:title)
|
||||
hash[:linked_resource] = lr
|
||||
end
|
||||
hash
|
||||
end
|
||||
|
|
|
@ -22,19 +22,23 @@ module CC
|
|||
def add_course_files
|
||||
return if for_course_copy
|
||||
|
||||
@html_exporter.referenced_files.keys.each do |att_id|
|
||||
add_item_to_export("attachment_#{att_id}", "attachments")
|
||||
end
|
||||
|
||||
course_folder = Folder.root_folders(@course).first
|
||||
files_with_metadata = { :folders => [], :files => [] }
|
||||
@added_attachment_ids = Set.new
|
||||
|
||||
zipper = ContentZipper.new(:check_user => false)
|
||||
zipper.process_folder(course_folder, @zip_file, [CCHelper::WEB_RESOURCES_FOLDER]) do |file, folder_names|
|
||||
zipper.process_folder(course_folder, @zip_file, [CCHelper::WEB_RESOURCES_FOLDER], :exporter => @manifest.exporter) do |file, folder_names|
|
||||
begin
|
||||
if file.is_a? Folder
|
||||
dir = File.join(folder_names[1..-1])
|
||||
files_with_metadata[:folders] << [file, dir] if file.hidden? || file.locked
|
||||
next
|
||||
end
|
||||
|
||||
|
||||
@added_attachment_ids << file.id
|
||||
path = File.join(folder_names, file.display_name)
|
||||
migration_id = CCHelper.create_key(file)
|
||||
|
|
|
@ -242,11 +242,11 @@ class ContentZipper
|
|||
end
|
||||
end
|
||||
|
||||
def process_folder(folder, zipfile, start_dirs=[], &callback)
|
||||
def process_folder(folder, zipfile, start_dirs=[], opts={}, &callback)
|
||||
if callback
|
||||
zip_folder(folder, zipfile, start_dirs, &callback)
|
||||
zip_folder(folder, zipfile, start_dirs, opts, &callback)
|
||||
else
|
||||
zip_folder(folder, zipfile, start_dirs)
|
||||
zip_folder(folder, zipfile, start_dirs, opts)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -261,7 +261,7 @@ class ContentZipper
|
|||
end
|
||||
|
||||
# The callback should accept two arguments, the attachment/folder and the folder names
|
||||
def zip_folder(folder, zipfile, folder_names, &callback)
|
||||
def zip_folder(folder, zipfile, folder_names, opts={}, &callback)
|
||||
if callback && (folder.hidden? || folder.locked)
|
||||
callback.call(folder, folder_names)
|
||||
end
|
||||
|
@ -275,6 +275,8 @@ class ContentZipper
|
|||
else
|
||||
folder.visible_file_attachments
|
||||
end
|
||||
|
||||
attachments = attachments.select{|a| opts[:exporter].export_object?(a)} if opts[:exporter]
|
||||
attachments.select{|a| !@check_user || a.grants_right?(@user, :download)}.each do |attachment|
|
||||
callback.call(attachment, folder_names) if callback
|
||||
@context = folder.context
|
||||
|
@ -285,9 +287,9 @@ class ContentZipper
|
|||
folder.active_sub_folders.select{|f| !@check_user || f.grants_right?(@user, :read_contents)}.each do |sub_folder|
|
||||
new_names = Array.new(folder_names) << sub_folder.name
|
||||
if callback
|
||||
zip_folder(sub_folder, zipfile, new_names, &callback)
|
||||
zip_folder(sub_folder, zipfile, new_names, opts, &callback)
|
||||
else
|
||||
zip_folder(sub_folder, zipfile, new_names)
|
||||
zip_folder(sub_folder, zipfile, new_names, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -198,5 +198,153 @@ describe ContentExportsApiController, type: :request do
|
|||
end
|
||||
end
|
||||
|
||||
it "should create a selective course export with old migration id format" do
|
||||
att_to_copy = Attachment.create!(:context => t_course, :filename => 'hi.txt',
|
||||
:uploaded_data => StringIO.new("stuff"), :folder => Folder.unfiled_folder(t_course))
|
||||
att_to_not_copy = Attachment.create!(:context => t_course, :filename => 'derp.txt',
|
||||
:uploaded_data => StringIO.new("more stuff"), :folder => Folder.unfiled_folder(t_course))
|
||||
|
||||
page_to_copy = t_course.wiki.wiki_pages.create!(:title => "other page")
|
||||
page_to_copy.body = "<p><a href=\"/courses/#{t_course.id}/files/#{att_to_copy.id}/preview\">hey look a link</a></p>"
|
||||
page_to_copy.save!
|
||||
page_to_not_copy = t_course.wiki.wiki_pages.create!(:title => "another page")
|
||||
|
||||
# both the wiki page and the referenced attachment should be exported implicitly through the module
|
||||
mod = t_course.context_modules.create!(:name => "who cares")
|
||||
mod.add_item(:id => page_to_copy.id, :type => "wiki_page")
|
||||
|
||||
json = api_call_as_user(t_teacher, :post, "/api/v1/courses/#{t_course.id}/content_exports?export_type=common_cartridge",
|
||||
{ controller: 'content_exports_api', action: 'create', format: 'json', course_id: t_course.to_param, export_type: 'common_cartridge'},
|
||||
{ :select => {:context_modules => {mod.asset_string => "1"}}})
|
||||
export = t_course.content_exports.find_by_id(json['id'])
|
||||
export.should_not be_nil
|
||||
export.workflow_state.should eql 'created'
|
||||
export.export_type.should eql 'common_cartridge'
|
||||
export.user_id.should eql t_teacher.id
|
||||
export.settings['selected_content']['context_modules'].should == {CC::CCHelper.create_key(mod) => "1"}
|
||||
export.job_progress.should be_queued
|
||||
|
||||
run_jobs
|
||||
|
||||
export.reload
|
||||
export.workflow_state.should eql 'exported'
|
||||
export.job_progress.should be_completed
|
||||
export.attachment.should_not be_nil
|
||||
|
||||
course
|
||||
cm = @course.content_migrations.new
|
||||
cm.attachment = export.attachment
|
||||
cm.migration_type = "canvas_cartridge_importer"
|
||||
cm.migration_settings[:import_immediately] = true
|
||||
cm.save!
|
||||
cm.queue_migration
|
||||
|
||||
run_jobs
|
||||
|
||||
@course.context_modules.find_by_migration_id(CC::CCHelper.create_key(mod)).should_not be_nil
|
||||
copied_page = @course.wiki.wiki_pages.find_by_migration_id(CC::CCHelper.create_key(page_to_copy))
|
||||
copied_page.should_not be_nil
|
||||
@course.wiki.wiki_pages.find_by_migration_id(CC::CCHelper.create_key(page_to_not_copy)).should be_nil
|
||||
|
||||
copied_att = @course.attachments.find_by_filename(att_to_copy.filename)
|
||||
copied_att.should_not be_nil
|
||||
copied_page.body.should == "<p><a href=\"/courses/#{@course.id}/files/#{copied_att.id}/preview\">hey look a link</a></p>"
|
||||
@course.attachments.find_by_filename(att_to_not_copy.filename).should be_nil
|
||||
end
|
||||
|
||||
it "should create a selective course export with arrays of ids" do
|
||||
att_to_copy = Attachment.create!(:context => t_course, :filename => 'hi.txt',
|
||||
:uploaded_data => StringIO.new("stuff"), :folder => Folder.unfiled_folder(t_course))
|
||||
att_to_not_copy = Attachment.create!(:context => t_course, :filename => 'derp.txt',
|
||||
:uploaded_data => StringIO.new("more stuff"), :folder => Folder.unfiled_folder(t_course))
|
||||
|
||||
page_to_copy = t_course.wiki.wiki_pages.create!(:title => "other page")
|
||||
page_to_copy.body = "<p><a href=\"/courses/#{t_course.id}/files/#{att_to_copy.id}/preview\">hey look a link</a></p>"
|
||||
page_to_copy.save!
|
||||
page_to_not_copy = t_course.wiki.wiki_pages.create!(:title => "another page")
|
||||
|
||||
# both the wiki page and the referenced attachment should be exported implicitly through the module
|
||||
mod = t_course.context_modules.create!(:name => "who cares")
|
||||
mod.add_item(:id => page_to_copy.id, :type => "wiki_page")
|
||||
|
||||
json = api_call_as_user(t_teacher, :post, "/api/v1/courses/#{t_course.id}/content_exports?export_type=common_cartridge",
|
||||
{ controller: 'content_exports_api', action: 'create', format: 'json', course_id: t_course.to_param, export_type: 'common_cartridge'},
|
||||
{ :select => {:context_modules => [mod.id]}})
|
||||
export = t_course.content_exports.find_by_id(json['id'])
|
||||
export.should_not be_nil
|
||||
export.workflow_state.should eql 'created'
|
||||
export.export_type.should eql 'common_cartridge'
|
||||
export.user_id.should eql t_teacher.id
|
||||
export.settings['selected_content']['context_modules'].should == {CC::CCHelper.create_key(mod) => "1"}
|
||||
export.job_progress.should be_queued
|
||||
|
||||
run_jobs
|
||||
|
||||
export.reload
|
||||
export.workflow_state.should eql 'exported'
|
||||
export.job_progress.should be_completed
|
||||
export.attachment.should_not be_nil
|
||||
|
||||
course
|
||||
cm = @course.content_migrations.new
|
||||
cm.attachment = export.attachment
|
||||
cm.migration_type = "canvas_cartridge_importer"
|
||||
cm.migration_settings[:import_immediately] = true
|
||||
cm.save!
|
||||
cm.queue_migration
|
||||
|
||||
run_jobs
|
||||
|
||||
@course.context_modules.find_by_migration_id(CC::CCHelper.create_key(mod)).should_not be_nil
|
||||
copied_page = @course.wiki.wiki_pages.find_by_migration_id(CC::CCHelper.create_key(page_to_copy))
|
||||
copied_page.should_not be_nil
|
||||
@course.wiki.wiki_pages.find_by_migration_id(CC::CCHelper.create_key(page_to_not_copy)).should be_nil
|
||||
|
||||
copied_att = @course.attachments.find_by_filename(att_to_copy.filename)
|
||||
copied_att.should_not be_nil
|
||||
copied_page.body.should == "<p><a href=\"/courses/#{@course.id}/files/#{copied_att.id}/preview\">hey look a link</a></p>"
|
||||
@course.attachments.find_by_filename(att_to_not_copy.filename).should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "#content_list" do
|
||||
it "should return a list of exportable content for a course directly" do
|
||||
course_with_teacher_logged_in(:active_all => true)
|
||||
@dt1 = @course.discussion_topics.create!(:message => "hi", :title => "discussion title")
|
||||
@cm = @course.context_modules.create!(:name => "some module")
|
||||
@att = Attachment.create!(:filename => 'first.txt', :uploaded_data => StringIO.new('ohai'), :folder => Folder.unfiled_folder(@course), :context => @course)
|
||||
@wiki = @course.wiki.wiki_pages.create!(:title => "wiki", :body => "ohai")
|
||||
|
||||
@quiz = @course.quizzes.create!(:title => "quizz")
|
||||
@quiz.did_edit
|
||||
@quiz.offer!
|
||||
@quiz.assignment.should_not be_nil
|
||||
|
||||
list_url = "/api/v1/courses/#{@course.id}/content_list"
|
||||
params = {:controller => 'content_exports_api', :format => 'json', :course_id => @course.id.to_param, :action => 'content_list'}
|
||||
json = api_call(:get, list_url, params)
|
||||
json.sort_by{|h| h['type']}.should == [
|
||||
{"type"=>"assignments", "property"=>"select[all_assignments]", "title"=>"Assignments", "count"=>1, "sub_items_url"=>"http://www.example.com/api/v1/courses/#{@course.id}/content_list?type=assignments"},
|
||||
{"type"=>"attachments", "property"=>"select[all_attachments]", "title"=>"Files", "count"=>1, "sub_items_url"=>"http://www.example.com/api/v1/courses/#{@course.id}/content_list?type=attachments"},
|
||||
{"type"=>"context_modules", "property"=>"select[all_context_modules]", "title"=>"Modules", "count"=>1, "sub_items_url"=>"http://www.example.com/api/v1/courses/#{@course.id}/content_list?type=context_modules"},
|
||||
{"type"=>"course_settings", "property"=>"select[all_course_settings]", "title"=>"Course Settings"},
|
||||
{"type"=>"discussion_topics", "property"=>"select[all_discussion_topics]", "title"=>"Discussion Topics", "count"=>1, "sub_items_url"=>"http://www.example.com/api/v1/courses/#{@course.id}/content_list?type=discussion_topics"},
|
||||
{"type"=>"quizzes", "property"=>"select[all_quizzes]", "title"=>"Quizzes", "count"=>1, "sub_items_url"=>"http://www.example.com/api/v1/courses/#{@course.id}/content_list?type=quizzes"},
|
||||
{"type"=>"syllabus_body", "property"=>"select[all_syllabus_body]", "title"=>"Syllabus Body"},
|
||||
{"type"=>"wiki_pages", "property"=>"select[all_wiki_pages]", "title"=>"Wiki Pages", "count"=>1, "sub_items_url"=>"http://www.example.com/api/v1/courses/#{@course.id}/content_list?type=wiki_pages"}
|
||||
]
|
||||
|
||||
json = api_call(:get, list_url + '?type=context_modules', params.merge({type: 'context_modules'}))
|
||||
json.length.should == 1
|
||||
json.first["type"].should == 'context_modules'
|
||||
json.first["title"].should == @cm.name
|
||||
json.first["id"].should == @cm.asset_string
|
||||
|
||||
json = api_call(:get, list_url + '?type=quizzes', params.merge({type: 'quizzes'}))
|
||||
json.first["type"].should == 'quizzes'
|
||||
json.first["title"].should == @quiz.title
|
||||
json.first["id"].should == @quiz.asset_string
|
||||
json.first['linked_resource']['id'].should == @quiz.assignment.asset_string
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue