diff --git a/lib/classes/plugin_manager.php b/lib/classes/plugin_manager.php index 334de7c6b9d..22235f6d712 100644 --- a/lib/classes/plugin_manager.php +++ b/lib/classes/plugin_manager.php @@ -1052,6 +1052,10 @@ class core_plugin_manager { 'database', 'legacy', 'standard', ), + 'ltiservice' => array( + 'profile', 'toolproxy', 'toolsettings' + ), + 'message' => array( 'airnotifier', 'email', 'jabber', 'popup' ), diff --git a/mod/lti/OAuthBody.php b/mod/lti/OAuthBody.php index 5f43f69d39e..5b8033d1084 100644 --- a/mod/lti/OAuthBody.php +++ b/mod/lti/OAuthBody.php @@ -1,4 +1,19 @@ . +// // This file is part of BasicLTI4Moodle // // BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability) @@ -15,96 +30,51 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu -// -// OAuthBody.php is distributed under the MIT License -// -// The MIT License -// -// Copyright (c) 2007 Andy Smith -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -// 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 . +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. -namespace moodle\mod\lti;//Using a namespace as the basicLTI module imports classes with the same names +namespace moodle\mod\lti; // Using a namespace as the basicLTI module imports classes with the same names. defined('MOODLE_INTERNAL') || die; require_once($CFG->dirroot . '/mod/lti/OAuth.php'); require_once($CFG->dirroot . '/mod/lti/TrivialStore.php'); -function getOAuthKeyFromHeaders() -{ - $request_headers = OAuthUtil::get_headers(); - // print_r($request_headers); +function get_oauth_key_from_headers() { + $requestheaders = OAuthUtil::get_headers(); - if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") { - $header_parameters = OAuthUtil::split_header($request_headers['Authorization']); + if (@substr($requestheaders['Authorization'], 0, 6) == "OAuth ") { + $headerparameters = OAuthUtil::split_header($requestheaders['Authorization']); - // echo("HEADER PARMS=\n"); - // print_r($header_parameters); - return $header_parameters['oauth_consumer_key']; + return format_string($headerparameters['oauth_consumer_key']); } return false; } -function handleOAuthBodyPOST($oauth_consumer_key, $oauth_consumer_secret, $body, $request_headers = null) -{ - if($request_headers == null){ - $request_headers = OAuthUtil::get_headers(); +function handle_oauth_body_post($oauthconsumerkey, $oauthconsumersecret, $body, $requestheaders = null) { + + if ($requestheaders == null) { + $requestheaders = OAuthUtil::get_headers(); } - // Must reject application/x-www-form-urlencoded - if (isset($request_headers['Content-type'])) { - if ($request_headers['Content-type'] == 'application/x-www-form-urlencoded' ) { + // Must reject application/x-www-form-urlencoded. + if (isset($requestheaders['Content-type'])) { + if ($requestheaders['Content-type'] == 'application/x-www-form-urlencoded' ) { throw new OAuthException("OAuth request body signing must not use application/x-www-form-urlencoded"); } } - if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") { - $header_parameters = OAuthUtil::split_header($request_headers['Authorization']); - - // echo("HEADER PARMS=\n"); - // print_r($header_parameters); - $oauth_body_hash = $header_parameters['oauth_body_hash']; - // echo("OBH=".$oauth_body_hash."\n"); + if (@substr($requestheaders['Authorization'], 0, 6) == "OAuth ") { + $headerparameters = OAuthUtil::split_header($requestheaders['Authorization']); + $oauthbodyhash = $headerparameters['oauth_body_hash']; } - if ( ! isset($oauth_body_hash) ) { + if ( ! isset($oauthbodyhash) ) { throw new OAuthException("OAuth request body signing requires oauth_body_hash body"); } - // Verify the message signature + // Verify the message signature. $store = new TrivialOAuthDataStore(); - $store->add_consumer($oauth_consumer_key, $oauth_consumer_secret); + $store->add_consumer($oauthconsumerkey, $oauthconsumersecret); $server = new OAuthServer($store); @@ -120,46 +90,12 @@ function handleOAuthBodyPOST($oauth_consumer_key, $oauth_consumer_secret, $body, } $postdata = $body; - // echo($postdata); - $hash = base64_encode(sha1($postdata, TRUE)); + $hash = base64_encode(sha1($postdata, true)); - if ( $hash != $oauth_body_hash ) { + if ( $hash != $oauthbodyhash ) { throw new OAuthException("OAuth oauth_body_hash mismatch"); } return $postdata; } - -function sendOAuthBodyPOST($method, $endpoint, $oauth_consumer_key, $oauth_consumer_secret, $content_type, $body) -{ - $hash = base64_encode(sha1($body, TRUE)); - - $parms = array('oauth_body_hash' => $hash); - - $test_token = ''; - $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); - $test_consumer = new OAuthConsumer($oauth_consumer_key, $oauth_consumer_secret, NULL); - - $acc_req = OAuthRequest::from_consumer_and_token($test_consumer, $test_token, $method, $endpoint, $parms); - $acc_req->sign_request($hmac_method, $test_consumer, $test_token); - - $header = $acc_req->to_header(); - $header = $header . "\r\nContent-type: " . $content_type . "\r\n"; - - $params = array('http' => array( - 'method' => 'POST', - 'content' => $body, - 'header' => $header - )); - $ctx = stream_context_create($params); - $fp = @fopen($endpoint, 'rb', false, $ctx); - if (!$fp) { - throw new OAuthException("Problem with $endpoint, $php_errormsg"); - } - $response = @stream_get_contents($fp); - if ($response === false) { - throw new OAuthException("Problem reading data from $endpoint, $php_errormsg"); - } - return $response; -} diff --git a/mod/lti/TrivialStore.php b/mod/lti/TrivialStore.php index 73c3ad5a248..cc4765f2725 100644 --- a/mod/lti/TrivialStore.php +++ b/mod/lti/TrivialStore.php @@ -1,23 +1,21 @@ . +// // This file is part of BasicLTI4Moodle // -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -// // BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability) // consumer for Moodle 1.9 and Moodle 2.0. BasicLTI is a IMS Standard that allows web // based learning tools to be easily integrated in LMS as native ones. The IMS BasicLTI @@ -32,20 +30,7 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu -// -// 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 . +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * This file contains a Trivial memory-based store - no support for tokens @@ -58,50 +43,97 @@ * @license http://www.apache.org/licenses/LICENSE-2.0 */ -namespace moodle\mod\lti;//Using a namespace as the basicLTI module imports classes with the same names +namespace moodle\mod\lti; // Using a namespace as the basicLTI module imports classes with the same names. defined('MOODLE_INTERNAL') || die; /** - * A Trivial memory-based store - no support for tokens + * A Trivial memory-based store - no support for tokens. */ class TrivialOAuthDataStore extends OAuthDataStore { + + /** @var array $consumers Array of tool consumer keys and secrets */ private $consumers = array(); - function add_consumer($consumer_key, $consumer_secret) { - $this->consumers[$consumer_key] = $consumer_secret; + /** + * Add a consumer to the array + * + * @param string $consumerkey Consumer key + * @param string $consumersecret Consumer secret + */ + public function add_consumer($consumerkey, $consumersecret) { + $this->consumers[$consumerkey] = $consumersecret; } - function lookup_consumer($consumer_key) { - if ( strpos($consumer_key, "http://" ) === 0 ) { - $consumer = new OAuthConsumer($consumer_key, "secret", null); + /** + * Get OAuth consumer given its key + * + * @param string $consumerkey Consumer key + * + * @return moodle\mod\lti\OAuthConsumer OAuthConsumer object + */ + public function lookup_consumer($consumerkey) { + if (strpos($consumerkey, "http://" ) === 0) { + $consumer = new OAuthConsumer($consumerkey, "secret", null); return $consumer; } - if ( $this->consumers[$consumer_key] ) { - $consumer = new OAuthConsumer($consumer_key, $this->consumers[$consumer_key], null); + if ( $this->consumers[$consumerkey] ) { + $consumer = new OAuthConsumer($consumerkey, $this->consumers[$consumerkey], null); return $consumer; } return null; } - function lookup_token($consumer, $token_type, $token) { - return new OAuthToken($consumer, ""); + /** + * Create a dummy OAuthToken object for a consumer + * + * @param moodle\mod\lti\OAuthConsumer $consumer Consumer + * @param string $tokentype Type of token + * @param string $token Token ID + * + * @return moodle\mod\lti\OAuthToken OAuthToken object + */ + public function lookup_token($consumer, $tokentype, $token) { + return new OAuthToken($consumer, ''); } - // Return NULL if the nonce has not been used - // Return $nonce if the nonce was previously used - function lookup_nonce($consumer, $token, $nonce, $timestamp) { + /** + * Nonce values are not checked so just return a null + * + * @param moodle\mod\lti\OAuthConsumer $consumer Consumer + * @param string $token Token ID + * @param string $nonce Nonce value + * @param string $timestamp Timestamp + * + * @return null + */ + public function lookup_nonce($consumer, $token, $nonce, $timestamp) { // Should add some clever logic to keep nonces from - // being reused - for no we are really trusting - // that the timestamp will save us + // being reused - for now we are really trusting + // that the timestamp will save us. return null; } - function new_request_token($consumer) { + /** + * Tokens are not used so just return a null. + * + * @param moodle\mod\lti\OAuthConsumer $consumer Consumer + * + * @return null + */ + public function new_request_token($consumer) { return null; } - function new_access_token($token, $consumer) { + /** + * Tokens are not used so just return a null. + * + * @param string $token Token ID + * @param moodle\mod\lti\OAuthConsumer $consumer Consumer + * + * @return null + */ + public function new_access_token($token, $consumer) { return null; } } diff --git a/mod/lti/ajax.php b/mod/lti/ajax.php index ab75cd3e678..a411633d7cf 100644 --- a/mod/lti/ajax.php +++ b/mod/lti/ajax.php @@ -15,10 +15,12 @@ // along with Moodle. If not, see . /** - * AJAX service used when adding an External Tool to provide immediate feedback + * AJAX service used when adding an External Tool. + * + * It is used to provide immediate feedback * of which tool provider is to be used based on the Launch URL. * - * @package mod + * @package mod_lti * @subpackage xml * @copyright Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -41,10 +43,10 @@ switch ($action) { $toolurl = required_param('toolurl', PARAM_RAW); $toolid = optional_param('toolid', 0, PARAM_INT); - if(empty($toolid) && !empty($toolurl)){ + if (empty($toolid) && !empty($toolurl)) { $tool = lti_get_tool_by_url_match($toolurl, $courseid); - if(!empty($tool)){ + if (!empty($tool)) { $toolid = $tool->id; $response->toolid = $tool->id; @@ -56,7 +58,7 @@ switch ($action) { } if (!empty($toolid)) { - // Look up privacy settings + // Look up privacy settings. $query = ' SELECT name, value FROM {lti_types_config} @@ -66,7 +68,7 @@ switch ($action) { '; $privacyconfigs = $DB->get_records_sql($query, array('typeid' => $toolid)); - foreach($privacyconfigs as $config){ + foreach ($privacyconfigs as $config) { $configname = $config->name; $response->$configname = $config->value; } diff --git a/mod/lti/backup/moodle1/lib.php b/mod/lti/backup/moodle1/lib.php index 75b638d36c7..ce7f46688d1 100644 --- a/mod/lti/backup/moodle1/lib.php +++ b/mod/lti/backup/moodle1/lib.php @@ -34,18 +34,18 @@ class moodle1_mod_lti_handler extends moodle1_mod_handler { protected $moduleid = null; /** - * Declare the paths in moodle.xml we are able to convert - * - * The method returns list of {@link convert_path} instances. - * For each path returned, the corresponding conversion method must be - * defined. - * - * Note that the path /MOODLE_BACKUP/COURSE/MODULES/MOD/LTI does not - * actually exist in the file. The last element with the module name was - * appended by the moodle1_converter class. - * - * @return array of {@link convert_path} instances - */ + * Declare the paths in moodle.xml we are able to convert + * + * The method returns list of {@link convert_path} instances. + * For each path returned, the corresponding conversion method must be + * defined. + * + * Note that the path /MOODLE_BACKUP/COURSE/MODULES/MOD/LTI does not + * actually exist in the file. The last element with the module name was + * appended by the moodle1_converter class. + * + * @return array of {@link convert_path} instances + */ public function get_paths() { return array( @@ -57,33 +57,33 @@ class moodle1_mod_lti_handler extends moodle1_mod_handler { } /** - * This is executed every time we have one /MOODLE_BACKUP/COURSE/MODULES/MOD/LTI - * data available - */ + * This is executed every time we have one /MOODLE_BACKUP/COURSE/MODULES/MOD/LTI + * data available + */ public function process_basiclti($data) { global $DB; - // get the course module id and context id + // Get the course module id and context id. $instanceid = $data['id']; $cminfo = $this->get_cminfo($instanceid); $this->moduleid = $cminfo['id']; $contextid = $this->converter->get_contextid(CONTEXT_MODULE, $this->moduleid); - // get a fresh new file manager for this instance + // Get a fresh new file manager for this instance. $this->fileman = $this->converter->get_file_manager($contextid, 'mod_lti'); - // convert course files embedded into the intro + // Convert course files embedded into the intro. $this->fileman->filearea = 'intro'; $this->fileman->itemid = 0; $data['intro'] = moodle1_converter::migrate_referenced_files($data['intro'], $this->fileman); - // start writing assignment.xml + // Start writing assignment.xml. $this->open_xml_writer("activities/lti_{$this->moduleid}/lti.xml"); $this->xmlwriter->begin_tag('activity', array('id' => $instanceid, 'moduleid' => $this->moduleid, 'modulename' => 'lti', 'contextid' => $contextid)); $this->xmlwriter->begin_tag('lti', array('id' => $instanceid)); - $ignore_fields = array('id', 'modtype'); + $ignorefields = array('id', 'modtype'); if (!$DB->record_exists('lti_types', array('id' => $data['typeid']))) { $ntypeid = $DB->get_field('lti_types_config', 'typeid', @@ -105,7 +105,7 @@ class moodle1_mod_lti_handler extends moodle1_mod_handler { $data['servicesalt'] = uniqid('', true); } foreach ($data as $field => $value) { - if (!in_array($field, $ignore_fields)) { + if (!in_array($field, $ignorefields)) { $this->xmlwriter->full_tag($field, $value); } } @@ -114,15 +114,15 @@ class moodle1_mod_lti_handler extends moodle1_mod_handler { } /** - * This is executed when we reach the closing tag of our 'lti' path - */ + * This is executed when we reach the closing tag of our 'lti' path + */ public function on_basiclti_end() { - // finish writing basiclti.xml + // Finish writing basiclti.xml. $this->xmlwriter->end_tag('lti'); $this->xmlwriter->end_tag('activity'); $this->close_xml_writer(); - // write inforef.xml + // Write inforef.xml. $this->open_xml_writer("activities/lti_{$this->moduleid}/inforef.xml"); $this->xmlwriter->begin_tag('inforef'); $this->xmlwriter->begin_tag('fileref'); diff --git a/mod/lti/backup/moodle2/backup_lti_activity_task.class.php b/mod/lti/backup/moodle2/backup_lti_activity_task.class.php index 1c00c4e6692..8ca3ff1e943 100644 --- a/mod/lti/backup/moodle2/backup_lti_activity_task.class.php +++ b/mod/lti/backup/moodle2/backup_lti_activity_task.class.php @@ -30,7 +30,7 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * Defines backup_lti_activity_task class @@ -78,13 +78,13 @@ class backup_lti_activity_task extends backup_activity_task { $base = preg_quote($CFG->wwwroot, "/"); - // Link to the list of basiclti tools - $search="/(".$base."\/mod\/lti\/index.php\?id\=)([0-9]+)/"; - $content= preg_replace($search, '$@LTIINDEX*$2@$', $content); + // Link to the list of basiclti tools. + $search = "/(".$base."\/mod\/lti\/index.php\?id\=)([0-9]+)/"; + $content = preg_replace($search, '$@LTIINDEX*$2@$', $content); - // Link to basiclti view by moduleid - $search="/(".$base."\/mod\/lti\/view.php\?id\=)([0-9]+)/"; - $content= preg_replace($search, '$@LTIVIEWBYID*$2@$', $content); + // Link to basiclti view by moduleid. + $search = "/(".$base."\/mod\/lti\/view.php\?id\=)([0-9]+)/"; + $content = preg_replace($search, '$@LTIVIEWBYID*$2@$', $content); return $content; } diff --git a/mod/lti/backup/moodle2/backup_lti_stepslib.php b/mod/lti/backup/moodle2/backup_lti_stepslib.php index f74e29245c7..055a6efde2f 100644 --- a/mod/lti/backup/moodle2/backup_lti_stepslib.php +++ b/mod/lti/backup/moodle2/backup_lti_stepslib.php @@ -30,7 +30,7 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * This file contains all the backup steps that will be used @@ -57,10 +57,10 @@ class backup_lti_activity_structure_step extends backup_activity_structure_step // TODO: MDL-34161 - Fix restore to support course/site tools & submissions. - // To know if we are including userinfo + // To know if we are including userinfo. $userinfo = $this->get_setting_value('userinfo'); - // Define each element separated + // Define each element separated. $lti = new backup_nested_element('lti', array('id'), array( 'name', 'intro', @@ -88,21 +88,22 @@ class backup_lti_activity_structure_step extends backup_activity_structure_step ); // Build the tree - // (none) + // (none). - // Define sources + // Define sources. $lti->set_source_table('lti', array('id' => backup::VAR_ACTIVITYID)); // Define id annotations - // (none) + // (none). - // Define file annotations - $lti->annotate_files('mod_lti', 'intro', null); // This file areas haven't itemid + // Define file annotations. + $lti->annotate_files('mod_lti', 'intro', null); // This file areas haven't itemid. - // Add support for subplugin structure. + // Add support for subplugin structures. $this->add_subplugin_structure('ltisource', $lti, true); + $this->add_subplugin_structure('ltiservice', $lti, true); - // Return the root element (lti), wrapped into standard activity structure + // Return the root element (lti), wrapped into standard activity structure. return $this->prepare_activity_structure($lti); } } diff --git a/mod/lti/backup/moodle2/restore_lti_activity_task.class.php b/mod/lti/backup/moodle2/restore_lti_activity_task.class.php index a1a1e92dedb..bc291a99fd8 100644 --- a/mod/lti/backup/moodle2/restore_lti_activity_task.class.php +++ b/mod/lti/backup/moodle2/restore_lti_activity_task.class.php @@ -30,7 +30,7 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * This file contains the lti module restore class diff --git a/mod/lti/backup/moodle2/restore_lti_stepslib.php b/mod/lti/backup/moodle2/restore_lti_stepslib.php index c8fd8554902..989be59ca10 100644 --- a/mod/lti/backup/moodle2/restore_lti_stepslib.php +++ b/mod/lti/backup/moodle2/restore_lti_stepslib.php @@ -30,7 +30,7 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * This file contains all the restore steps that will be used @@ -59,8 +59,9 @@ class restore_lti_activity_structure_step extends restore_activity_structure_ste $lti = new restore_path_element('lti', '/activity/lti'); $paths[] = $lti; - // Add support for subplugin structure. + // Add support for subplugin structures. $this->add_subplugin_structure('ltisource', $lti); + $this->add_subplugin_structure('ltiservice', $lti); // Return the paths wrapped into standard activity structure. return $this->prepare_activity_structure($paths); diff --git a/mod/lti/basiclti.js b/mod/lti/basiclti.js deleted file mode 100644 index 9fb6cece11e..00000000000 --- a/mod/lti/basiclti.js +++ /dev/null @@ -1,56 +0,0 @@ -// 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 . -// -// This file is part of BasicLTI4Moodle -// -// BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability) -// consumer for Moodle 1.9 and Moodle 2.0. BasicLTI is a IMS Standard that allows web -// based learning tools to be easily integrated in LMS as native ones. The IMS BasicLTI -// specification is part of the IMS standard Common Cartridge 1.1 Sakai and other main LMS -// are already supporting or going to support BasicLTI. This project Implements the consumer -// for Moodle. Moodle is a Free Open source Learning Management System by Martin Dougiamas. -// BasicLTI4Moodle is a project iniciated and leaded by Ludo(Marc Alier) and Jordi Piguillem -// at the GESSI research group at UPC. -// SimpleLTI consumer for Moodle is an implementation of the early specification of LTI -// by Charles Severance (Dr Chuck) htp://dr-chuck.com , developed by Jordi Piguillem in a -// Google Summer of Code 2008 project co-mentored by Charles Severance and Marc Alier. -// -// BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis -// of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu - -/** - * This file contains a library of javasxript functions for the lti module - * - * @package mod - * @subpackage lti - * @copyright 2009 Marc Alier, Jordi Piguillem, Nikolas Galanis - * marc.alier@upc.edu - * @copyright 2009 Universitat Politecnica de Catalunya http://www.upc.edu - * @author Marc Alier - * @author Jordi Piguillem - * @author Nikolas Galanis - * @author Charles Severance - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -function basicltiDebugToggle() { - var ele = document.getElementById('basicltiDebug'); - if (ele.style.display == 'block') { - ele.style.display = 'none'; - } else { - ele.style.display = 'block'; - } -} diff --git a/mod/lti/classes/local/ltiservice/resource_base.php b/mod/lti/classes/local/ltiservice/resource_base.php new file mode 100644 index 00000000000..4d617fe1f21 --- /dev/null +++ b/mod/lti/classes/local/ltiservice/resource_base.php @@ -0,0 +1,277 @@ +. + +/** + * This file contains an abstract definition of an LTI resource + * + * @package mod_lti + * @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 mod_lti\local\ltiservice; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/lti/locallib.php'); + + +/** + * The mod_lti\local\ltiservice\resource_base class. + * + * @package mod_lti + * @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 + */ +abstract class resource_base { + + /** @var object Service associated with this resource. */ + private $service; + /** @var string Type for this resource. */ + protected $type; + /** @var string ID for this resource. */ + protected $id; + /** @var string Template for this resource. */ + protected $template; + /** @var array Custom parameter substitution variables associated with this resource. */ + protected $variables; + /** @var array Media types supported by this resource. */ + protected $formats; + /** @var array HTTP actions supported by this resource. */ + protected $methods; + /** @var array Template variables parsed from the resource template. */ + protected $params; + + + /** + * Class constructor. + * + * @param mod_lti\local\ltiservice\service_base $service Service instance + */ + public function __construct($service) { + + $this->service = $service; + $this->type = 'RestService'; + $this->id = null; + $this->template = null; + $this->methods = array(); + $this->variables = array(); + $this->formats = array(); + $this->methods = array(); + $this->params = null; + + } + + /** + * Get the resource ID. + * + * @return string + */ + public function get_id() { + + return $this->id; + + } + + /** + * Get the resource template. + * + * @return string + */ + public function get_template() { + + return $this->template; + + } + + /** + * Get the resource path. + * + * @return string + */ + public function get_path() { + + return $this->get_template(); + + } + + /** + * Get the resource type. + * + * @return string + */ + public function get_type() { + + return $this->type; + + } + + /** + * Get the resource's service. + * + * @return mod_lti\local\ltiservice\service_base + */ + public function get_service() { + + return $this->service; + + } + + /** + * Get the resource methods. + * + * @return array + */ + public function get_methods() { + + return $this->methods; + + } + + /** + * Get the resource media types. + * + * @return array + */ + public function get_formats() { + + return $this->formats; + + } + + /** + * Get the resource template variables. + * + * @return array + */ + public function get_variables() { + + return $this->variables; + + } + + /** + * Get the resource fully qualified endpoint. + * + * @return string + */ + public function get_endpoint() { + + $this->parse_template(); + $url = $this->get_service()->get_service_path() . $this->get_template(); + foreach ($this->params as $key => $value) { + $url = str_replace('{' . $key . '}', $value, $url); + } + $toolproxy = $this->get_service()->get_tool_proxy(); + if (!empty($toolproxy)) { + $url = str_replace('{tool_proxy_id}', $toolproxy->guid, $url); + } + + return $url; + + } + + /** + * Execute the request for this resource. + * + * @param mod_lti\local\ltiservice\response $response Response object for this request. + */ + public abstract function execute($response); + + /** + * Check to make sure the request is valid. + * + * @param string $toolproxyguid Consumer key + * @param string $body Body of HTTP request message + * + * @return boolean + */ + public function check_tool_proxy($toolproxyguid, $body = null) { + + $ok = false; + if ($this->get_service()->check_tool_proxy($toolproxyguid, $body)) { + $toolproxyjson = $this->get_service()->get_tool_proxy()->toolproxy; + if (empty($toolproxyjson)) { + $ok = true; + } else { + $toolproxy = json_decode($toolproxyjson); + if (!empty($toolproxy) && isset($toolproxy->security_contract->tool_service)) { + $contexts = lti_get_contexts($toolproxy); + $tpservices = $toolproxy->security_contract->tool_service; + foreach ($tpservices as $service) { + $fqid = lti_get_fqid($contexts, $service->service); + $id = explode('#', $fqid, 2); + if ($this->get_id() === $id[1]) { + $ok = true; + break; + } + } + } + if (!$ok) { + debugging('Requested service not included in tool proxy: ' . $this->get_id()); + } + } + } + + return $ok; + + } + + /** + * Parse a value for custom parameter substitution variables. + * + * @param string $value String to be parsed + * + * @return string + */ + public function parse_value($value) { + + return $value; + + } + + /** + * Parse the template for variables. + * + * @return array + */ + protected function parse_template() { + + if (empty($this->params)) { + $this->params = array(); + if (isset($_SERVER['PATH_INFO'])) { + $path = explode('/', $_SERVER['PATH_INFO']); + $parts = explode('/', $this->get_template()); + for ($i = 0; $i < count($parts); $i++) { + if ((substr($parts[$i], 0, 1) == '{') && (substr($parts[$i], -1) == '}')) { + $value = ''; + if ($i < count($path)) { + $value = $path[$i]; + } + $this->params[substr($parts[$i], 1, -1)] = $value; + } + } + } + } + + return $this->params; + + } + +} diff --git a/mod/lti/classes/local/ltiservice/response.php b/mod/lti/classes/local/ltiservice/response.php new file mode 100644 index 00000000000..5029d216ace --- /dev/null +++ b/mod/lti/classes/local/ltiservice/response.php @@ -0,0 +1,220 @@ +. + +/** + * This file contains an abstract definition of an LTI service + * + * @package mod_lti + * @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 mod_lti\local\ltiservice; + +defined('MOODLE_INTERNAL') || die; + +/** + * The mod_lti\local\ltiservice\response class. + * + * @package mod_lti + * @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 response { + + /** @var int HTTP response code. */ + private $code; + /** @var string HTTP response reason. */ + private $reason; + /** @var string HTTP request method. */ + private $requestmethod; + /** @var string HTTP request accept header. */ + private $accept; + /** @var string HTTP response content type. */ + private $contenttype; + /** @var string HTTP request body. */ + private $data; + /** @var string HTTP response body. */ + private $body; + /** @var array HTTP response codes. */ + private $responsecodes; + + /** + * Class constructor. + */ + public function __construct() { + + $this->code = 200; + $this->reason = ''; + $this->requestmethod = $_SERVER['REQUEST_METHOD']; + $this->accept = ''; + $this->contenttype = ''; + $this->data = ''; + $this->body = ''; + $this->responsecodes = array( + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 300 => 'Multiple Choices', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 415 => 'Unsupported Media Type', + 500 => 'Internal Server Error', + 501 => 'Not Implemented' + ); + + } + + /** + * Get the response code. + * + * @return int + */ + public function get_code() { + return $this->code; + } + + /** + * Set the response code. + * + * @param int $code Response code + */ + public function set_code($code) { + $this->code = $code; + $this->reason = ''; + } + + /** + * Get the response reason. + * + * @return string + */ + public function get_reason() { + if (empty($this->reason)) { + $this->reason = $this->responsecodes[$this->code]; + } + // Use generic reason for this category (based on first digit) if a specific reason is not defined. + if (empty($this->reason)) { + $this->reason = $this->responsecodes[intval($this->code / 100) * 100]; + } + return $this->reason; + } + + /** + * Set the response reason. + * + * @param string $reason Reason + */ + public function set_reason($reason) { + $this->reason = $reason; + } + + /** + * Get the request method. + * + * @return string + */ + public function get_request_method() { + return $this->requestmethod; + } + + /** + * Get the request accept header. + * + * @return string + */ + public function get_accept() { + return $this->accept; + } + + /** + * Set the request accept header. + * + * @param string $accept Accept header value + */ + public function set_accept($accept) { + $this->accept = $accept; + } + + /** + * Get the response content type. + * + * @return string + */ + public function get_content_type() { + return $this->contenttype; + } + + /** + * Set the response content type. + * + * @param string $contenttype Content type + */ + public function set_content_type($contenttype) { + $this->contenttype = $contenttype; + } + + /** + * Get the request body. + * + * @return string + */ + public function get_request_data() { + return $this->data; + } + + /** + * Set the response body. + * + * @param string $data Body data + */ + public function set_request_data($data) { + $this->data = $data; + } + + /** + * Set the response body. + * + * @param string $body Body data + */ + public function set_body($body) { + $this->body = $body; + } + + /** + * Send the response. + */ + public function send() { + header("HTTP/1.0 {$this->code} {$this->get_reason()}"); + if (($this->code >= 200) && ($this->code < 300)) { + if (!empty($this->contenttype)) { + header("Content-Type: {$this->contenttype};charset=UTF-8"); + } + if (!empty($this->body)) { + echo $this->body; + } + } + } + +} diff --git a/mod/lti/classes/local/ltiservice/service_base.php b/mod/lti/classes/local/ltiservice/service_base.php new file mode 100644 index 00000000000..73726bd4e22 --- /dev/null +++ b/mod/lti/classes/local/ltiservice/service_base.php @@ -0,0 +1,234 @@ +. + +/** + * This file contains an abstract definition of an LTI service + * + * @package mod_lti + * @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 mod_lti\local\ltiservice; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/lti/locallib.php'); +require_once($CFG->dirroot . '/mod/lti/OAuthBody.php'); + +// TODO: Switch to core oauthlib once implemented - MDL-30149. +use moodle\mod\lti as lti; + + +/** + * The mod_lti\local\ltiservice\service_base class. + * + * @package mod_lti + * @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 + */ +abstract class service_base { + + /** Label representing an LTI 2 message type */ + const LTI_VERSION2P0 = 'LTI-2p0'; + + /** @var string ID for the service. */ + protected $id; + /** @var string Human readable name for the service. */ + protected $name; + /** @var boolean true if requests for this service do not need to be signed. */ + protected $unsigned; + /** @var stdClass Tool proxy object for the current service request. */ + private $toolproxy; + /** @var array Instances of the resources associated with this service. */ + protected $resources; + + + /** + * Class constructor. + */ + public function __construct() { + + $this->id = null; + $this->name = null; + $this->unsigned = false; + $this->toolproxy = null; + $this->resources = null; + + } + + /** + * Get the service ID. + * + * @return string + */ + public function get_id() { + + return $this->id; + + } + + /** + * Get the service name. + * + * @return string + */ + public function get_name() { + + return $this->name; + + } + + /** + * Get whether the service requests need to be signed. + * + * @return boolean + */ + public function is_unsigned() { + + return $this->unsigned; + + } + + /** + * Get the tool proxy object. + * + * @return stdClass + */ + public function get_tool_proxy() { + + return $this->toolproxy; + + } + + /** + * Set the tool proxy object. + * + * @param object $toolproxy The tool proxy for this service request + * + * @var stdClass + */ + public function set_tool_proxy($toolproxy) { + + $this->toolproxy = $toolproxy; + + } + + /** + * Get the resources for this service. + * + * @return array + */ + abstract public function get_resources(); + + /** + * Get the path for service requests. + * + * @return string + */ + public static function get_service_path() { + + $url = new \moodle_url('/mod/lti/services.php'); + + return $url->out(false); + + } + + /** + * Parse a string for custom substitution parameter variables supported by this service's resources. + * + * @param string $value Value to be parsed + * + * @return string + */ + public function parse_value($value) { + + if (empty($this->resources)) { + $this->resources = $this->get_resources(); + } + if (!empty($this->resources)) { + foreach ($this->resources as $resource) { + $value = $resource->parse_value($value); + } + } + + return $value; + + } + + /** + * Check that the request has been properly signed. + * + * @param string $toolproxyguid Tool Proxy GUID + * @param string $body Request body (null if none) + * + * @return boolean + */ + public function check_tool_proxy($toolproxyguid, $body = null) { + + $ok = false; + $toolproxy = null; + $consumerkey = lti\get_oauth_key_from_headers(); + if (empty($toolproxyguid)) { + $toolproxyguid = $consumerkey; + } + + if (!empty($toolproxyguid)) { + $toolproxy = lti_get_tool_proxy_from_guid($toolproxyguid); + if ($toolproxy !== false) { + if (!$this->is_unsigned() && ($toolproxy->guid == $consumerkey)) { + $ok = $this->check_signature($toolproxy->guid, $toolproxy->secret, $body); + } else { + $ok = $this->is_unsigned(); + } + } + } + if ($ok) { + $this->toolproxy = $toolproxy; + } + + return $ok; + + } + + /** + * Check the request signature. + * + * @param string $consumerkey Consumer key + * @param string $secret Shared secret + * @param string $body Request body + * + * @return boolean + */ + private function check_signature($consumerkey, $secret, $body) { + + $ok = true; + try { + // TODO: Switch to core oauthlib once implemented - MDL-30149. + lti\handle_oauth_body_post($consumerkey, $secret, $body); + } catch (\Exception $e) { + debugging($e->getMessage() . "\n"); + $ok = false; + } + + return $ok; + + } + +} diff --git a/mod/lti/classes/plugininfo/ltiservice.php b/mod/lti/classes/plugininfo/ltiservice.php new file mode 100644 index 00000000000..32267a54cdc --- /dev/null +++ b/mod/lti/classes/plugininfo/ltiservice.php @@ -0,0 +1,59 @@ +. + +/** + * LTI service plugin info. + * + * @package mod_lti + * @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 mod_lti\plugininfo; + +use core\plugininfo\base; + +defined('MOODLE_INTERNAL') || die(); + + +/** + * The mod_lti\plugininfo\ltiservice class. + * + * @package mod_lti + * @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 ltiservice extends base { + + /** + * Should there be a way to uninstall the plugin via the administration UI? + * + * Uninstallation is not allowed for core subplugins. + * + * @return boolean + */ + public function is_uninstall_allowed() { + + if ($this->is_standard()) { + return false; + } + + return true; + + } + +} diff --git a/mod/lti/db/access.php b/mod/lti/db/access.php index 13a43157060..12e7941f502 100644 --- a/mod/lti/db/access.php +++ b/mod/lti/db/access.php @@ -57,7 +57,7 @@ $capabilities = array( ), // When the user arrives at the external tool, if they have this capability - // in Moodle, then they given the Instructor role in the remote system, + // in Moodle, then they are given the Instructor role in the remote system, // otherwise they are given Learner. See the lti_get_ims_role function. 'mod/lti:manage' => array( 'riskbitmask' => RISK_PERSONAL, // A bit of a guess, but seems likely. @@ -81,7 +81,7 @@ $capabilities = array( ) ), - // The ability to request the adminstirator to configure a particular + // The ability to request the administrator to configure a particular // External tool globally. 'mod/lti:requesttooladd' => array( 'captype' => 'write', diff --git a/mod/lti/db/install.xml b/mod/lti/db/install.xml index cdc0bffd3d5..1ca547f86e8 100644 --- a/mod/lti/db/install.xml +++ b/mod/lti/db/install.xml @@ -1,5 +1,5 @@ - @@ -40,6 +40,29 @@ + + + + + + + + + + + + + + + + + + + + + + +
@@ -49,6 +72,11 @@ + + + + + @@ -75,6 +103,23 @@
+ + + + + + + + + + + + + + + + +
diff --git a/mod/lti/db/log.php b/mod/lti/db/log.php index f00306e6049..fd3295f487e 100644 --- a/mod/lti/db/log.php +++ b/mod/lti/db/log.php @@ -27,7 +27,7 @@ defined('MOODLE_INTERNAL') || die(); $logs = array( - array('module'=>'lti', 'action'=>'view', 'mtable'=>'lti', 'field'=>'name'), - array('module'=>'lti', 'action'=>'launch', 'mtable'=>'lti', 'field'=>'name'), - array('module'=>'lti', 'action'=>'view all', 'mtable'=>'lti', 'field'=>'name') + array('module' => 'lti', 'action' => 'view', 'mtable' => 'lti', 'field' => 'name'), + array('module' => 'lti', 'action' => 'launch', 'mtable' => 'lti', 'field' => 'name'), + array('module' => 'lti', 'action' => 'view all', 'mtable' => 'lti', 'field' => 'name') ); \ No newline at end of file diff --git a/mod/lti/db/subplugins.php b/mod/lti/db/subplugins.php index ed665b5a9c2..d5244d2098e 100644 --- a/mod/lti/db/subplugins.php +++ b/mod/lti/db/subplugins.php @@ -1,5 +1,4 @@ 'mod/lti/source', + 'ltiservice' => 'mod/lti/service' ); diff --git a/mod/lti/db/upgrade.php b/mod/lti/db/upgrade.php index 7ecc04e84d9..f16ba8ca425 100644 --- a/mod/lti/db/upgrade.php +++ b/mod/lti/db/upgrade.php @@ -30,7 +30,7 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * This file keeps track of upgrades to the lti module @@ -61,24 +61,22 @@ function xmldb_lti_upgrade($oldversion) { global $CFG, $DB; + require_once(__DIR__ . '/upgradelib.php'); + $dbman = $DB->get_manager(); - // Moodle v2.2.0 release upgrade line - // Put any upgrade step following this + // Put any upgrade step following this. // Moodle v2.3.0 release upgrade line - // Put any upgrade step following this - + // Put any upgrade step following this. // Moodle v2.4.0 release upgrade line - // Put any upgrade step following this - + // Put any upgrade step following this. // Moodle v2.5.0 release upgrade line. // Put any upgrade step following this. - // Moodle v2.6.0 release upgrade line. // Put any upgrade step following this. @@ -99,6 +97,97 @@ function xmldb_lti_upgrade($oldversion) { upgrade_mod_savepoint(true, 2014060201, 'lti'); } + if ($oldversion < 2014061200) { + + // Define table lti_tool_proxies to be created. + $table = new xmldb_table('lti_tool_proxies'); + + // Adding fields to table lti_tool_proxies. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'Tool Provider'); + $table->add_field('regurl', XMLDB_TYPE_TEXT, null, null, null, null, null); + $table->add_field('state', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1'); + $table->add_field('guid', XMLDB_TYPE_CHAR, '255', null, null, null, null); + $table->add_field('secret', XMLDB_TYPE_CHAR, '255', null, null, null, null); + $table->add_field('vendorcode', XMLDB_TYPE_CHAR, '255', null, null, null, null); + $table->add_field('capabilityoffered', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null); + $table->add_field('serviceoffered', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null); + $table->add_field('toolproxy', XMLDB_TYPE_TEXT, null, null, null, null, null); + $table->add_field('createdby', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + + // Adding keys to table lti_tool_proxies. + $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); + + // Adding indexes to table lti_tool_proxies. + $table->add_index('guid', XMLDB_INDEX_UNIQUE, array('guid')); + + // Conditionally launch create table for lti_tool_proxies. + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + + // Define table lti_tool_settings to be created. + $table = new xmldb_table('lti_tool_settings'); + + // Adding fields to table lti_tool_settings. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('toolproxyid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + $table->add_field('coursemoduleid', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + $table->add_field('settings', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null); + $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + + // Adding keys to table lti_tool_settings. + $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); + $table->add_key('toolproxy', XMLDB_KEY_FOREIGN, array('toolproxyid'), 'lti_tool_proxies', array('id')); + $table->add_key('course', XMLDB_KEY_FOREIGN, array('course'), 'course', array('id')); + $table->add_key('coursemodule', XMLDB_KEY_FOREIGN, array('coursemoduleid'), 'lti', array('id')); + + // Conditionally launch create table for lti_tool_settings. + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + + // Define table lti_types to be updated. + $table = new xmldb_table('lti_types'); + + // Adding fields to table lti_types. + $field = new xmldb_field('toolproxyid', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + $field = new xmldb_field('enabledcapability', XMLDB_TYPE_TEXT, null, null, null, null, null); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + $field = new xmldb_field('parameter', XMLDB_TYPE_TEXT, null, null, null, null, null); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + $field = new xmldb_field('icon', XMLDB_TYPE_TEXT, null, null, null, null, null); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + $field = new xmldb_field('secureicon', XMLDB_TYPE_TEXT, null, null, null, null, null); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Lti savepoint reached. + upgrade_mod_savepoint(true, 2014061200, 'lti'); + } + + if ($oldversion < 2014100300) { + + mod_lti_upgrade_custom_separator(); + + // Lti savepoint reached. + upgrade_mod_savepoint(true, 2014100300, 'lti'); + } + return true; } diff --git a/mod/lti/db/upgradelib.php b/mod/lti/db/upgradelib.php new file mode 100644 index 00000000000..232d41011b8 --- /dev/null +++ b/mod/lti/db/upgradelib.php @@ -0,0 +1,62 @@ +. + +/** + * LTI upgrade script. + * + * @package mod_lti + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +/** + * Update any custom parameter settings separated by semicolons. + */ +function mod_lti_upgrade_custom_separator() { + global $DB; + + if ($DB->replace_all_text_supported()) { + + // Initialise parameter array. + $params = array('semicolon' => ';', 'likecr' => "%\r%", 'likelf' => "%\n%", 'lf' => "\n"); + + // Initialise NOT LIKE clauses to check for CR and LF characters. + $notlikecr = $DB->sql_like('value', ':likecr', true, true, true); + $notlikelf = $DB->sql_like('value', ':likelf', true, true, true); + + // Update any instances in the lti_types_config table. + $sql = 'UPDATE {lti_types_config} ' . + 'SET value = REPLACE(value, :semicolon, :lf) ' . + 'WHERE (name = \'customparameters\') AND (' . $notlikecr . ') AND (' . $notlikelf . ')'; + $DB->execute($sql, $params); + + // Initialise NOT LIKE clauses to check for CR and LF characters. + $notlikecr = $DB->sql_like('instructorcustomparameters', ':likecr', true, true, true); + $notlikelf = $DB->sql_like('instructorcustomparameters', ':likelf', true, true, true); + + // Update any instances in the lti table. + $sql = 'UPDATE {lti} ' . + 'SET instructorcustomparameters = REPLACE(instructorcustomparameters, :semicolon, :lf) ' . + 'WHERE (instructorcustomparameters IS NOT NULL) AND (' . $notlikecr . ') AND (' . $notlikelf . ')'; + $DB->execute($sql, $params); + + } + +} diff --git a/mod/lti/edit_form.php b/mod/lti/edit_form.php index 2b13b9e2a96..d57a0dc6754 100644 --- a/mod/lti/edit_form.php +++ b/mod/lti/edit_form.php @@ -30,7 +30,7 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * This file defines de main basiclti configuration form @@ -58,8 +58,9 @@ class mod_lti_edit_types_form extends moodleform{ $mform =& $this->_form; - //------------------------------------------------------------------------------- - // Add basiclti elements + $istool = $this->_customdata->istool; + + // Add basiclti elements. $mform->addElement('header', 'setup', get_string('tool_settings', 'lti')); $mform->addElement('text', 'lti_typename', get_string('typename', 'lti')); @@ -67,24 +68,37 @@ class mod_lti_edit_types_form extends moodleform{ $mform->addHelpButton('lti_typename', 'typename', 'lti'); $mform->addRule('lti_typename', null, 'required', null, 'client'); - $mform->addElement('text', 'lti_toolurl', get_string('toolurl', 'lti'), array('size'=>'64')); + $mform->addElement('text', 'lti_toolurl', get_string('toolurl', 'lti'), array('size' => '64')); $mform->setType('lti_toolurl', PARAM_TEXT); $mform->addHelpButton('lti_toolurl', 'toolurl', 'lti'); - $mform->addRule('lti_toolurl', null, 'required', null, 'client'); + if (!$istool) { + $mform->addRule('lti_toolurl', null, 'required', null, 'client'); + } else { + $mform->disabledIf('lti_toolurl', null); + } - $mform->addElement('text', 'lti_resourcekey', get_string('resourcekey_admin', 'lti')); - $mform->setType('lti_resourcekey', PARAM_TEXT); - $mform->addHelpButton('lti_resourcekey', 'resourcekey_admin', 'lti'); + if (!$istool) { + $mform->addElement('text', 'lti_resourcekey', get_string('resourcekey_admin', 'lti')); + $mform->setType('lti_resourcekey', PARAM_TEXT); + $mform->addHelpButton('lti_resourcekey', 'resourcekey_admin', 'lti'); - $mform->addElement('passwordunmask', 'lti_password', get_string('password_admin', 'lti')); - $mform->setType('lti_password', PARAM_TEXT); - $mform->addHelpButton('lti_password', 'password_admin', 'lti'); + $mform->addElement('passwordunmask', 'lti_password', get_string('password_admin', 'lti')); + $mform->setType('lti_password', PARAM_TEXT); + $mform->addHelpButton('lti_password', 'password_admin', 'lti'); + } - $mform->addElement('textarea', 'lti_customparameters', get_string('custom', 'lti'), array('rows'=>4, 'cols'=>60)); + if ($istool) { + $mform->addElement('textarea', 'lti_parameters', get_string('parameter', 'lti'), array('rows' => 4, 'cols' => 60)); + $mform->setType('lti_parameters', PARAM_TEXT); + $mform->addHelpButton('lti_parameters', 'parameter', 'lti'); + $mform->disabledIf('lti_parameters', null); + } + + $mform->addElement('textarea', 'lti_customparameters', get_string('custom', 'lti'), array('rows' => 4, 'cols' => 60)); $mform->setType('lti_customparameters', PARAM_TEXT); $mform->addHelpButton('lti_customparameters', 'custom', 'lti'); - if (!empty($this->_customdata->isadmin)) { + if (!$istool && !empty($this->_customdata->isadmin)) { $mform->addElement('checkbox', 'lti_coursevisible', ' ', ' ' . get_string('show_in_course', 'lti')); $mform->addHelpButton('lti_coursevisible', 'show_in_course', 'lti'); } else { @@ -95,7 +109,7 @@ class mod_lti_edit_types_form extends moodleform{ $mform->addElement('hidden', 'typeid'); $mform->setType('typeid', PARAM_INT); - $launchoptions=array(); + $launchoptions = array(); $launchoptions[LTI_LAUNCH_CONTAINER_EMBED] = get_string('embed', 'lti'); $launchoptions[LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS] = get_string('embed_no_blocks', 'lti'); $launchoptions[LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW] = get_string('existing_window', 'lti'); @@ -106,76 +120,72 @@ class mod_lti_edit_types_form extends moodleform{ $mform->addHelpButton('lti_launchcontainer', 'default_launch_container', 'lti'); $mform->setType('lti_launchcontainer', PARAM_INT); - // Add privacy preferences fieldset where users choose whether to send their data - $mform->addElement('header', 'privacy', get_string('privacy', 'lti')); + if (!$istool) { + // Add privacy preferences fieldset where users choose whether to send their data. + $mform->addElement('header', 'privacy', get_string('privacy', 'lti')); - $options=array(); - $options[0] = get_string('never', 'lti'); - $options[1] = get_string('always', 'lti'); - $options[2] = get_string('delegate', 'lti'); + $options = array(); + $options[0] = get_string('never', 'lti'); + $options[1] = get_string('always', 'lti'); + $options[2] = get_string('delegate', 'lti'); - $mform->addElement('select', 'lti_sendname', get_string('share_name_admin', 'lti'), $options); - $mform->setType('lti_sendname', PARAM_INT); - $mform->setDefault('lti_sendname', '2'); - $mform->addHelpButton('lti_sendname', 'share_name_admin', 'lti'); + $mform->addElement('select', 'lti_sendname', get_string('share_name_admin', 'lti'), $options); + $mform->setType('lti_sendname', PARAM_INT); + $mform->setDefault('lti_sendname', '2'); + $mform->addHelpButton('lti_sendname', 'share_name_admin', 'lti'); - $mform->addElement('select', 'lti_sendemailaddr', get_string('share_email_admin', 'lti'), $options); - $mform->setType('lti_sendemailaddr', PARAM_INT); - $mform->setDefault('lti_sendemailaddr', '2'); - $mform->addHelpButton('lti_sendemailaddr', 'share_email_admin', 'lti'); + $mform->addElement('select', 'lti_sendemailaddr', get_string('share_email_admin', 'lti'), $options); + $mform->setType('lti_sendemailaddr', PARAM_INT); + $mform->setDefault('lti_sendemailaddr', '2'); + $mform->addHelpButton('lti_sendemailaddr', 'share_email_admin', 'lti'); - //------------------------------------------------------------------------------- - // LTI Extensions + // LTI Extensions. - // Add grading preferences fieldset where the tool is allowed to return grades - $mform->addElement('select', 'lti_acceptgrades', get_string('accept_grades_admin', 'lti'), $options); - $mform->setType('lti_acceptgrades', PARAM_INT); - $mform->setDefault('lti_acceptgrades', '2'); - $mform->addHelpButton('lti_acceptgrades', 'accept_grades_admin', 'lti'); + // Add grading preferences fieldset where the tool is allowed to return grades. + $mform->addElement('select', 'lti_acceptgrades', get_string('accept_grades_admin', 'lti'), $options); + $mform->setType('lti_acceptgrades', PARAM_INT); + $mform->setDefault('lti_acceptgrades', '2'); + $mform->addHelpButton('lti_acceptgrades', 'accept_grades_admin', 'lti'); - // Add grading preferences fieldset where the tool is allowed to retrieve rosters - //$mform->addElement('select', 'lti_allowroster', get_string('share_roster_admin', 'lti'), $options); - //$mform->setDefault('lti_allowroster', '2'); - //$mform->addHelpButton('lti_allowroster', 'share_roster_admin', 'lti'); + $mform->addElement('checkbox', 'lti_forcessl', ' ', ' ' . get_string('force_ssl', 'lti'), $options); + $mform->setType('lti_forcessl', PARAM_BOOL); + if (!empty($CFG->mod_lti_forcessl)) { + $mform->setDefault('lti_forcessl', '1'); + $mform->freeze('lti_forcessl'); + } else { + $mform->setDefault('lti_forcessl', '0'); + } + $mform->addHelpButton('lti_forcessl', 'force_ssl', 'lti'); - $mform->addElement('checkbox', 'lti_forcessl', ' ', ' ' . get_string('force_ssl', 'lti'), $options); - $mform->setType('lti_forcessl', PARAM_BOOL); - if (!empty($CFG->mod_lti_forcessl)) { - $mform->setDefault('lti_forcessl', '1'); - $mform->freeze('lti_forcessl'); - } else { - $mform->setDefault('lti_forcessl', '0'); - } - $mform->addHelpButton('lti_forcessl', 'force_ssl', 'lti'); + if (!empty($this->_customdata->isadmin)) { + // Add setup parameters fieldset. + $mform->addElement('header', 'setupoptions', get_string('miscellaneous', 'lti')); - if (!empty($this->_customdata->isadmin)) { - //------------------------------------------------------------------------------- - // Add setup parameters fieldset - $mform->addElement('header', 'setupoptions', get_string('miscellaneous', 'lti')); + // Adding option to change id that is placed in context_id. + $idoptions = array(); + $idoptions[0] = get_string('id', 'lti'); + $idoptions[1] = get_string('courseid', 'lti'); - // Adding option to change id that is placed in context_id - $idoptions = array(); - $idoptions[0] = get_string('id', 'lti'); - $idoptions[1] = get_string('courseid', 'lti'); + $mform->addElement('text', 'lti_organizationid', get_string('organizationid', 'lti')); + $mform->setType('lti_organizationid', PARAM_TEXT); + $mform->addHelpButton('lti_organizationid', 'organizationid', 'lti'); - $mform->addElement('text', 'lti_organizationid', get_string('organizationid', 'lti')); - $mform->setType('lti_organizationid', PARAM_TEXT); - $mform->addHelpButton('lti_organizationid', 'organizationid', 'lti'); - - $mform->addElement('text', 'lti_organizationurl', get_string('organizationurl', 'lti')); - $mform->setType('lti_organizationurl', PARAM_TEXT); - $mform->addHelpButton('lti_organizationurl', 'organizationurl', 'lti'); + $mform->addElement('text', 'lti_organizationurl', get_string('organizationurl', 'lti')); + $mform->setType('lti_organizationurl', PARAM_TEXT); + $mform->addHelpButton('lti_organizationurl', 'organizationurl', 'lti'); + } } /* Suppress this for now - Chuck - $mform->addElement('text', 'lti_organizationdescr', get_string('organizationdescr', 'lti')); - $mform->setType('lti_organizationdescr', PARAM_TEXT); - $mform->addHelpButton('lti_organizationdescr', 'organizationdescr', 'lti'); - */ + * mform->addElement('text', 'lti_organizationdescr', get_string('organizationdescr', 'lti')) + * mform->setType('lti_organizationdescr', PARAM_TEXT) + * mform->addHelpButton('lti_organizationdescr', 'organizationdescr', 'lti') + */ - //------------------------------------------------------------------------------- + /* // Add a hidden element to signal a tool fixing operation after a problematic backup - restore process //$mform->addElement('hidden', 'lti_fix'); + */ $tab = optional_param('tab', '', PARAM_ALPHAEXT); $mform->addElement('hidden', 'tab', $tab); @@ -185,8 +195,7 @@ class mod_lti_edit_types_form extends moodleform{ $mform->addElement('hidden', 'course', $courseid); $mform->setType('course', PARAM_INT); - //------------------------------------------------------------------------------- - // Add standard buttons, common to all modules + // Add standard buttons, common to all modules. $this->add_action_buttons(); } diff --git a/mod/lti/grade.php b/mod/lti/grade.php index 3dd793153a8..6eec41f7b26 100644 --- a/mod/lti/grade.php +++ b/mod/lti/grade.php @@ -30,7 +30,7 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * This file contains submissions-specific code for the lti module diff --git a/mod/lti/index.php b/mod/lti/index.php index eab02809fa9..1a0e97b6b62 100644 --- a/mod/lti/index.php +++ b/mod/lti/index.php @@ -30,7 +30,7 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * This page lists all the instances of lti in a particular course @@ -48,9 +48,9 @@ require_once("../../config.php"); require_once($CFG->dirroot.'/mod/lti/lib.php'); -$id = required_param('id', PARAM_INT); // course id +$id = required_param('id', PARAM_INT); // Course id. -$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST); +$course = $DB->get_record('course', array('id' => $id), '*', MUST_EXIST); require_login($course); $PAGE->set_pagelayout('incourse'); @@ -69,16 +69,16 @@ $PAGE->set_heading($course->fullname); echo $OUTPUT->header(); -// Print the main part of the page +// Print the main part of the page. echo $OUTPUT->heading(get_string("modulenamepluralformatted", "lti")); -// Get all the appropriate data +// Get all the appropriate data. if (! $basicltis = get_all_instances_in_course("lti", $course)) { notice(get_string('noltis', 'lti'), "../../course/view.php?id=$course->id"); die; } -// Print the list of instances (your module will probably extend this) +// Print the list of instances (your module will probably extend this). $timenow = time(); $strname = get_string("name"); $usesections = course_format_uses_sections($course->format); @@ -96,10 +96,10 @@ if ($usesections) { foreach ($basicltis as $basiclti) { if (!$basiclti->visible) { - //Show dimmed if the mod is hidden + // Show dimmed if the mod is hidden. $link = "coursemodule\">$basiclti->name"; } else { - //Show normal if the mod is visible + // Show normal if the mod is visible. $link = "coursemodule\">$basiclti->name"; } @@ -114,5 +114,5 @@ echo "
"; echo html_writer::table($table); -// Finish the page +// Finish the page. echo $OUTPUT->footer(); diff --git a/mod/lti/instructor_edit_tool_type.php b/mod/lti/instructor_edit_tool_type.php index 2a2cefbaebb..7e283802b2b 100644 --- a/mod/lti/instructor_edit_tool_type.php +++ b/mod/lti/instructor_edit_tool_type.php @@ -59,7 +59,7 @@ if ($data = $form->get_data()) { $fromdb = lti_get_type($typeid); $json = json_encode($fromdb); - //Output script to update the calling window. + // Output script to update the calling window. $script = " \n"; @@ -1212,9 +1742,9 @@ function lti_get_launch_container($lti, $toolconfig) { $devicetype = core_useragent::get_device_type(); - //Scrolling within the object element doesn't work on iOS or Android - //Opening the popup window also had some issues in testing - //For mobile devices, always take up the entire screen to ensure the best experience + // Scrolling within the object element doesn't work on iOS or Android + // Opening the popup window also had some issues in testing + // For mobile devices, always take up the entire screen to ensure the best experience. if ($devicetype === core_useragent::DEVICETYPE_MOBILE || $devicetype === core_useragent::DEVICETYPE_TABLET ) { $launchcontainer = LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW; } @@ -1231,7 +1761,7 @@ function lti_ensure_url_is_https($url) { if (!strstr($url, '://')) { $url = 'https://' . $url; } else { - //If the URL starts with http, replace with https + // If the URL starts with http, replace with https. if (stripos($url, 'http://') === 0) { $url = 'https://' . substr($url, 7); } @@ -1259,7 +1789,7 @@ function lti_should_log_request($rawbody) { } try { - $xml = new SimpleXMLElement($rawbody); + $xml = new \SimpleXMLElement($rawbody); $ns = $xml->getNamespaces(); $ns = array_shift($ns); $xml->registerXPathNamespace('lti', $ns); @@ -1339,3 +1869,147 @@ function lti_force_type_config_settings($instance, array $typeconfig) { } } +/** + * Initializes an array with the capabilities supported by the LTI module + * + * @return array List of capability names (without a dollar sign prefix) + */ +function lti_get_capabilities() { + + $capabilities = array( + 'basic-lti-launch-request' => '', + 'Context.id' => 'context_id', + 'CourseSection.title' => 'context_title', + 'CourseSection.label' => 'context_label', + 'CourseSection.sourcedId' => 'lis_course_section_sourcedid', + 'CourseSection.longDescription' => '$COURSE->summary', + 'CourseSection.timeFrame.begin' => '$COURSE->startdate', + 'ResourceLink.id' => 'resource_link_id', + 'ResourceLink.title' => 'resource_link_title', + 'ResourceLink.description' => 'resource_link_description', + 'User.id' => 'user_id', + 'User.username' => '$USER->username', + 'Person.name.full' => 'lis_person_name_full', + 'Person.name.given' => 'lis_person_name_given', + 'Person.name.family' => 'lis_person_name_family', + 'Person.email.primary' => 'lis_person_contact_email_primary', + 'Person.sourcedId' => 'lis_person_sourcedid', + 'Person.name.middle' => '$USER->middlename', + 'Person.address.street1' => '$USER->address', + 'Person.address.locality' => '$USER->city', + 'Person.address.country' => '$USER->country', + 'Person.address.timezone' => '$USER->timezone', + 'Person.phone.primary' => '$USER->phone1', + 'Person.phone.mobile' => '$USER->phone2', + 'Person.webaddress' => '$USER->url', + 'Membership.role' => 'roles', + 'Result.sourcedId' => 'lis_result_sourcedid', + 'Result.autocreate' => 'lis_outcome_service_url'); + + return $capabilities; + +} + +/** + * Initializes an array with the services supported by the LTI module + * + * @return array List of services + */ +function lti_get_services() { + + $services = array(); + $definedservices = core_component::get_plugin_list('ltiservice'); + foreach ($definedservices as $name => $location) { + $classname = "\\ltiservice_{$name}\\local\\service\\{$name}"; + $services[] = new $classname(); + } + + return $services; + +} + +/** + * Initializes an instance of the named service + * + * @param string $servicename Name of service + * + * @return mod_lti\local\ltiservice\service_base Service + */ +function lti_get_service_by_name($servicename) { + + $service = false; + $classname = "\\ltiservice_{$servicename}\\local\\service\\{$servicename}"; + if (class_exists($classname)) { + $service = new $classname(); + } + + return $service; + +} + +/** + * Finds a service by id + * + * @param array $services Array of services + * @param string $resourceid ID of resource + * + * @return mod_lti\local\ltiservice\service_base Service + */ +function lti_get_service_by_resource_id($services, $resourceid) { + + $service = false; + foreach ($services as $aservice) { + foreach ($aservice->get_resources() as $resource) { + if ($resource->get_id() === $resourceid) { + $service = $aservice; + break 2; + } + } + } + + return $service; + +} + +/** + * Extracts the named contexts from a tool proxy + * + * @param object $json + * + * @return array Contexts + */ +function lti_get_contexts($json) { + + $contexts = array(); + if (isset($json->{'@context'})) { + foreach ($json->{'@context'} as $context) { + if (is_object($context)) { + $contexts = array_merge(get_object_vars($context), $contexts); + } + } + } + + return $contexts; + +} + +/** + * Converts an ID to a fully-qualified ID + * + * @param array $contexts + * @param string $id + * + * @return string Fully-qualified ID + */ +function lti_get_fqid($contexts, $id) { + + $parts = explode(':', $id, 2); + if (count($parts) > 1) { + if (array_key_exists($parts[0], $contexts)) { + $id = $contexts[$parts[0]] . $parts[1]; + } + } + + return $id; + +} diff --git a/mod/lti/mod_form.js b/mod/lti/mod_form.js index e11f8c3d047..306b90fa5f7 100644 --- a/mod/lti/mod_form.js +++ b/mod/lti/mod_form.js @@ -54,6 +54,19 @@ updateToolMatches(); self.toggleEditButtons(); + + if (self.getSelectedToolTypeOption().getAttribute('toolproxy')){ + var allowname = Y.one('#id_instructorchoicesendname'); + allowname.set('checked', !self.getSelectedToolTypeOption().getAttribute('noname')); + + var allowemail = Y.one('#id_instructorchoicesendemailaddr'); + allowemail.set('checked', !self.getSelectedToolTypeOption().getAttribute('noemail')); + + var allowgrades = Y.one('#id_instructorchoiceacceptgrades'); + allowgrades.set('checked', !self.getSelectedToolTypeOption().getAttribute('nogrades')); + self.toggleGradeSection(); + } + }); this.createTypeEditorButtons(); diff --git a/mod/lti/mod_form.php b/mod/lti/mod_form.php index e34eb239a0d..1cbeee224dd 100644 --- a/mod/lti/mod_form.php +++ b/mod/lti/mod_form.php @@ -30,7 +30,7 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * This file defines the main lti configuration form @@ -63,21 +63,20 @@ class mod_lti_mod_form extends moodleform_mod { $this->typeid = 0; $mform =& $this->_form; - //------------------------------------------------------------------------------- - // Adding the "general" fieldset, where all the common settings are shown + // Adding the "general" fieldset, where all the common settings are shown. $mform->addElement('header', 'general', get_string('general', 'form')); - // Adding the standard "name" field - $mform->addElement('text', 'name', get_string('basicltiname', 'lti'), array('size'=>'64')); + // Adding the standard "name" field. + $mform->addElement('text', 'name', get_string('basicltiname', 'lti'), array('size' => '64')); $mform->setType('name', PARAM_TEXT); $mform->addRule('name', null, 'required', null, 'client'); $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); - // Adding the optional "intro" and "introformat" pair of fields + // Adding the optional "intro" and "introformat" pair of fields. $this->add_intro_editor(false, get_string('basicltiintro', 'lti')); $mform->setAdvanced('introeditor'); - // Display the label to the right of the checkbox so it looks better & matches rest of the form + // Display the label to the right of the checkbox so it looks better & matches rest of the form. $coursedesc = $mform->getElement('showdescription'); - if(!empty($coursedesc)){ + if (!empty($coursedesc)) { $coursedesc->setText(' ' . $coursedesc->getLabel()); $coursedesc->setLabel(' '); } @@ -93,12 +92,27 @@ class mod_lti_mod_form extends moodleform_mod { $mform->setAdvanced('showdescriptionlaunch'); $mform->addHelpButton('showdescriptionlaunch', 'display_description', 'lti'); - // Tool settings + // Tool settings. $tooltypes = $mform->addElement('select', 'typeid', get_string('external_tool_type', 'lti'), array()); $mform->addHelpButton('typeid', 'external_tool_type', 'lti'); + $toolproxy = array(); foreach (lti_get_types_for_add_instance() as $id => $type) { - if ($type->course == $COURSE->id) { + if (!empty($type->toolproxyid)) { + $toolproxy[] = $type->id; + $attributes = array( 'globalTool' => 1, 'toolproxy' => 1); + $enabledcapabilities = explode("\n", $type->enabledcapability); + if (!in_array('Result.autocreate', $enabledcapabilities)) { + $attributes['nogrades'] = 1; + } + if (!in_array('Person.name.full', $enabledcapabilities) && !in_array('Person.name.family', $enabledcapabilities) && + !in_array('Person.name.given', $enabledcapabilities)) { + $attributes['noname'] = 1; + } + if (!in_array('Person.email.primary', $enabledcapabilities)) { + $attributes['noemail'] = 1; + } + } else if ($type->course == $COURSE->id) { $attributes = array( 'editable' => 1, 'courseTool' => 1, 'domain' => $type->tooldomain ); } else if ($id != 0) { $attributes = array( 'globalTool' => 1, 'domain' => $type->tooldomain); @@ -109,19 +123,21 @@ class mod_lti_mod_form extends moodleform_mod { $tooltypes->addOption($type->name, $id, $attributes); } - $mform->addElement('text', 'toolurl', get_string('launch_url', 'lti'), array('size'=>'64')); + $mform->addElement('text', 'toolurl', get_string('launch_url', 'lti'), array('size' => '64')); $mform->setType('toolurl', PARAM_TEXT); $mform->addHelpButton('toolurl', 'launch_url', 'lti'); + $mform->disabledIf('toolurl', 'typeid', 'neq', '0'); - $mform->addElement('text', 'securetoolurl', get_string('secure_launch_url', 'lti'), array('size'=>'64')); + $mform->addElement('text', 'securetoolurl', get_string('secure_launch_url', 'lti'), array('size' => '64')); $mform->setType('securetoolurl', PARAM_TEXT); $mform->setAdvanced('securetoolurl'); $mform->addHelpButton('securetoolurl', 'secure_launch_url', 'lti'); + $mform->disabledIf('securetoolurl', 'typeid', 'neq', '0'); $mform->addElement('hidden', 'urlmatchedtypeid', '', array( 'id' => 'id_urlmatchedtypeid' )); $mform->setType('urlmatchedtypeid', PARAM_INT); - $launchoptions=array(); + $launchoptions = array(); $launchoptions[LTI_LAUNCH_CONTAINER_DEFAULT] = get_string('default', 'lti'); $launchoptions[LTI_LAUNCH_CONTAINER_EMBED] = get_string('embed', 'lti'); $launchoptions[LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS] = get_string('embed_no_blocks', 'lti'); @@ -136,74 +152,57 @@ class mod_lti_mod_form extends moodleform_mod { $mform->setType('resourcekey', PARAM_TEXT); $mform->setAdvanced('resourcekey'); $mform->addHelpButton('resourcekey', 'resourcekey', 'lti'); + $mform->disabledIf('resourcekey', 'typeid', 'neq', '0'); $mform->addElement('passwordunmask', 'password', get_string('password', 'lti')); $mform->setType('password', PARAM_TEXT); $mform->setAdvanced('password'); $mform->addHelpButton('password', 'password', 'lti'); + $mform->disabledIf('password', 'typeid', 'neq', '0'); - $mform->addElement('textarea', 'instructorcustomparameters', get_string('custom', 'lti'), array('rows'=>4, 'cols'=>60)); + $mform->addElement('textarea', 'instructorcustomparameters', get_string('custom', 'lti'), array('rows' => 4, 'cols' => 60)); $mform->setType('instructorcustomparameters', PARAM_TEXT); $mform->setAdvanced('instructorcustomparameters'); $mform->addHelpButton('instructorcustomparameters', 'custom', 'lti'); - $mform->addElement('text', 'icon', get_string('icon_url', 'lti'), array('size'=>'64')); + $mform->addElement('text', 'icon', get_string('icon_url', 'lti'), array('size' => '64')); $mform->setType('icon', PARAM_TEXT); $mform->setAdvanced('icon'); $mform->addHelpButton('icon', 'icon_url', 'lti'); + $mform->disabledIf('icon', 'typeid', 'neq', '0'); - $mform->addElement('text', 'secureicon', get_string('secure_icon_url', 'lti'), array('size'=>'64')); + $mform->addElement('text', 'secureicon', get_string('secure_icon_url', 'lti'), array('size' => '64')); $mform->setType('secureicon', PARAM_TEXT); $mform->setAdvanced('secureicon'); $mform->addHelpButton('secureicon', 'secure_icon_url', 'lti'); + $mform->disabledIf('secureicon', 'typeid', 'neq', '0'); - //------------------------------------------------------------------------------- - // Add privacy preferences fieldset where users choose whether to send their data + // Add privacy preferences fieldset where users choose whether to send their data. $mform->addElement('header', 'privacy', get_string('privacy', 'lti')); $mform->addElement('advcheckbox', 'instructorchoicesendname', ' ', ' ' . get_string('share_name', 'lti')); $mform->setDefault('instructorchoicesendname', '1'); $mform->addHelpButton('instructorchoicesendname', 'share_name', 'lti'); + $mform->disabledIf('instructorchoicesendname', 'typeid', 'in', $toolproxy); $mform->addElement('advcheckbox', 'instructorchoicesendemailaddr', ' ', ' ' . get_string('share_email', 'lti')); $mform->setDefault('instructorchoicesendemailaddr', '1'); $mform->addHelpButton('instructorchoicesendemailaddr', 'share_email', 'lti'); + $mform->disabledIf('instructorchoicesendemailaddr', 'typeid', 'in', $toolproxy); $mform->addElement('advcheckbox', 'instructorchoiceacceptgrades', ' ', ' ' . get_string('accept_grades', 'lti')); $mform->setDefault('instructorchoiceacceptgrades', '1'); $mform->addHelpButton('instructorchoiceacceptgrades', 'accept_grades', 'lti'); - - //$mform->addElement('checkbox', 'instructorchoiceallowroster', ' ', ' ' . get_string('share_roster', 'lti')); - //$mform->setDefault('instructorchoiceallowroster', '1'); - //$mform->addHelpButton('instructorchoiceallowroster', 'share_roster', 'lti'); - - //------------------------------------------------------------------------------- - - /** - $debugoptions=array(); - $debugoptions[0] = get_string('debuglaunchoff', 'lti'); - $debugoptions[1] = get_string('debuglaunchon', 'lti'); - - $mform->addElement('select', 'debuglaunch', get_string('debuglaunch', 'lti'), $debugoptions); - - if (isset($this->typeconfig['debuglaunch'])) { - if ($this->typeconfig['debuglaunch'] == 0) { - $mform->setDefault('debuglaunch', '0'); - } else if ($this->typeconfig['debuglaunch'] == 1) { - $mform->setDefault('debuglaunch', '1'); - } - } - */ + $mform->disabledIf('instructorchoiceacceptgrades', 'typeid', 'in', $toolproxy); // Add standard course module grading elements. $this->standard_grading_coursemodule_elements(); - //------------------------------------------------------------------------------- - // add standard elements, common to all modules + // Add standard elements, common to all modules. $this->standard_coursemodule_elements(); $mform->setAdvanced('cmidnumber'); - //------------------------------------------------------------------------------- - // add standard buttons, common to all modules + + // Add standard buttons, common to all modules. $this->add_action_buttons(); $editurl = new moodle_url('/mod/lti/instructor_edit_tool_type.php', @@ -222,10 +221,10 @@ class mod_lti_mod_form extends moodleform_mod { ); $module = array( - 'name' => 'mod_lti_edit', - 'fullpath' => '/mod/lti/mod_form.js', - 'requires' => array('base', 'io', 'querystring-stringify-simple', 'node', 'event', 'json-parse'), - 'strings' => array( + 'name' => 'mod_lti_edit', + 'fullpath' => '/mod/lti/mod_form.js', + 'requires' => array('base', 'io', 'querystring-stringify-simple', 'node', 'event', 'json-parse'), + 'strings' => array( array('addtype', 'lti'), array('edittype', 'lti'), array('deletetype', 'lti'), @@ -245,24 +244,5 @@ class mod_lti_mod_form extends moodleform_mod { $PAGE->requires->js_init_call('M.mod_lti.editor.init', array(json_encode($jsinfo)), true, $module); } - /** - * Make fields editable or non-editable depending on the administrator choices - * @see moodleform_mod::definition_after_data() - */ - public function definition_after_data() { - parent::definition_after_data(); - - //$mform =& $this->_form; - } - - /** - * Function overwritten to change default values using - * global configuration - * - * @param array $default_values passed by reference - */ - public function data_preprocessing(&$default_values) { - - } } diff --git a/mod/lti/register.php b/mod/lti/register.php new file mode 100644 index 00000000000..20a460fae90 --- /dev/null +++ b/mod/lti/register.php @@ -0,0 +1,129 @@ +. + +/** + * This file contains all necessary code to launch a Tool Proxy registration + * + * @package mod_lti + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once('../../config.php'); +require_once($CFG->libdir.'/adminlib.php'); +require_once($CFG->dirroot.'/mod/lti/locallib.php'); + +$id = required_param('id', PARAM_INT); +$tab = optional_param('tab', '', PARAM_ALPHAEXT); + +require_login(0, false); + +$redirect = new moodle_url('/mod/lti/toolproxies.php', array('tab' => $tab)); +$redirect = $redirect->out(); + +require_sesskey(); + +$toolproxies = $DB->get_records('lti_tool_proxies'); + +$duplicate = false; +foreach ($toolproxies as $key => $toolproxy) { + if (($toolproxy->state == LTI_TOOL_PROXY_STATE_PENDING) || ($toolproxy->state == LTI_TOOL_PROXY_STATE_ACCEPTED)) { + if ($toolproxy->regurl == $toolproxies[$id]->regurl) { + $duplicate = true; + break; + } + } +} + +$redirect = new moodle_url('/mod/lti/toolproxies.php'); +if ($duplicate) { + redirect($redirect, get_string('duplicateregurl', 'lti')); +} + + +$profileservice = lti_get_service_by_name('profile'); +if (empty($profileservice)) { + redirect($redirect, get_string('noprofileservice', 'lti')); +} + +$url = new moodle_url('/mod/lti/register.php', array('id' => $id)); +$PAGE->set_url($url); + +admin_externalpage_setup('ltitoolproxies'); + + +$PAGE->set_heading(get_string('toolproxyregistration', 'lti')); +$PAGE->set_title("{$SITE->shortname}: " . get_string('toolproxyregistration', 'lti')); + +// Print the page header. +echo $OUTPUT->header(); + +echo $OUTPUT->heading(get_string('toolproxyregistration', 'lti')); + +echo $OUTPUT->box_start('generalbox'); + +// Request the registration request content with an object tag. +$registration = new moodle_url('/mod/lti/registration.php', + array('id' => $id, 'sesskey' => sesskey())); + +echo "

\n"; +echo get_string('register_warning', 'lti'); +echo "\n

\n"; + +echo ''; + +// Output script to make the object tag be as large as possible. +$resize = ' + +'; + +echo $resize; + +// Finish the page. +echo $OUTPUT->box_end(); +echo $OUTPUT->footer(); diff --git a/mod/lti/register_form.php b/mod/lti/register_form.php new file mode 100644 index 00000000000..42473a2cbd8 --- /dev/null +++ b/mod/lti/register_form.php @@ -0,0 +1,127 @@ +. + +/** + * This file defines the main tool registration configuration form + * + * @package mod_lti + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; + +require_once($CFG->libdir.'/formslib.php'); +require_once($CFG->dirroot.'/mod/lti/locallib.php'); + +/** + * The mod_lti_register_types_form class. + * + * @package mod_lti + * @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 mod_lti_register_types_form extends moodleform { + + /** + * Set up the form definition. + */ + public function definition() { + global $CFG; + + $mform =& $this->_form; + + $mform->addElement('header', 'setup', get_string('registration_options', 'lti')); + + // Tool Provider name. + + $mform->addElement('text', 'lti_registrationname', get_string('registrationname', 'lti')); + $mform->setType('lti_registrationname', PARAM_TEXT); + $mform->addHelpButton('lti_registrationname', 'registrationname', 'lti'); + $mform->addRule('lti_registrationname', null, 'required', null, 'client'); + + // Registration URL. + + $mform->addElement('text', 'lti_registrationurl', get_string('registrationurl', 'lti'), array('size' => '64')); + $mform->setType('lti_registrationurl', PARAM_TEXT); + $mform->addHelpButton('lti_registrationurl', 'registrationurl', 'lti'); + + // LTI Capabilities. + + $options = array_keys(lti_get_capabilities()); + natcasesort($options); + $attributes = array( 'multiple' => 1, 'size' => min(count($options), 10) ); + $mform->addElement('select', 'lti_capabilities', get_string('capabilities', 'lti'), + array_combine($options, $options), $attributes); + $mform->setType('lti_capabilities', PARAM_TEXT); + $mform->addHelpButton('lti_capabilities', 'capabilities', 'lti'); + + // LTI Services. + + $services = lti_get_services(); + $options = array(); + foreach ($services as $service) { + $options[$service->get_id()] = $service->get_name(); + } + $attributes = array( 'multiple' => 1, 'size' => min(count($options), 10) ); + $mform->addElement('select', 'lti_services', get_string('services', 'lti'), $options, $attributes); + $mform->setType('lti_services', PARAM_TEXT); + $mform->addHelpButton('lti_services', 'services', 'lti'); + + $mform->addElement('hidden', 'toolproxyid'); + $mform->setType('toolproxyid', PARAM_INT); + + $tab = optional_param('tab', '', PARAM_ALPHAEXT); + $mform->addElement('hidden', 'tab', $tab); + $mform->setType('tab', PARAM_ALPHAEXT); + + $courseid = optional_param('course', 1, PARAM_INT); + $mform->addElement('hidden', 'course', $courseid); + $mform->setType('course', PARAM_INT); + + // Add standard buttons, common to all modules. + + $this->add_action_buttons(); + + } + + /** + * Set up rules for disabling fields. + */ + public function disable_fields() { + + $mform =& $this->_form; + + $mform->disabledIf('lti_registrationurl', null); + $mform->disabledIf('lti_capabilities', null); + $mform->disabledIf('lti_services', null); + + } + + /** + * Set up rules for required fields. + */ + public function required_fields() { + + $mform =& $this->_form; + + $mform->addRule('lti_registrationurl', null, 'required', null, 'client'); + + } + +} diff --git a/mod/lti/registersettings.php b/mod/lti/registersettings.php new file mode 100644 index 00000000000..a96a6f1a8b8 --- /dev/null +++ b/mod/lti/registersettings.php @@ -0,0 +1,94 @@ +. + +/** + * This file contains the script used to register a new external tool. + * + * It is used to create a new form used to configure the capabilities + * and services to be offered to the tool provider. + * + * @package mod_lti + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once('../../config.php'); +require_once($CFG->libdir.'/adminlib.php'); +require_once($CFG->dirroot.'/mod/lti/register_form.php'); +require_once($CFG->dirroot.'/mod/lti/locallib.php'); + +$action = optional_param('action', null, PARAM_ALPHANUMEXT); +$id = optional_param('id', null, PARAM_INT); +$tab = optional_param('tab', '', PARAM_ALPHAEXT); + +// No guest autologin. +require_login(0, false); + +$isupdate = !empty($id); +$pageurl = new moodle_url('/mod/lti/registersettings.php'); +if ($isupdate) { + $pageurl->param('id', $id); +} +$PAGE->set_url($pageurl); + +admin_externalpage_setup('ltitoolproxies'); + +$redirect = new moodle_url('/mod/lti/toolproxies.php', array('tab' => $tab)); +$redirect = $redirect->out(); + +require_sesskey(); + +if ($action == 'delete') { + lti_delete_tool_proxy($id); + redirect($redirect); +} + +$data = array(); +if ($isupdate) { + $data['isupdate'] = true; +} + +$form = new mod_lti_register_types_form($pageurl, (object)$data); + +if ($form->is_cancelled()) { + redirect($redirect); +} else if ($data = $form->get_data()) { + $id = lti_add_tool_proxy($data); + redirect($redirect); +} else { + $PAGE->set_title("{$SITE->shortname}: " . get_string('toolregistration', 'lti')); + $PAGE->navbar->add(get_string('lti_administration', 'lti'), $redirect); + + echo $OUTPUT->header(); + echo $OUTPUT->heading(get_string('toolregistration', 'lti')); + echo $OUTPUT->box_start('generalbox'); + if ($action == 'update') { + $toolproxy = lti_get_tool_proxy_config($id); + $form->set_data($toolproxy); + if ($toolproxy->state == LTI_TOOL_PROXY_STATE_ACCEPTED) { + $form->disable_fields(); + } else { + $form->required_fields(); + } + } else { + $form->required_fields(); + } + $form->display(); + + echo $OUTPUT->box_end(); + echo $OUTPUT->footer(); +} diff --git a/mod/lti/registration.php b/mod/lti/registration.php new file mode 100644 index 00000000000..d1b5c7a2ed2 --- /dev/null +++ b/mod/lti/registration.php @@ -0,0 +1,39 @@ +. + +/** + * This file contains all necessary code to initiate a tool registration process + * + * @package mod_lti + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once("../../config.php"); +require_once($CFG->dirroot.'/mod/lti/lib.php'); +require_once($CFG->dirroot.'/mod/lti/locallib.php'); + +$id = required_param('id', PARAM_INT); // Tool Proxy ID. + +$toolproxy = $DB->get_record('lti_tool_proxies', array('id' => $id), '*', MUST_EXIST); + +require_login(0, false); + +$systemcontext = context_system::instance(); +require_capability('moodle/site:config', $systemcontext); + +lti_register($toolproxy); diff --git a/mod/lti/registrationreturn.php b/mod/lti/registrationreturn.php new file mode 100644 index 00000000000..3209a1ce347 --- /dev/null +++ b/mod/lti/registrationreturn.php @@ -0,0 +1,115 @@ +. + +/** + * Handle the return from the Tool Provider after registering a tool proxy. + * + * @package mod_lti + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once('../../config.php'); +require_once($CFG->dirroot.'/mod/lti/locallib.php'); + +$top = optional_param('top', 0, PARAM_INT); +$msg = optional_param('lti_msg', '', PARAM_RAW); +$err = optional_param('lti_errormsg', '', PARAM_RAW); +$id = optional_param('id', 0, PARAM_INT); + +// No guest autologin. +require_login(0, false); + +$systemcontext = context_system::instance(); +require_capability('moodle/site:config', $systemcontext); + +if (empty($top)) { + + $params = array(); + $params['top'] = '1'; + if (!empty($msg)) { + $params['lti_msg'] = $msg; + } + if (!empty($err)) { + $params['lti_errormsg'] = $err; + } + if (!empty($id)) { + $params['id'] = $id; + } + $redirect = new moodle_url('/mod/lti/registrationreturn.php', $params); + $redirect = $redirect->out(false); + + $clickhere = get_string('click_to_continue', 'lti', (object)array('link' => $redirect)); + $html = <<< EOD + + + + + + + + +EOD; + echo $html; + +} else if (!empty($msg) && !empty($err)) { + + $params = array(); + $params['top'] = '1'; + if (!empty($err)) { + $params['lti_errormsg'] = $err; + } + if (!empty($id)) { + $params['id'] = $id; + } + $redirect = new moodle_url('/mod/lti/registrationreturn.php', $params); + $redirect = $redirect->out(false); + redirect($redirect, $err); + +} else { + + $redirect = new moodle_url('/mod/lti/toolproxies.php'); + if (!empty($id)) { + $toolproxy = $DB->get_record('lti_tool_proxies', array('id' => $id)); + switch($toolproxy->state) { + case LTI_TOOL_PROXY_STATE_ACCEPTED: + $redirect->param('tab', 'tp_accepted'); + break; + case LTI_TOOL_PROXY_STATE_REJECTED: + $redirect->param('tab', 'tp_rejected'); + break; + case LTI_TOOL_PROXY_STATE_PENDING: + // Change the status to configured. + $toolproxy->state = LTI_TOOL_PROXY_STATE_CONFIGURED; + lti_update_tool_proxy($toolproxy); + } + } + + $redirect = $redirect->out(); + + if (empty($msg)) { + $msg = $err; + } + redirect($redirect, $msg); + +} diff --git a/mod/lti/request_tool.php b/mod/lti/request_tool.php index d929e739ac4..b044bf6622f 100644 --- a/mod/lti/request_tool.php +++ b/mod/lti/request_tool.php @@ -52,9 +52,9 @@ $PAGE->set_pagelayout('incourse'); echo $OUTPUT->header(); echo $OUTPUT->heading(format_string($lti->name, true, array('context' => $context))); -//Add a tool type if one does not exist already +// Add a tool type if one does not exist already. if (!lti_get_tool_by_url_match($lti->toolurl, $lti->course, LTI_TOOL_STATE_ANY)) { - //There are no tools (active, pending, or rejected) for the launch URL. Create a new pending tool + // There are no tools (active, pending, or rejected) for the launch URL. Create a new pending tool. $tooltype = new stdClass(); $toolconfig = new stdClass(); diff --git a/mod/lti/return.php b/mod/lti/return.php index 0446d406ea6..66e14ce57ea 100644 --- a/mod/lti/return.php +++ b/mod/lti/return.php @@ -56,7 +56,7 @@ if (!empty($errormsg) || !empty($msg)) { $PAGE->set_title($pagetitle); $PAGE->set_heading($course->fullname); - //Avoid frame-in-frame action + // Avoid frame-in-frame action. if ($launchcontainer == LTI_LAUNCH_CONTAINER_EMBED || $launchcontainer == LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS) { $PAGE->set_pagelayout('embedded'); } else { @@ -80,7 +80,8 @@ if (!empty($errormsg)) { echo '

'; $links = new stdClass(); - $coursetooleditor = new moodle_url('/mod/lti/instructor_edit_tool_type.php', array('course' => $courseid, 'action' => 'add')); + $coursetooleditor = new moodle_url('/mod/lti/instructor_edit_tool_type.php', + array('course' => $courseid, 'action' => 'add')); $links->course_tool_editor = $coursetooleditor->out(false); echo get_string('lti_launch_error_unsigned_help', 'lti', $links); @@ -103,9 +104,9 @@ if (!empty($errormsg)) { $courseurl = new moodle_url('/course/view.php', array('id' => $courseid)); $url = $courseurl->out(); - //Avoid frame-in-frame action + // Avoid frame-in-frame action. if ($launchcontainer == LTI_LAUNCH_CONTAINER_EMBED || $launchcontainer == LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS) { - //Output a page containing some script to break out of frames and redirect them + // Output a page containing some script to break out of frames and redirect them. echo ''; @@ -132,7 +133,7 @@ if (!empty($errormsg)) { echo ''; } else { - //If no error, take them back to the course + // If no error, take them back to the course. redirect($url); } } diff --git a/mod/lti/service.php b/mod/lti/service.php index f117087ebc7..0075f8eba93 100644 --- a/mod/lti/service.php +++ b/mod/lti/service.php @@ -24,12 +24,13 @@ */ define('NO_DEBUG_DISPLAY', true); +define('NO_MOODLE_COOKIES', true); require_once(dirname(__FILE__) . "/../../config.php"); require_once($CFG->dirroot.'/mod/lti/locallib.php'); require_once($CFG->dirroot.'/mod/lti/servicelib.php'); -// TODO: Switch to core oauthlib once implemented - MDL-30149 +// TODO: Switch to core oauthlib once implemented - MDL-30149. use moodle\mod\lti as lti; $rawbody = file_get_contents("php://input"); @@ -40,7 +41,7 @@ if (lti_should_log_request($rawbody)) { foreach (lti\OAuthUtil::get_headers() as $name => $value) { if ($name === 'Authorization') { - // TODO: Switch to core oauthlib once implemented - MDL-30149 + // TODO: Switch to core oauthlib once implemented - MDL-30149. $oauthparams = lti\OAuthUtil::split_header($value); $consumerkey = $oauthparams['oauth_consumer_key']; @@ -118,7 +119,7 @@ switch ($messagetype) { throw new Exception('Tool does not accept grades'); } - //Getting the grade requires the context is set + // Getting the grade requires the context is set. $context = context_course::instance($ltiinstance->course); $PAGE->set_context($context); @@ -127,7 +128,7 @@ switch ($messagetype) { $grade = lti_read_grade($ltiinstance, $parsed->userid); $responsexml = lti_get_response_xml( - 'success', // Empty grade is also 'success' + 'success', // Empty grade is also 'success'. 'Result read', $parsed->messageid, 'readResultResponse' @@ -168,9 +169,9 @@ switch ($messagetype) { break; default: - //Fire an event if we get a web service request which we don't support directly. - //This will allow others to extend the LTI services, which I expect to be a common - //use case, at least until the spec matures. + // Fire an event if we get a web service request which we don't support directly. + // This will allow others to extend the LTI services, which I expect to be a common + // use case, at least until the spec matures. $data = new stdClass(); $data->body = $rawbody; $data->xml = $xml; @@ -189,20 +190,20 @@ switch ($messagetype) { break; } - //If an event handler handles the web service, it should set this global to true - //So this code knows whether to send an "operation not supported" or not. - global $lti_web_service_handled; - $lti_web_service_handled = false; + // If an event handler handles the web service, it should set this global to true + // So this code knows whether to send an "operation not supported" or not. + global $ltiwebservicehandled; + $ltiwebservicehandled = false; try { $event = \mod_lti\event\unknown_service_api_called::create($eventdata); $event->set_message_data($data); $event->trigger(); } catch (Exception $e) { - $lti_web_service_handled = false; + $ltiwebservicehandled = false; } - if (!$lti_web_service_handled) { + if (!$ltiwebservicehandled) { $responsexml = lti_get_response_xml( 'unsupported', 'unsupported', @@ -215,10 +216,3 @@ switch ($messagetype) { break; } - - -//echo print_r(apache_request_headers(), true); - -//echo '
'; - -//echo file_get_contents("php://input"); diff --git a/mod/lti/service/profile/classes/local/resource/profile.php b/mod/lti/service/profile/classes/local/resource/profile.php new file mode 100644 index 00000000000..529a951a0bc --- /dev/null +++ b/mod/lti/service/profile/classes/local/resource/profile.php @@ -0,0 +1,228 @@ +. + +/** + * This file contains a class definition for the Tool Consumer Profile resource + * + * @package ltiservice_profile + * @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_profile\local\resource; + +use \mod_lti\local\ltiservice\service_base; + +defined('MOODLE_INTERNAL') || die(); + +/** + * A resource implementing the Tool Consumer Profile. + * + * @package ltiservice_profile + * @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 profile extends \mod_lti\local\ltiservice\resource_base { + + /** + * Class constructor. + * + * @param ltiservice_profile\local\resource\profile $service Service instance + */ + public function __construct($service) { + + parent::__construct($service); + $this->id = 'ToolConsumerProfile'; + $this->template = '/profile/{tool_proxy_id}'; + $this->variables[] = 'ToolConsumerProfile.url'; + $this->formats[] = 'application/vnd.ims.lti.v2.toolconsumerprofile+json'; + $this->methods[] = 'GET'; + + } + + /** + * Get the path for this resource. + * + * @return string + */ + public function get_path() { + + $path = $this->template; + $toolproxy = $this->get_service()->get_tool_proxy(); + if (!empty($toolproxy)) { + $path = str_replace('{tool_proxy_id}', $toolproxy->guid, $path); + } + + return $path; + + } + + /** + * Execute the request for this resource. + * + * @param mod_lti\local\ltiservice\response $response Response object for this request. + */ + public function execute($response) { + + global $CFG; + + $version = service_base::LTI_VERSION2P0; + $params = $this->parse_template(); + $ok = $this->get_service()->check_tool_proxy($params['tool_proxy_id']); + if (!$ok) { + $response->set_code(404); + } else if (optional_param('lti_version', '', PARAM_ALPHANUMEXT) != $version) { + $response->set_code(400); + } else { + $toolproxy = $this->get_service()->get_tool_proxy(); + $response->set_content_type($this->formats[0]); + + $servicepath = $this->get_service()->get_service_path(); + $id = $servicepath . $this->get_path(); + $now = date('Y-m-d\TH:iO'); + $capabilityofferedarr = explode("\n", $toolproxy->capabilityoffered); + $serviceofferedarr = explode("\n", $toolproxy->serviceoffered); + $serviceoffered = ''; + $sep = ''; + $services = \core_component::get_plugin_list('ltiservice'); + foreach ($services as $name => $location) { + if (in_array($name, $serviceofferedarr)) { + $classname = "\\ltiservice_{$name}\\local\\service\\{$name}"; + $service = new $classname(); + $service->set_tool_proxy($toolproxy); + $resources = $service->get_resources(); + foreach ($resources as $resource) { + $formats = implode("\", \"", $resource->get_formats()); + $methods = implode("\", \"", $resource->get_methods()); + $capabilityofferedarr = array_merge($capabilityofferedarr, $resource->get_variables()); + $path = $servicepath . preg_replace('/\{?.*\}$/', '', $resource->get_path()); + $serviceoffered .= <<< EOD +{$sep} + { + "@type":"{$resource->get_type()}", + "@id":"tcp:{$resource->get_id()}", + "endpoint":"{$path}", + "format":["{$formats}"], + "action":["{$methods}"] + } +EOD; + $sep = ','; + } + } + } + $capabilityoffered = implode("\",\n \"", $capabilityofferedarr); + if (strlen($capabilityoffered) > 0) { + $capabilityoffered = "\n \"{$capabilityoffered}\""; + } + $urlparts = parse_url($CFG->wwwroot); + $orgid = $urlparts['host']; + $name = 'Moodle'; + $code = 'moodle'; + $vendorname = 'Moodle.org'; + $vendorcode = 'mdl'; + $prodversion = strval($CFG->version); + if (!empty($CFG->mod_lti_institution_name)) { + $consumername = $CFG->mod_lti_institution_name; + $consumerdesc = ''; + } else { + $consumername = get_site()->fullname; + $consumerdesc = strip_tags(get_site()->summary); + } + $profile = <<< EOD +{ + "@context":[ + "http://purl.imsglobal.org/ctx/lti/v2/ToolConsumerProfile", + { + "tcp":"{$id}#" + } + ], + "@type":"ToolConsumerProfile", + "@id":"{$id}", + "lti_version":"{$version}", + "guid":"{$toolproxy->guid}", + "product_instance":{ + "guid":"{$orgid}", + "product_info":{ + "product_name":{ + "default_value":"{$name}", + "key":"product.name" + }, + "product_version":"{$prodversion}", + "product_family":{ + "code":"{$code}", + "vendor":{ + "code":"{$vendorcode}", + "vendor_name":{ + "default_value":"{$vendorname}", + "key":"product.vendor.name" + }, + "timestamp":"{$now}" + } + } + }, + "service_owner":{ + "@id":"ServiceOwner", + "service_owner_name":{ + "default_value":"{$consumername}", + "key":"service_owner.name" + }, + "description":{ + "default_value":"{$consumerdesc}", + "key":"service_owner.description" + } + } + }, + "capability_offered":[{$capabilityoffered} + ], + "service_offered":[{$serviceoffered} + ] +} +EOD; + $response->set_body($profile); + + } + } + + /** + * Get the resource fully qualified endpoint. + * + * @return string + */ + public function get_endpoint() { + + return parent::get_endpoint() . '?lti_version=' . service_base::LTI_VERSION2P0; + + } + + /** + * Parse a value for custom parameter substitution variables. + * + * @param string $value String to be parsed + * + * @return string + */ + public function parse_value($value) { + + $value = str_replace('$ToolConsumerProfile.url', $this->get_endpoint(), $value); + + return $value; + + } + +} diff --git a/mod/lti/service/profile/classes/local/service/profile.php b/mod/lti/service/profile/classes/local/service/profile.php new file mode 100644 index 00000000000..dcea867d2d9 --- /dev/null +++ b/mod/lti/service/profile/classes/local/service/profile.php @@ -0,0 +1,69 @@ +. + +/** + * This file contains a class definition for the Tool Consumer Profile service + * + * @package ltiservice_profile + * @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_profile\local\service; + +defined('MOODLE_INTERNAL') || die(); + +/** + * A service implementing the Tool Consumer Profile. + * + * @package ltiservice_profile + * @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 profile extends \mod_lti\local\ltiservice\service_base { + + /** + * Class constructor. + */ + public function __construct() { + + parent::__construct(); + $this->id = 'profile'; + $this->name = 'Tool Consumer Profile'; + $this->unsigned = true; + + } + + /** + * Get the resources for this service. + * + * @return array + */ + public function get_resources() { + + if (empty($this->resources)) { + $this->resources = array(); + $this->resources[] = new \ltiservice_profile\local\resource\profile($this); + } + + return $this->resources; + + } + +} diff --git a/mod/lti/service/profile/lang/en/ltiservice_profile.php b/mod/lti/service/profile/lang/en/ltiservice_profile.php new file mode 100644 index 00000000000..9001c1cecaa --- /dev/null +++ b/mod/lti/service/profile/lang/en/ltiservice_profile.php @@ -0,0 +1,26 @@ +. + +/** + * Strings for component 'ltiservice_profile', language 'en' + * + * @package ltiservice_profile + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['pluginname'] = 'Tool Consumer Profile LTI Service'; diff --git a/mod/lti/service/profile/version.php b/mod/lti/service/profile/version.php new file mode 100644 index 00000000000..5f4887bfc1d --- /dev/null +++ b/mod/lti/service/profile/version.php @@ -0,0 +1,32 @@ +. + +/** + * Version information for the ltiservice_profile service. + * + * @package ltiservice_profile + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$plugin->version = 2014092100; +$plugin->requires = 2014050800; +$plugin->component = 'ltiservice_profile'; diff --git a/mod/lti/service/readme.txt b/mod/lti/service/readme.txt new file mode 100644 index 00000000000..ff11d69a889 --- /dev/null +++ b/mod/lti/service/readme.txt @@ -0,0 +1,28 @@ +// 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 . + +/** + * This file contains all necessary code to initiate a tool registration process + * + * @package mod_lti + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +LTI Service plugins directory +============================== +This directory contains LTI service plugins which are discoverable from the Tool +Consumer Profile. diff --git a/mod/lti/service/toolproxy/classes/local/resource/toolproxy.php b/mod/lti/service/toolproxy/classes/local/resource/toolproxy.php new file mode 100644 index 00000000000..3eedc17a0ee --- /dev/null +++ b/mod/lti/service/toolproxy/classes/local/resource/toolproxy.php @@ -0,0 +1,274 @@ +. + +/** + * 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\resource; + +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\resource\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_proxy(null, $response->get_request_data()); + if ($ok) { + $toolproxy = $this->get_service()->get_tool_proxy(); + } else { + $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). + 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); + 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; + foreach ($resources as $resource) { + $found = false; + $tool = new \stdClass(); + foreach ($resource->message as $message) { + if ($message->message_type == 'basic-lti-launch-request') { + $found = true; + $tool->path = $message->path; + $tool->enabled_capability = $message->enabled_capability; + $tool->parameter = $message->parameter; + break; + } + } + if (!$found) { + continue; + } + + $tool->name = $resource->resource_name->default_value; + $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) { + $config = new \stdClass(); + $config->lti_toolurl = "{$baseurl}{$tool->path}"; + $config->lti_typename = $tool->name; + $config->lti_coursevisible = 1; + $config->lti_forcessl = 0; + + $type = new \stdClass(); + $type->state = LTI_TOOL_STATE_PENDING; + $type->toolproxyid = $toolproxy->id; + $type->enabledcapability = implode("\n", $tool->enabled_capability); + $type->parameter = self::lti_extract_parameters($tool->parameter); + + if (isset($resource->icon_info[0]->default_location->path)) { + $iconpath = $resource->icon_info[0]->default_location->path; + $type->icon = "{$baseurl}{$iconpath}"; + if (!empty($securebaseurl)) { + $type->secureicon = "{$securebaseurl}{$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); + + } + +} diff --git a/mod/lti/service/toolproxy/classes/local/service/toolproxy.php b/mod/lti/service/toolproxy/classes/local/service/toolproxy.php new file mode 100644 index 00000000000..1d4276163cf --- /dev/null +++ b/mod/lti/service/toolproxy/classes/local/service/toolproxy.php @@ -0,0 +1,68 @@ +. + +/** + * This file contains a class definition for the Tool Proxy service + * + * @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\service; + +defined('MOODLE_INTERNAL') || die(); + +/** + * A service 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\service_base { + + /** + * Class constructor. + */ + public function __construct() { + + parent::__construct(); + $this->id = 'toolproxy'; + $this->name = 'Tool Proxy'; + + } + + /** + * Get the resources for this service. + * + * @return array + */ + public function get_resources() { + + if (empty($this->resources)) { + $this->resources = array(); + $this->resources[] = new \ltiservice_toolproxy\local\resource\toolproxy($this); + } + + return $this->resources; + + } + +} diff --git a/mod/lti/service/toolproxy/lang/en/ltiservice_toolproxy.php b/mod/lti/service/toolproxy/lang/en/ltiservice_toolproxy.php new file mode 100644 index 00000000000..4e61740c4b1 --- /dev/null +++ b/mod/lti/service/toolproxy/lang/en/ltiservice_toolproxy.php @@ -0,0 +1,26 @@ +. + +/** + * Strings for component 'ltiservice_toolproxy', language 'en' + * + * @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 + */ + +$string['pluginname'] = 'Tool Proxy Service'; diff --git a/mod/lti/service/toolproxy/version.php b/mod/lti/service/toolproxy/version.php new file mode 100644 index 00000000000..0109782bffa --- /dev/null +++ b/mod/lti/service/toolproxy/version.php @@ -0,0 +1,33 @@ +. + +/** + * Version information for the ltiservice_toolproxy service. + * + * @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 + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$plugin->version = 2014092100; +$plugin->requires = 2014050800; +$plugin->component = 'ltiservice_toolproxy'; +$plugin->dependencies = array('ltiservice_profile' => ANY_VERSION); diff --git a/mod/lti/service/toolsettings/classes/local/resource/contextsettings.php b/mod/lti/service/toolsettings/classes/local/resource/contextsettings.php new file mode 100644 index 00000000000..dbf19ae775b --- /dev/null +++ b/mod/lti/service/toolsettings/classes/local/resource/contextsettings.php @@ -0,0 +1,182 @@ +. + +/** + * This file contains a class definition for the Context Settings resource + * + * @package ltiservice_toolsettings + * @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_toolsettings\local\resource; + +use ltiservice_toolsettings\local\resource\systemsettings; +use ltiservice_toolsettings\local\service\toolsettings; + +defined('MOODLE_INTERNAL') || die(); + +/** + * A resource implementing the Context-level (ToolProxyBinding) Settings. + * + * @package ltiservice_toolsettings + * @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 contextsettings extends \mod_lti\local\ltiservice\resource_base { + + /** + * Class constructor. + * + * @param ltiservice_toolsettings\local\resource\contextsettings $service Service instance + */ + public function __construct($service) { + + parent::__construct($service); + $this->id = 'ToolProxyBindingSettings'; + $this->template = '/{context_type}/{context_id}/bindings/{vendor_code}/{product_code}/custom'; + $this->variables[] = 'ToolProxyBinding.custom.url'; + $this->formats[] = 'application/vnd.ims.lti.v2.toolsettings+json'; + $this->formats[] = 'application/vnd.ims.lti.v2.toolsettings.simple+json'; + $this->methods[] = 'GET'; + $this->methods[] = 'PUT'; + + } + + /** + * Execute the request for this resource. + * + * @param mod_lti\local\ltiservice\response $response Response object for this request. + */ + public function execute($response) { + + $params = $this->parse_template(); + $contexttype = $params['context_type']; + $contextid = $params['context_id']; + $vendorcode = $params['vendor_code']; + $productcode = $params['product_code']; + $bubble = optional_param('bubble', '', PARAM_ALPHA); + $ok = !empty($contexttype) && !empty($contextid) && + !empty($vendorcode) && !empty($productcode) && + $this->check_tool_proxy($productcode, $response->get_request_data()); + if (!$ok) { + $response->set_code(401); + } + $contenttype = $response->get_accept(); + $simpleformat = !empty($contenttype) && ($contenttype == $this->formats[1]); + if ($ok) { + $ok = (empty($bubble) || ((($bubble == 'distinct') || ($bubble == 'all')))) && + (!$simpleformat || empty($bubble) || ($bubble != 'all')) && + (empty($bubble) || ($response->get_request_method() == 'GET')); + } + + if (!$ok) { + $response->set_code(404); + } else { + $systemsetting = null; + $contextsettings = lti_get_tool_settings($this->get_service()->get_tool_proxy()->id, $contextid); + if (!empty($bubble)) { + $systemsetting = new systemsettings($this->get_service()); + $systemsetting->params['tool_proxy_id'] = $productcode; + $systemsettings = lti_get_tool_settings($this->get_service()->get_tool_proxy()->id); + if ($bubble == 'distinct') { + toolsettings::distinct_settings($systemsettings, $contextsettings, null); + } + } else { + $systemsettings = null; + } + if ($response->get_request_method() == 'GET') { + $json = ''; + if ($simpleformat) { + $response->set_content_type($this->formats[1]); + $json .= "{"; + } else { + $response->set_content_type($this->formats[0]); + $json .= "{\n \"@context\":\"http://purl.imsglobal.org/ctx/lti/v2/ToolSettings\",\n \"@graph\":[\n"; + } + $settings = toolsettings::settings_to_json($systemsettings, $simpleformat, 'ToolProxy', $systemsetting); + $json .= $settings; + $isfirst = strlen($settings) <= 0; + $settings = toolsettings::settings_to_json($contextsettings, $simpleformat, 'ToolProxyBinding', $this); + if ((strlen($settings) > 0) && !$isfirst) { + $json .= ","; + } + $json .= $settings; + if ($simpleformat) { + $json .= "\n}"; + } else { + $json .= "\n ]\n}"; + } + $response->set_body($json); + } else { // PUT. + $settings = null; + if ($response->get_content_type() == $this->formats[0]) { + $json = json_decode($response->get_request_data()); + $ok = !empty($json); + if ($ok) { + $ok = isset($json->{"@graph"}) && is_array($json->{"@graph"}) && (count($json->{"@graph"}) == 1) && + ($json->{"@graph"}[0]->{"@type"} == 'ToolProxyBinding'); + } + if ($ok) { + $settings = $json->{"@graph"}[0]->custom; + } + } else { // Simple JSON. + $json = json_decode($response->get_request_data(), true); + $ok = !empty($json); + if ($ok) { + $ok = is_array($json); + } + if ($ok) { + $settings = $json; + } + } + if ($ok) { + lti_set_tool_settings($settings, $this->get_service()->get_tool_proxy()->id, $contextid); + } else { + $response->set_code(406); + } + } + } + } + + /** + * Parse a value for custom parameter substitution variables. + * + * @param string $value String to be parsed + * + * @return string + */ + public function parse_value($value) { + global $COURSE; + + if ($COURSE->format == 'site') { + $this->params['context_type'] = 'Group'; + } else { + $this->params['context_type'] = 'CourseSection'; + } + $this->params['context_id'] = $COURSE->id; + $this->params['vendor_code'] = $this->get_service()->get_tool_proxy()->vendorcode; + $this->params['product_code'] = $this->get_service()->get_tool_proxy()->guid; + $value = str_replace('$ToolProxyBinding.custom.url', parent::get_endpoint(), $value); + + return $value; + + } + +} diff --git a/mod/lti/service/toolsettings/classes/local/resource/linksettings.php b/mod/lti/service/toolsettings/classes/local/resource/linksettings.php new file mode 100644 index 00000000000..f092048349a --- /dev/null +++ b/mod/lti/service/toolsettings/classes/local/resource/linksettings.php @@ -0,0 +1,208 @@ +. + +/** + * This file contains a class definition for the Context Settings resource + * + * @package ltiservice_toolsettings + * @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_toolsettings\local\resource; + +use ltiservice_toolsettings\local\resource\systemsettings; +use ltiservice_toolsettings\local\resource\contextsettings; +use ltiservice_toolsettings\local\service\toolsettings; + +defined('MOODLE_INTERNAL') || die(); + +/** + * A resource implementing the Context-level (ToolProxyBinding) Settings. + * + * @package ltiservice_toolsettings + * @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 linksettings extends \mod_lti\local\ltiservice\resource_base { + + /** + * Class constructor. + * + * @param ltiservice_toolsettings\local\resource\linksettings $service Service instance + */ + public function __construct($service) { + + parent::__construct($service); + $this->id = 'LtiLinkSettings'; + $this->template = '/links/{link_id}/custom'; + $this->variables[] = 'LtiLink.custom.url'; + $this->formats[] = 'application/vnd.ims.lti.v2.toolsettings+json'; + $this->formats[] = 'application/vnd.ims.lti.v2.toolsettings.simple+json'; + $this->methods[] = 'GET'; + $this->methods[] = 'PUT'; + + } + + /** + * Execute the request for this resource. + * + * @param mod_lti\local\ltiservice\response $response Response object for this request. + */ + public function execute($response) { + global $DB, $COURSE; + + $params = $this->parse_template(); + $linkid = $params['link_id']; + $bubble = optional_param('bubble', '', PARAM_ALPHA); + $contenttype = $response->get_accept(); + $simpleformat = !empty($contenttype) && ($contenttype == $this->formats[1]); + $ok = (empty($bubble) || ((($bubble == 'distinct') || ($bubble == 'all')))) && + (!$simpleformat || empty($bubble) || ($bubble != 'all')) && + (empty($bubble) || ($response->get_request_method() == 'GET')); + if (!$ok) { + $response->set_code(406); + } + + $systemsetting = null; + $contextsetting = null; + if ($ok) { + $ok = !empty($linkid); + if ($ok) { + $lti = $DB->get_record('lti', array('id' => $linkid), 'course,typeid', MUST_EXIST); + $ltitype = $DB->get_record('lti_types', array('id' => $lti->typeid)); + $toolproxy = $DB->get_record('lti_tool_proxies', array('id' => $ltitype->toolproxyid)); + $ok = $this->check_tool_proxy($toolproxy->guid, $response->get_request_data()); + } + if (!$ok) { + $response->set_code(401); + } + } + if ($ok) { + $linksettings = lti_get_tool_settings($this->get_service()->get_tool_proxy()->id, $lti->course, $linkid); + if (!empty($bubble)) { + $contextsetting = new contextsettings($this->get_service()); + if ($COURSE == 'site') { + $contextsetting->params['context_type'] = 'Group'; + } else { + $contextsetting->params['context_type'] = 'CourseSection'; + } + $contextsetting->params['context_id'] = $lti->course; + $contextsetting->params['vendor_code'] = $this->get_service()->get_tool_proxy()->vendorcode; + $contextsetting->params['product_code'] = $this->get_service()->get_tool_proxy()->id; + $contextsettings = lti_get_tool_settings($this->get_service()->get_tool_proxy()->id, $lti->course); + $systemsetting = new systemsettings($this->get_service()); + $systemsetting->params['tool_proxy_id'] = $this->get_service()->get_tool_proxy()->id; + $systemsettings = lti_get_tool_settings($this->get_service()->get_tool_proxy()->id); + if ($bubble == 'distinct') { + toolsettings::distinct_settings($systemsettings, $contextsettings, $linksettings); + } + } else { + $contextsettings = null; + $systemsettings = null; + } + if ($response->get_request_method() == 'GET') { + $json = ''; + if ($simpleformat) { + $response->set_content_type($this->formats[1]); + $json .= "{"; + } else { + $response->set_content_type($this->formats[0]); + $json .= "{\n \"@context\":\"http://purl.imsglobal.org/ctx/lti/v2/ToolSettings\",\n \"@graph\":[\n"; + } + $settings = toolsettings::settings_to_json($systemsettings, $simpleformat, 'ToolProxy', $systemsetting); + $json .= $settings; + $isfirst = strlen($settings) <= 0; + $settings = toolsettings::settings_to_json($contextsettings, $simpleformat, 'ToolProxyBinding', $contextsetting); + if (strlen($settings) > 0) { + if (!$isfirst) { + $json .= ","; + if (!$simpleformat) { + $json .= "\n"; + } + } + $isfirst = false; + } + $json .= $settings; + $settings = toolsettings::settings_to_json($linksettings, $simpleformat, 'LtiLink', $this); + if ((strlen($settings) > 0) && !$isfirst) { + $json .= ","; + if (!$simpleformat) { + $json .= "\n"; + } + } + $json .= $settings; + if ($simpleformat) { + $json .= "\n}"; + } else { + $json .= "\n ]\n}"; + } + $response->set_body($json); + } else { // PUT. + $settings = null; + if ($response->get_content_type() == $this->formats[0]) { + $json = json_decode($response->get_request_data()); + $ok = !empty($json); + if ($ok) { + $ok = isset($json->{"@graph"}) && is_array($json->{"@graph"}) && (count($json->{"@graph"}) == 1) && + ($json->{"@graph"}[0]->{"@type"} == 'LtiLink'); + } + if ($ok) { + $settings = $json->{"@graph"}[0]->custom; + } + } else { // Simple JSON. + $json = json_decode($response->get_request_data(), true); + $ok = !empty($json); + if ($ok) { + $ok = is_array($json); + } + if ($ok) { + $settings = $json; + } + } + if ($ok) { + lti_set_tool_settings($settings, $this->get_service()->get_tool_proxy()->id, $lti->course, $linkid); + } else { + $response->set_code(406); + } + } + } + } + + /** + * Parse a value for custom parameter substitution variables. + * + * @param string $value String to be parsed + * + * @return string + */ + public function parse_value($value) { + + $id = optional_param('id', 0, PARAM_INT); // Course Module ID. + if (!empty($id)) { + $cm = get_coursemodule_from_id('lti', $id, 0, false, MUST_EXIST); + $this->params['link_id'] = $cm->instance; + } + $value = str_replace('$LtiLink.custom.url', parent::get_endpoint(), $value); + + return $value; + + } + +} diff --git a/mod/lti/service/toolsettings/classes/local/resource/systemsettings.php b/mod/lti/service/toolsettings/classes/local/resource/systemsettings.php new file mode 100644 index 00000000000..1fc73d4eaad --- /dev/null +++ b/mod/lti/service/toolsettings/classes/local/resource/systemsettings.php @@ -0,0 +1,152 @@ +. + +/** + * This file contains a class definition for the System Settings resource + * + * @package ltiservice_toolsettings + * @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_toolsettings\local\resource; + +use ltiservice_toolsettings\local\service\toolsettings; + +defined('MOODLE_INTERNAL') || die(); + +/** + * A resource implementing the System-level (ToolProxy) Settings. + * + * @package ltiservice_toolsettings + * @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 systemsettings extends \mod_lti\local\ltiservice\resource_base { + + /** + * Class constructor. + * + * @param ltiservice_toolsettings\local\service\toolsettings $service Service instance + */ + public function __construct($service) { + + parent::__construct($service); + $this->id = 'ToolProxySettings'; + $this->template = '/toolproxy/{tool_proxy_id}/custom'; + $this->variables[] = 'ToolProxy.custom.url'; + $this->formats[] = 'application/vnd.ims.lti.v2.toolsettings+json'; + $this->formats[] = 'application/vnd.ims.lti.v2.toolsettings.simple+json'; + $this->methods[] = 'GET'; + $this->methods[] = 'PUT'; + + } + + /** + * Execute the request for this resource. + * + * @param mod_lti\local\ltiservice\response $response Response object for this request. + */ + public function execute($response) { + + $params = $this->parse_template(); + $tpid = $params['tool_proxy_id']; + $bubble = optional_param('bubble', '', PARAM_ALPHA); + $ok = !empty($tpid) && $this->check_tool_proxy($tpid, $response->get_request_data()); + if (!$ok) { + $response->set_code(401); + } + $contenttype = $response->get_accept(); + $simpleformat = !empty($contenttype) && ($contenttype == $this->formats[1]); + if ($ok) { + $ok = (empty($bubble) || ((($bubble == 'distinct') || ($bubble == 'all')))) && + (!$simpleformat || empty($bubble) || ($bubble != 'all')) && + (empty($bubble) || ($response->get_request_method() == 'GET')); + if (!$ok) { + $response->set_code(406); + } + } + + if ($ok) { + $systemsettings = lti_get_tool_settings($this->get_service()->get_tool_proxy()->id); + if ($response->get_request_method() == 'GET') { + $json = ''; + if ($simpleformat) { + $response->set_content_type($this->formats[1]); + $json .= "{"; + } else { + $response->set_content_type($this->formats[0]); + $json .= "{\n \"@context\":\"http://purl.imsglobal.org/ctx/lti/v2/ToolSettings\",\n \"@graph\":[\n"; + } + $json .= toolsettings::settings_to_json($systemsettings, $simpleformat, + 'ToolProxy', $this); + if ($simpleformat) { + $json .= "\n}"; + } else { + $json .= "\n ]\n}"; + } + $response->set_body($json); + } else { // PUT. + $settings = null; + if ($response->get_content_type() == $this->formats[0]) { + $json = json_decode($response->get_request_data()); + $ok = !empty($json); + if ($ok) { + $ok = isset($json->{"@graph"}) && is_array($json->{"@graph"}) && (count($json->{"@graph"}) == 1) && + ($json->{"@graph"}[0]->{"@type"} == 'ToolProxy'); + } + if ($ok) { + $settings = $json->{"@graph"}[0]->custom; + } + } else { // Simple JSON. + $json = json_decode($response->get_request_data(), true); + $ok = !empty($json); + if ($ok) { + $ok = is_array($json); + } + if ($ok) { + $settings = $json; + } + } + if ($ok) { + lti_set_tool_settings($settings, $this->get_service()->get_tool_proxy()->id); + } else { + $response->set_code(406); + } + } + } + + } + + /** + * Parse a value for custom parameter substitution variables. + * + * @param string $value String to be parsed + * + * @return string + */ + public function parse_value($value) { + + $value = str_replace('$ToolProxy.custom.url', parent::get_endpoint(), $value); + + return $value; + + } + +} diff --git a/mod/lti/service/toolsettings/classes/local/service/toolsettings.php b/mod/lti/service/toolsettings/classes/local/service/toolsettings.php new file mode 100644 index 00000000000..8ee99e78c73 --- /dev/null +++ b/mod/lti/service/toolsettings/classes/local/service/toolsettings.php @@ -0,0 +1,138 @@ +. + +/** + * This file contains a class definition for the Tool Settings service + * + * @package ltiservice_toolsettings + * @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_toolsettings\local\service; + +defined('MOODLE_INTERNAL') || die(); + +/** + * A service implementing Tool Settings. + * + * @package ltiservice_toolsettings + * @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 toolsettings extends \mod_lti\local\ltiservice\service_base { + + /** + * Class constructor. + */ + public function __construct() { + + parent::__construct(); + $this->id = 'toolsettings'; + $this->name = 'Tool Settings'; + + } + + /** + * Get the resources for this service. + * + * @return array + */ + public function get_resources() { + + if (empty($this->resources)) { + $this->resources = array(); + $this->resources[] = new \ltiservice_toolsettings\local\resource\systemsettings($this); + $this->resources[] = new \ltiservice_toolsettings\local\resource\contextsettings($this); + $this->resources[] = new \ltiservice_toolsettings\local\resource\linksettings($this); + } + + return $this->resources; + + } + + /** + * Get the distinct settings from each level by removing any duplicates from higher levels. + * + * @param array $systemsettings System level settings + * @param array $contextsettings Context level settings + * @param array $linksettings Link level settings + */ + public static function distinct_settings(&$systemsettings, &$contextsettings, $linksettings) { + + if (!empty($systemsettings)) { + foreach ($systemsettings as $key => $value) { + if ((!empty($contextsettings) && array_key_exists($key, $contextsettings)) || + (!empty($linksettings) && array_key_exists($key, $linksettings))) { + unset($systemsettings[$key]); + } + } + } + if (!empty($contextsettings)) { + foreach ($contextsettings as $key => $value) { + if (!empty($linksettings) && array_key_exists($key, $linksettings)) { + unset($contextsettings[$key]); + } + } + } + } + + /** + * Get the JSON representation of the settings. + * + * @param array $settings Settings + * @param boolean $simpleformat true if simple JSON is to be returned + * @param string $type JSON-LD type + * @param \mod_lti\local\ltiservice\resource_base $resource Resource handling the request + * + * @return string + */ + public static function settings_to_json($settings, $simpleformat, $type, $resource) { + + $json = ''; + if (!empty($resource)) { + $indent = ''; + if (!$simpleformat) { + $json .= " {\n \"@type\":\"{$type}\",\n"; + $json .= " \"@id\":\"{$resource->get_endpoint()}\",\n"; + $json .= ' "custom":'; + $json .= "{"; + $indent = ' '; + } + $isfirst = true; + if (!empty($settings)) { + foreach ($settings as $key => $value) { + if (!$isfirst) { + $json .= ","; + } else { + $isfirst = false; + } + $json .= "\n{$indent} \"{$key}\":\"{$value}\""; + } + } + if (!$simpleformat) { + $json .= "\n{$indent}}\n }"; + } + } + + return $json; + + } + +} diff --git a/mod/lti/service/toolsettings/lang/en/ltiservice_toolsettings.php b/mod/lti/service/toolsettings/lang/en/ltiservice_toolsettings.php new file mode 100644 index 00000000000..b4043a7e550 --- /dev/null +++ b/mod/lti/service/toolsettings/lang/en/ltiservice_toolsettings.php @@ -0,0 +1,26 @@ +. + +/** + * Strings for component 'ltiservice_toolsettings', language 'en' + * + * @package ltiservice_toolsettings + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['pluginname'] = 'Tool Settings Service'; diff --git a/mod/lti/service/toolsettings/version.php b/mod/lti/service/toolsettings/version.php new file mode 100644 index 00000000000..1ff992c49f1 --- /dev/null +++ b/mod/lti/service/toolsettings/version.php @@ -0,0 +1,35 @@ +. + +/** + * Version information for the ltiservice_toolsettings service. + * + * @package ltiservice_toolsettings + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$plugin->version = 2014092100; +$plugin->requires = 2014050800; +$plugin->component = 'ltiservice_toolsettings'; +$plugin->dependencies = array('ltiservice_profile' => ANY_VERSION, + 'ltiservice_toolproxy' => ANY_VERSION +); diff --git a/mod/lti/servicelib.php b/mod/lti/servicelib.php index c1f3f97003d..438cea2d1cf 100644 --- a/mod/lti/servicelib.php +++ b/mod/lti/servicelib.php @@ -27,7 +27,7 @@ defined('MOODLE_INTERNAL') || die; require_once($CFG->dirroot.'/mod/lti/OAuthBody.php'); -// TODO: Switch to core oauthlib once implemented - MDL-30149 +// TODO: Switch to core oauthlib once implemented - MDL-30149. use moodle\mod\lti as lti; define('LTI_ITEM_TYPE', 'mod'); @@ -48,7 +48,7 @@ function lti_get_response_xml($codemajor, $description, $messageref, $messagetyp $statusinfo->addChild('imsx_severity', 'status'); $statusinfo->addChild('imsx_description', $description); $statusinfo->addChild('imsx_messageRefIdentifier', $messageref); - $incomingtype = str_replace('Response','Request', $messagetype); + $incomingtype = str_replace('Response', 'Request', $messagetype); $statusinfo->addChild('imsx_operationRefIdentifier', $incomingtype); $xml->addChild('imsx_POXBody')->addChild($messagetype); @@ -125,14 +125,23 @@ function lti_parse_grade_delete_message($xml) { } function lti_accepts_grades($ltiinstance) { + global $DB; + $acceptsgrades = true; - $typeconfig = lti_get_config($ltiinstance); + $ltitype = $DB->get_record('lti_types', array('id' => $ltiinstance->typeid)); - $typeacceptgrades = isset($typeconfig['acceptgrades']) ? $typeconfig['acceptgrades'] : LTI_SETTING_DELEGATE; + if (empty($ltitype->toolproxyid)) { + $typeconfig = lti_get_config($ltiinstance); - if (!($typeacceptgrades == LTI_SETTING_ALWAYS || - ($typeacceptgrades == LTI_SETTING_DELEGATE && $ltiinstance->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS))) { - $acceptsgrades = false; + $typeacceptgrades = isset($typeconfig['acceptgrades']) ? $typeconfig['acceptgrades'] : LTI_SETTING_DELEGATE; + + if (!($typeacceptgrades == LTI_SETTING_ALWAYS || + ($typeacceptgrades == LTI_SETTING_DELEGATE && $ltiinstance->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS))) { + $acceptsgrades = false; + } + } else { + $enabledcapabilities = explode("\n", $ltitype->enabledcapability); + $acceptsgrades = in_array('Result.autocreate', $enabledcapabilities); } return $acceptsgrades; @@ -166,7 +175,8 @@ function lti_update_grade($ltiinstance, $userid, $launchid, $gradeval) { $status = grade_update(LTI_SOURCE, $ltiinstance->course, LTI_ITEM_TYPE, LTI_ITEM_MODULE, $ltiinstance->id, 0, $grade, $params); - $record = $DB->get_record('lti_submission', array('ltiid' => $ltiinstance->id, 'userid' => $userid, 'launchid' => $launchid), 'id'); + $record = $DB->get_record('lti_submission', array('ltiid' => $ltiinstance->id, 'userid' => $userid, + 'launchid' => $launchid), 'id'); if ($record) { $id = $record->id; } else { @@ -235,14 +245,14 @@ function lti_verify_message($key, $sharedsecrets, $body, $headers = null) { $signaturefailed = false; try { - // TODO: Switch to core oauthlib once implemented - MDL-30149 - lti\handleOAuthBodyPOST($key, $secret, $body, $headers); + // TODO: Switch to core oauthlib once implemented - MDL-30149. + lti\handle_oauth_body_post($key, $secret, $body, $headers); } catch (Exception $e) { $signaturefailed = true; } if (!$signaturefailed) { - return $secret;//Return the secret used to sign the message) + return $secret; // Return the secret used to sign the message). } } @@ -276,7 +286,7 @@ function lti_extend_lti_services($data) { $plugins = get_plugin_list_with_function('ltisource', $data->messagetype); if (!empty($plugins)) { try { - // There can only be one + // There can only be one. if (count($plugins) > 1) { throw new coding_exception('More than one ltisource plugin handler found'); } diff --git a/mod/lti/services.php b/mod/lti/services.php new file mode 100644 index 00000000000..26cc6378785 --- /dev/null +++ b/mod/lti/services.php @@ -0,0 +1,78 @@ +. + +/** + * This file contains a controller for receiving LTI service requests + * + * @package mod_lti + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +define('NO_DEBUG_DISPLAY', true); +define('NO_MOODLE_COOKIES', true); + +require_once(dirname(__FILE__) . '/../../config.php'); +require_once($CFG->dirroot . '/mod/lti/locallib.php'); + + +$response = new \mod_lti\local\ltiservice\response(); + +$isget = $response->get_request_method() == 'GET'; + +if ($isget) { + $response->set_accept(isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : ''); +} else { + $response->set_content_type(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : ''); +} + +$ok = false; +$path = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; + +$accept = $response->get_accept(); + +$services = lti_get_services(); +foreach ($services as $service) { + $resources = $service->get_resources(); + foreach ($resources as $resource) { + if (($isget && !empty($accept) && (strpos($accept, '*/*') === false) && + !in_array($accept, $resource->get_formats())) || + (!$isget && !in_array($response->get_content_type(), $resource->get_formats()))) { + continue; + } + $template = $resource->get_template(); + $template = preg_replace('/\{[a-zA-Z_]+\}/', '[^/]+', $template); + $template = preg_replace('/\{\?[0-9a-zA-Z_\-,]+\}$/', '', $template); + $template = str_replace('/', '\/', $template); + if (preg_match("/{$template}/", $path) === 1) { + $ok = true; + break 2; + } + } +} +if (!$ok) { + $response->set_code(400); +} else { + $body = file_get_contents('php://input'); + $response->set_request_data($body); + if (in_array($response->get_request_method(), $resource->get_methods())) { + $resource->execute($response); + } else { + $response->set_code(405); + } +} +$response->send(); diff --git a/mod/lti/settings.php b/mod/lti/settings.php index 7c7b853ff9b..952428c9651 100644 --- a/mod/lti/settings.php +++ b/mod/lti/settings.php @@ -30,7 +30,7 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * This file defines the global lti administration form @@ -48,17 +48,27 @@ defined('MOODLE_INTERNAL') || die; -/** @var admin_settingpage $settings */ +/* + * @var admin_settingpage $settings + */ $modltifolder = new admin_category('modltifolder', new lang_string('pluginname', 'mod_lti'), $module->is_enabled() === false); $ADMIN->add('modsettings', $modltifolder); - +$settings->visiblename = new lang_string('manage_tools', 'mod_lti'); $ADMIN->add('modltifolder', $settings); +$ADMIN->add('modltifolder', new admin_externalpage('ltitoolproxies', + get_string('manage_tool_proxies', 'lti'), + new moodle_url('/mod/lti/toolproxies.php'))); foreach (core_plugin_manager::instance()->get_plugins_of_type('ltisource') as $plugin) { - /** @var \mod_lti\plugininfo\ltisource $plugin */ + /* + * @var \mod_lti\plugininfo\ltisource $plugin + */ $plugin->load_settings($ADMIN, 'modltifolder', $hassiteconfig); } +$toolproxiesurl = new moodle_url('/mod/lti/toolproxies.php'); +$toolproxiesurl = $toolproxiesurl->out(); + if ($ADMIN->fulltree) { require_once($CFG->dirroot.'/mod/lti/locallib.php'); @@ -110,28 +120,33 @@ if ($ADMIN->fulltree) { $activeselected = 'class="selected"'; break; } + $addtype = get_string('addtype', 'lti'); + $config = get_string('manage_tool_proxies', 'lti'); - $template = " -
-
    + $addtypeurl = "{$CFG->wwwroot}/mod/lti/typessettings.php?action=add&sesskey={$USER->sesskey}"; + + $template = <<< EOD +
    + -
    +
    + $configuredtoolshtml
    @@ -143,7 +158,7 @@ if ($ADMIN->fulltree) {
    - -"; - $settings->add(new admin_setting_heading('lti_types', new lang_string('external_tool_types', 'lti') . $OUTPUT->help_icon('main_admin', 'lti'), $template)); +EOD; + $settings->add(new admin_setting_heading('lti_types', new lang_string('external_tool_types', 'lti') . + $OUTPUT->help_icon('main_admin', 'lti'), $template)); } -if (count($modltifolder->children) <= 1) { - // No need for a folder, revert to default activity settings page. - $ADMIN->prune('modltifolder'); -} else { - // Using the folder, update settings name. - $settings->visiblename = new lang_string('ltisettings', 'mod_lti'); +// Tell core we already added the settings structure. +$settings = null; - // Tell core we already added the settings structure. - $settings = null; -} diff --git a/mod/lti/tests/event/unknown_service_api_called_test.php b/mod/lti/tests/event/unknown_service_api_called_test.php index 86359cbb4d1..0198d2d8fd5 100644 --- a/mod/lti/tests/event/unknown_service_api_called_test.php +++ b/mod/lti/tests/event/unknown_service_api_called_test.php @@ -34,7 +34,7 @@ use mod_lti\event\unknown_service_api_called; * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class mod_lti_event_unknown_service_api_called_test extends advanced_testcase { - /** + /* * Ensure create event works. */ public function test_create_event() { @@ -42,7 +42,7 @@ class mod_lti_event_unknown_service_api_called_test extends advanced_testcase { $this->assertInstanceOf('\mod_lti\event\unknown_service_api_called', $event); } - /** + /* * Ensure event context works. */ public function test_event_context() { @@ -50,7 +50,7 @@ class mod_lti_event_unknown_service_api_called_test extends advanced_testcase { $this->assertEquals(context_system::instance(), $event->get_context()); } - /** + /* * Ensure we can trigger the event. */ public function test_trigger_event() { @@ -62,7 +62,7 @@ class mod_lti_event_unknown_service_api_called_test extends advanced_testcase { $this->assertCount(1, $events); } - /** + /* * Ensure get/set message data is functioning as expected. */ public function test_get_message_data() { @@ -71,9 +71,11 @@ class mod_lti_event_unknown_service_api_called_test extends advanced_testcase { 'bat' => 'baz', ); - /** @var unknown_service_api_called $event */ + /* + * @var unknown_service_api_called $event + */ $event = unknown_service_api_called::create(); $event->set_message_data($data); $this->assertSame($data, $event->get_message_data()); } -} \ No newline at end of file +} diff --git a/mod/lti/tests/generator_test.php b/mod/lti/tests/generator_test.php index 8a2bbcfe889..d43af297284 100644 --- a/mod/lti/tests/generator_test.php +++ b/mod/lti/tests/generator_test.php @@ -45,7 +45,9 @@ class mod_lti_generator_testcase extends advanced_testcase { $course = $this->getDataGenerator()->create_course(); - /** @var mod_lti_generator $generator */ + /* + * @var mod_lti_generator $generator + */ $generator = $this->getDataGenerator()->get_plugin_generator('mod_lti'); $this->assertInstanceOf('mod_lti_generator', $generator); $this->assertEquals('lti', $generator->get_modulename()); @@ -63,9 +65,10 @@ class mod_lti_generator_testcase extends advanced_testcase { $context = context_module::instance($cm->id); $this->assertEquals($lti->cmid, $context->instanceid); - // test gradebook integration using low level DB access - DO NOT USE IN PLUGIN CODE! + // Test gradebook integration using low level DB access - DO NOT USE IN PLUGIN CODE! $lti = $generator->create_instance(array('course' => $course->id, 'assessed' => 1, 'scale' => 100)); - $gitem = $DB->get_record('grade_items', array('courseid' => $course->id, 'itemtype' => 'mod', 'itemmodule' => 'lti', 'iteminstance' => $lti->id)); + $gitem = $DB->get_record('grade_items', array('courseid' => $course->id, 'itemtype' => 'mod', + 'itemmodule' => 'lti', 'iteminstance' => $lti->id)); $this->assertNotEmpty($gitem); $this->assertEquals(100, $gitem->grademax); $this->assertEquals(0, $gitem->grademin); diff --git a/mod/lti/tests/locallib_test.php b/mod/lti/tests/locallib_test.php index 2875f59a7ab..18e939c4071 100644 --- a/mod/lti/tests/locallib_test.php +++ b/mod/lti/tests/locallib_test.php @@ -30,7 +30,7 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * This file contains unit tests for (some of) lti/locallib.php @@ -63,17 +63,19 @@ require_once($CFG->dirroot . '/mod/lti/servicelib.php'); class mod_lti_locallib_testcase extends advanced_testcase { public function test_split_custom_parameters() { - $this->assertEquals(lti_split_custom_parameters("x=1\ny=2"), - array('custom_x' => '1', 'custom_y'=> '2')); + $tool = new stdClass(); + $tool->enabledcapability = ''; + $this->assertEquals(lti_split_custom_parameters(null, $tool, array(), "x=1\ny=2", false), + array('custom_x' => '1', 'custom_y' => '2')); - $this->assertEquals(lti_split_custom_parameters('x=1;y=2'), - array('custom_x' => '1', 'custom_y'=> '2')); + // Removed repeat of previous test with a semicolon separator. - $this->assertEquals(lti_split_custom_parameters('Review:Chapter=1.2.56'), + $this->assertEquals(lti_split_custom_parameters(null, $tool, array(), 'Review:Chapter=1.2.56', false), array('custom_review_chapter' => '1.2.56')); - $this->assertEquals(lti_split_custom_parameters('Complex!@#$^*(){}[]KEY=Complex!@#$^*(){}[]Value'), - array('custom_complex____________key' => 'Complex!@#$^*(){}[]Value')); + $this->assertEquals(lti_split_custom_parameters(null, $tool, array(), + 'Complex!@#$^*(){}[]KEY=Complex!@#$^*;(){}[]½Value', false), + array('custom_complex____________key' => 'Complex!@#$^*;(){}[]½Value')); } /** @@ -83,9 +85,16 @@ class mod_lti_locallib_testcase extends advanced_testcase { * outside-checks to the conformance tests. MDL-30347 */ public function disabled_test_sign_parameters() { - $correct = array ( 'context_id' => '12345', 'context_label' => 'SI124', 'context_title' => 'Social Computing', 'ext_submit' => 'Click Me', 'lti_message_type' => 'basic-lti-launch-request', 'lti_version' => 'LTI-1p0', 'oauth_consumer_key' => 'lmsng.school.edu', 'oauth_nonce' => '47458148e33a8f9dafb888c3684cf476', 'oauth_signature' => 'qWgaBIezihCbeHgcwUy14tZcyDQ=', 'oauth_signature_method' => 'HMAC-SHA1', 'oauth_timestamp' => '1307141660', 'oauth_version' => '1.0', 'resource_link_id' => '123', 'resource_link_title' => 'Weekly Blog', 'roles' => 'Learner', 'tool_consumer_instance_guid' => 'lmsng.school.edu', 'user_id' => '789'); + $correct = array ( 'context_id' => '12345', 'context_label' => 'SI124', 'context_title' => 'Social Computing', + 'ext_submit' => 'Click Me', 'lti_message_type' => 'basic-lti-launch-request', 'lti_version' => 'LTI-1p0', + 'oauth_consumer_key' => 'lmsng.school.edu', 'oauth_nonce' => '47458148e33a8f9dafb888c3684cf476', + 'oauth_signature' => 'qWgaBIezihCbeHgcwUy14tZcyDQ=', 'oauth_signature_method' => 'HMAC-SHA1', + 'oauth_timestamp' => '1307141660', 'oauth_version' => '1.0', 'resource_link_id' => '123', + 'resource_link_title' => 'Weekly Blog', 'roles' => 'Learner', 'tool_consumer_instance_guid' => 'lmsng.school.edu', + 'user_id' => '789'); - $requestparams = array('resource_link_id' => '123', 'resource_link_title' => 'Weekly Blog', 'user_id' => '789', 'roles' => 'Learner', 'context_id' => '12345', 'context_label' => 'SI124', 'context_title' => 'Social Computing'); + $requestparams = array('resource_link_id' => '123', 'resource_link_title' => 'Weekly Blog', 'user_id' => '789', + 'roles' => 'Learner', 'context_id' => '12345', 'context_label' => 'SI124', 'context_title' => 'Social Computing'); $parms = lti_sign_parameters($requestparams, 'http://www.imsglobal.org/developer/LTI/tool.php', 'POST', 'lmsng.school.edu', 'secret', 'Click Me', 'lmsng.school.edu' /*, $org_desc*/); @@ -93,7 +102,7 @@ class mod_lti_locallib_testcase extends advanced_testcase { $this->assertTrue(isset($parms['oauth_signature'])); $this->assertTrue(isset($parms['oauth_timestamp'])); - // Those things that are hard to mock + // Those things that are hard to mock. $correct['oauth_nonce'] = $parms['oauth_nonce']; $correct['oauth_signature'] = $parms['oauth_signature']; $correct['oauth_timestamp'] = $parms['oauth_timestamp']; @@ -121,7 +130,10 @@ class mod_lti_locallib_testcase extends advanced_testcase { - {"data":{"instanceid":"2","userid":"2"},"hash":"0b5078feab59b9938c333ceaae21d8e003a7b295e43cdf55338445254421076b"} + ' . + '{"data":{"instanceid":"2","userid":"2"},"hash":' . + '"0b5078feab59b9938c333ceaae21d8e003a7b295e43cdf55338445254421076b"}' . + ' diff --git a/mod/lti/tests/upgradelib_test.php b/mod/lti/tests/upgradelib_test.php new file mode 100644 index 00000000000..87accdda27c --- /dev/null +++ b/mod/lti/tests/upgradelib_test.php @@ -0,0 +1,90 @@ +. + +/** + * LTI upgrade script. + * + * @package mod_lti + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/mod/lti/locallib.php'); +require_once($CFG->dirroot . '/mod/lti/db/upgradelib.php'); + + +/** + * Unit tests for mod_lti upgrades. + * + * @package mod_lti + * @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 mod_lti_upgradelib_testcase extends advanced_testcase { + + /** + * Test conversion of semicolon separated custom parameters. + */ + public function test_custom_parameter() { + global $DB, $SITE, $USER; + + $custom1 = 'a=one;b=two;three=3'; + $custom2 = "a=one\nb=two\nthree=3"; + + $this->resetAfterTest(true); + + $ltigenerator = $this->getDataGenerator()->get_plugin_generator('mod_lti'); + + // Create 2 tools with custom parameters. + $toolid1 = $DB->insert_record('lti_types', array('course' => $SITE->id, 'baseurl' => '', 'createdby' => $USER->id, + 'timecreated' => time(), 'timemodified' => time())); + $configid1 = $DB->insert_record('lti_types_config', array('typeid' => $toolid1, 'name' => 'customparameters', + 'value' => $custom1)); + $toolid2 = $DB->insert_record('lti_types', array('course' => $SITE->id, 'baseurl' => '', 'createdby' => $USER->id, + 'timecreated' => time(), 'timemodified' => time())); + $configid2 = $DB->insert_record('lti_types_config', array('typeid' => $toolid2, 'name' => 'customparameters', + 'value' => $custom2)); + + // Create 2 instances with custom parameters. + $activity1 = $ltigenerator->create_instance(array('course' => $SITE->id, 'name' => 'LTI activity 1', + 'typeid' => $toolid1, 'toolurl' => '', 'instructorcustomparameters' => $custom1)); + $activity2 = $ltigenerator->create_instance(array('course' => $SITE->id, 'name' => 'LTI activity 2', + 'typeid' => $toolid2, 'toolurl' => '', 'instructorcustomparameters' => $custom2)); + + // Run upgrade script. + mod_lti_upgrade_custom_separator(); + + // Check semicolon-separated custom parameters have been updated but others have not. + $config = $DB->get_record('lti_types_config', array('id' => $configid1)); + $this->assertEquals($config->value, $custom2); + + $config = $DB->get_record('lti_types_config', array('id' => $configid2)); + $this->assertEquals($config->value, $custom2); + + $config = $DB->get_record('lti', array('id' => $activity1->id)); + $this->assertEquals($config->instructorcustomparameters, $custom2); + + $config = $DB->get_record('lti', array('id' => $activity2->id)); + $this->assertEquals($config->instructorcustomparameters, $custom2); + } + +} diff --git a/mod/lti/toolproxies.php b/mod/lti/toolproxies.php new file mode 100644 index 00000000000..7926ef94ce0 --- /dev/null +++ b/mod/lti/toolproxies.php @@ -0,0 +1,193 @@ +. + +/** + * This file contains all necessary code to launch a Tool Proxy registration + * + * @package mod_lti + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once('../../config.php'); +require_once($CFG->libdir.'/adminlib.php'); +require_once($CFG->dirroot.'/mod/lti/locallib.php'); + +// No guest autologin. +require_login(0, false); + +$pageurl = new moodle_url('/mod/lti/toolproxies.php'); +$PAGE->set_url($pageurl); + +admin_externalpage_setup('ltitoolproxies'); + +$PAGE->set_title("{$SITE->shortname}: " . get_string('toolregistration', 'lti')); + +$configuredtoolproxieshtml = ''; +$pendingtoolproxieshtml = ''; +$acceptedtoolproxieshtml = ''; +$rejectedtoolproxieshtml = ''; + +$configured = get_string('configured', 'lti'); +$pending = get_string('pending', 'lti'); +$accepted = get_string('accepted', 'lti'); +$rejected = get_string('rejected', 'lti'); + +$name = get_string('name', 'lti'); +$url = get_string('registrationurl', 'lti'); +$action = get_string('action', 'lti'); +$createdon = get_string('createdon', 'lti'); + +$toolproxies = $DB->get_records('lti_tool_proxies'); + +$configuredtoolproxies = lti_filter_tool_proxy_types($toolproxies, LTI_TOOL_PROXY_STATE_CONFIGURED); +$configuredtoolproxieshtml = lti_get_tool_proxy_table($configuredtoolproxies, 'tp_configured'); + +$pendingtoolproxies = lti_filter_tool_proxy_types($toolproxies, LTI_TOOL_PROXY_STATE_PENDING); +$pendingtoolproxieshtml = lti_get_tool_proxy_table($pendingtoolproxies, 'tp_pending'); + +$acceptedtoolproxies = lti_filter_tool_proxy_types($toolproxies, LTI_TOOL_PROXY_STATE_ACCEPTED); +$acceptedtoolproxieshtml = lti_get_tool_proxy_table($acceptedtoolproxies, 'tp_accepted'); + +$rejectedtoolproxies = lti_filter_tool_proxy_types($toolproxies, LTI_TOOL_PROXY_STATE_REJECTED); +$rejectedtoolproxieshtml = lti_get_tool_proxy_table($rejectedtoolproxies, 'tp_rejected'); + +$tab = optional_param('tab', '', PARAM_ALPHAEXT); +$configuredselected = ''; +$pendingselected = ''; +$acceptedselected = ''; +$rejectedselected = ''; +switch ($tab) { + case 'tp_pending': + $pendingselected = 'class="selected"'; + break; + case 'tp_accepted': + $acceptedselected = 'class="selected"'; + break; + case 'tp_rejected': + $rejectedselected = 'class="selected"'; + break; + default: + $configuredselected = 'class="selected"'; + break; +} +$registertype = get_string('registertype', 'lti'); +$config = get_string('manage_tools', 'lti'); + +$registertypeurl = "{$CFG->wwwroot}/mod/lti/registersettings.php?action=add&sesskey={$USER->sesskey}&tab=tool_proxy"; + +$template = <<< EOD +
    + +
    +
    + + $configuredtoolproxieshtml +
    +
    + $pendingtoolproxieshtml +
    +
    + $acceptedtoolproxieshtml +
    +
    + $rejectedtoolproxieshtml +
    +
    +
    + + +EOD; + +echo $OUTPUT->header(); +echo $OUTPUT->heading(get_string('manage_tool_proxies', 'lti'), 2); +echo $OUTPUT->heading(new lang_string('toolproxy', 'lti') . + $OUTPUT->help_icon('toolproxy', 'lti'), 3); + +echo $OUTPUT->box_start('generalbox'); + +echo $template; + +echo $OUTPUT->box_end(); +echo $OUTPUT->footer(); diff --git a/mod/lti/toolssettings.php b/mod/lti/toolssettings.php new file mode 100644 index 00000000000..144ea395110 --- /dev/null +++ b/mod/lti/toolssettings.php @@ -0,0 +1,102 @@ +. + +/** + * This file contains the script used to register a new external tool. + * + * It is used to create a new form used to configure the capabilities + * and services to be offered to the tool provider. + * + * @package mod_lti + * @copyright 2014 Vital Source Technologies http://vitalsource.com + * @author Stephen Vickers + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once('../../config.php'); +require_once($CFG->libdir.'/adminlib.php'); +require_once($CFG->dirroot.'/mod/lti/edit_form.php'); +require_once($CFG->dirroot.'/mod/lti/locallib.php'); + +$action = optional_param('action', '', PARAM_ALPHANUMEXT); +$id = optional_param('id', '', PARAM_INT); +$tab = optional_param('tab', '', PARAM_ALPHAEXT); + +// No guest autologin. +require_login(0, false); + +require_sesskey(); + +// Check this is for a tool created from a tool proxy. +$err = empty($id); +if (!$err) { + $type = lti_get_type_type_config($id); + $err = empty($type->toolproxyid); +} +if ($err) { + $redirect = new moodle_url('/mod/lti/typessettings.php', + array('action' => $action, 'id' => $id, 'sesskey' => sesskey(), 'tab' => $tab)); + redirect($redirect); +} + +$pageurl = new moodle_url('/mod/lti/toolssettings.php'); +if (!empty($id)) { + $pageurl->param('id', $id); +} +$PAGE->set_url($pageurl); + +admin_externalpage_setup('managemodules'); // Hacky solution for printing the admin page. + +$redirect = "$CFG->wwwroot/$CFG->admin/settings.php?section=modsettinglti&tab={$tab}"; + +if ($action == 'accept') { + lti_set_state_for_type($id, LTI_TOOL_STATE_CONFIGURED); + redirect($redirect); +} else if (($action == 'reject') || ($action == 'delete')) { + lti_set_state_for_type($id, LTI_TOOL_STATE_REJECTED); + redirect($redirect); +} + +$form = new mod_lti_edit_types_form($pageurl, (object)array('isadmin' => true, 'istool' => true)); + +if ($data = $form->get_data()) { + $type = new stdClass(); + if (!empty($id)) { + $type->id = $id; + lti_update_type($type, $data); + } else { + $type->state = LTI_TOOL_STATE_CONFIGURED; + lti_add_type($type, $data); + } + redirect($redirect); +} else if ($form->is_cancelled()) { + redirect($redirect); +} + +$PAGE->set_title(format_string($SITE->shortname) . ': ' . get_string('toolsetup', 'lti')); +$PAGE->navbar->add(get_string('lti_administration', 'lti'), $CFG->wwwroot.'/'.$CFG->admin.'/settings.php?section=modsettinglti'); + +echo $OUTPUT->header(); +echo $OUTPUT->heading(get_string('toolsetup', 'lti')); +echo $OUTPUT->box_start('generalbox'); + +if ($action == 'update') { + $form->set_data($type); +} + +$form->display(); +echo $OUTPUT->box_end(); +echo $OUTPUT->footer(); diff --git a/mod/lti/typessettings.php b/mod/lti/typessettings.php index ab5895a2488..582e564b2f1 100644 --- a/mod/lti/typessettings.php +++ b/mod/lti/typessettings.php @@ -30,10 +30,11 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * This file contains the script used to clone Moodle admin setting page. + * * It is used to create a new form used to pre-configure lti activities * * @package mod_lti @@ -56,21 +57,34 @@ $action = optional_param('action', null, PARAM_ALPHANUMEXT); $id = optional_param('id', null, PARAM_INT); $tab = optional_param('tab', '', PARAM_ALPHAEXT); -// no guest autologin +// No guest autologin. require_login(0, false); +require_sesskey(); + +// Check this is not for a tool created from a tool proxy. +if (!empty($id)) { + $type = lti_get_type_type_config($id); + if (!empty($type->toolproxyid)) { + $sesskey = required_param('sesskey', PARAM_RAW); + $redirect = new moodle_url('/mod/lti/toolssettings.php', + array('action' => $action, 'id' => $id, 'sesskey' => $sesskey, 'tab' => $tab)); + redirect($redirect); + } +} else { + $type = new stdClass(); +} + $pageurl = new moodle_url('/mod/lti/typessettings.php'); if (!empty($id)) { $pageurl->param('id', $id); } $PAGE->set_url($pageurl); -admin_externalpage_setup('managemodules'); // Hacky solution for printing the admin page +admin_externalpage_setup('managemodules'); // Hacky solution for printing the admin page. $redirect = "$CFG->wwwroot/$CFG->admin/settings.php?section=modsettinglti&tab={$tab}"; -require_sesskey(); - if ($action == 'accept') { lti_set_state_for_type($id, LTI_TOOL_STATE_CONFIGURED); redirect($redirect); @@ -82,20 +96,17 @@ if ($action == 'accept') { redirect($redirect); } -$form = new mod_lti_edit_types_form($pageurl, (object)array('isadmin' => true)); +$form = new mod_lti_edit_types_form($pageurl, (object)array('isadmin' => true, 'istool' => false)); if ($data = $form->get_data()) { $type = new stdClass(); - if (!empty($id)) { $type->id = $id; - lti_update_type($type, $data); redirect($redirect); } else { $type->state = LTI_TOOL_STATE_CONFIGURED; - lti_add_type($type, $data); redirect($redirect); @@ -112,7 +123,6 @@ echo $OUTPUT->heading(get_string('toolsetup', 'lti')); echo $OUTPUT->box_start('generalbox'); if ($action == 'update') { - $type = lti_get_type_type_config($id); $form->set_data($type); } diff --git a/mod/lti/upgrade.txt b/mod/lti/upgrade.txt index dda6f1f5069..825f27173f7 100644 --- a/mod/lti/upgrade.txt +++ b/mod/lti/upgrade.txt @@ -9,3 +9,8 @@ This files describes API changes in the lti code. * mod_lti\event\unknown_service_api_called now has less data stored in 'other' but everything is still available for event observers via method get_message_data() + +=== 2.8 === + +* Support for LTI 2 added, including extensible services implemented as ltiservice plugins. +* Function sendOAuthBodyPOST removed from OAuthBody.php because it was not being used. diff --git a/mod/lti/version.php b/mod/lti/version.php index a20a0e60c7e..70caf223a5f 100644 --- a/mod/lti/version.php +++ b/mod/lti/version.php @@ -30,7 +30,7 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * This file defines the version of lti @@ -48,7 +48,7 @@ defined('MOODLE_INTERNAL') || die; -$plugin->version = 2014060201; // The current module version (Date: YYYYMMDDXX) -$plugin->requires = 2014050800; // Requires this Moodle version -$plugin->component = 'mod_lti'; // Full name of the plugin (used for diagnostics) +$plugin->version = 2014100300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2014050800; // Requires this Moodle version. +$plugin->component = 'mod_lti'; // Full name of the plugin (used for diagnostics). $plugin->cron = 0; diff --git a/mod/lti/view.php b/mod/lti/view.php index 874ac0a1344..4d0b8c41385 100644 --- a/mod/lti/view.php +++ b/mod/lti/view.php @@ -30,7 +30,7 @@ // // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis // of the Universitat Politecnica de Catalunya http://www.upc.edu -// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. /** * This file contains all necessary code to view a lti activity instance @@ -52,9 +52,9 @@ require_once($CFG->dirroot.'/mod/lti/lib.php'); require_once($CFG->dirroot.'/mod/lti/locallib.php'); $id = optional_param('id', 0, PARAM_INT); // Course Module ID, or -$l = optional_param('l', 0, PARAM_INT); // lti ID +$l = optional_param('l', 0, PARAM_INT); // lti ID. -if ($l) { // Two ways to specify the module +if ($l) { // Two ways to specify the module. $lti = $DB->get_record('lti', array('id' => $l), '*', MUST_EXIST); $cm = get_coursemodule_from_instance('lti', $lti->id, $lti->course, false, MUST_EXIST); @@ -72,21 +72,21 @@ if ($tool) { $toolconfig = array(); } -$PAGE->set_cm($cm, $course); // set's up global $COURSE +$PAGE->set_cm($cm, $course); // Set's up global $COURSE. $context = context_module::instance($cm->id); $PAGE->set_context($context); require_login($course, true, $cm); require_capability('mod/lti:view', $context); -$url = new moodle_url('/mod/lti/view.php', array('id'=>$cm->id)); +$url = new moodle_url('/mod/lti/view.php', array('id' => $cm->id)); $PAGE->set_url($url); $launchcontainer = lti_get_launch_container($lti, $toolconfig); if ($launchcontainer == LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS) { - $PAGE->set_pagelayout('frametop'); //Most frametops don't include footer, and pre-post blocks - $PAGE->blocks->show_only_fake_blocks(); //Disable blocks for layouts which do include pre-post blocks + $PAGE->set_pagelayout('frametop'); // Most frametops don't include footer, and pre-post blocks. + $PAGE->blocks->show_only_fake_blocks(); // Disable blocks for layouts which do include pre-post blocks. } else if ($launchcontainer == LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW) { redirect('launch.php?id=' . $cm->id); } else { @@ -111,11 +111,11 @@ $pagetitle = strip_tags($course->shortname.': '.format_string($lti->name)); $PAGE->set_title($pagetitle); $PAGE->set_heading($course->fullname); -// Print the page header +// Print the page header. echo $OUTPUT->header(); if ($lti->showtitlelaunch) { - // Print the main part of the page + // Print the main part of the page. echo $OUTPUT->heading(format_string($lti->name, true, array('context' => $context))); } @@ -133,7 +133,7 @@ if ( $launchcontainer == LTI_LAUNCH_CONTAINER_WINDOW ) { // Request the launch content with an iframe tag. echo ''; - // Output script to make the iframe be as large as possible. + // Output script to make the iframe tag be as large as possible. $resize = '