Proyectos de Subversion Moodle

Rev

| 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
 * Internal library of functions for module hvp
18
 *
19
 * All the hvp specific functions, needed to implement the module
20
 * logic, should go here. Never include this file from your lib.php!
21
 *
22
 * @package    mod_hvp
23
 * @copyright  2016 Joubel AS <contact@joubel.com>
24
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25
 */
26
 
27
use core\message\message;
28
 
29
defined('MOODLE_INTERNAL') || die();
30
 
31
require_once('autoloader.php');
32
 
33
/**
34
 * Get array with settings for hvp core
35
 *
36
 * @param \context_course|\context_module [$context]
37
 * @return array Settings
38
 */
39
function hvp_get_core_settings($context) {
40
    global $USER, $CFG;
41
 
42
    $systemcontext = \context_system::instance();
43
    $basepath = \mod_hvp\view_assets::getsiteroot() . '/';
44
 
45
    // Check permissions and generate ajax paths.
46
    $ajaxpaths = array();
47
    $savefreq = false;
48
    $ajaxpath = "{$basepath}mod/hvp/ajax.php?contextId={$context->instanceid}&token=";
49
    if ($context->contextlevel == CONTEXT_MODULE && has_capability('mod/hvp:saveresults', $context)) {
50
        $ajaxpaths['setFinished'] = $ajaxpath . \H5PCore::createToken('result') . '&action=set_finished';
51
        $ajaxpaths['xAPIResult'] = $ajaxpath . \H5PCore::createToken('xapiresult') . '&action=xapiresult';
52
    }
53
    if (has_capability('mod/hvp:savecontentuserdata', $context)) {
54
        $ajaxpaths['contentUserData'] = $ajaxpath . \H5PCore::createToken('contentuserdata') .
55
            '&action=contents_user_data&content_id=:contentId&data_type=:dataType&sub_content_id=:subContentId';
56
 
57
        if (get_config('mod_hvp', 'enable_save_content_state')) {
58
            $savefreq = get_config('mod_hvp', 'content_state_frequency');
59
        }
60
    }
61
 
62
    $core = \mod_hvp\framework::instance('core');
63
 
64
    $settings = array(
65
        'baseUrl' => $basepath,
66
        'url' => "{$basepath}pluginfile.php/{$context->instanceid}/mod_hvp",
67
        // NOTE: Separate context from content URL !
68
        'urlLibraries' => "{$basepath}pluginfile.php/{$systemcontext->id}/mod_hvp/libraries",
69
        'postUserStatistics' => true,
70
        'ajax' => $ajaxpaths,
71
        'saveFreq' => $savefreq,
72
        'siteUrl' => $CFG->wwwroot,
73
        'l10n' => array('H5P' => $core->getLocalization()),
74
        'user' => array(
75
            'name' => $USER->firstname . ' ' . $USER->lastname,
76
            'mail' => $USER->email
77
        ),
78
        'hubIsEnabled' => get_config('mod_hvp', 'hub_is_enabled') ? true : false,
79
        'reportingIsEnabled' => true,
80
        'crossorigin' => isset($CFG->mod_hvp_crossorigin) ? $CFG->mod_hvp_crossorigin : null,
81
        'crossoriginRegex' => isset($CFG->mod_hvp_crossoriginRegex) ? $CFG->mod_hvp_crossoriginRegex : null,
82
        'crossoriginCacheBuster' => isset($CFG->mod_hvp_crossoriginCacheBuster) ? $CFG->mod_hvp_crossoriginCacheBuster : null,
83
        'libraryConfig' => $core->h5pF->getLibraryConfig(),
84
        'pluginCacheBuster' => hvp_get_cache_buster(),
85
        'libraryUrl' => $basepath . 'mod/hvp/library/js'
86
    );
87
 
88
    return $settings;
89
}
90
 
91
/**
92
 * Get assets (scripts and styles) for hvp core.
93
 *
94
 * @param \context_course|\context_module $context
95
 * @return array
96
 */
97
function hvp_get_core_assets($context) {
98
    global $PAGE;
99
 
100
    // Get core settings.
101
    $settings = \hvp_get_core_settings($context);
102
    $settings['core'] = array(
103
        'styles' => array(),
104
        'scripts' => array()
105
    );
106
    $settings['loadedJs'] = array();
107
    $settings['loadedCss'] = array();
108
 
109
    // Make sure files are reloaded for each plugin update.
110
    $cachebuster = \hvp_get_cache_buster();
111
 
112
    // Use relative URL to support both http and https.
113
    $liburl = \mod_hvp\view_assets::getsiteroot() . '/mod/hvp/library/';
114
    $relpath = '/' . preg_replace('/^[^:]+:\/\/[^\/]+\//', '', $liburl);
115
 
116
    // Add core stylesheets.
117
    foreach (\H5PCore::$styles as $style) {
118
        $settings['core']['styles'][] = $relpath . $style . $cachebuster;
119
        $PAGE->requires->css(new moodle_url($liburl . $style . $cachebuster));
120
    }
121
    // Add core JavaScript.
122
    foreach (\H5PCore::$scripts as $script) {
123
        $settings['core']['scripts'][] = $relpath . $script . $cachebuster;
124
        $PAGE->requires->js(new moodle_url($liburl . $script . $cachebuster), true);
125
    }
126
 
127
    return $settings;
128
}
129
 
130
/**
131
 * Add required assets for displaying the editor.
132
 *
133
 * @param int $id Content being edited. null for creating new content
134
 * @param string $mformid Id of Moodle form
135
 *
136
 * @throws coding_exception
137
 * @throws moodle_exception
138
 */
139
function hvp_add_editor_assets($id = null, $mformid = null) {
140
    global $PAGE, $CFG, $COURSE;
141
 
142
    // First we need to determine the context for permission handling.
143
    if ($id) {
144
        // Use cm context when editing existing content.
145
        $cm = get_coursemodule_from_instance('hvp', $id);
146
        $context = \context_module::instance($cm->id);
147
    } else {
148
        // Use course context when there's no content, i.e. adding new content.
149
        $context = \context_course::instance($COURSE->id);
150
    }
151
 
152
    $settings = \hvp_get_core_assets($context);
153
 
154
    // Use jQuery and styles from core.
155
    $assets = array(
156
        'css' => $settings['core']['styles'],
157
        'js' => $settings['core']['scripts']
158
    );
159
 
160
    // Use relative URL to support both http and https.
161
    $url = \mod_hvp\view_assets::getsiteroot() . '/mod/hvp/';
162
    $url = '/' . preg_replace('/^[^:]+:\/\/[^\/]+\//', '', $url);
163
 
164
    // Make sure files are reloaded for each plugin update.
165
    $cachebuster = \hvp_get_cache_buster();
166
 
167
    // Add editor styles.
168
    foreach (H5peditor::$styles as $style) {
169
        $assets['css'][] = $url . 'editor/' . $style . $cachebuster;
170
    }
171
 
172
    // Add editor JavaScript.
173
    foreach (H5peditor::$scripts as $script) {
174
        // We do not want the creator of the iframe inside the iframe.
175
        if ($script !== 'scripts/h5peditor-editor.js') {
176
            $assets['js'][] = $url . 'editor/' . $script . $cachebuster;
177
        }
178
    }
179
 
180
    // Add JavaScript with library framework integration (editor part).
181
    $PAGE->requires->js(new moodle_url('/mod/hvp/editor/scripts/h5peditor-editor.js' . $cachebuster), true);
182
    $PAGE->requires->js(new moodle_url('/mod/hvp/editor/scripts/h5peditor-init.js' . $cachebuster), true);
183
    $PAGE->requires->js(new moodle_url('/mod/hvp/editor.js' . $cachebuster), true);
184
 
185
    // Add translations.
186
    $language = \mod_hvp\framework::get_language();
187
    $languagescript = "editor/language/{$language}.js";
188
    if (!file_exists("{$CFG->dirroot}/mod/hvp/{$languagescript}")) {
189
        $languagescript = 'editor/language/en.js';
190
    }
191
    $PAGE->requires->js(new moodle_url('/mod/hvp/' . $languagescript . $cachebuster), true);
192
 
193
    // Add JavaScript settings.
194
    $root = \mod_hvp\view_assets::getsiteroot();
195
    $filespathbase = "{$root}/pluginfile.php/{$context->id}/mod_hvp/";
196
    $contentvalidator = \mod_hvp\framework::instance('contentvalidator');
197
    $editorajaxtoken = \H5PCore::createToken('editorajax');
198
 
199
    $interface = \mod_hvp\framework::instance('interface');
200
    $siteuuid = $interface->getOption('site_uuid', null);
201
    $secret   = $interface->getOption('hub_secret', null);
202
    $enablecontenthub = !empty($siteuuid) && !empty($secret);
203
 
204
    $settings['editor'] = array(
205
      'filesPath' => $filespathbase . 'editor',
206
      'fileIcon' => array(
207
        'path' => $url . 'editor/images/binary-file.png',
208
        'width' => 50,
209
        'height' => 50,
210
      ),
211
      'ajaxPath' => "{$url}ajax.php?contextId={$context->id}&token={$editorajaxtoken}&action=",
212
      'libraryUrl' => $url . 'editor/',
213
      'copyrightSemantics' => $contentvalidator->getCopyrightSemantics(),
214
      'metadataSemantics' => $contentvalidator->getMetadataSemantics(),
215
      'assets' => $assets,
216
      // @codingStandardsIgnoreLine
217
      'apiVersion' => H5PCore::$coreApi,
218
      'language' => $language,
219
      'formId' => $mformid,
220
      'hub' => [
221
        'contentSearchUrl' => \H5PHubEndpoints::createURL(\H5PHubEndpoints::CONTENT) . '/search',
222
      ],
223
      'enableContentHub' => $enablecontenthub,
224
    );
225
 
226
    if ($id !== null) {
227
        $settings['editor']['nodeVersionId'] = $id;
228
 
229
        // Find cm context.
230
        $cm      = \get_coursemodule_from_instance('hvp', $id);
231
        $context = \context_module::instance($cm->id);
232
 
233
        // Override content URL.
234
        $contenturl = "{$root}/pluginfile.php/{$context->id}/mod_hvp/content/{$id}";
235
        $settings['contents']['cid-' . $id]['contentUrl'] = $contenturl;
236
    }
237
 
238
    $PAGE->requires->data_for_js('H5PIntegration', $settings, true);
239
}
240
 
241
/**
242
 * Add core JS and CSS to page.
243
 *
244
 * @param moodle_page $page
245
 * @param moodle_url|string $liburl
246
 * @param array|null $settings
247
 * @throws \coding_exception
248
 */
249
function hvp_admin_add_generic_css_and_js($page, $liburl, $settings = null) {
250
    // @codingStandardsIgnoreLine
251
    foreach (\H5PCore::$adminScripts as $script) {
252
        $page->requires->js(new moodle_url($liburl . $script . hvp_get_cache_buster()), true);
253
    }
254
 
255
    if ($settings === null) {
256
        $settings = array();
257
    }
258
 
259
    $settings['containerSelector'] = '#h5p-admin-container';
260
    $settings['l10n'] = array(
261
        'NA' => get_string('notapplicable', 'hvp'),
262
        'viewLibrary' => '',
263
        'deleteLibrary' => '',
264
        'upgradeLibrary' => get_string('upgradelibrarycontent', 'hvp')
265
    );
266
 
267
    $page->requires->data_for_js('H5PAdminIntegration', $settings, true);
268
    $page->requires->css(new moodle_url($liburl . 'styles/h5p.css' . hvp_get_cache_buster()));
269
    $page->requires->css(new moodle_url($liburl . 'styles/h5p-admin.css' . hvp_get_cache_buster()));
270
 
271
    // Add settings.
272
    $page->requires->data_for_js('h5p', hvp_get_core_settings(\context_system::instance()), true);
273
}
274
 
275
/**
276
 * Get a query string with the plugin version number to include at the end
277
 * of URLs. This is used to force the browser to reload the asset when the
278
 * plugin is updated.
279
 *
280
 * @return string
281
 */
282
function hvp_get_cache_buster() {
283
    return '?ver=' . get_config('mod_hvp', 'version');
284
}
285
 
286
/**
287
 * Restrict access to a given content type.
288
 *
289
 * @param int $library_id
290
 * @param bool $restrict
291
 */
292
function hvp_restrict_library($libraryid, $restrict) {
293
    global $DB;
294
    $DB->update_record('hvp_libraries', (object) array(
295
        'id' => $libraryid,
296
        'restricted' => $restrict ? 1 : 0
297
    ));
298
}
299
 
300
/**
301
 * Handle content upgrade progress
302
 *
303
 * @method hvp_content_upgrade_progress
304
 * @param  int $library_id
305
 * @return object An object including the json content for the H5P instances
306
 *                (maximum 40) that should be upgraded.
307
 */
308
function hvp_content_upgrade_progress($libraryid) {
309
    global $DB;
310
 
311
    $tolibraryid = filter_input(INPUT_POST, 'libraryId');
312
 
313
    // Verify security token.
314
    if (!\H5PCore::validToken('contentupgrade', required_param('token', PARAM_RAW))) {
315
        print get_string('upgradeinvalidtoken', 'hvp');
316
        return;
317
    }
318
 
319
    // Get the library we're upgrading to.
320
    $tolibrary = $DB->get_record('hvp_libraries', array(
321
        'id' => $tolibraryid
322
    ));
323
    if (!$tolibrary) {
324
        print get_string('upgradelibrarymissing', 'hvp');
325
        return;
326
    }
327
 
328
    // Prepare response.
329
    $out = new stdClass();
330
    $out->params = array();
331
    $out->token = \H5PCore::createToken('contentupgrade');
332
    $out->metadata = array();
333
 
334
    // Prepare our interface.
335
    $interface = \mod_hvp\framework::instance('interface');
336
 
337
    // Get updated params.
338
    $params = filter_input(INPUT_POST, 'params');
339
    if ($params !== null) {
340
        // Update params.
341
        $params = json_decode($params);
342
        foreach ($params as $id => $param) {
343
            $upgraded = json_decode($param);
344
            $metadata = isset($upgraded->metadata) ? $upgraded->metadata : array();
345
 
346
            $fields = array_merge(\H5PMetadata::toDBArray($metadata, false, false), array(
347
                'id' => $id,
348
                'main_library_id' => $tolibrary->id,
349
                'json_content' => json_encode($upgraded->params),
350
                'filtered' => ''
351
            ));
352
 
353
            $DB->update_record('hvp', $fields);
354
 
355
            // Log content upgrade successful.
356
            new \mod_hvp\event(
357
                'content', 'upgrade',
358
                $id, $DB->get_field_sql("SELECT name FROM {hvp} WHERE id = ?", array($id)),
359
                $tolibrary->machine_name, $tolibrary->major_version . '.' . $tolibrary->minor_version
360
            );
361
        }
362
    }
363
 
364
    // Determine if any content has been skipped during the process.
365
    $skipped = filter_input(INPUT_POST, 'skipped');
366
    if ($skipped !== null) {
367
        $out->skipped = json_decode($skipped);
368
        // Clean up input, only numbers.
369
        foreach ($out->skipped as $i => $id) {
370
            $out->skipped[$i] = intval($id);
371
        }
372
        $skipped = implode(',', $out->skipped);
373
    } else {
374
        $out->skipped = array();
375
    }
376
 
377
    // Get number of contents for this library.
378
    $out->left = $interface->getNumContent($libraryid, $skipped);
379
 
380
    if ($out->left) {
381
        $skipquery = empty($skipped) ? '' : " AND id NOT IN ($skipped)";
382
 
383
        // Find the 40 first contents using this library version and add to params.
384
        $results = $DB->get_records_sql(
385
            "SELECT id, json_content as params, name as title, authors, source, year_from, year_to,
386
                    license, license_version, changes, license_extras, author_comments, default_language,
387
                    a11y_title
388
               FROM {hvp}
389
              WHERE main_library_id = ?
390
                    {$skipquery}
391
           ORDER BY name ASC", array($libraryid), 0 , 40
392
        );
393
 
394
        foreach ($results as $content) {
395
            $out->params[$content->id] = '{"params":' . $content->params .
396
                                         ',"metadata":' . \H5PMetadata::toJSON($content) . '}';
397
        }
398
    }
399
 
400
    return $out;
401
}
402
 
403
/**
404
 * Gets the information needed when content is upgraded
405
 *
406
 * @method hvp_get_library_upgrade_info
407
 * @param  string $name
408
 * @param  int $major
409
 * @param  int $minor
410
 * @return object Library metadata including name, version, semantics and path
411
 *                to upgrade script
412
 */
413
function hvp_get_library_upgrade_info($name, $major, $minor) {
414
    $library = (object) array(
415
        'name' => $name,
416
        'version' => (object) array(
417
            'major' => $major,
418
            'minor' => $minor
419
        )
420
    );
421
 
422
    $core = \mod_hvp\framework::instance();
423
 
424
    $library->semantics = $core->loadLibrarySemantics($library->name, $library->version->major, $library->version->minor);
425
 
426
    $context = \context_system::instance();
427
    $libraryfoldername = "{$library->name}-{$library->version->major}.{$library->version->minor}";
428
    if (\mod_hvp\file_storage::fileExists($context->id, 'libraries', '/' . $libraryfoldername . '/', 'upgrades.js')) {
429
        $basepath = \mod_hvp\view_assets::getsiteroot() . '/';
430
        $library->upgradesScript = "{$basepath}pluginfile.php/{$context->id}/mod_hvp/libraries/{$libraryfoldername}/upgrades.js";
431
    }
432
 
433
    return $library;
434
}
435
 
436
/**
437
 * Check permissions to view given user's results
438
 *
439
 * @param int $userid Id of the user the results belong to
440
 * @param context $context Current context, usually course context
441
 *
442
 * @return bool true if current user has permission to view given user results
443
 */
444
function hvp_has_view_results_permission($userid, $context) {
445
    global $USER;
446
 
447
    // Check if user can view all results.
448
    if (has_capability('mod/hvp:viewallresults', $context)) {
449
        return true;
450
    }
451
 
452
    // Check if viewing own results, and have permission for it.
453
    return $userid === (int) $USER->id ? has_capability('mod/hvp:viewresults', $context) : false;
454
}
455
 
456
/**
457
 * Require view results capability for this page
458
 *
459
 * @param int $userid User id who owns results
460
 * @param context $context Current context
461
 * @param int $redirectcontentid Redirect to this content id if not allowed
462
 *  to view own results
463
 */
464
function hvp_require_view_results_permission($userid, $context, $redirectcontentid = null) {
465
    global $USER;
466
 
467
    if (!hvp_has_view_results_permission($userid, $context)) {
468
        if ($userid === (int) $USER->id && isset($redirectcontentid)) {
469
            // Not allowed to view own results, redirect.
470
            redirect(new moodle_url('/mod/hvp/view.php', ['id' => $redirectcontentid]));
471
        } else {
472
            // Other user's results, require capability to view all results.
473
            require_capability('mod/hvp:viewallresults', $context);
474
        }
475
    }
476
}
477
 
478
/**
479
 * Sends notification messages to the interested parties that assign the role capability
480
 *
481
 * @param object $recipient user object of the intended recipient
482
 * @param $submitter
483
 * @param object $a associative array of replaceable fields for the templates
484
 *
485
 * @return int|false as for {@link message_send()}.
486
 * @throws coding_exception
487
 */
488
function hvp_send_notification($recipient, $submitter, $a) {
489
    // Recipient info for template.
490
    $a->useridnumber = $recipient->id;
491
    $a->username     = fullname($recipient);
492
    $a->userusername = $recipient->username;
493
 
494
    // Prepare the message.
495
    $message                    = new message();
496
    $message->component         = 'mod_hvp';
497
    $message->name              = 'submission';
498
    $message->userfrom          = $submitter;
499
    $message->userto            = $recipient;
500
    $message->subject           = get_string('emailnotifysubject', 'hvp', $a);
501
    $message->fullmessage       = get_string('emailnotifybody', 'hvp', $a);
502
    $message->fullmessageformat = FORMAT_PLAIN;
503
    $message->fullmessagehtml   = '';
504
    $message->smallmessage      = get_string('emailnotifysmall', 'hvp', $a);
505
    $message->courseid          = $a->courseid;
506
 
507
    $message->contexturl     = $a->hvpreporturl;
508
    $message->contexturlname = $a->hvpname;
509
 
510
    return message_send($message);
511
}
512
 
513
/**
514
 * Sends a confirmation message to the student confirming that the attempt was processed.
515
 *
516
 * @param object $a useful information that can be used in the message
517
 *      subject and body.
518
 *
519
 * @return int|false as for {@link message_send()}.
520
 * @throws coding_exception
521
 */
522
function hvp_send_confirmation($recipient, $a) {
523
    // Add information about the recipient to $a.
524
    $a->username     = fullname($recipient);
525
    $a->userusername = $recipient->username;
526
 
527
    // Prepare the message.
528
    $eventdata               = new \core\message\message();
529
    $eventdata->courseid     = $a->courseid;
530
    $eventdata->component    = 'mod_hvp';
531
    $eventdata->name         = 'confirmation';
532
    $eventdata->notification = 1;
533
 
534
    $eventdata->userfrom          = core_user::get_noreply_user();
535
    $eventdata->userto            = $recipient;
536
    $eventdata->subject           = get_string('emailconfirmsubject', 'hvp', $a);
537
    $eventdata->fullmessage       = get_string('emailconfirmbody', 'hvp', $a);
538
    $eventdata->fullmessageformat = FORMAT_PLAIN;
539
    $eventdata->fullmessagehtml   = '';
540
 
541
    $eventdata->smallmessage   = get_string('emailconfirmsmall', 'hvp', $a);
542
    $eventdata->contexturl     = $a->hvpurl;
543
    $eventdata->contexturlname = $a->hvpname;
544
 
545
    return message_send($eventdata);
546
}
547
 
548
/**
549
 * Send all the required messages when a h5p attempt is submitted.
550
 *
551
 * @param object $course the course
552
 * @param object $hvp the h5p
553
 * @param object $attempt this attempt just finished
554
 * @param context $context the h5p context
555
 * @param object $cm the coursemodule for this h5p
556
 *
557
 * @return bool true if all necessary messages were sent successfully, else false.
558
 * @throws coding_exception
559
 * @throws dml_exception
560
 */
561
function hvp_send_notification_messages($course, $hvp, $attempt, $context, $cm) {
562
    global $CFG, $DB;
563
 
564
    // Do nothing if required objects not present.
565
    if (empty($course) or empty($hvp) or empty($attempt) or empty($context)) {
566
        throw new coding_exception('$course, $hvp, $attempt, $context and $cm must all be set.');
567
    }
568
 
569
    $submitter = $DB->get_record('user', array('id' => $attempt->userid), '*', MUST_EXIST);
570
 
571
    // Check for confirmation required.
572
    $sendconfirm        = false;
573
    $notifyexcludeusers = '';
574
    if (has_capability('mod/hvp:emailconfirmsubmission', $context, $submitter, false)) {
575
        $notifyexcludeusers = $submitter->id;
576
        $sendconfirm        = true;
577
    }
578
 
579
    // Check for notifications required.
580
    $notifyfields = 'u.id, u.username, u.idnumber, u.email, u.emailstop, u.lang,
581
            u.timezone, u.mailformat, u.maildisplay, u.auth, u.suspended, u.deleted, ';
582
    $notifyfields .= get_all_user_name_fields(true, 'u');
583
    $groups       = groups_get_all_groups($course->id, $submitter->id, $cm->groupingid);
584
    if (is_array($groups) && count($groups) > 0) {
585
        $groups = array_keys($groups);
586
    } else if (groups_get_activity_groupmode($cm, $course) != NOGROUPS) {
587
        // If the user is not in a group, and the hvp is set to group mode,
588
        // then set $groups to a non-existant id so that only users with
589
        // 'moodle/site:accessallgroups' get notified.
590
        $groups = - 1;
591
    } else {
592
        $groups = '';
593
    }
594
    $userstonotify = get_users_by_capability($context, 'mod/hvp:emailnotifysubmission',
595
        $notifyfields, '', '', '', $groups, $notifyexcludeusers, false, false, true);
596
 
597
    if (empty($userstonotify) && !$sendconfirm) {
598
        return true; // Nothing to do.
599
    }
600
 
601
    $a = new stdClass();
602
    // Course info.
603
    $a->courseid        = $course->id;
604
    $a->coursename      = $course->fullname;
605
    $a->courseshortname = $course->shortname;
606
 
607
    // H5P info.
608
    $a->hvpname       = $hvp->name;
609
    $report           = "{$CFG->wwwroot}/mod/hvp/review.php?id={$hvp->id}&course={$course->id}&user={$submitter->id}";
610
    $a->hvpreporturl  = $report;
611
    $a->hvpreportlink = '<a href="' . $a->hvpreporturl . '">' .
612
                        format_string($hvp->name, true, ['context' => $context]) . ' report</a>';
613
    $a->hvpurl        = $CFG->wwwroot . '/mod/hvp/view.php?id=' . $cm->id;
614
    $a->hvplink       = '<a href="' . $a->hvpurl . '">' .
615
                        format_string($hvp->name, true, ['context' => $context]) . '</a>';
616
    $a->hvpid         = $hvp->id;
617
    $a->hvpcmid       = $cm->id;
618
 
619
    // Student who sat the hvp info.
620
    $a->studentidnumber = $submitter->id;
621
    $a->studentname     = fullname($submitter);
622
    $a->studentusername = $submitter->username;
623
 
624
    $allok = true;
625
 
626
    // Send notifications if required.
627
    if (!empty($userstonotify)) {
628
        foreach ($userstonotify as $recipient) {
629
            $allok = $allok && hvp_send_notification($recipient, $submitter, $a);
630
        }
631
    }
632
 
633
    // Send confirmation if required. We send the student confirmation last, so
634
    // that if message sending is being intermittently buggy, which means we send
635
    // some but not all messages, and then try again later, then teachers may get
636
    // duplicate messages, but the student will always get exactly one.
637
    if ($sendconfirm) {
638
        $allok = $allok && hvp_send_confirmation($submitter, $a);
639
    }
640
 
641
    return $allok;
642
}
643
 
644
/**
645
 * Callback for the attempt_submitted event.
646
 * Sends out notification messages.
647
 *
648
 * @param $event
649
 *
650
 * @throws coding_exception
651
 * @throws dml_exception
652
 */
653
function hvp_attempt_submitted_handler($event) {
654
    global $DB, $PAGE;
655
    $course  = $DB->get_record('course', array('id' => $event->courseid));
656
    $cm      = get_coursemodule_from_id('hvp', $event->get_context()->instanceid, $event->courseid);
657
    $hvp     = $DB->get_record('hvp', array('id' => $cm->instance));
658
    $attempt = (object) [
659
        'userid' => $event->userid
660
    ];
661
    $context = context_module::instance($cm->id);
662
    $PAGE->set_context($context);
663
 
664
    hvp_send_notification_messages($course, $hvp, $attempt, $context, $cm);
665
}
666
 
667
/**
668
 * Check and update content hub status for shared content.
669
 *
670
 * @param $content
671
 */
672
function hvp_update_hub_status($content) {
673
    $synced = intval($content['synced']);
674
 
675
    // Only check sync status when waiting.
676
    if (empty($content['contentHubId']) || $synced !== H5PContentHubSyncStatus::WAITING) {
677
        return false;
678
    }
679
 
680
    $core = \mod_hvp\framework::instance();
681
    $newstate = $core->getHubContentStatus($content['contentHubId'], $synced);
682
    if ($newstate !== false) {
683
        $core->h5pF->updateContentFields($content['id'], array('synced' => $newstate));
684
 
685
        return $newstate;
686
    }
687
 
688
    return false;
689
}
690
 
691
/**
692
 * Create URL for Content Hub to download content
693
 *
694
 * @param $content
695
 */
696
function hvp_create_hub_export_url($cmid, $content) {
697
    // Create URL.
698
    $modulecontext = \context_module::instance($cmid);
699
    $slug          = $content['slug'] ? $content['slug'] . '-' : '';
700
    $filename      = "{$slug}{$content['id']}.h5p";
701
    $exporturl     = \moodle_url::make_pluginfile_url($modulecontext->id, 'mod_hvp', 'exports', '', '', $filename)
702
        ->out(false);
703
 
704
    // To prevent anyone else from downloading we add an extra token.
705
    $time  = time();
706
    $data  = $time . ':' . get_config('mod_hvp', 'site_uuid');
707
    $hash  = hash_hmac('SHA512', $data, get_config('mod_hvp', 'hub_secret'), true);
708
    $token = hvp_base64_encode($time) . '.' . hvp_base64_encode($hash);
709
 
710
    return "$exporturl?hub=$token";
711
}
712
 
713
/**
714
 * URL compatible base64 encoding.
715
 *
716
 * @param  string  $string
717
 *
718
 * @return string
719
 */
720
function hvp_base64_encode($string) {
721
    return str_replace('=', '', strtr(base64_encode($string), '+/', '-_'));
722
}