HEX
Server: Apache
System: Linux server1.ariadata.co 4.18.0-553.120.1.el8_10.x86_64 #1 SMP Mon Apr 20 18:04:27 EDT 2026 x86_64
User: nepi (1051)
PHP: 7.4.33
Disabled: exec,passthru,shell_exec,system,show_source,popen,proc_open
Upload Files
File: /home/nepi/public_html/src/core/Directus/Services/FilesServices.php
<?php

namespace Directus\Services;

use Directus\Application\Container;
use Directus\Database\Schema\SchemaManager;
use Directus\Exception\BadRequestException;
use Directus\Exception\BatchUploadNotAllowedException;
use Zend\Db\Sql\Select;
use function Directus\is_a_url;
use function Directus\validate_file;
use function Directus\get_directus_setting;
use Directus\Util\ArrayUtils;
use Directus\Util\DateTimeUtils;
use Directus\Validator\Exception\InvalidRequestException;
use function Directus\get_random_string;

class FilesServices extends AbstractService
{
    /**
     * @var string
     */
    protected $collection;

    public function __construct(Container $container)
    {
        parent::__construct($container);
        $this->collection = SchemaManager::COLLECTION_FILES;
    }

    public function create(array $data, array $params = [])
    {
        $this->enforceCreatePermissions($this->collection, $data, $params);
        $tableGateway = $this->createTableGateway($this->collection);

        $data['uploaded_by'] = $this->getAcl()->getUserId();
        $data['uploaded_on'] = DateTimeUtils::now()->toString();

        $validationConstraints = $this->createConstraintFor($this->collection);

        // Do not validate filename when uploading files using URL
        // The filename will be generate automatically if not defined
        if (is_a_url(ArrayUtils::get($data, 'data'))) {
            unset($validationConstraints['filename_disk']);
            unset($validationConstraints['filename_download']);
        }

        $this->validate($data, array_merge(['data' => 'required'], $validationConstraints));

        $files = $this->container->get('files');
        $result = $files->getFileSizeType($data['data']);

        if (get_directus_setting('file_mimetype_whitelist') != null) {
            validate_file($result['mimeType'], 'mimeTypes');
        }
        if($result['mimeType'] != 'embed/vimeo' && $result['mimeType'] != 'embed/youtube'){
            validate_file($result['size'], 'maxSize');
        }

        $recordData = $this->getSaveData($data, false);

        $newFile = $tableGateway->createRecord($recordData, $this->getCRUDParams($params));

        return $tableGateway->wrapData(
            \Directus\append_storage_information($newFile->toArray()),
            true,
            ArrayUtils::get($params, 'meta')
        );
    }

    public function getSaveData($data, $isUpdate)
    {
        $dataInfo = [];
        $files = $this->container->get('files');

        if (array_key_exists('data', $data) && is_a_url($data['data'])) {
            $dataInfo = $files->getLink($data['data']);

            // Set the URL payload data
            $data['data'] = ArrayUtils::get($dataInfo, 'data');
            $data['filename_disk'] = ArrayUtils::get($dataInfo, 'filename');
            $data['filename_download'] = ArrayUtils::get($dataInfo, 'filename');
            ArrayUtils::remove($dataInfo, 'filename');
        } else if (array_key_exists('data', $data) && !is_object($data['data'])) {
            $dataInfo = $files->getDataInfo($data['data']);
        }

        $type = ArrayUtils::get($dataInfo, 'type', ArrayUtils::get($data, 'type'));

        if (strpos($type, 'embed/') === 0) {
            $recordData = $files->saveEmbedData(array_merge($dataInfo, ArrayUtils::pick($data, ['filename_disk'])));
        } else {
            $newFileContents = array_key_exists('data', $data) ? $data['data'] : null;
            $recordData = $files->saveData($newFileContents, $data['filename_disk'], $isUpdate);
        }

        // NOTE: Use the user input title, tags, description and location when exists.
        $recordData = ArrayUtils::defaults($recordData, ArrayUtils::pick($data, [
            'title',
            'tags',
            'description',
            'location',
        ]));

        if (!$isUpdate) {
            $recordData['private_hash'] = get_random_string();
        }

        return ArrayUtils::omit(array_merge($data, $recordData), ['data', 'html']);
    }

    protected function findByPrivateHash($hash)
    {
        $result = $this->createTableGateway(SchemaManager::COLLECTION_FILES, false)->fetchAll(function (Select $select) use ($hash) {
            $select->columns(['filename_disk']);
            $select->where(['private_hash' => $hash]);
        })->current()->toArray();
        return $result;
    }

    public function find($id, array $params = [])
    {
        $tableGateway = $this->createTableGateway($this->collection);
        $params['id'] = $id;

        return $this->getItemsAndSetResponseCacheTags($tableGateway, $params);
    }

    public function findByIds($id, array $params = [])
    {
        $tableGateway = $this->createTableGateway($this->collection);

        return $this->getItemsByIdsAndSetResponseCacheTags($tableGateway, $id, $params);
    }

    public function update($id, array $data, array $params = [])
    {
        $this->enforceUpdatePermissions($this->collection, $data, $params);

        $this->checkItemExists($this->collection, $id);
        $this->validatePayload($this->collection, array_keys($data), $data, $params);

        $files = $this->container->get('files');

        if (isset($data['data'])) {
            $result = $files->getFileSizeType($data['data']);

            if (get_directus_setting('file_mimetype_whitelist') != null) {
                validate_file($result['mimeType'], 'mimeTypes');
            }
            if($result['mimeType'] != 'embed/vimeo' && $result['mimeType'] != 'embed/youtube'){
              validate_file($result['size'], 'maxSize');
            }
        }

        $tableGateway = $this->createTableGateway($this->collection);

        $currentItem = $tableGateway->getOneData($id);
        $currentFileName = ArrayUtils::get($currentItem, 'filename_disk');        

        if (array_key_exists('filename_disk', $data) && $data['filename_disk'] !== $currentFileName) {
            $oldFilePath = $currentFileName;
            $newFilePath = $data['filename_disk'];

            try {
                $this->container->get('filesystem')->getAdapter()->rename($oldFilePath, $newFilePath);
            } catch (\Exception $e) {
                throw new InvalidRequestException($e);
            }
        }

        // getSaveData requires filename_disk to be set, in order for Filesystem/Files to read the file info
        $fileName = $currentFileName;

        // If the user provided their own filename, the file has been renamed above. In that case, pass on
        // the new filename
        if (array_key_exists('filename_disk', $data)) {
            $fileName = $data['filename_disk'];
        }

        $data['filename_disk'] = $fileName;

        $recordData = $this->getSaveData($data, true);

        $newFile = $tableGateway->updateRecord($id, $recordData, $this->getCRUDParams($params));

        $thumb = $this->container->get('files_thumb');
        $thumb->deleteThumb($currentItem);

        return $tableGateway->wrapData(
            \Directus\append_storage_information([$newFile->toArray()]),
            true,
            ArrayUtils::get($params, 'meta')
        );
    }

    public function delete($id, array $params = [])
    {
        $this->enforcePermissions($this->collection, [], $params);
        $tableGateway = $this->createTableGateway($this->collection);
        $file = $tableGateway->getOneData($id);

        // Force delete files
        // TODO: Make the hook listen to deletes and catch ALL ids (from conditions)
        // and deletes every matched files
        /** @var \Directus\Filesystem\Files $files */
        $files = $this->container->get('files');
        $files->delete($file);

        //Force delete the thumbnails
        $thumb = $this->container->get('files_thumb');
        $thumb->deleteThumb($file);

        // Delete file record
        return $tableGateway->deleteRecord($id, $this->getCRUDParams($params));
    }

    public function findAll(array $params = [])
    {
        $tableGateway = $this->createTableGateway($this->collection);

        return $this->getItemsAndSetResponseCacheTags($tableGateway, $params);
    }

    /**
     * @param array $items
     * @param array $params
     *
     * @return array
     *
     * @throws InvalidRequestException
     */
    public function batchUpdate(array $items, array $params = [])
    {
        if (!isset($items[0]) || !is_array($items[0])) {
            throw new InvalidRequestException('batch update expect an array of items');
        }

        $collection = $this->collection;
        foreach ($items as $data) {
            $this->enforceCreatePermissions($collection, $data, $params);
            $this->validatePayload($collection, array_keys($data), $data, $params);
            $this->validatePayloadHasPrimaryKey($collection, $data);
            $this->validateEmptyData($data);
        }

        $collectionObject = $this->getSchemaManager()->getCollection($collection);
        $allItems = [];
        foreach ($items as $data) {
            $id = $data[$collectionObject->getPrimaryKeyName()];
            $item = $this->update($id, $data, $params);

            if (!is_null($item)) {
                $allItems[] = $item['data'];
            }
        }

        if (!empty($allItems)) {
            $allItems = ['data' => $allItems];
        }

        return $allItems;
    }

    /**
     * @param array $ids
     * @param array $payload
     * @param array $params
     *
     * @return array
     */
    public function batchUpdateWithIds(array $ids, array $payload, array $params = [])
    {
        $this->enforceUpdatePermissions($this->collection, $payload, $params);
        $this->validatePayload($this->collection, array_keys($payload), $payload, $params);
        $this->validateEmptyData($payload);

        $allItems = [];
        foreach ($ids as $id) {
            $item = $this->update($id, $payload, $params);
            if (!empty($item)) {
                $allItems[] = $item['data'];
            }
        }

        if (!empty($allItems)) {
            $allItems = ['data' => $allItems];
        }

        return $allItems;
    }

    /**
     * @param array $ids
     * @param array $params
     */
    public function batchDeleteWithIds(array $ids, array $params = [])
    {
        foreach ($ids as $id) {
            $this->delete($id, $params);
        }
    }

    /**
     * Throws exception if data property exists
     *
     * @param array $payload
     *
     * @throws BadRequestException
     */
    protected function validateEmptyData(array $payload)
    {
        if (ArrayUtils::has($payload, 'data')) {
            throw new BatchUploadNotAllowedException();
        }
    }
}