228 lines
7.7 KiB
Ruby
228 lines
7.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
#
|
|
# Copyright (C) 2017 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/>.
|
|
#
|
|
|
|
module Lti
|
|
class ContentItemSelectionRequest
|
|
include ActionDispatch::Routing::PolymorphicRoutes
|
|
include Rails.application.routes.url_helpers
|
|
|
|
def initialize(context:, domain_root_account:, base_url:, tool:, user: nil, secure_params: nil)
|
|
@context = context
|
|
@domain_root_account = domain_root_account
|
|
@user = user
|
|
@base_url = URI.parse(base_url)
|
|
@tool = tool
|
|
@secure_params = secure_params
|
|
end
|
|
|
|
def generate_lti_launch(placement:, opts: {}, expanded_variables: {})
|
|
lti_launch = Lti::Launch.new(opts)
|
|
lti_launch.resource_url = opts[:launch_url] || @tool.extension_setting(placement, :url)
|
|
lti_launch.link_text = @tool.label_for(placement.to_sym, I18n.locale)
|
|
lti_launch.analytics_id = @tool.tool_id
|
|
lti_launch.params = launch_params(
|
|
lti_launch.resource_url,
|
|
placement,
|
|
expanded_variables,
|
|
opts[:content_item_id],
|
|
opts[:assignment]
|
|
)
|
|
|
|
lti_launch
|
|
end
|
|
|
|
def self.default_lti_params(context, domain_root_account, user = nil)
|
|
lti_helper = Lti::SubstitutionsHelper.new(context, domain_root_account, user)
|
|
|
|
params = {
|
|
context_id: Lti::Asset.opaque_identifier_for(context),
|
|
tool_consumer_instance_guid: domain_root_account.lti_guid,
|
|
roles: lti_helper.current_lis_roles,
|
|
launch_presentation_locale: I18n.locale.to_s || I18n.default_locale.to_s,
|
|
launch_presentation_document_target: 'iframe',
|
|
ext_roles: lti_helper.all_roles,
|
|
oauth_callback: 'about:blank'
|
|
}
|
|
|
|
params[:user_id] = Lti::Asset.opaque_identifier_for(user, context: context) if user
|
|
params
|
|
end
|
|
|
|
private
|
|
|
|
def launch_params(resource_url, placement, expanded_variables, content_item_id = nil, assignment = nil)
|
|
content_item_return_url = return_url(content_item_id)
|
|
|
|
params = ContentItemSelectionRequest.default_lti_params(@context, @domain_root_account, @user).
|
|
merge(message_params(content_item_return_url)).
|
|
merge(data: data_hash_jwt(resource_url, content_item_id)).
|
|
merge(placement_params(placement, assignment: assignment)).
|
|
merge(expanded_variables)
|
|
|
|
params[:ext_lti_assignment_id] = lti_assignment_id(assignment: assignment)
|
|
|
|
Lti::Security.signed_post_params(
|
|
params,
|
|
resource_url,
|
|
@tool.consumer_key,
|
|
@tool.shared_secret,
|
|
@context.root_account.feature_enabled?(:disable_lti_post_only) || @tool.extension_setting(:oauth_compliant)
|
|
)
|
|
end
|
|
|
|
def lti_assignment_id(assignment: nil)
|
|
assignment.try(:lti_context_id) || Lti::Security.decoded_lti_assignment_id(@secure_params)
|
|
end
|
|
|
|
def message_params(content_item_return_url)
|
|
{
|
|
# required params
|
|
lti_message_type: 'ContentItemSelectionRequest',
|
|
lti_version: 'LTI-1p0',
|
|
content_item_return_url: content_item_return_url,
|
|
context_title: @context.name,
|
|
# optional params
|
|
accept_multiple: false
|
|
}
|
|
end
|
|
|
|
def data_hash_jwt(resource_url, content_item_id = nil)
|
|
data_hash = {default_launch_url: resource_url}
|
|
if content_item_id
|
|
data_hash[:content_item_id] = content_item_id
|
|
data_hash[:oauth_consumer_key] = @tool.consumer_key
|
|
end
|
|
|
|
Canvas::Security.create_jwt(data_hash)
|
|
end
|
|
|
|
def return_url(content_item_id)
|
|
return_url_opts = {
|
|
service: 'external_tool_dialog',
|
|
host: @base_url.host,
|
|
protocol: @base_url.scheme,
|
|
port: @base_url.port
|
|
}
|
|
|
|
if content_item_id
|
|
return_url_opts[:id] = content_item_id
|
|
polymorphic_url([@context, :external_content_update], return_url_opts)
|
|
else
|
|
polymorphic_url([@context, :external_content_success], return_url_opts)
|
|
end
|
|
end
|
|
|
|
INDEX_MENU_TOOL_TYPES = %w{
|
|
assignment_index_menu
|
|
assignment_group_menu
|
|
discussion_topic_index_menu
|
|
file_index_menu
|
|
module_index_menu
|
|
module_group_menu
|
|
quiz_index_menu
|
|
wiki_index_menu
|
|
}.freeze
|
|
|
|
def placement_params(placement, assignment: nil)
|
|
case placement
|
|
when 'migration_selection'
|
|
migration_selection_params
|
|
when 'editor_button'
|
|
editor_button_params
|
|
when 'resource_selection', 'link_selection', 'assignment_selection', 'submission_type_selection'
|
|
lti_launch_selection_params
|
|
when 'collaboration'
|
|
collaboration_params
|
|
when 'homework_submission'
|
|
homework_submission_params(assignment)
|
|
when *INDEX_MENU_TOOL_TYPES
|
|
{}
|
|
else
|
|
# TODO: we _could_, if configured, have any other placements return to the content migration page...
|
|
raise ::Lti::Errors::UnsupportedPlacement, "Content-Item not supported at this placement"
|
|
end
|
|
end
|
|
|
|
def migration_selection_params
|
|
accept_media_types = %w(
|
|
application/vnd.ims.imsccv1p1
|
|
application/vnd.ims.imsccv1p2
|
|
application/vnd.ims.imsccv1p3
|
|
application/zip
|
|
application/xml
|
|
)
|
|
|
|
{
|
|
accept_media_types: accept_media_types.join(','),
|
|
accept_presentation_document_targets: 'download',
|
|
accept_copy_advice: true,
|
|
ext_content_file_extensions: %w(zip imscc mbz xml).join(','),
|
|
accept_unsigned: true,
|
|
auto_create: false
|
|
}
|
|
end
|
|
|
|
def editor_button_params
|
|
{
|
|
accept_media_types: %w(image/* text/html application/vnd.ims.lti.v1.ltilink */*).join(','),
|
|
accept_presentation_document_targets: %w(embed frame iframe window).join(','),
|
|
accept_unsigned: true,
|
|
accept_multiple: true,
|
|
auto_create: false
|
|
}
|
|
end
|
|
|
|
def lti_launch_selection_params
|
|
{
|
|
accept_media_types: 'application/vnd.ims.lti.v1.ltilink',
|
|
accept_presentation_document_targets: %w(frame window).join(','),
|
|
accept_unsigned: true,
|
|
auto_create: false
|
|
}
|
|
end
|
|
|
|
def collaboration_params
|
|
{
|
|
accept_media_types: 'application/vnd.ims.lti.v1.ltilink',
|
|
accept_presentation_document_targets: 'window',
|
|
accept_unsigned: false,
|
|
auto_create: true,
|
|
}
|
|
end
|
|
|
|
def homework_submission_params(assignment)
|
|
params = {}
|
|
params[:accept_media_types] = '*/*'
|
|
accept_presentation_document_targets = []
|
|
accept_presentation_document_targets << 'window' if assignment.submission_types.include?('online_url')
|
|
accept_presentation_document_targets << 'none' if assignment.submission_types.include?('online_upload')
|
|
params[:accept_presentation_document_targets] = accept_presentation_document_targets.join(',')
|
|
params[:accept_copy_advice] = !!assignment.submission_types.include?('online_upload')
|
|
if assignment.submission_types.strip == 'online_upload' && assignment.allowed_extensions.present?
|
|
params[:ext_content_file_extensions] = assignment.allowed_extensions.compact.join(',')
|
|
params[:accept_media_types] = assignment.allowed_extensions.map do |ext|
|
|
MimetypeFu::EXTENSIONS[ext]
|
|
end.compact.join(',')
|
|
end
|
|
params
|
|
end
|
|
end
|
|
end
|