Skip to content
Snippets Groups Projects
Select Git revision
  • 1645ec88effaa4e2ce9518cdaadd6bdc691ce196
  • main default protected
  • dev/grabowski
  • origin/main
  • hsh_1.22.4
  • 1.22.4
  • 1.22.3
  • 1.22.2
  • 1.22.1
  • 1.22
  • 1.21
  • 1.20.2
  • 1.20.1
  • 1.20.0
  • 1.19.1
  • 1.19
  • 1.18
  • 1.17.2
  • 1.17.1
  • 1.17
  • 1.16
  • 1.15
  • 1.14
  • 1.13
24 results

framework.php

Blame
  • framework.php 57.39 KiB
    <?php
    // This file is part of Moodle - http://moodle.org/
    //
    // Moodle is free software: you can redistribute it and/or modify
    // it under the terms of the GNU General Public License as published by
    // the Free Software Foundation, either version 3 of the License, or
    // (at your option) any later version.
    //
    // Moodle is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU General Public License for more details.
    //
    // You should have received a copy of the GNU General Public License
    // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
    
    /**
     * \mod_hvp\framework class
     *
     * @package    mod_hvp
     * @copyright  2016 Joubel AS
     * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
     */
    
    namespace mod_hvp;
    
    defined('MOODLE_INTERNAL') || die();
    
    global $CFG;
    require_once(__DIR__ . '/../autoloader.php');
    require_once($CFG->libdir . '/filelib.php');
    require_once($CFG->libdir . '/adminlib.php');
    
    /**
     * Moodle's implementation of the H5P framework interface.
     *
     * @package    mod_hvp
     * @copyright  2016 Joubel AS
     * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
     *
     * @SuppressWarnings(PHPMD)
     */
    class framework implements \H5PFrameworkInterface {
    
        /**
         * Get type of hvp instance
         *
         * @param string $type Type of hvp instance to get
         * @return \H5PContentValidator|\H5PCore|\H5PStorage|\H5PValidator|\mod_hvp\framework|\H5peditor
         */
        public static function instance($type = null) {
            global $CFG;
            static $interface, $core, $editor, $editorinterface, $editorajaxinterface;
    
            if (!isset($interface)) {
                $interface = new \mod_hvp\framework();
    
                $fs = new \mod_hvp\file_storage();
    
                $context = \context_system::instance();
                $url = "{$CFG->httpswwwroot}/pluginfile.php/{$context->id}/mod_hvp";
    
                $language = self::get_language();
    
                $export = !(isset($CFG->mod_hvp_export) && $CFG->mod_hvp_export === '0');
    
                $core = new \H5PCore($interface, $fs, $url, $language, $export);
                $core->aggregateAssets = !(isset($CFG->mod_hvp_aggregate_assets) && $CFG->mod_hvp_aggregate_assets === '0');
            }
    
            switch ($type) {
                case 'validator':
                    return new \H5PValidator($interface, $core);
                case 'storage':
                    return new \H5PStorage($interface, $core);
                case 'contentvalidator':
                    return new \H5PContentValidator($interface, $core);
                case 'interface':
                    return $interface;
                case 'editor':
                    if (empty($editorinterface)) {
                        $editorinterface = new \mod_hvp\editor_framework();
                    }
    
                    if (empty($editorajaxinterface)) {
                        $editorajaxinterface = new editor_ajax();
                    }
    
                    if (empty($editor)) {
                        $editor = new \H5peditor($core, $editorinterface, $editorajaxinterface);
                    }
                    return $editor;
                case 'core':
                default:
                    return $core;
            }
        }
    
        /**
         * Check if the current user has editor access, if not then return the
         * given error message.
         *
         * @param string $error
         * @return boolean
         */
        public static function has_editor_access($error) {
            $context = \context::instance_by_id(required_param('contextId', PARAM_RAW));
            $cap = ($context->contextlevel === CONTEXT_COURSE ? 'addinstance' : 'manage');
    
            if (!has_capability("mod/hvp:$cap", $context)) {
                \H5PCore::ajaxError(get_string($error, 'hvp'));
                http_response_code(403);
                return false;
            }
    
            return true;
        }
    
        /**
         * Get current H5P language code.
         *
         * @return string Language Code
         */
        public static function get_language() {
            static $map;
    
            if (empty($map)) {
                // Create mapping for "converting" language codes.
                $map = array(
                    'no' => 'nb'
                );
            }
    
            // Get current language in Moodle.
            $language = str_replace('_', '-', strtolower(\current_language()));
    
            // Try to map.
            return isset($map[$language]) ? $map[$language] : $language;
        }
    
        /**
         * Implements getPlatformInfo
         */
        // @codingStandardsIgnoreLine
        public function getPlatformInfo() {
            global $CFG;
    
            return array(
                'name' => 'Moodle',
                'version' => $CFG->version,
                'h5pVersion' => get_component_version('mod_hvp'),
            );
        }
    
        /**
         * @inheritdoc
         */
        // @codingStandardsIgnoreLine
        public function fetchExternalData($url, $data = null, $blocking = true, $stream = null) {
            global $CFG;
    
            if ($stream !== null) {
                // Download file.
                @set_time_limit(0);
    
                // Generate local tmp file path.
                $localfolder = $CFG->tempdir . uniqid('/hvp-');
                $stream = $localfolder . '.h5p';
    
                // Add folder and file paths to H5P Core.
                $interface = self::instance('interface');
                $interface->getUploadedH5pFolderPath($localfolder);
                $interface->getUploadedH5pPath($stream);
            }
    
            $response = download_file_content($url, null, $data, true, 300, 20, false, $stream);
    
            if (empty($response->error)) {
                return $response->results;
            } else {
                $this->setErrorMessage($response->error, 'failed-fetching-external-data');
            }
        }
    
        /**
         * Implements setLibraryTutorialUrl
         *
         * Set the tutorial URL for a library. All versions of the library is set
         *
         * @param string $libraryname
         * @param string $url
         */
        // @codingStandardsIgnoreLine
        public function setLibraryTutorialUrl($libraryname, $url) {
            global $DB;
    
            $DB->execute("UPDATE {hvp_libraries} SET tutorial_url = ? WHERE machine_name = ?", array($url, $libraryname));
        }
    
        /**
         * Implements setErrorMessage
         *
         * @param string $message translated error message
         * @param string $code
         */
        // @codingStandardsIgnoreLine
        public function setErrorMessage($message, $code = null) {
            if ($message !== null) {
                self::messages('error', $message, $code);
            }
        }
    
        /**
         * Implements setInfoMessage
         */
        // @codingStandardsIgnoreLine
        public function setInfoMessage($message) {
            if ($message !== null) {
                self::messages('info', $message);
            }
        }
    
        /**
         * Store messages until they can be printed to the current user
         *
         * @param string $type Type of messages, e.g. 'info' or 'error'
         * @param string $newmessage Optional
         * @param string $code
         * @return array Array of stored messages
         */
        public static function messages($type, $newmessage = null, $code = null) {
            static $m = 'mod_hvp_messages';
    
            if ($newmessage === null) {
                // Return and reset messages.
                $messages = isset($_SESSION[$m][$type]) ? $_SESSION[$m][$type] : array();
                unset($_SESSION[$m][$type]);
                if (empty($_SESSION[$m])) {
                    unset($_SESSION[$m]);
                }
                return $messages;
            }
    
            // We expect to get out an array of strings when getting info
            // and an array of objects when getting errors for consistency across platforms.
            // This implementation should be improved for consistency across the data type returned here.
            if ($type === 'error') {
                $_SESSION[$m][$type][] = (object)array(
                    'code' => $code,
                    'message' => $newmessage
                );
            } else {
                $_SESSION[$m][$type][] = $newmessage;
            }
        }
    
        /**
         * Simple print of given messages.
         *
         * @param string $type One of error|info
         * @param array $messages
         */
        // @codingStandardsIgnoreLine
        public static function printMessages($type, $messages) {
            global $OUTPUT;
            foreach ($messages as $message) {
                $out = $type === 'error' ? $message->message : $message;
                print $OUTPUT->notification($out, ($type === 'error' ? 'notifyproblem' : 'notifymessage'));
            }
        }
    
        /**
         * Implements getMessages
         */
        // @codingStandardsIgnoreLine
        public function getMessages($type) {
            return self::messages($type);
        }
    
        /**
         * Implements t
         */
        public function t($message, $replacements = array()) {
            static $translationsmap;
    
            if (empty($translationsmap)) {
                // Create mapping.
                // @codingStandardsIgnoreStart
                $translationsmap = [
                    'Your PHP version does not support ZipArchive.' => 'noziparchive',
                    'The file you uploaded is not a valid HTML5 Package (It does not have the .h5p file extension)' => 'noextension',
                    'The file you uploaded is not a valid HTML5 Package (We are unable to unzip it)' => 'nounzip',
                    'Could not parse the main h5p.json file' => 'noparse',
                    'The main h5p.json file is not valid' => 'nojson',
                    'Invalid content folder' => 'invalidcontentfolder',
                    'Could not find or parse the content.json file' => 'nocontent',
                    'Library directory name must match machineName or machineName-majorVersion.minorVersion (from library.json). (Directory: %directoryName , machineName: %machineName, majorVersion: %majorVersion, minorVersion: %minorVersion)' => 'librarydirectoryerror',
                    'A valid content folder is missing' => 'missingcontentfolder',
                    'A valid main h5p.json file is missing' => 'invalidmainjson',
                    'Missing required library @library' => 'missinglibrary',
                    "Note that the libraries may exist in the file you uploaded, but you're not allowed to upload new libraries. Contact the site administrator about this." => 'missinguploadpermissions',
                    'Invalid library name: %name' => 'invalidlibraryname',
                    'Could not find library.json file with valid json format for library %name' => 'missinglibraryjson',
                    'Invalid semantics.json file has been included in the library %name' => 'invalidsemanticsjson',
                    'Invalid language file %file in library %library' => 'invalidlanguagefile',
                    'Invalid language file %languageFile has been included in the library %name' => 'invalidlanguagefile2',
                    'The file "%file" is missing from library: "%name"' => 'missinglibraryfile',
                    'The system was unable to install the <em>%component</em> component from the package, it requires a newer version of the H5P plugin. This site is currently running version %current, whereas the required version is %required or higher. You should consider upgrading and then try again.' => 'missingcoreversion',
                    "Invalid data provided for %property in %library. Boolean expected." => 'invalidlibrarydataboolean',
                    "Invalid data provided for %property in %library" => 'invalidlibrarydata',
                    "Can't read the property %property in %library" => 'invalidlibraryproperty',
                    'The required property %property is missing from %library' => 'missinglibraryproperty',
                    'Illegal option %option in %library' => 'invalidlibraryoption',
                    'Added %new new H5P library and updated %old old one.' => 'addedandupdatedss',
                    'Added %new new H5P library and updated %old old ones.' => 'addedandupdatedsp',
                    'Added %new new H5P libraries and updated %old old one.' => 'addedandupdatedps',
                    'Added %new new H5P libraries and updated %old old ones.' => 'addedandupdatedpp',
                    'Added %new new H5P library.' => 'addednewlibrary',
                    'Added %new new H5P libraries.' => 'addednewlibraries',
                    'Updated %old H5P library.' =>  'updatedlibrary',
                    'Updated %old H5P libraries.' => 'updatedlibraries',
                    'Missing dependency @dep required by @lib.' => 'missingdependency',
                    'Provided string is not valid according to regexp in semantics. (value: \"%value\", regexp: \"%regexp\")' => 'invalidstring',
                    'File "%filename" not allowed. Only files with the following extensions are allowed: %files-allowed.' => 'invalidfile',
                    'Invalid selected option in multi-select.' => 'invalidmultiselectoption',
                    'Invalid selected option in select.' => 'invalidselectoption',
                    'H5P internal error: unknown content type "@type" in semantics. Removing content!' => 'invalidsemanticstype',
                    'Copyright information' => 'copyrightinfo',
                    'Title' => 'title',
                    'Author' => 'author',
                    'Year(s)' => 'years',
                    'Year' => 'year',
                    'Source' => 'source',
                    'License' => 'license',
                    'Undisclosed' => 'undisclosed',
                    'Attribution 4.0' => 'attribution',
                    'Attribution-ShareAlike 4.0' => 'attributionsa',
                    'Attribution-NoDerivs 4.0' => 'attributionnd',
                    'Attribution-NonCommercial 4.0' => 'attributionnc',
                    'Attribution-NonCommercial-ShareAlike 4.0' => 'attributionncsa',
                    'Attribution-NonCommercial-NoDerivs 4.0' => 'attributionncnd',
                    'Attribution' => 'noversionattribution',
                    'Attribution-ShareAlike' => 'noversionattributionsa',
                    'Attribution-NoDerivs' => 'noversionattributionnd',
                    'Attribution-NonCommercial' => 'noversionattributionnc',
                    'Attribution-NonCommercial-ShareAlike' => 'noversionattributionncsa',
                    'Attribution-NonCommercial-NoDerivs' => 'noversionattributionncnd',
                    'General Public License v3' => 'gpl',
                    'Public Domain' => 'pd',
                    'Public Domain Dedication and Licence' => 'pddl',
                    'Public Domain Mark' => 'pdm',
                    'Public Domain Mark (PDM)' => 'pdm',
                    'Copyright' => 'copyrightstring',
                    'Unable to create directory.' => 'unabletocreatedir',
                    'Unable to get field type.' => 'unabletogetfieldtype',
                    "File type isn't allowed." => 'filetypenotallowed',
                    'Invalid field type.' => 'invalidfieldtype',
                    'Invalid image file format. Use jpg, png or gif.' => 'invalidimageformat',
                    'File is not an image.' => 'filenotimage',
                    'Invalid audio file format. Use mp3 or wav.' => 'invalidaudioformat',
                    'Invalid video file format. Use mp4 or webm.' => 'invalidvideoformat',
                    'Could not save file.' => 'couldnotsave',
                    'Could not copy file.' => 'couldnotcopy',
                    'The mbstring PHP extension is not loaded. H5P need this to function properly' => 'missingmbstring',
                    'The version of the H5P library %machineName used in this content is not valid. Content contains %contentLibrary, but it should be %semanticsLibrary.' => 'wrongversion',
                    'The H5P library %library used in the content is not valid' => 'invalidlibrarynamed',
                    'Your PHP version is outdated. H5P requires version 5.2 to function properly. Version 5.6 or later is recommended.' => 'oldphpversion',
                    'Your PHP max upload size is quite small. With your current setup, you may not upload files larger than %number MB. This might be a problem when trying to upload H5Ps, images and videos. Please consider to increase it to more than 5MB.' => 'maxuploadsizetoosmall',
                    'Your PHP max post size is quite small. With your current setup, you may not upload files larger than %number MB. This might be a problem when trying to upload H5Ps, images and videos. Please consider to increase it to more than 5MB' => 'maxpostsizetoosmall',
                    'Your server does not have SSL enabled. SSL should be enabled to ensure a secure connection with the H5P hub.' => 'sslnotenabled',
                    'H5P hub communication has been disabled because one or more H5P requirements failed.' => 'hubcommunicationdisabled',
                    'When you have revised your server setup you may re-enable H5P hub communication in H5P Settings.' => 'reviseserversetupandretry',
                    'A problem with the server write access was detected. Please make sure that your server can write to your data folder.' => 'nowriteaccess',
                    'Your PHP max upload size is bigger than your max post size. This is known to cause issues in some installations.' => 'uploadsizelargerthanpostsize',
                    'Library cache was successfully updated!' => 'ctcachesuccess',
                    'No content types were received from the H5P Hub. Please try again later.' => 'ctcachenolibraries',
                    "Couldn't communicate with the H5P Hub. Please try again later." => 'ctcacheconnectionfailed',
                    'The hub is disabled. You can re-enable it in the H5P settings.' => 'hubisdisabled',
                    'File not found on server. Check file upload settings.' => 'filenotfoundonserver',
                    'Invalid security token.' => 'invalidtoken',
                    'No content type was specified.' => 'nocontenttype',
                    'The chosen content type is invalid.' => 'invalidcontenttype',
                    'You do not have permission to install content types. Contact the administrator of your site.' => 'installdenied',
                    'You do not have permission to install content types.' => 'installdenied',
                    'Validating h5p package failed.' => 'validatingh5pfailed',
                    'Failed to download the requested H5P.' => 'failedtodownloadh5p',
                    'A post message is required to access the given endpoint' => 'postmessagerequired',
                    'Could not get posted H5P.' => 'invalidh5ppost',
                    'Site could not be registered with the hub. Please contact your site administrator.' => 'sitecouldnotberegistered',
                    'The H5P Hub has been disabled until this problem can be resolved. You may still upload libraries through the "H5P Libraries" page.' => 'hubisdisableduploadlibraries',
                    'Your site was successfully registered with the H5P Hub.' => 'successfullyregisteredwithhub',
                    'You have been provided a unique key that identifies you with the Hub when receiving new updates. The key is available for viewing in the "H5P Settings" page.' => 'sitekeyregistered',
                    'Fullscreen' => 'fullscreen',
                    'Disable fullscreen' => 'disablefullscreen',
                    'Download' => 'download',
                    'Rights of use' => 'copyright',
                    'Embed' => 'embed',
                    'Size' => 'size',
                    'Show advanced' => 'showadvanced',
                    'Hide advanced' => 'hideadvanced',
                    'Include this script on your website if you want dynamic sizing of the embedded content:' => 'resizescript',
                    'Close' => 'close',
                    'Thumbnail' => 'thumbnail',
                    'No copyright information available for this content.' => 'nocopyright',
                    'Download this content as a H5P file.' => 'downloadtitle',
                    'View copyright information for this content.' => 'copyrighttitle',
                    'View the embed code for this content.' => 'embedtitle',
                    'Visit H5P.org to check out more cool content.' => 'h5ptitle',
                    'This content has changed since you last used it.' => 'contentchanged',
                    "You'll be starting over." => 'startingover',
                    'by' => 'by',
                    'Show more' => 'showmore',
                    'Show less' => 'showless',
                    'Sublevel' => 'sublevel',
                    'Confirm action' => 'confirmdialogheader',
                    'Please confirm that you wish to proceed. This action is not reversible.' => 'confirmdialogbody',
                    'Cancel' => 'cancellabel',
                    'Confirm' => 'confirmlabel',
                    '4.0 International' => 'licenseCC40',
                    '3.0 Unported' => 'licenseCC30',
                    '2.5 Generic' => 'licenseCC25',
                    '2.0 Generic' => 'licenseCC20',
                    '1.0 Generic' => 'licenseCC10',
                    'General Public License' => 'licenseGPL',
                    'Version 3' => 'licenseV3',
                    'Version 2' => 'licenseV2',
                    'Version 1' => 'licenseV1',
                    'CC0 1.0 Universal (CC0 1.0) Public Domain Dedication' => 'licenseCC010',
                    'CC0 1.0 Universal' => 'licenseCC010U',
                    'License Version' => 'licenseversion',
                    'Creative Commons' => 'creativecommons',
                    'Attribution' => 'ccattribution',
                    'Attribution (CC BY)' => 'ccattribution',
                    'Attribution-ShareAlike' => 'ccattributionsa',
                    'Attribution-ShareAlike (CC BY-SA)' => 'ccattributionsa',
                    'Attribution-NoDerivs' => 'ccattributionnd',
                    'Attribution-NoDerivs (CC BY-ND)' => 'ccattributionnd',
                    'Attribution-NonCommercial' => 'ccattributionnc',
                    'Attribution-NonCommercial (CC BY-NC)' => 'ccattributionnc',
                    'Attribution-NonCommercial-ShareAlike' => 'ccattributionncsa',
                    'Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)' => 'ccattributionncsa',
                    'Attribution-NonCommercial-NoDerivs' => 'ccattributionncnd',
                    'Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)' => 'ccattributionncnd',
                    'Public Domain Dedication' => 'ccpdd',
                    'Public Domain Dedication (CC0)' => 'ccpdd',
                    'Years (from)' => 'yearsfrom',
                    'Years (to)' => 'yearsto',
                    "Author's name" => 'authorname',
                    "Author's role" => 'authorrole',
                    'Editor' => 'editor',
                    'Licensee' => 'licensee',
                    'Originator' => 'originator',
                    'Any additional information about the license' => 'additionallicenseinfo',
                    'License Extras' => 'licenseextras',
                    'Changelog' => 'changelog',
                    'Content Type' => 'contenttype',
                    'Question' => 'question',
                    'Date' => 'date',
                    'Changed by' => 'changedby',
                    'Description of change' => 'changedescription',
                    'Photo cropped, text changed, etc.' => 'changeplaceholder',
                    'Additional Information' => 'additionalinfo',
                    'Author comments' => 'authorcomments',
                    'Comments for the editor of the content (This text will not be published as a part of copyright info)' => 'authorcommentsdescription',
                ];
                // @codingStandardsIgnoreEnd
            }
    
            return get_string($translationsmap[$message], 'hvp', $replacements);
        }
    
        /**
         * Implements getH5PPath
         */
        // @codingStandardsIgnoreLine
        public function getH5pPath() {
            global $CFG;
    
            return $CFG->dirroot . '/mod/hvp/files';
        }
    
        /**
         * Implements getLibraryFileUrl
         */
        // @codingStandardsIgnoreLine
        public function getLibraryFileUrl($libraryfoldername, $fileName) {
            global $CFG;
            $context  = \context_system::instance();
            $basepath = $CFG->httpswwwroot . '/';
            return "{$basepath}pluginfile.php/{$context->id}/mod_hvp/libraries/{$libraryfoldername}/{$fileName}";
        }
    
        /**
         * Implements getUploadedH5PFolderPath
         */
        // @codingStandardsIgnoreLine
        public function getUploadedH5pFolderPath($setpath = null) {
            static $path;
    
            if ($setpath !== null) {
                $path = $setpath;
            }
    
            if (!isset($path)) {
                throw new \coding_exception('Using getUploadedH5pFolderPath() before path is set');
            }
    
            return $path;
        }
    
        /**
         * Implements getUploadedH5PPath
         */
        // @codingStandardsIgnoreLine
        public function getUploadedH5pPath($setpath = null) {
            static $path;
    
            if ($setpath !== null) {
                $path = $setpath;
            }
    
            return $path;
        }
    
        /**
         * Implements loadLibraries
         */
        // @codingStandardsIgnoreLine
        public function loadLibraries() {
            global $DB;
    
            $results = $DB->get_records_sql(
                  "SELECT id, machine_name, title, major_version, minor_version,
                          patch_version, runnable, restricted
                     FROM {hvp_libraries}
                 ORDER BY title ASC, major_version ASC, minor_version ASC");
    
            $libraries = array();
            foreach ($results as $library) {
                $libraries[$library->machine_name][] = $library;
            }
    
            return $libraries;
        }
    
        /**
         * @inheritdoc
         */
        // @codingStandardsIgnoreLine
        public function setUnsupportedLibraries($libraries) {
            // Not supported.
        }
    
        /**
         * Implements getUnsupportedLibraries.
         */
        // @codingStandardsIgnoreLine
        public function getUnsupportedLibraries() {
            // Not supported.
        }
    
        /**
         * Implements getAdminUrl.
         */
        // @codingStandardsIgnoreLine
        public function getAdminUrl() {
            // Not supported.
        }
    
        /**
         * @inheritdoc
         */
        // @codingStandardsIgnoreLine
        public function getLibraryId($machinename, $majorversion = null, $minorversion = null) {
            global $DB;
    
            // Look for specific library.
            $sqlwhere = 'WHERE machine_name = ?';
            $sqlargs = array($machinename);
    
            if ($majorversion !== null) {
                // Look for major version.
                $sqlwhere .= ' AND major_version = ?';
                $sqlargs[] = $majorversion;
                if ($minorversion !== null) {
                    // Look for minor version.
                    $sqlwhere .= ' AND minor_version = ?';
                    $sqlargs[] = $minorversion;
                }
            }
    
            // Get the lastest version which matches the input parameters.
            $libraries = $DB->get_records_sql("
                    SELECT id
                      FROM {hvp_libraries}
              {$sqlwhere}
                  ORDER BY major_version DESC,
                           minor_version DESC,
                           patch_version DESC
                    ", $sqlargs, 0, 1);
            if ($libraries) {
                $library = reset($libraries);
                return $library ? $library->id : false;
            } else {
                return false;
            }
        }
    
        /**
         * Implements isPatchedLibrary
         */
        // @codingStandardsIgnoreLine
        public function isPatchedLibrary($library) {
            global $DB, $CFG;
    
            if (isset($CFG->mod_hvp_dev) && $CFG->mod_hvp_dev) {
                // Makes sure libraries are updated, patch version does not matter.
                return true;
            }
    
            $operator = $this->isInDevMode() ? '<=' : '<';
            $library = $DB->get_record_sql(
                    'SELECT id
                      FROM {hvp_libraries}
                        WHERE machine_name = ?
                        AND major_version = ?
                        AND minor_version = ?
                        AND patch_version ' . $operator . ' ?',
                      array($library['machineName'],
                      $library['majorVersion'],
                      $library['minorVersion'],
                      $library['patchVersion'])
            );
    
            return $library ? true : false;
        }
    
        /**
         * Implements isInDevMode
         */
        // @codingStandardsIgnoreLine
        public function isInDevMode() {
            return false; // Not supported (Files in moodle not editable).
        }
    
        /**
         * Implements mayUpdateLibraries
         */
        // @codingStandardsIgnoreLine
        public function mayUpdateLibraries($allow = false) {
            static $override;
    
            // Allow overriding the permission check. Needed when installing.
            // since caps hasn't been set.
            if ($allow) {
                $override = true;
            }
            if ($override) {
                return true;
            }
    
            // Check permissions.
            $context = \context_system::instance();
            if (!has_capability('mod/hvp:updatelibraries', $context)) {
                return false;
            }
    
            return true;
        }
    
        /**
         * Implements getLibraryUsage
         *
         * Get number of content/nodes using a library, and the number of
         * dependencies to other libraries
         *
         * @param int $id
         * @param boolean $skipcontent Optional. Set as true to get number of content instances for library.
         * @return array The array contains two elements, keyed by 'content' and 'libraries'.
         *               Each element contains a number
         */
        // @codingStandardsIgnoreLine
        public function getLibraryUsage($id, $skipcontent = false) {
            global $DB;
    
            if ($skipcontent) {
                $content = -1;
            } else {
                $content = intval($DB->get_field_sql(
                    "SELECT COUNT(distinct c.id)
                    FROM {hvp_libraries} l
                    JOIN {hvp_contents_libraries} cl ON l.id = cl.library_id
                    JOIN {hvp} c ON cl.hvp_id = c.id
                    WHERE l.id = ?", array($id)
                ));
            }
    
            $libraries = intval($DB->get_field_sql(
                "SELECT COUNT(*)
                FROM {hvp_libraries_libraries}
                WHERE required_library_id = ?", array($id)
            ));
    
            return array(
                'content' => $content,
                'libraries' => $libraries,
            );
        }
    
        /**
         * Implements getLibraryContentCount
         */
        // @codingStandardsIgnoreLine
        public function getLibraryContentCount() {
            global $DB;
            $contentcount = array();
    
            // Count content using the same content type.
            $res = $DB->get_records_sql(
              "SELECT c.main_library_id,
                      l.machine_name,
                      l.major_version,
                      l.minor_version,
                      c.count
                 FROM (SELECT main_library_id,
                              count(id) as count
                         FROM {hvp}
                     GROUP BY main_library_id) c,
                     {hvp_libraries} l
                WHERE c.main_library_id = l.id"
            );
    
            // Extract results.
            foreach ($res as $lib) {
                $contentcount["{$lib->machine_name} {$lib->major_version}.{$lib->minor_version}"] = $lib->count;
            }
    
            return $contentcount;
        }
    
        /**
         * Implements saveLibraryData
         */
        // @codingStandardsIgnoreLine
        public function saveLibraryData(&$librarydata, $new = true) {
            global $DB;
    
            // Some special properties needs some checking and converting before they can be saved.
            $preloadedjs = $this->pathsToCsv($librarydata, 'preloadedJs');
            $preloadedcss = $this->pathsToCsv($librarydata, 'preloadedCss');
            $droplibrarycss = '';
    
            if (isset($librarydata['dropLibraryCss'])) {
                $libs = array();
                foreach ($librarydata['dropLibraryCss'] as $lib) {
                    $libs[] = $lib['machineName'];
                }
                $droplibrarycss = implode(', ', $libs);
            }
    
            $embedtypes = '';
            if (isset($librarydata['embedTypes'])) {
                $embedtypes = implode(', ', $librarydata['embedTypes']);
            }
            if (!isset($librarydata['semantics'])) {
                $librarydata['semantics'] = '';
            }
            if (!isset($librarydata['fullscreen'])) {
                $librarydata['fullscreen'] = 0;
            }
            if (!isset($librarydata['hasIcon'])) {
                $librarydata['hasIcon'] = 0;
            }
            // TODO: Can we move the above code to H5PCore? It's the same for multiple
            // implementations. Perhaps core can update the data objects before calling
            // this function?
            // I think maybe it's best to do this when classes are created for
            // library, content, etc.
    
            $library = (object) array(
                'title' => $librarydata['title'],
                'machine_name' => $librarydata['machineName'],
                'major_version' => $librarydata['majorVersion'],
                'minor_version' => $librarydata['minorVersion'],
                'patch_version' => $librarydata['patchVersion'],
                'runnable' => $librarydata['runnable'],
                'fullscreen' => $librarydata['fullscreen'],
                'embed_types' => $embedtypes,
                'preloaded_js' => $preloadedjs,
                'preloaded_css' => $preloadedcss,
                'drop_library_css' => $droplibrarycss,
                'semantics' => $librarydata['semantics'],
                'has_icon' => $librarydata['hasIcon'],
                'metadata_settings' => $librarydata['metadataSettings'],
                'add_to' => isset($librarydata['addTo']) ? json_encode($librarydata['addTo']) : null,
            );
    
            if ($new) {
                // Create new library and keep track of id.
                $library->id = $DB->insert_record('hvp_libraries', $library);
                $librarydata['libraryId'] = $library->id;
            } else {
                // Update library data.
                $library->id = $librarydata['libraryId'];
    
                // Save library data.
                $DB->update_record('hvp_libraries', (object) $library);
    
                // Remove old dependencies.
                $this->deleteLibraryDependencies($librarydata['libraryId']);
            }
    
            // Log library successfully installed/upgraded.
            new \mod_hvp\event(
                  'library', ($new ? 'create' : 'update'),
                  null, null,
                  $library->machine_name, $library->major_version . '.' . $library->minor_version
            );
    
            // Update library translations.
            $DB->delete_records('hvp_libraries_languages', array('library_id' => $librarydata['libraryId']));
            if (isset($librarydata['language'])) {
                foreach ($librarydata['language'] as $languagecode => $languagejson) {
                    $DB->insert_record('hvp_libraries_languages', array(
                        'library_id' => $librarydata['libraryId'],
                        'language_code' => $languagecode,
                        'language_json' => $languagejson,
                    ));
                }
            }
        }
    
        /**
         * Convert list of file paths to csv
         *
         * @param array $librarydata
         *  Library data as found in library.json files
         * @param string $key
         *  Key that should be found in $librarydata
         * @return string
         *  file paths separated by ', '
         */
        // @codingStandardsIgnoreLine
        private function pathsToCsv($librarydata, $key) {
            if (isset($librarydata[$key])) {
                $paths = array();
                foreach ($librarydata[$key] as $file) {
                    $paths[] = $file['path'];
                }
                return implode(', ', $paths);
            }
            return '';
        }
    
        /**
         * Implements lockDependencyStorage
         */
        // @codingStandardsIgnoreLine
        public function lockDependencyStorage() {
            // Library development mode not supported.
        }
    
        /**
         * Implements unlockDependencyStorage
         */
        // @codingStandardsIgnoreLine
        public function unlockDependencyStorage() {
            // Library development mode not supported.
        }
    
        /**
         * Implements deleteLibrary
         */
        // @codingStandardsIgnoreLine
        public function deleteLibrary($library) {
            global $DB;
    
            // Delete library files.
            $librarybase = $this->getH5pPath() . '/libraries/';
            $libname = "{$library->name}-{$library->major_version}.{$library->minor_version}";
            \H5PCore::deleteFileTree("{$librarybase}{$libname}");
    
            // Remove library data from database.
            $DB->delete('hvp_libraries_libraries', array('library_id' => $library->id));
            $DB->delete('hvp_libraries_languages', array('library_id' => $library->id));
            $DB->delete('hvp_libraries', array('id' => $library->id));
        }
    
        /**
         * Implements saveLibraryDependencies
         *
         * @inheritdoc
         */
        // @codingStandardsIgnoreLine
        public function saveLibraryDependencies($libraryid, $dependencies, $dependencytype) {
            global $DB;
    
            foreach ($dependencies as $dependency) {
                // Find dependency library.
                $dependencylibrary = $DB->get_record('hvp_libraries', array(
                    'machine_name' => $dependency['machineName'],
                    'major_version' => $dependency['majorVersion'],
                    'minor_version' => $dependency['minorVersion']
                ));
    
                // Create relation.
                $DB->insert_record('hvp_libraries_libraries', array(
                    'library_id' => $libraryid,
                    'required_library_id' => $dependencylibrary->id,
                    'dependency_type' => $dependencytype
                ));
            }
        }
    
        /**
         * @inheritdoc
         */
        // @codingStandardsIgnoreLine
        public function updateContent($content, $contentmainid = null) {
            global $DB;
    
            if (!isset($content['disable'])) {
                $content['disable'] = \H5PCore::DISABLE_NONE;
            }
    
            $data = array_merge(\H5PMetadata::toDBArray($content['metadata'], false), array(
                'name' => isset($content['metadata']->title) ? $content['metadata']->title : $content['name'],
                'course' => $content['course'],
                'intro' => $content['intro'],
                'introformat' => $content['introformat'],
                'json_content' => $content['params'],
                'embed_type' => 'div',
                'main_library_id' => $content['library']['libraryId'],
                'filtered' => '',
                'disable' => $content['disable'],
                'timemodified' => time(),
            ));
    
            if (!isset($content['id'])) {
                $data['slug'] = '';
                $data['timecreated'] = $data['timemodified'];
                $eventtype = 'create';
                $id = $DB->insert_record('hvp', $data);
            } else {
                $data['id'] = $content['id'];
                $DB->update_record('hvp', $data);
                $eventtype = 'update';
                $id = $data['id'];
            }
    
            // Log content create/update/upload.
            if (!empty($content['uploaded'])) {
                $eventtype .= ' upload';
            }
            new \mod_hvp\event(
                    'content', $eventtype,
                    $id, $content['name'],
                    $content['library']['machineName'],
                    $content['library']['majorVersion'] . '.' . $content['library']['minorVersion']
            );
    
            return $id;
        }
    
        /**
         * @inheritdoc
         */
        // @codingStandardsIgnoreLine
        public function insertContent($content, $contentmainid = null) {
            return $this->updateContent($content);
        }
    
        /**
         * @inheritdoc
         */
        // @codingStandardsIgnoreLine
        public function resetContentUserData($contentid) {
            global $DB;
    
            // Reset user data for this content.
            $DB->execute("UPDATE {hvp_content_user_data}
                             SET data = 'RESET'
                           WHERE hvp_id = ?
                             AND delete_on_content_change = 1",
                         array($contentid));
        }
    
        /**
         * @inheritdoc
         */
        // @codingStandardsIgnoreLine
        public function getWhitelist($islibrary, $defaultcontentwhitelist, $defaultlibrarywhitelist) {
            return $defaultcontentwhitelist . ($islibrary ? ' ' . $defaultlibrarywhitelist : '');
        }
    
        /**
         * @inheritdoc
         */
        // @codingStandardsIgnoreLine
        public function copyLibraryUsage($contentid, $copyfromid, $contentmainid = null) {
            global $DB;
    
            $libraryusage = $DB->get_record('hvp_contents_libraries', array(
                'id' => $copyfromid
            ));
    
            $libraryusage->id = $contentid;
            $DB->insert_record_raw('hvp_contents_libraries', (array)$libraryusage, false, false, true);
    
            // TODO: This must be verified at a later time.
            // Currently in Moodle copyLibraryUsage() will never be called.
        }
    
        /**
         * @inheritdoc
         */
        // @codingStandardsIgnoreLine
        public function loadLibrarySemantics($name, $majorversion, $minorversion) {
            global $DB;
    
            $semantics = $DB->get_field_sql(
                "SELECT semantics
                FROM {hvp_libraries}
                WHERE machine_name = ?
                AND major_version = ?
                AND minor_version = ?",
                array($name, $majorversion, $minorversion));
    
            return ($semantics === false ? null : $semantics);
        }
    
        /**
         * @inheritdoc
         */
        // @codingStandardsIgnoreLine
        public function alterLibrarySemantics(&$semantics, $name, $majorversion, $minorversion) {
            global $PAGE;
    
            $PAGE->set_context(null);
    
            $renderer = $PAGE->get_renderer('mod_hvp');
            $renderer->hvp_alter_semantics($semantics, $name, $majorversion, $minorversion);
        }
    
        /**
         * Implements loadContent
         */
        // @codingStandardsIgnoreLine
        public function loadContent($id) {
            global $DB;
    
            $data = $DB->get_record_sql("
              SELECT
                hc.id,
                hc.name,
                hc.intro,
                hc.introformat,
                hc.json_content,
                hc.filtered,
                hc.slug,
                hc.embed_type,
                hc.disable,
                hl.id AS library_id,
                hl.machine_name,
                hl.major_version,
                hl.minor_version,
                hl.embed_types,
                hl.fullscreen,
                hc.name as title,
                hc.authors,
                hc.source,
                hc.license,
                hc.license_version,
                hc.license_extras,
                hc.year_from,
                hc.year_to,
                hc.changes,
                hc.author_comments
              FROM {hvp} hc
              JOIN {hvp_libraries} hl ON hl.id = hc.main_library_id
              WHERE hc.id = ?", array($id)
            );
    
            // Return null if not found.
            if ($data === false) {
                return null;
            }
    
            // Some databases do not support camelCase, so we need to manually
            // map the values to the camelCase names used by the H5P core.
            $content = array(
                'id' => $data->id,
                'title' => $data->name,
                'intro' => $data->intro,
                'introformat' => $data->introformat,
                'params' => $data->json_content,
                'filtered' => $data->filtered,
                'slug' => $data->slug,
                'embedType' => $data->embed_type,
                'disable' => $data->disable,
                'libraryId' => $data->library_id,
                'libraryName' => $data->machine_name,
                'libraryMajorVersion' => $data->major_version,
                'libraryMinorVersion' => $data->minor_version,
                'libraryEmbedTypes' => $data->embed_types,
                'libraryFullscreen' => $data->fullscreen,
            );
    
            $metadatafields = [
                'title',
                'authors',
                'source',
                'license',
                'license_version',
                'license_extras',
                'year_from',
                'year_to',
                'changes',
                'author_comments',
            ];
    
            $content['metadata'] = \H5PCore::snakeToCamel(
                array_reduce($metadatafields, function ($array, $field) use ($data) {
                    if (isset($data->$field)) {
                        $value = $data->$field;
                        // Decode json fields.
                        if (in_array($field, ['authors', 'changes'])) {
                            $value = json_decode($data->$field);
                        }
    
                        $array[$field] = $value;
                    }
                    return $array;
                }, [])
            );
    
            return $content;
        }
    
        /**
         * Implements loadContentDependencies
         */
        // @codingStandardsIgnoreLine
        public function loadContentDependencies($id, $type = null) {
            global $DB;
    
            $query = "SELECT hcl.id AS unidepid
                           , hl.id
                           , hl.machine_name
                           , hl.major_version
                           , hl.minor_version
                           , hl.patch_version
                           , hl.preloaded_css
                           , hl.preloaded_js
                           , hcl.drop_css
                           , hcl.dependency_type
                       FROM {hvp_contents_libraries} hcl
                       JOIN {hvp_libraries} hl ON hcl.library_id = hl.id
                      WHERE hcl.hvp_id = ?";
            $queryargs = array($id);
    
            if ($type !== null) {
                $query .= " AND hcl.dependency_type = ?";
                $queryargs[] = $type;
            }
    
            $query .= " ORDER BY hcl.weight";
            $data = $DB->get_records_sql($query, $queryargs);
    
            $dependencies = array();
            foreach ($data as $dependency) {
                unset($dependency->unidepid);
                $dependencies[$dependency->machine_name] = \H5PCore::snakeToCamel($dependency);
            }
    
            return $dependencies;
        }
    
        /**
         * @inheritdoc
         */
        // @codingStandardsIgnoreLine
        public function getOption($name, $default = false) {
            $value = get_config('mod_hvp', $name);
            if ($value === false) {
                return $default;
            }
            return $value;
        }
    
        /**
         * Implements setOption().
         */
        // @codingStandardsIgnoreLine
        public function setOption($name, $value) {
            set_config($name, $value, 'mod_hvp');
        }
    
        /**
         * Implements updateContentFields().
         */
        // @codingStandardsIgnoreLine
        public function updateContentFields($id, $fields) {
            global $DB;
    
            $content = new \stdClass();
            $content->id = $id;
    
            foreach ($fields as $name => $value) {
                $content->$name = $value;
            }
    
            $DB->update_record('hvp', $content);
        }
    
        /**
         * Implements deleteLibraryDependencies
         */
        // @codingStandardsIgnoreLine
        public function deleteLibraryDependencies($libraryid) {
            global $DB;
    
            $DB->delete_records('hvp_libraries_libraries', array('library_id' => $libraryid));
        }
    
        /**
         * Implements deleteContentData
         */
        // @codingStandardsIgnoreLine
        public function deleteContentData($contentid) {
            global $DB;
    
            // Remove content.
            $DB->delete_records('hvp', array('id' => $contentid));
    
            // Remove content library dependencies.
            $this->deleteLibraryUsage($contentid);
    
            // Remove user data for content.
            $DB->delete_records('hvp_content_user_data', array('hvp_id' => $contentid));
        }
    
        /**
         * Implements deleteLibraryUsage
         */
        // @codingStandardsIgnoreLine
        public function deleteLibraryUsage($contentid) {
            global $DB;
    
            $DB->delete_records('hvp_contents_libraries', array('hvp_id' => $contentid));
        }
    
        /**
         * @inheritdoc
         */
        // @codingStandardsIgnoreLine
        public function saveLibraryUsage($contentid, $librariesinuse) {
            global $DB;
    
            $droplibrarycsslist = array();
            foreach ($librariesinuse as $dependency) {
                if (!empty($dependency['library']['dropLibraryCss'])) {
                    $droplibrarycsslist = array_merge($droplibrarycsslist, explode(', ', $dependency['library']['dropLibraryCss']));
                }
            }
            // TODO: Consider moving the above code to core. Same for all impl.
    
            foreach ($librariesinuse as $dependency) {
                $dropcss = in_array($dependency['library']['machineName'], $droplibrarycsslist) ? 1 : 0;
                $DB->insert_record('hvp_contents_libraries', array(
                    'hvp_id' => $contentid,
                    'library_id' => $dependency['library']['libraryId'],
                    'dependency_type' => $dependency['type'],
                    'drop_css' => $dropcss,
                    'weight' => $dependency['weight']
                ));
            }
        }
    
        /**
         * Implements loadLibrary
         */
        // @codingStandardsIgnoreLine
        public function loadLibrary($machinename, $majorversion, $minorversion) {
            global $DB;
    
            $library = $DB->get_record('hvp_libraries', array(
                'machine_name' => $machinename,
                'major_version' => $majorversion,
                'minor_version' => $minorversion
            ));
    
            $librarydata = array(
                'libraryId' => $library->id,
                'machineName' => $library->machine_name,
                'title' => $library->title,
                'majorVersion' => $library->major_version,
                'minorVersion' => $library->minor_version,
                'patchVersion' => $library->patch_version,
                'embedTypes' => $library->embed_types,
                'preloadedJs' => $library->preloaded_js,
                'preloadedCss' => $library->preloaded_css,
                'dropLibraryCss' => $library->drop_library_css,
                'fullscreen' => $library->fullscreen,
                'runnable' => $library->runnable,
                'semantics' => $library->semantics,
                'restricted' => $library->restricted,
                'hasIcon' => $library->has_icon
            );
    
            $dependencies = $DB->get_records_sql(
                    'SELECT hl.id, hl.machine_name, hl.major_version, hl.minor_version, hll.dependency_type
                       FROM {hvp_libraries_libraries} hll
                       JOIN {hvp_libraries} hl ON hll.required_library_id = hl.id
                      WHERE hll.library_id = ?', array($library->id));
            foreach ($dependencies as $dependency) {
                $librarydata[$dependency->dependency_type . 'Dependencies'][] = array(
                    'machineName' => $dependency->machine_name,
                    'majorVersion' => $dependency->major_version,
                    'minorVersion' => $dependency->minor_version
                );
            }
    
            return $librarydata;
        }
    
        /**
         * Implements clearFilteredParameters().
         */
        // @codingStandardsIgnoreLine
        public function clearFilteredParameters($libraryid) {
            global $DB;
    
            $DB->execute("UPDATE {hvp} SET filtered = null WHERE main_library_id = ?", array($libraryid));
        }
    
        /**
         * Implements getNumNotFiltered().
         */
        // @codingStandardsIgnoreLine
        public function getNumNotFiltered() {
            global $DB;
    
            return (int) $DB->get_field_sql(
                    "SELECT COUNT(id)
                       FROM {hvp}
                      WHERE " . $DB->sql_compare_text('filtered') . " = ''");
        }
    
        /**
         * Implements getNumContent().
         */
        // @codingStandardsIgnoreLine
        public function getNumContent($libraryid) {
            global $DB;
    
            return (int) $DB->get_field_sql(
                    "SELECT COUNT(id) FROM {hvp} WHERE main_library_id = ?",
                    array($libraryid));
        }
    
        /**
         * Implements isContentSlugAvailable
         */
        // @codingStandardsIgnoreLine
        public function isContentSlugAvailable($slug) {
            global $DB;
    
            return !$DB->get_records_sql("SELECT id, slug FROM {hvp} WHERE slug = ?", array($slug));
        }
    
        /**
         * Implements saveCachedAssets
         */
        // @codingStandardsIgnoreLine
        public function saveCachedAssets($key, $libraries) {
            global $DB;
    
            foreach ($libraries as $library) {
                $cachedasset = (object) array(
                    'library_id' => $library['id'],
                    'hash' => $key
                );
                $DB->insert_record('hvp_libraries_cachedassets', $cachedasset);
            }
        }
    
        /**
         * Implements deleteCachedAssets
         */
        // @codingStandardsIgnoreLine
        public function deleteCachedAssets($libraryid) {
            global $DB;
    
            // Get all the keys so we can remove the files.
            $results = $DB->get_records_sql(
                    'SELECT hash
                       FROM {hvp_libraries_cachedassets}
                      WHERE library_id = ?',
                    array($libraryid));
    
            // Remove all invalid keys.
            $hashes = array();
            foreach ($results as $key) {
                $hashes[] = $key->hash;
                $DB->delete_records('hvp_libraries_cachedassets', array('hash' => $key->hash));
            }
    
            return $hashes;
        }
    
        /**
         * Implements getLibraryStats
         */
        // @codingStandardsIgnoreLine
        public function getLibraryStats($type) {
            global $DB;
            $count = array();
    
            // Get the counts for the given type of event.
            $records = $DB->get_records_sql(
                    "SELECT id,
                            library_name AS name,
                            library_version AS version,
                            num
                       FROM {hvp_counters}
                      WHERE type = ?",
                    array($type));
    
            // Extract num from records.
            foreach ($records as $library) {
                $count[$library->name . ' ' . $library->version] = $library->num;
            }
    
            return $count;
        }
    
        /**
         * Implements getNumAuthors
         */
        // @codingStandardsIgnoreLine
        public function getNumAuthors() {
            global $DB;
    
            // Get number of unique courses using H5P.
            return intval($DB->get_field_sql(
                    "SELECT COUNT(DISTINCT course)
                       FROM {hvp}"
            ));
        }
    
        /**
         * @inheritdoc
         */
        // @codingStandardsIgnoreLine
        public function afterExportCreated($content, $filename) {
        }
    
        /**
         * Implements hasPermission
         * @method hasPermission
         * @param  \H5PPermission $permission
         * @param  int $cmid context module id
         * @return boolean
         */
        // @codingStandardsIgnoreLine
        public function hasPermission($permission, $cmid = null) {
            switch ($permission) {
                case \H5PPermission::DOWNLOAD_H5P:
                    $cmcontext = \context_module::instance($cmid);
                    return has_capability('mod/hvp:getexport', $cmcontext);
                case \H5PPermission::CREATE_RESTRICTED:
                    return has_capability('mod/hvp:userestrictedlibraries', $this->getajaxcoursecontext());
                case \H5PPermission::UPDATE_LIBRARIES:
                    $context = \context_system::instance();
                    return has_capability('mod/hvp:updatelibraries', $context);
                case \H5PPermission::INSTALL_RECOMMENDED:
                    return has_capability('mod/hvp:installrecommendedh5plibraries', $this->getajaxcoursecontext());
                case \H5PPermission::EMBED_H5P:
                    $cmcontext = \context_module::instance($cmid);
                    return has_capability('mod/hvp:getembedcode', $cmcontext);
            }
            return false;
        }
    
        /**
         * Gets course context in AJAX
         *
         * @return bool|\context|\context_course
         */
        private function getajaxcoursecontext() {
            $context = \context::instance_by_id(required_param('contextId', PARAM_RAW));
            if ($context->contextlevel === CONTEXT_COURSE) {
                return $context;
            }
    
            return $context->get_course_context();
        }
    
        /**
         * Replaces existing content type cache with the one passed in
         *
         * @param object $contenttypecache Json with an array called 'libraries'
         *  containing the new content type cache that should replace the old one.
         */
        // @codingStandardsIgnoreLine
        public function replaceContentTypeCache($contenttypecache) {
            global $DB;
    
            // Replace existing cache.
            $DB->delete_records('hvp_libraries_hub_cache');
            foreach ($contenttypecache->contentTypes as $ct) {
                $DB->insert_record('hvp_libraries_hub_cache', (object) array(
                    'machine_name'      => $ct->id,
                    'major_version'     => $ct->version->major,
                    'minor_version'     => $ct->version->minor,
                    'patch_version'     => $ct->version->patch,
                    'h5p_major_version' => $ct->coreApiVersionNeeded->major,
                    'h5p_minor_version' => $ct->coreApiVersionNeeded->minor,
                    'title'             => $ct->title,
                    'summary'           => $ct->summary,
                    'description'       => $ct->description,
                    'icon'              => $ct->icon,
                    'created_at'        => (new \DateTime($ct->createdAt))->getTimestamp(),
                    'updated_at'        => (new \DateTime($ct->updatedAt))->getTimestamp(),
                    'is_recommended'    => $ct->isRecommended === true ? 1 : 0,
                    'popularity'        => $ct->popularity,
                    'screenshots'       => json_encode($ct->screenshots),
                    'license'           => json_encode(isset($ct->license) ? $ct->license : array()),
                    'example'           => $ct->example,
                    'tutorial'          => isset($ct->tutorial) ? $ct->tutorial : '',
                    'keywords'          => json_encode(isset($ct->keywords) ? $ct->keywords : array()),
                    'categories'        => json_encode(isset($ct->categories) ? $ct->categories : array()),
                    'owner'             => $ct->owner
                ), false, true);
            }
        }
    
        /**
         * Implements loadAddons
         */
        // @codingStandardsIgnoreLine
        public function loadAddons() {
            global $DB;
            $addons = array();
    
            $records = $DB->get_records_sql(
                    "SELECT l1.id AS library_id,
                            l1.machine_name,
                            l1.major_version,
                            l1.minor_version,
                            l1.add_to,
                            l1.preloaded_js,
                            l1.preloaded_css
                       FROM {hvp_libraries} l1
                  LEFT JOIN {hvp_libraries} l2
                         ON l1.machine_name = l2.machine_name
                        AND (l1.major_version < l2.major_version
                             OR (l1.major_version = l2.major_version
                                 AND l1.minor_version < l2.minor_version))
                      WHERE l1.add_to IS NOT NULL
                        AND l2.machine_name IS NULL");
    
            // Extract num from records.
            foreach ($records as $addon) {
                $addons[] = \H5PCore::snakeToCamel($addon);
            }
    
            return $addons;
        }
    
        /**
         * Implements getLibraryConfig
         */
        // @codingStandardsIgnoreLine
        public function getLibraryConfig($libraries = null) {
            global $CFG;
            return (isset($CFG->mod_hvp_library_config) ? $CFG->mod_hvp_library_config : null);
        }
    }