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)) { |
||||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
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); |
||||
0 ignored issues
–
show
Are you sure the usage of
closedir($handle) is correct as it seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
It seems like
$handle can also be of type false ; however, parameter $dir_handle of closedir() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
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'); |
|||
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) { |
||||
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) { |
||||
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; |
||||
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) { |
||||
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) { |
||||
410 | 18 | $events->registerHandler('init', 'system', '_elgg_filestore_init', 100); |
|||
411 | }; |
||||
412 |