1 | <?php |
||||||
2 | /** |
||||||
3 | * Elgg filestore. |
||||||
4 | * This file contains functions for saving and retrieving data from files. |
||||||
5 | * |
||||||
6 | * @package Elgg.Core |
||||||
7 | * @subpackage DataModel.FileStorage |
||||||
8 | */ |
||||||
9 | |||||||
10 | use Symfony\Component\HttpFoundation\File\UploadedFile; |
||||||
11 | |||||||
12 | /** |
||||||
13 | * Get the size of the specified directory. |
||||||
14 | * |
||||||
15 | * @param string $dir The full path of the directory |
||||||
16 | * @param int $total_size Add to current dir size |
||||||
17 | * |
||||||
18 | * @return int The size of the directory in bytes |
||||||
19 | */ |
||||||
20 | function get_dir_size($dir, $total_size = 0) { |
||||||
21 | $handle = @opendir($dir); |
||||||
22 | while ($file = @readdir($handle)) { |
||||||
23 | if (in_array($file, ['.', '..'])) { |
||||||
24 | continue; |
||||||
25 | } |
||||||
26 | if (is_dir($dir . $file)) { |
||||||
27 | $total_size = get_dir_size($dir . $file . "/", $total_size); |
||||||
28 | } else { |
||||||
29 | $total_size += filesize($dir . $file); |
||||||
30 | } |
||||||
31 | } |
||||||
32 | @closedir($handle); |
||||||
33 | |||||||
34 | return($total_size); |
||||||
35 | } |
||||||
36 | |||||||
37 | /** |
||||||
38 | * Crops and resizes an image |
||||||
39 | * |
||||||
40 | * @param string $source Path to source image |
||||||
41 | * @param string $destination Path to destination |
||||||
42 | * If not set, will modify the source image |
||||||
43 | * @param array $params An array of cropping/resizing parameters |
||||||
44 | * - INT 'w' represents the width of the new image |
||||||
45 | * With upscaling disabled, this is the maximum width |
||||||
46 | * of the new image (in case the source image is |
||||||
47 | * smaller than the expected width) |
||||||
48 | * - INT 'h' represents the height of the new image |
||||||
49 | * With upscaling disabled, this is the maximum height |
||||||
50 | * - INT 'x1', 'y1', 'x2', 'y2' represent optional cropping |
||||||
51 | * coordinates. The source image will first be cropped |
||||||
52 | * to these coordinates, and then resized to match |
||||||
53 | * width/height parameters |
||||||
54 | * - BOOL 'square' - square images will fill the |
||||||
55 | * bounding box (width x height). In Imagine's terms, |
||||||
56 | * this equates to OUTBOUND mode |
||||||
57 | * - BOOL 'upscale' - if enabled, smaller images |
||||||
58 | * will be upscaled to fit the bounding box. |
||||||
59 | * @return bool |
||||||
60 | * @since 2.3 |
||||||
61 | */ |
||||||
62 | function elgg_save_resized_image($source, $destination = null, array $params = []) { |
||||||
63 | return _elgg_services()->imageService->resize($source, $destination, $params); |
||||||
64 | } |
||||||
65 | |||||||
66 | /** |
||||||
67 | * Delete a directory and all its contents |
||||||
68 | * |
||||||
69 | * @param string $directory Directory to delete |
||||||
70 | * |
||||||
71 | * @return bool |
||||||
72 | */ |
||||||
73 | function delete_directory($directory) { |
||||||
74 | |||||||
75 | 214 | if (!file_exists($directory)) { |
|||||
76 | 213 | return true; |
|||||
77 | } |
||||||
78 | |||||||
79 | 2 | if (!is_dir($directory)) { |
|||||
80 | return false; |
||||||
81 | } |
||||||
82 | |||||||
83 | // sanity check: must be a directory |
||||||
84 | 2 | if (!$handle = opendir($directory)) { |
|||||
85 | return false; |
||||||
86 | } |
||||||
87 | |||||||
88 | // loop through all files |
||||||
89 | 2 | while (($file = readdir($handle)) !== false) { |
|||||
90 | 2 | if (in_array($file, ['.', '..'])) { |
|||||
91 | 2 | continue; |
|||||
92 | } |
||||||
93 | |||||||
94 | 2 | $path = "$directory/$file"; |
|||||
95 | 2 | if (is_dir($path)) { |
|||||
96 | // recurse down through directory |
||||||
97 | 2 | if (!delete_directory($path)) { |
|||||
98 | 2 | return false; |
|||||
99 | } |
||||||
100 | } else { |
||||||
101 | // delete file |
||||||
102 | 1 | unlink($path); |
|||||
103 | } |
||||||
104 | } |
||||||
105 | |||||||
106 | // remove empty directory |
||||||
107 | 2 | closedir($handle); |
|||||
108 | 2 | return rmdir($directory); |
|||||
109 | } |
||||||
110 | |||||||
111 | /** |
||||||
112 | * Returns the category of a file from its MIME type |
||||||
113 | * |
||||||
114 | * @param string $mime_type The MIME type |
||||||
115 | * |
||||||
116 | * @return string 'document', 'audio', 'video', or 'general' if the MIME type was unrecognized |
||||||
117 | * @since 1.10 |
||||||
118 | */ |
||||||
119 | function elgg_get_file_simple_type($mime_type) { |
||||||
120 | 47 | $params = ['mime_type' => $mime_type]; |
|||||
121 | 47 | return elgg_trigger_plugin_hook('simple_type', 'file', $params, 'general'); |
|||||
122 | } |
||||||
123 | |||||||
124 | /** |
||||||
125 | * Register file-related handlers on "init, system" event |
||||||
126 | * |
||||||
127 | * @return void |
||||||
128 | * @access private |
||||||
129 | */ |
||||||
130 | function _elgg_filestore_init() { |
||||||
131 | |||||||
132 | // Fix MIME type detection for Microsoft zipped formats |
||||||
133 | 93 | elgg_register_plugin_hook_handler('mime_type', 'file', '_elgg_filestore_detect_mimetype'); |
|||||
134 | |||||||
135 | // Parse category of file from MIME type |
||||||
136 | 93 | elgg_register_plugin_hook_handler('simple_type', 'file', '_elgg_filestore_parse_simpletype'); |
|||||
137 | |||||||
138 | // Unit testing |
||||||
139 | 93 | elgg_register_plugin_hook_handler('unit_test', 'system', '_elgg_filestore_test'); |
|||||
140 | |||||||
141 | // Handler for serving embedded icons |
||||||
142 | 93 | elgg_register_page_handler('serve-icon', '_elgg_filestore_serve_icon_handler'); |
|||||
0 ignored issues
–
show
|
|||||||
143 | |||||||
144 | // Touch entity icons if entity access id has changed |
||||||
145 | 93 | elgg_register_event_handler('update:after', 'object', '_elgg_filestore_touch_icons'); |
|||||
146 | 93 | elgg_register_event_handler('update:after', 'group', '_elgg_filestore_touch_icons'); |
|||||
147 | |||||||
148 | // Move entity icons if entity owner has changed |
||||||
149 | 93 | elgg_register_event_handler('update:after', 'object', '_elgg_filestore_move_icons'); |
|||||
150 | 93 | elgg_register_event_handler('update:after', 'group', '_elgg_filestore_move_icons'); |
|||||
151 | 93 | } |
|||||
152 | |||||||
153 | /** |
||||||
154 | * Fix MIME type detection for Microsoft zipped formats |
||||||
155 | * |
||||||
156 | * @param string $hook "mime_type" |
||||||
157 | * @param string $type "file" |
||||||
158 | * @param string $mime_type Detected MIME type |
||||||
159 | * @param array $params Hook parameters |
||||||
160 | * |
||||||
161 | * @return string The MIME type |
||||||
162 | * @access private |
||||||
163 | */ |
||||||
164 | function _elgg_filestore_detect_mimetype($hook, $type, $mime_type, $params) { |
||||||
2 ignored issues
–
show
The parameter
$type is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.
Loading history...
The parameter
$hook is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.
Loading history...
|
|||||||
165 | |||||||
166 | 37 | $original_filename = elgg_extract('original_filename', $params); |
|||||
167 | 37 | $ext = pathinfo($original_filename, PATHINFO_EXTENSION); |
|||||
168 | |||||||
169 | 37 | return (new \Elgg\Filesystem\MimeTypeDetector())->fixDetectionErrors($mime_type, $ext); |
|||||
170 | } |
||||||
171 | |||||||
172 | /** |
||||||
173 | * Parse a file category of file from a MIME type |
||||||
174 | * |
||||||
175 | * @param string $hook "simple_type" |
||||||
176 | * @param string $type "file" |
||||||
177 | * @param string $simple_type The category of file |
||||||
178 | * @param array $params Hook parameters |
||||||
179 | * |
||||||
180 | * @return string 'document', 'audio', 'video', or 'general' if the MIME type is unrecognized |
||||||
181 | * @access private |
||||||
182 | */ |
||||||
183 | function _elgg_filestore_parse_simpletype($hook, $type, $simple_type, $params) { |
||||||
2 ignored issues
–
show
The parameter
$hook is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.
Loading history...
The parameter
$type is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.
Loading history...
|
|||||||
184 | |||||||
185 | 47 | $mime_type = elgg_extract('mime_type', $params); |
|||||
186 | |||||||
187 | 47 | switch ($mime_type) { |
|||||
188 | 1 | case "application/msword": |
|||||
189 | 1 | case "application/vnd.openxmlformats-officedocument.wordprocessingml.document": |
|||||
190 | 1 | case "application/pdf": |
|||||
191 | return "document"; |
||||||
192 | |||||||
193 | 1 | case "application/ogg": |
|||||
194 | return "audio"; |
||||||
195 | } |
||||||
196 | |||||||
197 | 47 | if (preg_match('~^(audio|image|video)/~', $mime_type, $m)) { |
|||||
198 | 46 | return $m[1]; |
|||||
199 | } |
||||||
200 | 1 | if (0 === strpos($mime_type, 'text/') || false !== strpos($mime_type, 'opendocument')) { |
|||||
201 | return "document"; |
||||||
202 | } |
||||||
203 | |||||||
204 | // unrecognized MIME |
||||||
205 | 1 | return $simple_type; |
|||||
206 | } |
||||||
207 | |||||||
208 | /** |
||||||
209 | * Unit tests for files |
||||||
210 | * |
||||||
211 | * @param string $hook 'unit_test' |
||||||
212 | * @param string $type 'system' |
||||||
213 | * @param mixed $value Array of tests |
||||||
214 | * |
||||||
215 | * @return array |
||||||
216 | * @access private |
||||||
217 | * @codeCoverageIgnore |
||||||
218 | */ |
||||||
219 | function _elgg_filestore_test($hook, $type, $value) { |
||||||
220 | $value[] = ElggCoreFilestoreTest::class; |
||||||
221 | return $value; |
||||||
222 | } |
||||||
223 | |||||||
224 | /** |
||||||
225 | * Returns file's download URL |
||||||
226 | * |
||||||
227 | * @note This does not work for files with custom filestores. |
||||||
228 | * |
||||||
229 | * @param \ElggFile $file File object or entity (must have the default filestore) |
||||||
230 | * @param bool $use_cookie Limit URL validity to current session only |
||||||
231 | * @param string $expires URL expiration, as a string suitable for strtotime() |
||||||
232 | * @return string |
||||||
233 | */ |
||||||
234 | function elgg_get_download_url(\ElggFile $file, $use_cookie = true, $expires = '+2 hours') { |
||||||
235 | return $file->getDownloadURL($use_cookie, $expires); |
||||||
236 | } |
||||||
237 | |||||||
238 | /** |
||||||
239 | * Returns file's URL for inline display |
||||||
240 | * Suitable for displaying cacheable resources, such as user avatars |
||||||
241 | * |
||||||
242 | * @note This does not work for files with custom filestores. |
||||||
243 | * |
||||||
244 | * @param \ElggFile $file File object or entity (must have the default filestore) |
||||||
245 | * @param bool $use_cookie Limit URL validity to current session only |
||||||
246 | * @param string $expires URL expiration, as a string suitable for strtotime() |
||||||
247 | * @return string |
||||||
248 | */ |
||||||
249 | function elgg_get_inline_url(\ElggFile $file, $use_cookie = false, $expires = '') { |
||||||
250 | 1 | return $file->getInlineURL($use_cookie, $expires); |
|||||
251 | } |
||||||
252 | |||||||
253 | /** |
||||||
254 | * Returns a URL suitable for embedding entity's icon in a text editor. |
||||||
255 | * We can not use elgg_get_inline_url() for these purposes due to a URL structure |
||||||
256 | * bound to user session and file modification time. |
||||||
257 | * This function returns a generic (permanent) URL that will then be resolved to |
||||||
258 | * an inline URL whenever requested. |
||||||
259 | * |
||||||
260 | * @param \ElggEntity $entity Entity |
||||||
261 | * @param string $size Size |
||||||
262 | * @return string |
||||||
263 | * @since 2.2 |
||||||
264 | */ |
||||||
265 | function elgg_get_embed_url(\ElggEntity $entity, $size) { |
||||||
266 | return elgg_normalize_url("serve-icon/$entity->guid/$size"); |
||||||
267 | } |
||||||
268 | |||||||
269 | /** |
||||||
270 | * Handler for /serve-icon resources |
||||||
271 | * /serve-icon/<entity_guid>/<size> |
||||||
272 | * |
||||||
273 | * @return void |
||||||
274 | * @access private |
||||||
275 | * @since 2.2 |
||||||
276 | */ |
||||||
277 | function _elgg_filestore_serve_icon_handler() { |
||||||
278 | $response = _elgg_services()->iconService->handleServeIconRequest(); |
||||||
279 | $response->send(); |
||||||
280 | exit; |
||||||
1 ignored issue
–
show
|
|||||||
281 | } |
||||||
282 | |||||||
283 | /** |
||||||
284 | * Reset icon URLs if access_id has changed |
||||||
285 | * |
||||||
286 | * @param string $event "update:after" |
||||||
287 | * @param string $type "object"|"group" |
||||||
288 | * @param ElggObject $entity Entity |
||||||
289 | * @return void |
||||||
290 | * @access private |
||||||
291 | */ |
||||||
292 | function _elgg_filestore_touch_icons($event, $type, $entity) { |
||||||
293 | 14 | $original_attributes = $entity->getOriginalAttributes(); |
|||||
294 | 14 | if (!array_key_exists('access_id', $original_attributes)) { |
|||||
295 | 9 | return; |
|||||
296 | } |
||||||
297 | 5 | if ($entity instanceof \ElggFile) { |
|||||
298 | // we touch the file to invalidate any previously generated download URLs |
||||||
299 | $entity->setModifiedTime(); |
||||||
300 | } |
||||||
301 | 5 | $sizes = array_keys(elgg_get_icon_sizes($entity->getType(), $entity->getSubtype())); |
|||||
302 | 5 | foreach ($sizes as $size) { |
|||||
303 | 5 | $icon = $entity->getIcon($size); |
|||||
304 | 5 | if ($icon->exists()) { |
|||||
305 | 5 | $icon->setModifiedTime(); |
|||||
306 | } |
||||||
307 | } |
||||||
308 | 5 | } |
|||||
309 | |||||||
310 | /** |
||||||
311 | * Listen to entity ownership changes and update icon ownership by moving |
||||||
312 | * icons to their new owner's directory on filestore. |
||||||
313 | * |
||||||
314 | * This will only transfer icons that have a custom location on filestore |
||||||
315 | * and are owned by the entity's owner (instead of the entity itself). |
||||||
316 | * Even though core icon service does not store icons in the entity's owner |
||||||
317 | * directory, there are plugins that do (e.g. file plugin) - this handler |
||||||
318 | * helps such plugins avoid ownership mismatch. |
||||||
319 | * |
||||||
320 | * @param string $event "update:after" |
||||||
321 | * @param string $type "object"|"group" |
||||||
322 | * @param ElggObject $entity Entity |
||||||
323 | * @return void |
||||||
324 | * @access private |
||||||
325 | */ |
||||||
326 | function _elgg_filestore_move_icons($event, $type, $entity) { |
||||||
1 ignored issue
–
show
The parameter
$event is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.
Loading history...
The parameter
$type is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.
Loading history...
|
|||||||
327 | |||||||
328 | 14 | $original_attributes = $entity->getOriginalAttributes(); |
|||||
329 | 14 | if (empty($original_attributes['owner_guid'])) { |
|||||
330 | 13 | return; |
|||||
331 | } |
||||||
332 | |||||||
333 | 1 | $previous_owner_guid = $original_attributes['owner_guid']; |
|||||
334 | 1 | $new_owner_guid = $entity->owner_guid; |
|||||
335 | |||||||
336 | 1 | $sizes = elgg_get_icon_sizes($entity->getType(), $entity->getSubtype()); |
|||||
337 | |||||||
338 | 1 | foreach ($sizes as $size => $opts) { |
|||||
339 | 1 | $new_icon = $entity->getIcon($size); |
|||||
340 | 1 | if ($new_icon->owner_guid == $entity->guid) { |
|||||
341 | // we do not need to update icons that are owned by the entity itself |
||||||
342 | 1 | continue; |
|||||
343 | } |
||||||
344 | |||||||
345 | if ($new_icon->owner_guid != $new_owner_guid) { |
||||||
346 | // a plugin implements some custom logic |
||||||
347 | continue; |
||||||
348 | } |
||||||
349 | |||||||
350 | $old_icon = new \ElggIcon(); |
||||||
351 | $old_icon->owner_guid = $previous_owner_guid; |
||||||
352 | $old_icon->setFilename($new_icon->getFilename()); |
||||||
353 | if (!$old_icon->exists()) { |
||||||
354 | // there is no icon to move |
||||||
355 | continue; |
||||||
356 | } |
||||||
357 | |||||||
358 | if ($new_icon->exists()) { |
||||||
359 | // there is already a new icon |
||||||
360 | // just removing the old one |
||||||
361 | $old_icon->delete(); |
||||||
362 | elgg_log("Entity $entity->guid has been transferred to a new owner but an icon was " |
||||||
363 | . "left behind under {$old_icon->getFilenameOnFilestore()}. " |
||||||
364 | . "Old icon has been deleted", 'NOTICE'); |
||||||
365 | continue; |
||||||
366 | } |
||||||
367 | |||||||
368 | $old_icon->transfer($new_icon->owner_guid, $new_icon->getFilename()); |
||||||
369 | elgg_log("Entity $entity->guid has been transferred to a new owner. " |
||||||
370 | . "Icon was moved from {$old_icon->getFilenameOnFilestore()} to {$new_icon->getFilenameOnFilestore()}.", 'NOTICE'); |
||||||
371 | } |
||||||
372 | 1 | } |
|||||
373 | |||||||
374 | /** |
||||||
375 | * Returns an array of uploaded file objects regardless of upload status/errors |
||||||
376 | * |
||||||
377 | * @param string $input_name Form input name |
||||||
378 | * @return UploadedFile[]|false |
||||||
379 | */ |
||||||
380 | function elgg_get_uploaded_files($input_name) { |
||||||
381 | return _elgg_services()->uploads->getFiles($input_name); |
||||||
382 | } |
||||||
383 | |||||||
384 | /** |
||||||
385 | * Returns a single valid uploaded file object |
||||||
386 | * |
||||||
387 | * @param string $input_name Form input name |
||||||
388 | * @param bool $check_for_validity If there is an uploaded file, is it required to be valid |
||||||
389 | * |
||||||
390 | * @return UploadedFile|false |
||||||
391 | */ |
||||||
392 | function elgg_get_uploaded_file($input_name, $check_for_validity = true) { |
||||||
393 | return _elgg_services()->uploads->getFile($input_name, $check_for_validity); |
||||||
394 | } |
||||||
395 | |||||||
396 | /** |
||||||
397 | * Returns a ElggTempFile which can handle writing/reading of data to a temporary file location |
||||||
398 | * |
||||||
399 | * @return ElggTempFile |
||||||
400 | * @since 3.0 |
||||||
401 | */ |
||||||
402 | function elgg_get_temp_file() { |
||||||
403 | 1 | return new ElggTempFile(); |
|||||
404 | } |
||||||
405 | |||||||
406 | /** |
||||||
407 | * @see \Elgg\Application::loadCore Do not do work here. Just register for events. |
||||||
408 | */ |
||||||
409 | return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) { |
||||||
1 ignored issue
–
show
The parameter
$hooks is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.
Loading history...
|
|||||||
410 | 18 | $events->registerHandler('init', 'system', '_elgg_filestore_init', 100); |
|||||
411 | }; |
||||||
412 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.