lti2 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/lti2/version.rb +1 -1
- data/lti2_tc/Gemfile +14 -0
- data/lti2_tc/Gemfile.lock +94 -0
- data/lti2_tc/MIT-LICENSE +20 -0
- data/lti2_tc/README.rdoc +3 -0
- data/lti2_tc/Rakefile +34 -0
- data/lti2_tc/app/assets/javascripts/lti2_tc/application.js +13 -0
- data/lti2_tc/app/assets/stylesheets/lti2_tc/application.css +13 -0
- data/lti2_tc/app/controllers/lti2_tc/application_controller.rb +13 -0
- data/lti2_tc/app/controllers/lti2_tc/deployment_requests_controller.rb +9 -0
- data/lti2_tc/app/controllers/lti2_tc/tool_consumer_profiles_controller.rb +23 -0
- data/lti2_tc/app/controllers/lti2_tc/tool_proxies_controller.rb +8 -0
- data/lti2_tc/app/controllers/lti2_tc/tool_settings_controller.rb +215 -0
- data/lti2_tc/app/controllers/lti2_tc/tools_controller.rb +361 -0
- data/lti2_tc/app/models/lti2_tc/deployment_request.rb +43 -0
- data/lti2_tc/app/models/lti2_tc/link.rb +24 -0
- data/lti2_tc/app/models/lti2_tc/registry.rb +4 -0
- data/lti2_tc/app/models/lti2_tc/resource.rb +6 -0
- data/lti2_tc/app/models/lti2_tc/tool.rb +12 -0
- data/lti2_tc/app/models/lti2_tc/tool_consumer_profile.rb +4 -0
- data/lti2_tc/app/models/lti2_tc/tool_consumer_registry.rb +24 -0
- data/lti2_tc/app/models/lti2_tc/tool_registration.rb +83 -0
- data/lti2_tc/app/models/lti2_tc/tool_setting.rb +61 -0
- data/lti2_tc/app/services/lti2_tc/authorizer.rb +36 -0
- data/lti2_tc/app/services/lti2_tc/lti2_launch.rb +259 -0
- data/lti2_tc/app/views/layouts/lti2_tc/application.html.erb +14 -0
- data/lti2_tc/app/views/lti2_tc/deployment_requests/show.html.erb +2 -0
- data/lti2_tc/app/views/lti2_tc/tool_consumer_profiles/index.html.erb +2 -0
- data/lti2_tc/app/views/lti2_tc/tool_consumer_profiles/show.html.erb +2 -0
- data/lti2_tc/app/views/lti2_tc/tool_proxies_controller/index.html.erb +2 -0
- data/lti2_tc/app/views/lti2_tc/tool_settings_controller/show.html.erb +2 -0
- data/lti2_tc/app/views/lti2_tc/tool_settings_controller/update.html.erb +2 -0
- data/lti2_tc/app/views/lti2_tc/tools_controller/create.html.erb +2 -0
- data/lti2_tc/app/views/lti2_tc/tools_controller/show.html.erb +2 -0
- data/lti2_tc/bin/rails +8 -0
- data/lti2_tc/config/routes.rb +20 -0
- data/lti2_tc/db/migrate/20140826141858_create_lti2_tool_consumer_tables.rb +94 -0
- data/lti2_tc/db/migrate/20140826150753_create_lti2_tc_resources.rb +37 -0
- data/lti2_tc/db/migrate/20141027193548_insert_tool_guid.rb +5 -0
- data/lti2_tc/lib/lti2_tc.rb +4 -0
- data/lti2_tc/lib/lti2_tc/engine.rb +5 -0
- data/lti2_tc/lib/lti2_tc/version.rb +3 -0
- data/lti2_tc/lib/tasks/lti2_tc_tasks.rake +4 -0
- data/lti2_tc/lti2_tc.gemspec +27 -0
- data/lti2_tc/test/controllers/lti2_tc/deployment_requests_controller_test.rb +11 -0
- data/lti2_tc/test/controllers/lti2_tc/tool_consumer_profiles_controller_test.rb +16 -0
- data/lti2_tc/test/controllers/lti2_tc/tool_proxies_controller_controller_test.rb +11 -0
- data/lti2_tc/test/controllers/lti2_tc/tool_settings_controller_controller_test.rb +16 -0
- data/lti2_tc/test/controllers/lti2_tc/tools_controller_controller_test.rb +16 -0
- data/lti2_tc/test/dummy/README.rdoc +28 -0
- data/lti2_tc/test/dummy/Rakefile +6 -0
- data/lti2_tc/test/dummy/app/assets/javascripts/application.js +13 -0
- data/lti2_tc/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/lti2_tc/test/dummy/app/controllers/application_controller.rb +5 -0
- data/lti2_tc/test/dummy/app/helpers/application_helper.rb +2 -0
- data/lti2_tc/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/lti2_tc/test/dummy/bin/bundle +3 -0
- data/lti2_tc/test/dummy/bin/rails +4 -0
- data/lti2_tc/test/dummy/bin/rake +4 -0
- data/lti2_tc/test/dummy/config.ru +4 -0
- data/lti2_tc/test/dummy/config/application.rb +23 -0
- data/lti2_tc/test/dummy/config/boot.rb +5 -0
- data/lti2_tc/test/dummy/config/database.yml +39 -0
- data/lti2_tc/test/dummy/config/environment.rb +5 -0
- data/lti2_tc/test/dummy/config/environments/development.rb +29 -0
- data/lti2_tc/test/dummy/config/environments/production.rb +80 -0
- data/lti2_tc/test/dummy/config/environments/test.rb +36 -0
- data/lti2_tc/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/lti2_tc/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/lti2_tc/test/dummy/config/initializers/inflections.rb +16 -0
- data/lti2_tc/test/dummy/config/initializers/mime_types.rb +5 -0
- data/lti2_tc/test/dummy/config/initializers/secret_token.rb +12 -0
- data/lti2_tc/test/dummy/config/initializers/session_store.rb +3 -0
- data/lti2_tc/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/lti2_tc/test/dummy/config/locales/en.yml +23 -0
- data/lti2_tc/test/dummy/config/routes.rb +4 -0
- data/lti2_tc/test/dummy/db/database.yml +39 -0
- data/lti2_tc/test/dummy/db/migrate/20131121225051_create_lti2_tc_registries.lti2_tc.rb +55 -0
- data/lti2_tc/test/dummy/db/schema.rb +68 -0
- data/lti2_tc/test/dummy/public/404.html +58 -0
- data/lti2_tc/test/dummy/public/422.html +58 -0
- data/lti2_tc/test/dummy/public/500.html +57 -0
- data/lti2_tc/test/dummy/public/favicon.ico +0 -0
- data/lti2_tc/test/fixtures/lti2_tc/registries.yml +7 -0
- data/lti2_tc/test/helpers/lti2_tc/deployment_requests_helper_test.rb +6 -0
- data/lti2_tc/test/helpers/lti2_tc/tool_consumer_profiles_helper_test.rb +6 -0
- data/lti2_tc/test/helpers/lti2_tc/tool_proxies_controller_helper_test.rb +6 -0
- data/lti2_tc/test/helpers/lti2_tc/tool_settings_controller_helper_test.rb +6 -0
- data/lti2_tc/test/helpers/lti2_tc/tools_controller_helper_test.rb +6 -0
- data/lti2_tc/test/integration/navigation_test.rb +10 -0
- data/lti2_tc/test/lti2_tc_test.rb +7 -0
- data/lti2_tc/test/models/lti2_tc/registries_test.rb +9 -0
- data/lti2_tc/test/test_helper.rb +15 -0
- metadata +93 -1
@@ -0,0 +1,24 @@
|
|
1
|
+
module Lti2Tc
|
2
|
+
class ToolConsumerRegistry
|
3
|
+
|
4
|
+
attr_reader :tool_consumer_profile, :tc_deployment_url, :relaxed_oauth_check, :result_template,
|
5
|
+
:tool_consumer_name, :registry, :tool_consumer_profile_wrapper
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
registry_entries = Registry.all
|
9
|
+
@registry = {}
|
10
|
+
registry_entries.each { |entry| @registry[entry.name] = entry.content unless entry.name == 'content' }
|
11
|
+
|
12
|
+
@tc_deployment_url = registry['tc_deployment_url']
|
13
|
+
@relaxed_oauth_check = registry['relaxed_oauth_check']
|
14
|
+
@result_template = registry['result_template']
|
15
|
+
@tool_consumer_name = registry['tool_consumer_name']
|
16
|
+
|
17
|
+
# gets tcp root and returns as a json_object (optimizes successive accesses to tcp)
|
18
|
+
tcp = Lti2Commons::JsonWrapper.new registry['tool_consumer_profile_template']
|
19
|
+
tcp.substitute_text_in_all_nodes '{', '}', registry
|
20
|
+
@tool_consumer_profile_wrapper = tcp
|
21
|
+
@tool_consumer_profile = tcp.root
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Lti2Tc
|
2
|
+
class ToolRegistration
|
3
|
+
def self.remap_tool_consumer_profile( tool_consumer_profile_wrapper, tc_profile_guid )
|
4
|
+
tool_consumer_profile_wrapper.substitute_text_in_all_nodes(
|
5
|
+
'{', '}', { 'tool_consumer_profile_guid' => tc_profile_guid }
|
6
|
+
)
|
7
|
+
tool_consumer_profile_wrapper.root['guid'] = tc_profile_guid
|
8
|
+
JSON.pretty_generate( tool_consumer_profile_wrapper.root )
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.register_tool( user, deployment_request, tool_consumer_profile_wrapper, tc_deployment_url )
|
12
|
+
unless deployment_request.status == 'prepared'
|
13
|
+
if deployment_request.status == 'submitted'
|
14
|
+
raise 'This deployment request has already been send to the Tool Provider'
|
15
|
+
elsif deployment_request.status == 'registered'
|
16
|
+
raise 'This deployment request has already been provisioned. Consider a Reregistration request.'
|
17
|
+
else
|
18
|
+
raise 'Invalid status for deployment request'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
deployment_request.status = 'submitted'
|
22
|
+
deployment_request.save
|
23
|
+
|
24
|
+
tool_consumer_profile = Lti2Tc::ToolConsumerProfile.new
|
25
|
+
tool_consumer_profile.tc_profile = remap_tool_consumer_profile(
|
26
|
+
tool_consumer_profile_wrapper, deployment_request.tc_profile_guid
|
27
|
+
)
|
28
|
+
tool_consumer_profile.tc_profile_guid = deployment_request.tc_profile_guid
|
29
|
+
tool_consumer_profile.save
|
30
|
+
|
31
|
+
html_body = deployment_request.create_lti_message( tc_deployment_url, user.id )
|
32
|
+
|
33
|
+
html_body
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.reregister_tool( user, deployment_request, tool, tool_consumer_profile,
|
37
|
+
tool_consumer_profile_wrapper, tc_deployment_url, launch_presentation_return_url )
|
38
|
+
|
39
|
+
tool_proxy = tool.get_tool_proxy
|
40
|
+
tp_base_url = tool_proxy.select('tool_profile.base_url_choice',
|
41
|
+
"selector.applies_to", "MessageHandler", 'default_base_url')
|
42
|
+
singleton_messages = tool_proxy.first_at( 'tool_profile.message' )
|
43
|
+
service_endpoint = nil
|
44
|
+
singleton_messages.each { |singleton_message|
|
45
|
+
message_types = singleton_message['message_type']
|
46
|
+
if message_types.include? 'ToolProxyReregistrationRequest'
|
47
|
+
service_endpoint = singleton_message['path']
|
48
|
+
break
|
49
|
+
end
|
50
|
+
}
|
51
|
+
raise 'No message handler found for ToolProxyRegistrationRequest' unless service_endpoint
|
52
|
+
|
53
|
+
# deployment_request = DeploymentRequest.new
|
54
|
+
deployment_request.status = 'reregistering'
|
55
|
+
deployment_request.save
|
56
|
+
|
57
|
+
# tool_consumer_profile = ToolConsumerProfile.new
|
58
|
+
current_tcp = Lti2Tc::ToolConsumerProfile.where(:tc_profile_guid => deployment_request.tc_profile_guid).first
|
59
|
+
tool_consumer_profile = current_tcp if current_tcp.present?
|
60
|
+
tool_consumer_profile.tc_profile = remap_tool_consumer_profile(
|
61
|
+
tool_consumer_profile_wrapper, deployment_request.tc_profile_guid
|
62
|
+
)
|
63
|
+
tool_consumer_profile.tc_profile_guid = deployment_request.tc_profile_guid
|
64
|
+
tool_consumer_profile.save
|
65
|
+
|
66
|
+
# convert parameters
|
67
|
+
parameters = {}
|
68
|
+
tc_base_url = tc_deployment_url
|
69
|
+
parameters['tc_profile_url'] = "#{tc_base_url}/lti2_tc/tool_consumer_profiles/#{tool_consumer_profile.tc_profile_guid}"
|
70
|
+
parameters['launch_presentation_return_url'] = launch_presentation_return_url
|
71
|
+
parameters['lti_message_type'] = 'ToolProxyReregistrationRequest'
|
72
|
+
|
73
|
+
key = tool_proxy.first_at( 'tool_proxy_guid' )
|
74
|
+
secret = tool_proxy.first_at( 'security_contract.shared_secret' )
|
75
|
+
|
76
|
+
signed_request = Signer::create_signed_request( service_endpoint, 'post', key, secret, parameters )
|
77
|
+
puts "TC Signed Request: #{signed_request.signature_base_string}"
|
78
|
+
body = MessageSupport::create_lti_message_body( service_endpoint, parameters,
|
79
|
+
Rails.application.config.wire_log, 'Reregister Tool' )
|
80
|
+
body
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Lti2Tc
|
2
|
+
class ToolSetting < ActiveRecord::Base
|
3
|
+
belongs_to :scopeable, :polymorphic => true
|
4
|
+
|
5
|
+
COLLECTION_TYPES = [ :Tool, :Context, :Ltilink ]
|
6
|
+
|
7
|
+
def self.collection_index( collection_type )
|
8
|
+
COLLECTION_TYPES.find_index( collection_type.to_sym )
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.create_uri( tool_id, scopeable_type, scopeable_id=nil )
|
12
|
+
tool_consumer_registry = Rails.application.config.tool_consumer_registry
|
13
|
+
result = "#{tool_consumer_registry.tc_deployment_url}/lti2_tc/tool_settings/#{tool_id}"
|
14
|
+
if scopeable_type == 'Context'
|
15
|
+
result << "/context/#{scopeable_id}"
|
16
|
+
elsif scopeable_type == 'Ltilink'
|
17
|
+
result << "/ltilink/#{scopeable_id}"
|
18
|
+
end
|
19
|
+
result
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.create_reachable_uris( tool_id, scopeable_type, scopeable_id=nil )
|
23
|
+
result = {}
|
24
|
+
start_index = self.collection_index( scopeable_type )
|
25
|
+
(start_index...0).each do |index|
|
26
|
+
case index
|
27
|
+
when 0
|
28
|
+
result[:Tool] = self.create_uri( tool_id, 'Tool' )
|
29
|
+
when 1
|
30
|
+
result[:Context] = self.create_uri( tool_id, 'Context', scopeable_id )
|
31
|
+
when 2
|
32
|
+
result[:Ltilink] = self.create_uri( tool_id, 'Ltilink', scopeable_id )
|
33
|
+
link = Lti2Tc::Link.find( scopeable_id )
|
34
|
+
# for next iteration
|
35
|
+
scopeable_id = link.course.id
|
36
|
+
end
|
37
|
+
end
|
38
|
+
result
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.parse_uri( path )
|
42
|
+
m = /^\/lti2_tc\/tool_settings\/(.*)\/context\/(.*)$/.match(path)
|
43
|
+
if m.present?
|
44
|
+
return [ 'Context', m[1], m[2].to_i ]
|
45
|
+
end
|
46
|
+
m = /^\/lti2_tc\/tool_settings\/(.*)\/ltilink\/(.*)$/.match(path)
|
47
|
+
if m.present?
|
48
|
+
return [ 'Ltilink', m[1], m[2].to_i ]
|
49
|
+
end
|
50
|
+
m = /^\/lti2_tc\/tool_settings\/(.*)$/.match(path)
|
51
|
+
if m.present?
|
52
|
+
return [ 'Tool', m[1], nil ]
|
53
|
+
end
|
54
|
+
return [ nil, nil, nil ]
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_uri
|
58
|
+
ToolSetting.create_uri( self.tool_id, self.scopeable_type, self.scopeable_id )
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
include Lti2Commons
|
2
|
+
include Signer
|
3
|
+
include MessageSupport
|
4
|
+
include OAuth::OAuthProxy
|
5
|
+
|
6
|
+
module Lti2Tc
|
7
|
+
class Authorizer
|
8
|
+
def self.pre_process_tenant request
|
9
|
+
rack_parameters = OAuthRequest.collect_rack_parameters request
|
10
|
+
key = rack_parameters[:oauth_consumer_key]
|
11
|
+
@tool = Lti2Tc::Tool.where( :key => key ).first
|
12
|
+
secret = @tool.secret
|
13
|
+
self.validate( request, secret )
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.validate( request, secret )
|
17
|
+
# OAuth check here
|
18
|
+
tool_consumer_registry = Rails.application.config.tool_consumer_registry
|
19
|
+
unless tool_consumer_registry.relaxed_oauth_check == 'true'
|
20
|
+
request_wrapper = OAuthRequest.create_from_rack_request( request )
|
21
|
+
begin
|
22
|
+
request_wrapper.verify_signature?( secret, Rails.application.config.nonce_cache, false )
|
23
|
+
@oauth_error = false
|
24
|
+
return
|
25
|
+
rescue
|
26
|
+
# puts "Secret: #{secret}"
|
27
|
+
puts "TP Signed Request: #{request_wrapper.signature_base_string}"
|
28
|
+
@oauth_error = true
|
29
|
+
end
|
30
|
+
|
31
|
+
puts "request_wrapper: #{request_wrapper.request['parameters'].inspect}"
|
32
|
+
end
|
33
|
+
@oauth_error
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
module Lti2Tc
|
2
|
+
|
3
|
+
class Lti2Launch
|
4
|
+
|
5
|
+
def initialize( user, link, resource, course, enrollment, return_url, &substitute_final )
|
6
|
+
@user = user
|
7
|
+
@link = link
|
8
|
+
@resource = resource
|
9
|
+
@course = course
|
10
|
+
@enrollment = enrollment
|
11
|
+
@return_url = return_url
|
12
|
+
@tool_proxy = resource.tool.get_tool_proxy
|
13
|
+
@substitute_final = substitute_final
|
14
|
+
end
|
15
|
+
|
16
|
+
def launch( open_in_external_window=true )
|
17
|
+
tool = @resource.tool
|
18
|
+
tool_name = @tool_proxy.first_at('tool_profile.product_instance.product_info.product_name.default_value')
|
19
|
+
raise "Tool #{tool_name} is currently disabled" unless tool.is_enabled
|
20
|
+
|
21
|
+
base_url = @tool_proxy.select('tool_profile.base_url_choice', 'selector.applies_to',
|
22
|
+
'MessageHandler', 'default_base_url' )
|
23
|
+
resource_type = @resource.resource_type
|
24
|
+
resource_handler_node = @tool_proxy.search( 'tool_profile.resource_handler',
|
25
|
+
{ 'resource_type' => { 'code' => resource_type } }, '@' )
|
26
|
+
resource_handler = JsonWrapper.new resource_handler_node
|
27
|
+
message = resource_handler.search( '@..message', { 'message_type' => 'basic-lti-launch-request' }, '@' )
|
28
|
+
path = message['path']
|
29
|
+
m = /(.*)\?(.*)/.match( path )
|
30
|
+
if m.present?
|
31
|
+
final_qs = '?'
|
32
|
+
parmstr = CGI.unescapeHTML( m[2] )
|
33
|
+
parm_hash = Rack::Utils::parse_query( parmstr )
|
34
|
+
parm_hash.each_pair do |k,v|
|
35
|
+
final_qs += "#{k}="
|
36
|
+
final_qs += Rack::Utils.escape( v )
|
37
|
+
final_qs += '&'
|
38
|
+
end
|
39
|
+
# conformance issue: remove trailing ampersand
|
40
|
+
final_qs = final_qs[0..final_qs.length-2] if final_qs[-1] == '&'
|
41
|
+
final_path = m[1] + final_qs
|
42
|
+
else
|
43
|
+
final_path = path
|
44
|
+
end
|
45
|
+
|
46
|
+
tp_parameters = message['parameter']
|
47
|
+
service_endpoint = base_url + final_path
|
48
|
+
|
49
|
+
if @enrollment
|
50
|
+
role = @enrollment.role
|
51
|
+
else
|
52
|
+
role = @user.role
|
53
|
+
end
|
54
|
+
|
55
|
+
tool_consumer_registry = Rails.application.config.tool_consumer_registry
|
56
|
+
tcp_wrapper = tool_consumer_registry.tool_consumer_profile_wrapper
|
57
|
+
parameters = {
|
58
|
+
'lti_version' => @tool_proxy.first_at('lti_version'),
|
59
|
+
'lti_message_type' => 'basic-lti-launch-request',
|
60
|
+
'resource_link_id' => @link.id.to_s,
|
61
|
+
'user_id' => @user.id.to_s,
|
62
|
+
'roles' => role,
|
63
|
+
'launch_presentation_return_url' => "#{tool_consumer_registry.tc_deployment_url}#{@return_url}",
|
64
|
+
'launch_presentation_document_target' => 'window',
|
65
|
+
|
66
|
+
# optional
|
67
|
+
'context_id' => @course.id.to_s,
|
68
|
+
'launch_presentation_locale' => 'en_US',
|
69
|
+
|
70
|
+
# for conformance
|
71
|
+
'context_label' => @course.course_label,
|
72
|
+
'context_title' => @course.course_title,
|
73
|
+
'lis_course_section_sourcedid' => @course.id.to_s,
|
74
|
+
'tool_consumer_info_product_family_code' => "#{tcp_wrapper.first_at("product_instance.product_info.product_family.code")}",
|
75
|
+
'tool_consumer_info_version' => "#{tcp_wrapper.first_at("product_instance.product_info.product_version")}",
|
76
|
+
}
|
77
|
+
|
78
|
+
if @link.is_name_permission
|
79
|
+
parameters['lis_person_sourcedid'] = @user.id.to_s
|
80
|
+
parameters['lis_person_name_given'] = @user.first_name
|
81
|
+
parameters['lis_person_name_family'] = @user.last_name
|
82
|
+
parameters['lis_person_name_full'] = "#{@user.first_name} #{@user.last_name}"
|
83
|
+
end
|
84
|
+
|
85
|
+
if @link.is_email_permission
|
86
|
+
parameters['lis_person_contact_email_primary'] = @user.email
|
87
|
+
end
|
88
|
+
|
89
|
+
if role =~ /Mentor$/
|
90
|
+
parameters['role_scope_mentor'] = url_encode_users(@user.mentor)
|
91
|
+
end
|
92
|
+
|
93
|
+
# add parameters from ToolProxy
|
94
|
+
for parameter in tp_parameters
|
95
|
+
name = parameter['name']
|
96
|
+
if parameter.has_key? 'fixed'
|
97
|
+
value = parameter['fixed']
|
98
|
+
elsif parameter.has_key? 'variable'
|
99
|
+
value = parameter['variable']
|
100
|
+
else
|
101
|
+
value = '[link-to-resolve]'
|
102
|
+
end
|
103
|
+
parameters[name] = value
|
104
|
+
end
|
105
|
+
|
106
|
+
# add parameters from Link
|
107
|
+
link_parameter_json = @link.link_parameters
|
108
|
+
if link_parameter_json
|
109
|
+
link_parameters = JSON.parse( link_parameter_json )
|
110
|
+
parameters.merge!( link_parameters )
|
111
|
+
end
|
112
|
+
|
113
|
+
# add parameters from Settings levels
|
114
|
+
settings = Lti2Tc::ToolSetting.where( :tool_id => tool.id, :scopeable_type => 'Tool' )
|
115
|
+
settings.each { |setting| parameters[setting.name] = setting.value }
|
116
|
+
|
117
|
+
settings = Lti2Tc::ToolSetting.where( :tool_id => tool.id, :scopeable_id => @course.id, :scopeable_type => 'Context' )
|
118
|
+
settings.each { |setting| parameters[setting.name] = setting.value }
|
119
|
+
|
120
|
+
settings = Lti2Tc::ToolSetting.where( :tool_id => tool.id, :scopeable_id => @link.id, :scopeable_type => 'Ltilink' )
|
121
|
+
settings.each { |setting| parameters[setting.name] = setting.value }
|
122
|
+
|
123
|
+
# auto-create result (if required) and add to reference to parameters
|
124
|
+
capabilities = resource_handler.first_at('message[0].enabled_capability')
|
125
|
+
if capabilities and capabilities.include? 'Result.autocreate'
|
126
|
+
if @link.grade_item_id
|
127
|
+
grade_result = GradeResult.where( :link_id => @link.id, :admin_user_id => @user.id ).first
|
128
|
+
# TEST ONLY
|
129
|
+
# grade_result.delete if grade_result
|
130
|
+
# grade_result = nil
|
131
|
+
|
132
|
+
unless grade_result
|
133
|
+
grade_result = GradeResult.new
|
134
|
+
grade_result.link_id = @link.id
|
135
|
+
grade_result.admin_user_id = @user.id
|
136
|
+
grade_result.save
|
137
|
+
else
|
138
|
+
# note that a nil grade_result still let's us through
|
139
|
+
unless grade_result.result.nil?
|
140
|
+
raise 'Grade result associated with this tool launch has already been filled in'
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# perform variable substitution on all parameters
|
147
|
+
resolver = Lti2Commons::SubstitutionSupport::Resolver.new
|
148
|
+
resolver.add_resolver( 'User', @user.method(:user_resolver) )
|
149
|
+
resolver.add_resolver( 'Person', @user.method(:person_resolver) )
|
150
|
+
# now already resolved
|
151
|
+
#@course = Course.find( parameters['context_id'] )
|
152
|
+
resolver.add_resolver( 'CourseOffering', @course.method(:course_resolver) )
|
153
|
+
resolver.add_resolver( 'Result', grade_result.method(:grade_result_resolver) ) if grade_result
|
154
|
+
|
155
|
+
# This one is a bit deceptive. A TCP is usually associated with a @tool_proxy.
|
156
|
+
resolver.add_resolver( 'ToolConsumerProfile', self.method(:tool_consumer_profile_resolver) )
|
157
|
+
|
158
|
+
# Settings resolvers
|
159
|
+
resolver.add_resolver( 'ToolProxy', self.method(:tool_proxy_resolver) )
|
160
|
+
resolver.add_resolver( 'ToolProxyBinding', self.method(:tool_proxy_binding_resolver) )
|
161
|
+
resolver.add_resolver( 'LtiLink', self.method(:lti_link_resolver) )
|
162
|
+
|
163
|
+
# and resolve and normalize
|
164
|
+
final_parameters = {}
|
165
|
+
parameters.each { |k,v|
|
166
|
+
resolved_value = resolver.resolve( v )
|
167
|
+
if known_lti2_parameters.include? k or deprecated_lti_parameters.include? k
|
168
|
+
final_parameters[k] = resolved_value
|
169
|
+
else
|
170
|
+
name = 'custom_' + k
|
171
|
+
lti1_name = slugify( name )
|
172
|
+
unless name == lti1_name
|
173
|
+
final_parameters[lti1_name] = resolved_value
|
174
|
+
end
|
175
|
+
final_parameters[name] = resolved_value
|
176
|
+
end
|
177
|
+
|
178
|
+
}
|
179
|
+
|
180
|
+
subst_final_map = @substitute_final.nil? ? final_parameters : @substitute_final.call( final_parameters )
|
181
|
+
|
182
|
+
key = @resource.tool.key
|
183
|
+
secret = @resource.tool.secret
|
184
|
+
|
185
|
+
signed_request = Signer::create_signed_request( service_endpoint, 'post', key, secret, subst_final_map )
|
186
|
+
puts "TC Signed Request: #{signed_request.signature_base_string}"
|
187
|
+
puts "before"
|
188
|
+
puts Rails.application.config.wire_log.inspect
|
189
|
+
puts "after"
|
190
|
+
body = MessageSupport::create_lti_message_body( service_endpoint, subst_final_map,
|
191
|
+
Rails.application.config.wire_log, 'Lti Launch', open_in_external_window )
|
192
|
+
puts body
|
193
|
+
body
|
194
|
+
end
|
195
|
+
|
196
|
+
def deprecated_lti_parameters
|
197
|
+
[ 'context_title', 'context_label', 'resource_link_title', 'resource_link_description',
|
198
|
+
'lis_person_name_given', 'lis_person_name_family', 'lis_person_name_full', 'lis_person_contact_email_primary',
|
199
|
+
'user_image', 'lis_person_sourcedid', 'lis_course_offering_sourcedid', 'lis_course_section_sourcedid',
|
200
|
+
'tool_consumer_info_product_family_code', 'tool_consumer_info_version',
|
201
|
+
'tool_consumer_instance_name', 'tool_consumer_instance_description', 'tool_consumer_instance_url',
|
202
|
+
'tool_consumer_instance_contact_email' ]
|
203
|
+
end
|
204
|
+
|
205
|
+
def known_lti2_parameters
|
206
|
+
[ 'lti_message_type', 'lti_version', 'user_id', 'roles', 'resource_link_id',
|
207
|
+
'context_id', 'context_type',
|
208
|
+
'launch_presentation_locale', 'launch_presentation_document_target', 'launch_presentation_css_url',
|
209
|
+
'launch_presentation_width', 'launch_presentation_height', 'launch_presentation_return_url',
|
210
|
+
'role_scope_mentor' ]
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
def tool_consumer_profile_resolver( fieldname )
|
215
|
+
if fieldname == 'url'
|
216
|
+
@tool_proxy.first_at('tool_consumer_profile')
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def tool_proxy_resolver( fieldname )
|
221
|
+
if fieldname == 'custom.url'
|
222
|
+
tool_guid = @resource.tool.key
|
223
|
+
ToolSetting.create_uri( tool_guid, 'Tool' )
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def tool_proxy_binding_resolver( fieldname )
|
228
|
+
if fieldname == 'custom.url'
|
229
|
+
tool_guid = @resource.tool.key
|
230
|
+
ToolSetting.create_uri( tool_guid, 'Context', @course.id )
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def lti_link_resolver( fieldname )
|
235
|
+
if fieldname == 'custom.url'
|
236
|
+
tool_guid = @resource.tool.key
|
237
|
+
ToolSetting.create_uri( tool_guid, 'Ltilink', @link.id )
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def slugify name
|
242
|
+
result = ''
|
243
|
+
name.each_char { |c|
|
244
|
+
result << (c =~ /\w/ ? c.downcase : '_')
|
245
|
+
}
|
246
|
+
result
|
247
|
+
end
|
248
|
+
|
249
|
+
def url_encode_users( user_list_str )
|
250
|
+
user_list = []
|
251
|
+
unless user_list_str.nil?
|
252
|
+
users = user_list_str.split(',')
|
253
|
+
users.each { |user| user_list << URI.encode( user ) }
|
254
|
+
end
|
255
|
+
user_list.join(',')
|
256
|
+
end
|
257
|
+
|
258
|
+
end
|
259
|
+
end
|