See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]
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 /** 18 * Library of interface functions and constants. 19 * 20 * @package mod_h5pactivity 21 * @copyright 2020 Ferran Recio <ferran@moodle.com> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 defined('MOODLE_INTERNAL') || die(); 26 27 use mod_h5pactivity\local\manager; 28 use mod_h5pactivity\local\grader; 29 30 /** 31 * Checks if H5P activity supports a specific feature. 32 * 33 * @uses FEATURE_GROUPS 34 * @uses FEATURE_GROUPINGS 35 * @uses FEATURE_MOD_INTRO 36 * @uses FEATURE_SHOW_DESCRIPTION 37 * @uses FEATURE_COMPLETION_TRACKS_VIEWS 38 * @uses FEATURE_COMPLETION_HAS_RULES 39 * @uses FEATURE_MODEDIT_DEFAULT_COMPLETION 40 * @uses FEATURE_GRADE_HAS_GRADE 41 * @uses FEATURE_GRADE_OUTCOMES 42 * @uses FEATURE_BACKUP_MOODLE2 43 * @param string $feature FEATURE_xx constant for requested feature 44 * @return mixed True if module supports feature, false if not, null if doesn't know or string for the module purpose. 45 */ 46 function h5pactivity_supports(string $feature) { 47 switch($feature) { 48 case FEATURE_GROUPS: 49 return true; 50 case FEATURE_GROUPINGS: 51 return true; 52 case FEATURE_MOD_INTRO: 53 return true; 54 case FEATURE_SHOW_DESCRIPTION: 55 return true; 56 case FEATURE_COMPLETION_TRACKS_VIEWS: 57 return true; 58 case FEATURE_MODEDIT_DEFAULT_COMPLETION: 59 return true; 60 case FEATURE_GRADE_HAS_GRADE: 61 return true; 62 case FEATURE_GRADE_OUTCOMES: 63 return true; 64 case FEATURE_BACKUP_MOODLE2: 65 return true; 66 case FEATURE_MOD_PURPOSE: 67 return MOD_PURPOSE_CONTENT; 68 default: 69 return null; 70 } 71 } 72 73 /** 74 * Saves a new instance of the mod_h5pactivity into the database. 75 * 76 * Given an object containing all the necessary data, (defined by the form 77 * in mod_form.php) this function will create a new instance and return the id 78 * number of the instance. 79 * 80 * @param stdClass $data An object from the form. 81 * @param mod_h5pactivity_mod_form $mform The form. 82 * @return int The id of the newly inserted record. 83 */ 84 function h5pactivity_add_instance(stdClass $data, mod_h5pactivity_mod_form $mform = null): int { 85 global $DB; 86 87 $data->timecreated = time(); 88 $data->timemodified = $data->timecreated; 89 $cmid = $data->coursemodule; 90 91 $data->id = $DB->insert_record('h5pactivity', $data); 92 93 // We need to use context now, so we need to make sure all needed info is already in db. 94 $DB->set_field('course_modules', 'instance', $data->id, ['id' => $cmid]); 95 h5pactivity_set_mainfile($data); 96 97 // Extra fields required in grade related functions. 98 $data->cmid = $data->coursemodule; 99 h5pactivity_grade_item_update($data); 100 return $data->id; 101 } 102 103 /** 104 * Updates an instance of the mod_h5pactivity in the database. 105 * 106 * Given an object containing all the necessary data (defined in mod_form.php), 107 * this function will update an existing instance with new data. 108 * 109 * @param stdClass $data An object from the form in mod_form.php. 110 * @param mod_h5pactivity_mod_form $mform The form. 111 * @return bool True if successful, false otherwise. 112 */ 113 function h5pactivity_update_instance(stdClass $data, mod_h5pactivity_mod_form $mform = null): bool { 114 global $DB; 115 116 $data->timemodified = time(); 117 $data->id = $data->instance; 118 119 h5pactivity_set_mainfile($data); 120 121 // Update gradings if grading method or tracking are modified. 122 $data->cmid = $data->coursemodule; 123 $moduleinstance = $DB->get_record('h5pactivity', ['id' => $data->id]); 124 if (($moduleinstance->grademethod != $data->grademethod) 125 || $data->enabletracking != $moduleinstance->enabletracking) { 126 h5pactivity_update_grades($data); 127 } else { 128 h5pactivity_grade_item_update($data); 129 } 130 131 return $DB->update_record('h5pactivity', $data); 132 } 133 134 /** 135 * Removes an instance of the mod_h5pactivity from the database. 136 * 137 * @param int $id Id of the module instance. 138 * @return bool True if successful, false on failure. 139 */ 140 function h5pactivity_delete_instance(int $id): bool { 141 global $DB; 142 143 $activity = $DB->get_record('h5pactivity', ['id' => $id]); 144 if (!$activity) { 145 return false; 146 } 147 148 // Remove activity record, and all associated attempt data. 149 $attemptids = $DB->get_fieldset_select('h5pactivity_attempts', 'id', 'h5pactivityid = ?', [$id]); 150 if ($attemptids) { 151 $DB->delete_records_list('h5pactivity_attempts_results', 'attemptid', $attemptids); 152 $DB->delete_records_list('h5pactivity_attempts', 'id', $attemptids); 153 } 154 155 $DB->delete_records('h5pactivity', ['id' => $id]); 156 157 h5pactivity_grade_item_delete($activity); 158 159 return true; 160 } 161 162 /** 163 * Checks if scale is being used by any instance of mod_h5pactivity. 164 * 165 * This is used to find out if scale used anywhere. 166 * 167 * @param int $scaleid ID of the scale. 168 * @return bool True if the scale is used by any mod_h5pactivity instance. 169 */ 170 function h5pactivity_scale_used_anywhere(int $scaleid): bool { 171 global $DB; 172 173 if ($scaleid and $DB->record_exists('h5pactivity', ['grade' => -$scaleid])) { 174 return true; 175 } else { 176 return false; 177 } 178 } 179 180 /** 181 * Creates or updates grade item for the given mod_h5pactivity instance. 182 * 183 * Needed by {@link grade_update_mod_grades()}. 184 * 185 * @param stdClass $moduleinstance Instance object with extra cmidnumber and modname property. 186 * @param mixed $grades optional array/object of grade(s); 'reset' means reset grades in gradebook 187 * @return int int 0 if ok, error code otherwise 188 */ 189 function h5pactivity_grade_item_update(stdClass $moduleinstance, $grades = null): int { 190 $idnumber = $moduleinstance->idnumber ?? ''; 191 $grader = new grader($moduleinstance, $idnumber); 192 return $grader->grade_item_update($grades); 193 } 194 195 /** 196 * Delete grade item for given mod_h5pactivity instance. 197 * 198 * @param stdClass $moduleinstance Instance object. 199 * @return int Returns GRADE_UPDATE_OK, GRADE_UPDATE_FAILED, GRADE_UPDATE_MULTIPLE or GRADE_UPDATE_ITEM_LOCKED 200 */ 201 function h5pactivity_grade_item_delete(stdClass $moduleinstance): ?int { 202 $idnumber = $moduleinstance->idnumber ?? ''; 203 $grader = new grader($moduleinstance, $idnumber); 204 return $grader->grade_item_delete(); 205 } 206 207 /** 208 * Update mod_h5pactivity grades in the gradebook. 209 * 210 * Needed by {@link grade_update_mod_grades()}. 211 * 212 * @param stdClass $moduleinstance Instance object with extra cmidnumber and modname property. 213 * @param int $userid Update grade of specific user only, 0 means all participants. 214 */ 215 function h5pactivity_update_grades(stdClass $moduleinstance, int $userid = 0): void { 216 $idnumber = $moduleinstance->idnumber ?? ''; 217 $grader = new grader($moduleinstance, $idnumber); 218 $grader->update_grades($userid); 219 } 220 221 /** 222 * Rescale all grades for this activity and push the new grades to the gradebook. 223 * 224 * @param stdClass $course Course db record 225 * @param stdClass $cm Course module db record 226 * @param float $oldmin 227 * @param float $oldmax 228 * @param float $newmin 229 * @param float $newmax 230 * @return bool true if reescale is successful 231 */ 232 function h5pactivity_rescale_activity_grades(stdClass $course, stdClass $cm, float $oldmin, 233 float $oldmax, float $newmin, float $newmax): bool { 234 235 $manager = manager::create_from_coursemodule($cm); 236 $grader = $manager->get_grader(); 237 $grader->update_grades(); 238 return true; 239 } 240 241 /** 242 * Implementation of the function for printing the form elements that control 243 * whether the course reset functionality affects the H5P activity. 244 * 245 * @param object $mform form passed by reference 246 */ 247 function h5pactivity_reset_course_form_definition(&$mform): void { 248 $mform->addElement('header', 'h5pactivityheader', get_string('modulenameplural', 'mod_h5pactivity')); 249 $mform->addElement('advcheckbox', 'reset_h5pactivity', get_string('deleteallattempts', 'mod_h5pactivity')); 250 } 251 252 /** 253 * Course reset form defaults. 254 * 255 * @param stdClass $course the course object 256 * @return array 257 */ 258 function h5pactivity_reset_course_form_defaults(stdClass $course): array { 259 return ['reset_h5pactivity' => 1]; 260 } 261 262 263 /** 264 * This function is used by the reset_course_userdata function in moodlelib. 265 * 266 * This function will remove all H5P attempts in the database 267 * and clean up any related data. 268 * 269 * @param stdClass $data the data submitted from the reset course. 270 * @return array of reseting status 271 */ 272 function h5pactivity_reset_userdata(stdClass $data): array { 273 global $DB; 274 $componentstr = get_string('modulenameplural', 'mod_h5pactivity'); 275 $status = []; 276 if (!empty($data->reset_h5pactivity)) { 277 $params = ['courseid' => $data->courseid]; 278 $sql = "SELECT a.id FROM {h5pactivity} a WHERE a.course=:courseid"; 279 if ($activities = $DB->get_records_sql($sql, $params)) { 280 foreach ($activities as $activity) { 281 $cm = get_coursemodule_from_instance('h5pactivity', 282 $activity->id, 283 $data->courseid, 284 false, 285 MUST_EXIST); 286 mod_h5pactivity\local\attempt::delete_all_attempts ($cm); 287 } 288 } 289 // Remove all grades from gradebook. 290 if (empty($data->reset_gradebook_grades)) { 291 h5pactivity_reset_gradebook($data->courseid, 'reset'); 292 } 293 $status[] = [ 294 'component' => $componentstr, 295 'item' => get_string('deleteallattempts', 'mod_h5pactivity'), 296 'error' => false, 297 ]; 298 } 299 return $status; 300 } 301 302 /** 303 * Removes all grades from gradebook 304 * 305 * @param int $courseid Coude ID 306 * @param string $type optional type (default '') 307 */ 308 function h5pactivity_reset_gradebook(int $courseid, string $type=''): void { 309 global $DB; 310 311 $sql = "SELECT a.*, cm.idnumber as cmidnumber, a.course as courseid 312 FROM {h5pactivity} a, {course_modules} cm, {modules} m 313 WHERE m.name='h5pactivity' AND m.id=cm.module AND cm.instance=a.id AND a.course=?"; 314 315 if ($activities = $DB->get_records_sql($sql, [$courseid])) { 316 foreach ($activities as $activity) { 317 h5pactivity_grade_item_update($activity, 'reset'); 318 } 319 } 320 } 321 322 /** 323 * Return a list of page types 324 * 325 * @param string $pagetype current page type 326 * @param stdClass|null $parentcontext Block's parent context 327 * @param stdClass $currentcontext Current context of block 328 * @return array array of page types and it's names 329 */ 330 function h5pactivity_page_type_list(string $pagetype, ?stdClass $parentcontext, stdClass $currentcontext): array { 331 $modulepagetype = [ 332 'mod-h5pactivity-*' => get_string('page-mod-h5pactivity-x', 'h5pactivity'), 333 ]; 334 return $modulepagetype; 335 } 336 337 /** 338 * Check if the module has any update that affects the current user since a given time. 339 * 340 * @param cm_info $cm course module data 341 * @param int $from the time to check updates from 342 * @param array $filter if we need to check only specific updates 343 * @return stdClass an object with the different type of areas indicating if they were updated or not 344 */ 345 function h5pactivity_check_updates_since(cm_info $cm, int $from, array $filter = []): stdClass { 346 global $DB, $USER; 347 348 $updates = course_check_module_updates_since($cm, $from, ['package'], $filter); 349 350 $updates->tracks = (object) ['updated' => false]; 351 $select = 'h5pactivityid = ? AND userid = ? AND timemodified > ?'; 352 $params = [$cm->instance, $USER->id, $from]; 353 $tracks = $DB->get_records_select('h5pactivity_attempts', $select, $params, '', 'id'); 354 if (!empty($tracks)) { 355 $updates->tracks->updated = true; 356 $updates->tracks->itemids = array_keys($tracks); 357 } 358 359 // Now, teachers should see other students updates. 360 if (has_capability('mod/h5pactivity:reviewattempts', $cm->context)) { 361 $select = 'h5pactivityid = ? AND timemodified > ?'; 362 $params = [$cm->instance, $from]; 363 364 if (groups_get_activity_groupmode($cm) == SEPARATEGROUPS) { 365 $groupusers = array_keys(groups_get_activity_shared_group_members($cm)); 366 if (empty($groupusers)) { 367 return $updates; 368 } 369 list($insql, $inparams) = $DB->get_in_or_equal($groupusers); 370 $select .= ' AND userid ' . $insql; 371 $params = array_merge($params, $inparams); 372 } 373 374 $updates->usertracks = (object) ['updated' => false]; 375 $tracks = $DB->get_records_select('h5pactivity_attempts', $select, $params, '', 'id'); 376 if (!empty($tracks)) { 377 $updates->usertracks->updated = true; 378 $updates->usertracks->itemids = array_keys($tracks); 379 } 380 } 381 return $updates; 382 } 383 384 /** 385 * Returns the lists of all browsable file areas within the given module context. 386 * 387 * The file area 'intro' for the activity introduction field is added automatically 388 * by {@link file_browser::get_file_info_context_module()}. 389 * 390 * @param stdClass $course course object 391 * @param stdClass $cm course module object 392 * @param stdClass $context context object 393 * @return string[] array of pair file area => human file area name 394 */ 395 function h5pactivity_get_file_areas(stdClass $course, stdClass $cm, stdClass $context): array { 396 $areas = []; 397 $areas['package'] = get_string('areapackage', 'mod_h5pactivity'); 398 return $areas; 399 } 400 401 /** 402 * File browsing support for data module. 403 * 404 * @param file_browser $browser 405 * @param array $areas 406 * @param stdClass $course 407 * @param stdClass $cm 408 * @param context $context 409 * @param string $filearea 410 * @param int|null $itemid 411 * @param string|null $filepath 412 * @param string|null $filename 413 * @return file_info_stored|null file_info_stored instance or null if not found 414 */ 415 function h5pactivity_get_file_info(file_browser $browser, array $areas, stdClass $course, 416 stdClass $cm, context $context, string $filearea, ?int $itemid = null, 417 ?string $filepath = null, ?string $filename = null): ?file_info_stored { 418 global $CFG; 419 420 if (!has_capability('moodle/course:managefiles', $context)) { 421 return null; 422 } 423 424 $fs = get_file_storage(); 425 426 if ($filearea === 'package') { 427 $filepath = is_null($filepath) ? '/' : $filepath; 428 $filename = is_null($filename) ? '.' : $filename; 429 430 $urlbase = $CFG->wwwroot.'/pluginfile.php'; 431 if (!$storedfile = $fs->get_file($context->id, 'mod_h5pactivity', 'package', 0, $filepath, $filename)) { 432 if ($filepath === '/' and $filename === '.') { 433 $storedfile = new virtual_root_file($context->id, 'mod_h5pactivity', 'package', 0); 434 } else { 435 // Not found. 436 return null; 437 } 438 } 439 return new file_info_stored($browser, $context, $storedfile, $urlbase, $areas[$filearea], false, true, false, false); 440 } 441 return null; 442 } 443 444 /** 445 * Serves the files from the mod_h5pactivity file areas. 446 * 447 * @param mixed $course course or id of the course 448 * @param mixed $cm course module or id of the course module 449 * @param context $context 450 * @param string $filearea 451 * @param array $args 452 * @param bool $forcedownload 453 * @param array $options additional options affecting the file serving 454 * @return bool false if file not found, does not return if found - just send the file 455 */ 456 function h5pactivity_pluginfile($course, $cm, context $context, 457 string $filearea, array $args, bool $forcedownload, array $options = []): bool { 458 if ($context->contextlevel != CONTEXT_MODULE) { 459 return false; 460 } 461 462 require_login($course, true, $cm); 463 464 $fullpath = ''; 465 466 if ($filearea === 'package') { 467 $revision = (int)array_shift($args); // Prevents caching problems - ignored here. 468 $relativepath = implode('/', $args); 469 $fullpath = "/$context->id/mod_h5pactivity/package/0/$relativepath"; 470 } 471 if (empty($fullpath)) { 472 return false; 473 } 474 $fs = get_file_storage(); 475 $file = $fs->get_file_by_hash(sha1($fullpath)); 476 if (empty($file)) { 477 return false; 478 } 479 send_stored_file($file, $lifetime, 0, false, $options); 480 } 481 482 /** 483 * Saves draft files as the activity package. 484 * 485 * @param stdClass $data an object from the form 486 */ 487 function h5pactivity_set_mainfile(stdClass $data): void { 488 $fs = get_file_storage(); 489 $cmid = $data->coursemodule; 490 $context = context_module::instance($cmid); 491 492 if (!empty($data->packagefile)) { 493 $fs = get_file_storage(); 494 $fs->delete_area_files($context->id, 'mod_h5pactivity', 'package'); 495 file_save_draft_area_files($data->packagefile, $context->id, 'mod_h5pactivity', 'package', 496 0, ['subdirs' => 0, 'maxfiles' => 1]); 497 } 498 } 499 500 /** 501 * Register the ability to handle drag and drop file uploads 502 * @return array containing details of the files / types the mod can handle 503 */ 504 function h5pactivity_dndupload_register(): array { 505 return [ 506 'files' => [ 507 [ 508 'extension' => 'h5p', 509 'message' => get_string('dnduploadh5pactivity', 'h5pactivity') 510 ] 511 ] 512 ]; 513 } 514 515 /** 516 * Handle a file that has been uploaded 517 * @param object $uploadinfo details of the file / content that has been uploaded 518 * @return int instance id of the newly created mod 519 */ 520 function h5pactivity_dndupload_handle($uploadinfo): int { 521 global $CFG; 522 523 $context = context_module::instance($uploadinfo->coursemodule); 524 file_save_draft_area_files($uploadinfo->draftitemid, $context->id, 'mod_h5pactivity', 'package', 0); 525 $fs = get_file_storage(); 526 $files = $fs->get_area_files($context->id, 'mod_h5pactivity', 'package', 0, 'sortorder, itemid, filepath, filename', false); 527 $file = reset($files); 528 529 // Create a default h5pactivity object to pass to h5pactivity_add_instance()! 530 $h5p = get_config('h5pactivity'); 531 $h5p->intro = ''; 532 $h5p->introformat = FORMAT_HTML; 533 $h5p->course = $uploadinfo->course->id; 534 $h5p->coursemodule = $uploadinfo->coursemodule; 535 $h5p->grade = $CFG->gradepointdefault; 536 537 // Add some special handling for the H5P options checkboxes. 538 $factory = new \core_h5p\factory(); 539 $core = $factory->get_core(); 540 if (isset($uploadinfo->displayopt)) { 541 $config = (object) $uploadinfo->displayopt; 542 } else { 543 $config = \core_h5p\helper::decode_display_options($core); 544 } 545 $h5p->displayoptions = \core_h5p\helper::get_display_options($core, $config); 546 547 $h5p->cmidnumber = ''; 548 $h5p->name = $uploadinfo->displayname; 549 $h5p->reference = $file->get_filename(); 550 551 return h5pactivity_add_instance($h5p, null); 552 } 553 554 /** 555 * Print recent activity from all h5pactivities in a given course 556 * 557 * This is used by the recent activity block 558 * @param mixed $course the course to print activity for 559 * @param bool $viewfullnames boolean to determine whether to show full names or not 560 * @param int $timestart the time the rendering started 561 * @return bool true if activity was printed, false otherwise. 562 */ 563 function h5pactivity_print_recent_activity($course, bool $viewfullnames, int $timestart): bool { 564 global $CFG, $DB, $OUTPUT, $USER; 565 566 $dbparams = [$timestart, $course->id, 'h5pactivity']; 567 568 $userfieldsapi = \core_user\fields::for_userpic(); 569 $namefields = $userfieldsapi->get_sql('u', false, '', 'userid', false)->selects;; 570 571 $sql = "SELECT h5pa.id, h5pa.timemodified, cm.id as cmid, $namefields 572 FROM {h5pactivity_attempts} h5pa 573 JOIN {h5pactivity} h5p ON h5p.id = h5pa.h5pactivityid 574 JOIN {course_modules} cm ON cm.instance = h5p.id 575 JOIN {modules} md ON md.id = cm.module 576 JOIN {user} u ON u.id = h5pa.userid 577 WHERE h5pa.timemodified > ? 578 AND h5p.course = ? 579 AND md.name = ? 580 ORDER BY h5pa.timemodified ASC"; 581 582 if (!$submissions = $DB->get_records_sql($sql, $dbparams)) { 583 return false; 584 } 585 586 $modinfo = get_fast_modinfo($course); 587 588 $recentactivity = h5pactivity_fetch_recent_activity($submissions, $course->id); 589 590 if (empty($recentactivity)) { 591 return false; 592 } 593 594 $cms = $modinfo->get_cms(); 595 596 echo $OUTPUT->heading(get_string('newsubmissions', 'h5pactivity') . ':', 6); 597 598 foreach ($recentactivity as $submission) { 599 $cm = $cms[$submission->cmid]; 600 $link = $CFG->wwwroot.'/mod/h5pactivity/view.php?id='.$cm->id; 601 print_recent_activity_note($submission->timemodified, 602 $submission, 603 $cm->name, 604 $link, 605 false, 606 $viewfullnames); 607 } 608 609 return true; 610 } 611 612 /** 613 * Returns all h5pactivities since a given time. 614 * 615 * @param array $activities The activity information is returned in this array 616 * @param int $index The current index in the activities array 617 * @param int $timestart The earliest activity to show 618 * @param int $courseid Limit the search to this course 619 * @param int $cmid The course module id 620 * @param int $userid Optional user id 621 * @param int $groupid Optional group id 622 * @return void 623 */ 624 function h5pactivity_get_recent_mod_activity(array &$activities, int &$index, int $timestart, int $courseid, 625 int $cmid, int $userid=0, int $groupid=0) { 626 global $CFG, $DB, $USER; 627 628 $course = get_course($courseid); 629 $modinfo = get_fast_modinfo($course); 630 631 $cm = $modinfo->get_cm($cmid); 632 $params = []; 633 if ($userid) { 634 $userselect = 'AND u.id = :userid'; 635 $params['userid'] = $userid; 636 } else { 637 $userselect = ''; 638 } 639 640 if ($groupid) { 641 $groupselect = 'AND gm.groupid = :groupid'; 642 $groupjoin = 'JOIN {groups_members} gm ON gm.userid=u.id'; 643 $params['groupid'] = $groupid; 644 } else { 645 $groupselect = ''; 646 $groupjoin = ''; 647 } 648 649 $params['cminstance'] = $cm->instance; 650 $params['timestart'] = $timestart; 651 $params['cmid'] = $cmid; 652 653 $userfieldsapi = \core_user\fields::for_userpic(); 654 $userfields = $userfieldsapi->get_sql('u', false, '', 'userid', false)->selects; 655 656 $sql = "SELECT h5pa.id, h5pa.timemodified, cm.id as cmid, $userfields 657 FROM {h5pactivity_attempts} h5pa 658 JOIN {h5pactivity} h5p ON h5p.id = h5pa.h5pactivityid 659 JOIN {course_modules} cm ON cm.instance = h5p.id 660 JOIN {modules} md ON md.id = cm.module 661 JOIN {user} u ON u.id = h5pa.userid $groupjoin 662 WHERE h5pa.timemodified > :timestart 663 AND h5p.id = :cminstance $userselect $groupselect 664 AND cm.id = :cmid 665 ORDER BY h5pa.timemodified ASC"; 666 667 if (!$submissions = $DB->get_records_sql($sql, $params)) { 668 return; 669 } 670 671 $cmcontext = context_module::instance($cm->id); 672 $grader = has_capability('mod/h5pactivity:reviewattempts', $cmcontext); 673 $viewfullnames = has_capability('moodle/site:viewfullnames', $cmcontext); 674 675 $recentactivity = h5pactivity_fetch_recent_activity($submissions, $courseid); 676 677 if (empty($recentactivity)) { 678 return; 679 } 680 681 if ($grader) { 682 require_once($CFG->libdir.'/gradelib.php'); 683 $userids = []; 684 foreach ($recentactivity as $id => $submission) { 685 $userids[] = $submission->userid; 686 } 687 $grades = grade_get_grades($courseid, 'mod', 'h5pactivity', $cm->instance, $userids); 688 } 689 690 $aname = format_string($cm->name, true); 691 foreach ($recentactivity as $submission) { 692 $activity = new stdClass(); 693 694 $activity->type = 'h5pactivity'; 695 $activity->cmid = $cm->id; 696 $activity->name = $aname; 697 $activity->sectionnum = $cm->sectionnum; 698 $activity->timestamp = $submission->timemodified; 699 $activity->user = new stdClass(); 700 if ($grader) { 701 $activity->grade = $grades->items[0]->grades[$submission->userid]->str_long_grade; 702 } 703 704 $userfields = explode(',', implode(',', \core_user\fields::get_picture_fields())); 705 foreach ($userfields as $userfield) { 706 if ($userfield == 'id') { 707 // Aliased in SQL above. 708 $activity->user->{$userfield} = $submission->userid; 709 } else { 710 $activity->user->{$userfield} = $submission->{$userfield}; 711 } 712 } 713 $activity->user->fullname = fullname($submission, $viewfullnames); 714 715 $activities[$index++] = $activity; 716 } 717 718 return; 719 } 720 721 /** 722 * Print recent activity from all h5pactivities in a given course 723 * 724 * This is used by course/recent.php 725 * @param stdClass $activity 726 * @param int $courseid 727 * @param bool $detail 728 * @param array $modnames 729 */ 730 function h5pactivity_print_recent_mod_activity(stdClass $activity, int $courseid, bool $detail, array $modnames) { 731 global $OUTPUT; 732 733 $modinfo = []; 734 if ($detail) { 735 $modinfo['modname'] = $activity->name; 736 $modinfo['modurl'] = new moodle_url('/mod/h5pactivity/view.php', ['id' => $activity->cmid]); 737 $modinfo['modicon'] = $OUTPUT->image_icon('monologo', $modnames[$activity->type], 'h5pactivity'); 738 } 739 740 $userpicture = $OUTPUT->user_picture($activity->user); 741 742 $template = ['userpicture' => $userpicture, 743 'submissiontimestamp' => $activity->timestamp, 744 'modinfo' => $modinfo, 745 'userurl' => new moodle_url('/user/view.php', array('id' => $activity->user->id, 'course' => $courseid)), 746 'fullname' => $activity->user->fullname]; 747 if (isset($activity->grade)) { 748 $template['grade'] = get_string('grade_h5p', 'h5pactivity', $activity->grade); 749 } 750 751 echo $OUTPUT->render_from_template('mod_h5pactivity/reviewattempts', $template); 752 } 753 754 /** 755 * Fetches recent activity for course module. 756 * 757 * @param array $submissions The activity submissions 758 * @param int $courseid Limit the search to this course 759 * @return array $recentactivity recent activity in a course. 760 */ 761 function h5pactivity_fetch_recent_activity(array $submissions, int $courseid) : array { 762 global $USER; 763 764 $course = get_course($courseid); 765 $modinfo = get_fast_modinfo($course); 766 767 $recentactivity = []; 768 $grader = []; 769 770 $cms = $modinfo->get_cms(); 771 772 foreach ($submissions as $submission) { 773 if (!array_key_exists($submission->cmid, $cms)) { 774 continue; 775 } 776 $cm = $cms[$submission->cmid]; 777 if (!$cm->uservisible) { 778 continue; 779 } 780 781 if ($USER->id == $submission->userid) { 782 $recentactivity[$submission->userid] = $submission; 783 continue; 784 } 785 786 $cmcontext = context_module::instance($cm->id); 787 // The act of submitting of attempt may be considered private - 788 // only graders will see it if specified. 789 if (!array_key_exists($cm->id, $grader)) { 790 $grader[$cm->id] = has_capability('mod/h5pactivity:reviewattempts', $cmcontext); 791 } 792 if (!$grader[$cm->id]) { 793 continue; 794 } 795 796 $groups = []; 797 $usersgroups = []; 798 799 $groupmode = groups_get_activity_groupmode($cm, $course); 800 $accessallgroups = has_capability('moodle/site:accessallgroups', $cmcontext); 801 802 if ($groupmode == SEPARATEGROUPS && !$accessallgroups) { 803 804 if (isguestuser()) { 805 // Shortcut - guest user does not belong into any group. 806 continue; 807 } 808 809 if (!isset($groups[$cm->groupingid])) { 810 $groups[$cm->groupingid] = $modinfo->get_groups($cm->groupingid); 811 if (!$groups[$cm->groupingid]) { 812 continue; 813 } 814 } 815 816 if (!isset($usersgroups[$cm->groupingid][$submission->userid])) { 817 $usersgroups[$cm->groupingid][$submission->userid] = 818 groups_get_all_groups($course->id, $submission->userid, $cm->groupingid); 819 } 820 821 if (is_array($usersgroups[$cm->groupingid][$submission->userid])) { 822 $usersgroupstmp = array_keys($usersgroups[$cm->groupingid][$submission->userid]); 823 $intersect = array_intersect($usersgroupstmp, $groups[$cm->groupingid]); 824 if (empty($intersect)) { 825 continue; 826 } 827 } 828 } 829 830 $recentactivity[$submission->userid] = $submission; 831 } 832 833 return $recentactivity; 834 } 835 836 /** 837 * Extends the settings navigation with the H5P activity settings 838 839 * This function is called when the context for the page is an H5P activity. This is not called by AJAX 840 * so it is safe to rely on the $PAGE. 841 * 842 * @param settings_navigation $settingsnav The settings navigation object 843 * @param navigation_node $h5pactivitynode The node to add module settings to 844 */ 845 function h5pactivity_extend_settings_navigation(settings_navigation $settingsnav, 846 navigation_node $h5pactivitynode = null) { 847 global $USER; 848 849 $manager = manager::create_from_coursemodule($settingsnav->get_page()->cm); 850 851 // Attempts report. 852 if ($manager->can_view_all_attempts()) { 853 $attemptsreporturl = new moodle_url('/mod/h5pactivity/report.php', 854 ['a' => $settingsnav->get_page()->cm->instance]); 855 $h5pactivitynode->add(get_string('attempts_report', 'h5pactivity'), $attemptsreporturl, 856 settings_navigation::TYPE_SETTING, '', 'attemptsreport'); 857 } else if ($manager->can_view_own_attempts() && $manager->count_attempts($USER->id)) { 858 $attemptsreporturl = new moodle_url('/mod/h5pactivity/report.php', 859 ['a' => $settingsnav->get_page()->cm->instance, 'userid' => $USER->id]); 860 $h5pactivitynode->add(get_string('attempts_report', 'h5pactivity'), $attemptsreporturl, 861 settings_navigation::TYPE_SETTING, '', 'attemptsreport'); 862 } 863 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body