diff --git a/.gitignore b/.gitignore index cfcbe42d1..f1525ea96 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ node_modules/ app/assets/javascripts/react /public/packs /node_modules + +localhost.crt +localhost.key diff --git a/Gemfile b/Gemfile index 0a9165d2e..3fe394d70 100644 --- a/Gemfile +++ b/Gemfile @@ -48,6 +48,7 @@ group :development do gem 'letter_opener' gem 'web-console' gem 'bullet' + gem 'awesome_print' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem diff --git a/Gemfile.lock b/Gemfile.lock index 43db8a0c1..0037fc210 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -64,6 +64,7 @@ GEM arel (7.1.1) authority (3.2.0) activesupport (>= 3.0.0) + awesome_print (1.7.0) axiom-types (0.1.1) descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) @@ -376,6 +377,7 @@ DEPENDENCIES acts_as_list ahoy_matey authority + awesome_print bourbon bullet byebug diff --git a/Procfile.dev b/Procfile.dev index b0b3a7fcd..44a5680ff 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,2 +1,2 @@ -web: bin/rails s -p3000 -b0.0.0.0 +web: [ -z "$LOCALHOST_SSL" ] && bin/rails s -p3000 -b0.0.0.0 || bundle exec puma -b 'ssl://0.0.0.0:3000?key=/vagrant/localhost.key&cert=/vagrant/localhost.crt' webpack: bin/webpack-dev-server diff --git a/README.markdown b/README.markdown index 720aa78cf..71085a38f 100644 --- a/README.markdown +++ b/README.markdown @@ -12,3 +12,13 @@ can `vagrant plugin install vagrant-fsnotify`), and then 2. In one terminal window or tmux pane: `vagrant ssh -c 'cd /vagrant && foreman start'`. 6. In another, `vagrant fsnotify` 6. Browse to http://localhost:3000/ + +## https://localhost + +When developing the LTI tool provider components of Gala, it is useful to be able to use https with the development server. This is how to set that up. + +1. Do this in the vagrant instance. Generate a self-signed certificate: `openssl req -new -newkey rsa:2048 -sha1 -days 365 -nodes -x509 -keyout localhost.key -out localhost.crt` +2. Trust the certificate in your vagrant instance: `sudo cp localhost.crt /etc/ssl/cert && sudo cp localhost.key /etc/ssl/private && sudo c_rehash` +3. Trust the certificate on your host machine using Keychain Access. Drag `localhost.crt` into the app, then Get Info and choose Always Trust. +4. Start the development servers with `LOCALHOST_SSL=true foreman start` +6. Browse to https://localhost:3000 (http will not work) diff --git a/app/controllers/authentication_strategies/config_controller.rb b/app/controllers/authentication_strategies/config_controller.rb new file mode 100644 index 000000000..0d9799df8 --- /dev/null +++ b/app/controllers/authentication_strategies/config_controller.rb @@ -0,0 +1,6 @@ +class AuthenticationStrategies::ConfigController < ApplicationController + + def lti + end + +end diff --git a/app/controllers/authentication_strategies/omniauth_callbacks_controller.rb b/app/controllers/authentication_strategies/omniauth_callbacks_controller.rb index 7ff552f2d..e3c58e5fb 100644 --- a/app/controllers/authentication_strategies/omniauth_callbacks_controller.rb +++ b/app/controllers/authentication_strategies/omniauth_callbacks_controller.rb @@ -1,9 +1,12 @@ class AuthenticationStrategies::OmniauthCallbacksController < Devise::OmniauthCallbacksController before_action :set_authentication_strategy, except: [:failure] + before_action :set_reader, except: [:failure] + before_action :set_case, only: [:lti] + before_action :set_group, only: [:lti] def google if @authentication_strategy.persisted? - sign_in_and_redirect @authentication_strategy.reader, event: :authentication + sign_in_and_redirect @reader, event: :authentication else session["devise.google_data"] = request.env["omniauth.auth"].except(:extra) render 'devise/registrations/new', layout: "window" @@ -12,7 +15,10 @@ def google def lti if @authentication_strategy.persisted? - sign_in_and_redirect @authentication_strategy.reader, event: :authentication + sign_in @reader + add_reader_to_group + enroll_reader_in_case if @case + redirect_to redirect_url else session["devise.lti_data"] = request.env["omniauth.auth"] render 'devise/registrations/new', layout: "window" @@ -28,4 +34,40 @@ def set_authentication_strategy @authentication_strategy = AuthenticationStrategy.from_omniauth(request.env["omniauth.auth"]) end + def set_reader + @reader = @authentication_strategy.reader + end + + def set_case + @case = Case.find_by_slug params[:case_slug] + end + + def set_group + begin + @group = Group.upsert context_id: params[:context_id], name: params[:context_title] + rescue + retry + end + end + + def add_reader_to_group + unless @reader.group_memberships.exists? group: @group + @reader.group_memberships.create group: @group + end + end + + def enroll_reader_in_case + Enrollment.upsert reader_id: @reader.id, + case_id: @case.id, + status: Enrollment.status_from_lti_role(params[:ext_roles]) + end + + def redirect_url + if @case + case_url @case + else + root_path + end + end + end diff --git a/app/controllers/cases_controller.rb b/app/controllers/cases_controller.rb index 99bf5221b..96ee4c8b8 100644 --- a/app/controllers/cases_controller.rb +++ b/app/controllers/cases_controller.rb @@ -16,7 +16,7 @@ def show authenticate_reader! unless @case.published authorize_action_for @case - render layout: 'application' + render layout: 'with_header' end def new diff --git a/app/controllers/catalog_controller.rb b/app/controllers/catalog_controller.rb index 164bfebaa..b64c6d52b 100644 --- a/app/controllers/catalog_controller.rb +++ b/app/controllers/catalog_controller.rb @@ -9,4 +9,13 @@ def home @index = cases_in_catalog.select(&:in_index?).sort_by &:kicker render layout: "window" end + + # LTI Assignment Selection wants to POST a ContentItemSelectionRequest + def content_items + I18n.locale = params[:launch_presentation_locale] + @items = Case.where(published: true).sort_by(&:kicker) + @return_url = params[:content_item_return_url] + @data = params[:data] + render layout: "embed" + end end diff --git a/app/javascript/content_items/index.jsx b/app/javascript/content_items/index.jsx new file mode 100644 index 000000000..762ae7cda --- /dev/null +++ b/app/javascript/content_items/index.jsx @@ -0,0 +1,79 @@ +// @flow + +import React from 'react' +import { chooseContentItem } from 'shared/lti' + +type ContentItem = {| + kicker: string, + title: string, + dek: string, + coverUrl: string, + url: string, +|} + +type ContentItemsProps = {| + items: ContentItem[], + returnUrl: string, + returnData: string, +|} + +const ContentItems = ({ items, returnUrl, returnData }: ContentItemsProps) => { + const handleChooseContentItem = chooseContentItem.bind( + undefined, + returnUrl, + returnData + ) + return ( +
+ {dek} +
+