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,166 +0,0 @@
1
- module IMS::LTI
2
- # Class for consuming/generating LTI Outcome Responses
3
- #
4
- # Response documentation: http://www.imsglobal.org/lti/v1p1pd/ltiIMGv1p1pd.html#_Toc309649691
5
- #
6
- # Error code documentation: http://www.imsglobal.org/gws/gwsv1p0/imsgws_baseProfv1p0.html#1639667
7
- #
8
- # This class can be used by both Tool Providers and Tool Consumers. Each will
9
- # use it a bit differently. The Tool Provider will use it parse the result of
10
- # an OutcomeRequest to the Tool Consumer. A Tool Consumer will use it generate
11
- # proper response XML to send back to a Tool Provider
12
- #
13
- # === Tool Provider Usage
14
- # An OutcomeResponse will generally be created when POSTing an OutcomeRequest
15
- # through a configured ToolProvider. See the ToolProvider documentation for
16
- # typical usage.
17
- #
18
- # === Tool Consumer Usage
19
- # When an outcome request is sent from a Tool Provider the body of the request
20
- # is XML. This class parses that XML and provides a simple interface for
21
- # accessing the information in the request. Typical usage would be:
22
- #
23
- # # create a new response and set the appropriate values
24
- # res = IMS::LTI::OutcomeResponse.new
25
- # res.message_ref_identifier = outcome_request.message_identifier
26
- # res.operation = outcome_request.operation
27
- # res.code_major = 'success'
28
- # res.severity = 'status'
29
- #
30
- # # set a description (optional) and other information based on the type of response
31
- # if outcome_request.replace_request?
32
- # res.description = "Your old score of 0 has been replaced with #{outcome_request.score}"
33
- # elsif outcome_request.read_request?
34
- # res.description = "You score is 50"
35
- # res.score = 50
36
- # elsif outcome_request.delete_request?
37
- # res.description = "You score has been cleared"
38
- # else
39
- # res.code_major = 'unsupported'
40
- # res.severity = 'status'
41
- # res.description = "#{outcome_request.operation} is not supported"
42
- # end
43
- #
44
- # # the generated xml is returned to the Tool Provider
45
- # res.generate_response_xml
46
- #
47
- class OutcomeResponse
48
- include IMS::LTI::Extensions::Base
49
-
50
- attr_accessor :request_type, :score, :message_identifier, :response_code,
51
- :post_response, :code_major, :severity, :description, :operation,
52
- :message_ref_identifier
53
-
54
- CODE_MAJOR_CODES = %w{success processing failure unsupported}
55
- SEVERITY_CODES = %w{status warning error}
56
-
57
- # Create a new OutcomeResponse
58
- #
59
- # @param opts [Hash] initialization hash
60
- def initialize(opts={})
61
- opts.each_pair do |key, val|
62
- self.send("#{key}=", val) if self.respond_to?("#{key}=")
63
- end
64
- end
65
-
66
- # Convenience method for creating a new OutcomeResponse from a response object
67
- #
68
- # req = IMS::LTI::OutcomeResponse.from_post_response(response)
69
- def self.from_post_response(post_response)
70
- response = OutcomeResponse.new
71
- response.process_post_response(post_response)
72
- end
73
-
74
- def process_post_response(post_response)
75
- self.post_response = post_response
76
- self.response_code = post_response.code
77
- xml = post_response.body
78
- self.process_xml(xml)
79
- self
80
- end
81
-
82
- def success?
83
- @code_major == 'success'
84
- end
85
-
86
- def processing?
87
- @code_major == 'processing'
88
- end
89
-
90
- def failure?
91
- @code_major == 'failure'
92
- end
93
-
94
- def unsupported?
95
- @code_major == 'unsupported'
96
- end
97
-
98
- def has_warning?
99
- @severity == 'warning'
100
- end
101
-
102
- def has_error?
103
- @severity == 'error'
104
- end
105
-
106
- # Parse Outcome Response data from XML
107
- def process_xml(xml)
108
- begin
109
- doc = REXML::Document.new xml
110
- rescue => e
111
- raise IMS::LTI::XMLParseError, "#{e}\nOriginal xml: '#{xml}'"
112
- end
113
- @message_identifier = doc.text("//imsx_statusInfo/imsx_messageIdentifier").to_s
114
- @code_major = doc.text("//imsx_statusInfo/imsx_codeMajor")
115
- @code_major.downcase! if @code_major
116
- @severity = doc.text("//imsx_statusInfo/imsx_severity")
117
- @severity.downcase! if @severity
118
- @description = doc.text("//imsx_statusInfo/imsx_description")
119
- @description = @description.to_s if @description
120
- @message_ref_identifier = doc.text("//imsx_statusInfo/imsx_messageRefIdentifier")
121
- @operation = doc.text("//imsx_statusInfo/imsx_operationRefIdentifier")
122
- @score = doc.text("//readResultResponse//resultScore/textString")
123
- @score = @score.to_s if @score
124
- end
125
-
126
- # Generate XML based on the current configuration
127
- # @return [String] The response xml
128
- def generate_response_xml
129
- builder = Builder::XmlMarkup.new
130
- builder.instruct!
131
-
132
- builder.imsx_POXEnvelopeResponse("xmlns" => "http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0") do |env|
133
- env.imsx_POXHeader do |header|
134
- header.imsx_POXResponseHeaderInfo do |info|
135
- info.imsx_version "V1.0"
136
- info.imsx_messageIdentifier @message_identifier || IMS::LTI::generate_identifier
137
- info.imsx_statusInfo do |status|
138
- status.imsx_codeMajor @code_major
139
- status.imsx_severity @severity
140
- status.imsx_description @description
141
- status.imsx_messageRefIdentifier @message_ref_identifier
142
- status.imsx_operationRefIdentifier @operation
143
- end
144
- end
145
- end #/header
146
- env.imsx_POXBody do |body|
147
- unless unsupported?
148
- if @operation == OutcomeRequest::READ_REQUEST
149
- body.tag!(@operation + 'Response') do |request|
150
- request.result do |res|
151
- res.resultScore do |res_score|
152
- res_score.language "en" # 'en' represents the format of the number
153
- res_score.textString @score.to_s
154
- end
155
- end #/result
156
- end
157
- else
158
- body.tag!(@operation + 'Response')
159
- end #/operationResponse
160
- end
161
- end #/body
162
- end
163
- end
164
-
165
- end
166
- end
@@ -1,56 +0,0 @@
1
- module IMS::LTI
2
- # A mixin for OAuth request validation
3
- module RequestValidator
4
-
5
- attr_reader :oauth_signature_validator
6
-
7
- # Validates and OAuth request using the OAuth Gem - https://github.com/oauth/oauth-ruby
8
- #
9
- # To validate the OAuth signatures you need to require the appropriate
10
- # request proxy for your application. For example:
11
- #
12
- # # For a sinatra app:
13
- # require 'oauth/request_proxy/rack_request'
14
- #
15
- # # For a rails app:
16
- # require 'oauth/request_proxy/action_controller_request'
17
- # @return [Bool] Whether the request was valid
18
- def valid_request?(request, handle_error=true)
19
- begin
20
- @oauth_signature_validator = OAuth::Signature.build(request, :consumer_secret => @consumer_secret)
21
- @oauth_signature_validator.verify() or raise OAuth::Unauthorized.new(request)
22
- true
23
- rescue OAuth::Signature::UnknownSignatureMethod
24
- if handle_error
25
- false
26
- else
27
- raise $!
28
- end
29
- rescue OAuth::Unauthorized
30
- if handle_error
31
- false
32
- else
33
- raise OAuth::Unauthorized.new(request)
34
- end
35
- end
36
- end
37
-
38
- # Check whether the OAuth-signed request is valid and throw error if not
39
- #
40
- # @return [Bool] Whether the request was valid
41
- def valid_request!(request)
42
- valid_request?(request, false)
43
- end
44
-
45
- # convenience method for getting the oauth nonce from the request
46
- def request_oauth_nonce
47
- @oauth_signature_validator && @oauth_signature_validator.request.oauth_nonce
48
- end
49
-
50
- # convenience method for getting the oauth timestamp from the request
51
- def request_oauth_timestamp
52
- @oauth_signature_validator && @oauth_signature_validator.request.oauth_timestamp
53
- end
54
-
55
- end
56
- end
@@ -1,101 +0,0 @@
1
- module IMS::LTI
2
- # Some convenience methods for the most used roles
3
- # Take care when using context_ helpers, as the context of an LTI launch
4
- # determines the meaning of that role. For example, if the context is an
5
- # institution context instead of a course context, then the short role of
6
- # "Instructor" means they are a teacher at the institution, but not necessarily
7
- # of the course you're working in.
8
- #
9
- # Also note that these only check for the base roles. So, asking context_student?
10
- # only matches `urn:lti:role:ims/lis/Learner`, not `urn:lti:role:ims/lis/Learner/NonCreditLearner`
11
- # If you make use of the more specific roles you'll need to ask specifically for those:
12
- # @tool_provider.has_exact_role?("urn:lti:role:ims/lis/Learner/NonCreditLearner")
13
- # Or you can use `has_base_role?`
14
- module RoleChecks
15
-
16
- # Check whether the Launch Parameters have a given role
17
- def has_exact_role?(role)
18
- role = role.downcase
19
- @roles && @roles.any? { |r| r.downcase == role }
20
- end
21
-
22
- # Check whether the Launch Parameters have a given role ignoring
23
- # sub roles. So asking:
24
- # @tool_provider.has_base_role?("urn:lti:role:ims/lis/Instructor/")
25
- # will return true if the role is `urn:lti:role:ims/lis/Instructor/GuestInstructor`
26
- def has_base_role?(role)
27
- role = role.downcase
28
- @roles && @roles.any? { |r| r.downcase.start_with?(role) }
29
- end
30
-
31
- # Convenience method for checking if the user is the system administrator of the TC
32
- def system_administrator?
33
- has_exact_role?('urn:lti:sysrole:ims/lis/SysAdmin') ||
34
- has_exact_role?('SysAdmin') ||
35
- has_exact_role?('urn:lti:sysrole:ims/lis/Administrator')
36
- end
37
-
38
- ### Institution-level roles
39
- # Note, these only check if the role is explicitely an institution level role
40
- # if the context of the LTI launch is the institution, the short names
41
- # will apply, and you should use the context_x? helpers.
42
-
43
- # Convenience method for checking if the user has 'student' or 'learner' roles at the institution
44
- def institution_student?
45
- has_exact_role?('urn:lti:instrole:ims/lis/Student') || has_exact_role?('urn:lti:instrole:ims/lis/Learner')
46
- end
47
-
48
- # Convenience method for checking if the user has 'Instructor' role at the institution
49
- def institution_instructor?
50
- has_exact_role?('urn:lti:instrole:ims/lis/Instructor')
51
- end
52
-
53
- # Convenience method for checking if the user has 'Administrator' role at the institution
54
- def institution_admin?
55
- has_exact_role?('urn:lti:instrole:ims/lis/Administrator')
56
- end
57
-
58
-
59
- ### Context-level roles
60
- # Note, the most common LTI context is a course, but that is not always the
61
- # case. You should be aware of the context when using these helpers.
62
- # The difference for the context_ helpers is that they check for the
63
- # short version of the roles. So `Learner` and `urn:lti:role:ims/lis/Learner`
64
- # are both valid.
65
-
66
- # Convenience method for checking if the user has 'learner' role in the current launch context
67
- def context_student?
68
- has_exact_role?('Learner') || has_exact_role?('urn:lti:role:ims/lis/Learner')
69
- end
70
-
71
- # Convenience method for checking if the user has 'instructor' role in the current launch context
72
- def context_instructor?
73
- has_exact_role?('instructor') || has_exact_role?('urn:lti:role:ims/lis/Instructor')
74
- end
75
-
76
- # Convenience method for checking if the user has 'contentdeveloper' role in the current launch context
77
- def context_content_developer?
78
- has_exact_role?('ContentDeveloper') || has_exact_role?('urn:lti:role:ims/lis/ContentDeveloper')
79
- end
80
-
81
- # Convenience method for checking if the user has 'Mentor' role in the current launch context
82
- def context_mentor?
83
- has_exact_role?('Mentor') || has_exact_role?('urn:lti:role:ims/lis/Mentor')
84
- end
85
-
86
- # Convenience method for checking if the user has 'administrator' role in the current launch context
87
- def context_admin?
88
- has_exact_role?('Administrator') || has_exact_role?('urn:lti:role:ims/lis/Administrator')
89
- end
90
-
91
- # Convenience method for checking if the user has 'TeachingAssistant' role in the current launch context
92
- def context_ta?
93
- has_exact_role?('TeachingAssistant') || has_exact_role?('urn:lti:role:ims/lis/TeachingAssistant')
94
- end
95
-
96
- # Convenience method for checking if the user has 'Observer' role in the current launch context
97
- def context_observer?
98
- has_exact_role?('Observer') || has_exact_role?('urn:lti:instrole:ims/lis/Observer')
99
- end
100
- end
101
- end
@@ -1,29 +0,0 @@
1
- module IMS::LTI
2
- class ToolBase
3
- include IMS::LTI::Extensions::Base
4
- include IMS::LTI::LaunchParams
5
- include IMS::LTI::RequestValidator
6
-
7
- # OAuth credentials
8
- attr_accessor :consumer_key, :consumer_secret
9
-
10
- def initialize(consumer_key, consumer_secret, params={})
11
- @consumer_key = consumer_key
12
- @consumer_secret = consumer_secret
13
- @custom_params = {}
14
- @ext_params = {}
15
- @non_spec_params = {}
16
- process_params(params)
17
- end
18
-
19
- # Convenience method for doing oauth signed requests to services that
20
- # aren't supported by this library
21
- def post_service_request(url, content_type, body)
22
- IMS::LTI::post_service_request(@consumer_key,
23
- @consumer_secret,
24
- url,
25
- content_type,
26
- body)
27
- end
28
- end
29
- end
@@ -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