mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 22:08:20 +01:00
MDL-44902: Several additions to External Tool (LTI)
* LTI service related changes: ** Fixing exceptions in OAuth library. ** Added new launch option, Existing window: replaces entire page with the LTI object. ** The LTI tool ID used to perform the launch is now sent with the LTI launch parameters. This is sent back to Moodle on subsequent requests. ** Added $CFG->mod_lti_forcessl to force SSL on all LTI launches. ** Added new LTI launch parameter: tool_consumer_instance_name. Default value is site full name, but can be customized with $CFG->mod_lti_institution_name. ** The LTI grade service endpoints now set the affected user to the session. This was required for event listeners. ** Fix the grade deletion service. Was deleting the grade item instead of just the grade. ** Send error response when LTI instance does not accept grades and grades are being sent. ** Added a method for writing incoming LTI requests to disk for debugging. Disabled by default. * Changes for ltisource plugins: ** Can now to plug into backup/restore. ** Can now have settings.php files. ** Can now hook into the LTI launch and edit parameters. * Several grade changes: ** Added standard_grading_coursemodule_elements to LTI instance edit form. This means LTI instances can be configured with a grade. ** No longer assumes that grade is out of 100. ** Replaced modl/lti:grade capability with mod/lti:view. * JS on mod/lti/view.php for resizing the content object has been converted to YUI3. * Fixed misspellings in language file. * Added hooks for log post and view actions. * Bug fix for lti_get_url_thumbprint() when the URL is missing a schema.
This commit is contained in:
parent
f500ff4e52
commit
8fa50fdd34
@ -181,7 +181,7 @@ class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
|
||||
// (3) some sort of specific discovery code based on request
|
||||
//
|
||||
// either way should return a string representation of the certificate
|
||||
throw OAuthException("fetch_public_cert not implemented");
|
||||
throw new OAuthException("fetch_public_cert not implemented");
|
||||
}
|
||||
|
||||
protected function fetch_private_cert(&$request) {
|
||||
@ -189,7 +189,7 @@ class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
|
||||
// (1) do a lookup in a table of trusted certs keyed off of consumer
|
||||
//
|
||||
// either way should return a string representation of the certificate
|
||||
throw OAuthException("fetch_private_cert not implemented");
|
||||
throw new OAuthException("fetch_private_cert not implemented");
|
||||
}
|
||||
|
||||
public function build_signature(&$request, $consumer, $token) {
|
||||
|
@ -114,7 +114,7 @@ function handleOAuthBodyPOST($oauth_consumer_key, $oauth_consumer_secret, $body,
|
||||
|
||||
try {
|
||||
$server->verify_request($request);
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
throw new OAuthException("OAuth signature failed: " . $message);
|
||||
}
|
||||
|
@ -99,6 +99,9 @@ class backup_lti_activity_structure_step extends backup_activity_structure_step
|
||||
// Define file annotations
|
||||
$lti->annotate_files('mod_lti', 'intro', null); // This file areas haven't itemid
|
||||
|
||||
// Add support for subplugin structure.
|
||||
$this->add_subplugin_structure('ltisource', $lti, true);
|
||||
|
||||
// Return the root element (lti), wrapped into standard activity structure
|
||||
return $this->prepare_activity_structure($lti);
|
||||
}
|
||||
|
@ -47,7 +47,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/mod/lti/backup/moodle2/restore_lti_stepslib.php'); // Because it exists (must)
|
||||
require_once($CFG->dirroot . '/mod/lti/backup/moodle2/restore_lti_stepslib.php');
|
||||
|
||||
/**
|
||||
* basiclti restore task that provides all the settings and steps to perform one
|
||||
@ -59,14 +59,14 @@ class restore_lti_activity_task extends restore_activity_task {
|
||||
* Define (add) particular settings this activity can have
|
||||
*/
|
||||
protected function define_my_settings() {
|
||||
// No particular settings for this activity
|
||||
// No particular settings for this activity.
|
||||
}
|
||||
|
||||
/**
|
||||
* Define (add) particular steps this activity can have
|
||||
*/
|
||||
protected function define_my_steps() {
|
||||
// label only has one structure step
|
||||
// Label only has one structure step.
|
||||
$this->add_step(new restore_lti_activity_structure_step('lti_structure', 'lti.xml'));
|
||||
}
|
||||
|
||||
@ -129,4 +129,13 @@ class restore_lti_activity_task extends restore_activity_task {
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for ltisource plugins.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_old_moduleid() {
|
||||
return $this->oldmoduleid;
|
||||
}
|
||||
}
|
||||
|
@ -56,9 +56,13 @@ class restore_lti_activity_structure_step extends restore_activity_structure_ste
|
||||
protected function define_structure() {
|
||||
|
||||
$paths = array();
|
||||
$paths[] = new restore_path_element('lti', '/activity/lti');
|
||||
$lti = new restore_path_element('lti', '/activity/lti');
|
||||
$paths[] = $lti;
|
||||
|
||||
// Return the paths wrapped into standard activity structure
|
||||
// Add support for subplugin structure.
|
||||
$this->add_subplugin_structure('ltisource', $lti);
|
||||
|
||||
// Return the paths wrapped into standard activity structure.
|
||||
return $this->prepare_activity_structure($paths);
|
||||
}
|
||||
|
||||
@ -78,12 +82,12 @@ class restore_lti_activity_structure_step extends restore_activity_structure_ste
|
||||
|
||||
$newitemid = $DB->insert_record('lti', $data);
|
||||
|
||||
// immediately after inserting "activity" record, call this
|
||||
// Immediately after inserting "activity" record, call this.
|
||||
$this->apply_activity_instance($newitemid);
|
||||
}
|
||||
|
||||
protected function after_execute() {
|
||||
// Add lti related files, no need to match by itemname (just internally handled context)
|
||||
// Add lti related files, no need to match by itemname (just internally handled context).
|
||||
$this->add_related_files('mod_lti', 'intro', null);
|
||||
}
|
||||
}
|
||||
|
118
mod/lti/classes/factory.php
Normal file
118
mod/lti/classes/factory.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class Builder
|
||||
*
|
||||
* @package mod
|
||||
* @subpackage lti
|
||||
* @copyright Copyright (c) 2009 Moodlerooms Inc. (http://www.moodlerooms.com)
|
||||
* @license http://opensource.org/licenses/gpl-3.0.html GNU Public License
|
||||
*/
|
||||
|
||||
namespace mod_lti;
|
||||
|
||||
use coding_exception;
|
||||
use mod_lti\observer\dispatcher;
|
||||
|
||||
/**
|
||||
* Builds various classes
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright Copyright (c) 2009 Moodlerooms Inc. (http://www.moodlerooms.com)
|
||||
* @license http://opensource.org/licenses/gpl-3.0.html GNU Public License
|
||||
*/
|
||||
class factory {
|
||||
/**
|
||||
* Given a component and class name suffix, create a full
|
||||
* class name and ensure that it exists.
|
||||
*
|
||||
* Classes are namespace based.
|
||||
*
|
||||
* @param string $component The component to find the class file in
|
||||
* @param string $suffix This is appended to the component name, together make the class name we want
|
||||
* @return string
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
protected function build_class_name($component, $suffix) {
|
||||
$classname = "\\$component\\$suffix";
|
||||
if (!class_exists($classname)) {
|
||||
throw new coding_exception("Expected to find $classname in the classes directory of $component");
|
||||
}
|
||||
return $classname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a class and optionally verify parent class
|
||||
*
|
||||
* @param string $class Create a new instance of this class
|
||||
* @param null|string $parent Ensure that this is the parent class
|
||||
* @return mixed
|
||||
* @throws coding_exception
|
||||
*/
|
||||
protected function build_generic_instance($class, $parent = null) {
|
||||
if (!is_null($parent)) {
|
||||
$reflection = new \ReflectionClass($class);
|
||||
if (!$reflection->isSubclassOf($parent)) {
|
||||
throw new coding_exception("The $class must be a subclass of $parent");
|
||||
}
|
||||
}
|
||||
return new $class();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a single ltisource plugin listener
|
||||
*
|
||||
* @param string $component The component to find the class file in
|
||||
* @return \mod_lti\observer\listener_interface
|
||||
*/
|
||||
public function build_listener($component) {
|
||||
return $this->build_generic_instance(
|
||||
$this->build_class_name($component, 'listener'),
|
||||
'\mod_lti\observer\listener_interface'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds ltisource plugin listeners
|
||||
*
|
||||
* @return \mod_lti\observer\listener_interface[]
|
||||
*/
|
||||
public function build_listeners() {
|
||||
$plugins = \core_component::get_plugin_list('ltisource');
|
||||
$listeners = array();
|
||||
foreach (array_keys($plugins) as $pluginname) {
|
||||
try {
|
||||
$listeners[] = $this->build_listener('ltisource_'.$pluginname);
|
||||
} catch (\Exception $e) {
|
||||
// Class is optional, so ignore if not found.
|
||||
}
|
||||
}
|
||||
return $listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an event dispatcher with listeners.
|
||||
*
|
||||
* @return dispatcher
|
||||
*/
|
||||
public function build_dispatcher() {
|
||||
$dispatcher = new dispatcher();
|
||||
$dispatcher->set_listeners($this->build_listeners());
|
||||
|
||||
return $dispatcher;
|
||||
}
|
||||
}
|
69
mod/lti/classes/observer/before_launch_event.php
Normal file
69
mod/lti/classes/observer/before_launch_event.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Event for ltisource plugins.
|
||||
*
|
||||
* @package mod
|
||||
* @subpackage lti
|
||||
* @copyright Copyright (c) 2009 Moodlerooms Inc. (http://www.moodlerooms.com)
|
||||
* @license http://opensource.org/licenses/gpl-3.0.html GNU Public License
|
||||
*/
|
||||
|
||||
namespace mod_lti\observer;
|
||||
|
||||
/**
|
||||
* This event occurs prior to an LTI launch.
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright Copyright (c) 2009 Moodlerooms Inc. (http://www.moodlerooms.com)
|
||||
* @license http://opensource.org/licenses/gpl-3.0.html GNU Public License
|
||||
*/
|
||||
class before_launch_event {
|
||||
/**
|
||||
* LTI activity instance
|
||||
*
|
||||
* @var \stdClass
|
||||
*/
|
||||
public $instance;
|
||||
|
||||
/**
|
||||
* Launch URL
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $endpoint;
|
||||
|
||||
/**
|
||||
* Launch request parameters
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $params;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \stdClass $instance
|
||||
* @param string $endpoint
|
||||
* @param array $params
|
||||
*/
|
||||
public function __construct($instance, $endpoint, array $params) {
|
||||
$this->instance = $instance;
|
||||
$this->endpoint = $endpoint;
|
||||
$this->params = $params;
|
||||
}
|
||||
}
|
87
mod/lti/classes/observer/dispatcher.php
Normal file
87
mod/lti/classes/observer/dispatcher.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Dispatches events to ltisource plugin listeners
|
||||
*
|
||||
* @package mod
|
||||
* @subpackage lti
|
||||
* @copyright Copyright (c) 2009 Moodlerooms Inc. (http://www.moodlerooms.com)
|
||||
* @license http://opensource.org/licenses/gpl-3.0.html GNU Public License
|
||||
*/
|
||||
|
||||
namespace mod_lti\observer;
|
||||
|
||||
/**
|
||||
* Event dispatcher
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright Copyright (c) 2009 Moodlerooms Inc. (http://www.moodlerooms.com)
|
||||
* @license http://opensource.org/licenses/gpl-3.0.html GNU Public License
|
||||
*/
|
||||
class dispatcher {
|
||||
/**
|
||||
* @var listener_interface[]
|
||||
*/
|
||||
protected $listeners = array();
|
||||
|
||||
/**
|
||||
* Add a listener
|
||||
*
|
||||
* @param listener_interface $listener
|
||||
*/
|
||||
public function add_listener(listener_interface $listener) {
|
||||
$this->listeners[] = $listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a list of listeners
|
||||
*
|
||||
* @param listener_interface[] $listeners
|
||||
*/
|
||||
public function set_listeners($listeners) {
|
||||
$this->listeners = array();
|
||||
foreach ($listeners as $listener) {
|
||||
$this->add_listener($listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an event.
|
||||
*
|
||||
* Very trivial right now.
|
||||
*
|
||||
* @param string $name Event name
|
||||
* @param mixed $event The event object
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public function dispatch($name, $event) {
|
||||
foreach ($this->listeners as $listener) {
|
||||
$subscribed = $listener->get_subscribed_events();
|
||||
|
||||
if (!array_key_exists($name, $subscribed)) {
|
||||
continue;
|
||||
}
|
||||
$callable = array($listener, $subscribed[$name]);
|
||||
if (!is_callable($callable)) {
|
||||
throw new \coding_exception(
|
||||
sprintf('The method %s is not callable on %s class', $subscribed[$name], get_class($listener))
|
||||
);
|
||||
}
|
||||
call_user_func($callable, $event);
|
||||
}
|
||||
}
|
||||
}
|
44
mod/lti/classes/observer/events.php
Normal file
44
mod/lti/classes/observer/events.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Defines internal events that ltisource
|
||||
* plugins can subscribe to.
|
||||
*
|
||||
* @package mod
|
||||
* @subpackage lti
|
||||
* @copyright Copyright (c) 2009 Moodlerooms Inc. (http://www.moodlerooms.com)
|
||||
* @license http://opensource.org/licenses/gpl-3.0.html GNU Public License
|
||||
*/
|
||||
|
||||
namespace mod_lti\observer;
|
||||
|
||||
/**
|
||||
* These are all of the event names
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright Copyright (c) 2009 Moodlerooms Inc. (http://www.moodlerooms.com)
|
||||
* @license http://opensource.org/licenses/gpl-3.0.html GNU Public License
|
||||
*/
|
||||
final class events {
|
||||
/**
|
||||
* This is thrown before an LTI launch
|
||||
*
|
||||
* The event listener will recevie an instance
|
||||
* of \mod_lti\observer\before_launch_event
|
||||
*/
|
||||
const BEFORE_LAUNCH = 'before.launch';
|
||||
}
|
47
mod/lti/classes/observer/listener_interface.php
Normal file
47
mod/lti/classes/observer/listener_interface.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Listener interface for ltisource plugins
|
||||
*
|
||||
* @package mod
|
||||
* @subpackage lti
|
||||
* @copyright Copyright (c) 2009 Moodlerooms Inc. (http://www.moodlerooms.com)
|
||||
* @license http://opensource.org/licenses/gpl-3.0.html GNU Public License
|
||||
*/
|
||||
|
||||
namespace mod_lti\observer;
|
||||
|
||||
/**
|
||||
* This interface allows a class to define the ltisource
|
||||
* plugin events that the class would like to subscribe
|
||||
* to
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright Copyright (c) 2009 Moodlerooms Inc. (http://www.moodlerooms.com)
|
||||
* @license http://opensource.org/licenses/gpl-3.0.html GNU Public License
|
||||
*/
|
||||
interface listener_interface {
|
||||
/**
|
||||
* Register for events
|
||||
*
|
||||
* Example return:
|
||||
* array('eventname' => 'methodToCall');
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_subscribed_events();
|
||||
}
|
@ -56,20 +56,6 @@ $capabilities = array(
|
||||
'clonepermissionsfrom' => 'moodle/course:manageactivities'
|
||||
),
|
||||
|
||||
// Controls access to the grade.php script, which shows all the submissions
|
||||
// made to the external tool that have been reported back to Moodle.
|
||||
'mod/lti:grade' => array(
|
||||
'riskbitmask' => RISK_PERSONAL,
|
||||
|
||||
'captype' => 'write',
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'archetypes' => array(
|
||||
'teacher' => CAP_ALLOW,
|
||||
'editingteacher' => CAP_ALLOW,
|
||||
'manager' => CAP_ALLOW
|
||||
)
|
||||
),
|
||||
|
||||
// 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,
|
||||
// otherwise they are given Learner. See the lti_get_ims_role function.
|
||||
|
@ -54,6 +54,8 @@ require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
||||
|
||||
class mod_lti_edit_types_form extends moodleform{
|
||||
public function definition() {
|
||||
global $CFG;
|
||||
|
||||
$mform =& $this->_form;
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
@ -96,6 +98,7 @@ class mod_lti_edit_types_form extends moodleform{
|
||||
$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');
|
||||
$launchoptions[LTI_LAUNCH_CONTAINER_WINDOW] = get_string('new_window', 'lti');
|
||||
|
||||
$mform->addElement('select', 'lti_launchcontainer', get_string('default_launch_container', 'lti'), $launchoptions);
|
||||
@ -137,7 +140,12 @@ class mod_lti_edit_types_form extends moodleform{
|
||||
|
||||
$mform->addElement('checkbox', 'lti_forcessl', ' ', ' ' . get_string('force_ssl', 'lti'), $options);
|
||||
$mform->setType('lti_forcessl', PARAM_BOOL);
|
||||
$mform->setDefault('lti_forcessl', '0');
|
||||
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)) {
|
||||
|
@ -68,7 +68,10 @@ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST)
|
||||
|
||||
require_login($course, false, $cm);
|
||||
$context = context_module::instance($cm->id);
|
||||
require_capability('mod/lti:grade', $context);
|
||||
require_capability('mod/lti:view', $context);
|
||||
|
||||
// Redirecting to lti view. May need to wrap this in config.
|
||||
redirect(new moodle_url('/mod/lti/view.php', array('l' => $lti->id)));
|
||||
|
||||
$url = new moodle_url('/mod/lti/grade.php', array('id' => $cm->id));
|
||||
if ($mode !== 'all') {
|
||||
|
@ -111,11 +111,11 @@ $string['debuglaunchon'] = 'Debug launch';
|
||||
$string['default'] = 'Default';
|
||||
$string['default_launch_container'] = 'Default Launch Container';
|
||||
$string['default_launch_container_help'] = 'The launch container affects the display of the tool when launched from the course. Some launch containers provide more screen
|
||||
real estate to the tool, and others provide a more integrated feel with the Moodle environemnt.
|
||||
real estate to the tool, and others provide a more integrated feel with the Moodle environment.
|
||||
|
||||
* **Default** - Use the launch container specified by the tool configuration.
|
||||
* **Embed** - The tool is displayed within the existing Moodle window, in a manner similar to most other Activity types.
|
||||
* **Embed, without blocks** - The tool is displayed within the existing Moodle window, with just the neavigation controls
|
||||
* **Embed, without blocks** - The tool is displayed within the existing Moodle window, with just the navigation controls
|
||||
at the top of the page.
|
||||
* **New window** - The tool opens in a new window, occupying all the available space.
|
||||
Depending on the browser, it will open in a new tab or a popup window.
|
||||
@ -147,6 +147,7 @@ $string['embed_no_blocks'] = 'Embed, without blocks';
|
||||
$string['enableemailnotification'] = 'Send notification emails';
|
||||
$string['enableemailnotification_help'] = 'If enabled, students will receive email notification when their tool submissions are graded.';
|
||||
$string['errormisconfig'] = 'Misconfigured tool. Please ask your Moodle administrator to fix the configuration of the tool.';
|
||||
$string['existing_window'] = 'Existing window';
|
||||
$string['extensions'] = 'LTI Extension Services';
|
||||
$string['external_tool_type'] = 'External tool type';
|
||||
$string['external_tool_type_help'] = 'The main purpose of a tool configuration is to set up a secure communication channel between Moodle and the tool provider.
|
||||
@ -205,11 +206,11 @@ If you have selected a specific tool type, you may not need to enter a Launch UR
|
||||
into the tool provider\'s system, and not go to a specific resource, this will likely be the case.';
|
||||
$string['launchinpopup'] = 'Launch Container';
|
||||
$string['launchinpopup_help'] = 'The launch container affects the display of the tool when launched from the course. Some launch containers provide more screen
|
||||
real estate to the tool, and others provide a more integrated feel with the Moodle environemnt.
|
||||
real estate to the tool, and others provide a more integrated feel with the Moodle environment.
|
||||
|
||||
* **Default** - Use the launch container specified by the tool configuration.
|
||||
* **Embed** - The tool is displayed within the existing Moodle window, in a manner similar to most other Activity types.
|
||||
* **Embed, without blocks** - The tool is displayed within the existing Moodle window, with just the neavigation controls
|
||||
* **Embed, without blocks** - The tool is displayed within the existing Moodle window, with just the navigation controls
|
||||
at the top of the page.
|
||||
* **New window** - The tool opens in a new window, occupying all the available space.
|
||||
Depending on the browser, it will open in a new tab or a popup window.
|
||||
|
@ -95,17 +95,20 @@ function lti_add_instance($lti, $mform) {
|
||||
$lti->timemodified = $lti->timecreated;
|
||||
$lti->servicesalt = uniqid('', true);
|
||||
|
||||
lti_force_type_config_settings($lti, lti_get_type_config_by_instance($lti));
|
||||
|
||||
if (empty($lti->typeid) && isset($lti->urlmatchedtypeid)) {
|
||||
$lti->typeid = $lti->urlmatchedtypeid;
|
||||
}
|
||||
|
||||
if (!isset($lti->grade)) {
|
||||
$lti->grade = 100; // TODO: Why is this harcoded here and default @ DB
|
||||
if (!isset($lti->instructorchoiceacceptgrades) || $lti->instructorchoiceacceptgrades != LTI_SETTING_ALWAYS) {
|
||||
// The instance does not accept grades back from the provider, so set to "No grade" value 0.
|
||||
$lti->grade = 0;
|
||||
}
|
||||
|
||||
$lti->id = $DB->insert_record('lti', $lti);
|
||||
|
||||
if ($lti->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS) {
|
||||
if (isset($lti->instructorchoiceacceptgrades) && $lti->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS) {
|
||||
if (!isset($lti->cmidnumber)) {
|
||||
$lti->cmidnumber = '';
|
||||
}
|
||||
@ -139,13 +142,15 @@ function lti_update_instance($lti, $mform) {
|
||||
$lti->showdescriptionlaunch = 0;
|
||||
}
|
||||
|
||||
if (!isset($lti->grade)) {
|
||||
$lti->grade = $DB->get_field('lti', 'grade', array('id' => $lti->id));
|
||||
}
|
||||
lti_force_type_config_settings($lti, lti_get_type_config_by_instance($lti));
|
||||
|
||||
if ($lti->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS) {
|
||||
if (isset($lti->instructorchoiceacceptgrades) && $lti->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS) {
|
||||
lti_grade_item_update($lti);
|
||||
} else {
|
||||
// Instance is no longer accepting grades from Provider, set grade to "No grade" value 0.
|
||||
$lti->grade = 0;
|
||||
$lti->instructorchoiceacceptgrades = 0;
|
||||
|
||||
lti_grade_item_delete($lti);
|
||||
}
|
||||
|
||||
@ -468,7 +473,7 @@ function lti_grade_item_delete($basiclti) {
|
||||
function lti_extend_settings_navigation($settings, $parentnode) {
|
||||
global $PAGE;
|
||||
|
||||
if (has_capability('mod/lti:grade', context_module::instance($PAGE->cm->id))) {
|
||||
if (has_capability('mod/lti:manage', context_module::instance($PAGE->cm->id))) {
|
||||
$keys = $parentnode->get_children_key_list();
|
||||
|
||||
$node = navigation_node::create('Submissions',
|
||||
@ -478,3 +483,21 @@ function lti_extend_settings_navigation($settings, $parentnode) {
|
||||
$parentnode->add_node($node, $keys[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log post actions
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function lti_get_post_actions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Log view actions
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function lti_get_view_actions() {
|
||||
return array('view all', 'view');
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ function lti_view($instance) {
|
||||
$orgid = $typeconfig['organizationid'];
|
||||
|
||||
$course = $PAGE->course;
|
||||
$requestparams = lti_build_request($instance, $typeconfig, $course);
|
||||
$requestparams = lti_build_request($instance, $typeconfig, $course, $typeid);
|
||||
|
||||
$launchcontainer = lti_get_launch_container($instance, $typeconfig);
|
||||
$returnurlparams = array('course' => $course->id, 'launch_container' => $launchcontainer, 'instanceid' => $instance->id);
|
||||
@ -158,7 +158,11 @@ function lti_view($instance) {
|
||||
if ( $orgid ) {
|
||||
$requestparams["tool_consumer_instance_guid"] = $orgid;
|
||||
}
|
||||
|
||||
if (!empty($CFG->mod_lti_institution_name)) {
|
||||
$requestparams['tool_consumer_instance_name'] = $CFG->mod_lti_institution_name;
|
||||
} else {
|
||||
$requestparams['tool_consumer_instance_name'] = get_site()->fullname;
|
||||
}
|
||||
if (empty($key) || empty($secret)) {
|
||||
$returnurlparams['unsigned'] = '1';
|
||||
}
|
||||
@ -171,8 +175,33 @@ function lti_view($instance) {
|
||||
$returnurl = lti_ensure_url_is_https($returnurl);
|
||||
}
|
||||
|
||||
$target = null;
|
||||
switch($launchcontainer) {
|
||||
case LTI_LAUNCH_CONTAINER_EMBED:
|
||||
case LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS:
|
||||
$target = 'iframe';
|
||||
break;
|
||||
case LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW:
|
||||
$target = 'frame';
|
||||
break;
|
||||
case LTI_LAUNCH_CONTAINER_WINDOW:
|
||||
$target = 'window';
|
||||
break;
|
||||
}
|
||||
if (!is_null($target)) {
|
||||
$requestparams['launch_presentation_document_target'] = $target;
|
||||
}
|
||||
|
||||
$requestparams['launch_presentation_return_url'] = $returnurl;
|
||||
|
||||
$factory = new \mod_lti\factory();
|
||||
$dispatcher = $factory->build_dispatcher();
|
||||
$event = new \mod_lti\observer\before_launch_event($instance, $endpoint, $requestparams);
|
||||
$dispatcher->dispatch(\mod_lti\observer\events::BEFORE_LAUNCH, $event);
|
||||
|
||||
// Allow params to be updated.
|
||||
$requestparams = $event->params;
|
||||
|
||||
if (!empty($key) && !empty($secret)) {
|
||||
$parms = lti_sign_parameters($requestparams, $endpoint, "POST", $key, $secret);
|
||||
|
||||
@ -200,11 +229,22 @@ function lti_view($instance) {
|
||||
echo $content;
|
||||
}
|
||||
|
||||
function lti_build_sourcedid($instanceid, $userid, $launchid = null, $servicesalt) {
|
||||
/**
|
||||
* Build source ID
|
||||
*
|
||||
* @param int $instanceid
|
||||
* @param int $userid
|
||||
* @param string $servicesalt
|
||||
* @param null|int $typeid
|
||||
* @param null|int $launchid
|
||||
* @return stdClass
|
||||
*/
|
||||
function lti_build_sourcedid($instanceid, $userid, $servicesalt, $typeid = null, $launchid = null) {
|
||||
$data = new stdClass();
|
||||
|
||||
$data->instanceid = $instanceid;
|
||||
$data->userid = $userid;
|
||||
$data->typeid = $typeid;
|
||||
if (!empty($launchid)) {
|
||||
$data->launchid = $launchid;
|
||||
} else {
|
||||
@ -228,10 +268,11 @@ function lti_build_sourcedid($instanceid, $userid, $launchid = null, $servicesal
|
||||
* @param object $instance Basic LTI instance object
|
||||
* @param object $typeconfig Basic LTI tool configuration
|
||||
* @param object $course Course object
|
||||
* @param int|null $typeid Basic LTI tool ID
|
||||
*
|
||||
* @return array $request Request details
|
||||
*/
|
||||
function lti_build_request($instance, $typeconfig, $course) {
|
||||
function lti_build_request($instance, $typeconfig, $course, $typeid = null) {
|
||||
global $USER, $CFG;
|
||||
|
||||
if (empty($instance->cmid)) {
|
||||
@ -252,22 +293,30 @@ function lti_build_request($instance, $typeconfig, $course) {
|
||||
'launch_presentation_locale' => current_language()
|
||||
);
|
||||
|
||||
if (property_exists($instance, 'resource_link_id') and !empty($instance->resource_link_id)) {
|
||||
$requestparams['resource_link_id'] = $instance->resource_link_id;
|
||||
}
|
||||
$placementsecret = $instance->servicesalt;
|
||||
|
||||
if ( isset($placementsecret) ) {
|
||||
$sourcedid = json_encode(lti_build_sourcedid($instance->id, $USER->id, null, $placementsecret));
|
||||
$sourcedid = json_encode(lti_build_sourcedid($instance->id, $USER->id, $placementsecret, $typeid));
|
||||
$requestparams['lis_result_sourcedid'] = $sourcedid;
|
||||
}
|
||||
|
||||
if ( isset($placementsecret) &&
|
||||
( $typeconfig['acceptgrades'] == LTI_SETTING_ALWAYS ||
|
||||
( $typeconfig['acceptgrades'] == LTI_SETTING_DELEGATE && $instance->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS ) ) ) {
|
||||
$requestparams['lis_result_sourcedid'] = $sourcedid;
|
||||
|
||||
//Add outcome service URL
|
||||
$serviceurl = new moodle_url('/mod/lti/service.php');
|
||||
$serviceurl = $serviceurl->out();
|
||||
|
||||
if ($typeconfig['forcessl'] == '1') {
|
||||
$forcessl = false;
|
||||
if (!empty($CFG->mod_lti_forcessl)) {
|
||||
$forcessl = true;
|
||||
}
|
||||
|
||||
if ($typeconfig['forcessl'] == '1' or $forcessl) {
|
||||
$serviceurl = lti_ensure_url_is_https($serviceurl);
|
||||
}
|
||||
|
||||
@ -497,7 +546,7 @@ function lti_get_ims_role($user, $cmid, $courseid) {
|
||||
}
|
||||
|
||||
if (is_siteadmin($user)) {
|
||||
array_push($roles, 'urn:lti:sysrole:ims/lis/Administrator');
|
||||
array_push($roles, 'urn:lti:sysrole:ims/lis/Administrator', 'urn:lti:instrole:ims/lis/Administrator');
|
||||
}
|
||||
|
||||
return join(',', $roles);
|
||||
@ -638,6 +687,10 @@ function lti_get_tool_by_url_match($url, $courseid = null, $state = LTI_TOOL_STA
|
||||
}
|
||||
|
||||
function lti_get_url_thumbprint($url) {
|
||||
// Parse URL requires a schema otherwise everything goes into 'path'. Fixed 5.4.7 or later.
|
||||
if (preg_match('/https?:\/\//', $url) !== 1) {
|
||||
$url = 'http://'.$url;
|
||||
}
|
||||
$urlparts = parse_url(strtolower($url));
|
||||
if (!isset($urlparts['path'])) {
|
||||
$urlparts['path'] = '';
|
||||
@ -1173,3 +1226,103 @@ function lti_ensure_url_is_https($url) {
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if we should try to log the request
|
||||
*
|
||||
* @param string $rawbody
|
||||
* @return bool
|
||||
*/
|
||||
function lti_should_log_request($rawbody) {
|
||||
global $CFG;
|
||||
|
||||
if (empty($CFG->mod_lti_log_users)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$logusers = explode(',', $CFG->mod_lti_log_users);
|
||||
if (empty($logusers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$xml = new SimpleXMLElement($rawbody);
|
||||
$ns = $xml->getNamespaces();
|
||||
$ns = array_shift($ns);
|
||||
$xml->registerXPathNamespace('lti', $ns);
|
||||
$requestuserid = '';
|
||||
if ($node = $xml->xpath('//lti:userId')) {
|
||||
$node = $node[0];
|
||||
$requestuserid = clean_param((string) $node, PARAM_INT);
|
||||
} else if ($node = $xml->xpath('//lti:sourcedId')) {
|
||||
$node = $node[0];
|
||||
$resultjson = json_decode((string) $node);
|
||||
$requestuserid = clean_param($resultjson->data->userid, PARAM_INT);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($requestuserid) or !in_array($requestuserid, $logusers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the request to a file in temp dir
|
||||
*
|
||||
* @param string $rawbody
|
||||
*/
|
||||
function lti_log_request($rawbody) {
|
||||
if ($tempdir = make_temp_directory('mod_lti', false)) {
|
||||
if ($tempfile = tempnam($tempdir, 'mod_lti_request'.date('YmdHis'))) {
|
||||
file_put_contents($tempfile, $rawbody);
|
||||
chmod($tempfile, 0644);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches LTI type configuration for an LTI instance
|
||||
*
|
||||
* @param stdClass $instance
|
||||
* @return array Can be empty if no type is found
|
||||
*/
|
||||
function lti_get_type_config_by_instance($instance) {
|
||||
$typeid = null;
|
||||
if (empty($instance->typeid)) {
|
||||
$tool = lti_get_tool_by_url_match($instance->toolurl, $instance->course);
|
||||
if ($tool) {
|
||||
$typeid = $tool->id;
|
||||
}
|
||||
} else {
|
||||
$typeid = $instance->typeid;
|
||||
}
|
||||
if (!empty($typeid)) {
|
||||
return lti_get_type_config($typeid);
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforce type config settings onto the LTI instance
|
||||
*
|
||||
* @param stdClass $instance
|
||||
* @param array $typeconfig
|
||||
*/
|
||||
function lti_force_type_config_settings($instance, array $typeconfig) {
|
||||
$forced = array(
|
||||
'instructorchoicesendname' => 'sendname',
|
||||
'instructorchoicesendemailaddr' => 'sendemailaddr',
|
||||
'instructorchoiceacceptgrades' => 'acceptgrades',
|
||||
);
|
||||
|
||||
foreach ($forced as $instanceparam => $typeconfigparam) {
|
||||
if (array_key_exists($typeconfigparam, $typeconfig) && $typeconfig[$typeconfigparam] != LTI_SETTING_DELEGATE) {
|
||||
$instance->$instanceparam = $typeconfig[$typeconfigparam];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,9 +77,25 @@
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
var allowgrades = Y.one('#id_instructorchoiceacceptgrades');
|
||||
allowgrades.on('change', this.toggleGradeSection, this);
|
||||
|
||||
updateToolMatches();
|
||||
},
|
||||
|
||||
toggleGradeSection: function(e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
var allowgrades = Y.one('#id_instructorchoiceacceptgrades');
|
||||
var gradefieldset = Y.one('#modstandardgrade');
|
||||
if (!allowgrades.get('checked')) {
|
||||
gradefieldset.hide();
|
||||
} else {
|
||||
gradefieldset.show();
|
||||
}
|
||||
},
|
||||
|
||||
clearToolCache: function(){
|
||||
this.urlCache = {};
|
||||
this.toolTypeCache = {};
|
||||
@ -138,9 +154,10 @@
|
||||
automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + '" />' + M.str.lti.custom_config);
|
||||
}
|
||||
|
||||
var continuation = function(toolInfo){
|
||||
self.updatePrivacySettings(toolInfo);
|
||||
|
||||
var continuation = function(toolInfo, inputfield){
|
||||
if (inputfield === undefined || (inputfield.get('id') != 'id_securetoolurl' || inputfield.get('value'))) {
|
||||
self.updatePrivacySettings(toolInfo);
|
||||
}
|
||||
if(toolInfo.toolname){
|
||||
automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + '" />' + M.str.lti.using_tool_configuration + toolInfo.toolname);
|
||||
} else if(!selectedToolType) {
|
||||
@ -159,7 +176,7 @@
|
||||
return continuation(self.urlCache[url]);
|
||||
} else if(!selectedToolType && !url) {
|
||||
// No tool type or url set
|
||||
return continuation({});
|
||||
return continuation({}, field);
|
||||
} else {
|
||||
self.findToolByUrl(url, selectedToolType, function(toolInfo){
|
||||
if(toolInfo){
|
||||
@ -237,6 +254,8 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.toggleGradeSection();
|
||||
},
|
||||
|
||||
getSelectedToolTypeOption: function(){
|
||||
|
@ -125,6 +125,7 @@ class mod_lti_mod_form extends moodleform_mod {
|
||||
$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');
|
||||
$launchoptions[LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW] = get_string('existing_window', 'lti');
|
||||
$launchoptions[LTI_LAUNCH_CONTAINER_WINDOW] = get_string('new_window', 'lti');
|
||||
|
||||
$mform->addElement('select', 'launchcontainer', get_string('launchinpopup', 'lti'), $launchoptions);
|
||||
@ -194,6 +195,9 @@ class mod_lti_mod_form extends moodleform_mod {
|
||||
}
|
||||
*/
|
||||
|
||||
// Add standard course module grading elements.
|
||||
$this->standard_grading_coursemodule_elements();
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// add standard elements, common to all modules
|
||||
$this->standard_coursemodule_elements();
|
||||
|
@ -34,6 +34,10 @@ use moodle\mod\lti as lti;
|
||||
|
||||
$rawbody = file_get_contents("php://input");
|
||||
|
||||
if (lti_should_log_request($rawbody)) {
|
||||
lti_log_request($rawbody);
|
||||
}
|
||||
|
||||
foreach (lti\OAuthUtil::get_headers() as $name => $value) {
|
||||
if ($name === 'Authorization') {
|
||||
// TODO: Switch to core oauthlib once implemented - MDL-30149
|
||||
@ -78,7 +82,12 @@ switch ($messagetype) {
|
||||
|
||||
$ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid));
|
||||
|
||||
if (!lti_accepts_grades($ltiinstance)) {
|
||||
throw new Exception('Tool does not accept grades');
|
||||
}
|
||||
|
||||
lti_verify_sourcedid($ltiinstance, $parsed);
|
||||
lti_set_session_user($parsed->userid);
|
||||
|
||||
$gradestatus = lti_update_grade($ltiinstance, $parsed->userid, $parsed->launchid, $parsed->gradeval);
|
||||
|
||||
@ -98,6 +107,10 @@ switch ($messagetype) {
|
||||
|
||||
$ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid));
|
||||
|
||||
if (!lti_accepts_grades($ltiinstance)) {
|
||||
throw new Exception('Tool does not accept grades');
|
||||
}
|
||||
|
||||
//Getting the grade requires the context is set
|
||||
$context = context_course::instance($ltiinstance->course);
|
||||
$PAGE->set_context($context);
|
||||
@ -127,7 +140,12 @@ switch ($messagetype) {
|
||||
|
||||
$ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid));
|
||||
|
||||
if (!lti_accepts_grades($ltiinstance)) {
|
||||
throw new Exception('Tool does not accept grades');
|
||||
}
|
||||
|
||||
lti_verify_sourcedid($ltiinstance, $parsed);
|
||||
lti_set_session_user($parsed->userid);
|
||||
|
||||
$gradestatus = lti_delete_grade($ltiinstance, $parsed->userid);
|
||||
|
||||
|
@ -79,11 +79,12 @@ function lti_parse_grade_replace_message($xml) {
|
||||
}
|
||||
|
||||
$parsed = new stdClass();
|
||||
$parsed->gradeval = $grade * 100;
|
||||
$parsed->gradeval = $grade;
|
||||
|
||||
$parsed->instanceid = $resultjson->data->instanceid;
|
||||
$parsed->userid = $resultjson->data->userid;
|
||||
$parsed->launchid = $resultjson->data->launchid;
|
||||
$parsed->typeid = $resultjson->data->typeid;
|
||||
$parsed->sourcedidhash = $resultjson->hash;
|
||||
|
||||
$parsed->messageid = lti_parse_message_id($xml);
|
||||
@ -99,6 +100,7 @@ function lti_parse_grade_read_message($xml) {
|
||||
$parsed->instanceid = $resultjson->data->instanceid;
|
||||
$parsed->userid = $resultjson->data->userid;
|
||||
$parsed->launchid = $resultjson->data->launchid;
|
||||
$parsed->typeid = $resultjson->data->typeid;
|
||||
$parsed->sourcedidhash = $resultjson->hash;
|
||||
|
||||
$parsed->messageid = lti_parse_message_id($xml);
|
||||
@ -114,6 +116,7 @@ function lti_parse_grade_delete_message($xml) {
|
||||
$parsed->instanceid = $resultjson->data->instanceid;
|
||||
$parsed->userid = $resultjson->data->userid;
|
||||
$parsed->launchid = $resultjson->data->launchid;
|
||||
$parsed->typeid = $resultjson->data->typeid;
|
||||
$parsed->sourcedidhash = $resultjson->hash;
|
||||
|
||||
$parsed->messageid = lti_parse_message_id($xml);
|
||||
@ -121,6 +124,33 @@ function lti_parse_grade_delete_message($xml) {
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
function lti_accepts_grades($ltiinstance) {
|
||||
$acceptsgrades = true;
|
||||
$typeconfig = lti_get_config($ltiinstance);
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
return $acceptsgrades;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the passed user ID to the session user.
|
||||
*
|
||||
* @param int $userid
|
||||
*/
|
||||
function lti_set_session_user($userid) {
|
||||
global $DB;
|
||||
|
||||
if ($user = $DB->get_record('user', array('id' => $userid))) {
|
||||
\core\session\manager::set_user($user);
|
||||
}
|
||||
}
|
||||
|
||||
function lti_update_grade($ltiinstance, $userid, $launchid, $gradeval) {
|
||||
global $CFG, $DB;
|
||||
require_once($CFG->libdir . '/gradelib.php');
|
||||
@ -128,6 +158,8 @@ function lti_update_grade($ltiinstance, $userid, $launchid, $gradeval) {
|
||||
$params = array();
|
||||
$params['itemname'] = $ltiinstance->name;
|
||||
|
||||
$gradeval = $gradeval * floatval($ltiinstance->grade);
|
||||
|
||||
$grade = new stdClass();
|
||||
$grade->userid = $userid;
|
||||
$grade->rawgrade = $gradeval;
|
||||
@ -170,10 +202,12 @@ function lti_read_grade($ltiinstance, $userid) {
|
||||
|
||||
$grades = grade_get_grades($ltiinstance->course, LTI_ITEM_TYPE, LTI_ITEM_MODULE, $ltiinstance->id, $userid);
|
||||
|
||||
if (isset($grades) && isset($grades->items[0]) && is_array($grades->items[0]->grades)) {
|
||||
$ltigrade = floatval($ltiinstance->grade);
|
||||
|
||||
if (!empty($ltigrade) && isset($grades) && isset($grades->items[0]) && is_array($grades->items[0]->grades)) {
|
||||
foreach ($grades->items[0]->grades as $agrade) {
|
||||
$grade = $agrade->grade;
|
||||
$grade = $grade / 100.0;
|
||||
$grade = $grade / $ltigrade;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -191,7 +225,7 @@ function lti_delete_grade($ltiinstance, $userid) {
|
||||
$grade->userid = $userid;
|
||||
$grade->rawgrade = null;
|
||||
|
||||
$status = grade_update(LTI_SOURCE, $ltiinstance->course, LTI_ITEM_TYPE, LTI_ITEM_MODULE, $ltiinstance->id, 0, $grade, array('deleted'=>1));
|
||||
$status = grade_update(LTI_SOURCE, $ltiinstance->course, LTI_ITEM_TYPE, LTI_ITEM_MODULE, $ltiinstance->id, 0, $grade);
|
||||
|
||||
return $status == GRADE_UPDATE_OK;
|
||||
}
|
||||
@ -215,8 +249,16 @@ function lti_verify_message($key, $sharedsecrets, $body, $headers = null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate source ID from external request
|
||||
*
|
||||
* @param object $ltiinstance
|
||||
* @param object $parsed
|
||||
* @throws Exception
|
||||
*/
|
||||
function lti_verify_sourcedid($ltiinstance, $parsed) {
|
||||
$sourceid = lti_build_sourcedid($parsed->instanceid, $parsed->userid, $parsed->launchid, $ltiinstance->servicesalt);
|
||||
$sourceid = lti_build_sourcedid($parsed->instanceid, $parsed->userid,
|
||||
$ltiinstance->servicesalt, $parsed->typeid, $parsed->launchid);
|
||||
|
||||
if ($sourceid->hash != $parsed->sourcedidhash) {
|
||||
throw new Exception('SourcedId hash not valid');
|
||||
@ -238,6 +280,7 @@ function lti_extend_lti_services($data) {
|
||||
if (count($plugins) > 1) {
|
||||
throw new coding_exception('More than one ltisource plugin handler found');
|
||||
}
|
||||
$data->xml = new SimpleXMLElement($data->body);
|
||||
$callback = current($plugins);
|
||||
call_user_func($callback, $data);
|
||||
} catch (moodle_exception $e) {
|
||||
@ -258,4 +301,4 @@ function lti_extend_lti_services($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -173,4 +173,39 @@ if ($ADMIN->fulltree) {
|
||||
</script>
|
||||
";
|
||||
$settings->add(new admin_setting_heading('lti_types', get_string('external_tool_types', 'lti') . $OUTPUT->help_icon('main_admin', 'lti'), $template));
|
||||
|
||||
if (!during_initial_install()) {
|
||||
// Process subplugin settings pages if any.
|
||||
// Every subplugin that wishes to have settings page should provide it's own
|
||||
// settings.php assuming it will be added as a custom settings page.
|
||||
// A type will be passed through subtype parameter.
|
||||
// All such links will be placed in separate category called LTI.
|
||||
$plugins = get_plugin_list('ltisource');
|
||||
if (!empty($plugins)) {
|
||||
$toadd = array();
|
||||
foreach ($plugins as $name => $path) {
|
||||
if (file_exists($path.DIRECTORY_SEPARATOR.'settings.php')) {
|
||||
$toadd[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($toadd)) {
|
||||
$ADMIN->add('modules',
|
||||
new admin_category('ltisource',
|
||||
new lang_string('lti', 'lti'),
|
||||
$module->is_enabled() === false)
|
||||
);
|
||||
|
||||
foreach ($toadd as $name) {
|
||||
$component = 'ltisource_'.$name;
|
||||
$ADMIN->add($component,
|
||||
new admin_externalpage($name,
|
||||
new lang_string('pluginname', $component),
|
||||
new moodle_url("/mod/lti/source/{$name}/settings.php",
|
||||
array('subtype' => $component)))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,4 +145,19 @@ class mod_lti_locallib_testcase extends basic_testcase {
|
||||
$this->assertEquals('https://moodle.org', lti_ensure_url_is_https('moodle.org'));
|
||||
$this->assertEquals('https://moodle.org', lti_ensure_url_is_https('https://moodle.org'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test lti_get_url_thumbprint against various URLs
|
||||
*/
|
||||
public function test_lti_get_url_thumbprint() {
|
||||
// Note: trailing and double slash are expected right now. Must evaluate if it must be removed at some point.
|
||||
$this->assertEquals('moodle.org/', lti_get_url_thumbprint('http://MOODLE.ORG'));
|
||||
$this->assertEquals('moodle.org/', lti_get_url_thumbprint('http://www.moodle.org'));
|
||||
$this->assertEquals('moodle.org/', lti_get_url_thumbprint('https://www.moodle.org'));
|
||||
$this->assertEquals('moodle.org/', lti_get_url_thumbprint('moodle.org'));
|
||||
$this->assertEquals('moodle.org//this/is/moodle', lti_get_url_thumbprint('http://moodle.org/this/is/moodle'));
|
||||
$this->assertEquals('moodle.org//this/is/moodle', lti_get_url_thumbprint('https://moodle.org/this/is/moodle'));
|
||||
$this->assertEquals('moodle.org//this/is/moodle', lti_get_url_thumbprint('moodle.org/this/is/moodle'));
|
||||
$this->assertEquals('moodle.org//this/is/moodle', lti_get_url_thumbprint('moodle.org/this/is/moodle?foo=bar'));
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,7 @@
|
||||
*/
|
||||
|
||||
require_once('../../config.php');
|
||||
require_once($CFG->libdir.'/completionlib.php');
|
||||
require_once($CFG->dirroot.'/mod/lti/lib.php');
|
||||
require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
||||
|
||||
@ -75,6 +76,9 @@ $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));
|
||||
$PAGE->set_url($url);
|
||||
|
||||
@ -89,8 +93,6 @@ if ($launchcontainer == LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS) {
|
||||
$PAGE->set_pagelayout('incourse');
|
||||
}
|
||||
|
||||
require_login($course);
|
||||
|
||||
// Mark viewed by user (if required).
|
||||
$completion = new completion_info($course);
|
||||
$completion->set_module_viewed($cm);
|
||||
@ -135,31 +137,25 @@ if ( $launchcontainer == LTI_LAUNCH_CONTAINER_WINDOW ) {
|
||||
$resize = '
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
YUI().use("yui2-dom", function(Y) {
|
||||
YUI().use("node", "event", function(Y) {
|
||||
//Take scrollbars off the outer document to prevent double scroll bar effect
|
||||
document.body.style.overflow = "hidden";
|
||||
|
||||
var dom = Y.YUI2.util.Dom;
|
||||
var frame = document.getElementById("contentframe");
|
||||
var doc = Y.one("body");
|
||||
doc.setStyle("overflow", "hidden");
|
||||
|
||||
var frame = Y.one("#contentframe");
|
||||
var padding = 15; //The bottom of the iframe wasn\'t visible on some themes. Probably because of border widths, etc.
|
||||
|
||||
var lastHeight;
|
||||
|
||||
var resize = function(){
|
||||
var viewportHeight = dom.getViewportHeight();
|
||||
|
||||
if(lastHeight !== Math.min(dom.getDocumentHeight(), viewportHeight)){
|
||||
|
||||
frame.style.height = viewportHeight - dom.getY(frame) - padding + "px";
|
||||
|
||||
lastHeight = Math.min(dom.getDocumentHeight(), dom.getViewportHeight());
|
||||
var resize = function(e) {
|
||||
var viewportHeight = doc.get("winHeight");
|
||||
if(lastHeight !== Math.min(doc.get("docHeight"), viewportHeight)){
|
||||
frame.setStyle("height", viewportHeight - frame.getY() - padding + "px");
|
||||
lastHeight = Math.min(doc.get("docHeight"), doc.get("winHeight"));
|
||||
}
|
||||
};
|
||||
|
||||
resize();
|
||||
|
||||
setInterval(resize, 250);
|
||||
Y.on("windowresize", resize);
|
||||
});
|
||||
//]]
|
||||
</script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user