From d209c0bd22eda522e0054dfe4122f4f6c40a4032 Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Wed, 18 Sep 2019 11:04:59 +0200 Subject: [PATCH] Automatically downscale pictures to reduce page loading times (closes #275) --- changelog/52_UNRELEASED_2019-xx-xx.md | 2 + composer.json | 3 +- composer.lock | 380 ++++++++++++++---------- controllers/FilesApiController.php | 27 +- grocy.openapi.json | 31 ++ public/viewjs/components/productcard.js | 2 +- public/viewjs/mealplan.js | 2 +- services/FilesService.php | 30 ++ views/productform.blade.php | 2 +- views/recipeform.blade.php | 2 +- views/recipes.blade.php | 6 +- 11 files changed, 314 insertions(+), 173 deletions(-) diff --git a/changelog/52_UNRELEASED_2019-xx-xx.md b/changelog/52_UNRELEASED_2019-xx-xx.md index 91a504ec..525ac354 100644 --- a/changelog/52_UNRELEASED_2019-xx-xx.md +++ b/changelog/52_UNRELEASED_2019-xx-xx.md @@ -35,12 +35,14 @@ ### General improvements/fixes - Improved the handling which entry page to use with disabled feature flags (thanks @nielstholenaar) - Boolean settings provided via environment variables (so the strings `true` and `false`) are now parsed correctly (thanks @mduret) +- All uploaded pictures (currently for products and recipes) are now automatically downscaled to the appropriate size when serving them to improve page load times ### API improvements & non-breaking changes - New endpoint `/stock/shoppinglist/add-product` to add a product to a shopping list (thanks @Forceu) - New endpoint `/stock/shoppinglist/remove-product` to remove a product from a shopping list (thanks @Forceu) - New endpoint `/chores/executions/calculate-next-assignments` to (re)calculate next user assignments for a single or all chores - New endpoint `/objects/{entity}/search/{searchString}` search for objects by name (contains search) + - Endpoint `GET /files/{group}/{fileName}` can now also downscale pictures (see API documentation on [/api](https://demo-en.grocy.info/api)) - When adding a product (through `stock/product/{productId}/add` or `stock/product/{productId}/inventory`) with omitted best before date and if the given product has "Default best before days" set, the best before date is calculated based on that (so far always today was used which is still the case when no date is supplied and also the product has no "Default best before days set) (thanks @Forceu) - Field `stock_amount` of endpoint `/stock/products/{productId}ยด now returns `0` instead of `null` when the given product is not in stock (thanks @Forceu) - Fixed that `/system/db-changed-time` always returned the current time (more or less) due to that that time is the database file modification time and the database is effectively changed on each request because of session information tracking - which now explicitly does not change the database file modification time, so this should work again to determine if any data changes happened diff --git a/composer.json b/composer.json index c8645ca8..326c6b9e 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,8 @@ "tuupola/cors-middleware": "^0.9.4", "eluceo/ical": "^0.15.0", "erusev/parsedown": "^1.7.3", - "gettext/gettext": "^4.6.2" + "gettext/gettext": "^4.6.2", + "gumlet/php-image-resize": "^1.9.2" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 3d46926e..8fbf99b1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "613590bc6e46e2b4542023617bd56778", + "content-hash": "17eb560bc9d67d2f79118929ae59ca34", "packages": [ { "name": "container-interop/container-interop", @@ -106,16 +106,16 @@ }, { "name": "eluceo/ical", - "version": "0.15.0", + "version": "0.15.1", "source": { "type": "git", "url": "https://github.com/markuspoerschke/iCal.git", - "reference": "add0ca99aa1f77f134a2e8b071f2ebc22b115139" + "reference": "bdd24747587f6f9b10770a7b873a13e273f85f39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/markuspoerschke/iCal/zipball/add0ca99aa1f77f134a2e8b071f2ebc22b115139", - "reference": "add0ca99aa1f77f134a2e8b071f2ebc22b115139", + "url": "https://api.github.com/repos/markuspoerschke/iCal/zipball/bdd24747587f6f9b10770a7b873a13e273f85f39", + "reference": "bdd24747587f6f9b10770a7b873a13e273f85f39", "shasum": "" }, "require": { @@ -153,7 +153,7 @@ "ics", "php calendar" ], - "time": "2019-01-13T22:00:58+00:00" + "time": "2019-08-06T20:33:43+00:00" }, { "name": "erusev/parsedown", @@ -203,16 +203,16 @@ }, { "name": "gettext/gettext", - "version": "v4.6.2", + "version": "v4.6.3", "source": { "type": "git", "url": "https://github.com/oscarotero/Gettext.git", - "reference": "93176b272d61fb58a9767be71c50d19149cb1e48" + "reference": "70c6ff2fecd275e6ef9cdd542f55939a3d1904d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/oscarotero/Gettext/zipball/93176b272d61fb58a9767be71c50d19149cb1e48", - "reference": "93176b272d61fb58a9767be71c50d19149cb1e48", + "url": "https://api.github.com/repos/oscarotero/Gettext/zipball/70c6ff2fecd275e6ef9cdd542f55939a3d1904d6", + "reference": "70c6ff2fecd275e6ef9cdd542f55939a3d1904d6", "shasum": "" }, "require": { @@ -261,7 +261,7 @@ "po", "translation" ], - "time": "2019-01-12T18:40:56+00:00" + "time": "2019-07-15T12:56:31+00:00" }, { "name": "gettext/languages", @@ -325,17 +325,72 @@ "time": "2018-11-13T22:06:07+00:00" }, { - "name": "illuminate/container", - "version": "v5.8.15", + "name": "gumlet/php-image-resize", + "version": "1.9.2", "source": { "type": "git", - "url": "https://github.com/illuminate/container.git", - "reference": "9405989993a48c2cd50ad1e5b2b08a33383c3807" + "url": "https://github.com/gumlet/php-image-resize.git", + "reference": "06339a9c1b167acd58173db226f57957a6617547" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/container/zipball/9405989993a48c2cd50ad1e5b2b08a33383c3807", - "reference": "9405989993a48c2cd50ad1e5b2b08a33383c3807", + "url": "https://api.github.com/repos/gumlet/php-image-resize/zipball/06339a9c1b167acd58173db226f57957a6617547", + "reference": "06339a9c1b167acd58173db226f57957a6617547", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "ext-gd": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "apigen/apigen": "^4.1", + "ext-exif": "*", + "ext-gd": "*", + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^4.8" + }, + "suggest": { + "ext-exif": "Auto-rotate jpeg files" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gumlet\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aditya Patadia", + "homepage": "http://aditya.patadia.org/" + } + ], + "description": "PHP class to re-size and scale images", + "homepage": "https://github.com/gumlet/php-image-resize", + "keywords": [ + "image", + "php", + "resize", + "scale" + ], + "time": "2019-01-01T13:53:00+00:00" + }, + { + "name": "illuminate/container", + "version": "v5.8.35", + "source": { + "type": "git", + "url": "https://github.com/illuminate/container.git", + "reference": "b42e5ef939144b77f78130918da0ce2d9ee16574" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/container/zipball/b42e5ef939144b77f78130918da0ce2d9ee16574", + "reference": "b42e5ef939144b77f78130918da0ce2d9ee16574", "shasum": "" }, "require": { @@ -367,20 +422,20 @@ ], "description": "The Illuminate Container package.", "homepage": "https://laravel.com", - "time": "2019-04-22T13:12:35+00:00" + "time": "2019-08-20T02:00:23+00:00" }, { "name": "illuminate/contracts", - "version": "v5.8.15", + "version": "v5.8.35", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", - "reference": "0b3cbe19051c9a8c247091cc0867d3b65250d093" + "reference": "00fc6afee788fa07c311b0650ad276585f8aef96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/0b3cbe19051c9a8c247091cc0867d3b65250d093", - "reference": "0b3cbe19051c9a8c247091cc0867d3b65250d093", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/00fc6afee788fa07c311b0650ad276585f8aef96", + "reference": "00fc6afee788fa07c311b0650ad276585f8aef96", "shasum": "" }, "require": { @@ -411,11 +466,11 @@ ], "description": "The Illuminate Contracts package.", "homepage": "https://laravel.com", - "time": "2019-04-21T18:51:09+00:00" + "time": "2019-07-30T13:57:21+00:00" }, { "name": "illuminate/events", - "version": "v5.8.15", + "version": "v5.8.35", "source": { "type": "git", "url": "https://github.com/illuminate/events.git", @@ -460,16 +515,16 @@ }, { "name": "illuminate/filesystem", - "version": "v5.8.15", + "version": "v5.8.35", "source": { "type": "git", "url": "https://github.com/illuminate/filesystem.git", - "reference": "e3c7302b147704420041c07aac538b9de67ebb8f" + "reference": "494ba903402d64ec49c8d869ab61791db34b2288" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/filesystem/zipball/e3c7302b147704420041c07aac538b9de67ebb8f", - "reference": "e3c7302b147704420041c07aac538b9de67ebb8f", + "url": "https://api.github.com/repos/illuminate/filesystem/zipball/494ba903402d64ec49c8d869ab61791db34b2288", + "reference": "494ba903402d64ec49c8d869ab61791db34b2288", "shasum": "" }, "require": { @@ -508,20 +563,20 @@ ], "description": "The Illuminate Filesystem package.", "homepage": "https://laravel.com", - "time": "2019-04-08T12:56:11+00:00" + "time": "2019-08-14T13:38:15+00:00" }, { "name": "illuminate/support", - "version": "v5.8.15", + "version": "v5.8.35", "source": { "type": "git", "url": "https://github.com/illuminate/support.git", - "reference": "7fbf8d76946ee53587955b670bd8a47e3d48e854" + "reference": "e63a495d3bf01654f70def1046fb925c4bb56506" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/7fbf8d76946ee53587955b670bd8a47e3d48e854", - "reference": "7fbf8d76946ee53587955b670bd8a47e3d48e854", + "url": "https://api.github.com/repos/illuminate/support/zipball/e63a495d3bf01654f70def1046fb925c4bb56506", + "reference": "e63a495d3bf01654f70def1046fb925c4bb56506", "shasum": "" }, "require": { @@ -569,20 +624,20 @@ ], "description": "The Illuminate Support package.", "homepage": "https://laravel.com", - "time": "2019-04-25T14:06:24+00:00" + "time": "2019-09-03T16:36:47+00:00" }, { "name": "illuminate/view", - "version": "v5.8.15", + "version": "v5.8.35", "source": { "type": "git", "url": "https://github.com/illuminate/view.git", - "reference": "a62ef6b6c4392a8bb5cf3af5f5076459525286c5" + "reference": "c859919bc3be97a3f114377d5d812f047b8ea90d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/view/zipball/a62ef6b6c4392a8bb5cf3af5f5076459525286c5", - "reference": "a62ef6b6c4392a8bb5cf3af5f5076459525286c5", + "url": "https://api.github.com/repos/illuminate/view/zipball/c859919bc3be97a3f114377d5d812f047b8ea90d", + "reference": "c859919bc3be97a3f114377d5d812f047b8ea90d", "shasum": "" }, "require": { @@ -618,7 +673,7 @@ ], "description": "The Illuminate View package.", "homepage": "https://laravel.com", - "time": "2019-04-17T14:14:38+00:00" + "time": "2019-06-20T13:13:59+00:00" }, { "name": "morris/lessql", @@ -726,16 +781,16 @@ }, { "name": "nesbot/carbon", - "version": "2.17.1", + "version": "2.24.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "96acbc0c03782e8115156dd4dd8b736267155066" + "reference": "934459c5ac0658bc765ad1e53512c7c77adcac29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/96acbc0c03782e8115156dd4dd8b736267155066", - "reference": "96acbc0c03782e8115156dd4dd8b736267155066", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/934459c5ac0658bc765ad1e53512c7c77adcac29", + "reference": "934459c5ac0658bc765ad1e53512c7c77adcac29", "shasum": "" }, "require": { @@ -746,11 +801,14 @@ "require-dev": { "friendsofphp/php-cs-fixer": "^2.14 || ^3.0", "kylekatarnls/multi-tester": "^1.1", - "phpmd/phpmd": "^2.6", + "phpmd/phpmd": "dev-php-7.1-compatibility", "phpstan/phpstan": "^0.11", "phpunit/phpunit": "^7.5 || ^8.0", "squizlabs/php_codesniffer": "^3.4" }, + "bin": [ + "bin/carbon" + ], "type": "library", "extra": { "laravel": { @@ -773,16 +831,20 @@ "name": "Brian Nesbitt", "email": "brian@nesbot.com", "homepage": "http://nesbot.com" + }, + { + "name": "kylekatarnls", + "homepage": "http://github.com/kylekatarnls" } ], - "description": "A simple API extension for DateTime.", + "description": "A API extension for DateTime that supports 281 different languages.", "homepage": "http://carbon.nesbot.com", "keywords": [ "date", "datetime", "time" ], - "time": "2019-04-27T18:04:27+00:00" + "time": "2019-08-31T16:37:55+00:00" }, { "name": "nikic/fast-route", @@ -1325,20 +1387,23 @@ }, { "name": "slim/slim", - "version": "3.12.1", + "version": "3.12.2", "source": { "type": "git", "url": "https://github.com/slimphp/Slim.git", - "reference": "eaee12ef8d0750db62b8c548016d82fb33addb6b" + "reference": "200c6143f15baa477601879b64ab2326847aac0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim/zipball/eaee12ef8d0750db62b8c548016d82fb33addb6b", - "reference": "eaee12ef8d0750db62b8c548016d82fb33addb6b", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/200c6143f15baa477601879b64ab2326847aac0b", + "reference": "200c6143f15baa477601879b64ab2326847aac0b", "shasum": "" }, "require": { "container-interop/container-interop": "^1.2", + "ext-json": "*", + "ext-libxml": "*", + "ext-simplexml": "*", "nikic/fast-route": "^1.0", "php": ">=5.5.0", "pimple/pimple": "^3.0", @@ -1363,25 +1428,25 @@ "MIT" ], "authors": [ - { - "name": "Rob Allen", - "email": "rob@akrabat.com", - "homepage": "http://akrabat.com" - }, { "name": "Josh Lockhart", "email": "hello@joshlockhart.com", "homepage": "https://joshlockhart.com" }, - { - "name": "Gabriel Manricks", - "email": "gmanricks@me.com", - "homepage": "http://gabrielmanricks.com" - }, { "name": "Andrew Smith", "email": "a.smith@silentworks.co.uk", "homepage": "http://silentworks.co.uk" + }, + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, + { + "name": "Gabriel Manricks", + "email": "gmanricks@me.com", + "homepage": "http://gabrielmanricks.com" } ], "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs", @@ -1392,91 +1457,20 @@ "micro", "router" ], - "time": "2019-04-16T16:47:29+00:00" - }, - { - "name": "symfony/contracts", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/contracts.git", - "reference": "d3636025e8253c6144358ec0a62773cae588395b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/contracts/zipball/d3636025e8253c6144358ec0a62773cae588395b", - "reference": "d3636025e8253c6144358ec0a62773cae588395b", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "require-dev": { - "psr/cache": "^1.0", - "psr/container": "^1.0", - "symfony/polyfill-intl-idn": "^1.10" - }, - "suggest": { - "psr/cache": "When using the Cache contracts", - "psr/container": "When using the Service contracts", - "symfony/cache-contracts-implementation": "", - "symfony/event-dispatcher-implementation": "", - "symfony/http-client-contracts-implementation": "", - "symfony/service-contracts-implementation": "", - "symfony/translation-contracts-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\": "" - }, - "exclude-from-classmap": [ - "**/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A set of abstractions extracted out of the Symfony components", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "time": "2019-04-27T14:29:50+00:00" + "time": "2019-08-20T18:46:05+00:00" }, { "name": "symfony/debug", - "version": "v4.2.8", + "version": "v4.3.4", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "2d279b6bb1d582dd5740d4d3251ae8c18812ed37" + "reference": "afcdea44a2e399c1e4b52246ec8d54c715393ced" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/2d279b6bb1d582dd5740d4d3251ae8c18812ed37", - "reference": "2d279b6bb1d582dd5740d4d3251ae8c18812ed37", + "url": "https://api.github.com/repos/symfony/debug/zipball/afcdea44a2e399c1e4b52246ec8d54c715393ced", + "reference": "afcdea44a2e399c1e4b52246ec8d54c715393ced", "shasum": "" }, "require": { @@ -1492,7 +1486,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -1519,20 +1513,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2019-04-11T11:27:41+00:00" + "time": "2019-08-20T14:27:59+00:00" }, { "name": "symfony/finder", - "version": "v4.2.8", + "version": "v4.3.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "e45135658bd6c14b61850bf131c4f09a55133f69" + "reference": "86c1c929f0a4b24812e1eb109262fc3372c8e9f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/e45135658bd6c14b61850bf131c4f09a55133f69", - "reference": "e45135658bd6c14b61850bf131c4f09a55133f69", + "url": "https://api.github.com/repos/symfony/finder/zipball/86c1c929f0a4b24812e1eb109262fc3372c8e9f2", + "reference": "86c1c929f0a4b24812e1eb109262fc3372c8e9f2", "shasum": "" }, "require": { @@ -1541,7 +1535,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -1568,20 +1562,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2019-04-06T13:51:08+00:00" + "time": "2019-08-14T12:26:46+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.11.0", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", "shasum": "" }, "require": { @@ -1593,7 +1587,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -1627,26 +1621,26 @@ "portable", "shim" ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/translation", - "version": "v4.2.8", + "version": "v4.3.4", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "181a426dd129cb496f12d7e7555f6d0b37a7615b" + "reference": "28498169dd334095fa981827992f3a24d50fed0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/181a426dd129cb496f12d7e7555f6d0b37a7615b", - "reference": "181a426dd129cb496f12d7e7555f6d0b37a7615b", + "url": "https://api.github.com/repos/symfony/translation/zipball/28498169dd334095fa981827992f3a24d50fed0f", + "reference": "28498169dd334095fa981827992f3a24d50fed0f", "shasum": "" }, "require": { "php": "^7.1.3", - "symfony/contracts": "^1.0.2", - "symfony/polyfill-mbstring": "~1.0" + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^1.1.6" }, "conflict": { "symfony/config": "<3.4", @@ -1654,7 +1648,7 @@ "symfony/yaml": "<3.4" }, "provide": { - "symfony/translation-contracts-implementation": "1.0" + "symfony/translation-implementation": "1.0" }, "require-dev": { "psr/log": "~1.0", @@ -1664,6 +1658,7 @@ "symfony/finder": "~2.8|~3.0|~4.0", "symfony/http-kernel": "~3.4|~4.0", "symfony/intl": "~3.4|~4.0", + "symfony/service-contracts": "^1.1.2", "symfony/var-dumper": "~3.4|~4.0", "symfony/yaml": "~3.4|~4.0" }, @@ -1675,7 +1670,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -1702,7 +1697,64 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2019-05-01T12:55:36+00:00" + "time": "2019-08-26T08:55:16+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v1.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "325b17c24f3ee23cbecfa63ba809c6d89b5fa04a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/325b17c24f3ee23cbecfa63ba809c6d89b5fa04a", + "reference": "325b17c24f3ee23cbecfa63ba809c6d89b5fa04a", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "time": "2019-08-02T12:15:04+00:00" }, { "name": "tuupola/callable-handler", @@ -1818,16 +1870,16 @@ }, { "name": "tuupola/http-factory", - "version": "1.0.3", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/tuupola/http-factory.git", - "reference": "1fd4eaafe3a6e0c26d288e3b3e17d777ea1991bf" + "reference": "5fbde4c65a10d09a85652684a6e569542265a749" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tuupola/http-factory/zipball/1fd4eaafe3a6e0c26d288e3b3e17d777ea1991bf", - "reference": "1fd4eaafe3a6e0c26d288e3b3e17d777ea1991bf", + "url": "https://api.github.com/repos/tuupola/http-factory/zipball/5fbde4c65a10d09a85652684a6e569542265a749", + "reference": "5fbde4c65a10d09a85652684a6e569542265a749", "shasum": "" }, "require": { @@ -1871,7 +1923,7 @@ "psr-17", "psr-7" ], - "time": "2019-01-11T15:13:01+00:00" + "time": "2019-08-07T07:10:58+00:00" } ], "packages-dev": [], diff --git a/controllers/FilesApiController.php b/controllers/FilesApiController.php index 9af9f751..fb7f619b 100644 --- a/controllers/FilesApiController.php +++ b/controllers/FilesApiController.php @@ -51,7 +51,32 @@ class FilesApiController extends BaseApiController throw new \Exception('Invalid filename'); } - $filePath = $this->FilesService->GetFilePath($args['group'], $fileName); + $forceServeAs = null; + if (isset($request->getQueryParams()['force_serve_as']) && !empty($request->getQueryParams()['force_serve_as'])) + { + $forceServeAs = $request->getQueryParams()['force_serve_as']; + } + + if ($forceServeAs == FilesService::FILE_SERVE_TYPE_PICTURE) + { + $bestFitHeight = 999999; + if (isset($request->getQueryParams()['best_fit_height']) && !empty($request->getQueryParams()['best_fit_height']) && is_numeric($request->getQueryParams()['best_fit_height'])) + { + $bestFitHeight = $request->getQueryParams()['best_fit_height']; + } + + $bestFitWidth = 999999; + if (isset($request->getQueryParams()['best_fit_width']) && !empty($request->getQueryParams()['best_fit_width']) && is_numeric($request->getQueryParams()['best_fit_width'])) + { + $bestFitWidth = $request->getQueryParams()['best_fit_width']; + } + + $filePath = $this->FilesService->DownscaleImage($args['group'], $fileName, $bestFitHeight, $bestFitWidth); + } + else + { + $filePath = $this->FilesService->GetFilePath($args['group'], $fileName); + } if (file_exists($filePath)) { diff --git a/grocy.openapi.json b/grocy.openapi.json index bcb77989..ad023943 100644 --- a/grocy.openapi.json +++ b/grocy.openapi.json @@ -655,6 +655,37 @@ "schema": { "type": "string" } + }, + { + "in": "query", + "name": "force_serve_as", + "required": false, + "description": "Force the file to be served as the given type", + "schema": { + "type": "string", + "enum": [ + "picture" + ] + } + }, + { + "in": "query", + "name": "best_fit_height", + "required": false, + "description": "Only when using `force_serve_as` = `picture`: Downscale the picture to the given height while maintaining the aspect ratio", + "schema": { + "type": "integer" + } + } + , + { + "in": "query", + "name": "best_fit_width", + "required": false, + "description": "Only when using `force_serve_as` = `picture`: Downscale the picture to the given width while maintaining the aspect ratio", + "schema": { + "type": "integer" + } } ], "responses": { diff --git a/public/viewjs/components/productcard.js b/public/viewjs/components/productcard.js index 4f063b42..78f2cf8e 100644 --- a/public/viewjs/components/productcard.js +++ b/public/viewjs/components/productcard.js @@ -82,7 +82,7 @@ Grocy.Components.ProductCard.Refresh = function(productId) { $("#productcard-no-product-picture").addClass("d-none"); $("#productcard-product-picture").removeClass("d-none"); - $("#productcard-product-picture").attr("src", U('/api/files/productpictures/' + btoa(productDetails.product.picture_file_name))); + $("#productcard-product-picture").attr("src", U('/api/files/productpictures/' + btoa(productDetails.product.picture_file_name) + '?force_serve_as=picture&best_fit_height=400&best_fit_width=400')); } else { diff --git a/public/viewjs/mealplan.js b/public/viewjs/mealplan.js index 33d30478..463ddbbf 100644 --- a/public/viewjs/mealplan.js +++ b/public/viewjs/mealplan.js @@ -103,7 +103,7 @@ var calendar = $("#calendar").fullCalendar({ if (recipe.picture_file_name && !recipe.picture_file_name.isEmpty()) { - element.html(element.html() + '') + element.html(element.html() + '') } }, "eventAfterAllRender": function(view) diff --git a/services/FilesService.php b/services/FilesService.php index ea85f5e1..c1824b2f 100644 --- a/services/FilesService.php +++ b/services/FilesService.php @@ -2,8 +2,12 @@ namespace Grocy\Services; +use \Gumlet\ImageResize; + class FilesService extends BaseService { + const FILE_SERVE_TYPE_PICTURE = 'picture'; + public function __construct() { parent::__construct(); @@ -28,4 +32,30 @@ class FilesService extends BaseService return $groupFolderPath . '/' . $fileName; } + + public function DownscaleImage($group, $fileName, $bestFitHeight, $bestFitWidth) + { + $filePath = $this->GetFilePath($group, $fileName); + $fileNameWithoutExtension = pathinfo($filePath, PATHINFO_FILENAME); + $fileExtension = pathinfo($filePath, PATHINFO_EXTENSION); + + $fileNameDownscaled = $fileNameWithoutExtension . '__downscaledto' . $bestFitHeight . 'x' . $bestFitWidth . '.' . $fileExtension; + $filePathDownscaled = $this->GetFilePath($group, $fileNameDownscaled); + + try + { + if (!file_exists($filePathDownscaled)) + { + $image = new ImageResize($filePath); + $image->resizeToBestFit($bestFitHeight, $bestFitWidth); + $image->save($filePathDownscaled); + } + } + catch (ImageResizeException $ex) + { + return $filePath; + } + + return $filePathDownscaled; + } } diff --git a/views/productform.blade.php b/views/productform.blade.php index ccb7319c..71201c24 100644 --- a/views/productform.blade.php +++ b/views/productform.blade.php @@ -275,7 +275,7 @@ @if(!empty($product->picture_file_name)) -

+

{{ $__t('The current picture will be deleted when you save the product') }}

@else

{{ $__t('No picture available') }}

diff --git a/views/recipeform.blade.php b/views/recipeform.blade.php index 0905a041..c12da217 100644 --- a/views/recipeform.blade.php +++ b/views/recipeform.blade.php @@ -209,7 +209,7 @@ @if(!empty($recipe->picture_file_name)) -

+

{{ $__t('The current picture will be deleted when you save the recipe') }}

@else

{{ $__t('No picture available') }}

diff --git a/views/recipes.blade.php b/views/recipes.blade.php index 80d2ead4..a39dd86b 100644 --- a/views/recipes.blade.php +++ b/views/recipes.blade.php @@ -102,7 +102,7 @@
@if(!empty($recipe->picture_file_name)) - + @endif
{{ $recipe->name }}
@@ -173,7 +173,7 @@
@if(!empty($selectedRecipeSubRecipe->picture_file_name)) -

+

@endif @php $selectedRecipeSubRecipePositionsFiltered = FindAllObjectsInArrayByPropertyValue($selectedRecipeSubRecipesPositions, 'child_recipe_id', $selectedRecipeSubRecipe->id); @endphp @@ -225,7 +225,7 @@ @if(!empty($selectedRecipe->picture_file_name)) -

+

@endif @if($selectedRecipePositionsResolved->count() > 0)