| @@ -106,4 +106,7 @@ | |||||
| - File Uploading: | - File Uploading: | ||||
| https://api-platform.com/docs/core/file-upload/ | https://api-platform.com/docs/core/file-upload/ | ||||
| https://github.com/dustin10/VichUploaderBundle/blob/master/docs/index.md | https://github.com/dustin10/VichUploaderBundle/blob/master/docs/index.md | ||||
| >>>>>>> Stashed changes | |||||
| # List Routes and Service Tags | |||||
| - ddev exec bin/console debug:router | |||||
| - ddev exec bin/console debug:container | |||||
| @@ -43,7 +43,8 @@ | |||||
| "symfony/yaml": "7.0.*", | "symfony/yaml": "7.0.*", | ||||
| "symfonycasts/micro-mapper": "^0.1.4", | "symfonycasts/micro-mapper": "^0.1.4", | ||||
| "twig/extra-bundle": "^2.12|^3.0", | "twig/extra-bundle": "^2.12|^3.0", | ||||
| "twig/twig": "^2.12|^3.0" | |||||
| "twig/twig": "^2.12|^3.0", | |||||
| "vich/uploader-bundle": "^2.3" | |||||
| }, | }, | ||||
| "config": { | "config": { | ||||
| "allow-plugins": { | "allow-plugins": { | ||||
| @@ -90,7 +91,7 @@ | |||||
| }, | }, | ||||
| "extra": { | "extra": { | ||||
| "symfony": { | "symfony": { | ||||
| "allow-contrib": false, | |||||
| "allow-contrib": true, | |||||
| "require": "7.0.*" | "require": "7.0.*" | ||||
| } | } | ||||
| }, | }, | ||||
| @@ -4,7 +4,7 @@ | |||||
| "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", | ||||
| "This file is @generated automatically" | "This file is @generated automatically" | ||||
| ], | ], | ||||
| "content-hash": "90d8363fb071963db3a216753262c590", | |||||
| "content-hash": "e41f83bf907500bc2cae3d1f8eb56816", | |||||
| "packages": [ | "packages": [ | ||||
| { | { | ||||
| "name": "api-platform/core", | "name": "api-platform/core", | ||||
| @@ -1561,6 +1561,70 @@ | |||||
| ], | ], | ||||
| "time": "2023-10-06T06:47:41+00:00" | "time": "2023-10-06T06:47:41+00:00" | ||||
| }, | }, | ||||
| { | |||||
| "name": "jms/metadata", | |||||
| "version": "2.8.0", | |||||
| "source": { | |||||
| "type": "git", | |||||
| "url": "https://github.com/schmittjoh/metadata.git", | |||||
| "reference": "7ca240dcac0c655eb15933ee55736ccd2ea0d7a6" | |||||
| }, | |||||
| "dist": { | |||||
| "type": "zip", | |||||
| "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/7ca240dcac0c655eb15933ee55736ccd2ea0d7a6", | |||||
| "reference": "7ca240dcac0c655eb15933ee55736ccd2ea0d7a6", | |||||
| "shasum": "" | |||||
| }, | |||||
| "require": { | |||||
| "php": "^7.2|^8.0" | |||||
| }, | |||||
| "require-dev": { | |||||
| "doctrine/cache": "^1.0", | |||||
| "doctrine/coding-standard": "^8.0", | |||||
| "mikey179/vfsstream": "^1.6.7", | |||||
| "phpunit/phpunit": "^8.5|^9.0", | |||||
| "psr/container": "^1.0|^2.0", | |||||
| "symfony/cache": "^3.1|^4.0|^5.0", | |||||
| "symfony/dependency-injection": "^3.1|^4.0|^5.0" | |||||
| }, | |||||
| "type": "library", | |||||
| "extra": { | |||||
| "branch-alias": { | |||||
| "dev-master": "2.x-dev" | |||||
| } | |||||
| }, | |||||
| "autoload": { | |||||
| "psr-4": { | |||||
| "Metadata\\": "src/" | |||||
| } | |||||
| }, | |||||
| "notification-url": "https://packagist.org/downloads/", | |||||
| "license": [ | |||||
| "MIT" | |||||
| ], | |||||
| "authors": [ | |||||
| { | |||||
| "name": "Johannes M. Schmitt", | |||||
| "email": "schmittjoh@gmail.com" | |||||
| }, | |||||
| { | |||||
| "name": "Asmir Mustafic", | |||||
| "email": "goetas@gmail.com" | |||||
| } | |||||
| ], | |||||
| "description": "Class/method/property metadata management in PHP", | |||||
| "keywords": [ | |||||
| "annotations", | |||||
| "metadata", | |||||
| "xml", | |||||
| "yaml" | |||||
| ], | |||||
| "support": { | |||||
| "issues": "https://github.com/schmittjoh/metadata/issues", | |||||
| "source": "https://github.com/schmittjoh/metadata/tree/2.8.0" | |||||
| }, | |||||
| "time": "2023-02-15T13:44:18+00:00" | |||||
| }, | |||||
| { | { | ||||
| "name": "lcobucci/clock", | "name": "lcobucci/clock", | ||||
| "version": "3.2.0", | "version": "3.2.0", | ||||
| @@ -7832,6 +7896,115 @@ | |||||
| ], | ], | ||||
| "time": "2023-11-21T18:54:41+00:00" | "time": "2023-11-21T18:54:41+00:00" | ||||
| }, | }, | ||||
| { | |||||
| "name": "vich/uploader-bundle", | |||||
| "version": "2.3.0", | |||||
| "source": { | |||||
| "type": "git", | |||||
| "url": "https://github.com/dustin10/VichUploaderBundle.git", | |||||
| "reference": "0d748cfe676bcf894b3f324f69797c9b9d124aa5" | |||||
| }, | |||||
| "dist": { | |||||
| "type": "zip", | |||||
| "url": "https://api.github.com/repos/dustin10/VichUploaderBundle/zipball/0d748cfe676bcf894b3f324f69797c9b9d124aa5", | |||||
| "reference": "0d748cfe676bcf894b3f324f69797c9b9d124aa5", | |||||
| "shasum": "" | |||||
| }, | |||||
| "require": { | |||||
| "doctrine/persistence": "^3", | |||||
| "ext-simplexml": "*", | |||||
| "jms/metadata": "^2.4", | |||||
| "php": "^8.1", | |||||
| "symfony/config": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/console": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/event-dispatcher-contracts": "^3.1", | |||||
| "symfony/http-foundation": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/mime": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/property-access": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/string": "^5.4 || ^6.0 || ^7.0" | |||||
| }, | |||||
| "conflict": { | |||||
| "doctrine/annotations": "<1.12", | |||||
| "league/flysystem": "<2.0" | |||||
| }, | |||||
| "require-dev": { | |||||
| "dg/bypass-finals": "^1.3", | |||||
| "doctrine/doctrine-bundle": "^2.7", | |||||
| "doctrine/mongodb-odm": "^2.4", | |||||
| "doctrine/orm": "^2.13", | |||||
| "ext-sqlite3": "*", | |||||
| "knplabs/knp-gaufrette-bundle": "dev-master", | |||||
| "league/flysystem-bundle": "^2.3", | |||||
| "league/flysystem-memory": "^2.0", | |||||
| "matthiasnoback/symfony-dependency-injection-test": "^4.3", | |||||
| "mikey179/vfsstream": "^1.6.11", | |||||
| "phpunit/phpunit": "^9.6", | |||||
| "symfony/asset": "^5.4 || ^6.0", | |||||
| "symfony/browser-kit": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/css-selector": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/doctrine-bridge": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/dom-crawler": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/form": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/phpunit-bridge": "^7.0", | |||||
| "symfony/security-csrf": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/translation": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/twig-bridge": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/twig-bundle": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/validator": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/var-dumper": "^5.4 || ^6.0 || ^7.0", | |||||
| "symfony/yaml": "^5.4 || ^6.0 || ^7.0", | |||||
| "yoast/phpunit-polyfills": "^2.0" | |||||
| }, | |||||
| "suggest": { | |||||
| "doctrine/annotations": "If you use doctrine/doctrine-bundle >2.7, this package is required to use annotations", | |||||
| "doctrine/doctrine-bundle": "For integration with Doctrine", | |||||
| "doctrine/mongodb-odm-bundle": "For integration with Doctrine ODM", | |||||
| "doctrine/orm": "For integration with Doctrine ORM", | |||||
| "doctrine/phpcr-odm": "For integration with Doctrine PHPCR", | |||||
| "knplabs/knp-gaufrette-bundle": "For integration with Gaufrette", | |||||
| "league/flysystem-bundle": "For integration with Flysystem", | |||||
| "liip/imagine-bundle": "To generate image thumbnails", | |||||
| "oneup/flysystem-bundle": "For integration with Flysystem", | |||||
| "symfony/asset": "To generate better links", | |||||
| "symfony/form": "To handle uploads in forms", | |||||
| "symfony/yaml": "To use YAML mapping" | |||||
| }, | |||||
| "type": "symfony-bundle", | |||||
| "extra": { | |||||
| "branch-alias": { | |||||
| "dev-master": "2.x-dev" | |||||
| } | |||||
| }, | |||||
| "autoload": { | |||||
| "psr-4": { | |||||
| "Vich\\UploaderBundle\\": "src" | |||||
| } | |||||
| }, | |||||
| "notification-url": "https://packagist.org/downloads/", | |||||
| "license": [ | |||||
| "MIT" | |||||
| ], | |||||
| "authors": [ | |||||
| { | |||||
| "name": "Dustin Dobervich", | |||||
| "email": "ddobervich@gmail.com" | |||||
| } | |||||
| ], | |||||
| "description": "Ease file uploads attached to entities", | |||||
| "homepage": "https://github.com/dustin10/VichUploaderBundle", | |||||
| "keywords": [ | |||||
| "file uploads", | |||||
| "upload" | |||||
| ], | |||||
| "support": { | |||||
| "issues": "https://github.com/dustin10/VichUploaderBundle/issues", | |||||
| "source": "https://github.com/dustin10/VichUploaderBundle/tree/2.3.0" | |||||
| }, | |||||
| "time": "2023-12-18T07:49:22+00:00" | |||||
| }, | |||||
| { | { | ||||
| "name": "webmozart/assert", | "name": "webmozart/assert", | ||||
| "version": "1.11.0", | "version": "1.11.0", | ||||
| @@ -17,4 +17,5 @@ return [ | |||||
| Zenstruck\Foundry\ZenstruckFoundryBundle::class => ['dev' => true, 'test' => true], | Zenstruck\Foundry\ZenstruckFoundryBundle::class => ['dev' => true, 'test' => true], | ||||
| Symfonycasts\MicroMapper\SymfonycastsMicroMapperBundle::class => ['all' => true], | Symfonycasts\MicroMapper\SymfonycastsMicroMapperBundle::class => ['all' => true], | ||||
| Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], | Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], | ||||
| Vich\UploaderBundle\VichUploaderBundle::class => ['all' => true], | |||||
| ]; | ]; | ||||
| @@ -0,0 +1,15 @@ | |||||
| vich_uploader: | |||||
| db_driver: orm | |||||
| metadata: | |||||
| type: attribute | |||||
| mappings: | |||||
| media_object: | |||||
| uri_prefix: /media | |||||
| upload_destination: '%kernel.project_dir%/public/media' | |||||
| # Will rename uploaded files using a uniqueid as a prefix. | |||||
| namer: Vich\UploaderBundle\Naming\SmartUniqueNamer | |||||
| download: | |||||
| uri_prefix: /download | |||||
| upload_destination: '%kernel.project_dir%/public/download' | |||||
| # Will rename uploaded files using a uniqueid as a prefix. | |||||
| namer: Vich\UploaderBundle\Naming\SmartUniqueNamer | |||||
| @@ -27,4 +27,10 @@ services: | |||||
| class: App\EventListener\AuthenticationSuccessListener | class: App\EventListener\AuthenticationSuccessListener | ||||
| tags: | tags: | ||||
| - { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_success, method: onAuthenticationSuccessResponse } | - { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_success, method: onAuthenticationSuccessResponse } | ||||
| arguments: ['@symfonycasts.micro_mapper', '@api_platform.iri_converter'] | |||||
| arguments: ['@symfonycasts.micro_mapper', '@api_platform.iri_converter'] | |||||
| App\Serializer\MediaObjectNormalizer: | |||||
| arguments: | |||||
| $storage: '@vich_uploader.storage.file_system' | |||||
| tags: | |||||
| - { name: 'api_platform.jsonld.normalizer.item', priority: 1000 } | |||||
| @@ -0,0 +1,35 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace DoctrineMigrations; | |||||
| use Doctrine\DBAL\Schema\Schema; | |||||
| use Doctrine\Migrations\AbstractMigration; | |||||
| /** | |||||
| * Auto-generated Migration: Please modify to your needs! | |||||
| */ | |||||
| final class Version20240125102320 extends AbstractMigration | |||||
| { | |||||
| public function getDescription(): string | |||||
| { | |||||
| return ''; | |||||
| } | |||||
| public function up(Schema $schema): void | |||||
| { | |||||
| // this up() migration is auto-generated, please modify it to your needs | |||||
| $this->addSql('CREATE TABLE media_object (id INT AUTO_INCREMENT NOT NULL, file_path VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); | |||||
| $this->addSql('ALTER TABLE api_token DROP FOREIGN KEY FK_7BA2F5EB5E70BCD7'); | |||||
| $this->addSql('DROP TABLE api_token'); | |||||
| } | |||||
| public function down(Schema $schema): void | |||||
| { | |||||
| // this down() migration is auto-generated, please modify it to your needs | |||||
| $this->addSql('CREATE TABLE api_token (id INT AUTO_INCREMENT NOT NULL, owned_by_id INT NOT NULL, expires_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', token VARCHAR(68) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, scopes JSON NOT NULL COMMENT \'(DC2Type:json)\', INDEX IDX_7BA2F5EB5E70BCD7 (owned_by_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB COMMENT = \'\' '); | |||||
| $this->addSql('ALTER TABLE api_token ADD CONSTRAINT FK_7BA2F5EB5E70BCD7 FOREIGN KEY (owned_by_id) REFERENCES user (id)'); | |||||
| $this->addSql('DROP TABLE media_object'); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,33 @@ | |||||
| <?php | |||||
| /** | |||||
| * @author Daniel Knudsen <d.knudsen@spawntree.de> | |||||
| * @date 25.01.24 | |||||
| */ | |||||
| namespace App\Controller; | |||||
| use App\Entity\MediaObject; | |||||
| use App\Serializer\MediaObjectNormalizer; | |||||
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | |||||
| use Symfony\Component\HttpFoundation\Request; | |||||
| use Symfony\Component\HttpKernel\Attribute\AsController; | |||||
| use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; | |||||
| #[AsController] | |||||
| final class CreateMediaObjectAction extends AbstractController | |||||
| { | |||||
| public function __invoke(Request $request): MediaObject | |||||
| { | |||||
| $uploadedFile = $request->files->get('file'); | |||||
| if (!$uploadedFile) { | |||||
| throw new BadRequestHttpException('"file" is required'); | |||||
| } | |||||
| $mediaObject = new MediaObject(); | |||||
| $mediaObject->file = $uploadedFile; | |||||
| return $mediaObject; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,75 @@ | |||||
| <?php | |||||
| /** | |||||
| * @author Daniel Knudsen <d.knudsen@spawntree.de> | |||||
| * @date 25.01.24 | |||||
| */ | |||||
| namespace App\Entity; | |||||
| use ApiPlatform\Metadata\ApiProperty; | |||||
| use ApiPlatform\Metadata\ApiResource; | |||||
| use ApiPlatform\Metadata\Get; | |||||
| use ApiPlatform\Metadata\GetCollection; | |||||
| use ApiPlatform\Metadata\Post; | |||||
| use ApiPlatform\OpenApi\Model; | |||||
| use App\Controller\CreateMediaObjectAction; | |||||
| use Doctrine\ORM\Mapping as ORM; | |||||
| use Symfony\Component\HttpFoundation\File\File; | |||||
| use Symfony\Component\Serializer\Annotation\Groups; | |||||
| use Symfony\Component\Validator\Constraints as Assert; | |||||
| use Vich\UploaderBundle\Mapping\Annotation as Vich; | |||||
| #[Vich\Uploadable] | |||||
| #[ORM\Entity] | |||||
| #[ApiResource( | |||||
| types: ['https://schema.org/MediaObject'], | |||||
| operations: [ | |||||
| new Get(), | |||||
| new GetCollection(), | |||||
| new Post( | |||||
| controller: CreateMediaObjectAction::class, | |||||
| openapi: new Model\Operation( | |||||
| requestBody: new Model\RequestBody( | |||||
| content: new \ArrayObject([ | |||||
| 'multipart/form-data' => [ | |||||
| 'schema' => [ | |||||
| 'type' => 'object', | |||||
| 'properties' => [ | |||||
| 'file' => [ | |||||
| 'type' => 'string', | |||||
| 'format' => 'binary' | |||||
| ] | |||||
| ] | |||||
| ] | |||||
| ] | |||||
| ]) | |||||
| ) | |||||
| ), | |||||
| validationContext: ['groups' => ['Default', 'media_object_create']], | |||||
| deserialize: false | |||||
| ) | |||||
| ], | |||||
| normalizationContext: ['groups' => ['media_object:read']] | |||||
| )] | |||||
| class MediaObject | |||||
| { | |||||
| #[ORM\Id, ORM\Column, ORM\GeneratedValue] | |||||
| private ?int $id = null; | |||||
| #[ApiProperty(types: ['https://schema.org/contentUrl'])] | |||||
| #[Groups(['media_object:read'])] | |||||
| public ?string $contentUrl = null; | |||||
| #[Vich\UploadableField(mapping: 'media_object', fileNameProperty: 'filePath')] | |||||
| #[Assert\NotNull(groups: ['media_object_create'])] | |||||
| public ?File $file = null; | |||||
| #[ORM\Column(nullable: true)] | |||||
| public ?string $filePath = null; | |||||
| public function getId(): ?int | |||||
| { | |||||
| return $this->id; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,63 @@ | |||||
| <?php | |||||
| /** | |||||
| * @author Daniel Knudsen <d.knudsen@spawntree.de> | |||||
| * @date 25.01.24 | |||||
| */ | |||||
| namespace App\Serializer; | |||||
| use App\Entity\MediaObject; | |||||
| use Symfony\Component\DependencyInjection\Attribute\AsDecorator; | |||||
| use Symfony\Component\Serializer\Normalizer\NormalizerInterface; | |||||
| use Symfony\Component\Serializer\SerializerAwareInterface; | |||||
| use Symfony\Component\Serializer\SerializerInterface; | |||||
| use Vich\UploaderBundle\Storage\StorageInterface; | |||||
| #[AsDecorator('api_platform.jsonld.normalizer.item')] | |||||
| final class MediaObjectNormalizer implements NormalizerInterface, SerializerAwareInterface | |||||
| { | |||||
| private const ALREADY_CALLED = 'MEDIA_OBJECT_NORMALIZER_ALREADY_CALLED'; | |||||
| public function __construct( | |||||
| private StorageInterface $storage, | |||||
| private NormalizerInterface $normalizer | |||||
| ) | |||||
| { | |||||
| } | |||||
| public function normalize($object, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null | |||||
| { | |||||
| $context[self::ALREADY_CALLED] = true; | |||||
| $object->contentUrl = $this->storage->resolveUri($object, 'file'); | |||||
| return $this->normalizer->normalize($object, $format, $context); | |||||
| } | |||||
| public function supportsNormalization($data, ?string $format = null, array $context = []): bool | |||||
| { | |||||
| if (isset($context[self::ALREADY_CALLED])) { | |||||
| return false; | |||||
| } | |||||
| return $data instanceof MediaObject; | |||||
| } | |||||
| public function setSerializer(SerializerInterface $serializer): void | |||||
| { | |||||
| if ($this->normalizer instanceof SerializerAwareInterface) { | |||||
| $this->normalizer->setSerializer($serializer); | |||||
| } | |||||
| } | |||||
| public function getSupportedTypes(?string $format): array | |||||
| { | |||||
| if (method_exists($this->normalizer, 'getSupportedTypes')) { | |||||
| return $this->normalizer->getSupportedTypes($format); | |||||
| } | |||||
| return 'jsonld' === $format ? ['*' => true] : []; | |||||
| } | |||||
| } | |||||
| @@ -300,6 +300,18 @@ | |||||
| "twig/extra-bundle": { | "twig/extra-bundle": { | ||||
| "version": "v3.8.0" | "version": "v3.8.0" | ||||
| }, | }, | ||||
| "vich/uploader-bundle": { | |||||
| "version": "2.3", | |||||
| "recipe": { | |||||
| "repo": "github.com/symfony/recipes-contrib", | |||||
| "branch": "main", | |||||
| "version": "1.13", | |||||
| "ref": "1b3064c2f6b255c2bc2f56461aaeb76b11e07e36" | |||||
| }, | |||||
| "files": [ | |||||
| "config/packages/vich_uploader.yaml" | |||||
| ] | |||||
| }, | |||||
| "zenstruck/foundry": { | "zenstruck/foundry": { | ||||
| "version": "1.36", | "version": "1.36", | ||||
| "recipe": { | "recipe": { | ||||