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:
Mysti Lilla 2023-07-13 16:45:22 -06:00
parent ebebf1e9f4
commit 215f35e5e0
15 changed files with 541 additions and 183 deletions

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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?

View File

@ -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

View File

@ -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

View File

@ -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 &amp;^%$ 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

View File

@ -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

View File

@ -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"
}

View File

@ -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

View File

@ -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 || "")

View File

@ -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 &amp;^%$ url">Linkage</a><br><a href="http://www.example.com/poop">Linkage</a>)
expect(convert_exported_html(test_string)).to eq %(<a href="stupid &amp;^%$ 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"