From af0a9c288621e418975b46cea0cb213a8b06d2a9 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Mon, 27 Nov 2023 17:01:50 +0100 Subject: [PATCH 01/64] Add libsvg to worker.dockerfile --- .docker/worker.dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.docker/worker.dockerfile b/.docker/worker.dockerfile index 3f22309f5..72eb912e6 100644 --- a/.docker/worker.dockerfile +++ b/.docker/worker.dockerfile @@ -68,6 +68,7 @@ RUN apk add --no-cache --virtual .build-deps \ libjpeg-turbo-dev \ libpng-dev \ tiff-dev \ + librsvg-dev \ && apk add --no-cache \ expat \ glib \ @@ -75,6 +76,7 @@ RUN apk add --no-cache --virtual .build-deps \ libjpeg-turbo \ libpng \ tiff \ + librsvg \ && cd /tmp \ && curl -L https://github.com/libvips/libvips/releases/download/v${LIBVIPS_VERSION}/vips-${LIBVIPS_VERSION}.tar.gz -o vips-${LIBVIPS_VERSION}.tar.gz \ && tar -xzf vips-${LIBVIPS_VERSION}.tar.gz \ From f47fbecee742e23d677077b0fb0ff276fa8e2da4 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Wed, 6 Dec 2023 15:52:59 +0100 Subject: [PATCH 02/64] Upgrade PHP dependencies --- composer.json | 2 +- composer.lock | 1371 ++++++++++++++++++++++++++----------------------- 2 files changed, 734 insertions(+), 639 deletions(-) diff --git a/composer.json b/composer.json index 95fc3371b..6d7909886 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,6 @@ "ext-soap": "*", "ext-vips": "*", "ext-yaml": "*", - "ankane/pgvector": "^0.1.3", "biigle/laravel-file-cache": "^4.0", "doctrine/dbal": "^3.0", "duncan3dc/bom-string": "^1.1", @@ -34,6 +33,7 @@ "laravel/tinker": "^2.8", "laravel/ui": "^4.0", "msurguy/honeypot": "^1.0", + "pgvector/pgvector": "^0.1.4", "php-ffmpeg/php-ffmpeg": "^1.0", "pusher/pusher-php-server": "^7.2", "ramsey/uuid": "^4.0", diff --git a/composer.lock b/composer.lock index 4fbd6435f..2f0a46f09 100644 --- a/composer.lock +++ b/composer.lock @@ -4,60 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ab35d47f47714e545261a34f23ee8be7", + "content-hash": "ca56d5f114fc956ac8efe6bcdefe8180", "packages": [ - { - "name": "ankane/pgvector", - "version": "v0.1.3", - "source": { - "type": "git", - "url": "https://github.com/pgvector/pgvector-php.git", - "reference": "ff407388899b5e9e268021448c106655846ede91" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pgvector/pgvector-php/zipball/ff407388899b5e9e268021448c106655846ede91", - "reference": "ff407388899b5e9e268021448c106655846ede91", - "shasum": "" - }, - "require": { - "php": ">= 7.4" - }, - "require-dev": { - "illuminate/database": ">= 9.0", - "phpunit/phpunit": "^9" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Pgvector\\Laravel\\PgvectorServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Pgvector\\": "src/", - "Pgvector\\Laravel\\": "src/laravel/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Andrew Kane", - "email": "andrew@ankane.org" - } - ], - "description": "pgvector support for PHP", - "support": { - "issues": "https://github.com/pgvector/pgvector-php/issues", - "source": "https://github.com/pgvector/pgvector-php" - }, - "time": "2023-10-03T23:29:14+00:00" - }, { "name": "bacon/bacon-qr-code", "version": "2.0.8", @@ -226,6 +174,75 @@ ], "time": "2023-01-15T23:15:59+00:00" }, + { + "name": "carbonphp/carbon-doctrine-types", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "67a77972b9f398ae7068dabacc39c08aeee170d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/67a77972b9f398ae7068dabacc39c08aeee170d5", + "reference": "67a77972b9f398ae7068dabacc39c08aeee170d5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "doctrine/dbal": "<3.7.0 || >=4.0.0" + }, + "require-dev": { + "doctrine/dbal": "^3.7.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2023-10-01T14:29:01+00:00" + }, { "name": "dasprid/enum", "version": "1.0.5", @@ -446,16 +463,16 @@ }, { "name": "doctrine/dbal", - "version": "3.7.0", + "version": "3.7.2", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "00d03067f07482f025d41ab55e4ba0db5eca2cdf" + "reference": "0ac3c270590e54910715e9a1a044cc368df282b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/00d03067f07482f025d41ab55e4ba0db5eca2cdf", - "reference": "00d03067f07482f025d41ab55e4ba0db5eca2cdf", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/0ac3c270590e54910715e9a1a044cc368df282b2", + "reference": "0ac3c270590e54910715e9a1a044cc368df282b2", "shasum": "" }, "require": { @@ -471,7 +488,7 @@ "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.10.35", + "phpstan/phpstan": "1.10.42", "phpstan/phpstan-strict-rules": "^1.5", "phpunit/phpunit": "9.6.13", "psalm/plugin-phpunit": "0.18.4", @@ -539,7 +556,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.7.0" + "source": "https://github.com/doctrine/dbal/tree/3.7.2" }, "funding": [ { @@ -555,20 +572,20 @@ "type": "tidelift" } ], - "time": "2023-09-26T20:56:55+00:00" + "time": "2023-11-19T08:06:58+00:00" }, { "name": "doctrine/deprecations", - "version": "v1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", "shasum": "" }, "require": { @@ -600,9 +617,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" }, - "time": "2023-06-03T09:27:29+00:00" + "time": "2023-09-27T20:04:15+00:00" }, { "name": "doctrine/event-manager", @@ -983,16 +1000,16 @@ }, { "name": "egulias/email-validator", - "version": "4.0.1", + "version": "4.0.2", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff" + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/3a85486b709bc384dae8eb78fb2eec649bdb64ff", - "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", "shasum": "" }, "require": { @@ -1001,8 +1018,8 @@ "symfony/polyfill-intl-idn": "^1.26" }, "require-dev": { - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^4.30" + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" }, "suggest": { "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" @@ -1038,7 +1055,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/4.0.1" + "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" }, "funding": [ { @@ -1046,20 +1063,20 @@ "type": "github" } ], - "time": "2023-01-14T14:17:03+00:00" + "time": "2023-10-06T06:47:41+00:00" }, { "name": "endroid/qr-code", - "version": "4.8.4", + "version": "4.8.5", "source": { "type": "git", "url": "https://github.com/endroid/qr-code.git", - "reference": "a122b85d4a5a3257d471257a43ac3e5676a27ffe" + "reference": "0db25b506a8411a5e1644ebaa67123a6eb7b6a77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/endroid/qr-code/zipball/a122b85d4a5a3257d471257a43ac3e5676a27ffe", - "reference": "a122b85d4a5a3257d471257a43ac3e5676a27ffe", + "url": "https://api.github.com/repos/endroid/qr-code/zipball/0db25b506a8411a5e1644ebaa67123a6eb7b6a77", + "reference": "0db25b506a8411a5e1644ebaa67123a6eb7b6a77", "shasum": "" }, "require": { @@ -1113,7 +1130,7 @@ ], "support": { "issues": "https://github.com/endroid/qr-code/issues", - "source": "https://github.com/endroid/qr-code/tree/4.8.4" + "source": "https://github.com/endroid/qr-code/tree/4.8.5" }, "funding": [ { @@ -1121,7 +1138,7 @@ "type": "github" } ], - "time": "2023-08-28T18:12:07+00:00" + "time": "2023-09-29T14:03:20+00:00" }, { "name": "evenement/evenement", @@ -1172,21 +1189,21 @@ }, { "name": "fruitcake/php-cors", - "version": "v1.2.0", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/fruitcake/php-cors.git", - "reference": "58571acbaa5f9f462c9c77e911700ac66f446d4e" + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/58571acbaa5f9f462c9c77e911700ac66f446d4e", - "reference": "58571acbaa5f9f462c9c77e911700ac66f446d4e", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", "shasum": "" }, "require": { "php": "^7.4|^8.0", - "symfony/http-foundation": "^4.4|^5.4|^6" + "symfony/http-foundation": "^4.4|^5.4|^6|^7" }, "require-dev": { "phpstan/phpstan": "^1.4", @@ -1196,7 +1213,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.1-dev" + "dev-master": "1.2-dev" } }, "autoload": { @@ -1227,7 +1244,7 @@ ], "support": { "issues": "https://github.com/fruitcake/php-cors/issues", - "source": "https://github.com/fruitcake/php-cors/tree/v1.2.0" + "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" }, "funding": [ { @@ -1239,28 +1256,28 @@ "type": "github" } ], - "time": "2022-02-20T15:07:15+00:00" + "time": "2023-10-12T05:21:21+00:00" }, { "name": "graham-campbell/result-type", - "version": "v1.1.1", + "version": "v1.1.2", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831" + "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", - "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862", + "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.1" + "phpoption/phpoption": "^1.9.2" }, "require-dev": { - "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" }, "type": "library", "autoload": { @@ -1289,7 +1306,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.1" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.2" }, "funding": [ { @@ -1301,20 +1318,20 @@ "type": "tidelift" } ], - "time": "2023-02-25T20:23:15+00:00" + "time": "2023-11-12T22:16:48+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.8.0", + "version": "7.8.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9" + "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9", - "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", + "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", "shasum": "" }, "require": { @@ -1329,11 +1346,11 @@ "psr/http-client-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", + "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -1411,7 +1428,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.8.0" + "source": "https://github.com/guzzle/guzzle/tree/7.8.1" }, "funding": [ { @@ -1427,28 +1444,28 @@ "type": "tidelift" } ], - "time": "2023-08-27T10:20:53+00:00" + "time": "2023-12-03T20:35:24+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" + "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", - "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", + "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", + "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15" }, "type": "library", "extra": { @@ -1494,7 +1511,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.1" + "source": "https://github.com/guzzle/promises/tree/2.0.2" }, "funding": [ { @@ -1510,20 +1527,20 @@ "type": "tidelift" } ], - "time": "2023-08-03T15:11:55+00:00" + "time": "2023-12-03T20:19:20+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.6.1", + "version": "2.6.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727" + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727", - "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", "shasum": "" }, "require": { @@ -1537,9 +1554,9 @@ "psr/http-message-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", + "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" + "phpunit/phpunit": "^8.5.36 || ^9.6.15" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1610,7 +1627,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.1" + "source": "https://github.com/guzzle/psr7/tree/2.6.2" }, "funding": [ { @@ -1626,32 +1643,38 @@ "type": "tidelift" } ], - "time": "2023-08-27T10:13:57+00:00" + "time": "2023-12-03T20:05:35+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.2", + "version": "v1.0.3", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "61bf437fc2197f587f6857d3ff903a24f1731b5d" + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/61bf437fc2197f587f6857d3ff903a24f1731b5d", - "reference": "61bf437fc2197f587f6857d3ff903a24f1731b5d", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/ecea8feef63bd4fef1f037ecb288386999ecc11c", + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "symfony/polyfill-php80": "^1.17" + "symfony/polyfill-php80": "^1.24" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "phpunit/phpunit": "^8.5.19 || ^9.5.8", + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", "uri-template/tests": "1.0.0" }, "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, "autoload": { "psr-4": { "GuzzleHttp\\UriTemplate\\": "src" @@ -1690,7 +1713,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.2" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.3" }, "funding": [ { @@ -1706,7 +1729,7 @@ "type": "tidelift" } ], - "time": "2023-08-27T10:19:19+00:00" + "time": "2023-12-03T19:50:20+00:00" }, { "name": "jcupitt/vips", @@ -1771,16 +1794,16 @@ }, { "name": "laravel/framework", - "version": "v10.25.1", + "version": "v10.35.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "cd0a440f43eaaad247d6f6575d3782c156ec913c" + "reference": "91ec2d92d2f6007e9084fe06438b99c91845da69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/cd0a440f43eaaad247d6f6575d3782c156ec913c", - "reference": "cd0a440f43eaaad247d6f6575d3782c156ec913c", + "url": "https://api.github.com/repos/laravel/framework/zipball/91ec2d92d2f6007e9084fe06438b99c91845da69", + "reference": "91ec2d92d2f6007e9084fe06438b99c91845da69", "shasum": "" }, "require": { @@ -1813,7 +1836,7 @@ "symfony/console": "^6.2", "symfony/error-handler": "^6.2", "symfony/finder": "^6.2", - "symfony/http-foundation": "^6.2", + "symfony/http-foundation": "^6.4", "symfony/http-kernel": "^6.2", "symfony/mailer": "^6.2", "symfony/mime": "^6.2", @@ -1880,13 +1903,15 @@ "league/flysystem-read-only": "^3.3", "league/flysystem-sftp-v3": "^3.0", "mockery/mockery": "^1.5.1", - "orchestra/testbench-core": "^8.12", + "nyholm/psr7": "^1.2", + "orchestra/testbench-core": "^8.15.1", "pda/pheanstalk": "^4.0", "phpstan/phpstan": "^1.4.7", "phpunit/phpunit": "^10.0.7", "predis/predis": "^2.0.2", "symfony/cache": "^6.2", - "symfony/http-client": "^6.2.4" + "symfony/http-client": "^6.2.4", + "symfony/psr-http-message-bridge": "^2.0" }, "suggest": { "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", @@ -1967,27 +1992,27 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-09-27T01:29:32+00:00" + "time": "2023-12-05T14:50:33+00:00" }, { "name": "laravel/prompts", - "version": "v0.1.9", + "version": "v0.1.13", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "b603410e7af1040aa2d29e0a2cdca570bb63e827" + "reference": "e1379d8ead15edd6cc4369c22274345982edc95a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/b603410e7af1040aa2d29e0a2cdca570bb63e827", - "reference": "b603410e7af1040aa2d29e0a2cdca570bb63e827", + "url": "https://api.github.com/repos/laravel/prompts/zipball/e1379d8ead15edd6cc4369c22274345982edc95a", + "reference": "e1379d8ead15edd6cc4369c22274345982edc95a", "shasum": "" }, "require": { "ext-mbstring": "*", "illuminate/collections": "^10.0|^11.0", "php": "^8.1", - "symfony/console": "^6.2" + "symfony/console": "^6.2|^7.0" }, "conflict": { "illuminate/console": ">=10.17.0 <10.25.0", @@ -2022,22 +2047,22 @@ ], "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.1.9" + "source": "https://github.com/laravel/prompts/tree/v0.1.13" }, - "time": "2023-09-26T13:14:20+00:00" + "time": "2023-10-27T13:53:59+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.3.1", + "version": "v1.3.3", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "e5a3057a5591e1cfe8183034b0203921abe2c902" + "reference": "3dbf8a8e914634c48d389c1234552666b3d43754" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/e5a3057a5591e1cfe8183034b0203921abe2c902", - "reference": "e5a3057a5591e1cfe8183034b0203921abe2c902", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3dbf8a8e914634c48d389c1234552666b3d43754", + "reference": "3dbf8a8e914634c48d389c1234552666b3d43754", "shasum": "" }, "require": { @@ -2084,7 +2109,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2023-07-14T13:56:28+00:00" + "time": "2023-11-08T14:08:06+00:00" }, { "name": "laravel/tinker", @@ -2157,16 +2182,16 @@ }, { "name": "laravel/ui", - "version": "v4.2.2", + "version": "v4.2.3", "source": { "type": "git", "url": "https://github.com/laravel/ui.git", - "reference": "a58ec468db4a340b33f3426c778784717a2c144b" + "reference": "eb532ea096ca1c0298c87c19233daf011fda743a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/ui/zipball/a58ec468db4a340b33f3426c778784717a2c144b", - "reference": "a58ec468db4a340b33f3426c778784717a2c144b", + "url": "https://api.github.com/repos/laravel/ui/zipball/eb532ea096ca1c0298c87c19233daf011fda743a", + "reference": "eb532ea096ca1c0298c87c19233daf011fda743a", "shasum": "" }, "require": { @@ -2213,9 +2238,9 @@ "ui" ], "support": { - "source": "https://github.com/laravel/ui/tree/v4.2.2" + "source": "https://github.com/laravel/ui/tree/v4.2.3" }, - "time": "2023-05-09T19:47:28+00:00" + "time": "2023-11-23T14:44:22+00:00" }, { "name": "league/commonmark", @@ -2407,16 +2432,16 @@ }, { "name": "league/flysystem", - "version": "3.16.0", + "version": "3.23.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729" + "reference": "d4ad81e2b67396e33dc9d7e54ec74ccf73151dcc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4fdf372ca6b63c6e281b1c01a624349ccb757729", - "reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/d4ad81e2b67396e33dc9d7e54ec74ccf73151dcc", + "reference": "d4ad81e2b67396e33dc9d7e54ec74ccf73151dcc", "shasum": "" }, "require": { @@ -2434,8 +2459,8 @@ "symfony/http-client": "<5.2" }, "require-dev": { - "async-aws/s3": "^1.5", - "async-aws/simple-s3": "^1.1", + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", "aws/aws-sdk-php": "^3.220.0", "composer/semver": "^3.0", "ext-fileinfo": "*", @@ -2444,8 +2469,8 @@ "friendsofphp/php-cs-fixer": "^3.5", "google/cloud-storage": "^1.23", "microsoft/azure-storage-blob": "^1.1", - "phpseclib/phpseclib": "^3.0.14", - "phpstan/phpstan": "^0.12.26", + "phpseclib/phpseclib": "^3.0.34", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.5.11|^10.0", "sabre/dav": "^4.3.1" }, @@ -2481,7 +2506,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.16.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.23.0" }, "funding": [ { @@ -2493,20 +2518,20 @@ "type": "github" } ], - "time": "2023-09-07T19:22:17+00:00" + "time": "2023-12-04T10:16:17+00:00" }, { "name": "league/flysystem-local", - "version": "3.16.0", + "version": "3.23.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "ec7383f25642e6fd4bb0c9554fc2311245391781" + "reference": "5cf046ba5f059460e86a997c504dd781a39a109b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/ec7383f25642e6fd4bb0c9554fc2311245391781", - "reference": "ec7383f25642e6fd4bb0c9554fc2311245391781", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/5cf046ba5f059460e86a997c504dd781a39a109b", + "reference": "5cf046ba5f059460e86a997c504dd781a39a109b", "shasum": "" }, "require": { @@ -2541,7 +2566,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem-local/issues", - "source": "https://github.com/thephpleague/flysystem-local/tree/3.16.0" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.23.0" }, "funding": [ { @@ -2553,20 +2578,20 @@ "type": "github" } ], - "time": "2023-08-30T10:23:59+00:00" + "time": "2023-12-04T10:14:46+00:00" }, { "name": "league/mime-type-detection", - "version": "1.13.0", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "a6dfb1194a2946fcdc1f38219445234f65b35c96" + "reference": "b6a5854368533df0295c5761a0253656a2e52d9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/a6dfb1194a2946fcdc1f38219445234f65b35c96", - "reference": "a6dfb1194a2946fcdc1f38219445234f65b35c96", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/b6a5854368533df0295c5761a0253656a2e52d9e", + "reference": "b6a5854368533df0295c5761a0253656a2e52d9e", "shasum": "" }, "require": { @@ -2597,7 +2622,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.13.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.14.0" }, "funding": [ { @@ -2609,20 +2634,20 @@ "type": "tidelift" } ], - "time": "2023-08-05T12:09:49+00:00" + "time": "2023-10-17T14:13:20+00:00" }, { "name": "monolog/monolog", - "version": "3.4.0", + "version": "3.5.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "e2392369686d420ca32df3803de28b5d6f76867d" + "reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/e2392369686d420ca32df3803de28b5d6f76867d", - "reference": "e2392369686d420ca32df3803de28b5d6f76867d", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c915e2634718dbc8a4a15c61b0e62e7a44e14448", + "reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448", "shasum": "" }, "require": { @@ -2698,7 +2723,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.4.0" + "source": "https://github.com/Seldaek/monolog/tree/3.5.0" }, "funding": [ { @@ -2710,7 +2735,7 @@ "type": "tidelift" } ], - "time": "2023-06-21T08:46:11+00:00" + "time": "2023-10-27T15:32:31+00:00" }, { "name": "msurguy/honeypot", @@ -2777,19 +2802,20 @@ }, { "name": "nesbot/carbon", - "version": "2.71.0", + "version": "2.72.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "98276233188583f2ff845a0f992a235472d9466a" + "reference": "a6885fcbad2ec4360b0e200ee0da7d9b7c90786b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/98276233188583f2ff845a0f992a235472d9466a", - "reference": "98276233188583f2ff845a0f992a235472d9466a", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/a6885fcbad2ec4360b0e200ee0da7d9b7c90786b", + "reference": "a6885fcbad2ec4360b0e200ee0da7d9b7c90786b", "shasum": "" }, "require": { + "carbonphp/carbon-doctrine-types": "*", "ext-json": "*", "php": "^7.1.8 || ^8.0", "psr/clock": "^1.0", @@ -2801,8 +2827,8 @@ "psr/clock-implementation": "1.0" }, "require-dev": { - "doctrine/dbal": "^2.0 || ^3.1.4", - "doctrine/orm": "^2.7", + "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0", + "doctrine/orm": "^2.7 || ^3.0", "friendsofphp/php-cs-fixer": "^3.0", "kylekatarnls/multi-tester": "^2.0", "ondrejmirtes/better-reflection": "*", @@ -2879,20 +2905,20 @@ "type": "tidelift" } ], - "time": "2023-09-25T11:31:05+00:00" + "time": "2023-11-28T10:13:25+00:00" }, { "name": "nette/schema", - "version": "v1.2.4", + "version": "v1.2.5", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab" + "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/c9ff517a53903b3d4e29ec547fb20feecb05b8ab", - "reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab", + "url": "https://api.github.com/repos/nette/schema/zipball/0462f0166e823aad657c9224d0f849ecac1ba10a", + "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a", "shasum": "" }, "require": { @@ -2939,22 +2965,22 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.2.4" + "source": "https://github.com/nette/schema/tree/v1.2.5" }, - "time": "2023-08-05T18:56:25+00:00" + "time": "2023-10-05T20:37:59+00:00" }, { "name": "nette/utils", - "version": "v4.0.2", + "version": "v4.0.3", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "cead6637226456b35e1175cc53797dd585d85545" + "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/cead6637226456b35e1175cc53797dd585d85545", - "reference": "cead6637226456b35e1175cc53797dd585d85545", + "url": "https://api.github.com/repos/nette/utils/zipball/a9d127dd6a203ce6d255b2e2db49759f7506e015", + "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015", "shasum": "" }, "require": { @@ -3025,9 +3051,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.2" + "source": "https://github.com/nette/utils/tree/v4.0.3" }, - "time": "2023-09-19T11:58:07+00:00" + "time": "2023-10-29T21:02:13+00:00" }, { "name": "nikic/php-parser", @@ -3307,6 +3333,58 @@ }, "time": "2023-04-30T00:54:53+00:00" }, + { + "name": "pgvector/pgvector", + "version": "v0.1.4", + "source": { + "type": "git", + "url": "https://github.com/pgvector/pgvector-php.git", + "reference": "301ce729091b496b477c28f548bcba285d59d86d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pgvector/pgvector-php/zipball/301ce729091b496b477c28f548bcba285d59d86d", + "reference": "301ce729091b496b477c28f548bcba285d59d86d", + "shasum": "" + }, + "require": { + "php": ">= 7.4" + }, + "require-dev": { + "illuminate/database": ">= 9.0", + "phpunit/phpunit": "^9" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Pgvector\\Laravel\\PgvectorServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Pgvector\\": "src/", + "Pgvector\\Laravel\\": "src/laravel/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrew Kane", + "email": "andrew@ankane.org" + } + ], + "description": "pgvector support for PHP", + "support": { + "issues": "https://github.com/pgvector/pgvector-php/issues", + "source": "https://github.com/pgvector/pgvector-php" + }, + "time": "2023-11-15T02:20:37+00:00" + }, { "name": "php-ffmpeg/php-ffmpeg", "version": "v1.1.0", @@ -3398,16 +3476,16 @@ }, { "name": "phpoption/phpoption", - "version": "1.9.1", + "version": "1.9.2", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e" + "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e", - "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/80735db690fe4fc5c76dfa7f9b770634285fa820", + "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820", "shasum": "" }, "require": { @@ -3415,7 +3493,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" }, "type": "library", "extra": { @@ -3457,7 +3535,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.1" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.2" }, "funding": [ { @@ -3469,7 +3547,7 @@ "type": "tidelift" } ], - "time": "2023-02-25T19:38:58+00:00" + "time": "2023-11-12T21:59:55+00:00" }, { "name": "psr/cache", @@ -3934,16 +4012,16 @@ }, { "name": "psy/psysh", - "version": "v0.11.21", + "version": "v0.11.22", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "bcb22101107f3bf770523b65630c9d547f60c540" + "reference": "128fa1b608be651999ed9789c95e6e2a31b5802b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/bcb22101107f3bf770523b65630c9d547f60c540", - "reference": "bcb22101107f3bf770523b65630c9d547f60c540", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/128fa1b608be651999ed9789c95e6e2a31b5802b", + "reference": "128fa1b608be651999ed9789c95e6e2a31b5802b", "shasum": "" }, "require": { @@ -3972,7 +4050,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "0.11.x-dev" + "dev-0.11": "0.11.x-dev" }, "bamarni-bin": { "bin-links": false, @@ -4008,9 +4086,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.11.21" + "source": "https://github.com/bobthecow/psysh/tree/v0.11.22" }, - "time": "2023-09-17T21:15:54+00:00" + "time": "2023-10-14T21:56:36+00:00" }, { "name": "pusher/pusher-php-server", @@ -4208,16 +4286,16 @@ }, { "name": "ramsey/uuid", - "version": "4.7.4", + "version": "4.7.5", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "60a4c63ab724854332900504274f6150ff26d286" + "reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/60a4c63ab724854332900504274f6150ff26d286", - "reference": "60a4c63ab724854332900504274f6150ff26d286", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e", + "reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e", "shasum": "" }, "require": { @@ -4284,7 +4362,7 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.7.4" + "source": "https://github.com/ramsey/uuid/tree/4.7.5" }, "funding": [ { @@ -4296,7 +4374,7 @@ "type": "tidelift" } ], - "time": "2023-04-15T23:01:58+00:00" + "time": "2023-11-08T05:53:05+00:00" }, { "name": "ramsey/uuid-doctrine", @@ -4428,16 +4506,16 @@ }, { "name": "symfony/cache", - "version": "v6.3.4", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "e60d00b4f633efa4c1ef54e77c12762d9073e7b3" + "reference": "ac2d25f97b17eec6e19760b6b9962a4f7c44356a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/e60d00b4f633efa4c1ef54e77c12762d9073e7b3", - "reference": "e60d00b4f633efa4c1ef54e77c12762d9073e7b3", + "url": "https://api.github.com/repos/symfony/cache/zipball/ac2d25f97b17eec6e19760b6b9962a4f7c44356a", + "reference": "ac2d25f97b17eec6e19760b6b9962a4f7c44356a", "shasum": "" }, "require": { @@ -4446,7 +4524,7 @@ "psr/log": "^1.1|^2|^3", "symfony/cache-contracts": "^2.5|^3", "symfony/service-contracts": "^2.5|^3", - "symfony/var-exporter": "^6.2.10" + "symfony/var-exporter": "^6.3.6|^7.0" }, "conflict": { "doctrine/dbal": "<2.13.1", @@ -4461,15 +4539,15 @@ }, "require-dev": { "cache/integration-tests": "dev-master", - "doctrine/dbal": "^2.13.1|^3.0", + "doctrine/dbal": "^2.13.1|^3|^4", "predis/predis": "^1.1|^2.0", "psr/simple-cache": "^1.0|^2.0|^3.0", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/filesystem": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/messenger": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/filesystem": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -4504,7 +4582,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v6.3.4" + "source": "https://github.com/symfony/cache/tree/v6.4.0" }, "funding": [ { @@ -4520,20 +4598,20 @@ "type": "tidelift" } ], - "time": "2023-08-05T09:10:27+00:00" + "time": "2023-11-24T19:28:07+00:00" }, { "name": "symfony/cache-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/cache-contracts.git", - "reference": "ad945640ccc0ae6e208bcea7d7de4b39b569896b" + "reference": "1d74b127da04ffa87aa940abe15446fa89653778" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/ad945640ccc0ae6e208bcea7d7de4b39b569896b", - "reference": "ad945640ccc0ae6e208bcea7d7de4b39b569896b", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/1d74b127da04ffa87aa940abe15446fa89653778", + "reference": "1d74b127da04ffa87aa940abe15446fa89653778", "shasum": "" }, "require": { @@ -4580,7 +4658,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/cache-contracts/tree/v3.4.0" }, "funding": [ { @@ -4596,20 +4674,20 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2023-09-25T12:52:38+00:00" }, { "name": "symfony/console", - "version": "v6.3.4", + "version": "v6.4.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6" + "reference": "a550a7c99daeedef3f9d23fb82e3531525ff11fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6", - "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6", + "url": "https://api.github.com/repos/symfony/console/zipball/a550a7c99daeedef3f9d23fb82e3531525ff11fd", + "reference": "a550a7c99daeedef3f9d23fb82e3531525ff11fd", "shasum": "" }, "require": { @@ -4617,7 +4695,7 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0" + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { "symfony/dependency-injection": "<5.4", @@ -4631,12 +4709,16 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -4670,7 +4752,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.3.4" + "source": "https://github.com/symfony/console/tree/v6.4.1" }, "funding": [ { @@ -4686,20 +4768,20 @@ "type": "tidelift" } ], - "time": "2023-08-16T10:10:12+00:00" + "time": "2023-11-30T10:54:28+00:00" }, { "name": "symfony/css-selector", - "version": "v6.3.2", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "883d961421ab1709877c10ac99451632a3d6fa57" + "reference": "d036c6c0d0b09e24a14a35f8292146a658f986e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/883d961421ab1709877c10ac99451632a3d6fa57", - "reference": "883d961421ab1709877c10ac99451632a3d6fa57", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/d036c6c0d0b09e24a14a35f8292146a658f986e4", + "reference": "d036c6c0d0b09e24a14a35f8292146a658f986e4", "shasum": "" }, "require": { @@ -4735,7 +4817,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.3.2" + "source": "https://github.com/symfony/css-selector/tree/v6.4.0" }, "funding": [ { @@ -4751,11 +4833,11 @@ "type": "tidelift" } ], - "time": "2023-07-12T16:00:22+00:00" + "time": "2023-10-31T08:40:20+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", @@ -4802,7 +4884,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" }, "funding": [ { @@ -4822,30 +4904,31 @@ }, { "name": "symfony/error-handler", - "version": "v6.3.2", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "85fd65ed295c4078367c784e8a5a6cee30348b7a" + "reference": "c873490a1c97b3a0a4838afc36ff36c112d02788" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/85fd65ed295c4078367c784e8a5a6cee30348b7a", - "reference": "85fd65ed295c4078367c784e8a5a6cee30348b7a", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/c873490a1c97b3a0a4838afc36ff36c112d02788", + "reference": "c873490a1c97b3a0a4838afc36ff36c112d02788", "shasum": "" }, "require": { "php": ">=8.1", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^5.4|^6.0" + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "conflict": { - "symfony/deprecation-contracts": "<2.5" + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" }, "require-dev": { "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/serializer": "^5.4|^6.0" + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^5.4|^6.0|^7.0" }, "bin": [ "Resources/bin/patch-type-declarations" @@ -4876,7 +4959,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.3.2" + "source": "https://github.com/symfony/error-handler/tree/v6.4.0" }, "funding": [ { @@ -4892,20 +4975,20 @@ "type": "tidelift" } ], - "time": "2023-07-16T17:05:46+00:00" + "time": "2023-10-18T09:43:34+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.3.2", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e" + "reference": "d76d2632cfc2206eecb5ad2b26cd5934082941b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/adb01fe097a4ee930db9258a3cc906b5beb5cf2e", - "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d76d2632cfc2206eecb5ad2b26cd5934082941b6", + "reference": "d76d2632cfc2206eecb5ad2b26cd5934082941b6", "shasum": "" }, "require": { @@ -4922,13 +5005,13 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/error-handler": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^5.4|^6.0" + "symfony/stopwatch": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -4956,7 +5039,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.3.2" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.0" }, "funding": [ { @@ -4972,11 +5055,11 @@ "type": "tidelift" } ], - "time": "2023-07-06T06:56:43+00:00" + "time": "2023-07-27T06:52:43+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", @@ -5032,7 +5115,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.0" }, "funding": [ { @@ -5052,23 +5135,23 @@ }, { "name": "symfony/finder", - "version": "v6.3.3", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "9915db259f67d21eefee768c1abcf1cc61b1fc9e" + "reference": "11d736e97f116ac375a81f96e662911a34cd50ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/9915db259f67d21eefee768c1abcf1cc61b1fc9e", - "reference": "9915db259f67d21eefee768c1abcf1cc61b1fc9e", + "url": "https://api.github.com/repos/symfony/finder/zipball/11d736e97f116ac375a81f96e662911a34cd50ce", + "reference": "11d736e97f116ac375a81f96e662911a34cd50ce", "shasum": "" }, "require": { "php": ">=8.1" }, "require-dev": { - "symfony/filesystem": "^6.0" + "symfony/filesystem": "^6.0|^7.0" }, "type": "library", "autoload": { @@ -5096,7 +5179,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.3.3" + "source": "https://github.com/symfony/finder/tree/v6.4.0" }, "funding": [ { @@ -5112,20 +5195,20 @@ "type": "tidelift" } ], - "time": "2023-07-31T08:31:44+00:00" + "time": "2023-10-31T17:30:12+00:00" }, { "name": "symfony/http-foundation", - "version": "v6.3.4", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "cac1556fdfdf6719668181974104e6fcfa60e844" + "reference": "44a6d39a9cc11e154547d882d5aac1e014440771" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cac1556fdfdf6719668181974104e6fcfa60e844", - "reference": "cac1556fdfdf6719668181974104e6fcfa60e844", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/44a6d39a9cc11e154547d882d5aac1e014440771", + "reference": "44a6d39a9cc11e154547d882d5aac1e014440771", "shasum": "" }, "require": { @@ -5135,17 +5218,17 @@ "symfony/polyfill-php83": "^1.27" }, "conflict": { - "symfony/cache": "<6.2" + "symfony/cache": "<6.3" }, "require-dev": { - "doctrine/dbal": "^2.13.1|^3.0", + "doctrine/dbal": "^2.13.1|^3|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", - "symfony/mime": "^5.4|^6.0", - "symfony/rate-limiter": "^5.2|^6.0" + "symfony/cache": "^6.3|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/rate-limiter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -5173,7 +5256,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.3.4" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.0" }, "funding": [ { @@ -5189,29 +5272,29 @@ "type": "tidelift" } ], - "time": "2023-08-22T08:20:46+00:00" + "time": "2023-11-20T16:41:16+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.3.4", + "version": "v6.4.1", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "36abb425b4af863ae1fe54d8a8b8b4c76a2bccdb" + "reference": "2953274c16a229b3933ef73a6898e18388e12e1b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/36abb425b4af863ae1fe54d8a8b8b4c76a2bccdb", - "reference": "36abb425b4af863ae1fe54d8a8b8b4c76a2bccdb", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/2953274c16a229b3933ef73a6898e18388e12e1b", + "reference": "2953274c16a229b3933ef73a6898e18388e12e1b", "shasum": "" }, "require": { "php": ">=8.1", "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.3", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/http-foundation": "^6.3.4", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -5219,7 +5302,7 @@ "symfony/cache": "<5.4", "symfony/config": "<6.1", "symfony/console": "<5.4", - "symfony/dependency-injection": "<6.3.4", + "symfony/dependency-injection": "<6.4", "symfony/doctrine-bridge": "<5.4", "symfony/form": "<5.4", "symfony/http-client": "<5.4", @@ -5229,7 +5312,7 @@ "symfony/translation": "<5.4", "symfony/translation-contracts": "<2.5", "symfony/twig-bridge": "<5.4", - "symfony/validator": "<5.4", + "symfony/validator": "<6.4", "symfony/var-dumper": "<6.3", "twig/twig": "<2.13" }, @@ -5238,26 +5321,26 @@ }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/clock": "^6.2", - "symfony/config": "^6.1", - "symfony/console": "^5.4|^6.0", - "symfony/css-selector": "^5.4|^6.0", - "symfony/dependency-injection": "^6.3.4", - "symfony/dom-crawler": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/clock": "^6.2|^7.0", + "symfony/config": "^6.1|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", "symfony/http-client-contracts": "^2.5|^3", - "symfony/process": "^5.4|^6.0", - "symfony/property-access": "^5.4.5|^6.0.5", - "symfony/routing": "^5.4|^6.0", - "symfony/serializer": "^6.3", - "symfony/stopwatch": "^5.4|^6.0", - "symfony/translation": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4.5|^6.0.5|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.3|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^5.4|^6.0", - "symfony/validator": "^6.3", - "symfony/var-exporter": "^6.2", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-exporter": "^6.2|^7.0", "twig/twig": "^2.13|^3.0.4" }, "type": "library", @@ -5286,7 +5369,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.3.4" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.1" }, "funding": [ { @@ -5302,20 +5385,20 @@ "type": "tidelift" } ], - "time": "2023-08-26T13:54:49+00:00" + "time": "2023-12-01T17:02:02+00:00" }, { "name": "symfony/mailer", - "version": "v6.3.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "7b03d9be1dea29bfec0a6c7b603f5072a4c97435" + "reference": "ca8dcf8892cdc5b4358ecf2528429bb5e706f7ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/7b03d9be1dea29bfec0a6c7b603f5072a4c97435", - "reference": "7b03d9be1dea29bfec0a6c7b603f5072a4c97435", + "url": "https://api.github.com/repos/symfony/mailer/zipball/ca8dcf8892cdc5b4358ecf2528429bb5e706f7ba", + "reference": "ca8dcf8892cdc5b4358ecf2528429bb5e706f7ba", "shasum": "" }, "require": { @@ -5323,8 +5406,8 @@ "php": ">=8.1", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/mime": "^6.2", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/mime": "^6.2|^7.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -5335,10 +5418,10 @@ "symfony/twig-bridge": "<6.2.1" }, "require-dev": { - "symfony/console": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/messenger": "^6.2", - "symfony/twig-bridge": "^6.2" + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/messenger": "^6.2|^7.0", + "symfony/twig-bridge": "^6.2|^7.0" }, "type": "library", "autoload": { @@ -5366,7 +5449,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v6.3.0" + "source": "https://github.com/symfony/mailer/tree/v6.4.0" }, "funding": [ { @@ -5382,20 +5465,20 @@ "type": "tidelift" } ], - "time": "2023-05-29T12:49:39+00:00" + "time": "2023-11-12T18:02:22+00:00" }, { "name": "symfony/mime", - "version": "v6.3.3", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "9a0cbd52baa5ba5a5b1f0cacc59466f194730f98" + "reference": "ca4f58b2ef4baa8f6cecbeca2573f88cd577d205" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/9a0cbd52baa5ba5a5b1f0cacc59466f194730f98", - "reference": "9a0cbd52baa5ba5a5b1f0cacc59466f194730f98", + "url": "https://api.github.com/repos/symfony/mime/zipball/ca4f58b2ef4baa8f6cecbeca2573f88cd577d205", + "reference": "ca4f58b2ef4baa8f6cecbeca2573f88cd577d205", "shasum": "" }, "require": { @@ -5409,16 +5492,16 @@ "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", "symfony/mailer": "<5.4", - "symfony/serializer": "<6.2.13|>=6.3,<6.3.2" + "symfony/serializer": "<6.3.2" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/property-access": "^5.4|^6.0", - "symfony/property-info": "^5.4|^6.0", - "symfony/serializer": "~6.2.13|^6.3.2" + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.3.2|^7.0" }, "type": "library", "autoload": { @@ -5450,7 +5533,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v6.3.3" + "source": "https://github.com/symfony/mime/tree/v6.4.0" }, "funding": [ { @@ -5466,7 +5549,7 @@ "type": "tidelift" } ], - "time": "2023-07-31T07:08:24+00:00" + "time": "2023-10-17T11:49:05+00:00" }, { "name": "symfony/polyfill-ctype", @@ -6208,16 +6291,16 @@ }, { "name": "symfony/process", - "version": "v6.3.4", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54" + "reference": "191703b1566d97a5425dc969e4350d32b8ef17aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/0b5c29118f2e980d455d2e34a5659f4579847c54", - "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54", + "url": "https://api.github.com/repos/symfony/process/zipball/191703b1566d97a5425dc969e4350d32b8ef17aa", + "reference": "191703b1566d97a5425dc969e4350d32b8ef17aa", "shasum": "" }, "require": { @@ -6249,7 +6332,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.3.4" + "source": "https://github.com/symfony/process/tree/v6.4.0" }, "funding": [ { @@ -6265,20 +6348,20 @@ "type": "tidelift" } ], - "time": "2023-08-07T10:39:22+00:00" + "time": "2023-11-17T21:06:49+00:00" }, { "name": "symfony/routing", - "version": "v6.3.3", + "version": "v6.4.1", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "e7243039ab663822ff134fbc46099b5fdfa16f6a" + "reference": "0c95c164fdba18b12523b75e64199ca3503e6d40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/e7243039ab663822ff134fbc46099b5fdfa16f6a", - "reference": "e7243039ab663822ff134fbc46099b5fdfa16f6a", + "url": "https://api.github.com/repos/symfony/routing/zipball/0c95c164fdba18b12523b75e64199ca3503e6d40", + "reference": "0c95c164fdba18b12523b75e64199ca3503e6d40", "shasum": "" }, "require": { @@ -6294,11 +6377,11 @@ "require-dev": { "doctrine/annotations": "^1.12|^2", "psr/log": "^1|^2|^3", - "symfony/config": "^6.2", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/yaml": "^5.4|^6.0" + "symfony/config": "^6.2|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6332,7 +6415,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.3.3" + "source": "https://github.com/symfony/routing/tree/v6.4.1" }, "funding": [ { @@ -6348,20 +6431,20 @@ "type": "tidelift" } ], - "time": "2023-07-31T07:08:24+00:00" + "time": "2023-12-01T14:54:37+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4" + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", - "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838", "shasum": "" }, "require": { @@ -6414,7 +6497,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.4.0" }, "funding": [ { @@ -6430,20 +6513,20 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2023-07-30T20:28:31+00:00" }, { "name": "symfony/string", - "version": "v6.3.2", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "53d1a83225002635bca3482fcbf963001313fb68" + "reference": "b45fcf399ea9c3af543a92edf7172ba21174d809" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/53d1a83225002635bca3482fcbf963001313fb68", - "reference": "53d1a83225002635bca3482fcbf963001313fb68", + "url": "https://api.github.com/repos/symfony/string/zipball/b45fcf399ea9c3af543a92edf7172ba21174d809", + "reference": "b45fcf399ea9c3af543a92edf7172ba21174d809", "shasum": "" }, "require": { @@ -6457,11 +6540,11 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/intl": "^6.2", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0" + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6500,7 +6583,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.3.2" + "source": "https://github.com/symfony/string/tree/v6.4.0" }, "funding": [ { @@ -6516,20 +6599,20 @@ "type": "tidelift" } ], - "time": "2023-07-05T08:41:27+00:00" + "time": "2023-11-28T20:41:49+00:00" }, { "name": "symfony/translation", - "version": "v6.3.3", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "3ed078c54bc98bbe4414e1e9b2d5e85ed5a5c8bd" + "reference": "b1035dbc2a344b21f8fa8ac451c7ecec4ea45f37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/3ed078c54bc98bbe4414e1e9b2d5e85ed5a5c8bd", - "reference": "3ed078c54bc98bbe4414e1e9b2d5e85ed5a5c8bd", + "url": "https://api.github.com/repos/symfony/translation/zipball/b1035dbc2a344b21f8fa8ac451c7ecec4ea45f37", + "reference": "b1035dbc2a344b21f8fa8ac451c7ecec4ea45f37", "shasum": "" }, "require": { @@ -6554,17 +6637,17 @@ "require-dev": { "nikic/php-parser": "^4.13", "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/intl": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^5.4|^6.0", + "symfony/routing": "^5.4|^6.0|^7.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^5.4|^6.0" + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6595,7 +6678,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.3.3" + "source": "https://github.com/symfony/translation/tree/v6.4.0" }, "funding": [ { @@ -6611,20 +6694,20 @@ "type": "tidelift" } ], - "time": "2023-07-31T07:08:24+00:00" + "time": "2023-11-29T08:14:36+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "02c24deb352fb0d79db5486c0c79905a85e37e86" + "reference": "dee0c6e5b4c07ce851b462530088e64b255ac9c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/02c24deb352fb0d79db5486c0c79905a85e37e86", - "reference": "02c24deb352fb0d79db5486c0c79905a85e37e86", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/dee0c6e5b4c07ce851b462530088e64b255ac9c5", + "reference": "dee0c6e5b4c07ce851b462530088e64b255ac9c5", "shasum": "" }, "require": { @@ -6673,7 +6756,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.4.0" }, "funding": [ { @@ -6689,20 +6772,20 @@ "type": "tidelift" } ], - "time": "2023-05-30T17:17:10+00:00" + "time": "2023-07-25T15:08:44+00:00" }, { "name": "symfony/uid", - "version": "v6.3.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "01b0f20b1351d997711c56f1638f7a8c3061e384" + "reference": "8092dd1b1a41372110d06374f99ee62f7f0b9a92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/01b0f20b1351d997711c56f1638f7a8c3061e384", - "reference": "01b0f20b1351d997711c56f1638f7a8c3061e384", + "url": "https://api.github.com/repos/symfony/uid/zipball/8092dd1b1a41372110d06374f99ee62f7f0b9a92", + "reference": "8092dd1b1a41372110d06374f99ee62f7f0b9a92", "shasum": "" }, "require": { @@ -6710,7 +6793,7 @@ "symfony/polyfill-uuid": "^1.15" }, "require-dev": { - "symfony/console": "^5.4|^6.0" + "symfony/console": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6747,7 +6830,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v6.3.0" + "source": "https://github.com/symfony/uid/tree/v6.4.0" }, "funding": [ { @@ -6763,20 +6846,20 @@ "type": "tidelift" } ], - "time": "2023-04-08T07:25:02+00:00" + "time": "2023-10-31T08:18:17+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.3.4", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "2027be14f8ae8eae999ceadebcda5b4909b81d45" + "reference": "c40f7d17e91d8b407582ed51a2bbf83c52c367f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2027be14f8ae8eae999ceadebcda5b4909b81d45", - "reference": "2027be14f8ae8eae999ceadebcda5b4909b81d45", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c40f7d17e91d8b407582ed51a2bbf83c52c367f6", + "reference": "c40f7d17e91d8b407582ed51a2bbf83c52c367f6", "shasum": "" }, "require": { @@ -6789,10 +6872,11 @@ }, "require-dev": { "ext-iconv": "*", - "symfony/console": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/uid": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^6.3|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", "twig/twig": "^2.13|^3.0.4" }, "bin": [ @@ -6831,7 +6915,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.3.4" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.0" }, "funding": [ { @@ -6847,27 +6931,28 @@ "type": "tidelift" } ], - "time": "2023-08-24T14:51:05+00:00" + "time": "2023-11-09T08:28:32+00:00" }, { "name": "symfony/var-exporter", - "version": "v6.3.4", + "version": "v6.4.1", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "df1f8aac5751871b83d30bf3e2c355770f8f0691" + "reference": "2d08ca6b9cc704dce525615d1e6d1788734f36d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/df1f8aac5751871b83d30bf3e2c355770f8f0691", - "reference": "df1f8aac5751871b83d30bf3e2c355770f8f0691", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/2d08ca6b9cc704dce525615d1e6d1788734f36d9", + "reference": "2d08ca6b9cc704dce525615d1e6d1788734f36d9", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" }, "require-dev": { - "symfony/var-dumper": "^5.4|^6.0" + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6905,7 +6990,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v6.3.4" + "source": "https://github.com/symfony/var-exporter/tree/v6.4.1" }, "funding": [ { @@ -6921,7 +7006,7 @@ "type": "tidelift" } ], - "time": "2023-08-16T18:14:47+00:00" + "time": "2023-11-30T10:32:10+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -6978,31 +7063,31 @@ }, { "name": "vlucas/phpdotenv", - "version": "v5.5.0", + "version": "v5.6.0", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7" + "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", - "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", + "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", "shasum": "" }, "require": { "ext-pcre": "*", - "graham-campbell/result-type": "^1.0.2", - "php": "^7.1.3 || ^8.0", - "phpoption/phpoption": "^1.8", - "symfony/polyfill-ctype": "^1.23", - "symfony/polyfill-mbstring": "^1.23.1", - "symfony/polyfill-php80": "^1.23.1" + "graham-campbell/result-type": "^1.1.2", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.2", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", + "bamarni/composer-bin-plugin": "^1.8.2", "ext-filter": "*", - "phpunit/phpunit": "^7.5.20 || ^8.5.30 || ^9.5.25" + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" }, "suggest": { "ext-filter": "Required to use the boolean validator." @@ -7014,7 +7099,7 @@ "forward-command": true }, "branch-alias": { - "dev-master": "5.5-dev" + "dev-master": "5.6-dev" } }, "autoload": { @@ -7046,7 +7131,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.0" }, "funding": [ { @@ -7058,7 +7143,7 @@ "type": "tidelift" } ], - "time": "2022-10-16T01:01:54+00:00" + "time": "2023-11-12T22:43:29+00:00" }, { "name": "voku/portable-ascii", @@ -7581,16 +7666,16 @@ }, { "name": "composer/pcre", - "version": "3.1.0", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9", "shasum": "" }, "require": { @@ -7632,7 +7717,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.0" + "source": "https://github.com/composer/pcre/tree/3.1.1" }, "funding": [ { @@ -7648,7 +7733,7 @@ "type": "tidelift" } ], - "time": "2022-11-17T09:50:14+00:00" + "time": "2023-10-11T07:11:09+00:00" }, { "name": "composer/semver", @@ -8066,16 +8151,16 @@ }, { "name": "filp/whoops", - "version": "2.15.3", + "version": "2.15.4", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "c83e88a30524f9360b11f585f71e6b17313b7187" + "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/c83e88a30524f9360b11f585f71e6b17313b7187", - "reference": "c83e88a30524f9360b11f585f71e6b17313b7187", + "url": "https://api.github.com/repos/filp/whoops/zipball/a139776fa3f5985a50b509f2a02ff0f709d2a546", + "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546", "shasum": "" }, "require": { @@ -8125,7 +8210,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.15.3" + "source": "https://github.com/filp/whoops/tree/2.15.4" }, "funding": [ { @@ -8133,56 +8218,54 @@ "type": "github" } ], - "time": "2023-07-13T12:00:00+00:00" + "time": "2023-11-03T12:00:00+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.30.0", + "version": "v3.40.2", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "95c64693b2f149966a2bc05a7a4981b0343ea52f" + "reference": "4344562a516b76afe8f2d64b2e52214c30d64ed8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/95c64693b2f149966a2bc05a7a4981b0343ea52f", - "reference": "95c64693b2f149966a2bc05a7a4981b0343ea52f", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/4344562a516b76afe8f2d64b2e52214c30d64ed8", + "reference": "4344562a516b76afe8f2d64b2e52214c30d64ed8", "shasum": "" }, "require": { - "composer/semver": "^3.3", + "composer/semver": "^3.4", "composer/xdebug-handler": "^3.0.3", "ext-json": "*", "ext-tokenizer": "*", "php": "^7.4 || ^8.0", "sebastian/diff": "^4.0 || ^5.0", - "symfony/console": "^5.4 || ^6.0", - "symfony/event-dispatcher": "^5.4 || ^6.0", - "symfony/filesystem": "^5.4 || ^6.0", - "symfony/finder": "^5.4 || ^6.0", - "symfony/options-resolver": "^5.4 || ^6.0", - "symfony/polyfill-mbstring": "^1.27", - "symfony/polyfill-php80": "^1.27", - "symfony/polyfill-php81": "^1.27", - "symfony/process": "^5.4 || ^6.0", - "symfony/stopwatch": "^5.4 || ^6.0" + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", + "symfony/finder": "^5.4 || ^6.0 || ^7.0", + "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0", + "symfony/polyfill-mbstring": "^1.28", + "symfony/polyfill-php80": "^1.28", + "symfony/polyfill-php81": "^1.28", + "symfony/process": "^5.4 || ^6.0 || ^7.0", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0" }, "require-dev": { "facile-it/paraunit": "^1.3 || ^2.0", "justinrainbow/json-schema": "^5.2", - "keradus/cli-executor": "^2.0", + "keradus/cli-executor": "^2.1", "mikey179/vfsstream": "^1.6.11", - "php-coveralls/php-coveralls": "^2.5.3", + "php-coveralls/php-coveralls": "^2.7", "php-cs-fixer/accessible-object": "^1.1", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", - "phpspec/prophecy": "^1.16", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.4", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.4", + "phpspec/prophecy": "^1.17", "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.5", - "phpunitgoodpractices/polyfill": "^1.6", - "phpunitgoodpractices/traits": "^1.9.2", - "symfony/phpunit-bridge": "^6.2.3", - "symfony/yaml": "^5.4 || ^6.0" + "phpunit/phpunit": "^9.6", + "symfony/phpunit-bridge": "^6.3.8 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -8220,7 +8303,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.30.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.40.2" }, "funding": [ { @@ -8228,7 +8311,7 @@ "type": "github" } ], - "time": "2023-09-26T22:10:43+00:00" + "time": "2023-12-03T09:21:33+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -8478,16 +8561,16 @@ }, { "name": "nunomaduro/collision", - "version": "v7.9.0", + "version": "v7.10.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "296d0cf9fe462837ac0da8a568b56fc026b132da" + "reference": "49ec67fa7b002712da8526678abd651c09f375b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/296d0cf9fe462837ac0da8a568b56fc026b132da", - "reference": "296d0cf9fe462837ac0da8a568b56fc026b132da", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/49ec67fa7b002712da8526678abd651c09f375b2", + "reference": "49ec67fa7b002712da8526678abd651c09f375b2", "shasum": "" }, "require": { @@ -8496,19 +8579,22 @@ "php": "^8.1.0", "symfony/console": "^6.3.4" }, + "conflict": { + "laravel/framework": ">=11.0.0" + }, "require-dev": { - "brianium/paratest": "^7.2.7", - "laravel/framework": "^10.23.1", - "laravel/pint": "^1.13.1", + "brianium/paratest": "^7.3.0", + "laravel/framework": "^10.28.0", + "laravel/pint": "^1.13.3", "laravel/sail": "^1.25.0", "laravel/sanctum": "^3.3.1", "laravel/tinker": "^2.8.2", "nunomaduro/larastan": "^2.6.4", - "orchestra/testbench-core": "^8.11.0", - "pestphp/pest": "^2.19.1", - "phpunit/phpunit": "^10.3.5", + "orchestra/testbench-core": "^8.13.0", + "pestphp/pest": "^2.23.2", + "phpunit/phpunit": "^10.4.1", "sebastian/environment": "^6.0.1", - "spatie/laravel-ignition": "^2.3.0" + "spatie/laravel-ignition": "^2.3.1" }, "type": "library", "extra": { @@ -8567,34 +8653,36 @@ "type": "patreon" } ], - "time": "2023-09-19T10:45:09+00:00" + "time": "2023-10-11T15:45:01+00:00" }, { "name": "orchestra/canvas", - "version": "v8.10.1", + "version": "v8.11.4", "source": { "type": "git", "url": "https://github.com/orchestral/canvas.git", - "reference": "eef3eb34e263cc462c0eb284ba002f2829b04e2a" + "reference": "466ed3d7c1755e49be1c8e5557b434381b8ab6d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/orchestral/canvas/zipball/eef3eb34e263cc462c0eb284ba002f2829b04e2a", - "reference": "eef3eb34e263cc462c0eb284ba002f2829b04e2a", + "url": "https://api.github.com/repos/orchestral/canvas/zipball/466ed3d7c1755e49be1c8e5557b434381b8ab6d1", + "reference": "466ed3d7c1755e49be1c8e5557b434381b8ab6d1", "shasum": "" }, "require": { "composer-runtime-api": "^2.2", "composer/semver": "^3.0", - "illuminate/database": "^10.23", - "illuminate/support": "^10.23", - "orchestra/canvas-core": "^8.8", + "illuminate/console": "^10.33", + "illuminate/database": "^10.33", + "illuminate/support": "^10.33", + "orchestra/canvas-core": "^8.10.1", "orchestra/testbench-core": "^8.11", "php": "^8.1", + "symfony/polyfill-php83": "^1.28", "symfony/yaml": "^6.2" }, "require-dev": { - "laravel/framework": "^10.23", + "laravel/framework": "^10.33", "laravel/pint": "^1.6", "mockery/mockery": "^1.5.1", "phpstan/phpstan": "^1.10.5", @@ -8637,42 +8725,41 @@ "description": "Code Generators for Laravel Applications and Packages", "support": { "issues": "https://github.com/orchestral/canvas/issues", - "source": "https://github.com/orchestral/canvas/tree/v8.10.1" + "source": "https://github.com/orchestral/canvas/tree/v8.11.4" }, - "time": "2023-09-25T08:37:12+00:00" + "time": "2023-11-27T04:47:24+00:00" }, { "name": "orchestra/canvas-core", - "version": "v8.8.0", + "version": "v8.10.1", "source": { "type": "git", "url": "https://github.com/orchestral/canvas-core.git", - "reference": "d44ffd6895685a6535949c7cce2650a67dd7d364" + "reference": "d4c3325d231deceecdc33fa0b3698efd132ce9f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/orchestral/canvas-core/zipball/d44ffd6895685a6535949c7cce2650a67dd7d364", - "reference": "d44ffd6895685a6535949c7cce2650a67dd7d364", + "url": "https://api.github.com/repos/orchestral/canvas-core/zipball/d4c3325d231deceecdc33fa0b3698efd132ce9f0", + "reference": "d4c3325d231deceecdc33fa0b3698efd132ce9f0", "shasum": "" }, "require": { "composer-runtime-api": "^2.2", "composer/semver": "^3.0", - "illuminate/console": "^10.17", - "illuminate/filesystem": "^10.17", - "php": "^8.1" + "illuminate/console": "^10.26", + "illuminate/filesystem": "^10.26", + "php": "^8.1", + "symfony/polyfill-php83": "^1.28" }, "conflict": { - "orchestra/canvas": "<8.9.0", + "orchestra/canvas": "<8.11.0", "orchestra/testbench-core": "<8.2.0" }, "require-dev": { - "fakerphp/faker": "^1.21", - "laravel/framework": "^10.17", + "laravel/framework": "^10.26", "laravel/pint": "^1.6", "mockery/mockery": "^1.5.1", - "orchestra/testbench-core": "^8.11", - "orchestra/workbench": "^0.3", + "orchestra/testbench-core": "^8.15", "phpstan/phpstan": "^1.10.6", "phpunit/phpunit": "^10.1", "symfony/yaml": "^6.2" @@ -8710,34 +8797,33 @@ "description": "Code Generators Builder for Laravel Applications and Packages", "support": { "issues": "https://github.com/orchestral/canvas/issues", - "source": "https://github.com/orchestral/canvas-core/tree/v8.8.0" + "source": "https://github.com/orchestral/canvas-core/tree/v8.10.1" }, - "time": "2023-09-19T04:26:25+00:00" + "time": "2023-11-27T03:19:28+00:00" }, { "name": "orchestra/testbench", - "version": "v8.12.1", + "version": "v8.17.0", "source": { "type": "git", "url": "https://github.com/orchestral/testbench.git", - "reference": "9bae3c28789295db875eee0d3395c4ba23f1e5ae" + "reference": "3bbe0956df4b6f811ca879e2a8d69a8a8b626587" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/orchestral/testbench/zipball/9bae3c28789295db875eee0d3395c4ba23f1e5ae", - "reference": "9bae3c28789295db875eee0d3395c4ba23f1e5ae", + "url": "https://api.github.com/repos/orchestral/testbench/zipball/3bbe0956df4b6f811ca879e2a8d69a8a8b626587", + "reference": "3bbe0956df4b6f811ca879e2a8d69a8a8b626587", "shasum": "" }, "require": { "composer-runtime-api": "^2.2", "fakerphp/faker": "^1.21", - "laravel/framework": ">=10.23.1 <10.26.0", + "laravel/framework": "^10.23.1", "mockery/mockery": "^1.5.1", - "orchestra/testbench-core": ">=8.12.0 <8.13.0", - "orchestra/workbench": "^0.4.0", + "orchestra/testbench-core": "^8.17", + "orchestra/workbench": "^1.2 || ^8.2", "php": "^8.1", "phpunit/phpunit": "^9.6 || ^10.1", - "spatie/laravel-ray": "^1.32.4", "symfony/process": "^6.2", "symfony/yaml": "^6.2", "vlucas/phpdotenv": "^5.4.1" @@ -8766,27 +8852,35 @@ ], "support": { "issues": "https://github.com/orchestral/testbench/issues", - "source": "https://github.com/orchestral/testbench/tree/v8.12.1" + "source": "https://github.com/orchestral/testbench/tree/v8.17.0" }, - "time": "2023-09-25T14:33:44+00:00" + "time": "2023-12-06T03:25:17+00:00" }, { "name": "orchestra/testbench-core", - "version": "v8.12.1", + "version": "v8.17.1", "source": { "type": "git", "url": "https://github.com/orchestral/testbench-core.git", - "reference": "9a7b63f9cd10dd15cf7c9d4aad2ccaa688465a1f" + "reference": "6856e979790c18705812b2b6c3c73bb1f6d8baa8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/9a7b63f9cd10dd15cf7c9d4aad2ccaa688465a1f", - "reference": "9a7b63f9cd10dd15cf7c9d4aad2ccaa688465a1f", + "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/6856e979790c18705812b2b6c3c73bb1f6d8baa8", + "reference": "6856e979790c18705812b2b6c3c73bb1f6d8baa8", "shasum": "" }, "require": { "composer-runtime-api": "^2.2", - "php": "^8.1" + "php": "^8.1", + "symfony/polyfill-php83": "^1.28" + }, + "conflict": { + "brianium/paratest": "<6.4.0 || >=7.0.0 <7.1.4 || >=8.0.0", + "laravel/framework": "<10.23.1 || >=11.0.0", + "nunomaduro/collision": "<6.4.0 || >=7.0.0 <7.4.0 || >=8.0.0", + "orchestra/workbench": "<1.0.0", + "phpunit/phpunit": "<9.6.0 || >=10.6.0" }, "require-dev": { "fakerphp/faker": "^1.21", @@ -8801,7 +8895,7 @@ "vlucas/phpdotenv": "^5.4.1" }, "suggest": { - "brianium/paratest": "Allow using parallel tresting (^6.4 || ^7.1.4).", + "brianium/paratest": "Allow using parallel testing (^6.4 || ^7.1.4).", "fakerphp/faker": "Allow using Faker for testing (^1.21).", "laravel/framework": "Required for testing (^10.23).", "mockery/mockery": "Allow using Mockery for testing (^1.5.1).", @@ -8849,45 +8943,45 @@ "issues": "https://github.com/orchestral/testbench/issues", "source": "https://github.com/orchestral/testbench-core" }, - "time": "2023-09-26T12:50:24+00:00" + "time": "2023-12-06T08:00:50+00:00" }, { "name": "orchestra/workbench", - "version": "v0.4.1", + "version": "v8.2.0", "source": { "type": "git", "url": "https://github.com/orchestral/workbench.git", - "reference": "21acf1015ac48e36cb468bffd161115689958782" + "reference": "63b3961efa515abfcfd340805e6713eb540b7a39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/orchestral/workbench/zipball/21acf1015ac48e36cb468bffd161115689958782", - "reference": "21acf1015ac48e36cb468bffd161115689958782", + "url": "https://api.github.com/repos/orchestral/workbench/zipball/63b3961efa515abfcfd340805e6713eb540b7a39", + "reference": "63b3961efa515abfcfd340805e6713eb540b7a39", "shasum": "" }, "require": { "composer-runtime-api": "^2.2", - "illuminate/console": "^9.52.15 || ^10.23.0", - "illuminate/support": "^9.52.15 || ^10.23.0", + "fakerphp/faker": "^1.21", + "laravel/framework": "^10.26", "laravel/tinker": "^2.8.2", - "orchestra/canvas": "^7.10.0 || ^8.9.0", - "orchestra/testbench-core": "^7.32.0 || ^8.12.0", - "php": "^8.0" + "orchestra/canvas": "^8.11.4", + "orchestra/testbench-core": "^8.17", + "php": "^8.1", + "spatie/laravel-ray": "^1.32.4", + "symfony/polyfill-php83": "^1.28", + "symfony/yaml": "^6.2" }, "require-dev": { - "fakerphp/faker": "^1.21", - "laravel/framework": "^9.52.15 || ^10.23.0", "laravel/pint": "^1.4", "mockery/mockery": "^1.5.1", "phpstan/phpstan": "^1.10.7", - "phpunit/phpunit": "^9.6", - "spatie/laravel-ray": "^1.32.4", - "symfony/yaml": "^6.0.9" + "phpunit/phpunit": "^10.1", + "symfony/process": "^6.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.4.x-dev" + "dev-master": "0.5.x-dev" } }, "autoload": { @@ -8914,9 +9008,9 @@ ], "support": { "issues": "https://github.com/orchestral/workbench/issues", - "source": "https://github.com/orchestral/workbench/tree/v0.4.1" + "source": "https://github.com/orchestral/workbench/tree/v8.2.0" }, - "time": "2023-09-26T13:04:34+00:00" + "time": "2023-12-06T02:59:43+00:00" }, { "name": "phar-io/manifest", @@ -9199,16 +9293,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.24.2", + "version": "1.24.4", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "bcad8d995980440892759db0c32acae7c8e79442" + "reference": "6bd0c26f3786cd9b7c359675cb789e35a8e07496" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442", - "reference": "bcad8d995980440892759db0c32acae7c8e79442", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6bd0c26f3786cd9b7c359675cb789e35a8e07496", + "reference": "6bd0c26f3786cd9b7c359675cb789e35a8e07496", "shasum": "" }, "require": { @@ -9240,22 +9334,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.4" }, - "time": "2023-09-26T12:28:12+00:00" + "time": "2023-11-26T18:29:22+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.6", + "version": "10.1.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "56f33548fe522c8d82da7ff3824b42829d324364" + "reference": "a56a9ab2f680246adcf3db43f38ddf1765774735" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/56f33548fe522c8d82da7ff3824b42829d324364", - "reference": "56f33548fe522c8d82da7ff3824b42829d324364", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/a56a9ab2f680246adcf3db43f38ddf1765774735", + "reference": "a56a9ab2f680246adcf3db43f38ddf1765774735", "shasum": "" }, "require": { @@ -9312,7 +9406,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.6" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.9" }, "funding": [ { @@ -9320,7 +9414,7 @@ "type": "github" } ], - "time": "2023-09-19T04:59:03+00:00" + "time": "2023-11-23T12:23:20+00:00" }, { "name": "phpunit/php-file-iterator", @@ -9567,16 +9661,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.3.5", + "version": "10.5.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503" + "reference": "5aedff46afba98dddecaa12349ec044d9103d4fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/747c3b2038f1139e3dcd9886a3f5a948648b7503", - "reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5aedff46afba98dddecaa12349ec044d9103d4fe", + "reference": "5aedff46afba98dddecaa12349ec044d9103d4fe", "shasum": "" }, "require": { @@ -9616,7 +9710,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.3-dev" + "dev-main": "10.5-dev" } }, "autoload": { @@ -9648,7 +9742,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.5" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.2" }, "funding": [ { @@ -9664,7 +9758,7 @@ "type": "tidelift" } ], - "time": "2023-09-19T05:42:37+00:00" + "time": "2023-12-05T14:54:33+00:00" }, { "name": "pimple/pimple", @@ -10038,16 +10132,16 @@ }, { "name": "sebastian/complexity", - "version": "3.0.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "c70b73893e10757af9c6a48929fa6a333b56a97a" + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/c70b73893e10757af9c6a48929fa6a333b56a97a", - "reference": "c70b73893e10757af9c6a48929fa6a333b56a97a", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957", "shasum": "" }, "require": { @@ -10060,7 +10154,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.1-dev" } }, "autoload": { @@ -10084,7 +10178,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" }, "funding": [ { @@ -10092,7 +10186,7 @@ "type": "github" } ], - "time": "2023-08-31T09:55:53+00:00" + "time": "2023-09-28T11:50:59+00:00" }, { "name": "sebastian/diff", @@ -10709,16 +10803,16 @@ }, { "name": "spatie/array-to-xml", - "version": "3.2.0", + "version": "3.2.2", "source": { "type": "git", "url": "https://github.com/spatie/array-to-xml.git", - "reference": "f9ab39c808500c347d5a8b6b13310bd5221e39e7" + "reference": "96be97e664c87613121d073ea39af4c74e57a7f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/f9ab39c808500c347d5a8b6b13310bd5221e39e7", - "reference": "f9ab39c808500c347d5a8b6b13310bd5221e39e7", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/96be97e664c87613121d073ea39af4c74e57a7f8", + "reference": "96be97e664c87613121d073ea39af4c74e57a7f8", "shasum": "" }, "require": { @@ -10756,7 +10850,7 @@ "xml" ], "support": { - "source": "https://github.com/spatie/array-to-xml/tree/3.2.0" + "source": "https://github.com/spatie/array-to-xml/tree/3.2.2" }, "funding": [ { @@ -10768,7 +10862,7 @@ "type": "github" } ], - "time": "2023-07-19T18:30:26+00:00" + "time": "2023-11-14T14:08:51+00:00" }, { "name": "spatie/backtrace", @@ -10969,16 +11063,16 @@ }, { "name": "spatie/ray", - "version": "1.39.0", + "version": "1.40.1", "source": { "type": "git", "url": "https://github.com/spatie/ray.git", - "reference": "7ab6bd01dc6a8ecdd836b3182d40a04308ae0c75" + "reference": "8e6547ff47aae2e4f615a5dcea1e5e4911b1dc9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ray/zipball/7ab6bd01dc6a8ecdd836b3182d40a04308ae0c75", - "reference": "7ab6bd01dc6a8ecdd836b3182d40a04308ae0c75", + "url": "https://api.github.com/repos/spatie/ray/zipball/8e6547ff47aae2e4f615a5dcea1e5e4911b1dc9f", + "reference": "8e6547ff47aae2e4f615a5dcea1e5e4911b1dc9f", "shasum": "" }, "require": { @@ -11029,7 +11123,7 @@ ], "support": { "issues": "https://github.com/spatie/ray/issues", - "source": "https://github.com/spatie/ray/tree/1.39.0" + "source": "https://github.com/spatie/ray/tree/1.40.1" }, "funding": [ { @@ -11041,20 +11135,20 @@ "type": "other" } ], - "time": "2023-09-18T10:36:07+00:00" + "time": "2023-11-20T08:20:15+00:00" }, { "name": "symfony/filesystem", - "version": "v6.3.1", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae" + "reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", - "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/952a8cb588c3bc6ce76f6023000fb932f16a6e59", + "reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59", "shasum": "" }, "require": { @@ -11088,7 +11182,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.3.1" + "source": "https://github.com/symfony/filesystem/tree/v6.4.0" }, "funding": [ { @@ -11104,20 +11198,20 @@ "type": "tidelift" } ], - "time": "2023-06-01T08:30:39+00:00" + "time": "2023-07-26T17:27:13+00:00" }, { "name": "symfony/options-resolver", - "version": "v6.3.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd" + "reference": "22301f0e7fdeaacc14318928612dee79be99860e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a10f19f5198d589d5c33333cffe98dc9820332dd", - "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/22301f0e7fdeaacc14318928612dee79be99860e", + "reference": "22301f0e7fdeaacc14318928612dee79be99860e", "shasum": "" }, "require": { @@ -11155,7 +11249,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.3.0" + "source": "https://github.com/symfony/options-resolver/tree/v6.4.0" }, "funding": [ { @@ -11171,7 +11265,7 @@ "type": "tidelift" } ], - "time": "2023-05-12T14:21:09+00:00" + "time": "2023-08-08T10:16:24+00:00" }, { "name": "symfony/polyfill-iconv", @@ -11337,7 +11431,7 @@ }, { "name": "symfony/stopwatch", - "version": "v6.3.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -11379,7 +11473,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v6.3.0" + "source": "https://github.com/symfony/stopwatch/tree/v6.4.0" }, "funding": [ { @@ -11399,16 +11493,16 @@ }, { "name": "symfony/yaml", - "version": "v6.3.3", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "e23292e8c07c85b971b44c1c4b87af52133e2add" + "reference": "4f9237a1bb42455d609e6687d2613dde5b41a587" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/e23292e8c07c85b971b44c1c4b87af52133e2add", - "reference": "e23292e8c07c85b971b44c1c4b87af52133e2add", + "url": "https://api.github.com/repos/symfony/yaml/zipball/4f9237a1bb42455d609e6687d2613dde5b41a587", + "reference": "4f9237a1bb42455d609e6687d2613dde5b41a587", "shasum": "" }, "require": { @@ -11420,7 +11514,7 @@ "symfony/console": "<5.4" }, "require-dev": { - "symfony/console": "^5.4|^6.0" + "symfony/console": "^5.4|^6.0|^7.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -11451,7 +11545,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.3.3" + "source": "https://github.com/symfony/yaml/tree/v6.4.0" }, "funding": [ { @@ -11467,20 +11561,20 @@ "type": "tidelift" } ], - "time": "2023-07-31T07:08:24+00:00" + "time": "2023-11-06T11:00:25+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", "shasum": "" }, "require": { @@ -11509,7 +11603,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.2" }, "funding": [ { @@ -11517,20 +11611,20 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2023-11-20T00:12:19+00:00" }, { "name": "vimeo/psalm", - "version": "5.15.0", + "version": "5.17.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "5c774aca4746caf3d239d9c8cadb9f882ca29352" + "reference": "c620f6e80d0abfca532b00bda366062aaedf6e5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/5c774aca4746caf3d239d9c8cadb9f882ca29352", - "reference": "5c774aca4746caf3d239d9c8cadb9f882ca29352", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/c620f6e80d0abfca532b00bda366062aaedf6e5d", + "reference": "c620f6e80d0abfca532b00bda366062aaedf6e5d", "shasum": "" }, "require": { @@ -11555,8 +11649,8 @@ "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", "sebastian/diff": "^4.0 || ^5.0", "spatie/array-to-xml": "^2.17.0 || ^3.0", - "symfony/console": "^4.1.6 || ^5.0 || ^6.0", - "symfony/filesystem": "^5.4 || ^6.0" + "symfony/console": "^4.1.6 || ^5.0 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0" }, "conflict": { "nikic/php-parser": "4.17.0" @@ -11578,7 +11672,7 @@ "psalm/plugin-phpunit": "^0.18", "slevomat/coding-standard": "^8.4", "squizlabs/php_codesniffer": "^3.6", - "symfony/process": "^4.4 || ^5.0 || ^6.0" + "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0" }, "suggest": { "ext-curl": "In order to send data to shepherd", @@ -11591,7 +11685,7 @@ "psalm-refactor", "psalter" ], - "type": "library", + "type": "project", "extra": { "branch-alias": { "dev-master": "5.x-dev", @@ -11623,10 +11717,11 @@ "static analysis" ], "support": { + "docs": "https://psalm.dev/docs", "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/5.15.0" + "source": "https://github.com/vimeo/psalm" }, - "time": "2023-08-20T23:07:30+00:00" + "time": "2023-12-03T20:21:41+00:00" }, { "name": "zbateson/mail-mime-parser", @@ -11854,5 +11949,5 @@ "platform-overrides": { "php": "8.1.13" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.2.0" } From 48e2fb2c48b01e0e721a6dafbd91a60cd288ddf7 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Wed, 6 Dec 2023 16:06:27 +0100 Subject: [PATCH 03/64] Fix precomputed password hashes in tests --- database/factories/ApiTokenFactory.php | 4 +- database/factories/UserFactory.php | 4 +- .../Api/ApiTokenControllerTest.php | 8 ++-- .../Controllers/Api/UserControllerTest.php | 44 +++++++++---------- .../Http/Controllers/Auth/ControllerTest.php | 8 ++-- .../Http/Middleware/VerifyCsrfTokenTest.php | 4 +- tests/php/Services/Auth/ApiGuardTest.php | 20 ++++----- 7 files changed, 46 insertions(+), 46 deletions(-) diff --git a/database/factories/ApiTokenFactory.php b/database/factories/ApiTokenFactory.php index 8cfbf76e2..493e67e11 100644 --- a/database/factories/ApiTokenFactory.php +++ b/database/factories/ApiTokenFactory.php @@ -17,8 +17,8 @@ public function definition() return [ 'owner_id' => User::factory(), 'purpose' => $this->faker->sentence(), - // 'password' - 'hash' => '$2y$10$CD13uR2iKSZ2Eyuro5H4yu9sflwe/AA2GAJsdrzRyKnkV9qaz1FaK', + // 'password', hashed with 4 rounds as defined in phpunit.xml + 'password' => '$2y$04$aqV2XBF34eexL9ezbQZs1eM872NWgH5MhvrmD0SC9qUbhmg9EoxJq', ]; } } diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 7e40765c4..e5180945a 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -18,8 +18,8 @@ public function definition(): array return [ 'firstname' => $this->faker->firstName(), 'lastname' => $this->faker->lastName(), - // 'password' - 'password' => '$2y$10$CD13uR2iKSZ2Eyuro5H4yu9sflwe/AA2GAJsdrzRyKnkV9qaz1FaK', + // 'password', hashed with 4 rounds as defined in phpunit.xml + 'password' => '$2y$04$aqV2XBF34eexL9ezbQZs1eM872NWgH5MhvrmD0SC9qUbhmg9EoxJq', 'email' => $this->faker->unique()->email(), 'remember_token' => Str::random(10), 'uuid' => $this->faker->unique()->uuid(), diff --git a/tests/php/Http/Controllers/Api/ApiTokenControllerTest.php b/tests/php/Http/Controllers/Api/ApiTokenControllerTest.php index 5d4bc1f66..04ac07497 100644 --- a/tests/php/Http/Controllers/Api/ApiTokenControllerTest.php +++ b/tests/php/Http/Controllers/Api/ApiTokenControllerTest.php @@ -30,8 +30,8 @@ public function testIndex() public function testStoreWithToken() { $token = ApiTokenTest::create([ - // 'test_token' - 'hash' => '$2y$10$.rR7YrU9K2ZR4xgPbKs1x.AGUUKIA733CT72eC6I2piTiPY59V7.O', + // 'test_token', hashed with 4 rounds as defined in phpunit.xml + 'hash' => '$2y$04$9Ncj6qJVqenJ13VtdtV5yOca8rQyN1UwATdGpAQ80FeRjS67.Efaq', ]); $response = $this->call('POST', '/api/v1/api-tokens', [], [], [], [ 'PHP_AUTH_USER' => $token->owner->email, @@ -46,8 +46,8 @@ public function testStore() $this->doTestApiRoute('POST', '/api/v1/api-tokens'); $token = ApiTokenTest::create([ - // 'test_token' - 'hash' => '$2y$10$.rR7YrU9K2ZR4xgPbKs1x.AGUUKIA733CT72eC6I2piTiPY59V7.O', + // 'test_token', hashed with 4 rounds as defined in phpunit.xml + 'hash' => '$2y$04$9Ncj6qJVqenJ13VtdtV5yOca8rQyN1UwATdGpAQ80FeRjS67.Efaq', ]); $this->be($token->owner); diff --git a/tests/php/Http/Controllers/Api/UserControllerTest.php b/tests/php/Http/Controllers/Api/UserControllerTest.php index 70d10267b..642ce3de5 100644 --- a/tests/php/Http/Controllers/Api/UserControllerTest.php +++ b/tests/php/Http/Controllers/Api/UserControllerTest.php @@ -16,8 +16,8 @@ class UserControllerTest extends ApiTestCase private function callToken($verb, $route, $user) { $token = ApiTokenTest::create([ - // 'test_token' - 'hash' => '$2y$10$.rR7YrU9K2ZR4xgPbKs1x.AGUUKIA733CT72eC6I2piTiPY59V7.O', + // 'test_token', hashed with 4 rounds as defined in phpunit.xml + 'hash' => '$2y$04$9Ncj6qJVqenJ13VtdtV5yOca8rQyN1UwATdGpAQ80FeRjS67.Efaq', 'owner_id' => $user->id, ]); @@ -116,8 +116,8 @@ public function testUpdate() $response = $this->put('/api/v1/users/'.$this->guest()->id); $response->assertStatus(403); - // 'adminpassword' - $this->globalAdmin()->password = '$2y$10$O/OuPUHuswXD.6LRVUeHueY5hbiFkHVFaPLcdOd.sp3U9C8H9dcJS'; + // 'adminpassword', hashed with 4 rounds as defined in phpunit.xml + $this->globalAdmin()->password = '$2y$04$Cwx.818Z0GgxhFxF3JN4Rejpuu9M0vBChtZTRCcgSASN.xl0TmM8a'; $this->globalAdmin()->save(); $this->beGlobalAdmin(); @@ -234,8 +234,8 @@ public function testUpdate() public function testUpdateEmailCaseInsensitive() { - // 'adminpassword' - $this->globalAdmin()->password = '$2y$10$O/OuPUHuswXD.6LRVUeHueY5hbiFkHVFaPLcdOd.sp3U9C8H9dcJS'; + // 'adminpassword', hashed with 4 rounds as defined in phpunit.xml + $this->globalAdmin()->password = '$2y$04$Cwx.818Z0GgxhFxF3JN4Rejpuu9M0vBChtZTRCcgSASN.xl0TmM8a'; $this->globalAdmin()->save(); $this->beGlobalAdmin(); @@ -274,8 +274,8 @@ public function testUpdateAffiliation() public function testUpdateRole() { $user = $this->guest(); - // 'adminpassword' - $this->globalAdmin()->password = '$2y$10$O/OuPUHuswXD.6LRVUeHueY5hbiFkHVFaPLcdOd.sp3U9C8H9dcJS'; + // 'adminpassword', hashed with 4 rounds as defined in phpunit.xml + $this->globalAdmin()->password = '$2y$04$Cwx.818Z0GgxhFxF3JN4Rejpuu9M0vBChtZTRCcgSASN.xl0TmM8a'; $this->globalAdmin()->save(); $this->beGlobalAdmin(); $this @@ -308,8 +308,8 @@ public function testUpdateRole() public function testUpdateCanReview() { - // 'adminpassword' - $this->globalAdmin()->password = '$2y$10$O/OuPUHuswXD.6LRVUeHueY5hbiFkHVFaPLcdOd.sp3U9C8H9dcJS'; + // 'adminpassword', hashed with 4 rounds as defined in phpunit.xml + $this->globalAdmin()->password = '$2y$04$Cwx.818Z0GgxhFxF3JN4Rejpuu9M0vBChtZTRCcgSASN.xl0TmM8a'; $this->globalAdmin()->save(); $this->beGlobalAdmin(); @@ -344,8 +344,8 @@ public function testUpdateCanReview() public function testDowngradeRoleWithCanReview() { - // 'adminpassword' - $this->globalAdmin()->password = '$2y$10$O/OuPUHuswXD.6LRVUeHueY5hbiFkHVFaPLcdOd.sp3U9C8H9dcJS'; + // 'adminpassword', hashed with 4 rounds as defined in phpunit.xml + $this->globalAdmin()->password = '$2y$04$Cwx.818Z0GgxhFxF3JN4Rejpuu9M0vBChtZTRCcgSASN.xl0TmM8a'; $this->globalAdmin()->save(); $this->beGlobalAdmin(); $user = $this->user(); @@ -378,8 +378,8 @@ public function testUpdateOwnWithToken() public function testUpdateOwn() { - // 'guest-password' - $this->guest()->password = '$2y$10$X/s/ecsLboxkL7T/WQzI.emOeMDXIFjj2jVXEMAK1im.7IHwT0VWi'; + // 'guest-password', hashed with 4 rounds as defined in phpunit.xml + $this->guest()->password = '$2y$04$j3f9h84KswH3h30Q1CnXZuthgMt569YJdOo2NCWpS4AdLlj3emupO'; $this->guest()->save(); $this->doTestApiRoute('PUT', '/api/v1/users/my'); @@ -428,8 +428,8 @@ public function testUpdateOwn() public function testUpdateOwnEmailCaseInsensitive() { - // 'guest-password' - $this->guest()->password = '$2y$10$X/s/ecsLboxkL7T/WQzI.emOeMDXIFjj2jVXEMAK1im.7IHwT0VWi'; + // 'guest-password', hashed with 4 rounds as defined in phpunit.xml + $this->guest()->password = '$2y$04$j3f9h84KswH3h30Q1CnXZuthgMt569YJdOo2NCWpS4AdLlj3emupO'; $this->guest()->save(); $this->beGuest(); @@ -600,8 +600,8 @@ public function testDestroyWithToken() public function testDestroy() { - // 'globalAdmin-password' - $this->globalAdmin()->password = '$2y$10$44FMBIkBS2hNhI09ep6UMen7fDT4/RuQKNtDTOPRCvhQxg2H0TXIm'; + // 'globalAdmin-password', hashed with 4 rounds as defined in phpunit.xml + $this->globalAdmin()->password = '$2y$04$RQljsDh/mpnnPcYMAR622ueuqmNEvucy9vMT/nQyJ.jPnFWpErzIS'; $this->globalAdmin()->save(); $id = $this->guest()->id; @@ -664,11 +664,11 @@ public function testDestroyOwnWithToken() public function testDestroyOwn() { - // 'guest-password' - $this->guest()->password = '$2y$10$X/s/ecsLboxkL7T/WQzI.emOeMDXIFjj2jVXEMAK1im.7IHwT0VWi'; + // 'guest-password', hashed with 4 rounds as defined in phpunit.xml + $this->guest()->password = '$2y$04$j3f9h84KswH3h30Q1CnXZuthgMt569YJdOo2NCWpS4AdLlj3emupO'; $this->guest()->save(); - // 'editor-password' - $this->editor()->password = '$2y$10$2BwVwEw1AOzWx01twMmHg.FHp5N/TrK1X0KtHxRH0MN5CRrpc6k46'; + // 'editor-password', hashed with 4 rounds as defined in phpunit.xml + $this->editor()->password = '$2y$04$TwIgO65v19BE9x9osMl9zeV.FX4.ZnJ/Tm9.nd.vrozYIoKMmfWme'; $this->guest()->save(); $this->doTestApiRoute('DELETE', '/api/v1/users/my'); diff --git a/tests/php/Http/Controllers/Auth/ControllerTest.php b/tests/php/Http/Controllers/Auth/ControllerTest.php index 01dc486e8..b35eaec64 100644 --- a/tests/php/Http/Controllers/Auth/ControllerTest.php +++ b/tests/php/Http/Controllers/Auth/ControllerTest.php @@ -56,8 +56,8 @@ public function testLoginSuccess() { $user = UserTest::create([ 'email' => 'test@test.com', - // 'password' - 'password' => '$2y$10$EEcVvtsqcG3cscQC9UE5.uLkWRM7IrsqPBiSPhtbslfnx9KdJtVMG', + // 'password', hashed with 4 rounds as defined in phpunit.xml + 'password' => '$2y$04$aqV2XBF34eexL9ezbQZs1eM872NWgH5MhvrmD0SC9qUbhmg9EoxJq', ]); // login_at attribute should be null after creation $this->assertNull($user->login_at); @@ -78,8 +78,8 @@ public function testLoginCaseInsensitive() { $user = UserTest::create([ 'email' => 'test@test.com', - // 'password' - 'password' => '$2y$10$EEcVvtsqcG3cscQC9UE5.uLkWRM7IrsqPBiSPhtbslfnx9KdJtVMG', + // 'password', hashed with 4 rounds as defined in phpunit.xml + 'password' => '$2y$04$aqV2XBF34eexL9ezbQZs1eM872NWgH5MhvrmD0SC9qUbhmg9EoxJq', ]); $response = $response = $this->post('/login', [ diff --git a/tests/php/Http/Middleware/VerifyCsrfTokenTest.php b/tests/php/Http/Middleware/VerifyCsrfTokenTest.php index bcff25582..22caf925d 100644 --- a/tests/php/Http/Middleware/VerifyCsrfTokenTest.php +++ b/tests/php/Http/Middleware/VerifyCsrfTokenTest.php @@ -19,8 +19,8 @@ public function setUp(): void App::bind(VerifyCsrfToken::class, VerifyCsrfTokenStub::class); $this->token = ApiTokenTest::create([ - // 'test_token' - 'hash' => '$2y$10$.rR7YrU9K2ZR4xgPbKs1x.AGUUKIA733CT72eC6I2piTiPY59V7.O', + // 'test_token', hashed with 4 rounds as defined in phpunit.xml + 'hash' => '$2y$04$9Ncj6qJVqenJ13VtdtV5yOca8rQyN1UwATdGpAQ80FeRjS67.Efaq', 'owner_id' => $this->globalAdmin()->id, ]); } diff --git a/tests/php/Services/Auth/ApiGuardTest.php b/tests/php/Services/Auth/ApiGuardTest.php index 6e29895b4..898331380 100644 --- a/tests/php/Services/Auth/ApiGuardTest.php +++ b/tests/php/Services/Auth/ApiGuardTest.php @@ -24,8 +24,8 @@ public function testNoCredentialsJson() public function testWrongCredentials() { $token = ApiTokenTest::create([ - // 'test_token' - 'hash' => '$2y$10$.rR7YrU9K2ZR4xgPbKs1x.AGUUKIA733CT72eC6I2piTiPY59V7.O', + // 'test_token', hashed with 4 rounds as defined in phpunit.xml + 'hash' => '$2y$04$9Ncj6qJVqenJ13VtdtV5yOca8rQyN1UwATdGpAQ80FeRjS67.Efaq', ]); $response = $this->json('GET', '/api/v1/users', [], [ 'PHP_AUTH_USER' => $token->owner->email, @@ -53,8 +53,8 @@ public function testNoToken() public function testSuccess() { $token = ApiTokenTest::create([ - // 'test_token' - 'hash' => '$2y$10$.rR7YrU9K2ZR4xgPbKs1x.AGUUKIA733CT72eC6I2piTiPY59V7.O', + // 'test_token', hashed with 4 rounds as defined in phpunit.xml + 'hash' => '$2y$04$9Ncj6qJVqenJ13VtdtV5yOca8rQyN1UwATdGpAQ80FeRjS67.Efaq', ]); $response = $this->call('GET', '/api/v1/users', [], [], [], [ 'PHP_AUTH_USER' => $token->owner->email, @@ -64,8 +64,8 @@ public function testSuccess() $token2 = ApiTokenTest::create([ 'owner_id' => $token->owner->id, - // 'test_token2' - 'hash' => '$2y$10$bqKeHzuH0hf9gIOUBnzd0ezQkVkUU12faCOu2twnBguONfx8.XhlO', + // 'test_token2', hashed with 4 rounds as defined in phpunit.xml + 'hash' => '$2y$04$YO8JZPK35rlk48cFprp8luo9lN/SPZaKeYjnhM2IpLMWpP8anC08e', ]); $response = $this->json('GET', '/api/v1/users', [], [ @@ -78,8 +78,8 @@ public function testSuccess() public function testEmailCaseInsensitive() { $token = ApiTokenTest::create([ - // 'test_token' - 'hash' => '$2y$10$.rR7YrU9K2ZR4xgPbKs1x.AGUUKIA733CT72eC6I2piTiPY59V7.O', + // 'test_token', hashed with 4 rounds as defined in phpunit.xml + 'hash' => '$2y$04$9Ncj6qJVqenJ13VtdtV5yOca8rQyN1UwATdGpAQ80FeRjS67.Efaq', ]); $token->owner->email = 'test@test.com'; @@ -95,8 +95,8 @@ public function testEmailCaseInsensitive() public function testTouchToken() { $token = ApiTokenTest::create([ - // 'test_token' - 'hash' => '$2y$10$.rR7YrU9K2ZR4xgPbKs1x.AGUUKIA733CT72eC6I2piTiPY59V7.O', + // 'test_token', hashed with 4 rounds as defined in phpunit.xml + 'hash' => '$2y$04$9Ncj6qJVqenJ13VtdtV5yOca8rQyN1UwATdGpAQ80FeRjS67.Efaq', ]); $token->updated_at = Carbon::now(-5); $token->save(); From c7c73c8f16b198d4cb6ec62dc708044db8fa3780 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Wed, 6 Dec 2023 16:08:31 +0100 Subject: [PATCH 04/64] Fix ApiTokenFactory --- database/factories/ApiTokenFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/factories/ApiTokenFactory.php b/database/factories/ApiTokenFactory.php index 493e67e11..85c242576 100644 --- a/database/factories/ApiTokenFactory.php +++ b/database/factories/ApiTokenFactory.php @@ -18,7 +18,7 @@ public function definition() 'owner_id' => User::factory(), 'purpose' => $this->faker->sentence(), // 'password', hashed with 4 rounds as defined in phpunit.xml - 'password' => '$2y$04$aqV2XBF34eexL9ezbQZs1eM872NWgH5MhvrmD0SC9qUbhmg9EoxJq', + 'hash' => '$2y$04$aqV2XBF34eexL9ezbQZs1eM872NWgH5MhvrmD0SC9qUbhmg9EoxJq', ]; } } From a573b26077f554a4ac947ca6674f01c7c5071120 Mon Sep 17 00:00:00 2001 From: Max Tiessen Date: Tue, 5 Dec 2023 11:17:34 +0100 Subject: [PATCH 05/64] create fileCount-method for the filters --- .../volumes/components/filterListComponent.vue | 17 ++++++++++++++++- resources/views/volumes/show/filters.blade.php | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/resources/assets/js/volumes/components/filterListComponent.vue b/resources/assets/js/volumes/components/filterListComponent.vue index eb84345a4..ea7b4f7e2 100644 --- a/resources/assets/js/volumes/components/filterListComponent.vue +++ b/resources/assets/js/volumes/components/filterListComponent.vue @@ -18,16 +18,31 @@ export default { type: Object, required: true, }, + type: { + type: String, + required: true, + }, }, data() { return { name: this.rule.id, }; }, + methods: { + createFileCount(rule) { + let typeForm = rule.sequence.length === 1 ? `${this.type}` : `${this.type}s`; + return `(${rule.sequence.length} ${typeForm})`; + }, + }, computed: { dataName() { if (this.rule.data) { - return this.rule.data.name; + let fileCount = this.createFileCount(this.rule); + if(this.rule.data.name) { + return `${this.rule.data.name} ${fileCount}`; + } else { + return fileCount; + } } return ''; diff --git a/resources/views/volumes/show/filters.blade.php b/resources/views/volumes/show/filters.blade.php index e51978ecd..53dc68fb5 100644 --- a/resources/views/volumes/show/filters.blade.php +++ b/resources/views/volumes/show/filters.blade.php @@ -36,7 +36,7 @@
  • - +
  • No filter rules
From 2651e117617358848049b06f542cb0d23137ddc3 Mon Sep 17 00:00:00 2001 From: Max Tiessen Date: Tue, 5 Dec 2023 12:02:23 +0100 Subject: [PATCH 06/64] add an array-equality check in hasRule() --- resources/assets/js/volumes/components/filterTab.vue | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/assets/js/volumes/components/filterTab.vue b/resources/assets/js/volumes/components/filterTab.vue index 572f8bcd8..51d35f0b1 100644 --- a/resources/assets/js/volumes/components/filterTab.vue +++ b/resources/assets/js/volumes/components/filterTab.vue @@ -161,12 +161,20 @@ export default { return null; }, + equalityCheck(itemData, ruleData) { + if(itemData instanceof Array) { + return itemData.length === ruleData.length && + itemData.every((val, index) => val === ruleData[index]); + } else { + return item.data === rule.data; + } + }, hasRule(rule) { return this.rules.findIndex(function (item) { return item.id === rule.id && item.negate === rule.negate && - item.data === rule.data; - }) !== -1; + this.equalityCheck(item.data, rule.data); + }.bind(this)) !== -1; }, addRule(data) { if (!this.selectedFilter) return; From 5fb4d91e844920e83c8eea3c0879dbcee37d2e75 Mon Sep 17 00:00:00 2001 From: Max Tiessen Date: Tue, 5 Dec 2023 13:58:01 +0100 Subject: [PATCH 07/64] handle null values in equalityCheck --- .../assets/js/volumes/components/filterTab.vue | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/resources/assets/js/volumes/components/filterTab.vue b/resources/assets/js/volumes/components/filterTab.vue index 51d35f0b1..ee634a3f2 100644 --- a/resources/assets/js/volumes/components/filterTab.vue +++ b/resources/assets/js/volumes/components/filterTab.vue @@ -162,12 +162,15 @@ export default { return null; }, equalityCheck(itemData, ruleData) { - if(itemData instanceof Array) { - return itemData.length === ruleData.length && - itemData.every((val, index) => val === ruleData[index]); - } else { - return item.data === rule.data; + // handle Array + if(itemData !== null && ruleData !== null) { + if(itemData instanceof Array) { + return itemData.length === ruleData.length && + itemData.every((val, index) => val === ruleData[index]); + } } + // handle all other types (Objects, null) + return itemData === ruleData; }, hasRule(rule) { return this.rules.findIndex(function (item) { @@ -184,7 +187,7 @@ export default { data: data, negate: this.negate, }; - + if (this.hasRule(rule)) return; this.startLoading(); From 4b2007eca715eb4aecc628d1fb762d8adec108d5 Mon Sep 17 00:00:00 2001 From: mtiessen1175 Date: Thu, 7 Dec 2023 10:16:49 +0100 Subject: [PATCH 08/64] Apply code-linting from code review Co-authored-by: Martin Zurowietz --- resources/assets/js/volumes/components/filterTab.vue | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/assets/js/volumes/components/filterTab.vue b/resources/assets/js/volumes/components/filterTab.vue index ee634a3f2..3ce7ed60b 100644 --- a/resources/assets/js/volumes/components/filterTab.vue +++ b/resources/assets/js/volumes/components/filterTab.vue @@ -163,8 +163,8 @@ export default { }, equalityCheck(itemData, ruleData) { // handle Array - if(itemData !== null && ruleData !== null) { - if(itemData instanceof Array) { + if (itemData !== null && ruleData !== null) { + if (itemData instanceof Array) { return itemData.length === ruleData.length && itemData.every((val, index) => val === ruleData[index]); } @@ -187,7 +187,6 @@ export default { data: data, negate: this.negate, }; - if (this.hasRule(rule)) return; this.startLoading(); From 5cc56c7ba6acdd24a761e925357876b5d5d0b2f5 Mon Sep 17 00:00:00 2001 From: Max Tiessen Date: Thu, 7 Dec 2023 10:44:03 +0100 Subject: [PATCH 09/64] always show fileCount for filters --- .../components/filterListComponent.vue | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/resources/assets/js/volumes/components/filterListComponent.vue b/resources/assets/js/volumes/components/filterListComponent.vue index ea7b4f7e2..7a2bfdf03 100644 --- a/resources/assets/js/volumes/components/filterListComponent.vue +++ b/resources/assets/js/volumes/components/filterListComponent.vue @@ -2,7 +2,7 @@ without - + @@ -28,24 +28,21 @@ export default { name: this.rule.id, }; }, - methods: { - createFileCount(rule) { - let typeForm = rule.sequence.length === 1 ? `${this.type}` : `${this.type}s`; - return `(${rule.sequence.length} ${typeForm})`; - }, - }, computed: { - dataName() { + createFileCount() { + let typeForm = this.rule.sequence.length === 1 ? `${this.type}` : `${this.type}s`; + return `(${this.rule.sequence.length} ${typeForm})`; + }, + dataSpecs() { + let fileCount = this.createFileCount; + // if present, combine rule-name and filecount if (this.rule.data) { - let fileCount = this.createFileCount(this.rule); if(this.rule.data.name) { return `${this.rule.data.name} ${fileCount}`; - } else { - return fileCount; } } - return ''; + return fileCount; }, }, }; From 31a3ac0c6d69aa055eab1d8e539894177f1625ae Mon Sep 17 00:00:00 2001 From: Max Tiessen Date: Thu, 7 Dec 2023 11:37:24 +0100 Subject: [PATCH 10/64] modify name and structure of functions for better readability --- resources/assets/js/volumes/components/filterTab.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/assets/js/volumes/components/filterTab.vue b/resources/assets/js/volumes/components/filterTab.vue index 3ce7ed60b..8e1d6f4cd 100644 --- a/resources/assets/js/volumes/components/filterTab.vue +++ b/resources/assets/js/volumes/components/filterTab.vue @@ -161,7 +161,7 @@ export default { return null; }, - equalityCheck(itemData, ruleData) { + itemsAreEqual(itemData, ruleData) { // handle Array if (itemData !== null && ruleData !== null) { if (itemData instanceof Array) { @@ -173,11 +173,11 @@ export default { return itemData === ruleData; }, hasRule(rule) { - return this.rules.findIndex(function (item) { + return this.rules.findIndex((item) => { return item.id === rule.id && item.negate === rule.negate && - this.equalityCheck(item.data, rule.data); - }.bind(this)) !== -1; + this.itemsAreEqual(item.data, rule.data); + }) !== -1; }, addRule(data) { if (!this.selectedFilter) return; From 1ef89da38b1d78ceb711df7353dad43628757c08 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Fri, 8 Dec 2023 09:52:08 +0100 Subject: [PATCH 11/64] Update developing.md --- DEVELOPING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEVELOPING.md b/DEVELOPING.md index cb74a00df..f5fadd97f 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -34,7 +34,7 @@ composer create-project biigle/core:dev-dev-modules \ Note the `--ignore-platform-reqs` flag to keep Composer from complaining about missing requirements. These requirements will be met by the Docker containers. -This will set up the project in the `dev-modules` branch of this repository. The `dev-modules` branch is configured with all BIIGLE modules which makes it easy to start module development. If you want to develop a feature directly in `biigle/core`, please switch to the `master` branch (or create your own branch based on `master`). +This will set up the project in the `dev-modules` branch of this repository. The `dev-modules` branch is configured with all BIIGLE modules which makes it easy to start module development. If you want to develop a feature directly in `biigle/core`, please switch to the `master` branch (or create your own branch based on `master`). Pull requests from a `dev-modules`-based branch will not be accepted. ### 2. Build and run the application From 1fa5370913a55c67d1e97f58acab11d2274e6e34 Mon Sep 17 00:00:00 2001 From: Max Tiessen Date: Fri, 8 Dec 2023 16:09:13 +0100 Subject: [PATCH 12/64] split fileCount and dataName functions and add different style --- .../js/volumes/components/filterListComponent.vue | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/resources/assets/js/volumes/components/filterListComponent.vue b/resources/assets/js/volumes/components/filterListComponent.vue index 7a2bfdf03..092f31580 100644 --- a/resources/assets/js/volumes/components/filterListComponent.vue +++ b/resources/assets/js/volumes/components/filterListComponent.vue @@ -2,7 +2,8 @@ without - + + @@ -29,20 +30,16 @@ export default { }; }, computed: { - createFileCount() { + fileCount() { let typeForm = this.rule.sequence.length === 1 ? `${this.type}` : `${this.type}s`; return `(${this.rule.sequence.length} ${typeForm})`; }, - dataSpecs() { - let fileCount = this.createFileCount; - // if present, combine rule-name and filecount + dataName() { if (this.rule.data) { if(this.rule.data.name) { - return `${this.rule.data.name} ${fileCount}`; + return this.rule.data.name; } } - - return fileCount; }, }, }; From 6ff89d105b47575bcdfec8550a38ac8aa6b6165d Mon Sep 17 00:00:00 2001 From: Max Tiessen Date: Fri, 8 Dec 2023 16:11:49 +0100 Subject: [PATCH 13/64] remove unnecessary if-statement --- resources/assets/js/volumes/components/filterListComponent.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/assets/js/volumes/components/filterListComponent.vue b/resources/assets/js/volumes/components/filterListComponent.vue index 092f31580..b3f9c0a0e 100644 --- a/resources/assets/js/volumes/components/filterListComponent.vue +++ b/resources/assets/js/volumes/components/filterListComponent.vue @@ -3,7 +3,7 @@ without - + From 89153078193429bf7b341587e0d52885ce20771b Mon Sep 17 00:00:00 2001 From: Max Tiessen Date: Fri, 8 Dec 2023 16:14:05 +0100 Subject: [PATCH 14/64] add return value for absent data-name --- resources/assets/js/volumes/components/filterListComponent.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/assets/js/volumes/components/filterListComponent.vue b/resources/assets/js/volumes/components/filterListComponent.vue index b3f9c0a0e..24ad1315c 100644 --- a/resources/assets/js/volumes/components/filterListComponent.vue +++ b/resources/assets/js/volumes/components/filterListComponent.vue @@ -40,6 +40,8 @@ export default { return this.rule.data.name; } } + + return ''; }, }, }; From 5c248a7247be824be434b6adba38c582e951a2bf Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Tue, 12 Dec 2023 15:34:51 +0100 Subject: [PATCH 15/64] Remove separate vector database The default database is now used as vector database, too. References https://github.com/biigle/maia/pull/150 --- .env.example | 6 ----- .github/workflows/test.yml | 2 -- config/database.php | 15 ----------- ...2_08_03_000000_create_vector_extension.php | 15 +++++++++-- ...3_12_12_150300_create_vector_extension.php | 26 +++++++++++++++++++ docker-compose.yml | 26 +------------------ phpunit.xml | 4 --- tests/TestCase.php | 26 ------------------- 8 files changed, 40 insertions(+), 80 deletions(-) create mode 100644 database/migrations/2023_12_12_150300_create_vector_extension.php diff --git a/.env.example b/.env.example index c28ab4eec..95fc1dfb5 100644 --- a/.env.example +++ b/.env.example @@ -21,12 +21,6 @@ DB_DATABASE="biigle" DB_USERNAME="biigle" DB_PASSWORD="secret" -VECTOR_DB_HOST="vector_database" -VECTOR_DB_PORT=5432 -VECTOR_DB_DATABASE="biigle" -VECTOR_DB_USERNAME="biigle" -VECTOR_DB_PASSWORD="secret" - VOLUME_ADMIN_STORAGE_DISKS=local BROADCAST_DRIVER=pusher diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 186e9b977..20158218a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,6 @@ jobs: - name: Start test database run: | docker-compose up -d --no-build database_testing - docker-compose up -d --no-build vector_database_testing sleep 5 - name: Run tests @@ -80,7 +79,6 @@ jobs: - name: Start test database run: | docker-compose up -d --no-build database_testing - docker-compose up -d --no-build vector_database_testing sleep 5 - name: Run tests diff --git a/config/database.php b/config/database.php index b3a9e5583..90ce98207 100644 --- a/config/database.php +++ b/config/database.php @@ -78,21 +78,6 @@ 'sslmode' => 'prefer', ], - 'pgvector' => [ - 'driver' => 'pgsql', - 'url' => env('VECTOR_DATABASE_URL'), - 'host' => env('VECTOR_DB_HOST', 'localhost'), - 'port' => env('VECTOR_DB_PORT', '5432'), - 'database' => env('VECTOR_DB_DATABASE', 'forge'), - 'username' => env('VECTOR_DB_USERNAME', 'forge'), - 'password' => env('VECTOR_DB_PASSWORD', ''), - 'charset' => 'utf8', - 'prefix' => '', - 'prefix_indexes' => true, - 'search_path' => 'public', - 'sslmode' => 'prefer', - ], - 'sqlsrv' => [ 'driver' => 'sqlsrv', 'url' => env('DATABASE_URL'), diff --git a/database/migrations/2022_08_03_000000_create_vector_extension.php b/database/migrations/2022_08_03_000000_create_vector_extension.php index a81ef7679..e785a2415 100644 --- a/database/migrations/2022_08_03_000000_create_vector_extension.php +++ b/database/migrations/2022_08_03_000000_create_vector_extension.php @@ -11,7 +11,14 @@ */ public function up() { - DB::connection('pgvector')->statement('CREATE EXTENSION IF NOT EXISTS vector'); + // The pgvector connection was removed later so it may not always be present. + // There is a second migration that enables pgvector for the default connection. + // See: https://github.com/biigle/maia/pull/150 + if (!is_null(config('database.connections-pgvector'))) { + DB::connection('pgvector') + ->statement('CREATE EXTENSION IF NOT EXISTS vector'); + + } } /** @@ -21,6 +28,10 @@ public function up() */ public function down() { - DB::connection('pgvector')->statement('DROP EXTENSION IF EXISTS vector'); + if (!is_null(config('database.connections-pgvector'))) { + DB::connection('pgvector') + ->statement('DROP EXTENSION IF NOT EXISTS vector'); + + } } }; diff --git a/database/migrations/2023_12_12_150300_create_vector_extension.php b/database/migrations/2023_12_12_150300_create_vector_extension.php new file mode 100644 index 000000000..b53387f08 --- /dev/null +++ b/database/migrations/2023_12_12_150300_create_vector_extension.php @@ -0,0 +1,26 @@ + - - - - diff --git a/tests/TestCase.php b/tests/TestCase.php index ca99c5317..f074fe419 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -10,20 +10,10 @@ class TestCase extends BaseTestCase { use CreatesApplication, MockeryPHPUnitIntegration, RefreshDatabase; - use RefreshDatabase { - refreshTestDatabase as protected originalRefreshTestDatabase; - } public static $cachedPdo; - public static $cachedVectorPdo; - protected $baseUrl = 'http://localhost'; - protected $connectionsToTransact = [ - 'pgsql', - 'pgvector', - ]; - /** * Default preparation for each test. */ @@ -54,21 +44,5 @@ protected function beforeRefreshingDatabase() } else { static::$cachedPdo = DB::getPdo(); } - - if (static::$cachedVectorPdo) { - DB::connection('pgvector')->setPdo(static::$cachedVectorPdo); - } else { - static::$cachedVectorPdo = DB::connection('pgvector')->getPdo(); - } - } - - // Custom implementation to wipe the vector database, too. - protected function refreshTestDatabase() - { - if (!RefreshDatabaseState::$migrated) { - $this->artisan('db:wipe', ['--database' => 'pgvector']); - } - - $this->originalRefreshTestDatabase(); } } From 779564ddb8db3080af9356123b160b7d59aa66d0 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Tue, 12 Dec 2023 15:38:36 +0100 Subject: [PATCH 16/64] Fix CS issue --- tests/TestCase.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index f074fe419..dd25deb8c 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,7 +2,6 @@ use Biigle\Tests\CreatesApplication; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\RefreshDatabaseState; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; use Illuminate\Support\Facades\Queue; use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; From 3a574284d0ad741ce493b98a9f5fd22604880d68 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Thu, 14 Dec 2023 09:23:47 +0100 Subject: [PATCH 17/64] Add length restriction to request URL --- app/Http/Requests/UpdateVolume.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Requests/UpdateVolume.php b/app/Http/Requests/UpdateVolume.php index c9be93624..91a1a1668 100644 --- a/app/Http/Requests/UpdateVolume.php +++ b/app/Http/Requests/UpdateVolume.php @@ -37,7 +37,7 @@ public function rules() { return [ 'name' => 'filled|max:512', - 'url' => ['filled', new VolumeUrl], + 'url' => ['filled', 'max:256', new VolumeUrl], 'handle' => ['nullable', 'max:256', new Handle], ]; } From fdd75eeceb769d76ea342fe100cc4eae3f0d6da2 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Thu, 14 Dec 2023 11:21:52 +0100 Subject: [PATCH 18/64] Add test for invalid url --- .../Controllers/Api/VolumeControllerTest.php | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/tests/php/Http/Controllers/Api/VolumeControllerTest.php b/tests/php/Http/Controllers/Api/VolumeControllerTest.php index e3c59b237..7152059a5 100644 --- a/tests/php/Http/Controllers/Api/VolumeControllerTest.php +++ b/tests/php/Http/Controllers/Api/VolumeControllerTest.php @@ -2,15 +2,16 @@ namespace Biigle\Tests\Http\Controllers\Api; +use Queue; use ApiTestCase; -use Biigle\Jobs\CloneImagesOrVideos; -use Biigle\Jobs\ProcessNewVolumeFiles; -use Biigle\MediaType; use Biigle\Role; +use Biigle\MediaType; use Biigle\Tests\ProjectTest; +use Biigle\Jobs\CloneImagesOrVideos; use Illuminate\Support\Facades\Cache; +use Biigle\Jobs\ProcessNewVolumeFiles; use Illuminate\Support\Facades\Storage; -use Queue; +use Illuminate\Validation\ValidationException; class VolumeControllerTest extends ApiTestCase { @@ -186,6 +187,24 @@ public function testUpdateUrl() Queue::assertPushed(ProcessNewVolumeFiles::class); } + public function testUpdateInvalidUrl(){ + $volume = $this->volume(); + + config(['volumes.admin_storage_disks' => ['admin-test']]); + $disk = Storage::fake('admin-test'); + $disk->put('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/file.txt', 'abc'); + + $this->beGlobalAdmin(); + + // // invalid url (>256 characters) + $response = $this->json('PUT','/api/v1/volumes/'.$volume->id,[ + 'url' => 'admin-test://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + ])->assertStatus(422); + + $this->assertEquals('The url must not be greater than 256 characters.',$response->exception->getMessage()); + Queue::assertNothingPushed(); + } + public function testUpdateUrlProviderDenylist() { $this->beAdmin(); From 5067d9f8f0efe74ff88f099492c31ce3fed4b6bc Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Fri, 15 Dec 2023 08:00:32 +0100 Subject: [PATCH 19/64] Edit comment --- tests/php/Http/Controllers/Api/VolumeControllerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/php/Http/Controllers/Api/VolumeControllerTest.php b/tests/php/Http/Controllers/Api/VolumeControllerTest.php index 7152059a5..0fba9d774 100644 --- a/tests/php/Http/Controllers/Api/VolumeControllerTest.php +++ b/tests/php/Http/Controllers/Api/VolumeControllerTest.php @@ -196,7 +196,7 @@ public function testUpdateInvalidUrl(){ $this->beGlobalAdmin(); - // // invalid url (>256 characters) + // invalid url (>256 characters) $response = $this->json('PUT','/api/v1/volumes/'.$volume->id,[ 'url' => 'admin-test://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', ])->assertStatus(422); From ded7660847569673737d46f56f4f88d73e40b6fd Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Fri, 15 Dec 2023 08:14:39 +0100 Subject: [PATCH 20/64] Fix 'composer fix' errors --- .../Controllers/Api/VolumeControllerTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/php/Http/Controllers/Api/VolumeControllerTest.php b/tests/php/Http/Controllers/Api/VolumeControllerTest.php index 0fba9d774..44c8cdc43 100644 --- a/tests/php/Http/Controllers/Api/VolumeControllerTest.php +++ b/tests/php/Http/Controllers/Api/VolumeControllerTest.php @@ -2,16 +2,15 @@ namespace Biigle\Tests\Http\Controllers\Api; -use Queue; use ApiTestCase; -use Biigle\Role; +use Biigle\Jobs\CloneImagesOrVideos; +use Biigle\Jobs\ProcessNewVolumeFiles; use Biigle\MediaType; +use Biigle\Role; use Biigle\Tests\ProjectTest; -use Biigle\Jobs\CloneImagesOrVideos; use Illuminate\Support\Facades\Cache; -use Biigle\Jobs\ProcessNewVolumeFiles; use Illuminate\Support\Facades\Storage; -use Illuminate\Validation\ValidationException; +use Queue; class VolumeControllerTest extends ApiTestCase { @@ -187,7 +186,8 @@ public function testUpdateUrl() Queue::assertPushed(ProcessNewVolumeFiles::class); } - public function testUpdateInvalidUrl(){ + public function testUpdateInvalidUrl() + { $volume = $this->volume(); config(['volumes.admin_storage_disks' => ['admin-test']]); @@ -197,11 +197,11 @@ public function testUpdateInvalidUrl(){ $this->beGlobalAdmin(); // invalid url (>256 characters) - $response = $this->json('PUT','/api/v1/volumes/'.$volume->id,[ + $response = $this->json('PUT', '/api/v1/volumes/'.$volume->id, [ 'url' => 'admin-test://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', ])->assertStatus(422); - $this->assertEquals('The url must not be greater than 256 characters.',$response->exception->getMessage()); + $this->assertEquals('The url must not be greater than 256 characters.', $response->exception->getMessage()); Queue::assertNothingPushed(); } From a57b2e5abc948104300f6cec531b4bd29e829b8f Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Fri, 15 Dec 2023 10:03:42 +0100 Subject: [PATCH 21/64] Fix active spinning wheel after video finished loading Set seeking on false again after loading video, because seek() is called everytime a video is loaded. Fixes #695. --- resources/assets/js/videos/videoContainer.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/resources/assets/js/videos/videoContainer.vue b/resources/assets/js/videos/videoContainer.vue index b1c905813..05e65202a 100644 --- a/resources/assets/js/videos/videoContainer.vue +++ b/resources/assets/js/videos/videoContainer.vue @@ -553,7 +553,11 @@ export default { .then(this.handleVideoInformationResponse) .then(this.fetchVideoContent) .catch(this.handleVideoError) - .finally(this.finishLoading); + .finally(() => { + this.finishLoading(); + // Avoid spinning wheel continuing to be displayed after moving fast through videos + this.seeking = false; + }); return promise; }, From ca3516057d86952b00594465d95a377e6270d896 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Fri, 22 Dec 2023 08:02:30 +0100 Subject: [PATCH 22/64] Fix video timeline reset bug by using timeupdate event Replace seeked event by timeupdate, because seeked event does not always reset time after switching videos. Fix #724 --- .../js/videos/components/videoScreen/videoPlayback.vue | 2 +- resources/assets/js/videos/components/videoTimeline.vue | 3 +-- resources/assets/js/videos/videoContainer.vue | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue index b832cd141..a60a2121a 100644 --- a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue @@ -167,7 +167,7 @@ export default { this.dummyCanvas.height = 1; this.video.addEventListener('play', this.setPlaying); this.video.addEventListener('pause', this.setPaused); - this.video.addEventListener('seeked', this.handleSeeked); + this.video.addEventListener('timeupdate', this.handleSeeked); this.video.addEventListener('loadeddata', this.renderVideo); let mapPromise = new Vue.Promise((resolve) => { diff --git a/resources/assets/js/videos/components/videoTimeline.vue b/resources/assets/js/videos/components/videoTimeline.vue index d0329d8d2..0a1334ed0 100644 --- a/resources/assets/js/videos/components/videoTimeline.vue +++ b/resources/assets/js/videos/components/videoTimeline.vue @@ -227,11 +227,10 @@ export default { }, }, created() { - // this.video.addEventListener('timeupdate', this.updateCurrentTime); + this.video.addEventListener('timeupdate', this.updateCurrentTime); this.video.addEventListener('play', this.startUpdateLoop); this.video.addEventListener('pause', this.stopUpdateLoop); this.video.addEventListener('loadedmetadata', this.setDuration); - this.video.addEventListener('seeked', this.updateCurrentTime); }, }; diff --git a/resources/assets/js/videos/videoContainer.vue b/resources/assets/js/videos/videoContainer.vue index 05e65202a..4f94295d4 100644 --- a/resources/assets/js/videos/videoContainer.vue +++ b/resources/assets/js/videos/videoContainer.vue @@ -189,7 +189,7 @@ export default { } let promise = new Vue.Promise((resolve, reject) => { - this.video.addEventListener('seeked', resolve); + this.video.addEventListener('timeupdate', resolve); this.video.addEventListener('error', reject); }); this.seeking = true; @@ -676,9 +676,9 @@ export default { Messages.danger('Error while loading video file.'); } }); - this.video.addEventListener('seeked', this.handleVideoSeeked); + this.video.addEventListener('timeupdate', this.handleVideoSeeked); this.video.addEventListener('pause', this.updateVideoUrlParams); - this.video.addEventListener('seeked', this.updateVideoUrlParams); + this.video.addEventListener('timeupdate', this.updateVideoUrlParams); if (Settings.has('openTab')) { this.openTab = Settings.get('openTab'); From 1c3d2554e9d0f9d6fa396ee6ff2f61e4df1f8f51 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Fri, 22 Dec 2023 09:40:51 +0100 Subject: [PATCH 23/64] Check feature's availability before removing it Fix #660 --- .../videos/components/videoScreen/annotationPlayback.vue | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue b/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue index ac65ed8ea..4e8c06e8f 100644 --- a/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue @@ -90,10 +90,13 @@ export default { toCreate.push(annotation.self); } } - + if (hasRenderedFeatures) { Object.values(oldRendered).forEach(function (feature) { - source.removeFeature(feature); + // source.hasFeature(feature) does not work here, because it is always true + if (source.getFeatures().includes(feature)) { + source.removeFeature(feature); + } selected.remove(feature); }); } else { From 6d43aa8023244032b167b6d2788d4763ecc775e0 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Fri, 22 Dec 2023 10:56:48 +0100 Subject: [PATCH 24/64] Remove null (gap) to prevent validation errors If single and clip annotations are linked and are not touching each other, then null is added. When deleting the single frame annotation, delete also the null. Fix #488 --- resources/assets/js/videos/models/Annotation.vue | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/resources/assets/js/videos/models/Annotation.vue b/resources/assets/js/videos/models/Annotation.vue index bf014ff2b..4222d34eb 100644 --- a/resources/assets/js/videos/models/Annotation.vue +++ b/resources/assets/js/videos/models/Annotation.vue @@ -282,6 +282,12 @@ export default Vue.extend({ this.frames.splice(index, 1); this.points.splice(index, 1); + // Remove null (gap filler) as last element to prevent validation errors + if (this.frames.at(-1) === null) { + this.frames.pop(); + this.points.pop(); + } + return VideoAnnotationApi.update({id: this.id}, { frames: this.frames, points: this.points From c28bc699d81bea0fd739537d1e484d81b99efa5a Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Thu, 28 Dec 2023 10:55:22 +0100 Subject: [PATCH 25/64] Fix flag display Fixes #718 --- .../sass/volumes/components/_volume-image-grid-image.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/assets/sass/volumes/components/_volume-image-grid-image.scss b/resources/assets/sass/volumes/components/_volume-image-grid-image.scss index 34a2d430a..1cf6be64f 100644 --- a/resources/assets/sass/volumes/components/_volume-image-grid-image.scss +++ b/resources/assets/sass/volumes/components/_volume-image-grid-image.scss @@ -2,7 +2,7 @@ position: relative; &.image-grid__image--selected { - &:before { + &:after { position: absolute; content: ''; top: $padding-base-horizontal; From 54b48701b034277456ea94ab793379dba47f266d Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Wed, 3 Jan 2024 08:09:38 +0100 Subject: [PATCH 26/64] Remove null at array start to prevent validation error Check for null at array beginning and delete remaining gap filler --- resources/assets/js/videos/models/Annotation.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/resources/assets/js/videos/models/Annotation.vue b/resources/assets/js/videos/models/Annotation.vue index 4222d34eb..da3fff800 100644 --- a/resources/assets/js/videos/models/Annotation.vue +++ b/resources/assets/js/videos/models/Annotation.vue @@ -282,7 +282,12 @@ export default Vue.extend({ this.frames.splice(index, 1); this.points.splice(index, 1); - // Remove null (gap filler) as last element to prevent validation errors + // Remove null (gap filler) as first/last element to prevent validation errors + if (this.frames[0] === null) { + this.frames.shift(); + this.points.shift(); + } + if (this.frames.at(-1) === null) { this.frames.pop(); this.points.pop(); From 7a2c43a18ba59c667bbb4627d1fafde47f0abe27 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Wed, 3 Jan 2024 09:25:02 +0100 Subject: [PATCH 27/64] Revert "Fix video timeline reset bug by using timeupdate event" This reverts commit ca3516057d86952b00594465d95a377e6270d896. --- .../js/videos/components/videoScreen/videoPlayback.vue | 2 +- resources/assets/js/videos/components/videoTimeline.vue | 3 ++- resources/assets/js/videos/videoContainer.vue | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue index a60a2121a..b832cd141 100644 --- a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue @@ -167,7 +167,7 @@ export default { this.dummyCanvas.height = 1; this.video.addEventListener('play', this.setPlaying); this.video.addEventListener('pause', this.setPaused); - this.video.addEventListener('timeupdate', this.handleSeeked); + this.video.addEventListener('seeked', this.handleSeeked); this.video.addEventListener('loadeddata', this.renderVideo); let mapPromise = new Vue.Promise((resolve) => { diff --git a/resources/assets/js/videos/components/videoTimeline.vue b/resources/assets/js/videos/components/videoTimeline.vue index 0a1334ed0..d0329d8d2 100644 --- a/resources/assets/js/videos/components/videoTimeline.vue +++ b/resources/assets/js/videos/components/videoTimeline.vue @@ -227,10 +227,11 @@ export default { }, }, created() { - this.video.addEventListener('timeupdate', this.updateCurrentTime); + // this.video.addEventListener('timeupdate', this.updateCurrentTime); this.video.addEventListener('play', this.startUpdateLoop); this.video.addEventListener('pause', this.stopUpdateLoop); this.video.addEventListener('loadedmetadata', this.setDuration); + this.video.addEventListener('seeked', this.updateCurrentTime); }, }; diff --git a/resources/assets/js/videos/videoContainer.vue b/resources/assets/js/videos/videoContainer.vue index 4f94295d4..05e65202a 100644 --- a/resources/assets/js/videos/videoContainer.vue +++ b/resources/assets/js/videos/videoContainer.vue @@ -189,7 +189,7 @@ export default { } let promise = new Vue.Promise((resolve, reject) => { - this.video.addEventListener('timeupdate', resolve); + this.video.addEventListener('seeked', resolve); this.video.addEventListener('error', reject); }); this.seeking = true; @@ -676,9 +676,9 @@ export default { Messages.danger('Error while loading video file.'); } }); - this.video.addEventListener('timeupdate', this.handleVideoSeeked); + this.video.addEventListener('seeked', this.handleVideoSeeked); this.video.addEventListener('pause', this.updateVideoUrlParams); - this.video.addEventListener('timeupdate', this.updateVideoUrlParams); + this.video.addEventListener('seeked', this.updateVideoUrlParams); if (Settings.has('openTab')) { this.openTab = Settings.get('openTab'); From e9299a07a3e2100b3d46928f7f6a3411ba546830 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Wed, 3 Jan 2024 09:39:57 +0100 Subject: [PATCH 28/64] Fix timeline reset bug by resetting video time --- resources/assets/js/videos/videoContainer.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/assets/js/videos/videoContainer.vue b/resources/assets/js/videos/videoContainer.vue index 05e65202a..3a78d4a5a 100644 --- a/resources/assets/js/videos/videoContainer.vue +++ b/resources/assets/js/videos/videoContainer.vue @@ -579,6 +579,7 @@ export default { this.annotations = []; this.seeking = false; this.initialCurrentTime = 0; + this.video.currentTime = 0; this.initialFocussedAnnotation = 0; this.$refs.videoTimeline.reset(); this.$refs.videoScreen.reset(); From 5834dbbe1ebcd65fe0431283a600bc29153da687 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Fri, 5 Jan 2024 09:33:01 +0100 Subject: [PATCH 29/64] Add check for empty arrays Empty arrays represent gaps --- app/Traits/HasPointsAttribute.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Traits/HasPointsAttribute.php b/app/Traits/HasPointsAttribute.php index 3cc130122..11b66fb34 100644 --- a/app/Traits/HasPointsAttribute.php +++ b/app/Traits/HasPointsAttribute.php @@ -15,6 +15,11 @@ trait HasPointsAttribute */ public function validatePoints(array $points) { + // Gaps are represented as empty arrays + if (empty($points)) { + return; + } + // check if all elements are integer $valid = array_reduce($points, fn ($carry, $point) => $carry && (is_float($point) || is_int($point)), true); From b06f89931414f4ff4a8d782b68b7205dd3d466c8 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Fri, 5 Jan 2024 09:47:16 +0100 Subject: [PATCH 30/64] Fix bug with multiple consecutive null --- resources/assets/js/videos/models/Annotation.vue | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/assets/js/videos/models/Annotation.vue b/resources/assets/js/videos/models/Annotation.vue index da3fff800..59b1a59ae 100644 --- a/resources/assets/js/videos/models/Annotation.vue +++ b/resources/assets/js/videos/models/Annotation.vue @@ -283,12 +283,14 @@ export default Vue.extend({ this.points.splice(index, 1); // Remove null (gap filler) as first/last element to prevent validation errors - if (this.frames[0] === null) { + // Multiple consecutive 'null' can occur after removing elements of linked anntations + // in specific order + while (this.frames[0] === null) { this.frames.shift(); this.points.shift(); } - if (this.frames.at(-1) === null) { + while (this.frames.at(-1) === null) { this.frames.pop(); this.points.pop(); } From f034203e5fdd284f4153b52ed01880aebcdb3dbf Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Fri, 5 Jan 2024 17:00:20 +0100 Subject: [PATCH 31/64] Change worker Docker image to Debian with PyTorch --- .docker/worker.dockerfile | 146 +++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 80 deletions(-) diff --git a/.docker/worker.dockerfile b/.docker/worker.dockerfile index 3f22309f5..8e56a4487 100644 --- a/.docker/worker.dockerfile +++ b/.docker/worker.dockerfile @@ -1,32 +1,36 @@ -# PHP 8.1.13 -#FROM php:8.1-alpine -FROM php@sha256:f9e31f22bdd89c1334a03db5c8800a5f3b1e1fe042d470adccf58a29672c6202 +# PHP 8.1.27 +# FROM php:8.1 +FROM php@sha256:9b5dfb7deef3e48d67b2599e4d3967bb3ece19fd5ba09cb8e7ee10f5facf36e0 MAINTAINER Martin Zurowietz LABEL org.opencontainers.image.source https://github.com/biigle/core -ARG OPENCV_VERSION=4.6.0-r3 -RUN apk add --no-cache \ - eigen \ +RUN LC_ALL=C.UTF-8 apt-get update \ + && apt-get install -y --no-install-recommends \ ffmpeg \ - lapack \ - openblas \ - py3-numpy \ python3 \ - py3-opencv="$OPENCV_VERSION" + python3-numpy \ + python3-opencv \ + python3-scipy \ + python3-sklearn \ + python3-matplotlib \ + python3-shapely \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* RUN ln -s "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" ADD ".docker/all-php.ini" "$PHP_INI_DIR/conf.d/all.ini" -RUN apk add --no-cache \ - libxml2 \ - libzip \ - openssl \ - postgresql \ - && apk add --no-cache --virtual .build-deps \ +RUN LC_ALL=C.UTF-8 apt-get update \ + && apt-get install -y --no-install-recommends \ libxml2-dev \ libzip-dev \ - postgresql-dev \ - && docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \ + libpq-dev \ + && apt-get install -y --no-install-recommends \ + libxml2 \ + libzip4 \ + postgresql-client \ + && docker-php-ext-configure pgsql -with-pgsql=/usr/bin/pgsql \ && docker-php-ext-install -j$(nproc) \ exif \ pcntl \ @@ -35,15 +39,29 @@ RUN apk add --no-cache \ pgsql \ soap \ zip \ - && apk del --purge .build-deps + && apt-get purge -y \ + libxml2-dev \ + libzip-dev \ + libpq-dev \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* # Configure proxy if there is any. See: https://stackoverflow.com/a/2266500/1796523 RUN [ -z "$HTTP_PROXY" ] || pear config-set http_proxy $HTTP_PROXY -RUN apk add --no-cache yaml \ - && apk add --no-cache --virtual .build-deps g++ make autoconf yaml-dev \ + +RUN LC_ALL=C.UTF-8 apt-get update \ + && apt-get install -y --no-install-recommends \ + libyaml-dev \ + && apt-get install -y --no-install-recommends \ + libyaml-0-2 \ && pecl install yaml \ - && docker-php-ext-enable yaml \ - && apk del --purge .build-deps + && printf "\n" | docker-php-ext-enable yaml \ + && apt-get purge -y \ + libyaml-dev \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* ARG PHPREDIS_VERSION=5.3.7 RUN curl -L -o /tmp/redis.tar.gz https://github.com/phpredis/phpredis/archive/${PHPREDIS_VERSION}.tar.gz \ @@ -53,70 +71,38 @@ RUN curl -L -o /tmp/redis.tar.gz https://github.com/phpredis/phpredis/archive/${ && mv phpredis-${PHPREDIS_VERSION} /usr/src/php/ext/redis \ && docker-php-ext-install -j$(nproc) redis -ENV PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:${PKG_CONFIG_PATH}" -# Install vips from source because the apk package does not have dzsave support! Install -# libvips and the vips PHP extension in one go so the *-dev dependencies are reused. -ARG LIBVIPS_VERSION=8.12.2 -ARG PHP_VIPS_EXT_VERSION=1.0.13 -RUN apk add --no-cache --virtual .build-deps \ - autoconf \ - automake \ - build-base \ - expat-dev \ - glib-dev \ - libgsf-dev \ - libjpeg-turbo-dev \ - libpng-dev \ - tiff-dev \ - && apk add --no-cache \ - expat \ - glib \ - libgsf \ - libjpeg-turbo \ - libpng \ - tiff \ - && cd /tmp \ - && curl -L https://github.com/libvips/libvips/releases/download/v${LIBVIPS_VERSION}/vips-${LIBVIPS_VERSION}.tar.gz -o vips-${LIBVIPS_VERSION}.tar.gz \ - && tar -xzf vips-${LIBVIPS_VERSION}.tar.gz \ - && cd vips-${LIBVIPS_VERSION} \ - && ./configure \ - --without-python \ - --enable-debug=no \ - --disable-dependency-tracking \ - --disable-static \ - && make -j $(nproc) \ - && make -s install-strip \ - && cd /tmp \ - && curl -L https://github.com/libvips/php-vips-ext/raw/master/vips-${PHP_VIPS_EXT_VERSION}.tgz -o vips-${PHP_VIPS_EXT_VERSION}.tgz \ - && echo '' | pecl install vips-${PHP_VIPS_EXT_VERSION}.tgz \ +# ENV PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:${PKG_CONFIG_PATH}" + +RUN LC_ALL=C.UTF-8 apt-get update \ + && apt-get install -y --no-install-recommends \ + libvips-dev \ + && apt-get install -y --no-install-recommends \ + libvips42 \ + && pecl install vips \ && docker-php-ext-enable vips \ - && rm -r /tmp/* \ - && apk del --purge .build-deps \ - && rm -rf /var/cache/apk/* + && apt-get purge -y \ + libvips-dev \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* # Unset proxy configuration again. RUN [ -z "$HTTP_PROXY" ] || pear config-set http_proxy "" -# Other Python dependencies are added with the OpenCV build above. -RUN apk add --no-cache py3-scipy py3-scikit-learn py3-matplotlib py3-shapely - -# Set this library path so the Python modules are linked correctly. -# See: https://github.com/python-pillow/Pillow/issues/1763#issuecomment-204252397 -ENV LIBRARY_PATH=/lib:/usr/lib -# Install Python dependencies. Note that these also depend on some image processing libs -# that were installed along with vips. -RUN apk add --no-cache --virtual .build-deps \ - python3-dev \ - py3-pip \ - py3-wheel \ - build-base \ - libjpeg-turbo-dev \ - libpng-dev \ - && pip3 install --no-cache-dir \ +RUN LC_ALL=C.UTF-8 apt-get update \ + && apt-get install -y --no-install-recommends \ + python3-pip \ + && pip3 install --no-cache-dir --break-system-packages \ PyExcelerate==0.6.7 \ Pillow==10.0.1 \ - && apk del --purge .build-deps \ - && rm -rf /var/cache/apk/* + && pip3 install --no-cache-dir --break-system-packages --index-url https://download.pytorch.org/whl/cpu \ + torch==2.0.* \ + torchvision==0.15.* \ + && apt-get purge -y \ + python3-pip \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* WORKDIR /var/www From 9e44a8077b9712b64a42c61ecc70ac0a87d0d9ea Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Tue, 9 Jan 2024 14:44:33 +0100 Subject: [PATCH 32/64] Update requirements file --- requirements.txt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 5f46be82d..9c4b3e24c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,12 @@ # This file is just used to get security alerts from GitHub. Make sure the versions match # in .docker/worker.dockerfile. -numpy==1.22.* +numpy==1.24.* opencv-contrib-python-headless==4.6.0 -scipy==1.10.0 -scikit-learn -matplotlib==3.5.2 +scipy==1.10.* +scikit-learn??1.2.* +matplotlib==3.6.* PyExcelerate==0.6.7 Pillow==10.0.1 -Shapely==1.8.1 +Shapely==1.8.* +torch==2.0.* +torchvision==0.15.* From f2a618b9b4d6adb9dab69f715e9c692892d5f9d1 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Tue, 9 Jan 2024 14:45:25 +0100 Subject: [PATCH 33/64] Fix typo --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9c4b3e24c..7d100a6b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ numpy==1.24.* opencv-contrib-python-headless==4.6.0 scipy==1.10.* -scikit-learn??1.2.* +scikit-learn==1.2.* matplotlib==3.6.* PyExcelerate==0.6.7 Pillow==10.0.1 From 8d31ff63d3a5661ba98d30c24ef39ce74ce3ae38 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Tue, 9 Jan 2024 16:43:17 +0100 Subject: [PATCH 34/64] Update update-schema.yml --- .github/workflows/update-schema.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-schema.yml b/.github/workflows/update-schema.yml index 46ea35c70..c117d1cab 100644 --- a/.github/workflows/update-schema.yml +++ b/.github/workflows/update-schema.yml @@ -16,7 +16,7 @@ jobs: - name: Trigger schema update run: | curl -X POST --fail \ - -H "Authorization: token ${{ secrets.TOKEN }}" \ + -H "Authorization: token ${{ secrets.BIIGLE_SCHEMA_API_TOKEN }}" \ -H "Content-Type: application/json" \ --data '{"event_type": "build_application"}' \ https://api.github.com/repos/biigle/schema/dispatches From dc4d2ffdeae5ebed59b1c43c2f8fb25909dd18b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 15:43:46 +0000 Subject: [PATCH 35/64] Bump follow-redirects from 1.14.8 to 1.15.4 Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.8 to 1.15.4. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.8...v1.15.4) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3ccdb9948..532e0b250 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5872,9 +5872,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.14.8", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", - "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", "dev": true, "funding": [ { @@ -16087,9 +16087,9 @@ "dev": true }, "follow-redirects": { - "version": "1.14.8", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", - "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", "dev": true }, "forwarded": { From 314eaa9a0cd5872277ccc35a9229710b8dc8628d Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Thu, 11 Jan 2024 10:55:04 +0100 Subject: [PATCH 36/64] Disable seeking when switiching to new video --- resources/assets/js/videos/components/videoScreen.vue | 4 ++-- .../js/videos/components/videoScreen/videoPlayback.vue | 6 ++++-- resources/assets/js/videos/videoContainer.vue | 3 +-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen.vue b/resources/assets/js/videos/components/videoScreen.vue index 60a446f1e..3e4d6a6ec 100644 --- a/resources/assets/js/videos/components/videoScreen.vue +++ b/resources/assets/js/videos/components/videoScreen.vue @@ -500,8 +500,8 @@ export default { emitNext() { this.$emit('next'); }, - reset() { - this.setPaused(); + reset(dontSeek = false) { + this.setPaused(dontSeek); this.resetInteractionMode(); }, }, diff --git a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue index b832cd141..0bffe3ae9 100644 --- a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue @@ -118,7 +118,7 @@ export default { this.startRenderLoop(); } }, - setPaused() { + setPaused(dontSeek) { this.playing = false; this.stopRenderLoop(); // Force render the video frame that belongs to currentTime. This is a @@ -126,7 +126,9 @@ export default { // currentTime (in most cases). With the workaround we can create annotations // at currentTime and be sure that the same frame can be reproduced later for // the annotations. See: https://github.com/biigle/core/issues/433 - this.$emit('seek', this.video.currentTime, true); + if (!dontSeek) { + this.$emit('seek', this.video.currentTime, true); + } }, togglePlaying() { if (this.playing) { diff --git a/resources/assets/js/videos/videoContainer.vue b/resources/assets/js/videos/videoContainer.vue index 3a78d4a5a..4ed2f706f 100644 --- a/resources/assets/js/videos/videoContainer.vue +++ b/resources/assets/js/videos/videoContainer.vue @@ -579,10 +579,9 @@ export default { this.annotations = []; this.seeking = false; this.initialCurrentTime = 0; - this.video.currentTime = 0; this.initialFocussedAnnotation = 0; this.$refs.videoTimeline.reset(); - this.$refs.videoScreen.reset(); + this.$refs.videoScreen.reset(true); }, initVideoIds(ids) { // Look for a sequence of video IDs in local storage. This sequence is From b8e69e8b7d0441b08a43e47c3e1e38a82cdedb4f Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Fri, 12 Jan 2024 10:43:39 +0100 Subject: [PATCH 37/64] Swap arguments to reset video screen for bugfix --- resources/assets/js/videos/components/videoScreen.vue | 4 ++-- .../assets/js/videos/components/videoScreen/videoPlayback.vue | 2 +- resources/assets/js/videos/videoContainer.vue | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen.vue b/resources/assets/js/videos/components/videoScreen.vue index 3e4d6a6ec..89b6dc631 100644 --- a/resources/assets/js/videos/components/videoScreen.vue +++ b/resources/assets/js/videos/components/videoScreen.vue @@ -500,8 +500,8 @@ export default { emitNext() { this.$emit('next'); }, - reset(dontSeek = false) { - this.setPaused(dontSeek); + reset() { + this.setPaused(true); this.resetInteractionMode(); }, }, diff --git a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue index 0bffe3ae9..121bbc524 100644 --- a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue @@ -118,7 +118,7 @@ export default { this.startRenderLoop(); } }, - setPaused(dontSeek) { + setPaused(dontSeek = false) { this.playing = false; this.stopRenderLoop(); // Force render the video frame that belongs to currentTime. This is a diff --git a/resources/assets/js/videos/videoContainer.vue b/resources/assets/js/videos/videoContainer.vue index 4ed2f706f..05e65202a 100644 --- a/resources/assets/js/videos/videoContainer.vue +++ b/resources/assets/js/videos/videoContainer.vue @@ -581,7 +581,7 @@ export default { this.initialCurrentTime = 0; this.initialFocussedAnnotation = 0; this.$refs.videoTimeline.reset(); - this.$refs.videoScreen.reset(true); + this.$refs.videoScreen.reset(); }, initVideoIds(ids) { // Look for a sequence of video IDs in local storage. This sequence is From 8beb565051a09d9e1617c66ccd3a9d4a612b4c66 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Mon, 15 Jan 2024 09:25:52 +0100 Subject: [PATCH 38/64] Revert "Fix bug with multiple consecutive null" This reverts commit b06f89931414f4ff4a8d782b68b7205dd3d466c8. --- resources/assets/js/videos/models/Annotation.vue | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/resources/assets/js/videos/models/Annotation.vue b/resources/assets/js/videos/models/Annotation.vue index 59b1a59ae..da3fff800 100644 --- a/resources/assets/js/videos/models/Annotation.vue +++ b/resources/assets/js/videos/models/Annotation.vue @@ -283,14 +283,12 @@ export default Vue.extend({ this.points.splice(index, 1); // Remove null (gap filler) as first/last element to prevent validation errors - // Multiple consecutive 'null' can occur after removing elements of linked anntations - // in specific order - while (this.frames[0] === null) { + if (this.frames[0] === null) { this.frames.shift(); this.points.shift(); } - while (this.frames.at(-1) === null) { + if (this.frames.at(-1) === null) { this.frames.pop(); this.points.pop(); } From 7fa0a9050612886c2197d8cade688a8b24ee2176 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Mon, 15 Jan 2024 10:40:05 +0100 Subject: [PATCH 39/64] Delete and update features from annotationSource --- .../videoScreen/annotationPlayback.vue | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue b/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue index 4e8c06e8f..0eed3d29b 100644 --- a/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue @@ -63,7 +63,6 @@ export default { // a seek was performed to the exact annotation time. Hence, we round the // time here to the maximum of 4 decimals, too. time = rtp(time); - for (let i = 0, length = annotations.length; i < length; i++) { // We can skip ahead and break early because of the sorting in the // annotationsPreparedToRender array. @@ -90,13 +89,13 @@ export default { toCreate.push(annotation.self); } } - if (hasRenderedFeatures) { Object.values(oldRendered).forEach(function (feature) { - // source.hasFeature(feature) does not work here, because it is always true - if (source.getFeatures().includes(feature)) { - source.removeFeature(feature); - } + // The annotation source uses featureChangeKeys method with featureKey as parameter. + // Feature and oldFeature do not have the same key, + // which is why featureChangeKeys returns undefined in removeFeature(feature). + let oldFeature = source.getFeatureById(feature.getId()); + source.removeFeature(oldFeature); selected.remove(feature); }); } else { @@ -110,8 +109,7 @@ export default { selected.clear(); } } - - + let features = toCreate.map(this.createFeature); features.forEach(function (feature) { newRendered[feature.getId()] = feature; @@ -138,7 +136,13 @@ export default { source.addFeature(newFeature); }, createFeature(annotation) { - let feature = new Feature(this.getGeometryFromPoints(annotation.shape, annotation.points[0])); + // Use feature from annotationSource if it exists, + // otherwise annotations are not updated on video after label-swap and deletion. + let feature = this.annotationSource.getFeatureById(annotation.id); + + if (feature === null){ + feature = new Feature(this.getGeometryFromPoints(annotation.shape, annotation.points[0])); + } feature.setId(annotation.id); feature.set('annotation', annotation); From ba1fe40c4e21425d7faebab1507162ec8108de70 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Mon, 15 Jan 2024 11:16:30 +0100 Subject: [PATCH 40/64] Implement alternative solution to handle duplicate null keyframes --- .../assets/js/videos/models/Annotation.vue | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/resources/assets/js/videos/models/Annotation.vue b/resources/assets/js/videos/models/Annotation.vue index da3fff800..a300e8bab 100644 --- a/resources/assets/js/videos/models/Annotation.vue +++ b/resources/assets/js/videos/models/Annotation.vue @@ -282,15 +282,14 @@ export default Vue.extend({ this.frames.splice(index, 1); this.points.splice(index, 1); - // Remove null (gap filler) as first/last element to prevent validation errors - if (this.frames[0] === null) { - this.frames.shift(); - this.points.shift(); - } - - if (this.frames.at(-1) === null) { - this.frames.pop(); - this.points.pop(); + // Remove "null" elements of adjacent gaps to + // avoid multiple consecutive "null"s. + if (index === 0 && this.frames[0] === null) { + this.frames.splice(0, 1); + this.points.splice(0, 1); + } else if (this.frames[index - 1] === null) { + this.frames.splice(index - 1, 1); + this.points.splice(index - 1, 1); } return VideoAnnotationApi.update({id: this.id}, { From 02d209ac764937aa64f41a84b711075fbc432d23 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Tue, 16 Jan 2024 08:33:09 +0100 Subject: [PATCH 41/64] Revert "Delete and update features from annotationSource" This reverts commit 7fa0a9050612886c2197d8cade688a8b24ee2176. --- .../videoScreen/annotationPlayback.vue | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue b/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue index 0eed3d29b..4e8c06e8f 100644 --- a/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue @@ -63,6 +63,7 @@ export default { // a seek was performed to the exact annotation time. Hence, we round the // time here to the maximum of 4 decimals, too. time = rtp(time); + for (let i = 0, length = annotations.length; i < length; i++) { // We can skip ahead and break early because of the sorting in the // annotationsPreparedToRender array. @@ -89,13 +90,13 @@ export default { toCreate.push(annotation.self); } } + if (hasRenderedFeatures) { Object.values(oldRendered).forEach(function (feature) { - // The annotation source uses featureChangeKeys method with featureKey as parameter. - // Feature and oldFeature do not have the same key, - // which is why featureChangeKeys returns undefined in removeFeature(feature). - let oldFeature = source.getFeatureById(feature.getId()); - source.removeFeature(oldFeature); + // source.hasFeature(feature) does not work here, because it is always true + if (source.getFeatures().includes(feature)) { + source.removeFeature(feature); + } selected.remove(feature); }); } else { @@ -109,7 +110,8 @@ export default { selected.clear(); } } - + + let features = toCreate.map(this.createFeature); features.forEach(function (feature) { newRendered[feature.getId()] = feature; @@ -136,13 +138,7 @@ export default { source.addFeature(newFeature); }, createFeature(annotation) { - // Use feature from annotationSource if it exists, - // otherwise annotations are not updated on video after label-swap and deletion. - let feature = this.annotationSource.getFeatureById(annotation.id); - - if (feature === null){ - feature = new Feature(this.getGeometryFromPoints(annotation.shape, annotation.points[0])); - } + let feature = new Feature(this.getGeometryFromPoints(annotation.shape, annotation.points[0])); feature.setId(annotation.id); feature.set('annotation', annotation); From 77bf4c02ca6f3d0d0ff17c87579efbada08ed5e8 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Tue, 16 Jan 2024 08:34:17 +0100 Subject: [PATCH 42/64] Revert "Check feature's availability before removing it" This reverts commit 1c3d2554e9d0f9d6fa396ee6ff2f61e4df1f8f51. --- .../videos/components/videoScreen/annotationPlayback.vue | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue b/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue index 4e8c06e8f..ac65ed8ea 100644 --- a/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue @@ -90,13 +90,10 @@ export default { toCreate.push(annotation.self); } } - + if (hasRenderedFeatures) { Object.values(oldRendered).forEach(function (feature) { - // source.hasFeature(feature) does not work here, because it is always true - if (source.getFeatures().includes(feature)) { - source.removeFeature(feature); - } + source.removeFeature(feature); selected.remove(feature); }); } else { From 85858d98dccf2c1b2899b21fbc54e783182c5fbc Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Tue, 16 Jan 2024 08:49:40 +0100 Subject: [PATCH 43/64] Change color only when refreshing annotation Fix bug by using old feature instead of creating new one. --- .../components/videoScreen/annotationPlayback.vue | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue b/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue index ac65ed8ea..cfdbe4b17 100644 --- a/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue @@ -128,11 +128,12 @@ export default { refreshSingleAnnotation(annotation) { let source = this.annotationSource; - let newFeature = this.createFeature(annotation); - let oldFeature = source.getFeatureById(annotation.id) - - source.removeFeature(oldFeature); - source.addFeature(newFeature); + let feature = source.getFeatureById(annotation.id) + + source.removeFeature(feature); + + feature.set('color', annotation.labels[0].label.color); + source.addFeature(feature); }, createFeature(annotation) { let feature = new Feature(this.getGeometryFromPoints(annotation.shape, annotation.points[0])); From 8bc71316db3a88cab648f507e20dc00491b62407 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Wed, 17 Jan 2024 07:28:15 +0100 Subject: [PATCH 44/64] Remove unnecessary code --- .../js/videos/components/videoScreen/annotationPlayback.vue | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue b/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue index cfdbe4b17..fa0f65cc2 100644 --- a/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/annotationPlayback.vue @@ -128,12 +128,9 @@ export default { refreshSingleAnnotation(annotation) { let source = this.annotationSource; - let feature = source.getFeatureById(annotation.id) - - source.removeFeature(feature); + let feature = source.getFeatureById(annotation.id); feature.set('color', annotation.labels[0].label.color); - source.addFeature(feature); }, createFeature(annotation) { let feature = new Feature(this.getGeometryFromPoints(annotation.shape, annotation.points[0])); From a84530faaf228c83dc1cc8f3e311c45fe78c2143 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Wed, 17 Jan 2024 14:34:06 +0100 Subject: [PATCH 45/64] Fix volume URL validation Someone managed to pass an array as volume URL. This threw an error in the VolumeUrl rule which expects a string. --- app/Http/Requests/StoreVolume.php | 2 +- app/Http/Requests/UpdateVolume.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Requests/StoreVolume.php b/app/Http/Requests/StoreVolume.php index 0536b53b0..ae34feb48 100644 --- a/app/Http/Requests/StoreVolume.php +++ b/app/Http/Requests/StoreVolume.php @@ -48,7 +48,7 @@ public function rules() return [ 'name' => 'required|max:512', 'media_type' => ['filled', Rule::in(array_keys(MediaType::INSTANCES))], - 'url' => ['required', 'max:256', new VolumeUrl], + 'url' => ['required', 'string', 'max:256', new VolumeUrl], 'files' => [ 'required', 'array', diff --git a/app/Http/Requests/UpdateVolume.php b/app/Http/Requests/UpdateVolume.php index 91a1a1668..d0b88070b 100644 --- a/app/Http/Requests/UpdateVolume.php +++ b/app/Http/Requests/UpdateVolume.php @@ -37,7 +37,7 @@ public function rules() { return [ 'name' => 'filled|max:512', - 'url' => ['filled', 'max:256', new VolumeUrl], + 'url' => ['required', 'string', 'max:256', new VolumeUrl], 'handle' => ['nullable', 'max:256', new Handle], ]; } From 6d2b843d673315279e1719957875c6536509079c Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Wed, 17 Jan 2024 14:35:23 +0100 Subject: [PATCH 46/64] Update fic update volume URL validation rules --- app/Http/Requests/UpdateVolume.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Requests/UpdateVolume.php b/app/Http/Requests/UpdateVolume.php index d0b88070b..d1ad2c6b3 100644 --- a/app/Http/Requests/UpdateVolume.php +++ b/app/Http/Requests/UpdateVolume.php @@ -37,7 +37,7 @@ public function rules() { return [ 'name' => 'filled|max:512', - 'url' => ['required', 'string', 'max:256', new VolumeUrl], + 'url' => ['filled', 'string', 'max:256', new VolumeUrl], 'handle' => ['nullable', 'max:256', new Handle], ]; } From 576f6df43bcb19bbd2675f3c7a18aaf91ee85a6f Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Thu, 18 Jan 2024 08:02:45 +0100 Subject: [PATCH 47/64] Move empty array handling --- app/Traits/HasPointsAttribute.php | 5 ----- app/VideoAnnotation.php | 3 ++- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/Traits/HasPointsAttribute.php b/app/Traits/HasPointsAttribute.php index 11b66fb34..3cc130122 100644 --- a/app/Traits/HasPointsAttribute.php +++ b/app/Traits/HasPointsAttribute.php @@ -15,11 +15,6 @@ trait HasPointsAttribute */ public function validatePoints(array $points) { - // Gaps are represented as empty arrays - if (empty($points)) { - return; - } - // check if all elements are integer $valid = array_reduce($points, fn ($carry, $point) => $carry && (is_float($point) || is_int($point)), true); diff --git a/app/VideoAnnotation.php b/app/VideoAnnotation.php index f3bde3df5..a8bb6b9c1 100644 --- a/app/VideoAnnotation.php +++ b/app/VideoAnnotation.php @@ -105,7 +105,8 @@ public function validatePoints(array $points = []) throw new Exception('The number of key frames does not match the number of annotation coordinates.'); } - array_map([$this, 'parent::validatePoints'], $this->points); + // Gaps are represented as empty arrays + array_map(function ($point) { if (count($point)) { parent::validatePoints($point); } }, $this->points); } /** From 7a7aec9b72081adf307fe64e728fd6bcce4b5434 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Thu, 18 Jan 2024 15:51:17 +0100 Subject: [PATCH 48/64] Update for new ProcessAnnotatedFile job of biigle/largo --- app/Jobs/CloneImagesOrVideos.php | 30 +++++++++------------- tests/php/Jobs/CloneImagesOrVideosTest.php | 22 ++++++++-------- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/app/Jobs/CloneImagesOrVideos.php b/app/Jobs/CloneImagesOrVideos.php index f087975d4..246a6fda6 100644 --- a/app/Jobs/CloneImagesOrVideos.php +++ b/app/Jobs/CloneImagesOrVideos.php @@ -8,8 +8,8 @@ use Biigle\ImageAnnotation; use Biigle\ImageAnnotationLabel; use Biigle\ImageLabel; -use Biigle\Modules\Largo\Jobs\GenerateImageAnnotationPatch; -use Biigle\Modules\Largo\Jobs\GenerateVideoAnnotationPatch; +use Biigle\Modules\Largo\Jobs\ProcessAnnotatedImage; +use Biigle\Modules\Largo\Jobs\ProcessAnnotatedVideo; use Biigle\Project; use Biigle\Traits\ChecksMetadataStrings; use Biigle\Video; @@ -162,24 +162,18 @@ public function postProcessCloning($volume) { ProcessNewVolumeFiles::dispatch($volume); - if (class_exists(GenerateImageAnnotationPatch::class)) { - ImageAnnotation::join('images', 'images.id', '=', 'image_annotations.image_id') - ->where('images.volume_id', "=", $volume->id) - ->select('image_annotations.id') - ->eachById(function ($annotation) { - GenerateImageAnnotationPatch::dispatch($annotation) - ->onQueue(config('largo.generate_annotation_patch_queue')); - }, 1000, 'image_annotations.id', 'id'); + if (class_exists(ProcessAnnotatedImage::class)) { + $volume->images()->whereHas('annotations')->eachById(function ($image) { + ProcessAnnotatedImage::dispatch($image) + ->onQueue(config('largo.generate_annotation_patch_queue')); + }); } - if (class_exists(GenerateVideoAnnotationPatch::class)) { - VideoAnnotation::join('videos', 'videos.id', '=', 'video_annotations.video_id') - ->where('videos.volume_id', "=", $volume->id) - ->select('video_annotations.id') - ->eachById(function ($annotation) { - GenerateVideoAnnotationPatch::dispatch($annotation) - ->onQueue(config('largo.generate_annotation_patch_queue')); - }, 1000, 'video_annotations.id', 'id'); + if (class_exists(ProcessAnnotatedVideo::class)) { + $volume->videos()->whereHas('annotations')->eachById(function ($video) { + ProcessAnnotatedVideo::dispatch($video) + ->onQueue(config('largo.generate_annotation_patch_queue')); + }); } } diff --git a/tests/php/Jobs/CloneImagesOrVideosTest.php b/tests/php/Jobs/CloneImagesOrVideosTest.php index 8da61a120..166e9da87 100644 --- a/tests/php/Jobs/CloneImagesOrVideosTest.php +++ b/tests/php/Jobs/CloneImagesOrVideosTest.php @@ -5,8 +5,8 @@ use Biigle\Jobs\CloneImagesOrVideos; use Biigle\Jobs\ProcessNewVolumeFiles; use Biigle\MediaType; -use Biigle\Modules\Largo\Jobs\GenerateImageAnnotationPatch; -use Biigle\Modules\Largo\Jobs\GenerateVideoAnnotationPatch; +use Biigle\Modules\Largo\Jobs\ProcessAnnotatedImage; +use Biigle\Modules\Largo\Jobs\ProcessAnnotatedVideo; use Biigle\Tests\ImageAnnotationLabelTest; use Biigle\Tests\ImageAnnotationTest; use Biigle\Tests\ImageLabelTest; @@ -616,13 +616,13 @@ public function testHandleVolumeImages() (new CloneImagesOrVideos($request, $copy))->handle(); Queue::assertPushed(ProcessNewVolumeFiles::class); - Queue::assertNotPushed(GenerateImageAnnotationPatch::class); + Queue::assertNotPushed(ProcessAnnotatedImage::class); } public function testHandleImageAnnotationPatches() { - if (!class_exists(GenerateImageAnnotationPatch::class)) { - $this->markTestSkipped('Requires '.GenerateImageAnnotationPatch::class); + if (!class_exists(ProcessAnnotatedImage::class)) { + $this->markTestSkipped('Requires '.ProcessAnnotatedImage::class); } // The target project. @@ -647,15 +647,15 @@ public function testHandleImageAnnotationPatches() ]); (new CloneImagesOrVideos($request, $copy))->handle(); - // One job for the creation of the annotation and one job for GenerateImageAnnotationPatch + // One job for the creation of the annotation and one job for ProcessAnnotatedImage Queue::assertPushed(ProcessNewVolumeFiles::class); - Queue::assertPushed(GenerateImageAnnotationPatch::class); + Queue::assertPushed(ProcessAnnotatedImage::class); } public function testHandleVideoAnnotationPatches() { - if (!class_exists(GenerateVideoAnnotationPatch::class)) { - $this->markTestSkipped('Requires '.GenerateVideoAnnotationPatch::class); + if (!class_exists(ProcessAnnotatedVideo::class)) { + $this->markTestSkipped('Requires '.ProcessAnnotatedVideo::class); } // The target project. @@ -680,8 +680,8 @@ public function testHandleVideoAnnotationPatches() ]); (new CloneImagesOrVideos($request, $copy))->handle(); - // One job for the creation of the annotation and one job for GenerateVideoAnnotationPatch + // One job for the creation of the annotation and one job for ProcessAnnotatedVideo Queue::assertPushed(ProcessNewVolumeFiles::class); - Queue::assertPushed(GenerateVideoAnnotationPatch::class); + Queue::assertPushed(ProcessAnnotatedVideo::class); } } From 36ef08672410b81d216d0f717969262081e5c71b Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Thu, 18 Jan 2024 15:51:55 +0100 Subject: [PATCH 49/64] Implement delay to (somewhat) ensure order of processing --- app/Jobs/CloneImagesOrVideos.php | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/app/Jobs/CloneImagesOrVideos.php b/app/Jobs/CloneImagesOrVideos.php index 246a6fda6..52a41c68f 100644 --- a/app/Jobs/CloneImagesOrVideos.php +++ b/app/Jobs/CloneImagesOrVideos.php @@ -162,18 +162,26 @@ public function postProcessCloning($volume) { ProcessNewVolumeFiles::dispatch($volume); + // Give the ProcessNewVolumeFiles job a head start so the file thumbnails are + // generated (mostly) before the annotation thumbnails. + $delay = now()->addSeconds(30); + if (class_exists(ProcessAnnotatedImage::class)) { - $volume->images()->whereHas('annotations')->eachById(function ($image) { - ProcessAnnotatedImage::dispatch($image) - ->onQueue(config('largo.generate_annotation_patch_queue')); - }); + $volume->images()->whereHas('annotations') + ->eachById(function ($image) use ($delay) { + ProcessAnnotatedImage::dispatch($image) + ->delay($delay) + ->onQueue(config('largo.generate_annotation_patch_queue')); + }); } if (class_exists(ProcessAnnotatedVideo::class)) { - $volume->videos()->whereHas('annotations')->eachById(function ($video) { - ProcessAnnotatedVideo::dispatch($video) - ->onQueue(config('largo.generate_annotation_patch_queue')); - }); + $volume->videos() + ->whereHas('annotations')->eachById(function ($video) use ($delay) { + ProcessAnnotatedVideo::dispatch($video) + ->delay($delay) + ->onQueue(config('largo.generate_annotation_patch_queue')); + }); } } From 50334c8872149523f1a21756d6102dd814eb37b1 Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Fri, 19 Jan 2024 09:35:31 +0100 Subject: [PATCH 50/64] Set creating_async before saving volume Fixes #732 --- app/Jobs/CloneImagesOrVideos.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Jobs/CloneImagesOrVideos.php b/app/Jobs/CloneImagesOrVideos.php index f087975d4..4f337e11a 100644 --- a/app/Jobs/CloneImagesOrVideos.php +++ b/app/Jobs/CloneImagesOrVideos.php @@ -144,10 +144,10 @@ public function handle() if ($volume->hasIfdo()) { $this->copyIfdoFile($volume->id, $copy->id); } - $copy->save(); - $copy->creating_async = false; + $copy->save(); + event('volume.cloned', [$copy->id]); }); From 7746e0e808a85cb8adedf599ae67c37d6c654b8c Mon Sep 17 00:00:00 2001 From: Leane Schlundt Date: Fri, 19 Jan 2024 09:36:39 +0100 Subject: [PATCH 51/64] Update tests --- tests/php/Jobs/CloneImagesOrVideosTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/php/Jobs/CloneImagesOrVideosTest.php b/tests/php/Jobs/CloneImagesOrVideosTest.php index 8da61a120..f53016c06 100644 --- a/tests/php/Jobs/CloneImagesOrVideosTest.php +++ b/tests/php/Jobs/CloneImagesOrVideosTest.php @@ -32,6 +32,9 @@ public function testCloneImageVolume() $volume = $this->volume([ 'created_at' => '2022-11-09 14:37:00', 'updated_at' => '2022-11-09 14:37:00', + 'attrs' => [ + 'creating_async' => true, + ], ])->fresh(); // Use fresh() to load even the null fields. $copy = $volume->replicate(); @@ -63,6 +66,7 @@ public function testCloneImageVolume() $this->assertNotEquals($volume->created_at, $copy->created_at); $this->assertNotEquals($volume->updated_at, $copy->updated_at); $this->assertEmpty($copy->images()->first()->labels()->get()); + $this->assertFalse($copy->creating_async); $ignore = ['id', 'created_at', 'updated_at']; $this->assertEquals( @@ -78,6 +82,9 @@ public function testCloneVideoVolume() 'created_at' => '2022-01-09 14:37:00', 'updated_at' => '2022-01-09 14:37:00', 'media_type_id' => MediaType::videoId(), + 'attrs' => [ + 'creating_async' => true, + ], ])->fresh(); // Use fresh() to load even the null fields. $copy = $volume->replicate(); $copy->save(); @@ -104,6 +111,7 @@ public function testCloneVideoVolume() $copy = $project->volumes()->first(); $this->assertEmpty($copy->videos()->first()->labels()->get()); + $this->assertFalse($copy->creating_async); } public function testCloneVolumeImages() From 16d1f2a37bad6272790f03bb1e315ea21897e9f1 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Fri, 19 Jan 2024 10:44:49 +0100 Subject: [PATCH 52/64] Add test case for points with gap validation behavior --- tests/php/VideoAnnotationTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/php/VideoAnnotationTest.php b/tests/php/VideoAnnotationTest.php index 2c2aac66e..d2339cb3a 100644 --- a/tests/php/VideoAnnotationTest.php +++ b/tests/php/VideoAnnotationTest.php @@ -49,6 +49,14 @@ public function testValidatePointsFramesMismatch() $this->model->validatePoints(); } + public function testValidatePointsWithGap() + { + $this->expectNotToPerformAssertions(); + $this->model->points = [[10, 10], [], [20, 20]]; + $this->model->frames = [0.0, null, 1.0]; + $this->model->validatePoints(); + } + public function testValidatePointsPoint() { $this->model->shape_id = Shape::pointId(); From 0caf075af2ec1089f3b2a062353841d0aa315856 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 22:00:12 +0000 Subject: [PATCH 53/64] Bump pillow from 10.0.1 to 10.2.0 Bumps [pillow](https://github.com/python-pillow/Pillow) from 10.0.1 to 10.2.0. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) - [Commits](https://github.com/python-pillow/Pillow/compare/10.0.1...10.2.0) --- updated-dependencies: - dependency-name: pillow dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5f46be82d..6370bda9c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,5 +6,5 @@ scipy==1.10.0 scikit-learn matplotlib==3.5.2 PyExcelerate==0.6.7 -Pillow==10.0.1 +Pillow==10.2.0 Shapely==1.8.1 From 2133d4c08308731285020b48a4b4a4b08cfb11d3 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Tue, 23 Jan 2024 09:08:28 +0100 Subject: [PATCH 54/64] Update Pillow version in dockerfile References #736 --- .docker/worker.dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.docker/worker.dockerfile b/.docker/worker.dockerfile index 3f22309f5..3d980b036 100644 --- a/.docker/worker.dockerfile +++ b/.docker/worker.dockerfile @@ -114,7 +114,7 @@ RUN apk add --no-cache --virtual .build-deps \ libpng-dev \ && pip3 install --no-cache-dir \ PyExcelerate==0.6.7 \ - Pillow==10.0.1 \ + Pillow==10.2.0 \ && apk del --purge .build-deps \ && rm -rf /var/cache/apk/* From ef4a72c662c6a105c343caea8d6740f19cb42a8f Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Tue, 23 Jan 2024 09:28:12 +0100 Subject: [PATCH 55/64] Add Dependabot config --- .docker/requirements.txt | 10 ++++++++++ .github/dependabot.yml | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 .docker/requirements.txt create mode 100644 .github/dependabot.yml diff --git a/.docker/requirements.txt b/.docker/requirements.txt new file mode 100644 index 000000000..f4aa52ee1 --- /dev/null +++ b/.docker/requirements.txt @@ -0,0 +1,10 @@ +# This file is just used to get security alerts from GitHub. Make sure the versions match +# in worker.dockerfile. +numpy==1.22.* +opencv-contrib-python-headless==4.6.0 +scipy==1.10.0 +scikit-learn +matplotlib==3.5.2 +PyExcelerate==0.6.7 +Pillow==10.2.0 +Shapely==1.8.1 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..f22aaf472 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +version: 2 +updates: + - package-ecosystem: pip + directory: "/.docker" + schedule: + interval: "weekly" + - package-ecosystem: composer + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: npm + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: "weekly" From c62ac9eb3c5e9bbc8309b9b25cdb721a484cba84 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Tue, 23 Jan 2024 09:42:39 +0100 Subject: [PATCH 56/64] Update dependabot config --- .github/dependabot.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f22aaf472..62d13e938 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,15 +4,23 @@ updates: directory: "/.docker" schedule: interval: "weekly" + # Disable version updates + open-pull-requests-limit: 0 - package-ecosystem: composer directory: "/" schedule: interval: "weekly" + # Disable version updates + open-pull-requests-limit: 0 - package-ecosystem: npm directory: "/" schedule: interval: "weekly" + # Disable version updates + open-pull-requests-limit: 0 - package-ecosystem: github-actions directory: "/" schedule: interval: "weekly" + # Disable version updates + open-pull-requests-limit: 0 From 68659fecc88b2317145574aa94dae2c1159500f5 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Wed, 24 Jan 2024 16:09:19 +0100 Subject: [PATCH 57/64] Implement AnnotationLabelAttached event --- app/Events/AnnotationLabelAttached.php | 17 +++++++++++++++++ .../Api/ImageAnnotationLabelController.php | 3 +++ .../Api/VideoAnnotationLabelController.php | 3 +++ .../Api/ImageAnnotationLabelControllerTest.php | 7 +++++++ .../Api/VideoAnnotationLabelControllerTest.php | 4 ++++ 5 files changed, 34 insertions(+) create mode 100644 app/Events/AnnotationLabelAttached.php diff --git a/app/Events/AnnotationLabelAttached.php b/app/Events/AnnotationLabelAttached.php new file mode 100644 index 000000000..c710c9991 --- /dev/null +++ b/app/Events/AnnotationLabelAttached.php @@ -0,0 +1,17 @@ +annotation); + AnnotationLabelAttached::dispatch($annotationLabel); + return response($annotationLabel, 201); } catch (QueryException $e) { // Although we check for existence above, this error happened some time. diff --git a/app/Http/Controllers/Api/VideoAnnotationLabelController.php b/app/Http/Controllers/Api/VideoAnnotationLabelController.php index c949988c7..377e3be1b 100644 --- a/app/Http/Controllers/Api/VideoAnnotationLabelController.php +++ b/app/Http/Controllers/Api/VideoAnnotationLabelController.php @@ -2,6 +2,7 @@ namespace Biigle\Http\Controllers\Api; +use Biigle\Events\AnnotationLabelAttached; use Biigle\Http\Requests\DestroyVideoAnnotationLabel; use Biigle\Http\Requests\StoreVideoAnnotationLabel; use Biigle\VideoAnnotationLabel; @@ -65,6 +66,8 @@ public function store(StoreVideoAnnotationLabel $request) // should not be returned unset($annotationLabel->annotation); + AnnotationLabelAttached::dispatch($annotationLabel); + return response($annotationLabel, 201); } catch (QueryException $e) { // Although we check for existence above, this error happened some time. diff --git a/tests/php/Http/Controllers/Api/ImageAnnotationLabelControllerTest.php b/tests/php/Http/Controllers/Api/ImageAnnotationLabelControllerTest.php index 1f40b3137..dba26c170 100644 --- a/tests/php/Http/Controllers/Api/ImageAnnotationLabelControllerTest.php +++ b/tests/php/Http/Controllers/Api/ImageAnnotationLabelControllerTest.php @@ -3,11 +3,13 @@ namespace Biigle\Tests\Http\Controllers\Api; use ApiTestCase; +use Biigle\Events\AnnotationLabelAttached; use Biigle\Tests\AnnotationSessionTest; use Biigle\Tests\ImageAnnotationLabelTest; use Biigle\Tests\ImageAnnotationTest; use Cache; use Carbon\Carbon; +use Illuminate\Support\Facades\Event; use Session; class ImageAnnotationLabelControllerTest extends ApiTestCase @@ -115,6 +117,7 @@ public function testStoreLegacy() public function store($url) { + Event::fake(); $id = $this->annotation->id; $this->doTestApiRoute('POST', "{$url}/{$id}/labels"); @@ -150,12 +153,16 @@ public function store($url) $response->assertStatus(201); $this->assertEquals(1, $this->annotation->labels()->count()); + Event::assertDispatched(AnnotationLabelAttached::class); + $this->beAdmin(); $response = $this->json('POST', "{$url}/{$id}/labels", [ 'label_id' => $this->labelRoot()->id, 'confidence' => 0.1, ]); $response->assertStatus(201); + + Event::assertDispatched(AnnotationLabelAttached::class); $this->assertEquals(2, $this->annotation->labels()->count()); $response->assertJsonFragment([ 'id' => $this->labelRoot()->id, diff --git a/tests/php/Http/Controllers/Api/VideoAnnotationLabelControllerTest.php b/tests/php/Http/Controllers/Api/VideoAnnotationLabelControllerTest.php index 055e8f1b9..353d7e04d 100644 --- a/tests/php/Http/Controllers/Api/VideoAnnotationLabelControllerTest.php +++ b/tests/php/Http/Controllers/Api/VideoAnnotationLabelControllerTest.php @@ -3,11 +3,13 @@ namespace Biigle\Tests\Http\Controllers\Api; use ApiTestCase; +use Biigle\Events\AnnotationLabelAttached; use Biigle\MediaType; use Biigle\Tests\LabelTest; use Biigle\Tests\VideoAnnotationLabelTest; use Biigle\Tests\VideoAnnotationTest; use Biigle\Tests\VideoTest; +use Illuminate\Support\Facades\Event; class VideoAnnotationLabelControllerTest extends ApiTestCase { @@ -20,6 +22,7 @@ public function setUp(): void public function testStore() { + Event::fake(); $annotation = VideoAnnotationTest::create(['video_id' => $this->video->id]); $id = $annotation->id; @@ -51,6 +54,7 @@ public function testStore() $this->assertNotNull($label); $this->assertEquals($this->labelRoot()->id, $label->label_id); $this->assertEquals($this->editor()->id, $label->user_id); + Event::assertDispatched(AnnotationLabelAttached::class); $this ->postJson("api/v1/video-annotations/{$id}/labels", [ From 5ce3bc969f816798b22067e7105ab32705a33e44 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Thu, 25 Jan 2024 14:22:52 +0100 Subject: [PATCH 58/64] Update Python requirements specifications With the new Dependabot config, the reqirements file in the root is no longer needed. --- .docker/requirements.txt | 10 ++++++---- .docker/worker.dockerfile | 4 ++-- requirements.txt | 12 ------------ 3 files changed, 8 insertions(+), 18 deletions(-) delete mode 100644 requirements.txt diff --git a/.docker/requirements.txt b/.docker/requirements.txt index f4aa52ee1..f1f39ef03 100644 --- a/.docker/requirements.txt +++ b/.docker/requirements.txt @@ -1,10 +1,12 @@ # This file is just used to get security alerts from GitHub. Make sure the versions match # in worker.dockerfile. -numpy==1.22.* +numpy==1.24.* opencv-contrib-python-headless==4.6.0 -scipy==1.10.0 -scikit-learn -matplotlib==3.5.2 +scipy==1.10.* +scikit-learn==1.2.* +matplotlib==3.6.* PyExcelerate==0.6.7 Pillow==10.2.0 Shapely==1.8.1 +torch==2.1.* +torchvision==0.16.* diff --git a/.docker/worker.dockerfile b/.docker/worker.dockerfile index 0e2138555..bac552ee9 100644 --- a/.docker/worker.dockerfile +++ b/.docker/worker.dockerfile @@ -96,8 +96,8 @@ RUN LC_ALL=C.UTF-8 apt-get update \ PyExcelerate==0.6.7 \ Pillow==10.2.0 \ && pip3 install --no-cache-dir --break-system-packages --index-url https://download.pytorch.org/whl/cpu \ - torch==2.0.* \ - torchvision==0.15.* \ + torch==2.1.* \ + torchvision==0.16.* \ && apt-get purge -y \ python3-pip \ && apt-get -y autoremove \ diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index ee130248f..000000000 --- a/requirements.txt +++ /dev/null @@ -1,12 +0,0 @@ -# This file is just used to get security alerts from GitHub. Make sure the versions match -# in .docker/worker.dockerfile. -numpy==1.24.* -opencv-contrib-python-headless==4.6.0 -scipy==1.10.* -scikit-learn==1.2.* -matplotlib==3.6.* -PyExcelerate==0.6.7 -Pillow==10.2.0 -Shapely==1.8.1 -torch==2.0.* -torchvision==0.15.* From 13641e4b445ce587e517a217d67b97b008263fec Mon Sep 17 00:00:00 2001 From: Anouk Liberge <39592591+ToukL@users.noreply.github.com> Date: Mon, 29 Jan 2024 17:49:10 +0100 Subject: [PATCH 59/64] fix error on console command creating new user without admin rights --- app/Console/Commands/NewUser.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Console/Commands/NewUser.php b/app/Console/Commands/NewUser.php index 1059b6b1e..785d503c2 100644 --- a/app/Console/Commands/NewUser.php +++ b/app/Console/Commands/NewUser.php @@ -39,6 +39,8 @@ public function handle() if ($this->confirm('Should the user be global admin? [y|N]')) { $u->role_id = Role::adminId(); + } else { + $u->role_id = Role::editorId(); } if ($this->confirm('Do you wish to auto-generate a password? [y|N]')) { From bbea48c803704edb69d8da7e04b8fbdf637a2717 Mon Sep 17 00:00:00 2001 From: gaby kourie Date: Thu, 25 Jan 2024 23:52:46 +0100 Subject: [PATCH 60/64] Fix Vips thumbnails buffer aspect ratio issue. This commit fixes an issue with the aspect ratio of Vips thumbnails buffer by setting the size to 'force'. It also adjusts the corresponding test cases. --- app/Jobs/ProcessNewVideo.php | 26 +++++----- config/videos.php | 14 +++--- tests/php/Jobs/ProcessNewVideoTest.php | 70 ++++++++++++-------------- 3 files changed, 53 insertions(+), 57 deletions(-) diff --git a/app/Jobs/ProcessNewVideo.php b/app/Jobs/ProcessNewVideo.php index 4efa99580..f411d33e5 100644 --- a/app/Jobs/ProcessNewVideo.php +++ b/app/Jobs/ProcessNewVideo.php @@ -240,7 +240,7 @@ protected function generateVideoThumbnail($path, $time, $width, $height) $buffer = $this->ffmpegVideo->frame(TimeCode::fromSeconds($time)) ->save(null, false, true); - return VipsImage::thumbnail_buffer($buffer, $width, ['height' => $height]) + return VipsImage::thumbnail_buffer($buffer, $width, ['height' => $height, 'size' => 'force']) ; } @@ -290,29 +290,29 @@ protected function getThumbnailTimes($duration) protected function generateSprites($path, $duration, $disk, $fragment) { - $maxFrames = config('videos.sprites_max_frames'); - $minFrames = config('videos.sprites_min_frames'); - $framesPerSprite = config('videos.frames_per_sprite'); - $thumbsPerRow = sqrt($framesPerSprite); + $maxThumbnails = config('videos.sprites_max_thumbnails'); + $minThumbnails = config('videos.sprites_min_thumbnails'); + $thumbnailsPerSprite = config('videos.sprites_thumbnails_per_sprite'); + $thumbnailsPerRow = sqrt($thumbnailsPerSprite); $thumbnailWidth = config('videos.sprites_thumbnail_width'); $thumbnailHeight = config('videos.sprites_thumbnail_height'); $spriteFormat = config('videos.sprites_format'); - $intervalSeconds = config('videos.sprites_interval_seconds'); + $defaultThumbnailInterval = config('videos.sprites_thumbnail_interval'); $durationRounded = floor($duration * 10) / 10; - $estimatedFrames = $durationRounded / $intervalSeconds; - // Adjust the frame time based on the number of estimated frames - $intervalSeconds = ($estimatedFrames > $maxFrames) ? $durationRounded / $maxFrames - : (($estimatedFrames < $minFrames) ? $durationRounded / $minFrames : 10); + $estimatedThumbnails = $durationRounded / $defaultThumbnailInterval; + // Adjust the frame time based on the number of estimated thumbnails + $thumbnailInterval = ($estimatedThumbnails > $maxThumbnails) ? $durationRounded / $maxThumbnails + : (($estimatedThumbnails < $minThumbnails) ? $durationRounded / $minThumbnails : $defaultThumbnailInterval); $thumbnails = []; $spritesCounter = 0; - for ($time = 0.0; $time < $durationRounded && $durationRounded > 0; $time += $intervalSeconds) { + for ($time = 0.0; $time < $durationRounded && $durationRounded > 0; $time += $thumbnailInterval) { $time = round($time, 1); // Create a thumbnail from the video at the specified time and add it to the thumbnails array. $thumbnails[] = $this->generateVideoThumbnail($path, $time, $thumbnailWidth, $thumbnailHeight); - if (count($thumbnails) === $framesPerSprite || $time >= abs($durationRounded - $intervalSeconds)) { + if (count($thumbnails) === $thumbnailsPerSprite || $time >= abs($durationRounded - $thumbnailInterval)) { // Join the thumbnails into a NxN sprite - $sprite = VipsImage::arrayjoin($thumbnails, ['across' => $thumbsPerRow]); + $sprite = VipsImage::arrayjoin($thumbnails, ['across' => $thumbnailsPerRow]); // Write the sprite to buffer with quality 75 and stripped metadata $spriteBuffer = $sprite->writeToBuffer(".{$spriteFormat}", [ 'Q' => 75, diff --git a/config/videos.php b/config/videos.php index 4cdcd6967..eea11cd55 100644 --- a/config/videos.php +++ b/config/videos.php @@ -53,20 +53,20 @@ 'track_object_max_jobs_per_user' => env('VIDEOS_TRACK_OBJECT_MAX_JOBS_PER_USER', 10), /* - | Number of max frames to generate for sprites. + | Number of max thumbnails to generate for sprites. */ - 'sprites_max_frames' => 1500, + 'sprites_max_thumbnails' => 1500, /* - | Number of min frames to generate for sprites. + | Number of min thumbnails to generate for sprites. */ - 'sprites_min_frames' => 5, + 'sprites_min_thumbnails' => 5, /* - | Number of frames per sprite. Default 5x5 = 25. + | Number of thumbnails per sprite. Default 5x5 = 25. | The square root of the number must be an integer. */ - 'frames_per_sprite' => 25, + 'sprites_thumbnails_per_sprite' => 25, /* | Dimensions of the thumbnail images to create in sprites. @@ -83,5 +83,5 @@ | Standard time interval at which thumbnails should be sampled when generating sprites. | It will be adjusted dynamically during the process. */ - 'sprites_interval_seconds' => 10, + 'sprites_thumbnail_interval' => 2.5, ]; diff --git a/tests/php/Jobs/ProcessNewVideoTest.php b/tests/php/Jobs/ProcessNewVideoTest.php index c7c76d957..c5dfc2627 100644 --- a/tests/php/Jobs/ProcessNewVideoTest.php +++ b/tests/php/Jobs/ProcessNewVideoTest.php @@ -36,20 +36,18 @@ public function testHandleThumbnails() public function testGenerateSprites() { Storage::fake('video-thumbs'); - config(['videos.frames_per_sprite' => 9]); $video = VideoTest::create(['filename' => 'test.mp4']); $job = new ProcessNewVideoStub($video); $job->duration = 180; $job->handle(); $this->assertEquals(180, $video->fresh()->duration); - $expected_frames = 18; - // additional frames caused by generating regular thumbnails - $additional_frames = $job->frames - $expected_frames; - $this->assertEquals($expected_frames, $job->frames - $additional_frames); - $this->assertEquals([0.0, 10.0, 20.0, 30.0, 40.0, 50.0, - 60.0, 70.0, 80.0, 90.0, 100.0, 110.0, 120.0, 130.0, - 140.0, 150.0, 160.0, 170.0], array_slice($job->times, $additional_frames, $job->frames)); + $expectedThumbnails = 72; + $expectedIntervals = range(0, 177.5, 2.5); + // additional thumbnails caused by generating regular thumbnails + $additionalThumbnails = $job->thumbnails - $expectedThumbnails; + $this->assertEquals($expectedThumbnails, $job->thumbnails - $additionalThumbnails); + $this->assertEquals($expectedIntervals, array_slice($job->times, $additionalThumbnails, $job->thumbnails)); $disk = Storage::disk('video-thumbs'); $fragment = fragment_uuid_path($video->uuid); @@ -65,11 +63,11 @@ public function testGenerateSpritesZeroDuration() $job->duration = 0; $job->handle(); - $expected_frames = 0; - // additional frames caused by generating regular thumbnails - $additional_frames = $job->frames - $expected_frames; - $this->assertEquals($expected_frames, $job->frames - $additional_frames); - $this->assertEquals([], array_slice($job->times, $additional_frames, $job->frames)); + $expectedThumbnails = 0; + // additional thumbnails caused by generating regular thumbnails + $additionalThumbnails = $job->thumbnails - $expectedThumbnails; + $this->assertEquals($expectedThumbnails, $job->thumbnails - $additionalThumbnails); + $this->assertEquals([], array_slice($job->times, $additionalThumbnails, $job->thumbnails)); $disk = Storage::disk('video-thumbs'); $fragment = fragment_uuid_path($video->uuid); @@ -85,11 +83,11 @@ public function testGenerateSpritesOneSecondDuration() $job->handle(); $this->assertEquals(1, $video->fresh()->duration); - $expected_frames = 5; - // additional frames caused by generating regular thumbnails - $additional_frames = $job->frames - $expected_frames; - $this->assertEquals($expected_frames, $job->frames - $additional_frames); - $this->assertEquals([0.0, 0.2, 0.4, 0.6, 0.8], array_slice($job->times, $additional_frames, $job->frames)); + $expectedThumbnails = 5; + // additional thumbnails caused by generating regular thumbnails + $additionalThumbnails = $job->thumbnails - $expectedThumbnails; + $this->assertEquals($expectedThumbnails, $job->thumbnails - $additionalThumbnails); + $this->assertEquals([0.0, 0.2, 0.4, 0.6, 0.8], array_slice($job->times, $additionalThumbnails, $job->thumbnails)); $disk = Storage::disk('video-thumbs'); $fragment = fragment_uuid_path($video->uuid); @@ -97,45 +95,43 @@ public function testGenerateSpritesOneSecondDuration() } - public function testGenerateSpritesExceedsMaxFrames() + public function testGenerateSpritesExceedsMaxThumbnails() { Storage::fake('video-thumbs'); - config(['videos.sprites_max_frames' => 25]); - config(['videos.sprites_interval_seconds' => 5]); + config(['videos.sprites_max_thumbnails' => 50]); $video = VideoTest::create(['filename' => 'test.mp4']); $job = new ProcessNewVideoStub($video); $job->duration = 130; $job->handle(); $this->assertEquals(130, $video->fresh()->duration); - $expected_frames = 25; + $expectedThumbnails = 50; + $expectedIntervals = array_map(fn ($value) => round($value, 2), range(0, 127.4, 2.6)); // additional frames caused by generating regular thumbnails - $additional_frames = $job->frames - $expected_frames; - $this->assertEquals($expected_frames, $job->frames - $additional_frames); - $this->assertEquals([0.0, 5.2, 10.4, 15.6, 20.8, 26.0, 31.2, 36.4, - 41.6, 46.8, 52.0, 57.2, 62.4, 67.6, 72.8, 78.0, 83.2, 88.4, - 93.6, 98.8, 104.0, 109.2, 114.4, 119.6, 124.8], array_slice($job->times, $additional_frames, $job->frames)); + $additionalThumbnails = $job->thumbnails - $expectedThumbnails; + $this->assertEquals($expectedThumbnails, $job->thumbnails - $additionalThumbnails); + $this->assertEquals($expectedIntervals, array_slice($job->times, $additionalThumbnails, $job->thumbnails)); $disk = Storage::disk('video-thumbs'); $fragment = fragment_uuid_path($video->uuid); $this->assertTrue($disk->exists("{$fragment}/sprite_0.webp")); + $this->assertTrue($disk->exists("{$fragment}/sprite_1.webp")); } - public function testGenerateSpritesFallsBelowMinFrames() + public function testGenerateSpritesFallsBelowMinThumbnails() { Storage::fake('video-thumbs'); $video = VideoTest::create(['filename' => 'test.mp4']); $job = new ProcessNewVideoStub($video); - $job->duration = 20; + $job->duration = 5; $job->handle(); - $this->assertEquals(20, $video->fresh()->duration); - $expected_frames = 5; + $this->assertEquals(5, $video->fresh()->duration); + $expectedThumbnails = 5; // additional frames caused by generating regular thumbnails - $additional_frames = $job->frames - $expected_frames; - $this->assertEquals($expected_frames, $job->frames - $additional_frames); - $this->assertEquals([0.0, 4.0, 8.0, 12.0, 16.0], array_slice($job->times, $additional_frames, $job->frames)); - + $additionalThumbnails = $job->thumbnails - $expectedThumbnails; + $this->assertEquals($expectedThumbnails, $job->thumbnails - $additionalThumbnails); + $this->assertEquals([0.0, 1.0, 2.0, 3.0, 4.0], array_slice($job->times, $additionalThumbnails, $job->thumbnails)); $disk = Storage::disk('video-thumbs'); $fragment = fragment_uuid_path($video->uuid); @@ -268,7 +264,7 @@ class ProcessNewVideoStub extends ProcessNewVideo public $duration = 0; public $codec = ''; public $times = []; - public $frames = 0; + public $thumbnails = 0; protected function getCodec($path) { @@ -283,7 +279,7 @@ protected function getVideoDuration($path) protected function generateVideoThumbnail($path, $time, $width, $height) { $this->times[] = $time; - $this->frames += 1; + $this->thumbnails += 1; return VipsImage::black(240, 138) ->embed(30, 40, 240, 138, ['extend' => Extend::WHITE]) // Extend left & top edges with white color From 1b6c587db6c0252c1f8f29d055ff352075d4b927 Mon Sep 17 00:00:00 2001 From: gaby kourie Date: Sat, 27 Jan 2024 19:36:51 +0100 Subject: [PATCH 61/64] Implement Thumbnail Preview feature on frontend --- .../Views/Videos/VideoController.php | 45 ++++-- .../js/videos/components/scrollStrip.vue | 30 ++++ .../js/videos/components/settingsTab.vue | 12 ++ .../js/videos/components/thumbnailPreview.vue | 141 ++++++++++++++++++ .../js/videos/components/videoProgress.vue | 12 ++ .../js/videos/components/videoTimeline.vue | 5 + resources/assets/js/videos/main.js | 3 +- resources/assets/js/videos/stores/settings.js | 1 + resources/assets/js/videos/videoContainer.vue | 1 + .../sass/videos/components/_thumbnail.scss | 10 ++ resources/assets/sass/videos/main.scss | 1 + resources/views/videos/show.blade.php | 8 + resources/views/videos/show/content.blade.php | 1 + .../videos/show/sidebar-settings.blade.php | 3 + 14 files changed, 262 insertions(+), 11 deletions(-) create mode 100644 resources/assets/js/videos/components/thumbnailPreview.vue create mode 100644 resources/assets/sass/videos/components/_thumbnail.scss diff --git a/app/Http/Controllers/Views/Videos/VideoController.php b/app/Http/Controllers/Views/Videos/VideoController.php index 873fd9d74..bfca29e4e 100644 --- a/app/Http/Controllers/Views/Videos/VideoController.php +++ b/app/Http/Controllers/Views/Videos/VideoController.php @@ -72,15 +72,40 @@ public function show(Request $request, $id) 'too-large' => VIDEO::ERROR_TOO_LARGE, ]); - return view('videos.show', compact( - 'user', - 'video', - 'volume', - 'videos', - 'shapes', - 'labelTrees', - 'annotationSessions', - 'errors' - )); + $fileIds = $volume->orderedFiles()->pluck('uuid', 'id'); + + if ($volume->isImageVolume()) { + $thumbUriTemplate = thumbnail_url(':uuid'); + } else { + $thumbUriTemplate = thumbnail_url(':uuid', config('videos.thumbnail_storage_disk')); + } + + $spritesThumbnailsPerSprite = config('videos.sprites_thumbnails_per_sprite'); + $spritesThumbnailInterval = config('videos.sprites_thumbnail_interval'); + $spritesMaxThumbnails = config('videos.sprites_max_thumbnails'); + $spritesMinThumbnails = config('videos.sprites_min_thumbnails'); + $spritesThumbnailWidth = config('videos.sprites_thumbnail_width'); + $spritesThumbnailHeight = config('videos.sprites_thumbnail_height'); + return view( + 'videos.show', + compact( + 'user', + 'video', + 'volume', + 'videos', + 'shapes', + 'labelTrees', + 'annotationSessions', + 'errors', + 'fileIds', + 'thumbUriTemplate', + 'spritesThumbnailsPerSprite', + 'spritesThumbnailInterval', + 'spritesMaxThumbnails', + 'spritesMinThumbnails', + 'spritesThumbnailWidth', + 'spritesThumbnailHeight' + ) + ); } } diff --git a/resources/assets/js/videos/components/scrollStrip.vue b/resources/assets/js/videos/components/scrollStrip.vue index 4636cf5c2..a6a0fdf9f 100644 --- a/resources/assets/js/videos/components/scrollStrip.vue +++ b/resources/assets/js/videos/components/scrollStrip.vue @@ -10,10 +10,19 @@ :style="scrollerStyle" @mousemove="handleUpdateHoverTime" > +
this.initialElementWidth; }, + showThumbPreview() { + return this.showThumbnailPreview && this.showThumb; + } }, methods: { updateInitialElementWidth() { @@ -210,6 +232,14 @@ export default { this.hasOverflowTop = false; this.hasOverflowBottom = false; }, + handleVideoProgressMousemove(clientX) { + this.showThumb = true; + this.clientMouseX = clientX; + this.scrollstripTop = this.$refs.scroller.getBoundingClientRect().top; + }, + hideThumbnailPreview() { + this.showThumb = false; + }, }, watch: { hoverTime(time) { diff --git a/resources/assets/js/videos/components/settingsTab.vue b/resources/assets/js/videos/components/settingsTab.vue index 3932af765..f5d6c6d38 100644 --- a/resources/assets/js/videos/components/settingsTab.vue +++ b/resources/assets/js/videos/components/settingsTab.vue @@ -15,6 +15,7 @@ export default { 'showLabelTooltip', 'showMousePosition', 'showProgressIndicator', + 'showThumbnailPreview' ], annotationOpacity: 1, showMinimap: true, @@ -23,6 +24,7 @@ export default { showMousePosition: false, playbackRate: 1.0, showProgressIndicator: true, + showThumbnailPreview: true, }; }, methods: { @@ -50,6 +52,12 @@ export default { handleHideProgressIndicator() { this.showProgressIndicator = false; }, + handleShowThumbnailPreview() { + this.showThumbnailPreview = true; + }, + handleHideThumbnailPreview() { + this.showThumbnailPreview = false; + }, }, watch: { annotationOpacity(value) { @@ -86,6 +94,10 @@ export default { this.$emit('update', 'showProgressIndicator', show); Settings.set('showProgressIndicator', show); }, + showThumbnailPreview(show) { + this.$emit('update', 'showThumbnailPreview', show); + Settings.set('showThumbnailPreview', show); + }, }, created() { this.restoreKeys.forEach((key) => { diff --git a/resources/assets/js/videos/components/thumbnailPreview.vue b/resources/assets/js/videos/components/thumbnailPreview.vue new file mode 100644 index 000000000..121dbc59c --- /dev/null +++ b/resources/assets/js/videos/components/thumbnailPreview.vue @@ -0,0 +1,141 @@ + + + \ No newline at end of file diff --git a/resources/assets/js/videos/components/videoProgress.vue b/resources/assets/js/videos/components/videoProgress.vue index 0906a08ad..ec859d1cc 100644 --- a/resources/assets/js/videos/components/videoProgress.vue +++ b/resources/assets/js/videos/components/videoProgress.vue @@ -2,6 +2,8 @@
diff --git a/resources/assets/js/videos/components/videoTimeline.vue b/resources/assets/js/videos/components/videoTimeline.vue index d0329d8d2..fef6867d2 100644 --- a/resources/assets/js/videos/components/videoTimeline.vue +++ b/resources/assets/js/videos/components/videoTimeline.vue @@ -20,6 +20,7 @@ :duration="duration" :current-time="currentTime" :seeking="seeking" + :showThumbnailPreview="showThumbnailPreview" @seek="emitSeek" @select="emitSelect" @deselect="emitDeselect" @@ -65,6 +66,10 @@ export default { return null; }, }, + showThumbnailPreview: { + type: Boolean, + default: true, + }, }, data() { return { diff --git a/resources/assets/js/videos/main.js b/resources/assets/js/videos/main.js index 414c9ea6b..d415ad484 100644 --- a/resources/assets/js/videos/main.js +++ b/resources/assets/js/videos/main.js @@ -2,7 +2,8 @@ import './filters/videoTime'; import Navbar from './navbar'; import SearchResults from './searchResults'; import VideoContainer from './videoContainer'; - +import ThumbnailPreview from './components/thumbnailPreview'; biigle.$mount('search-results', SearchResults); biigle.$mount('video-annotations-navbar', Navbar); biigle.$mount('video-container', VideoContainer); +biigle.$mount('thumbnail-preview', ThumbnailPreview); \ No newline at end of file diff --git a/resources/assets/js/videos/stores/settings.js b/resources/assets/js/videos/stores/settings.js index 5a88bd2c4..589880ac3 100644 --- a/resources/assets/js/videos/stores/settings.js +++ b/resources/assets/js/videos/stores/settings.js @@ -7,6 +7,7 @@ let defaults = { showLabelTooltip: false, showMousePosition: false, showProgressIndicator: true, + showThumbnailPreview: true, }; export default new Settings({ diff --git a/resources/assets/js/videos/videoContainer.vue b/resources/assets/js/videos/videoContainer.vue index b1c905813..04f17523f 100644 --- a/resources/assets/js/videos/videoContainer.vue +++ b/resources/assets/js/videos/videoContainer.vue @@ -68,6 +68,7 @@ export default { showMousePosition: false, playbackRate: 1.0, showProgressIndicator: true, + showThumbnailPreview: true, }, openTab: '', urlParams: { diff --git a/resources/assets/sass/videos/components/_thumbnail.scss b/resources/assets/sass/videos/components/_thumbnail.scss new file mode 100644 index 000000000..efbc6ef8c --- /dev/null +++ b/resources/assets/sass/videos/components/_thumbnail.scss @@ -0,0 +1,10 @@ +.thumbnail-preview { + position: fixed; + z-index: 1; +} + +.thumbnail-canvas { + @extend %info-box; + padding: 0; + box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.25); +} \ No newline at end of file diff --git a/resources/assets/sass/videos/main.scss b/resources/assets/sass/videos/main.scss index 35aade99b..51c588646 100644 --- a/resources/assets/sass/videos/main.scss +++ b/resources/assets/sass/videos/main.scss @@ -53,3 +53,4 @@ @import 'components/annotationClip'; @import 'components/annotationSegment'; @import 'components/annotationKeyframe'; +@import 'components/thumbnail'; \ No newline at end of file diff --git a/resources/views/videos/show.blade.php b/resources/views/videos/show.blade.php index 09e3164a5..5d666ce6e 100644 --- a/resources/views/videos/show.blade.php +++ b/resources/views/videos/show.blade.php @@ -69,5 +69,13 @@ class="sidebar-container__content" biigle.$declare('videos.videoFilenames', {!! $videos->values() !!}); biigle.$declare('videos.user', {!! $user !!}); biigle.$declare('videos.isAdmin', @can('update', $volume) true @else false @endcan); + biigle.$declare('videos.fileUuids', {!! $fileIds !!}); + biigle.$declare('videos.thumbUri', '{{ $thumbUriTemplate }}'); + biigle.$declare('videos.spritesThumbnailsPerSprite', {!! $spritesThumbnailsPerSprite !!}); + biigle.$declare('videos.spritesThumbnailInterval', {!! $spritesThumbnailInterval !!}); + biigle.$declare('videos.spritesMaxThumbnails', {!! $spritesMaxThumbnails !!}); + biigle.$declare('videos.spritesMinThumbnails', {!! $spritesMinThumbnails !!}); + biigle.$declare('videos.spritesThumbnailWidth', {!! $spritesThumbnailWidth !!}); + biigle.$declare('videos.spritesThumbnailHeight', {!! $spritesThumbnailHeight !!}); @endpush diff --git a/resources/views/videos/show/content.blade.php b/resources/views/videos/show/content.blade.php index b6cf7a816..9f9f15d79 100644 --- a/resources/views/videos/show/content.blade.php +++ b/resources/views/videos/show/content.blade.php @@ -69,6 +69,7 @@ :seeking="seeking" :height-offset="timelineHeightOffset" :pending-annotation="pendingAnnotation" + :show-thumbnail-preview="settings.showThumbnailPreview" v-on:seek="seek" v-on:select="selectAnnotation" v-on:deselect="deselectAnnotation" diff --git a/resources/views/videos/show/sidebar-settings.blade.php b/resources/views/videos/show/sidebar-settings.blade.php index f77eb6e4c..dec80fe3c 100644 --- a/resources/views/videos/show/sidebar-settings.blade.php +++ b/resources/views/videos/show/sidebar-settings.blade.php @@ -35,6 +35,9 @@ Mouse Position
+
From 731f01c193f4a6625955382ce0a9ae2365f7ad0f Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Tue, 30 Jan 2024 11:23:14 +0100 Subject: [PATCH 62/64] Add missing pandas requirement References https://github.com/biigle/reports/issues/111 --- .docker/requirements.txt | 1 + .docker/worker.dockerfile | 1 + 2 files changed, 2 insertions(+) diff --git a/.docker/requirements.txt b/.docker/requirements.txt index f1f39ef03..285820e6d 100644 --- a/.docker/requirements.txt +++ b/.docker/requirements.txt @@ -10,3 +10,4 @@ Pillow==10.2.0 Shapely==1.8.1 torch==2.1.* torchvision==0.16.* +pandas==1.5.3 diff --git a/.docker/worker.dockerfile b/.docker/worker.dockerfile index bac552ee9..5339ed683 100644 --- a/.docker/worker.dockerfile +++ b/.docker/worker.dockerfile @@ -14,6 +14,7 @@ RUN LC_ALL=C.UTF-8 apt-get update \ python3-sklearn \ python3-matplotlib \ python3-shapely \ + python3-pandas \ && apt-get -y autoremove \ && apt-get clean \ && rm -r /var/lib/apt/lists/* From 8d866b25aa51fc71ada74399bf69bb7d0c6a358e Mon Sep 17 00:00:00 2001 From: gaby kourie Date: Thu, 1 Feb 2024 20:58:12 +0100 Subject: [PATCH 63/64] Fix issue with flashing thumbnail when sprite is not found --- .../Views/Videos/VideoController.php | 6 +--- .../js/videos/components/thumbnailPreview.vue | 33 ++++++++++--------- resources/assets/js/videos/main.js | 4 +-- .../sass/videos/components/_thumbnail.scss | 2 ++ .../manual/tutorials/videos/sidebar.blade.php | 5 +++ 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/app/Http/Controllers/Views/Videos/VideoController.php b/app/Http/Controllers/Views/Videos/VideoController.php index bfca29e4e..7bcbc516a 100644 --- a/app/Http/Controllers/Views/Videos/VideoController.php +++ b/app/Http/Controllers/Views/Videos/VideoController.php @@ -74,11 +74,7 @@ public function show(Request $request, $id) $fileIds = $volume->orderedFiles()->pluck('uuid', 'id'); - if ($volume->isImageVolume()) { - $thumbUriTemplate = thumbnail_url(':uuid'); - } else { - $thumbUriTemplate = thumbnail_url(':uuid', config('videos.thumbnail_storage_disk')); - } + $thumbUriTemplate = thumbnail_url(':uuid', config('videos.thumbnail_storage_disk')); $spritesThumbnailsPerSprite = config('videos.sprites_thumbnails_per_sprite'); $spritesThumbnailInterval = config('videos.sprites_thumbnail_interval'); diff --git a/resources/assets/js/videos/components/thumbnailPreview.vue b/resources/assets/js/videos/components/thumbnailPreview.vue index 121dbc59c..3dc2a52b5 100644 --- a/resources/assets/js/videos/components/thumbnailPreview.vue +++ b/resources/assets/js/videos/components/thumbnailPreview.vue @@ -2,8 +2,10 @@
+ :height="thumbnailHeight" + v-show="!spriteNotFound"> { + this.spriteNotFound = false; this.viewThumbnailPreview(); } - // can't hide the error 404 message on the browser console - // trying to use a http request to ask if the file exists and wrap it with try/catch - // does prevent the GET 404(Not Found) error - // but we get a HEAD 404(Not Found) error instead (maybe server side?) this.sprite.onerror = () => { - if (this.thumbnailPreview.style.display !== 'none') { - this.thumbnailPreview.style.display = 'none'; - } + this.spriteNotFound = true; } } }; diff --git a/resources/assets/js/videos/main.js b/resources/assets/js/videos/main.js index d415ad484..fee4837c8 100644 --- a/resources/assets/js/videos/main.js +++ b/resources/assets/js/videos/main.js @@ -2,8 +2,6 @@ import './filters/videoTime'; import Navbar from './navbar'; import SearchResults from './searchResults'; import VideoContainer from './videoContainer'; -import ThumbnailPreview from './components/thumbnailPreview'; biigle.$mount('search-results', SearchResults); biigle.$mount('video-annotations-navbar', Navbar); -biigle.$mount('video-container', VideoContainer); -biigle.$mount('thumbnail-preview', ThumbnailPreview); \ No newline at end of file +biigle.$mount('video-container', VideoContainer); \ No newline at end of file diff --git a/resources/assets/sass/videos/components/_thumbnail.scss b/resources/assets/sass/videos/components/_thumbnail.scss index efbc6ef8c..22d485b7d 100644 --- a/resources/assets/sass/videos/components/_thumbnail.scss +++ b/resources/assets/sass/videos/components/_thumbnail.scss @@ -1,5 +1,7 @@ .thumbnail-preview { position: fixed; + top: 0; + left: 0; z-index: 1; } diff --git a/resources/views/manual/tutorials/videos/sidebar.blade.php b/resources/views/manual/tutorials/videos/sidebar.blade.php index a2f628dc4..ce74e42f2 100644 --- a/resources/views/manual/tutorials/videos/sidebar.blade.php +++ b/resources/views/manual/tutorials/videos/sidebar.blade.php @@ -71,5 +71,10 @@

The mouse position switch controls the display of an additional map overlay that shows the current position of the cursor on the video in pixels.

+ +

+ The thumbnail switch controls the display of a thumbnail preview that appears when you hover your cursor over the video progress bar. The thumbnail shows a preview of the video frame at the hovered time position. +

+
@endsection From da6f0ea8babbaa8195b6d8c3f6886d7c4267ef4d Mon Sep 17 00:00:00 2001 From: gaby kourie Date: Sun, 4 Feb 2024 19:14:26 +0100 Subject: [PATCH 64/64] Add limit to sprite loading retry attempts --- .../js/videos/components/thumbnailPreview.vue | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/resources/assets/js/videos/components/thumbnailPreview.vue b/resources/assets/js/videos/components/thumbnailPreview.vue index 3dc2a52b5..4607ea889 100644 --- a/resources/assets/js/videos/components/thumbnailPreview.vue +++ b/resources/assets/js/videos/components/thumbnailPreview.vue @@ -49,6 +49,9 @@ export default { thumbProgressBarSpace: 150, sideButtonsWidth: 52, spritesFolderPath: null, + triedUrls: {}, + // retry sprite loading x times + retryAttempts: 2, // start with true to hide flashing black thumbnail spriteNotFound: true, // default values but will be overwritten in created() @@ -72,7 +75,16 @@ export default { methods: { updateSprite() { this.spriteIdx = Math.floor(this.hoverTime / (this.thumbnailInterval * this.thumbnailsPerSprite)); - this.sprite.src = this.spritesFolderPath + "sprite_" + this.spriteIdx + ".webp"; + let SpriteUrl = this.spritesFolderPath + "sprite_" + this.spriteIdx + ".webp"; + + if (!this.triedUrls[SpriteUrl]) { + this.triedUrls[SpriteUrl] = 0 + } + if (this.triedUrls[SpriteUrl] < this.retryAttempts) { + this.sprite.src = SpriteUrl; + } else { + this.spriteNotFound = true; + } }, viewThumbnailPreview() { // calculate the current row and column of the sprite @@ -138,6 +150,9 @@ export default { } this.sprite.onerror = () => { this.spriteNotFound = true; + if (this.sprite.src in this.triedUrls) { + this.triedUrls[this.sprite.src]++; + } } } };