canvas_cc 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 (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