1 |
efrain |
1 |
<?php
|
|
|
2 |
// This file is part of Moodle - http://moodle.org/
|
|
|
3 |
//
|
|
|
4 |
// Moodle is free software: you can redistribute it and/or modify
|
|
|
5 |
// it under the terms of the GNU General Public License as published by
|
|
|
6 |
// the Free Software Foundation, either version 3 of the License, or
|
|
|
7 |
// (at your option) any later version.
|
|
|
8 |
//
|
|
|
9 |
// Moodle is distributed in the hope that it will be useful,
|
|
|
10 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
11 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
12 |
// GNU General Public License for more details.
|
|
|
13 |
//
|
|
|
14 |
// You should have received a copy of the GNU General Public License
|
|
|
15 |
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
16 |
//
|
|
|
17 |
// This file is part of BasicLTI4Moodle
|
|
|
18 |
//
|
|
|
19 |
// BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability)
|
|
|
20 |
// consumer for Moodle 1.9 and Moodle 2.0. BasicLTI is a IMS Standard that allows web
|
|
|
21 |
// based learning tools to be easily integrated in LMS as native ones. The IMS BasicLTI
|
|
|
22 |
// specification is part of the IMS standard Common Cartridge 1.1 Sakai and other main LMS
|
|
|
23 |
// are already supporting or going to support BasicLTI. This project Implements the consumer
|
|
|
24 |
// for Moodle. Moodle is a Free Open source Learning Management System by Martin Dougiamas.
|
|
|
25 |
// BasicLTI4Moodle is a project iniciated and leaded by Ludo(Marc Alier) and Jordi Piguillem
|
|
|
26 |
// at the GESSI research group at UPC.
|
|
|
27 |
// SimpleLTI consumer for Moodle is an implementation of the early specification of LTI
|
|
|
28 |
// by Charles Severance (Dr Chuck) htp://dr-chuck.com , developed by Jordi Piguillem in a
|
|
|
29 |
// Google Summer of Code 2008 project co-mentored by Charles Severance and Marc Alier.
|
|
|
30 |
//
|
|
|
31 |
// BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis
|
|
|
32 |
// of the Universitat Politecnica de Catalunya http://www.upc.edu
|
|
|
33 |
// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu.
|
|
|
34 |
|
|
|
35 |
/**
|
|
|
36 |
* This file contains a library of functions and constants for the lti module
|
|
|
37 |
*
|
|
|
38 |
* @package mod_lti
|
|
|
39 |
* @copyright 2009 Marc Alier, Jordi Piguillem, Nikolas Galanis
|
|
|
40 |
* marc.alier@upc.edu
|
|
|
41 |
* @copyright 2009 Universitat Politecnica de Catalunya http://www.upc.edu
|
|
|
42 |
* @author Marc Alier
|
|
|
43 |
* @author Jordi Piguillem
|
|
|
44 |
* @author Nikolas Galanis
|
|
|
45 |
* @author Chris Scribner
|
|
|
46 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
47 |
*/
|
|
|
48 |
|
|
|
49 |
defined('MOODLE_INTERNAL') || die;
|
|
|
50 |
|
|
|
51 |
/**
|
|
|
52 |
* List of features supported in URL module
|
|
|
53 |
* @param string $feature FEATURE_xx constant for requested feature
|
|
|
54 |
* @return mixed True if module supports feature, false if not, null if doesn't know or string for the module purpose.
|
|
|
55 |
*/
|
|
|
56 |
function lti_supports($feature) {
|
|
|
57 |
switch ($feature) {
|
|
|
58 |
case FEATURE_GROUPS:
|
|
|
59 |
case FEATURE_GROUPINGS:
|
|
|
60 |
return false;
|
|
|
61 |
case FEATURE_MOD_INTRO:
|
|
|
62 |
case FEATURE_COMPLETION_TRACKS_VIEWS:
|
|
|
63 |
case FEATURE_GRADE_HAS_GRADE:
|
|
|
64 |
case FEATURE_GRADE_OUTCOMES:
|
|
|
65 |
case FEATURE_BACKUP_MOODLE2:
|
|
|
66 |
case FEATURE_SHOW_DESCRIPTION:
|
|
|
67 |
return true;
|
|
|
68 |
case FEATURE_MOD_PURPOSE:
|
|
|
69 |
return MOD_PURPOSE_OTHER;
|
|
|
70 |
|
|
|
71 |
default:
|
|
|
72 |
return null;
|
|
|
73 |
}
|
|
|
74 |
}
|
|
|
75 |
|
|
|
76 |
/**
|
|
|
77 |
* Given an object containing all the necessary data,
|
|
|
78 |
* (defined by the form in mod.html) this function
|
|
|
79 |
* will create a new instance and return the id number
|
|
|
80 |
* of the new instance.
|
|
|
81 |
*
|
|
|
82 |
* @param object $instance An object from the form in mod.html
|
|
|
83 |
* @return int The id of the newly inserted basiclti record
|
|
|
84 |
**/
|
|
|
85 |
function lti_add_instance($lti, $mform) {
|
|
|
86 |
global $DB, $CFG;
|
|
|
87 |
require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
|
|
88 |
|
|
|
89 |
if (!isset($lti->toolurl)) {
|
|
|
90 |
$lti->toolurl = '';
|
|
|
91 |
}
|
|
|
92 |
|
|
|
93 |
lti_load_tool_if_cartridge($lti);
|
|
|
94 |
|
|
|
95 |
$lti->timecreated = time();
|
|
|
96 |
$lti->timemodified = $lti->timecreated;
|
|
|
97 |
$lti->servicesalt = uniqid('', true);
|
|
|
98 |
if (!isset($lti->typeid)) {
|
|
|
99 |
$lti->typeid = null;
|
|
|
100 |
}
|
|
|
101 |
|
|
|
102 |
lti_force_type_config_settings($lti, lti_get_type_config_by_instance($lti));
|
|
|
103 |
|
|
|
104 |
if (empty($lti->typeid) && isset($lti->urlmatchedtypeid)) {
|
|
|
105 |
$lti->typeid = $lti->urlmatchedtypeid;
|
|
|
106 |
}
|
|
|
107 |
|
|
|
108 |
if (!isset($lti->instructorchoiceacceptgrades) || $lti->instructorchoiceacceptgrades != LTI_SETTING_ALWAYS) {
|
|
|
109 |
// The instance does not accept grades back from the provider, so set to "No grade" value 0.
|
|
|
110 |
$lti->grade = 0;
|
|
|
111 |
}
|
|
|
112 |
|
|
|
113 |
$lti->id = $DB->insert_record('lti', $lti);
|
|
|
114 |
|
|
|
115 |
if (isset($lti->instructorchoiceacceptgrades) && $lti->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS) {
|
|
|
116 |
if (!isset($lti->cmidnumber)) {
|
|
|
117 |
$lti->cmidnumber = '';
|
|
|
118 |
}
|
|
|
119 |
|
|
|
120 |
lti_grade_item_update($lti);
|
|
|
121 |
}
|
|
|
122 |
|
|
|
123 |
$services = lti_get_services();
|
|
|
124 |
foreach ($services as $service) {
|
|
|
125 |
$service->instance_added( $lti );
|
|
|
126 |
}
|
|
|
127 |
|
|
|
128 |
$completiontimeexpected = !empty($lti->completionexpected) ? $lti->completionexpected : null;
|
|
|
129 |
\core_completion\api::update_completion_date_event($lti->coursemodule, 'lti', $lti->id, $completiontimeexpected);
|
|
|
130 |
|
|
|
131 |
return $lti->id;
|
|
|
132 |
}
|
|
|
133 |
|
|
|
134 |
/**
|
|
|
135 |
* Given an object containing all the necessary data,
|
|
|
136 |
* (defined by the form in mod.html) this function
|
|
|
137 |
* will update an existing instance with new data.
|
|
|
138 |
*
|
|
|
139 |
* @param object $instance An object from the form in mod.html
|
|
|
140 |
* @return boolean Success/Fail
|
|
|
141 |
**/
|
|
|
142 |
function lti_update_instance($lti, $mform) {
|
|
|
143 |
global $DB, $CFG;
|
|
|
144 |
require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
|
|
145 |
|
|
|
146 |
lti_load_tool_if_cartridge($lti);
|
|
|
147 |
|
|
|
148 |
$lti->timemodified = time();
|
|
|
149 |
$lti->id = $lti->instance;
|
|
|
150 |
|
|
|
151 |
if (!isset($lti->showtitlelaunch)) {
|
|
|
152 |
$lti->showtitlelaunch = 0;
|
|
|
153 |
}
|
|
|
154 |
|
|
|
155 |
if (!isset($lti->showdescriptionlaunch)) {
|
|
|
156 |
$lti->showdescriptionlaunch = 0;
|
|
|
157 |
}
|
|
|
158 |
|
|
|
159 |
lti_force_type_config_settings($lti, lti_get_type_config_by_instance($lti));
|
|
|
160 |
|
|
|
161 |
if (isset($lti->instructorchoiceacceptgrades) && $lti->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS) {
|
|
|
162 |
lti_grade_item_update($lti);
|
|
|
163 |
} else {
|
|
|
164 |
// Instance is no longer accepting grades from Provider, set grade to "No grade" value 0.
|
|
|
165 |
$lti->grade = 0;
|
|
|
166 |
$lti->instructorchoiceacceptgrades = 0;
|
|
|
167 |
|
|
|
168 |
lti_grade_item_delete($lti);
|
|
|
169 |
}
|
|
|
170 |
|
|
|
171 |
if ($lti->typeid == 0 && isset($lti->urlmatchedtypeid)) {
|
|
|
172 |
$lti->typeid = $lti->urlmatchedtypeid;
|
|
|
173 |
}
|
|
|
174 |
|
|
|
175 |
$services = lti_get_services();
|
|
|
176 |
foreach ($services as $service) {
|
|
|
177 |
$service->instance_updated( $lti );
|
|
|
178 |
}
|
|
|
179 |
|
|
|
180 |
$completiontimeexpected = !empty($lti->completionexpected) ? $lti->completionexpected : null;
|
|
|
181 |
\core_completion\api::update_completion_date_event($lti->coursemodule, 'lti', $lti->id, $completiontimeexpected);
|
|
|
182 |
|
|
|
183 |
return $DB->update_record('lti', $lti);
|
|
|
184 |
}
|
|
|
185 |
|
|
|
186 |
/**
|
|
|
187 |
* Given an ID of an instance of this module,
|
|
|
188 |
* this function will permanently delete the instance
|
|
|
189 |
* and any data that depends on it.
|
|
|
190 |
*
|
|
|
191 |
* @param int $id Id of the module instance
|
|
|
192 |
* @return boolean Success/Failure
|
|
|
193 |
**/
|
|
|
194 |
function lti_delete_instance($id) {
|
|
|
195 |
global $DB, $CFG;
|
|
|
196 |
require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
|
|
197 |
|
|
|
198 |
if (! $basiclti = $DB->get_record("lti", array("id" => $id))) {
|
|
|
199 |
return false;
|
|
|
200 |
}
|
|
|
201 |
|
|
|
202 |
$result = true;
|
|
|
203 |
|
|
|
204 |
// Delete any dependent records here.
|
|
|
205 |
lti_grade_item_delete($basiclti);
|
|
|
206 |
|
|
|
207 |
$ltitype = $DB->get_record('lti_types', array('id' => $basiclti->typeid));
|
|
|
208 |
if ($ltitype) {
|
|
|
209 |
$DB->delete_records('lti_tool_settings',
|
|
|
210 |
array('toolproxyid' => $ltitype->toolproxyid, 'course' => $basiclti->course, 'coursemoduleid' => $id));
|
|
|
211 |
}
|
|
|
212 |
|
|
|
213 |
$cm = get_coursemodule_from_instance('lti', $id);
|
|
|
214 |
\core_completion\api::update_completion_date_event($cm->id, 'lti', $id, null);
|
|
|
215 |
|
|
|
216 |
// We must delete the module record after we delete the grade item.
|
|
|
217 |
if ($DB->delete_records("lti", array("id" => $basiclti->id)) ) {
|
|
|
218 |
$services = lti_get_services();
|
|
|
219 |
foreach ($services as $service) {
|
|
|
220 |
$service->instance_deleted( $id );
|
|
|
221 |
}
|
|
|
222 |
return true;
|
|
|
223 |
}
|
|
|
224 |
return false;
|
|
|
225 |
|
|
|
226 |
}
|
|
|
227 |
|
|
|
228 |
/**
|
|
|
229 |
* Return the preconfigured tools which are configured for inclusion in the activity picker.
|
|
|
230 |
*
|
|
|
231 |
* @param \core_course\local\entity\content_item $defaultmodulecontentitem reference to the content item for the LTI module.
|
|
|
232 |
* @param \stdClass $user the user object, to use for cap checks if desired.
|
|
|
233 |
* @param stdClass $course the course to scope items to.
|
|
|
234 |
* @return array the array of content items.
|
|
|
235 |
*/
|
|
|
236 |
function lti_get_course_content_items(\core_course\local\entity\content_item $defaultmodulecontentitem, \stdClass $user,
|
|
|
237 |
\stdClass $course) {
|
|
|
238 |
global $CFG, $OUTPUT;
|
|
|
239 |
require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
|
|
240 |
|
|
|
241 |
$types = [];
|
|
|
242 |
|
|
|
243 |
// Use of a tool type, whether site or course level, is controlled by the following cap.
|
|
|
244 |
if (!has_capability('mod/lti:addpreconfiguredinstance', \core\context\course::instance($course->id), $user)) {
|
|
|
245 |
return $types;
|
|
|
246 |
}
|
|
|
247 |
$preconfiguredtools = lti_get_configured_types($course->id, $defaultmodulecontentitem->get_link()->param('sr'));
|
|
|
248 |
|
|
|
249 |
foreach ($preconfiguredtools as $preconfiguredtool) {
|
|
|
250 |
|
|
|
251 |
// Append the help link to the help text.
|
|
|
252 |
if (isset($preconfiguredtool->help)) {
|
|
|
253 |
if (isset($preconfiguredtool->helplink)) {
|
|
|
254 |
$linktext = get_string('morehelp');
|
|
|
255 |
$preconfiguredtool->help .= html_writer::tag('div',
|
|
|
256 |
$OUTPUT->doc_link($preconfiguredtool->helplink, $linktext, true), ['class' => 'helpdoclink']);
|
|
|
257 |
}
|
|
|
258 |
} else {
|
|
|
259 |
$preconfiguredtool->help = '';
|
|
|
260 |
}
|
|
|
261 |
|
|
|
262 |
// Preconfigured tools take their own id + 1. This logic exists because, previously, the entry permitting manual instance
|
|
|
263 |
// creation (the $defaultmodulecontentitem, or 'External tool' item) was included and had the id 1. This logic prevented id
|
|
|
264 |
// collisions.
|
|
|
265 |
$types[] = new \core_course\local\entity\content_item(
|
|
|
266 |
$preconfiguredtool->id + 1,
|
|
|
267 |
$preconfiguredtool->name,
|
|
|
268 |
new \core_course\local\entity\string_title($preconfiguredtool->title),
|
|
|
269 |
$preconfiguredtool->link,
|
|
|
270 |
$preconfiguredtool->icon,
|
|
|
271 |
$preconfiguredtool->help,
|
|
|
272 |
$defaultmodulecontentitem->get_archetype(),
|
|
|
273 |
$defaultmodulecontentitem->get_component_name(),
|
|
|
274 |
$defaultmodulecontentitem->get_purpose()
|
|
|
275 |
);
|
|
|
276 |
}
|
|
|
277 |
return $types;
|
|
|
278 |
}
|
|
|
279 |
|
|
|
280 |
/**
|
|
|
281 |
* Return all content items which can be added to any course.
|
|
|
282 |
*
|
|
|
283 |
* @param \core_course\local\entity\content_item $defaultmodulecontentitem
|
|
|
284 |
* @return array the array of content items.
|
|
|
285 |
*/
|
|
|
286 |
function mod_lti_get_all_content_items(\core_course\local\entity\content_item $defaultmodulecontentitem): array {
|
|
|
287 |
global $OUTPUT, $CFG;
|
|
|
288 |
require_once($CFG->dirroot . '/mod/lti/locallib.php'); // For access to constants.
|
|
|
289 |
|
|
|
290 |
$types = [];
|
|
|
291 |
|
|
|
292 |
foreach (lti_get_lti_types() as $ltitype) {
|
|
|
293 |
if ($ltitype->coursevisible != LTI_COURSEVISIBLE_ACTIVITYCHOOSER) {
|
|
|
294 |
continue;
|
|
|
295 |
}
|
|
|
296 |
$type = new stdClass();
|
|
|
297 |
$type->id = $ltitype->id;
|
|
|
298 |
$type->modclass = MOD_CLASS_ACTIVITY;
|
|
|
299 |
$type->name = 'lti_type_' . $ltitype->id;
|
|
|
300 |
// Clean the name. We don't want tags here.
|
|
|
301 |
$type->title = clean_param($ltitype->name, PARAM_NOTAGS);
|
|
|
302 |
$trimmeddescription = trim($ltitype->description ?? '');
|
|
|
303 |
$type->help = '';
|
|
|
304 |
if ($trimmeddescription != '') {
|
|
|
305 |
// Clean the description. We don't want tags here.
|
|
|
306 |
$type->help = clean_param($trimmeddescription, PARAM_NOTAGS);
|
|
|
307 |
$type->helplink = get_string('modulename_shortcut_link', 'lti');
|
|
|
308 |
}
|
|
|
309 |
if (empty($ltitype->icon)) {
|
|
|
310 |
$type->icon = $OUTPUT->pix_icon('monologo', '', 'lti', array('class' => 'icon'));
|
|
|
311 |
} else {
|
|
|
312 |
$type->icon = html_writer::empty_tag('img', array('src' => $ltitype->icon, 'alt' => $ltitype->name, 'class' => 'icon'));
|
|
|
313 |
}
|
|
|
314 |
$type->link = new moodle_url('/course/modedit.php', array('add' => 'lti', 'return' => 0, 'typeid' => $ltitype->id));
|
|
|
315 |
|
|
|
316 |
// Preconfigured tools take their own id + 1. This logic exists because, previously, the entry permitting manual instance
|
|
|
317 |
// creation (the $defaultmodulecontentitem, or 'External tool' item) was included and had the id 1. This logic prevented id
|
|
|
318 |
// collisions.
|
|
|
319 |
$types[] = new \core_course\local\entity\content_item(
|
|
|
320 |
$type->id + 1,
|
|
|
321 |
$type->name,
|
|
|
322 |
new \core_course\local\entity\string_title($type->title),
|
|
|
323 |
$type->link,
|
|
|
324 |
$type->icon,
|
|
|
325 |
$type->help,
|
|
|
326 |
$defaultmodulecontentitem->get_archetype(),
|
|
|
327 |
$defaultmodulecontentitem->get_component_name(),
|
|
|
328 |
$defaultmodulecontentitem->get_purpose()
|
|
|
329 |
);
|
|
|
330 |
}
|
|
|
331 |
|
|
|
332 |
return $types;
|
|
|
333 |
}
|
|
|
334 |
|
|
|
335 |
/**
|
|
|
336 |
* Given a coursemodule object, this function returns the extra
|
|
|
337 |
* information needed to print this activity in various places.
|
|
|
338 |
* For this module we just need to support external urls as
|
|
|
339 |
* activity icons
|
|
|
340 |
*
|
|
|
341 |
* @param stdClass $coursemodule
|
|
|
342 |
* @return cached_cm_info info
|
|
|
343 |
*/
|
|
|
344 |
function lti_get_coursemodule_info($coursemodule) {
|
|
|
345 |
global $DB, $CFG;
|
|
|
346 |
require_once($CFG->dirroot.'/mod/lti/locallib.php');
|
|
|
347 |
|
|
|
348 |
if (!$lti = $DB->get_record('lti', array('id' => $coursemodule->instance),
|
|
|
349 |
'icon, secureicon, intro, introformat, name, typeid, toolurl, launchcontainer')) {
|
|
|
350 |
return null;
|
|
|
351 |
}
|
|
|
352 |
|
|
|
353 |
$info = new cached_cm_info();
|
|
|
354 |
|
|
|
355 |
if ($coursemodule->showdescription) {
|
|
|
356 |
// Convert intro to html. Do not filter cached version, filters run at display time.
|
|
|
357 |
$info->content = format_module_intro('lti', $lti, $coursemodule->id, false);
|
|
|
358 |
}
|
|
|
359 |
|
|
|
360 |
if (!empty($lti->typeid)) {
|
|
|
361 |
$toolconfig = lti_get_type_config($lti->typeid);
|
|
|
362 |
} else if ($tool = lti_get_tool_by_url_match($lti->toolurl)) {
|
|
|
363 |
$toolconfig = lti_get_type_config($tool->id);
|
|
|
364 |
} else {
|
|
|
365 |
$toolconfig = array();
|
|
|
366 |
}
|
|
|
367 |
|
|
|
368 |
// We want to use the right icon based on whether the
|
|
|
369 |
// current page is being requested over http or https.
|
|
|
370 |
if (lti_request_is_using_ssl() &&
|
|
|
371 |
(!empty($lti->secureicon) || (isset($toolconfig['secureicon']) && !empty($toolconfig['secureicon'])))) {
|
|
|
372 |
if (!empty($lti->secureicon)) {
|
|
|
373 |
$info->iconurl = new moodle_url($lti->secureicon);
|
|
|
374 |
} else {
|
|
|
375 |
$info->iconurl = new moodle_url($toolconfig['secureicon']);
|
|
|
376 |
}
|
|
|
377 |
} else if (!empty($lti->icon)) {
|
|
|
378 |
$info->iconurl = new moodle_url($lti->icon);
|
|
|
379 |
} else if (isset($toolconfig['icon']) && !empty($toolconfig['icon'])) {
|
|
|
380 |
$info->iconurl = new moodle_url($toolconfig['icon']);
|
|
|
381 |
}
|
|
|
382 |
|
|
|
383 |
// Does the link open in a new window?
|
|
|
384 |
$launchcontainer = lti_get_launch_container($lti, $toolconfig);
|
|
|
385 |
if ($launchcontainer == LTI_LAUNCH_CONTAINER_WINDOW) {
|
|
|
386 |
$launchurl = new moodle_url('/mod/lti/launch.php', array('id' => $coursemodule->id));
|
|
|
387 |
$info->onclick = "window.open('" . $launchurl->out(false) . "', 'lti-".$coursemodule->id."'); return false;";
|
|
|
388 |
}
|
|
|
389 |
|
|
|
390 |
$info->name = $lti->name;
|
|
|
391 |
|
|
|
392 |
return $info;
|
|
|
393 |
}
|
|
|
394 |
|
|
|
395 |
/**
|
|
|
396 |
* Return a small object with summary information about what a
|
|
|
397 |
* user has done with a given particular instance of this module
|
|
|
398 |
* Used for user activity reports.
|
|
|
399 |
* $return->time = the time they did it
|
|
|
400 |
* $return->info = a short text description
|
|
|
401 |
*
|
|
|
402 |
* @return null
|
|
|
403 |
* @TODO: implement this moodle function (if needed)
|
|
|
404 |
**/
|
|
|
405 |
function lti_user_outline($course, $user, $mod, $basiclti) {
|
|
|
406 |
return null;
|
|
|
407 |
}
|
|
|
408 |
|
|
|
409 |
/**
|
|
|
410 |
* Print a detailed representation of what a user has done with
|
|
|
411 |
* a given particular instance of this module, for user activity reports.
|
|
|
412 |
*
|
|
|
413 |
* @return boolean
|
|
|
414 |
* @TODO: implement this moodle function (if needed)
|
|
|
415 |
**/
|
|
|
416 |
function lti_user_complete($course, $user, $mod, $basiclti) {
|
|
|
417 |
return true;
|
|
|
418 |
}
|
|
|
419 |
|
|
|
420 |
/**
|
|
|
421 |
* Given a course and a time, this module should find recent activity
|
|
|
422 |
* that has occurred in basiclti activities and print it out.
|
|
|
423 |
* Return true if there was output, or false is there was none.
|
|
|
424 |
*
|
|
|
425 |
* @uses $CFG
|
|
|
426 |
* @return boolean
|
|
|
427 |
* @TODO: implement this moodle function
|
|
|
428 |
**/
|
|
|
429 |
function lti_print_recent_activity($course, $isteacher, $timestart) {
|
|
|
430 |
return false; // True if anything was printed, otherwise false.
|
|
|
431 |
}
|
|
|
432 |
|
|
|
433 |
/**
|
|
|
434 |
* Function to be run periodically according to the moodle cron
|
|
|
435 |
* This function searches for things that need to be done, such
|
|
|
436 |
* as sending out mail, toggling flags etc ...
|
|
|
437 |
*
|
|
|
438 |
* @uses $CFG
|
|
|
439 |
* @return boolean
|
|
|
440 |
**/
|
|
|
441 |
function lti_cron () {
|
|
|
442 |
return true;
|
|
|
443 |
}
|
|
|
444 |
|
|
|
445 |
/**
|
|
|
446 |
* Must return an array of grades for a given instance of this module,
|
|
|
447 |
* indexed by user. It also returns a maximum allowed grade.
|
|
|
448 |
*
|
|
|
449 |
* Example:
|
|
|
450 |
* $return->grades = array of grades;
|
|
|
451 |
* $return->maxgrade = maximum allowed grade;
|
|
|
452 |
*
|
|
|
453 |
* return $return;
|
|
|
454 |
*
|
|
|
455 |
* @param int $basicltiid ID of an instance of this module
|
|
|
456 |
* @return mixed Null or object with an array of grades and with the maximum grade
|
|
|
457 |
*
|
|
|
458 |
* @TODO: implement this moodle function (if needed)
|
|
|
459 |
**/
|
|
|
460 |
function lti_grades($basicltiid) {
|
|
|
461 |
return null;
|
|
|
462 |
}
|
|
|
463 |
|
|
|
464 |
/**
|
|
|
465 |
* @deprecated since Moodle 3.8
|
|
|
466 |
*/
|
|
|
467 |
function lti_scale_used() {
|
|
|
468 |
throw new coding_exception('lti_scale_used() can not be used anymore. Plugins can implement ' .
|
|
|
469 |
'<modname>_scale_used_anywhere, all implementations of <modname>_scale_used are now ignored');
|
|
|
470 |
}
|
|
|
471 |
|
|
|
472 |
/**
|
|
|
473 |
* Checks if scale is being used by any instance of basiclti.
|
|
|
474 |
* This function was added in 1.9
|
|
|
475 |
*
|
|
|
476 |
* This is used to find out if scale used anywhere
|
|
|
477 |
* @param $scaleid int
|
|
|
478 |
* @return boolean True if the scale is used by any basiclti
|
|
|
479 |
*
|
|
|
480 |
*/
|
|
|
481 |
function lti_scale_used_anywhere($scaleid) {
|
|
|
482 |
global $DB;
|
|
|
483 |
|
|
|
484 |
if ($scaleid and $DB->record_exists('lti', array('grade' => -$scaleid))) {
|
|
|
485 |
return true;
|
|
|
486 |
} else {
|
|
|
487 |
return false;
|
|
|
488 |
}
|
|
|
489 |
}
|
|
|
490 |
|
|
|
491 |
/**
|
|
|
492 |
* Execute post-install custom actions for the module
|
|
|
493 |
* This function was added in 1.9
|
|
|
494 |
*
|
|
|
495 |
* @return boolean true if success, false on error
|
|
|
496 |
*/
|
|
|
497 |
function lti_install() {
|
|
|
498 |
return true;
|
|
|
499 |
}
|
|
|
500 |
|
|
|
501 |
/**
|
|
|
502 |
* Execute post-uninstall custom actions for the module
|
|
|
503 |
* This function was added in 1.9
|
|
|
504 |
*
|
|
|
505 |
* @return boolean true if success, false on error
|
|
|
506 |
*/
|
|
|
507 |
function lti_uninstall() {
|
|
|
508 |
return true;
|
|
|
509 |
}
|
|
|
510 |
|
|
|
511 |
/**
|
|
|
512 |
* Returns available Basic LTI types
|
|
|
513 |
*
|
|
|
514 |
* @return array of basicLTI types
|
|
|
515 |
*/
|
|
|
516 |
function lti_get_lti_types() {
|
|
|
517 |
global $DB;
|
|
|
518 |
|
|
|
519 |
return $DB->get_records('lti_types', null, 'state DESC, timemodified DESC');
|
|
|
520 |
}
|
|
|
521 |
|
|
|
522 |
/**
|
|
|
523 |
* Returns available Basic LTI types that match the given
|
|
|
524 |
* tool proxy id
|
|
|
525 |
*
|
|
|
526 |
* @param int $toolproxyid Tool proxy id
|
|
|
527 |
* @return array of basicLTI types
|
|
|
528 |
*/
|
|
|
529 |
function lti_get_lti_types_from_proxy_id($toolproxyid) {
|
|
|
530 |
global $DB;
|
|
|
531 |
|
|
|
532 |
return $DB->get_records('lti_types', array('toolproxyid' => $toolproxyid), 'state DESC, timemodified DESC');
|
|
|
533 |
}
|
|
|
534 |
|
|
|
535 |
/**
|
|
|
536 |
* Create grade item for given basiclti
|
|
|
537 |
*
|
|
|
538 |
* @category grade
|
|
|
539 |
* @param object $basiclti object with extra cmidnumber
|
|
|
540 |
* @param mixed optional array/object of grade(s); 'reset' means reset grades in gradebook
|
|
|
541 |
* @return int 0 if ok, error code otherwise
|
|
|
542 |
*/
|
|
|
543 |
function lti_grade_item_update($basiclti, $grades = null) {
|
|
|
544 |
global $CFG;
|
|
|
545 |
require_once($CFG->libdir.'/gradelib.php');
|
|
|
546 |
require_once($CFG->dirroot.'/mod/lti/servicelib.php');
|
|
|
547 |
|
|
|
548 |
if (!lti_accepts_grades($basiclti)) {
|
|
|
549 |
return 0;
|
|
|
550 |
}
|
|
|
551 |
|
|
|
552 |
$params = array('itemname' => $basiclti->name, 'idnumber' => $basiclti->cmidnumber);
|
|
|
553 |
|
|
|
554 |
if ($basiclti->grade > 0) {
|
|
|
555 |
$params['gradetype'] = GRADE_TYPE_VALUE;
|
|
|
556 |
$params['grademax'] = $basiclti->grade;
|
|
|
557 |
$params['grademin'] = 0;
|
|
|
558 |
|
|
|
559 |
} else if ($basiclti->grade < 0) {
|
|
|
560 |
$params['gradetype'] = GRADE_TYPE_SCALE;
|
|
|
561 |
$params['scaleid'] = -$basiclti->grade;
|
|
|
562 |
|
|
|
563 |
} else {
|
|
|
564 |
$params['gradetype'] = GRADE_TYPE_TEXT; // Allow text comments only.
|
|
|
565 |
}
|
|
|
566 |
|
|
|
567 |
if ($grades === 'reset') {
|
|
|
568 |
$params['reset'] = true;
|
|
|
569 |
$grades = null;
|
|
|
570 |
}
|
|
|
571 |
|
|
|
572 |
return grade_update('mod/lti', $basiclti->course, 'mod', 'lti', $basiclti->id, 0, $grades, $params);
|
|
|
573 |
}
|
|
|
574 |
|
|
|
575 |
/**
|
|
|
576 |
* Update activity grades
|
|
|
577 |
*
|
|
|
578 |
* @param stdClass $basiclti The LTI instance
|
|
|
579 |
* @param int $userid Specific user only, 0 means all.
|
|
|
580 |
* @param bool $nullifnone Not used
|
|
|
581 |
*/
|
|
|
582 |
function lti_update_grades($basiclti, $userid=0, $nullifnone=true) {
|
|
|
583 |
global $CFG;
|
|
|
584 |
require_once($CFG->dirroot.'/mod/lti/servicelib.php');
|
|
|
585 |
// LTI doesn't have its own grade table so the only thing to do is update the grade item.
|
|
|
586 |
if (lti_accepts_grades($basiclti)) {
|
|
|
587 |
lti_grade_item_update($basiclti);
|
|
|
588 |
}
|
|
|
589 |
}
|
|
|
590 |
|
|
|
591 |
/**
|
|
|
592 |
* Delete grade item for given basiclti
|
|
|
593 |
*
|
|
|
594 |
* @category grade
|
|
|
595 |
* @param object $basiclti object
|
|
|
596 |
* @return object basiclti
|
|
|
597 |
*/
|
|
|
598 |
function lti_grade_item_delete($basiclti) {
|
|
|
599 |
global $CFG;
|
|
|
600 |
require_once($CFG->libdir.'/gradelib.php');
|
|
|
601 |
|
|
|
602 |
return grade_update('mod/lti', $basiclti->course, 'mod', 'lti', $basiclti->id, 0, null, array('deleted' => 1));
|
|
|
603 |
}
|
|
|
604 |
|
|
|
605 |
/**
|
|
|
606 |
* Log post actions
|
|
|
607 |
*
|
|
|
608 |
* @return array
|
|
|
609 |
*/
|
|
|
610 |
function lti_get_post_actions() {
|
|
|
611 |
return array();
|
|
|
612 |
}
|
|
|
613 |
|
|
|
614 |
/**
|
|
|
615 |
* Log view actions
|
|
|
616 |
*
|
|
|
617 |
* @return array
|
|
|
618 |
*/
|
|
|
619 |
function lti_get_view_actions() {
|
|
|
620 |
return array('view all', 'view');
|
|
|
621 |
}
|
|
|
622 |
|
|
|
623 |
/**
|
|
|
624 |
* Mark the activity completed (if required) and trigger the course_module_viewed event.
|
|
|
625 |
*
|
|
|
626 |
* @param stdClass $lti lti object
|
|
|
627 |
* @param stdClass $course course object
|
|
|
628 |
* @param stdClass $cm course module object
|
|
|
629 |
* @param stdClass $context context object
|
|
|
630 |
* @since Moodle 3.0
|
|
|
631 |
*/
|
|
|
632 |
function lti_view($lti, $course, $cm, $context) {
|
|
|
633 |
|
|
|
634 |
// Trigger course_module_viewed event.
|
|
|
635 |
$params = array(
|
|
|
636 |
'context' => $context,
|
|
|
637 |
'objectid' => $lti->id
|
|
|
638 |
);
|
|
|
639 |
|
|
|
640 |
$event = \mod_lti\event\course_module_viewed::create($params);
|
|
|
641 |
$event->add_record_snapshot('course_modules', $cm);
|
|
|
642 |
$event->add_record_snapshot('course', $course);
|
|
|
643 |
$event->add_record_snapshot('lti', $lti);
|
|
|
644 |
$event->trigger();
|
|
|
645 |
|
|
|
646 |
// Completion.
|
|
|
647 |
$completion = new completion_info($course);
|
|
|
648 |
$completion->set_module_viewed($cm);
|
|
|
649 |
}
|
|
|
650 |
|
|
|
651 |
/**
|
|
|
652 |
* Check if the module has any update that affects the current user since a given time.
|
|
|
653 |
*
|
|
|
654 |
* @param cm_info $cm course module data
|
|
|
655 |
* @param int $from the time to check updates from
|
|
|
656 |
* @param array $filter if we need to check only specific updates
|
|
|
657 |
* @return stdClass an object with the different type of areas indicating if they were updated or not
|
|
|
658 |
* @since Moodle 3.2
|
|
|
659 |
*/
|
|
|
660 |
function lti_check_updates_since(cm_info $cm, $from, $filter = array()) {
|
|
|
661 |
global $DB, $USER;
|
|
|
662 |
|
|
|
663 |
$updates = course_check_module_updates_since($cm, $from, array(), $filter);
|
|
|
664 |
|
|
|
665 |
// Check if there is a new submission.
|
|
|
666 |
$updates->submissions = (object) array('updated' => false);
|
|
|
667 |
$select = 'ltiid = :id AND userid = :userid AND (datesubmitted > :since1 OR dateupdated > :since2)';
|
|
|
668 |
$params = array('id' => $cm->instance, 'userid' => $USER->id, 'since1' => $from, 'since2' => $from);
|
|
|
669 |
$submissions = $DB->get_records_select('lti_submission', $select, $params, '', 'id');
|
|
|
670 |
if (!empty($submissions)) {
|
|
|
671 |
$updates->submissions->updated = true;
|
|
|
672 |
$updates->submissions->itemids = array_keys($submissions);
|
|
|
673 |
}
|
|
|
674 |
|
|
|
675 |
// Now, teachers should see other students updates.
|
|
|
676 |
if (has_capability('mod/lti:manage', $cm->context)) {
|
|
|
677 |
$select = 'ltiid = :id AND (datesubmitted > :since1 OR dateupdated > :since2)';
|
|
|
678 |
$params = array('id' => $cm->instance, 'since1' => $from, 'since2' => $from);
|
|
|
679 |
|
|
|
680 |
if (groups_get_activity_groupmode($cm) == SEPARATEGROUPS) {
|
|
|
681 |
$groupusers = array_keys(groups_get_activity_shared_group_members($cm));
|
|
|
682 |
if (empty($groupusers)) {
|
|
|
683 |
return $updates;
|
|
|
684 |
}
|
|
|
685 |
list($insql, $inparams) = $DB->get_in_or_equal($groupusers, SQL_PARAMS_NAMED);
|
|
|
686 |
$select .= ' AND userid ' . $insql;
|
|
|
687 |
$params = array_merge($params, $inparams);
|
|
|
688 |
}
|
|
|
689 |
|
|
|
690 |
$updates->usersubmissions = (object) array('updated' => false);
|
|
|
691 |
$submissions = $DB->get_records_select('lti_submission', $select, $params, '', 'id');
|
|
|
692 |
if (!empty($submissions)) {
|
|
|
693 |
$updates->usersubmissions->updated = true;
|
|
|
694 |
$updates->usersubmissions->itemids = array_keys($submissions);
|
|
|
695 |
}
|
|
|
696 |
}
|
|
|
697 |
|
|
|
698 |
return $updates;
|
|
|
699 |
}
|
|
|
700 |
|
|
|
701 |
/**
|
|
|
702 |
* Get icon mapping for font-awesome.
|
|
|
703 |
*/
|
|
|
704 |
function mod_lti_get_fontawesome_icon_map() {
|
|
|
705 |
return [
|
|
|
706 |
'mod_lti:warning' => 'fa-exclamation text-warning',
|
|
|
707 |
];
|
|
|
708 |
}
|
|
|
709 |
|
|
|
710 |
/**
|
|
|
711 |
* This function receives a calendar event and returns the action associated with it, or null if there is none.
|
|
|
712 |
*
|
|
|
713 |
* This is used by block_myoverview in order to display the event appropriately. If null is returned then the event
|
|
|
714 |
* is not displayed on the block.
|
|
|
715 |
*
|
|
|
716 |
* @param calendar_event $event
|
|
|
717 |
* @param \core_calendar\action_factory $factory
|
|
|
718 |
* @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default).
|
|
|
719 |
* @return \core_calendar\local\event\entities\action_interface|null
|
|
|
720 |
*/
|
|
|
721 |
function mod_lti_core_calendar_provide_event_action(calendar_event $event,
|
|
|
722 |
\core_calendar\action_factory $factory,
|
|
|
723 |
int $userid = 0) {
|
|
|
724 |
global $USER;
|
|
|
725 |
|
|
|
726 |
if (empty($userid)) {
|
|
|
727 |
$userid = $USER->id;
|
|
|
728 |
}
|
|
|
729 |
|
|
|
730 |
$cm = get_fast_modinfo($event->courseid, $userid)->instances['lti'][$event->instance];
|
|
|
731 |
|
|
|
732 |
if (!$cm->uservisible) {
|
|
|
733 |
// The module is not visible to the user for any reason.
|
|
|
734 |
return null;
|
|
|
735 |
}
|
|
|
736 |
|
|
|
737 |
$completion = new \completion_info($cm->get_course());
|
|
|
738 |
|
|
|
739 |
$completiondata = $completion->get_data($cm, false, $userid);
|
|
|
740 |
|
|
|
741 |
if ($completiondata->completionstate != COMPLETION_INCOMPLETE) {
|
|
|
742 |
return null;
|
|
|
743 |
}
|
|
|
744 |
|
|
|
745 |
return $factory->create_instance(
|
|
|
746 |
get_string('view'),
|
|
|
747 |
new \moodle_url('/mod/lti/view.php', ['id' => $cm->id]),
|
|
|
748 |
1,
|
|
|
749 |
true
|
|
|
750 |
);
|
|
|
751 |
}
|
|
|
752 |
|
|
|
753 |
/**
|
|
|
754 |
* Extend the course navigation with an "LTI External tools" link which redirects to a list of all tools available for course use.
|
|
|
755 |
*
|
|
|
756 |
* @param settings_navigation $navigation The settings navigation object
|
|
|
757 |
* @param stdClass $course The course
|
|
|
758 |
* @param stdclass $context Course context
|
|
|
759 |
* @return void
|
|
|
760 |
*/
|
|
|
761 |
function mod_lti_extend_navigation_course($navigation, $course, $context): void {
|
|
|
762 |
if (has_capability('mod/lti:addpreconfiguredinstance', $context)) {
|
|
|
763 |
$url = new moodle_url('/mod/lti/coursetools.php', ['id' => $course->id]);
|
|
|
764 |
$settingsnode = navigation_node::create(get_string('courseexternaltools', 'mod_lti'), $url, navigation_node::TYPE_SETTING,
|
|
|
765 |
null, 'coursetools', new pix_icon('i/settings', ''));
|
|
|
766 |
$navigation->add_node($settingsnode);
|
|
|
767 |
}
|
|
|
768 |
}
|