From 6ba1cff1149bb4207246566394f63015fc6f793b Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Thu, 28 Apr 2022 13:55:25 +0300 Subject: [PATCH 01/18] Added classes for defining relationships --- composer.json | 4 +- composer.lock | 110 ++++---- .../Media/Interfaces/MediaObjectInterface.php | 7 + .../Contracts/Media/MediaObjectInterface.php | 52 ++++ .../Contracts/Object/IdentifierInterface.php | 27 ++ .../RelationshipIdentifierInterface.php | 28 ++ .../Relationships/RelationshipInterface.php | 80 ++++++ .../Relationships/RelationshipsInterface.php | 48 ++++ .../ToManyRelationshipInterface.php | 49 ++++ .../ToOneRelationshipInterface.php | 37 +++ .../Grav/Framework/Flex/FlexIdentifier.php | 72 ++++++ .../Flex/Traits/FlexRelationshipsTrait.php | 43 +++ .../Grav/Framework/Media/MediaIdentifier.php | 135 ++++++++++ .../src/Grav/Framework/Media/MediaObject.php | 210 +++++++++++++++ .../Framework/Media/UploadedMediaObject.php | 158 ++++++++++++ .../Object/Identifiers/Identifier.php | 64 +++++ .../Framework/Relationships/Relationships.php | 211 +++++++++++++++ .../Relationships/ToManyRelationship.php | 244 ++++++++++++++++++ .../Relationships/ToOneRelationship.php | 205 +++++++++++++++ .../Traits/RelationshipTrait.php | 122 +++++++++ 20 files changed, 1851 insertions(+), 55 deletions(-) create mode 100644 system/src/Grav/Framework/Contracts/Media/MediaObjectInterface.php create mode 100644 system/src/Grav/Framework/Contracts/Object/IdentifierInterface.php create mode 100644 system/src/Grav/Framework/Contracts/Relationships/RelationshipIdentifierInterface.php create mode 100644 system/src/Grav/Framework/Contracts/Relationships/RelationshipInterface.php create mode 100644 system/src/Grav/Framework/Contracts/Relationships/RelationshipsInterface.php create mode 100644 system/src/Grav/Framework/Contracts/Relationships/ToManyRelationshipInterface.php create mode 100644 system/src/Grav/Framework/Contracts/Relationships/ToOneRelationshipInterface.php create mode 100644 system/src/Grav/Framework/Flex/FlexIdentifier.php create mode 100644 system/src/Grav/Framework/Flex/Traits/FlexRelationshipsTrait.php create mode 100644 system/src/Grav/Framework/Media/MediaIdentifier.php create mode 100644 system/src/Grav/Framework/Media/MediaObject.php create mode 100644 system/src/Grav/Framework/Media/UploadedMediaObject.php create mode 100644 system/src/Grav/Framework/Object/Identifiers/Identifier.php create mode 100644 system/src/Grav/Framework/Relationships/Relationships.php create mode 100644 system/src/Grav/Framework/Relationships/ToManyRelationship.php create mode 100644 system/src/Grav/Framework/Relationships/ToOneRelationship.php create mode 100644 system/src/Grav/Framework/Relationships/Traits/RelationshipTrait.php diff --git a/composer.json b/composer.json index 5855970e53..8cc4ff00c6 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "homepage": "https://getgrav.org", "license": "MIT", "require": { - "php": "^7.3.6 || ^8.0", + "php": "^7.4.1 || ^8.0", "ext-json": "*", "ext-openssl": "*", "ext-curl": "*", @@ -89,7 +89,7 @@ "config": { "apcu-autoloader": true, "platform": { - "php": "7.3.6" + "php": "7.4.1" } }, "autoload": { diff --git a/composer.lock b/composer.lock index 64b97f08d6..803d3dc3ae 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f0530b0fd3e574fef0852376653da5a0", + "content-hash": "7645535ce7348e38bad6dad9ce8b2fdb", "packages": [ { "name": "composer/ca-bundle", @@ -1655,20 +1655,20 @@ }, { "name": "psr/container", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", "shasum": "" }, "require": { - "php": ">=7.2.0" + "php": ">=7.4.0" }, "type": "library", "autoload": { @@ -1697,9 +1697,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.1" + "source": "https://github.com/php-fig/container/tree/1.1.2" }, - "time": "2021-03-05T17:36:06+00:00" + "time": "2021-11-05T16:50:12+00:00" }, { "name": "psr/http-factory", @@ -2224,16 +2224,16 @@ }, { "name": "symfony/console", - "version": "v4.4.40", + "version": "v4.4.41", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "bdcc66f3140421038f495e5b50e3ca6ffa14c773" + "reference": "0e1e62083b20ccb39c2431293de060f756af905c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/bdcc66f3140421038f495e5b50e3ca6ffa14c773", - "reference": "bdcc66f3140421038f495e5b50e3ca6ffa14c773", + "url": "https://api.github.com/repos/symfony/console/zipball/0e1e62083b20ccb39c2431293de060f756af905c", + "reference": "0e1e62083b20ccb39c2431293de060f756af905c", "shasum": "" }, "require": { @@ -2294,7 +2294,7 @@ "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/console/tree/v4.4.40" + "source": "https://github.com/symfony/console/tree/v4.4.41" }, "funding": [ { @@ -2310,7 +2310,7 @@ "type": "tidelift" } ], - "time": "2022-03-26T22:12:04+00:00" + "time": "2022-04-12T15:19:55+00:00" }, { "name": "symfony/contracts", @@ -2492,16 +2492,16 @@ }, { "name": "symfony/http-client", - "version": "v4.4.40", + "version": "v4.4.41", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "c66fc3b60900359ea10a7b22921c797446783bb3" + "reference": "bad7c3296590c5a69a9ed89e8a51f13c07c34b54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/c66fc3b60900359ea10a7b22921c797446783bb3", - "reference": "c66fc3b60900359ea10a7b22921c797446783bb3", + "url": "https://api.github.com/repos/symfony/http-client/zipball/bad7c3296590c5a69a9ed89e8a51f13c07c34b54", + "reference": "bad7c3296590c5a69a9ed89e8a51f13c07c34b54", "shasum": "" }, "require": { @@ -2553,7 +2553,7 @@ "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-client/tree/v4.4.40" + "source": "https://github.com/symfony/http-client/tree/v4.4.41" }, "funding": [ { @@ -2569,7 +2569,7 @@ "type": "tidelift" } ], - "time": "2022-04-01T12:25:39+00:00" + "time": "2022-04-12T15:19:55+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3063,16 +3063,16 @@ }, { "name": "symfony/process", - "version": "v4.4.40", + "version": "v4.4.41", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "54e9d763759268e07eb13b921d8631fc2816206f" + "reference": "9eedd60225506d56e42210a70c21bb80ca8456ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/54e9d763759268e07eb13b921d8631fc2816206f", - "reference": "54e9d763759268e07eb13b921d8631fc2816206f", + "url": "https://api.github.com/repos/symfony/process/zipball/9eedd60225506d56e42210a70c21bb80ca8456ce", + "reference": "9eedd60225506d56e42210a70c21bb80ca8456ce", "shasum": "" }, "require": { @@ -3105,7 +3105,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v4.4.40" + "source": "https://github.com/symfony/process/tree/v4.4.41" }, "funding": [ { @@ -3121,20 +3121,20 @@ "type": "tidelift" } ], - "time": "2022-03-18T16:18:39+00:00" + "time": "2022-04-04T10:19:07+00:00" }, { "name": "symfony/var-dumper", - "version": "v4.4.39", + "version": "v4.4.41", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "35237c5e5dcb6593a46a860ba5b29c1d4683d80e" + "reference": "58eb36075c04aaf92a7a9f38ee9a8b97e24eb481" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/35237c5e5dcb6593a46a860ba5b29c1d4683d80e", - "reference": "35237c5e5dcb6593a46a860ba5b29c1d4683d80e", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/58eb36075c04aaf92a7a9f38ee9a8b97e24eb481", + "reference": "58eb36075c04aaf92a7a9f38ee9a8b97e24eb481", "shasum": "" }, "require": { @@ -3194,7 +3194,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v4.4.39" + "source": "https://github.com/symfony/var-dumper/tree/v4.4.41" }, "funding": [ { @@ -3210,7 +3210,7 @@ "type": "tidelift" } ], - "time": "2022-02-25T10:38:15+00:00" + "time": "2022-04-25T21:15:06+00:00" }, { "name": "symfony/yaml", @@ -3862,20 +3862,24 @@ }, { "name": "codeception/stub", - "version": "3.7.0", + "version": "4.0.2", "source": { "type": "git", "url": "https://github.com/Codeception/Stub.git", - "reference": "468dd5fe659f131fc997f5196aad87512f9b1304" + "reference": "18a148dacd293fc7b044042f5aa63a82b08bff5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/468dd5fe659f131fc997f5196aad87512f9b1304", - "reference": "468dd5fe659f131fc997f5196aad87512f9b1304", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/18a148dacd293fc7b044042f5aa63a82b08bff5d", + "reference": "18a148dacd293fc7b044042f5aa63a82b08bff5d", "shasum": "" }, "require": { - "phpunit/phpunit": "^8.4 | ^9.0" + "php": "^7.4 | ^8.0", + "phpunit/phpunit": "^8.4 | ^9.0 | ^10.0 | 10.0.x-dev" + }, + "require-dev": { + "consolidation/robo": "^3.0" }, "type": "library", "autoload": { @@ -3890,9 +3894,9 @@ "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", "support": { "issues": "https://github.com/Codeception/Stub/issues", - "source": "https://github.com/Codeception/Stub/tree/3.7.0" + "source": "https://github.com/Codeception/Stub/tree/4.0.2" }, - "time": "2020-07-03T15:54:43+00:00" + "time": "2022-01-31T19:25:15+00:00" }, { "name": "doctrine/instantiator", @@ -4679,16 +4683,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.6.0", + "version": "1.6.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b480ba2ae0699a72d43a340c20b9c00ede91ee3e" + "reference": "becb9603a31d70f5007d505877a7b812598dfe46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b480ba2ae0699a72d43a340c20b9c00ede91ee3e", - "reference": "b480ba2ae0699a72d43a340c20b9c00ede91ee3e", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/becb9603a31d70f5007d505877a7b812598dfe46", + "reference": "becb9603a31d70f5007d505877a7b812598dfe46", "shasum": "" }, "require": { @@ -4714,7 +4718,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.6.0" + "source": "https://github.com/phpstan/phpstan/tree/1.6.2" }, "funding": [ { @@ -4734,7 +4738,7 @@ "type": "tidelift" } ], - "time": "2022-04-26T05:43:03+00:00" + "time": "2022-04-27T11:05:24+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -6505,16 +6509,16 @@ }, { "name": "symfony/finder", - "version": "v5.4.3", + "version": "v5.4.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "231313534dded84c7ecaa79d14bc5da4ccb69b7d" + "reference": "9b630f3427f3ebe7cd346c277a1408b00249dad9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/231313534dded84c7ecaa79d14bc5da4ccb69b7d", - "reference": "231313534dded84c7ecaa79d14bc5da4ccb69b7d", + "url": "https://api.github.com/repos/symfony/finder/zipball/9b630f3427f3ebe7cd346c277a1408b00249dad9", + "reference": "9b630f3427f3ebe7cd346c277a1408b00249dad9", "shasum": "" }, "require": { @@ -6548,7 +6552,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.3" + "source": "https://github.com/symfony/finder/tree/v5.4.8" }, "funding": [ { @@ -6564,7 +6568,7 @@ "type": "tidelift" } ], - "time": "2022-01-26T16:34:36+00:00" + "time": "2022-04-15T08:07:45+00:00" }, { "name": "theseer/tokenizer", @@ -6681,7 +6685,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^7.3.6 || ^8.0", + "php": "^7.4.1 || ^8.0", "ext-json": "*", "ext-openssl": "*", "ext-curl": "*", @@ -6691,7 +6695,7 @@ }, "platform-dev": [], "platform-overrides": { - "php": "7.3.6" + "php": "7.4.1" }, "plugin-api-version": "2.2.0" } diff --git a/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php b/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php index 9334759626..d379d81e09 100644 --- a/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php +++ b/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php @@ -50,6 +50,13 @@ public function setTimestamp($timestamp = null); */ public function metadata(); + /** + * Returns an array containing the file metadata + * + * @return array + */ + public function getMeta(); + /** * Add meta file for the medium. * diff --git a/system/src/Grav/Framework/Contracts/Media/MediaObjectInterface.php b/system/src/Grav/Framework/Contracts/Media/MediaObjectInterface.php new file mode 100644 index 0000000000..75b80f0d6d --- /dev/null +++ b/system/src/Grav/Framework/Contracts/Media/MediaObjectInterface.php @@ -0,0 +1,52 @@ + + */ + public function getIterator(): iterable; +} diff --git a/system/src/Grav/Framework/Contracts/Relationships/RelationshipsInterface.php b/system/src/Grav/Framework/Contracts/Relationships/RelationshipsInterface.php new file mode 100644 index 0000000000..5e42cdf088 --- /dev/null +++ b/system/src/Grav/Framework/Contracts/Relationships/RelationshipsInterface.php @@ -0,0 +1,48 @@ + + */ +interface ToManyRelationshipInterface extends RelationshipInterface +{ + /** + * @param string $id + * @param string|null $type + * @return T|null + * @phpstan-pure + */ + public function getIdentifier(string $id, string $type = null): ?IdentifierInterface; + + /** + * @param string $id + * @param string|null $type + * @return T|null + * @phpstan-pure + */ + public function getObject(string $id, string $type = null): ?object; + + /** + * @param iterable $identifiers + * @return bool + */ + public function addIdentifiers(iterable $identifiers): bool; + + /** + * @param iterable $identifiers + * @return bool + */ + public function replaceIdentifiers(iterable $identifiers): bool; + + /** + * @param iterable $identifiers + * @return bool + */ + public function removeIdentifiers(iterable $identifiers): bool; +} diff --git a/system/src/Grav/Framework/Contracts/Relationships/ToOneRelationshipInterface.php b/system/src/Grav/Framework/Contracts/Relationships/ToOneRelationshipInterface.php new file mode 100644 index 0000000000..0e6aeb9738 --- /dev/null +++ b/system/src/Grav/Framework/Contracts/Relationships/ToOneRelationshipInterface.php @@ -0,0 +1,37 @@ + + */ +interface ToOneRelationshipInterface extends RelationshipInterface +{ + /** + * @param string|null $id + * @param string|null $type + * @return T|null + * @phpstan-pure + */ + public function getIdentifier(string $id = null, string $type = null): ?IdentifierInterface; + + /** + * @param string|null $id + * @param string|null $type + * @return T|null + * @phpstan-pure + */ + public function getObject(string $id = null, string $type = null): ?object; + + /** + * @param T|null $identifier + * @return bool + */ + public function replaceIdentifier(IdentifierInterface $identifier = null): bool; +} diff --git a/system/src/Grav/Framework/Flex/FlexIdentifier.php b/system/src/Grav/Framework/Flex/FlexIdentifier.php new file mode 100644 index 0000000000..0643dbde87 --- /dev/null +++ b/system/src/Grav/Framework/Flex/FlexIdentifier.php @@ -0,0 +1,72 @@ +getKey(), $object->getFlexType(), 'key'); + $instance->setObject($object); + + return $instance; + } + + /** + * IdentifierInterface constructor. + * @param string $id + * @param string $type + * @param string $keyField + */ + public function __construct(string $id, string $type, string $keyField = 'key') + { + parent::__construct($id, $type); + + $this->keyField = $keyField; + } + + /** + * @return T + */ + public function getObject(): ?FlexObjectInterface + { + if (!isset($this->object)) { + /** @var Flex $flex */ + $flex = Grav::instance()['flex']; + + $this->object = $flex->getObject($this->getId(), $this->getType(), $this->keyField); + } + + return $this->object; + } + + /** + * @param T $object + */ + public function setObject(FlexObjectInterface $object): void + { + $type = $this->getType(); + if ($type !== $object->getFlexType()) { + throw new RuntimeException(sprintf('Object has to be type %s, %s given', $type, $object->getFlexType())); + } + + $this->object = $object; + } +} diff --git a/system/src/Grav/Framework/Flex/Traits/FlexRelationshipsTrait.php b/system/src/Grav/Framework/Flex/Traits/FlexRelationshipsTrait.php new file mode 100644 index 0000000000..44ee5a106e --- /dev/null +++ b/system/src/Grav/Framework/Flex/Traits/FlexRelationshipsTrait.php @@ -0,0 +1,43 @@ +_relationships)) { + $blueprint = $this->getBlueprint(); + $options = $blueprint->get('config/relationships', []); + $parent = FlexIdentifier::createFromObject($this); + + $this->_relationships = new Relationships($parent, $options); + } + + return $this->_relationships; + } + + /** + * @param string $name + * @return RelationshipInterface|null + */ + public function getRelationship(string $name): ?RelationshipInterface + { + return $this->getRelationships()[$name]; + } + + protected function resetRelationships(): void + { + $this->_relationships = null; + } +} diff --git a/system/src/Grav/Framework/Media/MediaIdentifier.php b/system/src/Grav/Framework/Media/MediaIdentifier.php new file mode 100644 index 0000000000..d78e6ab964 --- /dev/null +++ b/system/src/Grav/Framework/Media/MediaIdentifier.php @@ -0,0 +1,135 @@ +getId()); + $instance->setObject($object); + + return $instance; + } + + /** + * @param string $id + */ + public function __construct(string $id) + { + parent::__construct($id, 'media'); + } + + /** + * @return T + */ + public function getObject(): ?MediaObjectInterface + { + if (!isset($this->object)) { + $type = $this->getType(); + $id = $this->getId(); + + $parts = explode('/', $id); + if ($type === 'media' && str_starts_with($id, 'uploads/')) { + array_shift($parts); + $type = array_shift($parts); + $uniqueId = array_shift($parts); + $field = array_shift($parts); + $filename = implode('/', $parts); + + $flash = $this->getFlash($type, $uniqueId); + if ($flash->exists()) { + $uploadedFile = $flash->getFilesByField($field)[$filename] ?? null; + + $this->object = new UploadedMediaObject($field, $filename, $flash, $uploadedFile); + } + } else { + $type = array_shift($parts); + $key = array_shift($parts); + $field = array_shift($parts); + $filename = implode('/', $parts); + + $flexObject = $this->getFlexObject($type, $key); + if ($flexObject && method_exists($flexObject, 'getMediaField') && method_exists($flexObject, 'getMedia')) { + $media = $field !== 'media' ? $flexObject->getMediaField($field) : $flexObject->getMedia(); + $image = null; + if ($media) { + $image = $media[$filename]; + } + + $this->object = new MediaObject($field, $filename, $image, $flexObject); + } + } + + if (!isset($this->object)) { + throw new \RuntimeException(sprintf('Object not found for identifier {type: "%s", id: "%s"}', $type, $id)); + } + } + + return $this->object; + } + + /** + * @param T $object + */ + public function setObject(MediaObjectInterface $object): void + { + $type = $this->getType(); + $objectType = $object->getType(); + + if ($type !== $objectType) { + throw new \RuntimeException(sprintf('Object has to be type %s, %s given', $type, $objectType)); + } + + $this->object = $object; + } + + protected function getFlash(string $type, string $uniqueId): FlexFormFlash + { + /** @var UserInterface|null $user */ + $user = Grav::instance()['user'] ?? null; + if (null !== $user && $user->exists()) { + // TODO: Modify uniqueid so we can detect if flash is user or session based. + $mediaFolder = $user->getMediaFolder(); + } else { + // TODO: Implement session based flash. + throw new \RuntimeException('Not implemented'); + } + + $folder = "{$mediaFolder}/tmp/api/flex-{$type}"; + + $config = [ + 'unique_id' => $uniqueId, + 'folder' => $folder + ]; + + return new FlexFormFlash($config); + } + + protected function getFlexObject(string $type, string $key): ?FlexObjectInterface + { + /** @var Flex $flex */ + $flex = Grav::instance()['flex']; + + return $flex->getObject($key, $type); + } +} diff --git a/system/src/Grav/Framework/Media/MediaObject.php b/system/src/Grav/Framework/Media/MediaObject.php new file mode 100644 index 0000000000..37963ec838 --- /dev/null +++ b/system/src/Grav/Framework/Media/MediaObject.php @@ -0,0 +1,210 @@ +field = $field; + $this->filename = $filename; + $this->media = $media; + $this->object = $object; + } + + /** + * @return string + */ + public function getType(): string + { + return 'media'; + } + + /** + * @return string + */ + public function getId(): string + { + $field = $this->field; + $object = $this->object; + $path = $field ? "/{$field}/" : '/media/'; + + return $object->getType() . '/' . $object->getKey() . $path . basename($this->filename); + } + + /** + * @return bool + */ + public function exists(): bool + { + return $this->media !== null; + } + + /** + * @return array + */ + public function getMeta(): array + { + if (!isset($this->media)) { + return []; + } + + return $this->media->getMeta(); + } + + /** + * @param string $field + * @return mixed|null + */ + public function get(string $field) + { + if (!isset($this->media)) { + return null; + } + + return $this->media->get($field); + } + + /** + * @return string + */ + public function getUrl(): string + { + if (!isset($this->media)) { + return ''; + } + + return $this->media->url(); + } + + /** + * Create media response. + * + * @param array $actions + * @return Response + */ + public function createResponse(array $actions): Response + { + if (!isset($this->media)) { + return $this->create404Response($actions); + } + + $media = $this->media; + + if ($actions) { + $media = $this->processMediaActions($media, $actions); + } + + // FIXME: This only works for images + if (!$media instanceof ImageMedium) { + throw new \RuntimeException('Not Implemented', 500); + } + + $filename = $media->path(false); + $time = filemtime($filename); + $size = filesize($filename); + $body = fopen($filename, 'rb'); + $headers = [ + 'Content-Type' => $media->get('mime'), + 'Last-Modified' => gmdate('D, d M Y H:i:s', $time) . ' GMT', + 'ETag' => sprintf('%x-%x', $size, $time) + ]; + + return new Response(200, $headers, $body); + } + + /** + * Process media actions + * + * @param GravMediaObjectInterface $medium + * @param array $actions + * @return GravMediaObjectInterface + */ + protected function processMediaActions(GravMediaObjectInterface $medium, array $actions): GravMediaObjectInterface + { + // loop through actions for the image and call them + foreach ($actions as $method => $params) { + $matches = []; + + if (preg_match('/\[(.*)]/', $params, $matches)) { + $args = [explode(',', $matches[1])]; + } else { + $args = explode(',', $params); + } + + try { + $medium->{$method}(...$args); + } catch (Throwable $e) { + // Ignore all errors for now and just skip the action. + } + } + + return $medium; + } + + /** + * @param array $actions + * @return Response + */ + protected function create404Response(array $actions): Response + { + // Display placeholder image. + $filename = static::$placeholderImage; + + $time = filemtime($filename); + $size = filesize($filename); + $body = fopen($filename, 'rb'); + $headers = [ + 'Content-Type' => 'image/svg', + 'Last-Modified' => gmdate('D, d M Y H:i:s', $time) . ' GMT', + 'ETag' => sprintf('%x-%x', $size, $time) + ]; + + return new Response(404, $headers, $body); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return [ + 'type' => $this->getType(), + 'id' => $this->getId() + ]; + } + + /** + * @return string[] + */ + public function __debugInfo(): array + { + return $this->jsonSerialize(); + } +} diff --git a/system/src/Grav/Framework/Media/UploadedMediaObject.php b/system/src/Grav/Framework/Media/UploadedMediaObject.php new file mode 100644 index 0000000000..f12c87d91c --- /dev/null +++ b/system/src/Grav/Framework/Media/UploadedMediaObject.php @@ -0,0 +1,158 @@ +field = $field; + $this->filename = $filename; + $this->object = $object; + $this->uploadedFile = $uploadedFile; + if ($uploadedFile) { + $this->meta = [ + 'filename' => $uploadedFile->getClientFilename(), + 'mime' => $uploadedFile->getClientMediaType(), + 'size' => $uploadedFile->getSize() + ]; + } else { + $this->meta = []; + } + } + + /** + * @return string + */ + public function getType(): string + { + return 'media'; + } + + /** + * @return string + */ + public function getId(): string + { + $field = $this->field; + $object = $this->object; + if ($object instanceof FlexFormFlash) { + $type = $object->getObject()->getFlexType(); + } else { + $type = 'undefined'; + } + + $id = $type . '/' . $object->getUniqueId(); + $path = $field ? "/{$field}/" : ''; + + return 'uploads/' . $id . $path . basename($this->filename); + } + + /** + * @return bool + */ + public function exists(): bool + { + //return $this->uploadedFile !== null; + return false; + } + + /** + * @return array + */ + public function getMeta(): array + { + return $this->meta; + } + + /** + * @param string $field + * @return mixed|null + */ + public function get(string $field) + { + return $this->meta[$field] ?? null; + } + + /** + * @return string + */ + public function getUrl(): string + { + return ''; + } + + /** + * @return UploadedFileInterface|null + */ + public function getUploadedFile(): ?UploadedFileInterface + { + return $this->uploadedFile; + } + + /** + * @param array $actions + * @return Response + */ + public function createResponse(array $actions): Response + { + // Display placeholder image. + $filename = static::$placeholderImage; + + $time = filemtime($filename); + $size = filesize($filename); + $body = fopen($filename, 'rb'); + $headers = [ + 'Content-Type' => 'image/svg', + 'Last-Modified' => gmdate('D, d M Y H:i:s', $time) . ' GMT', + 'ETag' => sprintf('%x-%x', $size, $time) + ]; + + return new Response(404, $headers, $body); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return [ + 'type' => $this->getType(), + 'id' => $this->getId() + ]; + } + + /** + * @return string[] + */ + public function __debugInfo(): array + { + return $this->jsonSerialize(); + } +} diff --git a/system/src/Grav/Framework/Object/Identifiers/Identifier.php b/system/src/Grav/Framework/Object/Identifiers/Identifier.php new file mode 100644 index 0000000000..23e4b56c02 --- /dev/null +++ b/system/src/Grav/Framework/Object/Identifiers/Identifier.php @@ -0,0 +1,64 @@ +id = $id; + $this->type = $type; + } + + /** + * @return string + * @phpstan-pure + */ + public function getId(): string + { + return $this->id; + } + + /** + * @return string + * @phpstan-pure + */ + public function getType(): string + { + return $this->type; + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return [ + 'type' => $this->type, + 'id' => $this->id + ]; + } + + /** + * @return array + */ + public function __debugInfo(): array + { + return $this->jsonSerialize(); + } +} diff --git a/system/src/Grav/Framework/Relationships/Relationships.php b/system/src/Grav/Framework/Relationships/Relationships.php new file mode 100644 index 0000000000..84fa79cee0 --- /dev/null +++ b/system/src/Grav/Framework/Relationships/Relationships.php @@ -0,0 +1,211 @@ +parent = $parent; + $this->options = $options; + $this->relationships = []; + } + + /** + * @return bool + * @phpstan-pure + */ + public function isModified(): bool + { + return !empty($this->getModified()); + } + + /** + * @return RelationshipInterface[] + * @phpstan-pure + */ + public function getModified(): array + { + $list = []; + foreach ($this->relationships as $name => $relationship) { + if ($relationship->isModified()) { + $list[$name] = $relationship; + } + } + + return $list; + } + + /** + * @return int + * @phpstan-pure + */ + public function count(): int + { + return count($this->options); + } + + /** + * @param string $offset + * @return bool + * @phpstan-pure + */ + public function offsetExists($offset): bool + { + return isset($this->options[$offset]); + } + + /** + * @param string $offset + * @return RelationshipInterface|null + */ + public function offsetGet($offset): ?RelationshipInterface + { + if (!isset($this->relationships[$offset])) { + $options = $this->options[$offset] ?? null; + if (null === $options) { + return null; + } + + $this->relationships[$offset] = $this->createRelationship($offset, $options); + } + + return $this->relationships[$offset]; + } + + /** + * @param string $offset + * @param mixed $value + * @return never-return + */ + public function offsetSet($offset, $value) + { + throw new RuntimeException('Setting relationship is not supported', 500); + } + + /** + * @param string $offset + * @return never-return + */ + public function offsetUnset($offset) + { + throw new RuntimeException('Removing relationship is not allowed', 500); + } + + /** + * @return RelationshipInterface|null + */ + public function current(): ?RelationshipInterface + { + $name = key($this->options); + if ($name === null) { + return null; + } + + return $this->offsetGet($name); + } + + /** + * @return string + * @phpstan-pure + */ + public function key(): string + { + return key($this->options); + } + + /** + * @return void + * @phpstan-pure + */ + public function next(): void + { + next($this->options); + } + + /** + * @return void + * @phpstan-pure + */ + public function rewind(): void + { + reset($this->options); + } + + /** + * @return bool + * @phpstan-pure + */ + public function valid(): bool + { + return key($this->options) !== null; + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + $list = []; + foreach ($this as $name => $relationship) { + $list[$name] = $relationship->jsonSerialize(); + } + + return $list; + } + + /** + * @param string $name + * @param array $options + * @return RelationshipInterface + */ + private function createRelationship(string $name, array $options): RelationshipInterface + { + $data = null; + + $parent = $this->parent; + if ($parent instanceof FlexIdentifier) { + $object = $parent->getObject(); + if (!method_exists($object, 'initRelationship')) { + throw new RuntimeException(sprintf('Bad relationship %s', $name), 500); + } + + $data = $object->initRelationship($name); + } + + $cardinality = $options['cardinality'] ?? ''; + switch ($cardinality) { + case 'to-one': + $relationship = new ToOneRelationship($parent, $name, $options, $data); + break; + case 'to-many': + $relationship = new ToManyRelationship($parent, $name, $options, $data ?? []); + break; + default: + throw new RuntimeException(sprintf('Bad relationship cardinality %s', $cardinality), 500); + } + + return $relationship; + } +} diff --git a/system/src/Grav/Framework/Relationships/ToManyRelationship.php b/system/src/Grav/Framework/Relationships/ToManyRelationship.php new file mode 100644 index 0000000000..73dbc6ccec --- /dev/null +++ b/system/src/Grav/Framework/Relationships/ToManyRelationship.php @@ -0,0 +1,244 @@ + + */ +class ToManyRelationship implements ToManyRelationshipInterface +{ + /** @template-use RelationshipTrait */ + use RelationshipTrait; + use Serializable; + + /** @var IdentifierInterface[] */ + protected array $identifiers = []; + + /** + * ToManyRelationship constructor. + * @param string $name + * @param IdentifierInterface $parent + * @param iterable $identifiers + */ + public function __construct(IdentifierInterface $parent, string $name, array $options, iterable $identifiers = []) + { + $this->parent = $parent; + $this->name = $name; + + $this->parseOptions($options); + $this->addIdentifiers($identifiers); + + $this->modified = false; + } + + /** + * @return string + * @phpstan-pure + */ + public function getCardinality(): string + { + return 'to-many'; + } + + /** + * @return int + * @phpstan-pure + */ + public function count(): int + { + return count($this->identifiers); + } + + /** + * @return array + */ + public function fetch(): array + { + $list = []; + foreach ($this->identifiers as $identifier) { + if (is_callable([$identifier, 'getObject'])) { + $identifier = $identifier->getObject(); + } + $list[] = $identifier; + } + + return $list; + } + + /** + * @param string $id + * @param string|null $type + * @return bool + * @phpstan-pure + */ + public function has(string $id, string $type = null): bool + { + return $this->getIdentifier($id, $type) !== null; + } + + /** + * @param string $id + * @param string|null $type + * @return IdentifierInterface|null + * @phpstan-pure + */ + public function getIdentifier(string $id, string $type = null): ?IdentifierInterface + { + if (null === $type) { + $type = $this->getType(); + } + + if ($type === 'media' && !str_contains($id, '/')) { + $name = $this->name; + $id = $this->parent->getType() . '/' . $this->parent->getId() . '/'. $name . '/' . $id; + } + + $key = "{$type}/{$id}"; + + return $this->identifiers[$key] ?? null; + } + + /** + * @param string $id + * @param string|null $type + * @return T|null + */ + public function getObject(string $id, string $type = null): ?object + { + $identifier = $this->getIdentifier($id, $type); + if ($identifier && is_callable([$identifier, 'getObject'])) { + $identifier = $identifier->getObject(); + } + + return $identifier; + } + + /** + * @param IdentifierInterface $identifier + * @return bool + */ + public function addIdentifier(IdentifierInterface $identifier): bool + { + return $this->addIdentifiers([$identifier]); + } + + /** + * @param IdentifierInterface|null $identifier + * @return bool + */ + public function removeIdentifier(IdentifierInterface $identifier = null): bool + { + return !$identifier || $this->removeIdentifiers([$identifier]); + } + + /** + * @param iterable $identifiers + * @return bool + */ + public function addIdentifiers(iterable $identifiers): bool + { + foreach ($identifiers as $identifier) { + $type = $identifier->getType(); + $id = $identifier->getId(); + $key = "{$type}/{$id}"; + + $this->identifiers[$key] = $this->checkIdentifier($identifier); + $this->modified = true; + } + + return true; + } + + /** + * @param iterable $identifiers + * @return bool + */ + public function replaceIdentifiers(iterable $identifiers): bool + { + $this->identifiers = []; + $this->modified = true; + + return $this->addIdentifiers($identifiers); + } + + /** + * @param iterable $identifiers + * @return bool + */ + public function removeIdentifiers(iterable $identifiers): bool + { + foreach ($identifiers as $identifier) { + $type = $identifier->getType(); + $id = $identifier->getId(); + $key = "{$type}/{$id}"; + + unset($this->identifiers[$key]); + $this->modified = true; + } + + return true; + } + + /** + * @return iterable + * @phpstan-pure + */ + public function getIterator(): iterable + { + return new ArrayIterator($this->identifiers); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + $list = []; + foreach ($this->getIterator() as $item) { + $list[] = $item->jsonSerialize(); + } + + return $list; + } + + /** + * @return array + */ + public function __serialize(): array + { + return [ + 'parent' => $this->parent, + 'name' => $this->name, + 'type' => $this->type, + 'options' => $this->options, + 'modified' => $this->modified, + 'identifiers' => $this->identifiers, + ]; + } + + /** + * @param array $data + * @return void + */ + public function __unserialize(array $data): void + { + $this->parent = $data['parent']; + $this->name = $data['name']; + $this->type = $data['type']; + $this->options = $data['options']; + $this->modified = $data['modified']; + $this->identifiers = $data['identifiers']; + } +} diff --git a/system/src/Grav/Framework/Relationships/ToOneRelationship.php b/system/src/Grav/Framework/Relationships/ToOneRelationship.php new file mode 100644 index 0000000000..a34ca046a9 --- /dev/null +++ b/system/src/Grav/Framework/Relationships/ToOneRelationship.php @@ -0,0 +1,205 @@ + + */ +class ToOneRelationship implements ToOneRelationshipInterface +{ + /** @template-use RelationshipTrait */ + use RelationshipTrait; + use Serializable; + + protected ?IdentifierInterface $identifier = null; + + public function __construct(IdentifierInterface $parent, string $name, array $options, IdentifierInterface $identifier = null) + { + $this->parent = $parent; + $this->name = $name; + + $this->parseOptions($options); + $this->replaceIdentifier($identifier); + + $this->modified = false; + } + + /** + * @return string + * @phpstan-pure + */ + public function getCardinality(): string + { + return 'to-one'; + } + + /** + * @return int + * @phpstan-pure + */ + public function count(): int + { + return $this->identifier ? 1 : 0; + } + + /** + * @return object|null + */ + public function fetch(): ?object + { + $identifier = $this->identifier; + if (is_callable([$identifier, 'getObject'])) { + $identifier = $identifier->getObject(); + } + + return $identifier; + } + + + /** + * @param string|null $id + * @param string|null $type + * @return bool + * @phpstan-pure + */ + public function has(string $id = null, string $type = null): bool + { + return $this->getIdentifier($id, $type) !== null; + } + + /** + * @param string|null $id + * @param string|null $type + * @return IdentifierInterface|null + * @phpstan-pure + */ + public function getIdentifier(string $id = null, string $type = null): ?IdentifierInterface + { + if ($id && $this->getType() === 'media' && !str_contains($id, '/')) { + $name = $this->name; + $id = $this->parent->getType() . '/' . $this->parent->getId() . '/'. $name . '/' . $id; + } + + $identifier = $this->identifier ?? null; + if (null === $identifier || ($type && $type !== $identifier->getType()) || ($id && $id !== $identifier->getId())) { + return null; + } + + return $identifier; + } + + /** + * @param string|null $id + * @param string|null $type + * @return T|null + */ + public function getObject(string $id = null, string $type = null): ?object + { + $identifier = $this->getIdentifier($id, $type); + if ($identifier && is_callable([$identifier, 'getObject'])) { + $identifier = $identifier->getObject(); + } + + return $identifier; + } + + /** + * @param IdentifierInterface $identifier + * @return bool + */ + public function addIdentifier(IdentifierInterface $identifier): bool + { + $this->identifier = $this->checkIdentifier($identifier); + $this->modified = true; + + return true; + } + + /** + * @param IdentifierInterface|null $identifier + * @return bool + */ + public function replaceIdentifier(IdentifierInterface $identifier = null): bool + { + if ($identifier === null) { + $this->identifier = null; + $this->modified = true; + + return true; + } + + return $this->addIdentifier($identifier); + } + + /** + * @param IdentifierInterface|null $identifier + * @return bool + */ + public function removeIdentifier(IdentifierInterface $identifier = null): bool + { + if (null === $identifier || $this->has($identifier->getId(), $identifier->getType())) { + $this->identifier = null; + $this->modified = true; + + return true; + } + + return false; + } + + /** + * @return iterable + * @phpstan-pure + */ + public function getIterator(): iterable + { + return new ArrayIterator((array)$this->identifier); + } + + /** + * @return array|null + */ + public function jsonSerialize(): ?array + { + return $this->identifier ? $this->identifier->jsonSerialize() : null; + } + + /** + * @return array + */ + public function __serialize(): array + { + return [ + 'parent' => $this->parent, + 'name' => $this->name, + 'type' => $this->type, + 'options' => $this->options, + 'modified' => $this->modified, + 'identifier' => $this->identifier, + ]; + } + + /** + * @param array $data + * @return void + */ + public function __unserialize(array $data): void + { + $this->parent = $data['parent']; + $this->name = $data['name']; + $this->type = $data['type']; + $this->options = $data['options']; + $this->modified = $data['modified']; + $this->identifier = $data['identifier']; + } +} diff --git a/system/src/Grav/Framework/Relationships/Traits/RelationshipTrait.php b/system/src/Grav/Framework/Relationships/Traits/RelationshipTrait.php new file mode 100644 index 0000000000..276b96b05d --- /dev/null +++ b/system/src/Grav/Framework/Relationships/Traits/RelationshipTrait.php @@ -0,0 +1,122 @@ +name; + } + + /** + * @return string + * @phpstan-pure + */ + public function getType(): string + { + return $this->type; + } + + /** + * @return bool + * @phpstan-pure + */ + public function isModified(): bool + { + return $this->modified; + } + + /** + * @return IdentifierInterface + * @phpstan-pure + */ + public function getParent(): IdentifierInterface + { + return $this->parent; + } + + /** + * @param IdentifierInterface $identifier + * @return bool + * @phpstan-pure + */ + public function hasIdentifier(IdentifierInterface $identifier): bool + { + return $this->getIdentifier($identifier->getId(), $identifier->getType()) !== null; + } + + /** + * @return int + * @phpstan-pure + */ + abstract public function count(): int; + + /** + * @return void + * @phpstan-pure + */ + public function check(): void + { + $min = $this->options['min'] ?? 0; + $max = $this->options['max'] ?? 0; + + if ($min || $max) { + $count = $this->count(); + if ($min && $count < $min) { + throw new \RuntimeException(sprintf('%s relationship has too few objects in it', $this->name)); + } + if ($max && $count > $max) { + throw new \RuntimeException(sprintf('%s relationship has too many objects in it', $this->name)); + } + } + } + + /** + * @param IdentifierInterface $identifier + * @return IdentifierInterface + */ + private function checkIdentifier(IdentifierInterface $identifier): IdentifierInterface + { + if ($this->type !== $identifier->getType()) { + throw new \RuntimeException(sprintf('Bad identifier type %s', $identifier->getType())); + } + + if (get_class($identifier) !== Identifier::class) { + return $identifier; + } + + if ($this->type === 'media') { + return new MediaIdentifier($identifier->getId()); + } + + return new FlexIdentifier($identifier->getId(), $identifier->getType()); + } + + private function parseOptions(array $options): void + { + $this->type = $options['type']; + $this->options = $options; + } +} From 2957077935c6ca884e3116a9f08a2ac9a6c471b6 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Tue, 3 May 2022 10:27:50 +0300 Subject: [PATCH 02/18] Added relationship logic for flex --- .../Grav/Framework/Flex/FlexDirectoryForm.php | 1 + system/src/Grav/Framework/Flex/FlexForm.php | 1 + .../Framework/Flex/Traits/FlexMediaTrait.php | 53 ++++++++++++++++++- .../Flex/Traits/FlexRelationshipsTrait.php | 17 ++++++ system/src/Grav/Framework/Form/FormFlash.php | 20 +++++-- .../src/Grav/Framework/Form/FormFlashFile.php | 9 ++++ .../Form/Interfaces/FormFlashInterface.php | 7 +++ .../Grav/Framework/Form/Traits/FormTrait.php | 29 ++++++++-- .../Grav/Framework/Media/MediaIdentifier.php | 2 +- .../src/Grav/Framework/Media/MediaObject.php | 3 +- .../Framework/Media/UploadedMediaObject.php | 34 +++++++----- 11 files changed, 152 insertions(+), 24 deletions(-) diff --git a/system/src/Grav/Framework/Flex/FlexDirectoryForm.php b/system/src/Grav/Framework/Flex/FlexDirectoryForm.php index 709f418919..1e715a5002 100644 --- a/system/src/Grav/Framework/Flex/FlexDirectoryForm.php +++ b/system/src/Grav/Framework/Flex/FlexDirectoryForm.php @@ -275,6 +275,7 @@ public function getFlash() 'unique_id' => $this->getUniqueId(), 'form_name' => $this->getName(), 'folder' => $this->getFlashFolder(), + 'id' => $this->getFlashId(), 'directory' => $this->getDirectory() ]; diff --git a/system/src/Grav/Framework/Flex/FlexForm.php b/system/src/Grav/Framework/Flex/FlexForm.php index a1263f67ff..55759dff27 100644 --- a/system/src/Grav/Framework/Flex/FlexForm.php +++ b/system/src/Grav/Framework/Flex/FlexForm.php @@ -326,6 +326,7 @@ public function getFlash() 'unique_id' => $this->getUniqueId(), 'form_name' => $this->getName(), 'folder' => $this->getFlashFolder(), + 'id' => $this->getFlashId(), 'object' => $this->getObject() ]; diff --git a/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php b/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php index ad437ebe48..f96b09eed1 100644 --- a/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php +++ b/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php @@ -12,6 +12,7 @@ use Grav\Common\Config\Config; use Grav\Common\Grav; use Grav\Common\Media\Interfaces\MediaCollectionInterface; +use Grav\Common\Media\Interfaces\MediaObjectInterface; use Grav\Common\Media\Interfaces\MediaUploadInterface; use Grav\Common\Media\Traits\MediaTrait; use Grav\Common\Page\Media; @@ -22,6 +23,8 @@ use Grav\Framework\Filesystem\Filesystem; use Grav\Framework\Flex\FlexDirectory; use Grav\Framework\Form\FormFlashFile; +use Grav\Framework\Media\MediaObject; +use Grav\Framework\Media\UploadedMediaObject; use Psr\Http\Message\UploadedFileInterface; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; use RuntimeException; @@ -30,7 +33,6 @@ use function is_array; use function is_callable; use function is_int; -use function is_object; use function is_string; use function strpos; @@ -297,6 +299,54 @@ public function __debugInfo() ]; } + /** + * @param string|null $field + * @param string $filename + * @param MediaObjectInterface|null $image + * @return MediaObject|UploadedMediaObject + */ + protected function buildMediaObject(?string $field, string $filename, MediaObjectInterface $image = null) + { + if (!$image) { + $media = $field ? $this->getMediaField($field) : null; + if ($media) { + $image = $media[$filename]; + } + } + + return new MediaObject($field, $filename, $image, $this); + } + + /** + * @param string|null $field + * @return array + */ + protected function buildMediaList(?string $field): array + { + $names = $field ? (array)$this->getNestedProperty($field) : []; + $media = $field ? $this->getMediaField($field) : null; + if (null === $media) { + $media = $this->getMedia(); + } + + $list = []; + foreach ($names as $key => $val) { + $name = is_string($val) ? $val : $key; + $medium = $media[$name]; + if ($medium) { + if ($medium->uploaded_file) { + $upload = $medium->uploaded_file; + + $list[] = new UploadedMediaObject($upload->getId(), $field, $name, $upload); + } else { + $list[] = $this->buildMediaObject($field, $name, $medium); + } + } + } + + return $list; + } + /** * @param array $files * @return void @@ -388,6 +438,7 @@ protected function addUpdatedMedia(MediaCollectionInterface $media): void $updated = true; if ($medium) { $medium->uploaded = true; + $medium->uploaded_file = $upload; $media->add($filename, $medium); } elseif (is_callable([$media, 'hide'])) { $media->hide($filename); diff --git a/system/src/Grav/Framework/Flex/Traits/FlexRelationshipsTrait.php b/system/src/Grav/Framework/Flex/Traits/FlexRelationshipsTrait.php index 44ee5a106e..03c838201b 100644 --- a/system/src/Grav/Framework/Flex/Traits/FlexRelationshipsTrait.php +++ b/system/src/Grav/Framework/Flex/Traits/FlexRelationshipsTrait.php @@ -7,6 +7,9 @@ use Grav\Framework\Flex\FlexIdentifier; use Grav\Framework\Relationships\Relationships; +/** + * Trait FlexRelationshipsTrait + */ trait FlexRelationshipsTrait { private ?RelationshipsInterface $_relationships = null; @@ -40,4 +43,18 @@ protected function resetRelationships(): void { $this->_relationships = null; } + + /** + * @param iterable $collection + * @return array + */ + protected function buildFlexIdentifierList(iterable $collection): array + { + $list = []; + foreach ($collection as $object) { + $list[] = FlexIdentifier::createFromObject($object); + } + + return $list; + } } diff --git a/system/src/Grav/Framework/Form/FormFlash.php b/system/src/Grav/Framework/Form/FormFlash.php index 863eb4af69..207015bb02 100644 --- a/system/src/Grav/Framework/Form/FormFlash.php +++ b/system/src/Grav/Framework/Form/FormFlash.php @@ -31,6 +31,8 @@ class FormFlash implements FormFlashInterface /** @var bool */ protected $exists; /** @var string */ + protected $id; + /** @var string */ protected $sessionId; /** @var string */ protected $uniqueId; @@ -75,9 +77,12 @@ public function __construct($config) }); } - $this->sessionId = $config['session_id'] ?? 'no-session'; + $this->id = $config['id'] ?? ''; + $this->sessionId = $config['session_id'] ?? ''; $this->uniqueId = $config['unique_id'] ?? ''; + $this->setUser($config['user'] ?? null); + $folder = $config['folder'] ?? ($this->sessionId ? 'tmp://forms/' . $this->sessionId : ''); /** @var UniformResourceLocator $locator */ @@ -133,6 +138,14 @@ protected function loadStoredForm(): ?array return $data; } + /** + * @inheritDoc + */ + public function getId(): string + { + return $this->id && $this->uniqueId ? $this->id . '/' . $this->uniqueId : ''; + } + /** * @inheritDoc */ @@ -390,8 +403,8 @@ public function removeFile(string $name, string $field = null): bool */ public function clearFiles() { - foreach ($this->files as $field => $files) { - foreach ($files as $name => $upload) { + foreach ($this->files as $files) { + foreach ($files as $upload) { $this->removeTmpFile($upload['tmp_name'] ?? ''); } } @@ -406,6 +419,7 @@ public function jsonSerialize(): array { return [ 'form' => $this->formName, + 'id' => $this->getId(), 'unique_id' => $this->uniqueId, 'url' => $this->url, 'user' => $this->user, diff --git a/system/src/Grav/Framework/Form/FormFlashFile.php b/system/src/Grav/Framework/Form/FormFlashFile.php index 65af544d1b..54047d1bc5 100644 --- a/system/src/Grav/Framework/Form/FormFlashFile.php +++ b/system/src/Grav/Framework/Form/FormFlashFile.php @@ -28,6 +28,8 @@ */ class FormFlashFile implements UploadedFileInterface, JsonSerializable { + /** @var string */ + private $id; /** @var string */ private $field; /** @var bool */ @@ -45,6 +47,7 @@ class FormFlashFile implements UploadedFileInterface, JsonSerializable */ public function __construct(string $field, array $upload, FormFlash $flash) { + $this->id = $flash->getId() ?: $flash->getUniqueId(); $this->field = $field; $this->upload = $upload; $this->flash = $flash; @@ -107,6 +110,11 @@ public function moveTo($targetPath) } } + public function getId(): string + { + return $this->id; + } + /** * @return string */ @@ -222,6 +230,7 @@ public function getTmpFile(): ?string public function __debugInfo() { return [ + 'id:private' => $this->id, 'field:private' => $this->field, 'moved:private' => $this->moved, 'upload:private' => $this->upload, diff --git a/system/src/Grav/Framework/Form/Interfaces/FormFlashInterface.php b/system/src/Grav/Framework/Form/Interfaces/FormFlashInterface.php index 44bee025c4..093006a751 100644 --- a/system/src/Grav/Framework/Form/Interfaces/FormFlashInterface.php +++ b/system/src/Grav/Framework/Form/Interfaces/FormFlashInterface.php @@ -22,6 +22,13 @@ interface FormFlashInterface extends \JsonSerializable */ public function __construct($config); + /** + * Get unique form flash id if set. + * + * @return string + */ + public function getId(): string; + /** * Get session Id associated to this form instance. * diff --git a/system/src/Grav/Framework/Form/Traits/FormTrait.php b/system/src/Grav/Framework/Form/Traits/FormTrait.php index 710d8b0cc8..a69ad1e923 100644 --- a/system/src/Grav/Framework/Form/Traits/FormTrait.php +++ b/system/src/Grav/Framework/Form/Traits/FormTrait.php @@ -453,10 +453,10 @@ public function getFlash() 'session_id' => $this->getSessionId(), 'unique_id' => $this->getUniqueId(), 'form_name' => $this->getName(), - 'folder' => $this->getFlashFolder() + 'folder' => $this->getFlashFolder(), + 'id' => $this->getFlashId() ]; - $this->flash = new FormFlash($config); $this->flash->setUrl($grav['uri']->url)->setUser($grav['user'] ?? null); } @@ -486,7 +486,8 @@ public function getAllFlashes(): array 'session_id' => $this->getSessionId(), 'unique_id' => $uniqueId, 'form_name' => $name, - 'folder' => $this->getFlashFolder() + 'folder' => $this->getFlashFolder(), + 'id' => $this->getFlashId() ]; $flash = new FormFlash($config); if ($flash->exists() && $flash->getFormName() === $name) { @@ -614,6 +615,28 @@ protected function getFlashFolder(): ?string return strpos($path, '!!') === false ? rtrim($path, '/') : null; } + /** + * @return string|null + */ + protected function getFlashId(): ?string + { + // Fill template token keys/value pairs. + $dataMap = [ + '[FORM_NAME]' => $this->getName(), + '[SESSIONID]' => 'session', + '[USERNAME]' => '!!', + '[USERNAME_OR_SESSIONID]' => '!!', + '[ACCOUNT]' => 'account' + ]; + + $flashLookupFolder = $this->getFlashLookupFolder(); + + $path = str_replace(array_keys($dataMap), array_values($dataMap), $flashLookupFolder); + + // Make sure we only return valid paths. + return strpos($path, '!!') === false ? rtrim($path, '/') : null; + } + /** * @return string */ diff --git a/system/src/Grav/Framework/Media/MediaIdentifier.php b/system/src/Grav/Framework/Media/MediaIdentifier.php index d78e6ab964..1551483e52 100644 --- a/system/src/Grav/Framework/Media/MediaIdentifier.php +++ b/system/src/Grav/Framework/Media/MediaIdentifier.php @@ -60,7 +60,7 @@ public function getObject(): ?MediaObjectInterface if ($flash->exists()) { $uploadedFile = $flash->getFilesByField($field)[$filename] ?? null; - $this->object = new UploadedMediaObject($field, $filename, $flash, $uploadedFile); + $this->object = UploadedMediaObject::createFromFlash($flash, $field, $filename, $uploadedFile); } } else { $type = array_shift($parts); diff --git a/system/src/Grav/Framework/Media/MediaObject.php b/system/src/Grav/Framework/Media/MediaObject.php index 37963ec838..0c3c98d84a 100644 --- a/system/src/Grav/Framework/Media/MediaObject.php +++ b/system/src/Grav/Framework/Media/MediaObject.php @@ -14,8 +14,7 @@ */ class MediaObject implements MediaObjectInterface { - // FIXME: - static public string $placeholderImage = 'theme://img/revkit-temp.svg'; + static public string $placeholderImage = 'image://media/thumb.png'; public FlexObjectInterface $object; public ?GravMediaObjectInterface $media; diff --git a/system/src/Grav/Framework/Media/UploadedMediaObject.php b/system/src/Grav/Framework/Media/UploadedMediaObject.php index f12c87d91c..113440a934 100644 --- a/system/src/Grav/Framework/Media/UploadedMediaObject.php +++ b/system/src/Grav/Framework/Media/UploadedMediaObject.php @@ -13,28 +13,41 @@ */ class UploadedMediaObject implements MediaObjectInterface { - // FIXME: - static public string $placeholderImage = 'theme://img/revkit-temp.svg'; + static public string $placeholderImage = 'image://media/thumb.png'; public FormFlashInterface $object; + private string $id; private ?string $field; private string $filename; private array $meta; private ?UploadedFileInterface $uploadedFile; /** - * UploadedMediaObject constructor. + * @param FlexFormFlash $flash * @param string|null $field * @param string $filename - * @param FormFlashInterface $object * @param UploadedFileInterface|null $uploadedFile + * @return static */ - public function __construct(?string $field, string $filename, FormFlashInterface $object, ?UploadedFileInterface $uploadedFile = null) + public static function createFromFlash(FlexFormFlash $flash, ?string $field, string $filename, ?UploadedFileInterface $uploadedFile = null) { + $id = $flash->getId(); + + return new static($id, $field, $filename, $uploadedFile); + } + + /** + * @param string $id + * @param string|null $field + * @param string $filename + * @param UploadedFileInterface|null $uploadedFile + */ + public function __construct(string $id, ?string $field, string $filename, ?UploadedFileInterface $uploadedFile = null) + { + $this->id = $id; $this->field = $field; $this->filename = $filename; - $this->object = $object; $this->uploadedFile = $uploadedFile; if ($uploadedFile) { $this->meta = [ @@ -60,15 +73,8 @@ public function getType(): string */ public function getId(): string { + $id = $this->id; $field = $this->field; - $object = $this->object; - if ($object instanceof FlexFormFlash) { - $type = $object->getObject()->getFlexType(); - } else { - $type = 'undefined'; - } - - $id = $type . '/' . $object->getUniqueId(); $path = $field ? "/{$field}/" : ''; return 'uploads/' . $id . $path . basename($this->filename); From d6dcd9630130e2136dd8b6bf76c4de69514c5477 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Tue, 3 May 2022 13:18:57 +0300 Subject: [PATCH 03/18] Added relationships support for user accounts --- system/blueprints/flex/user-accounts.yaml | 12 +++ .../Common/Flex/Types/Users/UserObject.php | 80 +++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/system/blueprints/flex/user-accounts.yaml b/system/blueprints/flex/user-accounts.yaml index 8d336f9938..91c826339c 100644 --- a/system/blueprints/flex/user-accounts.yaml +++ b/system/blueprints/flex/user-accounts.yaml @@ -125,6 +125,18 @@ config: - username - fullname + relationships: + relationships: + media: + type: media + cardinality: to-many + avatar: + type: media + cardinality: to-one +# roles: +# type: user-groups +# cardinality: to-many + blueprints: configure: fields: diff --git a/system/src/Grav/Common/Flex/Types/Users/UserObject.php b/system/src/Grav/Common/Flex/Types/Users/UserObject.php index 9dec9729fb..f6c9982d46 100644 --- a/system/src/Grav/Common/Flex/Types/Users/UserObject.php +++ b/system/src/Grav/Common/Flex/Types/Users/UserObject.php @@ -31,6 +31,7 @@ use Grav\Common\User\Interfaces\UserInterface; use Grav\Common\User\Traits\UserTrait; use Grav\Common\Utils; +use Grav\Framework\Contracts\Relationships\ToOneRelationshipInterface; use Grav\Framework\File\Formatter\JsonFormatter; use Grav\Framework\File\Formatter\YamlFormatter; use Grav\Framework\Filesystem\Filesystem; @@ -38,7 +39,10 @@ use Grav\Framework\Flex\FlexDirectory; use Grav\Framework\Flex\Storage\FileStorage; use Grav\Framework\Flex\Traits\FlexMediaTrait; +use Grav\Framework\Flex\Traits\FlexRelationshipsTrait; use Grav\Framework\Form\FormFlashFile; +use Grav\Framework\Media\MediaIdentifier; +use Grav\Framework\Media\UploadedMediaObject; use Psr\Http\Message\UploadedFileInterface; use RocketTheme\Toolbox\Event\Event; use RocketTheme\Toolbox\File\FileInterface; @@ -77,6 +81,7 @@ class UserObject extends FlexObject implements UserInterface, Countable } use UserTrait; use UserObjectLegacyTrait; + use FlexRelationshipsTrait; /** @var Closure|null */ static public $authorizeCallable; @@ -672,6 +677,81 @@ public function getMediaFolder(): ?string return $folder; } + /** + * @param string $name + * @return array|object|null + * @internal + */ + public function initRelationship(string $name) + { + switch ($name) { + case 'media': + $list = []; + foreach ($this->getMedia()->all() as $filename => $object) { + $list[] = $this->buildMediaObject(null, $filename, $object); + } + + return $list; + case 'avatar': + return $this->buildMediaObject('avatar', basename($this->getAvatarUrl()), $this->getAvatarImage()); + } + + throw new \InvalidArgumentException(sprintf('%s: Relationship %s does not exist', $this->getFlexType(), $name)); + } + + /** + * @return bool Return true if relationships were updated. + */ + protected function updateRelationships(): bool + { + $modified = $this->getRelationships()->getModified(); + if ($modified) { + foreach ($modified as $relationship) { + $name = $relationship->getName(); + switch ($name) { + case 'avatar': + \assert($relationship instanceof ToOneRelationshipInterface); + $this->updateAvatarRelationship($relationship); + break; + default: + throw new \InvalidArgumentException(sprintf('%s: Relationship %s cannot be modified', $this->getFlexType(), $name), 400); + } + } + + $this->resetRelationships(); + + return true; + } + + return false; + } + + /** + * @param ToOneRelationshipInterface $relationship + */ + protected function updateAvatarRelationship(ToOneRelationshipInterface $relationship): void + { + $files = []; + $avatar = $this->getAvatarImage(); + if ($avatar) { + $files['avatar'][$avatar->filename] = null; + } + + $identifier = $relationship->getIdentifier(); + if ($identifier) { + \assert($identifier instanceof MediaIdentifier); + $object = $identifier->getObject(); + if ($object instanceof UploadedMediaObject) { + $uploadedFile = $object->getUploadedFile(); + if ($uploadedFile) { + $files['avatar'][$uploadedFile->getClientFilename()] = $uploadedFile; + } + } + } + + $this->update([], $files); + } + /** * @param string $name * @return Blueprint From 9ec3e7d73152ca101786fc73cb84c56507187225 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Tue, 3 May 2022 16:28:02 +0300 Subject: [PATCH 04/18] Minor fix on account relationships --- system/blueprints/flex/user-accounts.yaml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/system/blueprints/flex/user-accounts.yaml b/system/blueprints/flex/user-accounts.yaml index 91c826339c..74baeb623c 100644 --- a/system/blueprints/flex/user-accounts.yaml +++ b/system/blueprints/flex/user-accounts.yaml @@ -126,16 +126,15 @@ config: - fullname relationships: - relationships: - media: - type: media - cardinality: to-many - avatar: - type: media - cardinality: to-one -# roles: -# type: user-groups -# cardinality: to-many + media: + type: media + cardinality: to-many + avatar: + type: media + cardinality: to-one +# roles: +# type: user-groups +# cardinality: to-many blueprints: configure: From 1237f0a6d6fd56e0739da93ddc49ab9e50ce9941 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Wed, 4 May 2022 14:45:59 +0300 Subject: [PATCH 05/18] Added support for `multipart/form-data` content type in PUT and PATCH requests --- CHANGELOG.md | 7 + system/src/Grav/Common/Grav.php | 5 + .../RequestHandler/Middlewares/Exceptions.php | 4 +- .../Middlewares/MultipartRequestSupport.php | 122 ++++++++++++++++++ .../Traits/RequestHandlerTrait.php | 4 +- 5 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 system/src/Grav/Framework/RequestHandler/Middlewares/MultipartRequestSupport.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 04b07a81e0..4fdcb15013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v1.NEXT +## mm/dd/2022 + +1. [](#new) + * Added support for `multipart/form-data` content type in PUT and PATCH requests + * Added support for flex object relationships + # v1.7.33 ## 04/25/2022 diff --git a/system/src/Grav/Common/Grav.php b/system/src/Grav/Common/Grav.php index 7e3dcaff86..d2fce73512 100644 --- a/system/src/Grav/Common/Grav.php +++ b/system/src/Grav/Common/Grav.php @@ -48,6 +48,7 @@ use Grav\Common\Twig\Twig; use Grav\Framework\DI\Container; use Grav\Framework\Psr7\Response; +use Grav\Framework\RequestHandler\Middlewares\MultipartRequestSupport; use Grav\Framework\RequestHandler\RequestHandler; use Grav\Framework\Route\Route; use Grav\Framework\Session\Messages; @@ -117,6 +118,7 @@ class Grav extends Container * @var array All middleware processors that are processed in $this->process() */ protected $middleware = [ + 'multipartRequestSupport', 'initializeProcessor', 'pluginsProcessor', 'themesProcessor', @@ -259,6 +261,9 @@ public function process(): void $container = new Container( [ + 'multipartRequestSupport' => function () { + return new MultipartRequestSupport(); + }, 'initializeProcessor' => function () { return new InitializeProcessor($this); }, diff --git a/system/src/Grav/Framework/RequestHandler/Middlewares/Exceptions.php b/system/src/Grav/Framework/RequestHandler/Middlewares/Exceptions.php index 36ef3a53f5..6c961d2167 100644 --- a/system/src/Grav/Framework/RequestHandler/Middlewares/Exceptions.php +++ b/system/src/Grav/Framework/RequestHandler/Middlewares/Exceptions.php @@ -1,4 +1,4 @@ -getHeaderLine('content-type'); + $method = $request->getMethod(); + if (!str_starts_with($contentType, 'multipart/form-data') || !in_array($method, ['PUT', 'PATH'], true)) { + return $handler->handle($request); + } + + $boundary = explode('; boundary=', $contentType, 2)[1] ?? ''; + $parts = explode("--{$boundary}", $request->getBody()->getContents()); + $parts = array_slice($parts, 1, count($parts) - 2); + + $params = []; + $files = []; + foreach ($parts as $part) { + $this->processPart($params, $files, $part); + } + + return $handler->handle($request->withParsedBody($params)->withUploadedFiles($files)); + } + + /** + * @param array $params + * @param array $files + * @param string $part + * @return void + */ + protected function processPart(array &$params, array &$files, string $part): void + { + $part = ltrim($part, "\r\n"); + [$rawHeaders, $body] = explode("\r\n\r\n", $part, 2); + + // Parse headers. + $rawHeaders = explode("\r\n", $rawHeaders); + $headers = array_reduce( + $rawHeaders, + static function (array $headers, $header) { + [$name, $value] = explode(':', $header); + $headers[strtolower($name)] = ltrim($value, ' '); + + return $headers; + }, + [] + ); + + if (!isset($headers['content-disposition'])) { + return; + } + + // Parse content disposition header. + $contentDisposition = $headers['content-disposition']; + preg_match('/^(.+); *name="([^"]+)"(; *filename="([^"]+)")?/', $contentDisposition, $matches); + $name = $matches[2]; + $filename = $matches[4] ?? null; + + if ($filename !== null) { + $stream = Stream::create($body); + $this->addFile($files, $name, new UploadedFile($stream, strlen($body), UPLOAD_ERR_OK, $filename, $headers['content-type'] ?? null)); + } elseif (strpos($contentDisposition, 'filename') !== false) { + // Not uploaded file. + $this->addFile($files, $name, new UploadedFile(null, 0, UPLOAD_ERR_NO_FILE)); + } else { + // Regular field. + $params[$name] = substr($body, 0, -2); + } + } + + /** + * @param array $files + * @param string $name + * @param UploadedFileInterface $file + * @return void + */ + protected function addFile(array &$files, string $name, UploadedFileInterface $file): void + { + if (strpos($name, '[]') === strlen($name) - 2) { + $name = substr($name, 0, -2); + + if (isset($files[$name]) && is_array($files[$name])) { + $files[$name][] = $file; + } else { + $files[$name] = [$file]; + } + } else { + $files[$name] = $file; + } + } +} diff --git a/system/src/Grav/Framework/RequestHandler/Traits/RequestHandlerTrait.php b/system/src/Grav/Framework/RequestHandler/Traits/RequestHandlerTrait.php index ff82889d43..6b4e5c768f 100644 --- a/system/src/Grav/Framework/RequestHandler/Traits/RequestHandlerTrait.php +++ b/system/src/Grav/Framework/RequestHandler/Traits/RequestHandlerTrait.php @@ -28,10 +28,10 @@ trait RequestHandlerTrait protected $middleware; /** @var callable */ - private $handler; + protected $handler; /** @var ContainerInterface|null */ - private $container; + protected $container; /** * {@inheritdoc} From 32a486f1d41fce987e8904151d427fe52bc84342 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Thu, 5 May 2022 11:03:06 +0300 Subject: [PATCH 06/18] Added ToManyRelationship::getNthIdentifier() --- .../Relationships/ToManyRelationshipInterface.php | 6 ++++++ .../Relationships/ToManyRelationship.php | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/system/src/Grav/Framework/Contracts/Relationships/ToManyRelationshipInterface.php b/system/src/Grav/Framework/Contracts/Relationships/ToManyRelationshipInterface.php index 4ad5af1dad..723bef6905 100644 --- a/system/src/Grav/Framework/Contracts/Relationships/ToManyRelationshipInterface.php +++ b/system/src/Grav/Framework/Contracts/Relationships/ToManyRelationshipInterface.php @@ -13,6 +13,12 @@ */ interface ToManyRelationshipInterface extends RelationshipInterface { + /** + * @param positive-int $pos + * @return IdentifierInterface|null + */ + public function getNthIdentifier(int $pos): ?IdentifierInterface; + /** * @param string $id * @param string|null $type diff --git a/system/src/Grav/Framework/Relationships/ToManyRelationship.php b/system/src/Grav/Framework/Relationships/ToManyRelationship.php index 73dbc6ccec..26b138e43f 100644 --- a/system/src/Grav/Framework/Relationships/ToManyRelationship.php +++ b/system/src/Grav/Framework/Relationships/ToManyRelationship.php @@ -88,6 +88,21 @@ public function has(string $id, string $type = null): bool return $this->getIdentifier($id, $type) !== null; } + /** + * @param positive-int $pos + * @return IdentifierInterface|null + */ + public function getNthIdentifier(int $pos): ?IdentifierInterface + { + $items = array_keys($this->identifiers); + $key = $items[$pos - 1] ?? null; + if (null === $key) { + return null; + } + + return $this->identifiers[$key] ?? null; + } + /** * @param string $id * @param string|null $type From ec16b5184e3fc088b8a40d8e1779362571c077b7 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Fri, 20 May 2022 10:59:53 +0300 Subject: [PATCH 07/18] Fixed creating empty uploaded file --- .../RequestHandler/Middlewares/MultipartRequestSupport.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/src/Grav/Framework/RequestHandler/Middlewares/MultipartRequestSupport.php b/system/src/Grav/Framework/RequestHandler/Middlewares/MultipartRequestSupport.php index 8629352c54..e59bc76135 100644 --- a/system/src/Grav/Framework/RequestHandler/Middlewares/MultipartRequestSupport.php +++ b/system/src/Grav/Framework/RequestHandler/Middlewares/MultipartRequestSupport.php @@ -92,7 +92,8 @@ static function (array $headers, $header) { $this->addFile($files, $name, new UploadedFile($stream, strlen($body), UPLOAD_ERR_OK, $filename, $headers['content-type'] ?? null)); } elseif (strpos($contentDisposition, 'filename') !== false) { // Not uploaded file. - $this->addFile($files, $name, new UploadedFile(null, 0, UPLOAD_ERR_NO_FILE)); + $stream = Stream::create(''); + $this->addFile($files, $name, new UploadedFile($stream, 0, UPLOAD_ERR_NO_FILE)); } else { // Regular field. $params[$name] = substr($body, 0, -2); From 60ce105fa5d6a1dd545646534771c4204eb66749 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Fri, 20 May 2022 11:58:09 +0300 Subject: [PATCH 08/18] Improved flash media handling in MediaIdentifier --- .../Grav/Framework/Media/MediaIdentifier.php | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/system/src/Grav/Framework/Media/MediaIdentifier.php b/system/src/Grav/Framework/Media/MediaIdentifier.php index 1551483e52..e9ac15a16e 100644 --- a/system/src/Grav/Framework/Media/MediaIdentifier.php +++ b/system/src/Grav/Framework/Media/MediaIdentifier.php @@ -51,13 +51,11 @@ public function getObject(): ?MediaObjectInterface $parts = explode('/', $id); if ($type === 'media' && str_starts_with($id, 'uploads/')) { array_shift($parts); - $type = array_shift($parts); - $uniqueId = array_shift($parts); - $field = array_shift($parts); - $filename = implode('/', $parts); + [, $folder, $uniqueId, $field, $filename] = $this->findFlash($parts); - $flash = $this->getFlash($type, $uniqueId); + $flash = $this->getFlash($folder, $uniqueId); if ($flash->exists()) { + $uploadedFile = $flash->getFilesByField($field)[$filename] ?? null; $this->object = UploadedMediaObject::createFromFlash($flash, $field, $filename, $uploadedFile); @@ -103,20 +101,35 @@ public function setObject(MediaObjectInterface $object): void $this->object = $object; } - protected function getFlash(string $type, string $uniqueId): FlexFormFlash + protected function findFlash(array $parts): ?array { - /** @var UserInterface|null $user */ - $user = Grav::instance()['user'] ?? null; - if (null !== $user && $user->exists()) { - // TODO: Modify uniqueid so we can detect if flash is user or session based. - $mediaFolder = $user->getMediaFolder(); + $type = array_shift($parts); + if ($type === 'account') { + /** @var UserInterface|null $user */ + $user = Grav::instance()['user'] ?? null; + $folder = $user->getMediaFolder(); } else { - // TODO: Implement session based flash. - throw new \RuntimeException('Not implemented'); + $folder = 'tmp://'; + } + + if (!$folder) { + return null; } - $folder = "{$mediaFolder}/tmp/api/flex-{$type}"; + do { + $part = array_shift($parts); + $folder .= "/{$part}"; + } while (!str_starts_with($part, 'flex-')); + + $uniqueId = array_shift($parts); + $field = array_shift($parts); + $filename = implode('/', $parts); + return [$type, $folder, $uniqueId, $field, $filename]; + } + + protected function getFlash(string $folder, string $uniqueId): FlexFormFlash + { $config = [ 'unique_id' => $uniqueId, 'folder' => $folder From 8dbc394ae7b471acc13004e2bdc19ae263e2b273 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Fri, 20 May 2022 16:42:06 +0300 Subject: [PATCH 09/18] Composer update --- composer.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.lock b/composer.lock index 803d3dc3ae..b0a0ef990e 100644 --- a/composer.lock +++ b/composer.lock @@ -974,16 +974,16 @@ }, { "name": "matthiasmullie/minify", - "version": "1.3.67", + "version": "1.3.68", "source": { "type": "git", "url": "https://github.com/matthiasmullie/minify.git", - "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9" + "reference": "c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", - "reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297", + "reference": "c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297", "shasum": "" }, "require": { @@ -1032,7 +1032,7 @@ ], "support": { "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.67" + "source": "https://github.com/matthiasmullie/minify/tree/1.3.68" }, "funding": [ { @@ -1040,7 +1040,7 @@ "type": "github" } ], - "time": "2022-03-24T08:54:59+00:00" + "time": "2022-04-19T08:28:56+00:00" }, { "name": "matthiasmullie/path-converter", @@ -4683,16 +4683,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.6.2", + "version": "1.6.8", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "becb9603a31d70f5007d505877a7b812598dfe46" + "reference": "d76498c5531232cb8386ceb6004f7e013138d3ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/becb9603a31d70f5007d505877a7b812598dfe46", - "reference": "becb9603a31d70f5007d505877a7b812598dfe46", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d76498c5531232cb8386ceb6004f7e013138d3ba", + "reference": "d76498c5531232cb8386ceb6004f7e013138d3ba", "shasum": "" }, "require": { @@ -4718,7 +4718,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.6.2" + "source": "https://github.com/phpstan/phpstan/tree/1.6.8" }, "funding": [ { @@ -4738,7 +4738,7 @@ "type": "tidelift" } ], - "time": "2022-04-27T11:05:24+00:00" + "time": "2022-05-10T06:54:21+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", From b9800b7c353dbef564df30c3b7112980d1fc2331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bogus=C5=82awski?= Date: Wed, 15 Jun 2022 18:38:59 +0200 Subject: [PATCH 10/18] Allow to configure syslog tag (#3596) * Allow to configure syslog tag Author-Change-Id: IB#1120629 * Update InitializeProcessor.php --- system/blueprints/config/system.yaml | 7 +++++++ system/config/system.yaml | 1 + system/src/Grav/Common/Processors/InitializeProcessor.php | 3 ++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/system/blueprints/config/system.yaml b/system/blueprints/config/system.yaml index 787fcd5775..09e80837d7 100644 --- a/system/blueprints/config/system.yaml +++ b/system/blueprints/config/system.yaml @@ -1156,6 +1156,13 @@ form: local6: local6 local7: local7 + log.syslog.tag: + type: text + size: small + label: PLUGIN_ADMIN.SYSLOG_TAG + help: PLUGIN_ADMIN.SYSLOG_TAG_HELP + placeholder: "grav" + debugger: type: tab title: PLUGIN_ADMIN.DEBUGGER diff --git a/system/config/system.yaml b/system/config/system.yaml index 380d650ebc..3be86d7d3b 100644 --- a/system/config/system.yaml +++ b/system/config/system.yaml @@ -144,6 +144,7 @@ log: handler: file # Log handler. Currently supported: file | syslog syslog: facility: local6 # Syslog facilities output + tag: grav # Syslog tag. Default: "grav". debugger: enabled: false # Enable Grav debugger and following settings diff --git a/system/src/Grav/Common/Processors/InitializeProcessor.php b/system/src/Grav/Common/Processors/InitializeProcessor.php index 04e06edf32..24f4cf960d 100644 --- a/system/src/Grav/Common/Processors/InitializeProcessor.php +++ b/system/src/Grav/Common/Processors/InitializeProcessor.php @@ -251,7 +251,8 @@ protected function initializeLogger(Config $config): Logger $log->popHandler(); $facility = $config->get('system.log.syslog.facility', 'local6'); - $logHandler = new SyslogHandler('grav', $facility); + $tag = $config->get('system.log.syslog.tag', 'grav'); + $logHandler = new SyslogHandler($tag, $facility); $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%"); $logHandler->setFormatter($formatter); From 00cb9c3540be379810153220b024e1c1337821e2 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Mon, 13 Jun 2022 17:25:32 +0300 Subject: [PATCH 11/18] Make the new relationships code to work in PHP 7.3 --- composer.json | 4 +- composer.lock | 177 +++++++++--------- .../Media/Interfaces/MediaObjectInterface.php | 27 --- .../RelationshipIdentifierInterface.php | 2 +- .../Relationships/RelationshipInterface.php | 1 + .../Relationships/RelationshipsInterface.php | 9 +- .../Grav/Framework/Flex/FlexIdentifier.php | 9 +- .../Framework/Flex/Traits/FlexMediaTrait.php | 5 +- .../Flex/Traits/FlexRelationshipsTrait.php | 3 +- .../Media/Interfaces/MediaObjectInterface.php | 30 +++ .../Grav/Framework/Media/MediaIdentifier.php | 6 +- .../src/Grav/Framework/Media/MediaObject.php | 20 +- .../Framework/Media/UploadedMediaObject.php | 28 ++- .../Object/Identifiers/Identifier.php | 6 +- .../Framework/Relationships/Relationships.php | 24 ++- .../Relationships/ToManyRelationship.php | 8 +- .../Relationships/ToOneRelationship.php | 8 +- .../Traits/RelationshipTrait.php | 22 ++- 18 files changed, 215 insertions(+), 174 deletions(-) diff --git a/composer.json b/composer.json index 8cc4ff00c6..5855970e53 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "homepage": "https://getgrav.org", "license": "MIT", "require": { - "php": "^7.4.1 || ^8.0", + "php": "^7.3.6 || ^8.0", "ext-json": "*", "ext-openssl": "*", "ext-curl": "*", @@ -89,7 +89,7 @@ "config": { "apcu-autoloader": true, "platform": { - "php": "7.4.1" + "php": "7.3.6" } }, "autoload": { diff --git a/composer.lock b/composer.lock index 29d0bda101..739da13370 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7645535ce7348e38bad6dad9ce8b2fdb", + "content-hash": "f0530b0fd3e574fef0852376653da5a0", "packages": [ { "name": "composer/ca-bundle", @@ -731,16 +731,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.8.5", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "337e3ad8e5716c15f9657bd214d16cc5e69df268" + "reference": "e98e3e6d4f86621a9b75f623996e6bbdeb4b9318" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/337e3ad8e5716c15f9657bd214d16cc5e69df268", - "reference": "337e3ad8e5716c15f9657bd214d16cc5e69df268", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/e98e3e6d4f86621a9b75f623996e6bbdeb4b9318", + "reference": "e98e3e6d4f86621a9b75f623996e6bbdeb4b9318", "shasum": "" }, "require": { @@ -761,7 +761,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7-dev" + "dev-master": "1.9-dev" } }, "autoload": { @@ -821,7 +821,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.8.5" + "source": "https://github.com/guzzle/psr7/tree/1.9.0" }, "funding": [ { @@ -837,7 +837,7 @@ "type": "tidelift" } ], - "time": "2022-03-20T21:51:18+00:00" + "time": "2022-06-20T21:43:03+00:00" }, { "name": "itsgoingd/clockwork", @@ -909,27 +909,27 @@ }, { "name": "league/climate", - "version": "3.8.1", + "version": "3.8.2", "source": { "type": "git", "url": "https://github.com/thephpleague/climate.git", - "reference": "22243322c6f049240e0fa6ad6c3873343b6f6055" + "reference": "a785a3ac8f584eed4abd45e4e16fe64c46659a28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/climate/zipball/22243322c6f049240e0fa6ad6c3873343b6f6055", - "reference": "22243322c6f049240e0fa6ad6c3873343b6f6055", + "url": "https://api.github.com/repos/thephpleague/climate/zipball/a785a3ac8f584eed4abd45e4e16fe64c46659a28", + "reference": "a785a3ac8f584eed4abd45e4e16fe64c46659a28", "shasum": "" }, "require": { "php": "^7.3 || ^8.0", - "psr/log": "^1.0", + "psr/log": "^1.0 || ^2.0 || ^3.0", "seld/cli-prompt": "^1.0" }, "require-dev": { - "mikey179/vfsstream": "^1.4", + "mikey179/vfsstream": "^1.6.10", "mockery/mockery": "^1.4.2", - "phpunit/phpunit": "^9.5.0" + "phpunit/phpunit": "^9.5.10" }, "suggest": { "ext-mbstring": "If ext-mbstring is not available you MUST install symfony/polyfill-mbstring" @@ -968,9 +968,9 @@ ], "support": { "issues": "https://github.com/thephpleague/climate/issues", - "source": "https://github.com/thephpleague/climate/tree/3.8.1" + "source": "https://github.com/thephpleague/climate/tree/3.8.2" }, - "time": "2022-01-23T14:38:49+00:00" + "time": "2022-06-18T14:42:08+00:00" }, { "name": "matthiasmullie/minify", @@ -1356,16 +1356,16 @@ }, { "name": "nyholm/psr7", - "version": "1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/Nyholm/psr7.git", - "reference": "1461e07a0f2a975a52082ca3b769ca912b816226" + "reference": "f734364e38a876a23be4d906a2a089e1315be18a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Nyholm/psr7/zipball/1461e07a0f2a975a52082ca3b769ca912b816226", - "reference": "1461e07a0f2a975a52082ca3b769ca912b816226", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/f734364e38a876a23be4d906a2a089e1315be18a", + "reference": "f734364e38a876a23be4d906a2a089e1315be18a", "shasum": "" }, "require": { @@ -1417,7 +1417,7 @@ ], "support": { "issues": "https://github.com/Nyholm/psr7/issues", - "source": "https://github.com/Nyholm/psr7/tree/1.5.0" + "source": "https://github.com/Nyholm/psr7/tree/1.5.1" }, "funding": [ { @@ -1429,7 +1429,7 @@ "type": "github" } ], - "time": "2022-02-02T18:37:57+00:00" + "time": "2022-06-22T07:13:36+00:00" }, { "name": "nyholm/psr7-server", @@ -1655,20 +1655,20 @@ }, { "name": "psr/container", - "version": "1.1.2", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { - "php": ">=7.4.0" + "php": ">=7.2.0" }, "type": "library", "autoload": { @@ -1697,9 +1697,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "source": "https://github.com/php-fig/container/tree/1.1.1" }, - "time": "2021-11-05T16:50:12+00:00" + "time": "2021-03-05T17:36:06+00:00" }, { "name": "psr/http-factory", @@ -2224,16 +2224,16 @@ }, { "name": "symfony/console", - "version": "v4.4.42", + "version": "v4.4.43", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cce7a9f99e22937a71a16b23afa762558808d587" + "reference": "8a2628d2d5639f35113dc1b833ecd91e1ed1cf46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cce7a9f99e22937a71a16b23afa762558808d587", - "reference": "cce7a9f99e22937a71a16b23afa762558808d587", + "url": "https://api.github.com/repos/symfony/console/zipball/8a2628d2d5639f35113dc1b833ecd91e1ed1cf46", + "reference": "8a2628d2d5639f35113dc1b833ecd91e1ed1cf46", "shasum": "" }, "require": { @@ -2294,7 +2294,7 @@ "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/console/tree/v4.4.42" + "source": "https://github.com/symfony/console/tree/v4.4.43" }, "funding": [ { @@ -2310,20 +2310,20 @@ "type": "tidelift" } ], - "time": "2022-05-14T12:35:33+00:00" + "time": "2022-06-23T12:22:25+00:00" }, { "name": "symfony/contracts", - "version": "v1.1.12", + "version": "v1.1.13", "source": { "type": "git", "url": "https://github.com/symfony/contracts.git", - "reference": "5236c15b24aeeecee4b9c6ad4b22f6331f2cbdcb" + "reference": "9e27f5c175ecbd6fff554d839ff4a432da797168" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/contracts/zipball/5236c15b24aeeecee4b9c6ad4b22f6331f2cbdcb", - "reference": "5236c15b24aeeecee4b9c6ad4b22f6331f2cbdcb", + "url": "https://api.github.com/repos/symfony/contracts/zipball/9e27f5c175ecbd6fff554d839ff4a432da797168", + "reference": "9e27f5c175ecbd6fff554d839ff4a432da797168", "shasum": "" }, "require": { @@ -2388,7 +2388,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/contracts/tree/v1.1.12" + "source": "https://github.com/symfony/contracts/tree/v1.1.13" }, "funding": [ { @@ -2404,7 +2404,7 @@ "type": "tidelift" } ], - "time": "2022-03-09T17:53:12+00:00" + "time": "2022-06-27T13:16:42+00:00" }, { "name": "symfony/event-dispatcher", @@ -3214,16 +3214,16 @@ }, { "name": "symfony/yaml", - "version": "v4.4.37", + "version": "v4.4.43", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "d7f637cc0f0cc14beb0984f2bb50da560b271311" + "reference": "07e392f0ef78376d080d5353c081a5e5704835bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/d7f637cc0f0cc14beb0984f2bb50da560b271311", - "reference": "d7f637cc0f0cc14beb0984f2bb50da560b271311", + "url": "https://api.github.com/repos/symfony/yaml/zipball/07e392f0ef78376d080d5353c081a5e5704835bd", + "reference": "07e392f0ef78376d080d5353c081a5e5704835bd", "shasum": "" }, "require": { @@ -3265,7 +3265,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v4.4.37" + "source": "https://github.com/symfony/yaml/tree/v4.4.43" }, "funding": [ { @@ -3281,7 +3281,7 @@ "type": "tidelift" } ], - "time": "2022-01-24T20:11:01+00:00" + "time": "2022-06-20T08:31:17+00:00" }, { "name": "twig/twig", @@ -3484,16 +3484,16 @@ }, { "name": "codeception/codeception", - "version": "4.1.31", + "version": "4.2.1", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "15524571ae0686a7facc2eb1f40f600e5bbce9e5" + "reference": "77b3e2003fd4446b35826cb9dc397129c521c888" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/15524571ae0686a7facc2eb1f40f600e5bbce9e5", - "reference": "15524571ae0686a7facc2eb1f40f600e5bbce9e5", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/77b3e2003fd4446b35826cb9dc397129c521c888", + "reference": "77b3e2003fd4446b35826cb9dc397129c521c888", "shasum": "" }, "require": { @@ -3556,11 +3556,11 @@ { "name": "Michael Bodnarchuk", "email": "davert@mail.ua", - "homepage": "http://codegyre.com" + "homepage": "https://codegyre.com" } ], "description": "BDD-style testing framework", - "homepage": "http://codeception.com/", + "homepage": "https://codeception.com/", "keywords": [ "BDD", "TDD", @@ -3570,7 +3570,7 @@ ], "support": { "issues": "https://github.com/Codeception/Codeception/issues", - "source": "https://github.com/Codeception/Codeception/tree/4.1.31" + "source": "https://github.com/Codeception/Codeception/tree/4.2.1" }, "funding": [ { @@ -3578,7 +3578,7 @@ "type": "open_collective" } ], - "time": "2022-03-13T17:07:08+00:00" + "time": "2022-06-22T06:18:59+00:00" }, { "name": "codeception/lib-asserts", @@ -3862,24 +3862,20 @@ }, { "name": "codeception/stub", - "version": "4.0.2", + "version": "3.7.0", "source": { "type": "git", "url": "https://github.com/Codeception/Stub.git", - "reference": "18a148dacd293fc7b044042f5aa63a82b08bff5d" + "reference": "468dd5fe659f131fc997f5196aad87512f9b1304" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/18a148dacd293fc7b044042f5aa63a82b08bff5d", - "reference": "18a148dacd293fc7b044042f5aa63a82b08bff5d", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/468dd5fe659f131fc997f5196aad87512f9b1304", + "reference": "468dd5fe659f131fc997f5196aad87512f9b1304", "shasum": "" }, "require": { - "php": "^7.4 | ^8.0", - "phpunit/phpunit": "^8.4 | ^9.0 | ^10.0 | 10.0.x-dev" - }, - "require-dev": { - "consolidation/robo": "^3.0" + "phpunit/phpunit": "^8.4 | ^9.0" }, "type": "library", "autoload": { @@ -3894,9 +3890,9 @@ "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", "support": { "issues": "https://github.com/Codeception/Stub/issues", - "source": "https://github.com/Codeception/Stub/tree/4.0.2" + "source": "https://github.com/Codeception/Stub/tree/3.7.0" }, - "time": "2022-01-31T19:25:15+00:00" + "time": "2020-07-03T15:54:43+00:00" }, { "name": "doctrine/instantiator", @@ -4022,22 +4018,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.4.4", + "version": "7.4.5", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "e3ff079b22820c2029d4c2a87796b6a0b8716ad8" + "reference": "1dd98b0564cb3f6bd16ce683cb755f94c10fbd82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/e3ff079b22820c2029d4c2a87796b6a0b8716ad8", - "reference": "e3ff079b22820c2029d4c2a87796b6a0b8716ad8", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1dd98b0564cb3f6bd16ce683cb755f94c10fbd82", + "reference": "1dd98b0564cb3f6bd16ce683cb755f94c10fbd82", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/promises": "^1.5", - "guzzlehttp/psr7": "^1.8.3 || ^2.1", + "guzzlehttp/psr7": "^1.9 || ^2.4", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -4126,7 +4122,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.4.4" + "source": "https://github.com/guzzle/guzzle/tree/7.4.5" }, "funding": [ { @@ -4142,7 +4138,7 @@ "type": "tidelift" } ], - "time": "2022-06-09T21:39:15+00:00" + "time": "2022-06-20T22:16:13+00:00" }, { "name": "guzzlehttp/promises", @@ -4683,16 +4679,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.7.14", + "version": "1.7.15", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "e6f145f196a59c7ca91ea926c87ef3d936c4305f" + "reference": "cd0202ea1b1fc6d1bbe156c6e2e18a03e0ff160a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e6f145f196a59c7ca91ea926c87ef3d936c4305f", - "reference": "e6f145f196a59c7ca91ea926c87ef3d936c4305f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd0202ea1b1fc6d1bbe156c6e2e18a03e0ff160a", + "reference": "cd0202ea1b1fc6d1bbe156c6e2e18a03e0ff160a", "shasum": "" }, "require": { @@ -4718,7 +4714,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.7.14" + "source": "https://github.com/phpstan/phpstan/tree/1.7.15" }, "funding": [ { @@ -4738,7 +4734,7 @@ "type": "tidelift" } ], - "time": "2022-06-14T13:09:35+00:00" + "time": "2022-06-20T08:29:01+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -5110,16 +5106,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.20", + "version": "9.5.21", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" + "reference": "0e32b76be457de00e83213528f6bb37e2a38fcb1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0e32b76be457de00e83213528f6bb37e2a38fcb1", + "reference": "0e32b76be457de00e83213528f6bb37e2a38fcb1", "shasum": "" }, "require": { @@ -5153,7 +5149,6 @@ "sebastian/version": "^3.0.2" }, "require-dev": { - "ext-pdo": "*", "phpspec/prophecy-phpunit": "^2.0.1" }, "suggest": { @@ -5197,7 +5192,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.21" }, "funding": [ { @@ -5209,7 +5204,7 @@ "type": "github" } ], - "time": "2022-04-01T12:37:26+00:00" + "time": "2022-06-19T12:14:25+00:00" }, { "name": "psr/http-client", @@ -6367,7 +6362,7 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v2.5.1", + "version": "v2.5.2", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", @@ -6414,7 +6409,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2" }, "funding": [ { @@ -6685,7 +6680,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^7.4.1 || ^8.0", + "php": "^7.3.6 || ^8.0", "ext-json": "*", "ext-openssl": "*", "ext-curl": "*", @@ -6695,7 +6690,7 @@ }, "platform-dev": [], "platform-overrides": { - "php": "7.4.1" + "php": "7.3.6" }, "plugin-api-version": "2.2.0" } diff --git a/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php b/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php index d379d81e09..912a7c417c 100644 --- a/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php +++ b/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php @@ -50,13 +50,6 @@ public function setTimestamp($timestamp = null); */ public function metadata(); - /** - * Returns an array containing the file metadata - * - * @return array - */ - public function getMeta(); - /** * Add meta file for the medium. * @@ -165,14 +158,6 @@ public function thumbnailExists($type = 'page'); */ public function thumbnail($type = 'auto'); - /** - * Return URL to file. - * - * @param bool $reset - * @return string - */ - public function url($reset = true); - /** * Turn the current Medium into a Link * @@ -228,18 +213,6 @@ public function style($style); #[\ReturnTypeWillChange] public function __call($method, $args); - /** - * Get value by using dot notation for nested arrays/objects. - * - * @example $value = $this->get('this.is.my.nested.variable'); - * - * @param string $name Dot separated path to the requested value. - * @param mixed $default Default value (or null). - * @param string|null $separator Separator, defaults to '.' - * @return mixed Value. - */ - public function get($name, $default = null, $separator = null); - /** * Set value by using dot notation for nested arrays/objects. * diff --git a/system/src/Grav/Framework/Contracts/Relationships/RelationshipIdentifierInterface.php b/system/src/Grav/Framework/Contracts/Relationships/RelationshipIdentifierInterface.php index 027b5bbb12..3d7ea4a046 100644 --- a/system/src/Grav/Framework/Contracts/Relationships/RelationshipIdentifierInterface.php +++ b/system/src/Grav/Framework/Contracts/Relationships/RelationshipIdentifierInterface.php @@ -21,7 +21,7 @@ public function hasIdentifierMeta(): bool; /** * Get identifier meta. * - * @return array|ArrayAccess + * @return array|ArrayAccess * @phpstan-pure */ public function getIdentifierMeta(); diff --git a/system/src/Grav/Framework/Contracts/Relationships/RelationshipInterface.php b/system/src/Grav/Framework/Contracts/Relationships/RelationshipInterface.php index 6a693163d6..c0a7edfe40 100644 --- a/system/src/Grav/Framework/Contracts/Relationships/RelationshipInterface.php +++ b/system/src/Grav/Framework/Contracts/Relationships/RelationshipInterface.php @@ -13,6 +13,7 @@ * * @template T of IdentifierInterface * @template P of IdentifierInterface + * @extends IteratorAggregate */ interface RelationshipInterface extends Countable, IteratorAggregate, JsonSerializable, Serializable { diff --git a/system/src/Grav/Framework/Contracts/Relationships/RelationshipsInterface.php b/system/src/Grav/Framework/Contracts/Relationships/RelationshipsInterface.php index 5e42cdf088..4bd90a30b9 100644 --- a/system/src/Grav/Framework/Contracts/Relationships/RelationshipsInterface.php +++ b/system/src/Grav/Framework/Contracts/Relationships/RelationshipsInterface.php @@ -9,6 +9,11 @@ /** * Interface RelationshipsInterface + * + * @template T of \Grav\Framework\Contracts\Object\IdentifierInterface + * @template P of \Grav\Framework\Contracts\Object\IdentifierInterface + * @extends ArrayAccess> + * @extends Iterator> */ interface RelationshipsInterface extends Countable, ArrayAccess, Iterator, JsonSerializable { @@ -31,12 +36,12 @@ public function count(): int; /** * @param string $offset - * @return RelationshipInterface|null + * @return RelationshipInterface|null */ public function offsetGet($offset): ?RelationshipInterface; /** - * @return RelationshipInterface|null + * @return RelationshipInterface|null */ public function current(): ?RelationshipInterface; diff --git a/system/src/Grav/Framework/Flex/FlexIdentifier.php b/system/src/Grav/Framework/Flex/FlexIdentifier.php index 0643dbde87..ec47ed8c97 100644 --- a/system/src/Grav/Framework/Flex/FlexIdentifier.php +++ b/system/src/Grav/Framework/Flex/FlexIdentifier.php @@ -11,15 +11,18 @@ * Interface IdentifierInterface * * @template T of FlexObjectInterface + * @extends Identifier */ class FlexIdentifier extends Identifier { - private string $keyField; - private ?FlexObjectInterface $object = null; + /** @var string */ + private $keyField; + /** @var FlexObjectInterface|null */ + private $object = null; /** * @param FlexObjectInterface $object - * @return FlexIdentifier + * @return FlexIdentifier */ public static function createFromObject(FlexObjectInterface $object): FlexIdentifier { diff --git a/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php b/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php index f96b09eed1..4eb015a4fe 100644 --- a/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php +++ b/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php @@ -12,7 +12,6 @@ use Grav\Common\Config\Config; use Grav\Common\Grav; use Grav\Common\Media\Interfaces\MediaCollectionInterface; -use Grav\Common\Media\Interfaces\MediaObjectInterface; use Grav\Common\Media\Interfaces\MediaUploadInterface; use Grav\Common\Media\Traits\MediaTrait; use Grav\Common\Page\Media; @@ -23,6 +22,7 @@ use Grav\Framework\Filesystem\Filesystem; use Grav\Framework\Flex\FlexDirectory; use Grav\Framework\Form\FormFlashFile; +use Grav\Framework\Media\Interfaces\MediaObjectInterface; use Grav\Framework\Media\MediaObject; use Grav\Framework\Media\UploadedMediaObject; use Psr\Http\Message\UploadedFileInterface; @@ -336,8 +336,9 @@ protected function buildMediaList(?string $field): array if ($medium) { if ($medium->uploaded_file) { $upload = $medium->uploaded_file; + $id = $upload->getId(); - $list[] = new UploadedMediaObject($upload->getId(), $field, $name, $upload); + $list[] = new UploadedMediaObject($id, $field, $name, $upload); } else { $list[] = $this->buildMediaObject($field, $name, $medium); } diff --git a/system/src/Grav/Framework/Flex/Traits/FlexRelationshipsTrait.php b/system/src/Grav/Framework/Flex/Traits/FlexRelationshipsTrait.php index 03c838201b..2a73ebaa03 100644 --- a/system/src/Grav/Framework/Flex/Traits/FlexRelationshipsTrait.php +++ b/system/src/Grav/Framework/Flex/Traits/FlexRelationshipsTrait.php @@ -12,7 +12,8 @@ */ trait FlexRelationshipsTrait { - private ?RelationshipsInterface $_relationships = null; + /** @var RelationshipsInterface|null */ + private $_relationships = null; /** * @return Relationships diff --git a/system/src/Grav/Framework/Media/Interfaces/MediaObjectInterface.php b/system/src/Grav/Framework/Media/Interfaces/MediaObjectInterface.php index f4409c5c98..39a063f723 100644 --- a/system/src/Grav/Framework/Media/Interfaces/MediaObjectInterface.php +++ b/system/src/Grav/Framework/Media/Interfaces/MediaObjectInterface.php @@ -9,9 +9,39 @@ namespace Grav\Framework\Media\Interfaces; +use Psr\Http\Message\UploadedFileInterface; + /** * Class implements media object interface. + * + * @property UploadedFileInterface|null $uploaded_file */ interface MediaObjectInterface { + /** + * Returns an array containing the file metadata + * + * @return array + */ + public function getMeta(); + + /** + * Return URL to file. + * + * @param bool $reset + * @return string + */ + public function url($reset = true); + + /** + * Get value by using dot notation for nested arrays/objects. + * + * @example $value = $this->get('this.is.my.nested.variable'); + * + * @param string $name Dot separated path to the requested value. + * @param mixed $default Default value (or null). + * @param string|null $separator Separator, defaults to '.' + * @return mixed Value. + */ + public function get($name, $default = null, $separator = null); } diff --git a/system/src/Grav/Framework/Media/MediaIdentifier.php b/system/src/Grav/Framework/Media/MediaIdentifier.php index e9ac15a16e..986e997c70 100644 --- a/system/src/Grav/Framework/Media/MediaIdentifier.php +++ b/system/src/Grav/Framework/Media/MediaIdentifier.php @@ -14,14 +14,16 @@ * Interface IdentifierInterface * * @template T of MediaObjectInterface + * @extends Identifier */ class MediaIdentifier extends Identifier { - private ?MediaObjectInterface $object = null; + /** @var MediaObjectInterface|null */ + private $object = null; /** * @param MediaObjectInterface $object - * @return MediaIdentifier + * @return MediaIdentifier */ public static function createFromObject(MediaObjectInterface $object): MediaIdentifier { diff --git a/system/src/Grav/Framework/Media/MediaObject.php b/system/src/Grav/Framework/Media/MediaObject.php index 0c3c98d84a..8a438bf93a 100644 --- a/system/src/Grav/Framework/Media/MediaObject.php +++ b/system/src/Grav/Framework/Media/MediaObject.php @@ -2,11 +2,12 @@ namespace Grav\Framework\Media; -use Grav\Common\Media\Interfaces\MediaObjectInterface as GravMediaObjectInterface; use Grav\Common\Page\Medium\ImageMedium; use Grav\Framework\Contracts\Media\MediaObjectInterface; use Grav\Framework\Flex\Interfaces\FlexObjectInterface; +use Grav\Framework\Media\Interfaces\MediaObjectInterface as GravMediaObjectInterface; use Grav\Framework\Psr7\Response; +use Psr\Http\Message\ResponseInterface; use Throwable; /** @@ -14,13 +15,18 @@ */ class MediaObject implements MediaObjectInterface { - static public string $placeholderImage = 'image://media/thumb.png'; + /** @var string */ + static public $placeholderImage = 'image://media/thumb.png'; - public FlexObjectInterface $object; - public ?GravMediaObjectInterface $media; + /** @var FlexObjectInterface */ + public $object; + /** @var GravMediaObjectInterface|null */ + public $media; - private ?string $field; - private string $filename; + /** @var string|null */ + private $field; + /** @var string */ + private $filename; /** * MediaObject constructor. @@ -108,7 +114,7 @@ public function getUrl(): string * @param array $actions * @return Response */ - public function createResponse(array $actions): Response + public function createResponse(array $actions): ResponseInterface { if (!isset($this->media)) { return $this->create404Response($actions); diff --git a/system/src/Grav/Framework/Media/UploadedMediaObject.php b/system/src/Grav/Framework/Media/UploadedMediaObject.php index 113440a934..0fe12e1cc0 100644 --- a/system/src/Grav/Framework/Media/UploadedMediaObject.php +++ b/system/src/Grav/Framework/Media/UploadedMediaObject.php @@ -6,6 +6,7 @@ use Grav\Framework\Flex\FlexFormFlash; use Grav\Framework\Form\Interfaces\FormFlashInterface; use Grav\Framework\Psr7\Response; +use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\UploadedFileInterface; /** @@ -13,15 +14,22 @@ */ class UploadedMediaObject implements MediaObjectInterface { - static public string $placeholderImage = 'image://media/thumb.png'; - - public FormFlashInterface $object; - - private string $id; - private ?string $field; - private string $filename; - private array $meta; - private ?UploadedFileInterface $uploadedFile; + /** @var string */ + static public $placeholderImage = 'image://media/thumb.png'; + + /** @var FormFlashInterface */ + public $object; + + /** @var string */ + private $id; + /** @var string|null */ + private $field; + /** @var string */ + private $filename; + /** @var array */ + private $meta; + /** @var UploadedFileInterface|null */ + private $uploadedFile; /** * @param FlexFormFlash $flash @@ -126,7 +134,7 @@ public function getUploadedFile(): ?UploadedFileInterface * @param array $actions * @return Response */ - public function createResponse(array $actions): Response + public function createResponse(array $actions): ResponseInterface { // Display placeholder image. $filename = static::$placeholderImage; diff --git a/system/src/Grav/Framework/Object/Identifiers/Identifier.php b/system/src/Grav/Framework/Object/Identifiers/Identifier.php index 23e4b56c02..69f41d2b87 100644 --- a/system/src/Grav/Framework/Object/Identifiers/Identifier.php +++ b/system/src/Grav/Framework/Object/Identifiers/Identifier.php @@ -11,8 +11,10 @@ */ class Identifier implements IdentifierInterface { - private string $id; - private string $type; + /** @var string */ + private $id; + /** @var string */ + private $type; /** * IdentifierInterface constructor. diff --git a/system/src/Grav/Framework/Relationships/Relationships.php b/system/src/Grav/Framework/Relationships/Relationships.php index 84fa79cee0..6485682048 100644 --- a/system/src/Grav/Framework/Relationships/Relationships.php +++ b/system/src/Grav/Framework/Relationships/Relationships.php @@ -11,18 +11,24 @@ /** * Class Relationships + * + * @template T of \Grav\Framework\Contracts\Object\IdentifierInterface + * @template P of \Grav\Framework\Contracts\Object\IdentifierInterface + * @implements RelationshipsInterface */ class Relationships implements RelationshipsInterface { - protected IdentifierInterface $parent; - protected array $options; + /** @var P */ + protected $parent; + /** @var array */ + protected $options; - /** @var RelationshipInterface[] */ - protected array $relationships; + /** @var RelationshipInterface[] */ + protected $relationships; /** * Relationships constructor. - * @param IdentifierInterface $parent + * @param P $parent * @param array $options */ public function __construct(IdentifierInterface $parent, array $options) @@ -42,7 +48,7 @@ public function isModified(): bool } /** - * @return RelationshipInterface[] + * @return RelationshipInterface[] * @phpstan-pure */ public function getModified(): array @@ -78,7 +84,7 @@ public function offsetExists($offset): bool /** * @param string $offset - * @return RelationshipInterface|null + * @return RelationshipInterface|null */ public function offsetGet($offset): ?RelationshipInterface { @@ -114,7 +120,7 @@ public function offsetUnset($offset) } /** - * @return RelationshipInterface|null + * @return RelationshipInterface|null */ public function current(): ?RelationshipInterface { @@ -178,7 +184,7 @@ public function jsonSerialize(): array /** * @param string $name * @param array $options - * @return RelationshipInterface + * @return ToOneRelationship|ToManyRelationship */ private function createRelationship(string $name, array $options): RelationshipInterface { diff --git a/system/src/Grav/Framework/Relationships/ToManyRelationship.php b/system/src/Grav/Framework/Relationships/ToManyRelationship.php index 26b138e43f..3ea501b29c 100644 --- a/system/src/Grav/Framework/Relationships/ToManyRelationship.php +++ b/system/src/Grav/Framework/Relationships/ToManyRelationship.php @@ -13,9 +13,9 @@ /** * Class ToManyRelationship * - * @template T of object - * @template P of object - * @template-implements ToManyRelationshipInterface + * @template T of IdentifierInterface + * @template P of IdentifierInterface + * @template-implements ToManyRelationshipInterface */ class ToManyRelationship implements ToManyRelationshipInterface { @@ -24,7 +24,7 @@ class ToManyRelationship implements ToManyRelationshipInterface use Serializable; /** @var IdentifierInterface[] */ - protected array $identifiers = []; + protected $identifiers = []; /** * ToManyRelationship constructor. diff --git a/system/src/Grav/Framework/Relationships/ToOneRelationship.php b/system/src/Grav/Framework/Relationships/ToOneRelationship.php index a34ca046a9..9b09651430 100644 --- a/system/src/Grav/Framework/Relationships/ToOneRelationship.php +++ b/system/src/Grav/Framework/Relationships/ToOneRelationship.php @@ -12,8 +12,9 @@ /** * Class ToOneRelationship * - * @template T of object - * @template-implements ToOneRelationshipInterface + * @template T of IdentifierInterface + * @template P of IdentifierInterface + * @template-implements ToOneRelationshipInterface */ class ToOneRelationship implements ToOneRelationshipInterface { @@ -21,7 +22,8 @@ class ToOneRelationship implements ToOneRelationshipInterface use RelationshipTrait; use Serializable; - protected ?IdentifierInterface $identifier = null; + /** @var IdentifierInterface|null */ + protected $identifier = null; public function __construct(IdentifierInterface $parent, string $name, array $options, IdentifierInterface $identifier = null) { diff --git a/system/src/Grav/Framework/Relationships/Traits/RelationshipTrait.php b/system/src/Grav/Framework/Relationships/Traits/RelationshipTrait.php index 276b96b05d..dbe146fcc3 100644 --- a/system/src/Grav/Framework/Relationships/Traits/RelationshipTrait.php +++ b/system/src/Grav/Framework/Relationships/Traits/RelationshipTrait.php @@ -6,6 +6,7 @@ use Grav\Framework\Flex\FlexIdentifier; use Grav\Framework\Media\MediaIdentifier; use Grav\Framework\Object\Identifiers\Identifier; +use RuntimeException; use function get_class; /** @@ -15,11 +16,16 @@ */ trait RelationshipTrait { - protected IdentifierInterface $parent; - protected string $name; - protected string $type; - protected array $options; - protected bool $modified = false; + /** @var IdentifierInterface */ + protected $parent; + /** @var string */ + protected $name; + /** @var string */ + protected $type; + /** @var array */ + protected $options; + /** @var bool */ + protected $modified = false; /** * @return string @@ -85,10 +91,10 @@ public function check(): void if ($min || $max) { $count = $this->count(); if ($min && $count < $min) { - throw new \RuntimeException(sprintf('%s relationship has too few objects in it', $this->name)); + throw new RuntimeException(sprintf('%s relationship has too few objects in it', $this->name)); } if ($max && $count > $max) { - throw new \RuntimeException(sprintf('%s relationship has too many objects in it', $this->name)); + throw new RuntimeException(sprintf('%s relationship has too many objects in it', $this->name)); } } } @@ -100,7 +106,7 @@ public function check(): void private function checkIdentifier(IdentifierInterface $identifier): IdentifierInterface { if ($this->type !== $identifier->getType()) { - throw new \RuntimeException(sprintf('Bad identifier type %s', $identifier->getType())); + throw new RuntimeException(sprintf('Bad identifier type %s', $identifier->getType())); } if (get_class($identifier) !== Identifier::class) { From 1b0c3d1fcef5b346a3b00e66ba9ba0a6df8d4400 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Wed, 29 Jun 2022 13:06:43 +0300 Subject: [PATCH 12/18] Fix phpstan error --- system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php b/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php index 4eb015a4fe..0f55595ccc 100644 --- a/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php +++ b/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php @@ -336,7 +336,7 @@ protected function buildMediaList(?string $field): array if ($medium) { if ($medium->uploaded_file) { $upload = $medium->uploaded_file; - $id = $upload->getId(); + $id = $upload instanceof FormFlashFile ? $upload->getId() : "{$field}-{$name}"; $list[] = new UploadedMediaObject($id, $field, $name, $upload); } else { @@ -426,7 +426,7 @@ protected function addUpdatedMedia(MediaCollectionInterface $media): void $updated = false; foreach ($this->getUpdatedMedia() as $filename => $upload) { if (is_array($upload)) { - // Uses new format with [UploadedFileInterface, array]. + /** @var array{UploadedFileInterface,array} $upload */ $settings = $upload[1]; if (isset($settings['destination']) && $settings['destination'] === $media->getPath()) { $upload = $upload[0]; From dac1614306ff5d24e86aacaf1cf543caba9312d0 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Wed, 29 Jun 2022 13:10:26 +0300 Subject: [PATCH 13/18] Composer update --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 5855970e53..d1696285a1 100644 --- a/composer.json +++ b/composer.json @@ -64,7 +64,7 @@ }, "require-dev": { "codeception/codeception": "^4.1", - "phpstan/phpstan": "^1.2", + "phpstan/phpstan": "^1.8", "phpstan/phpstan-deprecation-rules": "^1.0", "phpunit/php-code-coverage": "~9.2", "getgrav/markdowndocs": "^2.0", diff --git a/composer.lock b/composer.lock index 739da13370..c445419b37 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f0530b0fd3e574fef0852376653da5a0", + "content-hash": "635351f885bc7b8223ce14d79c7a0daf", "packages": [ { "name": "composer/ca-bundle", @@ -4679,16 +4679,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.7.15", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "cd0202ea1b1fc6d1bbe156c6e2e18a03e0ff160a" + "reference": "b7648d4ee9321665acaf112e49da9fd93df8fbd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd0202ea1b1fc6d1bbe156c6e2e18a03e0ff160a", - "reference": "cd0202ea1b1fc6d1bbe156c6e2e18a03e0ff160a", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b7648d4ee9321665acaf112e49da9fd93df8fbd5", + "reference": "b7648d4ee9321665acaf112e49da9fd93df8fbd5", "shasum": "" }, "require": { @@ -4714,7 +4714,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.7.15" + "source": "https://github.com/phpstan/phpstan/tree/1.8.0" }, "funding": [ { @@ -4734,7 +4734,7 @@ "type": "tidelift" } ], - "time": "2022-06-20T08:29:01+00:00" + "time": "2022-06-29T08:53:31+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", From ec3175fc896f4666b12fda20b87d1f23b254077e Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Wed, 29 Jun 2022 13:11:14 +0300 Subject: [PATCH 14/18] Changelog update --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e9ba45821..e60344f0d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,9 @@ -# v1.NEXT +# v1.7.35 ## mm/dd/2022 1. [](#new) * Added support for `multipart/form-data` content type in PUT and PATCH requests - * Added support for flex object relationships + * Added support for object relationships # v1.7.34 ## 06/14/2022 From 32dd550178e79a0aa51cdbedae552f14b72f0926 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Wed, 29 Jun 2022 21:32:50 +0300 Subject: [PATCH 15/18] Fixed FlexDirectory::reloadIndex() to actually get the items from the filesystem --- system/src/Grav/Framework/Flex/FlexDirectory.php | 1 + 1 file changed, 1 insertion(+) diff --git a/system/src/Grav/Framework/Flex/FlexDirectory.php b/system/src/Grav/Framework/Flex/FlexDirectory.php index 04f5f0cfbc..dce8bf5230 100644 --- a/system/src/Grav/Framework/Flex/FlexDirectory.php +++ b/system/src/Grav/Framework/Flex/FlexDirectory.php @@ -788,6 +788,7 @@ protected function loadCachedObjects(array $fetch): array public function reloadIndex(): void { $this->getCache('index')->clear(); + $this->getIndex()::loadEntriesFromStorage($this->getStorage()); $this->indexes = []; $this->objects = []; From aba6382f2e28b38f95754974b2dbe0bb45e06897 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Thu, 30 Jun 2022 12:56:31 +0300 Subject: [PATCH 16/18] Added variables `$environment` (string), `$request` (PSR-7 ServerRequestInterface|null) and `$uri` (PSR-7 Uri|null) to be used in `setup.php` --- CHANGELOG.md | 1 + system/src/Grav/Common/Config/Setup.php | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e60344f0d0..3788427255 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ 1. [](#new) * Added support for `multipart/form-data` content type in PUT and PATCH requests * Added support for object relationships + * Added variables `$environment` (string), `$request` (PSR-7 ServerRequestInterface|null) and `$uri` (PSR-7 Uri|null) to be used in `setup.php` # v1.7.34 ## 06/14/2022 diff --git a/system/src/Grav/Common/Config/Setup.php b/system/src/Grav/Common/Config/Setup.php index f8ce943940..b8c121a1c5 100644 --- a/system/src/Grav/Common/Config/Setup.php +++ b/system/src/Grav/Common/Config/Setup.php @@ -182,13 +182,14 @@ public function __construct($container) // If no environment is set, make sure we get one (CLI or hostname). if (null === $environment) { if (defined('GRAV_CLI')) { + $request = null; + $uri = null; $environment = 'cli'; } else { /** @var ServerRequestInterface $request */ $request = $container['request']; - $host = $request->getUri()->getHost(); - - $environment = Utils::substrToString($host, ':'); + $uri = $request->getUri(); + $environment = $uri->getHost(); } } From 20c4cdefe8762d544a24d25dd32129b0bc75ee97 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Tue, 26 Jul 2022 12:16:23 -0600 Subject: [PATCH 17/18] minor vendor updates --- composer.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/composer.lock b/composer.lock index c445419b37..5a664d178f 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "composer/ca-bundle", - "version": "1.3.2", + "version": "1.3.3", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "fd5dd441932a7e10ca6e5b490e272d34c8430640" + "reference": "30897edbfb15e784fe55587b4f73ceefd3c4d98c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/fd5dd441932a7e10ca6e5b490e272d34c8430640", - "reference": "fd5dd441932a7e10ca6e5b490e272d34c8430640", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/30897edbfb15e784fe55587b4f73ceefd3c4d98c", + "reference": "30897edbfb15e784fe55587b4f73ceefd3c4d98c", "shasum": "" }, "require": { @@ -64,7 +64,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.3.2" + "source": "https://github.com/composer/ca-bundle/tree/1.3.3" }, "funding": [ { @@ -80,7 +80,7 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:56:16+00:00" + "time": "2022-07-20T07:14:26+00:00" }, { "name": "composer/semver", @@ -4679,16 +4679,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.8.0", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b7648d4ee9321665acaf112e49da9fd93df8fbd5" + "reference": "c53312ecc575caf07b0e90dee43883fdf90ca67c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b7648d4ee9321665acaf112e49da9fd93df8fbd5", - "reference": "b7648d4ee9321665acaf112e49da9fd93df8fbd5", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c53312ecc575caf07b0e90dee43883fdf90ca67c", + "reference": "c53312ecc575caf07b0e90dee43883fdf90ca67c", "shasum": "" }, "require": { @@ -4714,7 +4714,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.8.0" + "source": "https://github.com/phpstan/phpstan/tree/1.8.2" }, "funding": [ { @@ -4734,7 +4734,7 @@ "type": "tidelift" } ], - "time": "2022-06-29T08:53:31+00:00" + "time": "2022-07-20T09:57:31+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -6692,5 +6692,5 @@ "platform-overrides": { "php": "7.3.6" }, - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } From 2258adcb0584efe3b5196c8b2d3fee8535e6fb21 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Thu, 4 Aug 2022 15:29:22 -0600 Subject: [PATCH 18/18] prepare for release --- CHANGELOG.md | 4 +++- system/defines.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3788427255..0958fe4518 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ # v1.7.35 -## mm/dd/2022 +## 08/04/2022 1. [](#new) * Added support for `multipart/form-data` content type in PUT and PATCH requests * Added support for object relationships * Added variables `$environment` (string), `$request` (PSR-7 ServerRequestInterface|null) and `$uri` (PSR-7 Uri|null) to be used in `setup.php` +1. [](#improved) + * Minor vendor updates # v1.7.34 ## 06/14/2022 diff --git a/system/defines.php b/system/defines.php index f053d729c7..2e4853cf69 100644 --- a/system/defines.php +++ b/system/defines.php @@ -9,7 +9,7 @@ // Some standard defines define('GRAV', true); -define('GRAV_VERSION', '1.7.34'); +define('GRAV_VERSION', '1.7.35'); define('GRAV_SCHEMA', '1.7.0_2020-11-20_1'); define('GRAV_TESTING', false);