You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
312 lines
13 KiB
312 lines
13 KiB
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle 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 General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
/**
|
|
* This file contains a class definition for the Tool Proxy resource
|
|
*
|
|
* @package ltiservice_toolproxy
|
|
* @copyright 2014 Vital Source Technologies http://vitalsource.com
|
|
* @author Stephen Vickers
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
|
|
namespace ltiservice_toolproxy\local\resources;
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
require_once($CFG->dirroot . '/mod/lti/OAuth.php');
|
|
require_once($CFG->dirroot . '/mod/lti/TrivialStore.php');
|
|
|
|
// TODO: Switch to core oauthlib once implemented - MDL-30149.
|
|
use moodle\mod\lti as lti;
|
|
|
|
/**
|
|
* A resource implementing the Tool Proxy.
|
|
*
|
|
* @package ltiservice_toolproxy
|
|
* @since Moodle 2.8
|
|
* @copyright 2014 Vital Source Technologies http://vitalsource.com
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class toolproxy extends \mod_lti\local\ltiservice\resource_base {
|
|
|
|
/**
|
|
* Class constructor.
|
|
*
|
|
* @param ltiservice_toolproxy\local\resources\toolproxy $service Service instance
|
|
*/
|
|
public function __construct($service) {
|
|
|
|
parent::__construct($service);
|
|
$this->id = 'ToolProxy.collection';
|
|
$this->template = '/toolproxy';
|
|
$this->formats[] = 'application/vnd.ims.lti.v2.toolproxy+json';
|
|
$this->methods[] = 'POST';
|
|
|
|
}
|
|
|
|
/**
|
|
* Execute the request for this resource.
|
|
*
|
|
* @param mod_lti\local\ltiservice\response $response Response object for this request.
|
|
*/
|
|
public function execute($response) {
|
|
|
|
$ok = $this->check_tool(null, $response->get_request_data());
|
|
$ok = $ok && ($this->get_service()->get_tool_proxy());
|
|
if ($ok) {
|
|
$toolproxy = $this->get_service()->get_tool_proxy();
|
|
}
|
|
if (!$ok) {
|
|
$toolproxy = null;
|
|
$response->set_code(401);
|
|
}
|
|
$tools = array();
|
|
|
|
// Ensure all required elements are present in the Tool Proxy.
|
|
if ($ok) {
|
|
$toolproxyjson = json_decode($response->get_request_data());
|
|
$ok = !empty($toolproxyjson);
|
|
if (!$ok) {
|
|
debugging('Tool proxy is not properly formed JSON');
|
|
} else {
|
|
$ok = isset($toolproxyjson->tool_profile->product_instance->product_info->product_family->vendor->code);
|
|
$ok = $ok && isset($toolproxyjson->security_contract->shared_secret);
|
|
$ok = $ok && isset($toolproxyjson->tool_profile->resource_handler);
|
|
if (!$ok) {
|
|
debugging('One or more missing elements from tool proxy: vendor code, shared secret or resource handlers');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check all capabilities requested were offered.
|
|
if ($ok) {
|
|
$offeredcapabilities = explode("\n", $toolproxy->capabilityoffered);
|
|
$resources = $toolproxyjson->tool_profile->resource_handler;
|
|
$errors = array();
|
|
foreach ($resources as $resource) {
|
|
if (isset($resource->message)) {
|
|
foreach ($resource->message as $message) {
|
|
if (!in_array($message->message_type, $offeredcapabilities)) {
|
|
$errors[] = $message->message_type;
|
|
} else if (isset($resource->parameter)) {
|
|
foreach ($message->parameter as $parameter) {
|
|
if (isset($parameter->variable) && !in_array($parameter->variable, $offeredcapabilities)) {
|
|
$errors[] = $parameter->variable;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (count($errors) > 0) {
|
|
$ok = false;
|
|
debugging('Tool proxy contains capabilities which were not offered: ' . implode(', ', $errors));
|
|
}
|
|
}
|
|
|
|
// Check all services requested were offered (only tool services currently supported).
|
|
$requestsbasicoutcomes = false;
|
|
if ($ok && isset($toolproxyjson->security_contract->tool_service)) {
|
|
$contexts = lti_get_contexts($toolproxyjson);
|
|
$profileservice = lti_get_service_by_name('profile');
|
|
$profileservice->set_tool_proxy($toolproxy);
|
|
$context = $profileservice->get_service_path() . $profileservice->get_resources()[0]->get_path() . '#';
|
|
$offeredservices = explode("\n", $toolproxy->serviceoffered);
|
|
$services = lti_get_services();
|
|
$tpservices = $toolproxyjson->security_contract->tool_service;
|
|
$errors = array();
|
|
foreach ($tpservices as $service) {
|
|
$fqid = lti_get_fqid($contexts, $service->service);
|
|
$requestsbasicoutcomes = $requestsbasicoutcomes || (substr($fqid, -13) === 'Outcomes.LTI1');
|
|
if (substr($fqid, 0, strlen($context)) !== $context) {
|
|
$errors[] = $service->service;
|
|
} else {
|
|
$id = explode('#', $fqid, 2);
|
|
$aservice = lti_get_service_by_resource_id($services, $id[1]);
|
|
$classname = explode('\\', get_class($aservice));
|
|
if (empty($aservice) || !in_array($classname[count($classname) - 1], $offeredservices)) {
|
|
$errors[] = $service->service;
|
|
}
|
|
}
|
|
}
|
|
if (count($errors) > 0) {
|
|
$ok = false;
|
|
debugging('Tool proxy contains services which were not offered: ' . implode(', ', $errors));
|
|
}
|
|
}
|
|
|
|
// Extract all launchable tools from the resource handlers.
|
|
if ($ok) {
|
|
$resources = $toolproxyjson->tool_profile->resource_handler;
|
|
$messagetypes = [
|
|
'basic-lti-launch-request',
|
|
'ContentItemSelectionRequest',
|
|
];
|
|
foreach ($resources as $resource) {
|
|
$launchable = false;
|
|
$messages = array();
|
|
$tool = new \stdClass();
|
|
|
|
$iconinfo = null;
|
|
if (is_array($resource->icon_info)) {
|
|
$iconinfo = $resource->icon_info[0];
|
|
} else {
|
|
$iconinfo = $resource->icon_info;
|
|
}
|
|
if (isset($iconinfo) && isset($iconinfo->default_location) && isset($iconinfo->default_location->path)) {
|
|
$tool->iconpath = $iconinfo->default_location->path;
|
|
}
|
|
|
|
foreach ($resource->message as $message) {
|
|
if (in_array($message->message_type, $messagetypes)) {
|
|
$launchable = $launchable || ($message->message_type === 'basic-lti-launch-request');
|
|
$messages[$message->message_type] = $message;
|
|
}
|
|
}
|
|
if (!$launchable) {
|
|
continue;
|
|
}
|
|
$tool->name = $resource->resource_name->default_value;
|
|
$tool->messages = $messages;
|
|
$tools[] = $tool;
|
|
}
|
|
$ok = count($tools) > 0;
|
|
if (!$ok) {
|
|
debugging('No launchable messages found in tool proxy');
|
|
}
|
|
}
|
|
|
|
// Add tools and custom parameters.
|
|
if ($ok) {
|
|
$baseurl = '';
|
|
if (isset($toolproxyjson->tool_profile->base_url_choice[0]->default_base_url)) {
|
|
$baseurl = $toolproxyjson->tool_profile->base_url_choice[0]->default_base_url;
|
|
}
|
|
$securebaseurl = '';
|
|
if (isset($toolproxyjson->tool_profile->base_url_choice[0]->secure_base_url)) {
|
|
$securebaseurl = $toolproxyjson->tool_profile->base_url_choice[0]->secure_base_url;
|
|
}
|
|
foreach ($tools as $tool) {
|
|
$messages = $tool->messages;
|
|
$launchrequest = $messages['basic-lti-launch-request'];
|
|
$config = new \stdClass();
|
|
$config->lti_toolurl = "{$baseurl}{$launchrequest->path}";
|
|
$config->lti_typename = $tool->name;
|
|
$config->lti_coursevisible = 1;
|
|
$config->lti_forcessl = 0;
|
|
if (isset($messages['ContentItemSelectionRequest'])) {
|
|
$contentitemrequest = $messages['ContentItemSelectionRequest'];
|
|
$config->lti_contentitem = 1;
|
|
if ($launchrequest->path !== $contentitemrequest->path) {
|
|
$config->lti_toolurl_ContentItemSelectionRequest = $baseurl . $contentitemrequest->path;
|
|
}
|
|
$contentitemcapabilities = implode("\n", $contentitemrequest->enabled_capability);
|
|
$config->lti_enabledcapability_ContentItemSelectionRequest = $contentitemcapabilities;
|
|
$contentitemparams = self::lti_extract_parameters($contentitemrequest->parameter);
|
|
$config->lti_parameter_ContentItemSelectionRequest = $contentitemparams;
|
|
}
|
|
|
|
$type = new \stdClass();
|
|
$type->state = LTI_TOOL_STATE_PENDING;
|
|
$type->ltiversion = LTI_VERSION_2;
|
|
$type->toolproxyid = $toolproxy->id;
|
|
// Ensure gradebook column is created.
|
|
if ($requestsbasicoutcomes && !in_array('BasicOutcome.url', $launchrequest->enabled_capability)) {
|
|
$launchrequest->enabled_capability[] = 'BasicOutcome.url';
|
|
}
|
|
if ($requestsbasicoutcomes && !in_array('BasicOutcome.sourcedId', $launchrequest->enabled_capability)) {
|
|
$launchrequest->enabled_capability[] = 'BasicOutcome.sourcedId';
|
|
}
|
|
$type->enabledcapability = implode("\n", $launchrequest->enabled_capability);
|
|
$type->parameter = self::lti_extract_parameters($launchrequest->parameter);
|
|
|
|
if (!empty($tool->iconpath)) {
|
|
$type->icon = "{$baseurl}{$tool->iconpath}";
|
|
if (!empty($securebaseurl)) {
|
|
$type->secureicon = "{$securebaseurl}{$tool->iconpath}";
|
|
}
|
|
}
|
|
|
|
$ok = $ok && (lti_add_type($type, $config) !== false);
|
|
}
|
|
if (isset($toolproxyjson->custom)) {
|
|
lti_set_tool_settings($toolproxyjson->custom, $toolproxy->id);
|
|
}
|
|
}
|
|
|
|
if (!empty($toolproxy)) {
|
|
if ($ok) {
|
|
// If all went OK accept the tool proxy.
|
|
$toolproxy->state = LTI_TOOL_PROXY_STATE_ACCEPTED;
|
|
$toolproxy->toolproxy = $response->get_request_data();
|
|
$toolproxy->secret = $toolproxyjson->security_contract->shared_secret;
|
|
$toolproxy->vendorcode = $toolproxyjson->tool_profile->product_instance->product_info->product_family->vendor->code;
|
|
|
|
$url = $this->get_endpoint();
|
|
$body = <<< EOD
|
|
{
|
|
"@context" : "http://purl.imsglobal.org/ctx/lti/v2/ToolProxyId",
|
|
"@type" : "ToolProxy",
|
|
"@id" : "{$url}",
|
|
"tool_proxy_guid" : "{$toolproxy->guid}"
|
|
}
|
|
EOD;
|
|
$response->set_code(201);
|
|
$response->set_content_type('application/vnd.ims.lti.v2.toolproxy.id+json');
|
|
$response->set_body($body);
|
|
} else {
|
|
// Otherwise reject the tool proxy.
|
|
$toolproxy->state = LTI_TOOL_PROXY_STATE_REJECTED;
|
|
$response->set_code(400);
|
|
}
|
|
lti_update_tool_proxy($toolproxy);
|
|
} else {
|
|
$response->set_code(400);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts the message parameters from the tool proxy entry
|
|
*
|
|
* @param array $parameters Parameter section of a message
|
|
*
|
|
* @return String containing parameters
|
|
*/
|
|
private static function lti_extract_parameters($parameters) {
|
|
|
|
$params = array();
|
|
foreach ($parameters as $parameter) {
|
|
if (isset($parameter->variable)) {
|
|
$value = '$' . $parameter->variable;
|
|
} else {
|
|
$value = $parameter->fixed;
|
|
if (strlen($value) > 0) {
|
|
$first = substr($value, 0, 1);
|
|
if (($first == '$') || ($first == '\\')) {
|
|
$value = '\\' . $value;
|
|
}
|
|
}
|
|
}
|
|
$params[] = "{$parameter->name}={$value}";
|
|
}
|
|
|
|
return implode("\n", $params);
|
|
|
|
}
|
|
|
|
}
|
|
|