qalam_ims_lti 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/Changelog.txt +0 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +39 -0
  5. data/lib/ims/lis/context_type/handles.rb +10 -0
  6. data/lib/ims/lis/context_type/urns.rb +10 -0
  7. data/lib/ims/lis/roles/context/handles.rb +60 -0
  8. data/lib/ims/lis/roles/context/urns.rb +60 -0
  9. data/lib/ims/lis/roles/institution/handles.rb +22 -0
  10. data/lib/ims/lis/roles/institution/urns.rb +22 -0
  11. data/lib/ims/lis/roles/system/handles.rb +15 -0
  12. data/lib/ims/lis/roles/system/urns.rb +15 -0
  13. data/lib/ims/lis/statuses/simple_names.rb +9 -0
  14. data/lib/ims/lis/statuses/uris.rb +9 -0
  15. data/lib/ims/lis.rb +14 -0
  16. data/lib/ims/lti/converters/time_json_converter.rb +13 -0
  17. data/lib/ims/lti/converters.rb +5 -0
  18. data/lib/ims/lti/deprecated_role_checks.rb +52 -0
  19. data/lib/ims/lti/errors/authentication_failed_error.rb +11 -0
  20. data/lib/ims/lti/errors/invalid_lti_config_error.rb +4 -0
  21. data/lib/ims/lti/errors/invalid_tool_consumer_profile.rb +4 -0
  22. data/lib/ims/lti/errors/tool_proxy_registration_error.rb +15 -0
  23. data/lib/ims/lti/errors.rb +8 -0
  24. data/lib/ims/lti/extensions/canvas.rb +125 -0
  25. data/lib/ims/lti/extensions/content.rb +209 -0
  26. data/lib/ims/lti/extensions/outcome_data.rb +216 -0
  27. data/lib/ims/lti/extensions.rb +45 -0
  28. data/lib/ims/lti/launch_params.rb +166 -0
  29. data/lib/ims/lti/models/base_url_choice.rb +15 -0
  30. data/lib/ims/lti/models/base_url_selector.rb +5 -0
  31. data/lib/ims/lti/models/contact.rb +5 -0
  32. data/lib/ims/lti/models/content_item_container.rb +14 -0
  33. data/lib/ims/lti/models/content_item_placement.rb +20 -0
  34. data/lib/ims/lti/models/content_items/content_item.rb +32 -0
  35. data/lib/ims/lti/models/content_items/file_item.rb +15 -0
  36. data/lib/ims/lti/models/content_items/lti_link_item.rb +14 -0
  37. data/lib/ims/lti/models/content_items.rb +7 -0
  38. data/lib/ims/lti/models/icon_endpoint.rb +5 -0
  39. data/lib/ims/lti/models/icon_info.rb +6 -0
  40. data/lib/ims/lti/models/image.rb +7 -0
  41. data/lib/ims/lti/models/localized_name.rb +12 -0
  42. data/lib/ims/lti/models/localized_text.rb +12 -0
  43. data/lib/ims/lti/models/lti_model.rb +227 -0
  44. data/lib/ims/lti/models/membership_service/agent.rb +11 -0
  45. data/lib/ims/lti/models/membership_service/container.rb +11 -0
  46. data/lib/ims/lti/models/membership_service/context.rb +11 -0
  47. data/lib/ims/lti/models/membership_service/lis_membership_container.rb +13 -0
  48. data/lib/ims/lti/models/membership_service/lis_person.rb +14 -0
  49. data/lib/ims/lti/models/membership_service/membership.rb +14 -0
  50. data/lib/ims/lti/models/membership_service/organization.rb +14 -0
  51. data/lib/ims/lti/models/membership_service/page.rb +16 -0
  52. data/lib/ims/lti/models/membership_service/person.rb +13 -0
  53. data/lib/ims/lti/models/membership_service.rb +16 -0
  54. data/lib/ims/lti/models/message_handler.rb +14 -0
  55. data/lib/ims/lti/models/messages/basic_lti_launch_request.rb +24 -0
  56. data/lib/ims/lti/models/messages/content_item_selection.rb +32 -0
  57. data/lib/ims/lti/models/messages/content_item_selection_request.rb +26 -0
  58. data/lib/ims/lti/models/messages/message.rb +222 -0
  59. data/lib/ims/lti/models/messages/registration_request.rb +20 -0
  60. data/lib/ims/lti/models/messages/request_message.rb +12 -0
  61. data/lib/ims/lti/models/messages/tool_proxy_update_request.rb +15 -0
  62. data/lib/ims/lti/models/messages.rb +11 -0
  63. data/lib/ims/lti/models/parameter.rb +28 -0
  64. data/lib/ims/lti/models/product_family.rb +8 -0
  65. data/lib/ims/lti/models/product_info.rb +26 -0
  66. data/lib/ims/lti/models/product_instance.rb +10 -0
  67. data/lib/ims/lti/models/resource_handler.rb +22 -0
  68. data/lib/ims/lti/models/resource_type.rb +6 -0
  69. data/lib/ims/lti/models/rest_service.rb +30 -0
  70. data/lib/ims/lti/models/rest_service_profile.rb +15 -0
  71. data/lib/ims/lti/models/security_contract.rb +21 -0
  72. data/lib/ims/lti/models/security_profile.rb +10 -0
  73. data/lib/ims/lti/models/serializable.rb +12 -0
  74. data/lib/ims/lti/models/service_owner.rb +26 -0
  75. data/lib/ims/lti/models/service_provider.rb +11 -0
  76. data/lib/ims/lti/models/tool_consumer_profile.rb +45 -0
  77. data/lib/ims/lti/models/tool_profile.rb +35 -0
  78. data/lib/ims/lti/models/tool_proxy.rb +21 -0
  79. data/lib/ims/lti/models/tool_setting.rb +12 -0
  80. data/lib/ims/lti/models/tool_setting_container.rb +14 -0
  81. data/lib/ims/lti/models/vendor.rb +28 -0
  82. data/lib/ims/lti/models.rb +38 -0
  83. data/lib/ims/lti/outcome_request.rb +225 -0
  84. data/lib/ims/lti/outcome_response.rb +166 -0
  85. data/lib/ims/lti/request_validator.rb +56 -0
  86. data/lib/ims/lti/role_checks.rb +101 -0
  87. data/lib/ims/lti/serializers/base.rb +125 -0
  88. data/lib/ims/lti/serializers/membership_service/agent_serializer.rb +5 -0
  89. data/lib/ims/lti/serializers/membership_service/container_serializer.rb +6 -0
  90. data/lib/ims/lti/serializers/membership_service/context_serializer.rb +9 -0
  91. data/lib/ims/lti/serializers/membership_service/lis_membership_container_serializer.rb +9 -0
  92. data/lib/ims/lti/serializers/membership_service/lis_person_serializer.rb +12 -0
  93. data/lib/ims/lti/serializers/membership_service/membership_serializer.rb +7 -0
  94. data/lib/ims/lti/serializers/membership_service/organization_serializer.rb +8 -0
  95. data/lib/ims/lti/serializers/membership_service/page_serializer.rb +10 -0
  96. data/lib/ims/lti/serializers/membership_service/person_serializer.rb +8 -0
  97. data/lib/ims/lti/serializers/membership_service.rb +13 -0
  98. data/lib/ims/lti/serializers.rb +6 -0
  99. data/lib/ims/lti/services/authentication_service.rb +67 -0
  100. data/lib/ims/lti/services/message_authenticator.rb +80 -0
  101. data/lib/ims/lti/services/oauth2_client.rb +18 -0
  102. data/lib/ims/lti/services/tool_config.rb +223 -0
  103. data/lib/ims/lti/services/tool_consumer_profile_service.rb +16 -0
  104. data/lib/ims/lti/services/tool_proxy_registration_service.rb +84 -0
  105. data/lib/ims/lti/services/tool_proxy_validator.rb +182 -0
  106. data/lib/ims/lti/services.rb +11 -0
  107. data/lib/ims/lti/tool_base.rb +29 -0
  108. data/lib/ims/lti/tool_config.rb +231 -0
  109. data/lib/ims/lti/tool_consumer.rb +86 -0
  110. data/lib/ims/lti/tool_provider.rb +143 -0
  111. data/lib/ims/lti/version.rb +5 -0
  112. data/lib/ims/lti.rb +61 -0
  113. data/lib/ims.rb +4 -0
  114. metadata +379 -0
@@ -0,0 +1,209 @@
1
+ module IMS::LTI
2
+ module Extensions
3
+
4
+ # An LTI extension that adds support for content back to the consumer
5
+ #
6
+ # # Initialize TP object with OAuth creds and post parameters
7
+ # provider = IMS::LTI::ToolProvider.new(consumer_key, consumer_secret, params)
8
+ # # add extension
9
+ # provider.extend IMS::LTI::Extensions::Content::ToolProvider
10
+ #
11
+ # If the tool was launched as an content request and it supports the content extension
12
+ # you can redirect the user to the tool consumer using the return url helper methods.
13
+ # The tool consumer is then responsible for consuming the content.
14
+ #
15
+ # #Check if a certain response type is available
16
+ # if provider.accepts_url? do
17
+ # #Generate the URL for the user
18
+ # redirect provider.url_content_return_url(url)
19
+ # end
20
+ #
21
+ module Content
22
+ module ToolProvider
23
+ include IMS::LTI::Extensions::ExtensionBase
24
+ include Base
25
+
26
+ # a list of the supported outcome data types
27
+ def accepted_content_types
28
+ return @content_types if @content_types
29
+ @content_types = []
30
+ if val = @ext_params["content_return_types"]
31
+ @content_types = val.split(',').map {|i| i.to_sym}
32
+ end
33
+
34
+ @content_types
35
+ end
36
+
37
+ def accepted_file_extensions
38
+ return @file_extensions if @file_extensions
39
+ @file_extensions = []
40
+ if val = @ext_params["content_file_extensions"]
41
+ @file_extensions = val.split(',').map {|i| i.downcase.strip}
42
+ end
43
+
44
+ @file_extensions
45
+ end
46
+
47
+ def accepts_file?(file_name = nil)
48
+ accepted_content_types.include?(:file) &&
49
+ ( file_name.nil? ||
50
+ accepted_file_extensions.empty? ||
51
+ accepted_file_extensions.any?{|ext| file_name.downcase[/#{ext}$/]} )
52
+ end
53
+
54
+ def accepts_url?
55
+ accepted_content_types.include?(:url)
56
+ end
57
+
58
+ def accepts_lti_launch_url?
59
+ accepted_content_types.include?(:lti_launch_url)
60
+ end
61
+
62
+ def accepts_image_url?
63
+ accepted_content_types.include?(:image_url)
64
+ end
65
+
66
+ def accepts_iframe?
67
+ accepted_content_types.include?(:iframe)
68
+ end
69
+
70
+ def accepts_oembed?
71
+ accepted_content_types.include?(:oembed)
72
+ end
73
+
74
+ def content_intended_use
75
+ @ext_params["content_intended_use"].to_sym if @ext_params["content_intended_use"]
76
+ end
77
+
78
+ # check if the content extension is supported
79
+ def accepts_content?
80
+ !!@ext_params["content_return_types"]
81
+ end
82
+
83
+ # check if the consumer accepts a given type of content
84
+ def accepts_content_type?(content_type)
85
+ accepted_content_types.include? content_type.to_sym
86
+ end
87
+
88
+ #check the use of the content
89
+ def is_content_for? (intended_use)
90
+ content_intended_use == intended_use
91
+ end
92
+
93
+ def content_return_url
94
+ @ext_params["content_return_url"]
95
+ end
96
+
97
+ #generates the return url for file submissions
98
+ def file_content_return_url(url, text, content_type = nil)
99
+ url = CGI::escape(url)
100
+ text = CGI::escape(text)
101
+ content_type = CGI::escape(content_type) if content_type
102
+
103
+ return_url = "#{content_return_url}?return_type=file&url=#{url}&text=#{text}"
104
+ return_url = "#{return_url}&content_type=#{content_type}" if content_type
105
+
106
+ return return_url
107
+ end
108
+
109
+ #generates the return url for url submissions
110
+ def url_content_return_url(url, title = nil, text = 'link', target = '_blank')
111
+ url = CGI::escape(url)
112
+ text = CGI::escape(text)
113
+ target = CGI::escape(target)
114
+
115
+ return_url = "#{content_return_url}?return_type=url&url=#{url}&text=#{text}&target=#{target}"
116
+ return_url = "#{return_url}&title=#{CGI::escape(title)}" if title
117
+
118
+ return return_url
119
+ end
120
+
121
+ #generates the return url for lti launch submissions
122
+ def lti_launch_content_return_url(url, text='link', title=nil)
123
+ url = CGI::escape(url)
124
+ text = CGI::escape(text)
125
+
126
+ return_url = "#{content_return_url}?return_type=lti_launch_url&url=#{url}&text=#{text}"
127
+ return_url = "#{return_url}&title=#{CGI::escape(title)}" if title
128
+
129
+ return return_url
130
+ end
131
+
132
+ #generates the return url for image submissions
133
+ def image_content_return_url(url, width, height, alt = '')
134
+ url = CGI::escape(url)
135
+ width = CGI::escape(width.to_s)
136
+ height = CGI::escape(height.to_s)
137
+ alt = CGI::escape(alt)
138
+
139
+ "#{content_return_url}?return_type=image_url&url=#{url}&width=#{width}&height=#{height}&alt=#{alt}"
140
+ end
141
+
142
+ #generates the return url for iframe submissions
143
+ def iframe_content_return_url(url, width, height, title = nil)
144
+ url = CGI::escape(url)
145
+ width = CGI::escape(width.to_s)
146
+ height = CGI::escape(height.to_s)
147
+
148
+ return_url = "#{content_return_url}?return_type=iframe&url=#{url}&width=#{width}&height=#{height}"
149
+ return_url = "#{return_url}&title=#{CGI::escape(title)}" if title
150
+
151
+ return return_url
152
+ end
153
+
154
+ #generates the return url for oembed submissions
155
+ def oembed_content_return_url(url, endpoint)
156
+ url = CGI::escape(url)
157
+ endpoint = CGI::escape(endpoint)
158
+
159
+ "#{content_return_url}?return_type=oembed&url=#{url}&endpoint=#{endpoint}"
160
+ end
161
+ end
162
+
163
+ module ToolConsumer
164
+ include IMS::LTI::Extensions::ExtensionBase
165
+ include Base
166
+
167
+ # a list of the content types accepted
168
+ #
169
+ # tc.add_content_return_types=(['url', 'text'])
170
+ # tc.add_content_return_types=("url,text")
171
+ def content_return_types=(val)
172
+ val = val.join(',') if val.is_a? Array
173
+ set_ext_param('content_return_types', val)
174
+ end
175
+
176
+ # a comma-separated string of the supported outcome data types
177
+ def content_return_types
178
+ get_ext_param('content_return_types')
179
+ end
180
+
181
+ def content_intended_use=(val)
182
+ set_ext_param('content_intended_use', val)
183
+ end
184
+
185
+ def content_intended_use
186
+ get_ext_param('content_intended_use')
187
+ end
188
+
189
+ # convenience method for setting support for homework content
190
+ def support_homework_content!
191
+ self.content_intended_use = 'homework'
192
+ self.content_return_types = 'file,url'
193
+ end
194
+
195
+ # convenience method for setting support for embed content
196
+ def support_embed_content!
197
+ self.content_intended_use = 'embed'
198
+ self.content_return_types = 'oembed,lti_launch_url,url,image_url,iframe'
199
+ end
200
+
201
+ # convenience method for setting support for navigation content
202
+ def support_navigation_content!
203
+ self.content_intended_use = 'navigation'
204
+ self.content_return_types = 'lti_launch_url'
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,216 @@
1
+ module IMS::LTI
2
+ module Extensions
3
+
4
+ # An LTI extension that adds support for sending data back to the consumer
5
+ # in addition to the score.
6
+ #
7
+ # # Initialize TP object with OAuth creds and post parameters
8
+ # provider = IMS::LTI::ToolProvider.new(consumer_key, consumer_secret, params)
9
+ # # add extension
10
+ # provider.extend IMS::LTI::Extensions::OutcomeData::ToolProvider
11
+ #
12
+ # If the tool was launch as an outcome service and it supports the data extension
13
+ # you can POST a score to the TC.
14
+ # The POST calls all return an OutcomeResponse object which can be used to
15
+ # handle the response appropriately.
16
+ #
17
+ # # post the score to the TC, score should be a float >= 0.0 and <= 1.0
18
+ # # this returns an OutcomeResponse object
19
+ # if provider.accepts_outcome_text?
20
+ # response = provider.post_extended_replace_result!(score: score, text: "submission text")
21
+ # else
22
+ # response = provider.post_replace_result!(score)
23
+ # end
24
+ # if response.success?
25
+ # # grade write worked
26
+ # elsif response.processing?
27
+ # elsif response.unsupported?
28
+ # else
29
+ # # failed
30
+ # end
31
+ module OutcomeData
32
+
33
+ #IMS::LTI::Extensions::OutcomeData::ToolProvider
34
+ module Base
35
+ def outcome_request_extensions
36
+ super + [IMS::LTI::Extensions::OutcomeData::OutcomeRequest]
37
+ end
38
+ end
39
+
40
+ module ToolProvider
41
+ include IMS::LTI::Extensions::ExtensionBase
42
+ include Base
43
+
44
+ # a list of the supported outcome data types
45
+ def accepted_outcome_types
46
+ return @outcome_types if @outcome_types
47
+ @outcome_types = []
48
+ if val = @ext_params["outcome_data_values_accepted"]
49
+ @outcome_types = val.split(',')
50
+ end
51
+
52
+ @outcome_types
53
+ end
54
+
55
+ # check if the outcome data extension is supported
56
+ def accepts_outcome_data?
57
+ !!@ext_params["outcome_data_values_accepted"]
58
+ end
59
+
60
+ # check if the consumer accepts text as outcome data
61
+ def accepts_outcome_text?
62
+ accepted_outcome_types.member?("text")
63
+ end
64
+
65
+ # check if the consumer accepts a url as outcome data
66
+ def accepts_outcome_url?
67
+ accepted_outcome_types.member?("url")
68
+ end
69
+
70
+ # check if the consumer accepts a submitted at date as outcome data
71
+ def accepts_submitted_at?
72
+ accepted_outcome_types.member?("submitted_at")
73
+ end
74
+
75
+ def accepts_outcome_lti_launch_url?
76
+ accepted_outcome_types.member?("lti_launch_url")
77
+ end
78
+
79
+ def accepts_outcome_result_total_score?
80
+ !!@ext_params["outcome_result_total_score_accepted"]
81
+ end
82
+
83
+ # POSTs the given score to the Tool Consumer with a replaceResult and
84
+ # adds the specified data. The data hash can have the keys "text", "cdata_text", "url", "submitted_at" or "lti_launch_url"
85
+ #
86
+ # If both cdata_text and text are sent, cdata_text will be used
87
+ #
88
+ # If score is nil, the replace result XML will not contain a resultScore node
89
+ #
90
+ # Creates a new OutcomeRequest object and stores it in @outcome_requests
91
+ #
92
+ # @return [OutcomeResponse] the response from the Tool Consumer
93
+ # @deprecated Use #post_extended_replace_result! instead
94
+ def post_replace_result_with_data!(score = nil, data={})
95
+ data[:score] = score if score
96
+ post_extended_replace_result!(data)
97
+ end
98
+
99
+ # POSTs the given score to the Tool Consumer with a replaceResult and
100
+ # adds the specified data. The options hash can have the keys
101
+ # :text, :cdata_text, :url, :submitted_at, :lti_launch_url, :score, or :total_score
102
+ #
103
+ # If both cdata_text and text are sent, cdata_text will be used
104
+ # If both total_score and score are sent, total_score will be used
105
+ # If score is nil, the replace result XML will not contain a resultScore node
106
+ #
107
+ # Creates a new OutcomeRequest object and stores it in @outcome_requests
108
+ #
109
+ # @return [OutcomeResponse] the response from the Tool Consumer
110
+ def post_extended_replace_result!(options = {})
111
+ opts = {}
112
+ options.each {|k,v| opts[k.to_sym] = v}
113
+
114
+ req = new_request
115
+ req.outcome_cdata_text = opts[:cdata_text]
116
+ req.outcome_text = opts[:text]
117
+ req.outcome_url = opts[:url]
118
+ req.submitted_at = opts[:submitted_at]
119
+ req.outcome_lti_launch_url = opts[:lti_launch_url]
120
+ req.total_score = opts[:total_score]
121
+ req.post_replace_result!(opts[:score])
122
+ end
123
+ end
124
+
125
+ module ToolConsumer
126
+ include IMS::LTI::Extensions::ExtensionBase
127
+ include Base
128
+
129
+ OUTCOME_DATA_TYPES = %w{text url lti_launch_url submitted_at}
130
+
131
+ # a list of the outcome data types accepted, currently only 'url', 'submitted_at' and
132
+ # 'text' are valid
133
+ #
134
+ # tc.outcome_data_values_accepted(['url', 'text'])
135
+ # tc.outcome_data_valued_accepted("url,text")
136
+ def outcome_data_values_accepted=(val)
137
+ if val.is_a? Array
138
+ val = val.join(',')
139
+ end
140
+
141
+ set_ext_param('outcome_data_values_accepted', val)
142
+ end
143
+
144
+ # a comma-separated string of the supported outcome data types
145
+ def outcome_data_values_accepted
146
+ get_ext_param('outcome_data_values_accepted')
147
+ end
148
+
149
+ # convenience method for setting support for all current outcome data types
150
+ def support_outcome_data!
151
+ self.outcome_data_values_accepted = OUTCOME_DATA_TYPES
152
+ end
153
+ end
154
+
155
+ module OutcomeRequest
156
+ include IMS::LTI::Extensions::ExtensionBase
157
+ include Base
158
+
159
+ attr_accessor :outcome_text, :outcome_url, :submitted_at, :outcome_lti_launch_url, :outcome_cdata_text, :total_score
160
+
161
+ def result_values(node)
162
+ super
163
+
164
+ if total_score
165
+ node.resultTotalScore do |res_total_score|
166
+ res_total_score.language "en" # 'en' represents the format of the number
167
+ res_total_score.textString total_score.to_s
168
+ end
169
+ end
170
+
171
+ if outcome_text || outcome_url || outcome_cdata_text || outcome_lti_launch_url
172
+ node.resultData do |res_data|
173
+ if outcome_cdata_text
174
+ res_data.text {
175
+ res_data.cdata! outcome_cdata_text
176
+ }
177
+ elsif outcome_text
178
+ res_data.text outcome_text
179
+ elsif outcome_lti_launch_url
180
+ res_data.ltiLaunchUrl outcome_lti_launch_url
181
+ end
182
+ res_data.url outcome_url if outcome_url
183
+ end
184
+ end
185
+ end
186
+
187
+ def details(node)
188
+ super
189
+ return unless has_details_data?
190
+
191
+ node.submittedAt submitted_at
192
+ end
193
+
194
+ def score
195
+ total_score ? nil : @score
196
+ end
197
+
198
+ def has_result_data?
199
+ !!outcome_text || !!outcome_url || !!outcome_lti_launch_url || !!outcome_cdata_text || !!total_score || super
200
+ end
201
+
202
+ def has_details_data?
203
+ !!submitted_at
204
+ end
205
+
206
+ def extention_process_xml(doc)
207
+ super
208
+ @outcome_text = doc.get_text("//resultRecord/result/resultData/text")
209
+ @outcome_url = doc.get_text("//resultRecord/result/resultData/url")
210
+ @outcome_lti_launch_url = doc.get_text("//resultRecord/result/resultData/ltiLaunchUrl")
211
+ end
212
+ end
213
+
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,45 @@
1
+
2
+ module IMS::LTI
3
+ module Extensions
4
+
5
+ # Base functionality for creating LTI extension modules
6
+ # See the test for this class for a simple example of how to create an extension module
7
+ module Base
8
+ def outcome_request_extensions
9
+ []
10
+ end
11
+
12
+ def outcome_response_extensions
13
+ []
14
+ end
15
+
16
+ def extend_outcome_request(request)
17
+ outcome_request_extensions.each do |ext|
18
+ request.extend(ext)
19
+ end
20
+ request
21
+ end
22
+
23
+ def extend_outcome_response(response)
24
+ outcome_response_extensions.each do |ext|
25
+ response.extend(ext)
26
+ end
27
+ response
28
+ end
29
+ end
30
+
31
+ module ExtensionBase
32
+ def outcome_request_extensions
33
+ super
34
+ end
35
+
36
+ def outcome_response_extensions
37
+ super
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ require 'ims/lti/extensions/outcome_data'
44
+ require 'ims/lti/extensions/content'
45
+ require 'ims/lti/extensions/canvas'
@@ -0,0 +1,166 @@
1
+ module IMS::LTI
2
+ # Mixin module for managing LTI Launch Data
3
+ #
4
+ # Launch data documentation:
5
+ # http://www.imsglobal.org/lti/v1p1pd/ltiIMGv1p1pd.html#_Toc309649684
6
+ module LaunchParams
7
+
8
+ # List of the standard launch parameters for an LTI launch
9
+ LAUNCH_DATA_PARAMETERS = %w{
10
+ accept_media_types
11
+ accept_multiple
12
+ accept_presentation_document_targets
13
+ accept_unsigned
14
+ auto_create
15
+ content_item_return_url
16
+ context_id
17
+ context_label
18
+ context_title
19
+ context_type
20
+ launch_presentation_css_url
21
+ launch_presentation_document_target
22
+ launch_presentation_height
23
+ launch_presentation_locale
24
+ launch_presentation_return_url
25
+ launch_presentation_width
26
+ lis_course_offering_sourcedid
27
+ lis_course_section_sourcedid
28
+ lis_outcome_service_url
29
+ lis_person_contact_email_primary
30
+ lis_person_name_family
31
+ lis_person_name_full
32
+ lis_person_name_given
33
+ lis_person_sourcedid
34
+ lis_result_sourcedid
35
+ lti_message_type
36
+ lti_version
37
+ oauth_callback
38
+ oauth_consumer_key
39
+ oauth_nonce
40
+ oauth_signature
41
+ oauth_signature_method
42
+ oauth_timestamp
43
+ oauth_version
44
+ resource_link_description
45
+ resource_link_id
46
+ resource_link_title
47
+ roles
48
+ role_scope_mentor
49
+ tool_consumer_info_product_family_code
50
+ tool_consumer_info_version
51
+ tool_consumer_instance_contact_email
52
+ tool_consumer_instance_description
53
+ tool_consumer_instance_guid
54
+ tool_consumer_instance_name
55
+ tool_consumer_instance_url
56
+ user_id
57
+ user_image
58
+ }
59
+
60
+ LAUNCH_DATA_PARAMETERS.each { |p| attr_accessor p }
61
+
62
+ # Hash of custom parameters, the keys will be prepended with "custom_" at launch
63
+ attr_accessor :custom_params
64
+
65
+ # Hash of extension parameters, the keys will be prepended with "ext_" at launch
66
+ attr_accessor :ext_params
67
+
68
+ # Hash of parameters to add to the launch. These keys will not be prepended
69
+ # with any value at launch
70
+ attr_accessor :non_spec_params
71
+
72
+ # Set the roles for the current launch
73
+ #
74
+ # Full list of roles can be found here:
75
+ # http://www.imsglobal.org/LTI/v1p1pd/ltiIMGv1p1pd.html#_Toc309649700
76
+ #
77
+ # LIS roles include:
78
+ # * Student
79
+ # * Faculty
80
+ # * Member
81
+ # * Learner
82
+ # * Instructor
83
+ # * Mentor
84
+ # * Staff
85
+ # * Alumni
86
+ # * ProspectiveStudent
87
+ # * Guest
88
+ # * Other
89
+ # * Administrator
90
+ # * Observer
91
+ # * None
92
+ #
93
+ # @param roles_list [String,Array] An Array or comma-separated String of roles
94
+ def roles=(roles_list)
95
+ if roles_list
96
+ if roles_list.is_a?(Array)
97
+ @roles = roles_list
98
+ else
99
+ @roles = roles_list.split(",")
100
+ end
101
+ else
102
+ @roles = nil
103
+ end
104
+ end
105
+
106
+ def set_custom_param(key, val)
107
+ @custom_params[key] = val
108
+ end
109
+
110
+ def get_custom_param(key)
111
+ @custom_params[key]
112
+ end
113
+
114
+ def set_non_spec_param(key, val)
115
+ @non_spec_params[key] = val
116
+ end
117
+
118
+ def get_non_spec_param(key)
119
+ @non_spec_params[key]
120
+ end
121
+
122
+ def set_ext_param(key, val)
123
+ @ext_params[key] = val
124
+ end
125
+
126
+ def get_ext_param(key)
127
+ @ext_params[key]
128
+ end
129
+
130
+ # Create a new Hash with all launch data. Custom/Extension keys will have the
131
+ # appropriate value prepended to the keys and the roles are set as a comma
132
+ # separated String
133
+ def to_params
134
+ params = launch_data_hash.merge(add_key_prefix(@custom_params, 'custom')).merge(add_key_prefix(@ext_params, 'ext')).merge(@non_spec_params)
135
+ params["roles"] = @roles.join(",") if @roles
136
+ params
137
+ end
138
+
139
+ # Populates the launch data from a Hash
140
+ #
141
+ # Only keys in LAUNCH_DATA_PARAMETERS and that start with 'custom_' or 'ext_'
142
+ # will be pulled from the provided Hash
143
+ def process_params(params)
144
+ params.each_pair do |key, val|
145
+ if LAUNCH_DATA_PARAMETERS.member?(key)
146
+ self.send("#{key}=", val)
147
+ elsif key =~ /\Acustom_(.+)\Z/
148
+ @custom_params[$1] = val
149
+ elsif key =~ /\Aext_(.+)\Z/
150
+ @ext_params[$1] = val
151
+ end
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ def launch_data_hash
158
+ LAUNCH_DATA_PARAMETERS.inject({}) { |h, k| h[k] = self.send(k) if self.send(k); h }
159
+ end
160
+
161
+ def add_key_prefix(hash, prefix)
162
+ hash.keys.inject({}) { |h, k| h["#{prefix}_#{k}"] = hash[k]; h }
163
+ end
164
+
165
+ end
166
+ end
@@ -0,0 +1,15 @@
1
+ module IMS::LTI::Models
2
+ class BaseUrlChoice < LTIModel
3
+ add_attributes :default_base_url, :secure_base_url
4
+ add_attribute :selector, relation:'IMS::LTI::Models::BaseUrlSelector'
5
+
6
+ def default_message_url
7
+ if selector.nil? || (selector.applies_to && selector.applies_to.include?('MessageHandler'))
8
+ secure_base_url || default_base_url || ''
9
+ else
10
+ ''
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module IMS::LTI::Models
2
+ class BaseUrlSelector < LTIModel
3
+ add_attribute :applies_to
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module IMS::LTI::Models
2
+ class Contact < LTIModel
3
+ add_attributes :email
4
+ end
5
+ end
@@ -0,0 +1,14 @@
1
+ module IMS::LTI::Models
2
+ class ContentItemContainer < IMS::LTI::Models::LTIModel
3
+ add_attribute :context, json_key: '@context'
4
+ add_attribute :graph, json_key: '@graph', relation: 'IMS::LTI::Models::ContentItems::ContentItem'
5
+
6
+ CONTENT_ITEM_CONTAINER_CONTEXT = 'http://purl.imsglobal.org/ctx/lti/v1/ContentItem'
7
+
8
+ def initialize(opts = {})
9
+ super(opts)
10
+ self.context = CONTENT_ITEM_CONTAINER_CONTEXT
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ module IMS::LTI::Models
2
+ class ContentItemPlacement < LTIModel
3
+
4
+ DOCUMENT_TARGETS = {
5
+ embed: 'http://purl.imsglobal.org/vocab/lti/v2/lti#embed',
6
+ frame: 'http://purl.imsglobal.org/vocab/lti/v2/lti#frame',
7
+ iframe: 'http://purl.imsglobal.org/vocab/lti/v2/lti#iframe',
8
+ none: 'http://purl.imsglobal.org/vocab/lti/v2/lti#none',
9
+ overlay: 'http://purl.imsglobal.org/vocab/lti/v2/lti#overlay',
10
+ popup: 'http://purl.imsglobal.org/vocab/lti/v2/lti#popup',
11
+ window: 'http://purl.imsglobal.org/vocab/lti/v2/lti#window'
12
+ }
13
+
14
+ add_attribute :display_height, json_key: 'displayHeight'
15
+ add_attribute :display_width, json_key: 'displayWidth'
16
+ add_attribute :window_target, json_key: 'windowTarget'
17
+ add_attribute :presentation_document_target, json_key: 'presentationDocumentTarget'
18
+
19
+ end
20
+ end