Set up methods for html migration
README updates as well closes LF-380 flag=none Test plan - Canvas migrations should still work properly with links as before - Quizzes people are okay with the methods Change-Id: I0c1816391e625c86ba531cff9394ea78915cf8d8 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/322237 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Paul Gray <paul.gray@instructure.com> QA-Review: Paul Gray <paul.gray@instructure.com> Product-Review: Paul Gray <paul.gray@instructure.com>
This commit is contained in:
parent
ebebf1e9f4
commit
215f35e5e0
|
@ -20,7 +20,7 @@
|
|||
|
||||
class ContentMigration < ActiveRecord::Base
|
||||
include Workflow
|
||||
include TextHelper
|
||||
include HtmlTextHelper
|
||||
include Rails.application.routes.url_helpers
|
||||
include CanvasOutcomesHelper
|
||||
|
||||
|
@ -941,15 +941,15 @@ class ContentMigration < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def html_converter
|
||||
@html_converter ||= ImportedHtmlConverter.new(self, Importers::DbMigrationQueryService.new(context, self))
|
||||
@html_converter ||= CanvasImportedHtmlConverter.new(self)
|
||||
end
|
||||
|
||||
def convert_html(*args)
|
||||
html_converter.convert(*args)
|
||||
def convert_html(*args, **keyword_args)
|
||||
html_converter.convert(*args, **keyword_args)
|
||||
end
|
||||
|
||||
def convert_text(*args)
|
||||
html_converter.convert_text(*args)
|
||||
def convert_text(text)
|
||||
format_message(text || "")[0]
|
||||
end
|
||||
|
||||
delegate :resolve_content_links!, to: :html_converter
|
||||
|
|
|
@ -194,7 +194,7 @@ module Importers
|
|||
next unless hash[field].present?
|
||||
|
||||
hash[field] = migration.convert_html(
|
||||
hash[field], item_type, hash[:migration_id], field, { remove_outer_nodes_if_one_child: true }
|
||||
hash[field], item_type, hash[:migration_id], field, remove_outer_nodes_if_one_child: true
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -218,7 +218,7 @@ module Importers
|
|||
next unless answer[field].present?
|
||||
|
||||
answer[field] = migration.convert_html(
|
||||
answer[field], item_type, hash[:migration_id], key, { remove_outer_nodes_if_one_child: true }
|
||||
answer[field], item_type, hash[:migration_id], key, remove_outer_nodes_if_one_child: true
|
||||
)
|
||||
end
|
||||
if answer[:comments].present? && answer[:comments] == answer[:comments_html]
|
||||
|
|
|
@ -25,8 +25,8 @@ module Importers
|
|||
# Each function returns exactly one id (if available), and nil if an id
|
||||
# cannot be resolved
|
||||
class DbMigrationQueryService
|
||||
def initialize(context, migration)
|
||||
@context = context
|
||||
def initialize(migration)
|
||||
@context = migration.context
|
||||
@migration = migration
|
||||
end
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
# CanvasLinkMigration
|
||||
|
||||
CanvasLinkMigration handles importing links to other course content (ie Assignments, Quizzes, Content Pages etc, that are created by the RichContentService using Canvas APIs) exported from Canvas.
|
||||
CanvasLinkMigration processes html content from a Canvas export package and allows it to be retranslated into html with links to Canvas course content (ie Assignments, Quizzes, Content Pages etc).
|
||||
|
||||
## Usage
|
||||
|
||||
Create a CanvasLinkMigrator::ImportedHtmlConvert using an asset_id_mapping (Get the asset_id_mapping from Canvas API after a migration) and then pass in the html_string data to be imported.
|
||||
Create a CanvasLinkMigrator::ImportedHtmlConverter using an asset_id_map (Get the asset_map from the Canvas API after a migration) and then pass in the html_string data to be imported.
|
||||
|
||||
First create the ImportedHtmlConverter, and then pass the html string into the converter's convert_exported_html method to get the final html product
|
||||
|
||||
If any links cannot be resolved appropriately, a list will be returned to you with the resolved html
|
||||
```ruby
|
||||
converter = CanvasLinkMigrator::ImportedHtmlConvert.new(ResourceMapService.new(asset_id_mapping))
|
||||
converter.convert_and_replace(html_string)
|
||||
converter = CanvasLinkMigrator::ImportedHtmlConverter.new(resource_map: asset_id_map)
|
||||
new_html, missing_links = converter.convert_exported_html(html_string)
|
||||
```
|
||||
|
||||
Use convert and resolve_content_links! separately if desired
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
require "canvas_link_migrator/resource_map_service"
|
||||
require "canvas_link_migrator/imported_html_converter"
|
||||
require "canvas_link_migrator/link_parser"
|
||||
require "canvas_link_migrator/link_replacer"
|
||||
require "canvas_link_migrator/link_resolver"
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2023 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
require "active_support/core_ext/module"
|
||||
|
||||
module CanvasLinkMigrator
|
||||
class ImportedHtmlConverter
|
||||
attr_reader :link_parser, :link_resolver, :migration_id_converter
|
||||
|
||||
def initialize(resource_map: nil, migration_id_converter: nil)
|
||||
@migration_id_converter = migration_id_converter || ResourceMapService.new(resource_map)
|
||||
@link_parser = LinkParser.new(@migration_id_converter)
|
||||
@link_resolver = LinkResolver.new(@migration_id_converter)
|
||||
end
|
||||
|
||||
delegate :convert, to: :link_parser
|
||||
delegate :resolver_links!, to: :link_resolver
|
||||
|
||||
def convert_exported_html(input_html)
|
||||
new_html = link_parser.convert(input_html, "type", "lookup_id", "field")
|
||||
replace!(new_html)
|
||||
|
||||
# missing links comes back as a list for all types and fields, but if the user's only
|
||||
# sending one piece of html at a time, we only need the first set of missing links,
|
||||
# and only the actual missing links, not the look up information we set in this method
|
||||
bad_links = missing_links&.first&.dig(:missing_links)
|
||||
link_parser.reset!
|
||||
[new_html, bad_links]
|
||||
end
|
||||
|
||||
def replace!(placeholder_html)
|
||||
link_map = link_parser.unresolved_link_map
|
||||
return unless link_map.present?
|
||||
|
||||
link_resolver.resolve_links!(link_map)
|
||||
LinkReplacer.sub_placeholders!(placeholder_html, link_map.values.map(&:values).flatten)
|
||||
placeholder_html
|
||||
end
|
||||
|
||||
def missing_links
|
||||
link_parser.unresolved_link_map.each_with_object([]) do |(item_key, field_links), bad_links|
|
||||
field_links.each do |field, links|
|
||||
unresolved_links = links.select { |link| link[:replaced] && (link[:missing_url] || !link[:new_value]) }
|
||||
unresolved_links = unresolved_links.map { |link| link.slice(:link_type, :missing_url) }
|
||||
next unless unresolved_links.any?
|
||||
|
||||
bad_links << { object_lookup_id: item_key[:migration_id], object_type: item_key[:type], object_field: field, missing_links: unresolved_links }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -17,6 +17,9 @@
|
|||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
require "nokogiri"
|
||||
require "digest"
|
||||
|
||||
module CanvasLinkMigrator
|
||||
class LinkParser
|
||||
REFERENCE_KEYWORDS = %w[CANVAS_COURSE_REFERENCE CANVAS_OBJECT_REFERENCE WIKI_REFERENCE IMS_CC_FILEBASE IMS-CC-FILEBASE].freeze
|
||||
|
@ -76,7 +79,7 @@ module CanvasLinkMigrator
|
|||
"#{LINK_PLACEHOLDER}_#{Digest::MD5.hexdigest(old_value)}"
|
||||
end
|
||||
|
||||
def convert(html, item_type, mig_id, field, opts = {})
|
||||
def convert(html, item_type, mig_id, field, remove_outer_nodes_if_one_child: nil)
|
||||
mig_id = mig_id.to_s
|
||||
doc = Nokogiri::HTML5(html || "")
|
||||
|
||||
|
@ -99,7 +102,7 @@ module CanvasLinkMigrator
|
|||
node = doc.at_css("body")
|
||||
return "" unless node
|
||||
|
||||
if opts[:remove_outer_nodes_if_one_child]
|
||||
if remove_outer_nodes_if_one_child
|
||||
while node.children.size == 1 && node.child.child
|
||||
break unless CONTAINER_TYPES.member?(node.child.name) && node.child.attributes.blank?
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
module CanvasLinkMigrator
|
||||
class LinkReplacer
|
||||
# returns false if no substitutions were made
|
||||
def sub_placeholders!(html, links)
|
||||
def self.sub_placeholders!(html, links)
|
||||
subbed = false
|
||||
links.each do |link|
|
||||
new_value = link[:new_value] || link[:old_value]
|
||||
|
@ -32,7 +32,7 @@ module CanvasLinkMigrator
|
|||
subbed
|
||||
end
|
||||
|
||||
def recursively_sub_placeholders!(object, links)
|
||||
def self.recursively_sub_placeholders!(object, links)
|
||||
subbed = false
|
||||
case object
|
||||
when Hash
|
||||
|
|
|
@ -35,10 +35,36 @@ module CanvasLinkMigrator
|
|||
migration_data["resource_mapping"]
|
||||
end
|
||||
|
||||
### Overwritable methods
|
||||
def supports_embedded_images
|
||||
false
|
||||
end
|
||||
|
||||
def fix_relative_urls?
|
||||
true
|
||||
end
|
||||
|
||||
def process_domain_substitutions(url)
|
||||
url
|
||||
end
|
||||
|
||||
def context_hosts
|
||||
migration_data["destination_hosts"]
|
||||
end
|
||||
|
||||
def attachment_path_id_lookup; end
|
||||
|
||||
def attachment_path_id_lookup_lower; end
|
||||
|
||||
def root_folder_name
|
||||
migration_data["destination_root_folder"]
|
||||
end
|
||||
### End of Ovewritable methods
|
||||
|
||||
# Returns the path for the context, for a course, it should return something like
|
||||
# "courses/1"
|
||||
def context_path
|
||||
"/courses/#{migration_data["source_course"]}"
|
||||
"/courses/#{migration_data["destination_course"]}"
|
||||
end
|
||||
|
||||
# Looks up a wiki page slug for a migration id
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2012 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require "spec_helper"
|
||||
require "json"
|
||||
|
||||
describe CanvasLinkMigrator::ImportedHtmlConverter do
|
||||
# tests link_parser and link_resolver
|
||||
|
||||
describe ".convert" do
|
||||
before do
|
||||
@path = "/courses/2/"
|
||||
@converter = CanvasLinkMigrator::ImportedHtmlConverter.new(resource_map: JSON.parse(File.read("spec/fixtures/canvas_resource_map.json")))
|
||||
end
|
||||
|
||||
it "converts a wiki reference" do
|
||||
test_string = %(<a href="%24WIKI_REFERENCE%24/wiki/test-wiki-page?query=blah">Test Wiki Page</a>)
|
||||
html, bad_links = @converter.convert_exported_html(test_string)
|
||||
expect(html).to eq %(<a href="#{@path}pages/test-wiki-page?query=blah">Test Wiki Page</a>)
|
||||
expect(bad_links).to be_nil
|
||||
end
|
||||
|
||||
context "when course attachments exist" do
|
||||
subject { @converter.convert_exported_html(test_string) }
|
||||
|
||||
let(:migration_id) { "E" }
|
||||
|
||||
context "and a data-download-url attribute references an icon maker icon" do
|
||||
let(:test_string) do
|
||||
%(<img src="$CANVAS_COURSE_REFERENCE$/file_ref/#{migration_id}/download?download_frd=1" alt="" data-inst-icon-maker-icon="true" data-download-url="$CANVAS_COURSE_REFERENCE$/file_ref/#{migration_id}/download?download_frd=1&icon_maker_icon=1">)
|
||||
end
|
||||
|
||||
it "converts data-download-url for files without appending a context" do
|
||||
html, bad_links = subject
|
||||
expect(html).to eq(
|
||||
"<img src=\"#{@path}files/5/download?download_frd=1\" alt=\"\" data-inst-icon-maker-icon=\"true\" data-download-url=\"/files/5/download?download_frd=1&icon_maker_icon=1\">"
|
||||
)
|
||||
expect(bad_links).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
# it "finds an attachment by migration id" do
|
||||
# test_string = %{<p>This is an image: <br /><img src="%24CANVAS_OBJECT_REFERENCE%24/attachments/F" alt=":(" /></p>}
|
||||
# expect(@converter.convert_exported_html(test_string)).to eq([%{<p>This is an image: <br><img src="#{@path}files/6/preview" alt=":("></p>}, nil])
|
||||
# end
|
||||
|
||||
# it "finds an attachment by path" do
|
||||
# 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
|
||||
# expect(@converter.convert_exported_html(test_string)).to eq %{<p>This is an image: <br><img src="#{@path}file_contents/course%20files/test.png" alt=":("></p>}
|
||||
|
||||
# @migration.attachment_path_id_lookup = { "test.png" => att.migration_id }
|
||||
# expect(@converter.convert_exported_html(test_string)).to eq %{<p>This is an image: <br><img src="#{@path}files/#{att.id}/preview" alt=":("></p>}
|
||||
# end
|
||||
|
||||
# it "finds an attachment by a path with a space" do
|
||||
# att = make_test_att
|
||||
# @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" />)
|
||||
# expect(@converter.convert_exported_html(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" />)
|
||||
# expect(@converter.convert_exported_html(test_string)).to eq %(<img src="#{@path}files/#{att.id}/preview" alt="nope">)
|
||||
# end
|
||||
|
||||
# it "finds an attachment even if the link has an extraneous folder" do
|
||||
# att = make_test_att
|
||||
# @migration.attachment_path_id_lookup = { "subfolder/test.png" => att.migration_id }
|
||||
|
||||
# test_string = %(<img src="anotherfolder/subfolder/test.png" alt="nope" />)
|
||||
# expect(@converter.convert_exported_html(test_string)).to eq %(<img src="#{@path}files/#{att.id}/preview" alt="nope">)
|
||||
# end
|
||||
|
||||
# it "finds an attachment by path if capitalization is different" do
|
||||
# att = make_test_att
|
||||
# @migration.attachment_path_id_lookup = { "subfolder/withCapital/test.png" => "wrong!" }
|
||||
# @migration.attachment_path_id_lookup_lower = { "subfolder/withcapital/test.png" => att.migration_id }
|
||||
|
||||
# test_string = %(<img src="subfolder/WithCapital/TEST.png" alt="nope" />)
|
||||
# expect(@converter.convert_exported_html(test_string)).to eq %(<img src="#{@path}files/#{att.id}/preview" alt="nope">)
|
||||
# end
|
||||
|
||||
# it "finds an attachment with query params" do
|
||||
# att = make_test_att
|
||||
# @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" />)
|
||||
# expect(@converter.convert_exported_html(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" />)
|
||||
# expect(@converter.convert_exported_html(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" />)
|
||||
# expect(@converter.convert_exported_html(test_string)).to eq %(<img src="#{@path}files/#{att.id}/preview" alt="nope">)
|
||||
# end
|
||||
end
|
||||
|
||||
it "converts picture source srcsets" do
|
||||
test_string = %(<source srcset="$CANVAS_COURSE_REFERENCE$/img.src">)
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([%(<source srcset="/courses/2/img.src">), nil])
|
||||
end
|
||||
|
||||
it "converts a wiki reference without $ escaped" do
|
||||
test_string = %(<a href="$WIKI_REFERENCE$/wiki/test-wiki-page?query=blah">Test Wiki Page</a>)
|
||||
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([%(<a href="#{@path}pages/test-wiki-page?query=blah">Test Wiki Page</a>), nil])
|
||||
end
|
||||
|
||||
it "converts a wiki reference by migration id" do
|
||||
test_string = %(<a href="wiki_page_migration_id=A">Test Wiki Page</a>)
|
||||
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([%(<a href="#{@path}pages/slug-a">Test Wiki Page</a>), nil])
|
||||
end
|
||||
|
||||
it "converts a discussion reference by migration id" do
|
||||
test_string = %(<a href="discussion_topic_migration_id=G">Test topic</a>)
|
||||
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([%(<a href="#{@path}discussion_topics/7">Test topic</a>), nil])
|
||||
end
|
||||
|
||||
it "converts course section urls" do
|
||||
test_string = %(<a href="%24CANVAS_COURSE_REFERENCE%24/discussion_topics">discussions</a>)
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([%(<a href="#{@path}discussion_topics">discussions</a>), nil])
|
||||
end
|
||||
|
||||
it "leaves invalid and absolute urls alone" do
|
||||
test_string = %(<a href="stupid &^%$ url">Linkage</a><br><a href="http://www.example.com/poop">Linkage</a>)
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([%(<a href="stupid &^%$ url">Linkage</a><br><a href="http://www.example.com/poop">Linkage</a>), nil])
|
||||
end
|
||||
|
||||
it "leaves invalid mailto addresses alone" do
|
||||
test_string = %(<a href="mailto:.">Bad mailto</a><br><a href="mailto:test@example.com">Good mailto</a>)
|
||||
expect(@converter.convert_exported_html(test_string)).to eq(
|
||||
[
|
||||
%(<a href="mailto:.">Bad mailto</a><br><a href="mailto:test@example.com">Good mailto</a>),
|
||||
nil
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it "recognizes and relative-ize absolute links outside the course but in one of the course's domains" do
|
||||
test_string = %(<a href="https://apple.edu/courses/123">Mine</a><br><a href="https://kiwi.edu/courses/456">Vain</a><br><a href="http://other-canvas.example.com/">Other Instance</a>)
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([%(<a href="/courses/123">Mine</a><br><a href="/courses/456">Vain</a><br><a href="http://other-canvas.example.com/">Other Instance</a>), nil])
|
||||
end
|
||||
|
||||
it "prepends course files for unrecognized relative urls" do
|
||||
test_string = %(<a href="/relative/path/to/file">Linkage</a>)
|
||||
html, bad_links = @converter.convert_exported_html(test_string)
|
||||
expect(html).to eq %(<a href="#{@path}file_contents/course%20files/relative/path/to/file">Linkage</a>)
|
||||
expect(bad_links.length).to eq 1
|
||||
expect(bad_links[0]).to include({ link_type: :file, missing_url: "/courses/2/file_contents/course%20files/relative/path/to/file" })
|
||||
|
||||
test_string = %(<a href="relative/path/to/file">Linkage</a>)
|
||||
html, bad_links = @converter.convert_exported_html(test_string)
|
||||
expect(html).to eq %(<a href="#{@path}file_contents/course%20files/relative/path/to/file">Linkage</a>)
|
||||
expect(bad_links.length).to eq 1
|
||||
expect(bad_links[0]).to include({ link_type: :file, missing_url: "/courses/2/file_contents/course%20files/relative/path/to/file" })
|
||||
|
||||
test_string = %(<a href="relative/path/to/file%20with%20space.html">Linkage</a>)
|
||||
html, bad_links = @converter.convert_exported_html(test_string)
|
||||
expect(html).to eq %(<a href="#{@path}file_contents/course%20files/relative/path/to/file%20with%20space.html">Linkage</a>)
|
||||
expect(bad_links.length).to eq 1
|
||||
expect(bad_links[0]).to include({ link_type: :file, missing_url: "/courses/2/file_contents/course%20files/relative/path/to/file%20with%20space.html" })
|
||||
end
|
||||
|
||||
it "preserves media comment links" do
|
||||
test_string = <<~HTML.strip
|
||||
<p>
|
||||
with media object url: <a id="media_comment_m-stuff" class="instructure_inline_media_comment video_comment" href="/media_objects/m-stuff">this is a media comment</a>
|
||||
with file content url: <a id="media_comment_0_bq09qam2" class="instructure_inline_media_comment video_comment" href="/courses/2/file_contents/course%20files/media_objects/0_bq09qam2">this is a media comment</a>
|
||||
</p>
|
||||
HTML
|
||||
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([test_string, nil])
|
||||
end
|
||||
|
||||
it "handles and repair half broken media links" do
|
||||
test_string = %(<p><a href="/courses/2/file_contents/%24IMS_CC_FILEBASE%24/#" class="instructure_inline_media_comment video_comment" id="media_comment_m-stuff">this is a media comment</a><br><br></p>)
|
||||
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([%(<p><a href="/media_objects/m-stuff" class="instructure_inline_media_comment video_comment" id="media_comment_m-stuff">this is a media comment</a><br><br></p>), nil])
|
||||
end
|
||||
|
||||
it "preserves new RCE media iframes" do
|
||||
test_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" src="/media_objects_iframe/m-stuff?type=video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-stuff"></iframe>)
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([test_string, nil])
|
||||
end
|
||||
|
||||
it "handles and repair half broken new RCE media iframes" do
|
||||
test_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" src="%24IMS_CC_FILEBASE%24/#" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-abcde"></iframe>)
|
||||
repaired_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" src="/media_objects_iframe/m-abcde?type=video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-abcde"></iframe>)
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([repaired_string, nil])
|
||||
end
|
||||
|
||||
it "converts source tags to RCE media iframes" do
|
||||
test_string = %(<video style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-stuff"><source src="/media_objects_iframe/m-stuff?type=video" data-media-id="m-stuff" data-media-type="video"></video>)
|
||||
converted_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-stuff" src="/media_objects_iframe/m-stuff?type=video"></iframe>)
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([converted_string, nil])
|
||||
|
||||
test_string = %(<audio style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="audio" data-media-id="m-stuff"><source src="/media_objects_iframe/m-stuff?type=audio" data-media-id="m-stuff" data-media-type="audio"></audio>)
|
||||
converted_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="audio" data-media-id="m-stuff" src="/media_objects_iframe/m-stuff?type=audio"></iframe>)
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([converted_string, nil])
|
||||
end
|
||||
|
||||
it "converts source tags to RCE media attachment iframes" do
|
||||
test_string = %(<video style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-stuff"><source src="$CANVAS_OBJECT_REFERENCE$/media_attachments_iframe/E?type=video" data-media-id="m-stuff" data-media-type="video"></video>)
|
||||
converted_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-stuff" src="/media_attachments_iframe/5?type=video"></iframe>)
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([converted_string, nil])
|
||||
|
||||
test_string = %(<audio style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="audio" data-media-id="m-stuff"><source src="$CANVAS_OBJECT_REFERENCE$/media_attachments_iframe/E?type=audio" data-media-id="m-stuff" data-media-type="audio"></video>)
|
||||
converted_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="audio" data-media-id="m-stuff" src="/media_attachments_iframe/5?type=audio"></iframe>)
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([converted_string, nil])
|
||||
end
|
||||
|
||||
it "converts source tags to RCE media attachment iframes when link is untranslated" do
|
||||
test_string = %(<video style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-stuff"><source src="/media_attachments_iframe/5?type=video" data-media-id="m-stuff" data-media-type="video"></video>)
|
||||
converted_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-stuff" src="/media_attachments_iframe/5?type=video"></iframe>)
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([converted_string, nil])
|
||||
|
||||
test_string = %(<audio style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="audio" data-media-id="m-stuff"><source src="/media_attachments_iframe/5?type=audio" data-media-id="m-stuff" data-media-type="audio"></video>)
|
||||
converted_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="audio" data-media-id="m-stuff" src="/media_attachments_iframe/5?type=audio"></iframe>)
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([converted_string, nil])
|
||||
end
|
||||
|
||||
it "leaves source tags without data-media-id alone" do
|
||||
test_string = %(<video style="width: 400px; height: 225px; display: inline-block;" title="this is a non-canvas video" allowfullscreen="allowfullscreen" allow="fullscreen"><source src="http://www.example.com/video.mov"></video>)
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([test_string, nil])
|
||||
end
|
||||
|
||||
it "only converts url params" do
|
||||
test_string = <<~HTML
|
||||
<object>
|
||||
<param name="controls" value="CONSOLE" />
|
||||
<param name="controller" value="true" />
|
||||
<param name="autostart" value="false" />
|
||||
<param name="loop" value="false" />
|
||||
<param name="src" value="%24IMS_CC_FILEBASE%24/test.mp3" />
|
||||
<EMBED name="tag" src="%24IMS_CC_FILEBASE%24/test.mp3" loop="false" autostart="false" controller="true" controls="CONSOLE" >
|
||||
</EMBED>
|
||||
</object>
|
||||
HTML
|
||||
|
||||
expect(@converter.convert_exported_html(test_string)[0]).to match(<<~HTML.strip)
|
||||
<object>
|
||||
<param name="controls" value="CONSOLE">
|
||||
<param name="controller" value="true">
|
||||
<param name="autostart" value="false">
|
||||
<param name="loop" value="false">
|
||||
<param name="src" value="/courses/2/file_contents/course%20files/test.mp3">
|
||||
<embed name="tag" src="/courses/2/file_contents/course%20files/test.mp3" loop="false" autostart="false" controller="true" controls="CONSOLE">
|
||||
|
||||
</object>
|
||||
HTML
|
||||
end
|
||||
|
||||
it "leaves an anchor tag alone" do
|
||||
test_string = '<p><a href="#anchor_ref">ref</a></p>'
|
||||
expect(@converter.convert_exported_html(test_string)).to eq([test_string, nil])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -22,45 +22,45 @@ require "spec_helper"
|
|||
require "json"
|
||||
|
||||
describe CanvasLinkMigrator::LinkResolver do
|
||||
def course_based_converter(assets = JSON.parse(File.read("spec/fixtures/canvas_resource_map.json")))
|
||||
def resolver(assets = JSON.parse(File.read("spec/fixtures/canvas_resource_map.json")))
|
||||
CanvasLinkMigrator::LinkResolver.new(CanvasLinkMigrator::ResourceMapService.new(assets))
|
||||
end
|
||||
|
||||
describe "resolve_link!" do
|
||||
it "converts wiki_pages links" do
|
||||
link = { link_type: :wiki_page, migration_id: "A", query: "?foo=bar" }
|
||||
course_based_converter.resolve_link!(link)
|
||||
expect(link[:new_value]).to eq("/courses/1/pages/slug-a?foo=bar")
|
||||
resolver.resolve_link!(link)
|
||||
expect(link[:new_value]).to eq("/courses/2/pages/slug-a?foo=bar")
|
||||
end
|
||||
|
||||
it "converts module_item links" do
|
||||
link = { link_type: :module_item, migration_id: "C", query: "?foo=bar" }
|
||||
course_based_converter.resolve_link!(link)
|
||||
expect(link[:new_value]).to eq("/courses/1/modules/items/3?foo=bar")
|
||||
resolver.resolve_link!(link)
|
||||
expect(link[:new_value]).to eq("/courses/2/modules/items/3?foo=bar")
|
||||
end
|
||||
|
||||
it "converts file_ref urls" do
|
||||
link = { link_type: :file_ref, migration_id: "F" }
|
||||
course_based_converter.resolve_link!(link)
|
||||
expect(link[:new_value]).to eq("/courses/1/files/6/preview")
|
||||
resolver.resolve_link!(link)
|
||||
expect(link[:new_value]).to eq("/courses/2/files/6/preview")
|
||||
end
|
||||
|
||||
it "converts attachment urls" do
|
||||
link = { link_type: :object, type: "attachments", migration_id: "E", query: "?foo=bar" }
|
||||
course_based_converter.resolve_link!(link)
|
||||
expect(link[:new_value]).to eq("/courses/1/files/5/preview")
|
||||
resolver.resolve_link!(link)
|
||||
expect(link[:new_value]).to eq("/courses/2/files/5/preview")
|
||||
end
|
||||
|
||||
it "converts media_attachments_iframe urls" do
|
||||
link = { link_type: :object, type: "media_attachments_iframe", migration_id: "F", query: "?foo=bar" }
|
||||
course_based_converter.resolve_link!(link)
|
||||
resolver.resolve_link!(link)
|
||||
expect(link[:new_value]).to eq("/media_attachments_iframe/6?foo=bar")
|
||||
end
|
||||
|
||||
it "converts discussion_topic links" do
|
||||
link = { link_type: :discussion_topic, migration_id: "G", query: "?foo=bar" }
|
||||
course_based_converter.resolve_link!(link)
|
||||
expect(link[:new_value]).to eq("/courses/1/discussion_topics/7?foo=bar")
|
||||
resolver.resolve_link!(link)
|
||||
expect(link[:new_value]).to eq("/courses/2/discussion_topics/7?foo=bar")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,67 +1,73 @@
|
|||
{
|
||||
"contains_migration_ids": true,
|
||||
"destination_course": "2",
|
||||
"destination_hosts": [
|
||||
"apple.edu",
|
||||
"kiwi.edu"
|
||||
],
|
||||
"destination_root_folder": "course files/",
|
||||
"resource_mapping": {
|
||||
"discussion_topics": {
|
||||
"G": {
|
||||
"destination": {
|
||||
"id": "7"
|
||||
},
|
||||
"source": {
|
||||
"id": "6"
|
||||
}
|
||||
}
|
||||
"contains_migration_ids": true,
|
||||
"destination_course": "2",
|
||||
"destination_hosts": [
|
||||
"apple.edu",
|
||||
"kiwi.edu"
|
||||
],
|
||||
"destination_root_folder": "course files/",
|
||||
"resource_mapping": {
|
||||
"discussion_topics": {
|
||||
"G": {
|
||||
"destination": {
|
||||
"id": "7"
|
||||
},
|
||||
"files": {
|
||||
"E": {
|
||||
"destination": {
|
||||
"id": "5",
|
||||
"media_entry_id": "m-stuff"
|
||||
},
|
||||
"source": {
|
||||
"id": "3",
|
||||
"media_entry_id": "m-stuff"
|
||||
}
|
||||
},
|
||||
"F": {
|
||||
"destination": {
|
||||
"id": "6",
|
||||
"media_entry_id": "m-stuff"
|
||||
},
|
||||
"source": {
|
||||
"id": "4",
|
||||
"media_entry_id": "m-stuff"
|
||||
}
|
||||
}
|
||||
},
|
||||
"module_items": {
|
||||
"C": {
|
||||
"destination": {
|
||||
"id": "3"
|
||||
},
|
||||
"source": {
|
||||
"id": "2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"wiki_pages": {
|
||||
"A": {
|
||||
"destination": {
|
||||
"id": "2",
|
||||
"url": "slug-a"
|
||||
},
|
||||
"source": {
|
||||
"id": "1",
|
||||
"url": "slug-a"
|
||||
}
|
||||
}
|
||||
"source": {
|
||||
"id": "6"
|
||||
}
|
||||
}
|
||||
},
|
||||
"source_course": "1",
|
||||
"source_host": "pineapple.edu"
|
||||
"files": {
|
||||
"E": {
|
||||
"destination": {
|
||||
"id": "5",
|
||||
"media_entry_id": "m-stuff"
|
||||
},
|
||||
"source": {
|
||||
"id": "3",
|
||||
"media_entry_id": "m-stuff"
|
||||
}
|
||||
},
|
||||
"F": {
|
||||
"destination": {
|
||||
"id": "6",
|
||||
"media_entry_id": "m-stuff"
|
||||
},
|
||||
"source": {
|
||||
"id": "4",
|
||||
"media_entry_id": "m-stuff"
|
||||
}
|
||||
}
|
||||
},
|
||||
"module_items": {
|
||||
"C": {
|
||||
"destination": {
|
||||
"id": "3"
|
||||
},
|
||||
"source": {
|
||||
"id": "2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"wiki_pages": {
|
||||
"A": {
|
||||
"destination": {
|
||||
"id": "2",
|
||||
"url": "slug-a"
|
||||
},
|
||||
"source": {
|
||||
"id": "1",
|
||||
"url": "slug-a"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"file_tree": {
|
||||
"folder 1": {
|
||||
"id": "1",
|
||||
"sub_folders": {}
|
||||
}
|
||||
},
|
||||
"source_course": "1",
|
||||
"source_host": "pineapple.edu"
|
||||
}
|
|
@ -20,9 +20,8 @@
|
|||
|
||||
require "nokogiri"
|
||||
|
||||
class ImportedHtmlConverter
|
||||
include TextHelper
|
||||
include HtmlTextHelper
|
||||
class CanvasImportedHtmlConverter < CanvasLinkMigrator::ImportedHtmlConverter
|
||||
include CanvasLinkMigrator
|
||||
|
||||
LINK_TYPE_TO_CLASS = {
|
||||
announcement: Announcement,
|
||||
|
@ -35,37 +34,9 @@ class ImportedHtmlConverter
|
|||
wiki_page: WikiPage
|
||||
}.freeze
|
||||
|
||||
attr_reader :link_parser, :link_resolver, :link_replacer
|
||||
|
||||
delegate :convert, to: :link_parser
|
||||
|
||||
def initialize(migration, migration_id_converter)
|
||||
def initialize(migration)
|
||||
@migration = migration
|
||||
@migration_id_converter = migration_id_converter
|
||||
@link_parser = CanvasLinkMigrator::LinkParser.new(migration_id_converter)
|
||||
@link_resolver = CanvasLinkMigrator::LinkResolver.new(migration_id_converter)
|
||||
@link_replacer = CanvasLinkMigrator::LinkReplacer.new
|
||||
end
|
||||
|
||||
def convert_text(text)
|
||||
format_message(text || "")[0]
|
||||
end
|
||||
|
||||
def resolve_content_links!
|
||||
link_map = @link_parser.unresolved_link_map
|
||||
return unless link_map.present?
|
||||
|
||||
@link_resolver.resolve_links!(link_map)
|
||||
replace_placeholders!(link_map)
|
||||
@link_parser.reset!
|
||||
end
|
||||
|
||||
def self.relative_url?(url)
|
||||
URI.parse(url).relative? && !url.to_s.start_with?("//")
|
||||
rescue URI::Error
|
||||
# leave the url as it was
|
||||
Rails.logger.warn "attempting to translate invalid url: #{url}"
|
||||
false
|
||||
super(migration_id_converter: Importers::DbMigrationQueryService.new(migration))
|
||||
end
|
||||
|
||||
def context
|
||||
|
@ -76,6 +47,15 @@ class ImportedHtmlConverter
|
|||
@migration_id_converter.context_path
|
||||
end
|
||||
|
||||
def resolve_content_links!
|
||||
link_map = link_parser.unresolved_link_map
|
||||
return unless link_map.present?
|
||||
|
||||
link_resolver.resolve_links!(link_map)
|
||||
replace_placeholders!(link_map)
|
||||
link_parser.reset!
|
||||
end
|
||||
|
||||
def replace_placeholders!(link_map)
|
||||
load_questions!(link_map)
|
||||
|
||||
|
@ -150,7 +130,7 @@ class ImportedHtmlConverter
|
|||
case item_key[:type]
|
||||
when :syllabus
|
||||
syllabus = context.syllabus_body
|
||||
if link_replacer.sub_placeholders!(syllabus, field_links.values.flatten)
|
||||
if LinkReplacer.sub_placeholders!(syllabus, field_links.values.flatten)
|
||||
context.class.where(id: context.id).update_all(syllabus_body: syllabus)
|
||||
end
|
||||
when :assessment_question
|
||||
|
@ -162,7 +142,7 @@ class ImportedHtmlConverter
|
|||
item_updates = {}
|
||||
field_links.each do |field, links|
|
||||
html = item.read_attribute(field)
|
||||
if link_replacer.sub_placeholders!(html, links)
|
||||
if LinkReplacer.sub_placeholders!(html, links)
|
||||
item_updates[field] = html
|
||||
end
|
||||
end
|
||||
|
@ -210,7 +190,7 @@ class ImportedHtmlConverter
|
|||
# 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|
|
||||
if link_replacer.recursively_sub_placeholders!(qq["question_data"], links)
|
||||
if LinkReplacer.recursively_sub_placeholders!(qq["question_data"], links)
|
||||
Quizzes::QuizQuestion.where(id: qq.id).update_all(question_data: qq["question_data"])
|
||||
quiz_ids << qq.quiz_id
|
||||
end
|
||||
|
@ -218,7 +198,7 @@ class ImportedHtmlConverter
|
|||
|
||||
if quiz_ids.any?
|
||||
Quizzes::Quiz.where(id: quiz_ids.uniq).where.not(quiz_data: nil).find_each do |quiz|
|
||||
if link_replacer.recursively_sub_placeholders!(quiz["quiz_data"], links)
|
||||
if LinkReplacer.recursively_sub_placeholders!(quiz["quiz_data"], links)
|
||||
Quizzes::Quiz.where(id: quiz.id).update_all(quiz_data: quiz["quiz_data"])
|
||||
end
|
||||
end
|
||||
|
@ -233,18 +213,18 @@ class ImportedHtmlConverter
|
|||
link[:new_value] = aq.translate_file_link(link[:new_value])
|
||||
end
|
||||
|
||||
if link_replacer.recursively_sub_placeholders!(aq["question_data"], links)
|
||||
if LinkReplacer.recursively_sub_placeholders!(aq["question_data"], links)
|
||||
AssessmentQuestion.where(id: aq.id).update_all(question_data: aq["question_data"])
|
||||
end
|
||||
end
|
||||
|
||||
def process_quiz_question!(qq, links)
|
||||
if link_replacer.recursively_sub_placeholders!(qq["question_data"], links)
|
||||
if LinkReplacer.recursively_sub_placeholders!(qq["question_data"], links)
|
||||
Quizzes::QuizQuestion.where(id: qq.id).update_all(question_data: qq["question_data"])
|
||||
end
|
||||
|
||||
quiz = Quizzes::Quiz.where(id: qq.quiz_id).where.not(quiz_data: nil).first
|
||||
if quiz && link_replacer.recursively_sub_placeholders!(quiz["quiz_data"], links)
|
||||
if quiz && LinkReplacer.recursively_sub_placeholders!(quiz["quiz_data"], links)
|
||||
Quizzes::Quiz.where(id: quiz.id).update_all(quiz_data: quiz["quiz_data"])
|
||||
end
|
||||
end
|
|
@ -191,7 +191,7 @@ class CourseLinkValidator
|
|||
end
|
||||
end
|
||||
|
||||
# pretty much copied from ImportedHtmlConverter
|
||||
# pretty much copied from CanvasImportedHtmlConverter
|
||||
def find_invalid_links(html)
|
||||
links = []
|
||||
doc = Nokogiri::HTML5(html || "")
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
describe ImportedHtmlConverter do
|
||||
describe CanvasImportedHtmlConverter do
|
||||
# tests link_parser and link_resolver
|
||||
before :once do
|
||||
course_factory
|
||||
|
@ -27,26 +27,20 @@ describe ImportedHtmlConverter do
|
|||
@converter = @migration.html_converter
|
||||
end
|
||||
|
||||
context ".convert" do
|
||||
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
|
||||
describe ".convert" do
|
||||
def convert_exported_html(*args)
|
||||
@converter.convert_exported_html(*args)[0]
|
||||
end
|
||||
|
||||
it "converts a wiki reference" do
|
||||
test_string = %(<a href="%24WIKI_REFERENCE%24/wiki/test-wiki-page?query=blah">Test Wiki Page</a>)
|
||||
@course.wiki_pages.create!(title: "Test Wiki Page", body: "stuff")
|
||||
|
||||
expect(convert_and_replace(test_string)).to eq %(<a href="#{@path}pages/test-wiki-page?query=blah">Test Wiki Page</a>)
|
||||
expect(convert_exported_html(test_string)).to eq %(<a href="#{@path}pages/test-wiki-page?query=blah">Test Wiki Page</a>)
|
||||
end
|
||||
|
||||
context "when course attachments exist" do
|
||||
subject { convert_and_replace(test_string) }
|
||||
subject { convert_exported_html(test_string) }
|
||||
|
||||
let_once(:attachment) { attachment_model(context: course, migration_id:) }
|
||||
let(:course) { @course }
|
||||
|
@ -67,14 +61,14 @@ describe ImportedHtmlConverter do
|
|||
|
||||
it "converts picture source srcsets" do
|
||||
test_string = %(<source srcset="$CANVAS_COURSE_REFERENCE$/img.src">)
|
||||
expect(convert_and_replace(test_string)).to eq %(<source srcset="/courses/#{@course.id}/img.src">)
|
||||
expect(convert_exported_html(test_string)).to eq %(<source srcset="/courses/#{@course.id}/img.src">)
|
||||
end
|
||||
|
||||
it "converts a wiki reference without $ escaped" do
|
||||
test_string = %(<a href="$WIKI_REFERENCE$/wiki/test-wiki-page?query=blah">Test Wiki Page</a>)
|
||||
@course.wiki_pages.create!(title: "Test Wiki Page", body: "stuff")
|
||||
|
||||
expect(convert_and_replace(test_string)).to eq %(<a href="#{@path}pages/test-wiki-page?query=blah">Test Wiki Page</a>)
|
||||
expect(convert_exported_html(test_string)).to eq %(<a href="#{@path}pages/test-wiki-page?query=blah">Test Wiki Page</a>)
|
||||
end
|
||||
|
||||
it "converts a wiki reference by migration id" do
|
||||
|
@ -83,7 +77,7 @@ describe ImportedHtmlConverter do
|
|||
wiki.migration_id = "123456677788"
|
||||
wiki.save!
|
||||
|
||||
expect(convert_and_replace(test_string)).to eq %(<a href="#{@path}pages/test-wiki-page">Test Wiki Page</a>)
|
||||
expect(convert_exported_html(test_string)).to eq %(<a href="#{@path}pages/test-wiki-page">Test Wiki Page</a>)
|
||||
end
|
||||
|
||||
it "converts a discussion reference by migration id" do
|
||||
|
@ -92,7 +86,7 @@ describe ImportedHtmlConverter do
|
|||
topic.migration_id = "123456677788"
|
||||
topic.save!
|
||||
|
||||
expect(convert_and_replace(test_string)).to eq %(<a href="#{@path}discussion_topics/#{topic.id}">Test topic</a>)
|
||||
expect(convert_exported_html(test_string)).to eq %(<a href="#{@path}discussion_topics/#{topic.id}">Test topic</a>)
|
||||
end
|
||||
|
||||
def make_test_att
|
||||
|
@ -106,7 +100,7 @@ describe ImportedHtmlConverter do
|
|||
att = make_test_att
|
||||
|
||||
test_string = %{<p>This is an image: <br /><img src="%24CANVAS_OBJECT_REFERENCE%24/attachments/1768525836051" 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>}
|
||||
expect(convert_exported_html(test_string)).to eq %{<p>This is an image: <br><img src="#{@path}files/#{att.id}/preview" alt=":("></p>}
|
||||
end
|
||||
|
||||
it "finds an attachment by path" do
|
||||
|
@ -115,10 +109,10 @@ describe ImportedHtmlConverter do
|
|||
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
|
||||
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>}
|
||||
expect(convert_exported_html(test_string)).to eq %{<p>This is an image: <br><img src="#{@path}file_contents/course%20files/test.png" alt=":("></p>}
|
||||
|
||||
@migration.attachment_path_id_lookup = { "test.png" => att.migration_id }
|
||||
expect(convert_and_replace(test_string)).to eq %{<p>This is an image: <br><img src="#{@path}files/#{att.id}/preview" alt=":("></p>}
|
||||
expect(convert_exported_html(test_string)).to eq %{<p>This is an image: <br><img src="#{@path}files/#{att.id}/preview" alt=":("></p>}
|
||||
end
|
||||
|
||||
it "finds an attachment by a path with a space" do
|
||||
|
@ -126,10 +120,10 @@ describe ImportedHtmlConverter do
|
|||
@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" />)
|
||||
expect(convert_and_replace(test_string)).to eq %(<img src="#{@path}files/#{att.id}/preview" alt="nope">)
|
||||
expect(convert_exported_html(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" />)
|
||||
expect(convert_and_replace(test_string)).to eq %(<img src="#{@path}files/#{att.id}/preview" alt="nope">)
|
||||
expect(convert_exported_html(test_string)).to eq %(<img src="#{@path}files/#{att.id}/preview" alt="nope">)
|
||||
end
|
||||
|
||||
it "finds an attachment even if the link has an extraneous folder" do
|
||||
|
@ -137,7 +131,7 @@ describe ImportedHtmlConverter do
|
|||
@migration.attachment_path_id_lookup = { "subfolder/test.png" => att.migration_id }
|
||||
|
||||
test_string = %(<img src="anotherfolder/subfolder/test.png" alt="nope" />)
|
||||
expect(convert_and_replace(test_string)).to eq %(<img src="#{@path}files/#{att.id}/preview" alt="nope">)
|
||||
expect(convert_exported_html(test_string)).to eq %(<img src="#{@path}files/#{att.id}/preview" alt="nope">)
|
||||
end
|
||||
|
||||
it "finds an attachment by path if capitalization is different" do
|
||||
|
@ -146,7 +140,7 @@ describe ImportedHtmlConverter do
|
|||
@migration.attachment_path_id_lookup_lower = { "subfolder/withcapital/test.png" => att.migration_id }
|
||||
|
||||
test_string = %(<img src="subfolder/WithCapital/TEST.png" alt="nope" />)
|
||||
expect(convert_and_replace(test_string)).to eq %(<img src="#{@path}files/#{att.id}/preview" alt="nope">)
|
||||
expect(convert_exported_html(test_string)).to eq %(<img src="#{@path}files/#{att.id}/preview" alt="nope">)
|
||||
end
|
||||
|
||||
it "finds an attachment with query params" do
|
||||
|
@ -154,28 +148,28 @@ describe ImportedHtmlConverter do
|
|||
@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" />)
|
||||
expect(convert_and_replace(test_string)).to eq %(<img src="#{@path}files/#{att.id}/customaction?customparam=1" alt="nope">)
|
||||
expect(convert_exported_html(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" />)
|
||||
expect(convert_and_replace(test_string)).to eq %(<img src="#{@path}files/#{att.id}/preview?customparam2=3" alt="nope">)
|
||||
expect(convert_exported_html(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" />)
|
||||
expect(convert_and_replace(test_string)).to eq %(<img src="#{@path}files/#{att.id}/preview" alt="nope">)
|
||||
expect(convert_exported_html(test_string)).to eq %(<img src="#{@path}files/#{att.id}/preview" alt="nope">)
|
||||
end
|
||||
|
||||
it "converts course section urls" do
|
||||
test_string = %(<a href="%24CANVAS_COURSE_REFERENCE%24/discussion_topics">discussions</a>)
|
||||
expect(convert_and_replace(test_string)).to eq %(<a href="#{@path}discussion_topics">discussions</a>)
|
||||
expect(convert_exported_html(test_string)).to eq %(<a href="#{@path}discussion_topics">discussions</a>)
|
||||
end
|
||||
|
||||
it "leaves invalid and absolute urls alone" do
|
||||
test_string = %(<a href="stupid &^%$ url">Linkage</a><br><a href="http://www.example.com/poop">Linkage</a>)
|
||||
expect(convert_and_replace(test_string)).to eq %(<a href="stupid &^%$ url">Linkage</a><br><a href="http://www.example.com/poop">Linkage</a>)
|
||||
expect(convert_exported_html(test_string)).to eq %(<a href="stupid &^%$ url">Linkage</a><br><a href="http://www.example.com/poop">Linkage</a>)
|
||||
end
|
||||
|
||||
it "leaves invalid mailto addresses alone" do
|
||||
test_string = %(<a href="mailto:.">Bad mailto</a><br><a href="mailto:test@example.com">Good mailto</a>)
|
||||
expect(convert_and_replace(test_string)).to eq(
|
||||
expect(convert_exported_html(test_string)).to eq(
|
||||
%(<a href="mailto:.">Bad mailto</a><br><a href="mailto:test@example.com">Good mailto</a>)
|
||||
)
|
||||
end
|
||||
|
@ -183,16 +177,16 @@ describe ImportedHtmlConverter do
|
|||
it "recognizes and relative-ize absolute links outside the course but in one of the course's domains" do
|
||||
allow(HostUrl).to receive(:context_hosts).with(@course.root_account).and_return(["my-canvas.example.com", "vanity.my-canvas.edu"])
|
||||
test_string = %(<a href="https://my-canvas.example.com/courses/123">Mine</a><br><a href="https://vanity.my-canvas.edu/courses/456">Vain</a><br><a href="http://other-canvas.example.com/">Other Instance</a>)
|
||||
expect(convert_and_replace(test_string)).to eq %(<a href="/courses/123">Mine</a><br><a href="/courses/456">Vain</a><br><a href="http://other-canvas.example.com/">Other Instance</a>)
|
||||
expect(convert_exported_html(test_string)).to eq %(<a href="/courses/123">Mine</a><br><a href="/courses/456">Vain</a><br><a href="http://other-canvas.example.com/">Other Instance</a>)
|
||||
end
|
||||
|
||||
it "prepends course files for unrecognized relative urls" do
|
||||
test_string = %(<a href="/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>)
|
||||
expect(convert_exported_html(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>)
|
||||
expect(convert_and_replace(test_string)).to eq %(<a href="#{@path}file_contents/course%20files/relative/path/to/file">Linkage</a>)
|
||||
expect(convert_exported_html(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>)
|
||||
expect(convert_and_replace(test_string)).to eq %(<a href="#{@path}file_contents/course%20files/relative/path/to/file%20with%20space.html">Linkage</a>)
|
||||
expect(convert_exported_html(test_string)).to eq %(<a href="#{@path}file_contents/course%20files/relative/path/to/file%20with%20space.html">Linkage</a>)
|
||||
end
|
||||
|
||||
it "preserves media comment links" do
|
||||
|
@ -203,34 +197,34 @@ describe ImportedHtmlConverter do
|
|||
</p>
|
||||
HTML
|
||||
|
||||
expect(convert_and_replace(test_string)).to eq test_string
|
||||
expect(convert_exported_html(test_string)).to eq test_string
|
||||
end
|
||||
|
||||
it "handles 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>)
|
||||
|
||||
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>)
|
||||
expect(convert_exported_html(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
|
||||
|
||||
it "preserves new RCE media iframes" do
|
||||
test_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" src="/media_objects_iframe/0_l4l5n0wt?type=video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="0_l4l5n0wt"></iframe>)
|
||||
expect(convert_and_replace(test_string)).to eq test_string
|
||||
expect(convert_exported_html(test_string)).to eq test_string
|
||||
end
|
||||
|
||||
it "handles and repair half broken new RCE media iframes" do
|
||||
test_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" src="%24IMS_CC_FILEBASE%24/#" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-abcde"></iframe>)
|
||||
repaired_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" src="/media_objects_iframe/m-abcde?type=video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-abcde"></iframe>)
|
||||
expect(convert_and_replace(test_string)).to eq repaired_string
|
||||
expect(convert_exported_html(test_string)).to eq repaired_string
|
||||
end
|
||||
|
||||
it "converts source tags to RCE media iframes" do
|
||||
test_string = %(<video style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="0_l4l5n0wt"><source src="/media_objects_iframe/0_l4l5n0wt?type=video" data-media-id="0_l4l5n0wt" data-media-type="video"></video>)
|
||||
converted_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="0_l4l5n0wt" src="/media_objects_iframe/0_l4l5n0wt?type=video"></iframe>)
|
||||
expect(convert_and_replace(test_string)).to eq converted_string
|
||||
expect(convert_exported_html(test_string)).to eq converted_string
|
||||
|
||||
test_string = %(<audio style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="audio" data-media-id="0_l4l5n0wt"><source src="/media_objects_iframe/0_l4l5n0wt?type=audio" data-media-id="0_l4l5n0wt" data-media-type="audio"></audio>)
|
||||
converted_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="audio" data-media-id="0_l4l5n0wt" src="/media_objects_iframe/0_l4l5n0wt?type=audio"></iframe>)
|
||||
expect(convert_and_replace(test_string)).to eq converted_string
|
||||
expect(convert_exported_html(test_string)).to eq converted_string
|
||||
end
|
||||
|
||||
it "converts source tags to RCE media attachment iframes" do
|
||||
|
@ -238,11 +232,11 @@ describe ImportedHtmlConverter do
|
|||
|
||||
test_string = %(<video style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="0_l4l5n0wt"><source src="$CANVAS_OBJECT_REFERENCE$/media_attachments_iframe/#{att.migration_id}?type=video" data-media-id="0_l4l5n0wt" data-media-type="video"></video>)
|
||||
converted_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="0_l4l5n0wt" src="/media_attachments_iframe/#{att.id}?type=video"></iframe>)
|
||||
expect(convert_and_replace(test_string)).to eq converted_string
|
||||
expect(convert_exported_html(test_string)).to eq converted_string
|
||||
|
||||
test_string = %(<audio style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="audio" data-media-id="0_l4l5n0wt"><source src="$CANVAS_OBJECT_REFERENCE$/media_attachments_iframe/#{att.migration_id}?type=audio" data-media-id="0_l4l5n0wt" data-media-type="audio"></video>)
|
||||
converted_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="audio" data-media-id="0_l4l5n0wt" src="/media_attachments_iframe/#{att.id}?type=audio"></iframe>)
|
||||
expect(convert_and_replace(test_string)).to eq converted_string
|
||||
expect(convert_exported_html(test_string)).to eq converted_string
|
||||
end
|
||||
|
||||
it "converts source tags to RCE media attachment iframes when link is untranslated" do
|
||||
|
@ -250,16 +244,16 @@ describe ImportedHtmlConverter do
|
|||
|
||||
test_string = %(<video style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="0_l4l5n0wt"><source src="/media_attachments_iframe/#{att.id}?type=video" data-media-id="0_l4l5n0wt" data-media-type="video"></video>)
|
||||
converted_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="0_l4l5n0wt" src="/media_attachments_iframe/#{att.id}?type=video"></iframe>)
|
||||
expect(convert_and_replace(test_string)).to eq converted_string
|
||||
expect(convert_exported_html(test_string)).to eq converted_string
|
||||
|
||||
test_string = %(<audio style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="audio" data-media-id="0_l4l5n0wt"><source src="/media_attachments_iframe/#{att.id}?type=audio" data-media-id="0_l4l5n0wt" data-media-type="audio"></video>)
|
||||
converted_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="audio" data-media-id="0_l4l5n0wt" src="/media_attachments_iframe/#{att.id}?type=audio"></iframe>)
|
||||
expect(convert_and_replace(test_string)).to eq converted_string
|
||||
expect(convert_exported_html(test_string)).to eq converted_string
|
||||
end
|
||||
|
||||
it "leaves source tags without data-media-id alone" do
|
||||
test_string = %(<video style="width: 400px; height: 225px; display: inline-block;" title="this is a non-canvas video" allowfullscreen="allowfullscreen" allow="fullscreen"><source src="http://www.example.com/video.mov"></video>)
|
||||
expect(convert_and_replace(test_string)).to eq test_string
|
||||
expect(convert_exported_html(test_string)).to eq test_string
|
||||
end
|
||||
|
||||
it "only converts url params" do
|
||||
|
@ -275,7 +269,7 @@ describe ImportedHtmlConverter do
|
|||
</object>
|
||||
HTML
|
||||
|
||||
expect(convert_and_replace(test_string)).to match_ignoring_whitespace(<<~HTML.strip)
|
||||
expect(convert_exported_html(test_string)).to match_ignoring_whitespace(<<~HTML.strip)
|
||||
<object>
|
||||
<param name="controls" value="CONSOLE">
|
||||
<param name="controller" value="true">
|
||||
|
@ -288,13 +282,13 @@ describe ImportedHtmlConverter do
|
|||
|
||||
it "leaves an anchor tag alone" do
|
||||
test_string = '<p><a href="#anchor_ref">ref</a></p>'
|
||||
expect(convert_and_replace(test_string)).to eq test_string
|
||||
expect(convert_exported_html(test_string)).to eq test_string
|
||||
end
|
||||
|
||||
it "converts base64 images to file links" do
|
||||
base64 = "R0lGODlhCQAJAIAAAICAgP///yH5BAEAAAEALAAAAAAJAAkAAAIQTGCZgGrc\nFIxvSuhwpsuFAgA7\n"
|
||||
test_string = "<p><img src=\"data:image/gif;base64,#{base64}\"></p>"
|
||||
new_string = convert_and_replace(test_string)
|
||||
new_string = convert_exported_html(test_string)
|
||||
attachment = Attachment.last
|
||||
expect(attachment.content_type).to eq "image/gif"
|
||||
expect(attachment.name).to eq "1d1fde3d669ed5c4fc68a49d643f140d.gif"
|
Loading…
Reference in New Issue