diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2bc57d2..51803a4f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,7 +96,7 @@ jobs: - upload-coverage runs-on: ubuntu-24.04 steps: - - uses: geekyeggo/delete-artifact@v2 + - uses: geekyeggo/delete-artifact@v5 with: name: | coverage-* diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index a2783f97..a6b923b4 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -45,6 +45,6 @@ jobs: needs: ['publish'] runs-on: ubuntu-24.04 steps: - - uses: geekyeggo/delete-artifact@v2 + - uses: geekyeggo/delete-artifact@v5 with: name: dist-files-* diff --git a/CHANGELOG.md b/CHANGELOG.md index ad221705..818a25f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org). +## [4.4.6] - 2025-03-20 +### Added +* *Nothing* + +### Changed +* *Nothing* + +### Deprecated +* *Nothing* + +### Removed +* *Nothing* + +### Fixed +* [#2391](https://github.com/shlinkio/shlink/issues/2391) When sending visits to Matomo, send the country code, not the country name. +* Fix error with new option introduced by `endroid/qr-code` 6.0.4. + + ## [4.4.5] - 2025-03-01 ### Added * *Nothing* diff --git a/composer.json b/composer.json index d23ec7cf..fa8cc972 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "doctrine/migrations": "^3.8", "doctrine/orm": "^3.3", "donatj/phpuseragentparser": "^1.10", - "endroid/qr-code": "^6.0", + "endroid/qr-code": "^6.0.5", "friendsofphp/proxy-manager-lts": "^1.0", "geoip2/geoip2": "^3.1", "guzzlehttp/guzzle": "^7.9", @@ -77,7 +77,8 @@ "veewee/composer-run-parallel": "^1.4" }, "conflict": { - "symfony/var-exporter": ">=6.3.9,<=6.4.0" + "symfony/var-exporter": ">=6.3.9,<=6.4.0", + "phpunit/phpunit": "12.0.9" }, "autoload": { "psr-4": { diff --git a/module/Core/src/Action/Model/QrCodeParams.php b/module/Core/src/Action/Model/QrCodeParams.php index 459c99b7..3b25b611 100644 --- a/module/Core/src/Action/Model/QrCodeParams.php +++ b/module/Core/src/Action/Model/QrCodeParams.php @@ -38,6 +38,7 @@ final readonly class QrCodeParams public int $size, public int $margin, public WriterInterface $writer, + public array $writerOptions, public ErrorCorrectionLevel $errorCorrectionLevel, public RoundBlockSizeMode $roundBlockSizeMode, public ColorInterface $color, @@ -49,11 +50,13 @@ final readonly class QrCodeParams public static function fromRequest(ServerRequestInterface $request, QrCodeOptions $defaults): self { $query = $request->getQueryParams(); + [$writer, $writerOptions] = self::resolveWriterAndWriterOptions($query, $defaults); return new self( size: self::resolveSize($query, $defaults), margin: self::resolveMargin($query, $defaults), - writer: self::resolveWriter($query, $defaults), + writer: $writer, + writerOptions: $writerOptions, errorCorrectionLevel: self::resolveErrorCorrection($query, $defaults), roundBlockSizeMode: self::resolveRoundBlockSize($query, $defaults), color: self::resolveColor($query, $defaults), @@ -83,14 +86,17 @@ final readonly class QrCodeParams return max($intMargin, 0); } - private static function resolveWriter(array $query, QrCodeOptions $defaults): WriterInterface + /** + * @return array{WriterInterface, array} + */ + private static function resolveWriterAndWriterOptions(array $query, QrCodeOptions $defaults): array { $qFormat = self::normalizeParam($query['format'] ?? ''); $format = contains($qFormat, self::SUPPORTED_FORMATS) ? $qFormat : self::normalizeParam($defaults->format); return match ($format) { - 'svg' => new SvgWriter(), - default => new PngWriter(), + 'svg' => [new SvgWriter(), []], + default => [new PngWriter(), [PngWriter::WRITER_OPTION_NUMBER_OF_COLORS => null]], }; } diff --git a/module/Core/src/Action/QrCodeAction.php b/module/Core/src/Action/QrCodeAction.php index fbc83c21..889d1d04 100644 --- a/module/Core/src/Action/QrCodeAction.php +++ b/module/Core/src/Action/QrCodeAction.php @@ -45,6 +45,7 @@ readonly class QrCodeAction implements MiddlewareInterface $params = QrCodeParams::fromRequest($request, $this->options); $qrCodeBuilder = new Builder( writer: $params->writer, + writerOptions: $params->writerOptions, data: $this->stringifier->stringify($shortUrl), errorCorrectionLevel: $params->errorCorrectionLevel, size: $params->size, diff --git a/module/Core/src/Matomo/MatomoVisitSender.php b/module/Core/src/Matomo/MatomoVisitSender.php index 9fc0176a..6a32c2a5 100644 --- a/module/Core/src/Matomo/MatomoVisitSender.php +++ b/module/Core/src/Matomo/MatomoVisitSender.php @@ -11,6 +11,8 @@ use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Repository\VisitIterationRepositoryInterface; use Throwable; +use function strtolower; + readonly class MatomoVisitSender implements MatomoVisitSenderInterface { public function __construct( @@ -60,7 +62,7 @@ readonly class MatomoVisitSender implements MatomoVisitSenderInterface if ($location !== null) { $tracker ->setCity($location->cityName) - ->setCountry($location->countryName) + ->setCountry(strtolower($location->countryCode)) ->setLatitude($location->latitude) ->setLongitude($location->longitude); } diff --git a/module/Core/test/Matomo/MatomoVisitSenderTest.php b/module/Core/test/Matomo/MatomoVisitSenderTest.php index 0acccd1d..7d66d868 100644 --- a/module/Core/test/Matomo/MatomoVisitSenderTest.php +++ b/module/Core/test/Matomo/MatomoVisitSenderTest.php @@ -43,6 +43,9 @@ class MatomoVisitSenderTest extends TestCase } #[Test, DataProvider('provideTrackerMethods')] + /** + * @param array $invokedMethods + */ public function visitIsSentToMatomo(Visit $visit, string|null $originalIpAddress, array $invokedMethods): void { $tracker = $this->createMock(MatomoTracker::class); @@ -66,8 +69,8 @@ class MatomoVisitSenderTest extends TestCase )->willReturn($tracker); } - foreach ($invokedMethods as $invokedMethod) { - $tracker->expects($this->once())->method($invokedMethod)->willReturn($tracker); + foreach ($invokedMethods as $invokedMethod => $args) { + $tracker->expects($this->once())->method($invokedMethod)->with(...$args)->willReturn($tracker); } $this->trackerBuilder->expects($this->once())->method('buildMatomoTracker')->willReturn($tracker); @@ -81,18 +84,28 @@ class MatomoVisitSenderTest extends TestCase yield 'located regular visit' => [ Visit::forValidShortUrl(ShortUrl::withLongUrl('https://shlink.io'), Visitor::empty()) ->locate(VisitLocation::fromGeolocation(new Location( - countryCode: 'countryCode', + countryCode: 'US', countryName: 'countryName', regionName: 'regionName', city: 'city', latitude: 123, - longitude: 123, + longitude: 456, timeZone: 'timeZone', ))), '1.2.3.4', - ['setCity', 'setCountry', 'setLatitude', 'setLongitude', 'setIp'], + [ + 'setCity' => ['city'], + 'setCountry' => ['us'], + 'setLatitude' => [123], + 'setLongitude' => [456], + 'setIp' => ['1.2.3.4'], + ], + ]; + yield 'fallback IP' => [ + Visit::forBasePath(Visitor::fromParams(remoteAddress: '5.6.7.8')), + null, + ['setIp' => ['5.6.7.0']], ]; - yield 'fallback IP' => [Visit::forBasePath(Visitor::fromParams(remoteAddress: '1.2.3.4')), null, ['setIp']]; } #[Test, DataProvider('provideUrlsToTrack')]