2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Class for converting files between different file formats using unoconv.
21 * @copyright 2017 Damyon Wiese
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') ||
die();
31 * Class for converting files between different formats using unoconv.
34 * @copyright 2017 Damyon Wiese
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40 * Get a list of enabled plugins and classes.
42 * @return array List of enabled plugins
44 protected function get_enabled_plugins() {
45 $plugins = \core\plugininfo\fileconverter
::get_enabled_plugins();
48 foreach ($plugins as $plugin) {
49 $pluginclasses[$plugin] = \core\plugininfo\fileconverter
::get_classname($plugin);
52 return $pluginclasses;
56 * Return the file_storage API.
58 * This allows for mocking of the file_storage API.
60 * @return \file_storage
62 protected function get_file_storage() {
63 return get_file_storage();
67 * Start the conversion for a stored_file into a new format.
69 * @param stored_file $file The file to convert
70 * @param string $format The desired target file format (file extension)
71 * @param boolean $forcerefresh If true, the file will be converted every time (not cached).
72 * @return conversion conversion object
74 public function start_conversion(stored_file
$file, $format, $forcerefresh = false) {
75 $conversions = conversion
::get_conversions_for_file($file, $format);
77 if ($forcerefresh ||
count($conversions) > 1) {
78 while ($conversion = array_shift($conversions)) {
79 if ($conversion->get('id')) {
80 $conversion->delete();
85 if (empty($conversions)) {
86 $conversion = new conversion(0, (object) [
87 'sourcefileid' => $file->get_id(),
88 'targetformat' => $format,
90 $conversion->create();
92 $conversion = array_shift($conversions);
95 if ($conversion->get('status') !== conversion
::STATUS_COMPLETE
) {
96 $this->poll_conversion($conversion);
103 * Poll for updates to the supplied conversion.
105 * @param conversion $conversion The conversion in progress
108 public function poll_conversion(conversion
$conversion) {
109 $format = $conversion->get('targetformat');
110 $file = $conversion->get_sourcefile();
112 if ($conversion->get('status') == conversion
::STATUS_IN_PROGRESS
) {
113 // The current conversion is in progress.
114 // Check for updates.
115 if ($instance = $conversion->get_converter_instance()) {
116 $instance->poll_conversion_status($conversion);
118 // Unable to fetch the converter instance.
119 // Reset the status back to PENDING so that it may be picked up again.
120 $conversion->set('status', conversion
::STATUS_PENDING
);
122 $conversion->update();
125 // Refresh the status.
126 $status = $conversion->get('status');
127 if ($status === conversion
::STATUS_PENDING ||
$status === conversion
::STATUS_FAILED
) {
128 // The current status is either pending or failed.
129 // Attempt to pick up a new converter and convert the document.
130 $from = pathinfo($file->get_filename(), PATHINFO_EXTENSION
);
131 $converters = $this->get_document_converter_classes($from, $format);
132 $currentconverter = $this->get_next_converter($converters, $conversion->get('converter'));
134 if (!$currentconverter) {
135 // No more converters available.
136 $conversion->set('status', conversion
::STATUS_FAILED
);
137 $conversion->update();
143 ->set('converter', $currentconverter)
144 ->set('status', conversion
::STATUS_IN_PROGRESS
)
147 $instance = $conversion->get_converter_instance();
148 $instance->start_document_conversion($conversion);
149 $failed = $conversion->get('status') === conversion
::STATUS_FAILED
;
150 $currentconverter = $this->get_next_converter($converters, $currentconverter);
151 } while ($failed && $currentconverter);
153 $conversion->update();
160 * Fetch the next converter to try.
162 * @param array $converters The list of converters to try
163 * @param string|null $currentconverter The converter currently in use
164 * @return string|false Name of next converter if present
166 protected function get_next_converter($converters, $currentconverter = null) {
167 if ($currentconverter) {
168 $keys = array_keys($converters, $currentconverter);
170 if (isset($converters[$key +
1])) {
171 return $converters[$key +
1];
175 } else if (!empty($converters)) {
176 return $converters[0];
183 * Fetch the class for the preferred document converter.
185 * @param string $from The source target file (file extension)
186 * @param string $to The desired target file format (file extension)
187 * @return string The class for document conversion
189 protected function get_document_converter_classes($from, $to) {
192 $converters = $this->get_enabled_plugins();
193 foreach ($converters as $plugin => $classname) {
194 if (!class_exists($classname)) {
198 if (!$classname::are_requirements_met()) {
202 if ($classname::supports($from, $to)) {
203 $classes[] = $classname;
211 * Check whether document conversion is supported for this file and target format.
213 * @param stored_file $file The file to convert
214 * @param string $to The desired target file format (file extension)
215 * @return bool Whether the target type can be converted
217 public function can_convert_storedfile_to(stored_file
$file, $to) {
218 if ($file->is_directory()) {
219 // Directories cannot be converted.
223 if (!$file->get_filesize()) {
224 // Empty files cannot be converted.
228 $from = pathinfo($file->get_filename(), PATHINFO_EXTENSION
);
230 // No file extension could be found. Unable to determine converter.
234 return $this->can_convert_format_to($from, $to);
238 * Check whether document conversion is supported for this file and target format.
240 * @param string $from The source target file (file extension)
241 * @param string $to The desired target file format (file extension)
242 * @return bool Whether the target type can be converted
244 public function can_convert_format_to($from, $to) {
245 return !empty($this->get_document_converter_classes($from, $to));