ims-lti 2.0.0.beta.18 → 2.0.0.beta.19
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.
- checksums.yaml +4 -4
- data/lib/ims/lti.rb +3 -0
- data/lib/ims/lti/errors.rb +5 -0
- data/lib/ims/lti/errors/invalid_lti_config_error.rb +4 -0
- data/lib/ims/lti/models/messages/basic_lti_launch_request.rb +1 -1
- data/lib/ims/lti/services.rb +1 -0
- data/lib/ims/lti/services/service_definition.rb +10 -8
- data/lib/ims/lti/services/service_lookup.rb +25 -23
- data/lib/ims/lti/services/tool_config.rb +225 -0
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: abb31c30499a793de7682bdd02c9e1e8a71f1500
|
4
|
+
data.tar.gz: a4ed57fa69bf00c8128ec2986d47d9d3ef2c7c7b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 473cafa07eada8291040ecc68b7b5ee5815914f72522576fdd442fbce7ed91bbbfed19d150a55f9178aaef3564d9ec53dce72c4a48214102b7c079c5cc2eeb6e
|
7
|
+
data.tar.gz: 70e79ca8a862600a05508314b99a281a4800600038f4e106d92bf1aebc140df14ccf1d4d6011fc2aea8ad5e6ff3b2228ffab29cef6268762a4cdf8a074be1300
|
data/lib/ims/lti.rb
CHANGED
@@ -3,11 +3,14 @@ require 'securerandom'
|
|
3
3
|
require 'simple_oauth'
|
4
4
|
require 'faraday'
|
5
5
|
require 'faraday_middleware'
|
6
|
+
require 'builder'
|
7
|
+
require 'rexml/document'
|
6
8
|
|
7
9
|
module IMS
|
8
10
|
module LTI
|
9
11
|
require_relative 'lti/models'
|
10
12
|
require_relative 'lti/converters'
|
11
13
|
require_relative 'lti/services'
|
14
|
+
require_relative 'lti/errors'
|
12
15
|
end
|
13
16
|
end
|
@@ -10,7 +10,7 @@ module IMS::LTI::Models::Messages
|
|
10
10
|
:lis_course_offering_sourcedid, :lis_course_section_sourcedid,
|
11
11
|
:tool_consumer_info_product_family_code, :tool_consumer_info_product_family_version,
|
12
12
|
:tool_consumer_instance_name, :tool_consumer_instance_description, :tool_consumer_instance_url,
|
13
|
-
:tool_consumer_instance_contact_email
|
13
|
+
:tool_consumer_instance_contact_email, :tool_consumer_info_version
|
14
14
|
|
15
15
|
MESSAGE_TYPE = 'basic-lti-launch-request'
|
16
16
|
|
data/lib/ims/lti/services.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module IMS::LTI::Services
|
2
|
+
class ServiceDefinition
|
3
|
+
attr_reader :name, :formats, :paramater_variables
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
def initialize(name, formats, parameter_variables = [])
|
6
|
+
@name = name
|
7
|
+
@formats = formats
|
8
|
+
@parameter_variables = parameter_variables
|
9
|
+
end
|
9
10
|
|
10
|
-
end
|
11
|
+
end
|
12
|
+
end
|
@@ -1,30 +1,32 @@
|
|
1
|
-
|
1
|
+
module IMS::LTI::Services
|
2
|
+
class ServiceLookup
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
TOOL_SETTING_SERVICE = ServiceDefinition.new(
|
5
|
+
'ToolSettingsContainer Service',
|
6
|
+
%w(application/vnd.ims.lti.v2.toolsettings+json application/vnd.ims.lti.v2.toolsettings.simple+json),
|
7
|
+
%w(LtiLink.custom.url ToolProxyBinding.custom.url ToolProxy.custom.url)
|
8
|
+
)
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
TOOL_CONSUMER_PROFILE_SERVICE = ServiceDefinition.new(
|
11
|
+
'ToolConsumerProfile Service',
|
12
|
+
%w(application/vnd.ims.lti.v2.toolconsumerprofile+json),
|
13
|
+
[]
|
14
|
+
)
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
TOOL_PROXY_SERVICE = ServiceDefinition.new(
|
17
|
+
'ToolProxy Service',
|
18
|
+
%w(application/vnd.ims.lti.v2.toolproxy+json),
|
19
|
+
[]
|
20
|
+
)
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
def self.services
|
23
|
+
[TOOL_SETTING_SERVICE, TOOL_CONSUMER_PROFILE_SERVICE, TOOL_PROXY_SERVICE]
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
def self.lookup(format)
|
27
|
+
services.select { |service| service.formats.include? format }
|
28
|
+
end
|
28
29
|
|
29
30
|
|
30
|
-
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
module IMS::LTI::Services
|
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::Services::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::Services::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
|
+
REXML::XPath.each(vendor_ext_node, 'lticm:options', LTI_NAMESPACES) do |options_node|
|
119
|
+
opt_name = options_node.attributes['name']
|
120
|
+
options = {}
|
121
|
+
set_properties(options, options_node)
|
122
|
+
properties[opt_name] = options
|
123
|
+
end
|
124
|
+
|
125
|
+
self.set_ext_params(platform, properties)
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Generate XML from the current settings
|
132
|
+
def to_xml(opts = {})
|
133
|
+
raise IMS::LTI::Errors::InvalidLTIConfigError, "A launch url is required for an LTI configuration." unless self.launch_url || self.secure_launch_url
|
134
|
+
|
135
|
+
builder = Builder::XmlMarkup.new(:indent => opts[:indent] || 0)
|
136
|
+
builder.instruct!
|
137
|
+
builder.cartridge_basiclti_link("xmlns" => "http://www.imsglobal.org/xsd/imslticc_v1p0",
|
138
|
+
"xmlns:blti" => 'http://www.imsglobal.org/xsd/imsbasiclti_v1p0',
|
139
|
+
"xmlns:lticm" => 'http://www.imsglobal.org/xsd/imslticm_v1p0',
|
140
|
+
"xmlns:lticp" => 'http://www.imsglobal.org/xsd/imslticp_v1p0',
|
141
|
+
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
|
142
|
+
"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"
|
143
|
+
) do |blti_node|
|
144
|
+
|
145
|
+
%w{title description launch_url secure_launch_url icon secure_icon}.each do |key|
|
146
|
+
blti_node.blti key.to_sym, self.send(key) if self.send(key)
|
147
|
+
end
|
148
|
+
|
149
|
+
vendor_keys = %w{name code description url}
|
150
|
+
if vendor_keys.any? { |k| self.send("vendor_#{k}") } || vendor_contact_email
|
151
|
+
blti_node.blti :vendor do |v_node|
|
152
|
+
vendor_keys.each do |key|
|
153
|
+
v_node.lticp key.to_sym, self.send("vendor_#{key}") if self.send("vendor_#{key}")
|
154
|
+
end
|
155
|
+
if vendor_contact_email
|
156
|
+
v_node.lticp :contact do |c_node|
|
157
|
+
c_node.lticp :name, vendor_contact_name
|
158
|
+
c_node.lticp :email, vendor_contact_email
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
if !@custom_params.empty?
|
165
|
+
blti_node.tag!("blti:custom") do |custom_node|
|
166
|
+
@custom_params.keys.sort.each do |key|
|
167
|
+
val = @custom_params[key]
|
168
|
+
custom_node.lticm :property, val, 'name' => key
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
if !@extensions.empty?
|
174
|
+
@extensions.keys.sort.each do |ext_platform|
|
175
|
+
ext_params = @extensions[ext_platform]
|
176
|
+
blti_node.blti(:extensions, :platform => ext_platform) do |ext_node|
|
177
|
+
ext_params.keys.sort.each do |key|
|
178
|
+
val = ext_params[key]
|
179
|
+
if val.is_a?(Hash)
|
180
|
+
ext_node.lticm(:options, :name => key) do |type_node|
|
181
|
+
val.keys.sort.each do |p_key|
|
182
|
+
p_val = val[p_key]
|
183
|
+
type_node.lticm :property, p_val, 'name' => p_key
|
184
|
+
end
|
185
|
+
end
|
186
|
+
else
|
187
|
+
ext_node.lticm :property, val, 'name' => key
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
blti_node.cartridge_bundle(:identifierref => @cartridge_bundle) if @cartridge_bundle
|
195
|
+
blti_node.cartridge_icon(:identifierref => @cartridge_icon) if @cartridge_icon
|
196
|
+
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
|
202
|
+
def get_node_text(node, path)
|
203
|
+
if val = REXML::XPath.first(node, path, LTI_NAMESPACES)
|
204
|
+
val.text
|
205
|
+
else
|
206
|
+
nil
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def get_node_att(node, path, att)
|
211
|
+
if val = REXML::XPath.first(node, path, LTI_NAMESPACES)
|
212
|
+
val.attributes[att]
|
213
|
+
else
|
214
|
+
nil
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def set_properties(hash, node)
|
219
|
+
REXML::XPath.each(node, 'lticm:property', LTI_NAMESPACES) do |prop|
|
220
|
+
hash[prop.attributes['name']] = prop.text
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
225
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ims-lti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.beta.
|
4
|
+
version: 2.0.0.beta.19
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Instructure
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: simple_oauth
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: builder
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: rake
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -107,6 +121,8 @@ files:
|
|
107
121
|
- lib/ims/lti.rb
|
108
122
|
- lib/ims/lti/converters.rb
|
109
123
|
- lib/ims/lti/converters/time_json_converter.rb
|
124
|
+
- lib/ims/lti/errors.rb
|
125
|
+
- lib/ims/lti/errors/invalid_lti_config_error.rb
|
110
126
|
- lib/ims/lti/models.rb
|
111
127
|
- lib/ims/lti/models/base_url_choice.rb
|
112
128
|
- lib/ims/lti/models/base_url_selector.rb
|
@@ -141,6 +157,7 @@ files:
|
|
141
157
|
- lib/ims/lti/services.rb
|
142
158
|
- lib/ims/lti/services/service_definition.rb
|
143
159
|
- lib/ims/lti/services/service_lookup.rb
|
160
|
+
- lib/ims/lti/services/tool_config.rb
|
144
161
|
- lib/ims/lti/services/tool_proxy_registration_service.rb
|
145
162
|
- lib/ims/lti/version.rb
|
146
163
|
homepage: http://github.com/instructure/ims-lti
|