moodle2cc 0.0.1

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.
Files changed (95) hide show
  1. data/.gitignore +20 -0
  2. data/Gemfile +4 -0
  3. data/Guardfile +14 -0
  4. data/LICENSE +661 -0
  5. data/README.md +38 -0
  6. data/Rakefile +12 -0
  7. data/bin/moodle2cc +6 -0
  8. data/lib/moodle2cc.rb +60 -0
  9. data/lib/moodle2cc/canvas/.converter.rb.swo +0 -0
  10. data/lib/moodle2cc/canvas/.wiki.rb.swo +0 -0
  11. data/lib/moodle2cc/canvas/assessment.rb +109 -0
  12. data/lib/moodle2cc/canvas/assignment.rb +108 -0
  13. data/lib/moodle2cc/canvas/converter.rb +28 -0
  14. data/lib/moodle2cc/canvas/course.rb +145 -0
  15. data/lib/moodle2cc/canvas/discussion_topic.rb +64 -0
  16. data/lib/moodle2cc/canvas/label.rb +8 -0
  17. data/lib/moodle2cc/canvas/question.rb +468 -0
  18. data/lib/moodle2cc/canvas/question_bank.rb +54 -0
  19. data/lib/moodle2cc/canvas/resource.rb +16 -0
  20. data/lib/moodle2cc/canvas/templates/assignment.html.erb +9 -0
  21. data/lib/moodle2cc/canvas/templates/syllabus.html.erb +9 -0
  22. data/lib/moodle2cc/canvas/templates/wiki_content.html.erb +10 -0
  23. data/lib/moodle2cc/canvas/web_content.rb +10 -0
  24. data/lib/moodle2cc/canvas/web_link.rb +17 -0
  25. data/lib/moodle2cc/canvas/wiki.rb +25 -0
  26. data/lib/moodle2cc/cc/assessment.rb +25 -0
  27. data/lib/moodle2cc/cc/assignment.rb +66 -0
  28. data/lib/moodle2cc/cc/cc_helper.rb +282 -0
  29. data/lib/moodle2cc/cc/converter.rb +136 -0
  30. data/lib/moodle2cc/cc/course.rb +16 -0
  31. data/lib/moodle2cc/cc/discussion_topic.rb +53 -0
  32. data/lib/moodle2cc/cc/label.rb +6 -0
  33. data/lib/moodle2cc/cc/question.rb +23 -0
  34. data/lib/moodle2cc/cc/resource.rb +30 -0
  35. data/lib/moodle2cc/cc/templates/assignment.html.erb +9 -0
  36. data/lib/moodle2cc/cc/templates/syllabus.html.erb +9 -0
  37. data/lib/moodle2cc/cc/templates/wiki_content.html.erb +10 -0
  38. data/lib/moodle2cc/cc/web_content.rb +40 -0
  39. data/lib/moodle2cc/cc/web_link.rb +53 -0
  40. data/lib/moodle2cc/cc/wiki.rb +72 -0
  41. data/lib/moodle2cc/cli.rb +14 -0
  42. data/lib/moodle2cc/error.rb +3 -0
  43. data/lib/moodle2cc/migrator.rb +23 -0
  44. data/lib/moodle2cc/moodle/backup.rb +38 -0
  45. data/lib/moodle2cc/moodle/course.rb +24 -0
  46. data/lib/moodle2cc/moodle/grade_item.rb +24 -0
  47. data/lib/moodle2cc/moodle/info.rb +8 -0
  48. data/lib/moodle2cc/moodle/mod.rb +127 -0
  49. data/lib/moodle2cc/moodle/question.rb +98 -0
  50. data/lib/moodle2cc/moodle/question_category.rb +17 -0
  51. data/lib/moodle2cc/moodle/section.rb +37 -0
  52. data/lib/moodle2cc/resource_factory.rb +32 -0
  53. data/lib/moodle2cc/version.rb +3 -0
  54. data/moodle2cc.gemspec +29 -0
  55. data/test/acceptance/migrator_test.rb +23 -0
  56. data/test/fixtures/cc.imscc +0 -0
  57. data/test/fixtures/moodle_backup/course_files/folder/test.txt +1 -0
  58. data/test/fixtures/moodle_backup/course_files/test.txt +1 -0
  59. data/test/fixtures/moodle_backup/moodle.xml +916 -0
  60. data/test/test_helper.rb +43 -0
  61. data/test/test_question_helper.rb +107 -0
  62. data/test/test_wiki_helper.rb +21 -0
  63. data/test/unit/canvas/assessment_test.rb +218 -0
  64. data/test/unit/canvas/assignment_test.rb +220 -0
  65. data/test/unit/canvas/converter_test.rb +159 -0
  66. data/test/unit/canvas/course_test.rb +176 -0
  67. data/test/unit/canvas/discussion_topic_test.rb +129 -0
  68. data/test/unit/canvas/label_test.rb +32 -0
  69. data/test/unit/canvas/question_bank_test.rb +73 -0
  70. data/test/unit/canvas/question_test.rb +824 -0
  71. data/test/unit/canvas/web_content_test.rb +37 -0
  72. data/test/unit/canvas/web_link_test.rb +48 -0
  73. data/test/unit/canvas/wiki_test.rb +74 -0
  74. data/test/unit/cc/assessment_test.rb +48 -0
  75. data/test/unit/cc/assignment_test.rb +67 -0
  76. data/test/unit/cc/cc_helper_test.rb +19 -0
  77. data/test/unit/cc/converter_test.rb +159 -0
  78. data/test/unit/cc/course_test.rb +36 -0
  79. data/test/unit/cc/discussion_topic_test.rb +83 -0
  80. data/test/unit/cc/label_test.rb +29 -0
  81. data/test/unit/cc/question_test.rb +49 -0
  82. data/test/unit/cc/web_content_test.rb +76 -0
  83. data/test/unit/cc/web_link_test.rb +79 -0
  84. data/test/unit/cc/wiki_test.rb +139 -0
  85. data/test/unit/migrator_test.rb +56 -0
  86. data/test/unit/moodle/backup_test.rb +30 -0
  87. data/test/unit/moodle/course_test.rb +57 -0
  88. data/test/unit/moodle/grade_item_test.rb +72 -0
  89. data/test/unit/moodle/info_test.rb +21 -0
  90. data/test/unit/moodle/mod_test.rb +301 -0
  91. data/test/unit/moodle/question_category_test.rb +34 -0
  92. data/test/unit/moodle/question_test.rb +185 -0
  93. data/test/unit/moodle/section_test.rb +75 -0
  94. data/test/unit/resource_factory_test.rb +119 -0
  95. metadata +342 -0
@@ -0,0 +1,54 @@
1
+ module Moodle2CC::Canvas
2
+ class QuestionBank
3
+ include Moodle2CC::CC::CCHelper
4
+ attr_accessor :id, :title, :identifier
5
+
6
+ def initialize(question_category)
7
+ @id = question_category.id
8
+ @title = question_category.name
9
+ @identifier = create_key(@id, 'objectbank_')
10
+ @question_category = question_category
11
+ end
12
+
13
+ def create_resource_node(resources_node)
14
+ href = File.join(ASSESSMENT_NON_CC_FOLDER, "#{identifier}.xml.qti")
15
+ resources_node.resource(
16
+ :href => href,
17
+ :type => LOR,
18
+ :identifier => identifier
19
+ ) do |resource_node|
20
+ resource_node.file(:href => href)
21
+ end
22
+ end
23
+
24
+ def create_files(export_dir)
25
+ create_qti_xml(export_dir)
26
+ end
27
+
28
+ def create_qti_xml(export_dir)
29
+ path = File.join(export_dir, ASSESSMENT_NON_CC_FOLDER, "#{identifier}.xml.qti")
30
+ FileUtils.mkdir_p(File.dirname(path))
31
+ File.open(path, 'w') do |file|
32
+ node = Builder::XmlMarkup.new(:target => file, :indent => 2)
33
+ node.instruct!
34
+ node.questestinterop(
35
+ 'xsi:schemaLocation' => "http://www.imsglobal.org/xsd/ims_qtiasiv1p2 http://www.imsglobal.org/xsd/ims_qtiasiv1p2p1.xsd",
36
+ 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
37
+ 'xmlns' => "http://www.imsglobal.org/xsd/ims_qtiasiv1p2"
38
+ ) do |root_node|
39
+ root_node.objectbank(:ident => identifier) do |objectbank_node|
40
+ objectbank_node.qtimetadata do |qtimetadata_node|
41
+ qtimetadata_node.qtimetadatafield do |qtimetadatafield_node|
42
+ qtimetadatafield_node.fieldlabel "bank_title"
43
+ qtimetadatafield_node.fieldentry @title
44
+ end
45
+ end
46
+ @question_category.questions.each do |question|
47
+ Question.new(question).create_item_xml(objectbank_node) if question
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,16 @@
1
+ module Moodle2CC::Canvas
2
+ module Resource
3
+ def create_module_meta_item_node(items_node, position)
4
+ items_node.item(:identifier => create_mod_key(@mod)) do |item_node|
5
+ item_node.title @title
6
+ item_node.position position
7
+ item_node.new_tab ''
8
+ item_node.indent @indent
9
+ create_module_meta_item_elements(item_node)
10
+ end
11
+ end
12
+
13
+ def create_module_meta_item_elements(item_node)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ <html>
2
+ <head>
3
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
4
+ <title>Assignment: <%= title %></title>
5
+ </head>
6
+ <body>
7
+ <%= body %>
8
+ </body>
9
+ </html>
@@ -0,0 +1,9 @@
1
+ <html>
2
+ <head>
3
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
4
+ <title>Syllabus</title>
5
+ </head>
6
+ <body>
7
+ <%= syllabus_body %>
8
+ </body>
9
+ </html>
@@ -0,0 +1,10 @@
1
+ <html>
2
+ <head>
3
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
4
+ <title><%= title %></title>
5
+ <meta name="identifier" content="<%= identifier %>"/>
6
+ </head>
7
+ <body>
8
+ <%= body %>
9
+ </body>
10
+ </html>
@@ -0,0 +1,10 @@
1
+ module Moodle2CC::Canvas
2
+ class WebContent < Moodle2CC::CC::WebContent
3
+ include Resource
4
+
5
+ def create_module_meta_item_elements(item_node)
6
+ item_node.content_type 'WikiPage'
7
+ item_node.identifierref @identifier
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ module Moodle2CC::Canvas
2
+ class WebLink < Moodle2CC::CC::WebLink
3
+ include Resource
4
+
5
+ def create_module_meta_item_elements(item_node)
6
+ item_node.identifierref @identifier
7
+
8
+ uri = URI.parse(@url)
9
+ if uri.scheme
10
+ item_node.url @url
11
+ item_node.content_type 'ExternalUrl'
12
+ else
13
+ item_node.content_type 'Attachment'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ module Moodle2CC::Canvas
2
+ class Wiki < Moodle2CC::CC::Wiki
3
+ include Resource
4
+ attr_accessor :pages
5
+
6
+ def initialize(mod)
7
+ super
8
+
9
+ @pages.map! do |page|
10
+ page.body.gsub!(/\[(.*?)\]/) do |match|
11
+ title_slug = file_slug(@title)
12
+ slug = [title_slug, file_slug(match)].join('-')
13
+ href = File.join(CGI.escape(WIKI_TOKEN), 'wiki', slug)
14
+ %(<a href="#{href}" title="#{$1}">#{$1}</a>)
15
+ end
16
+ page
17
+ end
18
+ end
19
+
20
+ def create_module_meta_item_elements(item_node)
21
+ item_node.content_type 'WikiPage'
22
+ item_node.identifierref @identifier
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Moodle2CC::CC
2
+ class Assessment
3
+ include CCHelper
4
+ include Resource
5
+
6
+ def initialize(mod, position=0)
7
+ super
8
+ end
9
+
10
+ def create_resource_node(resources_node)
11
+ resources_node.resource(
12
+ :identifier => identifier,
13
+ :type => ASSESSMENT_TYPE
14
+ ) do |resource_node|
15
+ create_resource_sub_nodes(resource_node)
16
+ end
17
+ end
18
+
19
+ def create_resource_sub_nodes(resource_node)
20
+ end
21
+
22
+ def create_files(export_dir)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,66 @@
1
+ module Moodle2CC::CC
2
+ class Assignment
3
+ include CCHelper
4
+ include Resource
5
+
6
+
7
+ attr_accessor :body
8
+
9
+ def initialize(mod, position=0)
10
+ super
11
+ @body = convert_file_path_tokens(mod.description)
12
+ end
13
+
14
+ def get_submission_types(mod)
15
+ if mod.mod_type == 'assignment'
16
+ case mod.assignment_type
17
+ when 'online'
18
+ 'online_text_entry'
19
+ when 'upload'
20
+ if mod.var2 == 1
21
+ 'online_upload,online_text_entry'
22
+ else
23
+ 'online_upload'
24
+ end
25
+ when 'uploadsingle'
26
+ 'online_upload'
27
+ else
28
+ 'none'
29
+ end
30
+ elsif mod.mod_type == 'workshop'
31
+ submission_types = ['online_text_entry']
32
+ submission_types.unshift('online_upload') if mod.number_of_attachments > 0
33
+ submission_types.join(',')
34
+ end
35
+ end
36
+
37
+ def create_resource_node(resources_node)
38
+ href = "#{identifier}/#{file_slug(@title)}.html"
39
+ resources_node.resource(
40
+ :href => href,
41
+ :type => LOR,
42
+ :identifier => identifier
43
+ ) do |resource_node|
44
+ resource_node.file(:href => href)
45
+ create_resource_sub_nodes(resource_node)
46
+ end
47
+ end
48
+
49
+ def create_resource_sub_nodes(resource_node)
50
+ end
51
+
52
+ def create_files(export_dir)
53
+ create_html(export_dir)
54
+ end
55
+
56
+ def create_html(export_dir)
57
+ template = File.expand_path('../templates/assignment.html.erb', __FILE__)
58
+ path = File.join(export_dir, identifier, "#{file_slug(title)}.html")
59
+ FileUtils.mkdir_p(File.dirname(path))
60
+ File.open(path, 'w') do |file|
61
+ erb = ERB.new(File.read(template))
62
+ file.write(erb.result(binding))
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,282 @@
1
+ require 'digest/md5'
2
+
3
+ module Moodle2CC::CC
4
+ module CCHelper
5
+
6
+ CANVAS_NAMESPACE = 'http://canvas.instructure.com/xsd/cccv1p0'
7
+ XSD_URI = 'http://canvas.instructure.com/xsd/cccv1p0.xsd'
8
+
9
+ # IMS formats/types
10
+ IMS_DATE = "%Y-%m-%d"
11
+ IMS_DATETIME = "%Y-%m-%dT%H:%M:%S"
12
+ CC_EXTENSION = 'imscc'
13
+ QTI_EXTENSION = ".xml.qti"
14
+ CANVAS_PLATFORM = 'canvas.instructure.com'
15
+
16
+ # Common Cartridge 1.0
17
+ # associatedcontent/imscc_xmlv1p0/learning-application-resource
18
+ # imsdt_xmlv1p0
19
+ # imswl_xmlv1p0
20
+ # imsqti_xmlv1p2/imscc_xmlv1p0/assessment
21
+ # imsqti_xmlv1p2/imscc_xmlv1p0/question-bank
22
+
23
+ # Common Cartridge 1.1 (What Canvas exports)
24
+ ASSESSMENT_TYPE = 'imsqti_xmlv1p2/imscc_xmlv1p1/assessment'
25
+ QUESTION_BANK = 'imsqti_xmlv1p2/imscc_xmlv1p1/question-bank'
26
+ DISCUSSION_TOPIC = "imsdt_xmlv1p1"
27
+ LOR = "associatedcontent/imscc_xmlv1p1/learning-application-resource"
28
+ WEB_LINK = "imswl_xmlv1p1"
29
+ WEBCONTENT = "webcontent"
30
+ BASIC_LTI = 'imsbasiclti_xmlv1p0'
31
+ BLTI_NAMESPACE = "http://www.imsglobal.org/xsd/imsbasiclti_v1p0"
32
+
33
+ # Common Cartridge 1.2
34
+ # associatedcontent/imscc_xmlv1p2/learning-application-resource
35
+ # imsdt_xmlv1p2
36
+ # imswl_xmlv1p2
37
+ # imsqti_xmlv1p2/imscc_xmlv1p2/assessment
38
+ # imsqti_xmlv1p2/imscc_xmlv1p2/question-bank
39
+ # imsbasiclti_xmlv1p0
40
+
41
+ # QTI-only export
42
+ QTI_ASSESSMENT_TYPE = 'imsqti_xmlv1p2'
43
+
44
+ # substitution tokens
45
+ OBJECT_TOKEN = "$CANVAS_OBJECT_REFERENCE$"
46
+ COURSE_TOKEN = "$CANVAS_COURSE_REFERENCE$"
47
+ WIKI_TOKEN = "$WIKI_REFERENCE$"
48
+ WEB_CONTENT_TOKEN = "$IMS_CC_FILEBASE$"
49
+ MOODLE_FILEBASE_TOKEN = "$@FILEPHP@$"
50
+ MOODLE_SLASH_TOKEN = "$@SLASH@$"
51
+
52
+ # file names/paths
53
+ ASSESSMENT_CC_QTI = "assessment_qti.xml"
54
+ ASSESSMENT_NON_CC_FOLDER = 'non_cc_assessments'
55
+ ASSESSMENT_META = "assessment_meta.xml"
56
+ ASSIGNMENT_GROUPS = "assignment_groups.xml"
57
+ ASSIGNMENT_SETTINGS = "assignment_settings.xml"
58
+ COURSE_SETTINGS = "course_settings.xml"
59
+ COURSE_SETTINGS_DIR = "course_settings"
60
+ EXTERNAL_FEEDS = "external_feeds.xml"
61
+ GRADING_STANDARDS = "grading_standards.xml"
62
+ EVENTS = "events.xml"
63
+ LEARNING_OUTCOMES = "learning_outcomes.xml"
64
+ MANIFEST = 'imsmanifest.xml'
65
+ MODULE_META = "module_meta.xml"
66
+ RUBRICS = "rubrics.xml"
67
+ EXTERNAL_TOOLS = "external_tools.xml"
68
+ FILES_META = "files_meta.xml"
69
+ SYLLABUS = "syllabus.html"
70
+ WEB_RESOURCES_FOLDER = 'web_resources'
71
+ WIKI_FOLDER = 'wiki_content'
72
+ MEDIA_OBJECTS_FOLDER = 'media_objects'
73
+
74
+ def create_key(object, prepend="")
75
+ CCHelper.create_key(object, prepend)
76
+ end
77
+
78
+ def create_mod_key(mod)
79
+ CCHelper.create_mod_key(mod)
80
+ end
81
+
82
+ def create_resource_key(mod)
83
+ CCHelper.create_resource_key(mod)
84
+ end
85
+
86
+ def ims_date(date=nil)
87
+ CCHelper.ims_date(date)
88
+ end
89
+
90
+ def ims_datetime(date=nil)
91
+ CCHelper.ims_datetime(date)
92
+ end
93
+
94
+ def file_slug(name)
95
+ CCHelper.file_slug(name)
96
+ end
97
+
98
+ def convert_file_path_tokens(content)
99
+ CCHelper.convert_file_path_tokens(content)
100
+ end
101
+
102
+ def self.create_key(object, prepend="")
103
+ key = object.to_s
104
+ "i" + Digest::MD5.hexdigest(prepend + key)
105
+ end
106
+
107
+ def self.create_mod_key(mod)
108
+ create_key("#{mod.mod_type}_#{mod.id}", 'mod_')
109
+ end
110
+
111
+ def self.create_resource_key(mod)
112
+ create_key("#{mod.mod_type}_#{mod.id}", 'resource_')
113
+ end
114
+
115
+ def self.ims_date(date=nil)
116
+ date ||= Time.now
117
+ date.respond_to?(:utc) ? date.utc.strftime(IMS_DATE) : date.strftime(IMS_DATE)
118
+ end
119
+
120
+ def self.ims_datetime(date=nil)
121
+ date ||= Time.now
122
+ date.respond_to?(:utc) ? date.utc.strftime(IMS_DATETIME) : date.strftime(IMS_DATETIME)
123
+ end
124
+
125
+ def self.file_slug(name)
126
+ slug = name.downcase.gsub(/\s/, '-').gsub(/[^a-z0-9\.\-]/, '').gsub(/\.*$/, '')
127
+ end
128
+
129
+ def self.convert_file_path_tokens(content)
130
+ content.gsub(MOODLE_FILEBASE_TOKEN, WEB_CONTENT_TOKEN).gsub(MOODLE_SLASH_TOKEN, '/')
131
+ end
132
+
133
+ def get_html_title_and_body_and_id(doc)
134
+ id = get_node_val(doc, 'html head meta[name=identifier] @content')
135
+ get_html_title_and_body(doc) << id
136
+ end
137
+
138
+ def get_html_title_and_body_and_meta_fields(doc)
139
+ meta_fields = {}
140
+ doc.css('html head meta').each do |meta_node|
141
+ if key = meta_node['name']
142
+ meta_fields[key] = meta_node['content']
143
+ end
144
+ end
145
+ get_html_title_and_body(doc) << meta_fields
146
+ end
147
+
148
+ def get_html_title_and_body(doc)
149
+ title = get_node_val(doc, 'html head title')
150
+ body = doc.at_css('html body').to_s.gsub(%r{</?body>}, '').strip
151
+ [title, body]
152
+ end
153
+
154
+ require 'set'
155
+ class HtmlContentExporter
156
+ attr_reader :used_media_objects, :media_object_flavor, :media_object_infos
157
+ attr_accessor :referenced_files
158
+
159
+ def initialize(course, user, opts = {})
160
+ @media_object_flavor = opts[:media_object_flavor]
161
+ @used_media_objects = Set.new
162
+ @media_object_infos = {}
163
+ @rewriter = UserContent::HtmlRewriter.new(course, user)
164
+ @course = course
165
+ @user = user
166
+ @track_referenced_files = opts[:track_referenced_files]
167
+ @for_course_copy = opts[:for_course_copy]
168
+ @referenced_files = {}
169
+
170
+ @rewriter.set_handler('file_contents') do |match|
171
+ if match.url =~ %r{/media_objects/(\d_\w+)}
172
+ # This is a media object referencing an attachment that it shouldn't be
173
+ "/media_objects/#{$1}"
174
+ else
175
+ match.url.gsub(/course( |%20)files/, WEB_CONTENT_TOKEN)
176
+ end
177
+ end
178
+ @rewriter.set_handler('files') do |match|
179
+ obj = match.obj_class.find_by_id(match.obj_id)
180
+ next(match.url) unless obj && @rewriter.user_can_view_content?(obj)
181
+ folder = obj.folder.full_name.gsub(/course( |%20)files/, WEB_CONTENT_TOKEN)
182
+ @referenced_files[obj.id] = CCHelper.create_key(obj) if @track_referenced_files && !@referenced_files[obj.id]
183
+ # for files, turn it into a relative link by path, rather than by file id
184
+ # we retain the file query string parameters
185
+ "#{folder}/#{URI.escape(obj.display_name)}#{CCHelper.file_query_string(match.rest)}"
186
+ end
187
+ @rewriter.set_handler('wiki') do |match|
188
+ "#{WIKI_TOKEN}/#{match.type}#{match.rest}"
189
+ end
190
+ @rewriter.set_default_handler do |match|
191
+ new_url = match.url
192
+ if match.obj_id
193
+ obj = match.obj_class.find_by_id(match.obj_id)
194
+ if obj && @rewriter.user_can_view_content?(obj)
195
+ # for all other types,
196
+ # create a migration id for the object, and use that as the new link
197
+ migration_id = CCHelper.create_key(obj)
198
+ new_url = "#{OBJECT_TOKEN}/#{match.type}/#{migration_id}"
199
+ end
200
+ else
201
+ new_url = "#{COURSE_TOKEN}/#{match.type}#{match.rest}"
202
+ end
203
+ new_url
204
+ end
205
+ end
206
+
207
+ attr_reader :course, :user
208
+
209
+ def html_page(html, title, meta_fields={})
210
+ content = html_content(html)
211
+ meta_html = ""
212
+ meta_fields.each_pair do |k, v|
213
+ next unless v.present?
214
+ meta_html += %{<meta name="#{k}" content="#{v}"/>\n}
215
+ end
216
+
217
+ %{<html>\n<head>\n<meta http-equiv="Content-Type" content="text/html; charset=utf-8">\n<title>#{title}</title>\n#{meta_html}</head>\n<body>\n#{content}\n</body>\n</html>}
218
+ end
219
+
220
+ def html_content(html)
221
+ html = @rewriter.translate_content(html)
222
+ return html if html.blank? || @for_course_copy
223
+
224
+ # keep track of found media comments, and translate them into links into the files tree
225
+ # if imported back into canvas, they'll get uploaded to the media server
226
+ # and translated back into media comments
227
+ doc = Nokogiri::HTML::DocumentFragment.parse(html)
228
+ doc.css('a.instructure_inline_media_comment').each do |anchor|
229
+ next unless anchor['id']
230
+ media_id = anchor['id'].gsub(/^media_comment_/, '')
231
+ obj = course.media_objects.by_media_id(media_id).first
232
+ if obj && obj.context == course && migration_id = CCHelper.create_key(obj)
233
+ @used_media_objects << obj
234
+ info = CCHelper.media_object_info(obj, nil, media_object_flavor)
235
+ @media_object_infos[obj.id] = info
236
+ anchor['href'] = File.join(WEB_CONTENT_TOKEN, MEDIA_OBJECTS_FOLDER, info[:filename])
237
+ end
238
+ end
239
+
240
+ return doc.to_s
241
+ end
242
+ end
243
+
244
+ def self.media_object_info(obj, client = nil, flavor = nil)
245
+ unless client
246
+ client = Kaltura::ClientV3.new
247
+ client.startSession(Kaltura::SessionType::ADMIN)
248
+ end
249
+ if flavor
250
+ assets = client.flavorAssetGetByEntryId(obj.media_id)
251
+ asset = assets.sort_by { |f| f[:size].to_i }.reverse.find { |f| f[:containerFormat] == flavor }
252
+ asset ||= assets.first
253
+ else
254
+ asset = client.flavorAssetGetOriginalAsset(obj.media_id)
255
+ end
256
+ # we use the media_id as the export filename, since it is guaranteed to
257
+ # be unique
258
+ filename = "#{obj.media_id}.#{asset[:fileExt]}" if asset
259
+ { :asset => asset, :filename => filename }
260
+ end
261
+
262
+ # sub_path is the last part of a file url: /courses/1/files/1(/download)
263
+ # we want to handle any sort of extra params to the file url, both in the
264
+ # path components and the query string
265
+ def self.file_query_string(sub_path)
266
+ return if sub_path.blank?
267
+ qs = []
268
+ uri = URI.parse(sub_path)
269
+ unless uri.path == "/preview" # defaults to preview, so no qs added
270
+ qs << "canvas_#{Rack::Utils.escape(uri.path[1..-1])}=1"
271
+ end
272
+
273
+ Rack::Utils.parse_query(uri.query).each do |k,v|
274
+ qs << "canvas_qs_#{Rack::Utils.escape(k)}=#{Rack::Utils.escape(v)}"
275
+ end
276
+
277
+ return nil if qs.blank?
278
+ "?#{qs.join("&")}"
279
+ end
280
+
281
+ end
282
+ end