From ee59781be5abdf0ca08e8c40defbcbdfb5109c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20Hu=CC=88bner?= Date: Tue, 2 Oct 2018 19:13:00 +0200 Subject: [PATCH 1/8] Copy staticmap stuff from critical mass. --- src/SeoPage/SeoPage.php | 29 +++----- src/StaticMap/StaticMapableInterface.php | 7 ++ .../UrlGenerator/AbstractUrlGenerator.php | 19 ++++++ src/StaticMap/UrlGenerator/UrlGenerator.php | 67 +++++++++++++++++++ .../UrlGenerator/UrlGeneratorInterface.php | 10 +++ 5 files changed, 114 insertions(+), 18 deletions(-) create mode 100644 src/StaticMap/StaticMapableInterface.php create mode 100644 src/StaticMap/UrlGenerator/AbstractUrlGenerator.php create mode 100644 src/StaticMap/UrlGenerator/UrlGenerator.php create mode 100644 src/StaticMap/UrlGenerator/UrlGeneratorInterface.php diff --git a/src/SeoPage/SeoPage.php b/src/SeoPage/SeoPage.php index 310e68a4..7ffaa502 100644 --- a/src/SeoPage/SeoPage.php +++ b/src/SeoPage/SeoPage.php @@ -2,6 +2,7 @@ namespace App\SeoPage; +use App\StaticMap\UrlGenerator\UrlGeneratorInterface; use Sonata\SeoBundle\Seo\SeoPageInterface; class SeoPage @@ -9,9 +10,13 @@ class SeoPage /** @var SeoPageInterface */ protected $sonataSeoPage; - public function __construct(SeoPageInterface $sonataSeoPage) + /** @var UrlGeneratorInterface $urlGenerator */ + protected $urlGenerator; + + public function __construct(SeoPageInterface $sonataSeoPage, UrlGeneratorInterface $urlGenerator) { $this->sonataSeoPage = $sonataSeoPage; + $this->urlGenerator = $urlGenerator; } public function setTitle(string $title): SeoPage @@ -34,27 +39,15 @@ public function setDescription(string $description): SeoPage return $this; } - /* - public function setPreviewPhoto(PhotoInterface $object): SeoPage + public function setPreviewMap(StaticMapableInterface $staticMapable): SeoPageInterface { - if (!$object->getImageName()) { - return $this; - } - - $imageFilename = $this->uploaderHelper->asset($object, 'imageFile'); - - $facebookPreviewPath = $this->cacheManager->getBrowserPath($imageFilename, 'facebook_preview_image'); - $twitterPreviewPath = $this->cacheManager->getBrowserPath($imageFilename, 'twitter_summary_large_image'); - $this->sonataSeoPage - ->addMeta('property', 'og:image', $facebookPreviewPath) - ->addMeta('name', 'twitter:image', $twitterPreviewPath) - ->addMeta('name', 'twitter:card', 'summary_large_image') - ; + ->addMeta('property', 'og:image', $this->urlGenerator->generate($staticMapable, 600, 315), ['escape' => false]) + ->addMeta('name', 'twitter:image', $this->urlGenerator->generate($staticMapable, 800, 320), ['escape' => false]) + ->addMeta('name', 'twitter:card', 'summary_large_image'); return $this; - }*/ - + } public function setCanonicalLink(string $link): SeoPage { $this->sonataSeoPage diff --git a/src/StaticMap/StaticMapableInterface.php b/src/StaticMap/StaticMapableInterface.php new file mode 100644 index 00000000..21ec9f22 --- /dev/null +++ b/src/StaticMap/StaticMapableInterface.php @@ -0,0 +1,7 @@ + '865x512', + ]; + + public function __construct(string $staticmapsHost) + { + $this->staticmapsHost = $staticmapsHost; + } +} diff --git a/src/StaticMap/UrlGenerator/UrlGenerator.php b/src/StaticMap/UrlGenerator/UrlGenerator.php new file mode 100644 index 00000000..aef706bd --- /dev/null +++ b/src/StaticMap/UrlGenerator/UrlGenerator.php @@ -0,0 +1,67 @@ +staticmapsRide($object, $width, $height, $zoom); + } elseif ($object instanceof City) { + return $this->staticmapsCity($object, $width, $height, $zoom); + } + + return ''; + } + + public function staticmapsCity(City $city, int $width = null, int $height = null, int $zoom = null): string + { + $parameters = [ + 'markers' => sprintf('%f,%f,%s,%s,%s', $city->getLatitude(), $city->getLongitude(), 'circle', 'blue', 'university'), + ]; + + return $this->generateMapUrl($parameters, $width, $height, $zoom); + } + + public function staticmapsStation(Station $station, int $width = null, int $height = null, int $zoom = null): string + { + $parameters = [ + 'markers' => sprintf('%f,%f,%s,%s,%s', $station->getLatitude(), $station->getLongitude(), 'circle', 'blue', 'university'), + ]; + + return $this->generateMapUrl($parameters, $width, $height, $zoom); + } + + protected function generateMapUrl(array $parameters = [], int $width = null, int $height = null, int $zoom = null): string + { + $viewParameters = []; + + if ($width && $height) { + $viewParameters['size'] = sprintf('%dx%d', $width, $height); + } + + if ($zoom) { + $viewParameters['zoom'] = sprintf('%d', $zoom); + } + + $parameters = array_merge($parameters, $this->defaultParameters, $viewParameters); + + return sprintf('%sstaticmap.php?%s', $this->staticmapsHost, $this->generateMapUrlParameters($parameters)); + } + + protected function generateMapUrlParameters(array $parameters = []): string + { + $list = []; + + foreach ($parameters as $key => $value) { + $list [] = sprintf('%s=%s', $key, $value); + } + + return implode('&', $list); + } +} diff --git a/src/StaticMap/UrlGenerator/UrlGeneratorInterface.php b/src/StaticMap/UrlGenerator/UrlGeneratorInterface.php new file mode 100644 index 00000000..77ee42d5 --- /dev/null +++ b/src/StaticMap/UrlGenerator/UrlGeneratorInterface.php @@ -0,0 +1,10 @@ + Date: Tue, 2 Oct 2018 19:13:10 +0200 Subject: [PATCH 2/8] Add config. --- .env.dist | 1 + config/services.yaml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.env.dist b/.env.dist index 4b78526c..949e1735 100644 --- a/.env.dist +++ b/.env.dist @@ -28,3 +28,4 @@ TWITTER_CLIENT_SECRET=ThisTokenIsNotSoSecretChangeIt YOURLS_API_URL=http://sqi.be/yourls-api.php YOURLS_API_USERNAME=username YOURLS_API_PASSWORD=password +STATICMAPS_HOST=https://maps.caldera.cc/ diff --git a/config/services.yaml b/config/services.yaml index 50c3f7d4..254f4ae9 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -7,6 +7,7 @@ parameters: yourls.api_url: '%env(YOURLS_HOSTNAME)%' yourls.api_username: '%env(YOURLS_USERNAME)%' yourls.api_password: '%env(YOURLS_PASSWORD)%' + staticmaps.host: '%env(STATICMAPS_HOST)%' services: # default configuration for services in *this* file @@ -22,6 +23,7 @@ services: $apiUrl: '%env(YOURLS_API_URL)%' $apiUsername: '%env(YOURLS_API_USERNAME)%' $apiPassword: '%env(YOURLS_API_PASSWORD)%' + $staticmapsHost: '%staticmaps.host%' # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name From ddef038f4c783e6f53b381e249fae50ae52dee05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20Hu=CC=88bner?= Date: Tue, 2 Oct 2018 19:13:25 +0200 Subject: [PATCH 3/8] Add $latitude und $longitude properties to City entity. --- src/Entity/City.php | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/Entity/City.php b/src/Entity/City.php index cf2a6086..5a69ffe5 100644 --- a/src/Entity/City.php +++ b/src/Entity/City.php @@ -46,6 +46,18 @@ class City */ protected $description; + /** + * @ORM\Column(type="float", nullable=false) + * @JMS\Expose() + */ + protected $latitude; + + /** + * @ORM\Column(type="float", nullable=false) + * @JMS\Expose() + */ + protected $longitude; + /** * @ORM\OneToMany(targetEntity="TwitterSchedule", mappedBy="city") */ @@ -87,6 +99,30 @@ public function setCreatedAt(\DateTime $createdAt): City return $this; } + public function getLatitude(): float + { + return $this->latitude; + } + + public function setLatitude(float $latitude): City + { + $this->latitude = $latitude; + + return $this; + } + + public function getLongitude(): float + { + return $this->longitude; + } + + public function setLongitude(float $longitude): City + { + $this->longitude = $longitude; + + return $this; + } + public function getName(): ?string { return $this->name; From 48b53424b75d394a90575df74418168c134158e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20Hu=CC=88bner?= Date: Tue, 2 Oct 2018 19:23:36 +0200 Subject: [PATCH 4/8] Add missing migration. --- src/Migrations/Version20181002172100.php | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/Migrations/Version20181002172100.php diff --git a/src/Migrations/Version20181002172100.php b/src/Migrations/Version20181002172100.php new file mode 100644 index 00000000..8ecdd3ff --- /dev/null +++ b/src/Migrations/Version20181002172100.php @@ -0,0 +1,28 @@ +abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('ALTER TABLE city ADD latitude DOUBLE PRECISION NOT NULL, ADD longitude DOUBLE PRECISION NOT NULL'); + } + + public function down(Schema $schema) : void + { + // this down() migration is auto-generated, please modify it to your needs + $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('ALTER TABLE city DROP latitude, DROP longitude'); + } +} From 203dda8b6d78679069a8ce1f6216753ad05b5987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20Hu=CC=88bner?= Date: Wed, 3 Oct 2018 22:47:29 +0200 Subject: [PATCH 5/8] Introduce more Dependency injection stuff. --- src/DependencyInjection/AppExtension.php | 15 +++++++++++++++ src/DependencyInjection/Configuration.php | 17 +++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/DependencyInjection/AppExtension.php create mode 100644 src/DependencyInjection/Configuration.php diff --git a/src/DependencyInjection/AppExtension.php b/src/DependencyInjection/AppExtension.php new file mode 100644 index 00000000..1ecf556c --- /dev/null +++ b/src/DependencyInjection/AppExtension.php @@ -0,0 +1,15 @@ +processConfiguration($configuration, $configs); + } +} diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php new file mode 100644 index 00000000..e2913aaa --- /dev/null +++ b/src/DependencyInjection/Configuration.php @@ -0,0 +1,17 @@ +root('caldera'); + + return $treeBuilder; + } +} From 0ca6389ca39b20634e82a031a078388f65e3fed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20Hu=CC=88bner?= Date: Wed, 3 Oct 2018 22:47:49 +0200 Subject: [PATCH 6/8] Register TwigSeoExtensionPass to override sonata twig extension. --- .../Compiler/TwigSeoExtensionPass.php | 19 ++++++++ src/Kernel.php | 4 ++ src/Twig/Extension/SeoTwigExtension.php | 44 +++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 src/DependencyInjection/Compiler/TwigSeoExtensionPass.php create mode 100644 src/Twig/Extension/SeoTwigExtension.php diff --git a/src/DependencyInjection/Compiler/TwigSeoExtensionPass.php b/src/DependencyInjection/Compiler/TwigSeoExtensionPass.php new file mode 100644 index 00000000..8432ac89 --- /dev/null +++ b/src/DependencyInjection/Compiler/TwigSeoExtensionPass.php @@ -0,0 +1,19 @@ +hasDefinition('sonata.seo.twig.extension')) { + return; + } + + $container->removeDefinition('sonata.seo.twig.extension'); + } +} diff --git a/src/Kernel.php b/src/Kernel.php index c9420adf..86dbfc36 100644 --- a/src/Kernel.php +++ b/src/Kernel.php @@ -2,8 +2,10 @@ namespace App; +use App\DependencyInjection\Compiler\TwigSeoExtensionPass; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Kernel as BaseKernel; use Symfony\Component\Routing\RouteCollectionBuilder; @@ -46,6 +48,8 @@ protected function configureContainer(ContainerBuilder $container, LoaderInterfa $loader->load($confDir.'/{packages}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob'); $loader->load($confDir.'/{services}'.self::CONFIG_EXTS, 'glob'); $loader->load($confDir.'/{services}_'.$this->environment.self::CONFIG_EXTS, 'glob'); + + $container->addCompilerPass(new TwigSeoExtensionPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 100); } protected function configureRoutes(RouteCollectionBuilder $routes) diff --git a/src/Twig/Extension/SeoTwigExtension.php b/src/Twig/Extension/SeoTwigExtension.php new file mode 100644 index 00000000..747c65f0 --- /dev/null +++ b/src/Twig/Extension/SeoTwigExtension.php @@ -0,0 +1,44 @@ +page->getMetas() as $type => $metas) { + foreach ((array) $metas as $name => $meta) { + list($content, $extras) = $meta; + + if (!empty($content)) { + $html .= sprintf("\n", + $type, + $this->normalize($name), + array_key_exists('escape', $extras) && $extras['escape'] === false ? $content : $this->normalize($content) + ); + } else { + $html .= sprintf("\n", + $type, + $this->normalize($name) + ); + } + } + } + + return $html; + } + + private function normalize($string): string + { + return htmlentities(strip_tags($string), ENT_COMPAT, $this->encoding); + } +} From 2c5fffec8aa36020465a845cc97bb5cb7ab14463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20Hu=CC=88bner?= Date: Wed, 3 Oct 2018 22:48:04 +0200 Subject: [PATCH 7/8] WIP. --- config/services.yaml | 4 ++++ src/Controller/DisplayController.php | 2 ++ src/Entity/City.php | 3 ++- src/Entity/Station.php | 3 ++- src/SeoPage/SeoPage.php | 10 +++++++--- src/StaticMap/UrlGenerator/UrlGenerator.php | 2 +- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/config/services.yaml b/config/services.yaml index 254f4ae9..04c2b5d8 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -40,6 +40,9 @@ services: # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones + Sonata\SeoBundle\Seo\SeoPageInterface: + alias: sonata.seo.page.default + App\StationLoader\StationLoader: ~ App\Twitter\MessageFactory\MessageFactoryInterface: @@ -59,3 +62,4 @@ services: App\SeoPage\SeoPage: arguments: $sonataSeoPage: '@sonata.seo.page.default' + diff --git a/src/Controller/DisplayController.php b/src/Controller/DisplayController.php index 0011e2ef..e279a0e5 100644 --- a/src/Controller/DisplayController.php +++ b/src/Controller/DisplayController.php @@ -30,6 +30,8 @@ public function stationAction(SeoPage $seoPage, string $stationCode, PollutionDa $seoPage->setTitle(sprintf('Luftmesswerte für die Station %s', $station->getStationCode())); } + $seoPage->setPreviewMap($station); + return $this->render('Default/station.html.twig', [ 'station' => $station, 'boxList' => $boxList, diff --git a/src/Entity/City.php b/src/Entity/City.php index 5a69ffe5..0222ae47 100644 --- a/src/Entity/City.php +++ b/src/Entity/City.php @@ -2,6 +2,7 @@ namespace App\Entity; +use App\StaticMap\StaticMapableInterface; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; @@ -12,7 +13,7 @@ * @ORM\Table(name="city") * @JMS\ExclusionPolicy("ALL") */ -class City +class City implements StaticMapableInterface { /** * @ORM\Id diff --git a/src/Entity/Station.php b/src/Entity/Station.php index 0f3bfac0..e669b25f 100644 --- a/src/Entity/Station.php +++ b/src/Entity/Station.php @@ -2,6 +2,7 @@ namespace App\Entity; +use App\StaticMap\StaticMapableInterface; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Caldera\GeoBasic\Coord\Coord; @@ -15,7 +16,7 @@ * @UniqueEntity("stationCode") * @JMS\ExclusionPolicy("ALL") */ -class Station extends Coord +class Station extends Coord implements StaticMapableInterface { /** * @ORM\Id diff --git a/src/SeoPage/SeoPage.php b/src/SeoPage/SeoPage.php index 7ffaa502..d62bf955 100644 --- a/src/SeoPage/SeoPage.php +++ b/src/SeoPage/SeoPage.php @@ -2,6 +2,7 @@ namespace App\SeoPage; +use App\StaticMap\StaticMapableInterface; use App\StaticMap\UrlGenerator\UrlGeneratorInterface; use Sonata\SeoBundle\Seo\SeoPageInterface; @@ -39,15 +40,18 @@ public function setDescription(string $description): SeoPage return $this; } - public function setPreviewMap(StaticMapableInterface $staticMapable): SeoPageInterface + public function setPreviewMap(StaticMapableInterface $staticMapable): SeoPage { $this->sonataSeoPage - ->addMeta('property', 'og:image', $this->urlGenerator->generate($staticMapable, 600, 315), ['escape' => false]) - ->addMeta('name', 'twitter:image', $this->urlGenerator->generate($staticMapable, 800, 320), ['escape' => false]) + ->addMeta('property', 'og:image', $this->urlGenerator->generate($staticMapable, 600, 315), + ['escape' => false]) + ->addMeta('name', 'twitter:image', $this->urlGenerator->generate($staticMapable, 800, 320), + ['escape' => false]) ->addMeta('name', 'twitter:card', 'summary_large_image'); return $this; } + public function setCanonicalLink(string $link): SeoPage { $this->sonataSeoPage diff --git a/src/StaticMap/UrlGenerator/UrlGenerator.php b/src/StaticMap/UrlGenerator/UrlGenerator.php index aef706bd..675fb810 100644 --- a/src/StaticMap/UrlGenerator/UrlGenerator.php +++ b/src/StaticMap/UrlGenerator/UrlGenerator.php @@ -11,7 +11,7 @@ class UrlGenerator extends AbstractUrlGenerator public function generate(StaticMapableInterface $object, int $width = null, int $height = null, int $zoom = null): string { if ($object instanceof Station) { - return $this->staticmapsRide($object, $width, $height, $zoom); + return $this->staticmapsStation($object, $width, $height, $zoom); } elseif ($object instanceof City) { return $this->staticmapsCity($object, $width, $height, $zoom); } From 69c4e31831e4d664dd4271fefd855a111d47f832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20Hu=CC=88bner?= Date: Thu, 4 Oct 2018 08:09:28 +0200 Subject: [PATCH 8/8] Change icons for static maps. --- src/StaticMap/UrlGenerator/UrlGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StaticMap/UrlGenerator/UrlGenerator.php b/src/StaticMap/UrlGenerator/UrlGenerator.php index 675fb810..9816076e 100644 --- a/src/StaticMap/UrlGenerator/UrlGenerator.php +++ b/src/StaticMap/UrlGenerator/UrlGenerator.php @@ -31,7 +31,7 @@ public function staticmapsCity(City $city, int $width = null, int $height = null public function staticmapsStation(Station $station, int $width = null, int $height = null, int $zoom = null): string { $parameters = [ - 'markers' => sprintf('%f,%f,%s,%s,%s', $station->getLatitude(), $station->getLongitude(), 'circle', 'blue', 'university'), + 'markers' => sprintf('%f,%f,%s,%s,%s', $station->getLatitude(), $station->getLongitude(), 'circle', 'blue', 'thermometer-full'), ]; return $this->generateMapUrl($parameters, $width, $height, $zoom);