mirror of
https://github.com/moodle/moodle.git
synced 2025-08-04 16:36:37 +02:00
MDL-67473 mod_lti: deeplinking multiple support
AMOS BEGIN CPY [contentitem_help,mod_lti],[contentitem_deeplinking_help,mod_lti] AMOS END
This commit is contained in:
parent
5486b031ee
commit
957da4b564
15 changed files with 553 additions and 123 deletions
2
mod/lti/amd/build/contentitem.min.js
vendored
2
mod/lti/amd/build/contentitem.min.js
vendored
|
@ -1,2 +1,2 @@
|
||||||
define ("mod_lti/contentitem",["jquery","core/notification","core/str","core/templates","mod_lti/form-field","core/modal_factory","core/modal_events"],function(a,b,c,d,e,f,g){var h,i,j=[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),new e("grade_modgrade_point",e.TYPES.TEXT,!1,""),new e("lineitemresourceid",e.TYPES.TEXT,!0,""),new e("lineitemtag",e.TYPES.TEXT,!0,"")];window.processContentItemReturnData=function(a){if(h){h.hide()}for(var b in j){var c=j[b],d=null;if("undefined"!=typeof a[c.name]){d=a[c.name]}c.setFieldValue(d)}if(i){i()}};return{init:function init(a,e,j){i=j;var k=d.render("mod_lti/contentitem",{url:a,postData:e});if(h){h.setBody(k);h.show();return}c.get_string("selectcontent","lti").then(function(a){return f.create({title:a,body:k,large:!0})}).then(function(a){h=a;a.getRoot().on(g.hidden,function(){a.setBody("");b.fetchNotifications()});a.show()}).catch(b.exception)}}});
|
function asyncGeneratorStep(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function _asyncToGenerator(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var h=a.apply(b,c);function f(a){asyncGeneratorStep(h,d,e,f,g,"next",a)}function g(a){asyncGeneratorStep(h,d,e,f,g,"throw",a)}f(void 0)})}}define ("mod_lti/contentitem",["jquery","core/notification","core/str","core/templates","mod_lti/form-field","core/modal_factory","core/modal_events"],function(a,b,c,d,e,f,g){var h,i,j=[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),new e("grade_modgrade_point",e.TYPES.TEXT,!1,""),new e("lineitemresourceid",e.TYPES.TEXT,!0,""),new e("lineitemtag",e.TYPES.TEXT,!0,"")],k=function(a){a.setAttribute("hidden","true");a.setAttribute("aria-hidden","true");a.setAttribute("tab-index","-1")},l=function(a){a.removeAttribute("hidden");a.setAttribute("aria-hidden","false");a.setAttribute("tab-index","1")},m=function(){var a=_asyncToGenerator(regeneratorRuntime.mark(function a(b){var c,e,f,g,h,i,j;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:c=document.querySelector("#region-main-box form");e=c.querySelector("[data-attribute=\"dynamic-import\"]");f=c.querySelector("#fgroup_id_buttonar");g=c.querySelector("#id_submitbutton");Array.from(c.children).forEach(k);k(g);a.next=8;return d.renderForPromise("mod_lti/tool_deeplinking_results",{items:b});case 8:h=a.sent;i=h.html;j=h.js;a.next=13;return d.replaceNodeContents(e,i,j);case 13:l(e);l(f);case 15:case"end":return a.stop();}}},a)}));return function(){return a.apply(this,arguments)}}(),n=function(a){var b={};["name","toolurl","securetoolurl","instructorcustomparameters","icon","secureicon","launchcontainer"].forEach(function(c){b[c]=a[c]||""});b["introeditor[text]"]=a.introeditor?a.introeditor.text:"";b["introeditor[format]"]=a.introeditor?a.introeditor.format:"";if(1===a.instructorchoiceacceptgrades){b.instructorchoiceacceptgrades="1";b["grade[modgrade_point]"]=a.grade_modgrade_point||"100"}else{b.instructorchoiceacceptgrades="0"}return b};window.processContentItemReturnData=function(a){if(h){h.hide()}if(a.multiple){for(var b in j){j[b].setFieldValue("name"===j[b].name?"item":null)}var c=[];a.multiple.forEach(function(a){c.push(n(a))});m(a.multiple);var f=document.querySelector("#id_submitbutton2");f.onclick=function(a){a.preventDefault();f.disabled=!0;var b=new FormData(document.querySelector("form.mform")),d=function(){document.querySelector("#id_cancel").click()};c.reduce(function postVariant(a,c){Object.entries(c).forEach(function(a){return b.set(a[0],a[1])});var d=new URLSearchParams(b),e=function(){return fetch(document.location.pathname,{method:"post",body:d})};return a.then(e).catch(e)},Promise.resolve()).then(d).catch(d)}}else{for(b in j){var d=j[b],e=null;if("undefined"!=typeof a[d.name]){e=a[d.name]}d.setFieldValue(e)}d.setFieldValue(e)}if(i){i(a)}};return{init:function init(a,e,j){i=j;var k=d.render("mod_lti/contentitem",{url:a,postData:e});if(h){h.setBody(k);h.show();return}c.get_string("selectcontent","lti").then(function(a){return f.create({title:a,body:k,large:!0})}).then(function(a){h=a;a.getRoot().on(g.hidden,function(){a.setBody("");b.fetchNotifications()});a.show()}).catch(b.exception)}}});
|
||||||
//# sourceMappingURL=contentitem.min.js.map
|
//# sourceMappingURL=contentitem.min.js.map
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -109,8 +109,81 @@ define(
|
||||||
new FormField('lineitemtag', FormField.TYPES.TEXT, true, '')
|
new FormField('lineitemtag', FormField.TYPES.TEXT, true, '')
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the element, including aria and tab index.
|
||||||
|
* @param {HTMLElement} e the element to be hidden.
|
||||||
|
*/
|
||||||
|
const hideElement = (e) => {
|
||||||
|
e.setAttribute('hidden', 'true');
|
||||||
|
e.setAttribute('aria-hidden', 'true');
|
||||||
|
e.setAttribute('tab-index', '-1');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the element, including aria and tab index (set to 1).
|
||||||
|
* @param {HTMLElement} e the element to be shown.
|
||||||
|
*/
|
||||||
|
const showElement = (e) => {
|
||||||
|
e.removeAttribute('hidden');
|
||||||
|
e.setAttribute('aria-hidden', 'false');
|
||||||
|
e.setAttribute('tab-index', '1');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When more than one item needs to be added, the UI is simplified
|
||||||
|
* to just list the items to be added. Form is hidden and the only
|
||||||
|
* options is (save and return to course) or cancel.
|
||||||
|
* This function injects the summary to the form page, and hides
|
||||||
|
* the unneeded elements.
|
||||||
|
* @param {Object[]} items items to be added to the course.
|
||||||
|
*/
|
||||||
|
const showMultipleSummaryAndHideForm = async function(items) {
|
||||||
|
const form = document.querySelector('#region-main-box form');
|
||||||
|
const toolArea = form.querySelector('[data-attribute="dynamic-import"]');
|
||||||
|
const buttonGroup = form.querySelector('#fgroup_id_buttonar');
|
||||||
|
const submitAndLaunch = form.querySelector('#id_submitbutton');
|
||||||
|
Array.from(form.children).forEach(hideElement);
|
||||||
|
hideElement(submitAndLaunch);
|
||||||
|
const {html, js} = await templates.renderForPromise('mod_lti/tool_deeplinking_results',
|
||||||
|
{items: items});
|
||||||
|
|
||||||
|
await templates.replaceNodeContents(toolArea, html, js);
|
||||||
|
showElement(toolArea);
|
||||||
|
showElement(buttonGroup);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms config values aimed at populating the lti mod form to JSON variant
|
||||||
|
* which are used to insert more than one activity modules in one submit
|
||||||
|
* by applying variation to the submitted form.
|
||||||
|
* See /course/modedit.php.
|
||||||
|
* @private
|
||||||
|
* @param {Object} config transforms a config to an actual form data to be posted.
|
||||||
|
* @return {Object} variant that will be used to modify form values on submit.
|
||||||
|
*/
|
||||||
|
var configToVariant = (config) => {
|
||||||
|
const variant = {};
|
||||||
|
['name', 'toolurl', 'securetoolurl', 'instructorcustomparameters', 'icon', 'secureicon', 'launchcontainer'].forEach(
|
||||||
|
function(name) {
|
||||||
|
variant[name] = config[name] || '';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
variant['introeditor[text]'] = config.introeditor ? config.introeditor.text : '';
|
||||||
|
variant['introeditor[format]'] = config.introeditor ? config.introeditor.format : '';
|
||||||
|
if (config.instructorchoiceacceptgrades === 1) {
|
||||||
|
variant.instructorchoiceacceptgrades = '1';
|
||||||
|
variant['grade[modgrade_point]'] = config.grade_modgrade_point || '100';
|
||||||
|
} else {
|
||||||
|
variant.instructorchoiceacceptgrades = '0';
|
||||||
|
}
|
||||||
|
return variant;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Window function that can be called from mod_lti/contentitem_return to close the dialogue and process the return data.
|
* Window function that can be called from mod_lti/contentitem_return to close the dialogue and process the return data.
|
||||||
|
* If the return data contains more than one item, the form will not be populated with item data
|
||||||
|
* but rather hidden, and the item data will be added to a single input field used to create multiple
|
||||||
|
* instances in one request.
|
||||||
*
|
*
|
||||||
* @param {object} returnData The fetched configuration data from the Content-Item selection dialogue.
|
* @param {object} returnData The fetched configuration data from the Content-Item selection dialogue.
|
||||||
*/
|
*/
|
||||||
|
@ -118,20 +191,49 @@ define(
|
||||||
if (dialogue) {
|
if (dialogue) {
|
||||||
dialogue.hide();
|
dialogue.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate LTI configuration fields from return data.
|
|
||||||
var index;
|
var index;
|
||||||
for (index in ltiFormFields) {
|
if (returnData.multiple) {
|
||||||
var field = ltiFormFields[index];
|
for (index in ltiFormFields) {
|
||||||
var value = null;
|
// Name is required, so putting a placeholder as it will not be used
|
||||||
if (typeof returnData[field.name] !== 'undefined') {
|
// in multi-items add.
|
||||||
value = returnData[field.name];
|
ltiFormFields[index].setFieldValue(ltiFormFields[index].name === 'name' ? 'item' : null);
|
||||||
|
}
|
||||||
|
var variants = [];
|
||||||
|
returnData.multiple.forEach(function(v) {
|
||||||
|
variants.push(configToVariant(v));
|
||||||
|
});
|
||||||
|
showMultipleSummaryAndHideForm(returnData.multiple);
|
||||||
|
const submitAndCourse = document.querySelector('#id_submitbutton2');
|
||||||
|
submitAndCourse.onclick = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
submitAndCourse.disabled = true;
|
||||||
|
const fd = new FormData(document.querySelector('form.mform'));
|
||||||
|
const postVariant = (promise, variant) => {
|
||||||
|
Object.entries(variant).forEach((entry) => fd.set(entry[0], entry[1]));
|
||||||
|
const body = new URLSearchParams(fd);
|
||||||
|
const doPost = () => fetch(document.location.pathname, {method: 'post', body});
|
||||||
|
return promise.then(doPost).catch(doPost);
|
||||||
|
};
|
||||||
|
const backToCourse = () => {
|
||||||
|
document.querySelector("#id_cancel").click();
|
||||||
|
};
|
||||||
|
variants.reduce(postVariant, Promise.resolve()).then(backToCourse).catch(backToCourse);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Populate LTI configuration fields from return data.
|
||||||
|
for (index in ltiFormFields) {
|
||||||
|
var field = ltiFormFields[index];
|
||||||
|
var value = null;
|
||||||
|
if (typeof returnData[field.name] !== 'undefined') {
|
||||||
|
value = returnData[field.name];
|
||||||
|
}
|
||||||
|
field.setFieldValue(value);
|
||||||
}
|
}
|
||||||
field.setFieldValue(value);
|
field.setFieldValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doneCallback) {
|
if (doneCallback) {
|
||||||
doneCallback();
|
doneCallback(returnData);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -120,7 +120,7 @@ if ($ok) {
|
||||||
$title = base64_decode($titleb64);
|
$title = base64_decode($titleb64);
|
||||||
$text = base64_decode($textb64);
|
$text = base64_decode($textb64);
|
||||||
$request = lti_build_content_item_selection_request($typeid, $course, $returnurl, $title, $text,
|
$request = lti_build_content_item_selection_request($typeid, $course, $returnurl, $title, $text,
|
||||||
[], [], false, false, false, false, false, $nonce);
|
[], [], false, true, false, false, false, $nonce);
|
||||||
$endpoint = $request->url;
|
$endpoint = $request->url;
|
||||||
$params = $request->params;
|
$params = $request->params;
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,9 +214,8 @@ class mod_lti_edit_types_form extends moodleform {
|
||||||
$mform->addHelpButton('lti_launchcontainer', 'default_launch_container', 'lti');
|
$mform->addHelpButton('lti_launchcontainer', 'default_launch_container', 'lti');
|
||||||
$mform->setType('lti_launchcontainer', PARAM_INT);
|
$mform->setType('lti_launchcontainer', PARAM_INT);
|
||||||
|
|
||||||
$mform->addElement('advcheckbox', 'lti_contentitem', get_string('contentitem', 'lti'));
|
$mform->addElement('advcheckbox', 'lti_contentitem', get_string('contentitem_deeplinking', 'lti'));
|
||||||
$mform->addHelpButton('lti_contentitem', 'contentitem', 'lti');
|
$mform->addHelpButton('lti_contentitem', 'contentitem_deeplinking', 'lti');
|
||||||
$mform->setAdvanced('lti_contentitem');
|
|
||||||
if ($istool) {
|
if ($istool) {
|
||||||
$mform->disabledIf('lti_contentitem', null);
|
$mform->disabledIf('lti_contentitem', null);
|
||||||
}
|
}
|
||||||
|
@ -224,7 +223,6 @@ class mod_lti_edit_types_form extends moodleform {
|
||||||
$mform->addElement('text', 'lti_toolurl_ContentItemSelectionRequest',
|
$mform->addElement('text', 'lti_toolurl_ContentItemSelectionRequest',
|
||||||
get_string('toolurl_contentitemselectionrequest', 'lti'), array('size' => '64'));
|
get_string('toolurl_contentitemselectionrequest', 'lti'), array('size' => '64'));
|
||||||
$mform->setType('lti_toolurl_ContentItemSelectionRequest', PARAM_URL);
|
$mform->setType('lti_toolurl_ContentItemSelectionRequest', PARAM_URL);
|
||||||
$mform->setAdvanced('lti_toolurl_ContentItemSelectionRequest');
|
|
||||||
$mform->addHelpButton('lti_toolurl_ContentItemSelectionRequest', 'toolurl_contentitemselectionrequest', 'lti');
|
$mform->addHelpButton('lti_toolurl_ContentItemSelectionRequest', 'toolurl_contentitemselectionrequest', 'lti');
|
||||||
$mform->disabledIf('lti_toolurl_ContentItemSelectionRequest', 'lti_contentitem', 'notchecked');
|
$mform->disabledIf('lti_toolurl_ContentItemSelectionRequest', 'lti_contentitem', 'notchecked');
|
||||||
if ($istool) {
|
if ($istool) {
|
||||||
|
@ -271,7 +269,12 @@ class mod_lti_edit_types_form extends moodleform {
|
||||||
// LTI Extensions.
|
// LTI Extensions.
|
||||||
|
|
||||||
// Add grading preferences fieldset where the tool is allowed to return grades.
|
// Add grading preferences fieldset where the tool is allowed to return grades.
|
||||||
$mform->addElement('select', 'lti_acceptgrades', get_string('accept_grades_admin', 'lti'), $options);
|
$gradeoptions = array();
|
||||||
|
$gradeoptions[] = get_string('never', 'lti');
|
||||||
|
$gradeoptions[] = get_string('always', 'lti');
|
||||||
|
$gradeoptions[] = get_string('delegate_tool', 'lti');
|
||||||
|
|
||||||
|
$mform->addElement('select', 'lti_acceptgrades', get_string('accept_grades_admin', 'lti'), $gradeoptions);
|
||||||
$mform->setType('lti_acceptgrades', PARAM_INT);
|
$mform->setType('lti_acceptgrades', PARAM_INT);
|
||||||
$mform->setDefault('lti_acceptgrades', '2');
|
$mform->setDefault('lti_acceptgrades', '2');
|
||||||
$mform->addHelpButton('lti_acceptgrades', 'accept_grades_admin', 'lti');
|
$mform->addHelpButton('lti_acceptgrades', 'accept_grades_admin', 'lti');
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
leaveblank,mod_lti
|
leaveblank,mod_lti
|
||||||
organizationid,mod_lti
|
organizationid,mod_lti
|
||||||
organizationid_help,mod_lti
|
organizationid_help,mod_lti
|
||||||
|
contentitem,mod_lti
|
||||||
|
contentitem_help,mod_lti
|
||||||
|
|
|
@ -108,8 +108,10 @@ $string['configtoolurl'] = 'Default remote tool URL';
|
||||||
$string['configtypes'] = 'Enable LTI applications';
|
$string['configtypes'] = 'Enable LTI applications';
|
||||||
$string['configured'] = 'Configured';
|
$string['configured'] = 'Configured';
|
||||||
$string['confirmtoolactivation'] = 'Are you sure you would like to activate this tool?';
|
$string['confirmtoolactivation'] = 'Are you sure you would like to activate this tool?';
|
||||||
$string['contentitem'] = 'Content-Item Message';
|
$string['contentitem_deeplinking'] = 'Supports Deep Linking (Content-Item Message)';
|
||||||
$string['contentitem_help'] = 'If ticked, the option \'Select content\' will be available when adding an external tool.';
|
$string['contentitem_deeplinking_help'] = 'If ticked, the option \'Select content\' will be available when adding an external tool.';
|
||||||
|
$string['contentitem_multiple_description'] = 'The following items will be added to your course:';
|
||||||
|
$string['contentitem_multiple_graded'] = 'Graded activity (Maximum grade: {$a})';
|
||||||
$string['course_tool_types'] = 'Course tools';
|
$string['course_tool_types'] = 'Course tools';
|
||||||
$string['courseactivitiesorresources'] = 'Course activities or resources';
|
$string['courseactivitiesorresources'] = 'Course activities or resources';
|
||||||
$string['courseid'] = 'Course ID number';
|
$string['courseid'] = 'Course ID number';
|
||||||
|
@ -141,6 +143,7 @@ real estate to the tool, and others provide a more integrated feel with the Mood
|
||||||
Depending on the browser, it will open in a new tab or a popup window.
|
Depending on the browser, it will open in a new tab or a popup window.
|
||||||
It is possible that browsers will prevent the new window from opening.';
|
It is possible that browsers will prevent the new window from opening.';
|
||||||
$string['delegate'] = 'Delegate to teacher';
|
$string['delegate'] = 'Delegate to teacher';
|
||||||
|
$string['delegate_tool'] = 'As specified in Deep Linking definition or Delegate to teacher';
|
||||||
$string['delete'] = 'Delete';
|
$string['delete'] = 'Delete';
|
||||||
$string['delete_confirmation'] = 'Are you sure you want to delete this preconfigured tool?';
|
$string['delete_confirmation'] = 'Are you sure you want to delete this preconfigured tool?';
|
||||||
$string['deletetype'] = 'Delete preconfigured tool';
|
$string['deletetype'] = 'Delete preconfigured tool';
|
||||||
|
@ -593,3 +596,8 @@ $string['organizationid'] = 'Organisation ID';
|
||||||
$string['organizationid_help'] = 'A unique identifier for this Moodle instance. Typically, the DNS name of the organisation is used.
|
$string['organizationid_help'] = 'A unique identifier for this Moodle instance. Typically, the DNS name of the organisation is used.
|
||||||
|
|
||||||
If this field is left blank, the host name of this Moodle site will be used as the default value.';
|
If this field is left blank, the host name of this Moodle site will be used as the default value.';
|
||||||
|
|
||||||
|
// Deprecated since Moodle 3.10.
|
||||||
|
$string['contentitem'] = 'Content-Item Message';
|
||||||
|
$string['contentitem_help'] = 'If ticked, the option \'Select content\' will be available when adding an external tool.';
|
||||||
|
|
||||||
|
|
|
@ -1083,7 +1083,7 @@ function lti_build_custom_parameters($toolproxy, $tool, $instance, $params, $cus
|
||||||
* @throws coding_exception For invalid media type and presentation target parameters.
|
* @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 = [],
|
function lti_build_content_item_selection_request($id, $course, moodle_url $returnurl, $title = '', $text = '', $mediatypes = [],
|
||||||
$presentationtargets = [], $autocreate = false, $multiple = false,
|
$presentationtargets = [], $autocreate = false, $multiple = true,
|
||||||
$unsigned = false, $canconfirm = false, $copyadvice = false, $nonce = '') {
|
$unsigned = false, $canconfirm = false, $copyadvice = false, $nonce = '') {
|
||||||
global $USER;
|
global $USER;
|
||||||
|
|
||||||
|
@ -1421,6 +1421,109 @@ function lti_verify_jwt_signature($typeid, $consumerkey, $jwtparam) {
|
||||||
return $tool;
|
return $tool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts LTI 1.1 Content Item for LTI Link to Form data.
|
||||||
|
*
|
||||||
|
* @param object $tool Tool for which the item is created for.
|
||||||
|
* @param object $typeconfig The tool configuration.
|
||||||
|
* @param object $item Item populated from JSON to be converted to Form form
|
||||||
|
*
|
||||||
|
* @return stdClass Form config for the item
|
||||||
|
*/
|
||||||
|
function content_item_to_form(object $tool, object $typeconfig, object $item) : stdClass {
|
||||||
|
$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
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$config->introeditor = [
|
||||||
|
'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);
|
||||||
|
$config->toolurl = $url->out(false);
|
||||||
|
$config->typeid = 0;
|
||||||
|
} else {
|
||||||
|
$config->typeid = $tool->id;
|
||||||
|
}
|
||||||
|
$config->instructorchoiceacceptgrades = LTI_SETTING_NEVER;
|
||||||
|
$islti2 = $tool->ltiversion === LTI_VERSION_2;
|
||||||
|
if (!$islti2 && isset($typeconfig->lti_acceptgrades)) {
|
||||||
|
$acceptgrades = $typeconfig->lti_acceptgrades;
|
||||||
|
if ($acceptgrades == LTI_SETTING_ALWAYS) {
|
||||||
|
// We create a line item regardless if the definition contains one or not.
|
||||||
|
$config->instructorchoiceacceptgrades = LTI_SETTING_ALWAYS;
|
||||||
|
$config->grade_modgrade_point = 100;
|
||||||
|
}
|
||||||
|
if ($acceptgrades == LTI_SETTING_DELEGATE || $acceptgrades == LTI_SETTING_ALWAYS) {
|
||||||
|
if (isset($item->lineItem)) {
|
||||||
|
$lineitem = $item->lineItem;
|
||||||
|
$config->instructorchoiceacceptgrades = LTI_SETTING_ALWAYS;
|
||||||
|
$maxscore = 100;
|
||||||
|
if (isset($lineitem->scoreConstraints)) {
|
||||||
|
$sc = $lineitem->scoreConstraints;
|
||||||
|
if (isset($sc->totalMaximum)) {
|
||||||
|
$maxscore = $sc->totalMaximum;
|
||||||
|
} else if (isset($sc->normalMaximum)) {
|
||||||
|
$maxscore = $sc->normalMaximum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$config->grade_modgrade_point = $maxscore;
|
||||||
|
$config->lineitemresourceid = '';
|
||||||
|
$config->lineitemtag = '';
|
||||||
|
if (isset($lineitem->assignedActivity) && isset($lineitem->assignedActivity->activityId)) {
|
||||||
|
$config->lineitemresourceid = $lineitem->assignedActivity->activityId?:'';
|
||||||
|
}
|
||||||
|
if (isset($lineitem->tag)) {
|
||||||
|
$config->lineitemtag = $lineitem->tag?:'';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$config->instructorchoicesendname = LTI_SETTING_NEVER;
|
||||||
|
$config->instructorchoicesendemailaddr = 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);
|
||||||
|
}
|
||||||
|
// Including a JSON version of the form data to support adding many items in one submit.
|
||||||
|
$config->contentitemjson = json_encode($item);
|
||||||
|
return $config;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes the tool provider's response to the ContentItemSelectionRequest and builds the configuration data from the
|
* 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.
|
* selected content item. This configuration data can be then used when adding a tool into the course.
|
||||||
|
@ -1460,97 +1563,24 @@ function lti_tool_configuration_from_content_item($typeid, $messagetype, $ltiver
|
||||||
if (empty($items)) {
|
if (empty($items)) {
|
||||||
throw new moodle_exception('errorinvaliddata', 'mod_lti', '', $contentitemsjson);
|
throw new moodle_exception('errorinvaliddata', 'mod_lti', '', $contentitemsjson);
|
||||||
}
|
}
|
||||||
if (!isset($items->{'@graph'}) || !is_array($items->{'@graph'}) || (count($items->{'@graph'}) > 1)) {
|
if (!isset($items->{'@graph'}) || !is_array($items->{'@graph'})) {
|
||||||
throw new moodle_exception('errorinvalidresponseformat', 'mod_lti');
|
throw new moodle_exception('errorinvalidresponseformat', 'mod_lti');
|
||||||
}
|
}
|
||||||
|
|
||||||
$config = null;
|
$config = null;
|
||||||
if (!empty($items->{'@graph'})) {
|
$items = $items->{'@graph'};
|
||||||
$item = $items->{'@graph'}[0];
|
if (!empty($items)) {
|
||||||
$typeconfig = lti_get_type_type_config($tool->id);
|
$typeconfig = lti_get_type_type_config($tool->id);
|
||||||
|
if (count($items) == 1) {
|
||||||
$config = new stdClass();
|
$config = content_item_to_form($tool, $typeconfig, $items[0]);
|
||||||
$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);
|
|
||||||
$config->toolurl = $url->out(false);
|
|
||||||
$config->typeid = 0;
|
|
||||||
} else {
|
} else {
|
||||||
$config->typeid = $tool->id;
|
$multiple = [];
|
||||||
}
|
foreach ($items as $item) {
|
||||||
$config->instructorchoiceacceptgrades = LTI_SETTING_NEVER;
|
$multiple[] = content_item_to_form($tool, $typeconfig, $item);
|
||||||
if (!$islti2 && isset($typeconfig->lti_acceptgrades)) {
|
|
||||||
$acceptgrades = $typeconfig->lti_acceptgrades;
|
|
||||||
if ($acceptgrades == LTI_SETTING_ALWAYS) {
|
|
||||||
// We create a line item regardless if the definition contains one or not.
|
|
||||||
$config->instructorchoiceacceptgrades = LTI_SETTING_ALWAYS;
|
|
||||||
}
|
|
||||||
if ($acceptgrades == LTI_SETTING_DELEGATE || $acceptgrades == LTI_SETTING_ALWAYS) {
|
|
||||||
if (isset($item->lineItem)) {
|
|
||||||
$lineitem = $item->lineItem;
|
|
||||||
$config->instructorchoiceacceptgrades = LTI_SETTING_ALWAYS;
|
|
||||||
$maxscore = 100;
|
|
||||||
if (isset($lineitem->scoreConstraints)) {
|
|
||||||
$sc = $lineitem->scoreConstraints;
|
|
||||||
if (isset($sc->totalMaximum)) {
|
|
||||||
$maxscore = $sc->totalMaximum;
|
|
||||||
} else if (isset($sc->normalMaximum)) {
|
|
||||||
$maxscore = $sc->normalMaximum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$config->grade_modgrade_point = $maxscore;
|
|
||||||
$config->lineitemresourceid = '';
|
|
||||||
$config->lineitemtag = '';
|
|
||||||
if (isset($lineitem->assignedActivity) && isset($lineitem->assignedActivity->activityId)) {
|
|
||||||
$config->lineitemresourceid = $lineitem->assignedActivity->activityId ? : '';
|
|
||||||
}
|
|
||||||
if (isset($lineitem->tag)) {
|
|
||||||
$config->lineitemtag = $lineitem->tag ? : '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
$config = new stdClass();
|
||||||
|
$config->multiple = $multiple;
|
||||||
}
|
}
|
||||||
$config->instructorchoicesendname = LTI_SETTING_NEVER;
|
|
||||||
$config->instructorchoicesendemailaddr = 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);
|
|
||||||
}
|
|
||||||
$config->contentitemjson = json_encode($item);
|
|
||||||
}
|
}
|
||||||
return $config;
|
return $config;
|
||||||
}
|
}
|
||||||
|
@ -1587,7 +1617,35 @@ function lti_convert_content_items($param) {
|
||||||
$newitem->text = $item->html;
|
$newitem->text = $item->html;
|
||||||
unset($newitem->html);
|
unset($newitem->html);
|
||||||
}
|
}
|
||||||
if (isset($item->presentation)) {
|
if (isset($item->iframe)) {
|
||||||
|
// DeepLinking allows multiple options to be declared as supported.
|
||||||
|
// We favor iframe over new window if both are specified.
|
||||||
|
$newitem->placementAdvice = new stdClass();
|
||||||
|
$newitem->placementAdvice->presentationDocumentTarget = 'iframe';
|
||||||
|
if (isset($item->iframe->width)) {
|
||||||
|
$newitem->placementAdvice->displayWidth = $item->iframe->width;
|
||||||
|
}
|
||||||
|
if (isset($item->iframe->height)) {
|
||||||
|
$newitem->placementAdvice->displayHeight = $item->iframe->height;
|
||||||
|
}
|
||||||
|
unset($newitem->iframe);
|
||||||
|
unset($newitem->window);
|
||||||
|
} else if (isset($item->window)) {
|
||||||
|
$newitem->placementAdvice = new stdClass();
|
||||||
|
$newitem->placementAdvice->presentationDocumentTarget = 'window';
|
||||||
|
if (isset($item->window->targetName)) {
|
||||||
|
$newitem->placementAdvice->windowTarget = $item->window->targetName;
|
||||||
|
}
|
||||||
|
if (isset($item->window->width)) {
|
||||||
|
$newitem->placementAdvice->displayWidth = $item->window->width;
|
||||||
|
}
|
||||||
|
if (isset($item->window->height)) {
|
||||||
|
$newitem->placementAdvice->displayHeight = $item->window->height;
|
||||||
|
}
|
||||||
|
unset($newitem->window);
|
||||||
|
} else if (isset($item->presentation)) {
|
||||||
|
// This may have been part of an early draft but is not in the final spec
|
||||||
|
// so keeping it around for now in case it's actually been used.
|
||||||
$newitem->placementAdvice = new stdClass();
|
$newitem->placementAdvice = new stdClass();
|
||||||
if (isset($item->presentation->documentTarget)) {
|
if (isset($item->presentation->documentTarget)) {
|
||||||
$newitem->placementAdvice->presentationDocumentTarget = $item->presentation->documentTarget;
|
$newitem->placementAdvice->presentationDocumentTarget = $item->presentation->documentTarget;
|
||||||
|
|
|
@ -97,8 +97,10 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
require(['mod_lti/contentitem'], function(contentitem) {
|
require(['mod_lti/contentitem'], function(contentitem) {
|
||||||
contentitem.init(contentItemUrl, postData, function() {
|
contentitem.init(contentItemUrl, postData, function(returnData) {
|
||||||
M.mod_lti.editor.toggleGradeSection();
|
if (!returnData.multiple) {
|
||||||
|
M.mod_lti.editor.toggleGradeSection();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,8 +79,11 @@ class mod_lti_mod_form extends moodleform_mod {
|
||||||
$this->typeid = 0;
|
$this->typeid = 0;
|
||||||
|
|
||||||
$mform =& $this->_form;
|
$mform =& $this->_form;
|
||||||
|
|
||||||
// Adding the "general" fieldset, where all the common settings are shown.
|
// Adding the "general" fieldset, where all the common settings are shown.
|
||||||
|
$mform->addElement('html', "<div data-attribute='dynamic-import' hidden aria-hidden='true' role='alert'></div>");
|
||||||
$mform->addElement('header', 'general', get_string('general', 'form'));
|
$mform->addElement('header', 'general', get_string('general', 'form'));
|
||||||
|
|
||||||
// Adding the standard "name" field.
|
// Adding the standard "name" field.
|
||||||
$mform->addElement('text', 'name', get_string('basicltiname', 'lti'), array('size' => '64'));
|
$mform->addElement('text', 'name', get_string('basicltiname', 'lti'), array('size' => '64'));
|
||||||
$mform->setType('name', PARAM_TEXT);
|
$mform->setType('name', PARAM_TEXT);
|
||||||
|
|
59
mod/lti/templates/tool_deeplinking_results.mustache
Normal file
59
mod/lti/templates/tool_deeplinking_results.mustache
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
{{!
|
||||||
|
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/tool_deeplinking_results
|
||||||
|
|
||||||
|
This template lists the items that will be added to the course after
|
||||||
|
a deep linking flow.
|
||||||
|
|
||||||
|
Classes required for JS:
|
||||||
|
* none
|
||||||
|
|
||||||
|
Data attributes required for JS:
|
||||||
|
* none
|
||||||
|
|
||||||
|
Context variables required for this template:
|
||||||
|
*
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "Chapter 1",
|
||||||
|
"instructorchoiceacceptgrades": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Quiz 1",
|
||||||
|
"instructorchoiceacceptgrades": 1,
|
||||||
|
"grade_modgrade_point": 20.5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
<p>
|
||||||
|
{{#str}} contentitem_multiple_description, mod_lti {{/str}}
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
{{#items}}
|
||||||
|
<li>
|
||||||
|
<strong>{{{name}}}</strong>
|
||||||
|
{{#instructorchoiceacceptgrades}}
|
||||||
|
<em>{{#str}} contentitem_multiple_graded , mod_lti, {{{grade_modgrade_point}}} {{/str}}</em>
|
||||||
|
{{/instructorchoiceacceptgrades}}
|
||||||
|
</li>
|
||||||
|
{{/items}}
|
||||||
|
</ul>
|
|
@ -2,7 +2,7 @@
|
||||||
Feature: Content-Item support
|
Feature: Content-Item support
|
||||||
In order to easily add activities and content in a course from an external tool
|
In order to easily add activities and content in a course from an external tool
|
||||||
As a teacher
|
As a teacher
|
||||||
I need to utilise a tool that supports the Content-Item Message type
|
I need to utilise a tool that supports the Deep Linking (Content-Item Message) type
|
||||||
|
|
||||||
Background:
|
Background:
|
||||||
Given the following "users" exist:
|
Given the following "users" exist:
|
||||||
|
@ -16,25 +16,25 @@ Feature: Content-Item support
|
||||||
| teacher1 | C1 | editingteacher |
|
| teacher1 | C1 | editingteacher |
|
||||||
And I log in as "admin"
|
And I log in as "admin"
|
||||||
And I navigate to "Plugins > Activity modules > External tool > Manage tools" in site administration
|
And I navigate to "Plugins > Activity modules > External tool > Manage tools" in site administration
|
||||||
# Create tool type that supports content-item.
|
# Create tool type that supports deep linking.
|
||||||
And I follow "configure a tool manually"
|
And I follow "configure a tool manually"
|
||||||
And I set the field "Tool name" to "Teaching Tool 1"
|
And I set the field "Tool name" to "Teaching Tool 1"
|
||||||
And I set the field "Tool URL" to local url "/mod/lti/tests/fixtures/tool_provider.php"
|
And I set the field "Tool URL" to local url "/mod/lti/tests/fixtures/tool_provider.php"
|
||||||
And I set the field "Tool configuration usage" to "Show in activity chooser and as a preconfigured tool"
|
And I set the field "Tool configuration usage" to "Show in activity chooser and as a preconfigured tool"
|
||||||
And I expand all fieldsets
|
And I expand all fieldsets
|
||||||
And I set the field "Content-Item Message" to "1"
|
And I set the field "Supports Deep Linking (Content-Item Message)" to "1"
|
||||||
And I press "Save changes"
|
And I press "Save changes"
|
||||||
And I log out
|
And I log out
|
||||||
|
|
||||||
@javascript
|
@javascript
|
||||||
Scenario: Tool that supports Content-Item Message type should be able to configure a tool via the Select content button
|
Scenario: Tool that supports Deep Linking should be able to configure a tool via the Select content button
|
||||||
When I log in as "teacher1"
|
When I log in as "teacher1"
|
||||||
And I am on "Course 1" course homepage with editing mode on
|
And I am on "Course 1" course homepage with editing mode on
|
||||||
And I add a "Teaching Tool 1" to section "1"
|
And I add a "Teaching Tool 1" to section "1"
|
||||||
Then the "Select content" "button" should be enabled
|
Then the "Select content" "button" should be enabled
|
||||||
|
|
||||||
@javascript
|
@javascript
|
||||||
Scenario: Editing a tool's settings that was configured from a preconfigured tool that supports Content-Item.
|
Scenario: Editing a tool's settings that was configured from a preconfigured tool that supports Deep Linking.
|
||||||
When I log in as "teacher1"
|
When I log in as "teacher1"
|
||||||
And I am on "Course 1" course homepage with editing mode on
|
And I am on "Course 1" course homepage with editing mode on
|
||||||
And I add a "Teaching Tool 1" to section "1"
|
And I add a "Teaching Tool 1" to section "1"
|
||||||
|
@ -67,12 +67,12 @@ Feature: Content-Item support
|
||||||
And I set the field "Activity name" to "Test tool activity 1"
|
And I set the field "Activity name" to "Test tool activity 1"
|
||||||
And the "Select content" "button" should be disabled
|
And the "Select content" "button" should be disabled
|
||||||
And the "Tool URL" "field" should be enabled
|
And the "Tool URL" "field" should be enabled
|
||||||
# Selecting a tool that supports content-item: Select content button - enabled, Tool URL - enabled.
|
# Selecting a tool that supports deep linking: Select content button - enabled, Tool URL - enabled.
|
||||||
And I set the field "Preconfigured tool" to "Teaching Tool 1"
|
And I set the field "Preconfigured tool" to "Teaching Tool 1"
|
||||||
And I set the field "Activity name" to "Test tool activity 1"
|
And I set the field "Activity name" to "Test tool activity 1"
|
||||||
Then the "Select content" "button" should be enabled
|
Then the "Select content" "button" should be enabled
|
||||||
And the "Tool URL" "field" should be enabled
|
And the "Tool URL" "field" should be enabled
|
||||||
# Selecting a tool that does not support content-item: Select content button - disabled, Tool URL - disabled.
|
# Selecting a tool that does not support deep linking: Select content button - disabled, Tool URL - disabled.
|
||||||
And I set the field "Preconfigured tool" to "Teaching Tool 2"
|
And I set the field "Preconfigured tool" to "Teaching Tool 2"
|
||||||
And I set the field "Activity name" to "Test tool activity 1"
|
And I set the field "Activity name" to "Test tool activity 1"
|
||||||
And the "Select content" "button" should be disabled
|
And the "Select content" "button" should be disabled
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
@mod @mod_lti
|
@mod @mod_lti
|
||||||
Feature: Create/edit tool configuration that has Content-Item support
|
Feature: Create/edit tool configuration that has Deep Linking support
|
||||||
In order to provide external tools that support the Content-Item Message type for teachers and learners
|
In order to provide external tools that support Deep Linking for teachers and learners
|
||||||
As an admin
|
As an admin
|
||||||
I need to be able to configure external tool registrations that support the Content-Item Message type.
|
I need to be able to configure external tool registrations that support Deep Linking.
|
||||||
|
|
||||||
Background:
|
Background:
|
||||||
Given I log in as "admin"
|
Given I log in as "admin"
|
||||||
|
@ -20,13 +20,13 @@ Feature: Create/edit tool configuration that has Content-Item support
|
||||||
And I set the field "Tool URL" to local url "/mod/lti/tests/fixtures/tool_provider.php"
|
And I set the field "Tool URL" to local url "/mod/lti/tests/fixtures/tool_provider.php"
|
||||||
And I set the field "Tool configuration usage" to "Show in activity chooser and as a preconfigured tool"
|
And I set the field "Tool configuration usage" to "Show in activity chooser and as a preconfigured tool"
|
||||||
And I expand all fieldsets
|
And I expand all fieldsets
|
||||||
And I set the field "Content-Item Message" to "1"
|
And I set the field "Supports Deep Linking (Content-Item Message)" to "1"
|
||||||
And I press "Save changes"
|
And I press "Save changes"
|
||||||
And I follow "Edit"
|
And I follow "Edit"
|
||||||
And I expand all fieldsets
|
And I expand all fieldsets
|
||||||
Then the field "Content-Item Message" matches value "1"
|
Then the field "Supports Deep Linking (Content-Item Message)" matches value "1"
|
||||||
And I set the field "Content-Item Message" to "0"
|
And I set the field "Supports Deep Linking (Content-Item Message)" to "0"
|
||||||
And I press "Save changes"
|
And I press "Save changes"
|
||||||
And I follow "Edit"
|
And I follow "Edit"
|
||||||
And I expand all fieldsets
|
And I expand all fieldsets
|
||||||
And the field "Content-Item Message" matches value "0"
|
And the field "Supports Deep Linking (Content-Item Message)" matches value "0"
|
||||||
|
|
|
@ -377,7 +377,7 @@ class mod_lti_locallib_testcase extends advanced_testcase {
|
||||||
$this->assertEquals('frame,iframe,window', $params['accept_presentation_document_targets']);
|
$this->assertEquals('frame,iframe,window', $params['accept_presentation_document_targets']);
|
||||||
$this->assertEquals($returnurl->out(false), $params['content_item_return_url']);
|
$this->assertEquals($returnurl->out(false), $params['content_item_return_url']);
|
||||||
$this->assertEquals('false', $params['accept_unsigned']);
|
$this->assertEquals('false', $params['accept_unsigned']);
|
||||||
$this->assertEquals('false', $params['accept_multiple']);
|
$this->assertEquals('true', $params['accept_multiple']);
|
||||||
$this->assertEquals('false', $params['accept_copy_advice']);
|
$this->assertEquals('false', $params['accept_copy_advice']);
|
||||||
$this->assertEquals('false', $params['auto_create']);
|
$this->assertEquals('false', $params['auto_create']);
|
||||||
$this->assertEquals($type->name, $params['title']);
|
$this->assertEquals($type->name, $params['title']);
|
||||||
|
@ -1185,7 +1185,6 @@ MwIDAQAB
|
||||||
*/
|
*/
|
||||||
public function test_lti_verify_jwt_signature_no_public_key() {
|
public function test_lti_verify_jwt_signature_no_public_key() {
|
||||||
$this->resetAfterTest();
|
$this->resetAfterTest();
|
||||||
|
|
||||||
$this->setAdminUser();
|
$this->setAdminUser();
|
||||||
|
|
||||||
// Create a tool type, associated with that proxy.
|
// Create a tool type, associated with that proxy.
|
||||||
|
@ -1214,7 +1213,28 @@ MwIDAQAB
|
||||||
'url' => 'http://example.com/messages/launch',
|
'url' => 'http://example.com/messages/launch',
|
||||||
'title' => 'Test title',
|
'title' => 'Test title',
|
||||||
'text' => 'Test text',
|
'text' => 'Test text',
|
||||||
'frame' => []
|
'iframe' => []
|
||||||
|
];
|
||||||
|
$contentitems[] = [
|
||||||
|
'type' => 'ltiResourceLink',
|
||||||
|
'url' => 'http://example.com/messages/launch2',
|
||||||
|
'title' => 'Test title2',
|
||||||
|
'text' => 'Test text2',
|
||||||
|
'iframe' => [
|
||||||
|
'height' => 200,
|
||||||
|
'width' => 300
|
||||||
|
],
|
||||||
|
'window' => []
|
||||||
|
];
|
||||||
|
$contentitems[] = [
|
||||||
|
'type' => 'ltiResourceLink',
|
||||||
|
'url' => 'http://example.com/messages/launch3',
|
||||||
|
'title' => 'Test title3',
|
||||||
|
'text' => 'Test text3',
|
||||||
|
'window' => [
|
||||||
|
'targetName' => 'test-win',
|
||||||
|
'height' => 400
|
||||||
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$contentitems = json_encode($contentitems);
|
$contentitems = json_encode($contentitems);
|
||||||
|
@ -1231,18 +1251,186 @@ MwIDAQAB
|
||||||
$objgraph->url = 'http://example.com/messages/launch';
|
$objgraph->url = 'http://example.com/messages/launch';
|
||||||
$objgraph->title = 'Test title';
|
$objgraph->title = 'Test title';
|
||||||
$objgraph->text = 'Test text';
|
$objgraph->text = 'Test text';
|
||||||
$objgraph->frame = [];
|
$objgraph->placementAdvice = new stdClass();
|
||||||
|
$objgraph->placementAdvice->presentationDocumentTarget = 'iframe';
|
||||||
$objgraph->{$strtype} = 'LtiLinkItem';
|
$objgraph->{$strtype} = 'LtiLinkItem';
|
||||||
$objgraph->mediaType = 'application\/vnd.ims.lti.v1.ltilink';
|
$objgraph->mediaType = 'application\/vnd.ims.lti.v1.ltilink';
|
||||||
|
|
||||||
|
$objgraph2 = new stdClass();
|
||||||
|
$objgraph2->url = 'http://example.com/messages/launch2';
|
||||||
|
$objgraph2->title = 'Test title2';
|
||||||
|
$objgraph2->text = 'Test text2';
|
||||||
|
$objgraph2->placementAdvice = new stdClass();
|
||||||
|
$objgraph2->placementAdvice->presentationDocumentTarget = 'iframe';
|
||||||
|
$objgraph2->placementAdvice->displayHeight = 200;
|
||||||
|
$objgraph2->placementAdvice->displayWidth = 300;
|
||||||
|
$objgraph2->{$strtype} = 'LtiLinkItem';
|
||||||
|
$objgraph2->mediaType = 'application\/vnd.ims.lti.v1.ltilink';
|
||||||
|
|
||||||
|
$objgraph3 = new stdClass();
|
||||||
|
$objgraph3->url = 'http://example.com/messages/launch3';
|
||||||
|
$objgraph3->title = 'Test title3';
|
||||||
|
$objgraph3->text = 'Test text3';
|
||||||
|
$objgraph3->placementAdvice = new stdClass();
|
||||||
|
$objgraph3->placementAdvice->presentationDocumentTarget = 'window';
|
||||||
|
$objgraph3->placementAdvice->displayHeight = 400;
|
||||||
|
$objgraph3->placementAdvice->windowTarget = 'test-win';
|
||||||
|
$objgraph3->{$strtype} = 'LtiLinkItem';
|
||||||
|
$objgraph3->mediaType = 'application\/vnd.ims.lti.v1.ltilink';
|
||||||
|
|
||||||
$expected = new stdClass();
|
$expected = new stdClass();
|
||||||
$expected->{$strcontext} = 'http://purl.imsglobal.org/ctx/lti/v1/ContentItem';
|
$expected->{$strcontext} = 'http://purl.imsglobal.org/ctx/lti/v1/ContentItem';
|
||||||
$expected->{$strgraph} = [];
|
$expected->{$strgraph} = [];
|
||||||
$expected->{$strgraph}[] = $objgraph;
|
$expected->{$strgraph}[] = $objgraph;
|
||||||
|
$expected->{$strgraph}[] = $objgraph2;
|
||||||
|
$expected->{$strgraph}[] = $objgraph3;
|
||||||
|
|
||||||
$this->assertEquals($expected, $jsondecode);
|
$this->assertEquals($expected, $jsondecode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test adding a single gradable item through content item.
|
||||||
|
*/
|
||||||
|
public function test_lti_tool_configuration_from_content_item_single_gradable() {
|
||||||
|
$this->resetAfterTest();
|
||||||
|
$this->setAdminUser();
|
||||||
|
|
||||||
|
$type = new stdClass();
|
||||||
|
$type->name = "Test tool";
|
||||||
|
$type->baseurl = "http://example.com";
|
||||||
|
$config = new stdClass();
|
||||||
|
$config->lti_acceptgrades = LTI_SETTING_DELEGATE;
|
||||||
|
$typeid = lti_add_type($type, $config);
|
||||||
|
|
||||||
|
$generator = $this->getDataGenerator()->get_plugin_generator('mod_lti');
|
||||||
|
$contentitems = [];
|
||||||
|
$contentitems[] = [
|
||||||
|
'type' => 'ltiResourceLink',
|
||||||
|
'url' => 'http://example.com/messages/launch',
|
||||||
|
'title' => 'Test title',
|
||||||
|
'lineItem' => [
|
||||||
|
'resourceId' => 'r12345',
|
||||||
|
'tag' => 'final',
|
||||||
|
'scoreMaximum' => 10.0
|
||||||
|
],
|
||||||
|
'frame' => []
|
||||||
|
];
|
||||||
|
$contentitemsjson13 = json_encode($contentitems);
|
||||||
|
$json11 = lti_convert_content_items($contentitemsjson13);
|
||||||
|
|
||||||
|
$config = lti_tool_configuration_from_content_item($typeid,
|
||||||
|
'ContentItemSelection',
|
||||||
|
$type->ltiversion,
|
||||||
|
'ConsumerKey',
|
||||||
|
$json11);
|
||||||
|
|
||||||
|
$this->assertEquals($contentitems[0]['url'], $config->toolurl);
|
||||||
|
$this->assertEquals(LTI_SETTING_ALWAYS, $config->instructorchoiceacceptgrades);
|
||||||
|
$this->assertEquals($contentitems[0]['lineItem']['tag'], $config->lineitemtag);
|
||||||
|
$this->assertEquals($contentitems[0]['lineItem']['resourceId'], $config->lineitemresourceid);
|
||||||
|
$this->assertEquals($contentitems[0]['lineItem']['scoreMaximum'], $config->grade_modgrade_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test adding multiple gradable items through content item.
|
||||||
|
*/
|
||||||
|
public function test_lti_tool_configuration_from_content_item_multiple() {
|
||||||
|
$this->resetAfterTest();
|
||||||
|
$this->setAdminUser();
|
||||||
|
|
||||||
|
$type = new stdClass();
|
||||||
|
$type->name = "Test tool";
|
||||||
|
$type->baseurl = "http://example.com";
|
||||||
|
$config = new stdClass();
|
||||||
|
$config->lti_acceptgrades = LTI_SETTING_DELEGATE;
|
||||||
|
$typeid = lti_add_type($type, $config);
|
||||||
|
|
||||||
|
$generator = $this->getDataGenerator()->get_plugin_generator('mod_lti');
|
||||||
|
$contentitems = [];
|
||||||
|
$contentitems[] = [
|
||||||
|
'type' => 'ltiResourceLink',
|
||||||
|
'url' => 'http://example.com/messages/launch',
|
||||||
|
'title' => 'Test title',
|
||||||
|
'text' => 'Test text',
|
||||||
|
'icon' => [
|
||||||
|
'url' => 'http://lti.example.com/image.jpg',
|
||||||
|
'width' => 100
|
||||||
|
],
|
||||||
|
'frame' => []
|
||||||
|
];
|
||||||
|
$contentitems[] = [
|
||||||
|
'type' => 'ltiResourceLink',
|
||||||
|
'url' => 'http://example.com/messages/launchgraded',
|
||||||
|
'title' => 'Test Graded',
|
||||||
|
'lineItem' => [
|
||||||
|
'resourceId' => 'r12345',
|
||||||
|
'tag' => 'final',
|
||||||
|
'scoreMaximum' => 10.0
|
||||||
|
],
|
||||||
|
'frame' => []
|
||||||
|
];
|
||||||
|
$contentitemsjson13 = json_encode($contentitems);
|
||||||
|
$json11 = lti_convert_content_items($contentitemsjson13);
|
||||||
|
|
||||||
|
$config = lti_tool_configuration_from_content_item($typeid,
|
||||||
|
'ContentItemSelection',
|
||||||
|
$type->ltiversion,
|
||||||
|
'ConsumerKey',
|
||||||
|
$json11);
|
||||||
|
$this->assertNotNull($config->multiple);
|
||||||
|
$this->assertEquals(2, count( $config->multiple ));
|
||||||
|
$this->assertEquals($contentitems[0]['title'], $config->multiple[0]->name);
|
||||||
|
$this->assertEquals($contentitems[0]['url'], $config->multiple[0]->toolurl);
|
||||||
|
$this->assertEquals(LTI_SETTING_NEVER, $config->multiple[0]->instructorchoiceacceptgrades);
|
||||||
|
$this->assertEquals($contentitems[1]['url'], $config->multiple[1]->toolurl);
|
||||||
|
$this->assertEquals(LTI_SETTING_ALWAYS, $config->multiple[1]->instructorchoiceacceptgrades);
|
||||||
|
$this->assertEquals($contentitems[1]['lineItem']['tag'], $config->multiple[1]->lineitemtag);
|
||||||
|
$this->assertEquals($contentitems[1]['lineItem']['resourceId'], $config->multiple[1]->lineitemresourceid);
|
||||||
|
$this->assertEquals($contentitems[1]['lineItem']['scoreMaximum'], $config->multiple[1]->grade_modgrade_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test adding a single non gradable item through content item.
|
||||||
|
*/
|
||||||
|
public function test_lti_tool_configuration_from_content_item_single() {
|
||||||
|
$this->resetAfterTest();
|
||||||
|
$this->setAdminUser();
|
||||||
|
|
||||||
|
$type = new stdClass();
|
||||||
|
$type->name = "Test tool";
|
||||||
|
$type->baseurl = "http://example.com";
|
||||||
|
$config = new stdClass();
|
||||||
|
$typeid = lti_add_type($type, $config);
|
||||||
|
|
||||||
|
$generator = $this->getDataGenerator()->get_plugin_generator('mod_lti');
|
||||||
|
$contentitems = [];
|
||||||
|
$contentitems[] = [
|
||||||
|
'type' => 'ltiResourceLink',
|
||||||
|
'url' => 'http://example.com/messages/launch',
|
||||||
|
'title' => 'Test title',
|
||||||
|
'text' => 'Test text',
|
||||||
|
'icon' => [
|
||||||
|
'url' => 'http://lti.example.com/image.jpg',
|
||||||
|
'width' => 100
|
||||||
|
],
|
||||||
|
'frame' => []
|
||||||
|
];
|
||||||
|
$contentitemsjson13 = json_encode($contentitems);
|
||||||
|
$json11 = lti_convert_content_items($contentitemsjson13);
|
||||||
|
|
||||||
|
$config = lti_tool_configuration_from_content_item($typeid,
|
||||||
|
'ContentItemSelection',
|
||||||
|
$type->ltiversion,
|
||||||
|
'ConsumerKey',
|
||||||
|
$json11);
|
||||||
|
$this->assertEquals($contentitems[0]['title'], $config->name);
|
||||||
|
$this->assertEquals($contentitems[0]['text'], $config->introeditor['text']);
|
||||||
|
$this->assertEquals($contentitems[0]['url'], $config->toolurl);
|
||||||
|
$this->assertEquals($contentitems[0]['icon']['url'], $config->icon);
|
||||||
|
$this->assertEquals(LTI_SETTING_NEVER, $config->instructorchoiceacceptgrades);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test lti_sign_jwt().
|
* Test lti_sign_jwt().
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
This files describes API changes in the lti code.
|
This files describes API changes in the lti code.
|
||||||
|
|
||||||
|
=== 3.10 ===
|
||||||
|
|
||||||
|
* Select Content supports multiple, allowing a tool to return more than one link at a time.
|
||||||
|
Parameter multiple in function lti_build_content_item_selection_request() is now set to true.
|
||||||
|
|
||||||
=== 3.8 ===
|
=== 3.8 ===
|
||||||
|
|
||||||
* The following functions have been finally deprecated and can not be used anymore:
|
* The following functions have been finally deprecated and can not be used anymore:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue