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