canvas_link_migrator 1.0.9 → 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/canvas_link_migrator/link_parser.rb +4 -10
- data/lib/canvas_link_migrator/link_resolver.rb +36 -18
- data/lib/canvas_link_migrator/resource_map_service.rb +13 -13
- data/lib/canvas_link_migrator/version.rb +1 -1
- data/spec/canvas_link_migrator/imported_html_converter_spec.rb +151 -117
- data/spec/canvas_link_migrator/link_resolver_spec.rb +4 -4
- data/spec/fixtures/canvas_resource_map.json +21 -10
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7cbc11edecf38395bcf3dfda752a5a2946e9f691f86f95f35d80fe12a28f228
|
4
|
+
data.tar.gz: d55a7c3509ce6da3b423e20b119f17ad5dbfd61f32d8f4f6a926ae0e84da343f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f54e3e63f1a47b50f0568bfcfce57f2b15c59a7097e7ea4fde9a89433554cba0a24bc60a854e267d2efab49c4cceec532d2c3a99e2b2b779cadbcc379a0c349
|
7
|
+
data.tar.gz: 5b290e62a21a044efea17939e03ab3cd197e9acf7030fda2683b8f4c9a0033ef4ff72fa5f82b8dfe09affb1f3a86d8c4c55c21ef853768b93e16a6f19041a0ac
|
@@ -19,7 +19,6 @@
|
|
19
19
|
|
20
20
|
require "nokogiri"
|
21
21
|
require "digest"
|
22
|
-
require "addressable"
|
23
22
|
|
24
23
|
module CanvasLinkMigrator
|
25
24
|
class LinkParser
|
@@ -85,7 +84,7 @@ module CanvasLinkMigrator
|
|
85
84
|
doc = Nokogiri::HTML5.fragment(html || "")
|
86
85
|
|
87
86
|
# Replace source tags with iframes
|
88
|
-
doc.search("source[data-media-id]").each do |source|
|
87
|
+
doc.search("source[data-media-type],source[data-media-id]").each do |source|
|
89
88
|
next unless RCE_MEDIA_TYPES.include?(source.parent.name)
|
90
89
|
|
91
90
|
media_node = source.parent
|
@@ -144,12 +143,7 @@ module CanvasLinkMigrator
|
|
144
143
|
url.gsub!("%24#{ref}%24", "$#{ref}$")
|
145
144
|
end
|
146
145
|
|
147
|
-
|
148
|
-
result = parse_url(url, node, attr)
|
149
|
-
rescue Addressable::URI::InvalidURIError
|
150
|
-
return
|
151
|
-
end
|
152
|
-
|
146
|
+
result = parse_url(url, node, attr)
|
153
147
|
if result[:resolved]
|
154
148
|
# resolved, just replace and carry on
|
155
149
|
new_url = result[:new_url] || url
|
@@ -238,12 +232,12 @@ module CanvasLinkMigrator
|
|
238
232
|
elsif url =~ %r{\$IMS(?:-|_)CC(?:-|_)FILEBASE\$/(.*)}
|
239
233
|
rel_path = URI::DEFAULT_PARSER.unescape($1)
|
240
234
|
if (attr == "href" && node["class"]&.include?("instructure_inline_media_comment")) ||
|
241
|
-
(attr == "src" && ["iframe", "source"].include?(node.name)
|
235
|
+
(attr == "src" && ["iframe", "source"].include?(node.name))
|
242
236
|
unresolved(:media_object, rel_path: rel_path)
|
243
237
|
else
|
244
238
|
unresolved(:file, rel_path: rel_path)
|
245
239
|
end
|
246
|
-
elsif (attr == "src" && ["iframe", "source"].include?(node.name) && node["data-media-id"])
|
240
|
+
elsif (attr == "src" && ["iframe", "source"].include?(node.name) && (node["data-media-id"] || node["data-media-type"]))
|
247
241
|
# media_objects_iframe course copy reference without an attachment id, change to media_attachments_iframe
|
248
242
|
unresolved(:media_object, rel_path: node["src"])
|
249
243
|
elsif @migration_query_service.supports_embedded_images && attr == "src" && (info_match = url.match(%r{\Adata:(?<mime_type>[-\w]+/[-\w+.]+)?;base64,(?<image>.*)}m))
|
@@ -18,6 +18,7 @@
|
|
18
18
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
19
|
|
20
20
|
require "active_support/core_ext/object"
|
21
|
+
require "addressable"
|
21
22
|
require "rack"
|
22
23
|
|
23
24
|
module CanvasLinkMigrator
|
@@ -44,6 +45,14 @@ module CanvasLinkMigrator
|
|
44
45
|
@attachment_path_id_lookup_lower ||= attachment_path_id_lookup&.transform_keys(&:downcase)
|
45
46
|
end
|
46
47
|
|
48
|
+
def add_verifier_to_query(url, uuid)
|
49
|
+
parsed_url = Addressable::URI.parse(url)
|
50
|
+
parsed_url.query_values = (parsed_url.query_values || {}).merge("verifier" => uuid)
|
51
|
+
parsed_url.to_s
|
52
|
+
rescue Addressable::InvalidURIError
|
53
|
+
url
|
54
|
+
end
|
55
|
+
|
47
56
|
# finds the :new_value to use to replace the placeholder
|
48
57
|
def resolve_link!(link)
|
49
58
|
case link[:link_type]
|
@@ -71,13 +80,17 @@ module CanvasLinkMigrator
|
|
71
80
|
linked_wiki_url = @migration_id_converter.convert_wiki_page_migration_id_to_slug(migration_id) || migration_id
|
72
81
|
link[:new_value] = "#{context_path}/pages/#{linked_wiki_url}#{query}"
|
73
82
|
elsif type == "attachments"
|
74
|
-
att_id = @migration_id_converter.convert_attachment_migration_id(migration_id)
|
83
|
+
att_id, uuid = @migration_id_converter.convert_attachment_migration_id(migration_id)
|
75
84
|
if att_id
|
76
|
-
|
85
|
+
new_url = "#{context_path}/files/#{att_id}/preview"
|
86
|
+
new_url = add_verifier_to_query(new_url, uuid) if uuid
|
87
|
+
link[:new_value] = new_url
|
77
88
|
end
|
78
89
|
elsif type == "media_attachments_iframe"
|
79
|
-
att_id = @migration_id_converter.convert_attachment_migration_id(migration_id)
|
80
|
-
|
90
|
+
att_id, uuid = @migration_id_converter.convert_attachment_migration_id(migration_id)
|
91
|
+
new_url = att_id ? "/media_attachments_iframe/#{att_id}#{link[:query]}" : link[:old_value]
|
92
|
+
new_url = add_verifier_to_query(new_url, uuid) if uuid
|
93
|
+
link[:new_value] = new_url
|
81
94
|
else
|
82
95
|
object_id = @migration_id_converter.convert_migration_id(type, migration_id)
|
83
96
|
if object_id
|
@@ -116,7 +129,7 @@ module CanvasLinkMigrator
|
|
116
129
|
end
|
117
130
|
link[:new_value] = new_url
|
118
131
|
when :file_ref
|
119
|
-
file_id = @migration_id_converter.convert_attachment_migration_id(link[:migration_id])
|
132
|
+
file_id, uuid = @migration_id_converter.convert_attachment_migration_id(link[:migration_id])
|
120
133
|
if file_id
|
121
134
|
rest = link[:rest].presence
|
122
135
|
rest ||= "/preview" unless link[:target_blank]
|
@@ -125,13 +138,15 @@ module CanvasLinkMigrator
|
|
125
138
|
# context prepended to the URL. This prevents
|
126
139
|
# redirects to non cross-origin friendly urls
|
127
140
|
# during a file fetch
|
128
|
-
if rest&.include?("icon_maker_icon=1")
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
141
|
+
new_url = if rest&.include?("icon_maker_icon=1")
|
142
|
+
"/files/#{file_id}#{rest}"
|
143
|
+
elsif link[:in_media_iframe]
|
144
|
+
"/media_attachments_iframe/#{file_id}#{rest}"
|
145
|
+
else
|
146
|
+
"#{context_path}/files/#{file_id}#{rest}"
|
147
|
+
end
|
148
|
+
new_url = add_verifier_to_query(new_url, uuid) if uuid
|
149
|
+
link[:new_value] = new_url
|
135
150
|
else
|
136
151
|
link[:missing_url] = link[:old_value].partition("$CANVAS_COURSE_REFERENCE$").last
|
137
152
|
end
|
@@ -200,6 +215,7 @@ module CanvasLinkMigrator
|
|
200
215
|
# CCHelper::file_query_string
|
201
216
|
params = Rack::Utils.parse_nested_query(qs.presence || "")
|
202
217
|
qs = []
|
218
|
+
qs << "verifier=#{file["uuid"]}" if file["uuid"].present?
|
203
219
|
new_action = ""
|
204
220
|
params.each do |k, v|
|
205
221
|
case k
|
@@ -217,23 +233,25 @@ module CanvasLinkMigrator
|
|
217
233
|
new_url
|
218
234
|
end
|
219
235
|
|
220
|
-
def media_attachment_iframe_url(file_id, media_type = nil)
|
236
|
+
def media_attachment_iframe_url(file_id, uuid = nil, media_type = nil)
|
221
237
|
url = "/media_attachments_iframe/#{file_id}?embedded=true"
|
222
238
|
url += "&type=#{media_type}" if media_type.present?
|
239
|
+
url += "&verifier=#{uuid}" if uuid.present?
|
223
240
|
url
|
224
241
|
end
|
225
242
|
|
226
243
|
def resolve_media_data(node, rel_path)
|
227
244
|
if rel_path && (file = find_file_in_context(rel_path[/^[^?]+/])) # strip query string for this search
|
228
|
-
media_id = file
|
245
|
+
media_id = file["media_entry_id"]
|
229
246
|
node["data-media-id"] = media_id # safe to delete?
|
230
|
-
media_attachment_iframe_url(file["id"], node["data-media-type"])
|
247
|
+
media_attachment_iframe_url(file["id"], file["uuid"], node["data-media-type"])
|
231
248
|
elsif rel_path&.match(/\/media_attachments_iframe\/\d+/)
|
232
249
|
# media attachment from another course or something
|
233
250
|
rel_path
|
234
|
-
elsif node["data-media-id"]
|
235
|
-
|
236
|
-
|
251
|
+
elsif (file_id, uuid = @migration_id_converter.convert_attachment_media_id(node["data-media-id"]))
|
252
|
+
file_id ? media_attachment_iframe_url(file_id, uuid, node["data-media-type"]) : nil
|
253
|
+
elsif (file_id, uuid = @migration_id_converter.convert_attachment_media_id(rel_path.match(/media_objects(?:_iframe)?\/([^?.]+)/)&.[](1)))
|
254
|
+
file_id ? media_attachment_iframe_url(file_id, uuid, node["data-media-type"]) : nil
|
237
255
|
else
|
238
256
|
node.delete("class")
|
239
257
|
node.delete("id")
|
@@ -89,7 +89,19 @@ module CanvasLinkMigrator
|
|
89
89
|
end
|
90
90
|
|
91
91
|
def convert_attachment_migration_id(migration_id)
|
92
|
-
resources.dig("files", migration_id, "destination", "
|
92
|
+
resources.dig("files", migration_id, "destination")&.slice("id", "uuid")&.values
|
93
|
+
end
|
94
|
+
|
95
|
+
def media_map
|
96
|
+
@media_map ||= resources["files"].each_with_object({}) do |(_mig_id, file), map|
|
97
|
+
media_id = file.dig("destination", "media_entry_id")
|
98
|
+
next unless media_id
|
99
|
+
map[media_id] = file
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def convert_attachment_media_id(media_id)
|
104
|
+
media_map.dig(media_id, "destination")&.slice("id", "uuid")&.values
|
93
105
|
end
|
94
106
|
|
95
107
|
def convert_migration_id(type, migration_id)
|
@@ -106,17 +118,5 @@ module CanvasLinkMigrator
|
|
106
118
|
def lookup_attachment_by_migration_id(migration_id)
|
107
119
|
resources.dig("files", migration_id, "destination")
|
108
120
|
end
|
109
|
-
|
110
|
-
def media_map
|
111
|
-
@media_map ||= resources["files"].each_with_object({}) do |(_mig_id, file), map|
|
112
|
-
media_id = file.dig("destination", "media_entry_id")
|
113
|
-
next unless media_id
|
114
|
-
map[media_id] = file
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def lookup_attachment_by_media_id(media_id)
|
119
|
-
media_map.dig(media_id, "destination")
|
120
|
-
end
|
121
121
|
end
|
122
122
|
end
|
@@ -75,7 +75,7 @@ describe CanvasLinkMigrator::ImportedHtmlConverter do
|
|
75
75
|
it "converts data-download-url for files without appending a context" do
|
76
76
|
html, bad_links = subject
|
77
77
|
expect(html).to eq(
|
78
|
-
"<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\">"
|
78
|
+
"<img src=\"#{@path}files/5/download?download_frd=1&verifier=u5\" alt=\"\" data-inst-icon-maker-icon=\"true\" data-download-url=\"/files/5/download?download_frd=1&icon_maker_icon=1&verifier=u5\">"
|
79
79
|
)
|
80
80
|
expect(bad_links).to be_nil
|
81
81
|
end
|
@@ -83,7 +83,7 @@ describe CanvasLinkMigrator::ImportedHtmlConverter do
|
|
83
83
|
|
84
84
|
it "finds an attachment by migration id" do
|
85
85
|
test_string = %{<p>This is an image: <br /><img src="%24CANVAS_OBJECT_REFERENCE%24/attachments/F" alt=":(" /></p>}
|
86
|
-
expect(@converter.convert_exported_html(test_string)).to eq([%{<p>This is an image: <br><img src="#{@path}files/6/preview" alt=":("></p>}, nil])
|
86
|
+
expect(@converter.convert_exported_html(test_string)).to eq([%{<p>This is an image: <br><img src="#{@path}files/6/preview?verifier=u6" alt=":("></p>}, nil])
|
87
87
|
end
|
88
88
|
|
89
89
|
it "leaves relative user attachments alone" do
|
@@ -106,20 +106,20 @@ describe CanvasLinkMigrator::ImportedHtmlConverter do
|
|
106
106
|
expect(bad_links[0]).to include({ link_type: :file, missing_url: "/courses/2/file_contents/course%20files/test.png" })
|
107
107
|
|
108
108
|
expect(@converter.link_resolver).to receive(:attachment_path_id_lookup).twice.and_call_original
|
109
|
-
expect(@converter.convert_exported_html(test_string)).to eq([%{<p>This is an image: <br><img src="#{@path}files/5/preview" alt=":("></p>}, nil])
|
109
|
+
expect(@converter.convert_exported_html(test_string)).to eq([%{<p>This is an image: <br><img src="#{@path}files/5/preview?verifier=u5" alt=":("></p>}, nil])
|
110
110
|
end
|
111
111
|
|
112
112
|
it "finds an attachment by a path with a space" do
|
113
113
|
test_string = %(<img src="subfolder/with%20a%20space/test.png" alt="nope" />)
|
114
|
-
expect(@converter.convert_exported_html(test_string)).to eq([%(<img src="#{@path}files/6/preview" alt="nope">), nil])
|
114
|
+
expect(@converter.convert_exported_html(test_string)).to eq([%(<img src="#{@path}files/6/preview?verifier=u6" alt="nope">), nil])
|
115
115
|
|
116
116
|
test_string = %(<img src="subfolder/with+a+space/test.png" alt="nope" />)
|
117
|
-
expect(@converter.convert_exported_html(test_string)).to eq([%(<img src="#{@path}files/6/preview" alt="nope">), nil])
|
117
|
+
expect(@converter.convert_exported_html(test_string)).to eq([%(<img src="#{@path}files/6/preview?verifier=u6" alt="nope">), nil])
|
118
118
|
end
|
119
119
|
|
120
120
|
it "finds an attachment even if the link has an extraneous folder" do
|
121
121
|
test_string = %(<img src="anotherfolder/subfolder/test.png" alt="nope" />)
|
122
|
-
expect(@converter.convert_exported_html(test_string)).to eq([%(<img src="#{@path}files/7/preview" alt="nope">), nil])
|
122
|
+
expect(@converter.convert_exported_html(test_string)).to eq([%(<img src="#{@path}files/7/preview?verifier=u7" alt="nope">), nil])
|
123
123
|
end
|
124
124
|
|
125
125
|
it "finds an attachment by path if capitalization is different" do
|
@@ -127,18 +127,18 @@ describe CanvasLinkMigrator::ImportedHtmlConverter do
|
|
127
127
|
expect(@converter.link_resolver).to receive(:attachment_path_id_lookup).twice.and_return({ "subfolder/withcapital/test.png" => "F" })
|
128
128
|
|
129
129
|
test_string = %(<img src="subfolder/WithCapital/TEST.png" alt="nope" />)
|
130
|
-
expect(@converter.convert_exported_html(test_string)).to eq([%(<img src="#{@path}files/6/preview" alt="nope">), nil])
|
130
|
+
expect(@converter.convert_exported_html(test_string)).to eq([%(<img src="#{@path}files/6/preview?verifier=u6" alt="nope">), nil])
|
131
131
|
end
|
132
132
|
|
133
133
|
it "finds an attachment with query params" do
|
134
134
|
test_string = %(<img src="%24IMS_CC_FILEBASE%24/test.png?canvas_customaction=1&canvas_qs_customparam=1" alt="nope" />)
|
135
|
-
expect(@converter.convert_exported_html(test_string)).to eq([%(<img src="#{@path}files/5/customaction?customparam=1" alt="nope">), nil])
|
135
|
+
expect(@converter.convert_exported_html(test_string)).to eq([%(<img src="#{@path}files/5/customaction?verifier=u5&customparam=1" alt="nope">), nil])
|
136
136
|
|
137
137
|
test_string = %(<img src="%24IMS_CC_FILEBASE%24/test.png?canvas_qs_customparam2=3" alt="nope" />)
|
138
|
-
expect(@converter.convert_exported_html(test_string)).to eq([%(<img src="#{@path}files/5/preview?customparam2=3" alt="nope">), nil])
|
138
|
+
expect(@converter.convert_exported_html(test_string)).to eq([%(<img src="#{@path}files/5/preview?verifier=u5&customparam2=3" alt="nope">), nil])
|
139
139
|
|
140
140
|
test_string = %(<img src="%24IMS_CC_FILEBASE%24/test.png?notarelevantparam" alt="nope" />)
|
141
|
-
expect(@converter.convert_exported_html(test_string)).to eq([%(<img src="#{@path}files/5/preview" alt="nope">), nil])
|
141
|
+
expect(@converter.convert_exported_html(test_string)).to eq([%(<img src="#{@path}files/5/preview?verifier=u5" alt="nope">), nil])
|
142
142
|
end
|
143
143
|
end
|
144
144
|
|
@@ -210,130 +210,164 @@ describe CanvasLinkMigrator::ImportedHtmlConverter do
|
|
210
210
|
expect(bad_links[0]).to include({ link_type: :file, missing_url: "/courses/2/file_contents/course%20files/relative/path/to/file%20with%20space.html" })
|
211
211
|
end
|
212
212
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
213
|
+
context "with media links" do
|
214
|
+
it "changes old media URL types into media_attachments_iframe" do
|
215
|
+
test_string = <<~HTML.strip
|
216
|
+
<p>
|
217
|
+
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>
|
218
|
+
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>
|
219
|
+
with mediahref url: <iframe data-media-type="video" src="/media_objects_iframe?mediahref=$CANVAS_COURSE_REFERENCE$/file_ref/I/download" data-media-id="m-yodawg"></iframe>
|
220
|
+
</p>
|
221
|
+
HTML
|
222
|
+
|
223
|
+
expected_string = <<~HTML.strip
|
224
|
+
<p>
|
225
|
+
with media object url: <iframe id="media_comment_m-stuff" class="instructure_inline_media_comment video_comment" style="width: 320px; height: 240px; display: inline-block;" title="this is a media comment" data-media-type="video" src="/media_attachments_iframe/5?embedded=true&type=video&verifier=u5" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-stuff"></iframe>
|
226
|
+
with file content url: <iframe id="media_comment_0_bq09qam2" class="instructure_inline_media_comment video_comment" style="width: 320px; height: 240px; display: inline-block;" title="this is a media comment" data-media-type="video" src="/media_attachments_iframe/6?embedded=true&type=video&verifier=u6" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="0_bq09qam2"></iframe>
|
227
|
+
with mediahref url: <iframe data-media-type="video" src="/media_attachments_iframe/9?embedded=true&type=video&verifier=u9" data-media-id="m-yodawg"></iframe>
|
228
|
+
</p>
|
229
|
+
HTML
|
230
|
+
|
231
|
+
expect(@converter.convert_exported_html(test_string)).to eq([expected_string, nil])
|
232
|
+
end
|
221
233
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
HTML
|
234
|
+
it "finds attachments for media_object_iframes that don't have valid data-media-ids" do
|
235
|
+
test_string = <<~HTML.strip
|
236
|
+
<p>
|
237
|
+
in video format: <video style="width: 599px; height: 337px; display: inline-block;" title="0_bq09qam2" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="undefined"><source src="/media_objects_iframe/0_bq09qam2?type=video?type=video" data-media-id="undefined" data-media-type="video"></video>
|
238
|
+
</p>
|
239
|
+
HTML
|
229
240
|
|
230
|
-
|
231
|
-
|
241
|
+
expected_string = <<~HTML.strip
|
242
|
+
<p>
|
243
|
+
in video format: <iframe style="width: 599px; height: 337px; display: inline-block;" title="0_bq09qam2" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="undefined" src="/media_attachments_iframe/6?embedded=true&type=video&verifier=u6"></iframe>
|
244
|
+
</p>
|
245
|
+
HTML
|
232
246
|
|
233
|
-
|
234
|
-
|
235
|
-
<p>
|
236
|
-
with media object url: <a id="media_comment_m-stuff1" class="instructure_inline_media_comment video_comment" href="/media_objects/m-stuff1">this is a media comment</a>
|
237
|
-
with file content url: <a id="media_comment_0_bq09qam3" class="instructure_inline_media_comment video_comment" href="/courses/2/file_contents/course%20files/media_objects/0_bq09qam3">this is a media comment</a>
|
238
|
-
with mediahref url: <iframe data-media-type="video" src="/media_objects_iframe?mediahref=$CANVAS_COURSE_REFERENCE$/file_ref/yarg/download" data-media-id="m-yodawg"></iframe>
|
239
|
-
</p>
|
240
|
-
HTML
|
247
|
+
expect(@converter.convert_exported_html(test_string)).to eq([expected_string, nil])
|
248
|
+
end
|
241
249
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
HTML
|
250
|
+
it "finds attachments for media_object_iframes that don't have data-media-ids" do
|
251
|
+
test_string = <<~HTML.strip
|
252
|
+
<p>
|
253
|
+
in video format: <video style="width: 599px; height: 337px; display: inline-block;" title="0_bq09qam2" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen"><source src="/media_objects_iframe/0_bq09qam2?type=video?type=video" data-media-id="undefined" data-media-type="video"></video>
|
254
|
+
</p>
|
255
|
+
HTML
|
249
256
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
{
|
256
|
-
link_type: :media_object,
|
257
|
-
missing_url: "/courses/2/file_contents/course%20files/media_objects/0_bq09qam3"
|
258
|
-
},
|
259
|
-
{
|
260
|
-
link_type: :file_ref, missing_url: "/file_ref/yarg/download"
|
261
|
-
}
|
262
|
-
]
|
263
|
-
expect(@converter.convert_exported_html(test_string)).to eq([expected_string, expected_errors])
|
264
|
-
end
|
257
|
+
expected_string = <<~HTML.strip
|
258
|
+
<p>
|
259
|
+
in video format: <iframe style="width: 599px; height: 337px; display: inline-block;" title="0_bq09qam2" data-media-type="video" allowfullscreen="allowfullscreen" allow="fullscreen" src="/media_attachments_iframe/6?embedded=true&type=video&verifier=u6"></iframe>
|
260
|
+
</p>
|
261
|
+
HTML
|
265
262
|
|
266
|
-
|
267
|
-
|
268
|
-
<p>
|
269
|
-
with wrong file in href: <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>
|
270
|
-
with no href: <a class="instructure_inline_media_comment video_comment" id="media_comment_m-stuff" href="#"></a><br><br>
|
271
|
-
</p>
|
272
|
-
HTML
|
273
|
-
expected_string = <<~HTML.strip
|
274
|
-
<p>
|
275
|
-
with wrong file in href: <iframe class="instructure_inline_media_comment video_comment" id="media_comment_m-stuff" style="width: 320px; height: 240px; display: inline-block;" title="this is a media comment" data-media-type="video" src="/media_attachments_iframe/5?embedded=true&type=video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-stuff"></iframe><br><br>
|
276
|
-
with no href: <iframe class="instructure_inline_media_comment video_comment" id="media_comment_m-stuff" style="width: 320px; height: 240px; display: inline-block;" title="" data-media-type="video" src="/media_attachments_iframe/5?embedded=true&type=video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-stuff"></iframe><br><br>
|
277
|
-
</p>
|
278
|
-
HTML
|
279
|
-
expect(@converter.convert_exported_html(test_string)).to eq([expected_string, nil])
|
280
|
-
end
|
263
|
+
expect(@converter.convert_exported_html(test_string)).to eq([expected_string, nil])
|
264
|
+
end
|
281
265
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
266
|
+
it "handles old media types where we can't find the file" do
|
267
|
+
test_string = <<~HTML.strip
|
268
|
+
<p>
|
269
|
+
with media object url: <a id="media_comment_m-stuff1" class="instructure_inline_media_comment video_comment" href="/media_objects/m-stuff1">this is a media comment</a>
|
270
|
+
with file content url: <a id="media_comment_0_bq09qam3" class="instructure_inline_media_comment video_comment" href="/courses/2/file_contents/course%20files/media_objects/0_bq09qam3">this is a media comment</a>
|
271
|
+
with mediahref url: <iframe data-media-type="video" src="/media_objects_iframe?mediahref=$CANVAS_COURSE_REFERENCE$/file_ref/yarg/download" data-media-id="m-yodawg"></iframe>
|
272
|
+
</p>
|
273
|
+
HTML
|
274
|
+
|
275
|
+
expected_string = <<~HTML.strip
|
276
|
+
<p>
|
277
|
+
with media object url: <iframe title="this is a media comment" data-media-type="video" src="/courses/2/file_contents/course%20files/media_objects/m-stuff1" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-stuff1"></iframe>
|
278
|
+
with file content url: <iframe title="this is a media comment" data-media-type="video" src="/courses/2/file_contents/course%20files/media_objects/0_bq09qam3" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="0_bq09qam3"></iframe>
|
279
|
+
with mediahref url: <iframe data-media-type="video" src="/media_objects_iframe?mediahref=$CANVAS_COURSE_REFERENCE$/file_ref/yarg/download" data-media-id="m-yodawg"></iframe>
|
280
|
+
</p>
|
281
|
+
HTML
|
282
|
+
|
283
|
+
expected_errors = [
|
284
|
+
{
|
285
|
+
link_type: :media_object,
|
286
|
+
missing_url: "/courses/2/file_contents/course%20files/media_objects/m-stuff1"
|
287
|
+
},
|
288
|
+
{
|
289
|
+
link_type: :media_object,
|
290
|
+
missing_url: "/courses/2/file_contents/course%20files/media_objects/0_bq09qam3"
|
291
|
+
},
|
292
|
+
{
|
293
|
+
link_type: :file_ref, missing_url: "/file_ref/yarg/download"
|
294
|
+
}
|
295
|
+
]
|
296
|
+
expect(@converter.convert_exported_html(test_string)).to eq([expected_string, expected_errors])
|
297
|
+
end
|
287
298
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
299
|
+
it "handles and repair half broken media links" do
|
300
|
+
test_string = <<~HTML.strip
|
301
|
+
<p>
|
302
|
+
with wrong file in href: <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>
|
303
|
+
with no href: <a class="instructure_inline_media_comment video_comment" id="media_comment_m-stuff" href="#"></a><br><br>
|
304
|
+
</p>
|
305
|
+
HTML
|
306
|
+
expected_string = <<~HTML.strip
|
307
|
+
<p>
|
308
|
+
with wrong file in href: <iframe class="instructure_inline_media_comment video_comment" id="media_comment_m-stuff" style="width: 320px; height: 240px; display: inline-block;" title="this is a media comment" data-media-type="video" src="/media_attachments_iframe/5?embedded=true&type=video&verifier=u5" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-stuff"></iframe><br><br>
|
309
|
+
with no href: <iframe class="instructure_inline_media_comment video_comment" id="media_comment_m-stuff" style="width: 320px; height: 240px; display: inline-block;" title="" data-media-type="video" src="/media_attachments_iframe/5?embedded=true&type=video&verifier=u5" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-stuff"></iframe><br><br>
|
310
|
+
</p>
|
311
|
+
HTML
|
312
|
+
expect(@converter.convert_exported_html(test_string)).to eq([expected_string, nil])
|
313
|
+
end
|
293
314
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
315
|
+
it "converts old RCE media object iframes" do
|
316
|
+
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-lolcat?type=video" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-lolcat"></iframe>)
|
317
|
+
replacement_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" src="/media_attachments_iframe/8?embedded=true&type=video&verifier=u8" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-lolcat"></iframe>)
|
318
|
+
expect(@converter.convert_exported_html(test_string)).to eq([replacement_string, nil])
|
319
|
+
end
|
298
320
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
321
|
+
it "handles and repair half broken new RCE media iframes" do
|
322
|
+
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-lolcat"></iframe>)
|
323
|
+
repaired_string = %(<iframe style="width: 400px; height: 225px; display: inline-block;" title="this is a media comment" data-media-type="video" src="/media_attachments_iframe/8?embedded=true&type=video&verifier=u8" allowfullscreen="allowfullscreen" allow="fullscreen" data-media-id="m-lolcat"></iframe>)
|
324
|
+
expect(@converter.convert_exported_html(test_string)).to eq([repaired_string, nil])
|
325
|
+
end
|
303
326
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
327
|
+
it "converts source tags to RCE media iframes" do
|
328
|
+
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-lolcat"><source src="/media_objects_iframe/m-lolcat?type=video" data-media-id="m-lolcat" data-media-type="video"></video>)
|
329
|
+
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-lolcat" src="/media_attachments_iframe/8?embedded=true&type=video&verifier=u8"></iframe>)
|
330
|
+
expect(@converter.convert_exported_html(test_string)).to eq([converted_string, nil])
|
308
331
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
332
|
+
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-yodawg"><source src="/media_objects_iframe/m-yodawg?type=audio" data-media-id="m-yodawg" data-media-type="audio"></audio>)
|
333
|
+
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-yodawg" src="/media_attachments_iframe/9?embedded=true&type=audio&verifier=u9"></iframe>)
|
334
|
+
expect(@converter.convert_exported_html(test_string)).to eq([converted_string, nil])
|
335
|
+
end
|
313
336
|
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
337
|
+
it "converts source tags to RCE media attachment iframes" do
|
338
|
+
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="$IMS-CC-FILEBASE$/subfolder/with a space/yodawg.mov?canvas_=1&canvas_qs_type=video&canvas_qs_amp=&canvas_qs_embedded=true&media_attachment=true" data-media-id="m-stuff" data-media-type="video"></video>)
|
339
|
+
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-yodawg" src="/media_attachments_iframe/9?embedded=true&type=video&verifier=u9"></iframe>)
|
340
|
+
expect(@converter.convert_exported_html(test_string)).to eq([converted_string, nil])
|
318
341
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
342
|
+
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="$IMS-CC-FILEBASE$/lolcat.mp3?canvas_=1&canvas_qs_type=audio&canvas_qs_amp=&canvas_qs_embedded=true&media_attachment=true" data-media-id="m-stuff" data-media-type="audio"></video>)
|
343
|
+
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-lolcat" src="/media_attachments_iframe/8?embedded=true&type=audio&verifier=u8"></iframe>)
|
344
|
+
expect(@converter.convert_exported_html(test_string)).to eq([converted_string, nil])
|
345
|
+
end
|
323
346
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
347
|
+
it "converts source tags to RCE media attachment iframes when link is an unknown media attachment reference (link from a public file in another course)" do
|
348
|
+
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/18?type=video" data-media-id="0_l4l5n0wt" data-media-type="video"></video>)
|
349
|
+
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/18?type=video"></iframe>)
|
350
|
+
expect(@converter.convert_exported_html(test_string)).to eq([converted_string, nil])
|
328
351
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
352
|
+
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_l4l5n0wu"><source src="/media_attachments_iframe/19?type=audio" data-media-id="0_l4l5n0wu" data-media-type="audio"></video>)
|
353
|
+
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_l4l5n0wu" src="/media_attachments_iframe/19?type=audio"></iframe>)
|
354
|
+
expect(@converter.convert_exported_html(test_string)).to eq([converted_string, nil])
|
355
|
+
end
|
333
356
|
|
334
|
-
|
335
|
-
|
336
|
-
|
357
|
+
it "converts course copy style media attachmet iframe links" do
|
358
|
+
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-yodawg"><source src="$CANVAS_COURSE_REFERENCE$/file_ref/I?media_attachment=true&type=video" data-media-id="m-yodawg" data-media-type="video"></video>)
|
359
|
+
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-yodawg" src="/media_attachments_iframe/9?embedded=true&type=video&verifier=u9"></iframe>)
|
360
|
+
expect(@converter.convert_exported_html(test_string)).to eq([converted_string, nil])
|
361
|
+
|
362
|
+
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-lolcat"><source src="$CANVAS_COURSE_REFERENCE$/file_ref/H?media_attachment=true&type=audio" data-media-id="m-lolcat" data-media-type="audio"></audio>)
|
363
|
+
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-lolcat" src="/media_attachments_iframe/8?embedded=true&type=audio&verifier=u8"></iframe>)
|
364
|
+
expect(@converter.convert_exported_html(test_string)).to eq([converted_string, nil])
|
365
|
+
end
|
366
|
+
|
367
|
+
it "leaves source tags without data-media-id alone" do
|
368
|
+
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>)
|
369
|
+
expect(@converter.convert_exported_html(test_string)).to eq([test_string, nil])
|
370
|
+
end
|
337
371
|
end
|
338
372
|
|
339
373
|
it "only converts url params" do
|
@@ -48,25 +48,25 @@ describe CanvasLinkMigrator::LinkResolver do
|
|
48
48
|
it "converts file_ref urls" do
|
49
49
|
link = { link_type: :file_ref, migration_id: "F" }
|
50
50
|
resolver.resolve_link!(link)
|
51
|
-
expect(link[:new_value]).to eq("/courses/2/files/6/preview")
|
51
|
+
expect(link[:new_value]).to eq("/courses/2/files/6/preview?verifier=u6")
|
52
52
|
end
|
53
53
|
|
54
54
|
it "does not suffix /preview to target blank links" do
|
55
55
|
link = { link_type: :file_ref, target_blank: true, migration_id: "F" }
|
56
56
|
resolver.resolve_link!(link)
|
57
|
-
expect(link[:new_value]).to eq("/courses/2/files/6")
|
57
|
+
expect(link[:new_value]).to eq("/courses/2/files/6?verifier=u6")
|
58
58
|
end
|
59
59
|
|
60
60
|
it "converts attachment urls" do
|
61
61
|
link = { link_type: :object, type: "attachments", migration_id: "E", query: "?foo=bar" }
|
62
62
|
resolver.resolve_link!(link)
|
63
|
-
expect(link[:new_value]).to eq("/courses/2/files/5/preview")
|
63
|
+
expect(link[:new_value]).to eq("/courses/2/files/5/preview?verifier=u5")
|
64
64
|
end
|
65
65
|
|
66
66
|
it "converts media_attachments_iframe urls" do
|
67
67
|
link = { link_type: :object, type: "media_attachments_iframe", migration_id: "F", query: "?foo=bar" }
|
68
68
|
resolver.resolve_link!(link)
|
69
|
-
expect(link[:new_value]).to eq("/media_attachments_iframe/6?foo=bar")
|
69
|
+
expect(link[:new_value]).to eq("/media_attachments_iframe/6?foo=bar&verifier=u6")
|
70
70
|
end
|
71
71
|
|
72
72
|
it "converts discussion_topic links" do
|
@@ -50,49 +50,60 @@
|
|
50
50
|
"E": {
|
51
51
|
"destination": {
|
52
52
|
"id": "5",
|
53
|
-
"media_entry_id": "m-stuff"
|
53
|
+
"media_entry_id": "m-stuff",
|
54
|
+
"uuid": "u5"
|
54
55
|
},
|
55
56
|
"source": {
|
56
57
|
"id": "3",
|
57
|
-
"media_entry_id": "m-stuff"
|
58
|
+
"media_entry_id": "m-stuff",
|
59
|
+
"uuid": "u3"
|
58
60
|
}
|
59
61
|
},
|
60
62
|
"F": {
|
61
63
|
"destination": {
|
62
64
|
"id": "6",
|
63
|
-
"media_entry_id": "0_bq09qam2"
|
65
|
+
"media_entry_id": "0_bq09qam2",
|
66
|
+
"uuid": "u6"
|
64
67
|
},
|
65
68
|
"source": {
|
66
69
|
"id": "4",
|
67
|
-
"media_entry_id": "0_bq09qam2"
|
70
|
+
"media_entry_id": "0_bq09qam2",
|
71
|
+
"uuid": "u4"
|
68
72
|
}
|
69
73
|
},
|
70
74
|
"G": {
|
71
75
|
"destination": {
|
72
|
-
"id": "7"
|
76
|
+
"id": "7",
|
77
|
+
"uuid": "u7"
|
73
78
|
},
|
74
79
|
"source": {
|
75
|
-
"id": "2"
|
80
|
+
"id": "2",
|
81
|
+
"uuid": "u2"
|
76
82
|
}
|
77
83
|
},
|
78
84
|
"H": {
|
79
85
|
"destination": {
|
80
86
|
"id": "8",
|
81
|
-
"media_entry_id": "m-lolcat"
|
87
|
+
"media_entry_id": "m-lolcat",
|
88
|
+
"uuid": "u8"
|
89
|
+
|
82
90
|
},
|
83
91
|
"source": {
|
84
92
|
"id": "3",
|
85
|
-
"media_entry_id": "m-lolcat"
|
93
|
+
"media_entry_id": "m-lolcat",
|
94
|
+
"uuid": "u3"
|
86
95
|
}
|
87
96
|
},
|
88
97
|
"I": {
|
89
98
|
"destination": {
|
90
99
|
"id": "9",
|
91
|
-
"media_entry_id": "m-yodawg"
|
100
|
+
"media_entry_id": "m-yodawg",
|
101
|
+
"uuid": "u9"
|
92
102
|
},
|
93
103
|
"source": {
|
94
104
|
"id": "4",
|
95
|
-
"media_entry_id": "m-yodawg"
|
105
|
+
"media_entry_id": "m-yodawg",
|
106
|
+
"uuid": "u4"
|
96
107
|
}
|
97
108
|
}
|
98
109
|
},
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: canvas_link_migrator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mysti Lilla
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2024-
|
14
|
+
date: 2024-06-14 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: activesupport
|
@@ -162,7 +162,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
162
162
|
requirements:
|
163
163
|
- - ">="
|
164
164
|
- !ruby/object:Gem::Version
|
165
|
-
version: '
|
165
|
+
version: '2.7'
|
166
166
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
167
167
|
requirements:
|
168
168
|
- - ">="
|