canvas_cc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +1 -0
  7. data/canvas_cc.gemspec +37 -0
  8. data/lib/canvas_cc/canvas/assessment.rb +124 -0
  9. data/lib/canvas_cc/canvas/assignment.rb +98 -0
  10. data/lib/canvas_cc/canvas/converter.rb +30 -0
  11. data/lib/canvas_cc/canvas/course.rb +143 -0
  12. data/lib/canvas_cc/canvas/discussion_topic.rb +64 -0
  13. data/lib/canvas_cc/canvas/label.rb +8 -0
  14. data/lib/canvas_cc/canvas/question.rb +492 -0
  15. data/lib/canvas_cc/canvas/question_bank.rb +57 -0
  16. data/lib/canvas_cc/canvas/question_group.rb +35 -0
  17. data/lib/canvas_cc/canvas/resource.rb +16 -0
  18. data/lib/canvas_cc/canvas/templates/assignment.html.erb +9 -0
  19. data/lib/canvas_cc/canvas/templates/syllabus.html.erb +9 -0
  20. data/lib/canvas_cc/canvas/templates/wiki_content.html.erb +10 -0
  21. data/lib/canvas_cc/canvas/web_content.rb +15 -0
  22. data/lib/canvas_cc/canvas/web_link.rb +16 -0
  23. data/lib/canvas_cc/canvas/wiki.rb +26 -0
  24. data/lib/canvas_cc/canvas_cc/assessment_writer.rb +82 -0
  25. data/lib/canvas_cc/canvas_cc/assignment_group_writer.rb +48 -0
  26. data/lib/canvas_cc/canvas_cc/assignment_writer.rb +68 -0
  27. data/lib/canvas_cc/canvas_cc/calculated_question_writer.rb +72 -0
  28. data/lib/canvas_cc/canvas_cc/canvas_export_writer.rb +18 -0
  29. data/lib/canvas_cc/canvas_cc/cartridge_creator.rb +60 -0
  30. data/lib/canvas_cc/canvas_cc/course_setting_writer.rb +35 -0
  31. data/lib/canvas_cc/canvas_cc/discussion_writer.rb +52 -0
  32. data/lib/canvas_cc/canvas_cc/essay_question_writer.rb +19 -0
  33. data/lib/canvas_cc/canvas_cc/file_meta_writer.rb +38 -0
  34. data/lib/canvas_cc/canvas_cc/ims_manifest_generator.rb +123 -0
  35. data/lib/canvas_cc/canvas_cc/matching_question_writer.rb +43 -0
  36. data/lib/canvas_cc/canvas_cc/models/answer.rb +12 -0
  37. data/lib/canvas_cc/canvas_cc/models/assessment.rb +102 -0
  38. data/lib/canvas_cc/canvas_cc/models/assignment.rb +30 -0
  39. data/lib/canvas_cc/canvas_cc/models/assignment_group.rb +9 -0
  40. data/lib/canvas_cc/canvas_cc/models/calculated_question.rb +19 -0
  41. data/lib/canvas_cc/canvas_cc/models/canvas_file.rb +22 -0
  42. data/lib/canvas_cc/canvas_cc/models/canvas_module.rb +10 -0
  43. data/lib/canvas_cc/canvas_cc/models/course.rb +53 -0
  44. data/lib/canvas_cc/canvas_cc/models/discussion.rb +36 -0
  45. data/lib/canvas_cc/canvas_cc/models/discussion_topic.rb +5 -0
  46. data/lib/canvas_cc/canvas_cc/models/matching_question.rb +7 -0
  47. data/lib/canvas_cc/canvas_cc/models/module_item.rb +16 -0
  48. data/lib/canvas_cc/canvas_cc/models/multiple_dropdowns_question.rb +7 -0
  49. data/lib/canvas_cc/canvas_cc/models/numerical_question.rb +7 -0
  50. data/lib/canvas_cc/canvas_cc/models/page.rb +41 -0
  51. data/lib/canvas_cc/canvas_cc/models/question.rb +37 -0
  52. data/lib/canvas_cc/canvas_cc/models/question_bank.rb +42 -0
  53. data/lib/canvas_cc/canvas_cc/models/question_group.rb +10 -0
  54. data/lib/canvas_cc/canvas_cc/models/resource.rb +23 -0
  55. data/lib/canvas_cc/canvas_cc/models/web_content.rb +7 -0
  56. data/lib/canvas_cc/canvas_cc/models/web_link.rb +7 -0
  57. data/lib/canvas_cc/canvas_cc/models/workflow_state.rb +6 -0
  58. data/lib/canvas_cc/canvas_cc/module_meta_writer.rb +52 -0
  59. data/lib/canvas_cc/canvas_cc/multiple_answers_question_writer.rb +41 -0
  60. data/lib/canvas_cc/canvas_cc/multiple_blanks_question_writer.rb +51 -0
  61. data/lib/canvas_cc/canvas_cc/multiple_choice_question_writer.rb +48 -0
  62. data/lib/canvas_cc/canvas_cc/multiple_dropdowns_question_writer.rb +30 -0
  63. data/lib/canvas_cc/canvas_cc/numerical_question_writer.rb +40 -0
  64. data/lib/canvas_cc/canvas_cc/page_writer.rb +38 -0
  65. data/lib/canvas_cc/canvas_cc/question_bank_writer.rb +44 -0
  66. data/lib/canvas_cc/canvas_cc/question_group_writer.rb +23 -0
  67. data/lib/canvas_cc/canvas_cc/question_writer.rb +129 -0
  68. data/lib/canvas_cc/canvas_cc/short_answer_question_writer.rb +42 -0
  69. data/lib/canvas_cc/canvas_cc/text_only_question_writer.rb +19 -0
  70. data/lib/canvas_cc/canvas_cc/true_false_question_writer.rb +38 -0
  71. data/lib/canvas_cc/cc/assessment.rb +25 -0
  72. data/lib/canvas_cc/cc/assignment.rb +61 -0
  73. data/lib/canvas_cc/cc/cc_helper.rb +285 -0
  74. data/lib/canvas_cc/cc/converter.rb +137 -0
  75. data/lib/canvas_cc/cc/course.rb +16 -0
  76. data/lib/canvas_cc/cc/discussion_topic.rb +53 -0
  77. data/lib/canvas_cc/cc/label.rb +6 -0
  78. data/lib/canvas_cc/cc/question.rb +23 -0
  79. data/lib/canvas_cc/cc/resource.rb +30 -0
  80. data/lib/canvas_cc/cc/templates/assignment.html.erb +13 -0
  81. data/lib/canvas_cc/cc/templates/syllabus.html.erb +9 -0
  82. data/lib/canvas_cc/cc/templates/wiki_content.html.erb +10 -0
  83. data/lib/canvas_cc/cc/web_content.rb +48 -0
  84. data/lib/canvas_cc/cc/web_link.rb +75 -0
  85. data/lib/canvas_cc/cc/wiki.rb +73 -0
  86. data/lib/canvas_cc/version.rb +3 -0
  87. data/lib/canvas_cc.rb +94 -0
  88. data/spec/moodle2cc/canvas_cc/assessment_writer_spec.rb +95 -0
  89. data/spec/moodle2cc/canvas_cc/assignment_group_writer_spec.rb +51 -0
  90. data/spec/moodle2cc/canvas_cc/assignment_writer_spec.rb +74 -0
  91. data/spec/moodle2cc/canvas_cc/calculated_question_writer_spec.rb +88 -0
  92. data/spec/moodle2cc/canvas_cc/canvas_export_writer_spec.rb +26 -0
  93. data/spec/moodle2cc/canvas_cc/cartridge_creator_spec.rb +50 -0
  94. data/spec/moodle2cc/canvas_cc/course_setting_writer_spec.rb +47 -0
  95. data/spec/moodle2cc/canvas_cc/discussion_writer_spec.rb +38 -0
  96. data/spec/moodle2cc/canvas_cc/essay_question_writer_spec.rb +31 -0
  97. data/spec/moodle2cc/canvas_cc/file_meta_writer_spec.rb +57 -0
  98. data/spec/moodle2cc/canvas_cc/ims_manifest_generator_spec.rb +141 -0
  99. data/spec/moodle2cc/canvas_cc/matching_question_writer_spec.rb +51 -0
  100. data/spec/moodle2cc/canvas_cc/models/assessment_spec.rb +131 -0
  101. data/spec/moodle2cc/canvas_cc/models/assignment_group_spec.rb +9 -0
  102. data/spec/moodle2cc/canvas_cc/models/assignment_spec.rb +45 -0
  103. data/spec/moodle2cc/canvas_cc/models/calculated_question_spec.rb +25 -0
  104. data/spec/moodle2cc/canvas_cc/models/canvas_file_spec.rb +23 -0
  105. data/spec/moodle2cc/canvas_cc/models/canvas_module_spec.rb +16 -0
  106. data/spec/moodle2cc/canvas_cc/models/course_spec.rb +43 -0
  107. data/spec/moodle2cc/canvas_cc/models/discussion_spec.rb +47 -0
  108. data/spec/moodle2cc/canvas_cc/models/discussion_topic_spec.rb +7 -0
  109. data/spec/moodle2cc/canvas_cc/models/module_item_spec.rb +15 -0
  110. data/spec/moodle2cc/canvas_cc/models/page_spec.rb +34 -0
  111. data/spec/moodle2cc/canvas_cc/models/question_spec.rb +8 -0
  112. data/spec/moodle2cc/canvas_cc/models/resource_spec.rb +17 -0
  113. data/spec/moodle2cc/canvas_cc/models/web_content_spec.rb +7 -0
  114. data/spec/moodle2cc/canvas_cc/models/web_link_spec.rb +9 -0
  115. data/spec/moodle2cc/canvas_cc/module_meta_writer_spec.rb +118 -0
  116. data/spec/moodle2cc/canvas_cc/multiple_answers_question_writer_spec.rb +50 -0
  117. data/spec/moodle2cc/canvas_cc/multiple_blanks_question_writer_spec.rb +77 -0
  118. data/spec/moodle2cc/canvas_cc/multiple_choice_question_writer_spec.rb +69 -0
  119. data/spec/moodle2cc/canvas_cc/multiple_dropdowns_question_writer_spec.rb +46 -0
  120. data/spec/moodle2cc/canvas_cc/numerical_question_writer_spec.rb +69 -0
  121. data/spec/moodle2cc/canvas_cc/page_writer_spec.rb +29 -0
  122. data/spec/moodle2cc/canvas_cc/question_bank_writer_spec.rb +43 -0
  123. data/spec/moodle2cc/canvas_cc/question_writer_spec.rb +129 -0
  124. data/spec/moodle2cc/canvas_cc/short_answer_question_writer_spec.rb +50 -0
  125. data/spec/moodle2cc/canvas_cc/text_only_question_writer_spec.rb +23 -0
  126. data/spec/moodle2cc/canvas_cc/true_false_question_writer_spec.rb +62 -0
  127. data/spec/spec_helper.rb +12 -0
  128. data/spec/support/shared_examples_for_models.rb +26 -0
  129. data/test/acceptance/migrator_test.rb +24 -0
  130. data/test/fixtures/cc.imscc +0 -0
  131. data/test/fixtures/common_cartridge/ims_manifest_simple.xml +45 -0
  132. data/test/fixtures/common_cartridge/schema/cccv1p0.xsd +563 -0
  133. data/test/fixtures/common_cartridge/schema/ccv1p1_imsccauth_v1p1.xsd +203 -0
  134. data/test/fixtures/common_cartridge/schema/ccv1p1_imscp_v1p2_v1p0.xsd +827 -0
  135. data/test/fixtures/common_cartridge/schema/ccv1p1_lommanifest_v1p0.xsd +822 -0
  136. data/test/fixtures/common_cartridge/schema/ccv1p1_lomresource_v1p0.xsd +273 -0
  137. data/test/fixtures/common_cartridge/schema/xml.xsd +286 -0
  138. data/test/test_helper.rb +71 -0
  139. data/test/test_question_helper.rb +122 -0
  140. data/test/test_wiki_helper.rb +21 -0
  141. data/test/unit/canvas/assessment_test.rb +261 -0
  142. data/test/unit/canvas/assignment_test.rb +220 -0
  143. data/test/unit/canvas/converter_test.rb +171 -0
  144. data/test/unit/canvas/course_test.rb +176 -0
  145. data/test/unit/canvas/discussion_topic_test.rb +129 -0
  146. data/test/unit/canvas/label_test.rb +32 -0
  147. data/test/unit/canvas/question_bank_test.rb +92 -0
  148. data/test/unit/canvas/question_group_test.rb +90 -0
  149. data/test/unit/canvas/question_test.rb +876 -0
  150. data/test/unit/canvas/web_content_test.rb +46 -0
  151. data/test/unit/canvas/web_link_test.rb +48 -0
  152. data/test/unit/canvas/wiki_test.rb +74 -0
  153. data/test/unit/cc/assessment_test.rb +48 -0
  154. data/test/unit/cc/assignment_test.rb +72 -0
  155. data/test/unit/cc/cc_helper_test.rb +19 -0
  156. data/test/unit/cc/converter_test.rb +173 -0
  157. data/test/unit/cc/course_test.rb +36 -0
  158. data/test/unit/cc/discussion_topic_test.rb +83 -0
  159. data/test/unit/cc/label_test.rb +29 -0
  160. data/test/unit/cc/question_test.rb +54 -0
  161. data/test/unit/cc/web_content_test.rb +76 -0
  162. data/test/unit/cc/web_link_test.rb +159 -0
  163. data/test/unit/cc/wiki_test.rb +139 -0
  164. data/test/unit/logger_test.rb +33 -0
  165. data/test/unit/migrator_test.rb +67 -0
  166. data/test/unit/resource_factory_test.rb +216 -0
  167. metadata +499 -0
@@ -0,0 +1,285 @@
1
+ require 'digest/md5'
2
+
3
+ module CanvasCc::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
+ CC_ASSIGNMENT_FOLDER = File.join(WEB_RESOURCES_FOLDER, "assignments")
75
+ CC_WIKI_FOLDER = File.join(WEB_RESOURCES_FOLDER, "pages")
76
+
77
+ def create_key(object, prepend="")
78
+ CCHelper.create_key(object, prepend)
79
+ end
80
+
81
+ def create_mod_key(mod)
82
+ CCHelper.create_mod_key(mod)
83
+ end
84
+
85
+ def create_resource_key(mod)
86
+ CCHelper.create_resource_key(mod)
87
+ end
88
+
89
+ def ims_date(date=nil)
90
+ CCHelper.ims_date(date)
91
+ end
92
+
93
+ def ims_datetime(date=nil)
94
+ CCHelper.ims_datetime(date)
95
+ end
96
+
97
+ def file_slug(name)
98
+ CCHelper.file_slug(name)
99
+ end
100
+
101
+ def convert_file_path_tokens(content)
102
+ CCHelper.convert_file_path_tokens(content)
103
+ end
104
+
105
+ def self.create_key(object, prepend="")
106
+ key = object.to_s
107
+ "i" + Digest::MD5.hexdigest(prepend + key)
108
+ end
109
+
110
+ def self.create_mod_key(mod)
111
+ create_key("#{mod.mod_type}_#{mod.id}", 'mod_')
112
+ end
113
+
114
+ def self.create_resource_key(mod)
115
+ create_key("#{mod.mod_type}_#{mod.id}", 'resource_')
116
+ end
117
+
118
+ def self.ims_date(date=nil)
119
+ date ||= Time.now
120
+ date.respond_to?(:utc) ? date.utc.strftime(IMS_DATE) : date.strftime(IMS_DATE)
121
+ end
122
+
123
+ def self.ims_datetime(date=nil)
124
+ date ||= Time.now
125
+ date.respond_to?(:utc) ? date.utc.strftime(IMS_DATETIME) : date.strftime(IMS_DATETIME)
126
+ end
127
+
128
+ def self.file_slug(name)
129
+ slug = name.downcase.gsub(/\s/, '-').gsub(/[^a-z0-9\.\-]/, '').gsub(/\.*$/, '')
130
+ end
131
+
132
+ def self.convert_file_path_tokens(content)
133
+ content.gsub(MOODLE_FILEBASE_TOKEN, WEB_CONTENT_TOKEN).gsub(MOODLE_SLASH_TOKEN, '/')
134
+ end
135
+
136
+ def get_html_title_and_body_and_id(doc)
137
+ id = get_node_val(doc, 'html head meta[name=identifier] @content')
138
+ get_html_title_and_body(doc) << id
139
+ end
140
+
141
+ def get_html_title_and_body_and_meta_fields(doc)
142
+ meta_fields = {}
143
+ doc.css('html head meta').each do |meta_node|
144
+ if key = meta_node['name']
145
+ meta_fields[key] = meta_node['content']
146
+ end
147
+ end
148
+ get_html_title_and_body(doc) << meta_fields
149
+ end
150
+
151
+ def get_html_title_and_body(doc)
152
+ title = get_node_val(doc, 'html head title')
153
+ body = doc.at_css('html body').to_s.gsub(%r{</?body>}, '').strip
154
+ [title, body]
155
+ end
156
+
157
+ require 'set'
158
+ class HtmlContentExporter
159
+ attr_reader :used_media_objects, :media_object_flavor, :media_object_infos
160
+ attr_accessor :referenced_files
161
+
162
+ def initialize(course, user, opts = {})
163
+ @media_object_flavor = opts[:media_object_flavor]
164
+ @used_media_objects = Set.new
165
+ @media_object_infos = {}
166
+ @rewriter = UserContent::HtmlRewriter.new(course, user)
167
+ @course = course
168
+ @user = user
169
+ @track_referenced_files = opts[:track_referenced_files]
170
+ @for_course_copy = opts[:for_course_copy]
171
+ @referenced_files = {}
172
+
173
+ @rewriter.set_handler('file_contents') do |match|
174
+ if match.url =~ %r{/media_objects/(\d_\w+)}
175
+ # This is a media object referencing an attachment that it shouldn't be
176
+ "/media_objects/#{$1}"
177
+ else
178
+ match.url.gsub(/course( |%20)files/, WEB_CONTENT_TOKEN)
179
+ end
180
+ end
181
+ @rewriter.set_handler('files') do |match|
182
+ obj = match.obj_class.find_by_id(match.obj_id)
183
+ next(match.url) unless obj && @rewriter.user_can_view_content?(obj)
184
+ folder = obj.folder.full_name.gsub(/course( |%20)files/, WEB_CONTENT_TOKEN)
185
+ @referenced_files[obj.id] = CCHelper.create_key(obj) if @track_referenced_files && !@referenced_files[obj.id]
186
+ # for files, turn it into a relative link by path, rather than by file id
187
+ # we retain the file query string parameters
188
+ "#{folder}/#{URI.escape(obj.display_name)}#{CCHelper.file_query_string(match.rest)}"
189
+ end
190
+ @rewriter.set_handler('wiki') do |match|
191
+ "#{WIKI_TOKEN}/#{match.type}#{match.rest}"
192
+ end
193
+ @rewriter.set_default_handler do |match|
194
+ new_url = match.url
195
+ if match.obj_id
196
+ obj = match.obj_class.find_by_id(match.obj_id)
197
+ if obj && @rewriter.user_can_view_content?(obj)
198
+ # for all other types,
199
+ # create a migration id for the object, and use that as the new link
200
+ migration_id = CCHelper.create_key(obj)
201
+ new_url = "#{OBJECT_TOKEN}/#{match.type}/#{migration_id}"
202
+ end
203
+ else
204
+ new_url = "#{COURSE_TOKEN}/#{match.type}#{match.rest}"
205
+ end
206
+ new_url
207
+ end
208
+ end
209
+
210
+ attr_reader :course, :user
211
+
212
+ def html_page(html, title, meta_fields={})
213
+ content = html_content(html)
214
+ meta_html = ""
215
+ meta_fields.each_pair do |k, v|
216
+ next unless v.present?
217
+ meta_html += %{<meta name="#{k}" content="#{v}"/>\n}
218
+ end
219
+
220
+ %{<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>}
221
+ end
222
+
223
+ def html_content(html)
224
+ html = @rewriter.translate_content(html)
225
+ return html if html.blank? || @for_course_copy
226
+
227
+ # keep track of found media comments, and translate them into links into the files tree
228
+ # if imported back into canvas, they'll get uploaded to the media server
229
+ # and translated back into media comments
230
+ doc = Nokogiri::HTML::DocumentFragment.parse(html)
231
+ doc.css('a.instructure_inline_media_comment').each do |anchor|
232
+ next unless anchor['id']
233
+ media_id = anchor['id'].gsub(/^media_comment_/, '')
234
+ obj = course.media_objects.by_media_id(media_id).first
235
+ if obj && obj.context == course && migration_id = CCHelper.create_key(obj)
236
+ @used_media_objects << obj
237
+ info = CCHelper.media_object_info(obj, nil, media_object_flavor)
238
+ @media_object_infos[obj.id] = info
239
+ anchor['href'] = File.join(WEB_CONTENT_TOKEN, MEDIA_OBJECTS_FOLDER, info[:filename])
240
+ end
241
+ end
242
+
243
+ return doc.to_s
244
+ end
245
+ end
246
+
247
+ def self.media_object_info(obj, client = nil, flavor = nil)
248
+ unless client
249
+ client = Kaltura::ClientV3.new
250
+ client.startSession(Kaltura::SessionType::ADMIN)
251
+ end
252
+ if flavor
253
+ assets = client.flavorAssetGetByEntryId(obj.media_id)
254
+ asset = assets.sort_by { |f| f[:size].to_i }.reverse.find { |f| f[:containerFormat] == flavor }
255
+ asset ||= assets.first
256
+ else
257
+ asset = client.flavorAssetGetOriginalAsset(obj.media_id)
258
+ end
259
+ # we use the media_id as the export filename, since it is guaranteed to
260
+ # be unique
261
+ filename = "#{obj.media_id}.#{asset[:fileExt]}" if asset
262
+ { :asset => asset, :filename => filename }
263
+ end
264
+
265
+ # sub_path is the last part of a file url: /courses/1/files/1(/download)
266
+ # we want to handle any sort of extra params to the file url, both in the
267
+ # path components and the query string
268
+ def self.file_query_string(sub_path)
269
+ return if sub_path.blank?
270
+ qs = []
271
+ uri = URI.parse(sub_path)
272
+ unless uri.path == "/preview" # defaults to preview, so no qs added
273
+ qs << "canvas_#{Rack::Utils.escape(uri.path[1..-1])}=1"
274
+ end
275
+
276
+ Rack::Utils.parse_query(uri.query).each do |k,v|
277
+ qs << "canvas_qs_#{Rack::Utils.escape(k)}=#{Rack::Utils.escape(v)}"
278
+ end
279
+
280
+ return nil if qs.blank?
281
+ "?#{qs.join("&")}"
282
+ end
283
+
284
+ end
285
+ end
@@ -0,0 +1,137 @@
1
+ module CanvasCc::CC
2
+ class Converter
3
+ include CCHelper
4
+
5
+ def initialize(moodle_backup, destination_dir)
6
+ @resource_factory = CanvasCc::ResourceFactory.new CanvasCc::CC
7
+ @moodle_backup = moodle_backup
8
+ @export_dir = Dir.mktmpdir
9
+ @destination_dir = destination_dir
10
+ end
11
+
12
+ def imscc_file_name
13
+ "#{file_slug(@moodle_backup.course.fullname)}.imscc"
14
+ end
15
+
16
+ def imscc_path
17
+ @imscc_path ||= File.join(@destination_dir, imscc_file_name)
18
+ end
19
+
20
+ def imscc_tmp_path
21
+ @imscc_tmp_path ||= File.join(@export_dir, imscc_file_name)
22
+ end
23
+
24
+ def convert
25
+ File.open(File.join(@export_dir, MANIFEST), 'w') do |file|
26
+ @document = Builder::XmlMarkup.new(:target => file, :indent => 2)
27
+ @document.instruct!
28
+ create_manifest
29
+ end
30
+ create_web_resources
31
+ Zip::File.open(imscc_tmp_path, Zip::File::CREATE) do |zipfile|
32
+ Dir["#{@export_dir}/**/*"].each do |file|
33
+ zipfile.add(file.sub(@export_dir + '/', ''), file)
34
+ end
35
+ end
36
+ FileUtils.mv imscc_tmp_path, imscc_path
37
+ FileUtils.rm_r @export_dir
38
+ end
39
+
40
+ def create_manifest
41
+ @document.manifest(
42
+ "identifier" => create_key(@moodle_backup.course.id, "common_cartridge_"),
43
+ "xmlns" => "http://www.imsglobal.org/xsd/imsccv1p1/imscp_v1p1",
44
+ "xmlns:lom" => "http://ltsc.ieee.org/xsd/imsccv1p1/LOM/resource",
45
+ "xmlns:lomimscc" => "http://ltsc.ieee.org/xsd/imsccv1p1/LOM/manifest",
46
+ "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
47
+ "xsi:schemaLocation" => "http://www.imsglobal.org/xsd/imsccv1p1/imscp_v1p1 http://www.imsglobal.org/profile/cc/ccv1p1/ccv1p1_imscp_v1p2_v1p0.xsd http://ltsc.ieee.org/xsd/imsccv1p1/LOM/resource http://www.imsglobal.org/profile/cc/ccv1p1/LOM/ccv1p1_lomresource_v1p0.xsd http://ltsc.ieee.org/xsd/imsccv1p1/LOM/manifest http://www.imsglobal.org/profile/cc/ccv1p1/LOM/ccv1p1_lommanifest_v1p0.xsd"
48
+ ) do |manifest_node|
49
+ @manifest_node = manifest_node
50
+ @manifest_node.metadata do |md|
51
+ md.schema "IMS Common Cartridge"
52
+ md.schemaversion "1.1.0"
53
+ md.lomimscc :lom do |lom|
54
+ lom.lomimscc :general do |general|
55
+ general.lomimscc :title do |title|
56
+ title.lomimscc :string, @moodle_backup.course.fullname
57
+ end
58
+ end
59
+ lom.lomimscc :lifeCycle do |lifecycle|
60
+ lifecycle.lomimscc :contribute do |contribute|
61
+ contribute.lomimscc :date do |date|
62
+ date.lomimscc :dateTime, ims_date
63
+ end
64
+ end
65
+ end
66
+ lom.lomimscc :rights do |rights|
67
+ rights.lomimscc :copyrightAndOtherRestrictions do |node|
68
+ node.lomimscc :value, 'yes'
69
+ end
70
+ rights.lomimscc :description do |desc|
71
+ desc.lomimscc :string, 'Private (Copyrighted) - http://en.wikipedia.org/wiki/Copyright'
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ create_organizations
78
+
79
+ @manifest_node.resources do |resources_node|
80
+ create_resources(resources_node)
81
+ end
82
+ end
83
+ end
84
+
85
+ def create_organizations
86
+ course = Course.new(@moodle_backup.course)
87
+ @manifest_node.organizations do |orgs|
88
+ orgs.organization(
89
+ :identifier => 'org_1',
90
+ :structure => 'rooted-hierarchy'
91
+ ) do |org|
92
+ org.item(:identifier => "LearningModules") do |root_item|
93
+ @moodle_backup.course.sections.each do |section|
94
+ next if section.mods.length == 0
95
+ root_item.item(:identifier => create_key(section.id, "section_")) do |item_node|
96
+ item_node.title "#{course.format} #{section.number}"
97
+ section.mods.each do |mod|
98
+ resource = @resource_factory.get_resource_from_mod(mod.instance)
99
+ resource.create_organization_item_node(item_node) if resource
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ def create_resources(resources_node)
109
+ resources = []
110
+ @moodle_backup.course.mods.each_with_index do |mod, index|
111
+ resources << @resource_factory.get_resource_from_mod(mod, index)
112
+ end
113
+ @moodle_backup.files.each do |file|
114
+ unless resources.find { |r| r.respond_to?(:url) && r.url == file }
115
+ mod = CanvasCc::Moodle::Mod.new
116
+ mod.mod_type = 'resource'
117
+ mod.type = 'file'
118
+ mod.reference = file
119
+ mod.course = @moodle_backup.course
120
+ resources << @resource_factory.get_resource_from_mod(mod)
121
+ end
122
+ end
123
+
124
+ resources.compact.each do |resource|
125
+ resource.create_resource_node(resources_node)
126
+ resource.create_files(@export_dir)
127
+ end
128
+ end
129
+
130
+ def create_web_resources
131
+ web_resources_folder = File.join(@export_dir, WEB_RESOURCES_FOLDER)
132
+ FileUtils.mkdir_p(web_resources_folder)
133
+ @moodle_backup.copy_files_to(web_resources_folder)
134
+ end
135
+
136
+ end
137
+ end
@@ -0,0 +1,16 @@
1
+ module CanvasCc::CC
2
+ class Course
3
+ include CCHelper
4
+
5
+ attr_accessor :format
6
+
7
+ def initialize(course)
8
+ @format = case course.format
9
+ when 'weeks', 'weekscss'
10
+ 'Week'
11
+ else
12
+ 'Topic'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,53 @@
1
+ module Moodle2CC
2
+ module CC
3
+ class DiscussionTopic
4
+ include CCHelper
5
+ include Resource
6
+
7
+ attr_accessor :text
8
+
9
+ def initialize(mod, position=0)
10
+ super
11
+ @text = convert_file_path_tokens(mod.intro)
12
+ end
13
+
14
+ def identifierref
15
+ create_key(id, 'topic_meta_')
16
+ end
17
+
18
+ def create_resource_node(resources_node)
19
+ resources_node.resource(
20
+ :type => DISCUSSION_TOPIC,
21
+ :identifier => identifier
22
+ ) do |resource_node|
23
+ resource_node.file(:href => "#{identifier}.xml")
24
+ create_resource_sub_nodes(resource_node)
25
+ end
26
+ end
27
+
28
+ def create_resource_sub_nodes(resource_node)
29
+ end
30
+
31
+ def create_files(export_dir)
32
+ create_topic_xml(export_dir)
33
+ end
34
+
35
+ def create_topic_xml(export_dir)
36
+ path = File.join(export_dir, "#{identifier}.xml")
37
+ File.open(path, 'w') do |file|
38
+ document = Builder::XmlMarkup.new(:target => file, :indent => 2)
39
+ document.instruct!
40
+ document.topicMeta(
41
+ :identifier => identifierref,
42
+ 'xsi:schemaLocation' => "http://www.imsglobal.org/xsd/imsccv1p1/imsdt_v1p1 http://www.imsglobal.org/profile/cc/ccv1p1/ccv1p1_imsdt_v1p1.xsd",
43
+ 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
44
+ 'xmlns' => "http://www.imsglobal.org/xsd/imsccv1p1/imsdt_v1p1"
45
+ ) do |topic|
46
+ topic.title @title
47
+ topic.text @text, :texttype => 'text/html'
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,6 @@
1
+ module CanvasCc::CC
2
+ class Label
3
+ include CCHelper
4
+ include Resource
5
+ end
6
+ end
@@ -0,0 +1,23 @@
1
+ module CanvasCc::CC
2
+ class Question
3
+ include CCHelper
4
+
5
+ attr_accessor :id, :title, :identifier
6
+
7
+ def initialize(question, assessment=nil)
8
+ @id = question.id
9
+ @title = CGI.unescapeHTML((question.name || '').gsub('$@NULL@$', ''))
10
+ @identifier_prefix = "#{assessment.mod.mod_type}_" if assessment
11
+
12
+ if question.instance_id
13
+ @identifier = create_key(question.instance_id, "#{@identifier_prefix}question_instance_")
14
+ else
15
+ @identifier = create_key(question.id, "#{@identifier_prefix}question_")
16
+ end
17
+ end
18
+
19
+ def create_item_xml(section_node)
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ module CanvasCc::CC
2
+ module Resource
3
+ def self.included(klass)
4
+ klass.class_eval do
5
+ attr_accessor :mod, :identifier, :id, :title, :indent
6
+ end
7
+ end
8
+
9
+ def initialize(mod, *args)
10
+ @mod = mod
11
+ @id = mod.id
12
+ @title = mod.name
13
+ @indent = mod.section_mod.nil? ? 0 : mod.section_mod.indent
14
+ @identifier = create_resource_key(mod)
15
+ end
16
+
17
+ def create_files(export_dir)
18
+ end
19
+
20
+ def create_resource_node(resources_node)
21
+ end
22
+
23
+ def create_organization_item_node(item_node)
24
+ item_node.item(:identifier => create_mod_key(@mod), :identifierref => @identifier) do |sub_item|
25
+ sub_item.title @title
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ <html>
2
+ <head>
3
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
4
+ <% if meta_fields
5
+ meta_fields.each_pair do |key, val|%>
6
+ <meta name="<%= key %>" content="<%= val %>" /> <% end %>
7
+ <% end %>
8
+ <title>Assignment: <%= title %></title>
9
+ </head>
10
+ <body>
11
+ <%= body %>
12
+ </body>
13
+ </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,48 @@
1
+ module CanvasCc::CC
2
+ class WebContent
3
+ include CCHelper
4
+ include Resource
5
+
6
+ attr_accessor :body
7
+
8
+ def initialize(mod)
9
+ super
10
+ @rel_path = File.join(CC_WIKI_FOLDER, "#{file_slug(@title)}.html")
11
+ body = mod.alltext
12
+ body = mod.content || '' if body.nil? || body.length == 0
13
+ if body.nil? || body.length == 0 && mod.summary
14
+ body = mod.summary
15
+ url = mod.reference.to_s.strip
16
+ unless url.gsub(%r{http(s)?://}, '').length == 0
17
+ body += %(<p><a href="#{CGI.escapeHTML(url)}" title="#{CGI.escapeHTML(@title)}">#{@title}</a></p>)
18
+ end
19
+ end
20
+ @body = convert_file_path_tokens(body)
21
+ end
22
+
23
+ def create_resource_node(resources_node)
24
+ href = @rel_path
25
+ resources_node.resource(
26
+ :type => WEBCONTENT,
27
+ :identifier => identifier,
28
+ :href => href
29
+ ) do |resource_node|
30
+ resource_node.file(:href => href)
31
+ end
32
+ end
33
+
34
+ def create_files(export_dir)
35
+ create_html(export_dir)
36
+ end
37
+
38
+ def create_html(export_dir)
39
+ template = File.expand_path('../templates/wiki_content.html.erb', __FILE__)
40
+ path = File.join(export_dir, @rel_path)
41
+ FileUtils.mkdir_p(File.dirname(path))
42
+ File.open(path, 'w') do |file|
43
+ erb = ERB.new(File.read(template))
44
+ file.write(erb.result(binding))
45
+ end
46
+ end
47
+ end
48
+ end