ims-lti 1.2.8 → 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/lti/converters/time_json_converter.rb +13 -0
  6. data/lib/ims/lti/converters.rb +5 -0
  7. data/lib/ims/lti/models/base_url_choice.rb +15 -0
  8. data/lib/ims/lti/models/base_url_selector.rb +5 -0
  9. data/lib/ims/lti/models/contact.rb +5 -0
  10. data/lib/ims/lti/models/icon_endpoint.rb +5 -0
  11. data/lib/ims/lti/models/icon_info.rb +6 -0
  12. data/lib/ims/lti/models/localized_name.rb +11 -0
  13. data/lib/ims/lti/models/localized_text.rb +11 -0
  14. data/lib/ims/lti/models/lti_model.rb +169 -0
  15. data/lib/ims/lti/models/message_handler.rb +6 -0
  16. data/lib/ims/lti/models/messages/basic_lti_launch_request.rb +8 -0
  17. data/lib/ims/lti/models/messages/message.rb +43 -0
  18. data/lib/ims/lti/models/messages/registration_request.rb +17 -0
  19. data/lib/ims/lti/models/messages.rb +6 -0
  20. data/lib/ims/lti/models/parameter.rb +5 -0
  21. data/lib/ims/lti/models/product_family.rb +8 -0
  22. data/lib/ims/lti/models/product_info.rb +19 -0
  23. data/lib/ims/lti/models/product_instance.rb +10 -0
  24. data/lib/ims/lti/models/resource_handler.rb +18 -0
  25. data/lib/ims/lti/models/resource_type.rb +6 -0
  26. data/lib/ims/lti/models/rest_service.rb +14 -0
  27. data/lib/ims/lti/models/rest_service_profile.rb +7 -0
  28. data/lib/ims/lti/models/security_contract.rb +9 -0
  29. data/lib/ims/lti/models/service_owner.rb +8 -0
  30. data/lib/ims/lti/models/service_provider.rb +11 -0
  31. data/lib/ims/lti/models/tool_consumer_profile.rb +20 -0
  32. data/lib/ims/lti/models/tool_profile.rb +22 -0
  33. data/lib/ims/lti/models/tool_proxy.rb +11 -0
  34. data/lib/ims/lti/models/vendor.rb +28 -0
  35. data/lib/ims/lti/models.rb +29 -0
  36. data/lib/ims/lti/services/message_service.rb +40 -0
  37. data/lib/ims/lti/services.rb +5 -0
  38. data/lib/ims/lti/version.rb +5 -0
  39. data/lib/ims/lti.rb +6 -64
  40. data/lib/ims.rb +3 -1
  41. metadata +58 -56
  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/canvas.rb +0 -122
  46. data/lib/ims/lti/extensions/content.rb +0 -258
  47. data/lib/ims/lti/extensions/outcome_data.rb +0 -240
  48. data/lib/ims/lti/extensions.rb +0 -45
  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 -145
@@ -1,231 +0,0 @@
1
- module IMS::LTI
2
- # Class used to represent an LTI configuration
3
- #
4
- # It can create and read the Common Cartridge XML representation of LTI links
5
- # as described here: http://www.imsglobal.org/LTI/v1p1pd/ltiIMGv1p1pd.html#_Toc309649689
6
- #
7
- # == Usage
8
- # To generate an XML configuration:
9
- #
10
- # # Create a config object and set some options
11
- # tc = IMS::LTI::ToolConfig.new(:title => "Example Sinatra Tool Provider", :launch_url => url)
12
- # tc.description = "This example LTI Tool Provider supports LIS Outcome pass-back."
13
- #
14
- # # generate the XML
15
- # tc.to_xml
16
- #
17
- # Or to create a config object from an XML String:
18
- #
19
- # tc = IMS::LTI::ToolConfig.create_from_xml(xml)
20
- class ToolConfig
21
- attr_reader :custom_params, :extensions
22
-
23
- attr_accessor :title, :description, :launch_url, :secure_launch_url,
24
- :icon, :secure_icon, :cartridge_bundle, :cartridge_icon,
25
- :vendor_code, :vendor_name, :vendor_description, :vendor_url,
26
- :vendor_contact_email, :vendor_contact_name
27
-
28
- # Create a new ToolConfig with the given options
29
- #
30
- # @param opts [Hash] The initial options for the ToolConfig
31
- def initialize(opts={})
32
- @custom_params = opts.delete("custom_params") || {}
33
- @extensions = opts.delete("extensions") || {}
34
-
35
- opts.each_pair do |key, val|
36
- self.send("#{key}=", val) if self.respond_to?("#{key}=")
37
- end
38
- end
39
-
40
- # Create a ToolConfig from the given XML
41
- #
42
- # @param xml [String]
43
- def self.create_from_xml(xml)
44
- tc = ToolConfig.new
45
- tc.process_xml(xml)
46
-
47
- tc
48
- end
49
-
50
- def set_custom_param(key, val)
51
- @custom_params[key] = val
52
- end
53
-
54
- def get_custom_param(key)
55
- @custom_params[key]
56
- end
57
-
58
- # Set the extension parameters for a specific vendor
59
- #
60
- # @param ext_key [String] The identifier for the vendor-specific parameters
61
- # @param ext_params [Hash] The parameters, this is allowed to be two-levels deep
62
- def set_ext_params(ext_key, ext_params)
63
- raise ArgumentError unless ext_params.is_a?(Hash)
64
- @extensions[ext_key] = ext_params
65
- end
66
-
67
- def get_ext_params(ext_key)
68
- @extensions[ext_key]
69
- end
70
-
71
- def set_ext_param(ext_key, param_key, val)
72
- @extensions[ext_key] ||= {}
73
- @extensions[ext_key][param_key] = val
74
- end
75
-
76
- def get_ext_param(ext_key, param_key)
77
- @extensions[ext_key] && @extensions[ext_key][param_key]
78
- end
79
-
80
- # Namespaces used for parsing configuration XML
81
- LTI_NAMESPACES = {
82
- "xmlns" => 'http://www.imsglobal.org/xsd/imslticc_v1p0',
83
- "blti" => 'http://www.imsglobal.org/xsd/imsbasiclti_v1p0',
84
- "lticm" => 'http://www.imsglobal.org/xsd/imslticm_v1p0',
85
- "lticp" => 'http://www.imsglobal.org/xsd/imslticp_v1p0',
86
- }
87
-
88
- # Parse tool configuration data out of the Common Cartridge LTI link XML
89
- def process_xml(xml)
90
- doc = REXML::Document.new xml
91
- if root = REXML::XPath.first(doc, 'xmlns:cartridge_basiclti_link')
92
- @title = get_node_text(root, 'blti:title')
93
- @description = get_node_text(root, 'blti:description')
94
- @launch_url = get_node_text(root, 'blti:launch_url')
95
- @secure_launch_url = get_node_text(root, 'blti:secure_launch_url')
96
- @icon = get_node_text(root, 'blti:icon')
97
- @secure_icon = get_node_text(root, 'blti:secure_icon')
98
- @cartridge_bundle = get_node_att(root, 'xmlns:cartridge_bundle', 'identifierref')
99
- @cartridge_icon = get_node_att(root, 'xmlns:cartridge_icon', 'identifierref')
100
-
101
- if vendor = REXML::XPath.first(root, 'blti:vendor')
102
- @vendor_code = get_node_text(vendor, 'lticp:code')
103
- @vendor_description = get_node_text(vendor, 'lticp:description')
104
- @vendor_name = get_node_text(vendor, 'lticp:name')
105
- @vendor_url = get_node_text(vendor, 'lticp:url')
106
- @vendor_contact_email = get_node_text(vendor, '//lticp:contact/lticp:email')
107
- @vendor_contact_name = get_node_text(vendor, '//lticp:contact/lticp:name')
108
- end
109
-
110
- if custom = REXML::XPath.first(root, 'blti:custom', LTI_NAMESPACES)
111
- set_properties(@custom_params, custom)
112
- end
113
-
114
- REXML::XPath.each(root, 'blti:extensions', LTI_NAMESPACES) do |vendor_ext_node|
115
- platform = vendor_ext_node.attributes['platform']
116
- properties = {}
117
- set_properties(properties, vendor_ext_node)
118
- set_options(properties, vendor_ext_node)
119
- self.set_ext_params(platform, properties)
120
- end
121
-
122
- end
123
- end
124
-
125
- # Generate XML from the current settings
126
- def to_xml(opts = {})
127
- raise IMS::LTI::InvalidLTIConfigError, "A launch url is required for an LTI configuration." unless self.launch_url || self.secure_launch_url
128
-
129
- builder = Builder::XmlMarkup.new(:indent => opts[:indent] || 0)
130
- builder.instruct!
131
- builder.cartridge_basiclti_link("xmlns" => "http://www.imsglobal.org/xsd/imslticc_v1p0",
132
- "xmlns:blti" => 'http://www.imsglobal.org/xsd/imsbasiclti_v1p0',
133
- "xmlns:lticm" => 'http://www.imsglobal.org/xsd/imslticm_v1p0',
134
- "xmlns:lticp" => 'http://www.imsglobal.org/xsd/imslticp_v1p0',
135
- "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
136
- "xsi:schemaLocation" => "http://www.imsglobal.org/xsd/imslticc_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imslticc_v1p0.xsd http://www.imsglobal.org/xsd/imsbasiclti_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imsbasiclti_v1p0p1.xsd http://www.imsglobal.org/xsd/imslticm_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imslticm_v1p0.xsd http://www.imsglobal.org/xsd/imslticp_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imslticp_v1p0.xsd"
137
- ) do |blti_node|
138
-
139
- %w{title description launch_url secure_launch_url icon secure_icon}.each do |key|
140
- blti_node.blti key.to_sym, self.send(key) if self.send(key)
141
- end
142
-
143
- vendor_keys = %w{name code description url}
144
- if vendor_keys.any?{|k|self.send("vendor_#{k}")} || vendor_contact_email
145
- blti_node.blti :vendor do |v_node|
146
- vendor_keys.each do |key|
147
- v_node.lticp key.to_sym, self.send("vendor_#{key}") if self.send("vendor_#{key}")
148
- end
149
- if vendor_contact_email
150
- v_node.lticp :contact do |c_node|
151
- c_node.lticp :name, vendor_contact_name
152
- c_node.lticp :email, vendor_contact_email
153
- end
154
- end
155
- end
156
- end
157
-
158
- if !@custom_params.empty?
159
- blti_node.tag!("blti:custom") do |custom_node|
160
- @custom_params.keys.sort.each do |key|
161
- val = @custom_params[key]
162
- custom_node.lticm :property, val, 'name' => key
163
- end
164
- end
165
- end
166
-
167
- if !@extensions.empty?
168
- @extensions.keys.sort.each do |ext_platform|
169
- ext_params = @extensions[ext_platform]
170
- blti_node.blti(:extensions, :platform => ext_platform) do |ext_node|
171
- ext_params.keys.sort.each do |key|
172
- nest_xml(ext_node, key, ext_params[key])
173
- end
174
- end
175
- end
176
- end
177
-
178
- blti_node.cartridge_bundle(:identifierref => @cartridge_bundle) if @cartridge_bundle
179
- blti_node.cartridge_icon(:identifierref => @cartridge_icon) if @cartridge_icon
180
-
181
- end
182
- end
183
-
184
- private
185
-
186
- def nest_xml(ext_node, key, value)
187
- if value.is_a?(Hash)
188
- ext_node.lticm(:options, :name => key) do |type_node|
189
- value.keys.sort.each do |sub_key|
190
- nest_xml(type_node, sub_key, value[sub_key])
191
- end
192
- end
193
- else
194
- ext_node.lticm :property, value, 'name' => key
195
- end
196
- end
197
-
198
- def get_node_text(node, path)
199
- if val = REXML::XPath.first(node, path, LTI_NAMESPACES)
200
- val.text
201
- else
202
- nil
203
- end
204
- end
205
-
206
- def get_node_att(node, path, att)
207
- if val = REXML::XPath.first(node, path, LTI_NAMESPACES)
208
- val.attributes[att]
209
- else
210
- nil
211
- end
212
- end
213
-
214
- def set_properties(hash, node)
215
- REXML::XPath.each(node, 'lticm:property', LTI_NAMESPACES) do |prop|
216
- hash[prop.attributes['name']] = prop.text
217
- end
218
- end
219
-
220
- def set_options(hash, node)
221
- REXML::XPath.each(node, 'lticm:options', LTI_NAMESPACES) do |options_node|
222
- opt_name = options_node.attributes['name']
223
- options = {}
224
- set_properties(options, options_node)
225
- set_options(options, options_node)
226
- hash[opt_name] = options
227
- end
228
- end
229
-
230
- end
231
- end
@@ -1,86 +0,0 @@
1
- module IMS::LTI
2
- # Class for implementing an LTI Tool Consumer
3
- class ToolConsumer < ToolBase
4
- attr_accessor :launch_url, :timestamp, :nonce
5
-
6
- # Create a new ToolConsumer
7
- #
8
- # @param consumer_key [String] The OAuth consumer key
9
- # @param consumer_secret [String] The OAuth consumer secret
10
- # @param params [Hash] Set the launch parameters as described in LaunchParams
11
- def initialize(consumer_key, consumer_secret, params={})
12
- super(consumer_key, consumer_secret, params)
13
- @launch_url = params['launch_url']
14
- end
15
-
16
- def process_post_request(post_request)
17
- request = extend_outcome_request(OutcomeRequest.new)
18
- request.process_post_request(post_request)
19
- end
20
-
21
- # Set launch data from a ToolConfig
22
- #
23
- # @param config [ToolConfig]
24
- def set_config(config)
25
- @launch_url ||= config.secure_launch_url
26
- @launch_url ||= config.launch_url
27
- # any parameters already set will take priority
28
- @custom_params = config.custom_params.merge(@custom_params)
29
- end
30
-
31
- # Check if the required parameters for a tool launch are set
32
- def has_required_params?
33
- @consumer_key && @consumer_secret && @resource_link_id && @launch_url
34
- end
35
-
36
- # Generate the launch data including the necessary OAuth information
37
- #
38
- #
39
- def generate_launch_data
40
- raise IMS::LTI::InvalidLTIConfigError, "Not all required params set for tool launch" unless has_required_params?
41
-
42
- params = self.to_params
43
- params['lti_version'] ||= 'LTI-1p0'
44
- params['lti_message_type'] ||= 'basic-lti-launch-request'
45
- uri = URI.parse(@launch_url)
46
-
47
- if uri.port == uri.default_port
48
- host = uri.host
49
- else
50
- host = "#{uri.host}:#{uri.port}"
51
- end
52
-
53
- consumer = OAuth::Consumer.new(@consumer_key, @consumer_secret, {
54
- :site => "#{uri.scheme}://#{host}",
55
- :signature_method => "HMAC-SHA1"
56
- })
57
-
58
- path = uri.path
59
- path = '/' if path.empty?
60
- if uri.query && uri.query != ''
61
- CGI.parse(uri.query).each do |query_key, query_values|
62
- unless params[query_key]
63
- params[query_key] = query_values.first
64
- end
65
- end
66
- end
67
- options = {
68
- :scheme => 'body',
69
- :timestamp => @timestamp,
70
- :nonce => @nonce
71
- }
72
- request = consumer.create_signed_request(:post, path, nil, options, params)
73
-
74
- # the request is made by a html form in the user's browser, so we
75
- # want to revert the escapage and return the hash of post parameters ready
76
- # for embedding in a html view
77
- hash = {}
78
- request.body.split(/&/).each do |param|
79
- key, val = param.split(/=/).map { |v| CGI.unescape(v) }
80
- hash[key] = val
81
- end
82
- hash
83
- end
84
-
85
- end
86
- end
@@ -1,145 +0,0 @@
1
- require 'cgi'
2
-
3
- module IMS::LTI
4
-
5
- # Class for implementing an LTI Tool Provider
6
- #
7
- # # Initialize TP object with OAuth creds and post parameters
8
- # provider = IMS::LTI::ToolProvider.new(consumer_key, consumer_secret, params)
9
- #
10
- # # Verify OAuth signature by passing the request object
11
- # if provider.valid_request?(request)
12
- # # success
13
- # else
14
- # # handle invalid OAuth
15
- # end
16
- #
17
- # if provider.outcome_service?
18
- # # ready for grade write-back
19
- # else
20
- # # normal tool launch without grade write-back
21
- # end
22
- #
23
- # If the tool was launch as an outcome service you can POST a score to the TC.
24
- # The POST calls all return an OutcomeResponse object which can be used to
25
- # handle the response appropriately.
26
- #
27
- # # post the score to the TC, score should be a float >= 0.0 and <= 1.0
28
- # # this returns an OutcomeResponse object
29
- # response = provider.post_replace_result!(score)
30
- # if response.success?
31
- # # grade write worked
32
- # elsif response.processing?
33
- # elsif response.unsupported?
34
- # else
35
- # # failed
36
- # end
37
-
38
- class ToolProvider < ToolBase
39
- # List of outcome requests made through this instance
40
-
41
- include DeprecatedRoleChecks
42
- include RoleChecks
43
- attr_accessor :outcome_requests
44
- # Message to be sent back to the ToolConsumer when the user returns
45
- attr_accessor :lti_errormsg, :lti_errorlog, :lti_msg, :lti_log
46
-
47
- # Create a new ToolProvider
48
- #
49
- # @param consumer_key [String] The OAuth consumer key
50
- # @param consumer_secret [String] The OAuth consumer secret
51
- # @param params [Hash] Set the launch parameters as described in LaunchParams
52
- def initialize(consumer_key, consumer_secret, params={})
53
- super(consumer_key, consumer_secret, params)
54
- @outcome_requests = []
55
- end
56
-
57
- # Check if the request was an LTI Launch Request
58
- def launch_request?
59
- lti_message_type == 'basic-lti-launch-request'
60
- end
61
-
62
- # Check if the Tool Launch expects an Outcome Result
63
- def outcome_service?
64
- !!(lis_outcome_service_url && lis_result_sourcedid)
65
- end
66
-
67
- # Return the full, given, or family name if set
68
- def username(default=nil)
69
- lis_person_name_given || lis_person_name_family || lis_person_name_full || default
70
- end
71
-
72
- # POSTs the given score to the Tool Consumer with a replaceResult
73
- #
74
- # Creates a new OutcomeRequest object and stores it in @outcome_requests
75
- #
76
- # @return [OutcomeResponse] the response from the Tool Consumer
77
- def post_replace_result!(score)
78
- new_request.post_replace_result!(score)
79
- end
80
-
81
- # POSTs a delete request to the Tool Consumer
82
- #
83
- # Creates a new OutcomeRequest object and stores it in @outcome_requests
84
- #
85
- # @return [OutcomeResponse] the response from the Tool Consumer
86
- def post_delete_result!
87
- new_request.post_delete_result!
88
- end
89
-
90
- # POSTs the given score to the Tool Consumer with a replaceResult, the
91
- # returned OutcomeResponse will have the score
92
- #
93
- # Creates a new OutcomeRequest object and stores it in @outcome_requests
94
- #
95
- # @return [OutcomeResponse] the response from the Tool Consumer
96
- def post_read_result!
97
- new_request.post_read_result!
98
- end
99
-
100
- # Returns the most recent OutcomeRequest
101
- def last_outcome_request
102
- @outcome_requests.last
103
- end
104
-
105
- # Convenience method for whether the last OutcomeRequest was successful
106
- def last_outcome_success?
107
- last_outcome_request && last_outcome_request.outcome_post_successful?
108
- end
109
-
110
- # If the Tool Consumer sent a URL for the user to return to this will add
111
- # any set messages to the URL.
112
- #
113
- # Example:
114
- #
115
- # tc = IMS::LTI::tc.new
116
- # tc.launch_presentation_return_url = "http://example.com/return"
117
- # tc.lti_msg = "hi there"
118
- # tc.lti_errorlog = "error happens"
119
- #
120
- # tc.build_return_url # => "http://example.com/return?lti_msg=hi%20there&lti_errorlog=error%20happens"
121
- def build_return_url
122
- return nil unless launch_presentation_return_url
123
- messages = []
124
- %w{lti_errormsg lti_errorlog lti_msg lti_log}.each do |m|
125
- if message = self.send(m)
126
- messages << "#{m}=#{CGI.escape(message)}"
127
- end
128
- end
129
- q_string = messages.any? ? ("?" + messages.join("&")) : ''
130
- launch_presentation_return_url + q_string
131
- end
132
-
133
- private
134
-
135
- def new_request
136
- @outcome_requests << OutcomeRequest.new(:consumer_key => @consumer_key,
137
- :consumer_secret => @consumer_secret,
138
- :lis_outcome_service_url => lis_outcome_service_url,
139
- :lis_result_sourcedid =>lis_result_sourcedid)
140
-
141
- extend_outcome_request(@outcome_requests.last)
142
- end
143
-
144
- end
145
- end