first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
<?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/>.
/**
* Abstraction of general file archives.
*
* @package core_files
* @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_files;
use core_files\local\archive_writer\file_writer_interface as file_writer_interface;
use core_files\local\archive_writer\stream_writer_interface as stream_writer_interface;
/**
* Each file archive type must extend this class.
*
* @package core_files
* @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class archive_writer {
/**
* The zip writer class.
*/
public const ZIP_WRITER = 'zip_writer';
/**
* Returns the stream writer.
*
* @param string $filename
* @param string $type
* @return stream_writer_interface
*/
public static function get_stream_writer(string $filename, string $type): stream_writer_interface {
$classname = self::get_classname_for_type($type);
if (!is_a($classname, stream_writer_interface::class, true)) {
throw new \InvalidArgumentException("{$type} does not support streaming");
}
return $classname::stream_instance($filename);
}
/**
* Returns the file writer.
*
* @param string $filepath
* @param string $type
* @return file_writer_interface
*/
public static function get_file_writer(string $filepath, string $type): file_writer_interface {
$classname = self::get_classname_for_type($type);
if (!is_a($classname, file_writer_interface::class, true)) {
throw new \InvalidArgumentException("{$type} does not support writing to files");
}
return $classname::file_instance($filepath);
}
/**
* Sanitise the file path, removing any unsuitable characters.
*
* @param string $filepath
* @return string
*/
public function sanitise_filepath(string $filepath): string {
return clean_param($filepath, PARAM_PATH);
}
/**
* Returns the class name for the type that was provided in get_file_writer().
*
* @param string $type
* @return string
*/
protected static function get_classname_for_type(string $type): string {
return "core_files\local\archive_writer\\" . $type;
}
/**
* The archive_writer Constructor.
*/
protected function __construct() {
}
/**
* Adds a file from a file path.
*
* @param string $name The path of file in archive (including directory).
* @param string $path The path to file on disk (note: paths should be encoded using
* UNIX-style forward slashes -- e.g '/path/to/some/file').
*/
abstract public function add_file_from_filepath(string $name, string $path): void;
/**
* Adds a file from a string.
*
* @param string $name The path of file in archive (including directory).
* @param string $data The contents of file
*/
abstract public function add_file_from_string(string $name, string $data): void;
/**
* Adds a file from a stream.
*
* @param string $name The path of file in archive (including directory).
* @param resource $stream The contents of file as a stream resource
*/
abstract public function add_file_from_stream(string $name, $stream): void;
/**
* Adds a stored_file to archive.
*
* @param string $name The path of file in archive (including directory).
* @param \stored_file $file
*/
abstract public function add_file_from_stored_file(string $name, \stored_file $file): void;
/**
* Finish writing the zip footer.
*/
abstract public function finish(): void;
}
+387
View File
@@ -0,0 +1,387 @@
<?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/>.
/**
* Classes for converting files between different file formats.
*
* @package core_files
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_files;
defined('MOODLE_INTERNAL') || die();
use stored_file;
/**
* Class representing a conversion currently in progress.
*
* @package core_files
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class conversion extends \core\persistent {
/**
* Status value representing a conversion waiting to start.
*/
const STATUS_PENDING = 0;
/**
* Status value representing a conversion in progress.
*/
const STATUS_IN_PROGRESS = 1;
/**
* Status value representing a successful conversion.
*/
const STATUS_COMPLETE = 2;
/**
* Status value representing a failed conversion.
*/
const STATUS_FAILED = -1;
/**
* Table name for this persistent.
*/
const TABLE = 'file_conversion';
/**
* Define properties.
*
* @return array
*/
protected static function define_properties() {
return array(
'sourcefileid' => [
'type' => PARAM_INT,
],
'targetformat' => [
'type' => PARAM_ALPHANUMEXT,
],
'status' => [
'type' => PARAM_INT,
'choices' => [
self::STATUS_PENDING,
self::STATUS_IN_PROGRESS,
self::STATUS_COMPLETE,
self::STATUS_FAILED,
],
'default' => self::STATUS_PENDING,
],
'statusmessage' => [
'type' => PARAM_RAW,
'null' => NULL_ALLOWED,
'default' => null,
],
'converter' => [
'type' => PARAM_RAW,
'null' => NULL_ALLOWED,
'default' => null,
],
'destfileid' => [
'type' => PARAM_INT,
'null' => NULL_ALLOWED,
'default' => null,
],
'data' => [
'type' => PARAM_RAW,
'null' => NULL_ALLOWED,
'default' => null,
],
);
}
/**
* Fetch all conversions relating to the specified file.
*
* Only conversions which have a valid file are returned.
*
* @param stored_file $file The source file being converted
* @param string $format The targetforamt to filter to
* @return conversion[]
*/
public static function get_conversions_for_file(stored_file $file, $format) {
global $DB;
$instances = [];
// Conversion records are intended for tracking a conversion in progress or recently completed.
// The record is removed periodically, but the destination file is not.
// We need to fetch all conversion records which match the source file and target, and also all source and
// destination files which do not have a conversion record.
$sqlfields = self::get_sql_fields('c', 'conversion');
// Fetch actual conversions which relate to the specified source file, and have a matching conversion record,
// and either have a valid destination file which still exists, or do not have a destination file at all.
$sql = "SELECT {$sqlfields}
FROM {" . self::TABLE . "} c
JOIN {files} conversionsourcefile ON conversionsourcefile.id = c.sourcefileid
LEFT JOIN {files} conversiondestfile ON conversiondestfile.id = c.destfileid
WHERE conversionsourcefile.contenthash = :ccontenthash
AND c.targetformat = :cformat
AND (c.destfileid IS NULL OR conversiondestfile.id IS NOT NULL)";
// Fetch a empty conversion record for each source/destination combination that we find to match where the
// destination file is in the correct filearea/filepath/filename combination to meet the requirements.
// This ensures that existing conversions are used where possible, even if there is no 'conversion' record for
// them.
$sql .= "
UNION ALL
SELECT
NULL AS conversionid,
orphanedsourcefile.id AS conversionsourcefileid,
:oformat AS conversiontargetformat,
2 AS conversionstatus,
NULL AS conversionstatusmessage,
NULL AS conversionconverter,
orphaneddestfile.id AS conversiondestfileid,
NULL AS conversiondata,
0 AS conversiontimecreated,
0 AS conversiontimemodified,
0 AS conversionusermodified
FROM {files} orphanedsourcefile
INNER JOIN {files} orphaneddestfile ON (
orphaneddestfile.filename = orphanedsourcefile.contenthash
AND orphaneddestfile.component = 'core'
AND orphaneddestfile.filearea = 'documentconversion'
AND orphaneddestfile.filepath = :ofilepath
)
LEFT JOIN {" . self::TABLE . "} orphanedconversion ON orphanedconversion.destfileid = orphaneddestfile.id
WHERE
orphanedconversion.id IS NULL
AND
orphanedsourcefile.id = :osourcefileid
";
$records = $DB->get_records_sql($sql, [
'ccontenthash' => $file->get_contenthash(),
'osourcefileid' => $file->get_id(),
'ofilepath' => "/{$format}/",
'cformat' => $format,
'oformat' => $format,
]);
foreach ($records as $record) {
$data = self::extract_record($record, 'conversion');
$newrecord = new static(0, $data);
$instances[] = $newrecord;
}
return $instances;
}
/**
* Remove all old conversion records.
*/
public static function remove_old_conversion_records() {
global $DB;
$DB->delete_records_select(self::TABLE, 'timemodified <= :weekagosecs', [
'weekagosecs' => time() - WEEKSECS,
]);
}
/**
* Remove orphan records.
*
* Records are considered orphans when their source file not longer exists.
* In this scenario we do not want to keep the converted file any longer,
* in particular to be compliant with privacy laws.
*/
public static function remove_orphan_records() {
global $DB;
$sql = "
SELECT c.id
FROM {" . self::TABLE . "} c
LEFT JOIN {files} f
ON f.id = c.sourcefileid
WHERE f.id IS NULL";
$ids = $DB->get_fieldset_sql($sql, []);
if (empty($ids)) {
return;
}
list($insql, $inparams) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED);
$DB->delete_records_select(self::TABLE, "id $insql", $inparams);
}
/**
* Set the source file id for the conversion.
*
* @param stored_file $file The file to convert
* @return $this
*/
public function set_sourcefile(stored_file $file) {
$this->raw_set('sourcefileid', $file->get_id());
return $this;
}
/**
* Fetch the source file.
*
* @return stored_file|false The source file
*/
public function get_sourcefile() {
$fs = get_file_storage();
return $fs->get_file_by_id($this->get('sourcefileid'));
}
/**
* Set the destination file for this conversion.
*
* @param string $filepath The path to the converted file
* @return $this
*/
public function store_destfile_from_path($filepath) {
if ($record = $this->get_file_record()) {
$fs = get_file_storage();
$existing = $fs->get_file(
$record['contextid'],
$record['component'],
$record['filearea'],
$record['itemid'],
$record['filepath'],
$record['filename']
);
if ($existing) {
$existing->delete();
}
$file = $fs->create_file_from_pathname($record, $filepath);
$this->raw_set('destfileid', $file->get_id());
}
return $this;
}
/**
* Set the destination file for this conversion.
*
* @param string $content The content of the converted file
* @return $this
*/
public function store_destfile_from_string($content) {
if ($record = $this->get_file_record()) {
$fs = get_file_storage();
$existing = $fs->get_file(
$record['contextid'],
$record['component'],
$record['filearea'],
$record['itemid'],
$record['filepath'],
$record['filename']
);
if ($existing) {
$existing->delete();
}
$file = $fs->create_file_from_string($record, $content);
$this->raw_set('destfileid', $file->get_id());
}
return $this;
}
/**
* Get the destination file.
*
* @return stored_file|bool Destination file
*/
public function get_destfile() {
$fs = get_file_storage();
return $fs->get_file_by_id($this->get('destfileid'));
}
/**
* Helper to ensure that the returned status is always an int.
*
* @return int status
*/
protected function get_status() {
return (int) $this->raw_get('status');
}
/**
* Get an instance of the current converter.
*
* @return converter_interface|false current converter instance
*/
public function get_converter_instance() {
$currentconverter = $this->get('converter');
if ($currentconverter && class_exists($currentconverter)) {
return new $currentconverter();
} else {
return false;
}
}
/**
* Transform data into a storable format.
*
* @param \stdClass $data The data to be stored
* @return $this
*/
protected function set_data($data) {
$this->raw_set('data', json_encode($data));
return $this;
}
/**
* Transform data into a storable format.
*
* @return \stdClass The stored data
*/
protected function get_data() {
$data = $this->raw_get('data');
if (!empty($data)) {
return json_decode($data);
}
return (object) [];
}
/**
* Return the file record base for use in the files table.
*
* @return array|bool
*/
protected function get_file_record() {
$file = $this->get_sourcefile();
if (!$file) {
// If the source file was removed before we completed, we must return early.
return false;
}
return [
'contextid' => \context_system::instance()->id,
'component' => 'core',
'filearea' => 'documentconversion',
'itemid' => 0,
'filepath' => "/" . $this->get('targetformat') . "/",
'filename' => $file->get_contenthash(),
];
}
}
+248
View File
@@ -0,0 +1,248 @@
<?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 for converting files between different file formats using unoconv.
*
* @package core_files
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_files;
defined('MOODLE_INTERNAL') || die();
use stored_file;
/**
* Class for converting files between different formats using unoconv.
*
* @package core_files
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class converter {
/**
* Get a list of enabled plugins and classes.
*
* @return array List of enabled plugins
*/
protected function get_enabled_plugins() {
$plugins = \core\plugininfo\fileconverter::get_enabled_plugins();
$pluginclasses = [];
foreach ($plugins as $plugin) {
$pluginclasses[$plugin] = \core\plugininfo\fileconverter::get_classname($plugin);
}
return $pluginclasses;
}
/**
* Return the file_storage API.
*
* This allows for mocking of the file_storage API.
*
* @return \file_storage
*/
protected function get_file_storage() {
return get_file_storage();
}
/**
* Start the conversion for a stored_file into a new format.
*
* @param stored_file $file The file to convert
* @param string $format The desired target file format (file extension)
* @param boolean $forcerefresh If true, the file will be converted every time (not cached).
* @return conversion conversion object
*/
public function start_conversion(stored_file $file, $format, $forcerefresh = false) {
$conversions = conversion::get_conversions_for_file($file, $format);
if ($forcerefresh || count($conversions) > 1) {
while ($conversion = array_shift($conversions)) {
if ($conversion->get('id')) {
$conversion->delete();
}
}
}
if (empty($conversions)) {
$conversion = new conversion(0, (object) [
'sourcefileid' => $file->get_id(),
'targetformat' => $format,
]);
$conversion->create();
} else {
$conversion = array_shift($conversions);
}
if ($conversion->get('status') !== conversion::STATUS_COMPLETE) {
$this->poll_conversion($conversion);
}
return $conversion;
}
/**
* Poll for updates to the supplied conversion.
*
* @param conversion $conversion The conversion in progress
* @return $this
*/
public function poll_conversion(conversion $conversion) {
$format = $conversion->get('targetformat');
$file = $conversion->get_sourcefile();
if ($conversion->get('status') == conversion::STATUS_IN_PROGRESS) {
// The current conversion is in progress.
// Check for updates.
if ($instance = $conversion->get_converter_instance()) {
$instance->poll_conversion_status($conversion);
} else {
// Unable to fetch the converter instance.
// Reset the status back to PENDING so that it may be picked up again.
$conversion->set('status', conversion::STATUS_PENDING);
}
$conversion->update();
}
// Refresh the status.
$status = $conversion->get('status');
if ($status === conversion::STATUS_PENDING || $status === conversion::STATUS_FAILED) {
// The current status is either pending or failed.
// Attempt to pick up a new converter and convert the document.
$from = pathinfo($file->get_filename(), PATHINFO_EXTENSION);
$converters = $this->get_document_converter_classes($from, $format);
$currentconverter = $this->get_next_converter($converters, $conversion->get('converter'));
if (!$currentconverter) {
// No more converters available.
$conversion->set('status', conversion::STATUS_FAILED);
$conversion->update();
return $this;
}
do {
$conversion
->set('converter', $currentconverter)
->set('status', conversion::STATUS_IN_PROGRESS)
->update();
$instance = $conversion->get_converter_instance();
$instance->start_document_conversion($conversion);
$failed = $conversion->get('status') === conversion::STATUS_FAILED;
$currentconverter = $this->get_next_converter($converters, $currentconverter);
} while ($failed && $currentconverter);
$conversion->update();
}
return $this;
}
/**
* Fetch the next converter to try.
*
* @param array $converters The list of converters to try
* @param string|null $currentconverter The converter currently in use
* @return string|false Name of next converter if present
*/
protected function get_next_converter($converters, $currentconverter = null) {
if ($currentconverter) {
$keys = moodle_array_keys_filter($converters, $currentconverter);
$key = $keys[0];
if (isset($converters[$key + 1])) {
return $converters[$key + 1];
} else {
return false;
}
} else if (!empty($converters)) {
return $converters[0];
} else {
return false;
}
}
/**
* Fetch the class for the preferred document converter.
*
* @param string $from The source target file (file extension)
* @param string $to The desired target file format (file extension)
* @return string The class for document conversion
*/
protected function get_document_converter_classes($from, $to) {
$classes = [];
$converters = $this->get_enabled_plugins();
foreach ($converters as $plugin => $classname) {
if (!class_exists($classname)) {
continue;
}
if (!$classname::are_requirements_met()) {
continue;
}
if ($classname::supports($from, $to)) {
$classes[] = $classname;
}
}
return $classes;
}
/**
* Check whether document conversion is supported for this file and target format.
*
* @param stored_file $file The file to convert
* @param string $to The desired target file format (file extension)
* @return bool Whether the target type can be converted
*/
public function can_convert_storedfile_to(stored_file $file, $to) {
if ($file->is_directory()) {
// Directories cannot be converted.
return false;
}
if (!$file->get_filesize()) {
// Empty files cannot be converted.
return false;
}
$from = pathinfo($file->get_filename(), PATHINFO_EXTENSION);
if (!$from) {
// No file extension could be found. Unable to determine converter.
return false;
}
return $this->can_convert_format_to($from, $to);
}
/**
* Check whether document conversion is supported for this file and target format.
*
* @param string $from The source target file (file extension)
* @param string $to The desired target file format (file extension)
* @return bool Whether the target type can be converted
*/
public function can_convert_format_to($from, $to) {
return !empty($this->get_document_converter_classes($from, $to));
}
}
+81
View File
@@ -0,0 +1,81 @@
<?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 for converting files between different file formats.
*
* @package core_files
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_files;
defined('MOODLE_INTERNAL') || die();
/**
* Class for converting files between different file formats.
*
* @package docconvert_unoconv
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface converter_interface {
/**
* Whether the plugin is configured and requirements are met.
*
* Note: This function may be called frequently and caching is advisable.
*
* @return bool
*/
public static function are_requirements_met();
/**
* Convert a document to a new format and return a conversion object relating to the conversion in progress.
*
* @param conversion $conversion The file to be converted
* @return $this
*/
public function start_document_conversion(conversion $conversion);
/**
* Poll an existing conversion for status update.
*
* @param conversion $conversion The file to be converted
* @return $this
*/
public function poll_conversion_status(conversion $conversion);
/**
* Determine whether a conversion between the two supplied formats is achievable.
*
* Note: This function may be called frequently and caching is advisable.
*
* @param string $from The source type
* @param string $to The destination type
* @return bool
*/
public static function supports($from, $to);
/**
* A list of the supported conversions.
*
* Note: This information is only displayed to administrators.
*
* @return string
*/
public function get_supported_conversions();
}
+116
View File
@@ -0,0 +1,116 @@
<?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/>.
/**
* This is the external method for deleting draft files.
*
* @package core_files
* @since Moodle 3.10
* @copyright 2020 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_files\external\delete;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/filelib.php');
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use context_user;
/**
* This is the external method for deleting draft files.
*
* @copyright 2020 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class draft extends external_api {
/**
* Describes the parameters for execute.
*
* @return external_function_parameters
* @since Moodle 3.10
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters (
[
'draftitemid' => new external_value(PARAM_INT, 'Item id of the draft file area'),
'files' => new external_multiple_structure(
new external_single_structure(
[
'filepath' => new external_value(PARAM_PATH, 'Path to the file or directory to delete.'),
'filename' => new external_value(PARAM_FILE, 'Name of the file to delete.'),
]
), 'Files or directories to be deleted.'
),
]
);
}
/**
* Delete the indicated files (or directories) from a user draft file area.
*
* @param int $draftitemid item id of the draft file area
* @param array $files files to be deleted
* @return array of warnings and parent paths of the files deleted
* @since Moodle 3.10
*/
public static function execute(int $draftitemid, array $files): array {
global $CFG, $USER;
require_once($CFG->dirroot . '/repository/lib.php');
$params = self::validate_parameters(self::execute_parameters(), compact('draftitemid', 'files'));
[$draftitemid, $files] = array_values($params);
$usercontext = context_user::instance($USER->id);
self::validate_context($usercontext);
$files = array_map(function($file) {
return (object) $file;
}, $files);
$parentpaths = repository_delete_selected_files($usercontext, 'user', 'draft', $draftitemid, $files);
return [
'parentpaths' => array_keys($parentpaths),
'warnings' => [],
];
}
/**
* Describes the execute return value.
*
* @return external_single_structure
* @since Moodle 3.10
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure(
[
'parentpaths' => new external_multiple_structure(
new external_value(PARAM_PATH, 'Path to parent directory of the deleted files.')
),
'warnings' => new external_warnings(),
]
);
}
}
+98
View File
@@ -0,0 +1,98 @@
<?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/>.
/**
* Generate a new draft itemid for the current user.
*
* @package core_files
* @since Moodle 3.11
* @copyright 2020 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_files\external\get;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/filelib.php');
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use context_user;
/**
* Generate a new draft itemid for the current user.
*
* @copyright 2020 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class unused_draft extends external_api {
/**
* Describes the parameters for execute.
*
* @return external_function_parameters
* @since Moodle 3.11
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters ([]);
}
/**
* Generate a new draft itemid for the current user.
*
* @return array of information containing the draft item area and possible warnings.
* @since Moodle 3.11
*/
public static function execute(): array {
global $USER;
$usercontext = context_user::instance($USER->id);
self::validate_context($usercontext);
return [
'component' => 'user',
'contextid' => $usercontext->id,
'userid' => $USER->id,
'filearea' => 'draft',
'itemid' => file_get_unused_draft_itemid(),
'warnings' => [],
];
}
/**
* Describes the execute return value.
*
* @return external_single_structure
* @since Moodle 3.11
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure(
[
'component' => new external_value(PARAM_COMPONENT, 'File area component.'),
'contextid' => new external_value(PARAM_INT, 'File area context.'),
'userid' => new external_value(PARAM_INT, 'File area user id.'),
'filearea' => new external_value(PARAM_ALPHA, 'File area name.'),
'itemid' => new external_value(PARAM_INT, 'File are item id.'),
'warnings' => new external_warnings(),
]
);
}
}
+171
View File
@@ -0,0 +1,171 @@
<?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/>.
namespace core_files\external;
use coding_exception;
use core_text;
use moodle_url;
use renderer_base;
use stdClass;
use stored_file;
/**
* Class for exporting stored_file data.
*
* @package core_files
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class stored_file_exporter extends \core\external\exporter {
/** @var int Length of the shortened filename */
protected const FILENAMESHORT_LENGTH = 25;
/** @var stored_file */
protected $file;
public function __construct(stored_file $file, $related = array()) {
$this->file = $file;
$data = new stdClass();
$data->contextid = $file->get_contextid();
$data->component = $file->get_component();
$data->filearea = $file->get_filearea();
$data->itemid = $file->get_itemid();
$data->filepath = $file->get_filepath();
$data->filename = $file->get_filename();
$data->isdir = $file->is_directory();
$data->isimage = $file->is_valid_image();
$data->timemodified = $file->get_timemodified();
$data->timecreated = $file->get_timecreated();
$data->filesize = $file->get_filesize();
$data->author = $file->get_author();
$data->license = $file->get_license();
if ($related['context']->id != $data->contextid) {
throw new coding_exception('Unexpected context ID received.');
}
parent::__construct($data, $related);
}
protected static function define_related() {
return array('context' => 'context');
}
protected static function define_properties() {
return array(
'contextid' => array(
'type' => PARAM_INT
),
'component' => array(
'type' => PARAM_COMPONENT
),
'filearea' => array(
'type' => PARAM_AREA
),
'itemid' => array(
'type' => PARAM_INT
),
'filepath' => array(
'type' => PARAM_PATH
),
'filename' => array(
'type' => PARAM_FILE
),
'isdir' => array(
'type' => PARAM_BOOL
),
'isimage' => array(
'type' => PARAM_BOOL
),
'timemodified' => array(
'type' => PARAM_INT
),
'timecreated' => array(
'type' => PARAM_INT
),
'filesize' => array(
'type' => PARAM_INT
),
'author' => array(
'type' => PARAM_TEXT
),
'license' => array(
'type' => PARAM_TEXT
)
);
}
protected static function define_other_properties() {
return array(
'filenameshort' => array(
'type' => PARAM_RAW,
),
'filesizeformatted' => array(
'type' => PARAM_RAW
),
'icon' => array(
'type' => PARAM_RAW,
),
'timecreatedformatted' => array(
'type' => PARAM_RAW
),
'timemodifiedformatted' => array(
'type' => PARAM_RAW
),
'url' => array(
'type' => PARAM_URL
),
);
}
protected function get_other_values(renderer_base $output) {
$filename = $this->file->get_filename();
$filenameshort = $filename;
if (core_text::strlen($filename) > static::FILENAMESHORT_LENGTH) {
$extension = pathinfo($filename, PATHINFO_EXTENSION);
$extensionlength = core_text::strlen($extension) + 1;
$filenameshort = core_text::substr($filename, 0, -$extensionlength);
$filenameshort = shorten_text($filenameshort, static::FILENAMESHORT_LENGTH - $extensionlength, true, '..') .
".{$extension}";
}
$icon = $this->file->is_directory() ? file_folder_icon() : file_file_icon($this->file);
$url = moodle_url::make_pluginfile_url(
$this->file->get_contextid(),
$this->file->get_component(),
$this->file->get_filearea(),
$this->file->get_itemid(),
$this->file->get_filepath(),
$this->file->get_filename(),
true
);
return array(
'filenameshort' => $filenameshort,
'filesizeformatted' => display_size((int) $this->file->get_filesize()),
'icon' => $icon,
'url' => $url->out(false),
'timecreatedformatted' => userdate($this->file->get_timecreated()),
'timemodifiedformatted' => userdate($this->file->get_timemodified()),
);
}
}
@@ -0,0 +1,50 @@
<?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/>.
/**
* Interface used by archives that write to files.
*
* @package core_files
* @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_files\local\archive_writer;
/**
* Interface used by archives that write to files.
*
* @package core_files
* @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface file_writer_interface {
/**
* Return the file instance.
*
* @param string $filename
* @return static
*/
public static function file_instance(string $filename): self;
/**
* Get the path of the zip.
*
* @return string
*/
public function get_path_to_zip(): string;
}
@@ -0,0 +1,43 @@
<?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/>.
/**
* Interface used by archives that write to streams.
*
* @package core_files
* @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_files\local\archive_writer;
/**
* Interface used by archives that write to streams.
*
* @package core_files
* @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface stream_writer_interface {
/**
* Return the stream instance.
*
* @param string $filename
* @return static
*/
public static function stream_instance(string $filename): self;
}
@@ -0,0 +1,133 @@
<?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 used for creating ZIP archives.
*
* @package core_files
* @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_files\local\archive_writer;
use ZipStream\ZipStream;
use core_files\archive_writer;
use core_files\local\archive_writer\file_writer_interface as file_writer_interface;
use core_files\local\archive_writer\stream_writer_interface as stream_writer_interface;
/**
* Class used for creating ZIP archives.
*
* @package core_files
* @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class zip_writer extends archive_writer implements file_writer_interface, stream_writer_interface {
/**
* @var resource File resource for the file handle for a file-based zip stream
*/
private $zipfilehandle = null;
/**
* @var String The location of the zip file.
*/
private $zipfilepath = null;
/**
* @var ZipStream The zip stream.
*/
private $archive;
/**
* The zip_writer constructor.
*
* @param ZipStream $archive
*/
protected function __construct(ZipStream $archive) {
parent::__construct();
$this->archive = $archive;
}
public static function stream_instance(string $filename): stream_writer_interface {
$zipwriter = new ZipStream(
outputName: $filename,
);
return new static($zipwriter);
}
public static function file_instance(string $filename): file_writer_interface {
$dir = make_request_directory();
$filepath = "$dir/$filename";
$fh = fopen($filepath, 'w');
$zipstream = new ZipStream(
outputName: $filename,
outputStream: $fh,
sendHttpHeaders: false,
);
$zipwriter = new static($zipstream);
// ZipStream only takes a file handle resource.
// It does not close this resource itself, and it does not know the location of this resource on disk.
// Store references to the filehandle, and the location of the filepath in the new class so that the `finish()`
// function can close the fh, and move the temporary file into place.
// The filehandle must be closed when finishing the archive. ZipStream does not close it automatically.
$zipwriter->zipfilehandle = $fh;
$zipwriter->zipfilepath = $filepath;
return $zipwriter;
}
public function add_file_from_filepath(string $name, string $path): void {
$this->archive->addFileFromPath($this->sanitise_filepath($name), $path);
}
public function add_file_from_string(string $name, string $data): void {
$this->archive->addFile($this->sanitise_filepath($name), $data);
}
public function add_file_from_stream(string $name, $stream): void {
$this->archive->addFileFromStream($this->sanitise_filepath($name), $stream);
fclose($stream);
}
public function add_file_from_stored_file(string $name, \stored_file $file): void {
$datetime = new \DateTime();
$datetime->setTimestamp($file->get_timemodified());
$filehandle = $file->get_content_file_handle();
$this->archive->addFileFromStream(
fileName: $this->sanitise_filepath($name),
stream: $filehandle,
lastModificationDateTime: $datetime,
);
fclose($filehandle);
}
public function finish(): void {
$this->archive->finish();
if ($this->zipfilehandle) {
fclose($this->zipfilehandle);
}
}
public function get_path_to_zip(): string {
return $this->zipfilepath;
}
}
+201
View File
@@ -0,0 +1,201 @@
<?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/>.
/**
* Data provider.
*
* @package core_files
* @copyright 2018 Frédéric Massart
* @author Frédéric Massart <fred@branchup.tech>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_files\privacy;
defined('MOODLE_INTERNAL') || die();
use core_privacy\local\metadata\collection;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\userlist;
use core_privacy\local\request\approved_userlist;
/**
* Data provider class.
*
* This only describes the files table, all components must handle the file exporting
* and deletion themselves.
*
* @package core_files
* @copyright 2018 Frédéric Massart
* @author Frédéric Massart <fred@branchup.tech>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\subsystem\plugin_provider,
\core_privacy\local\request\core_userlist_provider,
// We store a userkey for token-based file access.
\core_privacy\local\request\subsystem\provider,
\core_privacy\local\request\shared_userlist_provider {
/**
* Returns metadata.
*
* @param collection $collection The initialised collection to add items to.
* @return collection A listing of user data stored through this system.
*/
public static function get_metadata(collection $collection): collection {
$collection->add_database_table('files', [
'contenthash' => 'privacy:metadata:files:contenthash',
'filepath' => 'privacy:metadata:files:filepath',
'filename' => 'privacy:metadata:files:filename',
'userid' => 'privacy:metadata:files:userid',
'filesize' => 'privacy:metadata:files:filesize',
'mimetype' => 'privacy:metadata:files:mimetype',
'source' => 'privacy:metadata:files:source',
'author' => 'privacy:metadata:files:author',
'license' => 'privacy:metadata:files:license',
'timecreated' => 'privacy:metadata:files:timecreated',
'timemodified' => 'privacy:metadata:files:timemodified',
], 'privacy:metadata:files');
// Regarding this block, we are unable to export or purge this data, as
// it would damage the file conversion data across the whole site.
$collection->add_database_table('file_conversion', [
'usermodified' => 'privacy:metadata:file_conversion:usermodified',
], 'privacy:metadata:file_conversions');
$collection->add_subsystem_link('core_userkey', [], 'privacy:metadata:core_userkey');
return $collection;
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* This is currently just the user context.
*
* @param int $userid The user to search.
* @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
$sql = "SELECT ctx.id
FROM {user_private_key} k
JOIN {user} u ON k.userid = u.id
JOIN {context} ctx ON ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel
WHERE k.userid = :userid AND k.script = :script";
$params = [
'userid' => $userid,
'contextlevel' => CONTEXT_USER,
'script' => 'core_files',
];
$contextlist = new contextlist();
$contextlist->add_from_sql($sql, $params);
return $contextlist;
}
/**
* Get the list of users within a specific context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
if (!$context instanceof \context_user) {
return;
}
\core_userkey\privacy\provider::get_user_contexts_with_script($userlist, $context, 'core_files');
}
/**
* Export all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts to export information for.
*/
public static function export_user_data(approved_contextlist $contextlist) {
// If the user has data, then only the CONTEXT_USER should be present so get the first context.
$contexts = $contextlist->get_contexts();
if (count($contexts) == 0) {
return;
}
// Sanity check that context is at the user context level, then get the userid.
$context = reset($contexts);
if ($context->contextlevel !== CONTEXT_USER) {
return;
}
// Export associated userkeys.
$subcontext = [
get_string('files'),
];
\core_userkey\privacy\provider::export_userkeys($context, $subcontext, 'core_files');
}
/**
* Delete all use data which matches the specified deletion_criteria.
*
* @param context $context A user context.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
// Sanity check that context is at the user context level, then get the userid.
if ($context->contextlevel !== CONTEXT_USER) {
return;
}
// Delete all the userkeys.
\core_userkey\privacy\provider::delete_userkeys('core_files', $context->instanceid);
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
$context = $userlist->get_context();
if ($context instanceof \context_user) {
\core_userkey\privacy\provider::delete_userkeys('core_files', $context->instanceid);
}
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
// If the user has data, then only the user context should be present so get the first context.
$contexts = $contextlist->get_contexts();
if (count($contexts) == 0) {
return;
}
// Sanity check that context is at the user context level, then get the userid.
$context = reset($contexts);
if ($context->contextlevel !== CONTEXT_USER) {
return;
}
// Delete all the userkeys for core_files..
\core_userkey\privacy\provider::delete_userkeys('core_files', $context->instanceid);
}
}
@@ -0,0 +1,130 @@
<?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/>.
declare(strict_types=1);
namespace core_files\reportbuilder\datasource;
use core\reportbuilder\local\entities\context;
use core_files\reportbuilder\local\entities\file;
use core_reportbuilder\datasource;
use core_reportbuilder\local\entities\user;
use core_reportbuilder\local\filters\boolean_select;
/**
* Files datasource
*
* @package core_files
* @copyright 2022 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class files extends datasource {
/**
* Return user friendly name of the report source
*
* @return string
*/
public static function get_name(): string {
return get_string('files');
}
/**
* Initialise report
*/
protected function initialise(): void {
$fileentity = new file();
$filesalias = $fileentity->get_table_alias('files');
$this->set_main_table('files', $filesalias);
$this->add_entity($fileentity);
// Join the context entity.
$contextentity = new context();
$contextalias = $contextentity->get_table_alias('context');
$this->add_entity($contextentity
->add_join("LEFT JOIN {context} {$contextalias} ON {$contextalias}.id = {$filesalias}.contextid")
);
// Join the user entity.
$userentity = new user();
$useralias = $userentity->get_table_alias('user');
$this->add_entity($userentity
->add_join("LEFT JOIN {user} {$useralias} ON {$useralias}.id = {$filesalias}.userid")
);
// Add report elements from each of the entities we added to the report.
$this->add_all_from_entities();
}
/**
* Return the columns that will be added to the report upon creation
*
* @return string[]
*/
public function get_default_columns(): array {
return [
'context:name',
'user:fullname',
'file:name',
'file:type',
'file:size',
'file:timecreated',
];
}
/**
* Return the column sorting that will be added to the report upon creation
*
* @return int[]
*/
public function get_default_column_sorting(): array {
return [
'context:name' => SORT_ASC,
'file:timecreated' => SORT_ASC,
];
}
/**
* Return the filters that will be added to the report upon creation
*
* @return string[]
*/
public function get_default_filters(): array {
return [
'file:size',
'file:timecreated',
];
}
/**
* Return the conditions that will be added to the report upon creation
*
* @return string[]
*/
public function get_default_conditions(): array {
return ['file:directory'];
}
/**
* Return the condition values that will be set for the report upon creation
*
* @return array
*/
public function get_default_condition_values(): array {
return ['file:directory_operator' => boolean_select::NOT_CHECKED];
}
}
@@ -0,0 +1,446 @@
<?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/>.
declare(strict_types=1);
namespace core_files\reportbuilder\local\entities;
use context;
use context_helper;
use core_collator;
use core_filetypes;
use html_writer;
use lang_string;
use license_manager;
use stdClass;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\helpers\format;
use core_reportbuilder\local\filters\{boolean_select, date, filesize, select, text};
use core_reportbuilder\local\report\{column, filter};
/**
* File entity
*
* @package core_files
* @copyright 2022 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class file extends base {
/**
* Database tables that this entity uses
*
* @return string[]
*/
protected function get_default_tables(): array {
return [
'files',
'context',
];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('file');
}
/**
* Initialise the entity
*
* @return base
*/
public function initialise(): base {
$columns = $this->get_all_columns();
foreach ($columns as $column) {
$this->add_column($column);
}
// All the filters defined by the entity can also be used as conditions.
$filters = $this->get_all_filters();
foreach ($filters as $filter) {
$this
->add_filter($filter)
->add_condition($filter);
}
return $this;
}
/**
* Returns list of all available columns
*
* @return column[]
*/
protected function get_all_columns(): array {
$filesalias = $this->get_table_alias('files');
$contextalias = $this->get_table_alias('context');
// Name.
$columns[] = (new column(
'name',
new lang_string('filename', 'core_repository'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$filesalias}.filename")
->set_is_sortable(true);
// Size.
$columns[] = (new column(
'size',
new lang_string('size'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_INTEGER)
->add_field("{$filesalias}.filesize")
->add_field("CASE WHEN {$filesalias}.filename = '.' THEN 1 ELSE 0 END", 'directory')
->set_is_sortable(true)
->add_callback(static function($filesize, stdClass $fileinfo): string {
// Absent file size and/or directory should not return output.
if ($fileinfo->filesize === null || $fileinfo->directory) {
return '';
}
return display_size($fileinfo->filesize);
});
// Path.
$columns[] = (new column(
'path',
new lang_string('path'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$filesalias}.filepath")
->set_is_sortable(true);
// Type.
$columns[] = (new column(
'type',
new lang_string('type', 'core_repository'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$filesalias}.mimetype")
->add_field("CASE WHEN {$filesalias}.filename = '.' THEN 1 ELSE 0 END", 'directory')
->set_is_sortable(true)
->add_callback(static function($mimetype, stdClass $fileinfo): string {
global $CFG;
require_once("{$CFG->libdir}/filelib.php");
// Absent mime type and/or directory has pre-determined output.
if ($fileinfo->mimetype === null && !$fileinfo->directory) {
return '';
} else if ($fileinfo->directory) {
return get_string('directory');
}
return get_mimetype_description($fileinfo->mimetype);
});
// Icon.
$columns[] = (new column(
'icon',
new lang_string('icon'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$filesalias}.mimetype")
->add_field("CASE WHEN {$filesalias}.filename = '.' THEN 1 ELSE 0 END", 'directory')
->set_disabled_aggregation_all()
->add_callback(static function($mimetype, stdClass $fileinfo): string {
global $CFG, $OUTPUT;
require_once("{$CFG->libdir}/filelib.php");
if ($fileinfo->mimetype === null && !$fileinfo->directory) {
return '';
}
if ($fileinfo->directory) {
$icon = file_folder_icon();
$description = get_string('directory');
} else {
$icon = file_file_icon($fileinfo);
$description = get_mimetype_description($fileinfo->mimetype);
}
return $OUTPUT->pix_icon($icon, $description, 'moodle', ['class' => 'iconsize-medium']);
});
// Author.
$columns[] = (new column(
'author',
new lang_string('author', 'core_repository'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$filesalias}.author")
->set_is_sortable(true);
// License.
$columns[] = (new column(
'license',
new lang_string('license', 'core_repository'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$filesalias}.license")
->set_is_sortable(true)
->add_callback(static function(?string $license): string {
global $CFG;
require_once("{$CFG->libdir}/licenselib.php");
$licenses = license_manager::get_licenses();
if ($license === null || !array_key_exists($license, $licenses)) {
return '';
}
return $licenses[$license]->fullname;
});
// Context.
$columns[] = (new column(
'context',
new lang_string('context'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_join("LEFT JOIN {context} {$contextalias} ON {$contextalias}.id = {$filesalias}.contextid")
->add_fields("{$filesalias}.contextid, " . context_helper::get_preload_record_columns_sql($contextalias))
// Sorting may not order alphabetically, but will at least group contexts together.
->set_is_sortable(true)
->set_is_deprecated('See \'context:name\' for replacement')
->add_callback(static function($contextid, stdClass $context): string {
if ($contextid === null) {
return '';
}
context_helper::preload_from_record($context);
return context::instance_by_id($contextid)->get_context_name();
});
// Context link.
$columns[] = (new column(
'contexturl',
new lang_string('contexturl'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_join("LEFT JOIN {context} {$contextalias} ON {$contextalias}.id = {$filesalias}.contextid")
->add_fields("{$filesalias}.contextid, " . context_helper::get_preload_record_columns_sql($contextalias))
// Sorting may not order alphabetically, but will at least group contexts together.
->set_is_sortable(true)
->set_is_deprecated('See \'context:link\' for replacement')
->add_callback(static function($contextid, stdClass $context): string {
if ($contextid === null) {
return '';
}
context_helper::preload_from_record($context);
$context = context::instance_by_id($contextid);
return html_writer::link($context->get_url(), $context->get_context_name());
});
// Content hash.
$columns[] = (new column(
'contenthash',
new lang_string('contenthash', 'core_files'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$filesalias}.contenthash")
->set_is_sortable(true);
// Component.
$columns[] = (new column(
'component',
new lang_string('plugin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$filesalias}.component")
->set_is_sortable(true);
// Area.
$columns[] = (new column(
'area',
new lang_string('pluginarea'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$filesalias}.filearea")
->set_is_sortable(true);
// Item ID.
$columns[] = (new column(
'itemid',
new lang_string('pluginitemid'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_INTEGER)
->add_fields("{$filesalias}.itemid")
->set_is_sortable(true)
->set_disabled_aggregation_all();
// Time created.
$columns[] = (new column(
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_field("{$filesalias}.timecreated")
->add_callback([format::class, 'userdate'])
->set_is_sortable(true);
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
$filesalias = $this->get_table_alias('files');
// Directory.
$filters[] = (new filter(
boolean_select::class,
'directory',
new lang_string('directory'),
$this->get_entity_name(),
"CASE WHEN {$filesalias}.filename = '.' THEN 1 ELSE 0 END"
))
->add_joins($this->get_joins());
// Draft.
$filters[] = (new filter(
boolean_select::class,
'draft',
new lang_string('areauserdraft', 'core_repository'),
$this->get_entity_name(),
"CASE WHEN {$filesalias}.component = 'user' AND {$filesalias}.filearea = 'draft' THEN 1 ELSE 0 END"
))
->add_joins($this->get_joins());
// Name.
$filters[] = (new filter(
text::class,
'name',
new lang_string('filename', 'core_repository'),
$this->get_entity_name(),
"{$filesalias}.filename"
))
->add_joins($this->get_joins());
// Size.
$filters[] = (new filter(
filesize::class,
'size',
new lang_string('size'),
$this->get_entity_name(),
"{$filesalias}.filesize"
))
->add_joins($this->get_joins());
// Type.
$filters[] = (new filter(
select::class,
'type',
new lang_string('type', 'core_repository'),
$this->get_entity_name(),
"{$filesalias}.mimetype"
))
->add_joins($this->get_joins())
->set_options_callback(static function(): array {
$mimetypenames = array_column(core_filetypes::get_types(), 'type');
// Convert the names into a map of name => description.
$mimetypes = array_combine($mimetypenames, array_map(static function(string $mimetype): string {
return get_mimetype_description($mimetype);
}, $mimetypenames));
core_collator::asort($mimetypes);
return $mimetypes;
});
// License (consider null = 'unknown/license not specified' for filtering purposes).
$filters[] = (new filter(
select::class,
'license',
new lang_string('license', 'core_repository'),
$this->get_entity_name(),
"COALESCE({$filesalias}.license, 'unknown')"
))
->add_joins($this->get_joins())
->set_options_callback(static function(): array {
global $CFG;
require_once("{$CFG->libdir}/licenselib.php");
$licenses = license_manager::get_licenses();
return array_map(static function(stdClass $license): string {
return $license->fullname;
}, $licenses);
});
// Content hash.
$filters[] = (new filter(
text::class,
'contenthash',
new lang_string('contenthash', 'core_files'),
$this->get_entity_name(),
"{$filesalias}.contenthash"
))
->add_joins($this->get_joins());
// Time created.
$filters[] = (new filter(
date::class,
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name(),
"{$filesalias}.timecreated"
))
->add_joins($this->get_joins())
->set_limited_operators([
date::DATE_ANY,
date::DATE_RANGE,
date::DATE_LAST,
date::DATE_CURRENT,
]);
return $filters;
}
}
@@ -0,0 +1,54 @@
<?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/>.
/**
* A scheduled task to clear up old conversion records.
*
* @package core_files
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_files\task;
defined('MOODLE_INTERNAL') || die();
/**
* A scheduled task to clear up old conversion records.
*
* @package core_files
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class conversion_cleanup_task extends \core\task\scheduled_task {
/**
* Get a descriptive name for this task (shown to admins).
*
* @return string
*/
public function get_name() {
return get_string('fileconversioncleanuptask', 'admin');
}
/**
* Run task.
*/
public function execute() {
\core_files\conversion::remove_old_conversion_records();
\core_files\conversion::remove_orphan_records();
}
}