panda_pal 5.15.1.beta3 → 5.16.0

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +0 -0
  3. data/README.md +38 -2
  4. data/Rakefile +0 -0
  5. data/app/assets/config/panda_pal_manifest.js +0 -0
  6. data/app/assets/javascripts/panda_pal/lti.js +0 -0
  7. data/app/assets/stylesheets/panda_pal/application.css +0 -0
  8. data/app/assets/stylesheets/panda_pal/lti.css +0 -0
  9. data/app/controllers/panda_pal/lti_v1_p3_controller.rb +28 -3
  10. data/app/helpers/panda_pal/application_helper.rb +0 -0
  11. data/app/jobs/panda_pal/jobs/grade_passback_job.rb +0 -0
  12. data/app/lib/lti_xml/base_platform.rb +0 -0
  13. data/app/lib/lti_xml/bridge_platform.rb +0 -0
  14. data/app/lib/lti_xml/canvas_platform.rb +0 -0
  15. data/app/lib/panda_pal/deep_linking_helpers.rb +98 -0
  16. data/app/lib/panda_pal/launch_url_helpers.rb +37 -0
  17. data/app/lib/panda_pal/lti_jwt_validator.rb +0 -0
  18. data/app/models/panda_pal/api_call.rb +0 -0
  19. data/app/models/panda_pal/organization_concerns/organization_builder.rb +0 -0
  20. data/app/models/panda_pal/organization_concerns/settings_validation.rb +0 -0
  21. data/app/models/panda_pal/organization_concerns/task_scheduling.rb +0 -0
  22. data/app/models/panda_pal/panda_pal_record.rb +0 -0
  23. data/app/models/panda_pal/platform.rb +0 -0
  24. data/app/models/panda_pal/session.rb +1 -1
  25. data/app/views/layouts/panda_pal/application.html.erb +0 -0
  26. data/app/views/panda_pal/lti/launch.html.erb +0 -0
  27. data/app/views/panda_pal/lti_v1_p3/login.html.erb +0 -0
  28. data/app/views/panda_pal/partials/_auto_submit_form.html.erb +0 -0
  29. data/config/dev_lti_key.key +0 -0
  30. data/config/initializers/apartment.rb +21 -31
  31. data/config/routes.rb +1 -0
  32. data/db/migrate/20160412205931_create_panda_pal_organizations.rb +0 -0
  33. data/db/migrate/20160413135653_create_panda_pal_sessions.rb +0 -0
  34. data/db/migrate/20160425130344_add_panda_pal_organization_to_session.rb +0 -0
  35. data/db/migrate/20170106165533_add_salesforce_id_to_organizations.rb +0 -0
  36. data/db/migrate/20171205183457_encrypt_organization_settings.rb +0 -0
  37. data/db/migrate/20171205194657_remove_old_organization_settings.rb +0 -0
  38. data/db/migrate/20220721095653_create_panda_pal_api_calls.rb +0 -0
  39. data/db/migrate/20250401214421_rename_session_session_key.rb +1 -1
  40. data/lib/panda_pal/concerns/ability_helper.rb +0 -0
  41. data/lib/panda_pal/helpers/console_helpers.rb +0 -0
  42. data/lib/panda_pal/helpers/misc_helper.rb +0 -0
  43. data/lib/panda_pal/helpers/route_helper.rb +0 -0
  44. data/lib/panda_pal/helpers/secure_headers.rb +0 -0
  45. data/lib/panda_pal/helpers.rb +0 -0
  46. data/lib/panda_pal/lti_constants.rb +48 -0
  47. data/lib/panda_pal/plugins.rb +0 -0
  48. data/lib/panda_pal/version.rb +1 -1
  49. data/lib/panda_pal.rb +18 -4
  50. data/lib/tasks/panda_pal_tasks.rake +0 -0
  51. metadata +3 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fb9b051afba3b9befb9e505f2c8d6bd8f595112ceea1778af4ea0190220d9a5
4
- data.tar.gz: 7368671098738bbd1229c0329d9919e48d675357fcf4d13db9e3d408ed1b03f0
3
+ metadata.gz: c78eb783dcf56cb075ce23d1218500128e6ac1ac58c3049bcfa13f6409835554
4
+ data.tar.gz: bb555a130fdab3e1805013700e04bddfe24a6c3460750ac2201a06f9dfe96c1d
5
5
  SHA512:
6
- metadata.gz: 24151b3bfe982fb78ff9093141482bcb0d065f1ca1a462c0ba252fb1a4e987f8001ea06bb8fc79258d93adf448db7bd67e24aaa5bd4f6b21608bac4b34250b97
7
- data.tar.gz: e8d9921e233f328b1d96efda77fa8a1520052562ff942f4a759f4d6c3d386f7de0c6effd5edf2b5c1a216eab9c47b1d2ce276bdf9ca143ef9f3ddfe041680013
6
+ metadata.gz: 5378ea91ce63e263dc93368f4d87cec35a14273b74f37452cec2b1dea67ff8bbb3a261e609bbc550bbcc4f08dac8c1de09e387e6ec8691e3d4901ac7b2af1e0a
7
+ data.tar.gz: aef30aa2d98b20f502a9274ce31c5bf17e66111c8a628cd61ce2765c06ef7fe280af459069a3fafe0a13aa885e9ba915f048553f14714e3ad274537b7bd4cfe3
data/MIT-LICENSE CHANGED
File without changes
data/README.md CHANGED
@@ -149,6 +149,42 @@ end
149
149
  the defined action. Setting it to `false` indicates that the defined action handles launch validation and setup itself (this has been the legacy approach).
150
150
  Because `auto_launch: false` is most similar to the previous behavior, it is the default for LTI 1.0/1.1 LTIs. For LTI 1.3 LTIs, `auto_launch: true` is the default. If not specified and `:organization_id` is detected in the Route Path, `auto_launch` will be set to `true`
151
151
 
152
+ ## Deep Linking (LTI 1.3)
153
+
154
+ PandaPal provides comprehensive support for LTI 1.3 Deep Linking, allowing tools to be configured for content selection that can be placed directly into Canvas courses.
155
+
156
+ ### Quick Start
157
+
158
+ 1. **Configure placements** in your LTI initializer:
159
+ ```ruby
160
+ # config/initializers/lti.rb
161
+ PandaPal.stage_placement(:content_selection, {
162
+ deep_linking: true,
163
+ deep_link_redirect_url: :content_selection_url,
164
+ text: 'Select Content',
165
+ enabled: true
166
+ })
167
+ ```
168
+
169
+ 2. **Include helpers** in your controller and build content items:
170
+ ```ruby
171
+ class ContentSelectionController < ApplicationController
172
+ include PandaPal::DeepLinkingHelpers
173
+
174
+ def create
175
+ content_items = [
176
+ build_link_content_item(
177
+ url: launch_url_for_content(params[:content_id]),
178
+ title: 'Selected Content'
179
+ )
180
+ ]
181
+ render_deep_linking_response(content_items)
182
+ end
183
+ end
184
+ ```
185
+
186
+ **For complete configuration options, implementation examples, helper method documentation, and troubleshooting, see [DEEP_LINKING.md](DEEP_LINKING.md).**
187
+
152
188
  ## Implementating data segregation
153
189
  This engine uses Apartment to keep data segregated between installations of the implementing LTI tool.
154
190
  By default, it does this by inspecting the path of the request, and matching URLs containing `orgs` or `organizations`,
@@ -457,7 +493,7 @@ data_type: If specified, and a settings hash contains this attribute, attribute
457
493
  use `class` to determine data type. For example: 30.minutes.class.to_s => "ActiveSupport::Duration"
458
494
 
459
495
  ## Bridge vs Canvas
460
- As of 3.2.0, the LTI XML config can have subtle differences based on whether your platform is bridge, or canvas.
496
+ As of 3.2.0, the LTI XML config can have subtle differences based on whether your platform is bridge, or canvas.
461
497
  This is determined by `PandaPal.lti_options[:platform]`.
462
498
  Set this to `platform: 'canvas.instructure.com'` (default)
463
499
  OR `platform: 'bridgeapp.com'`
@@ -490,7 +526,7 @@ You will want to watch out for a few scenarios:
490
526
  2) Use the `Authorization` header with `token={session_key}` to send your
491
527
  PandaPal session info into api calls.
492
528
  3) If you use `link_to` and navigate in your LTI (apps that are not single page)
493
- make sure you include the `link_nonce` like so:
529
+ make sure you include the `link_nonce` like so:
494
530
  ```ruby
495
531
  link_to "Link Name", somewhere_else_path(arg, session_token: link_nonce)
496
532
  ```
data/Rakefile CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -6,7 +6,7 @@ module PandaPal
6
6
  skip_before_action :verify_authenticity_token, raise: false
7
7
  end
8
8
 
9
- before_action ->{ validate_launch!(version: :v1p3) }, only: [:resource_link_request]
9
+ before_action ->{ validate_launch!(version: :v1p3) }, only: [:resource_link_request, :deep_link_request]
10
10
  before_action :enforce_environment!, only: [:resource_link_request]
11
11
 
12
12
  # Redirect to beta/test as necessary
@@ -40,7 +40,7 @@ module PandaPal
40
40
  end
41
41
 
42
42
  def resource_link_request
43
- ltype = @decoded_lti_jwt['https://www.instructure.com/placement']
43
+ ltype = @decoded_lti_jwt[LtiConstants::Canvas::PLACEMENT]
44
44
 
45
45
  if ltype
46
46
  current_session.data.merge!({
@@ -54,6 +54,32 @@ module PandaPal
54
54
  end
55
55
  end
56
56
 
57
+ def deep_link_request
58
+ message_type = @decoded_lti_jwt[LtiConstants::Claims::MESSAGE_TYPE]
59
+
60
+ unless message_type == LtiConstants::MessageTypes::DEEP_LINKING_REQUEST
61
+ render plain: 'Not a Deep Linking request', status: :bad_request
62
+ return
63
+ end
64
+
65
+ # Store deep linking session data
66
+ current_session.data.merge!({
67
+ lti_version: 'v1p3',
68
+ lti_launch_placement: 'deep_linking',
69
+ launch_url_params: request.query_parameters.to_h,
70
+ launch_params: @decoded_lti_jwt,
71
+ deep_link_return_url: @decoded_lti_jwt[LtiConstants::DeepLinking::DEEP_LINKING_SETTINGS][LtiConstants::DeepLinkingSettings::DEEP_LINK_RETURN_URL],
72
+ deep_link_data: @decoded_lti_jwt[LtiConstants::DeepLinking::DATA]
73
+ })
74
+
75
+ redirect_url = LaunchUrlHelpers.deep_link_redirect_url(request)
76
+ if redirect_url
77
+ redirect_with_session_to(redirect_url, route_context: main_app)
78
+ else
79
+ render plain: 'Deep linking redirect URL not configured. Please configure deep_link_redirect_url in your placement or global LTI options.', status: :internal_server_error
80
+ end
81
+ end
82
+
57
83
  def tool_config
58
84
  if PandaPal.lti_environments.empty?
59
85
  render plain: 'Domains must be set in lti_environments'
@@ -71,7 +97,6 @@ module PandaPal
71
97
  target_link_uri: LaunchUrlHelpers.absolute_launch_url(
72
98
  k.to_sym,
73
99
  host: parsed_request_url,
74
- launch_handler: v1p3_resource_link_request_path,
75
100
  default_auto_launch: true
76
101
  ),
77
102
  })
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,98 @@
1
+ module PandaPal
2
+ module DeepLinkingHelpers
3
+ # Build a link content item
4
+ def build_link_content_item(url:, title: nil, text: nil, icon: nil, thumbnail: nil, **options)
5
+ content_item = {
6
+ type: 'link',
7
+ url: url,
8
+ title: title
9
+ }
10
+ content_item[:text] = text if text.present?
11
+ content_item[:icon] = icon if icon.present?
12
+ content_item[:thumbnail] = thumbnail if thumbnail.present?
13
+ content_item.merge(options)
14
+ end
15
+
16
+ # Build a file content item
17
+ def build_file_content_item(url:, title: nil, text: nil, media_type: nil, icon: nil, **options)
18
+ content_item = {
19
+ type: 'file',
20
+ url: url,
21
+ title: title
22
+ }
23
+ content_item[:text] = text if text.present?
24
+ content_item[:mediaType] = media_type if media_type.present?
25
+ content_item[:icon] = icon if icon.present?
26
+ content_item.merge(options)
27
+ end
28
+
29
+ # Build an HTML content item
30
+ def build_html_content_item(html:, title: nil, text: nil, **options)
31
+ content_item = {
32
+ type: 'html',
33
+ html: html,
34
+ title: title
35
+ }
36
+ content_item[:text] = text if text.present?
37
+ content_item.merge(options)
38
+ end
39
+
40
+ # Render the deep linking response
41
+ # This builds the JWT and renders the auto-submit form
42
+ def render_deep_linking_response(content_items, custom_data: {})
43
+ deep_link_return_url = current_session[:deep_link_return_url]
44
+ deep_link_data = current_session[:deep_link_data]
45
+
46
+ unless deep_link_return_url.present?
47
+ render plain: 'No deep linking session found. Please ensure you accessed this through a proper deep linking flow.', status: :bad_request
48
+ return
49
+ end
50
+
51
+ # Build custom deep link data - merge Canvas data with custom data
52
+ launch_params = current_session[:launch_params]
53
+ merged_custom_data = (deep_link_data || {}).merge({
54
+ selection_time: Time.now.iso8601,
55
+ selected_by: launch_params&.dig('sub') || 'unknown'
56
+ }).merge(custom_data)
57
+
58
+ # Build the JWT response
59
+ jwt = build_deep_link_jwt(content_items, merged_custom_data)
60
+
61
+ # Set up the auto-submit form
62
+ @form_action = deep_link_return_url
63
+ @method = :post
64
+ @form_data = { JWT: jwt }
65
+
66
+ render partial: 'panda_pal/partials/auto_submit_form'
67
+ end
68
+
69
+ private
70
+
71
+ def build_deep_link_jwt(content_items, custom_deep_link_data)
72
+ # Get issuer (client_id) and audience from session
73
+ launch_params = current_session[:launch_params]
74
+ issuer = launch_params&.dig('aud') || current_organization.key
75
+ audience = launch_params&.dig('iss')
76
+
77
+ unless audience.present?
78
+ raise StandardError, 'Unable to determine audience for deep linking response'
79
+ end
80
+
81
+ deep_link_jwt_payload = {
82
+ iss: issuer,
83
+ aud: audience,
84
+ iat: Time.now.to_i,
85
+ exp: Time.now.to_i + 600,
86
+ nonce: SecureRandom.uuid,
87
+ PandaPal::LtiConstants::Claims::MESSAGE_TYPE => PandaPal::LtiConstants::MessageTypes::DEEP_LINKING_RESPONSE,
88
+ PandaPal::LtiConstants::Claims::VERSION => PandaPal::LtiConstants::Versions::V1_3,
89
+ PandaPal::LtiConstants::DeepLinking::CONTENT_ITEMS => content_items,
90
+ PandaPal::LtiConstants::DeepLinking::DATA => custom_deep_link_data,
91
+ }
92
+
93
+ require 'json/jwt'
94
+ jwk = JSON::JWK.new(PandaPal.lti_private_key)
95
+ JSON::JWT.new(deep_link_jwt_payload).sign(jwk, :RS256).to_s
96
+ end
97
+ end
98
+ end
@@ -2,6 +2,13 @@ module PandaPal
2
2
  module LaunchUrlHelpers
3
3
  def self.absolute_launch_url(launch_type, host: uri_host, launch_handler: nil, default_auto_launch: false)
4
4
  opts = PandaPal.lti_paths[launch_type]
5
+
6
+ if launch_handler.nil? && opts.present?
7
+ launch_handler = opts[:message_type] == PandaPal::LtiConstants::MessageTypes::DEEP_LINKING_REQUEST ?
8
+ :v1p3_deep_link_request_path :
9
+ :v1p3_resource_link_request_path
10
+ end
11
+
5
12
  auto_launch = opts[:auto_launch] != nil ? opts[:auto_launch] : default_auto_launch
6
13
  auto_launch = auto_launch && launch_handler.present?
7
14
 
@@ -100,5 +107,35 @@ module PandaPal
100
107
  engine = key_bits[0] == 'MainApp' ? Rails.application : (key_bits[0].constantize)::Engine
101
108
  engine.routes.url_helpers.send(key_bits[1], *arguments, **kwargs)
102
109
  end
110
+
111
+ def self.deep_link_redirect_url(request)
112
+ # Try placement-specific configuration first
113
+ launch_type = request.query_parameters['launch_type']&.to_sym
114
+ return global_configuration_url unless launch_type
115
+
116
+ placement_config = PandaPal.lti_paths[launch_type]
117
+ return global_configuration_url unless deep_linking_placement?(placement_config)
118
+
119
+ # Check for explicit deep_link_redirect_url configuration
120
+ redirect_url = placement_config[:deep_link_redirect_url]
121
+ return normalize_redirect_url(redirect_url) if redirect_url.present?
122
+
123
+ # Fall back to global configuration
124
+ global_configuration_url
125
+ end
126
+
127
+ def self.global_configuration_url
128
+ redirect_url = PandaPal.lti_options[:deep_link_redirect_url]
129
+ normalize_redirect_url(redirect_url) if redirect_url.present?
130
+ end
131
+
132
+ def self.deep_linking_placement?(placement_config)
133
+ placement_config&.dig(:message_type) == PandaPal::LtiConstants::MessageTypes::DEEP_LINKING_REQUEST
134
+ end
135
+
136
+ def self.normalize_redirect_url(url)
137
+ return nil unless url.present?
138
+ url.is_a?(Symbol) ? url : url.to_sym
139
+ end
103
140
  end
104
141
  end
File without changes
File without changes
File without changes
File without changes
@@ -199,7 +199,7 @@ module PandaPal
199
199
  def custom_lti_params
200
200
  @custom_lti_params ||= begin
201
201
  # LT 1.3
202
- custom_params = launch_params["https://purl.imsglobal.org/spec/lti/claim/custom"]
202
+ custom_params = launch_params[PandaPal::LtiConstants::Claims::CUSTOM]
203
203
  return custom_params if custom_params.present?
204
204
 
205
205
  # LTI 1.0/1.1
File without changes
File without changes
File without changes
File without changes
@@ -10,29 +10,25 @@ require "apartment/adapters/postgresql_adapter"
10
10
  module Apartment
11
11
  SHARD_PREFIXES = ["SHARD_DB", "HEROKU_POSTGRESQL"]
12
12
 
13
- class << self
14
- def_delegators :connection_class, :connected?
13
+ def self.shard_configurations
14
+ $shard_configurations ||= begin
15
+ shard_to_env = {}
15
16
 
16
- def shard_configurations
17
- $shard_configurations ||= begin
18
- shard_to_env = {}
17
+ base_config = ActiveRecord::Base.configurations.find_db_config(Rails.env).configuration_hash
19
18
 
20
- base_config = ActiveRecord::Base.configurations.find_db_config(Rails.env).configuration_hash
19
+ ENV.keys.each do |k|
20
+ m = /^(#{SHARD_PREFIXES.join("|")})_(\w+)_URL$/.match(k)
21
+ next unless m
21
22
 
22
- ENV.keys.each do |k|
23
- m = /^(#{SHARD_PREFIXES.join("|")})_(\w+)_URL$/.match(k)
24
- next unless m
25
-
26
- url = ENV[k]
27
- shardcfg = ActiveRecord::Base.configurations.resolve(url).configuration_hash
28
- shardcfg = base_config.merge(shardcfg || {})
29
- shard_to_env[m[2].downcase] = shardcfg
30
- end
23
+ url = ENV[k]
24
+ shardcfg = ActiveRecord::Base.configurations.resolve(url).configuration_hash
25
+ shardcfg = base_config.merge(shardcfg || {})
26
+ shard_to_env[m[2].downcase] = shardcfg
27
+ end
31
28
 
32
- shard_to_env.freeze unless Rails.env.test?
29
+ shard_to_env.freeze unless Rails.env.test?
33
30
 
34
- shard_to_env
35
- end
31
+ shard_to_env
36
32
  end
37
33
  end
38
34
 
@@ -124,12 +120,14 @@ module Apartment
124
120
 
125
121
  raise ActiveRecord::StatementInvalid, "PandaPal/Apartment Mutli-DB support does not currently support DB roles" if ActiveRecord::Base.current_role != ActiveRecord::Base.default_role
126
122
 
127
- shard, schema = Apartment::Tenant.split_tenant(@current)
128
- Apartment.establish_connection(multi_tenantify(tenant, false)) if shard != "default"
129
- Apartment.connection.verify!
123
+ unless ActiveRecord::Base.connected?
124
+ Apartment.establish_connection multi_tenantify(tenant, false)
125
+ Apartment.connection.verify!
126
+ end
127
+
130
128
  Apartment.connection.enable_query_cache! if query_cache_enabled
131
129
 
132
- raise ActiveRecord::StatementInvalid, "Could not find schema for tenant \"#{tenant}\": (#{tenant_schemas.inspect})" unless schema_exists?(tenant_schemas)
130
+ raise ActiveRecord::StatementInvalid, "Could not find schema for tenant #{tenant} (#{tenant_schemas.inspect})" unless schema_exists?(tenant_schemas)
133
131
 
134
132
  Apartment.connection.schema_search_path = full_search_path
135
133
  rescue *rescuable_exceptions => e
@@ -152,14 +150,6 @@ module Apartment
152
150
  schema
153
151
  end
154
152
  end
155
-
156
- def raise_schema_connect_to_new(tenant, exception)
157
- shard, schema = Tenant.split_tenant(tenant)
158
- raise TenantNotFound, <<~EXCEPTION_MESSAGE
159
- Could not set search path to schemas, they may be invalid: "#{schema}" #{full_search_path}.
160
- Original error: #{exception.class}: #{exception}
161
- EXCEPTION_MESSAGE
162
- end
163
153
  end
164
154
  end
165
155
 
@@ -220,7 +210,7 @@ Apartment.configure do |config|
220
210
  shard_configurations = Apartment.shard_configurations
221
211
 
222
212
  PandaPal::Organization.all.to_a.each_with_object({}) do |org, hash|
223
- shard = org.shard.to_s || "default"
213
+ shard = org.shard || "default"
224
214
  hash[org.tenant_name] = shard == "default" ? base_config : shard_configurations[shard.downcase]
225
215
  end
226
216
  else
data/config/routes.rb CHANGED
@@ -14,6 +14,7 @@ PandaPal::Engine.routes.draw do
14
14
  post '/oidc_login' => 'lti_v1_p3#login'
15
15
  get '/oidc_login' => 'lti_v1_p3#login'
16
16
  post '/resource_link_request' => 'lti_v1_p3#resource_link_request'
17
+ post '/deep_link_request' => 'lti_v1_p3#deep_link_request'
17
18
  get '/public_jwks' => 'lti_v1_p3#public_jwks'
18
19
  end
19
20
  end
@@ -1,4 +1,4 @@
1
- class RenameSessionSessionKey < PandaPal::MiscHelper::MigrationClass
1
+ class RenameSessionSessionKey < ActiveRecord::Migration[7.1]
2
2
  def change
3
3
  rename_column :panda_pal_sessions, :session_key, :session_secret
4
4
  end
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,48 @@
1
+ module PandaPal
2
+ module LtiConstants
3
+ # LTI 1.3 Standard Claims
4
+ module Claims
5
+ MESSAGE_TYPE = 'https://purl.imsglobal.org/spec/lti/claim/message_type'.freeze
6
+ VERSION = 'https://purl.imsglobal.org/spec/lti/claim/version'.freeze
7
+ DEPLOYMENT_ID = 'https://purl.imsglobal.org/spec/lti/claim/deployment_id'.freeze
8
+ TARGET_LINK_URI = 'https://purl.imsglobal.org/spec/lti/claim/target_link_uri'.freeze
9
+ CONTEXT = 'https://purl.imsglobal.org/spec/lti/claim/context'.freeze
10
+ RESOURCE_LINK = 'https://purl.imsglobal.org/spec/lti/claim/resource_link'.freeze
11
+ ROLES = 'https://purl.imsglobal.org/spec/lti/claim/roles'.freeze
12
+ CUSTOM = 'https://purl.imsglobal.org/spec/lti/claim/custom'.freeze
13
+ end
14
+
15
+ # LTI Deep Linking Claims
16
+ module DeepLinking
17
+ CONTENT_ITEMS = 'https://purl.imsglobal.org/spec/lti-dl/claim/content_items'.freeze
18
+ DATA = 'https://purl.imsglobal.org/spec/lti-dl/claim/data'.freeze
19
+ DEEP_LINKING_SETTINGS = 'https://purl.imsglobal.org/spec/lti-dl/claim/deep_linking_settings'.freeze
20
+ end
21
+
22
+ # Canvas-specific Claims
23
+ module Canvas
24
+ PLACEMENT = 'https://www.instructure.com/placement'.freeze
25
+ end
26
+
27
+ # LTI Message Types
28
+ module MessageTypes
29
+ DEEP_LINKING_REQUEST = 'LtiDeepLinkingRequest'.freeze
30
+ DEEP_LINKING_RESPONSE = 'LtiDeepLinkingResponse'.freeze
31
+ RESOURCE_LINK_REQUEST = 'LtiResourceLinkRequest'.freeze
32
+ end
33
+
34
+ # LTI Versions
35
+ module Versions
36
+ V1_3 = '1.3.0'.freeze
37
+ end
38
+
39
+ # Deep Linking Settings Keys
40
+ module DeepLinkingSettings
41
+ DEEP_LINK_RETURN_URL = 'deep_link_return_url'.freeze
42
+ ACCEPT_TYPES = 'accept_types'.freeze
43
+ ACCEPT_PRESENTATION_DOCUMENT_TARGETS = 'accept_presentation_document_targets'.freeze
44
+ ACCEPT_MEDIA_TYPES = 'accept_media_types'.freeze
45
+ AUTO_CREATE = 'auto_create'.freeze
46
+ end
47
+ end
48
+ end
File without changes
@@ -1,3 +1,3 @@
1
1
  module PandaPal
2
- VERSION = "5.15.1.beta3"
2
+ VERSION = "5.16.0"
3
3
  end
data/lib/panda_pal.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "panda_pal/engine"
2
2
  require 'panda_pal/plugins'
3
3
  require 'panda_pal/helpers'
4
+ require 'panda_pal/lti_constants'
4
5
  require 'oauth/request_proxy/rack_request'
5
6
  require 'oauth/request_proxy/action_dispatch_request'
6
7
 
@@ -50,9 +51,22 @@ module PandaPal
50
51
  @@lti_custom_params.deep_dup
51
52
  end
52
53
 
53
- def self.stage_navigation(navigation, options)
54
- @@lti_navigation[navigation] ||= {}
55
- @@lti_navigation[navigation].merge!(options)
54
+ def self.stage_navigation(navigation, options = {})
55
+ warn "[DEPRECATION] `stage_navigation` is deprecated. Please use `stage_placement` instead."
56
+ stage_placement(navigation, options)
57
+ end
58
+
59
+ def self.stage_placement(placement, options = {})
60
+ @@lti_navigation[placement] ||= {}
61
+
62
+ if options.delete(:deep_linking)
63
+ options = options.merge({
64
+ message_type: LtiConstants::MessageTypes::DEEP_LINKING_REQUEST,
65
+ route_helper_key: :deep_link_request
66
+ })
67
+ end
68
+
69
+ @@lti_navigation[placement].merge!(options)
56
70
  end
57
71
 
58
72
  def self.lti_paths
@@ -70,7 +84,7 @@ module PandaPal
70
84
  end
71
85
 
72
86
  def self.lti_private_key=(v)
73
- @@lti_private_key = k
87
+ @@lti_private_key = v
74
88
  end
75
89
 
76
90
  def self.register_extension(type, modul)
File without changes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: panda_pal
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.15.1.beta3
4
+ version: 5.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Instructure CustomDev
@@ -159,6 +159,7 @@ files:
159
159
  - app/lib/lti_xml/base_platform.rb
160
160
  - app/lib/lti_xml/bridge_platform.rb
161
161
  - app/lib/lti_xml/canvas_platform.rb
162
+ - app/lib/panda_pal/deep_linking_helpers.rb
162
163
  - app/lib/panda_pal/launch_url_helpers.rb
163
164
  - app/lib/panda_pal/lti_jwt_validator.rb
164
165
  - app/models/panda_pal/api_call.rb
@@ -197,6 +198,7 @@ files:
197
198
  - lib/panda_pal/helpers/route_helper.rb
198
199
  - lib/panda_pal/helpers/secure_headers.rb
199
200
  - lib/panda_pal/helpers/session_replacement.rb
201
+ - lib/panda_pal/lti_constants.rb
200
202
  - lib/panda_pal/plugins.rb
201
203
  - lib/panda_pal/spec_helper.rb
202
204
  - lib/panda_pal/version.rb