2022-06-17 17:51:59 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Kirby\Cms;
|
|
|
|
|
|
|
|
use Kirby\Filesystem\F;
|
2022-12-19 14:56:05 +01:00
|
|
|
use Kirby\Filesystem\Mime;
|
2022-06-17 17:51:59 +02:00
|
|
|
use Kirby\Toolkit\Str;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extension of the basic blueprint class
|
|
|
|
* to handle all blueprints for files.
|
|
|
|
*
|
|
|
|
* @package Kirby Cms
|
|
|
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
|
|
|
* @link https://getkirby.com
|
|
|
|
* @copyright Bastian Allgeier
|
|
|
|
* @license https://getkirby.com/license
|
|
|
|
*/
|
|
|
|
class FileBlueprint extends Blueprint
|
|
|
|
{
|
2022-08-31 15:02:43 +02:00
|
|
|
/**
|
|
|
|
* `true` if the default accepted
|
|
|
|
* types are being used
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
protected $defaultTypes = false;
|
|
|
|
|
|
|
|
public function __construct(array $props)
|
|
|
|
{
|
|
|
|
parent::__construct($props);
|
|
|
|
|
|
|
|
// normalize all available page options
|
|
|
|
$this->props['options'] = $this->normalizeOptions(
|
|
|
|
$this->props['options'] ?? true,
|
|
|
|
// defaults
|
|
|
|
[
|
|
|
|
'changeName' => null,
|
|
|
|
'create' => null,
|
|
|
|
'delete' => null,
|
|
|
|
'read' => null,
|
|
|
|
'replace' => null,
|
|
|
|
'update' => null,
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
// normalize the accept settings
|
|
|
|
$this->props['accept'] = $this->normalizeAccept($this->props['accept'] ?? []);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function accept(): array
|
|
|
|
{
|
|
|
|
return $this->props['accept'];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the list of all accepted MIME types for
|
|
|
|
* file upload or `*` if all MIME types are allowed
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function acceptMime(): string
|
|
|
|
{
|
|
|
|
// don't disclose the specific default types
|
|
|
|
if ($this->defaultTypes === true) {
|
|
|
|
return '*';
|
|
|
|
}
|
|
|
|
|
|
|
|
$accept = $this->accept();
|
|
|
|
$restrictions = [];
|
|
|
|
|
|
|
|
if (is_array($accept['mime']) === true) {
|
|
|
|
$restrictions[] = $accept['mime'];
|
|
|
|
} else {
|
|
|
|
// only fall back to the extension or type if
|
|
|
|
// no explicit MIME types were defined
|
|
|
|
// (allows to set custom MIME types for the frontend
|
|
|
|
// check but still restrict the extension and/or type)
|
|
|
|
|
|
|
|
if (is_array($accept['extension']) === true) {
|
|
|
|
// determine the main MIME type for each extension
|
2022-12-19 14:56:05 +01:00
|
|
|
$restrictions[] = array_map(
|
|
|
|
[Mime::class, 'fromExtension'],
|
|
|
|
$accept['extension']
|
|
|
|
);
|
2022-08-31 15:02:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (is_array($accept['type']) === true) {
|
|
|
|
// determine the MIME types of each file type
|
|
|
|
$mimes = [];
|
|
|
|
foreach ($accept['type'] as $type) {
|
|
|
|
if ($extensions = F::typeToExtensions($type)) {
|
2022-12-19 14:56:05 +01:00
|
|
|
$mimes[] = array_map(
|
|
|
|
[Mime::class, 'fromExtension'],
|
|
|
|
$extensions
|
|
|
|
);
|
2022-08-31 15:02:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$restrictions[] = array_merge(...$mimes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($restrictions !== []) {
|
|
|
|
if (count($restrictions) > 1) {
|
|
|
|
// only return the MIME types that are allowed by all restrictions
|
|
|
|
$mimes = array_intersect(...$restrictions);
|
|
|
|
} else {
|
|
|
|
$mimes = $restrictions[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
// filter out empty MIME types and duplicates
|
|
|
|
return implode(', ', array_filter(array_unique($mimes)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// no restrictions, accept everything
|
|
|
|
return '*';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param mixed $accept
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
protected function normalizeAccept($accept = null): array
|
|
|
|
{
|
2022-12-19 14:56:05 +01:00
|
|
|
$accept = match (true) {
|
|
|
|
is_string($accept) => ['mime' => $accept],
|
2022-08-31 15:02:43 +02:00
|
|
|
// explicitly no restrictions at all
|
2022-12-19 14:56:05 +01:00
|
|
|
$accept === true => ['mime' => null],
|
2022-08-31 15:02:43 +02:00
|
|
|
// no custom restrictions
|
2022-12-19 14:56:05 +01:00
|
|
|
empty($accept) === true => [],
|
|
|
|
// custom restrictions
|
|
|
|
default => $accept
|
|
|
|
};
|
2022-08-31 15:02:43 +02:00
|
|
|
|
|
|
|
$accept = array_change_key_case($accept);
|
|
|
|
|
|
|
|
$defaults = [
|
|
|
|
'extension' => null,
|
|
|
|
'mime' => null,
|
|
|
|
'maxheight' => null,
|
|
|
|
'maxsize' => null,
|
|
|
|
'maxwidth' => null,
|
|
|
|
'minheight' => null,
|
|
|
|
'minsize' => null,
|
|
|
|
'minwidth' => null,
|
|
|
|
'orientation' => null,
|
|
|
|
'type' => null
|
|
|
|
];
|
|
|
|
|
|
|
|
// default type restriction if none are configured;
|
|
|
|
// this ensures that no unexpected files are uploaded
|
|
|
|
if (
|
|
|
|
array_key_exists('mime', $accept) === false &&
|
|
|
|
array_key_exists('extension', $accept) === false &&
|
|
|
|
array_key_exists('type', $accept) === false
|
|
|
|
) {
|
|
|
|
$defaults['type'] = ['image', 'document', 'archive', 'audio', 'video'];
|
|
|
|
$this->defaultTypes = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$accept = array_merge($defaults, $accept);
|
|
|
|
|
|
|
|
// normalize the MIME, extension and type from strings into arrays
|
|
|
|
if (is_string($accept['mime']) === true) {
|
|
|
|
$accept['mime'] = array_map(
|
|
|
|
fn ($mime) => $mime['value'],
|
|
|
|
Str::accepted($accept['mime'])
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_string($accept['extension']) === true) {
|
|
|
|
$accept['extension'] = array_map(
|
|
|
|
'trim',
|
|
|
|
explode(',', $accept['extension'])
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_string($accept['type']) === true) {
|
|
|
|
$accept['type'] = array_map(
|
|
|
|
'trim',
|
|
|
|
explode(',', $accept['type'])
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $accept;
|
|
|
|
}
|
2022-06-17 17:51:59 +02:00
|
|
|
}
|