mirror of
https://github.com/moodle/moodle.git
synced 2025-01-17 13:38:32 +01:00
MDL-69275 mod_lti: submission review (gradebook launch)
This commit is contained in:
parent
d48873536b
commit
12e207de9d
@ -527,7 +527,6 @@ function upgrade_stale_php_files_present(): bool {
|
||||
'/mod/forum/pix/icon.gif',
|
||||
'/tag/templates/tagname.mustache',
|
||||
// Removed in 3.0.
|
||||
'/mod/lti/grade.php',
|
||||
'/tag/coursetagslib.php',
|
||||
// Removed in 2.9.
|
||||
'/lib/timezone.txt',
|
||||
|
2
mod/lti/amd/build/contentitem.min.js
vendored
2
mod/lti/amd/build/contentitem.min.js
vendored
@ -9,6 +9,6 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 3.2
|
||||
*/
|
||||
define("mod_lti/contentitem",["jquery","core/notification","core/str","core/templates","mod_lti/form-field","core/modal_factory","core/modal_events"],(function($,notification,str,templates,FormField,ModalFactory,ModalEvents){var dialogue,doneCallback,contentItem={init:function(url,postData,cb){doneCallback=cb;var context={url:url,postData:postData},bodyPromise=templates.render("mod_lti/contentitem",context);if(dialogue)return dialogue.setBody(bodyPromise),void dialogue.show();str.get_string("selectcontent","lti").then((function(title){return ModalFactory.create({title:title,body:bodyPromise,large:!0})})).then((function(modal){dialogue=modal,modal.getRoot().on(ModalEvents.hidden,(function(){modal.setBody(""),notification.fetchNotifications()})),modal.show()})).catch(notification.exception)}},ltiFormFields=[new FormField("name",FormField.TYPES.TEXT,!1,""),new FormField("introeditor",FormField.TYPES.EDITOR,!1,""),new FormField("toolurl",FormField.TYPES.TEXT,!0,""),new FormField("securetoolurl",FormField.TYPES.TEXT,!0,""),new FormField("instructorchoiceacceptgrades",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorchoicesendname",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorchoicesendemailaddr",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorcustomparameters",FormField.TYPES.TEXT,!0,""),new FormField("icon",FormField.TYPES.TEXT,!0,""),new FormField("secureicon",FormField.TYPES.TEXT,!0,""),new FormField("launchcontainer",FormField.TYPES.SELECT,!0,0),new FormField("grade_modgrade_point",FormField.TYPES.TEXT,!1,""),new FormField("lineitemresourceid",FormField.TYPES.TEXT,!0,""),new FormField("lineitemtag",FormField.TYPES.TEXT,!0,"")];const hideElement=e=>{e.setAttribute("hidden","true"),e.setAttribute("aria-hidden","true"),e.setAttribute("tab-index","-1")},showElement=e=>{e.removeAttribute("hidden"),e.setAttribute("aria-hidden","false"),e.setAttribute("tab-index","1")};return window.processContentItemReturnData=function(returnData){var index;if(dialogue&&dialogue.hide(),returnData.multiple){for(index in ltiFormFields)ltiFormFields[index].setFieldValue("name"===ltiFormFields[index].name?"item":null);var variants=[];returnData.multiple.forEach((function(v){variants.push((config=>{const variant={};return["name","toolurl","securetoolurl","instructorcustomparameters","icon","secureicon","launchcontainer","lineitemresourceid","lineitemtag"].forEach((function(name){variant[name]=config[name]||""})),variant["introeditor[text]"]=config.introeditor?config.introeditor.text:"",variant["introeditor[format]"]=config.introeditor?config.introeditor.format:"",1===config.instructorchoiceacceptgrades?(variant.instructorchoiceacceptgrades="1",variant["grade[modgrade_point]"]=config.grade_modgrade_point||"100"):variant.instructorchoiceacceptgrades="0",variant})(v))})),async function(items){const form=document.querySelector("#region-main-box form"),toolArea=form.querySelector('[data-attribute="dynamic-import"]'),buttonGroup=form.querySelector("#fgroup_id_buttonar"),submitAndLaunch=form.querySelector("#id_submitbutton");Array.from(form.children).forEach(hideElement),hideElement(submitAndLaunch);const{html:html,js:js}=await templates.renderForPromise("mod_lti/tool_deeplinking_results",{items:items});await templates.replaceNodeContents(toolArea,html,js),showElement(toolArea),showElement(buttonGroup)}(returnData.multiple);const submitAndCourse=document.querySelector("#id_submitbutton2");submitAndCourse.onclick=e=>{e.preventDefault(),submitAndCourse.disabled=!0;const fd=new FormData(document.querySelector("#region-main-box form")),backToCourse=()=>{document.querySelector("#id_cancel").click()};variants.reduce(((promise,variant)=>{Object.entries(variant).forEach((entry=>fd.set(entry[0],entry[1])));const body=new URLSearchParams(fd),doPost=()=>fetch(document.location.pathname,{method:"post",body:body});return promise.then(doPost).catch(doPost)}),Promise.resolve()).then(backToCourse).catch(backToCourse)}}else{for(index in ltiFormFields){var field=ltiFormFields[index],value=null;void 0!==returnData[field.name]&&(value=returnData[field.name]),field.setFieldValue(value)}field.setFieldValue(value)}doneCallback&&doneCallback(returnData)},contentItem}));
|
||||
define("mod_lti/contentitem",["jquery","core/notification","core/str","core/templates","mod_lti/form-field","core/modal_factory","core/modal_events"],(function($,notification,str,templates,FormField,ModalFactory,ModalEvents){var dialogue,doneCallback,contentItem={init:function(url,postData,cb){doneCallback=cb;var context={url:url,postData:postData},bodyPromise=templates.render("mod_lti/contentitem",context);if(dialogue)return dialogue.setBody(bodyPromise),void dialogue.show();str.get_string("selectcontent","lti").then((function(title){return ModalFactory.create({title:title,body:bodyPromise,large:!0})})).then((function(modal){dialogue=modal,modal.getRoot().on(ModalEvents.hidden,(function(){modal.setBody(""),notification.fetchNotifications()})),modal.show()})).catch(notification.exception)}},ltiFormFields=[new FormField("name",FormField.TYPES.TEXT,!1,""),new FormField("introeditor",FormField.TYPES.EDITOR,!1,""),new FormField("toolurl",FormField.TYPES.TEXT,!0,""),new FormField("securetoolurl",FormField.TYPES.TEXT,!0,""),new FormField("instructorchoiceacceptgrades",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorchoicesendname",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorchoicesendemailaddr",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorcustomparameters",FormField.TYPES.TEXT,!0,""),new FormField("icon",FormField.TYPES.TEXT,!0,""),new FormField("secureicon",FormField.TYPES.TEXT,!0,""),new FormField("launchcontainer",FormField.TYPES.SELECT,!0,0),new FormField("grade_modgrade_point",FormField.TYPES.TEXT,!1,""),new FormField("lineitemresourceid",FormField.TYPES.TEXT,!0,""),new FormField("lineitemtag",FormField.TYPES.TEXT,!0,""),new FormField("lineitemsubreviewurl",FormField.TYPES.TEXT,!0,""),new FormField("lineitemsubreviewparams",FormField.TYPES.TEXT,!0,"")];const hideElement=e=>{e.setAttribute("hidden","true"),e.setAttribute("aria-hidden","true"),e.setAttribute("tab-index","-1")},showElement=e=>{e.removeAttribute("hidden"),e.setAttribute("aria-hidden","false"),e.setAttribute("tab-index","1")};return window.processContentItemReturnData=function(returnData){var index;if(dialogue&&dialogue.hide(),returnData.multiple){for(index in ltiFormFields)ltiFormFields[index].setFieldValue("name"===ltiFormFields[index].name?"item":null);var variants=[];returnData.multiple.forEach((function(v){variants.push((config=>{const variant={};return["name","toolurl","securetoolurl","instructorcustomparameters","icon","secureicon","launchcontainer","lineitemresourceid","lineitemtag","lineitemsubreviewurl","lineitemsubreviewparams"].forEach((function(name){variant[name]=config[name]||""})),variant["introeditor[text]"]=config.introeditor?config.introeditor.text:"",variant["introeditor[format]"]=config.introeditor?config.introeditor.format:"",1===config.instructorchoiceacceptgrades?(variant.instructorchoiceacceptgrades="1",variant["grade[modgrade_point]"]=config.grade_modgrade_point||"100"):variant.instructorchoiceacceptgrades="0",variant})(v))})),async function(items){const form=document.querySelector("#region-main-box form"),toolArea=form.querySelector('[data-attribute="dynamic-import"]'),buttonGroup=form.querySelector("#fgroup_id_buttonar"),submitAndLaunch=form.querySelector("#id_submitbutton");Array.from(form.children).forEach(hideElement),hideElement(submitAndLaunch);const{html:html,js:js}=await templates.renderForPromise("mod_lti/tool_deeplinking_results",{items:items});await templates.replaceNodeContents(toolArea,html,js),showElement(toolArea),showElement(buttonGroup)}(returnData.multiple);const submitAndCourse=document.querySelector("#id_submitbutton2");submitAndCourse.onclick=e=>{e.preventDefault(),submitAndCourse.disabled=!0;const fd=new FormData(document.querySelector("#region-main-box form")),backToCourse=()=>{document.querySelector("#id_cancel").click()};variants.reduce(((promise,variant)=>{Object.entries(variant).forEach((entry=>fd.set(entry[0],entry[1])));const body=new URLSearchParams(fd),doPost=()=>fetch(document.location.pathname,{method:"post",body:body});return promise.then(doPost).catch(doPost)}),Promise.resolve()).then(backToCourse).catch(backToCourse)}}else{for(index in ltiFormFields){var field=ltiFormFields[index],value=null;void 0!==returnData[field.name]&&(value=returnData[field.name]),field.setFieldValue(value)}field.setFieldValue(value)}doneCallback&&doneCallback(returnData)},contentItem}));
|
||||
|
||||
//# sourceMappingURL=contentitem.min.js.map
|
File diff suppressed because one or more lines are too long
@ -102,7 +102,9 @@ define(
|
||||
new FormField('launchcontainer', FormField.TYPES.SELECT, true, 0),
|
||||
new FormField('grade_modgrade_point', FormField.TYPES.TEXT, false, ''),
|
||||
new FormField('lineitemresourceid', FormField.TYPES.TEXT, true, ''),
|
||||
new FormField('lineitemtag', FormField.TYPES.TEXT, true, '')
|
||||
new FormField('lineitemtag', FormField.TYPES.TEXT, true, ''),
|
||||
new FormField('lineitemsubreviewurl', FormField.TYPES.TEXT, true, ''),
|
||||
new FormField('lineitemsubreviewparams', FormField.TYPES.TEXT, true, '')
|
||||
];
|
||||
|
||||
/**
|
||||
@ -160,7 +162,8 @@ define(
|
||||
var configToVariant = (config) => {
|
||||
const variant = {};
|
||||
['name', 'toolurl', 'securetoolurl', 'instructorcustomparameters', 'icon', 'secureicon',
|
||||
'launchcontainer', 'lineitemresourceid', 'lineitemtag'].forEach(
|
||||
'launchcontainer', 'lineitemresourceid', 'lineitemtag', 'lineitemsubreviewurl',
|
||||
'lineitemsubreviewparams'].forEach(
|
||||
function(name) {
|
||||
variant[name] = config[name] || '';
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ $responsetype = optional_param('response_type', '', PARAM_TEXT);
|
||||
$clientid = optional_param('client_id', '', PARAM_TEXT);
|
||||
$redirecturi = optional_param('redirect_uri', '', PARAM_URL);
|
||||
$loginhint = optional_param('login_hint', '', PARAM_TEXT);
|
||||
$ltimessagehint = optional_param('lti_message_hint', 0, PARAM_INT);
|
||||
$ltimessagehintenc = optional_param('lti_message_hint', '', PARAM_TEXT);
|
||||
$state = optional_param('state', '', PARAM_TEXT);
|
||||
$responsemode = optional_param('response_mode', '', PARAM_TEXT);
|
||||
$nonce = optional_param('nonce', '', PARAM_TEXT);
|
||||
@ -51,11 +51,17 @@ $prompt = optional_param('prompt', '', PARAM_TEXT);
|
||||
|
||||
$ok = !empty($scope) && !empty($responsetype) && !empty($clientid) &&
|
||||
!empty($redirecturi) && !empty($loginhint) &&
|
||||
!empty($nonce) && !empty($SESSION->lti_message_hint);
|
||||
!empty($nonce);
|
||||
|
||||
if (!$ok) {
|
||||
$error = 'invalid_request';
|
||||
}
|
||||
$ltimessagehint = json_decode($ltimessagehintenc);
|
||||
$ok = $ok && isset($ltimessagehint->launchid);
|
||||
if (!$ok) {
|
||||
$error = 'invalid_request';
|
||||
$desc = 'No launch id in LTI hint';
|
||||
}
|
||||
if ($ok && ($scope !== 'openid')) {
|
||||
$ok = false;
|
||||
$error = 'invalid_scope';
|
||||
@ -65,16 +71,13 @@ if ($ok && ($responsetype !== 'id_token')) {
|
||||
$error = 'unsupported_response_type';
|
||||
}
|
||||
if ($ok) {
|
||||
list($courseid, $typeid, $id, $titleb64, $textb64) = explode(',', $SESSION->lti_message_hint, 5);
|
||||
$ok = ($id !== $ltimessagehint);
|
||||
$launchid = $ltimessagehint->launchid;
|
||||
list($courseid, $typeid, $id, $messagetype, $foruserid, $titleb64, $textb64) = explode(',', $SESSION->$launchid, 7);
|
||||
unset($SESSION->$launchid);
|
||||
$config = lti_get_type_type_config($typeid);
|
||||
$ok = ($clientid === $config->lti_clientid);
|
||||
if (!$ok) {
|
||||
$error = 'invalid_request';
|
||||
} else {
|
||||
$config = lti_get_type_type_config($typeid);
|
||||
$ok = ($clientid === $config->lti_clientid);
|
||||
if (!$ok) {
|
||||
$error = 'unauthorized_client';
|
||||
}
|
||||
$error = 'unauthorized_client';
|
||||
}
|
||||
}
|
||||
if ($ok && ($loginhint !== $USER->id)) {
|
||||
@ -119,7 +122,7 @@ if ($ok) {
|
||||
require_capability('mod/lti:view', $context);
|
||||
$lti = $DB->get_record('lti', array('id' => $cm->instance), '*', MUST_EXIST);
|
||||
$lti->cmid = $cm->id;
|
||||
list($endpoint, $params) = lti_get_launch_data($lti, $nonce);
|
||||
list($endpoint, $params) = lti_get_launch_data($lti, $nonce, $messagetype, $foruserid);
|
||||
} else {
|
||||
require_login($course);
|
||||
$context = context_course::instance($courseid);
|
||||
|
@ -200,6 +200,15 @@ class response {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response body.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_body() {
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the response body.
|
||||
*
|
||||
|
@ -255,6 +255,23 @@ abstract class service_base {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the launch data is created, offering a possibility to alter the
|
||||
* target link URI.
|
||||
*
|
||||
* @param string $messagetype message type for this launch
|
||||
* @param string $targetlinkuri current target link uri
|
||||
* @param null|string $customstr concatenated list of custom parameters
|
||||
* @param int $courseid
|
||||
* @param null|object $lti LTI Instance.
|
||||
*
|
||||
* @return array containing the target link URL and the custom params string to use.
|
||||
*/
|
||||
public function override_endpoint(string $messagetype, string $targetlinkuri,
|
||||
?string $customstr, int $courseid, ?object $lti = null): array {
|
||||
return [$targetlinkuri, $customstr];
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new LTI Instance is deleted.
|
||||
*
|
||||
|
36
mod/lti/grade.php
Normal file
36
mod/lti/grade.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This page is the grade hook allowing LTI launches from gradebook.
|
||||
*
|
||||
* @package mod_lti
|
||||
* @category grade
|
||||
* @copyright 2022 Cengage Group {@link https://cengage.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once(__DIR__ . '/../../config.php');
|
||||
|
||||
$id = required_param('id', PARAM_INT);
|
||||
$userid = optional_param('userid', 0, PARAM_INT);
|
||||
|
||||
$cm = get_coursemodule_from_id('lti', $id, 0, false, MUST_EXIST);
|
||||
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
|
||||
require_login($course, false, $cm);
|
||||
|
||||
redirect(new moodle_url('/mod/lti/view.php', array(
|
||||
'id' => $cm->id, 'action' => 'gradeReport', 'user' => $userid)));
|
@ -50,10 +50,12 @@ require_once("../../config.php");
|
||||
require_once($CFG->dirroot.'/mod/lti/lib.php');
|
||||
require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
||||
|
||||
$id = required_param('id', PARAM_INT); // Course Module ID.
|
||||
$cmid = required_param('id', PARAM_INT); // Course Module ID.
|
||||
$triggerview = optional_param('triggerview', 1, PARAM_BOOL);
|
||||
$action = optional_param('action', '', PARAM_TEXT);
|
||||
$foruserid = optional_param('user', 0, PARAM_INT);
|
||||
|
||||
$cm = get_coursemodule_from_id('lti', $id, 0, false, MUST_EXIST);
|
||||
$cm = get_coursemodule_from_id('lti', $cmid, 0, false, MUST_EXIST);
|
||||
$lti = $DB->get_record('lti', array('id' => $cm->instance), '*', MUST_EXIST);
|
||||
|
||||
$typeid = $lti->typeid;
|
||||
@ -64,7 +66,11 @@ if ($typeid) {
|
||||
$config = lti_get_type_type_config($typeid);
|
||||
if ($config->lti_ltiversion === LTI_VERSION_1P3) {
|
||||
if (!isset($SESSION->lti_initiatelogin_status)) {
|
||||
echo lti_initiate_login($cm->course, $id, $lti, $config);
|
||||
$msgtype = 'basic-lti-launch-request';
|
||||
if ($action === 'gradeReport') {
|
||||
$msgtype = 'LtiSubmissionReviewRequest';
|
||||
}
|
||||
echo lti_initiate_login($cm->course, $cmid, $lti, $config, $msgtype, '', '', $foruserid);
|
||||
exit;
|
||||
} else {
|
||||
unset($SESSION->lti_initiatelogin_status);
|
||||
@ -85,5 +91,4 @@ if ($triggerview) {
|
||||
}
|
||||
|
||||
$lti->cmid = $cm->id;
|
||||
lti_launch_tool($lti);
|
||||
|
||||
lti_launch_tool($lti, $foruserid);
|
||||
|
@ -116,6 +116,7 @@ function lti_get_jwt_message_type_mapping() {
|
||||
'basic-lti-launch-request' => 'LtiResourceLinkRequest',
|
||||
'ContentItemSelectionRequest' => 'LtiDeepLinkingRequest',
|
||||
'LtiDeepLinkingResponse' => 'ContentItemSelection',
|
||||
'LtiSubmissionReviewRequest' => 'LtiSubmissionReviewRequest',
|
||||
);
|
||||
}
|
||||
|
||||
@ -257,6 +258,12 @@ function lti_get_jwt_claim_mapping() {
|
||||
'claim' => 'type',
|
||||
'isarray' => true
|
||||
],
|
||||
'for_user_id' => [
|
||||
'suffix' => '',
|
||||
'group' => 'for_user',
|
||||
'claim' => 'user_id',
|
||||
'isarray' => false
|
||||
],
|
||||
'lis_course_offering_sourcedid' => [
|
||||
'suffix' => '',
|
||||
'group' => 'lis',
|
||||
@ -531,9 +538,9 @@ function lti_get_instance_type(object $instance) : ?object {
|
||||
* @return array the endpoint URL and parameters (including the signature)
|
||||
* @since Moodle 3.0
|
||||
*/
|
||||
function lti_get_launch_data($instance, $nonce = '') {
|
||||
global $PAGE, $CFG, $USER;
|
||||
|
||||
function lti_get_launch_data($instance, $nonce = '', $messagetype = 'basic-lti-launch-request', $foruserid = 0) {
|
||||
global $PAGE, $USER;
|
||||
$messagetype = $messagetype ? $messagetype : 'basic-lti-launch-request';
|
||||
$tool = lti_get_instance_type($instance);
|
||||
if ($tool) {
|
||||
$typeid = $tool->id;
|
||||
@ -606,17 +613,22 @@ function lti_get_launch_data($instance, $nonce = '') {
|
||||
|
||||
$course = $PAGE->course;
|
||||
$islti2 = isset($tool->toolproxyid);
|
||||
$allparams = lti_build_request($instance, $typeconfig, $course, $typeid, $islti2);
|
||||
$allparams = lti_build_request($instance, $typeconfig, $course, $typeid, $islti2, $messagetype, $foruserid);
|
||||
if ($islti2) {
|
||||
$requestparams = lti_build_request_lti2($tool, $allparams);
|
||||
} else {
|
||||
$requestparams = $allparams;
|
||||
}
|
||||
$requestparams = array_merge($requestparams, lti_build_standard_message($instance, $orgid, $ltiversion));
|
||||
$requestparams = array_merge($requestparams, lti_build_standard_message($instance, $orgid, $ltiversion, $messagetype));
|
||||
$customstr = '';
|
||||
if (isset($typeconfig['customparameters'])) {
|
||||
$customstr = $typeconfig['customparameters'];
|
||||
}
|
||||
$services = lti_get_services();
|
||||
foreach ($services as $service) {
|
||||
[$endpoint, $customstr] = $service->override_endpoint($messagetype,
|
||||
$endpoint, $customstr, $instance->course, $instance);
|
||||
}
|
||||
$requestparams = array_merge($requestparams, lti_build_custom_parameters($toolproxy, $tool, $instance, $allparams, $customstr,
|
||||
$instance->instructorcustomparameters, $islti2));
|
||||
|
||||
@ -708,12 +720,13 @@ function lti_get_launch_data($instance, $nonce = '') {
|
||||
/**
|
||||
* Launch an external tool activity.
|
||||
*
|
||||
* @param stdClass $instance the external tool activity settings
|
||||
* @param stdClass $instance the external tool activity settings
|
||||
* @param int $foruserid for user param, optional
|
||||
* @return string The HTML code containing the javascript code for the launch
|
||||
*/
|
||||
function lti_launch_tool($instance) {
|
||||
function lti_launch_tool($instance, $foruserid=0) {
|
||||
|
||||
list($endpoint, $parms) = lti_get_launch_data($instance);
|
||||
list($endpoint, $parms) = lti_get_launch_data($instance, '', '', $foruserid);
|
||||
$debuglaunch = ( $instance->debuglaunch == 1 );
|
||||
|
||||
$content = lti_post_launch_html($parms, $endpoint, $debuglaunch);
|
||||
@ -833,10 +846,13 @@ function lti_build_sourcedid($instanceid, $userid, $servicesalt, $typeid = null,
|
||||
* @param object $course Course object
|
||||
* @param int|null $typeid Basic LTI tool ID
|
||||
* @param boolean $islti2 True if an LTI 2 tool is being launched
|
||||
* @param string $messagetype LTI Message Type for this launch
|
||||
* @param int $foruserid User targeted by this launch
|
||||
*
|
||||
* @return array Request details
|
||||
*/
|
||||
function lti_build_request($instance, $typeconfig, $course, $typeid = null, $islti2 = false) {
|
||||
function lti_build_request($instance, $typeconfig, $course, $typeid = null, $islti2 = false,
|
||||
$messagetype = 'basic-lti-launch-request', $foruserid = 0) {
|
||||
global $USER, $CFG;
|
||||
|
||||
if (empty($instance->cmid)) {
|
||||
@ -853,6 +869,12 @@ function lti_build_request($instance, $typeconfig, $course, $typeid = null, $isl
|
||||
'context_label' => trim(html_to_text($course->shortname, 0)),
|
||||
'context_title' => trim(html_to_text($course->fullname, 0)),
|
||||
);
|
||||
if ($foruserid) {
|
||||
$requestparams['for_user_id'] = $foruserid;
|
||||
}
|
||||
if ($messagetype) {
|
||||
$requestparams['lti_message_type'] = $messagetype;
|
||||
}
|
||||
if (!empty($instance->name)) {
|
||||
$requestparams['resource_link_title'] = trim(html_to_text($instance->name, 0));
|
||||
}
|
||||
@ -1432,6 +1454,21 @@ function lti_verify_jwt_signature($typeid, $consumerkey, $jwtparam) {
|
||||
return $tool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array of custom parameters to a new line separated string.
|
||||
*
|
||||
* @param object $params list of params to concatenate
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function params_to_string(object $params) {
|
||||
$customparameters = [];
|
||||
foreach ($params as $key => $value) {
|
||||
$customparameters[] = "{$key}={$value}";
|
||||
}
|
||||
return implode("\n", $customparameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts LTI 1.1 Content Item for LTI Link to Form data.
|
||||
*
|
||||
@ -1502,12 +1539,24 @@ function content_item_to_form(object $tool, object $typeconfig, object $item) :
|
||||
$config->grade_modgrade_point = $maxscore;
|
||||
$config->lineitemresourceid = '';
|
||||
$config->lineitemtag = '';
|
||||
$config->lineitemsubreviewurl = '';
|
||||
$config->lineitemsubreviewparams = '';
|
||||
if (isset($lineitem->assignedActivity) && isset($lineitem->assignedActivity->activityId)) {
|
||||
$config->lineitemresourceid = $lineitem->assignedActivity->activityId?:'';
|
||||
}
|
||||
if (isset($lineitem->tag)) {
|
||||
$config->lineitemtag = $lineitem->tag?:'';
|
||||
}
|
||||
if (isset($lineitem->submissionReview)) {
|
||||
$subreview = $lineitem->submissionReview;
|
||||
$config->lineitemsubreviewurl = 'DEFAULT';
|
||||
if (!empty($subreview->url)) {
|
||||
$config->lineitemsubreviewurl = $subreview->url;
|
||||
}
|
||||
if (isset($subreview->custom)) {
|
||||
$config->lineitemsubreviewparams = params_to_string($subreview->custom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1524,11 +1573,7 @@ function content_item_to_form(object $tool, object $typeconfig, object $item) :
|
||||
}
|
||||
}
|
||||
if (isset($item->custom)) {
|
||||
$customparameters = [];
|
||||
foreach ($item->custom as $key => $value) {
|
||||
$customparameters[] = "{$key}={$value}";
|
||||
}
|
||||
$config->instructorcustomparameters = implode("\n", $customparameters);
|
||||
$config->instructorcustomparameters = params_to_string($item->custom);
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
@ -1698,6 +1743,9 @@ function lti_convert_content_items($param) {
|
||||
$newitem->lineItem->scoreConstraints->{'@type'} = 'NumericLimits';
|
||||
$newitem->lineItem->scoreConstraints->totalMaximum = $item->lineItem->scoreMaximum;
|
||||
}
|
||||
if (isset($item->lineItem->submissionReview)) {
|
||||
$newitem->lineItem->submissionReview = $item->lineItem->submissionReview;
|
||||
}
|
||||
}
|
||||
$items[] = $newitem;
|
||||
}
|
||||
@ -1950,17 +1998,13 @@ function lti_get_enabled_capabilities($tool) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the custom parameters field to the various parameters
|
||||
* Splits the custom parameters
|
||||
*
|
||||
* @param object $toolproxy Tool proxy instance object
|
||||
* @param object $tool Tool instance object
|
||||
* @param array $params LTI launch parameters
|
||||
* @param string $customstr String containing the parameters
|
||||
* @param boolean $islti2 True if an LTI 2 tool is being launched
|
||||
*
|
||||
* @return array of custom parameters
|
||||
*/
|
||||
function lti_split_custom_parameters($toolproxy, $tool, $params, $customstr, $islti2 = false) {
|
||||
function lti_split_parameters($customstr) {
|
||||
$customstr = str_replace("\r\n", "\n", $customstr);
|
||||
$customstr = str_replace("\n\r", "\n", $customstr);
|
||||
$customstr = str_replace("\r", "\n", $customstr);
|
||||
@ -1973,6 +2017,26 @@ function lti_split_custom_parameters($toolproxy, $tool, $params, $customstr, $is
|
||||
}
|
||||
$key = trim(core_text::substr($line, 0, $pos));
|
||||
$val = trim(core_text::substr($line, $pos + 1, strlen($line)));
|
||||
$retval[$key] = $val;
|
||||
}
|
||||
return $retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the custom parameters field to the various parameters
|
||||
*
|
||||
* @param object $toolproxy Tool proxy instance object
|
||||
* @param object $tool Tool instance object
|
||||
* @param array $params LTI launch parameters
|
||||
* @param string $customstr String containing the parameters
|
||||
* @param boolean $islti2 True if an LTI 2 tool is being launched
|
||||
*
|
||||
* @return array of custom parameters
|
||||
*/
|
||||
function lti_split_custom_parameters($toolproxy, $tool, $params, $customstr, $islti2 = false) {
|
||||
$splitted = lti_split_parameters($customstr);
|
||||
$retval = array();
|
||||
foreach ($splitted as $key => $val) {
|
||||
$val = lti_parse_custom_parameter($toolproxy, $tool, $params, $val, $islti2);
|
||||
$key2 = lti_map_keyname($key);
|
||||
$retval['custom_'.$key2] = $val;
|
||||
@ -3550,21 +3614,20 @@ function lti_post_launch_html($newparms, $endpoint, $debug=false) {
|
||||
* Generate the form for initiating a login request for an LTI 1.3 message
|
||||
*
|
||||
* @param int $courseid Course ID
|
||||
* @param int $id LTI instance ID
|
||||
* @param int $cmid LTI instance ID
|
||||
* @param stdClass|null $instance LTI instance
|
||||
* @param stdClass $config Tool type configuration
|
||||
* @param string $messagetype LTI message type
|
||||
* @param string $title Title of content item
|
||||
* @param string $text Description of content item
|
||||
* @param int $foruserid Id of the user targeted by the launch
|
||||
* @return string
|
||||
*/
|
||||
function lti_initiate_login($courseid, $id, $instance, $config, $messagetype = 'basic-lti-launch-request', $title = '',
|
||||
$text = '') {
|
||||
function lti_initiate_login($courseid, $cmid, $instance, $config, $messagetype = 'basic-lti-launch-request',
|
||||
$title = '', $text = '', $foruserid = 0) {
|
||||
global $SESSION;
|
||||
|
||||
$params = lti_build_login_request($courseid, $id, $instance, $config, $messagetype);
|
||||
$SESSION->lti_message_hint = "{$courseid},{$config->typeid},{$id}," . base64_encode($title) . ',' .
|
||||
base64_encode($text);
|
||||
$params = lti_build_login_request($courseid, $cmid, $instance, $config, $messagetype, $foruserid, $title, $text);
|
||||
|
||||
$r = "<form action=\"" . $config->lti_initiatelogin .
|
||||
"\" name=\"ltiInitiateLoginForm\" id=\"ltiInitiateLoginForm\" method=\"post\" " .
|
||||
@ -3590,25 +3653,39 @@ function lti_initiate_login($courseid, $id, $instance, $config, $messagetype = '
|
||||
* Prepares an LTI 1.3 login request
|
||||
*
|
||||
* @param int $courseid Course ID
|
||||
* @param int $id LTI instance ID
|
||||
* @param int $cmid Course Module instance ID
|
||||
* @param stdClass|null $instance LTI instance
|
||||
* @param stdClass $config Tool type configuration
|
||||
* @param string $messagetype LTI message type
|
||||
* @param int $foruserid Id of the user targeted by the launch
|
||||
* @param string $title Title of content item
|
||||
* @param string $text Description of content item
|
||||
* @return array Login request parameters
|
||||
*/
|
||||
function lti_build_login_request($courseid, $id, $instance, $config, $messagetype) {
|
||||
global $USER, $CFG;
|
||||
|
||||
function lti_build_login_request($courseid, $cmid, $instance, $config, $messagetype, $foruserid=0, $title = '', $text = '') {
|
||||
global $USER, $CFG, $SESSION;
|
||||
$ltihint = [];
|
||||
if (!empty($instance)) {
|
||||
$endpoint = !empty($instance->toolurl) ? $instance->toolurl : $config->lti_toolurl;
|
||||
$launchid = 'ltilaunch'.$instance->id.'_'.rand();
|
||||
$ltihint['cmid'] = $cmid;
|
||||
$SESSION->$launchid = "{$courseid},{$config->typeid},{$cmid},{$messagetype},{$foruserid},,";
|
||||
} else {
|
||||
$endpoint = $config->lti_toolurl;
|
||||
if (($messagetype === 'ContentItemSelectionRequest') && !empty($config->lti_toolurl_ContentItemSelectionRequest)) {
|
||||
$endpoint = $config->lti_toolurl_ContentItemSelectionRequest;
|
||||
}
|
||||
$launchid = "ltilaunch_$messagetype".rand();
|
||||
$SESSION->$launchid =
|
||||
"{$courseid},{$config->typeid},,{$messagetype},{$foruserid}," . base64_encode($title) . ',' . base64_encode($text);
|
||||
}
|
||||
$endpoint = trim($endpoint);
|
||||
$services = lti_get_services();
|
||||
foreach ($services as $service) {
|
||||
[$endpoint] = $service->override_endpoint($messagetype ?? 'basic-lti-launch-request', $endpoint, '', $courseid, $instance);
|
||||
}
|
||||
|
||||
$ltihint['launchid'] = $launchid;
|
||||
// If SSL is forced make sure https is on the normal launch URL.
|
||||
if (isset($config->lti_forcessl) && ($config->lti_forcessl == '1')) {
|
||||
$endpoint = lti_ensure_url_is_https($endpoint);
|
||||
@ -3620,7 +3697,7 @@ function lti_build_login_request($courseid, $id, $instance, $config, $messagetyp
|
||||
$params['iss'] = $CFG->wwwroot;
|
||||
$params['target_link_uri'] = $endpoint;
|
||||
$params['login_hint'] = $USER->id;
|
||||
$params['lti_message_hint'] = $id;
|
||||
$params['lti_message_hint'] = json_encode($ltihint);
|
||||
$params['client_id'] = $config->lti_clientid;
|
||||
$params['lti_deployment_id'] = $config->typeid;
|
||||
return $params;
|
||||
|
@ -221,6 +221,12 @@ class mod_lti_mod_form extends moodleform_mod {
|
||||
$mform->addElement('hidden', 'lineitemtag', '', array( 'id' => 'id_lineitemtag'));
|
||||
$mform->setType('lineitemtag', PARAM_TEXT);
|
||||
|
||||
$mform->addElement('hidden', 'lineitemsubreviewurl', '', array( 'id' => 'id_lineitemsubreviewurl'));
|
||||
$mform->setType('lineitemsubreviewurl', PARAM_URL);
|
||||
|
||||
$mform->addElement('hidden', 'lineitemsubreviewparams', '', array( 'id' => 'id_lineitemsubreviewparams'));
|
||||
$mform->setType('lineitemsubreviewparams', PARAM_TEXT);
|
||||
|
||||
$launchoptions = array();
|
||||
$launchoptions[LTI_LAUNCH_CONTAINER_DEFAULT] = get_string('default', 'lti');
|
||||
$launchoptions[LTI_LAUNCH_CONTAINER_EMBED] = get_string('embed', 'lti');
|
||||
|
@ -64,7 +64,9 @@ class backup_ltiservice_gradebookservices_subplugin extends backup_subplugin {
|
||||
'resourceid',
|
||||
'tag',
|
||||
'vendorcode',
|
||||
'guid'
|
||||
'guid',
|
||||
'subreviewurl',
|
||||
'subreviewparams'
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -115,7 +115,9 @@ class restore_ltiservice_gradebookservices_subplugin extends restore_subplugin {
|
||||
'typeid' => $newtypeid,
|
||||
'baseurl' => $data->baseurl,
|
||||
'resourceid' => $resourceid,
|
||||
'tag' => $data->tag
|
||||
'tag' => $data->tag,
|
||||
'subreviewparams' => $data->subreviewparams ?? '',
|
||||
'subreviewurl' => $data->subreviewurl ?? ''
|
||||
));
|
||||
$this->set_mapping('gbsgradeitemoldid', $newgbsid, $data->gradeitemid);
|
||||
$this->set_mapping('gbsgradeitemrestored', $data->id, $data->id);
|
||||
|
@ -178,6 +178,19 @@ class lineitem extends resource_base {
|
||||
}
|
||||
$gbs->tag = $tag;
|
||||
$gbs->resourceid = $resourceid;
|
||||
$incomingurl = null;
|
||||
$incomingparams = null;
|
||||
if (isset($json->submissionReview)) {
|
||||
$incomingurl = $json->submissionReview->url ?? 'DEFAULT';
|
||||
if (isset($json->submissionReview->custom)) {
|
||||
$incomingparams = params_to_string($json->submissionReview->custom);
|
||||
}
|
||||
}
|
||||
if ($gbs->subreviewurl ?? null !== $incomingurl || $gbs->subreviewparams ?? null !== $incomingparams) {
|
||||
$upgradegradebookservices = true;
|
||||
$gbs->subreviewurl = $incomingurl;
|
||||
$gbs->subreviewparams = $incomingparams;
|
||||
}
|
||||
}
|
||||
$ltilinkid = null;
|
||||
if (isset($json->resourceLinkId)) {
|
||||
@ -257,7 +270,9 @@ class lineitem extends resource_base {
|
||||
'baseurl' => $baseurl,
|
||||
'ltilinkid' => $ltilinkid,
|
||||
'resourceid' => $resourceid,
|
||||
'tag' => $gbs->tag
|
||||
'tag' => $gbs->tag,
|
||||
'subreviewurl' => $gbs->subreviewurl,
|
||||
'subreviewparams' => $gbs->subreviewparams
|
||||
));
|
||||
}
|
||||
|
||||
@ -315,7 +330,13 @@ class lineitem extends resource_base {
|
||||
}
|
||||
$id = optional_param('id', 0, PARAM_INT); // Course Module ID.
|
||||
if (empty($id)) {
|
||||
$id = optional_param('lti_message_hint', 0, PARAM_INT);
|
||||
$hint = optional_param('lti_message_hint', "", PARAM_TEXT);
|
||||
if ($hint) {
|
||||
$hintdec = json_decode($hint);
|
||||
if (isset($hintdec->cmid)) {
|
||||
$id = $hintdec->cmid;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($id)) {
|
||||
$cm = get_coursemodule_from_id('lti', $id, 0, false, MUST_EXIST);
|
||||
|
@ -31,9 +31,13 @@ use ltiservice_gradebookservices\local\resources\results;
|
||||
use ltiservice_gradebookservices\local\resources\scores;
|
||||
use mod_lti\local\ltiservice\resource_base;
|
||||
use mod_lti\local\ltiservice\service_base;
|
||||
use moodle_url;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||
|
||||
/**
|
||||
* A service implementing LTI Gradebook Services.
|
||||
*
|
||||
@ -143,6 +147,42 @@ class gradebookservices extends service_base {
|
||||
$mform->addHelpButton($selectelementname, $identifier, $this->get_component_id());
|
||||
}
|
||||
|
||||
/**
|
||||
* For submission review, if there is a dedicated URL, use it as the target link.
|
||||
*
|
||||
* @param string $messagetype message type for this launch
|
||||
* @param string $targetlinkuri current target link uri
|
||||
* @param string|null $customstr concatenated list of custom parameters
|
||||
* @param int $courseid
|
||||
* @param null|object $lti LTI Instance.
|
||||
*
|
||||
* @return array containing the target link URL and the custom params string to use.
|
||||
*/
|
||||
public function override_endpoint(string $messagetype, string $targetlinkuri, ?string $customstr, int $courseid,
|
||||
?object $lti = null): array {
|
||||
global $DB;
|
||||
if ($messagetype == 'LtiSubmissionReviewRequest' && isset($lti->id)) {
|
||||
$conditions = array('courseid' => $courseid, 'ltilinkid' => $lti->id);
|
||||
$coupledlineitems = $DB->get_records('ltiservice_gradebookservices', $conditions);
|
||||
if (count($coupledlineitems) == 1) {
|
||||
$item = reset($coupledlineitems);
|
||||
$url = $item->subreviewurl;
|
||||
$subreviewparams = $item->subreviewparams;
|
||||
if (!empty($url) && $url != 'DEFAULT') {
|
||||
$targetlinkuri = $url;
|
||||
}
|
||||
if (!empty($subreviewparams)) {
|
||||
if (!empty($customstr)) {
|
||||
$customstr .= "\n{$subreviewparams}";
|
||||
} else {
|
||||
$customstr = $subreviewparams;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [$targetlinkuri, $customstr];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of key/values to add to the launch parameters.
|
||||
*
|
||||
@ -489,6 +529,16 @@ class gradebookservices extends service_base {
|
||||
$lineitem->resourceLinkId = strval($gbs->ltilinkid);
|
||||
$lineitem->ltiLinkId = strval($gbs->ltilinkid);
|
||||
}
|
||||
if (!empty($gbs->subreviewurl)) {
|
||||
$submissionreview = new \stdClass();
|
||||
if ($gbs->subreviewurl != 'DEFAULT') {
|
||||
$submissionreview->url = $gbs->subreviewurl;
|
||||
}
|
||||
if (!empty($gbs->subreviewparams)) {
|
||||
$submissionreview->custom = lti_split_parameters($gbs->subreviewparams);
|
||||
}
|
||||
$lineitem->submissionReview = $submissionreview;
|
||||
}
|
||||
} else {
|
||||
$lineitem->tag = '';
|
||||
if (isset($item->iteminstance)) {
|
||||
@ -615,25 +665,31 @@ class gradebookservices extends service_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the tag and resourceid values for a grade item coupled to an lti link instance.
|
||||
* Updates the tag, resourceid and submission review values for a grade item coupled to an lti link instance.
|
||||
*
|
||||
* @param object $ltiinstance The lti instance to which the grade item is coupled to
|
||||
* @param string|null $resourceid The resourceid to apply to the lineitem. If empty string which will be stored as null.
|
||||
* @param string|null $tag The tag to apply to the lineitem. If empty string which will be stored as null.
|
||||
* @param moodle_url|null $subreviewurl The submission review target link URL
|
||||
* @param string|null $subreviewparams The submission review custom parameters.
|
||||
*
|
||||
*/
|
||||
public static function update_coupled_gradebookservices(object $ltiinstance, ?string $resourceid, ?string $tag) : void {
|
||||
public static function update_coupled_gradebookservices(object $ltiinstance,
|
||||
?string $resourceid, ?string $tag, ?\moodle_url $subreviewurl, ?string $subreviewparams) : void {
|
||||
global $DB;
|
||||
|
||||
if ($ltiinstance && $ltiinstance->typeid) {
|
||||
$gradeitem = $DB->get_record('grade_items', array('itemmodule' => 'lti', 'iteminstance' => $ltiinstance->id));
|
||||
if ($gradeitem) {
|
||||
$resourceid = (isset($resourceid) && empty(trim($resourceid))) ? null : $resourceid;
|
||||
$subreviewurlstr = $subreviewurl ? $subreviewurl->out(false) : null;
|
||||
$tag = (isset($tag) && empty(trim($tag))) ? null : $tag;
|
||||
$gbs = self::find_ltiservice_gradebookservice_for_lineitem($gradeitem->id);
|
||||
if ($gbs) {
|
||||
$gbs->resourceid = $resourceid;
|
||||
$gbs->tag = $tag;
|
||||
$gbs->subreviewurl = $subreviewurlstr;
|
||||
$gbs->subreviewparams = $subreviewparams;
|
||||
$DB->update_record('ltiservice_gradebookservices', $gbs);
|
||||
} else {
|
||||
$baseurl = lti_get_type_type_config($ltiinstance->typeid)->lti_toolurl;
|
||||
@ -644,7 +700,9 @@ class gradebookservices extends service_base {
|
||||
'baseurl' => $baseurl,
|
||||
'ltilinkid' => $ltiinstance->id,
|
||||
'resourceid' => $resourceid,
|
||||
'tag' => $tag
|
||||
'tag' => $tag,
|
||||
'subreviewurl' => $subreviewurlstr,
|
||||
'subreviewparams' => $subreviewparams
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -657,7 +715,9 @@ class gradebookservices extends service_base {
|
||||
* @param object $lti LTI Instance.
|
||||
*/
|
||||
public function instance_added(object $lti): void {
|
||||
self::update_coupled_gradebookservices($lti, $lti->lineitemresourceid ?? null, $lti->lineitemtag ?? null);
|
||||
self::update_coupled_gradebookservices($lti, $lti->lineitemresourceid ?? null, $lti->lineitemtag ?? null,
|
||||
isset($lti->lineitemsubreviewurl) ? new moodle_url($lti->lineitemsubreviewurl) : null,
|
||||
$lti->lineitemsubreviewparams ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -666,7 +726,9 @@ class gradebookservices extends service_base {
|
||||
* @param object $lti LTI Instance.
|
||||
*/
|
||||
public function instance_updated(object $lti): void {
|
||||
self::update_coupled_gradebookservices($lti, $lti->lineitemresourceid ?? null, $lti->lineitemtag ?? null);
|
||||
self::update_coupled_gradebookservices($lti, $lti->lineitemresourceid ?? null, $lti->lineitemtag ?? null,
|
||||
isset($lti->lineitemsubreviewurl) ? new moodle_url($lti->lineitemsubreviewurl) : null,
|
||||
$lti->lineitemsubreviewparams ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -677,11 +739,15 @@ class gradebookservices extends service_base {
|
||||
public function set_instance_form_values(object $defaultvalues): void {
|
||||
$defaultvalues->lineitemresourceid = '';
|
||||
$defaultvalues->lineitemtag = '';
|
||||
$defaultvalues->subreviewurl = '';
|
||||
$defaultvalues->subreviewparams = '';
|
||||
if (is_object($defaultvalues) && $defaultvalues->instance) {
|
||||
$gbs = self::find_ltiservice_gradebookservice_for_lti($defaultvalues->instance);
|
||||
if ($gbs) {
|
||||
$defaultvalues->lineitemresourceid = $gbs->resourceid;
|
||||
$defaultvalues->lineitemtag = $gbs->tag;
|
||||
$defaultvalues->lineitemsubreviewurl = $gbs->subreviewurl;
|
||||
$defaultvalues->lineitemsubreviewparams = $gbs->subreviewparams;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@
|
||||
<FIELD NAME="ltilinkid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="ID of the LTI element related with this lineitem."/>
|
||||
<FIELD NAME="resourceid" TYPE="char" LENGTH="512" NOTNULL="false" SEQUENCE="false" COMMENT="Resource id for the line item"/>
|
||||
<FIELD NAME="tag" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Tag type specified for the line item"/>
|
||||
<FIELD NAME="subreviewurl" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Submission review URL"/>
|
||||
<FIELD NAME="subreviewparams" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Submission review custom params"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
|
@ -113,5 +113,20 @@ function xmldb_ltiservice_gradebookservices_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2022051900) {
|
||||
$table = new xmldb_table('ltiservice_gradebookservices');
|
||||
$field = new xmldb_field('subreviewurl', XMLDB_TYPE_TEXT, null, null, null, null, null);
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
$field = new xmldb_field('subreviewparams', XMLDB_TYPE_TEXT, null, null, null, null, null);
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Lti savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2022051900, 'ltiservice', 'gradebookservices');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -25,10 +25,13 @@ use ltiservice_gradebookservices\local\service\gradebookservices;
|
||||
* @category test
|
||||
* @copyright 2020 Claude Vervoort <claude.vervoort@cengage.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \mod_lti\service\gradebookservices\local\gradebookservices
|
||||
*/
|
||||
class gradebookservices_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* @covers ::instance_added
|
||||
*
|
||||
* Test saving a graded LTI with resource and tag info (as a result of
|
||||
* content item selection) creates a gradebookservices record
|
||||
* that can be retrieved using the gradebook service API.
|
||||
@ -46,8 +49,10 @@ class gradebookservices_test extends \advanced_testcase {
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$resourceid = 'test-resource-id';
|
||||
$tag = 'tag';
|
||||
$subreviewurl = 'https://subreview.example.com';
|
||||
$subreviewparams = 'a=2';
|
||||
|
||||
$ltiinstance = $this->create_graded_lti($typeid, $course, $resourceid, $tag);
|
||||
$ltiinstance = $this->create_graded_lti($typeid, $course, $resourceid, $tag, $subreviewurl, $subreviewparams);
|
||||
|
||||
$this->assertNotNull($ltiinstance);
|
||||
|
||||
@ -56,11 +61,47 @@ class gradebookservices_test extends \advanced_testcase {
|
||||
$this->assertNotNull($gbs);
|
||||
$this->assertEquals($resourceid, $gbs->resourceid);
|
||||
$this->assertEquals($tag, $gbs->tag);
|
||||
|
||||
$this->assert_lineitems($course, $typeid, $ltiinstance->name, $ltiinstance, $resourceid, $tag);
|
||||
$this->assertEquals($subreviewurl, $gbs->subreviewurl);
|
||||
$this->assertEquals($subreviewparams, $gbs->subreviewparams);
|
||||
$this->assert_lineitems($course, $typeid, $ltiinstance->name,
|
||||
$ltiinstance, $resourceid, $tag, $subreviewurl, $subreviewparams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::instance_added
|
||||
*
|
||||
* Test saving a graded LTI with resource and tag info (as a result of
|
||||
* content item selection) creates a gradebookservices record
|
||||
* that can be retrieved using the gradebook service API.
|
||||
*/
|
||||
public function test_lti_add_coupled_lineitem_default_subreview() {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a tool type, associated with that proxy.
|
||||
|
||||
$typeid = $this->create_type();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$resourceid = 'test-resource-id';
|
||||
$tag = 'tag';
|
||||
|
||||
$ltiinstance = $this->create_graded_lti($typeid, $course, $resourceid, $tag, 'DEFAULT');
|
||||
|
||||
$this->assertNotNull($ltiinstance);
|
||||
|
||||
$gbs = gradebookservices::find_ltiservice_gradebookservice_for_lti($ltiinstance->id);
|
||||
|
||||
$this->assertNotNull($gbs);
|
||||
$this->assertEquals('DEFAULT', $gbs->subreviewurl);
|
||||
$this->assert_lineitems($course, $typeid, $ltiinstance->name, $ltiinstance, $resourceid, $tag, 'DEFAULT');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::add_standalone_lineitem
|
||||
*
|
||||
* Test saving a standalone LTI lineitem with resource and tag info
|
||||
* that can be retrieved using the gradebook service API.
|
||||
*/
|
||||
@ -79,6 +120,8 @@ class gradebookservices_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::find_ltiservice_gradebookservice_for_lti
|
||||
*
|
||||
* Test line item URL is populated for coupled line item only
|
||||
* if there is not another line item bound to the lti instance,
|
||||
* since in that case there would be no rule to define which of
|
||||
@ -96,7 +139,7 @@ class gradebookservices_test extends \advanced_testcase {
|
||||
$typeid = $this->create_type();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
$ltiinstance = $this->create_graded_lti($typeid, $course, 'resource-id', 'tag');
|
||||
$ltiinstance = $this->create_graded_lti($typeid, $course, 'resource-id', 'tag', 'https://subreview.url', 'sub=review');
|
||||
|
||||
$this->assertNotNull($ltiinstance);
|
||||
|
||||
@ -113,6 +156,70 @@ class gradebookservices_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::override_endpoint
|
||||
*
|
||||
* Test Submission Review URL and custom parameter is applied when the
|
||||
* launch is submission review.
|
||||
*/
|
||||
public function test_get_launch_parameters_coupled_subreview_override() {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a tool type, associated with that proxy.
|
||||
|
||||
$typeid = $this->create_type();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
$ltiinstance = $this->create_graded_lti($typeid, $course, 'resource-id', 'tag',
|
||||
'https://example.com/subreview', 'action=review');
|
||||
|
||||
$this->assertNotNull($ltiinstance);
|
||||
|
||||
$gbservice = new gradebookservices();
|
||||
$overrides = $gbservice->override_endpoint('LtiSubmissionReviewRequest', 'https://example.com/lti',
|
||||
"color=blue", $course->id, $ltiinstance);
|
||||
|
||||
$this->assertEquals('https://example.com/subreview', $overrides[0]);
|
||||
$this->assertEquals("color=blue\naction=review", $overrides[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::override_endpoint
|
||||
*
|
||||
* Test Submission Review URL and custom parameter is applied when the
|
||||
* launch is submission review.
|
||||
*/
|
||||
public function test_get_launch_parameters_coupled_subreview_override_default() {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a tool type, associated with that proxy.
|
||||
|
||||
$typeid = $this->create_type();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
$ltiinstance = $this->create_graded_lti($typeid, $course, 'resource-id', 'tag',
|
||||
'DEFAULT', '');
|
||||
|
||||
$this->assertNotNull($ltiinstance);
|
||||
|
||||
$gbservice = new gradebookservices();
|
||||
$overrides = $gbservice->override_endpoint('LtiSubmissionReviewRequest', 'https://example.com/lti',
|
||||
"color=blue", $course->id, $ltiinstance);
|
||||
|
||||
$this->assertEquals('https://example.com/lti', $overrides[0]);
|
||||
$this->assertEquals("color=blue", $overrides[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::get_launch_parameters
|
||||
*
|
||||
* Test line item URL is populated for not coupled line item only
|
||||
* if there is a single line item attached to that lti instance.
|
||||
*/
|
||||
@ -149,6 +256,8 @@ class gradebookservices_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::is_user_gradable_in_course
|
||||
*
|
||||
* Test if a user can be graded in a course.
|
||||
*/
|
||||
public function test_is_user_gradable_in_course() {
|
||||
@ -174,20 +283,36 @@ class gradebookservices_test extends \advanced_testcase {
|
||||
* @param object|null $ltiinstance lti instance related to that line item
|
||||
* @param string|null $resourceid resourceid the line item should have
|
||||
* @param string|null $tag tag the line item should have
|
||||
* @param string|null $subreviewurl submission review url
|
||||
* @param string|null $subreviewparams submission review custom params
|
||||
*/
|
||||
private function assert_lineitems(object $course, int $typeid,
|
||||
string $label, ?object $ltiinstance, ?string $resourceid, ?string $tag) : void {
|
||||
string $label, ?object $ltiinstance, ?string $resourceid, ?string $tag,
|
||||
?string $subreviewurl = null, ?string $subreviewparams = null) : void {
|
||||
$gbservice = new gradebookservices();
|
||||
$gradeitems = $gbservice->get_lineitems($course->id, null, null, null, null, null, $typeid);
|
||||
|
||||
// The 1st item in the array is the items count.
|
||||
$this->assertEquals(1, $gradeitems[0]);
|
||||
|
||||
$lineitem = gradebookservices::item_for_json($gradeitems[1][0], '', $typeid);
|
||||
$this->assertEquals(10, $lineitem->scoreMaximum);
|
||||
$this->assertEquals($resourceid, $lineitem->resourceId);
|
||||
$this->assertEquals($tag, $lineitem->tag);
|
||||
$this->assertEquals($label, $lineitem->label);
|
||||
$this->assertEquals(!empty($subreviewurl), isset($lineitem->submissionReview));
|
||||
if ($subreviewurl) {
|
||||
if ($subreviewurl == 'DEFAULT') {
|
||||
$this->assertFalse(isset($this->submissionReview->url));
|
||||
} else {
|
||||
$this->assertEquals($subreviewurl, $lineitem->submissionReview->url);
|
||||
}
|
||||
if ($subreviewparams) {
|
||||
$custom = $lineitem->submissionReview->custom;
|
||||
$this->assertEquals($subreviewparams, join("\n", array_map(fn($k) => $k.'='.$custom[$k], array_keys($custom))));
|
||||
} else {
|
||||
$this->assertFalse(isset($this->submissionReview->custom));
|
||||
}
|
||||
}
|
||||
|
||||
$gradeitems = $gbservice->get_lineitems($course->id, $resourceid, null, null, null, null, $typeid);
|
||||
$this->assertEquals(1, $gradeitems[0]);
|
||||
@ -216,17 +341,22 @@ class gradebookservices_test extends \advanced_testcase {
|
||||
* @param object $course course where to add the lti instance.
|
||||
* @param string|null $resourceid resource id
|
||||
* @param string|null $tag tag
|
||||
* @param string|null $subreviewurl submission review url
|
||||
* @param string|null $subreviewparams submission review custom params
|
||||
*
|
||||
* @return object lti instance created
|
||||
*/
|
||||
private function create_graded_lti(int $typeid, object $course, ?string $resourceid, ?string $tag) : object {
|
||||
private function create_graded_lti(int $typeid, object $course, ?string $resourceid, ?string $tag,
|
||||
?string $subreviewurl = null, ?string $subreviewparams = null) : object {
|
||||
|
||||
$lti = ['course' => $course->id,
|
||||
'typeid' => $typeid,
|
||||
'instructorchoiceacceptgrades' => LTI_SETTING_ALWAYS,
|
||||
'grade' => 10,
|
||||
'lineitemresourceid' => $resourceid,
|
||||
'lineitemtag' => $tag];
|
||||
'lineitemtag' => $tag,
|
||||
'lineitemsubreviewurl' => $subreviewurl,
|
||||
'lineitemsubreviewparams' => $subreviewparams];
|
||||
|
||||
return $this->getDataGenerator()->create_module('lti', $lti, array());
|
||||
}
|
||||
|
236
mod/lti/service/gradebookservices/tests/lineitem_test.php
Normal file
236
mod/lti/service/gradebookservices/tests/lineitem_test.php
Normal file
@ -0,0 +1,236 @@
|
||||
<?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/>.
|
||||
|
||||
namespace ltiservice_gradebookservices;
|
||||
|
||||
use ltiservice_gradebookservices\local\resources\lineitem;
|
||||
use ltiservice_gradebookservices\local\service\gradebookservices;
|
||||
|
||||
/**
|
||||
* Unit tests for lti lineitem.
|
||||
*
|
||||
* @package ltiservice_gradebookservices
|
||||
* @category test
|
||||
* @copyright 2022 Cengage Group <claude.vervoort@cengage.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \mod_lti\service\gradebookservices\local\resources\lineitem
|
||||
*/
|
||||
class lineitem_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* @covers ::execute
|
||||
*
|
||||
* Test updating the line item with submission review.
|
||||
*/
|
||||
public function test_execute_put_nosubreview() {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
$resourceid = 'test-resource-id';
|
||||
$tag = 'tag';
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$typeid = $this->create_type();
|
||||
|
||||
// The 1st item in the array is the items count.
|
||||
|
||||
$gbservice = new gradebookservices();
|
||||
$gbservice->set_type(lti_get_type($typeid));
|
||||
$this->create_graded_lti($typeid, $course, $resourceid, $tag);
|
||||
$gradeitems = $gbservice->get_lineitems($course->id, null, null, null, null, null, $typeid);
|
||||
$this->assertEquals(1, $gradeitems[0]);
|
||||
$lineitem = gradebookservices::item_for_json($gradeitems[1][0], '', $typeid);
|
||||
$this->assertFalse(isset($lineitem->submissionReview));
|
||||
|
||||
$lineitemresource = new lineitem($gbservice);
|
||||
|
||||
$this->set_server_for_put($course, $typeid, $lineitem);
|
||||
|
||||
$response = new \mod_lti\local\ltiservice\response();
|
||||
$lineitem->resourceId = $resourceid.'modified';
|
||||
$lineitem->tag = $tag.'modified';
|
||||
$response->set_request_data(json_encode($lineitem));
|
||||
|
||||
$lineitemresource->execute($response);
|
||||
|
||||
$lineitem = gradebookservices::item_for_json($gradeitems[1][0], '', $typeid);
|
||||
$this->assertFalse(isset($lineitem->submissionReview));
|
||||
$this->assertEquals($resourceid.'modified', $lineitem->resourceId);
|
||||
$this->assertEquals($tag.'modified', $lineitem->tag);
|
||||
$responseitem = json_decode($response->get_body());
|
||||
$this->assertEquals($resourceid.'modified', $responseitem->resourceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::execute
|
||||
*
|
||||
* Test updating the line item with submission review.
|
||||
*/
|
||||
public function test_execute_put_withsubreview() {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
$resourceid = 'test-resource-id';
|
||||
$tag = 'tag';
|
||||
$subreviewurl = 'https://subreview.example.com';
|
||||
$subreviewparams = 'a=2';
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$typeid = $this->create_type();
|
||||
|
||||
// The 1st item in the array is the items count.
|
||||
|
||||
$gbservice = new gradebookservices();
|
||||
$gbservice->set_type(lti_get_type($typeid));
|
||||
$this->create_graded_lti($typeid, $course, $resourceid, $tag, $subreviewurl, $subreviewparams);
|
||||
$gradeitems = $gbservice->get_lineitems($course->id, null, null, null, null, null, $typeid);
|
||||
$this->assertEquals(1, $gradeitems[0]);
|
||||
$lineitem = gradebookservices::item_for_json($gradeitems[1][0], '', $typeid);
|
||||
$this->assertTrue(isset($lineitem->submissionReview));
|
||||
|
||||
$lineitemresource = new lineitem($gbservice);
|
||||
|
||||
$this->set_server_for_put($course, $typeid, $lineitem);
|
||||
|
||||
$response = new \mod_lti\local\ltiservice\response();
|
||||
$lineitem->resourceId = $resourceid.'modified';
|
||||
$lineitem->tag = $tag.'modified';
|
||||
$lineitem->submissionReview->url = $subreviewurl.'modified';
|
||||
$lineitem->submissionReview->custom = ['a' => '3'];
|
||||
$response->set_request_data(json_encode($lineitem));
|
||||
|
||||
$lineitemresource->execute($response);
|
||||
|
||||
$lineitem = gradebookservices::item_for_json($gradeitems[1][0], '', $typeid);
|
||||
$this->assertEquals($resourceid.'modified', $lineitem->resourceId);
|
||||
$this->assertEquals($subreviewurl.'modified', $lineitem->submissionReview->url);
|
||||
$custom = $lineitem->submissionReview->custom;
|
||||
$this->assertEquals('a=3', join("\n", array_map(fn($k) => $k.'='.$custom[$k], array_keys($custom))));
|
||||
|
||||
$responseitem = json_decode($response->get_body());
|
||||
$this->assertEquals($resourceid.'modified', $responseitem->resourceId);
|
||||
$this->assertEquals($subreviewurl.'modified', $responseitem->submissionReview->url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::execute
|
||||
*
|
||||
* Test updating the line item with submission review.
|
||||
*/
|
||||
public function test_execute_put_addsubreview() {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
$resourceid = 'test-resource-id';
|
||||
$tag = 'tag';
|
||||
$subreviewurl = 'https://subreview.example.com';
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$typeid = $this->create_type();
|
||||
|
||||
// The 1st item in the array is the items count.
|
||||
|
||||
$gbservice = new gradebookservices();
|
||||
$gbservice->set_type(lti_get_type($typeid));
|
||||
$this->create_graded_lti($typeid, $course, $resourceid, $tag);
|
||||
$gradeitems = $gbservice->get_lineitems($course->id, null, null, null, null, null, $typeid);
|
||||
$this->assertEquals(1, $gradeitems[0]);
|
||||
$lineitem = gradebookservices::item_for_json($gradeitems[1][0], '', $typeid);
|
||||
$this->assertFalse(isset($lineitem->submissionReview));
|
||||
|
||||
$lineitemresource = new lineitem($gbservice);
|
||||
|
||||
$this->set_server_for_put($course, $typeid, $lineitem);
|
||||
|
||||
$response = new \mod_lti\local\ltiservice\response();
|
||||
$lineitem->resourceId = $resourceid.'modified';
|
||||
$lineitem->tag = $tag.'modified';
|
||||
$lineitem->submissionReview = ['url' => $subreviewurl];
|
||||
$response->set_request_data(json_encode($lineitem));
|
||||
|
||||
$lineitemresource->execute($response);
|
||||
|
||||
$lineitem = gradebookservices::item_for_json($gradeitems[1][0], '', $typeid);
|
||||
$this->assertEquals($resourceid.'modified', $lineitem->resourceId);
|
||||
$this->assertEquals($subreviewurl, $lineitem->submissionReview->url);
|
||||
$this->assertFalse(isset($lineitem->submissionReview->custom));
|
||||
|
||||
$responseitem = json_decode($response->get_body());
|
||||
$this->assertEquals($resourceid.'modified', $responseitem->resourceId);
|
||||
$this->assertEquals($subreviewurl, $responseitem->submissionReview->url);
|
||||
$this->assertFalse(isset($responseitem->submissionReview->custom));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a graded lti instance, which should create a grade_item and gradebookservices record.
|
||||
*
|
||||
* @param int $typeid Type ID of the LTI Tool.
|
||||
* @param object $course course where to add the lti instance.
|
||||
* @param string|null $resourceid resource id
|
||||
* @param string|null $tag tag
|
||||
* @param string|null $subreviewurl submission review url
|
||||
* @param string|null $subreviewparams submission review custom params
|
||||
*
|
||||
* @return object lti instance created
|
||||
*/
|
||||
private function create_graded_lti(int $typeid, object $course, ?string $resourceid, ?string $tag,
|
||||
?string $subreviewurl = null, ?string $subreviewparams = null) : object {
|
||||
|
||||
$lti = ['course' => $course->id,
|
||||
'typeid' => $typeid,
|
||||
'instructorchoiceacceptgrades' => LTI_SETTING_ALWAYS,
|
||||
'grade' => 10,
|
||||
'lineitemresourceid' => $resourceid,
|
||||
'lineitemtag' => $tag,
|
||||
'lineitemsubreviewurl' => $subreviewurl,
|
||||
'lineitemsubreviewparams' => $subreviewparams];
|
||||
|
||||
return $this->getDataGenerator()->create_module('lti', $lti, array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new LTI Tool Type.
|
||||
*/
|
||||
private function create_type() {
|
||||
$type = new \stdClass();
|
||||
$type->state = LTI_TOOL_STATE_CONFIGURED;
|
||||
$type->name = "Test tool";
|
||||
$type->description = "Example description";
|
||||
$type->clientid = "Test client ID";
|
||||
$type->baseurl = $this->getExternalTestFileUrl('/test.html');
|
||||
|
||||
$config = new \stdClass();
|
||||
$config->ltiservice_gradesynchronization = 2;
|
||||
return lti_add_type($type, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the server info and get to be configured for a PUT operation,
|
||||
* including having a proper auth token attached.
|
||||
*
|
||||
* @param object $course course where to add the lti instance.
|
||||
* @param int $typeid
|
||||
* @param object $lineitem
|
||||
*/
|
||||
private function set_server_for_put(object $course, int $typeid, object $lineitem) {
|
||||
$_SERVER['REQUEST_METHOD'] = \mod_lti\local\ltiservice\resource_base::HTTP_PUT;
|
||||
$_SERVER['PATH_INFO'] = "/$course->id/lineitems$lineitem->id";
|
||||
|
||||
$token = lti_new_access_token($typeid, ['https://purl.imsglobal.org/spec/lti-ags/scope/lineitem']);
|
||||
$_SERVER['HTTP_Authorization'] = 'Bearer '.$token->token;
|
||||
$_GET['type_id'] = (string)$typeid;
|
||||
}
|
||||
}
|
6
mod/lti/service/gradebookservices/upgrade.txt
Normal file
6
mod/lti/service/gradebookservices/upgrade.txt
Normal file
@ -0,0 +1,6 @@
|
||||
This files describes API changes in the lti code.
|
||||
|
||||
=== 4.1 ===
|
||||
|
||||
* The update_coupled_gradebookservices function now accept 2 additional optional parameters
|
||||
to update the newly added properties related to submission review support (url and custom params).
|
@ -25,6 +25,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2022041900;
|
||||
$plugin->version = 2022051900;
|
||||
$plugin->requires = 2022041200;
|
||||
$plugin->component = 'ltiservice_gradebookservices';
|
||||
|
@ -67,6 +67,19 @@ require_once($CFG->dirroot . '/mod/lti/tests/mod_lti_testcase.php');
|
||||
*/
|
||||
class locallib_test extends mod_lti_testcase {
|
||||
|
||||
/**
|
||||
* @covers ::lti_split_parameters()
|
||||
*
|
||||
* Test the split parameters function
|
||||
*/
|
||||
public function test_split_parameters() {
|
||||
$this->assertEquals(lti_split_parameters(''), array());
|
||||
$this->assertEquals(lti_split_parameters('a=1'), array('a' => '1'));
|
||||
$this->assertEquals(lti_split_parameters("a=1\nb=2"), array('a' => '1', 'b' => '2'));
|
||||
$this->assertEquals(lti_split_parameters("a=1\n\rb=2"), array('a' => '1', 'b' => '2'));
|
||||
$this->assertEquals(lti_split_parameters("a=1\r\nb=2"), array('a' => '1', 'b' => '2'));
|
||||
}
|
||||
|
||||
public function test_split_custom_parameters() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
@ -618,6 +631,7 @@ class locallib_test extends mod_lti_testcase {
|
||||
'basic-lti-launch-request' => 'LtiResourceLinkRequest',
|
||||
'ContentItemSelectionRequest' => 'LtiDeepLinkingRequest',
|
||||
'LtiDeepLinkingResponse' => 'ContentItemSelection',
|
||||
'LtiSubmissionReviewRequest' => 'LtiSubmissionReviewRequest'
|
||||
];
|
||||
|
||||
$this->assertEquals($mapping, lti_get_jwt_message_type_mapping());
|
||||
@ -1005,6 +1019,12 @@ class locallib_test extends mod_lti_testcase {
|
||||
'claim' => 'lis_result_sourcedid',
|
||||
'isarray' => false
|
||||
],
|
||||
'for_user_id' => [
|
||||
'suffix' => '',
|
||||
'group' => 'for_user',
|
||||
'claim' => 'user_id',
|
||||
'isarray' => false
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEquals($mapping, lti_get_jwt_claim_mapping());
|
||||
@ -1339,6 +1359,99 @@ MwIDAQAB
|
||||
$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);
|
||||
$this->assertEquals('', $config->lineitemsubreviewurl);
|
||||
$this->assertEquals('', $config->lineitemsubreviewparams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::lti_tool_configuration_from_content_item()
|
||||
*
|
||||
* Test adding a single gradable item through content item with an empty subreview url.
|
||||
*/
|
||||
public function test_lti_tool_configuration_from_content_item_single_gradable_subreview_default_emptyurl() {
|
||||
$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);
|
||||
|
||||
$contentitems = [];
|
||||
$contentitems[] = [
|
||||
'type' => 'ltiResourceLink',
|
||||
'url' => 'http://example.com/messages/launch',
|
||||
'title' => 'Test title',
|
||||
'lineItem' => [
|
||||
'resourceId' => 'r12345',
|
||||
'tag' => 'final',
|
||||
'scoreMaximum' => 10.0,
|
||||
'submissionReview' => [
|
||||
'url' => ''
|
||||
]
|
||||
],
|
||||
'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('DEFAULT', $config->lineitemsubreviewurl);
|
||||
$this->assertEquals('', $config->lineitemsubreviewparams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::lti_tool_configuration_from_content_item()
|
||||
*
|
||||
* Test adding a single gradable item through content item.
|
||||
*/
|
||||
public function test_lti_tool_configuration_from_content_item_single_gradable_subreview_default() {
|
||||
$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);
|
||||
|
||||
$contentitems = [];
|
||||
$contentitems[] = [
|
||||
'type' => 'ltiResourceLink',
|
||||
'url' => 'http://example.com/messages/launch',
|
||||
'title' => 'Test title',
|
||||
'lineItem' => [
|
||||
'resourceId' => 'r12345',
|
||||
'tag' => 'final',
|
||||
'scoreMaximum' => 10.0,
|
||||
'submissionReview' => []
|
||||
],
|
||||
'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);
|
||||
$this->assertEquals('DEFAULT', $config->lineitemsubreviewurl);
|
||||
$this->assertEquals('', $config->lineitemsubreviewparams);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1375,7 +1488,11 @@ MwIDAQAB
|
||||
'lineItem' => [
|
||||
'resourceId' => 'r12345',
|
||||
'tag' => 'final',
|
||||
'scoreMaximum' => 10.0
|
||||
'scoreMaximum' => 10.0,
|
||||
'submissionReview' => [
|
||||
'url' => 'https://testsub.url',
|
||||
'custom' => ['a' => 'b']
|
||||
]
|
||||
],
|
||||
'frame' => []
|
||||
];
|
||||
@ -1397,6 +1514,8 @@ MwIDAQAB
|
||||
$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);
|
||||
$this->assertEquals($contentitems[1]['lineItem']['submissionReview']['url'], $config->multiple[1]->lineitemsubreviewurl);
|
||||
$this->assertEquals("a=b", $config->multiple[1]->lineitemsubreviewparams);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1649,16 +1768,35 @@ MwIDAQAB
|
||||
$config->typeid = 'some-type-id';
|
||||
$config->lti_toolurl = 'some-lti-tool-url';
|
||||
|
||||
$request = lti_build_login_request($course->id, $instance->id, $instance, $config, 'basic-lti-launch-request');
|
||||
|
||||
$request = lti_build_login_request($course->id, $instance->cmid, $instance, $config, 'basic-lti-launch-request');
|
||||
$this->assertEquals($CFG->wwwroot, $request['iss']);
|
||||
$this->assertEquals('http://some-lti-tool-url', $request['target_link_uri']);
|
||||
$this->assertEquals(123456789, $request['login_hint']);
|
||||
$this->assertEquals($instance->id, $request['lti_message_hint']);
|
||||
$this->assertTrue(strpos($request['lti_message_hint'], "\"cmid\":{$instance->cmid}") > 0);
|
||||
$this->assertTrue(strpos($request['lti_message_hint'], "\"launchid\":\"ltilaunch{$instance->id}_") > 0);
|
||||
$this->assertEquals('some-client-id', $request['client_id']);
|
||||
$this->assertEquals('some-type-id', $request['lti_deployment_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::lti_get_launch_data()
|
||||
*
|
||||
* Test for_user is passed as parameter when specified.
|
||||
*/
|
||||
public function test_lti_get_launch_data_with_for_user() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
$config = new \stdClass();
|
||||
$config->lti_organizationid = '';
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$type = $this->create_type($config);
|
||||
$link = $this->create_instance($type, $course);
|
||||
$launchdata = lti_get_launch_data($link, '', '', 345);
|
||||
$this->assertEquals($launchdata[1]['lti_message_type'], 'basic-lti-launch-request');
|
||||
$this->assertEquals($launchdata[1]['for_user_id'], 345);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test default orgid is host if not specified in config (tool installed in earlier version of Moodle).
|
||||
*/
|
||||
|
@ -3,6 +3,7 @@ This files describes API changes in the lti code.
|
||||
=== 4.1 ===
|
||||
|
||||
* The callback get_shortcuts() is now removed. Please use get_course_content_items and get_all_content_items instead.
|
||||
* Services can now implement the override_endpoint function to replace the launch url and/or the custom parameters.
|
||||
|
||||
=== 3.10 ===
|
||||
|
||||
|
@ -53,6 +53,8 @@ require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
||||
|
||||
$id = optional_param('id', 0, PARAM_INT); // Course Module ID, or
|
||||
$l = optional_param('l', 0, PARAM_INT); // lti ID.
|
||||
$action = optional_param('action', '', PARAM_TEXT);
|
||||
$foruserid = optional_param('user', 0, PARAM_INT);
|
||||
$forceview = optional_param('forceview', 0, PARAM_BOOL);
|
||||
|
||||
if ($l) { // Two ways to specify the module.
|
||||
@ -85,6 +87,10 @@ $PAGE->set_context($context);
|
||||
require_login($course, true, $cm);
|
||||
require_capability('mod/lti:view', $context);
|
||||
|
||||
if (!empty($foruserid) && (int)$foruserid !== (int)$USER->id) {
|
||||
require_capability('gradereport/grader:view', $context);
|
||||
}
|
||||
|
||||
$url = new moodle_url('/mod/lti/view.php', array('id' => $cm->id));
|
||||
$PAGE->set_url($url);
|
||||
|
||||
@ -126,29 +132,30 @@ if ($typeid) {
|
||||
$config = new stdClass();
|
||||
$config->lti_ltiversion = LTI_VERSION_1;
|
||||
}
|
||||
|
||||
$launchurl = new moodle_url('/mod/lti/launch.php', ['id' => $cm->id, 'triggerview' => 0]);
|
||||
if ($action) {
|
||||
$launchurl->param('action', $action);;
|
||||
}
|
||||
if ($foruserid) {
|
||||
$launchurl->param('user', $foruserid);;
|
||||
}
|
||||
unset($SESSION->lti_initiatelogin_status);
|
||||
if ($launchcontainer == LTI_LAUNCH_CONTAINER_WINDOW) {
|
||||
if (($launchcontainer == LTI_LAUNCH_CONTAINER_WINDOW)) {
|
||||
if (!$forceview) {
|
||||
echo "<script language=\"javascript\">//<![CDATA[\n";
|
||||
echo "window.open('launch.php?id=" . $cm->id . "&triggerview=0','lti-" . $cm->id . "');";
|
||||
echo "window.open('{$launchurl->out(true)}','lti-$cm->id');";
|
||||
echo "//]]\n";
|
||||
echo "</script>\n";
|
||||
echo "<p>".get_string("basiclti_in_new_window", "lti")."</p>\n";
|
||||
}
|
||||
$url = new moodle_url('/mod/lti/launch.php', array('id' => $cm->id));
|
||||
echo html_writer::start_tag('p');
|
||||
echo html_writer::link($url, get_string("basiclti_in_new_window_open", "lti"), array('target' => '_blank'));
|
||||
echo html_writer::link($launchurl->out(false), get_string("basiclti_in_new_window_open", "lti"), array('target' => '_blank'));
|
||||
echo html_writer::end_tag('p');
|
||||
} else {
|
||||
$content = '';
|
||||
if ($config->lti_ltiversion === LTI_VERSION_1P3) {
|
||||
$content = lti_initiate_login($cm->course, $id, $lti, $config);
|
||||
}
|
||||
|
||||
// Build the allowed URL, since we know what it will be from $toolurl.
|
||||
// If the specified URL is invalid, the iframe won't load, but we still want to avoid parse related errors here.
|
||||
// So we set an empty default allowed URL, and only build a real one if the parse is successful.
|
||||
// Build the allowed URL, since we know what it will be from $lti->toolurl,
|
||||
// If the specified toolurl is invalid the iframe won't load, but we still want to avoid parse related errors here.
|
||||
// So we set an empty default allowed url, and only build a real one if the parse is successful.
|
||||
$ltiallow = '';
|
||||
$urlparts = parse_url($toolurl);
|
||||
if ($urlparts && array_key_exists('scheme', $urlparts) && array_key_exists('host', $urlparts)) {
|
||||
@ -164,7 +171,7 @@ if ($launchcontainer == LTI_LAUNCH_CONTAINER_WINDOW) {
|
||||
$attributes['id'] = "contentframe";
|
||||
$attributes['height'] = '600px';
|
||||
$attributes['width'] = '100%';
|
||||
$attributes['src'] = 'launch.php?id=' . $cm->id . '&triggerview=0';
|
||||
$attributes['src'] = $launchurl;
|
||||
$attributes['allow'] = "microphone $ltiallow; " .
|
||||
"camera $ltiallow; " .
|
||||
"geolocation $ltiallow; " .
|
||||
|
Loading…
x
Reference in New Issue
Block a user