Merge branch 'MDL-78909-master-2' of https://github.com/ilyatregubov/moodle

This commit is contained in:
Jake Dallimore
2023-09-11 12:32:57 +08:00
24 changed files with 852 additions and 50 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
define("mod_lti/course_tools_list",["exports","core/notification","core/pending","core/ajax","core/toast","core/str","core_table/dynamic","core_table/local/dynamic/selectors"],(function(_exports,_notification,_pending,_ajax,_toast,_str,_dynamic,Selectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_ajax=_interopRequireDefault(_ajax),Selectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Selectors);_exports.init=()=>{document.addEventListener("click",(event=>{const courseToolDelete=event.target.closest('[data-action="course-tool-delete"]');if(courseToolDelete){event.preventDefault();const deleteBodyStringId=courseToolDelete.dataset.courseToolUsage>0?"deletecoursetoolwithusageconfirm":"deletecoursetoolconfirm",requiredStrings=[{key:"deletecoursetool",component:"mod_lti",param:courseToolDelete.dataset.courseToolName},{key:deleteBodyStringId,component:"mod_lti",param:courseToolDelete.dataset.courseToolName},{key:"delete",component:"core",param:courseToolDelete.dataset.courseToolName},{key:"coursetooldeleted",component:"mod_lti",param:courseToolDelete.dataset.courseToolName}],triggerElement=courseToolDelete.closest(".dropdown").querySelector(".dropdown-toggle");(0,_str.getStrings)(requiredStrings).then((_ref=>{let[modalTitle,modalBody,deleteLabel]=_ref;return _notification.default.deleteCancelPromise(modalTitle,modalBody,deleteLabel,{triggerElement:triggerElement})})).then((()=>{const pendingPromise=new _pending.default("mod_lti/course_tools:delete"),request={methodname:"mod_lti_delete_course_tool_type",args:{tooltypeid:courseToolDelete.dataset.courseToolId}};return _ajax.default.call([request])[0].then((0,_toast.add)((0,_str.getString)("coursetooldeleted","mod_lti",courseToolDelete.dataset.courseToolName))).then((()=>{const tableRoot=triggerElement.closest(Selectors.main.region);return(0,_dynamic.refreshTableContent)(tableRoot)})).then(pendingPromise.resolve).catch(_notification.default.exception)})).catch((()=>{}))}}))}}));
define("mod_lti/course_tools_list",["exports","core/notification","core/pending","core/ajax","core/toast","core/str","core_table/dynamic","core_table/local/dynamic/selectors","./repository"],(function(_exports,_notification,_pending,_ajax,_toast,_str,_dynamic,Selectors,_repository){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_ajax=_interopRequireDefault(_ajax),Selectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Selectors);_exports.init=()=>{document.addEventListener("click",(event=>{const courseToolDelete=event.target.closest('[data-action="course-tool-delete"]');if(courseToolDelete){event.preventDefault();const deleteBodyStringId=courseToolDelete.dataset.courseToolUsage>0?"deletecoursetoolwithusageconfirm":"deletecoursetoolconfirm",requiredStrings=[{key:"deletecoursetool",component:"mod_lti",param:courseToolDelete.dataset.courseToolName},{key:deleteBodyStringId,component:"mod_lti",param:courseToolDelete.dataset.courseToolName},{key:"delete",component:"core",param:courseToolDelete.dataset.courseToolName},{key:"coursetooldeleted",component:"mod_lti",param:courseToolDelete.dataset.courseToolName}],triggerElement=courseToolDelete.closest(".dropdown").querySelector(".dropdown-toggle");(0,_str.getStrings)(requiredStrings).then((_ref=>{let[modalTitle,modalBody,deleteLabel]=_ref;return _notification.default.deleteCancelPromise(modalTitle,modalBody,deleteLabel,{triggerElement:triggerElement})})).then((()=>{const pendingPromise=new _pending.default("mod_lti/course_tools:delete"),request={methodname:"mod_lti_delete_course_tool_type",args:{tooltypeid:courseToolDelete.dataset.courseToolId}};return _ajax.default.call([request])[0].then((0,_toast.add)((0,_str.getString)("coursetooldeleted","mod_lti",courseToolDelete.dataset.courseToolName))).then((()=>{const tableRoot=triggerElement.closest(Selectors.main.region);return(0,_dynamic.refreshTableContent)(tableRoot)})).then(pendingPromise.resolve).catch(_notification.default.exception)})).catch((()=>{}))}const courseShowInActivityChooser=event.target.closest('[data-action="showinactivitychooser-toggle"]');if(courseShowInActivityChooser){const showInActivityChooserStateToggle="0"===courseShowInActivityChooser.dataset.state?1:0;return(0,_repository.toggleShowInActivityChooser)(courseShowInActivityChooser.dataset.id,courseShowInActivityChooser.dataset.courseid,showInActivityChooserStateToggle)}}))}}));
//# sourceMappingURL=course_tools_list.min.js.map
File diff suppressed because one or more lines are too long
+10
View File
@@ -0,0 +1,10 @@
define("mod_lti/repository",["exports","core/ajax"],(function(_exports,_ajax){var obj;
/**
* Module to handle AJAX interactions.
*
* @module mod_lti/repository
* @copyright 2023 Ilya Tregubov <ilya.a.tregubov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.toggleShowInActivityChooser=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.toggleShowInActivityChooser=(tooltypeid,courseid,showinactivitychooser)=>_ajax.default.call([{methodname:"mod_lti_toggle_showinactivitychooser",args:{tooltypeid:tooltypeid,courseid:courseid,showinactivitychooser:showinactivitychooser}}])[0]}));
//# sourceMappingURL=repository.min.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Module to handle AJAX interactions.\n *\n * @module mod_lti/repository\n * @copyright 2023 Ilya Tregubov <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\n\n/**\n * Toggle coursevisible of a tool\n *\n * @param {Number} tooltypeid Too type id\n * @param {Number} courseid Course ID\n * @param {Number} showinactivitychooser showinactivitychooser state\n * @return {Promise}\n */\nexport const toggleShowInActivityChooser = (\n tooltypeid,\n courseid,\n showinactivitychooser,\n) => Ajax.call([{\n methodname: 'mod_lti_toggle_showinactivitychooser',\n args: {\n tooltypeid,\n courseid,\n showinactivitychooser,\n },\n}])[0];\n"],"names":["tooltypeid","courseid","showinactivitychooser","Ajax","call","methodname","args"],"mappings":";;;;;;;kMAiC2C,CACvCA,WACAC,SACAC,wBACCC,cAAKC,KAAK,CAAC,CACZC,WAAY,uCACZC,KAAM,CACFN,WAAAA,WACAC,SAAAA,SACAC,sBAAAA,0BAEJ"}
+11
View File
@@ -30,6 +30,7 @@ import {add as addToast} from 'core/toast';
import {getString, getStrings} from 'core/str';
import {refreshTableContent} from 'core_table/dynamic';
import * as Selectors from 'core_table/local/dynamic/selectors';
import {toggleShowInActivityChooser} from "./repository";
/**
* Initialise module.
@@ -78,5 +79,15 @@ export const init = () => {
return;
});
}
const courseShowInActivityChooser = event.target.closest('[data-action="showinactivitychooser-toggle"]');
if (courseShowInActivityChooser) {
const showInActivityChooserStateToggle = courseShowInActivityChooser.dataset.state === "0" ? 1 : 0;
return toggleShowInActivityChooser(
courseShowInActivityChooser.dataset.id,
courseShowInActivityChooser.dataset.courseid,
showInActivityChooserStateToggle,
);
}
});
};
+45
View File
@@ -0,0 +1,45 @@
// 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/>.
/**
* Module to handle AJAX interactions.
*
* @module mod_lti/repository
* @copyright 2023 Ilya Tregubov <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Ajax from 'core/ajax';
/**
* Toggle coursevisible of a tool
*
* @param {Number} tooltypeid Too type id
* @param {Number} courseid Course ID
* @param {Number} showinactivitychooser showinactivitychooser state
* @return {Promise}
*/
export const toggleShowInActivityChooser = (
tooltypeid,
courseid,
showinactivitychooser,
) => Ajax.call([{
methodname: 'mod_lti_toggle_showinactivitychooser',
args: {
tooltypeid,
courseid,
showinactivitychooser,
},
}])[0];
@@ -146,6 +146,12 @@ class backup_lti_activity_structure_step extends backup_activity_structure_step
'state'
));
$lticoursevisible = new backup_nested_element('lticoursevisible', ['id'], [
'typeid',
'courseid',
'coursevisible',
]);
// Build the tree
$lti->add_child($ltitype);
$ltitype->add_child($ltitypesconfigs);
@@ -156,6 +162,7 @@ class backup_lti_activity_structure_step extends backup_activity_structure_step
$ltitoolsettings->add_child($ltitoolsetting);
$lti->add_child($ltisubmissions);
$ltisubmissions->add_child($ltisubmission);
$lti->add_child($lticoursevisible);
// Define sources.
$ltirecord = $DB->get_record('lti', ['id' => $this->task->get_activityid()]);
@@ -193,6 +200,9 @@ class backup_lti_activity_structure_step extends backup_activity_structure_step
$ltisubmission->set_source_table('lti_submission', array('ltiid' => backup::VAR_ACTIVITYID));
}
$lticoursevisibledata = $this->retrieve_lti_coursevisible($ltirecord);
$lticoursevisible->set_source_array($lticoursevisibledata ? [$lticoursevisibledata] : []);
// Define id annotations
$ltitype->annotate_ids('user', 'createdby');
$ltitype->annotate_ids('course', 'course');
@@ -238,4 +248,18 @@ class backup_lti_activity_structure_step extends backup_activity_structure_step
return $record;
}
/**
* Retrieves a record from {lti_coursevisible} table associated with the current type
*
* @param stdClass $ltirecord record from {lti} table
* @return mixed
*/
protected function retrieve_lti_coursevisible(stdClass $ltirecord): mixed {
global $DB;
if (!$ltirecord->typeid) {
return null;
}
return $DB->get_record('lti_coursevisible', ['typeid' => $ltirecord->typeid, 'courseid' => $ltirecord->course]);
}
}
@@ -78,6 +78,8 @@ class restore_lti_activity_structure_step extends restore_activity_structure_ste
$paths[] = $submission;
}
$paths[] = new restore_path_element('lticoursevisible', '/activity/lti/lticoursevisible');
// Add support for subplugin structures.
$this->add_subplugin_structure('ltisource', $lti);
$this->add_subplugin_structure('ltiservice', $lti);
@@ -149,6 +151,23 @@ class restore_lti_activity_structure_step extends restore_activity_structure_ste
$DB->update_record('lti', ['id' => $this->get_new_parentid('lti'), 'typeid' => $ltitypeid]);
}
/**
* Process an lti coursevisible restore
* @param mixed $data The data from backup XML file
* @return void
*/
protected function process_lticoursevisible($data) {
global $DB;
$data = (object)$data;
$data->typeid = $this->get_new_parentid('ltitype');
$data->courseid = $this->get_courseid();
if ($data->typeid) {
$DB->insert_record('lti_coursevisible', $data);
}
}
/**
* Attempts to find existing record in lti_type
* @param stdClass $data
@@ -0,0 +1,83 @@
<?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 mod_lti\external;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_value;
use mod_lti\local\types_helper;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/lti/locallib.php');
/**
* External function to toggle showinactivitychooser setting.
*
* @package mod_lti
* @copyright 2023 Ilya Tregubov <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class toggle_showinactivitychooser extends external_api {
/**
* Get parameter definition.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'tooltypeid' => new external_value(PARAM_INT, 'Tool type ID'),
'courseid' => new external_value(PARAM_INT, 'Course ID'),
'showinactivitychooser' => new external_value(PARAM_BOOL, 'Show in activity chooser'),
]);
}
/**
* Toggles showinactivitychooser setting.
*
* @param int $tooltypeid the id of the course external tool type.
* @param int $courseid the id of the course we are in.
* @param bool $showinactivitychooser Show in activity chooser setting.
* @return bool true or false
*/
public static function execute(int $tooltypeid, int $courseid, bool $showinactivitychooser): bool {
[
'tooltypeid' => $tooltypeid,
'courseid' => $courseid,
'showinactivitychooser' => $showinactivitychooser,
] = self::validate_parameters(self::execute_parameters(), [
'tooltypeid' => $tooltypeid,
'courseid' => $courseid,
'showinactivitychooser' => $showinactivitychooser,
]);
$context = \core\context\course::instance($courseid);
self::validate_context($context);
return types_helper::override_type_showinactivitychooser($tooltypeid, $courseid, $context, $showinactivitychooser);
}
/**
* Get service returns definition.
*
* @return external_value
*/
public static function execute_returns(): external_value {
return new external_value(PARAM_BOOL, 'Success');
}
}
+82 -12
View File
@@ -48,25 +48,95 @@ class types_helper {
if (empty($coursevisible)) {
$coursevisible = [LTI_COURSEVISIBLE_PRECONFIGURED, LTI_COURSEVISIBLE_ACTIVITYCHOOSER];
}
list($coursevisiblesql, $coursevisparams) = $DB->get_in_or_equal($coursevisible, SQL_PARAMS_NAMED, 'coursevisible');
[$coursevisiblesql, $coursevisparams] = $DB->get_in_or_equal($coursevisible, SQL_PARAMS_NAMED, 'coursevisible');
[$coursevisiblesql1, $coursevisparams1] = $DB->get_in_or_equal($coursevisible, SQL_PARAMS_NAMED, 'coursevisible');
[$coursevisibleoverriddensql, $coursevisoverriddenparams] = $DB->get_in_or_equal(
$coursevisible,
SQL_PARAMS_NAMED,
'coursevisibleoverridden');
$coursecond = implode(" OR ", ["t.course = :courseid", "t.course = :siteid"]);
$coursecategory = $DB->get_field('course', 'category', ['id' => $courseid]);
$query = "SELECT t.*
FROM {lti_types} t
LEFT JOIN {lti_types_categories} tc ON t.id = tc.typeid
WHERE t.coursevisible $coursevisiblesql
AND ($coursecond)
AND t.state = :active
AND (tc.id IS NULL OR tc.categoryid = :categoryid)
ORDER BY t.name ASC";
$query = "SELECT *
FROM (SELECT t.*, c.coursevisible as coursevisibleoverridden
FROM {lti_types} t
LEFT JOIN {lti_types_categories} tc ON t.id = tc.typeid
LEFT JOIN {lti_coursevisible} c ON c.typeid = t.id AND c.courseid = $courseid
WHERE (t.coursevisible $coursevisiblesql
OR (c.coursevisible $coursevisiblesql1 AND t.coursevisible NOT IN (:lticoursevisibleno)))
AND ($coursecond)
AND t.state = :active
AND (tc.id IS NULL OR tc.categoryid = :categoryid)) tt
WHERE tt.coursevisibleoverridden IS NULL
OR tt.coursevisibleoverridden $coursevisibleoverriddensql";
return $DB->get_records_sql($query,
return $DB->get_records_sql(
$query,
[
'siteid' => $SITE->id,
'courseid' => $courseid,
'active' => LTI_TOOL_STATE_CONFIGURED,
'categoryid' => $coursecategory
] + $coursevisparams);
'categoryid' => $coursecategory,
'coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER,
'lticoursevisibleno' => LTI_COURSEVISIBLE_NO,
] + $coursevisparams + $coursevisparams1 + $coursevisoverriddenparams
);
}
/**
* Override coursevisible for a given tool on course level.
*
* @param int $tooltypeid Type ID
* @param int $courseid Course ID
* @param \core\context\course $context Course context
* @param bool $showinactivitychooser Show or not show in activity chooser
* @return bool True if the coursevisible was changed, false otherwise.
*/
public static function override_type_showinactivitychooser(int $tooltypeid, int $courseid, \core\context\course $context, bool $showinactivitychooser): bool {
global $DB;
require_capability('mod/lti:addcoursetool', $context);
$ltitype = lti_get_type($tooltypeid);
if ($ltitype && ($ltitype->coursevisible != LTI_COURSEVISIBLE_NO)) {
$coursevisible = $showinactivitychooser ? LTI_COURSEVISIBLE_ACTIVITYCHOOSER : LTI_COURSEVISIBLE_PRECONFIGURED;
$ltitype->coursevisible = $coursevisible;
$config = new \stdClass();
$config->lti_coursevisible = $coursevisible;
if (intval($ltitype->course) != intval(get_site()->id)) {
// It is course tool - just update it.
lti_update_type($ltitype, $config);
} else {
$coursecategory = $DB->get_field('course', 'category', ['id' => $courseid]);
$sql = "SELECT COUNT(*) AS count
FROM {lti_types_categories} tc
WHERE tc.typeid = :typeid";
$restrictedtool = $DB->count_records_sql($sql, ['typeid' => $tooltypeid]);
if ($restrictedtool) {
$record = $DB->get_record('lti_types_categories', ['typeid' => $tooltypeid, 'categoryid' => $coursecategory]);
if (!$record) {
throw new \moodle_exception('You are not allowed to change this setting for this tool.');
}
}
// This is site tool, but we would like to have course level setting for it.
$lticoursevisible = $DB->get_record('lti_coursevisible', ['typeid' => $tooltypeid, 'courseid' => $courseid]);
if (!$lticoursevisible) {
$lticoursevisible = new \stdClass();
$lticoursevisible->typeid = $tooltypeid;
$lticoursevisible->courseid = $courseid;
$lticoursevisible->coursevisible = $coursevisible;
$DB->insert_record('lti_coursevisible', $lticoursevisible);
} else {
$lticoursevisible->coursevisible = $coursevisible;
$DB->update_record('lti_coursevisible', $lticoursevisible);
}
}
return true;
}
return false;
}
}
@@ -129,6 +129,54 @@ class course_external_tools_list extends system_report {
->add_field("{$entitymainalias}.id")
->add_callback(fn() => $this->perrowtoolusage);
// Enable toggle column.
$this->add_column((new column(
'showinactivitychooser',
new \lang_string('showinactivitychooser', 'mod_lti'),
$tooltypesentity->get_entity_name()
))
// Site tools can be overridden on course level.
->add_join("LEFT JOIN {lti_coursevisible} lc ON lc.typeid = {$entitymainalias}.id AND lc.courseid = " . $this->course->id)
->set_type(column::TYPE_INTEGER)
->add_fields("{$entitymainalias}.id, {$entitymainalias}.coursevisible, lc.coursevisible as coursevisibleoverridden")
->set_is_sortable(false)
->set_callback(function(int $id, \stdClass $row): string {
global $PAGE;
$coursevisible = $row->coursevisible;
$courseid = $this->course->id;
if (!empty($row->coursevisibleoverridden)) {
$coursevisible = $row->coursevisibleoverridden;
}
if ($coursevisible == LTI_COURSEVISIBLE_ACTIVITYCHOOSER) {
$coursevisible = true;
} else {
$coursevisible = false;
}
$renderer = $PAGE->get_renderer('core_reportbuilder');
$attributes = [
['name' => 'id', 'value' => $row->id],
['name' => 'courseid', 'value' => $courseid],
['name' => 'action', 'value' => 'showinactivitychooser-toggle'],
['name' => 'state', 'value' => $coursevisible],
];
$label = $coursevisible ? get_string('dontshowinactivitychooser', 'mod_lti')
: get_string('showinactivitychooser', 'mod_lti');
$disabled = !has_capability('mod/lti:addcoursetool', \context_course::instance($courseid));
return $renderer->render_from_template('core/toggle', [
'id' => 'showinactivitychooser-toggle-' . $row->id,
'checked' => $coursevisible,
'disabled' => $disabled,
'dataattributes' => $attributes,
'label' => $label,
'labelclasses' => 'sr-only'
]);
})
);
// Attempt to create a dummy actions column, working around the limitations of the official actions feature.
$this->add_column(new column(
'actions', new \lang_string('actions'),
+1 -1
View File
@@ -54,7 +54,7 @@ $PAGE->set_title($pageheading);
$PAGE->set_secondary_active_tab('coursetools');
$PAGE->add_body_class('limitedwidth');
$form = new mod_lti_edit_types_form($url, (object)array('id' => $typeid, 'clientid' => $type->lti_clientid));
$form = new mod_lti_edit_types_form($url, (object)array('id' => $typeid, 'clientid' => $type->lti_clientid, 'iscoursetool' => true));
if ($form->is_cancelled()) {
redirect(new moodle_url('/mod/lti/coursetools.php', ['id' => $courseid]));
+16 -1
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="mod/lti/db" VERSION="20230725" COMMENT="XMLDB file for Moodle mod/lti"
<XMLDB PATH="mod/lti/db" VERSION="20230814" COMMENT="XMLDB file for Moodle mod/lti"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
@@ -175,5 +175,20 @@
<KEY NAME="categoryid" TYPE="foreign" FIELDS="categoryid" REFTABLE="course_categories" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="lti_coursevisible" COMMENT="Table to store coursevisible setting for site tool on course level">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="typeid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Course ID"/>
<FIELD NAME="coursevisible" TYPE="int" LENGTH="1" NOTNULL="true" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="courseid" UNIQUE="false" FIELDS="courseid"/>
<INDEX NAME="typeid" UNIQUE="false" FIELDS="typeid"/>
</INDEXES>
</TABLE>
</TABLES>
</XMLDB>
+8
View File
@@ -154,6 +154,14 @@ $functions = array(
'ajax' => true
),
'mod_lti_toggle_showinactivitychooser' => array(
'classname' => 'mod_lti\external\toggle_showinactivitychooser',
'description' => 'Toggle showinactivitychooser for a tool type in a course',
'type' => 'write',
'capabilities' => 'mod/lti:addcoursetool',
'ajax' => true
),
'mod_lti_is_cartridge' => array(
'classname' => 'mod_lti_external',
'methodname' => 'is_cartridge',
+22 -1
View File
@@ -133,10 +133,31 @@ function xmldb_lti_upgrade($oldversion) {
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Lti savepoint reached.
upgrade_mod_savepoint(true, 2023070501, 'lti');
}
if ($oldversion < 2023081101) {
// Define table to override coursevisible for a tool on course level.
$table = new xmldb_table('lti_coursevisible');
// Adding fields to table lti_coursevisible.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE);
$table->add_field('typeid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'id');
$table->add_field('courseid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'typeid');
$table->add_field('coursevisible', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'courseid');
// Add key.
$table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
// Conditionally launch create table for overriding coursevisible.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Lti savepoint reached.
upgrade_mod_savepoint(true, 2023081101, 'lti');
}
return true;
}
+11 -9
View File
@@ -181,7 +181,7 @@ class mod_lti_edit_types_form extends moodleform {
if (!empty($this->_customdata->isadmin)) {
// Only site-level preconfigured tools allow the control of course visibility in the site admin tool type form.
if (!$this->_customdata->iscoursetool) {
if (empty($this->_customdata->iscoursetool) || !$this->_customdata->iscoursetool) {
$options = array(
LTI_COURSEVISIBLE_NO => get_string('show_in_course_no', 'lti'),
LTI_COURSEVISIBLE_PRECONFIGURED => get_string('show_in_course_preconfigured', 'lti'),
@@ -246,14 +246,16 @@ class mod_lti_edit_types_form extends moodleform {
$mform->addHelpButton('lti_secureicon', 'secure_icon_url', 'lti');
// Restrict to course categories.
$mform->addElement('header', 'coursecategory', get_string('restricttocategory', 'lti'));
$mform->addHelpButton('coursecategory', 'restricttocategory', 'lti');
$records = $DB->get_records('course_categories', [], 'sortorder, id', 'id,parent,name');
// Convert array of objects to two dimentional array.
$tree = $this->lti_build_category_tree(array_map(fn($record) => (array)$record, $records));
$mform->addElement('html', $OUTPUT->render_from_template('mod_lti/categorynode', ['nodes' => $tree]));
$mform->addElement('hidden', 'lti_coursecategories');
$mform->setType('lti_coursecategories', PARAM_TEXT);
if (empty($this->_customdata->iscoursetool) || !$this->_customdata->iscoursetool) {
$mform->addElement('header', 'coursecategory', get_string('restricttocategory', 'lti'));
$mform->addHelpButton('coursecategory', 'restricttocategory', 'lti');
$records = $DB->get_records('course_categories', [], 'sortorder, id', 'id,parent,name');
// Convert array of objects to two dimentional array.
$tree = $this->lti_build_category_tree(array_map(fn($record) => (array)$record, $records));
$mform->addElement('html', $OUTPUT->render_from_template('mod_lti/categorynode', ['nodes' => $tree]));
$mform->addElement('hidden', 'lti_coursecategories');
$mform->setType('lti_coursecategories', PARAM_TEXT);
}
if (!$istool) {
// Display the lti advantage services.
+2
View File
@@ -523,6 +523,8 @@ $string['share_roster_help'] = 'Specify whether the tool can access the list of
Note that this setting may be overridden in the tool configuration.';
$string['show_in_course_activity_chooser'] = 'Show in activity chooser and as a preconfigured tool';
$string['showinactivitychooser'] = 'Show in activity chooser';
$string['dontshowinactivitychooser'] = 'Don\'t show in activity chooser';
$string['show_in_course_lti1'] = 'Tool configuration usage';
$string['show_in_course_lti1_help'] = 'This tool may be shown in the activity chooser for a teacher to select to add to a course. Alternatively, it may be shown in the preconfigured tool drop-down menu when adding an external tool to a course. A further option is for the tool configuration to only be used if the exact tool URL is entered when adding an external tool to a course.';
$string['show_in_course_lti2'] = 'Tool configuration usage';
@@ -147,3 +147,136 @@ Feature: Manage course tools
And the field "Tool URL" matches value "http://www.example.com/lti/provider.php"
And the field "Icon URL" matches value "http://download.moodle.org/unittest/test.jpg"
And the field "Secure icon URL" matches value "https://download.moodle.org/unittest/test.jpg"
@javascript
Scenario: Site tool appearing in activity chooser according to settings
Given the following "mod_lti > tool types" exist:
| name | baseurl | coursevisible | state |
| Teaching Tool 1 | /mod/lti/tests/fixtures/tool_provider.php | 2 | 1 |
| Teaching Tool 2 | /mod/lti/tests/fixtures/tool_provider.php | 1 | 1 |
| Teaching Tool 3 | /mod/lti/tests/fixtures/tool_provider.php | 0 | 1 |
And the following "courses" exist:
| fullname | shortname | category |
| Course 2 | C2 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C2 | editingteacher |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
And I should see "Teaching Tool 1" in the ".modal-body" "css_element"
And I should not see "Teaching Tool 2" in the ".modal-body" "css_element"
And I should not see "Teaching Tool 3" in the ".modal-body" "css_element"
And I click on "Close" "button" in the ".modal-dialog" "css_element"
And I navigate to "LTI External tools" in current page administration
And I should not see "Teaching Tool 3"
And I click on "Don't show in activity chooser" "field" in the "Teaching Tool 1" "table_row"
And I click on "Show in activity chooser" "field" in the "Teaching Tool 2" "table_row"
And I am on "Course 1" course homepage
And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
And I should not see "Teaching Tool 1" in the ".modal-body" "css_element"
And I should see "Teaching Tool 2" in the ".modal-body" "css_element"
And I should not see "Teaching Tool 3" in the ".modal-body" "css_element"
And I click on "Close" "button" in the ".modal-dialog" "css_element"
# Should not affect other courses.
And I am on "Course 2" course homepage
And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
And I should see "Teaching Tool 1" in the ".modal-body" "css_element"
And I should not see "Teaching Tool 2" in the ".modal-body" "css_element"
And I should not see "Teaching Tool 3" in the ".modal-body" "css_element"
And I click on "Close" "button" in the ".modal-dialog" "css_element"
And I am on "Course 1" course homepage
And I navigate to "LTI External tools" in current page administration
And I click on "Show in activity chooser" "field" in the "Teaching Tool 1" "table_row"
And I click on "Don't show in activity chooser" "field" in the "Teaching Tool 2" "table_row"
And I am on "Course 1" course homepage
And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
And I should see "Teaching Tool 1" in the ".modal-body" "css_element"
And I should not see "Teaching Tool 2" in the ".modal-body" "css_element"
And I should not see "Teaching Tool 3" in the ".modal-body" "css_element"
When the following "role capability" exists:
| role | editingteacher |
| mod/lti:addcoursetool | prohibit |
And I am on "Course 1" course homepage with editing mode on
And I navigate to "LTI External tools" in current page administration
Then the "Don't show in activity chooser" "field" should be disabled
And the "Show in activity chooser" "field" should be disabled
@javascript
Scenario: Course tool appearing in activity chooser according to settings
Given the following "mod_lti > course tools" exist:
| name | baseurl | course | coursevisible |
| Course Tool 1 | /mod/lti/tests/fixtures/tool_provider.php | C1 | 2 |
| Course Tool 2 | /mod/lti/tests/fixtures/tool_provider.php | C1 | 1 |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
And I should see "Course Tool 1" in the ".modal-body" "css_element"
And I should not see "Course Tool 2" in the ".modal-body" "css_element"
And I click on "Close" "button" in the ".modal-dialog" "css_element"
And I navigate to "LTI External tools" in current page administration
And I click on "Don't show in activity chooser" "field" in the "Course Tool 1" "table_row"
And I click on "Show in activity chooser" "field" in the "Course Tool 2" "table_row"
And I am on "Course 1" course homepage
And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
And I should not see "Course Tool 1" in the ".modal-body" "css_element"
And I should see "Course Tool 2" in the ".modal-body" "css_element"
And I click on "Close" "button" in the ".modal-dialog" "css_element"
And I navigate to "LTI External tools" in current page administration
And I click on "Show in activity chooser" "field" in the "Course Tool 1" "table_row"
And I click on "Don't show in activity chooser" "field" in the "Course Tool 2" "table_row"
And I am on "Course 1" course homepage
And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
And I should see "Course Tool 1" in the ".modal-body" "css_element"
And I should not see "Course Tool 2" in the ".modal-body" "css_element"
When the following "role capability" exists:
| role | editingteacher |
| mod/lti:addcoursetool | prohibit |
And I am on "Course 1" course homepage with editing mode on
And I navigate to "LTI External tools" in current page administration
Then the "Don't show in activity chooser" "field" should be disabled
And the "Show in activity chooser" "field" should be disabled
@javascript
Scenario: Site and course tools settings are preserved when backup and restore
Given the following "mod_lti > tool types" exist:
| name | baseurl | coursevisible | state |
| Teaching Tool 1 | /mod/lti/tests/fixtures/tool_provider.php | 2 | 1 |
| Teaching Tool 2 | /mod/lti/tests/fixtures/tool_provider.php | 1 | 1 |
And the following "mod_lti > course tools" exist:
| name | description | baseurl | course |
| Course Tool 1 | Example description | https://example.com/tool | C1 |
And I log in as "admin"
And I am on "Course 1" course homepage with editing mode on
And I add a "Teaching Tool 1" to section "1"
And I set the field "Activity name" to "Test tool activity 1"
And I press "Save and return to course"
And I add a "Course Tool 1" to section "1"
And I set the field "Activity name" to "Course tool activity 1"
And I press "Save and return to course"
And I navigate to "LTI External tools" in current page administration
And I click on "Don't show in activity chooser" "field" in the "Teaching Tool 1" "table_row"
And I click on "Show in activity chooser" "field" in the "Teaching Tool 2" "table_row"
And I click on "Don't show in activity chooser" "field" in the "Course Tool 1" "table_row"
And I am on "Course 1" course homepage
And I add a "Teaching Tool 2" to section "1"
And I set the field "Activity name" to "Test tool activity 2"
And I press "Save and return to course"
When I backup "Course 1" course using this options:
| Confirmation | Filename | test_backup.mbz |
And I restore "test_backup.mbz" backup into a new course using this options:
| Schema | Course name | Restored course |
And I should see "Restored course"
And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
Then I should not see "Teaching Tool 1" in the ".modal-body" "css_element"
And I should see "Teaching Tool 2" in the ".modal-body" "css_element"
And I should not see "Course Tool 2" in the ".modal-body" "css_element"
And I click on "Close" "button" in the ".modal-dialog" "css_element"
And I navigate to "LTI External tools" in current page administration
And I should see "Show in activity chooser" in the "Teaching Tool 1" "table_row"
And I should see "Don't show in activity chooser" in the "Teaching Tool 2" "table_row"
And I should see "Show in activity chooser" in the "Course Tool 1" "table_row"
@@ -87,3 +87,28 @@ Feature: Make an LTI only available to specific course categories
Then the following fields match these values:
| catb | 1 |
| catcb | 0 |
@javascript
Scenario: Category restriction only shown for a site tool
Given the following "mod_lti > tool types" exist:
| name | baseurl | coursevisible | state |
| Teaching Tool 1 | /mod/lti/tests/fixtures/tool_provider.php | 2 | 1 |
And the following "mod_lti > course tools" exist:
| name | description | baseurl | course |
| Course Tool 1 | Example description | https://example.com/tool | C1 |
And I log in as "admin"
And I am on "Course 1" course homepage with editing mode on
And I navigate to "LTI External tools" in current page administration
When I click on "Add tool" "link"
And I should not see "Restrict to category"
And I press "Cancel"
And I open the action menu in "Course Tool 1" "table_row"
And I choose "Edit" in the open action menu
And I should not see "Restrict to category"
And I navigate to "Plugins > Activity modules > External tool > Manage tools" in site administration
And I follow "Manage preconfigured tools"
And I follow "Add preconfigured tool"
And I should see "Restrict to category"
And I press "Cancel"
And I click on "Update" "link" in the "Teaching Tool 1" "table_row"
Then I should see "Restrict to category"
@@ -0,0 +1,162 @@
<?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 mod_lti\external;
use core_external\external_api;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/lti/tests/mod_lti_testcase.php');
/**
* PHPUnit tests for toggle_showinactivitychooser external function.
*
* @package mod_lti
* @copyright 2023 Ilya Tregubov <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \mod_lti\external\toggle_showinactivitychooser
*/
class toggle_showinactivitychooser_test extends \mod_lti_testcase {
/**
* Test toggle_showinactivitychooser for course tool.
* @covers ::execute
*/
public function test_toggle_showinactivitychooser_course_tool() {
global $DB;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
$this->setUser($editingteacher);
$typeid = lti_add_type(
(object) [
'state' => LTI_TOOL_STATE_CONFIGURED,
'course' => $course->id,
'coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER
],
(object) [
'lti_typename' => "My course tool",
'lti_toolurl' => 'http://example.com',
'lti_ltiversion' => 'LTI-1p0',
'lti_coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER
]
);
$result = toggle_showinactivitychooser::execute($typeid, $course->id, false);
$result = external_api::clean_returnvalue(toggle_showinactivitychooser::execute_returns(), $result);
$this->assertTrue($result);
$sql = "SELECT lt.coursevisible coursevisible
FROM {lti_types} lt
WHERE lt.id = ?";
$actual = $DB->get_record_sql($sql, [$typeid]);
$this->assertEquals(LTI_COURSEVISIBLE_PRECONFIGURED, $actual->coursevisible);
$result = toggle_showinactivitychooser::execute($typeid, $course->id, true);
$result = external_api::clean_returnvalue(toggle_showinactivitychooser::execute_returns(), $result);
$this->assertTrue($result);
$actual = $DB->get_record_sql($sql, [$typeid]);
$this->assertEquals(LTI_COURSEVISIBLE_ACTIVITYCHOOSER, $actual->coursevisible);
}
/**
* Test toggle_showinactivitychooser for site tool.
* @covers ::execute
*/
public function test_toggle_showinactivitychooser_site_tool() {
global $DB;
$this->resetAfterTest();
$coursecat1 = $this->getDataGenerator()->create_category();
$coursecat2 = $this->getDataGenerator()->create_category();
$course = $this->getDataGenerator()->create_course(['category' => $coursecat1->id]);
$editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
$this->setUser($editingteacher);
$type = $this->generate_tool_type(123); // Creates a site tool.
$result = toggle_showinactivitychooser::execute($type->id, $course->id, false);
$result = external_api::clean_returnvalue(toggle_showinactivitychooser::execute_returns(), $result);
$this->assertTrue($result);
$sql = "SELECT lt.coursevisible coursevisible1, lc.coursevisible AS coursevisible2
FROM {lti_types} lt
LEFT JOIN {lti_coursevisible} lc ON lt.id = lc.typeid
WHERE lt.id = ?
AND lc.courseid = ?";
$actual = $DB->get_record_sql($sql, [$type->id, $course->id]);
$this->assertEquals(LTI_COURSEVISIBLE_ACTIVITYCHOOSER, $actual->coursevisible1);
$this->assertEquals(LTI_COURSEVISIBLE_PRECONFIGURED, $actual->coursevisible2);
$result = toggle_showinactivitychooser::execute($type->id, $course->id, true);
$result = external_api::clean_returnvalue(toggle_showinactivitychooser::execute_returns(), $result);
$this->assertTrue($result);
$actual = $DB->get_record_sql($sql, [$type->id, $course->id]);
$this->assertEquals(LTI_COURSEVISIBLE_ACTIVITYCHOOSER, $actual->coursevisible1);
$this->assertEquals(LTI_COURSEVISIBLE_ACTIVITYCHOOSER, $actual->coursevisible2);
$ltigenerator = $this->getDataGenerator()->get_plugin_generator('mod_lti');
$ltigenerator->create_tool_types([
'name' => 'site tool preconfigured and activity chooser, restricted to category 1',
'baseurl' => 'http://example.com/tool/1',
'coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER,
'state' => LTI_TOOL_STATE_CONFIGURED,
'lti_coursecategories' => $coursecat1->id
]);
$tool = $DB->get_record('lti_types', ['name' => 'site tool preconfigured and activity chooser, restricted to category 1']);
$result = toggle_showinactivitychooser::execute($tool->id, $course->id, false);
$result = external_api::clean_returnvalue(toggle_showinactivitychooser::execute_returns(), $result);
$this->assertTrue($result);
$actual = $DB->get_record_sql($sql, [$tool->id, $course->id]);
$this->assertEquals(LTI_COURSEVISIBLE_ACTIVITYCHOOSER, $actual->coursevisible1);
$this->assertEquals(LTI_COURSEVISIBLE_PRECONFIGURED, $actual->coursevisible2);
$ltigenerator = $this->getDataGenerator()->get_plugin_generator('mod_lti');
$ltigenerator->create_tool_types([
'name' => 'site tool preconfigured and activity chooser, restricted to category 2',
'baseurl' => 'http://example.com/tool/1',
'coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER,
'state' => LTI_TOOL_STATE_CONFIGURED,
'lti_coursecategories' => $coursecat2->id
]);
$tool = $DB->get_record('lti_types', ['name' => 'site tool preconfigured and activity chooser, restricted to category 2']);
$this->expectException('moodle_exception');
$this->expectExceptionMessage('You are not allowed to change this setting for this tool.');
toggle_showinactivitychooser::execute($tool->id, $course->id, true);
$ltigenerator = $this->getDataGenerator()->get_plugin_generator('mod_lti');
$ltigenerator->create_tool_types([
'name' => 'site tool dont show',
'baseurl' => 'http://example.com/tool/1',
'coursevisible' => LTI_COURSEVISIBLE_NO,
'state' => LTI_TOOL_STATE_CONFIGURED,
]);
$tool = $DB->get_record('lti_types', ['name' => 'site tool dont show']);
$result = toggle_showinactivitychooser::execute($tool->id, $course->id, false);
$result = external_api::clean_returnvalue(toggle_showinactivitychooser::execute_returns(), $result);
$this->assertFalse($result);
}
}
+8 -6
View File
@@ -116,8 +116,9 @@ class mod_lti_generator extends testing_module_generator {
* Create a tool type.
*
* @param array $data
* @return int ID of created tool
*/
public function create_tool_types(array $data) {
public function create_tool_types(array $data): int {
if (!isset($data['baseurl'])) {
throw new coding_exception('Must specify baseurl when creating a LTI tool type.');
}
@@ -130,17 +131,17 @@ class mod_lti_generator extends testing_module_generator {
['type' => $type, 'config' => $config] = $this->get_type_and_config_from_data($data);
lti_add_type(type: (object) $type, config: (object) $config);
return lti_add_type(type: (object) $type, config: (object) $config);
}
/**
* Create a course tool type.
*
* @param array $type the type info.
* @return void
* @return int ID of created tool.
* @throws coding_exception if any required fields are missing.
*/
public function create_course_tool_types(array $type): void {
public function create_course_tool_types(array $type): int {
global $SITE;
if (!isset($type['baseurl'])) {
@@ -151,13 +152,14 @@ class mod_lti_generator extends testing_module_generator {
}
$type['baseurl'] = (new moodle_url($type['baseurl']))->out(false); // Permits relative URLs in behat features.
$type['coursevisible'] = LTI_COURSEVISIBLE_ACTIVITYCHOOSER; // The default for course tools.
$type['coursevisible'] = $type['coursevisible'] ?? LTI_COURSEVISIBLE_ACTIVITYCHOOSER;
$type['state'] = LTI_TOOL_STATE_CONFIGURED; // The default for course tools.
// Sensible defaults permitting the tool type to be used in a launch.
$type['lti_acceptgrades'] = $type['lti_acceptgrades'] ?? LTI_SETTING_ALWAYS;
$type['lti_sendname'] = $type['lti_sendname'] ?? LTI_SETTING_ALWAYS;
$type['lti_sendemailaddr'] = $type['lti_sendemailaddr'] ?? LTI_SETTING_ALWAYS;
$type['lti_coursevisible'] = $type['coursevisible'] ?? LTI_COURSEVISIBLE_ACTIVITYCHOOSER;
// Required for cartridge processing support.
$type['lti_toolurl'] = $type['baseurl'];
@@ -171,6 +173,6 @@ class mod_lti_generator extends testing_module_generator {
['type' => $type, 'config' => $config] = $this->get_type_and_config_from_data($type);
lti_load_type_if_cartridge($config);
lti_add_type(type: $type, config: $config);
return lti_add_type(type: $type, config: $config);
}
}
+134 -16
View File
@@ -69,6 +69,8 @@ class types_helper_test extends mod_lti_testcase {
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
$teacher2 = $this->getDataGenerator()->create_and_enrol($course2, 'editingteacher');
$this->setUser($teacher);
// Create the following tool types for testing:
// - Site tool configured as "Do not show" (LTI_COURSEVISIBLE_NO).
// - Site tool configured as "Show as a preconfigured tool only" (LTI_COURSEVISIBLE_PRECONFIGURED).
@@ -113,36 +115,51 @@ class types_helper_test extends mod_lti_testcase {
// the tool restricted to category 2.
$coursetooltypes = types_helper::get_lti_types_by_course($course->id, $teacher->id);
$this->assertCount(3, $coursetooltypes);
$this->assertEmpty(array_diff(
['http://example.com/tool/2', 'http://example.com/tool/3', 'http://example.com/tool/4'],
array_column($coursetooltypes, 'baseurl')
));
$expected = [
'http://example.com/tool/2',
'http://example.com/tool/3',
'http://example.com/tool/4',
];
sort($expected);
$actual = array_column($coursetooltypes, 'baseurl');
sort($actual);
$this->assertEquals($expected, $actual);
// Request for only those tools configured to show in the activity chooser for the teacher.
$coursetooltypes = types_helper::get_lti_types_by_course($course->id, $teacher->id,
[LTI_COURSEVISIBLE_ACTIVITYCHOOSER]);
$this->assertCount(2, $coursetooltypes);
$this->assertEmpty(array_diff(
['http://example.com/tool/3', 'http://example.com/tool/4'],
array_column($coursetooltypes, 'baseurl')
));
$expected = [
'http://example.com/tool/3',
'http://example.com/tool/4',
];
sort($expected);
$actual = array_column($coursetooltypes, 'baseurl');
sort($actual);
$this->assertEquals($expected, $actual);
// Request for only those tools configured to show as a preconfigured tool for the teacher.
$coursetooltypes = types_helper::get_lti_types_by_course($course->id, $teacher->id,
[LTI_COURSEVISIBLE_PRECONFIGURED]);
$this->assertCount(1, $coursetooltypes);
$this->assertEmpty(array_diff(
['http://example.com/tool/2'],
array_column($coursetooltypes, 'baseurl')
));
$expected = [
'http://example.com/tool/2',
];
$actual = array_column($coursetooltypes, 'baseurl');
$this->assertEquals($expected, $actual);
// Request for teacher2 in course2 (course category 2).
$coursetooltypes = types_helper::get_lti_types_by_course($course2->id, $teacher2->id);
$this->assertCount(3, $coursetooltypes);
$this->assertEmpty(array_diff(
['http://example.com/tool/2', 'http://example.com/tool/3', 'http://example.com/tool/5'],
array_column($coursetooltypes, 'baseurl')
));
$expected = [
'http://example.com/tool/2',
'http://example.com/tool/3',
'http://example.com/tool/5',
];
sort($expected);
$actual = array_column($coursetooltypes, 'baseurl');
sort($actual);
$this->assertEquals($expected, $actual);
// Request for a teacher who cannot use preconfigured tools in the course.
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
@@ -151,4 +168,105 @@ class types_helper_test extends mod_lti_testcase {
$coursetooltypes = types_helper::get_lti_types_by_course($course->id, $teacher->id);
$this->assertCount(0, $coursetooltypes);
}
/**
* Test fetching tool types for a given course and user.
*
* @covers ::override_type_showinactivitychooser
* @return void.
*/
public function test_override_type_showinactivitychooser(): void {
$this->resetAfterTest();
global $DB;
$coursecat1 = $this->getDataGenerator()->create_category();
$coursecat2 = $this->getDataGenerator()->create_category();
$course = $this->getDataGenerator()->create_course(['category' => $coursecat1->id]);
$course2 = $this->getDataGenerator()->create_course(['category' => $coursecat2->id]);
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
$teacher2 = $this->getDataGenerator()->create_and_enrol($course2, 'editingteacher');
$context = \core\context\course::instance($course->id);
$this->setUser($teacher);
/*
Create the following tool types for testing:
| tooltype | coursevisible | restrictedtocategory |
| site | LTI_COURSEVISIBLE_NO | |
| site | LTI_COURSEVISIBLE_PRECONFIGURED | |
| site | LTI_COURSEVISIBLE_ACTIVITYCHOOSER | yes |
| site | LTI_COURSEVISIBLE_ACTIVITYCHOOSER | yes |
| course | LTI_COURSEVISIBLE_ACTIVITYCHOOSER | |
*/
/** @var \mod_lti_generator $ltigenerator */
$ltigenerator = $this->getDataGenerator()->get_plugin_generator('mod_lti');
$tool1id = $ltigenerator->create_tool_types([
'name' => 'site tool do not show',
'baseurl' => 'http://example.com/tool/1',
'coursevisible' => LTI_COURSEVISIBLE_NO,
'state' => LTI_TOOL_STATE_CONFIGURED
]);
$tool2id = $ltigenerator->create_tool_types([
'name' => 'site tool preconfigured only',
'baseurl' => 'http://example.com/tool/2',
'coursevisible' => LTI_COURSEVISIBLE_PRECONFIGURED,
'state' => LTI_TOOL_STATE_CONFIGURED
]);
$tool3id = $ltigenerator->create_course_tool_types([
'name' => 'course tool preconfigured and activity chooser',
'baseurl' => 'http://example.com/tool/3',
'course' => $course->id
]);
$tool4id = $ltigenerator->create_tool_types([
'name' => 'site tool preconfigured and activity chooser, restricted to category 2',
'baseurl' => 'http://example.com/tool/4',
'coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER,
'state' => LTI_TOOL_STATE_CONFIGURED,
'lti_coursecategories' => $coursecat2->id
]);
$tool5id = $ltigenerator->create_tool_types([
'name' => 'site tool preconfigured and activity chooser, restricted to category 1',
'baseurl' => 'http://example.com/tool/5',
'coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER,
'state' => LTI_TOOL_STATE_CONFIGURED,
'lti_coursecategories' => $coursecat1->id
]);
// LTI_COURSEVISIBLE_NO can't be updated.
$result = types_helper::override_type_showinactivitychooser($tool1id, $course->id, $context, true);
$this->assertFalse($result);
// Tool not exist.
$result = types_helper::override_type_showinactivitychooser($tool5id + 1, $course->id, $context, false);
$this->assertFalse($result);
$result = types_helper::override_type_showinactivitychooser($tool2id, $course->id, $context, true);
$this->assertTrue($result);
$coursevisibleoverriden = $DB->get_field('lti_coursevisible', 'coursevisible',
['typeid' => $tool2id, 'courseid' => $course->id]);
$this->assertEquals(LTI_COURSEVISIBLE_ACTIVITYCHOOSER, $coursevisibleoverriden);
$result = types_helper::override_type_showinactivitychooser($tool3id, $course->id, $context, false);
$this->assertTrue($result);
$coursevisible = $DB->get_field('lti_types', 'coursevisible', ['id' => $tool3id]);
$this->assertEquals(LTI_COURSEVISIBLE_PRECONFIGURED, $coursevisible);
// Restricted category no allowed.
$this->expectException('moodle_exception');
$this->expectExceptionMessage('You are not allowed to change this setting for this tool.');
types_helper::override_type_showinactivitychooser($tool4id, $course->id, $context, false);
// Restricted category allowed.
$result = types_helper::override_type_showinactivitychooser($tool5id, $course->id, $context, false);
$this->assertTrue($result);
$coursevisibleoverriden = $DB->get_field('lti_coursevisible', 'coursevisible',
['typeid' => $tool5id, 'courseid' => $course->id]);
$this->assertEquals(LTI_COURSEVISIBLE_PRECONFIGURED, $coursevisibleoverriden);
$this->setUser($teacher2);
$this->expectException(\required_capability_exception::class);
types_helper::override_type_showinactivitychooser($tool5id, $course->id, $context, false);
}
}
+4 -1
View File
@@ -48,8 +48,11 @@ abstract class mod_lti_testcase extends externallib_advanced_testcase {
$type->description = "Example description $uniqueid";
$type->toolproxyid = $toolproxyid;
$type->baseurl = $this->getExternalTestFileUrl("/test$uniqueid.html");
$type->coursevisible = LTI_COURSEVISIBLE_ACTIVITYCHOOSER;
$config = new stdClass();
$config->lti_coursevisible = LTI_COURSEVISIBLE_ACTIVITYCHOOSER;
$type->id = lti_add_type($type, new stdClass());
$type->id = lti_add_type($type, $config);
return $type;
}
+1 -1
View File
@@ -48,7 +48,7 @@
defined('MOODLE_INTERNAL') || die;
$plugin->version = 2023081100; // The current module version (Date: YYYYMMDDXX).
$plugin->version = 2023081101; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2023041800; // Requires this Moodle version.
$plugin->component = 'mod_lti'; // Full name of the plugin (used for diagnostics).
$plugin->cron = 0;