canvas-lms/app/controllers/external_content_controller.rb

176 lines
6.1 KiB
Ruby

#
# Copyright (C) 2011 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require 'ims/lti'
IMS::LTI::Models::ContentItems::ContentItem.add_attribute :canvas_url, json_key: 'canvasURL'
class ExternalContentController < ApplicationController
protect_from_forgery :except => [:selection_test, :success], with: :exception
def success
normalize_deprecated_data!
@retrieved_data = {}
if params[:service] == 'equella'
params.each do |key, value|
if key.to_s.match(/\Aeq_/)
@retrieved_data[key.to_s.gsub(/\Aeq_/, "")] = value
end
end
elsif params[:return_type] == 'oembed'
js_env(oembed: {endpoint: params[:endpoint], url: params[:url]})
elsif params[:service] == 'external_tool_dialog'
get_context
@retrieved_data = content_items_for_canvas
elsif params[:service] == 'external_tool_redirect'
@hide_message = true if params[:service] == 'external_tool_redirect'
params[:return_type] = nil unless ['oembed', 'lti_launch_url', 'url', 'image_url', 'iframe', 'file'].include?(params[:return_type])
@retrieved_data = params
if @retrieved_data[:url] && ['oembed', 'lti_launch_url'].include?(params[:return_type])
begin
uri = URI.parse(@retrieved_data[:url])
unless uri.scheme
value = "http://#{value}"
uri = URI.parse(value)
end
@retrieved_data[:url] = uri.to_s
rescue URI::Error
@retrieved_data[:url] = nil
end
end
end
if params[:id]
message_auth = Lti::MessageAuthenticator.new(request.original_url, request.GET.merge(request.POST))
render_unauthorized_action and return unless message_auth.valid?
render_unauthorized_action and return unless json_data[:content_item_id] == params[:id]
render_unauthorized_action and return unless json_data[:oauth_consumer_key] == params[:oauth_consumer_key]
end
@headers = false
js_env(retrieved_data: (@retrieved_data || {}), lti_response_messages: lti_response_messages,
service: params[:service], service_id: params[:id])
end
def normalize_deprecated_data!
params[:return_type] = params[:embed_type] if !params.key?(:return_type) && params.key?(:embed_type)
return_types = {'basic_lti' => 'lti_launch_url', 'link' => 'url', 'image' => 'image_url'}
params[:return_type] = return_types[params[:return_type]] if return_types.key? params[:return_type]
end
def oembed_retrieve
endpoint = params[:endpoint]
url = params[:url]
uri = URI.parse(endpoint + (endpoint.match(/\?/) ? '&url=' : '?url=') + CGI.escape(url) + '&format=json')
begin
res = CanvasHttp.get(uri.to_s)
data = JSON.parse(res.body)
content_item = Lti::ContentItemConverter.convert_oembed(data)
rescue StandardError
content_item = {}
end
render :json => [content_item]
end
# this is a simple LTI link selection extension example
# it's used by the selenium specs, and can be useful to demonstrate link
# selection and test configuration
def selection_test
@return_url = params[:launch_presentation_return_url]
if @return_url
uri = URI.parse(@return_url)
@return_url = nil unless uri.is_a?(URI::HTTP)
end
if @return_url.blank?
head :bad_request
end
@headers = false
end
def cancel
@headers = false
js_env(service: params[:service])
end
def content_items_for_canvas
content_item_selection.map do |item|
item.placement_advice ||= default_placement_advice
if item.type == IMS::LTI::Models::ContentItems::LtiLinkItem::TYPE
launch_url = item.url || json_data[:default_launch_url]
url_gen_params = {url: launch_url}
displays = {'iframe' => 'borderless', 'window' => 'borderless'}
url_gen_params[:display] =
displays[item.placement_advice.presentation_document_target]
item.canvas_url = named_context_url(@context, :retrieve_context_external_tools_path, url_gen_params)
end
item
end
end
private
def content_item_selection
if params[:lti_message_type]
message = IMS::LTI::Models::Messages::Message.generate(request.GET && request.POST)
message.content_items
else
filtered_params = params.permit(*%w(url text title return_type content_type height width))
[Lti::ContentItemConverter.convert_resource_selection(filtered_params)]
end
end
def lti_response_messages
@lti_response_messages ||= (
response_messages = {}
lti_msg = param_if_set "lti_msg"
lti_log = param_if_set "lti_log"
lti_errormsg = param_if_set("lti_errormsg") {|error_msg| logger.warn error_msg}
lti_errorlog = param_if_set("lti_errorlog") {|error_log| logger.warn error_log}
response_messages[:lti_msg] = lti_msg if lti_msg
response_messages[:lti_log] = lti_log if lti_log
response_messages[:lti_errormsg] = lti_errormsg if lti_errormsg
response_messages[:lti_errorlog] = lti_errorlog if lti_errorlog
response_messages
)
end
def param_if_set(param_key)
param_value = params[param_key] && !params[param_key].empty? && params[param_key]
if param_value && block_given?
yield param_value
end
param_value
end
def default_placement_advice
IMS::LTI::Models::ContentItemPlacement.new(
presentation_document_target: 'default',
display_height: 600,
display_width: 800
)
end
def json_data
@json_data ||= ((params[:data] && Canvas::Security.decode_jwt(params[:data])) || {}).with_indifferent_access
end
end