Merge branch 'MDL-78575-master' of https://github.com/snake/moodle
This commit is contained in:
+3
@@ -0,0 +1,3 @@
|
||||
define("mod_lti/course_tools_list",["exports","core/notification","core/pending","core/ajax","core/toast","core/prefetch","core/str","core_table/dynamic","core_table/local/dynamic/selectors"],(function(_exports,_notification,_pending,_ajax,_toast,_prefetch,_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=()=>{(0,_prefetch.prefetchStrings)("mod_lti",["deletecoursetool","deletecoursetoolconfirm","coursetooldeleted"]),(0,_prefetch.prefetchStrings)("core",["delete"]),document.addEventListener("click",(event=>{const courseToolDelete=event.target.closest('[data-action="course-tool-delete"]');if(courseToolDelete){event.preventDefault();const triggerElement=courseToolDelete.closest(".dropdown").querySelector(".dropdown-toggle");_notification.default.saveCancelPromise((0,_str.get_string)("deletecoursetool","mod_lti"),(0,_str.get_string)("deletecoursetoolconfirm","mod_lti",courseToolDelete.dataset.courseToolName),(0,_str.get_string)("delete","core"),{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.get_string)("coursetooldeleted","mod_lti"))).then((()=>{const tableRoot=triggerElement.closest(Selectors.main.region);return(0,_dynamic.refreshTableContent)(tableRoot)})).then(pendingPromise.resolve).catch(_notification.default.exception)})).catch((()=>{}))}}))}}));
|
||||
|
||||
//# sourceMappingURL=course_tools_list.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"course_tools_list.min.js","sources":["../src/course_tools_list.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 * Course LTI External tools list management.\n *\n * @module mod_lti/course_tools_list\n * @copyright 2023 Jake Dallimore <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Ajax from 'core/ajax';\nimport {add as addToast} from 'core/toast';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {refreshTableContent} from 'core_table/dynamic';\nimport * as Selectors from 'core_table/local/dynamic/selectors';\n\n/**\n * Initialise module.\n */\nexport const init = () => {\n prefetchStrings('mod_lti', [\n 'deletecoursetool',\n 'deletecoursetoolconfirm',\n 'coursetooldeleted'\n ]);\n\n prefetchStrings('core', [\n 'delete',\n ]);\n\n document.addEventListener('click', event => {\n\n const courseToolDelete = event.target.closest('[data-action=\"course-tool-delete\"]');\n if (courseToolDelete) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = courseToolDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n Notification.saveCancelPromise(\n getString('deletecoursetool', 'mod_lti'),\n getString('deletecoursetoolconfirm', 'mod_lti', courseToolDelete.dataset.courseToolName),\n getString('delete', 'core'),\n {triggerElement}\n ).then(() => {\n const pendingPromise = new Pending('mod_lti/course_tools:delete');\n\n const request = {\n methodname: 'mod_lti_delete_course_tool_type',\n args: {tooltypeid: courseToolDelete.dataset.courseToolId}\n };\n return Ajax.call([request])[0]\n .then(addToast(getString('coursetooldeleted', 'mod_lti')))\n .then(() => {\n const tableRoot = triggerElement.closest(Selectors.main.region);\n return refreshTableContent(tableRoot);\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n};\n"],"names":["document","addEventListener","event","courseToolDelete","target","closest","preventDefault","triggerElement","querySelector","saveCancelPromise","dataset","courseToolName","then","pendingPromise","Pending","request","methodname","args","tooltypeid","courseToolId","Ajax","call","tableRoot","Selectors","main","region","resolve","catch","Notification","exception"],"mappings":"8/CAqCoB,mCACA,UAAW,CACvB,mBACA,0BACA,oDAGY,OAAQ,CACpB,WAGJA,SAASC,iBAAiB,SAASC,cAEzBC,iBAAmBD,MAAME,OAAOC,QAAQ,yCAC1CF,iBAAkB,CAClBD,MAAMI,uBAGAC,eAAiBJ,iBAAiBE,QAAQ,aAAaG,cAAc,0CAC9DC,mBACT,mBAAU,mBAAoB,YAC9B,mBAAU,0BAA2B,UAAWN,iBAAiBO,QAAQC,iBACzE,mBAAU,SAAU,QACpB,CAACJ,eAAAA,iBACHK,MAAK,WACGC,eAAiB,IAAIC,iBAAQ,+BAE7BC,QAAU,CACZC,WAAY,kCACZC,KAAM,CAACC,WAAYf,iBAAiBO,QAAQS,sBAEzCC,cAAKC,KAAK,CAACN,UAAU,GACvBH,MAAK,eAAS,mBAAU,oBAAqB,aAC7CA,MAAK,WACIU,UAAYf,eAAeF,QAAQkB,UAAUC,KAAKC,eACjD,gCAAoBH,cAE9BV,KAAKC,eAAea,SACpBC,MAAMC,sBAAaC,cACzBF,OAAM"}
|
||||
@@ -0,0 +1,82 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Course LTI External tools list management.
|
||||
*
|
||||
* @module mod_lti/course_tools_list
|
||||
* @copyright 2023 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
import Notification from 'core/notification';
|
||||
import Pending from 'core/pending';
|
||||
import Ajax from 'core/ajax';
|
||||
import {add as addToast} from 'core/toast';
|
||||
import {prefetchStrings} from 'core/prefetch';
|
||||
import {get_string as getString} from 'core/str';
|
||||
import {refreshTableContent} from 'core_table/dynamic';
|
||||
import * as Selectors from 'core_table/local/dynamic/selectors';
|
||||
|
||||
/**
|
||||
* Initialise module.
|
||||
*/
|
||||
export const init = () => {
|
||||
prefetchStrings('mod_lti', [
|
||||
'deletecoursetool',
|
||||
'deletecoursetoolconfirm',
|
||||
'coursetooldeleted'
|
||||
]);
|
||||
|
||||
prefetchStrings('core', [
|
||||
'delete',
|
||||
]);
|
||||
|
||||
document.addEventListener('click', event => {
|
||||
|
||||
const courseToolDelete = event.target.closest('[data-action="course-tool-delete"]');
|
||||
if (courseToolDelete) {
|
||||
event.preventDefault();
|
||||
|
||||
// Use triggerElement to return focus to the action menu toggle.
|
||||
const triggerElement = courseToolDelete.closest('.dropdown').querySelector('.dropdown-toggle');
|
||||
Notification.saveCancelPromise(
|
||||
getString('deletecoursetool', 'mod_lti'),
|
||||
getString('deletecoursetoolconfirm', 'mod_lti', courseToolDelete.dataset.courseToolName),
|
||||
getString('delete', 'core'),
|
||||
{triggerElement}
|
||||
).then(() => {
|
||||
const pendingPromise = new Pending('mod_lti/course_tools:delete');
|
||||
|
||||
const request = {
|
||||
methodname: 'mod_lti_delete_course_tool_type',
|
||||
args: {tooltypeid: courseToolDelete.dataset.courseToolId}
|
||||
};
|
||||
return Ajax.call([request])[0]
|
||||
.then(addToast(getString('coursetooldeleted', 'mod_lti')))
|
||||
.then(() => {
|
||||
const tableRoot = triggerElement.closest(Selectors.main.region);
|
||||
return refreshTableContent(tableRoot);
|
||||
})
|
||||
.then(pendingPromise.resolve)
|
||||
.catch(Notification.exception);
|
||||
}).catch(() => {
|
||||
return;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
<?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;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||
|
||||
/**
|
||||
* External function to delete a course tool type.
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright 2023 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class delete_course_tool_type 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'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a course tool type.
|
||||
*
|
||||
* @param int $tooltypeid the id of the course external tool type.
|
||||
* @return bool true
|
||||
* @throws \invalid_parameter_exception if the provided id refers to a site level tool which cannot be deleted.
|
||||
*/
|
||||
public static function execute(int $tooltypeid): bool {
|
||||
|
||||
['tooltypeid' => $tooltypeid] = self::validate_parameters(self::execute_parameters(), ['tooltypeid' => $tooltypeid]);
|
||||
|
||||
global $DB;
|
||||
$course = (int) $DB->get_field('lti_types', 'course', ['id' => $tooltypeid]);
|
||||
if ($course == get_site()->id) {
|
||||
throw new \invalid_parameter_exception('This is a site-level tool and cannot be deleted via this service');
|
||||
}
|
||||
|
||||
$context = \context_course::instance($course);
|
||||
self::validate_context($context);
|
||||
require_capability('mod/lti:addcoursetool', $context);
|
||||
|
||||
\lti_delete_type($tooltypeid);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get service returns definition.
|
||||
*
|
||||
* @return external_value
|
||||
*/
|
||||
public static function execute_returns(): external_value {
|
||||
return new external_value(PARAM_BOOL, 'Success');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?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\output;
|
||||
|
||||
use core_reportbuilder\system_report_factory;
|
||||
use mod_lti\reportbuilder\local\systemreports\course_external_tools_list;
|
||||
|
||||
/**
|
||||
* The course tools page renderable, containing a page header renderable and a course tools system report.
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright 2023 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class course_tools_page implements \renderable {
|
||||
|
||||
/** @var course_external_tools_list the course tools system report instance. */
|
||||
protected course_external_tools_list $coursetoolsreport;
|
||||
|
||||
/** @var course_tools_page_header the page header renderable instance. */
|
||||
protected course_tools_page_header $coursetoolspageheader;
|
||||
|
||||
/**
|
||||
* Renderable constructor.
|
||||
*
|
||||
* @param int $courseid the id of the course.
|
||||
*/
|
||||
public function __construct(int $courseid) {
|
||||
global $DB;
|
||||
|
||||
$context = \context_course::instance($courseid);
|
||||
|
||||
// Page intro, zero state and 'add new' button.
|
||||
$canadd = has_capability('mod/lti:addcoursetool', $context);
|
||||
$sql = 'SELECT COUNT(1)
|
||||
FROM {lti_types} tt
|
||||
WHERE tt.course IN(:siteid, :courseid)
|
||||
AND tt.coursevisible NOT IN(:coursevisible)';
|
||||
$toolcount = $DB->count_records_sql($sql, ['siteid' => get_site()->id, 'courseid' => $courseid, 'coursevisible' => 0]);
|
||||
$this->coursetoolspageheader = new course_tools_page_header($courseid, $toolcount, $canadd);
|
||||
|
||||
// Course tools report itself.
|
||||
$this->coursetoolsreport = system_report_factory::create(course_external_tools_list::class, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the course tools page header renderable.
|
||||
*
|
||||
* @return course_tools_page_header the renderable.
|
||||
*/
|
||||
public function get_header(): course_tools_page_header {
|
||||
return $this->coursetoolspageheader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the course tools list system report.
|
||||
*
|
||||
* @return course_external_tools_list the course tools list report.
|
||||
*/
|
||||
public function get_table(): course_external_tools_list {
|
||||
return $this->coursetoolsreport;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?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\output;
|
||||
|
||||
use core\output\notification;
|
||||
use renderer_base;
|
||||
|
||||
/**
|
||||
* Course tools page header renderable, containing the data for the page zero state and 'add tool' button.
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright 2023 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class course_tools_page_header implements \templatable {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param int $courseid the course id.
|
||||
* @param int $toolcount the number of tools available in the course.
|
||||
* @param bool $canadd whether the user can add tools to the course or not.
|
||||
*/
|
||||
public function __construct(protected int $courseid, protected int $toolcount, protected bool $canadd) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the header's data for template use.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return object the data.
|
||||
*/
|
||||
public function export_for_template(renderer_base $output): \stdClass {
|
||||
|
||||
$context = (object) [];
|
||||
|
||||
if ($this->canadd) {
|
||||
$context->addlink = (new \moodle_url('/mod/lti/coursetooledit.php', ['course' => $this->courseid]))->out();
|
||||
}
|
||||
|
||||
if ($this->toolcount == 0) {
|
||||
$notification = new notification(get_string('nocourseexternaltoolsnotice', 'mod_lti'), notification::NOTIFY_INFO, true);
|
||||
$context->notoolsnotice = $notification->export_for_template($output);
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
@@ -84,4 +84,20 @@ class renderer extends plugin_renderer_base {
|
||||
return parent::render_from_template('mod_lti/repost_crosssite', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the course tools page header.
|
||||
*
|
||||
* @param course_tools_page $page the page renderable.
|
||||
* @return string the rendered html for the page.
|
||||
*/
|
||||
protected function render_course_tools_page(course_tools_page $page): string {
|
||||
|
||||
// Render the table header templatable + the report.
|
||||
$headerrenderable = $page->get_header();
|
||||
$table = $page->get_table();
|
||||
$headercontext = $headerrenderable->export_for_template($this);
|
||||
$headeroutput = parent::render_from_template('mod_lti/course_tools_page_header', $headercontext);
|
||||
|
||||
return $headeroutput . $table->output();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
<?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\reportbuilder\local\entities;
|
||||
|
||||
use core_reportbuilder\local\filters\select;
|
||||
use core_reportbuilder\local\filters\text;
|
||||
use lang_string;
|
||||
use core_reportbuilder\local\entities\base;
|
||||
use core_reportbuilder\local\report\column;
|
||||
use core_reportbuilder\local\report\filter;
|
||||
|
||||
/**
|
||||
* Course external tools entity class implementation.
|
||||
*
|
||||
* Defines all the columns and filters that can be added to reports that use this entity.
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright 2023 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_types extends base {
|
||||
|
||||
/**
|
||||
* Database tables that this entity uses and their default aliases
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default_table_aliases(): array {
|
||||
return ['lti_types' => 'tt', 'lti' => 'ti'];
|
||||
}
|
||||
|
||||
/**
|
||||
* The default title for this entity
|
||||
*
|
||||
* @return lang_string
|
||||
*/
|
||||
protected function get_default_entity_title(): lang_string {
|
||||
return new lang_string('entitycourseexternaltools', 'mod_lti');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the entity
|
||||
*
|
||||
* @return base
|
||||
*/
|
||||
public function initialise(): base {
|
||||
$columns = $this->get_all_columns();
|
||||
foreach ($columns as $column) {
|
||||
$this->add_column($column);
|
||||
}
|
||||
|
||||
$filters = $this->get_all_filters();
|
||||
foreach ($filters as $filter) {
|
||||
$this->add_filter($filter);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all available columns
|
||||
*
|
||||
* @return column[]
|
||||
*/
|
||||
protected function get_all_columns(): array {
|
||||
$tablealias = $this->get_table_alias('lti_types');
|
||||
|
||||
// Name column.
|
||||
$columns[] = (new column(
|
||||
'name',
|
||||
new lang_string('name', 'core'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_fields("{$tablealias}.name, {$tablealias}.icon")
|
||||
->set_is_sortable(true)
|
||||
->add_callback(static function(string $name, \stdClass $data) {
|
||||
global $OUTPUT;
|
||||
|
||||
$iconurl = $data->icon ?: $OUTPUT->image_url('monologo', 'lti')->out();
|
||||
$iconclass = $data->icon ? ' nofilter' : '';
|
||||
$iconcontainerclass = 'activityiconcontainer smaller content';
|
||||
$name = $data->name;
|
||||
$img = \html_writer::img($iconurl, get_string('courseexternaltooliconalt', 'mod_lti', $name),
|
||||
['class' => 'activityicon' . $iconclass]);
|
||||
$name = \html_writer::span($name, 'align-self-center');
|
||||
return \html_writer::div(\html_writer::div($img, 'mr-2 '.$iconcontainerclass) . $name, 'd-flex');
|
||||
});
|
||||
|
||||
// Description column.
|
||||
$columns[] = (new column(
|
||||
'description',
|
||||
new lang_string('description', 'core'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_field("{$tablealias}.description")
|
||||
->set_is_sortable(true);
|
||||
|
||||
// Course column.
|
||||
$columns[] = (new column(
|
||||
'course',
|
||||
new lang_string('course', 'core'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_INTEGER)
|
||||
->add_field("{$tablealias}.course")
|
||||
->set_is_sortable(true);
|
||||
|
||||
// LTI Version column.
|
||||
$columns[] = (new column(
|
||||
'ltiversion',
|
||||
new lang_string('version'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_field("{$tablealias}.ltiversion")
|
||||
->set_is_sortable(true);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of all available filters
|
||||
*
|
||||
* @return filter[]
|
||||
*/
|
||||
protected function get_all_filters(): array {
|
||||
$tablealias = $this->get_table_alias('lti_types');
|
||||
|
||||
return [
|
||||
// Name filter.
|
||||
(new filter(
|
||||
text::class,
|
||||
'name',
|
||||
new lang_string('name'),
|
||||
$this->get_entity_name(),
|
||||
"{$tablealias}.name"
|
||||
))
|
||||
->add_joins($this->get_joins()),
|
||||
|
||||
// Description filter.
|
||||
(new filter(
|
||||
text::class,
|
||||
'description',
|
||||
new lang_string('description'),
|
||||
$this->get_entity_name(),
|
||||
"{$tablealias}.description"
|
||||
))
|
||||
->add_joins($this->get_joins()),
|
||||
|
||||
// LTI Version filter.
|
||||
(new filter(
|
||||
select::class,
|
||||
'ltiversion',
|
||||
new lang_string('version'),
|
||||
$this->get_entity_name(),
|
||||
"{$tablealias}.ltiversion"
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_options_callback(static function() : array {
|
||||
return ['LTI-1p0' => 'Legacy LTI', '1.3.0' => "LTI Advantage"];
|
||||
})
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
<?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\reportbuilder\local\systemreports;
|
||||
|
||||
use core_reportbuilder\local\helpers\database;
|
||||
use core_reportbuilder\local\report\column;
|
||||
use mod_lti\reportbuilder\local\entities\tool_types;
|
||||
use core_reportbuilder\system_report;
|
||||
|
||||
/**
|
||||
* Course external tools list system report class implementation.
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright 2023 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class course_external_tools_list extends system_report {
|
||||
|
||||
/** @var \stdClass the course to constrain the report to. */
|
||||
protected \stdClass $course;
|
||||
|
||||
/**
|
||||
* Initialise report, we need to set the main table, load our entities and set columns/filters
|
||||
*/
|
||||
protected function initialise(): void {
|
||||
global $DB;
|
||||
|
||||
$this->course = get_course($this->get_context()->instanceid);
|
||||
|
||||
// Our main entity, it contains all the column definitions that we need.
|
||||
$entitymain = new tool_types();
|
||||
$entitymainalias = $entitymain->get_table_alias('lti_types');
|
||||
|
||||
$this->set_main_table('lti_types', $entitymainalias);
|
||||
$this->add_entity($entitymain);
|
||||
|
||||
// Now we can call our helper methods to add the content we want to include in the report.
|
||||
$this->add_columns($entitymain);
|
||||
$this->add_filters();
|
||||
$this->add_actions();
|
||||
|
||||
// We need id and course in the actions, without entity prefixes, so add these here.
|
||||
$this->add_base_fields("{$entitymainalias}.id, {$entitymainalias}.course");
|
||||
|
||||
// Scope the report to the course context only.
|
||||
$paramprefix = database::generate_param_name();
|
||||
$coursevisibleparam = database::generate_param_name();
|
||||
[$insql, $params] = $DB->get_in_or_equal([get_site()->id, $this->course->id], SQL_PARAMS_NAMED, "{$paramprefix}_");
|
||||
$wheresql = "{$entitymainalias}.course {$insql} AND {$entitymainalias}.coursevisible NOT IN (:{$coursevisibleparam})";
|
||||
$params = array_merge($params, [$coursevisibleparam => 0]);
|
||||
$this->add_base_condition_sql($wheresql, $params);
|
||||
|
||||
$this->set_downloadable(false, get_string('pluginname', 'mod_lti'));
|
||||
$this->set_default_per_page(10);
|
||||
$this->set_default_no_results_notice(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates access to view this report
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function can_view(): bool {
|
||||
return has_capability('mod/lti:addpreconfiguredinstance', $this->get_context());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the columns we want to display in the report.
|
||||
*
|
||||
* They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their
|
||||
* unique identifier
|
||||
* @param tool_types $tooltypesentity
|
||||
* @return void
|
||||
*/
|
||||
protected function add_columns(tool_types $tooltypesentity): void {
|
||||
$entitymainalias = $tooltypesentity->get_table_alias('lti_types');
|
||||
|
||||
$columns = [
|
||||
'tool_types:name',
|
||||
'tool_types:description',
|
||||
];
|
||||
|
||||
$this->add_columns_from_entities($columns);
|
||||
|
||||
// Tool usage column using a custom SQL subquery to count tool instances within the course.
|
||||
// TODO: This should be replaced with proper column aggregation once that's added to system_report instances in MDL-76392.
|
||||
$ti = database::generate_param_name(); // Tool instance param.
|
||||
$sql = "(SELECT count($ti.id) as usage
|
||||
FROM {lti} $ti
|
||||
WHERE $ti.typeid = {$entitymainalias}.id)";
|
||||
$this->add_column(new column(
|
||||
'usage',
|
||||
new \lang_string('usage', 'mod_lti'),
|
||||
$tooltypesentity->get_entity_name()
|
||||
))
|
||||
->set_type(column::TYPE_INTEGER)
|
||||
->set_is_sortable(true)
|
||||
->add_field($sql, 'usage');
|
||||
|
||||
// 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'),
|
||||
$tooltypesentity->get_entity_name()
|
||||
))
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->set_is_sortable(false)
|
||||
->add_fields("{$entitymainalias}.id, {$entitymainalias}.course, {$entitymainalias}.name")
|
||||
->add_callback(static function($field, $row) {
|
||||
global $OUTPUT;
|
||||
|
||||
// Lock actions for site-level preconfigured tools.
|
||||
if (get_site()->id == $row->course) {
|
||||
return \html_writer::div(
|
||||
\html_writer::div(
|
||||
$OUTPUT->pix_icon('t/locked', get_string('sitetoolnocourseediting', 'mod_lti')
|
||||
), 'tool-action-icon-container'), 'd-flex justify-content-end'
|
||||
);
|
||||
}
|
||||
|
||||
// Lock actions when the user can't add course tools.
|
||||
if (!has_capability('mod/lti:addcoursetool', \context_course::instance($row->course))) {
|
||||
return \html_writer::div(
|
||||
\html_writer::div(
|
||||
$OUTPUT->pix_icon('t/locked', get_string('courseexternaltoolsnoaddpermissions', 'mod_lti')
|
||||
), 'tool-action-icon-container'), 'd-flex justify-content-end'
|
||||
);
|
||||
}
|
||||
|
||||
// Build and display an action menu.
|
||||
$menu = new \action_menu();
|
||||
$menu->set_menu_trigger($OUTPUT->pix_icon('i/moremenu', get_string('actions', 'core')),
|
||||
'btn btn-icon d-flex align-items-center justify-content-center'); // TODO check 'actions' lang string with UX.
|
||||
|
||||
$menu->add(new \action_menu_link(
|
||||
new \moodle_url('/mod/lti/coursetooledit.php', ['course' => $row->course, 'typeid' => $row->id]),
|
||||
null,
|
||||
get_string('edit', 'core'),
|
||||
null
|
||||
));
|
||||
|
||||
$menu->add(new \action_menu_link(
|
||||
new \moodle_url('#'),
|
||||
null,
|
||||
get_string('delete', 'core'),
|
||||
null,
|
||||
[
|
||||
'data-action' => 'course-tool-delete',
|
||||
'data-course-tool-id' => $row->id,
|
||||
'data-course-tool-name' => $row->name
|
||||
],
|
||||
));
|
||||
|
||||
return $OUTPUT->render($menu);
|
||||
});
|
||||
|
||||
// Default sorting.
|
||||
$this->set_initial_sort_column('tool_types:name', SORT_ASC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any actions for this report.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function add_actions(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the filters we want to display in the report
|
||||
*
|
||||
* They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their
|
||||
* unique identifier
|
||||
*/
|
||||
protected function add_filters(): void {
|
||||
|
||||
$this->add_filters_from_entities([]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Page allowing instructors to configure course-level tools.
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright 2023 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
use core\output\notification;
|
||||
|
||||
require_once('../../config.php');
|
||||
require_once($CFG->dirroot.'/mod/lti/edit_form.php');
|
||||
require_once($CFG->dirroot.'/mod/lti/lib.php');
|
||||
|
||||
$courseid = required_param('course', PARAM_INT);
|
||||
$typeid = optional_param('typeid', null, PARAM_INT);
|
||||
|
||||
// Permissions etc.
|
||||
require_login($courseid, false);
|
||||
require_capability('mod/lti:addcoursetool', context_course::instance($courseid));
|
||||
if (!empty($typeid)) {
|
||||
$type = lti_get_type($typeid);
|
||||
if ($type->course != $courseid || $type->course == get_site()->id) {
|
||||
throw new moodle_exception('You do not have permissions to edit this tool type.');
|
||||
}
|
||||
}
|
||||
|
||||
// Page setup.
|
||||
$url = new moodle_url('/mod/lti/coursetooledit.php', ['courseid' => $courseid]);
|
||||
$PAGE->set_url($url);
|
||||
$PAGE->set_pagelayout('incourse');
|
||||
$PAGE->set_title(get_string('edittype', 'mod_lti')); // TODO confirm with UX about use of 'edit preconfigured tool' for the title.
|
||||
$PAGE->set_secondary_active_tab('coursetools');
|
||||
$PAGE->add_body_class('limitedwidth');
|
||||
|
||||
$type = !empty($typeid) ? lti_get_type_type_config($typeid) : (object) ['lti_clientid' => null];
|
||||
$pageheading = !empty($typeid) ? get_string('courseexternaltooleditheading', 'mod_lti') :
|
||||
get_string('courseexternaltooladdheading', 'mod_lti');
|
||||
$form = new mod_lti_edit_types_form($url, (object)array('id' => $typeid, 'clientid' => $type->lti_clientid));
|
||||
|
||||
if ($form->is_cancelled()) {
|
||||
|
||||
redirect(new moodle_url('/mod/lti/coursetools.php', ['id' => $courseid]));
|
||||
} else if ($data = $form->get_data()) {
|
||||
|
||||
require_sesskey();
|
||||
|
||||
if (!empty($data->typeid)) {
|
||||
$type = (object) ['id' => $data->typeid];
|
||||
lti_load_type_if_cartridge($data);
|
||||
lti_update_type($type, $data);
|
||||
$redirecturl = new moodle_url('/mod/lti/coursetools.php', ['id' => $courseid]);
|
||||
$notice = get_string('courseexternaltooleditsuccess', 'mod_lti', $type->name);
|
||||
} else {
|
||||
$type = (object) ['state' => LTI_TOOL_STATE_CONFIGURED, 'course' => $data->course];
|
||||
lti_load_type_if_cartridge($data);
|
||||
lti_add_type($type, $data);
|
||||
$redirecturl = new moodle_url('/mod/lti/coursetools.php', ['id' => $courseid]);
|
||||
$notice = get_string('courseexternaltooladdsuccess', 'mod_lti', $type->name);
|
||||
}
|
||||
|
||||
redirect($redirecturl, $notice, 0, notification::NOTIFY_SUCCESS);
|
||||
}
|
||||
|
||||
// Display the form.
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading($pageheading);
|
||||
|
||||
if (!empty($typeid)) {
|
||||
$form->set_data($type);
|
||||
}
|
||||
$form->display();
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
@@ -0,0 +1,62 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Shows a tabulated view of all the available LTI tools in a given course.
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright 2023 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
use mod_lti\output\course_tools_page;
|
||||
|
||||
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 Id.
|
||||
|
||||
// Access + permissions.
|
||||
$course = get_course($id);
|
||||
require_course_login($course, false);
|
||||
|
||||
$context = context_course::instance($course->id);
|
||||
if (!has_any_capability(['mod/lti:addpreconfiguredinstance', 'mod/lti:addcoursetool'], $context)) {
|
||||
throw new \moodle_exception('nopermissions', 'error', '', get_string('courseexternaltoolsnoviewpermissions', 'mod_lti'));
|
||||
}
|
||||
|
||||
// Page setup.
|
||||
global $PAGE, $OUTPUT;
|
||||
$pagetitle = get_string('courseexternaltools', 'mod_lti');
|
||||
$pageurl = new moodle_url('/mod/lti/coursetools.php', ['id' => $course->id]);
|
||||
$PAGE->set_pagelayout('incourse');
|
||||
$PAGE->set_context($context);
|
||||
$PAGE->set_url($pageurl);
|
||||
$PAGE->set_title($pagetitle);
|
||||
$PAGE->set_heading(format_string($course->fullname, true, ['context' => $context]));
|
||||
$PAGE->add_body_class('limitedwidth');
|
||||
|
||||
// Display.
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading($pagetitle);
|
||||
|
||||
$renderer = $PAGE->get_renderer('mod_lti');
|
||||
$coursetoolspage = new course_tools_page($course->id);
|
||||
echo $renderer->render($coursetoolspage);
|
||||
$PAGE->requires->js_call_amd('mod_lti/course_tools_list', 'init');
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
@@ -146,6 +146,14 @@ $functions = array(
|
||||
'ajax' => true
|
||||
),
|
||||
|
||||
'mod_lti_delete_course_tool_type' => array(
|
||||
'classname' => 'mod_lti\external\delete_course_tool_type',
|
||||
'description' => 'Delete a course tool type',
|
||||
'type' => 'write',
|
||||
'capabilities' => 'mod/lti:addcoursetool',
|
||||
'ajax' => true
|
||||
),
|
||||
|
||||
'mod_lti_is_cartridge' => array(
|
||||
'classname' => 'mod_lti_external',
|
||||
'methodname' => 'is_cartridge',
|
||||
|
||||
@@ -70,6 +70,7 @@ $string['add_ltiadv'] = 'Add LTI Advantage';
|
||||
$string['add_ltilegacy'] = 'Add Legacy LTI';
|
||||
$string['addnewapp'] = 'Enable external application';
|
||||
$string['addserver'] = 'Add new trusted server';
|
||||
$string['addtool'] = 'Add tool';
|
||||
$string['addtype'] = 'Add preconfigured tool';
|
||||
$string['allow'] = 'Allow';
|
||||
$string['allowsetting'] = 'Allow tool to store 8K of settings in Moodle';
|
||||
@@ -116,10 +117,20 @@ $string['contentitem_multiple_description'] = 'The following items will be added
|
||||
$string['contentitem_multiple_graded'] = 'Graded activity (Maximum grade: {$a})';
|
||||
$string['course_tool_types'] = 'Course tools';
|
||||
$string['courseactivitiesorresources'] = 'Course activities or resources';
|
||||
$string['courseexternaltooladdheading'] = 'Add new LTI External tool'; // TODO confirm wording with UX.
|
||||
$string['courseexternaltooladdsuccess'] = 'Successfully added course tool \'{$a}\'.'; // TODO confirm wording with UX.
|
||||
$string['courseexternaltooleditheading'] = 'Edit LTI External tool'; // TODO confirm wording with UX.
|
||||
$string['courseexternaltooleditsuccess'] = 'The changes to the course tool \'{$a}\' were saved.'; // TODO confirm wording with UX.
|
||||
$string['courseexternaltooliconalt'] = 'Icon for the \'{$a}\' LTI External tool'; // TODO: confirm wording with UX.
|
||||
$string['courseexternaltools'] = 'LTI External tools'; // TODO: confirm wording with UX.
|
||||
$string['courseexternaltoolsinfo'] = 'LTI External tools are add-on apps you can integrate into your course, such as interactive content or assessments. Your students can access and use them without leaving your course.'; // TODO: confirm wording with UX.
|
||||
$string['courseexternaltoolsnoaddpermissions'] = 'You don\'t have permission to add or edit course tools.'; // TODO: confirm wording with UX.
|
||||
$string['courseexternaltoolsnoviewpermissions'] = 'View course external tools';
|
||||
$string['courseid'] = 'Course ID number';
|
||||
$string['courseinformation'] = 'Course information';
|
||||
$string['courselink'] = 'Go to course';
|
||||
$string['coursemisconf'] = 'Course is misconfigured';
|
||||
$string['coursetooldeleted'] = 'Course tool deleted';
|
||||
$string['createdon'] = 'Created on';
|
||||
$string['curllibrarymissing'] = 'PHP cURL extension required for the External tool.';
|
||||
$string['custom'] = 'Custom parameters';
|
||||
@@ -148,6 +159,8 @@ $string['delegate'] = 'Delegate to teacher';
|
||||
$string['delegate_tool'] = 'As specified in Deep Linking definition or Delegate to teacher';
|
||||
$string['delete'] = 'Delete';
|
||||
$string['delete_confirmation'] = 'Are you sure you want to delete this preconfigured tool?';
|
||||
$string['deletecoursetool'] = 'Delete a course tool';
|
||||
$string['deletecoursetoolconfirm'] = 'Are you sure you want to delete this course tool?';
|
||||
$string['deletetype'] = 'Delete preconfigured tool';
|
||||
$string['display_description'] = 'Display activity description when launched';
|
||||
$string['display_description_help'] = 'If selected, the activity description (specified above) will display above the tool provider\'s content.
|
||||
@@ -184,6 +197,7 @@ $string['enableemailnotification'] = 'Send notification emails';
|
||||
$string['enableemailnotification_help'] = 'If enabled, students will receive email notification when their tool submissions are graded.';
|
||||
$string['enterkeyandsecret'] = 'Enter your consumer key and shared secret';
|
||||
$string['enterkeyandsecret_help'] = 'If you were given a consumer key and/or shared secret, input them here';
|
||||
$string['entitycourseexternaltools'] = 'LTI External tools';
|
||||
$string['errorbadurl'] = 'URL is not a valid tool URL or cartridge.';
|
||||
$string['errorincorrectconsumerkey'] = 'Consumer key is incorrect.';
|
||||
$string['errorinvaliddata'] = 'Invalid data: {$a}';
|
||||
@@ -339,6 +353,7 @@ $string['no_tp_configured'] = 'There are no unregistered external tool registrat
|
||||
$string['no_tp_pending'] = 'There are no pending external tool registrations.';
|
||||
$string['no_tp_rejected'] = 'There are no rejected external tool registrations.';
|
||||
$string['noattempts'] = 'No attempts have been made on this tool instance';
|
||||
$string['nocourseexternaltoolsnotice'] = 'There are no LTI external tools yet'; // TODO confirm wording with UX.
|
||||
$string['noltis'] = 'There are no external tool instances';
|
||||
$string['noprofileservice'] = 'Profile service not found';
|
||||
$string['noservers'] = 'No servers found';
|
||||
@@ -507,6 +522,7 @@ $string['show_in_course_lti2_help'] = 'This tool can be shown in the activity ch
|
||||
$string['show_in_course_no'] = 'Do not show; use only when a matching tool URL is entered';
|
||||
$string['show_in_course_preconfigured'] = 'Show as preconfigured tool when adding an external tool';
|
||||
$string['size'] = 'Size parameters';
|
||||
$string['sitetoolnocourseediting'] = 'This is a site level tool which cannot be edited.'; // TODO: confirm wording with UX.
|
||||
$string['opensslconfiginvalid'] = 'LTI 1.3 requires a valid openssl.cnf to be configured and available to your web server. Please contact the site administrator to configure and enable openssl for this site.';
|
||||
$string['submission'] = 'Submission';
|
||||
$string['submissions'] = 'Submissions';
|
||||
@@ -594,6 +610,7 @@ $string['unabletocreatetooltype'] = 'Unable to create tool';
|
||||
$string['unabletofindtooltype'] = 'Unable to find tool for {$a->id}';
|
||||
$string['unknownstate'] = 'Unknown state';
|
||||
$string['update'] = 'Update';
|
||||
$string['usage'] = 'Usage';
|
||||
$string['useraccountinformation'] = 'User account information';
|
||||
$string['userpersonalinformation'] = 'User personal information';
|
||||
$string['using_tool_cartridge'] = 'Using tool cartridge';
|
||||
|
||||
@@ -765,3 +765,20 @@ function mod_lti_core_calendar_provide_event_action(calendar_event $event,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the course navigation with an "LTI External tools" link which redirects to a list of all tools available for course use.
|
||||
*
|
||||
* @param settings_navigation $navigation The settings navigation object
|
||||
* @param stdClass $course The course
|
||||
* @param stdclass $context Course context
|
||||
* @return void
|
||||
*/
|
||||
function mod_lti_extend_navigation_course($navigation, $course, $context): void {
|
||||
if (has_any_capability(['mod/lti:addpreconfiguredinstance', 'mod/lti:addcoursetool'], $context)) {
|
||||
$url = new moodle_url('/mod/lti/coursetools.php', ['id' => $course->id]);
|
||||
$settingsnode = navigation_node::create(get_string('courseexternaltools', 'mod_lti'), $url, navigation_node::TYPE_SETTING,
|
||||
null, 'coursetools', new pix_icon('i/settings', ''));
|
||||
$navigation->add_node($settingsnode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,3 +403,32 @@
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Strip the caret from the action menu toggle, as the ... ellipsis icon is used. */
|
||||
#page-mod-lti-coursetools a.dropdown-toggle::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Icons in the 'actions' column of the course tools table need to be aligned with action menus which have a 36x36 trigger. */
|
||||
#page-mod-lti-coursetools table div.tool-action-icon-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
#page-mod-lti-coursetools table div.tool-action-icon-container i {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* The following removes the striping that lives in table_sql, which we can't change from within reportbuilder */
|
||||
#page-mod-lti-coursetools table.generaltable tbody tr:nth-of-type(2n+1),
|
||||
#page-mod-lti-coursetools table.table-striped tbody tr:nth-of-type(2n+1) {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
#page-mod-lti-coursetools table.generaltable tbody tr:hover,
|
||||
#page-mod-lti-coursetools table.table-striped tbody tr:hover {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template mod_lti/course_tool_page_header
|
||||
|
||||
Contains the page header for the 'LTI External tools' course page.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* notoolsnotice Object exported notification, shown when the page has 0 tools available.
|
||||
* addlink String link to the add tool type page.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"notoolsnotice": {
|
||||
"message": "There are no LTI External tools yet",
|
||||
"extraclasses": "",
|
||||
"announce": false,
|
||||
"closebutton": true,
|
||||
"isinfo": true
|
||||
},
|
||||
"addlink": "http://SITE/mod/lti/coursetooledit?course=45"
|
||||
}
|
||||
}}
|
||||
<div>
|
||||
<p>
|
||||
{{#str}} courseexternaltoolsinfo, mod_lti {{/str}}
|
||||
</p>
|
||||
{{#notoolsnotice}}
|
||||
{{> core/notification_info}}
|
||||
{{/notoolsnotice}}
|
||||
{{#addlink}}
|
||||
<div class="mb-3">
|
||||
<a class="btn btn-primary" href="{{{.}}}">{{#str}}addtool, mod_lti{{/str}}</a>
|
||||
</div>
|
||||
{{/addlink}}
|
||||
</div>
|
||||
@@ -0,0 +1,118 @@
|
||||
@mod @mod_lti
|
||||
Feature: Manage course tools
|
||||
In order to provide richer experiences for learners
|
||||
As a teacher
|
||||
I need to be able to add external tools to a course
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Terry1 | Teacher1 | teacher1@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
|
||||
Scenario: Create a course tool from the zero state
|
||||
Given I am on the "Course 1" course page logged in as teacher1
|
||||
And I navigate to "LTI External tools" in current page administration
|
||||
And I should see "LTI External tools are add-on apps"
|
||||
And I should see "There are no LTI external tools yet"
|
||||
When I click on "Add tool" "link"
|
||||
And I press "Cancel"
|
||||
Then I should see "LTI External tools are add-on apps"
|
||||
And I should see "There are no LTI external tools yet"
|
||||
And I click on "Add tool" "link"
|
||||
And I set the following fields to these values:
|
||||
| Tool name | Teaching Tool 1 |
|
||||
| Tool URL | http://example.com |
|
||||
| Tool description | A short description of the tool |
|
||||
And I press "Save changes"
|
||||
And I should see "Successfully added course tool"
|
||||
And I should see "A short description of the tool" in the "Teaching Tool 1" "table_row"
|
||||
|
||||
Scenario: Viewing a site level tool in the course tools table
|
||||
Given the following "mod_lti > tool types" exist:
|
||||
| name | description | baseurl | coursevisible |
|
||||
| Example tool | Another description | https://example.com/tool1 | 0 |
|
||||
| Test tool 2 | Tool2 description | https://example.com/tool2 | 1 |
|
||||
| Test tool 3 | Tool3 description | https://example.com/tool3 | 2 |
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
When I navigate to "LTI External tools" in current page administration
|
||||
Then I should see "Test tool 2" in the "reportbuilder-table" "table"
|
||||
And "This is a site level tool which cannot be edited" "icon" should exist in the "Test tool 2" "table_row"
|
||||
And I should see "Test tool 3" in the "reportbuilder-table" "table"
|
||||
And "This is a site level tool which cannot be edited" "icon" should exist in the "Test tool 3" "table_row"
|
||||
And I should not see "Example tool" in the "reportbuilder-table" "table"
|
||||
|
||||
Scenario: Viewing course tools without the capability to add/edit but having the capability to use
|
||||
Given the following "role capability" exists:
|
||||
| role | editingteacher |
|
||||
| mod/lti:addcoursetool | prohibit |
|
||||
| mod/lti:addpreconfiguredinstance | allow |
|
||||
And the following "mod_lti > course tools" exist:
|
||||
| name | description | baseurl | course |
|
||||
| Test tool | Example description | https://example.com/tool | C1 |
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
When I navigate to "LTI External tools" in current page administration
|
||||
Then "You don't have permission to add or edit course tools" "icon" should exist in the "Test tool" "table_row"
|
||||
|
||||
@javascript
|
||||
Scenario: Edit a course tool
|
||||
Given the following "mod_lti > course tools" exist:
|
||||
| name | description | baseurl | course |
|
||||
| Test tool | Example description | https://example.com/tool | C1 |
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
And I navigate to "LTI External tools" in current page administration
|
||||
And the "Edit" item should exist in the "Actions" action menu of the "Test tool" "table_row"
|
||||
And the "Delete" item should exist in the "Actions" action menu of the "Test tool" "table_row"
|
||||
When I open the action menu in "Test tool" "table_row"
|
||||
And I choose "Edit" in the open action menu
|
||||
And I press "Cancel"
|
||||
Then I should see "Test tool" in the "reportbuilder-table" "table"
|
||||
And I open the action menu in "Test tool" "table_row"
|
||||
And I choose "Edit" in the open action menu
|
||||
And I set the following fields to these values:
|
||||
| Tool name | Test tool (edited) |
|
||||
| Tool URL | http://example.com |
|
||||
| Tool description | A short description of the tool (edited) |
|
||||
And I press "Save changes"
|
||||
And I should see "The changes to the course tool 'Test tool (edited)' were saved"
|
||||
And I should see "A short description of the tool (edited)" in the "Test tool (edited)" "table_row"
|
||||
|
||||
@javascript
|
||||
Scenario: Navigate through the listing of course tools
|
||||
Given 20 "mod_lti > course tools" exist with the following data:
|
||||
| name | Test tool [count] |
|
||||
| description | Example description [count] |
|
||||
| baseurl | https://www.example.com/tool[count] |
|
||||
| course | C1 |
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
When I navigate to "LTI External tools" in current page administration
|
||||
Then I should see "Test tool 1" in the "reportbuilder-table" "table"
|
||||
And I click on "Name" "link"
|
||||
And I should see "Test tool 20" in the "reportbuilder-table" "table"
|
||||
And I click on "2" "link" in the "page" "region"
|
||||
And I should see "Test tool 1" in the "reportbuilder-table" "table"
|
||||
|
||||
@javascript
|
||||
Scenario: Delete a course tool
|
||||
Given the following "mod_lti > course tools" exist:
|
||||
| name | description | baseurl | course |
|
||||
| Test tool | Example description | https://example.com/tool | C1 |
|
||||
| Another tool | Example 123 | https://another.example.com/tool | C1 |
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
And I navigate to "LTI External tools" in current page administration
|
||||
When I open the action menu in "Test tool" "table_row"
|
||||
And I choose "Delete" in the open action menu
|
||||
Then I should see "Are you sure you want to delete this course tool?"
|
||||
And I click on "Cancel" "button" in the "Delete a course tool" "dialogue"
|
||||
And I should see "Test tool" in the "reportbuilder-table" "table"
|
||||
And I open the action menu in "Test tool" "table_row"
|
||||
And I choose "Delete" in the open action menu
|
||||
And I should see "Are you sure you want to delete this course tool?"
|
||||
And I click on "Delete" "button" in the "Delete a course tool" "dialogue"
|
||||
And I should see "Course tool deleted"
|
||||
And I should not see "Test tool" in the "reportbuilder-table" "table"
|
||||
@@ -0,0 +1,82 @@
|
||||
<?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 delete_course_tool_type external function.
|
||||
*
|
||||
* @package mod_lti
|
||||
* @copyright 2023 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \mod_lti\external\delete_course_tool_type
|
||||
*/
|
||||
class delete_course_tool_type_test extends \mod_lti_testcase {
|
||||
|
||||
/**
|
||||
* Test delete_course_tool() for a course tool.
|
||||
* @covers ::execute
|
||||
*/
|
||||
public function test_delete_course_tool() {
|
||||
$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
|
||||
],
|
||||
(object) [
|
||||
'lti_typename' => "My course tool",
|
||||
'lti_toolurl' => 'http://example.com',
|
||||
'lti_ltiversion' => 'LTI-1p0'
|
||||
]
|
||||
);
|
||||
|
||||
$data = delete_course_tool_type::execute($typeid);
|
||||
$data = external_api::clean_returnvalue(delete_course_tool_type::execute_returns(), $data);
|
||||
|
||||
$this->assertTrue($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test delete_course_tool() for a site tool, which is forbidden.
|
||||
* @covers ::execute
|
||||
*/
|
||||
public function test_delete_course_tool_site_tool() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
|
||||
$this->setUser($editingteacher);
|
||||
|
||||
$type = $this->generate_tool_type(123); // Creates a site tool.
|
||||
|
||||
$this->expectException(\invalid_parameter_exception::class);
|
||||
delete_course_tool_type::execute($type->id);
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,12 @@ class behat_mod_lti_generator extends behat_generator_base {
|
||||
'datagenerator' => 'tool_types',
|
||||
'required' => ['baseurl'],
|
||||
],
|
||||
'course tools' => [
|
||||
'singular' => 'course tool',
|
||||
'datagenerator' => 'course_tool_types',
|
||||
'required' => ['baseurl', 'course'],
|
||||
'switchids' => ['course' => 'course']
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,4 +100,25 @@ class mod_lti_generator extends testing_module_generator {
|
||||
}
|
||||
lti_add_type((object) $type, (object) $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a course tool type.
|
||||
*
|
||||
* @param array $type the type info.
|
||||
* @param array|null $config the type configuration.
|
||||
* @return void
|
||||
* @throws coding_exception if any required fields are missing.
|
||||
*/
|
||||
public function create_course_tool_types(array $type, ?array $config = null): void {
|
||||
global $SITE;
|
||||
|
||||
if (!isset($type['baseurl'])) {
|
||||
throw new coding_exception('Must specify baseurl when creating a course tool type.');
|
||||
}
|
||||
if (!isset($type['course']) || $type['course'] == $SITE->id) {
|
||||
throw new coding_exception('Must specify a non-site course when creating a course tool type.');
|
||||
}
|
||||
$type['coursevisible'] = LTI_COURSEVISIBLE_PRECONFIGURED; // The default for course tools.
|
||||
lti_add_type((object) $type, (object) $config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
|
||||
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||
|
||||
/**
|
||||
* Abstract base testcase for mod_lti unit tests.
|
||||
@@ -47,7 +48,8 @@ abstract class mod_lti_testcase extends externallib_advanced_testcase {
|
||||
$type->description = "Example description $uniqueid";
|
||||
$type->toolproxyid = $toolproxyid;
|
||||
$type->baseurl = $this->getExternalTestFileUrl("/test$uniqueid.html");
|
||||
lti_add_type($type, new stdClass());
|
||||
|
||||
$type->id = lti_add_type($type, new stdClass());
|
||||
return $type;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$plugin->version = 2023042400; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2023070500; // 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;
|
||||
|
||||
@@ -90,6 +90,9 @@ abstract class base {
|
||||
/** @var array $attributes */
|
||||
private $attributes = [];
|
||||
|
||||
/** @var lang_string $noresultsnotice */
|
||||
private $noresultsnotice;
|
||||
|
||||
/**
|
||||
* Base report constructor
|
||||
*
|
||||
@@ -97,6 +100,7 @@ abstract class base {
|
||||
*/
|
||||
public function __construct(report $report) {
|
||||
$this->report = $report;
|
||||
$this->noresultsnotice = new lang_string('nothingtodisplay');
|
||||
|
||||
// Initialise and validate the report.
|
||||
$this->initialise();
|
||||
@@ -767,6 +771,25 @@ abstract class base {
|
||||
$this->defaultperpage = $defaultperpage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default lang string for the notice used when no results are found.
|
||||
*
|
||||
* @param lang_string|null $notice string, or null to tell the report to omit the notice entirely.
|
||||
* @return void
|
||||
*/
|
||||
public function set_default_no_results_notice(?lang_string $notice): void {
|
||||
$this->noresultsnotice = $notice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default lang string for the notice used when no results are found.
|
||||
*
|
||||
* @return lang_string|null the lang_string instance or null if the report prefers not to use one.
|
||||
*/
|
||||
public function get_default_no_results_notice(): ?lang_string {
|
||||
return $this->noresultsnotice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default 'per page' size
|
||||
*
|
||||
|
||||
@@ -236,7 +236,9 @@ abstract class base_report_table extends table_sql implements dynamic, renderabl
|
||||
echo $this->get_dynamic_table_html_start();
|
||||
echo $this->render_reset_button();
|
||||
|
||||
echo $OUTPUT->render(new notification(get_string('nothingtodisplay'), notification::NOTIFY_INFO, false));
|
||||
if ($notice = $this->report->get_default_no_results_notice()) {
|
||||
echo $OUTPUT->render(new notification($notice->out(), notification::NOTIFY_INFO, false));
|
||||
}
|
||||
|
||||
echo $this->get_dynamic_table_html_end();
|
||||
}
|
||||
|
||||
@@ -13,6 +13,12 @@ Information provided here is intended especially for developers.
|
||||
- `enrolment:method` => `enrol:plugin`
|
||||
* Trying to add/annotate duplicate entity names to a report will now throw a coding exception
|
||||
* The `get_default_entity_name` method of the base entity class is now private, and shouldn't be overridden in extending classes
|
||||
* Two new methods:
|
||||
- `get_default_no_results_notice` and
|
||||
- `set_default_no_results_notice`
|
||||
have been added to core_reportbuilder\local\report\base, allowing report implementations to control what lang string is used in
|
||||
the notice when the report has no results. Reports can either set a preferred lang string, or pass null if the notice isn't
|
||||
required in that particular report.
|
||||
|
||||
=== 4.2 ===
|
||||
|
||||
|
||||
Reference in New Issue
Block a user