ims-lti 1.2.4 → 2.0.0.beta.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 (57) hide show
  1. checksums.yaml +5 -5
  2. data/Changelog.txt +0 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +15 -103
  5. data/lib/ims.rb +3 -1
  6. data/lib/ims/lti.rb +6 -64
  7. data/lib/ims/lti/converters.rb +5 -0
  8. data/lib/ims/lti/converters/time_json_converter.rb +13 -0
  9. data/lib/ims/lti/models.rb +29 -0
  10. data/lib/ims/lti/models/base_url_choice.rb +15 -0
  11. data/lib/ims/lti/models/base_url_selector.rb +5 -0
  12. data/lib/ims/lti/models/contact.rb +5 -0
  13. data/lib/ims/lti/models/icon_endpoint.rb +5 -0
  14. data/lib/ims/lti/models/icon_info.rb +6 -0
  15. data/lib/ims/lti/models/localized_name.rb +11 -0
  16. data/lib/ims/lti/models/localized_text.rb +11 -0
  17. data/lib/ims/lti/models/lti_model.rb +169 -0
  18. data/lib/ims/lti/models/message_handler.rb +6 -0
  19. data/lib/ims/lti/models/messages.rb +6 -0
  20. data/lib/ims/lti/models/messages/basic_lti_launch_request.rb +8 -0
  21. data/lib/ims/lti/models/messages/message.rb +43 -0
  22. data/lib/ims/lti/models/messages/registration_request.rb +17 -0
  23. data/lib/ims/lti/models/parameter.rb +5 -0
  24. data/lib/ims/lti/models/product_family.rb +8 -0
  25. data/lib/ims/lti/models/product_info.rb +19 -0
  26. data/lib/ims/lti/models/product_instance.rb +10 -0
  27. data/lib/ims/lti/models/resource_handler.rb +18 -0
  28. data/lib/ims/lti/models/resource_type.rb +6 -0
  29. data/lib/ims/lti/models/rest_service.rb +14 -0
  30. data/lib/ims/lti/models/rest_service_profile.rb +7 -0
  31. data/lib/ims/lti/models/security_contract.rb +9 -0
  32. data/lib/ims/lti/models/service_owner.rb +8 -0
  33. data/lib/ims/lti/models/service_provider.rb +11 -0
  34. data/lib/ims/lti/models/tool_consumer_profile.rb +20 -0
  35. data/lib/ims/lti/models/tool_profile.rb +22 -0
  36. data/lib/ims/lti/models/tool_proxy.rb +11 -0
  37. data/lib/ims/lti/models/vendor.rb +28 -0
  38. data/lib/ims/lti/services.rb +5 -0
  39. data/lib/ims/lti/services/message_service.rb +40 -0
  40. data/lib/ims/lti/version.rb +5 -0
  41. metadata +68 -52
  42. data/Changelog +0 -54
  43. data/LICENSE +0 -18
  44. data/lib/ims/lti/deprecated_role_checks.rb +0 -52
  45. data/lib/ims/lti/extensions.rb +0 -45
  46. data/lib/ims/lti/extensions/canvas.rb +0 -122
  47. data/lib/ims/lti/extensions/content.rb +0 -209
  48. data/lib/ims/lti/extensions/outcome_data.rb +0 -216
  49. data/lib/ims/lti/launch_params.rb +0 -166
  50. data/lib/ims/lti/outcome_request.rb +0 -225
  51. data/lib/ims/lti/outcome_response.rb +0 -166
  52. data/lib/ims/lti/request_validator.rb +0 -56
  53. data/lib/ims/lti/role_checks.rb +0 -101
  54. data/lib/ims/lti/tool_base.rb +0 -29
  55. data/lib/ims/lti/tool_config.rb +0 -231
  56. data/lib/ims/lti/tool_consumer.rb +0 -86
  57. data/lib/ims/lti/tool_provider.rb +0 -143
@@ -1,216 +0,0 @@
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
@@ -1,166 +0,0 @@
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
@@ -1,225 +0,0 @@
1
- module IMS::LTI
2
- # Class for consuming/generating LTI Outcome Requests
3
- #
4
- # Outcome Request documentation: http://www.imsglobal.org/lti/v1p1pd/ltiIMGv1p1pd.html#_Toc309649691
5
- #
6
- # This class can be used by both Tool Providers and Tool Consumers. Each will
7
- # use it a bit differently. The Tool Provider will use it to POST an OAuth-signed
8
- # request to a TC. A Tool Consumer will use it to parse such a request from a TP.
9
- #
10
- # === Tool Provider Usage
11
- # An OutcomeRequest will generally be created through a configured ToolProvider
12
- # object. See the ToolProvider documentation.
13
- #
14
- # === Tool Consumer Usage
15
- # When an outcome request is sent from a TP the body of the request is XML.
16
- # This class parses that XML and provides a simple interface for accessing the
17
- # information in the request. Typical usage would be:
18
- #
19
- # # create an OutcomeRequest from the request object
20
- # req = IMS::LTI::OutcomeRequest.from_post_request(request)
21
- #
22
- # # access the source id to identify the user who's grade you'd like to access
23
- # req.lis_result_sourcedid
24
- #
25
- # # process the request
26
- # if req.replace_request?
27
- # # set a new score for the user
28
- # elsif req.read_request?
29
- # # return the score for the user
30
- # elsif req.delete_request?
31
- # # clear the score for the user
32
- # else
33
- # # return an unsupported OutcomeResponse
34
- # end
35
- class OutcomeRequest < ToolBase
36
- include IMS::LTI::Extensions::Base
37
-
38
- REPLACE_REQUEST = 'replaceResult'
39
- DELETE_REQUEST = 'deleteResult'
40
- READ_REQUEST = 'readResult'
41
-
42
- attr_accessor :operation, :score, :outcome_response, :message_identifier,
43
- :lis_outcome_service_url, :lis_result_sourcedid,
44
- :consumer_key, :consumer_secret, :post_request
45
-
46
- # Create a new OutcomeRequest
47
- #
48
- # @param opts [Hash] initialization hash
49
- def initialize(opts={})
50
- opts.each_pair do |key, val|
51
- self.send("#{key}=", val) if self.respond_to?("#{key}=")
52
- end
53
- end
54
-
55
- # Convenience method for creating a new OutcomeRequest from a request object
56
- #
57
- # req = IMS::LTI::OutcomeRequest.from_post_request(request)
58
- def self.from_post_request(post_request)
59
- request = OutcomeRequest.new
60
- request.process_post_request(post_request)
61
- end
62
-
63
- def process_post_request(post_request)
64
- self.post_request = post_request
65
- if post_request.body.respond_to?(:read)
66
- xml = post_request.body.read
67
- post_request.body.rewind
68
- else
69
- xml = post_request.body
70
- end
71
- self.process_xml(xml)
72
- self
73
- end
74
-
75
- # POSTs the given score to the Tool Consumer with a replaceResult
76
- #
77
- # @return [OutcomeResponse] The response from the Tool Consumer
78
- def post_replace_result!(score)
79
- @operation = REPLACE_REQUEST
80
- @score = score
81
- post_outcome_request
82
- end
83
-
84
- # POSTs a deleteResult to the Tool Consumer
85
- #
86
- # @return [OutcomeResponse] The response from the Tool Consumer
87
- def post_delete_result!
88
- @operation = DELETE_REQUEST
89
- post_outcome_request
90
- end
91
-
92
- # POSTs a readResult to the Tool Consumer
93
- #
94
- # @return [OutcomeResponse] The response from the Tool Consumer
95
- def post_read_result!
96
- @operation = READ_REQUEST
97
- post_outcome_request
98
- end
99
-
100
- # Check whether this request is a replaceResult request
101
- def replace_request?
102
- @operation == REPLACE_REQUEST
103
- end
104
-
105
- # Check whether this request is a deleteResult request
106
- def delete_request?
107
- @operation == DELETE_REQUEST
108
- end
109
-
110
- # Check whether this request is a readResult request
111
- def read_request?
112
- @operation == READ_REQUEST
113
- end
114
-
115
- # Check whether the last outcome POST was successful
116
- def outcome_post_successful?
117
- @outcome_response && @outcome_response.success?
118
- end
119
-
120
- # POST an OAuth signed request to the Tool Consumer
121
- #
122
- # @return [OutcomeResponse] The response from the Tool Consumer
123
- def post_outcome_request
124
- raise IMS::LTI::InvalidLTIConfigError, "" unless has_required_attributes?
125
-
126
- res = post_service_request(@lis_outcome_service_url,
127
- 'application/xml',
128
- generate_request_xml)
129
-
130
- @outcome_response = extend_outcome_response(OutcomeResponse.new)
131
- @outcome_response.process_post_response(res)
132
- end
133
-
134
- # Parse Outcome Request data from XML
135
- def process_xml(xml)
136
- doc = REXML::Document.new xml
137
- @message_identifier = doc.text("//imsx_POXRequestHeaderInfo/imsx_messageIdentifier")
138
- @lis_result_sourcedid = doc.text("//resultRecord/sourcedGUID/sourcedId")
139
-
140
- if REXML::XPath.first(doc, "//deleteResultRequest")
141
- @operation = DELETE_REQUEST
142
- elsif REXML::XPath.first(doc, "//readResultRequest")
143
- @operation = READ_REQUEST
144
- elsif REXML::XPath.first(doc, "//replaceResultRequest")
145
- @operation = REPLACE_REQUEST
146
- @score = doc.get_text("//resultRecord/result/resultScore/textString")
147
- end
148
- extention_process_xml(doc)
149
- end
150
-
151
- def generate_request_xml
152
- raise IMS::LTI::InvalidLTIConfigError, "`@operation` and `@lis_result_sourcedid` are required" unless has_request_xml_attributes?
153
- builder = Builder::XmlMarkup.new #(:indent=>2)
154
- builder.instruct!
155
-
156
- builder.imsx_POXEnvelopeRequest("xmlns" => "http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0") do |env|
157
- env.imsx_POXHeader do |header|
158
- header.imsx_POXRequestHeaderInfo do |info|
159
- info.imsx_version "V1.0"
160
- info.imsx_messageIdentifier @message_identifier || IMS::LTI::generate_identifier
161
- end
162
- end
163
- env.imsx_POXBody do |body|
164
- body.tag!(@operation + 'Request') do |request|
165
- request.resultRecord do |record|
166
- record.sourcedGUID do |guid|
167
- guid.sourcedId @lis_result_sourcedid
168
- end
169
- results(record)
170
- end
171
- submission_details(request)
172
- end
173
- end
174
- end
175
- end
176
-
177
- private
178
-
179
- def extention_process_xml(doc)
180
- end
181
-
182
- def has_result_data?
183
- !!score
184
- end
185
-
186
- def has_details_data?
187
- false
188
- end
189
-
190
- def results(node)
191
- return unless has_result_data?
192
-
193
- node.result do |res|
194
- result_values(res)
195
- end
196
- end
197
-
198
- def submission_details(request)
199
- return unless has_details_data?
200
- request.submissionDetails do |record|
201
- details(record)
202
- end
203
- end
204
-
205
- def details(record)
206
- end
207
-
208
- def result_values(node)
209
- if score
210
- node.resultScore do |res_score|
211
- res_score.language "en" # 'en' represents the format of the number
212
- res_score.textString score.to_s
213
- end
214
- end
215
- end
216
-
217
- def has_required_attributes?
218
- @consumer_key && @consumer_secret && @lis_outcome_service_url && @lis_result_sourcedid && @operation
219
- end
220
-
221
- def has_request_xml_attributes?
222
- @operation && @lis_result_sourcedid
223
- end
224
- end
225
- end