diff --git a/mod/lti/OAuth.php b/mod/lti/OAuth.php
index 65e9b0f138a..e0365407e0d 100644
--- a/mod/lti/OAuth.php
+++ b/mod/lti/OAuth.php
@@ -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) {
diff --git a/mod/lti/OAuthBody.php b/mod/lti/OAuthBody.php
index 565cdd34163..5f43f69d39e 100644
--- a/mod/lti/OAuthBody.php
+++ b/mod/lti/OAuthBody.php
@@ -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);
}
diff --git a/mod/lti/backup/moodle2/backup_lti_stepslib.php b/mod/lti/backup/moodle2/backup_lti_stepslib.php
index 8a7ac39bb1c..f74e29245c7 100644
--- a/mod/lti/backup/moodle2/backup_lti_stepslib.php
+++ b/mod/lti/backup/moodle2/backup_lti_stepslib.php
@@ -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);
}
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 61113e00ed2..a1a1e92dedb 100644
--- a/mod/lti/backup/moodle2/restore_lti_activity_task.class.php
+++ b/mod/lti/backup/moodle2/restore_lti_activity_task.class.php
@@ -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;
+ }
}
diff --git a/mod/lti/backup/moodle2/restore_lti_stepslib.php b/mod/lti/backup/moodle2/restore_lti_stepslib.php
index 042ac9f20f0..eef676be87f 100644
--- a/mod/lti/backup/moodle2/restore_lti_stepslib.php
+++ b/mod/lti/backup/moodle2/restore_lti_stepslib.php
@@ -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);
}
}
diff --git a/mod/lti/classes/plugininfo/ltisource.php b/mod/lti/classes/plugininfo/ltisource.php
index d674c2392b6..7003fb04b7d 100644
--- a/mod/lti/classes/plugininfo/ltisource.php
+++ b/mod/lti/classes/plugininfo/ltisource.php
@@ -23,11 +23,50 @@
*/
namespace mod_lti\plugininfo;
-use core\plugininfo\base, core_plugin_manager, moodle_url;
+use core\plugininfo\base;
defined('MOODLE_INTERNAL') || die();
class ltisource extends base {
- // Accept base class implementation 100%.
+ /**
+ * Returns the node name used in admin settings menu for this plugin settings (if applicable)
+ *
+ * @return null|string node name or null if plugin does not create settings node (default)
+ */
+ public function get_settings_section_name() {
+ return 'ltisourcesetting'.$this->name;
+ }
+
+ /**
+ * Loads plugin settings to the settings tree
+ *
+ * This function usually includes settings.php file in plugins folder.
+ * Alternatively it can create a link to some settings page (instance of admin_externalpage)
+ *
+ * @param \part_of_admin_tree $adminroot
+ * @param string $parentnodename
+ * @param bool $hassiteconfig whether the current user has moodle/site:config capability
+ */
+ public function load_settings(\part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+ global $CFG, $USER, $DB, $OUTPUT, $PAGE; // In case settings.php wants to refer to them.
+ $ADMIN = $adminroot; // May be used in settings.php.
+ $plugininfo = $this; // Also can be used inside settings.php.
+
+ if (!$this->is_installed_and_upgraded()) {
+ return;
+ }
+ if (!$hassiteconfig or !file_exists($this->full_path('settings.php'))) {
+ return;
+ }
+ $section = $this->get_settings_section_name();
+ $settings = new \admin_settingpage($section, $this->displayname,
+ 'moodle/site:config', $this->is_enabled() === false);
+
+ include($this->full_path('settings.php')); // This may also set $settings to null.
+
+ if ($settings) {
+ $ADMIN->add($parentnodename, $settings);
+ }
+ }
}
diff --git a/mod/lti/db/access.php b/mod/lti/db/access.php
index 647b83f63d4..13a43157060 100644
--- a/mod/lti/db/access.php
+++ b/mod/lti/db/access.php
@@ -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.
diff --git a/mod/lti/edit_form.php b/mod/lti/edit_form.php
index 558c1b4f76d..2b13b9e2a96 100644
--- a/mod/lti/edit_form.php
+++ b/mod/lti/edit_form.php
@@ -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)) {
diff --git a/mod/lti/grade.php b/mod/lti/grade.php
index c3c063d41dc..3dd793153a8 100644
--- a/mod/lti/grade.php
+++ b/mod/lti/grade.php
@@ -44,21 +44,17 @@
* @author Nikolas Galanis
* @author Chris Scribner
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @deprecated since 2.8
*/
-require_once("../../config.php");
-require_once($CFG->dirroot.'/mod/lti/lib.php');
-require_once($CFG->libdir.'/plagiarismlib.php');
+require_once(dirname(dirname(__DIR__)).'/config.php');
-$id = optional_param('id', 0, PARAM_INT); // Course module ID
-$l = optional_param('l', 0, PARAM_INT); // lti instance ID
-$mode = optional_param('mode', 'all', PARAM_ALPHA); // What mode are we in?
-$download = optional_param('download' , 'none', PARAM_ALPHA); //ZIP download asked for?
+$id = optional_param('id', 0, PARAM_INT);
+$l = optional_param('l', 0, PARAM_INT);
-if ($l) { // Two ways to specify the module
+if ($l) {
$lti = $DB->get_record('lti', array('id' => $l), '*', MUST_EXIST);
$cm = get_coursemodule_from_instance('lti', $lti->id, $lti->course, false, MUST_EXIST);
-
} else {
$cm = get_coursemodule_from_id('lti', $id, 0, false, MUST_EXIST);
$lti = $DB->get_record('lti', array('id' => $cm->instance), '*', MUST_EXIST);
@@ -67,100 +63,9 @@ if ($l) { // Two ways to specify the module
$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_module::instance($cm->id));
-$url = new moodle_url('/mod/lti/grade.php', array('id' => $cm->id));
-if ($mode !== 'all') {
- $url->param('mode', $mode);
-}
-$PAGE->set_url($url);
+debugging('This file has been deprecated. Links to this file should automatically '.
+ 'fallback to /mod/lti/view.php once this file has been deleted.', DEBUG_DEVELOPER);
-$module = array(
- 'name' => 'mod_lti_submissions',
- 'fullpath' => '/mod/lti/submissions.js',
- 'requires' => array('base', 'yui2-datatable'),
- 'strings' => array(),
-);
-
-$PAGE->requires->js_init_call('M.mod_lti.submissions.init', array(), true, $module);
-
-$submissionquery = '
- SELECT s.id, u.firstname, u.lastname, u.id AS userid, s.datesubmitted, s.gradepercent
- FROM {lti_submission} s
- INNER JOIN {user} u ON s.userid = u.id
- WHERE s.ltiid = :ltiid
- ORDER BY s.datesubmitted DESC
-';
-
-$submissions = $DB->get_records_sql($submissionquery, array('ltiid' => $lti->id));
-
-$html = '
-
-
-
-
-
-
- User |
- Date |
- Grade |
-
-
-
-
-
-
-
-';
-
-$rowtemplate = '
-
-
-
- |
-
-
- |
-
-
- |
-
-';
-
-$rows = '';
-
-foreach ($submissions as $submission) {
- $row = $rowtemplate;
-
- foreach ($submission as $key => $value) {
- if ($key === 'datesubmitted') {
- $value = userdate($value);
- }
-
- $row = str_replace('', $value, $row);
- }
-
- $rows .= $row;
-}
-
-$table = str_replace('', $rows, $html);
-
-$title = get_string('submissionsfor', 'lti', $lti->name);
-
-$PAGE->set_title($title);
-$PAGE->set_heading($course->fullname);
-
-echo $OUTPUT->header();
-echo $OUTPUT->heading(format_string($lti->name, true, array('context' => $context)));
-echo $OUTPUT->heading(get_string('submissions', 'lti'), 3);
-
-echo $table;
-
-echo $OUTPUT->footer();
+redirect(new moodle_url('/mod/lti/view.php', array('l' => $lti->id)));
diff --git a/mod/lti/lang/en/lti.php b/mod/lti/lang/en/lti.php
index 10ed43a1c94..4c407005d36 100644
--- a/mod/lti/lang/en/lti.php
+++ b/mod/lti/lang/en/lti.php
@@ -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.
@@ -222,6 +223,7 @@ $string['lti:grade'] = 'View grades returned by the external tool';
$string['lti:manage'] = 'Be an Instructor when the tool is launched';
$string['lti:requesttooladd'] = 'Request a tool is configured site-wide';
$string['lti:view'] = 'Launch external tool activities';
+$string['ltisettings'] = 'LTI settings';
$string['lti_administration'] = 'LTI Administration';
$string['lti_errormsg'] = 'The tool returned the following error message: "{$a}"';
$string['lti_launch_error'] = 'An error occurred when launching the external tool:';
diff --git a/mod/lti/lib.php b/mod/lti/lib.php
index 8903475fd52..4e7f162cae1 100644
--- a/mod/lti/lib.php
+++ b/mod/lti/lib.php
@@ -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');
+}
diff --git a/mod/lti/locallib.php b/mod/lti/locallib.php
index 4b282cd8268..2bab6baa1f7 100644
--- a/mod/lti/locallib.php
+++ b/mod/lti/locallib.php
@@ -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,36 @@ 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;
+ // Allow request params to be updated by sub-plugins.
+ $plugins = core_component::get_plugin_list('ltisource');
+ foreach (array_keys($plugins) as $plugin) {
+ $pluginparams = component_callback('ltisource_'.$plugin, 'before_launch',
+ array($instance, $endpoint, $requestparams), array());
+
+ if (!empty($pluginparams) && is_array($pluginparams)) {
+ $requestparams = array_merge($requestparams, $pluginparams);
+ }
+ }
+
if (!empty($key) && !empty($secret)) {
$parms = lti_sign_parameters($requestparams, $endpoint, "POST", $key, $secret);
@@ -200,11 +232,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 +271,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 +296,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 +549,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 +690,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 +1229,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];
+ }
+ }
+}
+
diff --git a/mod/lti/mod_form.js b/mod/lti/mod_form.js
index aabce00bc93..6dc0e14f906 100644
--- a/mod/lti/mod_form.js
+++ b/mod/lti/mod_form.js
@@ -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', '
' + 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', '
' + 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(){
diff --git a/mod/lti/mod_form.php b/mod/lti/mod_form.php
index c2322681275..e34eb239a0d 100644
--- a/mod/lti/mod_form.php
+++ b/mod/lti/mod_form.php
@@ -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();
diff --git a/mod/lti/service.php b/mod/lti/service.php
index beae870997e..7cfb4258a5a 100644
--- a/mod/lti/service.php
+++ b/mod/lti/service.php
@@ -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);
diff --git a/mod/lti/servicelib.php b/mod/lti/servicelib.php
index 3d7cc0eb180..c1f3f97003d 100644
--- a/mod/lti/servicelib.php
+++ b/mod/lti/servicelib.php
@@ -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;
-}
\ No newline at end of file
+}
diff --git a/mod/lti/settings.php b/mod/lti/settings.php
index 20ca0625e94..bac2f4c74f4 100644
--- a/mod/lti/settings.php
+++ b/mod/lti/settings.php
@@ -48,6 +48,17 @@
defined('MOODLE_INTERNAL') || die;
+/** @var admin_settingpage $settings */
+$modltifolder = new admin_category('modltifolder', new lang_string('pluginname', 'mod_lti'), $module->is_enabled() === false);
+$ADMIN->add('modsettings', $modltifolder);
+
+$ADMIN->add('modltifolder', $settings);
+
+foreach (core_plugin_manager::instance()->get_plugins_of_type('ltisource') as $plugin) {
+ /** @var \mod_lti\plugininfo\ltisource $plugin */
+ $plugin->load_settings($ADMIN, 'modltifolder', $hassiteconfig);
+}
+
if ($ADMIN->fulltree) {
require_once($CFG->dirroot.'/mod/lti/locallib.php');
@@ -172,5 +183,16 @@ if ($ADMIN->fulltree) {
//]]
";
- $settings->add(new admin_setting_heading('lti_types', get_string('external_tool_types', 'lti') . $OUTPUT->help_icon('main_admin', 'lti'), $template));
+ $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;
}
diff --git a/mod/lti/styles.css b/mod/lti/styles.css
index 0b252f28de3..0b677b693b9 100644
--- a/mod/lti/styles.css
+++ b/mod/lti/styles.css
@@ -15,19 +15,6 @@
.path-mod-lti .late {color: red;}
.path-mod-lti .message {text-align: center;}
-/** Styles for submissions.php **/
-#page-mod-lti-submissions fieldset.felement {margin-left: 16%;}
-#page-mod-lti-submissions form#options div {text-align:right;margin-left:auto;margin-right:20px;}
-#page-mod-lti-submissions .header .commands {display: inline;}
-#page-mod-lti-submissions .picture {width: 35px;}
-#page-mod-lti-submissions .fullname,
-#page-mod-lti-submissions .timemodified,
-#page-mod-lti-submissions .timemarked {text-align: left;}
-#page-mod-lti-submissions .submissions .grade,
-#page-mod-lti-submissions .submissions .outcome,
-#page-mod-lti-submissions .submissions .finalgrade {text-align: right;}
-#page-mod-lti-submissions .qgprefs #optiontable {text-align:right;margin-left:auto;}
-
/* Styles for admin */
.path-admin-mod-lti .mform .fitem .fitemtitle { min-width:18em;padding-right:1em } /* Prevent setting titles from wrapping */
diff --git a/mod/lti/submissions.js b/mod/lti/submissions.js
deleted file mode 100644
index 2f4e730b5a6..00000000000
--- a/mod/lti/submissions.js
+++ /dev/null
@@ -1,74 +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 .
-
-/**
- * Javascript extensions for LTI submission viewer.
- *
- * @package mod
- * @subpackage lti
- * @copyright Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com)
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @author Chris Scribner
- */
-(function(){
- var Y;
-
- M.mod_lti = M.mod_lti || {};
-
- M.mod_lti.submissions = {
- init: function(yui3){
- if(yui3){
- Y = yui3;
- }
-
- this.setupTable();
- },
-
- setupTable: function(){
- var lti_submissions_table = Y.YUI2.util.Dom.get('lti_submissions_table');
-
- var dataSource = new Y.YUI2.util.DataSource(lti_submissions_table);
-
- var configuredColumns = [
- { key: "user", label: "User", sortable:true },
- { key: "date", label: "Submission Date", sortable:true, formatter: 'date' },
- { key: "grade",
- label: "Grade",
- sortable:true,
- formatter: function(cell, record, column, data){
- cell.innerHTML = parseFloat(data).toFixed(1) + '%';
- }
- }
- ];
-
- dataSource.responseType = Y.YUI2.util.DataSource.TYPE_HTMLTABLE;
- dataSource.responseSchema = {
- fields: [
- { key: "user" },
- { key: "date", parser: "date" },
- { key: "grade", parser: "number" },
- ]
- };
-
- new Y.YUI2.widget.DataTable("lti_submissions_table_container", configuredColumns, dataSource,
- {
- sortedBy: {key:"date", dir:"desc"}
- }
- );
-
- Y.one('#lti_submissions_table_container').setStyle('display', '');
- }
- }
-})();
diff --git a/mod/lti/tests/locallib_test.php b/mod/lti/tests/locallib_test.php
index 33a21878bb7..b0a3b16f091 100644
--- a/mod/lti/tests/locallib_test.php
+++ b/mod/lti/tests/locallib_test.php
@@ -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'));
+ }
}
diff --git a/mod/lti/version.php b/mod/lti/version.php
index 3e6719e4e06..b9db2d2e804 100644
--- a/mod/lti/version.php
+++ b/mod/lti/version.php
@@ -48,7 +48,7 @@
defined('MOODLE_INTERNAL') || die;
-$plugin->version = 2014051200; // The current module version (Date: YYYYMMDDXX)
+$plugin->version = 2014060200; // 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 e31adc7f052..d6e7f6eb61e 100644
--- a/mod/lti/view.php
+++ b/mod/lti/view.php
@@ -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 = '