diff --git a/appinfo/app.php b/appinfo/app.php deleted file mode 100644 index 66e666473..000000000 --- a/appinfo/app.php +++ /dev/null @@ -1,71 +0,0 @@ -, Vinzenz Rosenkranz - * @copyright Sander Brand 2014, Vinzenz Rosenkranz 2016, 2017 - */ - -namespace OCA\Maps\AppInfo; - -use OCP\AppFramework\App; -use OCA\Maps\Service\AddressService; -use OCP\Util; -use Symfony\Component\EventDispatcher\GenericEvent; -use OCA\Maps\Hooks\FileHooks; - -$app = \OC::$server->query(Application::class); -$container = $app->getContainer(); - -$eventDispatcher = \OC::$server->getEventDispatcher(); -$eventDispatcher->addListener('OCA\Files::loadAdditionalScripts', function() { - Util::addScript('maps', 'filetypes'); - Util::addStyle('maps', 'filetypes'); -}); - -// carddav/caldav lookup addresses -$listener = function($event) use ($container) { - if ($event instanceof GenericEvent) { - $cData = $event->getArgument('cardData'); - $cUri = $event->getArgument('cardUri'); - $a = $container->query(AddressService::class); - $a->scheduleVCardForLookup($cData, $cUri); - } -}; -$deletionListener = function($event) use ($container) { - if ($event instanceof GenericEvent) { - $cUri = $event->getArgument('cardUri'); - $a = $container->query(AddressService::class); - $a->deleteDBContactAddresses($cUri); - } -}; - -$eventDispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::createCard', $listener); -$eventDispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $listener); -$eventDispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', $deletionListener); - -$l = \OC::$server->getL10N('maps'); - -$container->query(\OCP\INavigationManager::class)->add(function () use ($container) { - $urlGenerator = $container->query(\OCP\IURLGenerator::class); - $l10n = $container->query(\OCP\IL10N::class); - return [ - 'id' => 'maps', - - 'order' => 10, - - // the route that will be shown on startup - 'href' => $urlGenerator->linkToRoute('maps.page.index'), - - // the icon that will be shown in the navigation - // this file needs to exist in img/ - 'icon' => $urlGenerator->imagePath('maps', 'maps.svg'), - - // the title of your application. This will be used in the - // navigation or on the settings page of your app - 'name' => $l10n->t('Maps'), - ]; -}); diff --git a/appinfo/info.xml b/appinfo/info.xml index 362c8576f..e0484286c 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -34,6 +34,7 @@ exif + diff --git a/composer.json b/composer.json index 1a7225fa2..48915ae53 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "require": { - "lsolesen/pel": "^0.9.6" + "lsolesen/pel": "^0.9.11" }, "require-dev": { "christophwurst/nextcloud_testing": "^0.9.1" diff --git a/composer.lock b/composer.lock index 12b5b1039..a264ec8d1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,30 +4,31 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "575e2e87565fe6f8708b125b6e5c2742", + "content-hash": "7bdae107df5b0fc9c3281204faef2dda", "packages": [ { "name": "lsolesen/pel", - "version": "0.9.9", + "version": "0.9.12", "source": { "type": "git", "url": "https://github.com/pel/pel.git", - "reference": "95dd3c16161c588f0ac66662c1870a073159e5ec" + "reference": "b95fe29cdacf9d36330da277f10910a13648c84c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pel/pel/zipball/95dd3c16161c588f0ac66662c1870a073159e5ec", - "reference": "95dd3c16161c588f0ac66662c1870a073159e5ec", + "url": "https://api.github.com/repos/pel/pel/zipball/b95fe29cdacf9d36330da277f10910a13648c84c", + "reference": "b95fe29cdacf9d36330da277f10910a13648c84c", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": ">=7.1.0" }, "require-dev": { + "ext-exif": "*", "ext-gd": "*", - "phpunit/phpunit": ">=4.8.36 <8", - "satooshi/php-coveralls": "1.0.*", - "squizlabs/php_codesniffer": "^3.0.0" + "php-coveralls/php-coveralls": ">2.4", + "squizlabs/php_codesniffer": ">3.5", + "symfony/phpunit-bridge": "^4 || ^5" }, "type": "library", "autoload": { @@ -59,7 +60,11 @@ "exif", "image" ], - "time": "2020-11-07T06:04:18+00:00" + "support": { + "issues": "https://github.com/pel/pel/issues", + "source": "https://github.com/pel/pel/tree/0.9.12" + }, + "time": "2022-02-18T13:20:54+00:00" } ], "packages-dev": [ @@ -101,33 +106,38 @@ } ], "description": "Simple and fast unit and integration testing framework for Nextcloud, based on PHPUnit", + "support": { + "issues": "https://github.com/ChristophWurst/nextcloud_testing/issues", + "source": "https://github.com/ChristophWurst/nextcloud_testing/tree/0.8.2" + }, "time": "2019-12-02T11:44:23+00:00" }, { "name": "doctrine/instantiator", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^8.0", + "doctrine/coding-standard": "^9", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.22" }, "type": "library", "autoload": { @@ -152,6 +162,10 @@ "constructor", "instantiate" ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", @@ -166,7 +180,7 @@ "type": "tidelift" } ], - "time": "2020-11-10T18:47:58+00:00" + "time": "2022-03-03T08:28:38+00:00" }, { "name": "facebook/webdriver", @@ -226,42 +240,48 @@ "selenium", "webdriver" ], + "support": { + "forum": "https://www.facebook.com/groups/phpwebdriver/", + "issues": "https://github.com/facebook/php-webdriver/issues", + "source": "https://github.com/facebook/php-webdriver" + }, "abandoned": "php-webdriver/webdriver", "time": "2019-06-13T08:02:18+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.10.2", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, - "replace": { - "myclabs/deep-copy": "self.version" + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" }, "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, "files": [ "src/DeepCopy/deep_copy.php" - ] + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -275,38 +295,43 @@ "object", "object graph" ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + }, "funding": [ { "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", "type": "tidelift" } ], - "time": "2020-11-13T09:40:50+00:00" + "time": "2022-03-03T13:19:32+00:00" }, { "name": "phar-io/manifest", - "version": "1.0.3", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -336,24 +361,28 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2018-07-08T19:23:20+00:00" + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" }, { "name": "phar-io/version", - "version": "2.0.1", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -383,7 +412,11 @@ } ], "description": "Library for handling version information and constraints", - "time": "2018-07-08T19:19:57+00:00" + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -432,20 +465,24 @@ "reflection", "static analysis" ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.2.2", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", "shasum": "" }, "require": { @@ -456,7 +493,8 @@ "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.2" + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" }, "type": "library", "extra": { @@ -484,20 +522,24 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2020-09-03T19:13:55+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.4.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" + "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706", + "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706", "shasum": "" }, "require": { @@ -505,7 +547,8 @@ "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "*" + "ext-tokenizer": "*", + "psalm/phar": "^4.8" }, "type": "library", "extra": { @@ -529,37 +572,41 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2020-09-17T18:55:26+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.0" + }, + "time": "2022-01-04T19:58:01+00:00" }, { "name": "phpspec/prophecy", - "version": "1.12.1", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "8ce87516be71aae9b956f81906aaf0338e0d8a2d" + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/8ce87516be71aae9b956f81906aaf0338e0d8a2d", - "reference": "8ce87516be71aae9b956f81906aaf0338e0d8a2d", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", "shasum": "" }, "require": { "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.1", + "php": "^7.2 || ~8.0, <8.2", "phpdocumentor/reflection-docblock": "^5.2", "sebastian/comparator": "^3.0 || ^4.0", "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^6.0", - "phpunit/phpunit": "^8.0 || ^9.0 <9.3" + "phpspec/phpspec": "^6.0 || ^7.0", + "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -592,29 +639,33 @@ "spy", "stub" ], - "time": "2020-09-29T09:10:42+00:00" + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" + }, + "time": "2021-12-08T12:19:24+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "7.0.10", + "version": "7.0.15", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf" + "reference": "819f92bba8b001d4363065928088de22f25a3a48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f1884187926fbb755a9aaf0b3836ad3165b478bf", - "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/819f92bba8b001d4363065928088de22f25a3a48", + "reference": "819f92bba8b001d4363065928088de22f25a3a48", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^7.2", + "php": ">=7.2", "phpunit/php-file-iterator": "^2.0.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.1.1", + "phpunit/php-token-stream": "^3.1.3 || ^4.0", "sebastian/code-unit-reverse-lookup": "^1.0.1", "sebastian/environment": "^4.2.2", "sebastian/version": "^2.0.1", @@ -655,27 +706,37 @@ "testing", "xunit" ], - "time": "2019-11-20T13:55:58+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-07-26T12:20:09+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "2.0.2", + "version": "2.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "050bedf145a257b1ff02746c31894800e5122946" + "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", - "reference": "050bedf145a257b1ff02746c31894800e5122946", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", + "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -705,7 +766,17 @@ "filesystem", "iterator" ], - "time": "2018-09-13T20:33:42+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:42:26+00:00" }, { "name": "phpunit/php-text-template", @@ -746,27 +817,31 @@ "keywords": [ "template" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" + }, "time": "2015-06-21T13:50:34+00:00" }, { "name": "phpunit/php-timer", - "version": "2.1.2", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -795,33 +870,43 @@ "keywords": [ "timer" ], - "time": "2019-06-07T04:22:29+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:20:02+00:00" }, { "name": "phpunit/php-token-stream", - "version": "3.1.1", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" + "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", - "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/a853a0e183b9db7eed023d7933a858fa1c8d25a3", + "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": "^7.1" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -844,21 +929,31 @@ "keywords": [ "tokenizer" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", + "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "abandoned": true, - "time": "2019-09-17T06:23:10+00:00" + "time": "2020-08-04T08:28:15+00:00" }, { "name": "phpunit/phpunit", - "version": "8.5.9", + "version": "8.5.25", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f5c8a5dd5e7e8d68d7562bfb48d47287d33937d6" + "reference": "9ff23f4dfde040ccd3b8db876192d1184b934158" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f5c8a5dd5e7e8d68d7562bfb48d47287d33937d6", - "reference": "f5c8a5dd5e7e8d68d7562bfb48d47287d33937d6", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9ff23f4dfde040ccd3b8db876192d1184b934158", + "reference": "9ff23f4dfde040ccd3b8db876192d1184b934158", "shasum": "" }, "require": { @@ -870,12 +965,12 @@ "ext-xml": "*", "ext-xmlwriter": "*", "myclabs/deep-copy": "^1.10.0", - "phar-io/manifest": "^1.0.3", - "phar-io/version": "^2.0.1", - "php": "^7.2", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.2", "phpspec/prophecy": "^1.10.3", - "phpunit/php-code-coverage": "^7.0.10", - "phpunit/php-file-iterator": "^2.0.2", + "phpunit/php-code-coverage": "^7.0.12", + "phpunit/php-file-iterator": "^2.0.4", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^2.1.2", "sebastian/comparator": "^3.0.2", @@ -928,9 +1023,13 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.25" + }, "funding": [ { - "url": "https://phpunit.de/donate.html", + "url": "https://phpunit.de/sponsors.html", "type": "custom" }, { @@ -938,27 +1037,27 @@ "type": "github" } ], - "time": "2020-11-10T12:51:38+00:00" + "time": "2022-03-16T16:24:13+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=5.6" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -983,29 +1082,39 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:15:22+00:00" }, { "name": "sebastian/comparator", - "version": "3.0.2", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", "shasum": "" }, "require": { - "php": "^7.1", + "php": ">=7.1", "sebastian/diff": "^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -1023,6 +1132,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -1034,10 +1147,6 @@ { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", @@ -1047,24 +1156,34 @@ "compare", "equality" ], - "time": "2018-07-12T15:12:46+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:04:30+00:00" }, { "name": "sebastian/diff", - "version": "3.0.2", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "^7.5 || ^8.0", @@ -1086,13 +1205,13 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", @@ -1103,24 +1222,34 @@ "unidiff", "unified diff" ], - "time": "2019-02-04T06:01:07+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:59:04+00:00" }, { "name": "sebastian/environment", - "version": "4.2.3", + "version": "4.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368" + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368", - "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "^7.5" @@ -1156,29 +1285,39 @@ "environment", "hhvm" ], - "time": "2019-11-20T08:46:58+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:53:42+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.2", + "version": "3.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" + "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", + "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", "shasum": "" }, "require": { - "php": "^7.0", + "php": ">=7.0", "sebastian/recursion-context": "^3.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -1223,24 +1362,34 @@ "export", "exporter" ], - "time": "2019-09-14T09:02:43+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-11-11T13:51:24+00:00" }, { "name": "sebastian/global-state", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" + "reference": "de036ec91d55d2a9e0db2ba975b512cdb1c23921" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", - "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/de036ec91d55d2a9e0db2ba975b512cdb1c23921", + "reference": "de036ec91d55d2a9e0db2ba975b512cdb1c23921", "shasum": "" }, "require": { - "php": "^7.2", + "php": ">=7.2", "sebastian/object-reflector": "^1.1.1", "sebastian/recursion-context": "^3.0" }, @@ -1277,24 +1426,34 @@ "keywords": [ "global state" ], - "time": "2019-02-01T05:30:01+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-10T06:55:38+00:00" }, { "name": "sebastian/object-enumerator", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", "shasum": "" }, "require": { - "php": "^7.0", + "php": ">=7.0", "sebastian/object-reflector": "^1.1.1", "sebastian/recursion-context": "^3.0" }, @@ -1324,24 +1483,34 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:40:27+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" @@ -1369,24 +1538,34 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:37:18+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" @@ -1407,14 +1586,14 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, { "name": "Adam Harvey", "email": "aharvey@php.net" @@ -1422,24 +1601,34 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:34:24+00:00" }, { "name": "sebastian/resource-operations", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "type": "library", "extra": { @@ -1464,24 +1653,34 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2018-10-04T04:07:39+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:30:19+00:00" }, { "name": "sebastian/type", - "version": "1.1.3", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3" + "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/3aaaa15fa71d27650d62a948be022fe3b48541a3", - "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4", + "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4", "shasum": "" }, "require": { - "php": "^7.2" + "php": ">=7.2" }, "require-dev": { "phpunit/phpunit": "^8.2" @@ -1510,7 +1709,17 @@ ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", - "time": "2019-07-02T08:10:15+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:25:11+00:00" }, { "name": "sebastian/version", @@ -1553,32 +1762,39 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/master" + }, "time": "2016-10-03T07:35:21+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.20.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41" + "reference": "30885182c981ab175d4d034db0f6f469898070ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f4ba089a5b6366e453971d3aad5fe8e897b37f41", - "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", + "reference": "30885182c981ab175d4d034db0f6f469898070ab", "shasum": "" }, "require": { "php": ">=7.1" }, + "provide": { + "ext-ctype": "*" + }, "suggest": { "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1586,12 +1802,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1615,6 +1831,92 @@ "polyfill", "portable" ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-10-20T20:35:02+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.25.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1629,24 +1931,25 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2022-03-04T08:16:47+00:00" }, { "name": "symfony/process", - "version": "v4.4.16", + "version": "v4.4.37", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "2f4b049fb80ca5e9874615a2a85dc2a502090f05" + "reference": "b2d924e5a4cb284f293d5092b1dbf0d364cb8b67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/2f4b049fb80ca5e9874615a2a85dc2a502090f05", - "reference": "2f4b049fb80ca5e9874615a2a85dc2a502090f05", + "url": "https://api.github.com/repos/symfony/process/zipball/b2d924e5a4cb284f293d5092b1dbf0d364cb8b67", + "reference": "b2d924e5a4cb284f293d5092b1dbf0d364cb8b67", "shasum": "" }, "require": { - "php": ">=7.1.3" + "php": ">=7.1.3", + "symfony/polyfill-php80": "^1.16" }, "type": "library", "autoload": { @@ -1671,8 +1974,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Process Component", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v4.4.37" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1687,20 +1993,20 @@ "type": "tidelift" } ], - "time": "2020-10-24T11:50:19+00:00" + "time": "2022-01-27T17:14:04+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "75a63c33a8577608444246075ea0af0d052e452a" + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", - "reference": "75a63c33a8577608444246075ea0af0d052e452a", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", "shasum": "" }, "require": { @@ -1727,40 +2033,49 @@ } ], "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" + }, "funding": [ { "url": "https://github.com/theseer", "type": "github" } ], - "time": "2020-07-12T23:59:07+00:00" + "time": "2021-07-28T10:34:58+00:00" }, { "name": "webmozart/assert", - "version": "1.9.1", + "version": "1.10.0", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" + "url": "https://github.com/webmozarts/assert.git", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0 || ^8.0", + "php": "^7.2 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<3.9.1" + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" + "phpunit/phpunit": "^8.5.13" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -1782,7 +2097,11 @@ "check", "validate" ], - "time": "2020-07-08T17:02:28+00:00" + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.10.0" + }, + "time": "2021-03-09T10:59:23+00:00" } ], "aliases": [], @@ -1792,5 +2111,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "1.1.0" + "plugin-api-version": "2.2.0" } diff --git a/css/style.scss b/css/style.scss index 78a180762..250d45481 100644 --- a/css/style.scss +++ b/css/style.scss @@ -15,6 +15,7 @@ } .app-navigation-toggle { display: block; + display: block; } } @@ -227,7 +228,8 @@ tr.selected td { .leaflet-marker-device.device-marker .thumbnail-wrapper:after { border-width: 12px 7px 0; } -.leaflet-marker-nonLocalizedPhoto.nonLocalizedPhoto-marker:after, +.leaflet-marker-photo-suggestion.photo-suggestion-marker:after, +.leaflet-marker-photo-suggestion.photo-suggestion-marker-selected:after, .leaflet-marker-photo.photo-marker:after { bottom: 17px; border-width: 11px 8px 0; @@ -235,7 +237,8 @@ tr.selected td { .leaflet-marker-contact.contact-marker:after, .leaflet-marker-track.track-marker .thumbnail-wrapper:after, .leaflet-marker-device.device-marker .thumbnail-wrapper:after, -.leaflet-marker-nonLocalizedPhoto.nonLocalizedPhoto-marker:after, +.leaflet-marker-photo-suggestion.photo-suggestion-marker:after, +.leaflet-marker-photo-suggestion.photo-suggestion-marker-selected:after, .leaflet-marker-photo.photo-marker:after { content:""; position: relative; @@ -246,8 +249,14 @@ tr.selected td { margin-left: auto; margin-right: auto; } -.leaflet-marker-nonLocalizedPhoto.nonLocalizedPhoto-marker:after { - border-color: var(--color-warning) transparent; +.leaflet-marker-photo-suggestion.photo-suggestion-marker:after { + border-color: var(--color-primary-element) transparent; +} +.leaflet-marker-photo-suggestion.photo-suggestion-marker-selected { + border-color: var(--color-warning) !important; +} +.leaflet-marker-photo-suggestion.photo-suggestion-marker-selected:after { + border-color: var(--color-warning) !important; } .leaflet-marker-device.device-marker .thumbnail-wrapper:after, .leaflet-marker-track.track-marker .thumbnail-wrapper:after { @@ -258,7 +267,7 @@ tr.selected td { .leaflet-marker-contact .thumbnail, .leaflet-marker-track .thumbnail, .leaflet-marker-device .thumbnail, -.leaflet-marker-nonLocalizedPhoto .thumbnail, +.leaflet-marker-photo-suggestion .thumbnail, .leaflet-marker-photo .thumbnail { width: 100%; height: 100%; @@ -267,7 +276,7 @@ tr.selected td { background-repeat: no-repeat; background-color: white; } -.leaflet-marker-nonLocalizedPhoto .thumbnail, +.leaflet-marker-photo-suggestion .thumbnail, .leaflet-marker-photo .thumbnail { //border-radius: 2px; } @@ -314,7 +323,7 @@ tr.selected td { .leaflet-marker-favorite-cluster .thumbnail { border-radius: 50%; } -.leaflet-marker-nonLocalizedPhoto .thumbnail { +.leaflet-marker-photo-suggestion .thumbnail { -webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */ filter: grayscale(100%); } @@ -322,7 +331,7 @@ tr.selected td { .leaflet-marker-track .label, .leaflet-marker-device .label, .leaflet-marker-contact .label, -.leaflet-marker-nonLocalizedPhoto .label, +.leaflet-marker-photo-suggestion .label, .leaflet-marker-photo .label { position: absolute; top: -7px; @@ -371,7 +380,7 @@ tr.selected td { border: 2px solid var(--color-main-background); border-radius: 50%; } -.leaflet-marker-nonLocalizedPhoto, +.leaflet-marker-photo-suggestion, .leaflet-marker-photo, .leaflet-marker-contact, .leaflet-marker-track, @@ -384,11 +393,15 @@ tr.selected td { .leaflet-marker-photo { border: 2px solid var(--color-main-background); } -.leaflet-marker-nonLocalizedPhoto { - border: 2px solid var(--color-warning); +.leaflet-marker-photo-suggestion { + border: 2px solid var(--color-primary-element); } -.leaflet-marker-nonLocalizedPhoto, -.leaflet-marker-photo { +.leaflet-marker-photo-suggestion-selected { + border: 2px solid var(--color-warning); +} +.leaflet-marker-photo-suggestion, +.leaflet-marker-photo, +.leaflet-marker-photo-suggestion-selected { border-radius: var(--border-radius); } @@ -400,7 +413,8 @@ tr.selected td { } .leaflet-marker-photo.photo-marker, -.leaflet-marker-nonLocalizedPhoto.nonLocalizedPhoto-marker{ +.leaflet-marker-photo-suggestion.photo-suggestion-marker, +.leaflet-marker-photo-suggestion.photo-suggestion-marker-selected{ top: -10px; } .photo-tooltip { diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php new file mode 100644 index 000000000..29af0cb6b --- /dev/null +++ b/lib/AppInfo/Application.php @@ -0,0 +1,92 @@ +, Vinzenz Rosenkranz + * @copyright Sander Brand 2014, Vinzenz Rosenkranz 2016, 2017 + */ + +namespace OCA\Maps\AppInfo; + + +use \OCP\AppFramework\App; +use OCP\AppFramework\Bootstrap\IBootContext; +use OCP\AppFramework\Bootstrap\IBootstrap; +use \OCP\IServerContainer; +use OCA\Maps\Hooks\FileHooks; +use OCA\Maps\Service\PhotofilesService; +use OCA\Maps\Service\TracksService; +use OCP\AppFramework\Bootstrap\IRegistrationContext; +use OCA\Files\Event\LoadAdditionalScriptsEvent; +use OCA\DAV\Events\CardCreatedEvent; +use OCA\DAV\Events\CardUpdatedEvent; +use OCA\DAV\Events\CardDeletedEvent; +use OCA\Maps\Listener\LoadAdditionalScriptsListener; +use OCA\Maps\Listener\CardCreatedListener; +use OCA\Maps\Listener\CardUpdatedListener; +use OCA\Maps\Listener\CardDeletedListener; + + +class Application extends App implements IBootstrap { + public const APP_ID = 'maps'; + + public function __construct (array $urlParams=array()) { + parent::__construct('maps', $urlParams); + } + + public function register(IRegistrationContext $context): void { + // ... registration logic goes here ... + + // Register the composer autoloader for packages shipped by this app, if applicable + include_once __DIR__ . '/../../vendor/autoload.php'; + $context->registerEventListener( + LoadAdditionalScriptsEvent::class, + LoadAdditionalScriptsListener::class + ); + + $context->registerEventListener( + CardCreatedEvent::class, + CardCreatedListener::class + ); + $context->registerEventListener( + CardUpdatedEvent::class, + CardUpdatedListener::class + ); + $context->registerEventListener( + CardDeletedEvent::class, + CardDeletedListener::class + ); + } + + public function boot(IBootContext $context): void { + // ... boot logic goes here ... + $context->getAppContainer()->registerService('FileHooks', function($c) { + return new FileHooks( + $c->query(IServerContainer::class)->getRootFolder(), + \OC::$server->query(PhotofilesService::class), + \OC::$server->query(TracksService::class), + $c->query(IServerContainer::class)->getLogger(), + $c->query('AppName'), + $c->query(IServerContainer::class)->getLockingProvider() + ); + }); + + $context->getAppContainer()->query('FileHooks')->register(); + + $this->registerFeaturePolicy(); + } + + private function registerFeaturePolicy() { + $dispatcher = $this->getContainer()->getServer()->getEventDispatcher(); + + $dispatcher->addListener('OCP\Security\FeaturePolicy\AddFeaturePolicyEvent', function (\OCP\Security\FeaturePolicy\AddFeaturePolicyEvent $e) { + $fp = new \OCP\AppFramework\Http\EmptyFeaturePolicy(); + $fp->addAllowedGeoLocationDomain('\'self\''); + $e->addPolicy($fp); + }); + } + +} diff --git a/lib/Command/RescanPhotos.php b/lib/Command/RescanPhotos.php index b08301f7a..d1bda98fb 100644 --- a/lib/Command/RescanPhotos.php +++ b/lib/Command/RescanPhotos.php @@ -20,6 +20,7 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; use OCP\IConfig; use OCA\Maps\Service\PhotofilesService; @@ -51,7 +52,13 @@ protected function configure() { 'user_id', InputArgument::OPTIONAL, 'Rescan photos GPS exif data for the given user' - ); + ) + ->addOption( + 'now', + null, + InputOption::VALUE_NONE, + 'Dot the rescan now and not as background jobs. Doing it now might run out of memory.' + ); } protected function execute(InputInterface $input, OutputInterface $output) { @@ -61,23 +68,24 @@ protected function execute(InputInterface $input, OutputInterface $output) { } $this->output = $output; $userId = $input->getArgument('user_id'); + $inBackground = !$input->getOption('now'); if ($userId === null) { $this->userManager->callForSeenUsers(function (IUser $user) { - $this->rescanUserPhotos($user->getUID()); + $this->rescanUserPhotos($user->getUID(), $inBackground); }); } else { $user = $this->userManager->get($userId); if ($user !== null) { - $this->rescanUserPhotos($userId); + $this->rescanUserPhotos($userId, $inBackground); } } return 0; } - private function rescanUserPhotos($userId) { + private function rescanUserPhotos($userId, $inBackground=true) { echo '======== User '.$userId.' ========'."\n"; $c = 1; - foreach ($this->photofilesService->rescan($userId) as $path) { + foreach ($this->photofilesService->rescan($userId, $inBackground) as $path) { echo '['.$c.'] Photo "'.$path.'" added'."\n"; $c++; } diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 2a4ed4ed8..a2c6147dc 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -51,7 +51,7 @@ public function __construct($AppName, * @NoCSRFRequired */ public function index(): TemplateResponse { - $this->eventDispatcher->dispatch(LoadSidebar::class, new LoadSidebar()); +// $this->eventDispatcher->dispatch(LoadSidebar::class, new LoadSidebar()); $this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer()); $params = array('user' => $this->userId); diff --git a/lib/Helper/ExifDataException.php b/lib/Helper/ExifDataInvalidException.php similarity index 85% rename from lib/Helper/ExifDataException.php rename to lib/Helper/ExifDataInvalidException.php index 74ff2bc03..3a92b05e7 100644 --- a/lib/Helper/ExifDataException.php +++ b/lib/Helper/ExifDataInvalidException.php @@ -14,6 +14,6 @@ /** * Class GeoPhotoException */ -class ExifDataException extends \Exception +class ExifDataInvalidException extends \Exception { } diff --git a/lib/Helper/ExifDataNoLocationException.php b/lib/Helper/ExifDataNoLocationException.php new file mode 100644 index 000000000..80ceed8fa --- /dev/null +++ b/lib/Helper/ExifDataNoLocationException.php @@ -0,0 +1,19 @@ + $pelDateTimeOriginal->getValue(PelEntryTime::EXIF_STRING) // for old pel 0.9.6 and above - self::TIMESTAMP => $pelDateTimeOriginal->getValue() // for new pel >= 0.9.11 + self::TIMESTAMP => (int) $pelDateTimeOriginal->getValue() // for new pel >= 0.9.11 ]; $pelIfdGPS = $pelIfd0->getSubIfd(PelIfd::GPS); if (!is_null($pelIfdGPS) && !is_null($pelIfdGPS->getEntry(PelTag::GPS_LATITUDE )) && !is_null( $pelIfdGPS->getEntry(PelTag::GPS_LONGITUDE))) { @@ -206,17 +206,17 @@ private final function __construct(array $exif_data) public function validate( $invalidate_zero_iland = false ) { if (!$this->exif_data) { - throw new ExifDataException('No exif_data found', 1); + throw new ExifDataInvalidException('No exif_data found', 1); } if (!is_array($this->exif_data)) { - throw new ExifDataException('exif_data is not an array', 2); + throw new ExifDataInvalidException('exif_data is not an array', 2); } if (!isset($this->exif_data[self::LATITUDE]) || !isset($this->exif_data[self::LONGITUDE])) { - throw new ExifDataException('Latitude and/or Longitude are missing from exif data', 3); + throw new ExifDataNoLocationException('Latitude and/or Longitude are missing from exif data', 1); } if( $invalidate_zero_iland && $this->isZeroIsland() ){ - throw new ExifDataException('Zero island is not valid', 4); + throw new ExifDataNoLocationException('Zero island is not valid', 2); } } @@ -229,7 +229,7 @@ public function isValid(): bool try { $this->validate(); $this->is_valid = true; - } catch (\Throwable $e) { + } catch (\Throwable $e) { $this->is_valid = false; } } @@ -241,7 +241,7 @@ public function isValid(): bool */ private function parse() { - if ($this->isValid() && null === $this->latitude && null === $this->longitude && null === $this->timestamp) { + if ($this->isValid() && (null === $this->latitude || null === $this->longitude)) { $this->longitude = $this->geo2float($this->exif_data[self::LONGITUDE]); if( isset($this->exif_data[self::LONGITUDE_REF]) && 'W' === $this->exif_data[self::LONGITUDE_REF] ){ $this->longitude*=-1; @@ -250,11 +250,12 @@ private function parse() if( isset($this->exif_data[self::LATITUDE_REF]) && 'S' === $this->exif_data[self::LATITUDE_REF] ){ $this->latitude*=-1; } - // optional - if (isset($this->exif_data[self::TIMESTAMP])) { - $this->timestamp = $this->string2time($this->exif_data[self::TIMESTAMP]); - } } + // optional + if (isset($this->exif_data[self::TIMESTAMP])) { + $t = $this->exif_data[self::TIMESTAMP]; + $this->timestamp = is_string($t) ? $this->string2time($t) : is_int($t) ? $t : null; + } } /** diff --git a/lib/Listener/CardCreatedListener.php b/lib/Listener/CardCreatedListener.php new file mode 100644 index 000000000..87a802510 --- /dev/null +++ b/lib/Listener/CardCreatedListener.php @@ -0,0 +1,55 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Maps\Listener; + +use OCA\DAV\Events\CardCreatedEvent; +use OCA\Maps\Service\AddressService; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; + +class CardCreatedListener implements IEventListener { + + /** @var AddressService */ + private $addressService; + + /** @var ILogger */ + private $logger; + + public function __construct( + AddressService $addressService, + ILogger $logger) { + $this->addressService = $addressService; + $this->logger = $logger; + } + + public function handle(Event $event): void { + if (!($event instanceof CardCreatedEvent)) { + // Unrelated + return; + } + $cData = $event->getCardData(); + $cUri = $cData->uri; + $this->addressService->scheduleVCardForLookup($cData, $cUri); + } +} diff --git a/lib/Listener/CardDeletedListener.php b/lib/Listener/CardDeletedListener.php new file mode 100644 index 000000000..e781c254d --- /dev/null +++ b/lib/Listener/CardDeletedListener.php @@ -0,0 +1,55 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Maps\Listener; + +use OCA\DAV\Events\CardDeletedEvent; +use OCA\Maps\Service\AddressService; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; + +class CardDeletedListener implements IEventListener { + + /** @var AddressService */ + private $addressService; + + /** @var ILogger */ + private $logger; + + public function __construct( + AddressService $addressService, + ILogger $logger) { + $this->addressService = $addressService; + $this->logger = $logger; + } + + public function handle(Event $event): void { + if (!($event instanceof CardDeletedEvent)) { + // Unrelated + return; + } + $cData = $event->getCardData(); + $cUri = $cData->uri; + $this->addressService->deleteDBContactAddresses($cUri); + } +} diff --git a/lib/Listener/CardUpdatedListener.php b/lib/Listener/CardUpdatedListener.php new file mode 100644 index 000000000..d0ca94a3b --- /dev/null +++ b/lib/Listener/CardUpdatedListener.php @@ -0,0 +1,55 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Maps\Listener; + +use OCA\DAV\Events\CardUpdatedEvent; +use OCA\Maps\Service\AddressService; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; + +class CardUpdatedListener implements IEventListener { + + /** @var AddressService */ + private $addressService; + + /** @var ILogger */ + private $logger; + + public function __construct( + AddressService $addressService, + ILogger $logger) { + $this->addressService = $addressService; + $this->logger = $logger; + } + + public function handle(Event $event): void { + if (!($event instanceof CardUpdatedEvent)) { + // Unrelated + return; + } + $cData = $event->getCardData(); + $cUri = $cData->uri; + $this->addressService->scheduleVCardForLookup($cData, $cUri); + } +} diff --git a/lib/Listener/LoadAdditionalScriptsListener.php b/lib/Listener/LoadAdditionalScriptsListener.php new file mode 100644 index 000000000..d2a8b8cf1 --- /dev/null +++ b/lib/Listener/LoadAdditionalScriptsListener.php @@ -0,0 +1,45 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Maps\Listener; + +use OCA\Files\Event\LoadAdditionalScriptsEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Util; + +class LoadAdditionalScriptsListener implements IEventListener { + + public function __construct() { + } + + public function handle(Event $event): void { + if (!($event instanceof LoadAdditionalScriptsEvent)) { + // Unrelated + return; + } + + Util::addScript('maps', 'filetypes'); + Util::addStyle('maps', 'filetypes'); + } +} diff --git a/lib/Service/AddressService.php b/lib/Service/AddressService.php index 933dbf1f5..91852ae61 100644 --- a/lib/Service/AddressService.php +++ b/lib/Service/AddressService.php @@ -13,15 +13,14 @@ namespace OCA\Maps\Service; use \OCA\Maps\BackgroundJob\LookupMissingGeoJob; +use OCP\ICacheFactory; use \OCP\ILogger; -use \OCP\IConfig; use \OCP\IDBConnection; use \OCP\BackgroundJob\IJobList; use \OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IMemcache; use \Sabre\VObject\Reader; use \OCP\Files\IAppData; -use \OCP\Files\SimpleFS\ISimpleFile; -use \OCP\Files\NotFoundException; /** * Class AddressService @@ -45,11 +44,14 @@ class AddressService { private $jobList; private $appData; - public function __construct(IConfig $config, ILogger $logger, IJobList $jobList, + /** @var IMemcache */ + private $memcache; + + public function __construct(ICacheFactory $cacheFactory, ILogger $logger, IJobList $jobList, IAppData $appData, IDBConnection $dbconnection) { $this->dbconnection = $dbconnection; $this->qb = $dbconnection->getQueryBuilder(); - $this->config = $config; + $this->memcache = $cacheFactory->createLocal('maps'); $this->logger = $logger; $this->jobList = $jobList; $this->appData = $appData; @@ -155,7 +157,7 @@ private function lookupAddressInternal($adr) { // looks up the address on external provider returns lat, lon, lookupstate // do lookup only if last one occured more than one second ago private function lookupAddressExternal($adr) { - if (time() - intval($this->config->getAppValue('maps', 'lastAddressLookup')) >= 1) { + if (time() - intval($this->memcache->get('lastAddressLookup')) >= 1) { $opts = [ 'http' => [ 'method' => 'GET', @@ -180,18 +182,22 @@ private function lookupAddressExternal($adr) { $result = \json_decode($result_json, true); if (!(key_exists('request_failed', $result) AND $result['request_failed'])) { $this->logger->debug('External looked up address: ' . $adr . ' with result' . print_r($result, true)); - $this->config->setAppValue('maps', 'lastAddressLookup', time()); - if (sizeof($result) > 0) { - if (key_exists('lat', $result[0]) AND - key_exists('lon', $result[0]) + $this->memcache->set('lastAddressLookup', time()); + $lat = null; + $lon = null; + foreach ($result as $addr) { + if (key_exists('lat', $addr) AND + key_exists('lon', $addr) ) { - return [ - $result[0]['lat'], - $result[0]['lon'], true - ]; + if (is_null($lat) OR + (key_exists('class', $addr) AND + ($addr['class'] == "building" OR $addr['class'] == "place"))) { + $lat = $addr['lat']; + $lon = $addr['lon']; + } } } - return [null, null, true]; + return [$lat, $lon, true]; } } $this->logger->debug('Externally looked failed'); diff --git a/lib/Service/DevicesService.php b/lib/Service/DevicesService.php index 1cad0aeeb..04780293e 100644 --- a/lib/Service/DevicesService.php +++ b/lib/Service/DevicesService.php @@ -168,9 +168,9 @@ public function addPointsToDB($deviceId, $points) { $this->db_quote_escape_string($p['lat']).', '. $this->db_quote_escape_string($p['lng']).', '. $this->db_quote_escape_string($p['date']).', '. - ((array_key_exists('altitude', $p) and is_numeric($p['altitude'])) ? $this->db_quote_escape_string(floatval($p['altitude'])) : 'NULL').', '. - ((array_key_exists('battery', $p) and is_numeric($p['battery'])) ? $this->db_quote_escape_string(floatval($p['battery'])) : 'NULL').', '. - ((array_key_exists('accuracy', $p) and is_numeric($p['accuracy'])) ? $this->db_quote_escape_string(floatval($p['accuracy'])) : 'NULL').')'; + ((isset($p['altitude']) and is_numeric($p['altitude'])) ? $this->db_quote_escape_string(floatval($p['altitude'])) : 'NULL').', '. + ((isset($p['battery']) and is_numeric($p['battery'])) ? $this->db_quote_escape_string(floatval($p['battery'])) : 'NULL').', '. + ((isset($p['accuracy']) and is_numeric($p['accuracy'])) ? $this->db_quote_escape_string(floatval($p['accuracy'])) : 'NULL').')'; array_push($values, $value); } $valuesStr = implode(', ', $values); @@ -463,10 +463,10 @@ private function gpxStartElement($parser, $name, $attrs) { } else if ($name === 'TRKPT') { $this->currentPoint = []; - if (array_key_exists('LAT', $attrs)) { + if (isset($attrs['LAT'])) { $this->currentPoint['lat'] = floatval($attrs['LAT']); } - if (array_key_exists('LON', $attrs)) { + if (isset($attrs['LON'])) { $this->currentPoint['lng'] = floatval($attrs['LON']); } } @@ -491,7 +491,7 @@ private function gpxEndElement($parser, $name) { // store track point // convert date - if (array_key_exists('date', $this->currentPoint)) { + if (isset($this->currentPoint['date'])) { $time = new \DateTime($this->currentPoint['date']); $timestamp = $time->getTimestamp(); $this->currentPoint['date'] = $timestamp; @@ -515,16 +515,16 @@ private function gpxDataElement($parser, $data) { $d = trim($data); if (!empty($d)) { if ($this->currentXmlTag === 'ELE') { - $this->currentPoint['altitude'] = (array_key_exists('altitude', $this->currentPoint)) ? $this->currentPoint['altitude'].$d : $d; + $this->currentPoint['altitude'] = (isset($this->currentPoint['altitude'])) ? $this->currentPoint['altitude'].$d : $d; } else if ($this->currentXmlTag === 'BATTERYLEVEL') { - $this->currentPoint['battery'] = (array_key_exists('battery', $this->currentPoint)) ? $this->currentPoint['battery'].$d : $d; + $this->currentPoint['battery'] = (isset($this->currentPoint['battery'])) ? $this->currentPoint['battery'].$d : $d; } else if ($this->currentXmlTag === 'ACCURACY') { - $this->currentPoint['accuracy'] = (array_key_exists('accuracy', $this->currentPoint)) ? $this->currentPoint['accuracy'].$d : $d; + $this->currentPoint['accuracy'] = (isset($this->currentPoint['accuracy'])) ? $this->currentPoint['accuracy'].$d : $d; } else if ($this->insideTrk and $this->currentXmlTag === 'TIME') { - $this->currentPoint['date'] = (array_key_exists('date', $this->currentPoint)) ? $this->currentPoint['date'].$d : $d; + $this->currentPoint['date'] = (isset($this->currentPoint['date'])) ? $this->currentPoint['date'].$d : $d; } else if ($this->insideTrk and $this->currentXmlTag === 'NAME') { $this->importDevName = $this->importDevName . $d; @@ -576,7 +576,7 @@ public function importDevicesFromKml($userId, $fp, $name) { private function kmlStartElement($parser, $name, $attrs) { $this->currentXmlTag = $name; if ($name === 'GX:TRACK') { - if (array_key_exists('ID', $attrs)) { + if (isset($attrs['ID'])) { $this->importDevName = $attrs['ID']; } else { @@ -603,13 +603,13 @@ private function kmlEndElement($parser, $name) { } else if ($name === 'GX:COORD') { // convert date - if (array_key_exists('date', $this->currentPoint)) { + if (isset($this->currentPoint['date'])) { $time = new \DateTime($this->currentPoint['date']); $timestamp = $time->getTimestamp(); $this->currentPoint['date'] = $timestamp; } // get latlng - if (array_key_exists('coords', $this->currentPoint)) { + if (isset($this->currentPoint['coords'])) { $spl = explode(' ', $this->currentPoint['coords']); if (count($spl) > 1) { $this->currentPoint['lat'] = floatval($spl[1]); @@ -636,10 +636,10 @@ private function kmlDataElement($parser, $data) { $d = trim($data); if (!empty($d)) { if ($this->currentXmlTag === 'WHEN') { - $this->currentPoint['date'] = (array_key_exists('date', $this->currentPoint)) ? $this->currentPoint['date'].$d : $d; + $this->currentPoint['date'] = (isset($this->currentPoint['date'])) ? $this->currentPoint['date'].$d : $d; } else if ($this->currentXmlTag === 'GX:COORD') { - $this->currentPoint['coords'] = (array_key_exists('coords', $this->currentPoint)) ? $this->currentPoint['coords'].$d : $d; + $this->currentPoint['coords'] = (isset($this->currentPoint['coords'])) ? $this->currentPoint['coords'].$d : $d; } } } diff --git a/lib/Service/FavoritesService.php b/lib/Service/FavoritesService.php index bc4d93af2..b157d2725 100644 --- a/lib/Service/FavoritesService.php +++ b/lib/Service/FavoritesService.php @@ -179,8 +179,8 @@ public function addMultipleFavoritesToDB($userId, $favoriteList) { $values = []; foreach ($favoriteList as $fav) { if ( - !array_key_exists('lat', $fav) or !is_numeric($fav['lat']) or - !array_key_exists('lng', $fav) or !is_numeric($fav['lng']) + !isset($fav['lat']) or !is_numeric($fav['lat']) or + !isset($fav['lng']) or !is_numeric($fav['lng']) ) { continue; } @@ -190,14 +190,14 @@ public function addMultipleFavoritesToDB($userId, $favoriteList) { } $value = '(' . $this->db_quote_escape_string($userId) . ', ' . - ((!array_key_exists('name', $fav) or !$fav['name']) ? 'NULL' : $this->db_quote_escape_string($fav['name'])) . ', ' . - ((!array_key_exists('date_created', $fav) or !is_numeric($fav['date_created'])) ? $this->db_quote_escape_string($nowTimeStamp) : $this->db_quote_escape_string($fav['date_created'])) . ', ' . + ((!isset($fav['name']) or !$fav['name']) ? 'NULL' : $this->db_quote_escape_string($fav['name'])) . ', ' . + ((!isset($fav['date_created']) or !is_numeric($fav['date_created'])) ? $this->db_quote_escape_string($nowTimeStamp) : $this->db_quote_escape_string($fav['date_created'])) . ', ' . $this->db_quote_escape_string($nowTimeStamp) . ', ' . $this->db_quote_escape_string($lat) . ', ' . $this->db_quote_escape_string($lng) . ', ' . - ((!array_key_exists('category', $fav) or !$fav['category']) ? 'NULL' : $this->db_quote_escape_string($fav['category'])) . ', ' . - ((!array_key_exists('comment', $fav) or !$fav['comment']) ? 'NULL' : $this->db_quote_escape_string($fav['comment'])) . ', ' . - ((!array_key_exists('extensions', $fav) or !$fav['extensions']) ? 'NULL' : $this->db_quote_escape_string($fav['extensions'])) . ')'; + ((!isset($fav['category']) or !$fav['category']) ? 'NULL' : $this->db_quote_escape_string($fav['category'])) . ', ' . + ((!isset($fav['comment']) or !$fav['comment']) ? 'NULL' : $this->db_quote_escape_string($fav['comment'])) . ', ' . + ((!isset($fav['extensions']) or !$fav['extensions']) ? 'NULL' : $this->db_quote_escape_string($fav['extensions'])) . ')'; array_push($values, $value); } $valuesStr = implode(', ', $values); @@ -519,16 +519,16 @@ private function kmlEndElement($parser, $name) { // store favorite $this->nbImported++; $this->currentFavorite['category'] = $this->kmlCurrentCategory; - if (!array_key_exists('category', $this->currentFavorite) or $this->currentFavorite['category'] === '') { + if (!isset($this->currentFavorite['category']) or $this->currentFavorite['category'] === '') { $this->currentFavorite['category'] = $this->l10n->t('Personal'); } // convert date - if (array_key_exists('date_created', $this->currentFavorite)) { + if (isset($this->currentFavorite['date_created'])) { $time = new \DateTime($this->currentFavorite['date_created']); $timestamp = $time->getTimestamp(); $this->currentFavorite['date_created'] = $timestamp; } - if (array_key_exists('coordinates', $this->currentFavorite)) { + if (isset($this->currentFavorite['coordinates'])) { $spl = explode(',', $this->currentFavorite['coordinates']); if (count($spl) > 1) { $this->currentFavorite['lat'] = floatval($spl[1]); @@ -555,16 +555,16 @@ private function kmlDataElement($parser, $data) { } else { if ($this->currentXmlTag === 'NAME') { - $this->currentFavorite['name'] = (array_key_exists('name', $this->currentFavorite)) ? $this->currentFavorite['name'] . $d : $d; + $this->currentFavorite['name'] = (isset($this->currentFavorite['name'])) ? $this->currentFavorite['name'] . $d : $d; } else if ($this->currentXmlTag === 'WHEN') { - $this->currentFavorite['date_created'] = (array_key_exists('date_created', $this->currentFavorite)) ? $this->currentFavorite['date_created'] . $d : $d; + $this->currentFavorite['date_created'] = (isset($this->currentFavorite['date_created'])) ? $this->currentFavorite['date_created'] . $d : $d; } else if ($this->currentXmlTag === 'COORDINATES') { - $this->currentFavorite['coordinates'] = (array_key_exists('coordinates', $this->currentFavorite)) ? $this->currentFavorite['coordinates'] . $d : $d; + $this->currentFavorite['coordinates'] = (isset($this->currentFavorite['coordinates'])) ? $this->currentFavorite['coordinates'] . $d : $d; } else if ($this->currentXmlTag === 'DESCRIPTION') { - $this->currentFavorite['comment'] = (array_key_exists('comment', $this->currentFavorite)) ? $this->currentFavorite['comment'] . $d : $d; + $this->currentFavorite['comment'] = (isset($this->currentFavorite['comment'])) ? $this->currentFavorite['comment'] . $d : $d; } } } @@ -610,10 +610,10 @@ private function gpxStartElement($parser, $name, $attrs) { if ($name === 'WPT') { $this->insideWpt = true; $this->currentFavorite = []; - if (array_key_exists('LAT', $attrs)) { + if (isset($attrs['LAT'])) { $this->currentFavorite['lat'] = floatval($attrs['LAT']); } - if (array_key_exists('LON', $attrs)) { + if (isset($attrs['LON'])) { $this->currentFavorite['lng'] = floatval($attrs['LON']); } } @@ -635,12 +635,12 @@ private function gpxEndElement($parser, $name) { // store favorite $this->nbImported++; // convert date - if (array_key_exists('date_created', $this->currentFavorite)) { + if (isset($this->currentFavorite['date_created'])) { $time = new \DateTime($this->currentFavorite['date_created']); $timestamp = $time->getTimestamp(); $this->currentFavorite['date_created'] = $timestamp; } - if (!array_key_exists('category', $this->currentFavorite) or $this->currentFavorite['category'] === '') { + if (!isset($this->currentFavorite['category']) or $this->currentFavorite['category'] === '') { $this->currentFavorite['category'] = $this->l10n->t('Personal'); } array_push($this->currentFavoritesList, $this->currentFavorite); @@ -657,19 +657,19 @@ private function gpxDataElement($parser, $data) { $d = trim($data); if (!empty($d)) { if ($this->insideWpt and $this->currentXmlTag === 'NAME') { - $this->currentFavorite['name'] = (array_key_exists('name', $this->currentFavorite)) ? $this->currentFavorite['name'] . $d : $d; + $this->currentFavorite['name'] = (isset($this->currentFavorite['name'])) ? $this->currentFavorite['name'] . $d : $d; } else if ($this->insideWpt and $this->currentXmlTag === 'TIME') { - $this->currentFavorite['date_created'] = (array_key_exists('date_created', $this->currentFavorite)) ? $this->currentFavorite['date_created'] . $d : $d; + $this->currentFavorite['date_created'] = (isset($this->currentFavorite['date_created'])) ? $this->currentFavorite['date_created'] . $d : $d; } else if ($this->insideWpt and $this->currentXmlTag === 'TYPE') { - $this->currentFavorite['category'] = (array_key_exists('category', $this->currentFavorite)) ? $this->currentFavorite['category'] . $d : $d; + $this->currentFavorite['category'] = (isset($this->currentFavorite['category'])) ? $this->currentFavorite['category'] . $d : $d; } else if ($this->insideWpt and $this->currentXmlTag === 'DESC') { - $this->currentFavorite['comment'] = (array_key_exists('comment', $this->currentFavorite)) ? $this->currentFavorite['comment'] . $d : $d; + $this->currentFavorite['comment'] = (isset($this->currentFavorite['comment'])) ? $this->currentFavorite['comment'] . $d : $d; } else if ($this->insideWpt and $this->currentXmlTag === 'MAPS-EXTENSIONS') { - $this->currentFavorite['extensions'] = (array_key_exists('extensions', $this->currentFavorite)) ? $this->currentFavorite['extensions'] . $d : $d; + $this->currentFavorite['extensions'] = (isset($this->currentFavorite['extensions'])) ? $this->currentFavorite['extensions'] . $d : $d; } } } @@ -687,7 +687,7 @@ public function importFavoritesFromGeoJSON($userId, $file) { // Decode file content from JSON $data = json_decode($fdata, true, 512, JSON_THROW_ON_ERROR); - if($data == null or !array_key_exists('features', $data)) { + if($data == null or !isset($data['features'])) { $this->logger->error( 'Exception parsing '.$file->getName().': no places found to import', array('app' => 'maps') @@ -717,7 +717,7 @@ public function importFavoritesFromGeoJSON($userId, $file) { $time = new \DateTime($value['properties']['Updated']); $this->currentFavorite['date_modified'] = $time->getTimestamp(); - if(array_key_exists('Address', $value['properties']['Location'])) { + if(isset($value['properties']['Location']['Address'])) { $this->currentFavorite['comment'] = $value['properties']['Location']['Address']; } diff --git a/lib/Service/PhotofilesService.php b/lib/Service/PhotofilesService.php index 3bad130c0..686125d1d 100644 --- a/lib/Service/PhotofilesService.php +++ b/lib/Service/PhotofilesService.php @@ -13,7 +13,8 @@ namespace OCA\Maps\Service; use lsolesen\pel\PelEntryTime; -use OCA\Maps\Helper\ExifDataException; +use OCA\Maps\Helper\ExifDataInvalidException; +use OCA\Maps\Helper\ExifDataNoLocationException; use OCA\Maps\Helper\ExifGeoData; use OCP\Files\FileInfo; use OCP\IL10N; @@ -64,12 +65,16 @@ public function __construct (ILogger $logger, IRootFolder $root, IL10N $l10n, $this->jobList = $jobList; } - public function rescan($userId) { + public function rescan($userId, $inBackground=true) { $userFolder = $this->root->getUserFolder($userId); $photos = $this->gatherPhotoFiles($userFolder, true); $this->photoMapper->deleteAll($userId); foreach ($photos as $photo) { - $this->addPhoto($photo, $userId); + if ($inBackground) { + $this->addPhoto($photo, $userId); + } else { + $this->addPhotoNow($photo, $userId); + } yield $photo->getPath(); } } @@ -348,7 +353,7 @@ private function gatherPhotoFiles ($folder, $recursive) { // we don't explore external storages for which previews are disabled if ($node->isMounted()) { $options = $node->getMountPoint()->getOptions(); - if (!(array_key_exists('previews', $options) && $options['previews'])) { + if (!(isset($options['previews']) && $options['previews'])) { continue; } } @@ -385,11 +390,13 @@ private function getExif($file) : ?ExifGeoData { $path = $file->getStorage()->getLocalFile($file->getInternalPath()); try{ $exif_geo_data = ExifGeoData::get($path); - $exif_geo_data->validate(true); - }catch(ExifDataException $e){ + $exif_geo_data->validate(true,); + }catch(ExifDataInvalidException $e){ $exif_geo_data = null; $this->logger->notice($e->getMessage(), ['code'=>$e->getCode(),'path'=>$path]); - }catch(\Throwable $f){ + }catch(ExifDataNoLocationException $e){ + $this->logger->notice($e->getMessage(), ['code'=>$e->getCode(),'path'=>$path]); + }catch(\Throwable $f){ $exif_geo_data = null; $this->logger->error($f->getMessage(), ['code'=>$f->getCode(),'path'=>$path]); } diff --git a/package-lock.json b/package-lock.json index 2b2eabefd..0a059c977 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@nextcloud/auth": "^1.3.0", "@nextcloud/axios": "^1.9.0", "@nextcloud/dialogs": "^3.1.2", + "@nextcloud/event-bus": "^2.1.1", "@nextcloud/l10n": "^1.4.1", "@nextcloud/moment": "^1.1.1", "@nextcloud/paths": "^2.1.0", @@ -1860,6 +1861,30 @@ "core-js": "^3.6.4" } }, + "node_modules/@nextcloud/auth/node_modules/@nextcloud/event-bus": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-1.3.0.tgz", + "integrity": "sha512-+U5MnCvfnNWvf0lvdqJg8F+Nm8wN+s9ayuBjtiEQxTAcootv7lOnlMgfreqF3l2T0Wet2uZh4JbFVUWf8l3w7g==", + "dependencies": { + "@types/semver": "^7.3.5", + "core-js": "^3.11.2", + "semver": "^7.3.5" + } + }, + "node_modules/@nextcloud/auth/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@nextcloud/axios": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@nextcloud/axios/-/axios-1.9.0.tgz", @@ -2010,13 +2035,13 @@ } }, "node_modules/@nextcloud/event-bus": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-1.3.0.tgz", - "integrity": "sha512-+U5MnCvfnNWvf0lvdqJg8F+Nm8wN+s9ayuBjtiEQxTAcootv7lOnlMgfreqF3l2T0Wet2uZh4JbFVUWf8l3w7g==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-2.1.1.tgz", + "integrity": "sha512-YEui6N+23uyjBSIUZhf8rEjG9vol7UGgxcgxMddEbO0HS7M/sh1cocRqtn+ZVi/yPybeToGmt68SDPCgwHQHKw==", "dependencies": { - "@types/semver": "^7.3.5", - "core-js": "^3.11.2", - "semver": "^7.3.5" + "@types/semver": "^7.1.0", + "core-js": "^3.6.2", + "semver": "^7.3.2" } }, "node_modules/@nextcloud/event-bus/node_modules/semver": { @@ -2186,16 +2211,6 @@ "npm": "^7.0.0" } }, - "node_modules/@nextcloud/vue/node_modules/@nextcloud/event-bus": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-2.1.1.tgz", - "integrity": "sha512-YEui6N+23uyjBSIUZhf8rEjG9vol7UGgxcgxMddEbO0HS7M/sh1cocRqtn+ZVi/yPybeToGmt68SDPCgwHQHKw==", - "dependencies": { - "@types/semver": "^7.1.0", - "core-js": "^3.6.2", - "semver": "^7.3.2" - } - }, "node_modules/@nextcloud/vue/node_modules/loader-utils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", @@ -2226,20 +2241,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/@nextcloud/vue/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@nextcloud/vue/node_modules/style-loader": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", @@ -13840,6 +13841,26 @@ "@nextcloud/event-bus": "^1.1.3", "@nextcloud/typings": "^0.2.2", "core-js": "^3.6.4" + }, + "dependencies": { + "@nextcloud/event-bus": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-1.3.0.tgz", + "integrity": "sha512-+U5MnCvfnNWvf0lvdqJg8F+Nm8wN+s9ayuBjtiEQxTAcootv7lOnlMgfreqF3l2T0Wet2uZh4JbFVUWf8l3w7g==", + "requires": { + "@types/semver": "^7.3.5", + "core-js": "^3.11.2", + "semver": "^7.3.5" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "@nextcloud/axios": { @@ -13953,13 +13974,13 @@ } }, "@nextcloud/event-bus": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-1.3.0.tgz", - "integrity": "sha512-+U5MnCvfnNWvf0lvdqJg8F+Nm8wN+s9ayuBjtiEQxTAcootv7lOnlMgfreqF3l2T0Wet2uZh4JbFVUWf8l3w7g==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-2.1.1.tgz", + "integrity": "sha512-YEui6N+23uyjBSIUZhf8rEjG9vol7UGgxcgxMddEbO0HS7M/sh1cocRqtn+ZVi/yPybeToGmt68SDPCgwHQHKw==", "requires": { - "@types/semver": "^7.3.5", - "core-js": "^3.11.2", - "semver": "^7.3.5" + "@types/semver": "^7.1.0", + "core-js": "^3.6.2", + "semver": "^7.3.2" }, "dependencies": { "semver": { @@ -14112,16 +14133,6 @@ "vue2-datepicker": "^3.6.3" }, "dependencies": { - "@nextcloud/event-bus": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-2.1.1.tgz", - "integrity": "sha512-YEui6N+23uyjBSIUZhf8rEjG9vol7UGgxcgxMddEbO0HS7M/sh1cocRqtn+ZVi/yPybeToGmt68SDPCgwHQHKw==", - "requires": { - "@types/semver": "^7.1.0", - "core-js": "^3.6.2", - "semver": "^7.3.2" - } - }, "loader-utils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", @@ -14142,14 +14153,6 @@ "ajv-keywords": "^3.5.2" } }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "requires": { - "lru-cache": "^6.0.0" - } - }, "style-loader": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", diff --git a/package.json b/package.json index aafce2df8..4af2cd796 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "@nextcloud/auth": "^1.3.0", "@nextcloud/axios": "^1.9.0", "@nextcloud/dialogs": "^3.1.2", + "@nextcloud/event-bus": "^2.1.1", "@nextcloud/l10n": "^1.4.1", "@nextcloud/moment": "^1.1.1", "@nextcloud/paths": "^2.1.0", diff --git a/src/components/AppNavigationPhotosItem.vue b/src/components/AppNavigationPhotosItem.vue index ad2a5d934..a1349a750 100644 --- a/src/components/AppNavigationPhotosItem.vue +++ b/src/components/AppNavigationPhotosItem.vue @@ -17,6 +17,12 @@ @click="$emit('draggable-clicked')"> {{ draggable ? t('maps', 'Disable photo drag') : t('maps', 'Enable photo drag') }} + + {{ showSuggestions ? t('maps', 'Hide suggestions') : t('maps', 'Suggest photo locations') }} + @@ -52,6 +58,11 @@ export default { type: Boolean, required: true, }, + showSuggestions: { + type: Boolean, + required: false, + default: false, + }, }, data() { @@ -99,4 +110,18 @@ export default { min-width: 38px !important; min-height: 36px !important; } + +::v-deep .icon-picture { + opacity: 1; + //background-color: var(--color-main-text); + padding: 0 !important; + //mask: url('../../img/hand.svg') no-repeat; + mask-size: 16px auto; + mask-position: center; + //-webkit-mask: url('../../img/hand.svg') no-repeat; + -webkit-mask-size: 16px auto; + -webkit-mask-position: center; + min-width: 38px !important; + min-height: 36px !important; +} diff --git a/src/components/Map.vue b/src/components/Map.vue index e497cb041..4bac24fb0 100644 --- a/src/components/Map.vue +++ b/src/components/Map.vue @@ -84,7 +84,17 @@ :photos="photos" :draggable="photosDraggable" @coords-reset="$emit('coords-reset', $event)" - @photo-moved="onPhotoMoved" /> + @photo-moved="onPhotoMoved" + @open-sidebar="$emit('open-sidebar',$event)" /> + +
+
+ + + + + diff --git a/src/components/PhotoSuggestionsSidebarTab.vue b/src/components/PhotoSuggestionsSidebarTab.vue new file mode 100644 index 000000000..d9dcb5479 --- /dev/null +++ b/src/components/PhotoSuggestionsSidebarTab.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue index 3e7cafe43..0bcd15302 100644 --- a/src/components/Sidebar.vue +++ b/src/components/Sidebar.vue @@ -1,11 +1,15 @@ @@ -23,6 +37,8 @@ import { generateUrl } from '@nextcloud/router' import FavoriteSidebarTab from '../components/FavoriteSidebarTab' import TrackSidebarTab from '../components/TrackSidebarTab' +import PhotoSidebarTab from '../components/PhotoSidebarTab' +import PhotoSuggestionsSidebarTab from './PhotoSuggestionsSidebarTab' export default { name: 'Sidebar', @@ -32,6 +48,8 @@ export default { AppSidebar, FavoriteSidebarTab, TrackSidebarTab, + PhotoSidebarTab, + PhotoSuggestionsSidebarTab, }, props: { @@ -43,6 +61,22 @@ export default { type: String, required: true, }, + photo: { + validator: prop => typeof prop === 'object' || prop === null, + required: true, + }, + photosLoading: { + required: true, + type: Boolean, + }, + photoSuggestions: { + required: true, + type: Array, + }, + photoSuggestionsSelectedIndices: { + required: true, + type: Array, + }, favorite: { validator: prop => typeof prop === 'object' || prop === null, required: true, @@ -55,28 +89,52 @@ export default { validator: prop => typeof prop === 'object' || prop === null, required: true, }, + isFullScreen: { + type: Boolean, + required: false, + default: false, + }, }, data() { - return { - } + return {} }, computed: { - backgroundImageUrl() { - const iconColor = OCA.Accessibility?.theme === 'dark' ? 'ffffff' : '000000' + sidebarTitle() { if (this.activeTab === 'track') { - return generateUrl('/svg/maps/road?color=' + iconColor) + return t('maps', 'Track') } else if (this.activeTab === 'favorite') { - return generateUrl('/svg/core/actions/star?color=' + iconColor) + return t('maps', 'Favorite') + } else if (this.activeTab === 'photo') { + return this.photo.basename + } else if (this.activeTab === 'photo-suggestion') { + return t('maps', 'Photo location suggestions') } - return '' + return t('maps', 'Sidebar') }, - sidebarTitle() { + sidebarSubtitle() { if (this.activeTab === 'track') { - return t('maps', 'Track') + return '' } else if (this.activeTab === 'favorite') { - return t('maps', 'Favorite') + return '' + } else if (this.activeTab === 'photo') { + return this.photo.filename + } else if (this.activeTab === 'photo-suggestion') { + return '' + } + return t('maps', 'shows cool information') + }, + backgroundImageUrl() { + const iconColor = OCA.Accessibility?.theme === 'dark' ? 'ffffff' : '000000' + if (this.activeTab === 'track') { + return generateUrl('/svg/maps/road?color=' + iconColor) + } else if (this.activeTab === 'favorite') { + return generateUrl('/svg/core/actions/star?color=' + iconColor) + } else if (this.activeTab === 'photo') { + return this.previewUrl() + } else if (this.activeTab === 'photo-suggestion') { + return generateUrl('/apps/theming/img/core/filetypes') + '/image.svg?v=2' } return '' }, @@ -86,6 +144,14 @@ export default { onActiveChanged(newActive) { this.$emit('active-changed', newActive) }, + previewUrl() { + return this.photo.hasPreview + ? generateUrl('core') + '/preview?fileId=' + this.photo.fileId + '&x=500&y=300&a=1' + : generateUrl('/apps/theming/img/core/filetypes') + '/image.svg?v=2' + }, + hasPreview() { + return this.activeTab === 'photo' && this.photo.hasPreview + }, }, } @@ -103,4 +169,25 @@ export default { min-width: 44px !important; min-height: 44px !important; } + +.app-sidebar { + &--full { + position: fixed !important; + z-index: 2025 !important; + top: 0 !important; + height: 100% !important; + } + &--has-preview::v-deep { + .app-sidebar-header__figure { + background-size: cover; + } + + &[data-mimetype="text/plain"], + &[data-mimetype="text/markdown"] { + .app-sidebar-header__figure { + background-size: contain; + } + } + } +} diff --git a/src/components/map/PhotoSuggestionsLayer.vue b/src/components/map/PhotoSuggestionsLayer.vue new file mode 100644 index 000000000..88c9bf798 --- /dev/null +++ b/src/components/map/PhotoSuggestionsLayer.vue @@ -0,0 +1,283 @@ + + + + + diff --git a/src/components/map/PhotosLayer.vue b/src/components/map/PhotosLayer.vue index 37f600cf2..c24d03229 100644 --- a/src/components/map/PhotosLayer.vue +++ b/src/components/map/PhotosLayer.vue @@ -151,6 +151,7 @@ export default { if (OCA.Viewer && OCA.Viewer.open) { this.displayCluster(a.layer) } else { + this.$emit('open-sidebar', a.layer.getAllChildMarkers()[0].options.data.path) a.layer.spiderfy() } } @@ -194,6 +195,7 @@ export default { photoList.sort((a, b) => { return a.dateTaken - b.dateTaken }) + this.$emit('open-sidebar', photoList[0].path) OCA.Viewer.open({ path: photoList[0].path, list: photoList }) this.map.closePopup() }, @@ -232,6 +234,7 @@ export default { this.$nextTick(() => { e.target.closePopup() }) + this.$emit('open-sidebar', photo.path) this.viewPhoto(photo) }, viewPhoto(photo) { diff --git a/src/main.js b/src/main.js index 0c3ee7fb6..02bb2ece0 100644 --- a/src/main.js +++ b/src/main.js @@ -29,6 +29,8 @@ import '../css/style.scss' import VueClipboard from 'vue-clipboard2' import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip' +import { emit } from '@nextcloud/event-bus' + Vue.directive('tooltip', Tooltip) Vue.use(VueClipboard) @@ -52,14 +54,32 @@ window.OCA.Maps.registerMapsAction = ({ label, callback, icon }) => { window.OCA.Maps.mapActions.push(mapAction) } -document.addEventListener('DOMContentLoaded', (event) => { - // SIDEBAR - if (!window.OCA.Files) { - window.OCA.Files = {} - } - // register unused client for the sidebar to have access to its parser methods - Object.assign(window.OCA.Files, { App: { fileList: { filesClient: OC.Files.getClient() } } }, window.OCA.Files) +// SIDEBAR +if (!window.OCA.Files) { + window.OCA.Files = {} +} +// register unused client for the sidebar to have access to its parser methods +if (!window.OCA.Files.Sidebar) { + Object.assign(window.OCA.Files, { + Sidebar: { + state: { + file: '', + }, + open: (path) => { + emit('files:sidebar:opened') + }, + close: () => { + emit('files:sidebar:closed') + }, + setFullScreenMode: () => {}, // SIDEBARFULLSCREEN, + }, + }, window.OCA.Files) +} +document.addEventListener('DOMContentLoaded', (event) => { + Object.assign(window.OCA.Files, { + App: { fileList: { filesClient: OC.Files.getClient() } }, + }, window.OCA.Files) optionsController.restoreOptions(main) }) diff --git a/src/network.js b/src/network.js index 9dbebaad2..c8a726d4e 100644 --- a/src/network.js +++ b/src/network.js @@ -212,6 +212,11 @@ export function getPhotos() { return axios.get(url) } +export function getPhotoSuggestions() { + const url = generateUrl('apps/maps/photos/nonlocalized') + return axios.get(url) +} + export function placePhotos(paths, lats, lngs, directory = false) { const req = { paths, diff --git a/src/views/App.vue b/src/views/App.vue index 3f52654ab..57ceeb136 100644 --- a/src/views/App.vue +++ b/src/views/App.vue @@ -39,10 +39,12 @@ :loading="photosLoading" :photos="photos" :draggable="photosDraggable" + :show-suggestions="showPhotoSuggestions" @photos-clicked="onPhotosClicked" @cancel-clicked="cancelPhotoMove" @redo-clicked="redoPhotoMove" - @draggable-clicked="photosDraggable = !photosDraggable" /> + @draggable-clicked="photosDraggable = !photosDraggable" + @suggestions-clicked="onPhotoSuggestionsClicked" /> + @close="onCloseSidebar" + @opened="onOpenedSidebar" + @select-all-photo-suggestions="onSelectAllPhotoSuggestions" + @clear-photo-suggestions-selection="photoSuggestionsSelectedIndices=[]" + @cancel-photo-suggestions="onCancelPhotoSuggestions" + @save-photo-suggestions-selection="onSavePhotoSuggestionsSelection" /> @@ -149,6 +167,7 @@ import Actions from '@nextcloud/vue/dist/Components/Actions' import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' import { showError, showInfo, showSuccess } from '@nextcloud/dialogs' import moment from '@nextcloud/moment' +import { emit } from '@nextcloud/event-bus' import Map from '../components/Map' import MapsNavigation from '../components/MapsNavigation' @@ -190,6 +209,7 @@ export default { optionValues: optionsController.optionValues, sendPositionTimer: null, showSidebar: false, + sidebarIsFullScreen: false, activeSidebarTab: '', // slider sliderEnabled: optionsController.optionValues.displaySlider === 'true', @@ -213,6 +233,10 @@ export default { photosEnabled: optionsController.photosEnabled, photosDraggable: false, photos: [], + selectedPhoto: null, + showPhotoSuggestions: false, + photoSuggestions: [], + photoSuggestionsSelectedIndices: [], // contacts contactsLoading: false, contactsEnabled: optionsController.contactsEnabled, @@ -491,6 +515,10 @@ export default { if (optionsController.optionValues.trackMe === 'true') { this.sendPositionLoop() } + // Register sidebar to be callable from viewer, possibly nicer in main.js but I failed to but it there + window.OCA.Files.Sidebar.open = this.openSidebar + window.OCA.Files.Sidebar.close = this.closeSidebar + window.OCA.Files.Sidebar.setFullScreenMode = this.sidebarSetFullScreenMode document.onkeyup = (e) => { if (e.ctrlKey) { @@ -517,14 +545,49 @@ export default { this.activeSidebarTab = newActive }, onMainDetailClicked() { - this.showSidebar = !this.showSidebar - this.activeSidebarTab = '' + this.showSidebar ? this.closeSidebar() : this.openSidebar() + this.activeSidebarTab = this.showPhotoSuggestions ? 'photo-suggestion' : '' this.deselectAll() }, onCloseSidebar() { + // Make shure that the active photo suggestions tab stays there if photo suggestions are loaded + if (this.showPhotoSuggestions && (this.activeSidebarTab && this.activeSidebarTab !== 'photo-suggestion')) { + this.activeSidebarTab = 'photo-suggestion' + } else { + this.closeSidebar() + } + }, + closeSidebar() { + emit('files:sidebar:closed') + window.OCA.Files.Sidebar.state.file = '' this.showSidebar = false this.deselectAll() }, + openSidebar(path) { + const photo = this.photos.find((p) => p.path === path) + if (photo) { + this.activeSidebarTab = 'photo' + this.selectedPhoto = photo + window.OCA.Files.Sidebar.state.file = path + } else { + window.OCA.Files.Sidebar.state.file = true + } + window.OCA.Files.Sidebar.state.file = true + this.showSidebar = true + emit('files:sidebar:opening') + }, + /** + * Allow to set the Sidebar as fullscreen from OCA.Files.Sidebar + * + * @param {boolean} isFullScreen - Wether or not to render the Sidebar in fullscreen. + */ + sidebarSetFullScreenMode(isFullScreen) { + this.sidebarIsFullScreen = isFullScreen + }, + onOpenedSidebar() { + emit('files:sidebar:opened') + console.info('files:sidebar:opened') + }, deselectAll() { if (this.selectedFavorite) { this.selectedFavorite.selected = false @@ -798,6 +861,64 @@ export default { this.resetPhotosCoords(paths, false) } }, + // ================ PHOTOSUGGESTIONS ======== + onPhotoSuggestionsClicked() { + this.showPhotoSuggestions = !this.showPhotoSuggestions + if (this.photosEnabled && this.showPhotoSuggestions && this.photoSuggestions.length === 0) { + this.getPhotoSuggestions() + } + this.activeSidebarTab = 'photo-suggestion' + this.showPhotoSuggestions ? this.openSidebar() : this.closeSidebar() + }, + onPhotoSuggestionSelected(index) { + const indexOfIndex = this.photoSuggestionsSelectedIndices.findIndex((e) => { return index === e }) + if (indexOfIndex >= 0) { + this.photoSuggestionsSelectedIndices.splice(indexOfIndex, 1) + } else { + this.photoSuggestionsSelectedIndices.push(index) + } + }, + onPhotoSuggestionMoved(index, latLng) { + this.photoSuggestions[index].lat = latLng.lat + this.photoSuggestions[index].lng = latLng.lng + }, + getPhotoSuggestions() { + if (!this.photosEnabled) { + return + } + this.photosLoading = true + network.getPhotoSuggestions().then((response) => { + this.photoSuggestions = response.data.sort((a, b) => { + if (a.dateTaken < b.dateTaken) { + return -1 + } else if (a.dateTaken > b.dateTaken) { + return 1 + } + return 0 + }) + }).catch((error) => { + console.error(error) + }).then(() => { + this.photosLoading = false + }) + }, + onSelectAllPhotoSuggestions() { + this.photoSuggestionsSelectedIndices = [] + this.photoSuggestionsSelectedIndices = [...this.photoSuggestions.keys()] + }, + onCancelPhotoSuggestions() { + this.photoSuggestionsSelectedIndices = [] + this.showPhotoSuggestions = false + this.closeSidebar() + }, + onSavePhotoSuggestionsSelection() { + const paths = this.photoSuggestionsSelectedIndices.map((i) => { return this.photoSuggestions[i].path }) + const lats = this.photoSuggestionsSelectedIndices.map((i) => { return this.photoSuggestions[i].lat }) + const lngs = this.photoSuggestionsSelectedIndices.map((i) => { return this.photoSuggestions[i].lng }) + this.placePhotos(paths, lats, lngs) + this.getPhotoSuggestions() + this.photoSuggestionsSelectedIndices = [] + }, // ================ CONTACTS ================= onContactsClicked() { this.contactsEnabled = !this.contactsEnabled @@ -1102,7 +1223,7 @@ export default { this.deselectAll() // select this.favorites[f.id].selected = true - this.showSidebar = true + this.openSidebar() this.activeSidebarTab = 'favorite' this.selectedFavorite = f }, @@ -1227,7 +1348,7 @@ export default { if (openSidebar) { this.selectedFavorite = this.favorites[fav.id] this.activeSidebarTab = 'favorite' - this.showSidebar = true + this.openSidebar() } return fav.id }).catch((error) => { @@ -1440,7 +1561,7 @@ export default { this.deselectAll() // select track.selected = true - this.showSidebar = true + this.openSidebar() this.activeSidebarTab = 'track' this.selectedTrack = track }, diff --git a/translationfiles/templates/maps.pot b/translationfiles/templates/maps.pot index e3b444716..3a4e03bf2 100644 --- a/translationfiles/templates/maps.pot +++ b/translationfiles/templates/maps.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Nextcloud 3.14159\n" "Report-Msgid-Bugs-To: translations\\@example.com\n" -"POT-Creation-Date: 2022-03-14 02:59+0000\n" +"POT-Creation-Date: 2022-03-23 03:01+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n"