# frozen_string_literal: true # # Copyright (C) 2014 - 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 . # module Lti class AppLaunchCollator CONTENT_MESSAGE_TYPES = %w( ContentItemSelection ContentItemSelectionRequest LtiDeepLinkingRequest ).freeze def self.external_tools_for(context, placements, options = {}) tools_options = {} if options[:current_user] tools_options[:current_user] = options[:current_user] tools_options[:user] = options[:current_user] end if options[:only_visible] tools_options[:only_visible] = options[:only_visible] tools_options[:session] = options[:session] if options[:session] tools_options[:visibility_placements] = placements end ContextExternalTool.all_tools_for(context, tools_options).placements(*placements) end def self.message_handlers_for(context, placements) MessageHandler.for_context(context).has_placements(*placements) .by_message_types('basic-lti-launch-request') end def self.bookmarked_collection(context, placements, options = {}) external_tools = external_tools_for(context, placements, options) external_tools = BookmarkedCollection.wrap(ExternalToolNameBookmarker, external_tools) message_handlers = message_handlers_for(context, placements) message_handlers = BookmarkedCollection.wrap(MessageHandlerNameBookmarker, message_handlers) BookmarkedCollection.merge( ['external_tools', external_tools], ['message_handlers', message_handlers] ) end def self.any?(context, placements) external_tools = external_tools_for(context, placements) message_handlers = message_handlers_for(context, placements) external_tools.exists? || message_handlers.exists? end def self.launch_definitions(collection, placements) collection.map do |o| case o when ContextExternalTool lti1_launch_definition(o, placements) when MessageHandler lti2_launch_definition(o, placements) end end end private def self.selection_property_value(property, tool, placement, message_type) placement = placement.to_sym # Only return selection property if the message type offers content selection return unless CONTENT_MESSAGE_TYPES.include?(message_type) || placement == :resource_selection # For backward compatibility, check the "resource_selection" placement before the requested placement tool.extension_setting(:resource_selection, property) || tool.extension_setting(placement, property) end private_class_method :selection_property_value def self.lti1_launch_definition(tool, placements) definition = { definition_type: tool.class.name, definition_id: tool.id, name: tool.label_for(placements.first, I18n.locale), description: tool.description, domain: tool.domain, placements: {} } placements.each do |p| if tool.has_placement?(p) definition[:placements][p.to_sym] = { message_type: tool.extension_setting(p, :message_type) || tool.extension_default_value(p, :message_type), url: tool.extension_setting(p, :url) || tool.extension_default_value(p, :url) || tool.extension_default_value(p, :target_link_uri), title: tool.label_for(p, I18n.locale || I18n.default_locale.to_s), } message_type = definition.dig(:placements, p.to_sym, :message_type) if (width = selection_property_value(:selection_width, tool, p, message_type)) definition[:placements][p.to_sym][:selection_width] = width end if (height = selection_property_value(:selection_height, tool, p, message_type)) definition[:placements][p.to_sym][:selection_height] = height end %i[launch_width launch_height].each do |property| if tool.extension_setting(p, property) definition[:placements][p.to_sym][property] = tool.extension_setting(p, property) end end end end definition end def self.lti2_launch_definition(message_handler, placements) { definition_type: message_handler.class.name, definition_id: message_handler.id, name: message_handler.resource_handler.name, description: message_handler.resource_handler.description, domain: URI(message_handler.launch_path).host, placements: self.lti2_placements(message_handler, placements) } end def self.lti2_placements(message_handler, placements) resource_placements = message_handler.placements.pluck(:placement) valid_placements = if resource_placements.present? resource_placements & placements.map(&:to_s) else ResourcePlacement::LEGACY_DEFAULT_PLACEMENTS end valid_placements.each_with_object({}) { |p, hsh| hsh[p.to_sym] = lti2_placement(message_handler) } end def self.lti2_placement(message_handler) { message_type: message_handler.message_type, url: message_handler.launch_path, title: message_handler.resource_handler.name } end end end