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 |
/**
|
|
|
19 |
* Definition of a class stored_file.
|
|
|
20 |
*
|
|
|
21 |
* @package core_files
|
|
|
22 |
* @copyright 2008 Petr Skoda {@link http://skodak.org}
|
|
|
23 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
24 |
*/
|
|
|
25 |
|
|
|
26 |
use Psr\Http\Message\StreamInterface;
|
|
|
27 |
|
|
|
28 |
defined('MOODLE_INTERNAL') || die();
|
|
|
29 |
|
|
|
30 |
require_once($CFG->dirroot . '/lib/filestorage/file_progress.php');
|
|
|
31 |
require_once($CFG->dirroot . '/lib/filestorage/file_system.php');
|
|
|
32 |
|
|
|
33 |
/**
|
|
|
34 |
* Class representing local files stored in a sha1 file pool.
|
|
|
35 |
*
|
|
|
36 |
* Since Moodle 2.0 file contents are stored in sha1 pool and
|
|
|
37 |
* all other file information is stored in new "files" database table.
|
|
|
38 |
*
|
|
|
39 |
* @package core_files
|
|
|
40 |
* @category files
|
|
|
41 |
* @copyright 2008 Petr Skoda {@link http://skodak.org}
|
|
|
42 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
43 |
* @since Moodle 2.0
|
|
|
44 |
*/
|
|
|
45 |
class stored_file {
|
|
|
46 |
/** @var file_storage file storage pool instance */
|
|
|
47 |
private $fs;
|
|
|
48 |
/** @var stdClass record from the files table left join files_reference table */
|
|
|
49 |
private $file_record;
|
|
|
50 |
/** @var repository repository plugin instance */
|
|
|
51 |
private $repository;
|
|
|
52 |
/** @var file_system filesystem instance */
|
|
|
53 |
private $filesystem;
|
|
|
54 |
|
|
|
55 |
/**
|
|
|
56 |
* @var int Indicates a file handle of the type returned by fopen.
|
|
|
57 |
*/
|
|
|
58 |
const FILE_HANDLE_FOPEN = 0;
|
|
|
59 |
|
|
|
60 |
/**
|
|
|
61 |
* @var int Indicates a file handle of the type returned by gzopen.
|
|
|
62 |
*/
|
|
|
63 |
const FILE_HANDLE_GZOPEN = 1;
|
|
|
64 |
|
|
|
65 |
|
|
|
66 |
/**
|
|
|
67 |
* Constructor, this constructor should be called ONLY from the file_storage class!
|
|
|
68 |
*
|
|
|
69 |
* @param file_storage $fs file storage instance
|
|
|
70 |
* @param stdClass $file_record description of file
|
|
|
71 |
* @param string $deprecated
|
|
|
72 |
*/
|
|
|
73 |
public function __construct(file_storage $fs, stdClass $file_record, $deprecated = null) {
|
|
|
74 |
global $DB, $CFG;
|
|
|
75 |
$this->fs = $fs;
|
|
|
76 |
$this->file_record = clone($file_record); // prevent modifications
|
|
|
77 |
|
|
|
78 |
if (!empty($file_record->repositoryid)) {
|
|
|
79 |
require_once("$CFG->dirroot/repository/lib.php");
|
|
|
80 |
$this->repository = repository::get_repository_by_id($file_record->repositoryid, SYSCONTEXTID);
|
|
|
81 |
if ($this->repository->supported_returntypes() & FILE_REFERENCE != FILE_REFERENCE) {
|
|
|
82 |
// Repository cannot do file reference.
|
|
|
83 |
throw new moodle_exception('error');
|
|
|
84 |
}
|
|
|
85 |
} else {
|
|
|
86 |
$this->repository = null;
|
|
|
87 |
}
|
|
|
88 |
// make sure all reference fields exist in file_record even when it is not a reference
|
|
|
89 |
foreach (array('referencelastsync', 'referencefileid', 'reference', 'repositoryid') as $key) {
|
|
|
90 |
if (empty($this->file_record->$key)) {
|
|
|
91 |
$this->file_record->$key = null;
|
|
|
92 |
}
|
|
|
93 |
}
|
|
|
94 |
|
|
|
95 |
$this->filesystem = $fs->get_file_system();
|
|
|
96 |
}
|
|
|
97 |
|
|
|
98 |
/**
|
|
|
99 |
* Magic method, called during serialization.
|
|
|
100 |
*
|
|
|
101 |
* @return array
|
|
|
102 |
*/
|
|
|
103 |
public function __sleep() {
|
|
|
104 |
// We only ever want the file_record saved, not the file_storage object.
|
|
|
105 |
return ['file_record'];
|
|
|
106 |
}
|
|
|
107 |
|
|
|
108 |
/**
|
|
|
109 |
* Magic method, called during unserialization.
|
|
|
110 |
*/
|
|
|
111 |
public function __wakeup() {
|
|
|
112 |
// Recreate our stored_file based on the file_record, and using file storage retrieved the correct way.
|
|
|
113 |
$this->__construct(get_file_storage(), $this->file_record);
|
|
|
114 |
}
|
|
|
115 |
|
|
|
116 |
/**
|
|
|
117 |
* Whether or not this is a external resource
|
|
|
118 |
*
|
|
|
119 |
* @return bool
|
|
|
120 |
*/
|
|
|
121 |
public function is_external_file() {
|
|
|
122 |
return !empty($this->repository);
|
|
|
123 |
}
|
|
|
124 |
|
|
|
125 |
/**
|
|
|
126 |
* Whether or not this is a controlled link. Note that repositories cannot support FILE_REFERENCE and FILE_CONTROLLED_LINK.
|
|
|
127 |
*
|
|
|
128 |
* @return bool
|
|
|
129 |
*/
|
|
|
130 |
public function is_controlled_link() {
|
|
|
131 |
return $this->is_external_file() && $this->repository->supported_returntypes() & FILE_CONTROLLED_LINK;
|
|
|
132 |
}
|
|
|
133 |
|
|
|
134 |
/**
|
|
|
135 |
* Update some file record fields
|
|
|
136 |
* NOTE: Must remain protected
|
|
|
137 |
*
|
|
|
138 |
* @param stdClass $dataobject
|
|
|
139 |
*/
|
|
|
140 |
protected function update($dataobject) {
|
|
|
141 |
global $DB;
|
|
|
142 |
$updatereferencesneeded = false;
|
|
|
143 |
$updatemimetype = false;
|
|
|
144 |
$keys = array_keys((array)$this->file_record);
|
|
|
145 |
$filepreupdate = clone($this->file_record);
|
|
|
146 |
foreach ($dataobject as $field => $value) {
|
|
|
147 |
if (in_array($field, $keys)) {
|
|
|
148 |
if ($field == 'contextid' and (!is_number($value) or $value < 1)) {
|
|
|
149 |
throw new file_exception('storedfileproblem', 'Invalid contextid');
|
|
|
150 |
}
|
|
|
151 |
|
|
|
152 |
if ($field == 'component') {
|
|
|
153 |
$value = clean_param($value, PARAM_COMPONENT);
|
|
|
154 |
if (empty($value)) {
|
|
|
155 |
throw new file_exception('storedfileproblem', 'Invalid component');
|
|
|
156 |
}
|
|
|
157 |
}
|
|
|
158 |
|
|
|
159 |
if ($field == 'filearea') {
|
|
|
160 |
$value = clean_param($value, PARAM_AREA);
|
|
|
161 |
if (empty($value)) {
|
|
|
162 |
throw new file_exception('storedfileproblem', 'Invalid filearea');
|
|
|
163 |
}
|
|
|
164 |
}
|
|
|
165 |
|
|
|
166 |
if ($field == 'itemid' and (!is_number($value) or $value < 0)) {
|
|
|
167 |
throw new file_exception('storedfileproblem', 'Invalid itemid');
|
|
|
168 |
}
|
|
|
169 |
|
|
|
170 |
|
|
|
171 |
if ($field == 'filepath') {
|
|
|
172 |
$value = clean_param($value, PARAM_PATH);
|
|
|
173 |
if (strpos($value, '/') !== 0 or strrpos($value, '/') !== strlen($value)-1) {
|
|
|
174 |
// path must start and end with '/'
|
|
|
175 |
throw new file_exception('storedfileproblem', 'Invalid file path');
|
|
|
176 |
}
|
|
|
177 |
}
|
|
|
178 |
|
|
|
179 |
if ($field == 'filename') {
|
|
|
180 |
// folder has filename == '.', so we pass this
|
|
|
181 |
if ($value != '.') {
|
|
|
182 |
$value = clean_param($value, PARAM_FILE);
|
|
|
183 |
}
|
|
|
184 |
if ($value === '') {
|
|
|
185 |
throw new file_exception('storedfileproblem', 'Invalid file name');
|
|
|
186 |
}
|
|
|
187 |
}
|
|
|
188 |
|
|
|
189 |
if ($field === 'timecreated' or $field === 'timemodified') {
|
|
|
190 |
if (!is_number($value)) {
|
|
|
191 |
throw new file_exception('storedfileproblem', 'Invalid timestamp');
|
|
|
192 |
}
|
|
|
193 |
if ($value < 0) {
|
|
|
194 |
$value = 0;
|
|
|
195 |
}
|
|
|
196 |
}
|
|
|
197 |
|
|
|
198 |
if ($field === 'referencefileid') {
|
|
|
199 |
if (!is_null($value) and !is_number($value)) {
|
|
|
200 |
throw new file_exception('storedfileproblem', 'Invalid reference info');
|
|
|
201 |
}
|
|
|
202 |
}
|
|
|
203 |
|
|
|
204 |
if (($field == 'contenthash' || $field == 'filesize') && $this->file_record->$field != $value) {
|
|
|
205 |
$updatereferencesneeded = true;
|
|
|
206 |
}
|
|
|
207 |
|
|
|
208 |
if ($updatereferencesneeded || ($field === 'filename' && $this->file_record->filename != $value)) {
|
|
|
209 |
$updatemimetype = true;
|
|
|
210 |
}
|
|
|
211 |
|
|
|
212 |
// adding the field
|
|
|
213 |
$this->file_record->$field = $value;
|
|
|
214 |
} else {
|
|
|
215 |
throw new coding_exception("Invalid field name, $field doesn't exist in file record");
|
|
|
216 |
}
|
|
|
217 |
}
|
|
|
218 |
// Validate mimetype field
|
|
|
219 |
if ($updatemimetype) {
|
|
|
220 |
$mimetype = $this->filesystem->mimetype_from_storedfile($this);
|
|
|
221 |
$this->file_record->mimetype = $mimetype;
|
|
|
222 |
}
|
|
|
223 |
|
|
|
224 |
$DB->update_record('files', $this->file_record);
|
|
|
225 |
if ($updatereferencesneeded) {
|
|
|
226 |
// Either filesize or contenthash of this file have changed. Update all files that reference to it.
|
|
|
227 |
$this->fs->update_references_to_storedfile($this);
|
|
|
228 |
}
|
|
|
229 |
|
|
|
230 |
// Callback for file update.
|
|
|
231 |
if (!$this->is_directory()) {
|
|
|
232 |
if ($pluginsfunction = get_plugins_with_function('after_file_updated')) {
|
|
|
233 |
foreach ($pluginsfunction as $plugintype => $plugins) {
|
|
|
234 |
foreach ($plugins as $pluginfunction) {
|
|
|
235 |
$pluginfunction($this->file_record, $filepreupdate);
|
|
|
236 |
}
|
|
|
237 |
}
|
|
|
238 |
}
|
|
|
239 |
}
|
|
|
240 |
}
|
|
|
241 |
|
|
|
242 |
/**
|
|
|
243 |
* Rename filename
|
|
|
244 |
*
|
|
|
245 |
* @param string $filepath file path
|
|
|
246 |
* @param string $filename file name
|
|
|
247 |
*/
|
|
|
248 |
public function rename($filepath, $filename) {
|
|
|
249 |
if ($this->fs->file_exists($this->get_contextid(), $this->get_component(), $this->get_filearea(), $this->get_itemid(), $filepath, $filename)) {
|
|
|
250 |
$a = new stdClass();
|
|
|
251 |
$a->contextid = $this->get_contextid();
|
|
|
252 |
$a->component = $this->get_component();
|
|
|
253 |
$a->filearea = $this->get_filearea();
|
|
|
254 |
$a->itemid = $this->get_itemid();
|
|
|
255 |
$a->filepath = $filepath;
|
|
|
256 |
$a->filename = $filename;
|
|
|
257 |
throw new file_exception('storedfilenotcreated', $a, 'file exists, cannot rename');
|
|
|
258 |
}
|
|
|
259 |
$filerecord = new stdClass;
|
|
|
260 |
$filerecord->filepath = $filepath;
|
|
|
261 |
$filerecord->filename = $filename;
|
|
|
262 |
// populate the pathname hash
|
|
|
263 |
$filerecord->pathnamehash = $this->fs->get_pathname_hash($this->file_record->contextid, $this->file_record->component, $this->file_record->filearea, $this->file_record->itemid, $filepath, $filename);
|
|
|
264 |
$this->update($filerecord);
|
|
|
265 |
}
|
|
|
266 |
|
|
|
267 |
/**
|
|
|
268 |
* Function stored_file::replace_content_with() is deprecated. Please use stored_file::replace_file_with()
|
|
|
269 |
*
|
|
|
270 |
* @deprecated since Moodle 2.6 MDL-42016 - please do not use this function any more.
|
|
|
271 |
* @see stored_file::replace_file_with()
|
|
|
272 |
*/
|
|
|
273 |
public function replace_content_with(stored_file $storedfile) {
|
|
|
274 |
throw new coding_exception('Function stored_file::replace_content_with() can not be used any more . ' .
|
|
|
275 |
'Please use stored_file::replace_file_with()');
|
|
|
276 |
}
|
|
|
277 |
|
|
|
278 |
/**
|
|
|
279 |
* Replaces the fields that might have changed when file was overriden in filepicker:
|
|
|
280 |
* reference, contenthash, filesize, userid
|
|
|
281 |
*
|
|
|
282 |
* Note that field 'source' must be updated separately because
|
|
|
283 |
* it has different format for draft and non-draft areas and
|
|
|
284 |
* this function will usually be used to replace non-draft area
|
|
|
285 |
* file with draft area file.
|
|
|
286 |
*
|
|
|
287 |
* @param stored_file $newfile
|
|
|
288 |
* @throws coding_exception
|
|
|
289 |
*/
|
|
|
290 |
public function replace_file_with(stored_file $newfile) {
|
|
|
291 |
if ($newfile->get_referencefileid() &&
|
|
|
292 |
$this->fs->get_references_count_by_storedfile($this)) {
|
|
|
293 |
// The new file is a reference.
|
|
|
294 |
// The current file has other local files referencing to it.
|
|
|
295 |
// Double reference is not allowed.
|
|
|
296 |
throw new moodle_exception('errordoublereference', 'repository');
|
|
|
297 |
}
|
|
|
298 |
|
|
|
299 |
$filerecord = new stdClass;
|
|
|
300 |
if ($this->filesystem->is_file_readable_remotely_by_storedfile($newfile)) {
|
|
|
301 |
$contenthash = $newfile->get_contenthash();
|
|
|
302 |
$filerecord->contenthash = $contenthash;
|
|
|
303 |
} else {
|
|
|
304 |
throw new file_exception('storedfileproblem', 'Invalid contenthash, content must be already in filepool', $contenthash);
|
|
|
305 |
}
|
|
|
306 |
$filerecord->filesize = $newfile->get_filesize();
|
|
|
307 |
$filerecord->referencefileid = $newfile->get_referencefileid();
|
|
|
308 |
$filerecord->userid = $newfile->get_userid();
|
|
|
309 |
$oldcontenthash = $this->get_contenthash();
|
|
|
310 |
$this->update($filerecord);
|
|
|
311 |
$this->filesystem->remove_file($oldcontenthash);
|
|
|
312 |
}
|
|
|
313 |
|
|
|
314 |
/**
|
|
|
315 |
* Unlink the stored file from the referenced file
|
|
|
316 |
*
|
|
|
317 |
* This methods destroys the link to the record in files_reference table. This effectively
|
|
|
318 |
* turns the stored file from being an alias to a plain copy. However, the caller has
|
|
|
319 |
* to make sure that the actual file's content has beed synced prior to calling this method.
|
|
|
320 |
*/
|
|
|
321 |
public function delete_reference() {
|
|
|
322 |
global $DB;
|
|
|
323 |
|
|
|
324 |
if (!$this->is_external_file()) {
|
|
|
325 |
throw new coding_exception('An attempt to unlink a non-reference file.');
|
|
|
326 |
}
|
|
|
327 |
|
|
|
328 |
$transaction = $DB->start_delegated_transaction();
|
|
|
329 |
|
|
|
330 |
// Are we the only one referring to the original file? If so, delete the
|
|
|
331 |
// referenced file record. Note we do not use file_storage::search_references_count()
|
|
|
332 |
// here because we want to count draft files too and we are at a bit lower access level here.
|
|
|
333 |
$countlinks = $DB->count_records('files',
|
|
|
334 |
array('referencefileid' => $this->file_record->referencefileid));
|
|
|
335 |
if ($countlinks == 1) {
|
|
|
336 |
$DB->delete_records('files_reference', array('id' => $this->file_record->referencefileid));
|
|
|
337 |
}
|
|
|
338 |
|
|
|
339 |
// Update the underlying record in the database.
|
|
|
340 |
$update = new stdClass();
|
|
|
341 |
$update->referencefileid = null;
|
|
|
342 |
$this->update($update);
|
|
|
343 |
|
|
|
344 |
$transaction->allow_commit();
|
|
|
345 |
|
|
|
346 |
// Update our properties and the record in the memory.
|
|
|
347 |
$this->repository = null;
|
|
|
348 |
$this->file_record->repositoryid = null;
|
|
|
349 |
$this->file_record->reference = null;
|
|
|
350 |
$this->file_record->referencefileid = null;
|
|
|
351 |
$this->file_record->referencelastsync = null;
|
|
|
352 |
}
|
|
|
353 |
|
|
|
354 |
/**
|
|
|
355 |
* Is this a directory?
|
|
|
356 |
*
|
|
|
357 |
* Directories are only emulated, internally they are stored as empty
|
|
|
358 |
* files with a "." instead of name - this means empty directory contains
|
|
|
359 |
* exactly one empty file with name dot.
|
|
|
360 |
*
|
|
|
361 |
* @return bool true means directory, false means file
|
|
|
362 |
*/
|
|
|
363 |
public function is_directory() {
|
|
|
364 |
return ($this->file_record->filename === '.');
|
|
|
365 |
}
|
|
|
366 |
|
|
|
367 |
/**
|
|
|
368 |
* Delete file from files table.
|
|
|
369 |
*
|
|
|
370 |
* The content of files stored in sha1 pool is reclaimed
|
|
|
371 |
* later - the occupied disk space is reclaimed much later.
|
|
|
372 |
*
|
|
|
373 |
* @return bool always true or exception if error occurred
|
|
|
374 |
*/
|
|
|
375 |
public function delete() {
|
|
|
376 |
global $DB;
|
|
|
377 |
|
|
|
378 |
if ($this->is_directory()) {
|
|
|
379 |
// Directories can not be referenced, just delete the record.
|
|
|
380 |
$DB->delete_records('files', array('id'=>$this->file_record->id));
|
|
|
381 |
|
|
|
382 |
} else {
|
|
|
383 |
$transaction = $DB->start_delegated_transaction();
|
|
|
384 |
|
|
|
385 |
// If there are other files referring to this file, convert them to copies.
|
|
|
386 |
if ($files = $this->fs->get_references_by_storedfile($this)) {
|
|
|
387 |
foreach ($files as $file) {
|
|
|
388 |
$this->fs->import_external_file($file);
|
|
|
389 |
}
|
|
|
390 |
}
|
|
|
391 |
|
|
|
392 |
// If this file is a reference (alias) to another file, unlink it first.
|
|
|
393 |
if ($this->is_external_file()) {
|
|
|
394 |
$this->delete_reference();
|
|
|
395 |
}
|
|
|
396 |
|
|
|
397 |
// Now delete the file record.
|
|
|
398 |
$DB->delete_records('files', array('id'=>$this->file_record->id));
|
|
|
399 |
|
|
|
400 |
$transaction->allow_commit();
|
|
|
401 |
|
|
|
402 |
if (!$this->is_directory()) {
|
|
|
403 |
// Callback for file deletion.
|
|
|
404 |
if ($pluginsfunction = get_plugins_with_function('after_file_deleted')) {
|
|
|
405 |
foreach ($pluginsfunction as $plugintype => $plugins) {
|
|
|
406 |
foreach ($plugins as $pluginfunction) {
|
|
|
407 |
$pluginfunction($this->file_record);
|
|
|
408 |
}
|
|
|
409 |
}
|
|
|
410 |
}
|
|
|
411 |
}
|
|
|
412 |
}
|
|
|
413 |
|
|
|
414 |
// Move pool file to trash if content not needed any more.
|
|
|
415 |
$this->filesystem->remove_file($this->file_record->contenthash);
|
|
|
416 |
return true; // BC only
|
|
|
417 |
}
|
|
|
418 |
|
|
|
419 |
/**
|
|
|
420 |
* adds this file path to a curl request (POST only)
|
|
|
421 |
*
|
|
|
422 |
* @param curl $curlrequest the curl request object
|
|
|
423 |
* @param string $key what key to use in the POST request
|
|
|
424 |
* @return void
|
|
|
425 |
*/
|
|
|
426 |
public function add_to_curl_request(&$curlrequest, $key) {
|
|
|
427 |
return $this->filesystem->add_to_curl_request($this, $curlrequest, $key);
|
|
|
428 |
}
|
|
|
429 |
|
|
|
430 |
/**
|
|
|
431 |
* Returns file handle - read only mode, no writing allowed into pool files!
|
|
|
432 |
*
|
|
|
433 |
* When you want to modify a file, create a new file and delete the old one.
|
|
|
434 |
*
|
|
|
435 |
* @param int $type Type of file handle (FILE_HANDLE_xx constant)
|
|
|
436 |
* @return resource file handle
|
|
|
437 |
*/
|
|
|
438 |
public function get_content_file_handle($type = self::FILE_HANDLE_FOPEN) {
|
|
|
439 |
return $this->filesystem->get_content_file_handle($this, $type);
|
|
|
440 |
}
|
|
|
441 |
|
|
|
442 |
/**
|
|
|
443 |
* Get a read-only PSR-7 stream for this file.
|
|
|
444 |
*
|
|
|
445 |
* Note: This stream is read-only. If you want to modify the file, create a new file and delete the old one.
|
|
|
446 |
* The File API creates immutable files.
|
|
|
447 |
*
|
|
|
448 |
* @return StreamInterface
|
|
|
449 |
*/
|
|
|
450 |
public function get_psr_stream(): StreamInterface {
|
|
|
451 |
return $this->filesystem->get_psr_stream($this);
|
|
|
452 |
}
|
|
|
453 |
|
|
|
454 |
/**
|
|
|
455 |
* Dumps file content to page.
|
|
|
456 |
*/
|
|
|
457 |
public function readfile() {
|
|
|
458 |
return $this->filesystem->readfile($this);
|
|
|
459 |
}
|
|
|
460 |
|
|
|
461 |
/**
|
|
|
462 |
* Returns file content as string.
|
|
|
463 |
*
|
|
|
464 |
* @return string content
|
|
|
465 |
*/
|
|
|
466 |
public function get_content() {
|
|
|
467 |
return $this->filesystem->get_content($this);
|
|
|
468 |
}
|
|
|
469 |
|
|
|
470 |
/**
|
|
|
471 |
* Copy content of file to given pathname.
|
|
|
472 |
*
|
|
|
473 |
* @param string $pathname real path to the new file
|
|
|
474 |
* @return bool success
|
|
|
475 |
*/
|
|
|
476 |
public function copy_content_to($pathname) {
|
|
|
477 |
return $this->filesystem->copy_content_from_storedfile($this, $pathname);
|
|
|
478 |
}
|
|
|
479 |
|
|
|
480 |
/**
|
|
|
481 |
* Copy content of file to temporary folder and returns file path
|
|
|
482 |
*
|
|
|
483 |
* @param string $dir name of the temporary directory
|
|
|
484 |
* @param string $fileprefix prefix of temporary file.
|
|
|
485 |
* @return string|bool path of temporary file or false.
|
|
|
486 |
*/
|
|
|
487 |
public function copy_content_to_temp($dir = 'files', $fileprefix = 'tempup_') {
|
|
|
488 |
$tempfile = false;
|
|
|
489 |
if (!$dir = make_temp_directory($dir)) {
|
|
|
490 |
return false;
|
|
|
491 |
}
|
|
|
492 |
if (!$tempfile = tempnam($dir, $fileprefix)) {
|
|
|
493 |
return false;
|
|
|
494 |
}
|
|
|
495 |
if (!$this->copy_content_to($tempfile)) {
|
|
|
496 |
// something went wrong
|
|
|
497 |
@unlink($tempfile);
|
|
|
498 |
return false;
|
|
|
499 |
}
|
|
|
500 |
return $tempfile;
|
|
|
501 |
}
|
|
|
502 |
|
|
|
503 |
/**
|
|
|
504 |
* List contents of archive.
|
|
|
505 |
*
|
|
|
506 |
* @param file_packer $packer file packer instance
|
|
|
507 |
* @return array of file infos
|
|
|
508 |
*/
|
|
|
509 |
public function list_files(file_packer $packer) {
|
|
|
510 |
return $this->filesystem->list_files($this, $packer);
|
|
|
511 |
}
|
|
|
512 |
|
|
|
513 |
/**
|
|
|
514 |
* Returns the total size (in bytes) of the contents of an archive.
|
|
|
515 |
*
|
|
|
516 |
* @param file_packer $packer file packer instance
|
|
|
517 |
* @return int|null total size in bytes
|
|
|
518 |
*/
|
|
|
519 |
public function get_total_content_size(file_packer $packer): ?int {
|
|
|
520 |
// Fetch the contents of the archive.
|
|
|
521 |
$files = $this->list_files($packer);
|
|
|
522 |
|
|
|
523 |
// Early return if the value of $files is not of type array.
|
|
|
524 |
// This can happen when the utility class is unable to open or read the contents of the archive.
|
|
|
525 |
if (!is_array($files)) {
|
|
|
526 |
return null;
|
|
|
527 |
}
|
|
|
528 |
|
|
|
529 |
return array_reduce($files, function ($contentsize, $file) {
|
|
|
530 |
return $contentsize + $file->size;
|
|
|
531 |
}, 0);
|
|
|
532 |
}
|
|
|
533 |
|
|
|
534 |
/**
|
|
|
535 |
* Extract file to given file path (real OS filesystem), existing files are overwritten.
|
|
|
536 |
*
|
|
|
537 |
* @param file_packer $packer file packer instance
|
|
|
538 |
* @param string $pathname target directory
|
|
|
539 |
* @param file_progress $progress Progress indicator callback or null if not required
|
|
|
540 |
* @return array|bool list of processed files; false if error
|
|
|
541 |
*/
|
|
|
542 |
public function extract_to_pathname(file_packer $packer, $pathname,
|
|
|
543 |
file_progress $progress = null) {
|
|
|
544 |
return $this->filesystem->extract_to_pathname($this, $packer, $pathname, $progress);
|
|
|
545 |
}
|
|
|
546 |
|
|
|
547 |
/**
|
|
|
548 |
* Extract file to given file path (real OS filesystem), existing files are overwritten.
|
|
|
549 |
*
|
|
|
550 |
* @param file_packer $packer file packer instance
|
|
|
551 |
* @param int $contextid context ID
|
|
|
552 |
* @param string $component component
|
|
|
553 |
* @param string $filearea file area
|
|
|
554 |
* @param int $itemid item ID
|
|
|
555 |
* @param string $pathbase path base
|
|
|
556 |
* @param int $userid user ID
|
|
|
557 |
* @param file_progress $progress Progress indicator callback or null if not required
|
|
|
558 |
* @return array|bool list of processed files; false if error
|
|
|
559 |
*/
|
|
|
560 |
public function extract_to_storage(file_packer $packer, $contextid,
|
|
|
561 |
$component, $filearea, $itemid, $pathbase, $userid = null, file_progress $progress = null) {
|
|
|
562 |
|
|
|
563 |
return $this->filesystem->extract_to_storage($this, $packer, $contextid, $component, $filearea,
|
|
|
564 |
$itemid, $pathbase, $userid, $progress);
|
|
|
565 |
}
|
|
|
566 |
|
|
|
567 |
/**
|
|
|
568 |
* Add file/directory into archive.
|
|
|
569 |
*
|
|
|
570 |
* @param file_archive $filearch file archive instance
|
|
|
571 |
* @param string $archivepath pathname in archive
|
|
|
572 |
* @return bool success
|
|
|
573 |
*/
|
|
|
574 |
public function archive_file(file_archive $filearch, $archivepath) {
|
|
|
575 |
if ($this->repository) {
|
|
|
576 |
$this->sync_external_file();
|
|
|
577 |
if ($this->compare_to_string('')) {
|
|
|
578 |
// This file is not stored locally - attempt to retrieve it from the repository.
|
|
|
579 |
// This may happen if the repository deliberately does not fetch files, or if there is a failure with the sync.
|
|
|
580 |
$fileinfo = $this->repository->get_file($this->get_reference());
|
|
|
581 |
if (isset($fileinfo['path'])) {
|
|
|
582 |
return $filearch->add_file_from_pathname($archivepath, $fileinfo['path']);
|
|
|
583 |
}
|
|
|
584 |
}
|
|
|
585 |
}
|
|
|
586 |
|
|
|
587 |
return $this->filesystem->add_storedfile_to_archive($this, $filearch, $archivepath);
|
|
|
588 |
}
|
|
|
589 |
|
|
|
590 |
/**
|
|
|
591 |
* Returns information about image,
|
|
|
592 |
* information is determined from the file content
|
|
|
593 |
*
|
|
|
594 |
* @return mixed array with width, height and mimetype; false if not an image
|
|
|
595 |
*/
|
|
|
596 |
public function get_imageinfo() {
|
|
|
597 |
return $this->filesystem->get_imageinfo($this);
|
|
|
598 |
}
|
|
|
599 |
|
|
|
600 |
/**
|
|
|
601 |
* Verifies the file is a valid web image - gif, png and jpeg only.
|
|
|
602 |
*
|
|
|
603 |
* It should be ok to serve this image from server without any other security workarounds.
|
|
|
604 |
*
|
|
|
605 |
* @return bool true if file ok
|
|
|
606 |
*/
|
|
|
607 |
public function is_valid_image() {
|
|
|
608 |
$mimetype = $this->get_mimetype();
|
|
|
609 |
if (!file_mimetype_in_typegroup($mimetype, 'web_image')) {
|
|
|
610 |
return false;
|
|
|
611 |
}
|
|
|
612 |
if (!$info = $this->get_imageinfo()) {
|
|
|
613 |
return false;
|
|
|
614 |
}
|
|
|
615 |
if ($info['mimetype'] !== $mimetype) {
|
|
|
616 |
return false;
|
|
|
617 |
}
|
|
|
618 |
// ok, GD likes this image
|
|
|
619 |
return true;
|
|
|
620 |
}
|
|
|
621 |
|
|
|
622 |
/**
|
|
|
623 |
* Returns parent directory, creates missing parents if needed.
|
|
|
624 |
*
|
|
|
625 |
* @return stored_file
|
|
|
626 |
*/
|
|
|
627 |
public function get_parent_directory() {
|
|
|
628 |
if ($this->file_record->filepath === '/' and $this->file_record->filename === '.') {
|
|
|
629 |
//root dir does not have parent
|
|
|
630 |
return null;
|
|
|
631 |
}
|
|
|
632 |
|
|
|
633 |
if ($this->file_record->filename !== '.') {
|
|
|
634 |
return $this->fs->create_directory($this->file_record->contextid, $this->file_record->component, $this->file_record->filearea, $this->file_record->itemid, $this->file_record->filepath);
|
|
|
635 |
}
|
|
|
636 |
|
|
|
637 |
$filepath = $this->file_record->filepath;
|
|
|
638 |
$filepath = trim($filepath, '/');
|
|
|
639 |
$dirs = explode('/', $filepath);
|
|
|
640 |
array_pop($dirs);
|
|
|
641 |
$filepath = implode('/', $dirs);
|
|
|
642 |
$filepath = ($filepath === '') ? '/' : "/$filepath/";
|
|
|
643 |
|
|
|
644 |
return $this->fs->create_directory($this->file_record->contextid, $this->file_record->component, $this->file_record->filearea, $this->file_record->itemid, $filepath);
|
|
|
645 |
}
|
|
|
646 |
|
|
|
647 |
/**
|
|
|
648 |
* Set synchronised content from file.
|
|
|
649 |
*
|
|
|
650 |
* @param string $path Path to the file.
|
|
|
651 |
*/
|
|
|
652 |
public function set_synchronised_content_from_file($path) {
|
|
|
653 |
$this->fs->synchronise_stored_file_from_file($this, $path, $this->file_record);
|
|
|
654 |
}
|
|
|
655 |
|
|
|
656 |
/**
|
|
|
657 |
* Set synchronised content from content.
|
|
|
658 |
*
|
|
|
659 |
* @param string $content File content.
|
|
|
660 |
*/
|
|
|
661 |
public function set_synchronised_content_from_string($content) {
|
|
|
662 |
$this->fs->synchronise_stored_file_from_string($this, $content, $this->file_record);
|
|
|
663 |
}
|
|
|
664 |
|
|
|
665 |
/**
|
|
|
666 |
* Synchronize file if it is a reference and needs synchronizing
|
|
|
667 |
*
|
|
|
668 |
* Updates contenthash and filesize
|
|
|
669 |
*/
|
|
|
670 |
public function sync_external_file() {
|
|
|
671 |
if (!empty($this->repository)) {
|
|
|
672 |
$this->repository->sync_reference($this);
|
|
|
673 |
}
|
|
|
674 |
}
|
|
|
675 |
|
|
|
676 |
/**
|
|
|
677 |
* Returns context id of the file
|
|
|
678 |
*
|
|
|
679 |
* @return int context id
|
|
|
680 |
*/
|
|
|
681 |
public function get_contextid() {
|
|
|
682 |
return $this->file_record->contextid;
|
|
|
683 |
}
|
|
|
684 |
|
|
|
685 |
/**
|
|
|
686 |
* Returns component name - this is the owner of the areas,
|
|
|
687 |
* nothing else is allowed to read or modify the files directly!!
|
|
|
688 |
*
|
|
|
689 |
* @return string
|
|
|
690 |
*/
|
|
|
691 |
public function get_component() {
|
|
|
692 |
return $this->file_record->component;
|
|
|
693 |
}
|
|
|
694 |
|
|
|
695 |
/**
|
|
|
696 |
* Returns file area name, this divides files of one component into groups with different access control.
|
|
|
697 |
* All files in one area have the same access control.
|
|
|
698 |
*
|
|
|
699 |
* @return string
|
|
|
700 |
*/
|
|
|
701 |
public function get_filearea() {
|
|
|
702 |
return $this->file_record->filearea;
|
|
|
703 |
}
|
|
|
704 |
|
|
|
705 |
/**
|
|
|
706 |
* Returns returns item id of file.
|
|
|
707 |
*
|
|
|
708 |
* @return int
|
|
|
709 |
*/
|
|
|
710 |
public function get_itemid() {
|
|
|
711 |
return $this->file_record->itemid;
|
|
|
712 |
}
|
|
|
713 |
|
|
|
714 |
/**
|
|
|
715 |
* Returns file path - starts and ends with /, \ are not allowed.
|
|
|
716 |
*
|
|
|
717 |
* @return string
|
|
|
718 |
*/
|
|
|
719 |
public function get_filepath() {
|
|
|
720 |
return $this->file_record->filepath;
|
|
|
721 |
}
|
|
|
722 |
|
|
|
723 |
/**
|
|
|
724 |
* Returns file name or '.' in case of directories.
|
|
|
725 |
*
|
|
|
726 |
* @return string
|
|
|
727 |
*/
|
|
|
728 |
public function get_filename() {
|
|
|
729 |
return $this->file_record->filename;
|
|
|
730 |
}
|
|
|
731 |
|
|
|
732 |
/**
|
|
|
733 |
* Returns id of user who created the file.
|
|
|
734 |
*
|
|
|
735 |
* @return int
|
|
|
736 |
*/
|
|
|
737 |
public function get_userid() {
|
|
|
738 |
return $this->file_record->userid;
|
|
|
739 |
}
|
|
|
740 |
|
|
|
741 |
/**
|
|
|
742 |
* Returns the size of file in bytes.
|
|
|
743 |
*
|
|
|
744 |
* @return int bytes
|
|
|
745 |
*/
|
|
|
746 |
public function get_filesize() {
|
|
|
747 |
$this->sync_external_file();
|
|
|
748 |
return $this->file_record->filesize;
|
|
|
749 |
}
|
|
|
750 |
|
|
|
751 |
/**
|
|
|
752 |
* Function stored_file::set_filesize() is deprecated. Please use stored_file::replace_file_with
|
|
|
753 |
*
|
|
|
754 |
* @deprecated since Moodle 2.6 MDL-42016 - please do not use this function any more.
|
|
|
755 |
* @see stored_file::replace_file_with()
|
|
|
756 |
*/
|
|
|
757 |
public function set_filesize($filesize) {
|
|
|
758 |
throw new coding_exception('Function stored_file::set_filesize() can not be used any more. ' .
|
|
|
759 |
'Please use stored_file::replace_file_with()');
|
|
|
760 |
}
|
|
|
761 |
|
|
|
762 |
/**
|
|
|
763 |
* Returns mime type of file.
|
|
|
764 |
*
|
|
|
765 |
* @return string
|
|
|
766 |
*/
|
|
|
767 |
public function get_mimetype() {
|
|
|
768 |
return $this->file_record->mimetype;
|
|
|
769 |
}
|
|
|
770 |
|
|
|
771 |
/**
|
|
|
772 |
* Returns unix timestamp of file creation date.
|
|
|
773 |
*
|
|
|
774 |
* @return int
|
|
|
775 |
*/
|
|
|
776 |
public function get_timecreated() {
|
|
|
777 |
return $this->file_record->timecreated;
|
|
|
778 |
}
|
|
|
779 |
|
|
|
780 |
/**
|
|
|
781 |
* Returns unix timestamp of last file modification.
|
|
|
782 |
*
|
|
|
783 |
* @return int
|
|
|
784 |
*/
|
|
|
785 |
public function get_timemodified() {
|
|
|
786 |
$this->sync_external_file();
|
|
|
787 |
return $this->file_record->timemodified;
|
|
|
788 |
}
|
|
|
789 |
|
|
|
790 |
/**
|
|
|
791 |
* set timemodified
|
|
|
792 |
*
|
|
|
793 |
* @param int $timemodified
|
|
|
794 |
*/
|
|
|
795 |
public function set_timemodified($timemodified) {
|
|
|
796 |
$filerecord = new stdClass;
|
|
|
797 |
$filerecord->timemodified = $timemodified;
|
|
|
798 |
$this->update($filerecord);
|
|
|
799 |
}
|
|
|
800 |
|
|
|
801 |
/**
|
|
|
802 |
* Returns file status flag.
|
|
|
803 |
*
|
|
|
804 |
* @return int 0 means file OK, anything else is a problem and file can not be used
|
|
|
805 |
*/
|
|
|
806 |
public function get_status() {
|
|
|
807 |
return $this->file_record->status;
|
|
|
808 |
}
|
|
|
809 |
|
|
|
810 |
/**
|
|
|
811 |
* Returns file id.
|
|
|
812 |
*
|
|
|
813 |
* @return int
|
|
|
814 |
*/
|
|
|
815 |
public function get_id() {
|
|
|
816 |
return $this->file_record->id;
|
|
|
817 |
}
|
|
|
818 |
|
|
|
819 |
/**
|
|
|
820 |
* Returns sha1 hash of file content.
|
|
|
821 |
*
|
|
|
822 |
* @return string
|
|
|
823 |
*/
|
|
|
824 |
public function get_contenthash() {
|
|
|
825 |
$this->sync_external_file();
|
|
|
826 |
return $this->file_record->contenthash;
|
|
|
827 |
}
|
|
|
828 |
|
|
|
829 |
/**
|
|
|
830 |
* Returns sha1 hash of all file path components sha1("contextid/component/filearea/itemid/dir/dir/filename.ext").
|
|
|
831 |
*
|
|
|
832 |
* @return string
|
|
|
833 |
*/
|
|
|
834 |
public function get_pathnamehash() {
|
|
|
835 |
return $this->file_record->pathnamehash;
|
|
|
836 |
}
|
|
|
837 |
|
|
|
838 |
/**
|
|
|
839 |
* Returns the license type of the file, it is a short name referred from license table.
|
|
|
840 |
*
|
|
|
841 |
* @return string
|
|
|
842 |
*/
|
|
|
843 |
public function get_license() {
|
|
|
844 |
return $this->file_record->license;
|
|
|
845 |
}
|
|
|
846 |
|
|
|
847 |
/**
|
|
|
848 |
* Set license
|
|
|
849 |
*
|
|
|
850 |
* @param string $license license
|
|
|
851 |
*/
|
|
|
852 |
public function set_license($license) {
|
|
|
853 |
$filerecord = new stdClass;
|
|
|
854 |
$filerecord->license = $license;
|
|
|
855 |
$this->update($filerecord);
|
|
|
856 |
}
|
|
|
857 |
|
|
|
858 |
/**
|
|
|
859 |
* Returns the author name of the file.
|
|
|
860 |
*
|
|
|
861 |
* @return string
|
|
|
862 |
*/
|
|
|
863 |
public function get_author() {
|
|
|
864 |
return $this->file_record->author;
|
|
|
865 |
}
|
|
|
866 |
|
|
|
867 |
/**
|
|
|
868 |
* Set author
|
|
|
869 |
*
|
|
|
870 |
* @param string $author
|
|
|
871 |
*/
|
|
|
872 |
public function set_author($author) {
|
|
|
873 |
$filerecord = new stdClass;
|
|
|
874 |
$filerecord->author = $author;
|
|
|
875 |
$this->update($filerecord);
|
|
|
876 |
}
|
|
|
877 |
|
|
|
878 |
/**
|
|
|
879 |
* Returns the source of the file, usually it is a url.
|
|
|
880 |
*
|
|
|
881 |
* @return string
|
|
|
882 |
*/
|
|
|
883 |
public function get_source() {
|
|
|
884 |
return $this->file_record->source;
|
|
|
885 |
}
|
|
|
886 |
|
|
|
887 |
/**
|
|
|
888 |
* Set license
|
|
|
889 |
*
|
|
|
890 |
* @param string $license license
|
|
|
891 |
*/
|
|
|
892 |
public function set_source($source) {
|
|
|
893 |
$filerecord = new stdClass;
|
|
|
894 |
$filerecord->source = $source;
|
|
|
895 |
$this->update($filerecord);
|
|
|
896 |
}
|
|
|
897 |
|
|
|
898 |
|
|
|
899 |
/**
|
|
|
900 |
* Returns the sort order of file
|
|
|
901 |
*
|
|
|
902 |
* @return int
|
|
|
903 |
*/
|
|
|
904 |
public function get_sortorder() {
|
|
|
905 |
return $this->file_record->sortorder;
|
|
|
906 |
}
|
|
|
907 |
|
|
|
908 |
/**
|
|
|
909 |
* Set file sort order
|
|
|
910 |
*
|
|
|
911 |
* @param int $sortorder
|
|
|
912 |
* @return int
|
|
|
913 |
*/
|
|
|
914 |
public function set_sortorder($sortorder) {
|
|
|
915 |
$oldorder = $this->file_record->sortorder;
|
|
|
916 |
$filerecord = new stdClass;
|
|
|
917 |
$filerecord->sortorder = $sortorder;
|
|
|
918 |
$this->update($filerecord);
|
|
|
919 |
if (!$this->is_directory()) {
|
|
|
920 |
// Callback for file sort order change.
|
|
|
921 |
if ($pluginsfunction = get_plugins_with_function('after_file_sorted')) {
|
|
|
922 |
foreach ($pluginsfunction as $plugintype => $plugins) {
|
|
|
923 |
foreach ($plugins as $pluginfunction) {
|
|
|
924 |
$pluginfunction($this->file_record, $oldorder, $sortorder);
|
|
|
925 |
}
|
|
|
926 |
}
|
|
|
927 |
}
|
|
|
928 |
}
|
|
|
929 |
}
|
|
|
930 |
|
|
|
931 |
/**
|
|
|
932 |
* Returns repository id
|
|
|
933 |
*
|
|
|
934 |
* @return int|null
|
|
|
935 |
*/
|
|
|
936 |
public function get_repository_id() {
|
|
|
937 |
if (!empty($this->repository)) {
|
|
|
938 |
return $this->repository->id;
|
|
|
939 |
} else {
|
|
|
940 |
return null;
|
|
|
941 |
}
|
|
|
942 |
}
|
|
|
943 |
|
|
|
944 |
/**
|
|
|
945 |
* Returns repository type.
|
|
|
946 |
*
|
|
|
947 |
* @return mixed str|null the repository type or null if is not an external file
|
|
|
948 |
* @since Moodle 3.3
|
|
|
949 |
*/
|
|
|
950 |
public function get_repository_type() {
|
|
|
951 |
|
|
|
952 |
if (!empty($this->repository)) {
|
|
|
953 |
return $this->repository->get_typename();
|
|
|
954 |
} else {
|
|
|
955 |
return null;
|
|
|
956 |
}
|
|
|
957 |
}
|
|
|
958 |
|
|
|
959 |
|
|
|
960 |
/**
|
|
|
961 |
* get reference file id
|
|
|
962 |
* @return int
|
|
|
963 |
*/
|
|
|
964 |
public function get_referencefileid() {
|
|
|
965 |
return $this->file_record->referencefileid;
|
|
|
966 |
}
|
|
|
967 |
|
|
|
968 |
/**
|
|
|
969 |
* Get reference last sync time
|
|
|
970 |
* @return int
|
|
|
971 |
*/
|
|
|
972 |
public function get_referencelastsync() {
|
|
|
973 |
return $this->file_record->referencelastsync;
|
|
|
974 |
}
|
|
|
975 |
|
|
|
976 |
/**
|
|
|
977 |
* Function stored_file::get_referencelifetime() is deprecated as reference
|
|
|
978 |
* life time is no longer stored in DB or returned by repository. Each
|
|
|
979 |
* repository should decide by itself when to synchronise the references.
|
|
|
980 |
*
|
|
|
981 |
* @deprecated since Moodle 2.6 MDL-42016 - please do not use this function any more.
|
|
|
982 |
* @see repository::sync_reference()
|
|
|
983 |
*/
|
|
|
984 |
public function get_referencelifetime() {
|
|
|
985 |
throw new coding_exception('Function stored_file::get_referencelifetime() can not be used any more. ' .
|
|
|
986 |
'See repository::sync_reference().');
|
|
|
987 |
}
|
|
|
988 |
/**
|
|
|
989 |
* Returns file reference
|
|
|
990 |
*
|
|
|
991 |
* @return string
|
|
|
992 |
*/
|
|
|
993 |
public function get_reference() {
|
|
|
994 |
return $this->file_record->reference;
|
|
|
995 |
}
|
|
|
996 |
|
|
|
997 |
/**
|
|
|
998 |
* Get human readable file reference information
|
|
|
999 |
*
|
|
|
1000 |
* @return string
|
|
|
1001 |
*/
|
|
|
1002 |
public function get_reference_details() {
|
|
|
1003 |
return $this->repository->get_reference_details($this->get_reference(), $this->get_status());
|
|
|
1004 |
}
|
|
|
1005 |
|
|
|
1006 |
/**
|
|
|
1007 |
* Called after reference-file has been synchronized with the repository
|
|
|
1008 |
*
|
|
|
1009 |
* We update contenthash, filesize and status in files table if changed
|
|
|
1010 |
* and we always update lastsync in files_reference table
|
|
|
1011 |
*
|
|
|
1012 |
* @param null|string $contenthash if set to null contenthash is not changed
|
|
|
1013 |
* @param int $filesize new size of the file
|
|
|
1014 |
* @param int $status new status of the file (0 means OK, 666 - source missing)
|
|
|
1015 |
* @param int $timemodified last time modified of the source, if known
|
|
|
1016 |
*/
|
|
|
1017 |
public function set_synchronized($contenthash, $filesize, $status = 0, $timemodified = null) {
|
|
|
1018 |
if (!$this->is_external_file()) {
|
|
|
1019 |
return;
|
|
|
1020 |
}
|
|
|
1021 |
$now = time();
|
|
|
1022 |
if ($contenthash === null) {
|
|
|
1023 |
$contenthash = $this->file_record->contenthash;
|
|
|
1024 |
}
|
|
|
1025 |
if ($contenthash != $this->file_record->contenthash) {
|
|
|
1026 |
$oldcontenthash = $this->file_record->contenthash;
|
|
|
1027 |
}
|
|
|
1028 |
// this will update all entries in {files} that have the same filereference id
|
|
|
1029 |
$this->fs->update_references($this->file_record->referencefileid, $now, null, $contenthash, $filesize, $status, $timemodified);
|
|
|
1030 |
// we don't need to call update() for this object, just set the values of changed fields
|
|
|
1031 |
$this->file_record->contenthash = $contenthash;
|
|
|
1032 |
$this->file_record->filesize = $filesize;
|
|
|
1033 |
$this->file_record->status = $status;
|
|
|
1034 |
$this->file_record->referencelastsync = $now;
|
|
|
1035 |
if ($timemodified) {
|
|
|
1036 |
$this->file_record->timemodified = $timemodified;
|
|
|
1037 |
}
|
|
|
1038 |
if (isset($oldcontenthash)) {
|
|
|
1039 |
$this->filesystem->remove_file($oldcontenthash);
|
|
|
1040 |
}
|
|
|
1041 |
}
|
|
|
1042 |
|
|
|
1043 |
/**
|
|
|
1044 |
* Sets the error status for a file that could not be synchronised
|
|
|
1045 |
*/
|
|
|
1046 |
public function set_missingsource() {
|
|
|
1047 |
$this->set_synchronized($this->file_record->contenthash, $this->file_record->filesize, 666);
|
|
|
1048 |
}
|
|
|
1049 |
|
|
|
1050 |
/**
|
|
|
1051 |
* Send file references
|
|
|
1052 |
*
|
|
|
1053 |
* @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours)
|
|
|
1054 |
* @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only
|
|
|
1055 |
* @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin
|
|
|
1056 |
* @param array $options additional options affecting the file serving
|
|
|
1057 |
*/
|
|
|
1058 |
public function send_file($lifetime, $filter, $forcedownload, $options) {
|
|
|
1059 |
$this->repository->send_file($this, $lifetime, $filter, $forcedownload, $options);
|
|
|
1060 |
}
|
|
|
1061 |
|
|
|
1062 |
/**
|
|
|
1063 |
* Imports the contents of an external file into moodle filepool.
|
|
|
1064 |
*
|
|
|
1065 |
* @throws moodle_exception if file could not be downloaded or is too big
|
|
|
1066 |
* @param int $maxbytes throw an exception if file size is bigger than $maxbytes (0 means no limit)
|
|
|
1067 |
*/
|
|
|
1068 |
public function import_external_file_contents($maxbytes = 0) {
|
|
|
1069 |
if ($this->repository) {
|
|
|
1070 |
$this->repository->import_external_file_contents($this, $maxbytes);
|
|
|
1071 |
}
|
|
|
1072 |
}
|
|
|
1073 |
|
|
|
1074 |
/**
|
|
|
1075 |
* Gets a file relative to this file in the repository and sends it to the browser.
|
|
|
1076 |
* Checks the function repository::supports_relative_file() to make sure it can be used.
|
|
|
1077 |
*
|
|
|
1078 |
* @param string $relativepath the relative path to the file we are trying to access
|
|
|
1079 |
*/
|
|
|
1080 |
public function send_relative_file($relativepath) {
|
|
|
1081 |
if ($this->repository && $this->repository->supports_relative_file()) {
|
|
|
1082 |
$relativepath = clean_param($relativepath, PARAM_PATH);
|
|
|
1083 |
$this->repository->send_relative_file($this, $relativepath);
|
|
|
1084 |
} else {
|
|
|
1085 |
send_file_not_found();
|
|
|
1086 |
}
|
|
|
1087 |
}
|
|
|
1088 |
|
|
|
1089 |
/**
|
|
|
1090 |
* Generates a thumbnail for this stored_file.
|
|
|
1091 |
*
|
|
|
1092 |
* If the GD library has at least version 2 and PNG support is available, the returned data
|
|
|
1093 |
* is the content of a transparent PNG file containing the thumbnail. Otherwise, the function
|
|
|
1094 |
* returns contents of a JPEG file with black background containing the thumbnail.
|
|
|
1095 |
*
|
|
|
1096 |
* @param int $width the width of the requested thumbnail
|
|
|
1097 |
* @param int $height the height of the requested thumbnail
|
|
|
1098 |
* @return string|bool false if a problem occurs, the thumbnail image data otherwise
|
|
|
1099 |
*/
|
|
|
1100 |
public function generate_image_thumbnail($width, $height) {
|
|
|
1101 |
global $CFG;
|
|
|
1102 |
require_once($CFG->libdir . '/gdlib.php');
|
|
|
1103 |
|
|
|
1104 |
if (empty($width) or empty($height)) {
|
|
|
1105 |
return false;
|
|
|
1106 |
}
|
|
|
1107 |
|
|
|
1108 |
$content = $this->get_content();
|
|
|
1109 |
|
|
|
1110 |
// Fetch the image information for this image.
|
|
|
1111 |
$imageinfo = @getimagesizefromstring($content);
|
|
|
1112 |
if (empty($imageinfo)) {
|
|
|
1113 |
return false;
|
|
|
1114 |
}
|
|
|
1115 |
|
|
|
1116 |
// Create a new image from the file.
|
|
|
1117 |
$original = @imagecreatefromstring($content);
|
|
|
1118 |
|
|
|
1119 |
// Generate the thumbnail.
|
|
|
1120 |
return generate_image_thumbnail_from_image($original, $imageinfo, $width, $height);
|
|
|
1121 |
}
|
|
|
1122 |
|
|
|
1123 |
/**
|
|
|
1124 |
* Generate a resized image for this stored_file.
|
|
|
1125 |
*
|
|
|
1126 |
* @param int|null $width The desired width, or null to only use the height.
|
|
|
1127 |
* @param int|null $height The desired height, or null to only use the width.
|
|
|
1128 |
* @return string|false False when a problem occurs, else the image data.
|
|
|
1129 |
*/
|
|
|
1130 |
public function resize_image($width, $height) {
|
|
|
1131 |
global $CFG;
|
|
|
1132 |
require_once($CFG->libdir . '/gdlib.php');
|
|
|
1133 |
|
|
|
1134 |
$content = $this->get_content();
|
|
|
1135 |
|
|
|
1136 |
// Fetch the image information for this image.
|
|
|
1137 |
$imageinfo = @getimagesizefromstring($content);
|
|
|
1138 |
if (empty($imageinfo)) {
|
|
|
1139 |
return false;
|
|
|
1140 |
}
|
|
|
1141 |
|
|
|
1142 |
// Create a new image from the file.
|
|
|
1143 |
$original = @imagecreatefromstring($content);
|
|
|
1144 |
if (empty($original)) {
|
|
|
1145 |
return false;
|
|
|
1146 |
}
|
|
|
1147 |
|
|
|
1148 |
// Generate the resized image.
|
|
|
1149 |
return resize_image_from_image($original, $imageinfo, $width, $height);
|
|
|
1150 |
}
|
|
|
1151 |
|
|
|
1152 |
/**
|
|
|
1153 |
* Check whether the supplied file is the same as this file.
|
|
|
1154 |
*
|
|
|
1155 |
* @param string $path The path to the file on disk
|
|
|
1156 |
* @return boolean
|
|
|
1157 |
*/
|
|
|
1158 |
public function compare_to_path($path) {
|
|
|
1159 |
return $this->get_contenthash() === file_storage::hash_from_path($path);
|
|
|
1160 |
}
|
|
|
1161 |
|
|
|
1162 |
/**
|
|
|
1163 |
* Check whether the supplied content is the same as this file.
|
|
|
1164 |
*
|
|
|
1165 |
* @param string $content The file content
|
|
|
1166 |
* @return boolean
|
|
|
1167 |
*/
|
|
|
1168 |
public function compare_to_string($content) {
|
|
|
1169 |
return $this->get_contenthash() === file_storage::hash_from_string($content);
|
|
|
1170 |
}
|
|
|
1171 |
|
|
|
1172 |
/**
|
|
|
1173 |
* Generate a rotated image for this stored_file based on exif information.
|
|
|
1174 |
*
|
|
|
1175 |
* @return array|false False when a problem occurs, else the image data and image size.
|
|
|
1176 |
* @since Moodle 3.8
|
|
|
1177 |
*/
|
|
|
1178 |
public function rotate_image() {
|
|
|
1179 |
$content = $this->get_content();
|
|
|
1180 |
$mimetype = $this->get_mimetype();
|
|
|
1181 |
|
|
|
1182 |
if ($mimetype === "image/jpeg" && function_exists("exif_read_data")) {
|
|
|
1183 |
$exif = @exif_read_data("data://image/jpeg;base64," . base64_encode($content));
|
|
|
1184 |
if (isset($exif['ExifImageWidth']) && isset($exif['ExifImageLength']) && isset($exif['Orientation'])) {
|
|
|
1185 |
$rotation = [
|
|
|
1186 |
3 => -180,
|
|
|
1187 |
6 => -90,
|
|
|
1188 |
8 => -270,
|
|
|
1189 |
];
|
|
|
1190 |
$orientation = $exif['Orientation'];
|
|
|
1191 |
if ($orientation !== 1) {
|
|
|
1192 |
$source = @imagecreatefromstring($content);
|
|
|
1193 |
$data = @imagerotate($source, $rotation[$orientation], 0);
|
|
|
1194 |
if (!empty($data)) {
|
|
|
1195 |
if ($orientation == 1 || $orientation == 3) {
|
|
|
1196 |
$size = [
|
|
|
1197 |
'width' => $exif["ExifImageWidth"],
|
|
|
1198 |
'height' => $exif["ExifImageLength"],
|
|
|
1199 |
];
|
|
|
1200 |
} else {
|
|
|
1201 |
$size = [
|
|
|
1202 |
'height' => $exif["ExifImageWidth"],
|
|
|
1203 |
'width' => $exif["ExifImageLength"],
|
|
|
1204 |
];
|
|
|
1205 |
}
|
|
|
1206 |
imagedestroy($source);
|
|
|
1207 |
return [$data, $size];
|
|
|
1208 |
}
|
|
|
1209 |
}
|
|
|
1210 |
}
|
|
|
1211 |
}
|
|
|
1212 |
return [false, false];
|
|
|
1213 |
}
|
|
|
1214 |
}
|