From 42fe4bd5ce2b93aee80a6596d7620622d663514d Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 26 Mar 2018 20:13:03 +0200 Subject: [PATCH] Created new action to track visits, which returns an empty pixel --- composer.json | 3 +- module/Common/src/Response/PixelResponse.php | 33 +++++++++ module/Core/config/dependencies.config.php | 6 ++ module/Core/config/routes.config.php | 6 ++ .../src/Action/AbstractTrackingAction.php | 74 +++++++++++++++++++ module/Core/src/Action/PixelAction.php | 15 ++++ module/Core/src/Action/RedirectAction.php | 70 ++---------------- 7 files changed, 140 insertions(+), 67 deletions(-) create mode 100644 module/Common/src/Response/PixelResponse.php create mode 100644 module/Core/src/Action/AbstractTrackingAction.php create mode 100644 module/Core/src/Action/PixelAction.php diff --git a/composer.json b/composer.json index f57d5c34..885a3103 100644 --- a/composer.json +++ b/composer.json @@ -22,10 +22,9 @@ "doctrine/dbal": "^2.5", "doctrine/migrations": "^1.4", "doctrine/orm": "^2.5", - "endroid/qrcode": "^1.7", + "endroid/qr-code": "^1.7", "firebase/php-jwt": "^4.0", "guzzlehttp/guzzle": "^6.2", - "http-interop/http-middleware": "^0.4.1", "mikehaertl/phpwkhtmltopdf": "^2.2", "monolog/monolog": "^1.21", "roave/security-advisories": "dev-master", diff --git a/module/Common/src/Response/PixelResponse.php b/module/Common/src/Response/PixelResponse.php new file mode 100644 index 00000000..a19c1c85 --- /dev/null +++ b/module/Common/src/Response/PixelResponse.php @@ -0,0 +1,33 @@ +createBody(), $status, $headers); + } + + /** + * Create the message body. + * + * @return StreamInterface + */ + private function createBody(): StreamInterface + { + $body = new Stream('php://temp', 'wb+'); + $body->write(\base64_decode(self::BASE_64_IMAGE)); + $body->rewind(); + return $body; + } +} diff --git a/module/Core/config/dependencies.config.php b/module/Core/config/dependencies.config.php index 11038e4b..9061a3f5 100644 --- a/module/Core/config/dependencies.config.php +++ b/module/Core/config/dependencies.config.php @@ -28,6 +28,7 @@ return [ // Middleware Action\RedirectAction::class => ConfigAbstractFactory::class, + Action\PixelAction::class => ConfigAbstractFactory::class, Action\QrCodeAction::class => ConfigAbstractFactory::class, Action\PreviewAction::class => ConfigAbstractFactory::class, Middleware\QrCodeCacheMiddleware::class => ConfigAbstractFactory::class, @@ -60,6 +61,11 @@ return [ Service\VisitsTracker::class, Options\AppOptions::class, ], + Action\PixelAction::class => [ + Service\UrlShortener::class, + Service\VisitsTracker::class, + Options\AppOptions::class, + ], Action\QrCodeAction::class => [RouterInterface::class, Service\UrlShortener::class, 'Logger_Shlink'], Action\PreviewAction::class => [PreviewGenerator::class, Service\UrlShortener::class], Middleware\QrCodeCacheMiddleware::class => [Cache::class], diff --git a/module/Core/config/routes.config.php b/module/Core/config/routes.config.php index e18cb448..2d8c80b3 100644 --- a/module/Core/config/routes.config.php +++ b/module/Core/config/routes.config.php @@ -13,6 +13,12 @@ return [ 'middleware' => Action\RedirectAction::class, 'allowed_methods' => ['GET'], ], + [ + 'name' => 'pixel-tracking', + 'path' => '/{shortCode}/track', + 'middleware' => Action\PixelAction::class, + 'allowed_methods' => ['GET'], + ], [ 'name' => 'short-url-qr-code', 'path' => '/{shortCode}/qr-code[/{size:[0-9]+}]', diff --git a/module/Core/src/Action/AbstractTrackingAction.php b/module/Core/src/Action/AbstractTrackingAction.php new file mode 100644 index 00000000..0ee475db --- /dev/null +++ b/module/Core/src/Action/AbstractTrackingAction.php @@ -0,0 +1,74 @@ +urlShortener = $urlShortener; + $this->visitTracker = $visitTracker; + $this->appOptions = $appOptions; + } + + /** + * Process an incoming server request and return a response, optionally delegating + * to the next middleware component to create the response. + * + * @param ServerRequestInterface $request + * @param RequestHandlerInterface $handler + * + * @return ResponseInterface + */ + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + $shortCode = $request->getAttribute('shortCode', ''); + $query = $request->getQueryParams(); + $disableTrackParam = $this->appOptions->getDisableTrackParam(); + + try { + $longUrl = $this->urlShortener->shortCodeToUrl($shortCode); + + // Track visit to this short code + if ($disableTrackParam === null || ! \array_key_exists($disableTrackParam, $query)) { + $this->visitTracker->track($shortCode, $request); + } + + return $this->createResp($longUrl); + } catch (InvalidShortCodeException | EntityDoesNotExistException $e) { + return $this->buildErrorResponse($request, $handler); + } + } + + abstract protected function createResp(string $longUrl): ResponseInterface; +} diff --git a/module/Core/src/Action/PixelAction.php b/module/Core/src/Action/PixelAction.php new file mode 100644 index 00000000..5f2c797a --- /dev/null +++ b/module/Core/src/Action/PixelAction.php @@ -0,0 +1,15 @@ +urlShortener = $urlShortener; - $this->visitTracker = $visitTracker; - $this->appOptions = $appOptions; - } - - /** - * Process an incoming server request and return a response, optionally delegating - * to the next middleware component to create the response. - * - * @param Request $request - * @param RequestHandlerInterface $handler - * - * @return Response - */ - public function process(Request $request, RequestHandlerInterface $handler): Response + protected function createResp(string $longUrl): Response { - $shortCode = $request->getAttribute('shortCode', ''); - $query = $request->getQueryParams(); - $disableTrackParam = $this->appOptions->getDisableTrackParam(); - - try { - $longUrl = $this->urlShortener->shortCodeToUrl($shortCode); - - // Track visit to this short code - if ($disableTrackParam === null || ! \array_key_exists($disableTrackParam, $query)) { - $this->visitTracker->track($shortCode, $request); - } - - // Return a redirect response to the long URL. - // Use a temporary redirect to make sure browsers always hit the server for analytics purposes - return new RedirectResponse($longUrl); - } catch (InvalidShortCodeException $e) { - return $this->buildErrorResponse($request, $handler); - } catch (EntityDoesNotExistException $e) { - return $this->buildErrorResponse($request, $handler); - } + // Return a redirect response to the long URL. + // Use a temporary redirect to make sure browsers always hit the server for analytics purposes + return new RedirectResponse($longUrl); } }