Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Class for converting files between different file formats using unoconv.
19
 *
20
 * @package    core_files
21
 * @copyright  2017 Damyon Wiese
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
namespace core_files;
25
 
26
defined('MOODLE_INTERNAL') || die();
27
 
28
use stored_file;
29
 
30
/**
31
 * Class for converting files between different formats using unoconv.
32
 *
33
 * @package    core_files
34
 * @copyright  2017 Damyon Wiese
35
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36
 */
37
class converter {
38
 
39
    /**
40
     * Get a list of enabled plugins and classes.
41
     *
42
     * @return  array List of enabled plugins
43
     */
44
    protected function get_enabled_plugins() {
45
        $plugins = \core\plugininfo\fileconverter::get_enabled_plugins();
46
 
47
        $pluginclasses = [];
48
        foreach ($plugins as $plugin) {
49
            $pluginclasses[$plugin] = \core\plugininfo\fileconverter::get_classname($plugin);
50
        }
51
 
52
        return $pluginclasses;
53
    }
54
 
55
    /**
56
     * Return the file_storage API.
57
     *
58
     * This allows for mocking of the file_storage API.
59
     *
60
     * @return \file_storage
61
     */
62
    protected function get_file_storage() {
63
        return get_file_storage();
64
    }
65
 
66
    /**
67
     * Start the conversion for a stored_file into a new format.
68
     *
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
73
     */
74
    public function start_conversion(stored_file $file, $format, $forcerefresh = false) {
75
        $conversions = conversion::get_conversions_for_file($file, $format);
76
 
77
        if ($forcerefresh || count($conversions) > 1) {
78
            while ($conversion = array_shift($conversions)) {
79
                if ($conversion->get('id')) {
80
                    $conversion->delete();
81
                }
82
            }
83
        }
84
 
85
        if (empty($conversions)) {
86
            $conversion = new conversion(0, (object) [
87
                'sourcefileid' => $file->get_id(),
88
                'targetformat' => $format,
89
            ]);
90
            $conversion->create();
91
        } else {
92
            $conversion = array_shift($conversions);
93
        }
94
 
95
        if ($conversion->get('status') !== conversion::STATUS_COMPLETE) {
96
            $this->poll_conversion($conversion);
97
        }
98
 
99
        return $conversion;
100
    }
101
 
102
    /**
103
     * Poll for updates to the supplied conversion.
104
     *
105
     * @param   conversion $conversion The conversion in progress
106
     * @return  $this
107
     */
108
    public function poll_conversion(conversion $conversion) {
109
        $format = $conversion->get('targetformat');
110
        $file = $conversion->get_sourcefile();
111
 
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);
117
            } else {
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);
121
            }
122
            $conversion->update();
123
        }
124
 
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'));
133
 
134
            if (!$currentconverter) {
135
                // No more converters available.
136
                $conversion->set('status', conversion::STATUS_FAILED);
137
                $conversion->update();
138
                return $this;
139
            }
140
 
141
            do {
142
                $conversion
143
                    ->set('converter', $currentconverter)
144
                    ->set('status', conversion::STATUS_IN_PROGRESS)
145
                    ->update();
146
 
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);
152
 
153
            $conversion->update();
154
        }
155
 
156
        return $this;
157
    }
158
 
159
    /**
160
     * Fetch the next converter to try.
161
     *
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
165
     */
166
    protected function get_next_converter($converters, $currentconverter = null) {
167
        if ($currentconverter) {
168
            $keys = moodle_array_keys_filter($converters, $currentconverter);
169
            $key = $keys[0];
170
            if (isset($converters[$key + 1])) {
171
                return $converters[$key + 1];
172
            } else {
173
                return false;
174
            }
175
        } else if (!empty($converters)) {
176
            return $converters[0];
177
        } else {
178
            return false;
179
        }
180
    }
181
 
182
    /**
183
     * Fetch the class for the preferred document converter.
184
     *
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
188
     */
189
    protected function get_document_converter_classes($from, $to) {
190
        $classes = [];
191
 
192
        $converters = $this->get_enabled_plugins();
193
        foreach ($converters as $plugin => $classname) {
194
            if (!class_exists($classname)) {
195
                continue;
196
            }
197
 
198
            if (!$classname::are_requirements_met()) {
199
                continue;
200
            }
201
 
202
            if ($classname::supports($from, $to)) {
203
                $classes[] = $classname;
204
            }
205
        }
206
 
207
        return $classes;
208
    }
209
 
210
    /**
211
     * Check whether document conversion is supported for this file and target format.
212
     *
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
216
     */
217
    public function can_convert_storedfile_to(stored_file $file, $to) {
218
        if ($file->is_directory()) {
219
            // Directories cannot be converted.
220
            return false;
221
        }
222
 
223
        if (!$file->get_filesize()) {
224
            // Empty files cannot be converted.
225
            return false;
226
        }
227
 
228
        $from = pathinfo($file->get_filename(), PATHINFO_EXTENSION);
229
        if (!$from) {
230
            // No file extension could be found. Unable to determine converter.
231
            return false;
232
        }
233
 
234
        return $this->can_convert_format_to($from, $to);
235
    }
236
 
237
    /**
238
     * Check whether document conversion is supported for this file and target format.
239
     *
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
243
     */
244
    public function can_convert_format_to($from, $to) {
245
        return !empty($this->get_document_converter_classes($from, $to));
246
    }
247
 
248
}