Tool base URLs don't need to start with http(s)
[moodle.git] / mod / lti / lib.php
CommitLineData
b7e436b0
CS
1<?php\r
2// This file is part of BasicLTI4Moodle\r
3//\r
4// BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability)\r
5// consumer for Moodle 1.9 and Moodle 2.0. BasicLTI is a IMS Standard that allows web\r
6// based learning tools to be easily integrated in LMS as native ones. The IMS BasicLTI\r
7// specification is part of the IMS standard Common Cartridge 1.1 Sakai and other main LMS\r
8// are already supporting or going to support BasicLTI. This project Implements the consumer\r
9// for Moodle. Moodle is a Free Open source Learning Management System by Martin Dougiamas.\r
10// BasicLTI4Moodle is a project iniciated and leaded by Ludo(Marc Alier) and Jordi Piguillem\r
11// at the GESSI research group at UPC.\r
12// SimpleLTI consumer for Moodle is an implementation of the early specification of LTI\r
13// by Charles Severance (Dr Chuck) htp://dr-chuck.com , developed by Jordi Piguillem in a\r
14// Google Summer of Code 2008 project co-mentored by Charles Severance and Marc Alier.\r
15//\r
16// BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis\r
17// of the Universitat Politecnica de Catalunya http://www.upc.edu\r
18// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu\r
19//\r
20// Moodle is free software: you can redistribute it and/or modify\r
21// it under the terms of the GNU General Public License as published by\r
22// the Free Software Foundation, either version 3 of the License, or\r
23// (at your option) any later version.\r
24//\r
25// Moodle is distributed in the hope that it will be useful,\r
26// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
27// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
28// GNU General Public License for more details.\r
29//\r
30// You should have received a copy of the GNU General Public License\r
31// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\r
32\r
33/**\r
34 * This file contains a library of functions and constants for the\r
35 * BasicLTI module\r
36 *\r
73300339 37 * @package lti\r
b7e436b0
CS
38 * @copyright 2009 Marc Alier, Jordi Piguillem, Nikolas Galanis\r
39 * marc.alier@upc.edu\r
40 * @copyright 2009 Universitat Politecnica de Catalunya http://www.upc.edu\r
41 *\r
42 * @author Marc Alier\r
43 * @author Jordi Piguillem\r
44 * @author Nikolas Galanis\r
45 *\r
46 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\r
47 */\r
48\r
49defined('MOODLE_INTERNAL') || die;\r
50\r
73300339 51require_once($CFG->dirroot.'/mod/lti/locallib.php');\r
b7e436b0
CS
52\r
53/**\r
54 * List of features supported in URL module\r
55 * @param string $feature FEATURE_xx constant for requested feature\r
56 * @return mixed True if module supports feature, false if not, null if doesn't know\r
57 */\r
73300339 58function lti_supports($feature) {\r
b7e436b0
CS
59 switch($feature) {\r
60 case FEATURE_GROUPS: return false;\r
61 case FEATURE_GROUPINGS: return false;\r
62 case FEATURE_GROUPMEMBERSONLY: return true;\r
63 case FEATURE_MOD_INTRO: return true;\r
64 case FEATURE_COMPLETION_TRACKS_VIEWS: return true;\r
65 case FEATURE_GRADE_HAS_GRADE: return true;\r
66 case FEATURE_GRADE_OUTCOMES: return true;\r
67 case FEATURE_BACKUP_MOODLE2: return true;\r
68\r
69 default: return null;\r
70 }\r
71}\r
72\r
73/**\r
74 * Given an object containing all the necessary data,\r
75 * (defined by the form in mod.html) this function\r
76 * will create a new instance and return the id number\r
77 * of the new instance.\r
78 *\r
79 * @param object $instance An object from the form in mod.html\r
80 * @return int The id of the newly inserted basiclti record\r
81 **/\r
73300339 82function lti_add_instance($formdata) {\r
b7e436b0 83 global $DB;\r
285f8250
CS
84 $formdata->timecreated = time();\r
85 $formdata->timemodified = $formdata->timecreated;\r
86 //$basiclti->placementsecret = uniqid('', true);\r
87 //$basiclti->timeplacementsecret = time();\r
b7e436b0 88\r
73300339 89 $id = $DB->insert_record("lti", $formdata);\r
b7e436b0 90\r
285f8250 91 if ($formdata->instructorchoiceacceptgrades == 1) {\r
73300339 92 $basiclti = $DB->get_record('lti', array('id'=>$id));\r
285f8250
CS
93 $basiclti->cmidnumber = $formdata->cmidnumber;\r
94 \r
73300339 95 lti_grade_item_update($basiclti);\r
b7e436b0
CS
96 }\r
97\r
98 return $id;\r
99}\r
100\r
101/**\r
102 * Given an object containing all the necessary data,\r
103 * (defined by the form in mod.html) this function\r
104 * will update an existing instance with new data.\r
105 *\r
106 * @param object $instance An object from the form in mod.html\r
107 * @return boolean Success/Fail\r
108 **/\r
73300339 109function lti_update_instance($formdata) {\r
b7e436b0
CS
110 global $DB;\r
111\r
285f8250
CS
112 $formdata->timemodified = time();\r
113 $formdata->id = $formdata->instance;\r
b7e436b0 114\r
d97d72aa
CS
115 if(!isset($formdata->showtitle)){\r
116 $formdata->showtitle = 0;\r
117 }\r
118 \r
119 if(!isset($formdata->showdescription)){\r
120 $formdata->showdescription = 0;\r
121 }\r
122 \r
285f8250 123 if ($formdata->instructorchoiceacceptgrades == 1) {\r
73300339 124 $basicltirec = $DB->get_record("lti", array("id" => $formdata->id));\r
285f8250
CS
125 $basicltirec->cmidnumber = $formdata->cmidnumber;\r
126 \r
73300339 127 lti_grade_item_update($basicltirec);\r
b7e436b0 128 } else {\r
73300339 129 lti_grade_item_delete($formdata);\r
b7e436b0
CS
130 }\r
131\r
73300339 132 return $DB->update_record("lti", $formdata);\r
b7e436b0
CS
133}\r
134\r
135/**\r
136 * Given an ID of an instance of this module,\r
137 * this function will permanently delete the instance\r
138 * and any data that depends on it.\r
139 *\r
140 * @param int $id Id of the module instance\r
141 * @return boolean Success/Failure\r
142 **/\r
73300339 143function lti_delete_instance($id) {\r
b7e436b0
CS
144 global $DB;\r
145\r
73300339 146 if (! $basiclti = $DB->get_record("lti", array("id" => $id))) {\r
b7e436b0
CS
147 return false;\r
148 }\r
149\r
150 $result = true;\r
151\r
152 # Delete any dependent records here #\r
73300339 153 lti_grade_item_delete($basiclti);\r
b7e436b0 154\r
73300339 155 return $DB->delete_records("lti", array("id" => $basiclti->id));\r
b7e436b0
CS
156}\r
157\r
158/**\r
159 * Return a small object with summary information about what a\r
160 * user has done with a given particular instance of this module\r
161 * Used for user activity reports.\r
162 * $return->time = the time they did it\r
163 * $return->info = a short text description\r
164 *\r
165 * @return null\r
166 * @TODO: implement this moodle function (if needed)\r
167 **/\r
73300339 168function lti_user_outline($course, $user, $mod, $basiclti) {\r
b7e436b0
CS
169 return $return;\r
170}\r
171\r
172/**\r
173 * Print a detailed representation of what a user has done with\r
174 * a given particular instance of this module, for user activity reports.\r
175 *\r
176 * @return boolean\r
177 * @TODO: implement this moodle function (if needed)\r
178 **/\r
73300339 179function lti_user_complete($course, $user, $mod, $basiclti) {\r
b7e436b0
CS
180 return true;\r
181}\r
182\r
183/**\r
184 * Given a course and a time, this module should find recent activity\r
185 * that has occurred in basiclti activities and print it out.\r
186 * Return true if there was output, or false is there was none.\r
187 *\r
188 * @uses $CFG\r
189 * @return boolean\r
190 * @TODO: implement this moodle function\r
191 **/\r
73300339 192function lti_print_recent_activity($course, $isteacher, $timestart) {\r
b7e436b0
CS
193 return false; // True if anything was printed, otherwise false\r
194}\r
195\r
196/**\r
197 * Function to be run periodically according to the moodle cron\r
198 * This function searches for things that need to be done, such\r
199 * as sending out mail, toggling flags etc ...\r
200 *\r
201 * @uses $CFG\r
202 * @return boolean\r
203 **/\r
73300339 204function lti_cron () {\r
b7e436b0
CS
205 return true;\r
206}\r
207\r
208/**\r
209 * Must return an array of grades for a given instance of this module,\r
210 * indexed by user. It also returns a maximum allowed grade.\r
211 *\r
212 * Example:\r
213 * $return->grades = array of grades;\r
214 * $return->maxgrade = maximum allowed grade;\r
215 *\r
216 * return $return;\r
217 *\r
218 * @param int $basicltiid ID of an instance of this module\r
219 * @return mixed Null or object with an array of grades and with the maximum grade\r
220 *\r
221 * @TODO: implement this moodle function (if needed)\r
222 **/\r
73300339 223function lti_grades($basicltiid) {\r
b7e436b0
CS
224 return null;\r
225}\r
226\r
227/**\r
228 * Must return an array of user records (all data) who are participants\r
229 * for a given instance of basiclti. Must include every user involved\r
230 * in the instance, independient of his role (student, teacher, admin...)\r
231 * See other modules as example.\r
232 *\r
233 * @param int $basicltiid ID of an instance of this module\r
234 * @return mixed boolean/array of students\r
235 *\r
236 * @TODO: implement this moodle function\r
237 **/\r
73300339 238function lti_get_participants($basicltiid) {\r
b7e436b0
CS
239 return false;\r
240}\r
241\r
242/**\r
243 * This function returns if a scale is being used by one basiclti\r
244 * it it has support for grading and scales. Commented code should be\r
245 * modified if necessary. See forum, glossary or journal modules\r
246 * as reference.\r
247 *\r
248 * @param int $basicltiid ID of an instance of this module\r
249 * @return mixed\r
250 *\r
251 * @TODO: implement this moodle function (if needed)\r
252 **/\r
73300339 253function lti_scale_used ($basicltiid, $scaleid) {\r
b7e436b0
CS
254 $return = false;\r
255\r
256 //$rec = get_record("basiclti","id","$basicltiid","scale","-$scaleid");\r
257 //\r
258 //if (!empty($rec) && !empty($scaleid)) {\r
259 // $return = true;\r
260 //}\r
261\r
262 return $return;\r
263}\r
264\r
265/**\r
266 * Checks if scale is being used by any instance of basiclti.\r
267 * This function was added in 1.9\r
268 *\r
269 * This is used to find out if scale used anywhere\r
270 * @param $scaleid int\r
271 * @return boolean True if the scale is used by any basiclti\r
272 *\r
273 */\r
73300339 274function lti_scale_used_anywhere($scaleid) {\r
b7e436b0
CS
275 global $DB;\r
276\r
73300339 277 if ($scaleid and $DB->record_exists('lti', array('grade' => -$scaleid))) {\r
b7e436b0
CS
278 return true;\r
279 } else {\r
280 return false;\r
281 }\r
282}\r
283\r
284/**\r
285 * Execute post-install custom actions for the module\r
286 * This function was added in 1.9\r
287 *\r
288 * @return boolean true if success, false on error\r
289 */\r
73300339 290function lti_install() {\r
b7e436b0
CS
291 return true;\r
292}\r
293\r
294/**\r
295 * Execute post-uninstall custom actions for the module\r
296 * This function was added in 1.9\r
297 *\r
298 * @return boolean true if success, false on error\r
299 */\r
73300339 300function lti_uninstall() {\r
b7e436b0
CS
301 return true;\r
302}\r
303\r
304/**\r
305 * Returns available Basic LTI types\r
306 *\r
307 * @return array of basicLTI types\r
308 */\r
73300339 309function lti_get_lti_types() {\r
b7e436b0
CS
310 global $DB;\r
311\r
73300339 312 return $DB->get_records('lti_types');\r
b7e436b0
CS
313}\r
314\r
315/**\r
316 * Returns Basic LTI types configuration\r
317 *\r
318 * @return array of basicLTI types\r
319 */\r
73300339 320/*function lti_get_types() {\r
b7e436b0
CS
321 $types = array();\r
322\r
73300339 323 $basicltitypes = lti_get_lti_types();\r
b7e436b0
CS
324 if (!empty($basicltitypes)) {\r
325 foreach ($basicltitypes as $basicltitype) {\r
73300339 326 $ltitypesconfig = lti_get_type_config($basicltitype->id);\r
b7e436b0
CS
327\r
328 $modclass = MOD_CLASS_ACTIVITY;\r
329 if (isset($ltitypesconfig['module_class_type'])) {\r
330 if ($ltitypesconfig['module_class_type']=='1') {\r
331 $modclass = MOD_CLASS_RESOURCE;\r
332 }\r
333 }\r
334\r
335 $type = new object();\r
336 $type->modclass = $modclass;\r
73300339 337 $type->type = 'lti&amp;type='.urlencode($basicltitype->rawname);\r
b7e436b0
CS
338 $type->typestr = $basicltitype->name;\r
339 $types[] = $type;\r
340 }\r
341 }\r
342\r
343 return $types;\r
285f8250 344}*/\r
b7e436b0
CS
345\r
346//////////////////////////////////////////////////////////////////////////////////////\r
347/// Any other basiclti functions go here. Each of them must have a name that\r
348/// starts with basiclti_\r
349/// Remember (see note in first lines) that, if this section grows, it's HIGHLY\r
350/// recommended to move all funcions below to a new "localib.php" file.\r
351\r
352///**\r
353// *\r
354// */\r
355//function process_outcomes($userid, $course, $basiclti) {\r
356// global $CFG, $USER;\r
357//\r
358// if (empty($CFG->enableoutcomes)) {\r
359// return;\r
360// }\r
361//\r
362// require_once($CFG->libdir.'/gradelib.php');\r
363//\r
364// if (!$formdata = data_submitted() or !confirm_sesskey()) {\r
365// return;\r
366// }\r
367//\r
368// $data = array();\r
369// $grading_info = grade_get_grades($course->id, 'mod', 'basiclti', $basiclti->id, $userid);\r
370//\r
371// if (!empty($grading_info->outcomes)) {\r
372// foreach ($grading_info->outcomes as $n => $old) {\r
373// $name = 'outcome_'.$n;\r
374// if (isset($formdata->{$name}[$userid]) and $old->grades[$userid]->grade != $formdata->{$name}[$userid]) {\r
375// $data[$n] = $formdata->{$name}[$userid];\r
376// }\r
377// }\r
378// }\r
379// if (count($data) > 0) {\r
380// grade_update_outcomes('mod/basiclti', $course->id, 'mod', 'basiclti', $basiclti->id, $userid, $data);\r
381// }\r
382//\r
383//}\r
384\r
385/**\r
386 * Top-level function for handling of submissions called by submissions.php\r
387 *\r
388 * This is for handling the teacher interaction with the grading interface\r
389 *\r
390 * @global object\r
391 * @param string $mode Specifies the kind of teacher interaction taking place\r
392 */\r
73300339 393function lti_submissions($cm, $course, $basiclti, $mode) {\r
b7e436b0
CS
394 ///The main switch is changed to facilitate\r
395 ///1) Batch fast grading\r
396 ///2) Skip to the next one on the popup\r
397 ///3) Save and Skip to the next one on the popup\r
398\r
399 //make user global so we can use the id\r
400 global $USER, $OUTPUT, $DB;\r
401\r
402 $mailinfo = optional_param('mailinfo', null, PARAM_BOOL);\r
403\r
404 if (optional_param('next', null, PARAM_BOOL)) {\r
405 $mode='next';\r
406 }\r
407 if (optional_param('saveandnext', null, PARAM_BOOL)) {\r
408 $mode='saveandnext';\r
409 }\r
410\r
411 if (is_null($mailinfo)) {\r
412 if (optional_param('sesskey', null, PARAM_BOOL)) {\r
73300339 413 set_user_preference('lti_mailinfo', $mailinfo);\r
b7e436b0 414 } else {\r
73300339 415 $mailinfo = get_user_preferences('lti_mailinfo', 0);\r
b7e436b0
CS
416 }\r
417 } else {\r
73300339 418 set_user_preference('lti_mailinfo', $mailinfo);\r
b7e436b0
CS
419 }\r
420\r
421 switch ($mode) {\r
422 case 'grade': // We are in a main window grading\r
423 if ($submission = process_feedback()) {\r
73300339 424 lti_display_submissions($cm, $course, $basiclti, get_string('changessaved'));\r
b7e436b0 425 } else {\r
73300339 426 lti_display_submissions($cm, $course, $basiclti);\r
b7e436b0
CS
427 }\r
428 break;\r
429\r
430 case 'single': // We are in a main window displaying one submission\r
431 if ($submission = process_feedback()) {\r
73300339 432 lti_display_submissions($cm, $course, $basiclti, get_string('changessaved'));\r
b7e436b0
CS
433 } else {\r
434 display_submission();\r
435 }\r
436 break;\r
437\r
438 case 'all': // Main window, display everything\r
73300339 439 lti_display_submissions($cm, $course, $basiclti);\r
b7e436b0
CS
440 break;\r
441\r
442 case 'fastgrade':\r
443 /// do the fast grading stuff - this process should work for all 3 subclasses\r
444 $grading = false;\r
445 $commenting = false;\r
446 $col = false;\r
447 if (isset($_POST['submissioncomment'])) {\r
448 $col = 'submissioncomment';\r
449 $commenting = true;\r
450 }\r
451 if (isset($_POST['menu'])) {\r
452 $col = 'menu';\r
453 $grading = true;\r
454 }\r
455 if (!$col) {\r
456 //both submissioncomment and grade columns collapsed..\r
73300339 457 lti_display_submissions($cm, $course, $basiclti);\r
b7e436b0
CS
458 break;\r
459 }\r
460\r
461 foreach ($_POST[$col] as $id => $unusedvalue) {\r
462\r
463 $id = (int)$id; //clean parameter name\r
464\r
465 // Get grade item\r
466 $gradeitem = $DB->get_record('grade_items', array('courseid' => $cm->course, 'iteminstance' => $cm->instance));\r
467\r
468 // Get grade\r
469 $gradeentry = $DB->get_record('grade_grades', array('userid' => $id, 'itemid' => $gradeitem->id));\r
470\r
471 $grade = $_POST['menu'][$id];\r
472 $feedback = trim($_POST['submissioncomment'][$id]);\r
473\r
474 if ((!$gradeentry) && (($grade != '-1') || ($feedback != ''))) {\r
475 $newsubmission = true;\r
476 } else {\r
477 $newsubmission = false;\r
478 }\r
479\r
480 //for fast grade, we need to check if any changes take place\r
481 $updatedb = false;\r
482\r
483 if ($gradeentry) {\r
484 if ($grading) {\r
485 $grade = $_POST['menu'][$id];\r
486 $updatedb = $updatedb || (($gradeentry->rawgrade != $grade) && ($gradeentry->rawgrade != '-1'));\r
487 if ($grade != '-1') {\r
488 $gradeentry->rawgrade = $grade;\r
489 $gradeentry->finalgrade = $grade;\r
490 } else {\r
491 $gradeentry->rawgrade = null;\r
492 $gradeentry->finalgrade = null;\r
493 }\r
494 } else {\r
495 if (!$newsubmission) {\r
496 unset($gradeentry->rawgrade); // Don't need to update this.\r
497 }\r
498 }\r
499\r
500 if ($commenting) {\r
501 $commentvalue = trim($_POST['submissioncomment'][$id]);\r
502 $updatedb = $updatedb || ($gradeentry->feedback != $commentvalue);\r
503 // Special case\r
504 if (($gradeentry->feedback == null) && ($commentvalue == "")) {\r
505 unset($gradeentry->feedback);\r
506 }\r
507 $gradeentry->feedback = $commentvalue;\r
508 } else {\r
509 unset($gradeentry->feedback); // Don't need to update this.\r
510 }\r
511\r
512 } else { // No previous grade entry found\r
513 if ($newsubmission) {\r
514 if ($grade != '-1') {\r
515 $gradeentry->rawgrade = $grade;\r
516 $updatedb = true;\r
517 }\r
518 if ($feedback != '') {\r
519 $gradeentry->feedback = $feedback;\r
520 $updatedb = true;\r
521 }\r
522 }\r
523 }\r
524\r
525 $gradeentry->usermodified = $USER->id;\r
526 if (!$gradeentry->timecreated) {\r
527 $gradeentry->timecreated = time();\r
528 }\r
529 $gradeentry->timemodified = time();\r
530\r
531 //if it is not an update, we don't change the last modified time etc.\r
532 //this will also not write into database if no submissioncomment and grade is entered.\r
533 if ($updatedb) {\r
534 if ($gradeentry->rawgrade == '-1') {\r
535 $gradeentry->rawgrade = null;\r
536 }\r
537\r
538 if ($newsubmission) {\r
539 if (!isset($gradeentry->feedback)) {\r
540 $gradeentry->feedback = '';\r
541 }\r
542 $gradeentry->itemid = $gradeitem->id;\r
543 $gradeentry->userid = $id;\r
544 $sid = $DB->insert_record("grade_grades", $gradeentry);\r
545 $gradeentry->id = $sid;\r
546 } else {\r
547 $DB->update_record("grade_grades", $gradeentry);\r
548 }\r
549\r
550 //add to log only if updating\r
73300339 551 add_to_log($course->id, 'lti', 'update grades',\r
b7e436b0
CS
552 'submissions.php?id='.$cm->id.'&user='.$USER->id,\r
553 $USER->id, $cm->id);\r
554 }\r
555\r
556 }\r
557\r
558 $message = $OUTPUT->notification(get_string('changessaved'), 'notifysuccess');\r
559\r
73300339 560 lti_display_submissions($cm, $course, $basiclti, $message);\r
b7e436b0
CS
561 break;\r
562\r
563 case 'saveandnext':\r
564 ///We are in pop up. save the current one and go to the next one.\r
565 //first we save the current changes\r
566 if ($submission = process_feedback()) {\r
567 //print_heading(get_string('changessaved'));\r
568 //$extra_javascript = $this->update_main_listing($submission);\r
569 }\r
570\r
571 case 'next':\r
572 /// We are currently in pop up, but we want to skip to next one without saving.\r
573 /// This turns out to be similar to a single case\r
574 /// The URL used is for the next submission.\r
575 $offset = required_param('offset', PARAM_INT);\r
576 $nextid = required_param('nextid', PARAM_INT);\r
577 $id = required_param('id', PARAM_INT);\r
578 $offset = (int)$offset+1;\r
579 //$this->display_submission($offset+1 , $nextid);\r
580 redirect('submissions.php?id='.$id.'&userid='. $nextid . '&mode=single&offset='.$offset);\r
581 break;\r
582\r
583 case 'singlenosave':\r
584 display_submission();\r
585 break;\r
586\r
587 default:\r
588 echo "Critical error. Something is seriously wrong!!";\r
589 break;\r
590 }\r
591}\r
592\r
593/**\r
594 * Display all the submissions ready for grading\r
595 *\r
596 * @global object\r
597 * @global object\r
598 * @global object\r
599 * @global object\r
600 * @param string $message\r
601 * @return bool|void\r
602 */\r
73300339 603function lti_display_submissions($cm, $course, $basiclti, $message='') {\r
b7e436b0
CS
604 global $CFG, $DB, $OUTPUT, $PAGE;\r
605 require_once($CFG->libdir.'/gradelib.php');\r
606\r
607 /* first we check to see if the form has just been submitted\r
608 * to request user_preference updates\r
609 */\r
610 $updatepref = optional_param('updatepref', 0, PARAM_INT);\r
611\r
612 if (isset($_POST['updatepref'])) {\r
613 $perpage = optional_param('perpage', 10, PARAM_INT);\r
614 $perpage = ($perpage <= 0) ? 10 : $perpage;\r
615 $filter = optional_param('filter', 0, PARAM_INT);\r
73300339
CS
616 set_user_preference('lti_perpage', $perpage);\r
617 set_user_preference('lti_quickgrade', optional_param('quickgrade', 0, PARAM_BOOL));\r
618 set_user_preference('lti_filter', $filter);\r
b7e436b0
CS
619 }\r
620\r
621 /* next we get perpage and quickgrade (allow quick grade) params\r
622 * from database\r
623 */\r
73300339
CS
624 $perpage = get_user_preferences('lti_perpage', 10);\r
625 $quickgrade = get_user_preferences('lti_quickgrade', 0);\r
626 $filter = get_user_preferences('lti_filter', 0);\r
627 $grading_info = grade_get_grades($course->id, 'mod', 'lti', $basiclti->id);\r
b7e436b0
CS
628\r
629 if (!empty($CFG->enableoutcomes) and !empty($grading_info->outcomes)) {\r
630 $uses_outcomes = true;\r
631 } else {\r
632 $uses_outcomes = false;\r
633 }\r
634\r
635 $page = optional_param('page', 0, PARAM_INT);\r
73300339 636 $strsaveallfeedback = get_string('saveallfeedback', 'lti');\r
b7e436b0
CS
637\r
638 $tabindex = 1; //tabindex for quick grading tabbing; Not working for dropdowns yet\r
73300339 639 add_to_log($course->id, 'lti', 'view submission', 'submissions.php?id='.$cm->id, $basiclti->id, $cm->id);\r
b7e436b0
CS
640\r
641 $PAGE->set_title(format_string($basiclti->name, true));\r
642 $PAGE->set_heading($course->fullname);\r
643 echo $OUTPUT->header();\r
644\r
645 echo '<div class="usersubmissions">';\r
646\r
647 //hook to allow plagiarism plugins to update status/print links.\r
648 plagiarism_update_status($course, $cm);\r
649\r
650 /// Print quickgrade form around the table\r
651 if ($quickgrade) {\r
652 $formattrs = array();\r
73300339 653 $formattrs['action'] = new moodle_url('/mod/lti/submissions.php');\r
b7e436b0
CS
654 $formattrs['id'] = 'fastg';\r
655 $formattrs['method'] = 'post';\r
656\r
657 echo html_writer::start_tag('form', $formattrs);\r
658 echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'id', 'value'=> $cm->id));\r
659 echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'mode', 'value'=> 'fastgrade'));\r
660 echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'page', 'value'=> $page));\r
661 echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'sesskey', 'value'=> sesskey()));\r
662 }\r
663\r
664 $course_context = get_context_instance(CONTEXT_COURSE, $course->id);\r
665 if (has_capability('gradereport/grader:view', $course_context) && has_capability('moodle/grade:viewall', $course_context)) {\r
666 echo '<div class="allcoursegrades"><a href="' . $CFG->wwwroot . '/grade/report/grader/index.php?id=' . $course->id . '">'\r
667 . get_string('seeallcoursegrades', 'grades') . '</a></div>';\r
668 }\r
669\r
670 if (!empty($message)) {\r
671 echo $message; // display messages here if any\r
672 }\r
673\r
674 $context = get_context_instance(CONTEXT_MODULE, $cm->id);\r
675\r
676/// Check to see if groups are being used in this tool\r
677\r
678 /// find out current groups mode\r
679 $groupmode = groups_get_activity_groupmode($cm);\r
680 $currentgroup = groups_get_activity_group($cm, true);\r
73300339 681 groups_print_activity_menu($cm, $CFG->wwwroot . '/mod/lti/submissions.php?id=' . $cm->id);\r
b7e436b0
CS
682\r
683 /// Get all ppl that are allowed to submit tools\r
73300339 684 list($esql, $params) = get_enrolled_sql($context, 'mod/lti:view', $currentgroup);\r
b7e436b0
CS
685\r
686 $sql = "SELECT u.id FROM {user} u ".\r
687 "LEFT JOIN ($esql) eu ON eu.id=u.id ".\r
688 "WHERE u.deleted = 0 AND eu.id=u.id ";\r
689\r
690 $users = $DB->get_records_sql($sql, $params);\r
691 if (!empty($users)) {\r
692 $users = array_keys($users);\r
693 }\r
694\r
695 // if groupmembersonly used, remove users who are not in any group\r
696 if ($users and !empty($CFG->enablegroupmembersonly) and $cm->groupmembersonly) {\r
697 if ($groupingusers = groups_get_grouping_members($cm->groupingid, 'u.id', 'u.id')) {\r
698 $users = array_intersect($users, array_keys($groupingusers));\r
699 }\r
700 }\r
701\r
702 $tablecolumns = array('picture', 'fullname', 'grade', 'submissioncomment', 'timemodified', 'timemarked', 'status', 'finalgrade');\r
703 if ($uses_outcomes) {\r
704 $tablecolumns[] = 'outcome'; // no sorting based on outcomes column\r
705 }\r
706\r
707 $tableheaders = array('',\r
708 get_string('fullname'),\r
709 get_string('grade'),\r
73300339
CS
710 get_string('comment', 'lti'),\r
711 get_string('lastmodified').' ('.get_string('submission', 'lti').')',\r
b7e436b0
CS
712 get_string('lastmodified').' ('.get_string('grade').')',\r
713 get_string('status'),\r
714 get_string('finalgrade', 'grades'));\r
715 if ($uses_outcomes) {\r
716 $tableheaders[] = get_string('outcome', 'grades');\r
717 }\r
718\r
719 require_once($CFG->libdir.'/tablelib.php');\r
73300339 720 $table = new flexible_table('mod-lti-submissions');\r
b7e436b0
CS
721\r
722 $table->define_columns($tablecolumns);\r
723 $table->define_headers($tableheaders);\r
73300339 724 $table->define_baseurl($CFG->wwwroot.'/mod/lti/submissions.php?id='.$cm->id.'&amp;currentgroup='.$currentgroup);\r
b7e436b0
CS
725\r
726 $table->sortable(true, 'lastname');//sorted by lastname by default\r
727 $table->collapsible(true);\r
728 $table->initialbars(true);\r
729\r
730 $table->column_suppress('picture');\r
731 $table->column_suppress('fullname');\r
732\r
733 $table->column_class('picture', 'picture');\r
734 $table->column_class('fullname', 'fullname');\r
735 $table->column_class('grade', 'grade');\r
736 $table->column_class('submissioncomment', 'comment');\r
737 $table->column_class('timemodified', 'timemodified');\r
738 $table->column_class('timemarked', 'timemarked');\r
739 $table->column_class('status', 'status');\r
740 $table->column_class('finalgrade', 'finalgrade');\r
741 if ($uses_outcomes) {\r
742 $table->column_class('outcome', 'outcome');\r
743 }\r
744\r
745 $table->set_attribute('cellspacing', '0');\r
746 $table->set_attribute('id', 'attempts');\r
747 $table->set_attribute('class', 'submissions');\r
748 $table->set_attribute('width', '100%');\r
749\r
750 $table->no_sorting('finalgrade');\r
751 $table->no_sorting('outcome');\r
752\r
753 // Start working -- this is necessary as soon as the niceties are over\r
754 $table->setup();\r
755\r
756 if (empty($users)) {\r
73300339 757 echo $OUTPUT->heading(get_string('noviewusers', 'lti'));\r
b7e436b0
CS
758 echo '</div>';\r
759 return true;\r
760 }\r
761\r
762 /// Construct the SQL\r
763 list($where, $params) = $table->get_sql_where();\r
764 if ($where) {\r
765 $where .= ' AND ';\r
766 }\r
767\r
768 if ($sort = $table->get_sql_sort()) {\r
769 $sort = ' ORDER BY '.$sort;\r
770 }\r
771\r
772 $ufields = user_picture::fields('u');\r
773\r
774 $gradeitem = $DB->get_record('grade_items', array('courseid' => $cm->course, 'iteminstance' => $cm->instance));\r
775\r
776 $select = "SELECT $ufields,\r
777 g.rawgrade, g.feedback,\r
778 g.timemodified, g.timecreated ";\r
779\r
780 $sql = 'FROM {user} u'.\r
781 ' LEFT JOIN {grade_grades} g ON u.id = g.userid AND g.itemid = '.$gradeitem->id.\r
782 ' LEFT JOIN {grade_items} i ON g.itemid = i.id'.\r
783 ' AND i.iteminstance = '.$basiclti->id.\r
784 ' WHERE '.$where.'u.id IN ('.implode(',', $users).') ';\r
785\r
786 $ausers = $DB->get_records_sql($select.$sql.$sort, $params, $table->get_page_start(), $table->get_page_size());\r
787\r
788 $table->pagesize($perpage, count($users));\r
789\r
790 ///offset used to calculate index of student in that particular query, needed for the pop up to know who's next\r
791 $offset = $page * $perpage;\r
792 $strupdate = get_string('update');\r
793 $strgrade = get_string('grade');\r
794 $grademenu = make_grades_menu($basiclti->grade);\r
795 if ($ausers !== false) {\r
73300339 796 $grading_info = grade_get_grades($course->id, 'mod', 'lti', $basiclti->id, array_keys($ausers));\r
b7e436b0
CS
797 $endposition = $offset + $perpage;\r
798 $currentposition = 0;\r
799 foreach ($ausers as $auser) {\r
800\r
801 if ($auser->timemodified > 0) {\r
802 $timemodified = '<div id="ts'.$auser->id.'">'.userdate($auser->timemodified).'</div>';\r
803 } else {\r
804 $timemodified = '<div id="ts'.$auser->id.'">&nbsp;</div>';\r
805 }\r
806 if ($auser->timecreated > 0) {\r
807 $timecreated = '<div id="ts'.$auser->id.'">'.userdate($auser->timecreated).'</div>';\r
808 } else {\r
809 $timecreated = '<div id="ts'.$auser->id.'">&nbsp;</div>';\r
810 }\r
811\r
812 if ($currentposition == $offset && $offset < $endposition) {\r
813 $final_grade = $grading_info->items[0]->grades[$auser->id];\r
814 $grademax = $grading_info->items[0]->grademax;\r
815 $final_grade->formatted_grade = round($final_grade->grade, 2) .' / ' . round($grademax, 2);\r
816 $locked_overridden = 'locked';\r
817 if ($final_grade->overridden) {\r
818 $locked_overridden = 'overridden';\r
819 }\r
820\r
821 /// Calculate user status\r
822 $picture = $OUTPUT->user_picture($auser);\r
823\r
824 $studentmodified = '<div id="ts'.$auser->id.'">&nbsp;</div>';\r
825 $teachermodified = '<div id="tt'.$auser->id.'">&nbsp;</div>';\r
826 $status = '<div id="st'.$auser->id.'">&nbsp;</div>';\r
827\r
828 if ($final_grade->locked or $final_grade->overridden) {\r
829 $grade = '<div id="g'.$auser->id.'">'.$final_grade->formatted_grade . '</div>';\r
830 } else if ($quickgrade) { // allow editing\r
831 $attributes = array();\r
832 $attributes['tabindex'] = $tabindex++;\r
833 if ($auser->rawgrade != "") {\r
834 $menu = html_writer::select(make_grades_menu($basiclti->grade), 'menu['.$auser->id.']', round($auser->rawgrade, 0), array(-1=>get_string('nograde')), $attributes);\r
835 } else {\r
836 $menu = html_writer::select(make_grades_menu($basiclti->grade), 'menu['.$auser->id.']', -1, array(-1=>get_string('nograde')), $attributes);\r
837 }\r
838 $grade = '<div id="g'.$auser->id.'">'.$menu.'</div>';\r
839 } else if ($final_grade->grade) {\r
840 if ($auser->rawgrade != "") {\r
841 $grade = '<div id="g'.$auser->id.'">'.$final_grade->formatted_grade.'</div>';\r
842 } else {\r
843 $grade = '<div id="g'.$auser->id.'">-1</div>';\r
844 }\r
845\r
846 } else {\r
847 $grade = '<div id="g'.$auser->id.'">No Grade</div>';\r
848 }\r
849\r
850 if ($final_grade->locked or $final_grade->overridden) {\r
851 $comment = '<div id="com'.$auser->id.'">'.$final_grade->str_feedback.'</div>';\r
852 } else if ($quickgrade) {\r
853 $comment = '<div id="com'.$auser->id.'">'\r
854 . '<textarea tabindex="'.$tabindex++.'" name="submissioncomment['.$auser->id.']" id="submissioncomment'\r
855 . $auser->id.'" rows="2" cols="20">'.($auser->feedback).'</textarea></div>';\r
856 } else {\r
857 $comment = '<div id="com'.$auser->id.'">'.shorten_text(strip_tags($auser->feedback), 15).'</div>';\r
858 }\r
859\r
860 if (empty($auser->status)) { /// Confirm we have exclusively 0 or 1\r
861 $auser->status = 0;\r
862 } else {\r
863 $auser->status = 1;\r
864 }\r
865\r
866 $buttontext = ($auser->status == 1) ? $strupdate : $strgrade;\r
867\r
868 ///No more buttons, we use popups ;-).\r
73300339 869 $popup_url = '/mod/lti/submissions.php?id='.$cm->id\r
b7e436b0
CS
870 . '&amp;userid='.$auser->id.'&amp;mode=single'.'&amp;filter='.$filter.'&amp;offset='.$offset++;\r
871\r
872 $button = $OUTPUT->action_link($popup_url, $buttontext);\r
873\r
874 $status = '<div id="up'.$auser->id.'" class="s'.$auser->status.'">'.$button.'</div>';\r
875\r
876 $finalgrade = '<span id="finalgrade_'.$auser->id.'">'.$final_grade->str_grade.'</span>';\r
877\r
878 $outcomes = '';\r
879\r
880 if ($uses_outcomes) {\r
881\r
882 foreach ($grading_info->outcomes as $n => $outcome) {\r
883 $outcomes .= '<div class="outcome"><label>'.$outcome->name.'</label>';\r
884 $options = make_grades_menu(-$outcome->scaleid);\r
885\r
886 if ($outcome->grades[$auser->id]->locked or !$quickgrade) {\r
887 $options[0] = get_string('nooutcome', 'grades');\r
888 $outcomes .= ': <span id="outcome_'.$n.'_'.$auser->id.'">'.$options[$outcome->grades[$auser->id]->grade].'</span>';\r
889 } else {\r
890 $attributes = array();\r
891 $attributes['tabindex'] = $tabindex++;\r
892 $attributes['id'] = 'outcome_'.$n.'_'.$auser->id;\r
893 $outcomes .= ' '.html_writer::select($options, 'outcome_'.$n.'['.$auser->id.']', $outcome->grades[$auser->id]->grade, array(0=>get_string('nooutcome', 'grades')), $attributes);\r
894 }\r
895 $outcomes .= '</div>';\r
896 }\r
897 }\r
898\r
899 $userlink = '<a href="' . $CFG->wwwroot . '/user/view.php?id=' . $auser->id . '&amp;course=' . $course->id . '">' . fullname($auser, has_capability('moodle/site:viewfullnames', $context)) . '</a>';\r
900 $row = array($picture, $userlink, $grade, $comment, $timemodified, $timecreated, $status, $finalgrade);\r
901 if ($uses_outcomes) {\r
902 $row[] = $outcomes;\r
903 }\r
904\r
905 $table->add_data($row);\r
906 }\r
907 $currentposition++;\r
908 }\r
909 }\r
910\r
911 $table->print_html(); /// Print the whole table\r
912\r
913 /// Print quickgrade form around the table\r
914 if ($quickgrade && $table->started_output) {\r
915 $mailinfopref = false;\r
73300339 916 if (get_user_preferences('lti_mailinfo', 1)) {\r
b7e436b0
CS
917 $mailinfopref = true;\r
918 }\r
73300339 919 $emailnotification = html_writer::checkbox('mailinfo', 1, $mailinfopref, get_string('enableemailnotification', 'lti'));\r
b7e436b0 920\r
73300339 921 $emailnotification .= $OUTPUT->help_icon('enableemailnotification', 'lti');\r
b7e436b0
CS
922 echo html_writer::tag('div', $emailnotification, array('class'=>'emailnotification'));\r
923\r
73300339 924 $savefeedback = html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'fastg', 'value'=>get_string('saveallfeedback', 'lti')));\r
b7e436b0
CS
925 echo html_writer::tag('div', $savefeedback, array('class'=>'fastgbutton'));\r
926\r
927 echo html_writer::end_tag('form');\r
928 } else if ($quickgrade) {\r
929 echo html_writer::end_tag('form');\r
930 }\r
931\r
932 echo '</div>';\r
933 /// End of fast grading form\r
934\r
935 /// Mini form for setting user preference\r
936\r
73300339 937 $formaction = new moodle_url('/mod/lti/submissions.php', array('id'=>$cm->id));\r
b7e436b0
CS
938 $mform = new MoodleQuickForm('optionspref', 'post', $formaction, '', array('class'=>'optionspref'));\r
939\r
940 $mform->addElement('hidden', 'updatepref');\r
941 $mform->setDefault('updatepref', 1);\r
73300339 942 $mform->addElement('header', 'qgprefs', get_string('optionalsettings', 'lti'));\r
b7e436b0
CS
943// $mform->addElement('select', 'filter', get_string('show'), $filters);\r
944\r
945 $mform->setDefault('filter', $filter);\r
946\r
73300339 947 $mform->addElement('text', 'perpage', get_string('pagesize', 'lti'), array('size'=>1));\r
b7e436b0
CS
948 $mform->setDefault('perpage', $perpage);\r
949\r
73300339 950 $mform->addElement('checkbox', 'quickgrade', get_string('quickgrade', 'lti'));\r
b7e436b0 951 $mform->setDefault('quickgrade', $quickgrade);\r
73300339 952 $mform->addHelpButton('quickgrade', 'quickgrade', 'lti');\r
b7e436b0
CS
953\r
954 $mform->addElement('submit', 'savepreferences', get_string('savepreferences'));\r
955\r
956 $mform->display();\r
957\r
958 echo $OUTPUT->footer();\r
959}\r
960\r
961/**\r
962 * Create grade item for given basiclti\r
963 *\r
964 * @param object $basiclti object with extra cmidnumber\r
965 * @param mixed optional array/object of grade(s); 'reset' means reset grades in gradebook\r
966 * @return int 0 if ok, error code otherwise\r
967 */\r
73300339 968function lti_grade_item_update($basiclti, $grades=null) {\r
b7e436b0
CS
969 global $CFG;\r
970 require_once($CFG->libdir.'/gradelib.php');\r
971\r
b7e436b0
CS
972 $params = array('itemname'=>$basiclti->name, 'idnumber'=>$basiclti->cmidnumber);\r
973\r
974 if ($basiclti->grade > 0) {\r
975 $params['gradetype'] = GRADE_TYPE_VALUE;\r
976 $params['grademax'] = $basiclti->grade;\r
977 $params['grademin'] = 0;\r
978\r
979 } else if ($basiclti->grade < 0) {\r
980 $params['gradetype'] = GRADE_TYPE_SCALE;\r
981 $params['scaleid'] = -$basiclti->grade;\r
982\r
983 } else {\r
984 $params['gradetype'] = GRADE_TYPE_TEXT; // allow text comments only\r
985 }\r
986\r
987 if ($grades === 'reset') {\r
988 $params['reset'] = true;\r
989 $grades = null;\r
990 }\r
991\r
73300339 992 return grade_update('mod/lti', $basiclti->course, 'mod', 'lti', $basiclti->id, 0, $grades, $params);\r
b7e436b0
CS
993}\r
994\r
995/**\r
996 * Delete grade item for given basiclti\r
997 *\r
998 * @param object $basiclti object\r
999 * @return object basiclti\r
1000 */\r
73300339 1001function lti_grade_item_delete($basiclti) {\r
b7e436b0
CS
1002 global $CFG;\r
1003 require_once($CFG->libdir.'/gradelib.php');\r
1004\r
73300339 1005 return grade_update('mod/lti', $basiclti->course, 'mod', 'lti', $basiclti->id, 0, null, array('deleted'=>1));\r
b7e436b0
CS
1006}\r
1007\r