Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

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