Passed
Push — master ( c7bfcd...219f2b )
by Aimeos
06:02
created

Standard::store()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 3
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2021
6
 * @package Controller
7
 * @subpackage Common
8
 */
9
10
11
namespace Aimeos\Controller\Common\Media;
12
13
14
/**
15
 * Common media controller methods
16
 *
17
 * @package Controller
18
 * @subpackage Common
19
 */
20
class Standard
21
	implements \Aimeos\Controller\Common\Media\Iface
22
{
23
	private $context;
24
25
26
	/**
27
	 * Initializes the object
28
	 *
29
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object
30
	 */
31
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context )
32
	{
33
		$this->context = $context;
34
	}
35
36
37
	/**
38
	 * Stores the uploaded file and adds the references to the media item
39
	 *
40
	 * {inheritDoc}
41
	 *
42
	 * @param \Aimeos\MShop\Media\Item\Iface $item Media item to add the file references to
43
	 * @param \Psr\Http\Message\UploadedFileInterface $file Uploaded file
44
	 * @return \Aimeos\MShop\Media\Item\Iface Added media item
45
	 */
46
	public function add( \Aimeos\MShop\Media\Item\Iface $item, \Psr\Http\Message\UploadedFileInterface $file ) : \Aimeos\MShop\Media\Item\Iface
47
	{
48
		$this->checkFileUpload( $file );
49
50
		$media = $this->getMediaFile( $file->getStream() );
51
		$mimetype = $this->getMimeType( $media, 'files' );
52
		$filepath = $this->getFilePath( $file->getClientFilename() ?: rand(), 'files', $mimetype );
53
54
		$path = $item->getUrl();
55
		$fsname = $item->getFileSystem();
56
		$fs = $this->context->getFilesystemManager()->get( $fsname );
57
58
		if( $path && $fs->has( $path ) ) {
59
			$fs->rm( $path );
60
		}
61
62
		$this->store( $filepath, $media->save(), $fsname );
0 ignored issues
show
Bug introduced by
It seems like $media->save() can also be of type null; however, parameter $content of Aimeos\Controller\Common\Media\Standard::store() does only seem to accept string, 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 ignore-type  annotation

62
		$this->store( $filepath, /** @scrutinizer ignore-type */ $media->save(), $fsname );
Loading history...
63
64
		if( $media instanceof \Aimeos\MW\Media\Image\Iface ) {
65
			$item = $this->addImages( $item, $media, $file->getClientFilename(), $fsname );
66
		} else {
67
			$item->setPreviews( [1 => $this->getMimeIcon( $mimetype, $fsname )] )->setMimeType( $mimetype );
68
		}
69
70
		return $item->setUrl( $filepath )->setMimeType( $media->getMimeType() )
71
			->setLabel( $item->getLabel() ?: $file->getClientFilename() );
72
	}
73
74
75
	/**
76
	 * Stores the uploaded preview and adds the references to the media item
77
	 *
78
	 * {inheritDoc}
79
	 *
80
	 * @param \Aimeos\MShop\Media\Item\Iface $item Media item to add the file references to
81
	 * @param \Psr\Http\Message\UploadedFileInterface $file Uploaded file
82
	 * @return \Aimeos\MShop\Media\Item\Iface Added media item
83
	 */
84
	public function addPreview( \Aimeos\MShop\Media\Item\Iface $item, \Psr\Http\Message\UploadedFileInterface $file ) : \Aimeos\MShop\Media\Item\Iface
85
	{
86
		$this->checkFileUpload( $file );
87
88
		$media = $this->getMediaFile( $file->getStream() );
89
90
		if( $media instanceof \Aimeos\MW\Media\Image\Iface ) {
91
			$item = $this->addImages( $item, $media, $file->getClientFilename(), $item->getFileSystem() );
92
		}
93
94
		return $item;
95
	}
96
97
98
	/**
99
	 * Copies the media item and the referenced files
100
	 *
101
	 * @param \Aimeos\MShop\Media\Item\Iface $item Media item whose files should be copied
102
	 * @return \Aimeos\MShop\Media\Item\Iface Copied media item with new files
103
	 */
104
	public function copy( \Aimeos\MShop\Media\Item\Iface $item ) : \Aimeos\MShop\Media\Item\Iface
105
	{
106
		$manager = \Aimeos\MShop::create( $this->context, 'media' );
107
108
		$search = $manager->filter()->slice( 0, 1 );
109
		$search->setConditions( $search->compare( '==', 'media.url', $item->getUrl() ) );
110
111
		$item = $manager->search( $search )->first( $item )->setId( null );
112
113
		$path = $item->getUrl();
114
		$previews = $item->getPreviews();
115
		$fsname = $item->getFileSystem();
116
		$fs = $this->context->getFilesystemManager()->get( $fsname );
117
118
		if( $fs->has( $path ) )
119
		{
120
			$newPath = $this->getFilePath( $path, 'files', $item->getMimeType() );
121
			$fs->copy( $path, $newPath );
122
			$item->setUrl( $newPath );
123
		}
124
125
		foreach( $previews as $size => $preview )
126
		{
127
			if( $fsname !== 'fs-mimeicon' && $fs->has( $preview ) )
128
			{
129
				$newPath = $this->getFilePath( $preview, 'preview', pathinfo( $preview, PATHINFO_EXTENSION ) );
0 ignored issues
show
Bug introduced by
It seems like pathinfo($preview, Aimeo...dia\PATHINFO_EXTENSION) can also be of type array; however, parameter $mimeext of Aimeos\Controller\Common...Standard::getFilePath() does only seem to accept string, 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 ignore-type  annotation

129
				$newPath = $this->getFilePath( $preview, 'preview', /** @scrutinizer ignore-type */ pathinfo( $preview, PATHINFO_EXTENSION ) );
Loading history...
130
				$fs->copy( $preview, $newPath );
131
				$previews[$size] = $newPath;
132
			}
133
		}
134
135
		return $item->setPreviews( $previews );
136
	}
137
138
139
	/**
140
	 * Deletes the files of the media item
141
	 *
142
	 * {inheritDoc}
143
	 *
144
	 * @param \Aimeos\MShop\Media\Item\Iface $item Media item whose files should be deleted
145
	 * @return \Aimeos\MShop\Media\Item\Iface Media item with deleted files
146
	 */
147
	public function delete( \Aimeos\MShop\Media\Item\Iface $item ) : \Aimeos\MShop\Media\Item\Iface
148
	{
149
		$manager = \Aimeos\MShop::create( $this->context, 'media' );
150
		$search = $manager->filter()->slice( 0, 2 );
151
		$search->setConditions( $search->compare( '==', 'media.url', $item->getUrl() ) );
152
153
		if( count( $manager->search( $search ) ) > 1 ) {
154
			return $item->setUrl( '' )->setPreview( '' );
155
		}
156
157
		$path = $item->getUrl();
158
		$fsname = $item->getFileSystem();
159
		$fs = $this->context->getFilesystemManager()->get( $fsname );
160
161
		if( $path && $fs->has( $path ) ) {
162
			$fs->rm( $path );
163
		}
164
165
		return $this->deletePreviews( $item, $fsname )->setUrl( '' )
166
			->deletePropertyItems( $item->getPropertyItems()->toArray() );
167
	}
168
169
170
	/**
171
	 * Rescales the files (original and preview) referenced by the media item
172
	 *
173
	 * The height/width configuration for scaling and which one should be scaled is used from
174
	 * - controller/common/media/<files|preview>/maxheight
175
	 * - controller/common/media/<files|preview>/maxwidth
176
	 * - controller/common/media/<files|preview>/scale
177
	 *
178
	 * @param \Aimeos\MShop\Media\Item\Iface $item Media item whose files should be scaled
179
	 * @param bool $force True to enforce creating new preview images
180
	 * @return \Aimeos\MShop\Media\Item\Iface Rescaled media item
181
	 */
182
	public function scale( \Aimeos\MShop\Media\Item\Iface $item, bool $force = false ) : \Aimeos\MShop\Media\Item\Iface
183
	{
184
		$fsname = $item->getFileSystem();
185
		$fs = $this->context->fs( $fsname );
186
		$is = ( $fs instanceof \Aimeos\MW\Filesystem\MetaIface ? true : false );
187
188
		if( !$force && !( $is && date( 'Y-m-d H:i:s', $fs->time( $item->getUrl() ) ) < $item->getTimeModified() ) ) {
0 ignored issues
show
Bug introduced by
The method time() does not exist on Aimeos\MW\Filesystem\Iface. Since it exists in all sub-types, consider adding an abstract or default implementation to Aimeos\MW\Filesystem\Iface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

188
		if( !$force && !( $is && date( 'Y-m-d H:i:s', $fs->/** @scrutinizer ignore-call */ time( $item->getUrl() ) ) < $item->getTimeModified() ) ) {
Loading history...
189
			return $item;
190
		}
191
192
		$name = basename( $item->getUrl() );
193
		$media = $this->getMediaFile( $this->getFileContent( $item->getUrl(), $fsname ) );
194
195
		if( $media instanceof \Aimeos\MW\Media\Image\Iface )
196
		{
197
			$item = $this->deletePreviews( $item, $fsname );
198
			$item = $this->addImages( $item, $media, $name, $fsname );
199
		}
200
		else
201
		{
202
			$mimetype = $this->getMimeType( $media, 'files' );
203
			$item = $item->setPreviews( [1 => $this->getMimeIcon( $mimetype, $fsname )] )
204
				->setMimeType( $mimetype )->setFileSystem( 'fs-mimeicon' );
205
		}
206
207
		return $item;
208
	}
209
210
211
	/**
212
	 * Adds original image and preview images to the media item
213
	 *
214
	 * @param \Aimeos\MShop\Media\Item\Iface $item Media item which will contain the image URLs afterwards
215
	 * @param \Aimeos\MW\Media\Image\Iface $media Image object to scale
216
	 * @param string|null $name Name of the file, NULL for random name
217
	 * @param string $fsname File system name the file is located at
218
	 * @return \Aimeos\MShop\Media\Item\Iface Updated media item with URLs
219
	 */
220
	protected function addImages( \Aimeos\MShop\Media\Item\Iface $item, \Aimeos\MW\Media\Image\Iface $media, ?string $name, string $fsname ) : \Aimeos\MShop\Media\Item\Iface
221
	{
222
		$previews = [];
223
		$mime = $this->getMimeType( $media, 'preview' );
224
		$item = $this->deletePreviews( $item, $fsname );
225
226
		foreach( $this->createPreviews( $media, $item->getDomain(), $item->getType() ) as $type => $mediaFile )
0 ignored issues
show
Bug introduced by
It seems like $item->getType() can also be of type null; however, parameter $type of Aimeos\Controller\Common...ndard::createPreviews() does only seem to accept string, 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 ignore-type  annotation

226
		foreach( $this->createPreviews( $media, $item->getDomain(), /** @scrutinizer ignore-type */ $item->getType() ) as $type => $mediaFile )
Loading history...
227
		{
228
			$filepath = $this->getFilePath( $name ?: rand(), 'preview', $media->getMimeType() );
229
			$this->store( $filepath, $mediaFile->save( null, $mime ), $fsname );
230
			$previews[$mediaFile->getWidth()] = $filepath;
231
			unset( $mediaFile );
232
		}
233
234
		return $item->setPreviews( $previews );
235
	}
236
237
238
	/**
239
	 * Checks if an error during upload occured
240
	 *
241
	 * @param \Psr\Http\Message\UploadedFileInterface $file Uploaded file
242
	 * @throws \Aimeos\Controller\Common\Exception If an error occured during upload
243
	 */
244
	protected function checkFileUpload( \Psr\Http\Message\UploadedFileInterface $file )
245
	{
246
		if( $file->getError() !== UPLOAD_ERR_OK )
247
		{
248
			switch( $file->getError() )
249
			{
250
				case UPLOAD_ERR_INI_SIZE:
251
				case UPLOAD_ERR_FORM_SIZE:
252
					throw new \Aimeos\Controller\Common\Exception( 'The uploaded file exceeds the max. allowed filesize' );
253
				case UPLOAD_ERR_PARTIAL:
254
					throw new \Aimeos\Controller\Common\Exception( 'The uploaded file was only partially uploaded' );
255
				case UPLOAD_ERR_NO_FILE:
256
					throw new \Aimeos\Controller\Common\Exception( 'No file was uploaded' );
257
				case UPLOAD_ERR_NO_TMP_DIR:
258
					throw new \Aimeos\Controller\Common\Exception( 'Temporary folder is missing' );
259
				case UPLOAD_ERR_CANT_WRITE:
260
					throw new \Aimeos\Controller\Common\Exception( 'Failed to write file to disk' );
261
				case UPLOAD_ERR_EXTENSION:
262
					throw new \Aimeos\Controller\Common\Exception( 'File upload stopped by extension' );
263
				default:
264
					throw new \Aimeos\Controller\Common\Exception( 'Unknown upload error' );
265
			}
266
		}
267
	}
268
269
270
	/**
271
	 * Creates scaled images according to the configuration settings
272
	 *
273
	 * @param \Aimeos\MW\Media\Image\Iface $media Media object
274
	 * @param string $domain Domain the item is from, e.g. product, catalog, etc.
275
	 * @param string $type Type of the item within the given domain, e.g. default, stage, etc.
276
	 * @return \Aimeos\MW\Media\Image\Iface[] Associative list of image width as keys and scaled media object as values
277
	 */
278
	protected function createPreviews( \Aimeos\MW\Media\Image\Iface $media, string $domain, string $type ) : array
279
	{
280
		$list = [];
281
		$config = $this->context->getConfig();
282
283
		/** controller/common/media/previews
284
		 * Scaling options for preview images
285
		 *
286
		 * For responsive images, several preview images of different sizes are
287
		 * generated. This setting controls how many preview images are generated,
288
		 * what's their maximum width and height and if the given width/height is
289
		 * enforced by cropping images that doesn't fit.
290
		 *
291
		 * The setting must consist of a list image size definitions like:
292
		 *
293
		 *  [
294
		 *    ['maxwidth' => 240, 'maxheight' => 320, 'force-size' => true],
295
		 *    ['maxwidth' => 720, 'maxheight' => 960, 'force-size' => false],
296
		 *    ['maxwidth' => 2160, 'maxheight' => 2880, 'force-size' => false],
297
		 *  ]
298
		 *
299
		 * "maxwidth" sets the maximum allowed width of the image whereas
300
		 * "maxheight" does the same for the maximum allowed height. If both
301
		 * values are given, the image is scaled proportionally so it fits into
302
		 * the box defined by both values. In case the image has different
303
		 * proportions than the specified ones and "force-size" is false, the
304
		 * image is resized to fit entirely into the specified box. One side of
305
		 * the image will be shorter than it would be possible by the specified
306
		 * box.
307
		 *
308
		 * If "force-size" is true, scaled images that doesn't fit into the
309
		 * given maximum width/height are centered and then cropped. By default,
310
		 * images aren't cropped.
311
		 *
312
		 * The values for "maxwidth" and "maxheight" can also be null or not
313
		 * used. In that case, the width or height or both is unbound. If none
314
		 * of the values are given, the image won't be scaled at all. If only
315
		 * one value is set, the image will be scaled exactly to the given width
316
		 * or height and the other side is scaled proportionally.
317
		 *
318
		 * You can also define different preview sizes for different domains (e.g.
319
		 * for catalog images) and for different types (e.g. catalog stage images).
320
		 * Use configuration settings like
321
		 *
322
		 *  controller/common/media/<domain>/previews
323
		 *  controller/common/media/<domain>/<type>/previews
324
		 *
325
		 * for example:
326
		 *
327
		 *  controller/common/media/catalog/previews => [
328
		 *    ['maxwidth' => 240, 'maxheight' => 320, 'force-size' => true],
329
		 *  ]
330
		 *  controller/common/media/catalog/previews => [
331
		 *    ['maxwidth' => 400, 'maxheight' => 300, 'force-size' => false]
332
		 *  ]
333
		 *  controller/common/media/catalog/stage/previews => [
334
		 *    ['maxwidth' => 360, 'maxheight' => 320, 'force-size' => true],
335
		 *    ['maxwidth' => 720, 'maxheight' => 480, 'force-size' => true]
336
		 *  ]
337
		 *
338
		 * These settings will create two preview images for catalog stage images,
339
		 * one with a different size for all other catalog images and all images
340
		 * from other domains will be sized to 240x320px. The available domains
341
		 * which can have images are:
342
		 *
343
		 * * attribute
344
		 * * catalog
345
		 * * product
346
		 * * service
347
		 * * supplier
348
		 *
349
		 * There are a few image types included per domain ("default" is always
350
		 * available). You can also add your own types in the admin backend and
351
		 * extend the frontend to display them where you need them.
352
		 *
353
		 * @param array List of image size definitions
354
		 * @category Developer
355
		 * @category User
356
		 * @since 2019.07
357
		 */
358
		$previews = $config->get( 'controller/common/media/previews', [] );
359
		$previews = $config->get( 'controller/common/media/' . $domain . '/previews', $previews );
360
		$previews = $config->get( 'controller/common/media/' . $domain . '/' . $type . '/previews', $previews );
361
362
		foreach( $previews as $entry )
363
		{
364
			$maxwidth = ( isset( $entry['maxwidth'] ) ? (int) $entry['maxwidth'] : null );
365
			$maxheight = ( isset( $entry['maxheight'] ) ? (int) $entry['maxheight'] : null );
366
			$fit = ( isset( $entry['force-size'] ) ? (int) $entry['force-size'] : 0 );
367
368
			if( $maxheight || $maxwidth )
0 ignored issues
show
Bug Best Practice introduced by
The expression $maxwidth of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $maxheight of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
369
			{
370
				$image = $media->scale( $maxwidth, $maxheight, $fit );
371
				$list[$image->getWidth()] = $image;
372
			}
373
		}
374
375
		return $list;
376
	}
377
378
379
	/**
380
	 * Removes the previes images from the storage
381
	 *
382
	 * @param \Aimeos\MShop\Media\Item\Iface $item Media item which will contains the image URLs afterwards
383
	 * @param string $fsname File system name the file is located at
384
	 * @return \Aimeos\MShop\Media\Item\Iface Media item with preview images removed
385
	 */
386
	protected function deletePreviews( \Aimeos\MShop\Media\Item\Iface $item, string $fsname )
387
	{
388
		$fs = $this->context->getFilesystemManager()->get( $fsname );
389
		$previews = $item->getPreviews();
390
391
		// don't delete first (smallest) image because it's referenced in past orders
392
		if( 'product' === $item->getDomain() ) {
393
			$previews = array_slice( $previews, 1 );
394
		}
395
396
		foreach( $previews as $preview )
397
		{
398
			if( $fsname !== 'fs-mimeicon' && $preview && $fs->has( $preview ) ) {
399
				$fs->rm( $preview );
400
			}
401
		}
402
403
		return $item->setPreviews( [] );
404
	}
405
406
407
	/**
408
	 * Returns the context item
409
	 *
410
	 * @return \Aimeos\MShop\Context\Item\Iface Context item
411
	 */
412
	protected function getContext() : \Aimeos\MShop\Context\Item\Iface
413
	{
414
		return $this->context;
415
	}
416
417
418
	/**
419
	 * Returns the file content of the file or URL
420
	 *
421
	 * @param string $path Path to the file or URL
422
	 * @param string $fsname File system name the file is located at
423
	 * @return string File content
424
	 * @throws \Aimeos\Controller\Common\Exception If no file is found
425
	 */
426
	protected function getFileContent( string $path, string $fsname ) : string
427
	{
428
		if( $path !== '' )
429
		{
430
			if( preg_match( '#^[a-zA-Z]{1,10}://#', $path ) === 1 )
431
			{
432
				if( ( $content = @file_get_contents( $path ) ) === false )
433
				{
434
					$msg = sprintf( 'Downloading file "%1$s" failed', $path );
435
					throw new \Aimeos\Controller\Common\Exception( $msg );
436
				}
437
438
				return $content;
439
			}
440
441
			$fs = $this->context->getFilesystemManager()->get( $fsname );
442
443
			if( $fs->has( $path ) !== false ) {
444
				return $fs->read( $path );
445
			}
446
		}
447
448
		throw new \Aimeos\Controller\Common\Exception( sprintf( 'File "%1$s" not found', $path ) );
449
	}
450
451
452
	/**
453
	 * Creates a new file path from the given arguments and random values
454
	 *
455
	 * @param string $filename Original file name, can contain the path as well
456
	 * @param string $type File type, i.e. "files" or "preview"
457
	 * @param string $mimeext Mime type or extension of the file
458
	 * @return string New file name including the file path
459
	 */
460
	protected function getFilePath( string $filename, string $type, string $mimeext ) : string
461
	{
462
		/** controller/common/media/extensions
463
		 * Available files extensions for mime types of uploaded files
464
		 *
465
		 * Uploaded files should have the right file extension (e.g. ".jpg" for
466
		 * JPEG images) so files are recognized correctly if downloaded by users.
467
		 * The extension of the uploaded file can't be trusted and only its mime
468
		 * type can be determined automatically. This configuration setting
469
		 * provides the file extensions for the configured mime types. You can
470
		 * add more mime type / file extension combinations if required.
471
		 *
472
		 * @param array Associative list of mime types as keys and file extensions as values
473
		 * @since 2018.04
474
		 * @category Developer
475
		 */
476
		$list = $this->context->getConfig()->get( 'controller/common/media/extensions', [] );
477
478
		$filename = \Aimeos\MW\Str::slug( substr( $filename, 0, strrpos( $filename, '.' ) ?: null ) );
479
		$filename = substr( md5( $filename . getmypid() . microtime( true ) ), -8 ) . '_' . $filename;
480
481
		$ext = isset( $list[$mimeext] ) ? '.' . $list[$mimeext] : ( ctype_alpha( $mimeext ) ? '.' . $mimeext : '' );
482
		$siteId = $this->context->getLocale()->getSiteId();
483
484
		// the "d" after {siteid} is the required extension for Windows (no dots at the and allowed)
485
		return "${siteId}d/${type}/${filename[0]}/${filename[1]}/${filename}${ext}";
486
	}
487
488
489
	/**
490
	 * Returns the media object for the given file name
491
	 *
492
	 * @param string $file Path to the file or file content
493
	 * @return \Aimeos\MW\Media\Iface Media object
494
	 */
495
	protected function getMediaFile( string $file ) : \Aimeos\MW\Media\Iface
496
	{
497
		/** controller/common/media/options
498
		 * Options used for processing the uploaded media files
499
		 *
500
		 * When uploading a file, a preview image for that file is generated if
501
		 * possible (especially for images). You can configure certain options
502
		 * for the generated images, namely the implementation of the scaling
503
		 * algorithm and the quality of the resulting images with
504
		 *
505
		 *  array(
506
		 *  	'image' => array(
507
		 *  		'name' => 'Imagick',
508
		 *  		'quality' => 75,
509
		 * 			'background' => '#f8f8f8' // only if "force-size" is true
510
		 *  	)
511
		 *  )
512
		 *
513
		 * @param array Multi-dimendional list of configuration options
514
		 * @since 2016.01
515
		 * @category Developer
516
		 * @category User
517
		 */
518
		$options = $this->context->getConfig()->get( 'controller/common/media/options', [] );
519
520
		return \Aimeos\MW\Media\Factory::get( $file, $options );
521
	}
522
523
524
	/**
525
	 * Returns the relative path to the mime icon for the given mime type.
526
	 *
527
	 * @param string $mimetype Mime type like "image/png"
528
	 * @param string File system name the icon should be stored at
0 ignored issues
show
Bug introduced by
The type Aimeos\Controller\Common\Media\File was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
529
	 * @return string Relative path to the mime icon
530
	 */
531
	protected function getMimeIcon( string $mimetype, string $fsname ) : string
532
	{
533
		$config = $this->context->getConfig();
534
535
		/** controller/common/media/mimeicon/extension
536
		 * File extension of the mime icon images
537
		 *
538
		 * If you would like to use different mime icons that are available in
539
		 * another file format, you have to change the file extension for the
540
		 * mime icons to the actual ones.
541
		 *
542
		 * Note: The configured file extension needs a leading dot!
543
		 *
544
		 * @param string File extension including a leading dot, e.g ".jpg"
545
		 * @since 2016.01
546
		 * @category Developer
547
		 */
548
		$ext = $config->get( 'controller/common/media/mimeicon/extension', '.png' );
549
550
		$fsmime = $this->context->fs( 'fs-mimeicon' );
551
		$filename = ( $fsmime->has( $mimetype . $ext ) ? $mimetype : 'unknown' ) . $ext;
552
553
		$filepath = $this->getFilePath( $filename, 'preview', ltrim( $ext, '.' ) );
554
		$fs = $this->context->fs( $fsname )->write( $filepath, $fsmime->read( $filename ) );
0 ignored issues
show
Unused Code introduced by
The assignment to $fs is dead and can be removed.
Loading history...
555
556
		return $filepath;
557
	}
558
559
560
	/**
561
	 * Returns the mime type for the new image
562
	 *
563
	 * @param \Aimeos\MW\Media\Iface $media Media object
564
	 * @param string $type Type of the image like "preview" or "files"
565
	 * @return string New mime type
566
	 * @throws \Aimeos\Controller\Common\Exception If no mime types are configured
567
	 */
568
	protected function getMimeType( \Aimeos\MW\Media\Iface $media, string $type ) : string
569
	{
570
		$mimetype = $media->getMimetype();
571
		$config = $this->context->getConfig();
572
573
		/** controller/common/media/files/allowedtypes
574
		 * A list of image mime types that are allowed for uploaded image files
575
		 *
576
		 * The list of allowed image types must be explicitly configured for the
577
		 * uploaded image files. Trying to upload and store an image file not
578
		 * available in the list of allowed mime types will result in an exception.
579
		 *
580
		 * @param array List of image mime types
581
		 * @since 2016.01
582
		 * @category Developer
583
		 * @category User
584
		 */
585
586
		/** controller/common/media/preview/allowedtypes
587
		 * A list of image mime types that are allowed for preview image files
588
		 *
589
		 * The list of allowed image types must be explicitly configured for the
590
		 * preview image files. Trying to create a preview image whose mime type
591
		 * is not available in the list of allowed mime types will result in an
592
		 * exception.
593
		 *
594
		 * @param array List of image mime types
595
		 * @since 2016.01
596
		 * @category Developer
597
		 * @category User
598
		 */
599
		$default = [
600
			'image/jpeg', 'image/png', 'image/gif', 'image/svg+xml',
601
			'application/pdf', 'application/zip',
602
			'video/mp4', 'video/webm'
603
		];
604
		$allowed = $config->get( 'controller/common/media/' . $type . '/allowedtypes', $default );
605
606
		if( in_array( $mimetype, $allowed ) === false )
607
		{
608
			if( ( $defaulttype = reset( $allowed ) ) === false ) {
609
				throw new \Aimeos\Controller\Common\Exception( sprintf( 'No allowed image types configured for "%1$s"', $type ) );
610
			}
611
612
			return $defaulttype;
613
		}
614
615
		return $mimetype;
616
	}
617
618
619
	/**
620
	 * Stores the file content
621
	 *
622
	 * @param string $filepath Path of the new file
623
	 * @param string $content File content
624
	 * @param string $fsname Name of the file system to store the files at
625
	 * @return \Aimeos\Controller\Common\Media\Iface Self object for fluent interface
626
	 */
627
	protected function store( string $filepath, string $content, string $fsname ) : Iface
628
	{
629
		$this->context->getFilesystemManager()->get( $fsname )->write( $filepath, $content );
630
		return $this;
631
	}
632
}
633