MDL-49609 mod_lti: Refactor content-item and make it work using JS
* Rebase and resolve conflicts from initial patch. * Reorganise contentitem and contentitem_return pages. * Add capability checks for contentitem and contentitem_return pages. * Move the building of Content-Item selection request to a local lib function. * Move contentitem_return processing logic to a local lib function. * Fix type settings update. Content-item checkbox does not get checked/unchecked on load. * Fix tool settings update. Disabled content-item checkbox gets unchecked when tool settings form is submitted. * Add "Select content" button on load which launches the content item selection dialogue. * Move hardcoded HTML and JS to mustache templates and AMD modules. * Use standard YUI dialog for displaying the Content-Item selection page. * Added processing for the following Content-Item properties: - text - icon * On ContentItem selection, fill out form with the configuration data retrieved, instead of automatically saving the tool and redirecting to the course page. * Removed section- and sectionreturn-related code since we're not automatically adding the tool on ContentItem selection. * On mod_lti_mod_form, enable configuration fields if they support ContentItem selection. * New form-field module for setting fields using JS * Change types_config table's 'value' column from char(255) to text
This commit is contained in:
Vendored
+1
@@ -0,0 +1 @@
|
||||
define(["jquery","core/notification","core/str","core/templates","mod_lti/form-field","core/yui"],function(a,b,c,d,e){var f,g={init:function(a,e){var g="";c.get_string("selectcontent","lti").then(function(b){g=b;var c={url:a,postData:e};return d.render("mod_lti/contentitem",c)}).then(function(a,c){f=new M.core.dialogue({modal:!0,headerContent:g,bodyContent:a,draggable:!0,width:"800px",height:"600px"}),f.show(),f.after("visibleChange",function(a){a.prevVal&&!a.newVal&&(this.destroy(),b.fetchNotifications())},f),d.runTemplateJS(c)}).fail(b.exception)}},h=[new e("name",e.TYPES.TEXT,(!1),""),new e("introeditor",e.TYPES.EDITOR,(!1),""),new e("toolurl",e.TYPES.TEXT,(!0),""),new e("securetoolurl",e.TYPES.TEXT,(!0),""),new e("instructorchoiceacceptgrades",e.TYPES.CHECKBOX,(!0),(!0)),new e("instructorchoicesendname",e.TYPES.CHECKBOX,(!0),(!0)),new e("instructorchoicesendemailaddr",e.TYPES.CHECKBOX,(!0),(!0)),new e("instructorcustomparameters",e.TYPES.TEXT,(!0),""),new e("icon",e.TYPES.TEXT,(!0),""),new e("secureicon",e.TYPES.TEXT,(!0),""),new e("launchcontainer",e.TYPES.SELECT,(!0),0)];return window.processContentItemReturnData=function(b){f&&f.hide();var c;for(c in h){var d=h[c],e=null;"undefined"!==a.type(b[d.name])&&(e=b[d.name]),d.setFieldValue(e)}},g});
|
||||
+1
@@ -0,0 +1 @@
|
||||
define([],function(){return{init:function(a){window!=top&&parent.processContentItemReturnData(a)}}});
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
define(["jquery"],function(a){var b=function(a,b,c,d){this.name=a,this.id="id_"+this.name,this.selector="#"+this.id,this.type=b,this.resetIfUndefined=c,this.defaultValue=d};return b.TYPES={TEXT:1,SELECT:2,CHECKBOX:3,EDITOR:4},b.prototype.setFieldValue=function(c){if(null===c){if(!this.resetIfUndefined)return;c=this.defaultValue}switch(this.type){case b.TYPES.CHECKBOX:c?a(this.selector).prop("checked",!0):a(this.selector).prop("checked",!1);break;case b.TYPES.EDITOR:if("undefined"!==a.type(c.text)){var d=a(this.selector+"editable");d.length?d.html(c.text):"undefined"!=typeof tinyMCE&&tinyMCE.execInstanceCommand(this.id,"mceInsertContent",!1,c.text),a(this.selector).val(c.text)}break;default:a(this.selector).val(c)}},b});
|
||||
@@ -0,0 +1,122 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Launches the modal dialogue that contains the iframe that sends the Content-Item selection request to an
|
||||
* LTI tool provider that supports Content-Item type message.
|
||||
*
|
||||
* See template: mod_lti/contentitem
|
||||
*
|
||||
* @module mod_lti/contentitem
|
||||
* @class contentitem
|
||||
* @package mod_lti
|
||||
* @copyright 2016 Jun Pataleta <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 3.2
|
||||
*/
|
||||
define(['jquery', 'core/notification', 'core/str', 'core/templates', 'mod_lti/form-field', 'core/yui'],
|
||||
function($, notification, str, templates, FormField) {
|
||||
var dialogue;
|
||||
var contentItem = {
|
||||
/**
|
||||
* Init function.
|
||||
*
|
||||
* @param {string} url The URL for the content item selection.
|
||||
* @param {object} postData The data to be sent for the content item selection request.
|
||||
*/
|
||||
init: function(url, postData) {
|
||||
var dialogueTitle = '';
|
||||
str.get_string('selectcontent', 'lti').then(function(title) {
|
||||
dialogueTitle = title;
|
||||
var context = {
|
||||
url: url,
|
||||
postData: postData
|
||||
};
|
||||
return templates.render('mod_lti/contentitem', context);
|
||||
|
||||
}).then(function(html, js) {
|
||||
// Set dialog's body content.
|
||||
dialogue = new M.core.dialogue({
|
||||
modal: true,
|
||||
headerContent: dialogueTitle,
|
||||
bodyContent: html,
|
||||
draggable: true,
|
||||
width: '800px',
|
||||
height: '600px'
|
||||
});
|
||||
|
||||
// Show dialog.
|
||||
dialogue.show();
|
||||
|
||||
// Destroy after hiding.
|
||||
dialogue.after('visibleChange', function(e) {
|
||||
// Going from visible to hidden.
|
||||
if (e.prevVal && !e.newVal) {
|
||||
this.destroy();
|
||||
// Fetch notifications.
|
||||
notification.fetchNotifications();
|
||||
}
|
||||
}, dialogue);
|
||||
|
||||
templates.runTemplateJS(js);
|
||||
|
||||
}).fail(notification.exception);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Array of form fields for LTI tool configuration.
|
||||
*
|
||||
* @type {*[]}
|
||||
*/
|
||||
var ltiFormFields = [
|
||||
new FormField('name', FormField.TYPES.TEXT, false, ''),
|
||||
new FormField('introeditor', FormField.TYPES.EDITOR, false, ''),
|
||||
new FormField('toolurl', FormField.TYPES.TEXT, true, ''),
|
||||
new FormField('securetoolurl', FormField.TYPES.TEXT, true, ''),
|
||||
new FormField('instructorchoiceacceptgrades', FormField.TYPES.CHECKBOX, true, true),
|
||||
new FormField('instructorchoicesendname', FormField.TYPES.CHECKBOX, true, true),
|
||||
new FormField('instructorchoicesendemailaddr', FormField.TYPES.CHECKBOX, true, true),
|
||||
new FormField('instructorcustomparameters', FormField.TYPES.TEXT, true, ''),
|
||||
new FormField('icon', FormField.TYPES.TEXT, true, ''),
|
||||
new FormField('secureicon', FormField.TYPES.TEXT, true, ''),
|
||||
new FormField('launchcontainer', FormField.TYPES.SELECT, true, 0)
|
||||
];
|
||||
|
||||
/**
|
||||
* Window function that can be called from mod_lti/contentitem_return to close the dialogue and process the return data.
|
||||
*
|
||||
* @param {object} returnData The fetched configuration data from the Content-Item selection dialogue.
|
||||
*/
|
||||
window.processContentItemReturnData = function(returnData) {
|
||||
if (dialogue) {
|
||||
dialogue.hide();
|
||||
}
|
||||
|
||||
// Populate LTI configuration fields from return data.
|
||||
var index;
|
||||
for (index in ltiFormFields) {
|
||||
var field = ltiFormFields[index];
|
||||
var value = null;
|
||||
if ($.type(returnData[field.name]) !== 'undefined') {
|
||||
value = returnData[field.name];
|
||||
}
|
||||
field.setFieldValue(value);
|
||||
}
|
||||
};
|
||||
|
||||
return contentItem;
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Processes the result of LTI tool creation from a Content-Item message type.
|
||||
*
|
||||
* @module mod_lti/contentitem_return
|
||||
* @class contentitem_return
|
||||
* @package mod_lti
|
||||
* @copyright 2016 Jun Pataleta <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 3.2
|
||||
*/
|
||||
define([], function() {
|
||||
return {
|
||||
/**
|
||||
* Init function.
|
||||
*
|
||||
* @param {string} returnData The returned data.
|
||||
*/
|
||||
init: function(returnData) {
|
||||
if (window != top) {
|
||||
// Send return data to be processed by the parent window.
|
||||
parent.processContentItemReturnData(returnData);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,107 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* A module that enables the setting of form field values on the client side.
|
||||
*
|
||||
* @module mod_lti/form-field
|
||||
* @class form-field
|
||||
* @package mod_lti
|
||||
* @copyright 2016 Jun Pataleta <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 3.2
|
||||
*/
|
||||
define(['jquery'],
|
||||
function($) {
|
||||
/**
|
||||
* Form field class.
|
||||
*
|
||||
* @param {string} name Field name.
|
||||
* @param {number} type The field type.
|
||||
* @param {boolean} resetIfUndefined Flag to reset the field to the default value if undefined in the return data.
|
||||
* @param {string|number|boolean} defaultValue The default value to use for the field.
|
||||
* @constructor
|
||||
*/
|
||||
var FormField = function(name, type, resetIfUndefined, defaultValue) {
|
||||
this.name = name;
|
||||
this.id = 'id_' + this.name;
|
||||
this.selector = '#' + this.id;
|
||||
this.type = type;
|
||||
this.resetIfUndefined = resetIfUndefined;
|
||||
this.defaultValue = defaultValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Form field types.
|
||||
*
|
||||
* @type {{TEXT: number, SELECT: number, CHECKBOX: number, EDITOR: number}}
|
||||
*/
|
||||
FormField.TYPES = {
|
||||
TEXT: 1,
|
||||
SELECT: 2,
|
||||
CHECKBOX: 3,
|
||||
EDITOR: 4
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the values for a form field.
|
||||
*
|
||||
* @param {string|boolean|number} value The value to be set into the field.
|
||||
*/
|
||||
FormField.prototype.setFieldValue = function(value) {
|
||||
if (value === null) {
|
||||
if (this.resetIfUndefined) {
|
||||
value = this.defaultValue;
|
||||
} else {
|
||||
// No need set the field value if value is null and there's no need to reset the field.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (this.type) {
|
||||
case FormField.TYPES.CHECKBOX:
|
||||
if (value) {
|
||||
$(this.selector).prop('checked', true);
|
||||
} else {
|
||||
$(this.selector).prop('checked', false);
|
||||
}
|
||||
break;
|
||||
case FormField.TYPES.EDITOR:
|
||||
if ($.type(value.text) !== 'undefined') {
|
||||
/* global tinyMCE:false */
|
||||
|
||||
// Set text in editor's editable content, if applicable.
|
||||
// Check if it is an Atto editor.
|
||||
var attoEditor = $(this.selector + 'editable');
|
||||
if (attoEditor.length) {
|
||||
attoEditor.html(value.text);
|
||||
} else if (typeof tinyMCE !== 'undefined') {
|
||||
// If the editor is not Atto, try to fallback to TinyMCE.
|
||||
tinyMCE.execInstanceCommand(this.id, 'mceInsertContent', false, value.text);
|
||||
}
|
||||
|
||||
// Set text to actual editor text area.
|
||||
$(this.selector).val(value.text);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$(this.selector).val(value);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return FormField;
|
||||
}
|
||||
);
|
||||
+22
-55
@@ -15,7 +15,7 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Display a page containing an iframe for the content-item selection process.
|
||||
* Handle sending a user to a tool provider to initiate a content-item selection.
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright 2015 Vital Source Technologies http://vitalsource.com
|
||||
@@ -24,66 +24,33 @@
|
||||
*/
|
||||
|
||||
require_once('../../config.php');
|
||||
require_once($CFG->dirroot.'/mod/lti/lib.php');
|
||||
require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
||||
require_once($CFG->dirroot . '/mod/lti/lib.php');
|
||||
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||
|
||||
$courseid = required_param('course', PARAM_INT);
|
||||
$sectionid = required_param('section', PARAM_INT);
|
||||
$id = required_param('id', PARAM_INT);
|
||||
$sectionreturn = required_param('sr', PARAM_INT);
|
||||
|
||||
$title = optional_param('title', null, PARAM_TEXT);
|
||||
$courseid = required_param('course', PARAM_INT);
|
||||
$title = optional_param('title', '', PARAM_TEXT);
|
||||
$text = optional_param('text', '', PARAM_RAW);
|
||||
|
||||
// Check access and capabilities.
|
||||
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
|
||||
|
||||
require_login($course);
|
||||
$context = context_course::instance($courseid);
|
||||
require_capability('moodle/course:manageactivities', $context);
|
||||
require_capability('mod/lti:addcoursetool', $context);
|
||||
|
||||
$url = new moodle_url('/mod/lti/contentitem.php', array('course' => $courseid));
|
||||
// Set the return URL. We send the launch container along to help us avoid frames-within-frames when the user returns.
|
||||
$returnurlparams = [
|
||||
'course' => $course->id,
|
||||
'id' => $id,
|
||||
'sesskey' => sesskey()
|
||||
];
|
||||
$returnurl = new \moodle_url('/mod/lti/contentitem_return.php', $returnurlparams);
|
||||
|
||||
$contentitem = new moodle_url('/mod/lti/contentitem2.php',
|
||||
array('course' => $courseid, 'section' => $sectionid, 'id' => $id, 'sr' => $sectionreturn, 'title' => $title));
|
||||
// Prepare the request.
|
||||
$request = lti_build_content_item_selection_request($id, $course, $returnurl, $title, $text, [], []);
|
||||
|
||||
echo "<p id=\"id_warning\" style=\"display: none; color: red; font-weight: bold; margin-top: 1em; padding-top: 1em;\">\n";
|
||||
echo get_string('register_warning', 'lti');
|
||||
echo "\n</p>\n";
|
||||
// Get the launch HTML.
|
||||
$content = lti_post_launch_html($request->params, $request->url, false);
|
||||
|
||||
echo '<iframe id="contentframe" height="600px" width="100%" src="' . $contentitem->out() . '" onload="doOnload()"></iframe>';
|
||||
|
||||
// Output script to make the object tag be as large as possible.
|
||||
$resize = '
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function doReveal() {
|
||||
var el = document.getElementById(\'id_warning\');
|
||||
el.style.display = \'block\';
|
||||
}
|
||||
function doOnload() {
|
||||
window.clearTimeout(mod_lti_timer);
|
||||
parent.M.mod_lti.editor.removeLoading();
|
||||
}
|
||||
var mod_lti_timer = window.setTimeout(doReveal, 20000);
|
||||
parent.YUI().use("node", "event", function(Y) {
|
||||
//Take scrollbars off the outer document to prevent double scroll bar effect
|
||||
var doc = parent.Y.one("body");
|
||||
doc.setStyle("overflow", "hidden");
|
||||
|
||||
var frame = parent.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(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();
|
||||
|
||||
parent.Y.on("windowresize", resize);
|
||||
});
|
||||
//]]
|
||||
</script>
|
||||
';
|
||||
|
||||
echo $resize;
|
||||
echo $content;
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Handle sending a user to a tool provider to initiate a content-item selection.
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright 2015 Vital Source Technologies http://vitalsource.com
|
||||
* @author Stephen Vickers
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once("../../config.php");
|
||||
require_once($CFG->dirroot.'/mod/lti/lib.php');
|
||||
require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
||||
|
||||
$courseid = required_param('course', PARAM_INT);
|
||||
$sectionid = required_param('section', PARAM_INT);
|
||||
$id = required_param('id', PARAM_INT);
|
||||
$sectionreturn = required_param('sr', PARAM_INT);
|
||||
|
||||
require_login($PAGE->course);
|
||||
|
||||
$tool = lti_get_type($id);
|
||||
$typeconfig = lti_get_type_config($id);
|
||||
if (isset($tool->toolproxyid)) {
|
||||
$toolproxy = lti_get_tool_proxy($tool->toolproxyid);
|
||||
$key = $toolproxy->guid;
|
||||
$secret = $toolproxy->secret;
|
||||
} else {
|
||||
$toolproxy = null;
|
||||
if (!empty($instance->resourcekey)) {
|
||||
$key = $instance->resourcekey;
|
||||
} else if (!empty($typeconfig['resourcekey'])) {
|
||||
$key = $typeconfig['resourcekey'];
|
||||
} else {
|
||||
$key = '';
|
||||
}
|
||||
if (!empty($instance->password)) {
|
||||
$secret = $instance->password;
|
||||
} else if (!empty($typeconfig['password'])) {
|
||||
$secret = $typeconfig['password'];
|
||||
} else {
|
||||
$secret = '';
|
||||
}
|
||||
}
|
||||
$tool->enabledcapability = $typeconfig['enabledcapability_ContentItemSelectionRequest'];
|
||||
$tool->parameter = $typeconfig['parameter_ContentItemSelectionRequest'];
|
||||
|
||||
$title = optional_param('title', $tool->name, PARAM_TEXT);
|
||||
|
||||
if (isset($typeconfig['toolurl_ContentItemSelectionRequest'])) {
|
||||
$endpoint = $typeconfig['toolurl_ContentItemSelectionRequest'];
|
||||
} else {
|
||||
$endpoint = !empty($instance->toolurl) ? $instance->toolurl : $typeconfig['toolurl'];
|
||||
}
|
||||
$endpoint = trim($endpoint);
|
||||
|
||||
// If the current request is using SSL and a secure tool URL is specified, use it.
|
||||
if (lti_request_is_using_ssl() && !empty($instance->securetoolurl)) {
|
||||
$endpoint = trim($instance->securetoolurl);
|
||||
}
|
||||
|
||||
// If SSL is forced, use the secure tool url if specified. Otherwise, make sure https is on the normal launch URL.
|
||||
if (isset($typeconfig['forcessl']) && ($typeconfig['forcessl'] == '1')) {
|
||||
if (!empty($instance->securetoolurl)) {
|
||||
$endpoint = trim($instance->securetoolurl);
|
||||
}
|
||||
|
||||
$endpoint = lti_ensure_url_is_https($endpoint);
|
||||
} else {
|
||||
if (!strstr($endpoint, '://')) {
|
||||
$endpoint = 'http://' . $endpoint;
|
||||
}
|
||||
}
|
||||
|
||||
$orgid = (isset($typeconfig['organizationid'])) ? $typeconfig['organizationid'] : '';
|
||||
|
||||
$course = $PAGE->course;
|
||||
$islti2 = isset($tool->toolproxyid);
|
||||
$instance = new stdClass();
|
||||
$instance->course = $courseid;
|
||||
$allparams = lti_build_request($instance, $typeconfig, $course, $id, $islti2);
|
||||
if ($islti2) {
|
||||
$requestparams = lti_build_request_lti2($tool, $allparams);
|
||||
} else {
|
||||
$requestparams = $allparams;
|
||||
}
|
||||
$requestparams = array_merge($requestparams, lti_build_standard_request(null, $orgid, $islti2));
|
||||
$customstr = '';
|
||||
if (isset($typeconfig['customparameters'])) {
|
||||
$customstr = $typeconfig['customparameters'];
|
||||
}
|
||||
$requestparams = array_merge($requestparams, lti_build_custom_parameters($toolproxy, $tool, $instance, $allparams, $customstr,
|
||||
'', $islti2));
|
||||
|
||||
// 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 ($islti2) {
|
||||
$requestparams['lti_version'] = 'LTI-2p0';
|
||||
} else {
|
||||
$requestparams['lti_version'] = 'LTI-1p0';
|
||||
}
|
||||
$requestparams['lti_message_type'] = 'ContentItemSelectionRequest';
|
||||
|
||||
$requestparams['accept_media_types'] = 'application/vnd.ims.lti.v1.ltilink';
|
||||
$requestparams['accept_presentation_document_targets'] = 'frame,iframe,window';
|
||||
$requestparams['accept_unsigned'] = 'false';
|
||||
$requestparams['accept_multiple'] = 'false';
|
||||
$requestparams['auto_create'] = 'true';
|
||||
$requestparams['can_confirm'] = 'false';
|
||||
$requestparams['accept_copy_advice'] = 'false';
|
||||
$requestparams['title'] = $title;
|
||||
|
||||
$returnurlparams = array('course' => $courseid,
|
||||
'section' => $sectionid,
|
||||
'id' => $id,
|
||||
'sr' => $sectionreturn,
|
||||
'sesskey' => sesskey());
|
||||
|
||||
// Add the return URL. We send the launch container along to help us avoid frames-within-frames when the user returns.
|
||||
$url = new \moodle_url('/mod/lti/contentitem_return.php', $returnurlparams);
|
||||
$returnurl = $url->out(false);
|
||||
|
||||
if (isset($typeconfig['forcessl']) && ($typeconfig['forcessl'] == '1')) {
|
||||
$returnurl = lti_ensure_url_is_https($returnurl);
|
||||
}
|
||||
|
||||
$requestparams['content_item_return_url'] = $returnurl;
|
||||
|
||||
|
||||
$parms = lti_sign_parameters($requestparams, $endpoint, "POST", $key, $secret);
|
||||
|
||||
$endpointurl = new \moodle_url($endpoint);
|
||||
$endpointparams = $endpointurl->params();
|
||||
|
||||
// Strip querystring params in endpoint url from $parms to avoid duplication.
|
||||
if (!empty($endpointparams) && !empty($parms)) {
|
||||
foreach (array_keys($endpointparams) as $paramname) {
|
||||
if (isset($parms[$paramname])) {
|
||||
unset($parms[$paramname]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "<p id=\"id_warning\" style=\"display: none; color: red; font-weight: bold; margin-top: 1em; padding-top: 1em;\">\n";
|
||||
echo get_string('register_warning', 'lti');
|
||||
echo "\n</p>\n";
|
||||
|
||||
$loading = $OUTPUT->render(new \pix_icon('i/loading', '', 'moodle',
|
||||
array('style' => 'margin:auto;vertical-align:middle;margin-top:125px;',
|
||||
'opacity' => '0.5')));
|
||||
|
||||
echo "<p style=\"text-align:center;\">\n";
|
||||
echo $loading;
|
||||
echo "\n</p>\n";
|
||||
|
||||
$script = '
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function doReveal() {
|
||||
var el = document.getElementById(\'id_warning\');
|
||||
el.style.display = \'block\';
|
||||
}
|
||||
var mod_lti_timer = window.setTimeout(doReveal, 20000);
|
||||
//]]
|
||||
</script>
|
||||
';
|
||||
|
||||
echo $script;
|
||||
|
||||
$content = lti_post_launch_html($parms, $endpoint, false);
|
||||
|
||||
echo $content;
|
||||
+28
-178
@@ -24,203 +24,53 @@
|
||||
*/
|
||||
|
||||
require_once('../../config.php');
|
||||
require_once($CFG->dirroot . '/course/modlib.php');
|
||||
require_once($CFG->dirroot . '/mod/lti/lib.php');
|
||||
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||
require_once($CFG->dirroot . '/mod/lti/OAuth.php');
|
||||
require_once($CFG->dirroot . '/mod/lti/TrivialStore.php');
|
||||
|
||||
use moodle\mod\lti as lti;
|
||||
|
||||
$courseid = required_param('course', PARAM_INT);
|
||||
$sectionid = required_param('section', PARAM_INT);
|
||||
$id = required_param('id', PARAM_INT);
|
||||
$sectionreturn = required_param('sr', PARAM_INT);
|
||||
$courseid = required_param('course', PARAM_INT);
|
||||
$messagetype = required_param('lti_message_type', PARAM_TEXT);
|
||||
$version = required_param('lti_version', PARAM_TEXT);
|
||||
$consumer_key = required_param('oauth_consumer_key', PARAM_RAW);
|
||||
|
||||
$consumerkey = required_param('oauth_consumer_key', PARAM_RAW);
|
||||
$items = optional_param('content_items', '', PARAM_RAW);
|
||||
$errormsg = optional_param('lti_errormsg', '', PARAM_TEXT);
|
||||
$msg = optional_param('lti_msg', '', PARAM_TEXT);
|
||||
|
||||
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
|
||||
$module = $DB->get_record('modules', array('name' => 'lti'), '*', MUST_EXIST);
|
||||
$tool = lti_get_type($id);
|
||||
$typeconfig = lti_get_type_config($id);
|
||||
|
||||
require_login($course);
|
||||
require_sesskey();
|
||||
$context = context_course::instance($courseid);
|
||||
require_capability('moodle/course:manageactivities', $context);
|
||||
require_capability('mod/lti:addcoursetool', $context);
|
||||
|
||||
if (isset($tool->toolproxyid)) {
|
||||
$toolproxy = lti_get_tool_proxy($tool->toolproxyid);
|
||||
$key = $toolproxy->guid;
|
||||
$secret = $toolproxy->secret;
|
||||
} else {
|
||||
$toolproxy = null;
|
||||
if (!empty($instance->resourcekey)) {
|
||||
$key = $instance->resourcekey;
|
||||
} else if (!empty($typeconfig['resourcekey'])) {
|
||||
$key = $typeconfig['resourcekey'];
|
||||
} else {
|
||||
$key = '';
|
||||
}
|
||||
if (!empty($instance->password)) {
|
||||
$secret = $instance->password;
|
||||
} else if (!empty($typeconfig['password'])) {
|
||||
$secret = $typeconfig['password'];
|
||||
} else {
|
||||
$secret = '';
|
||||
$redirecturl = null;
|
||||
$returndata = null;
|
||||
if (empty($errormsg) && !empty($items)) {
|
||||
try {
|
||||
$returndata = lti_tool_configuration_from_content_item($id, $messagetype, $version, $consumerkey, $items);
|
||||
} catch (moodle_exception $e) {
|
||||
$errormsg = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
if ($consumer_key !== $key) {
|
||||
throw new Exception('Consumer key is incorrect.');
|
||||
}
|
||||
$pageurl = new moodle_url('/mod/lti/contentitem_return.php');
|
||||
$PAGE->set_url($pageurl);
|
||||
$PAGE->set_pagelayout('popup');
|
||||
echo $OUTPUT->header();
|
||||
|
||||
$store = new lti\TrivialOAuthDataStore();
|
||||
$store->add_consumer($key, $secret);
|
||||
// Call JS module to redirect the user to the course page or close the dialogue on error/cancel.
|
||||
$PAGE->requires->js_call_amd('mod_lti/contentitem_return', 'init', [$returndata]);
|
||||
|
||||
$server = new lti\OAuthServer($store);
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
$method = new lti\OAuthSignatureMethod_HMAC_SHA1();
|
||||
$server->add_signature_method($method);
|
||||
$request = lti\OAuthRequest::from_request();
|
||||
// Add messages to notification stack for rendering later.
|
||||
if ($errormsg) {
|
||||
// Content item selection has encountered an error.
|
||||
\core\notification::error($errormsg);
|
||||
|
||||
try {
|
||||
$server->verify_request($request);
|
||||
} catch (\Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
debugging($e->getMessage() . "\n");
|
||||
throw new lti\OAuthException("OAuth signature failed: " . $message);
|
||||
}
|
||||
|
||||
if ($items) {
|
||||
$items = json_decode($items);
|
||||
if ($items->{'@context'} !== 'http://purl.imsglobal.org/ctx/lti/v1/ContentItem') {
|
||||
throw new Exception('Invalid media type.');
|
||||
}
|
||||
if (!isset($items->{'@graph'}) || !is_array($items->{'@graph'}) || (count($items->{'@graph'}) > 1)) {
|
||||
throw new Exception('Invalid format.');
|
||||
}
|
||||
}
|
||||
|
||||
$continueurl = course_get_url($course, $sectionid, array('sr' => $sectionreturn));
|
||||
if (count($items->{'@graph'}) > 0) {
|
||||
foreach ($items->{'@graph'} as $item) {
|
||||
$moduleinfo = new stdClass();
|
||||
$moduleinfo->modulename = 'lti';
|
||||
$moduleinfo->name = '';
|
||||
if (isset($item->title)) {
|
||||
$moduleinfo->name = $item->title;
|
||||
}
|
||||
if (empty($moduleinfo->name)) {
|
||||
$moduleinfo->name = $tool->name;
|
||||
}
|
||||
$moduleinfo->module = $module->id;
|
||||
$moduleinfo->section = $sectionid;
|
||||
$moduleinfo->visible = 1;
|
||||
if (isset($item->url)) {
|
||||
$moduleinfo->toolurl = $item->url;
|
||||
$moduleinfo->typeid = 0;
|
||||
} else {
|
||||
$moduleinfo->typeid = $id;
|
||||
}
|
||||
$moduleinfo->instructorchoicesendname = LTI_SETTING_NEVER;
|
||||
$moduleinfo->instructorchoicesendemailaddr = LTI_SETTING_NEVER;
|
||||
$moduleinfo->instructorchoiceacceptgrades = LTI_SETTING_NEVER;
|
||||
$moduleinfo->launchcontainer = LTI_LAUNCH_CONTAINER_DEFAULT;
|
||||
if (isset($item->placementAdvice->presentationDocumentTarget)) {
|
||||
if ($item->placementAdvice->presentationDocumentTarget === 'window') {
|
||||
$moduleinfo->launchcontainer = LTI_LAUNCH_CONTAINER_WINDOW;
|
||||
} else if ($item->placementAdvice->presentationDocumentTarget === 'frame') {
|
||||
$moduleinfo->launchcontainer = LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS;
|
||||
} else if ($item->placementAdvice->presentationDocumentTarget === 'iframe') {
|
||||
$moduleinfo->launchcontainer = LTI_LAUNCH_CONTAINER_EMBED;
|
||||
}
|
||||
}
|
||||
if (isset($item->custom)) {
|
||||
$moduleinfo->instructorcustomparameters = '';
|
||||
$first = true;
|
||||
foreach ($item->custom as $key => $value) {
|
||||
if (!$first) {
|
||||
$moduleinfo->instructorcustomparameters .= "\n";
|
||||
}
|
||||
$moduleinfo->instructorcustomparameters .= "{$key}={$value}";
|
||||
$first = false;
|
||||
}
|
||||
}
|
||||
$moduleinfo = add_moduleinfo($moduleinfo, $course, null);
|
||||
}
|
||||
$clickhere = get_string('click_to_continue', 'lti', (object)array('link' => $continueurl->out()));
|
||||
} else {
|
||||
$clickhere = get_string('return_to_course', 'lti', (object)array('link' => $continueurl->out()));
|
||||
}
|
||||
|
||||
if (!empty($errormsg) || !empty($msg)) {
|
||||
|
||||
$url = new moodle_url('/mod/lti/contentitem_return.php',
|
||||
array('course' => $courseid));
|
||||
$PAGE->set_url($url);
|
||||
|
||||
$pagetitle = strip_tags($course->shortname);
|
||||
$PAGE->set_title($pagetitle);
|
||||
$PAGE->set_heading($course->fullname);
|
||||
|
||||
$PAGE->set_pagelayout('embedded');
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
if (!empty($lti) and !empty($context)) {
|
||||
echo $OUTPUT->heading(format_string($lti->name, true, array('context' => $context)));
|
||||
}
|
||||
|
||||
if (!empty($errormsg)) {
|
||||
|
||||
echo '<p style="color: #f00; font-weight: bold; margin: 1em;">';
|
||||
echo get_string('lti_launch_error', 'lti') . ' ';
|
||||
p($errormsg);
|
||||
echo "</p>\n";
|
||||
|
||||
} else if (!empty($returndata)) {
|
||||
// Means success.
|
||||
if (!$msg) {
|
||||
$msg = get_string('successfullyfetchedtoolconfigurationfromcontent', 'lti');
|
||||
}
|
||||
|
||||
if (!empty($msg)) {
|
||||
|
||||
echo '<p style="margin: 1em;">';
|
||||
p($msg);
|
||||
echo "</p>\n";
|
||||
|
||||
}
|
||||
|
||||
echo "<p style=\"margin: 1em;\">{$clickhere}</p>";
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
} else {
|
||||
|
||||
$url = $continueurl->out();
|
||||
|
||||
echo '<html><body>';
|
||||
|
||||
$script = "
|
||||
<script type=\"text/javascript\">
|
||||
//<![CDATA[
|
||||
if(window != top){
|
||||
top.location.href = '{$url}';
|
||||
}
|
||||
//]]
|
||||
</script>
|
||||
";
|
||||
|
||||
$noscript = "
|
||||
<noscript>
|
||||
{$clickhere}
|
||||
</noscript>
|
||||
";
|
||||
|
||||
echo $script;
|
||||
echo $noscript;
|
||||
|
||||
echo '</body></html>';
|
||||
|
||||
\core\notification::success($msg);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="typeid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Basic LTI type id"/>
|
||||
<FIELD NAME="name" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false" COMMENT="Basic LTI param"/>
|
||||
<FIELD NAME="value" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="Param value"/>
|
||||
<FIELD NAME="value" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="Param value"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
|
||||
@@ -197,5 +197,20 @@ function xmldb_lti_upgrade($oldversion) {
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
if ($oldversion < 2016052301) {
|
||||
|
||||
// Changing type of field value on table lti_types_config to text.
|
||||
$table = new xmldb_table('lti_types_config');
|
||||
$field = new xmldb_field('value', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null, 'name');
|
||||
|
||||
// Launch change of type for field value.
|
||||
$dbman->change_field_type($table, $field);
|
||||
|
||||
// Lti savepoint reached.
|
||||
upgrade_mod_savepoint(true, 2016052301, 'lti');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+15
-3
@@ -101,7 +101,6 @@ class mod_lti_edit_types_form extends moodleform{
|
||||
$mform->addElement('textarea', 'lti_customparameters', get_string('custom', 'lti'), array('rows' => 4, 'cols' => 60));
|
||||
$mform->setType('lti_customparameters', PARAM_TEXT);
|
||||
$mform->addHelpButton('lti_customparameters', 'custom', 'lti');
|
||||
$mform->setAdvanced('lti_customparameters');
|
||||
|
||||
if (!empty($this->_customdata->isadmin)) {
|
||||
$options = array(
|
||||
@@ -137,9 +136,8 @@ class mod_lti_edit_types_form extends moodleform{
|
||||
$mform->setDefault('lti_launchcontainer', LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS);
|
||||
$mform->addHelpButton('lti_launchcontainer', 'default_launch_container', 'lti');
|
||||
$mform->setType('lti_launchcontainer', PARAM_INT);
|
||||
$mform->setAdvanced('lti_launchcontainer');
|
||||
|
||||
$mform->addElement('checkbox', 'lti_contentitem', ' ', ' ' . get_string('contentitem', 'lti'));
|
||||
$mform->addElement('advcheckbox', 'lti_contentitem', get_string('contentitem', 'lti'));
|
||||
$mform->addHelpButton('lti_contentitem', 'contentitem', 'lti');
|
||||
$mform->setAdvanced('lti_contentitem');
|
||||
if ($istool) {
|
||||
@@ -238,4 +236,18 @@ class mod_lti_edit_types_form extends moodleform{
|
||||
$this->add_action_buttons();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the data of the submitted form.
|
||||
*
|
||||
* @return stdClass
|
||||
*/
|
||||
public function get_data() {
|
||||
$data = parent::get_data();
|
||||
if ($data && !empty($this->_customdata->istool)) {
|
||||
// Content item checkbox is disabled in tool settings, so this cannot be edited. Just unset it.
|
||||
unset($data->lti_contentitem);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-5
@@ -105,12 +105,10 @@ $string['configtoolurl'] = 'Default remote tool URL';
|
||||
$string['configtypes'] = 'Enable LTI applications';
|
||||
$string['configured'] = 'Configured';
|
||||
$string['confirmtoolactivation'] = 'Are you sure you would like to activate this tool?';
|
||||
$string['courseactivitiesorresources'] = 'Course activities or resources';
|
||||
$string['contentitem'] = 'Content-Item Message';
|
||||
$string['contentitem_help'] = 'If ticked, the option \'Configure tool from link\' will be available when adding an external tool.';
|
||||
$string['course_tool_types'] = 'Course tools';
|
||||
$string['configure_item'] = 'Configure item';
|
||||
$string['contentitem'] = 'Tool supports Content-Item message';
|
||||
$string['contentitem_help'] = 'If selected, the tool provider will participate in the creation of new links added to courses.';
|
||||
$string['course_tool_types'] = 'Course tool types';
|
||||
$string['courseactivitiesorresources'] = 'Course activities or resources';
|
||||
$string['courseid'] = 'Course ID number';
|
||||
$string['courseinformation'] = 'Course information';
|
||||
$string['courselink'] = 'Go to course';
|
||||
@@ -169,7 +167,12 @@ $string['enableemailnotification'] = 'Send notification emails';
|
||||
$string['enableemailnotification_help'] = 'If enabled, students will receive email notification when their tool submissions are graded.';
|
||||
$string['enterkeyandsecret'] = 'Enter your consumer key and shared secret';
|
||||
$string['errorbadurl'] = 'URL is not a valid tool URL or cartridge.';
|
||||
$string['errorincorrectconsumerkey'] = 'Consumer key is incorrect.';
|
||||
$string['errorinvaliddata'] = 'Invalid data: {$a}';
|
||||
$string['errorinvalidmediatype'] = 'Invalid media type: {$a}';
|
||||
$string['errorinvalidresponseformat'] = 'Invalid Content-Item response format.';
|
||||
$string['errormisconfig'] = 'Misconfigured tool. Please ask your Moodle administrator to fix the configuration of the tool.';
|
||||
$string['errortooltypenotfound'] = 'LTI tool type not found.';
|
||||
$string['existing_window'] = 'Existing window';
|
||||
$string['extensions'] = 'LTI extension services';
|
||||
$string['external_tool_type'] = 'Preconfigured tool';
|
||||
@@ -407,6 +410,7 @@ $string['secure_launch_url'] = 'Secure launch URL';
|
||||
$string['secure_launch_url_help'] = 'Similar to the launch URL, but used instead of the launch URL if high security is required. Moodle will use the secure launch URL instead of the launch URL if the Moodle site is accessed through SSL, or if the tool configuration is set to always launch through SSL.
|
||||
|
||||
The launch URL may also be set to an https address to force launching through SSL, and this field may be left blank.';
|
||||
$string['selectcontent'] = 'Select content';
|
||||
$string['send'] = 'Send';
|
||||
$string['services'] = 'Services';
|
||||
$string['services_help'] = 'Select those services which you wish to offer to the tool provider. More than one service can be selected.';
|
||||
@@ -448,6 +452,7 @@ $string['submission'] = 'Submission';
|
||||
$string['submissions'] = 'Submissions';
|
||||
$string['submissionsfor'] = 'Submissions for {$a}';
|
||||
$string['successfullycreatedtooltype'] = 'Successfully created new tool!';
|
||||
$string['successfullyfetchedtoolconfigurationfromcontent'] = 'Successfully fetched tool configuration from the selected content.';
|
||||
$string['subplugintype_ltiresource'] = 'LTI service resource';
|
||||
$string['subplugintype_ltiresource_plural'] = 'LTI service resources';
|
||||
$string['subplugintype_ltiservice'] = 'LTI service';
|
||||
|
||||
+357
-20
@@ -55,6 +55,8 @@ use moodle\mod\lti as lti;
|
||||
|
||||
require_once($CFG->dirroot.'/mod/lti/OAuth.php');
|
||||
require_once($CFG->libdir.'/weblib.php');
|
||||
require_once($CFG->dirroot . '/course/modlib.php');
|
||||
require_once($CFG->dirroot . '/mod/lti/TrivialStore.php');
|
||||
|
||||
define('LTI_URL_DOMAIN_REGEX', '/(?:https?:\/\/)?(?:www\.)?([^\/]+)(?:\/|$)/i');
|
||||
|
||||
@@ -83,6 +85,9 @@ define('LTI_COURSEVISIBLE_NO', 0);
|
||||
define('LTI_COURSEVISIBLE_PRECONFIGURED', 1);
|
||||
define('LTI_COURSEVISIBLE_ACTIVITYCHOOSER', 2);
|
||||
|
||||
define('LTI_VERSION_1', 'LTI-1p0');
|
||||
define('LTI_VERSION_2', 'LTI-2p0');
|
||||
|
||||
/**
|
||||
* Return the launch data required for opening the external tool.
|
||||
*
|
||||
@@ -396,17 +401,6 @@ function lti_build_request($instance, $typeconfig, $course, $typeid = null, $isl
|
||||
$intro = str_replace("\n", "\r\n", $intro);
|
||||
$requestparams['resource_link_description'] = $intro;
|
||||
}
|
||||
if (!empty($instance->servicesalt)) {
|
||||
$placementsecret = $instance->servicesalt;
|
||||
if ( isset($placementsecret) && ($islti2 ||
|
||||
$typeconfig['acceptgrades'] == LTI_SETTING_ALWAYS ||
|
||||
($typeconfig['acceptgrades'] == LTI_SETTING_DELEGATE &&
|
||||
$instance->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS))) {
|
||||
|
||||
$sourcedid = json_encode(lti_build_sourcedid($instance->id, $USER->id, $placementsecret, $typeid));
|
||||
$requestparams['lis_result_sourcedid'] = $sourcedid;
|
||||
}
|
||||
}
|
||||
if (!empty($instance->id)) {
|
||||
$requestparams['resource_link_id'] = $instance->id;
|
||||
}
|
||||
@@ -420,10 +414,11 @@ function lti_build_request($instance, $typeconfig, $course, $typeid = null, $isl
|
||||
$requestparams['lis_course_section_sourcedid'] = $course->idnumber;
|
||||
}
|
||||
|
||||
if ( !empty($instance->id) && isset($placementsecret) && ($islti2 ||
|
||||
$typeconfig['acceptgrades'] == LTI_SETTING_ALWAYS ||
|
||||
($typeconfig['acceptgrades'] == LTI_SETTING_DELEGATE && $instance->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS))) {
|
||||
|
||||
if (!empty($instance->id) && !empty($instance->servicesalt) && ($islti2 ||
|
||||
$typeconfig['acceptgrades'] == LTI_SETTING_ALWAYS ||
|
||||
($typeconfig['acceptgrades'] == LTI_SETTING_DELEGATE && $instance->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS))
|
||||
) {
|
||||
$placementsecret = $instance->servicesalt;
|
||||
$sourcedid = json_encode(lti_build_sourcedid($instance->id, $USER->id, $placementsecret, $typeid));
|
||||
$requestparams['lis_result_sourcedid'] = $sourcedid;
|
||||
|
||||
@@ -445,7 +440,9 @@ function lti_build_request($instance, $typeconfig, $course, $typeid = null, $isl
|
||||
|
||||
// Send user's name and email data if appropriate.
|
||||
if ($islti2 || $typeconfig['sendname'] == LTI_SETTING_ALWAYS ||
|
||||
( $typeconfig['sendname'] == LTI_SETTING_DELEGATE && $instance->instructorchoicesendname == LTI_SETTING_ALWAYS ) ) {
|
||||
($typeconfig['sendname'] == LTI_SETTING_DELEGATE && isset($instance->instructorchoicesendname)
|
||||
&& $instance->instructorchoicesendname == LTI_SETTING_ALWAYS)
|
||||
) {
|
||||
$requestparams['lis_person_name_given'] = $USER->firstname;
|
||||
$requestparams['lis_person_name_family'] = $USER->lastname;
|
||||
$requestparams['lis_person_name_full'] = $USER->firstname . ' ' . $USER->lastname;
|
||||
@@ -453,7 +450,9 @@ function lti_build_request($instance, $typeconfig, $course, $typeid = null, $isl
|
||||
}
|
||||
|
||||
if ($islti2 || $typeconfig['sendemailaddr'] == LTI_SETTING_ALWAYS ||
|
||||
($typeconfig['sendemailaddr'] == LTI_SETTING_DELEGATE && $instance->instructorchoicesendemailaddr == LTI_SETTING_ALWAYS)) {
|
||||
($typeconfig['sendemailaddr'] == LTI_SETTING_DELEGATE && isset($instance->instructorchoicesendemailaddr)
|
||||
&& $instance->instructorchoicesendemailaddr == LTI_SETTING_ALWAYS)
|
||||
) {
|
||||
$requestparams['lis_person_contact_email_primary'] = $USER->email;
|
||||
}
|
||||
|
||||
@@ -492,13 +491,14 @@ function lti_build_request_lti2($tool, $params) {
|
||||
/**
|
||||
* This function builds the standard parameters for an LTI 1 or 2 request that must be sent to the tool producer
|
||||
*
|
||||
* @param object $instance Basic LTI instance object
|
||||
* @param stdClass $instance Basic LTI instance object
|
||||
* @param string $orgid Organisation ID
|
||||
* @param boolean $islti2 True if an LTI 2 tool is being launched
|
||||
* @param string $messagetype The request message type. Defaults to basic-lti-launch-request if empty.
|
||||
*
|
||||
* @return array Request details
|
||||
*/
|
||||
function lti_build_standard_request($instance, $orgid, $islti2) {
|
||||
function lti_build_standard_request($instance, $orgid, $islti2, $messagetype = 'basic-lti-launch-request') {
|
||||
global $CFG;
|
||||
|
||||
$requestparams = array();
|
||||
@@ -525,7 +525,7 @@ function lti_build_standard_request($instance, $orgid, $islti2) {
|
||||
} else {
|
||||
$requestparams['lti_version'] = 'LTI-2p0';
|
||||
}
|
||||
$requestparams['lti_message_type'] = 'basic-lti-launch-request';
|
||||
$requestparams['lti_message_type'] = $messagetype;
|
||||
|
||||
if ($orgid) {
|
||||
$requestparams["tool_consumer_instance_guid"] = $orgid;
|
||||
@@ -586,6 +586,343 @@ function lti_build_custom_parameters($toolproxy, $tool, $instance, $params, $cus
|
||||
return $custom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a standard LTI Content-Item selection request.
|
||||
*
|
||||
* @param int $id The tool type ID.
|
||||
* @param stdClass $course The course object.
|
||||
* @param moodle_url $returnurl The return URL in the tool consumer (TC) that the tool provider (TP)
|
||||
* will use to return the Content-Item message.
|
||||
* @param string $title The tool's title, if available.
|
||||
* @param string $text The text to display to represent the content item. This value may be a long description of the content item.
|
||||
* @param array $mediatypes Array of MIME types types supported by the TC. If empty, the TC will support ltilink by default.
|
||||
* @param array $presentationtargets Array of ways in which the selected content item(s) can be requested to be opened
|
||||
* (via the presentationDocumentTarget element for a returned content item).
|
||||
* If empty, "frame", "iframe", and "window" will be supported by default.
|
||||
* @param bool $autocreate Indicates whether any content items returned by the TP would be automatically persisted without
|
||||
* @param bool $multiple Indicates whether the user should be permitted to select more than one item. False by default.
|
||||
* any option for the user to cancel the operation. False by default.
|
||||
* @param bool $unsigned Indicates whether the TC is willing to accept an unsigned return message, or not.
|
||||
* A signed message should always be required when the content item is being created automatically in the
|
||||
* TC without further interaction from the user. False by default.
|
||||
* @param bool $canconfirm Flag for can_confirm parameter. False by default.
|
||||
* @param bool $copyadvice Indicates whether the TC is able and willing to make a local copy of a content item. False by default.
|
||||
* @return stdClass The object containing the signed request parameters and the URL to the TP's Content-Item selection interface.
|
||||
* @throws moodle_exception When the LTI tool type does not exist.`
|
||||
* @throws coding_exception For invalid media type and presentation target parameters.
|
||||
*/
|
||||
function lti_build_content_item_selection_request($id, $course, moodle_url $returnurl, $title = '', $text = '', $mediatypes = [],
|
||||
$presentationtargets = [], $autocreate = false, $multiple = false,
|
||||
$unsigned = false, $canconfirm = false, $copyadvice = false) {
|
||||
$tool = lti_get_type($id);
|
||||
// Validate parameters.
|
||||
if (!$tool) {
|
||||
throw new moodle_exception('errortooltypenotfound', 'mod_lti');
|
||||
}
|
||||
if (!is_array($mediatypes)) {
|
||||
throw new coding_exception('The list of accepted media types should be in an array');
|
||||
}
|
||||
if (!is_array($presentationtargets)) {
|
||||
throw new coding_exception('The list of accepted presentation targets should be in an array');
|
||||
}
|
||||
|
||||
// Check title. If empty, use the tool's name.
|
||||
if (empty($title)) {
|
||||
$title = $tool->name;
|
||||
}
|
||||
|
||||
$typeconfig = lti_get_type_config($id);
|
||||
$key = '';
|
||||
$secret = '';
|
||||
$islti2 = false;
|
||||
if (isset($tool->toolproxyid)) {
|
||||
$islti2 = true;
|
||||
$toolproxy = lti_get_tool_proxy($tool->toolproxyid);
|
||||
$key = $toolproxy->guid;
|
||||
$secret = $toolproxy->secret;
|
||||
} else {
|
||||
$toolproxy = null;
|
||||
if (!empty($typeconfig['resourcekey'])) {
|
||||
$key = $typeconfig['resourcekey'];
|
||||
}
|
||||
if (!empty($typeconfig['password'])) {
|
||||
$secret = $typeconfig['password'];
|
||||
}
|
||||
}
|
||||
$tool->enabledcapability = '';
|
||||
if (!empty($typeconfig['enabledcapability_ContentItemSelectionRequest'])) {
|
||||
$tool->enabledcapability = $typeconfig['enabledcapability_ContentItemSelectionRequest'];
|
||||
}
|
||||
|
||||
$tool->parameter = '';
|
||||
if (!empty($typeconfig['parameter_ContentItemSelectionRequest'])) {
|
||||
$tool->parameter = $typeconfig['parameter_ContentItemSelectionRequest'];
|
||||
}
|
||||
|
||||
// Set the tool URL.
|
||||
if (!empty($typeconfig['toolurl_ContentItemSelectionRequest'])) {
|
||||
$toolurl = new moodle_url($typeconfig['toolurl_ContentItemSelectionRequest']);
|
||||
} else {
|
||||
$toolurl = new moodle_url($typeconfig['toolurl']);
|
||||
}
|
||||
|
||||
// Check if SSL is forced.
|
||||
if (!empty($typeconfig['forcessl'])) {
|
||||
// Make sure the tool URL is set to https.
|
||||
if (strtolower($toolurl->get_scheme()) === 'http') {
|
||||
$toolurl->set_scheme('https');
|
||||
}
|
||||
// Make sure the return URL is set to https.
|
||||
if (strtolower($returnurl->get_scheme()) === 'http') {
|
||||
$returnurl->set_scheme('https');
|
||||
}
|
||||
}
|
||||
$toolurlout = $toolurl->out(false);
|
||||
|
||||
// Get base request parameters.
|
||||
$instance = new stdClass();
|
||||
$instance->course = $course->id;
|
||||
$requestparams = lti_build_request($instance, $typeconfig, $course, $id, $islti2);
|
||||
|
||||
// Get LTI2-specific request parameters and merge to the request parameters if applicable.
|
||||
if ($islti2) {
|
||||
$lti2params = lti_build_request_lti2($tool, $requestparams);
|
||||
$requestparams = array_merge($requestparams, $lti2params);
|
||||
}
|
||||
|
||||
// Get standard request parameters and merge to the request parameters.
|
||||
$orgid = !empty($typeconfig['organizationid']) ? $typeconfig['organizationid'] : '';
|
||||
$standardparams = lti_build_standard_request(null, $orgid, $islti2, 'ContentItemSelectionRequest');
|
||||
$requestparams = array_merge($requestparams, $standardparams);
|
||||
|
||||
// Get custom request parameters and merge to the request parameters.
|
||||
$customstr = '';
|
||||
if (!empty($typeconfig['customparameters'])) {
|
||||
$customstr = $typeconfig['customparameters'];
|
||||
}
|
||||
$customparams = lti_build_custom_parameters($toolproxy, $tool, $instance, $requestparams, $customstr, '', $islti2);
|
||||
$requestparams = array_merge($requestparams, $customparams);
|
||||
|
||||
// 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', [$instance, $toolurlout, $requestparams], []);
|
||||
|
||||
if (!empty($pluginparams) && is_array($pluginparams)) {
|
||||
$requestparams = array_merge($requestparams, $pluginparams);
|
||||
}
|
||||
}
|
||||
|
||||
// Media types. Set to ltilink by default if empty.
|
||||
if (empty($mediatypes)) {
|
||||
$mediatypes = [
|
||||
'application/vnd.ims.lti.v1.ltilink',
|
||||
];
|
||||
}
|
||||
$requestparams['accept_media_types'] = implode(',', $mediatypes);
|
||||
|
||||
// Presentation targets. Supports frame, iframe, window by default if empty.
|
||||
if (empty($presentationtargets)) {
|
||||
$presentationtargets = [
|
||||
'frame',
|
||||
'iframe',
|
||||
'window',
|
||||
];
|
||||
}
|
||||
$requestparams['accept_presentation_document_targets'] = implode(',', $presentationtargets);
|
||||
|
||||
// Other request parameters.
|
||||
$requestparams['accept_copy_advice'] = $copyadvice === true ? 'true' : 'false';
|
||||
$requestparams['accept_multiple'] = $multiple === true ? 'true' : 'false';
|
||||
$requestparams['accept_unsigned'] = $unsigned === true ? 'true' : 'false';
|
||||
$requestparams['auto_create'] = $autocreate === true ? 'true' : 'false';
|
||||
$requestparams['can_confirm'] = $canconfirm === true ? 'true' : 'false';
|
||||
$requestparams['content_item_return_url'] = $returnurl->out(false);
|
||||
$requestparams['title'] = $title;
|
||||
$requestparams['text'] = $text;
|
||||
$signedparams = lti_sign_parameters($requestparams, $toolurlout, 'POST', $key, $secret);
|
||||
$toolurlparams = $toolurl->params();
|
||||
|
||||
// Strip querystring params in endpoint url from $signedparams to avoid duplication.
|
||||
if (!empty($toolurlparams) && !empty($signedparams)) {
|
||||
foreach (array_keys($toolurlparams) as $paramname) {
|
||||
if (isset($signedparams[$paramname])) {
|
||||
unset($signedparams[$paramname]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for params that should not be passed. Unset if they are set.
|
||||
$unwantedparams = [
|
||||
'resource_link_id',
|
||||
'resource_link_title',
|
||||
'resource_link_description',
|
||||
'launch_presentation_return_url',
|
||||
'lis_result_sourcedid',
|
||||
];
|
||||
foreach ($unwantedparams as $param) {
|
||||
if (isset($signedparams[$param])) {
|
||||
unset($signedparams[$param]);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare result object.
|
||||
$result = new stdClass();
|
||||
$result->params = $signedparams;
|
||||
$result->url = $toolurlout;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the tool provider's response to the ContentItemSelectionRequest and builds the configuration data from the
|
||||
* selected content item. This configuration data can be then used when adding a tool into the course.
|
||||
*
|
||||
* @param int $typeid The tool type ID.
|
||||
* @param string $messagetype The value for the lti_message_type parameter.
|
||||
* @param string $ltiversion The value for the lti_version parameter.
|
||||
* @param string $consumerkey The consumer key.
|
||||
* @param string $contentitemsjson The JSON string for the content_items parameter.
|
||||
* @return stdClass The array of module information objects.
|
||||
* @throws moodle_exception
|
||||
* @throws lti\OAuthException
|
||||
*/
|
||||
function lti_tool_configuration_from_content_item($typeid, $messagetype, $ltiversion, $consumerkey, $contentitemsjson) {
|
||||
$tool = lti_get_type($typeid);
|
||||
// Validate parameters.
|
||||
if (!$tool) {
|
||||
throw new moodle_exception('errortooltypenotfound', 'mod_lti');
|
||||
}
|
||||
// Check lti_message_type. Show debugging if it's not set to ContentItemSelection.
|
||||
// No need to throw exceptions for now since lti_message_type does not seem to be used in this processing at the moment.
|
||||
if ($messagetype !== 'ContentItemSelection') {
|
||||
debugging("lti_message_type is invalid: {$messagetype}. It should be set to 'ContentItemSelection'.",
|
||||
DEBUG_DEVELOPER);
|
||||
}
|
||||
|
||||
$typeconfig = lti_get_type_config($typeid);
|
||||
|
||||
if (isset($tool->toolproxyid)) {
|
||||
$islti2 = true;
|
||||
$toolproxy = lti_get_tool_proxy($tool->toolproxyid);
|
||||
$key = $toolproxy->guid;
|
||||
$secret = $toolproxy->secret;
|
||||
} else {
|
||||
$islti2 = false;
|
||||
$toolproxy = null;
|
||||
if (!empty($typeconfig['resourcekey'])) {
|
||||
$key = $typeconfig['resourcekey'];
|
||||
} else {
|
||||
$key = '';
|
||||
}
|
||||
if (!empty($typeconfig['password'])) {
|
||||
$secret = $typeconfig['password'];
|
||||
} else {
|
||||
$secret = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Check LTI versions from our side and the response's side. Show debugging if they don't match.
|
||||
// No need to throw exceptions for now since LTI version does not seem to be used in this processing at the moment.
|
||||
$expectedversion = LTI_VERSION_1;
|
||||
if ($islti2) {
|
||||
$expectedversion = LTI_VERSION_2;
|
||||
}
|
||||
if ($ltiversion !== $expectedversion) {
|
||||
debugging("lti_version from response does not match the tool's configuration. Tool: {$expectedversion}," .
|
||||
" Response: {$ltiversion}", DEBUG_DEVELOPER);
|
||||
}
|
||||
|
||||
if ($consumerkey !== $key) {
|
||||
throw new moodle_exception('errorincorrectconsumerkey', 'mod_lti');
|
||||
}
|
||||
|
||||
$store = new lti\TrivialOAuthDataStore();
|
||||
$store->add_consumer($key, $secret);
|
||||
$server = new lti\OAuthServer($store);
|
||||
$method = new lti\OAuthSignatureMethod_HMAC_SHA1();
|
||||
$server->add_signature_method($method);
|
||||
$request = lti\OAuthRequest::from_request();
|
||||
try {
|
||||
$server->verify_request($request);
|
||||
} catch (lti\OAuthException $e) {
|
||||
throw new lti\OAuthException("OAuth signature failed: " . $e->getMessage());
|
||||
}
|
||||
|
||||
$items = json_decode($contentitemsjson);
|
||||
if (empty($items)) {
|
||||
throw new moodle_exception('errorinvaliddata', 'mod_lti', '', $contentitemsjson);
|
||||
}
|
||||
if ($items->{'@context'} !== 'http://purl.imsglobal.org/ctx/lti/v1/ContentItem') {
|
||||
throw new moodle_exception('errorinvalidmediatype', 'mod_lti', '', $items->{'@context'});
|
||||
}
|
||||
if (!isset($items->{'@graph'}) || !is_array($items->{'@graph'}) || (count($items->{'@graph'}) > 1)) {
|
||||
throw new moodle_exception('errorinvalidresponseformat', 'mod_lti');
|
||||
}
|
||||
|
||||
$config = null;
|
||||
if (!empty($items->{'@graph'})) {
|
||||
$item = $items->{'@graph'}[0];
|
||||
|
||||
$config = new stdClass();
|
||||
$config->name = '';
|
||||
if (isset($item->title)) {
|
||||
$config->name = $item->title;
|
||||
}
|
||||
if (empty($config->name)) {
|
||||
$config->name = $tool->name;
|
||||
}
|
||||
if (isset($item->text)) {
|
||||
$config->introeditor = [
|
||||
'text' => $item->text,
|
||||
'format' => FORMAT_PLAIN
|
||||
];
|
||||
}
|
||||
if (isset($item->icon->{'@id'})) {
|
||||
$iconurl = new moodle_url($item->icon->{'@id'});
|
||||
// Assign item's icon URL to secureicon or icon depending on its scheme.
|
||||
if (strtolower($iconurl->get_scheme()) === 'https') {
|
||||
$config->secureicon = $iconurl->out(false);
|
||||
} else {
|
||||
$config->icon = $iconurl->out(false);
|
||||
}
|
||||
}
|
||||
if (isset($item->url)) {
|
||||
$url = new moodle_url($item->url);
|
||||
// Assign item URL to securetoolurl or toolurl depending on its scheme.
|
||||
if (strtolower($url->get_scheme()) === 'https') {
|
||||
$config->securetoolurl = $url->out(false);
|
||||
} else {
|
||||
$config->toolurl = $url->out(false);
|
||||
}
|
||||
$config->typeid = 0;
|
||||
} else {
|
||||
$config->typeid = $typeid;
|
||||
}
|
||||
$config->instructorchoicesendname = LTI_SETTING_NEVER;
|
||||
$config->instructorchoicesendemailaddr = LTI_SETTING_NEVER;
|
||||
$config->instructorchoiceacceptgrades = LTI_SETTING_NEVER;
|
||||
$config->launchcontainer = LTI_LAUNCH_CONTAINER_DEFAULT;
|
||||
if (isset($item->placementAdvice->presentationDocumentTarget)) {
|
||||
if ($item->placementAdvice->presentationDocumentTarget === 'window') {
|
||||
$config->launchcontainer = LTI_LAUNCH_CONTAINER_WINDOW;
|
||||
} else if ($item->placementAdvice->presentationDocumentTarget === 'frame') {
|
||||
$config->launchcontainer = LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS;
|
||||
} else if ($item->placementAdvice->presentationDocumentTarget === 'iframe') {
|
||||
$config->launchcontainer = LTI_LAUNCH_CONTAINER_EMBED;
|
||||
}
|
||||
}
|
||||
if (isset($item->custom)) {
|
||||
$customparameters = [];
|
||||
foreach ($item->custom as $key => $value) {
|
||||
$customparameters[] = "{$key}={$value}";
|
||||
}
|
||||
$config->instructorcustomparameters = implode("\n", $customparameters);
|
||||
}
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
function lti_get_tool_table($tools, $id) {
|
||||
global $CFG, $OUTPUT, $USER;
|
||||
$html = '';
|
||||
|
||||
+34
-98
@@ -19,26 +19,11 @@
|
||||
* @package mod
|
||||
* @subpackage lti
|
||||
* @copyright Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com)
|
||||
* @copyright 2015 Vital Source Technologies http://vitalsource.com
|
||||
* @author Stephen Vickers
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
(function(){
|
||||
var Y;
|
||||
|
||||
var LTICIP = {
|
||||
BASE : 'base',
|
||||
};
|
||||
|
||||
var CSS = {
|
||||
PANEL : 'lti-contentitem-panel',
|
||||
HIDDEN : 'hidden',
|
||||
WRAP : 'lticip-wrap',
|
||||
HEADER : 'lticip-header',
|
||||
CLOSE : 'close',
|
||||
CONTENT : 'lticip-content',
|
||||
};
|
||||
|
||||
M.mod_lti = M.mod_lti || {};
|
||||
|
||||
M.mod_lti.LTI_SETTING_NEVER = 0;
|
||||
@@ -46,13 +31,6 @@
|
||||
M.mod_lti.LTI_SETTING_DELEGATE = 2;
|
||||
|
||||
M.mod_lti.editor = {
|
||||
_base : null,
|
||||
_escCloseEvent : null,
|
||||
_headingNode : null,
|
||||
_contentNode : null,
|
||||
_toolList : null,
|
||||
_onResize : null,
|
||||
_timer : null,
|
||||
init: function(yui3, settings){
|
||||
if(yui3){
|
||||
Y = yui3;
|
||||
@@ -71,22 +49,10 @@
|
||||
self.updateAutomaticToolMatch(Y.one('#id_securetoolurl'));
|
||||
};
|
||||
|
||||
var create = Y.Node.create;
|
||||
this._base = create('<div class="'+CSS.PANEL+' '+CSS.HIDDEN+'"></div>')
|
||||
.append(create('<div class="'+CSS.WRAP+'"></div>')
|
||||
.append(create('<div class="'+CSS.HEADER+' header"></div>')
|
||||
.append(create('<div class="'+CSS.CLOSE+'"></div>'))
|
||||
.append(create('<h2>'+M.util.get_string('configure_item', 'lti')+'</h2>')))
|
||||
.append(create('<div class="'+CSS.CONTENT+'">' +
|
||||
'<iframe class="id_contentitem_if" src="about:blank" style="width: 99%;"></iframe></div>'))
|
||||
);
|
||||
Y.one('.lti_contentitem').on('change', this.show, this);
|
||||
this._base.one('.'+CSS.HEADER+' .'+CSS.CLOSE).on('click', this.hide, this);
|
||||
this._headingNode = this._base.one('.'+CSS.HEADER);
|
||||
this._contentNode = this._base.one('.'+CSS.CONTENT);
|
||||
Y.one(document.body).append(this._base);
|
||||
|
||||
var contentItemButton = Y.one('[name="selectcontent"]');
|
||||
var contentItemUrl = contentItemButton.getAttribute('data-contentitemurl');
|
||||
var typeSelector = Y.one('#id_typeid');
|
||||
|
||||
typeSelector.on('change', function(e){
|
||||
updateToolMatches();
|
||||
|
||||
@@ -103,7 +69,28 @@
|
||||
allowgrades.set('checked', !self.getSelectedToolTypeOption().getAttribute('nogrades'));
|
||||
self.toggleGradeSection();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle configure from link button click.
|
||||
contentItemButton.on('click', function() {
|
||||
var contentItemId = self.getContentItemId();
|
||||
if (contentItemId) {
|
||||
// Get activity name and description values.
|
||||
var title = Y.one('#id_name').get('value').trim();
|
||||
var text = Y.one('#id_introeditor').get('value').trim();
|
||||
|
||||
// Set data to be POSTed.
|
||||
var postData = {
|
||||
id: contentItemId,
|
||||
course: self.settings.courseId,
|
||||
title: title,
|
||||
text: text
|
||||
};
|
||||
|
||||
require(['mod_lti/contentitem'], function(contentitem) {
|
||||
contentitem.init(contentItemUrl, postData);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.createTypeEditorButtons();
|
||||
@@ -531,68 +518,17 @@
|
||||
});
|
||||
},
|
||||
|
||||
contentItem: function(el){
|
||||
var opt = el.options[el.selectedIndex];
|
||||
if(opt.getAttribute('contentitem') == '1') {
|
||||
window.location.href = opt.getAttribute('contentitemurl') + '&title=' +
|
||||
encodeURIComponent(document.getElementById('id_name').value);
|
||||
/**
|
||||
* Gets the tool type ID of the selected tool that supports Content-Item selection.
|
||||
*
|
||||
* @returns {number|boolean} The ID of the tool type if it supports Content-Item selection. False, otherwise.
|
||||
*/
|
||||
getContentItemId: function() {
|
||||
var selected = this.getSelectedToolTypeOption();
|
||||
if (selected.getAttribute('data-contentitem')) {
|
||||
return selected.getAttribute('data-id');
|
||||
}
|
||||
},
|
||||
|
||||
show : function(e) {
|
||||
var opt = e.target.get('options').item(e.target.get('selectedIndex'));
|
||||
if (opt.getAttribute('contentitem') == '1') {
|
||||
this._toolList = e.target;
|
||||
e.preventDefault();
|
||||
e.halt();
|
||||
this._timer = window.setTimeout(this.doReveal, 20000);
|
||||
this._base.removeClass(CSS.HIDDEN);
|
||||
var w = this._base.get('winWidth') * 0.8;
|
||||
var h = this._base.get('winHeight') * 0.8;
|
||||
var x = (this._base.get('winWidth') - w) / 2;
|
||||
var y = (this._base.get('winHeight') - h) / 2;
|
||||
this._base.setStyle('width', '' + w + 'px');
|
||||
this._base.setStyle('height', '' + h + 'px');
|
||||
this._base.setXY([x,y]);
|
||||
|
||||
var padding = 15; //The bottom of the iframe wasn\'t visible on some themes. Probably because of border widths, etc.
|
||||
|
||||
var viewportHeight = h - parseInt(this._headingNode.getStyle('height')) - padding;
|
||||
|
||||
this._escCloseEvent = Y.on('key', this.hide, document.body, 'down:27', this);
|
||||
|
||||
var url = opt.getAttribute('contentitemurl') + '&title=' +
|
||||
encodeURIComponent(Y.one('#id_name').get('value'));
|
||||
|
||||
var ifr = Y.one('.id_contentitem_if');
|
||||
ifr.setAttribute('src', url);
|
||||
ifr.on('load', this.loaded, this);
|
||||
ifr.setStyle("height", viewportHeight);
|
||||
|
||||
var frm = Y.one('.mform');
|
||||
frm.reset();
|
||||
}
|
||||
},
|
||||
|
||||
doReveal : function() {
|
||||
var el = Y.one('#id_warning');
|
||||
el.removeClass(CSS.HIDDEN);
|
||||
},
|
||||
|
||||
loaded : function() {
|
||||
window.clearTimeout(this._timer);
|
||||
},
|
||||
|
||||
hide : function(e) {
|
||||
this.loaded();
|
||||
if (this._escCloseEvent) {
|
||||
this._escCloseEvent.detach();
|
||||
this._escCloseEvent = null;
|
||||
}
|
||||
this._base.addClass(CSS.HIDDEN);
|
||||
var ifr = Y.one('.id_contentitem_if');
|
||||
ifr.setAttribute('src', 'about:blank');
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
})();
|
||||
|
||||
+51
-17
@@ -43,8 +43,6 @@
|
||||
* @author Jordi Piguillem
|
||||
* @author Nikolas Galanis
|
||||
* @author Chris Scribner
|
||||
* @copyright 2015 Vital Source Technologies http://vitalsource.com
|
||||
* @author Stephen Vickers
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
@@ -56,12 +54,11 @@ require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
||||
class mod_lti_mod_form extends moodleform_mod {
|
||||
|
||||
public function definition() {
|
||||
global $DB, $PAGE, $OUTPUT, $USER, $COURSE, $sesskey, $section;
|
||||
global $PAGE, $OUTPUT, $COURSE;
|
||||
|
||||
if ($type = optional_param('type', false, PARAM_ALPHA)) {
|
||||
component_callback("ltisource_$type", 'add_instance_hook');
|
||||
}
|
||||
$sectionreturn = optional_param('sr', 0, PARAM_INT);
|
||||
|
||||
$this->typeid = 0;
|
||||
|
||||
@@ -109,6 +106,9 @@ class mod_lti_mod_form extends moodleform_mod {
|
||||
$mform->addHelpButton('typeid', 'external_tool_type', 'lti');
|
||||
$toolproxy = array();
|
||||
|
||||
// Array of tool type IDs that don't support ContentItemSelectionRequest.
|
||||
$noncontentitemtypes = ['0'];
|
||||
|
||||
foreach (lti_get_types_for_add_instance() as $id => $type) {
|
||||
if (!empty($type->toolproxyid)) {
|
||||
$toolproxy[] = $type->id;
|
||||
@@ -133,27 +133,46 @@ class mod_lti_mod_form extends moodleform_mod {
|
||||
}
|
||||
if (!$update && $id) {
|
||||
$config = lti_get_type_config($id);
|
||||
if (isset($config['contentitem']) && $config['contentitem']) {
|
||||
$contentitemurl = new moodle_url('/mod/lti/contentitem2.php',
|
||||
array('course' => $COURSE->id, 'section' => $section, 'id' => $id, 'sr' => $sectionreturn));
|
||||
$attributes['contentitem'] = 1;
|
||||
$attributes['contentitemurl'] = $contentitemurl->out(false);
|
||||
if (!empty($config['contentitem'])) {
|
||||
$attributes['data-contentitem'] = 1;
|
||||
$attributes['data-id'] = $id;
|
||||
} else {
|
||||
$noncontentitemtypes[] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
$tooltypes->addOption($type->name, $id, $attributes);
|
||||
}
|
||||
// Add button that launches the content-item selection dialogue.
|
||||
|
||||
// Set contentitem URL.
|
||||
$contentitemurl = new moodle_url('/mod/lti/contentitem.php');
|
||||
$contentbuttonattributes['data-contentitemurl'] = $contentitemurl->out(false);
|
||||
$mform->addElement('button', 'selectcontent', get_string('selectcontent', 'lti'), $contentbuttonattributes);
|
||||
if ($update) {
|
||||
$mform->disabledIf('selectcontent', 'typeid', 'neq', 0);
|
||||
} else {
|
||||
$mform->disabledIf('selectcontent', 'typeid', 'in', $noncontentitemtypes);
|
||||
}
|
||||
|
||||
$mform->addElement('text', 'toolurl', get_string('launch_url', 'lti'), array('size' => '64'));
|
||||
$mform->setType('toolurl', PARAM_URL);
|
||||
$mform->addHelpButton('toolurl', 'launch_url', 'lti');
|
||||
$mform->disabledIf('toolurl', 'typeid', 'neq', '0');
|
||||
if ($update) {
|
||||
$mform->disabledIf('toolurl', 'typeid', 'neq', 0);
|
||||
} else {
|
||||
$mform->disabledIf('toolurl', 'typeid', 'in', $noncontentitemtypes);
|
||||
}
|
||||
|
||||
$mform->addElement('text', 'securetoolurl', get_string('secure_launch_url', 'lti'), array('size' => '64'));
|
||||
$mform->setType('securetoolurl', PARAM_URL);
|
||||
$mform->setAdvanced('securetoolurl');
|
||||
$mform->addHelpButton('securetoolurl', 'secure_launch_url', 'lti');
|
||||
$mform->disabledIf('securetoolurl', 'typeid', 'neq', '0');
|
||||
if ($update) {
|
||||
$mform->disabledIf('securetoolurl', 'typeid', 'neq', 0);
|
||||
} else {
|
||||
$mform->disabledIf('securetoolurl', 'typeid', 'in', $noncontentitemtypes);
|
||||
}
|
||||
|
||||
$mform->addElement('hidden', 'urlmatchedtypeid', '', array( 'id' => 'id_urlmatchedtypeid' ));
|
||||
$mform->setType('urlmatchedtypeid', PARAM_INT);
|
||||
@@ -174,13 +193,21 @@ class mod_lti_mod_form extends moodleform_mod {
|
||||
$mform->setType('resourcekey', PARAM_TEXT);
|
||||
$mform->setAdvanced('resourcekey');
|
||||
$mform->addHelpButton('resourcekey', 'resourcekey', 'lti');
|
||||
$mform->disabledIf('resourcekey', 'typeid', 'neq', '0');
|
||||
if ($update) {
|
||||
$mform->disabledIf('resourcekey', 'typeid', 'neq', 0);
|
||||
} else {
|
||||
$mform->disabledIf('resourcekey', 'typeid', 'in', $noncontentitemtypes);
|
||||
}
|
||||
|
||||
$mform->addElement('passwordunmask', 'password', get_string('password', 'lti'));
|
||||
$mform->setType('password', PARAM_TEXT);
|
||||
$mform->setAdvanced('password');
|
||||
$mform->addHelpButton('password', 'password', 'lti');
|
||||
$mform->disabledIf('password', 'typeid', 'neq', '0');
|
||||
if ($update) {
|
||||
$mform->disabledIf('password', 'typeid', 'neq', 0);
|
||||
} else {
|
||||
$mform->disabledIf('password', 'typeid', 'in', $noncontentitemtypes);
|
||||
}
|
||||
|
||||
$mform->addElement('textarea', 'instructorcustomparameters', get_string('custom', 'lti'), array('rows' => 4, 'cols' => 60));
|
||||
$mform->setType('instructorcustomparameters', PARAM_TEXT);
|
||||
@@ -191,13 +218,21 @@ class mod_lti_mod_form extends moodleform_mod {
|
||||
$mform->setType('icon', PARAM_URL);
|
||||
$mform->setAdvanced('icon');
|
||||
$mform->addHelpButton('icon', 'icon_url', 'lti');
|
||||
$mform->disabledIf('icon', 'typeid', 'neq', '0');
|
||||
if ($update) {
|
||||
$mform->disabledIf('icon', 'typeid', 'neq', 0);
|
||||
} else {
|
||||
$mform->disabledIf('icon', 'typeid', 'in', $noncontentitemtypes);
|
||||
}
|
||||
|
||||
$mform->addElement('text', 'secureicon', get_string('secure_icon_url', 'lti'), array('size' => '64'));
|
||||
$mform->setType('secureicon', PARAM_URL);
|
||||
$mform->setAdvanced('secureicon');
|
||||
$mform->addHelpButton('secureicon', 'secure_icon_url', 'lti');
|
||||
$mform->disabledIf('secureicon', 'typeid', 'neq', '0');
|
||||
if ($update) {
|
||||
$mform->disabledIf('secureicon', 'typeid', 'neq', 0);
|
||||
} else {
|
||||
$mform->disabledIf('secureicon', 'typeid', 'in', $noncontentitemtypes);
|
||||
}
|
||||
|
||||
// Add privacy preferences fieldset where users choose whether to send their data.
|
||||
$mform->addElement('header', 'privacy', get_string('privacy', 'lti'));
|
||||
@@ -264,8 +299,7 @@ class mod_lti_mod_form extends moodleform_mod {
|
||||
array('tooltypedeleted', 'lti'),
|
||||
array('tooltypenotdeleted', 'lti'),
|
||||
array('tooltypeupdated', 'lti'),
|
||||
array('forced_help', 'lti'),
|
||||
array('configure_item', 'lti')
|
||||
array('forced_help', 'lti')
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -149,6 +149,10 @@ class toolproxy extends \mod_lti\local\ltiservice\resource_base {
|
||||
// Extract all launchable tools from the resource handlers.
|
||||
if ($ok) {
|
||||
$resources = $toolproxyjson->tool_profile->resource_handler;
|
||||
$messagetypes = [
|
||||
'basic-lti-launch-request',
|
||||
'ContentItemSelectionRequest',
|
||||
];
|
||||
foreach ($resources as $resource) {
|
||||
$launchable = false;
|
||||
$messages = array();
|
||||
@@ -165,8 +169,7 @@ class toolproxy extends \mod_lti\local\ltiservice\resource_base {
|
||||
}
|
||||
|
||||
foreach ($resource->message as $message) {
|
||||
if (($message->message_type === 'basic-lti-launch-request') ||
|
||||
($message->message_type === 'ContentItemSelectionRequest')) {
|
||||
if (in_array($message->message_type, $messagetypes)) {
|
||||
$launchable = $launchable || ($message->message_type === 'basic-lti-launch-request');
|
||||
$messages[$message->message_type] = $message;
|
||||
}
|
||||
@@ -196,26 +199,29 @@ class toolproxy extends \mod_lti\local\ltiservice\resource_base {
|
||||
}
|
||||
foreach ($tools as $tool) {
|
||||
$messages = $tool->messages;
|
||||
$launchrequest = $messages['basic-lti-launch-request'];
|
||||
$config = new \stdClass();
|
||||
$config->lti_toolurl = "{$baseurl}{$messages['basic-lti-launch-request']->path}";
|
||||
$config->lti_toolurl = "{$baseurl}{$launchrequest->path}";
|
||||
$config->lti_typename = $tool->name;
|
||||
$config->lti_coursevisible = 1;
|
||||
$config->lti_forcessl = 0;
|
||||
if (isset($messages['ContentItemSelectionRequest'])) {
|
||||
$contentitemrequest = $messages['ContentItemSelectionRequest'];
|
||||
$config->lti_contentitem = 1;
|
||||
if ($messages['basic-lti-launch-request']->path !== $messages['ContentItemSelectionRequest']->path) {
|
||||
$config->lti_toolurl_ContentItemSelectionRequest =
|
||||
"{$baseurl}{$messages['ContentItemSelectionRequest']->path}";
|
||||
if ($launchrequest->path !== $contentitemrequest->path) {
|
||||
$config->lti_toolurl_ContentItemSelectionRequest = $baseurl . $contentitemrequest->path;
|
||||
}
|
||||
$config->lti_enabledcapability_ContentItemSelectionRequest = implode("\n", $messages['ContentItemSelectionRequest']->enabled_capability);
|
||||
$config->lti_parameter_ContentItemSelectionRequest = self::lti_extract_parameters($messages['ContentItemSelectionRequest']->parameter);
|
||||
$contentitemcapabilities = implode("\n", $contentitemrequest->enabled_capability);
|
||||
$config->lti_enabledcapability_ContentItemSelectionRequest = $contentitemcapabilities;
|
||||
$contentitemparams = self::lti_extract_parameters($contentitemrequest->parameter);
|
||||
$config->lti_parameter_ContentItemSelectionRequest = $contentitemparams;
|
||||
}
|
||||
|
||||
$type = new \stdClass();
|
||||
$type->state = LTI_TOOL_STATE_PENDING;
|
||||
$type->toolproxyid = $toolproxy->id;
|
||||
$type->enabledcapability = implode("\n", $messages['basic-lti-launch-request']->enabled_capability);
|
||||
$type->parameter = self::lti_extract_parameters($messages['basic-lti-launch-request']->parameter);
|
||||
$type->enabledcapability = implode("\n", $launchrequest->enabled_capability);
|
||||
$type->parameter = self::lti_extract_parameters($launchrequest->parameter);
|
||||
|
||||
if (!empty($tool->iconpath)) {
|
||||
$type->icon = "{$baseurl}{$tool->iconpath}";
|
||||
|
||||
@@ -380,21 +380,3 @@
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Styles for mod_form.php to support Content-Item */
|
||||
.lti-contentitem-panel {width:400px;background-color:#666;position:absolute;top:10%;left:10%;border:1px solid #666;border-width:0 5px 5px 0;}
|
||||
.lti-contentitem-panel.hidden {display:none;}
|
||||
.lti-contentitem-panel .lticip-wrap {margin-top:-5px;margin-left:-5px;background-color:#FFF;border:1px solid #999;height:inherit;}
|
||||
|
||||
.lti-contentitem-panel .lticip-header {background-color:#eee;padding:1px;}
|
||||
.lti-contentitem-panel .lticip-header h2 {margin:3px 1em 0.5em 1em;font-size:1em;}
|
||||
.lti-contentitem-panel .lticip-header .close {width:25px;height:15px;position:absolute;top:2px;right:1em;cursor:pointer;background:url("../../../../mod/lti/pix/close.png") no-repeat scroll 0 0 transparent;}
|
||||
|
||||
.lti-contentitem-panel .lticip-content {text-align:center;position:relative;width:100%;border-top:1px solid #999;border-bottom:1px solid #999;}
|
||||
.lti-contentitem-panel .lticip-ajax-content {overflow:auto;}
|
||||
|
||||
.lti-contentitem-panel .lticip-loading-lightbox {position:absolute;width:100%;height:100%;top:0;left:0;background-color:#FFF;min-width:50px;min-height:50px;}
|
||||
.lti-contentitem-panel .lticip-loading-lightbox.hidden {display:none;}
|
||||
.lti-contentitem-panel .lticip-loading-lightbox .loading-icon {margin:auto;vertical-align:middle;margin-top:125px;}
|
||||
|
||||
.dir-rtl .lti-contentitem-panel .lticip-header .close {right: auto;left:1em;}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template mod_lti/contentitem
|
||||
|
||||
Provides a template for the creation of a new external tool instance via the content-item message.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* url The URL the iframe has to load.
|
||||
* postData The JSON object that contains the information to be POSTed for the ContentItemSelectionRequest.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"url": "/",
|
||||
"postData": {
|
||||
"id": "1",
|
||||
"course": "1",
|
||||
"title": "Sample title",
|
||||
"text": "This is a description"
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
<div id="contentitem-container" class="contentitem-container">
|
||||
<div class="contentitem-loading-container loading-screen">
|
||||
{{> mod_lti/loader }}
|
||||
<p class="loading-text">{{#str}} loadinghelp, moodle {{/str}}</p>
|
||||
<p id="tool-loading-failed" class="hidden">{{#str}} register_warning, mod_lti {{/str}}</p>
|
||||
</div>
|
||||
<div class="contentitem-page">
|
||||
<iframe id="contentitem-page-iframe" name="contentitem-page-iframe" class="hidden" tabindex="0">
|
||||
</iframe>
|
||||
<form target="contentitem-page-iframe" action="{{url}}" id="contentitem-request-form" method="post">
|
||||
<input type="hidden" name="id" value="{{postData.id}}" />
|
||||
<input type="hidden" name="course" value="{{postData.course}}" />
|
||||
<input type="hidden" name="title" value="{{postData.title}}" />
|
||||
<input type="hidden" name="text" value="{{postData.text}}" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{#js}}
|
||||
require(['jquery'], function($) {
|
||||
var loadingContainer = $('.contentitem-loading-container');
|
||||
var iframe = $('#contentitem-page-iframe');
|
||||
var timeout = setTimeout(function () {
|
||||
var failedContainer = $('#tool-loading-failed');
|
||||
failedContainer.removeClass('hidden');
|
||||
}, 20000);
|
||||
|
||||
// Submit form.
|
||||
$('#contentitem-request-form').submit();
|
||||
|
||||
iframe.on('load', function() {
|
||||
loadingContainer.addClass('hidden');
|
||||
iframe.removeClass('hidden');
|
||||
|
||||
// Adjust iframe's width to the fit the container's width.
|
||||
var containerWidth = $('div.contentitem-container').width();
|
||||
$('#contentitem-page-iframe').attr('width', containerWidth);
|
||||
|
||||
var dialogueContentDiv = $('div.contentitem-container').parent();
|
||||
var dialogueContainer = dialogueContentDiv.parent();
|
||||
// Adjust iframe's height to container's height - 55px (dialogue title bar + top/bottom margins).
|
||||
var containerHeight = dialogueContainer.height() - 55;
|
||||
$('#contentitem-page-iframe').attr('height', containerHeight);
|
||||
});
|
||||
});
|
||||
{{/js}}
|
||||
+1
-1
@@ -48,7 +48,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$plugin->version = 2016052300; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2016052301; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2016051900; // Requires this Moodle version.
|
||||
$plugin->component = 'mod_lti'; // Full name of the plugin (used for diagnostics).
|
||||
$plugin->cron = 0;
|
||||
|
||||
Reference in New Issue
Block a user