panda_pal 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +37 -0
- data/app/assets/javascripts/panda_pal/application.js +13 -0
- data/app/assets/javascripts/panda_pal/lti.js +2 -0
- data/app/assets/stylesheets/panda_pal/application.css +15 -0
- data/app/assets/stylesheets/panda_pal/lti.css +4 -0
- data/app/controllers/panda_pal/application_controller.rb +4 -0
- data/app/controllers/panda_pal/lti_controller.rb +50 -0
- data/app/helpers/panda_pal/application_helper.rb +4 -0
- data/app/models/panda_pal/organization.rb +28 -0
- data/app/models/panda_pal/session.rb +9 -0
- data/app/views/layouts/panda_pal/application.html.erb +14 -0
- data/app/views/panda_pal/lti/launch.html.erb +1 -0
- data/config/initializers/apartment.rb +15 -0
- data/config/initializers/delayed_job.rb +1 -0
- data/config/routes.rb +4 -0
- data/db/f60326d86923901b6cfdb11a6d8ebd68.sqlite3 +0 -0
- data/db/migrate/20160412205931_create_panda_pal_organizations.rb +15 -0
- data/db/migrate/20160413132229_create_delayed_jobs.rb +23 -0
- data/db/migrate/20160413135653_create_panda_pal_sessions.rb +11 -0
- data/lib/panda_pal/engine.rb +45 -0
- data/lib/panda_pal/helpers/controller_helper.rb +62 -0
- data/lib/panda_pal/helpers/route_helper.rb +17 -0
- data/lib/panda_pal/helpers.rb +4 -0
- data/lib/panda_pal/plugins/apartment_delayed_jobs_plugin.rb +40 -0
- data/lib/panda_pal/plugins.rb +3 -0
- data/lib/panda_pal/version.rb +3 -0
- data/lib/panda_pal.rb +63 -0
- data/lib/tasks/panda_pal_tasks.rake +6 -0
- data/panda_pal.gemspec +28 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config/application.rb +26 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20160415162152_create_panda_pal_organizations.panda_pal.rb +16 -0
- data/spec/dummy/db/migrate/20160415162153_create_delayed_jobs.panda_pal.rb +24 -0
- data/spec/dummy/db/migrate/20160415162154_create_panda_pal_sessions.panda_pal.rb +12 -0
- data/spec/dummy/db/schema.rb +55 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +101 -0
- data/spec/dummy/log/test.log +657 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/factories/panda_pal_organizations.rb +8 -0
- data/spec/factories/panda_pal_sessions.rb +7 -0
- data/spec/models/panda_pal/organization_spec.rb +26 -0
- data/spec/models/panda_pal/session_spec.rb +9 -0
- data/spec/rails_helper.rb +31 -0
- data/spec/spec_helper.rb +85 -0
- metadata +296 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1a4d8f00cdcebbcbcf57fb961606da6de1ddd429
|
4
|
+
data.tar.gz: 2ff5dc92d8bf4156db09664130c435a80a0546b2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8f7a8df21f81ce3f3f8083b29606d5650d99ae9ef5e7d206a57274c2596b0fe1654e2009ac8685c49f88df1d33cd2815d0db777e81f46953b1e1c10b69ade2ea
|
7
|
+
data.tar.gz: b9fd96b44ee9eab58e607a2cf7604acadc239dfcd537d110c21328b98abd5c77e0e9f8f65f280e351230828d50e400eee27dc871509444c3fb96d67e9ea4edfc
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2016
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'PandaPal'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
load 'rails/tasks/statistics.rake'
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
Bundler::GemHelper.install_tasks
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.libs << 'test'
|
32
|
+
t.pattern = 'test/**/*_test.rb'
|
33
|
+
t.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
task default: :test
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
11
|
+
* file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_dependency "panda_pal/application_controller"
|
2
|
+
|
3
|
+
module PandaPal
|
4
|
+
class LtiController < ApplicationController
|
5
|
+
|
6
|
+
def config
|
7
|
+
lti_options = PandaPal.lti_options
|
8
|
+
lti_nav = PandaPal.lti_paths
|
9
|
+
lti_environments = PandaPal.lti_environments
|
10
|
+
lti_custom_params = PandaPal.lti_custom_params
|
11
|
+
|
12
|
+
if lti_environments.empty?
|
13
|
+
render text: 'Domains must be set in lti_environments'
|
14
|
+
return
|
15
|
+
end
|
16
|
+
|
17
|
+
platform = lti_options.delete(:platform) || 'canvas.instructure.com'
|
18
|
+
host = "#{request.scheme}://#{request.host_with_port}"
|
19
|
+
tc = IMS::LTI::ToolConfig.new(:title => lti_options[:title], :launch_url => ("#{host}#{lti_options[:launch_route]}") || 'ABC')
|
20
|
+
tc.set_ext_param(platform, :domain, request.host)
|
21
|
+
tc.set_ext_param(platform, :privacy_level, 'public')
|
22
|
+
lti_custom_params.each do |k, v|
|
23
|
+
tc.set_custom_param k, v
|
24
|
+
end
|
25
|
+
if lti_options.has_key?(:custom_fields)
|
26
|
+
tc.set_ext_param(platform, :custom_fields, lti_options[:custom_fields])
|
27
|
+
lti_options[:custom_fields].each do |k, v|
|
28
|
+
tc.set_ext_param(platform, k, v)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
lti_nav.each do |k, v|
|
33
|
+
tc.set_ext_param(platform, k.to_sym, ext_params(v))
|
34
|
+
end
|
35
|
+
|
36
|
+
tc.set_ext_param(platform, :environments, lti_environments)
|
37
|
+
|
38
|
+
#strip the launch url
|
39
|
+
xml = tc.to_xml
|
40
|
+
xml = xml.sub(/<blti:launch_url>.*<\/blti:launch_url>/, '') if lti_options[:launch_route].blank?
|
41
|
+
render :xml => xml
|
42
|
+
end
|
43
|
+
|
44
|
+
def ext_params(options)
|
45
|
+
url = options.delete(:url)
|
46
|
+
options[:url] = main_app.send([url,'_url'].join)
|
47
|
+
options
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module PandaPal
|
2
|
+
class Organization < ActiveRecord::Base
|
3
|
+
|
4
|
+
validates :key, uniqueness: { case_sensitive: false }, presence: true
|
5
|
+
validates :secret, presence: true
|
6
|
+
validates :name, uniqueness: { case_sensitive: false }, presence: true, format: { with: /\A[a-z0-9_]+\z/i }
|
7
|
+
validates :canvas_account_id, presence: true
|
8
|
+
|
9
|
+
after_create :create_schema
|
10
|
+
after_commit :destroy_schema, on: :destroy
|
11
|
+
|
12
|
+
before_validation on: [:update] do
|
13
|
+
errors.add(:name, 'should not be changed after creation') if name_changed?
|
14
|
+
end
|
15
|
+
|
16
|
+
serialize :settings, Hash
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def create_schema
|
21
|
+
Apartment::Tenant.create name
|
22
|
+
end
|
23
|
+
|
24
|
+
def destroy_schema
|
25
|
+
Apartment::Tenant.drop name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>PandaPal</title>
|
5
|
+
<%= stylesheet_link_tag "panda_pal/application", media: "all" %>
|
6
|
+
<%= javascript_include_tag "panda_pal/application" %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<%= yield %>
|
12
|
+
|
13
|
+
</body>
|
14
|
+
</html>
|
@@ -0,0 +1 @@
|
|
1
|
+
<h1>You should do an LTI Tool Launch.</h1>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'apartment/elevators/generic'
|
2
|
+
|
3
|
+
Apartment.configure do |config|
|
4
|
+
config.excluded_models = ['PandaPal::Organization', 'PandaPal::Session', 'Delayed::Job']
|
5
|
+
|
6
|
+
config.tenant_names = lambda {
|
7
|
+
PandaPal::Organization.pluck(:name)
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
Rails.application.config.middleware.use 'Apartment::Elevators::Generic', lambda { |request|
|
12
|
+
if match = request.path.match(/\/(?:orgs|organizations)\/(\d+)/)
|
13
|
+
PandaPal::Organization.find_by(id: match[1]).try(:name)
|
14
|
+
end
|
15
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
Delayed::Worker.plugins << PandaPal::Plugins::ApartmentDelayedJobsPlugin
|
data/config/routes.rb
ADDED
Binary file
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreatePandaPalOrganizations < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :panda_pal_organizations do |t|
|
4
|
+
t.string :name
|
5
|
+
t.string :key
|
6
|
+
t.string :secret
|
7
|
+
t.string :canvas_account_id
|
8
|
+
t.text :settings
|
9
|
+
|
10
|
+
t.timestamps null: false
|
11
|
+
end
|
12
|
+
add_index :panda_pal_organizations, :name, unique: true
|
13
|
+
add_index :panda_pal_organizations, :key, unique: true
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class CreateDelayedJobs < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :delayed_jobs, force: true do |table|
|
4
|
+
table.integer :priority, default: 0, null: false # Allows some jobs to jump to the front of the queue
|
5
|
+
table.integer :attempts, default: 0, null: false # Provides for retries, but still fail eventually.
|
6
|
+
table.text :handler, null: false # YAML-encoded string of the object that will do work
|
7
|
+
table.text :last_error # reason for last failure (See Note below)
|
8
|
+
table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
|
9
|
+
table.datetime :locked_at # Set when a client is working on this object
|
10
|
+
table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
|
11
|
+
table.string :locked_by # Who is working on this object (if locked)
|
12
|
+
table.string :queue # The name of the queue this job is in
|
13
|
+
table.string :tenant
|
14
|
+
table.timestamps null: true
|
15
|
+
end
|
16
|
+
|
17
|
+
add_index :delayed_jobs, [:priority, :run_at], name: "delayed_jobs_priority"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.down
|
21
|
+
drop_table :delayed_jobs
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'apartment'
|
2
|
+
require 'delayed_job_active_record'
|
3
|
+
require 'ims/lti'
|
4
|
+
require 'oauth/request_proxy/rack_request'
|
5
|
+
require 'panda_pal/plugins'
|
6
|
+
require 'panda_pal/helpers'
|
7
|
+
|
8
|
+
module PandaPal
|
9
|
+
class Engine < ::Rails::Engine
|
10
|
+
config.autoload_once_paths += Dir["#{config.root}/lib/**/"]
|
11
|
+
|
12
|
+
isolate_namespace PandaPal
|
13
|
+
|
14
|
+
config.generators do |g|
|
15
|
+
g.test_framework :rspec
|
16
|
+
g.fixture_replacement :factory_girl, :dir => 'spec/factories'
|
17
|
+
end
|
18
|
+
|
19
|
+
initializer :append_migrations do |app|
|
20
|
+
unless app.root.to_s.match root.to_s
|
21
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
22
|
+
app.config.paths["db/migrate"] << expanded_path
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
initializer 'panda_pal.app_controller' do |app|
|
28
|
+
OAUTH_10_SUPPORT = true
|
29
|
+
ActiveSupport.on_load(:action_controller) do
|
30
|
+
include PandaPal::Helpers::ControllerHelper
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
initializer 'panda_pal.route_helper' do |route|
|
35
|
+
ActionDispatch::Routing::Mapper.send :include, PandaPal::Helpers::RouteHelper
|
36
|
+
end
|
37
|
+
|
38
|
+
initializer 'panda_pal.route_options', :after => :disable_dependency_loading do |app|
|
39
|
+
ActiveSupport.on_load(:action_controller) do
|
40
|
+
Rails.application.reload_routes!
|
41
|
+
PandaPal::propagate_lti_navigation
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module PandaPal::Helpers::ControllerHelper
|
2
|
+
def save_session
|
3
|
+
current_session.try(:save)
|
4
|
+
end
|
5
|
+
|
6
|
+
def current_session
|
7
|
+
@current_session ||= PandaPal::Session.find_by(session_key: session_key) if session_key
|
8
|
+
@current_session ||= PandaPal::Session.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def current_organization
|
12
|
+
@organization ||= PandaPal::Organization.find_by!(key: organization_key) if organization_key
|
13
|
+
@organization ||= PandaPal::Organization.find_by(id: params[:organization_id]) if params[:organization_id]
|
14
|
+
@organization ||= PandaPal::Organization.find_by_name(Apartment::Tenant.current)
|
15
|
+
end
|
16
|
+
|
17
|
+
def current_session_data
|
18
|
+
current_session.data
|
19
|
+
end
|
20
|
+
|
21
|
+
def session_changed?
|
22
|
+
current_session.changed? && current_session.changes[:data].present?
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate_launch!
|
26
|
+
authorized = false
|
27
|
+
if @organization = params['oauth_consumer_key'] && PandaPal::Organization.find_by_key(params['oauth_consumer_key'])
|
28
|
+
@tp = IMS::LTI::ToolProvider.new(@organization.key, @organization.secret, params)
|
29
|
+
authorized = @tp.valid_request?(request)
|
30
|
+
end
|
31
|
+
render :text => 'Invalid Credentials, please contact your Administrator.', :status => :unauthorized unless authorized
|
32
|
+
authorized
|
33
|
+
end
|
34
|
+
|
35
|
+
def switch_tenant(organization = current_organization, &block)
|
36
|
+
return unless organization
|
37
|
+
raise 'This method should be called in an around_action callback' unless block_given?
|
38
|
+
|
39
|
+
Apartment::Tenant.switch(organization.name) do
|
40
|
+
yield
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def forbid_access_if_lacking_session
|
45
|
+
render text: 'You should do an LTI Tool Launch.', status: :unauthorized unless current_session.persisted?
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def organization_key
|
50
|
+
params[:oauth_consumer_key] || current_session_data[:organization_key] || session[:organization_key]
|
51
|
+
end
|
52
|
+
|
53
|
+
def session_key
|
54
|
+
params[:session_key] || session_key_header || flash[:session_key] || session[:session_key]
|
55
|
+
end
|
56
|
+
|
57
|
+
def session_key_header
|
58
|
+
if match = request.headers['Authorization'].try(:match, /token=(.+)/)
|
59
|
+
match[1]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module PandaPal::Helpers::RouteHelper
|
2
|
+
def lti_nav(nav, *rest, &block)
|
3
|
+
base_path = Rails.application.routes.named_routes[:panda_pal].path.spec
|
4
|
+
raise LtiNavigationInUse.new('PandaPal must be mounted before defining lti_nav routes') if base_path.blank?
|
5
|
+
options = nav
|
6
|
+
nav, to = options.first
|
7
|
+
options[:to] = to
|
8
|
+
options.delete nav
|
9
|
+
lti_options = options.delete(:lti_options) || {}
|
10
|
+
path = "#{base_path}/#{nav.to_s}"
|
11
|
+
lti_options[:url] = path.split('/').reject(&:empty?).join('_')
|
12
|
+
post path, options, &block
|
13
|
+
get path, options, &block
|
14
|
+
PandaPal::register_navigation(nav)
|
15
|
+
PandaPal::lti_navigation(nav, lti_options)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module PandaPal::Plugins
|
2
|
+
class ApartmentDelayedJobsPlugin < ::Delayed::Plugin
|
3
|
+
callbacks do |lifecycle|
|
4
|
+
lifecycle.around(:enqueue) do |job, *args, &block|
|
5
|
+
current_tenant = Apartment::Tenant.current
|
6
|
+
|
7
|
+
#make sure enqueue on public tenant unless we are testing since delayed job is set to run immediately
|
8
|
+
Apartment::Tenant.switch!('public') unless Rails.env.test?
|
9
|
+
job.tenant = current_tenant
|
10
|
+
begin
|
11
|
+
block.call(job, *args)
|
12
|
+
rescue Exception => e
|
13
|
+
Rails.logger.error("Error enqueing job #{job.to_s} - #{e.backtrace}")
|
14
|
+
ensure
|
15
|
+
#switch back to prev tenant
|
16
|
+
Apartment::Tenant.switch!(current_tenant)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
lifecycle.before(:perform) do |worker, *args, &block|
|
22
|
+
tenant = args.first.tenant
|
23
|
+
Apartment::Tenant.switch!(tenant) if tenant.present?
|
24
|
+
Rails.logger.debug("Running job with tenant #{Apartment::Tenant.current}")
|
25
|
+
end
|
26
|
+
|
27
|
+
lifecycle.around(:invoke_job) do |job, *args, &block|
|
28
|
+
block.call(job, *args)
|
29
|
+
Apartment::Tenant.switch!('public')
|
30
|
+
Rails.logger.debug("Resetting Tenant back to: #{Apartment::Tenant.current}")
|
31
|
+
end
|
32
|
+
|
33
|
+
lifecycle.after(:failure) do |job, *args|
|
34
|
+
Rails.logger.error("Job failed on tenant: #{Apartment::Tenant.current}")
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/panda_pal.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require "panda_pal/engine"
|
2
|
+
|
3
|
+
module PandaPal
|
4
|
+
class LtiNavigationInUse < StandardError;end
|
5
|
+
class NotMounted < StandardError;end
|
6
|
+
|
7
|
+
@@lti_navigation = {}
|
8
|
+
@@staged_navigation = {}
|
9
|
+
@@lti_options = {}
|
10
|
+
@@lti_environments = {}
|
11
|
+
@@lti_custom_params = {}
|
12
|
+
|
13
|
+
def self.lti_options= lti_options
|
14
|
+
@@lti_options = lti_options
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.lti_options
|
18
|
+
@@lti_options.deep_dup
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.lti_environments=(lti_environments)
|
22
|
+
@@lti_environments = lti_environments
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.lti_environments
|
26
|
+
@@lti_environments.deep_dup
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.lti_custom_params=(custom_params)
|
30
|
+
@@lti_custom_params = custom_params
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.lti_custom_params
|
34
|
+
@@lti_custom_params.deep_dup
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.register_navigation(navigation)
|
38
|
+
@@lti_navigation[navigation] ||= {}
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.stage_navigation(navigation, options)
|
42
|
+
@@staged_navigation[navigation] = {} unless @@staged_navigation.has_key?(navigation)
|
43
|
+
@@staged_navigation[navigation].merge!(options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.lti_paths
|
47
|
+
@@lti_navigation.deep_dup
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.propagate_lti_navigation
|
51
|
+
@@staged_navigation.each do |k,v|
|
52
|
+
lti_navigation(k,v)
|
53
|
+
@@staged_navigation.delete(k)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def self.lti_navigation(navigation, options)
|
60
|
+
raise "lti navigation '#{navigation}' has not been registered!" unless @@lti_navigation.has_key?(navigation)
|
61
|
+
@@lti_navigation[navigation].merge!(options)
|
62
|
+
end
|
63
|
+
end
|
data/panda_pal.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
require "panda_pal/version"
|
4
|
+
|
5
|
+
# Describe your gem and declare its dependencies:
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "panda_pal"
|
8
|
+
s.version = PandaPal::VERSION
|
9
|
+
s.authors = ["Ben Young"]
|
10
|
+
s.email = ["byoung@instructure.com"]
|
11
|
+
s.homepage = "http://instructure.com"
|
12
|
+
s.summary = "LTI mountable engine"
|
13
|
+
s.license = "MIT"
|
14
|
+
|
15
|
+
s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc", "panda_pal.gemspec"]
|
16
|
+
s.test_files = Dir["spec/**/*"]
|
17
|
+
|
18
|
+
s.add_dependency "rails", "~> 4.2.5.2"
|
19
|
+
s.add_dependency "pg", '~> 0.18.4'
|
20
|
+
s.add_dependency 'apartment', '~> 1.0.2'
|
21
|
+
s.add_dependency 'delayed_job_active_record'
|
22
|
+
s.add_dependency 'ims-lti'
|
23
|
+
|
24
|
+
s.add_development_dependency 'sqlite3'
|
25
|
+
s.add_development_dependency 'rspec-rails'
|
26
|
+
s.add_development_dependency 'factory_girl_rails'
|
27
|
+
s.add_development_dependency 'test_after_commit'
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
== README
|
2
|
+
|
3
|
+
This README would normally document whatever steps are necessary to get the
|
4
|
+
application up and running.
|
5
|
+
|
6
|
+
Things you may want to cover:
|
7
|
+
|
8
|
+
* Ruby version
|
9
|
+
|
10
|
+
* System dependencies
|
11
|
+
|
12
|
+
* Configuration
|
13
|
+
|
14
|
+
* Database creation
|
15
|
+
|
16
|
+
* Database initialization
|
17
|
+
|
18
|
+
* How to run the test suite
|
19
|
+
|
20
|
+
* Services (job queues, cache servers, search engines, etc.)
|
21
|
+
|
22
|
+
* Deployment instructions
|
23
|
+
|
24
|
+
* ...
|
25
|
+
|
26
|
+
|
27
|
+
Please feel free to use a different markup language if you do not plan to run
|
28
|
+
<tt>rake doc:app</tt>.
|
data/spec/dummy/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
11
|
+
* file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|