MDL-65306 mod_lti: fix deeplinking support for tag and resourceid

This commit is contained in:
Claude Vervoort
2020-04-24 15:30:26 -04:00
parent 9df4a4de18
commit e5423ddb93
16 changed files with 592 additions and 74 deletions
+1 -1
View File
@@ -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("cmidnumber",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)}}});
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)}}});
//# sourceMappingURL=contentitem.min.js.map
File diff suppressed because one or more lines are too long
+2 -1
View File
@@ -105,7 +105,8 @@ define(
new FormField('secureicon', FormField.TYPES.TEXT, true, ''),
new FormField('launchcontainer', FormField.TYPES.SELECT, true, 0),
new FormField('grade_modgrade_point', FormField.TYPES.TEXT, false, ''),
new FormField('cmidnumber', FormField.TYPES.TEXT, true, '')
new FormField('lineitemresourceid', FormField.TYPES.TEXT, true, ''),
new FormField('lineitemtag', FormField.TYPES.TEXT, true, '')
];
/**
+5
View File
@@ -47,6 +47,7 @@
*/
defined('MOODLE_INTERNAL') || die;
use ltiservice_gradebookservices\local\service\gradebookservices;
/**
* List of features supported in URL module
@@ -118,6 +119,8 @@ function lti_add_instance($lti, $mform) {
lti_grade_item_update($lti);
}
gradebookservices::update_coupled_gradebookservices($lti, $lti->lineitemresourceid ?? '', $lti->lineitemtag ?? '');
$completiontimeexpected = !empty($lti->completionexpected) ? $lti->completionexpected : null;
\core_completion\api::update_completion_date_event($lti->coursemodule, 'lti', $lti->id, $completiontimeexpected);
@@ -165,6 +168,8 @@ function lti_update_instance($lti, $mform) {
$lti->typeid = $lti->urlmatchedtypeid;
}
gradebookservices::update_coupled_gradebookservices($lti, $lti->lineitemresourceid, $lti->lineitemtag);
$completiontimeexpected = !empty($lti->completionexpected) ? $lti->completionexpected : null;
\core_completion\api::update_completion_date_event($lti->coursemodule, 'lti', $lti->id, $completiontimeexpected);
+9 -1
View File
@@ -1515,8 +1515,13 @@ function lti_tool_configuration_from_content_item($typeid, $messagetype, $ltiver
}
}
$config->grade_modgrade_point = $maxscore;
$config->lineitemresourceid = '';
$config->lineitemtag = '';
if (isset($lineitem->assignedActivity) && isset($lineitem->assignedActivity->activityId)) {
$config->cmidnumber = $lineitem->assignedActivity->activityId;
$config->lineitemresourceid = $lineitem->assignedActivity->activityId ? : '';
}
if (isset($lineitem->tag)) {
$config->lineitemtag = $lineitem->tag ? : '';
}
}
}
@@ -1613,6 +1618,9 @@ function lti_convert_content_items($param) {
$newitem->lineItem->assignedActivity = new stdClass();
$newitem->lineItem->assignedActivity->activityId = $item->lineItem->resourceId;
}
if (isset($item->lineItem->tag)) {
$newitem->lineItem->tag = $item->lineItem->tag;
}
if (isset($item->lineItem->scoreMaximum)) {
$newitem->lineItem->scoreConstraints = new stdClass();
$newitem->lineItem->scoreConstraints->{'@type'} = 'NumericLimits';
+25
View File
@@ -50,6 +50,7 @@ defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot.'/course/moodleform_mod.php');
require_once($CFG->dirroot.'/mod/lti/locallib.php');
use ltiservice_gradebookservices\local\service\gradebookservices;
class mod_lti_mod_form extends moodleform_mod {
@@ -207,6 +208,12 @@ class mod_lti_mod_form extends moodleform_mod {
$mform->addElement('hidden', 'urlmatchedtypeid', '', array( 'id' => 'id_urlmatchedtypeid' ));
$mform->setType('urlmatchedtypeid', PARAM_INT);
$mform->addElement('hidden', 'lineitemresourceid', '', array( 'id' => 'id_lineitemresourceid' ));
$mform->setType('lineitemresourceid', PARAM_TEXT);
$mform->addElement('hidden', 'lineitemtag', '', array( 'id' => 'id_lineitemtag'));
$mform->setType('lineitemtag', PARAM_TEXT);
$launchoptions = array();
$launchoptions[LTI_LAUNCH_CONTAINER_DEFAULT] = get_string('default', 'lti');
$launchoptions[LTI_LAUNCH_CONTAINER_EMBED] = get_string('embed', 'lti');
@@ -331,4 +338,22 @@ class mod_lti_mod_form extends moodleform_mod {
$PAGE->requires->js_init_call('M.mod_lti.editor.init', array(json_encode($jsinfo)), true, $module);
}
/**
* Sets the current values for resource and tag in case of update.
*
* @param object $defaultvalues default values to populate the form with.
*/
public function set_data($defaultvalues) {
$defaultvalues->lineitemresourceid = '';
$defaultvalues->lineitemtag = '';
if (is_object($defaultvalues) && $defaultvalues->instance) {
$gbs = gradebookservices::find_ltiservice_gradebookservice_for_lti($defaultvalues->instance);
if ($gbs) {
$defaultvalues->lineitemresourceid = $gbs->resourceid;
$defaultvalues->lineitemtag = $gbs->tag;
}
}
parent::set_data($defaultvalues);
}
}
@@ -61,6 +61,7 @@ class backup_ltiservice_gradebookservices_subplugin extends backup_subplugin {
'typeid',
'baseurl',
'ltilinkid',
'resourceid',
'tag',
'vendorcode',
'guid'
@@ -97,6 +97,10 @@ class restore_ltiservice_gradebookservices_subplugin extends restore_subplugin {
} else {
$ltilinkid = null;
}
$resourceid = null;
if (property_exists( $data, 'resourceid' )) {
$resourceid = $data->resourceid;
}
// If this has not been restored before.
if ($this->get_mappingid('gbsgradeitemrestored', $data->id, 0) == 0) {
$newgbsid = $DB->insert_record('ltiservice_gradebookservices', (object) array(
@@ -106,6 +110,7 @@ class restore_ltiservice_gradebookservices_subplugin extends restore_subplugin {
'ltilinkid' => $ltilinkid,
'typeid' => $newtypeid,
'baseurl' => $data->baseurl,
'resourceid' => $resourceid,
'tag' => $data->tag
));
$this->set_mapping('gbsgradeitemoldid', $newgbsid, $data->gradeitemid);
@@ -170,17 +170,14 @@ class lineitem extends resource_base {
}
$item->grademax = grade_floatval($json->scoreMaximum);
}
$resourceid = (isset($json->resourceId)) ? $json->resourceId : '';
if ($item->idnumber !== $resourceid) {
$updategradeitem = true;
}
$item->idnumber = $resourceid;
if ($gbs) {
$tag = (isset($json->tag)) ? $json->tag : null;
if ($gbs->tag !== $tag) {
$resourceid = (isset($json->resourceId)) ? $json->resourceId : '';
$tag = (isset($json->tag)) ? $json->tag : '';
if ($gbs->tag !== $tag || $gbs->resourceid !== $resourceid) {
$upgradegradebookservices = true;
}
$gbs->tag = $tag;
$gbs->resourceid = $resourceid;
}
$ltilinkid = null;
if (isset($json->resourceLinkId)) {
@@ -259,6 +256,7 @@ class lineitem extends resource_base {
'typeid' => $typeid,
'baseurl' => $baseurl,
'ltilinkid' => $ltilinkid,
'resourceid' => $resourceid,
'tag' => $gbs->tag
));
}
@@ -266,33 +266,23 @@ class lineitems extends resource_base {
$toolproxyid = null;
$baseurl = lti_get_type_type_config($typeid)->lti_toolurl;
}
$params = array();
$params['itemname'] = $json->label;
$params['gradetype'] = GRADE_TYPE_VALUE;
$params['grademax'] = $max;
$params['grademin'] = 0;
$item = new \grade_item(array('id' => 0, 'courseid' => $contextid));
\grade_item::set_properties($item, $params);
$item->itemtype = 'manual';
$item->idnumber = $resourceid;
$item->grademax = $max;
$id = $item->insert('mod/ltiservice_gradebookservices');
$DB->insert_record('ltiservice_gradebookservices', (object)array(
'gradeitemid' => $id,
'courseid' => $contextid,
'toolproxyid' => $toolproxyid,
'typeid' => $typeid,
'baseurl' => $baseurl,
'ltilinkid' => $ltilinkid,
'tag' => $tag
));
$gradebookservices = new gradebookservices();
$id = $gradebookservices->add_standalone_lineitem($contextid,
$json->label,
$max,
$baseurl,
$ltilinkid,
$resourceid,
$tag,
$typeid,
$toolproxyid);
if (is_null($typeid)) {
$json->id = parent::get_endpoint() . "/{$id}/lineitem";
} else {
$json->id = parent::get_endpoint() . "/{$id}/lineitem?type_id={$typeid}";
}
return json_encode($json, JSON_UNESCAPED_SLASHES);
}
/**
@@ -158,25 +158,25 @@ class gradebookservices extends service_base {
$this->get_typeconfig()['ltiservice_gradesynchronization'] == self::GRADEBOOKSERVICES_FULL) {
// Check for used in context is only needed because there is no explicit site tool - course relation.
if ($this->is_allowed_in_context($typeid, $courseid)) {
if (is_null($modlti)) {
$id = null;
} else {
$id = null;
if (!is_null($modlti)) {
$conditions = array('courseid' => $courseid, 'itemtype' => 'mod',
'itemmodule' => 'lti', 'iteminstance' => $modlti);
$lineitems = $DB->get_records('grade_items', $conditions);
$coupledlineitems = $DB->get_records('grade_items', $conditions);
$conditionsgbs = array('courseid' => $courseid, 'ltilinkid' => $modlti);
$lineitemsgbs = $DB->get_records('ltiservice_gradebookservices', $conditionsgbs);
if (count($lineitems) + count($lineitemsgbs) == 1) {
if ($lineitems) {
$lineitem = reset($lineitems);
$id = $lineitem->id;
// If a link has more that one attached grade items, per spec we do not populate line item url.
if (count($lineitemsgbs) == 1) {
$id = reset($lineitemsgbs)->gradeitemid;
}
if (count($lineitemsgbs) < 2 && count($coupledlineitems) == 1) {
$coupledid = reset($coupledlineitems)->id;
if (!is_null($id) && $id != $coupledid) {
$id = null;
} else {
$lineitemsgb = reset($lineitemsgbs);
$id = $lineitemsgb->gradeitemid;
$id = $coupledid;
}
} else {
$id = null;
}
}
$launchparameters['gradebookservices_scope'] = implode(',', $this->get_permitted_scopes());
@@ -210,15 +210,9 @@ class gradebookservices extends service_base {
// Select all lti potential linetiems in site.
$params = array('courseid' => $courseid);
$optionalfilters = "";
if (isset($resourceid)) {
$optionalfilters .= " AND (i.idnumber = :resourceid)";
$params['resourceid'] = $resourceid;
}
$sql = "SELECT i.*
FROM {grade_items} i
WHERE (i.courseid = :courseid)
{$optionalfilters}
ORDER BY i.id";
$lineitems = $DB->get_records_sql($sql, $params);
@@ -230,7 +224,8 @@ class gradebookservices extends service_base {
foreach ($lineitems as $lineitem) {
$gbs = $this->find_ltiservice_gradebookservice_for_lineitem($lineitem->id);
if ($gbs && (!isset($tag) || (isset($tag) && $gbs->tag == $tag))
&& (!isset($ltilinkid) || (isset($ltilinkid) && $gbs->ltilinkid == $ltilinkid))) {
&& (!isset($ltilinkid) || (isset($ltilinkid) && $gbs->ltilinkid == $ltilinkid))
&& (!isset($resourceid) || (isset($resourceid) && $gbs->resourceid == $resourceid))) {
if (is_null($typeid)) {
if ($this->get_tool_proxy()->id == $gbs->toolproxyid) {
array_push($lineitemstoreturn, $lineitem);
@@ -240,8 +235,12 @@ class gradebookservices extends service_base {
array_push($lineitemstoreturn, $lineitem);
}
}
} else if (($lineitem->itemtype == 'mod') && ($lineitem->itemmodule == 'lti') && (!isset($tag) &&
(!isset($ltilinkid) || (isset($ltilinkid) && $lineitem->iteminstance == $ltilinkid)))) {
} else if (($lineitem->itemtype == 'mod'
&& $lineitem->itemmodule == 'lti'
&& !isset($resourceid)
&& !isset($tag)
&& (!isset($ltilinkid) || (isset($ltilinkid)
&& $lineitem->iteminstance == $ltilinkid)))) {
// We will need to check if the activity related belongs to our tool proxy.
$ltiactivity = $DB->get_record('lti', array('id' => $lineitem->iteminstance));
if (($ltiactivity) && (isset($ltiactivity->typeid))) {
@@ -323,6 +322,59 @@ class gradebookservices extends service_base {
return $lineitem;
}
/**
* Adds a decoupled (standalone) line item.
* Decoupled line items are not directly attached to
* an lti instance activity. They are recorded in
* the gradebook as manual activities and the
* gradebookservices is used to associate that manual column
* with the tool in addition to storing the LTI related
* metadata (resource id, tag).
*
* @param string $courseid ID of course
* @param string $label label of lineitem
* @param float $maximumscore maximum score of lineitem
* @param string $baseurl
* @param int|null $ltilinkid id of lti instance this line item is associated with
* @param string|null $resourceid resource id of lineitem
* @param string|null $tag tag of lineitem
* @param int $typeid lti type to which this line item is associated with
* @param int|null $toolproxyid lti2 tool proxy to which this lineitem is associated to
*
* @return int id of the created gradeitem
*/
public function add_standalone_lineitem(string $courseid,
string $label,
float $maximumscore,
string $baseurl,
?int $ltilinkid,
?string $resourceid,
?string $tag,
int $typeid,
int $toolproxyid = null) : int {
global $DB;
$params = array();
$params['itemname'] = $label;
$params['gradetype'] = GRADE_TYPE_VALUE;
$params['grademax'] = $maximumscore;
$params['grademin'] = 0;
$item = new \grade_item(array('id' => 0, 'courseid' => $courseid));
\grade_item::set_properties($item, $params);
$item->itemtype = 'manual';
$item->grademax = $maximumscore;
$id = $item->insert('mod/ltiservice_gradebookservices');
$DB->insert_record('ltiservice_gradebookservices', (object)array(
'gradeitemid' => $id,
'courseid' => $courseid,
'toolproxyid' => $toolproxyid,
'typeid' => $typeid,
'baseurl' => $baseurl,
'ltilinkid' => $ltilinkid,
'resourceid' => $resourceid,
'tag' => $tag
));
return $id;
}
/**
* Set a grade item.
@@ -341,7 +393,7 @@ class gradebookservices extends service_base {
}
/**
* Set a grade item.
* Saves a score received from the LTI tool.
*
* @param object $gradeitem Grade Item record
* @param object $score Result object
@@ -428,9 +480,9 @@ class gradebookservices extends service_base {
$lineitem->id = "{$endpoint}/{$item->id}/lineitem" . $typeidstring;
$lineitem->label = $item->itemname;
$lineitem->scoreMaximum = floatval($item->grademax);
$lineitem->resourceId = (!empty($item->idnumber)) ? $item->idnumber : '';
$gbs = self::find_ltiservice_gradebookservice_for_lineitem($item->id);
if ($gbs) {
$lineitem->resourceId = (!empty($gbs->resourceid)) ? $gbs->resourceid : '';
$lineitem->tag = (!empty($gbs->tag)) ? $gbs->tag : '';
if (isset($gbs->ltilinkid)) {
$lineitem->resourceLinkId = strval($gbs->ltilinkid);
@@ -561,6 +613,42 @@ class gradebookservices extends service_base {
}
}
/**
* Updates the tag and resourceid 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. Might be an empty string.
* @param string|null $tag The tag to apply to the lineitem. Might be an empty string.
*
*/
public static function update_coupled_gradebookservices(object $ltiinstance,
?string $resourceid,
?string $tag) : void {
global $DB;
if ($ltiinstance && $ltiinstance->typeid) {
$gradeitem = $DB->get_record('grade_items', array('itemmodule' => 'lti', 'iteminstance' => $ltiinstance->id));
if ($gradeitem) {
$gbs = self::find_ltiservice_gradebookservice_for_lineitem($gradeitem->id);
if ($gbs) {
$gbs->resourceid = $resourceid;
$gbs->tag = $tag;
$DB->update_record('ltiservice_gradebookservices', $gbs);
} else {
$baseurl = lti_get_type_type_config($ltiinstance->typeid)->lti_toolurl;
$DB->insert_record('ltiservice_gradebookservices', (object)array(
'gradeitemid' => $gradeitem->id,
'courseid' => $gradeitem->courseid,
'typeid' => $ltiinstance->typeid,
'baseurl' => $baseurl,
'ltilinkid' => $ltiinstance->id,
'resourceid' => $resourceid,
'tag' => $tag
));
}
}
}
}
/**
* Deletes orphaned rows from the 'ltiservice_gradebookservices' table.
*
@@ -606,29 +694,34 @@ class gradebookservices extends service_base {
return $gradableuser;
}
/**
* Find the right element in the ltiservice_gradebookservice table for an lti instance
*
* @param string $instanceid The LTI module instance id
* @return object gradebookservice for this line item
*/
public static function find_ltiservice_gradebookservice_for_lti($instanceid) {
global $DB;
if ($instanceid) {
$gradeitem = $DB->get_record('grade_items', array('itemmodule' => 'lti', 'iteminstance' => $instanceid));
if ($gradeitem) {
return self::find_ltiservice_gradebookservice_for_lineitem($gradeitem->id);
}
}
}
/**
* Find the right element in the ltiservice_gradebookservice table for a lineitem
*
* @param string $lineitemid The lineitem
* @return object|bool gradebookservice id or false if none
* @param string $lineitemid The lineitem (gradeitem) id
* @return object gradebookservice if it exists
*/
public static function find_ltiservice_gradebookservice_for_lineitem($lineitemid) {
global $DB;
if (!$lineitemid) {
return false;
}
$gradeitem = $DB->get_record('grade_items', array('id' => $lineitemid));
if ($gradeitem) {
$gbs = $DB->get_record('ltiservice_gradebookservices',
array('gradeitemid' => $gradeitem->id, 'courseid' => $gradeitem->courseid));
if ($gbs) {
return $gbs;
} else {
return false;
}
} else {
return false;
if ($lineitemid) {
return $DB->get_record('ltiservice_gradebookservices',
array('gradeitemid' => $lineitemid));
}
}
@@ -13,6 +13,7 @@
<FIELD NAME="typeid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="ID of the LTI Type if not Proxy."/>
<FIELD NAME="baseurl" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Lineitem URL that will be returned to the Tool provider"/>
<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"/>
</FIELDS>
<KEYS>
@@ -0,0 +1,110 @@
<?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 file is part of BasicLTI4Moodle
//
// BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability)
// consumer for Moodle 1.9 and Moodle 2.0. BasicLTI is a IMS Standard that allows web
// based learning tools to be easily integrated in LMS as native ones. The IMS BasicLTI
// specification is part of the IMS standard Common Cartridge 1.1 Sakai and other main LMS
// are already supporting or going to support BasicLTI. This project Implements the consumer
// for Moodle. Moodle is a Free Open source Learning Management System by Martin Dougiamas.
// BasicLTI4Moodle is a project iniciated and leaded by Ludo(Marc Alier) and Jordi Piguillem
// at the GESSI research group at UPC.
// SimpleLTI consumer for Moodle is an implementation of the early specification of LTI
// by Charles Severance (Dr Chuck) htp://dr-chuck.com , developed by Jordi Piguillem in a
// Google Summer of Code 2008 project co-mentored by Charles Severance and Marc Alier.
//
// BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis
// of the Universitat Politecnica de Catalunya http://www.upc.edu
// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu.
/**
* This file defines tasks performed by the plugin.
*
* @package ltiservice_gradebookservices
* @copyright 2020 Cengage Learning http://www.cengage.com
* @author Claude Vervoort
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
/**
* xmldb_ltiservice_gradebookservices_upgrade is the function that upgrades
* the gradebook lti service subplugin database when is needed.
*
* This function is automatically called when version number in
* version.php changes.
*
* @param int $oldversion New old version number.
*
* @return boolean
*/
function xmldb_ltiservice_gradebookservices_upgrade($oldversion) {
global $CFG, $DB, $OUTPUT;
$dbman = $DB->get_manager();
if ($oldversion < 2020020601) {
// Define field typeid to be added to lti_tool_settings.
$table = new xmldb_table('ltiservice_gradebookservices');
$field = new xmldb_field('resourceid', XMLDB_TYPE_CHAR, "512", null, null, null, null);
// Conditionally launch add field typeid.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Lti savepoint reached.
upgrade_plugin_savepoint(true, 2020020601, 'ltiservice', 'gradebookservices');
}
if ($oldversion < 2020020602) {
// Now that we have added the new column let's migrate it'
// Prior implementation was storing the resourceid under the grade item idnumber, so moving it to lti_gradebookservices.
// We only care for mod/lti grade items as manual columns would already have a matching gradebookservices record.
$DB->execute("INSERT INTO {ltiservice_gradebookservices} (gradeitemid, courseid, typeid, resourceid, baseurl)
SELECT gi.id, courseid, lti.typeid, gi.idnumber, t.baseurl
FROM {grade_items} gi
JOIN {lti} lti ON lti.id=gi.iteminstance AND gi.itemtype='mod' AND gi.itemmodule='lti'
JOIN {lti_types} t ON t.id = lti.typeid
WHERE gi.id NOT IN ( SELECT gradeitemid
FROM {ltiservice_gradebookservices} )
AND gi.idnumber IS NOT NULL
AND gi.idnumber <> ''");
// Lti savepoint reached.
upgrade_plugin_savepoint(true, 2020020602, 'ltiservice', 'gradebookservices');
}
if ($oldversion < 2020020603) {
// Here updating the resourceid of pre-existing lti_gradebookservices.
$DB->execute("UPDATE {ltiservice_gradebookservices}
SET resourceid = (SELECT idnumber FROM {grade_items} WHERE id=gradeitemid)
WHERE gradeitemid in (SELECT id FROM {grade_items}
WHERE ((itemtype='mod' AND itemmodule='lti') OR itemtype='manual')
AND idnumber IS NOT NULL
AND idnumber <> '')
AND (resourceid is null OR resourceid = '')");
// Lti savepoint reached.
upgrade_plugin_savepoint(true, 2020020603, 'ltiservice', 'gradebookservices');
}
return true;
}
@@ -0,0 +1,282 @@
<?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/>.
/**
* Unit tests for mod_lti gradebookservices
* @package ltiservice_gradebookservices
* @category external
* @copyright 2020 Claude Vervoort <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use ltiservice_gradebookservices\local\service\gradebookservices;
defined('MOODLE_INTERNAL') || die();
/**
* Unit tests for lti gradebookservices.
*/
class mod_lti_gradebookservices_testcase extends advanced_testcase {
/**
* 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() {
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);
$this->assertNotNull($ltiinstance);
$gbs = gradebookservices::find_ltiservice_gradebookservice_for_lti($ltiinstance->id);
$this->assertNotNull($gbs);
$this->assertEquals($resourceid, $gbs->resourceid);
$this->assertEquals($tag, $gbs->tag);
$this->assert_lineitems($course, $typeid, $ltiinstance->name, $ltiinstance, $resourceid, $tag);
}
/**
* Test saving a standalone LTI lineitem with resource and tag info
* that can be retrieved using the gradebook service API.
*/
public function test_lti_add_standalone_lineitem() {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$resourceid = "test-resource-standalone";
$tag = "test-tag-standalone";
$typeid = $this->create_type();
$this->create_standalone_lineitem($course->id, $typeid, $resourceid, $tag);
$this->assert_lineitems($course, $typeid, "manualtest", null, $resourceid, $tag);
}
/**
* 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
* the line items should be actually passed.
*/
public function test_get_launch_parameters_coupled() {
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');
$this->assertNotNull($ltiinstance);
$gbservice = new gradebookservices();
$params = $gbservice->get_launch_parameters('basic-lti-launch-request', $course->id, 111, $typeid, $ltiinstance->id);
$this->assertEquals('$LineItem.url', $params['lineitem_url']);
$this->assertEquals('$LineItem.url', $params['lineitem_url']);
$this->create_standalone_lineitem($course->id, $typeid, 'resource-id', 'tag', $ltiinstance->id);
$params = $gbservice->get_launch_parameters('basic-lti-launch-request', $course->id, 111, $typeid, $ltiinstance->id);
$this->assertEquals('$LineItems.url', $params['lineitems_url']);
// 2 line items for a single link, we cannot return a single line item url.
$this->assertFalse(array_key_exists('$LineItem.url', $params));
}
/**
* Test line item URL is populated for not coupled line item only
* if there is a single line item attached to that lti instance.
*/
public function test_get_launch_parameters_decoupled() {
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_notgraded_lti($typeid, $course);
$this->assertNotNull($ltiinstance);
$gbservice = new gradebookservices();
$params = $gbservice->get_launch_parameters('basic-lti-launch-request', $course->id, 111, $typeid, $ltiinstance->id);
$this->assertEquals('$LineItems.url', $params['lineitems_url']);
$this->assertFalse(array_key_exists('$LineItem.url', $params));
$this->create_standalone_lineitem($course->id, $typeid, 'resource-id', 'tag', $ltiinstance->id);
$params = $gbservice->get_launch_parameters('basic-lti-launch-request', $course->id, 111, $typeid, $ltiinstance->id);
$this->assertEquals('$LineItems.url', $params['lineitems_url']);
$this->assertEquals('$LineItem.url', $params['lineitem_url']);
// 2 line items for a single link, we cannot return a single line item url.
$this->create_standalone_lineitem($course->id, $typeid, 'resource-id', 'tag-2', $ltiinstance->id);
$this->assertFalse(array_key_exists('$LineItem.url', $params));
}
/**
* Asserts a matching gradebookservices record exist with the matching tag and resourceid.
*
* @param object $course current course
* @param int $typeid Type id of the tool
* @param string $label Label of the line item
* @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
*/
private function assert_lineitems(object $course,
int $typeid,
string $label,
?object $ltiinstance,
?string $resourceid,
?string $tag) : 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);
$gradeitems = $gbservice->get_lineitems($course->id, $resourceid, null, null, null, null, $typeid);
$this->assertEquals(1, $gradeitems[0]);
if (isset($ltiinstance)) {
$gradeitems = $gbservice->get_lineitems($course->id, null, $ltiinstance->id, null, null, null, $typeid);
$this->assertEquals(1, $gradeitems[0]);
$gradeitems = $gbservice->get_lineitems($course->id, null, $ltiinstance->id + 1, null, null, null, $typeid);
$this->assertEquals(0, $gradeitems[0]);
}
$gradeitems = $gbservice->get_lineitems($course->id, null, null, $tag, null, null, $typeid);
$this->assertEquals(1, $gradeitems[0]);
$gradeitems = $gbservice->get_lineitems($course->id, 'an unknown resource id', null, null, null, null, $typeid);
$this->assertEquals(0, $gradeitems[0]);
$gradeitems = $gbservice->get_lineitems($course->id, null, null, 'an unknown tag', null, null, $typeid);
$this->assertEquals(0, $gradeitems[0]);
}
/**
* 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
*
* @return object lti instance created
*/
private function create_graded_lti(int $typeid, object $course, ?string $resourceid, ?string $tag) : object {
$lti = ['course' => $course->id,
'typeid' => $typeid,
'instructorchoiceacceptgrades' => LTI_SETTING_ALWAYS,
'grade' => 10,
'lineitemresourceid' => $resourceid,
'lineitemtag' => $tag];
return $this->getDataGenerator()->create_module('lti', $lti, array());
}
/**
* Inserts an lti instance that is not graded.
*
* @param int $typeid Type Id of the LTI Tool.
* @param object $course course where to add the lti instance.
*
* @return object lti instance created
*/
private function create_notgraded_lti(int $typeid, object $course) : object {
$lti = ['course' => $course->id,
'typeid' => $typeid,
'instructorchoiceacceptgrades' => LTI_SETTING_NEVER];
return $this->getDataGenerator()->create_module('lti', $lti, array());
}
/**
* Inserts a standalone lineitem (gradeitem, gradebookservices entries).
*
* @param int $courseid Id of the course where the standalone line item will be added.
* @param int $typeid of the LTI Tool
* @param string|null $resourceid resource id
* @param string|null $tag tag
* @param int|null $ltiinstanceid Id of the LTI instance the standalone line item will be related to.
*
*/
private function create_standalone_lineitem(int $courseid,
int $typeid,
?string $resourceid,
?string $tag,
int $ltiinstanceid = null) : void {
$gbservice = new gradebookservices();
$gbservice->add_standalone_lineitem($courseid,
"manualtest",
10,
"https://test.phpunit",
$ltiinstanceid,
$resourceid,
$tag,
$typeid,
null /*toolproxyid*/);
}
/**
* 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);
}
}
@@ -25,6 +25,6 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2019111800;
$plugin->version = 2020020603;
$plugin->requires = 2019111200;
$plugin->component = 'ltiservice_gradebookservices';
-1
View File
@@ -26,7 +26,6 @@
defined('MOODLE_INTERNAL') || die();
/**
* Unit tests for mod_lti lib
*