diff --git a/composer.json b/composer.json index 775cb90..b4d493a 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "source": "https://github.com/netzmacht/contao-leaflet-maps" }, "require": { - "php": ">=7.0", + "php": ">=7.1", "ext-json": "*", "ext-pdo": "*", "contao/core-bundle": "~4.4", @@ -39,6 +39,7 @@ "netzmacht/php-javascript-builder": "^1.0", "netzmacht/php-leaflet": "^1.0.2", "netzmacht/contao-toolkit": "~3.0", + "netzmacht/contao-page-context": "~1.0", "contao-community-alliance/meta-palettes": "^2.0 || ^1.5", "menatwork/contao-multicolumnwizard": "^3.2", "doctrine/cache": "^1.0" diff --git a/js/Contao.js b/js/Contao.js index 004d65e..69ce015 100644 --- a/js/Contao.js +++ b/js/Contao.js @@ -138,7 +138,7 @@ L.Contao = L.Evented.extend({ * @param customLayer optional custom layer. * @param map Pass a map object so that the data loading events are passed to the map. */ - loadFile: function (url, type, options, customLayer, map) { + loadUrl: function (url, type, options, customLayer, map) { var layer = omnivore[type](url, options, customLayer); if (map) { @@ -172,6 +172,19 @@ L.Contao = L.Evented.extend({ return layer; }, + /** + * Load data from an url into a layer using omnivore. + * + * @param url A file url. + * @param type The response content format. + * @param options Parser options + * @param customLayer optional custom layer. + * @param map Pass a map object so that the data loading events are passed to the map. + */ + loadFile: function (url, type, options, customLayer, map) { + return this.loadUrl(url, type, options, customLayer, map); + }, + /** * Point to layer callback. Adds a geo json point to the layer. * diff --git a/src/Bundle/ContaoManager/Plugin.php b/src/Bundle/ContaoManager/Plugin.php index 8033179..f9f6d52 100644 --- a/src/Bundle/ContaoManager/Plugin.php +++ b/src/Bundle/ContaoManager/Plugin.php @@ -18,15 +18,20 @@ use Contao\CoreBundle\ContaoCoreBundle; use Contao\ManagerPlugin\Bundle\BundlePluginInterface; use Contao\ManagerPlugin\Bundle\Config\BundleConfig; use Contao\ManagerPlugin\Bundle\Parser\ParserInterface; +use Contao\ManagerPlugin\Routing\RoutingPluginInterface; use Netzmacht\Contao\Leaflet\Bundle\NetzmachtContaoLeafletBundle; +use Netzmacht\Contao\PageContext\NetzmachtContaoPageContextBundle; use Netzmacht\Contao\Toolkit\Bundle\NetzmachtContaoToolkitBundle; +use Symfony\Component\Config\Loader\LoaderResolverInterface; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Routing\RouteCollection; /** * Contao manager plugin. * * @package Netzmacht\Contao\Leaflet\ContaoManager */ -class Plugin implements BundlePluginInterface +class Plugin implements BundlePluginInterface, RoutingPluginInterface { /** * {@inheritdoc} @@ -35,8 +40,32 @@ class Plugin implements BundlePluginInterface { return [ BundleConfig::create(NetzmachtContaoLeafletBundle::class) - ->setLoadAfter([ContaoCoreBundle::class, NetzmachtContaoToolkitBundle::class]) + ->setLoadAfter( + [ + ContaoCoreBundle::class, + NetzmachtContaoToolkitBundle::class, + NetzmachtContaoPageContextBundle::class + ] + ) ->setReplace(['leaflet']), ]; } + + /** + * {@inheritdoc} + */ + public function getRouteCollection(LoaderResolverInterface $resolver, KernelInterface $kernel): ?RouteCollection + { + $loader = $resolver->resolve(__DIR__ . '/../Resources/config/routing.yml'); + if (!$loader) { + return null; + } + + $collection = $loader->load(__DIR__ . '/../Resources/config/routing.yml'); + if ($collection instanceof RouteCollection) { + $collection->addPrefix('leaflet/api'); + } + + return $collection; + } } diff --git a/src/Bundle/Resources/config/mappers.yml b/src/Bundle/Resources/config/mappers.yml index 39644e6..e45987c 100644 --- a/src/Bundle/Resources/config/mappers.yml +++ b/src/Bundle/Resources/config/mappers.yml @@ -32,6 +32,7 @@ services: class: Netzmacht\Contao\Leaflet\Mapper\Layer\MarkersLayerMapper arguments: - '@netzmacht.contao_toolkit.repository_manager' + - '@router' tags: - { name: netzmacht.contao_leaflet.mapper } @@ -46,6 +47,7 @@ services: class: Netzmacht\Contao\Leaflet\Mapper\Layer\VectorsLayerMapper arguments: - '@netzmacht.contao_toolkit.repository_manager' + - '@router' tags: - { name: netzmacht.contao_leaflet.mapper } diff --git a/src/Bundle/Resources/config/routing.yml b/src/Bundle/Resources/config/routing.yml new file mode 100644 index 0000000..371ec1a --- /dev/null +++ b/src/Bundle/Resources/config/routing.yml @@ -0,0 +1,11 @@ +leaflet_layer: + path: /layer/{layerId} + controller: Netzmacht\Contao\Leaflet\Frontend\Action\LayerDataAction + defaults: + _leaflet_scope: page + _format: geojson + _scope: frontend + requirements: + _format: geojson + context: \w+ + contextId: \d+ diff --git a/src/Bundle/Resources/config/services.yml b/src/Bundle/Resources/config/services.yml index 2eae139..454f01c 100644 --- a/src/Bundle/Resources/config/services.yml +++ b/src/Bundle/Resources/config/services.yml @@ -122,3 +122,12 @@ services: arguments: - '@netzmacht.contao_leaflet.filter_factory' - '%kernel.debug%' + + Netzmacht\Contao\Leaflet\Frontend\PageIdDeterminator: + tags: + - { name: Netzmacht\Contao\PageContext\Request\PageIdDeterminator } + + Netzmacht\Contao\Leaflet\Frontend\Action\LayerDataAction: + arguments: + - '@netzmacht.contao_leaflet.map.provider' + - '@netzmacht.contao_leaflet.filter_factory' diff --git a/src/Bundle/Resources/public/js/contao-leaflet.js b/src/Bundle/Resources/public/js/contao-leaflet.js index e26d02d..80ce810 100644 --- a/src/Bundle/Resources/public/js/contao-leaflet.js +++ b/src/Bundle/Resources/public/js/contao-leaflet.js @@ -1 +1 @@ -L.Contao=L.Evented.extend({statics:{ATTRIBUTION:' | netzmacht'},maps:{},icons:{},initialize:function(){L.Icon.Default.imagePath="assets/leaflet/libs/leaflet/images/",this.setGeoJsonListeners(L.GeoJSON)},addMap:function(t,o){return this.maps[t]=o,this.fire("map:added",{id:t,map:o}),this},getMap:function(t){return void 0===this.maps[t]?null:this.maps[t]},addIcon:function(t,o){return this.icons[t]=o,this.fire("icon:added",{id:t,icon:o}),this},loadIcons:function(t){for(var o=0;o=200&&t<300||304===t}function i(){void 0===a.status||n(a.status)?o.call(a,null,a):o.call(a,a,null)}var s=!1;if(void 0===window.XMLHttpRequest)return o(Error("Browser not supported"));if(void 0===e){var r=t.match(/^\s*https?:\/\/[^\/]*/);e=r&&r[0]!==location.protocol+"//"+location.hostname+(location.port?":"+location.port:"")}var a=new window.XMLHttpRequest;if(e&&!("withCredentials"in a)){a=new window.XDomainRequest;var p=o;o=function(){if(s)p.apply(this,arguments);else{var t=this,o=arguments;setTimeout(function(){p.apply(t,o)},0)}}}return"onload"in a?a.onload=i:a.onreadystatechange=function(){4===a.readyState&&i()},a.onerror=function(t){o.call(this,t||!0,null),o=function(){}},a.onprogress=function(){},a.ontimeout=function(t){o.call(this,t,null),o=function(){}},a.onabort=function(t){o.call(this,t,null),o=function(){}},a.open("GET",t,!0),a.send(null),s=!0,a}}); \ No newline at end of file +L.Contao=L.Evented.extend({statics:{ATTRIBUTION:' | netzmacht'},maps:{},icons:{},initialize:function(){L.Icon.Default.imagePath="assets/leaflet/libs/leaflet/images/",this.setGeoJsonListeners(L.GeoJSON)},addMap:function(t,o){return this.maps[t]=o,this.fire("map:added",{id:t,map:o}),this},getMap:function(t){return void 0===this.maps[t]?null:this.maps[t]},addIcon:function(t,o){return this.icons[t]=o,this.fire("icon:added",{id:t,icon:o}),this},loadIcons:function(t){for(var o=0;ogetValue(); $encoder = $event->getEncoder(); $template = 'L.contao.%s(%s, %s, %s, %s, map);'; - $method = 'loadFile'; + $method = 'loadUrl'; if ($value instanceof OmnivoreLayer) { $url = $value->getUrl(); diff --git a/src/Frontend/Action/LayerDataAction.php b/src/Frontend/Action/LayerDataAction.php new file mode 100644 index 0000000..da8f94c --- /dev/null +++ b/src/Frontend/Action/LayerDataAction.php @@ -0,0 +1,103 @@ + + * @copyright 2014-2018 netzmacht David Molineus. All rights reserved. + * @license LGPL-3.0 https://github.com/netzmacht/contao-leaflet-maps/blob/master/LICENSE + * @filesource + */ + +declare(strict_types=1); + +namespace Netzmacht\Contao\Leaflet\Frontend\Action; + +use Netzmacht\Contao\Leaflet\Filter\Filter; +use Netzmacht\Contao\Leaflet\Filter\FilterFactory; +use Netzmacht\Contao\Leaflet\MapProvider; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; + +/** + * Request action which handles request for layer data + */ +final class LayerDataAction +{ + /** + * Map provider. + * + * @var MapProvider + */ + private $mapProvider; + + /** + * Filter factory. + * + * @var FilterFactory + */ + private $filterFactory; + + /** + * LayerDataAction constructor. + * + * @param MapProvider $mapProvider Map provider. + * @param FilterFactory $filterFactory Filter factory. + */ + public function __construct(MapProvider $mapProvider, FilterFactory $filterFactory) + { + $this->mapProvider = $mapProvider; + $this->filterFactory = $filterFactory; + } + + /** + * Handle the request. + * + * @param int $layerId The layer id. + * @param string $_format The requested output format. + * @param Request $request The request. + * + * @return Response + * + * @throws BadRequestHttpException When unsupported format is given. + * + * @SuppressWarnings(PHPMD.CamelCaseParameterName) + * @SuppressWarnings(PHPMD.CamelCaseVariableName) + */ + public function __invoke(int $layerId, string $_format, Request $request): Response + { + $filter = $this->createFilter($request); + $data = $this->mapProvider->getFeatureCollection($layerId, $filter); + + if ($_format === 'geojson') { + $response = new JsonResponse($data); + $response->setEncodingOptions(JSON_UNESCAPED_SLASHES); + + return $response; + } + + throw new BadRequestHttpException(sprintf('Unsupported format "%s"', $_format)); + } + + /** + * Create the filter if defined in the request. + * + * @param Request $request The request. + * + * @return Filter|null + */ + private function createFilter(Request $request): ?Filter + { + if (!$request->query->has('filter')) { + return null; + } + + $filter = (string) $request->query->get('filter'); + $values = (string) $request->query->get('values'); + + return $this->filterFactory->create($filter, $values); + } +} diff --git a/src/Frontend/DataController.php b/src/Frontend/DataController.php index c5dd4cc..563b5e6 100644 --- a/src/Frontend/DataController.php +++ b/src/Frontend/DataController.php @@ -12,14 +12,18 @@ namespace Netzmacht\Contao\Leaflet\Frontend; +use const E_USER_DEPRECATED; use Netzmacht\Contao\Leaflet\Filter\Filter; use Netzmacht\Contao\Leaflet\Filter\FilterFactory; use Netzmacht\Contao\Leaflet\MapProvider; +use function trigger_error; /** * The data controller handles ajax request for sub data. * * @package Netzmacht\Contao\Leaflet\Frontend + * + * @deprecated */ class DataController { @@ -60,6 +64,13 @@ class DataController { $this->debugMode = $debugMode; $this->filterFactory = $filterFactory; + + // @codingStandardsIgnoreStart + @trigger_error( + 'Deprecated since 3.1.0 and will be removed in 4.0.0 - Use properly route instead.', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd } /** diff --git a/src/Frontend/PageIdDeterminator.php b/src/Frontend/PageIdDeterminator.php new file mode 100644 index 0000000..ad8807a --- /dev/null +++ b/src/Frontend/PageIdDeterminator.php @@ -0,0 +1,47 @@ + + * @copyright 2014-2018 netzmacht David Molineus. All rights reserved. + * @license LGPL-3.0 https://github.com/netzmacht/contao-leaflet-maps/blob/master/LICENSE + * @filesource + */ + +declare(strict_types=1); + +namespace Netzmacht\Contao\Leaflet\Frontend; + +use Netzmacht\Contao\PageContext\Exception\DeterminePageIdFailed; +use Netzmacht\Contao\PageContext\Request\PageIdDeterminator as PageContextPageIdDeterminator; +use Symfony\Component\HttpFoundation\Request; + +/** + * Class ApiPageIdDeterminator + */ +final class PageIdDeterminator implements PageContextPageIdDeterminator +{ + /** + * {@inheritDoc} + */ + public function match(Request $request): bool + { + return ($request->attributes->get('_leaflet_scope') === 'page' && $request->query->get('context') === 'page'); + } + + /** + * {@inheritDoc} + * + * @throws DeterminePageIdFailed When no context id is given. + */ + public function determinate(Request $request): int + { + if (!$request->query->has('contextId')) { + throw new DeterminePageIdFailed('Could not determine page id for from request.'); + } + + return $request->query->getInt('contextId'); + } +} diff --git a/src/Frontend/RequestUrl.php b/src/Frontend/RequestUrl.php index df7de63..25c393a 100644 --- a/src/Frontend/RequestUrl.php +++ b/src/Frontend/RequestUrl.php @@ -21,6 +21,8 @@ use Netzmacht\Contao\Leaflet\Mapper\Request; * Class RequestUrl creates the request url. * * @package Netzmacht\Contao\Leaflet\Request + * + * @deprecated */ class RequestUrl implements \JsonSerializable { @@ -89,6 +91,13 @@ class RequestUrl implements \JsonSerializable { $this->url = $url; $this->hash = $hash; + + // @codingStandardsIgnoreStart + @trigger_error( + 'Deprecated since 3.1.0 and will be removed in 4.0.0. Use The router instead.', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd } /** diff --git a/src/Mapper/Layer/MarkersLayerMapper.php b/src/Mapper/Layer/MarkersLayerMapper.php index 017e7e8..dc78d7e 100644 --- a/src/Mapper/Layer/MarkersLayerMapper.php +++ b/src/Mapper/Layer/MarkersLayerMapper.php @@ -13,7 +13,6 @@ namespace Netzmacht\Contao\Leaflet\Mapper\Layer; use Contao\Model; -use Netzmacht\Contao\Leaflet\Frontend\RequestUrl; use Netzmacht\Contao\Leaflet\Mapper\DefinitionMapper; use Netzmacht\Contao\Leaflet\Mapper\GeoJsonMapper; use Netzmacht\Contao\Leaflet\Mapper\Request; @@ -24,6 +23,7 @@ use Netzmacht\LeafletPHP\Definition; use Netzmacht\LeafletPHP\Definition\Group\GeoJson; use Netzmacht\LeafletPHP\Plugins\Omnivore\GeoJson as OmnivoreGeoJson; use Netzmacht\LeafletPHP\Value\GeoJson\FeatureCollection; +use Symfony\Component\Routing\RouterInterface; /** * Class MarkersLayerMapper maps the layer model to the markers definition. @@ -46,14 +46,23 @@ class MarkersLayerMapper extends AbstractLayerMapper implements GeoJsonMapper */ private $repositoryManager; + /** + * Router. + * + * @var RouterInterface + */ + private $router; + /** * Construct. * * @param RepositoryManager $repositoryManager Repository manager. + * @param RouterInterface $router Router. */ - public function __construct(RepositoryManager $repositoryManager) + public function __construct(RepositoryManager $repositoryManager, RouterInterface $router) { $this->repositoryManager = $repositoryManager; + $this->router = $router; parent::__construct(); } @@ -93,7 +102,7 @@ class MarkersLayerMapper extends AbstractLayerMapper implements GeoJsonMapper return [ $this->getElementId($model, $elementId), - RequestUrl::create($model->id, null, null, $request), + $this->generateUrl((int) $model->id, $request), [], $layer, ]; @@ -101,7 +110,7 @@ class MarkersLayerMapper extends AbstractLayerMapper implements GeoJsonMapper return [ $this->getElementId($model, $elementId), - RequestUrl::create($model->id, null, null, $request), + $this->generateUrl((int) $model->id, $request), ]; } @@ -182,4 +191,31 @@ class MarkersLayerMapper extends AbstractLayerMapper implements GeoJsonMapper return $repository->findByFilter($model->id); } + + /** + * Generate the data url for a layer. + * + * @param int $layerId The layer id. + * @param Request|null $request The request. + * + * @return string + * + * @SuppressWarnings(PHPMD.Superglobals) + */ + private function generateUrl(int $layerId, ?Request $request = null): string + { + $params = ['layerId' => $layerId]; + + if ($request && ($filter = $request->getFilter())) { + $params['filter'] = $filter::getName(); + $params['values'] = $filter->toRequest(); + } + + if (isset($GLOBALS['objPage'])) { + $params['context'] = 'page'; + $params['contextId'] = $GLOBALS['objPage']->id; + } + + return $this->router->generate('leaflet_layer', $params, RouterInterface::ABSOLUTE_URL); + } } diff --git a/src/Mapper/Layer/VectorsLayerMapper.php b/src/Mapper/Layer/VectorsLayerMapper.php index e5c7c97..2c692a3 100644 --- a/src/Mapper/Layer/VectorsLayerMapper.php +++ b/src/Mapper/Layer/VectorsLayerMapper.php @@ -14,7 +14,6 @@ namespace Netzmacht\Contao\Leaflet\Mapper\Layer; use Contao\Model; use Contao\Model\Collection; -use Netzmacht\Contao\Leaflet\Frontend\RequestUrl; use Netzmacht\Contao\Leaflet\Mapper\DefinitionMapper; use Netzmacht\Contao\Leaflet\Mapper\GeoJsonMapper; use Netzmacht\Contao\Leaflet\Mapper\Request; @@ -25,6 +24,7 @@ use Netzmacht\LeafletPHP\Definition; use Netzmacht\LeafletPHP\Definition\Group\GeoJson; use Netzmacht\LeafletPHP\Plugins\Omnivore\GeoJson as OmnivoreGeoJson; use Netzmacht\LeafletPHP\Value\GeoJson\FeatureCollection; +use Symfony\Component\Routing\RouterInterface; /** * Class VectorsLayerMapper maps the layer model for the Vectors layer definition. @@ -47,14 +47,23 @@ class VectorsLayerMapper extends AbstractLayerMapper implements GeoJsonMapper */ private $repositoryManager; + /** + * Router. + * + * @var RouterInterface + */ + private $router; + /** * Construct. * * @param RepositoryManager $repositoryManager Repository manager. + * @param RouterInterface $router Router */ - public function __construct(RepositoryManager $repositoryManager) + public function __construct(RepositoryManager $repositoryManager, RouterInterface $router) { $this->repositoryManager = $repositoryManager; + $this->router = $router; parent::__construct(); } @@ -101,7 +110,7 @@ class VectorsLayerMapper extends AbstractLayerMapper implements GeoJsonMapper return [ $this->getElementId($model, $elementId), - RequestUrl::create($model->id, null, null, $request), + $this->generateUrl((int) $model->id, $request), [], $layer, ]; @@ -109,7 +118,7 @@ class VectorsLayerMapper extends AbstractLayerMapper implements GeoJsonMapper return [ $this->getElementId($model, $elementId), - RequestUrl::create($model->id, null, null, $request), + $this->generateUrl((int) $model->id, $request), ]; } @@ -202,4 +211,31 @@ class VectorsLayerMapper extends AbstractLayerMapper implements GeoJsonMapper $definition->setOnEachFeature(new Expression($model->onEachFeature)); } } + + /** + * Generate the data url for a layer. + * + * @param int $layerId The layer id. + * @param Request|null $request The request. + * + * @return string + * + * @SuppressWarnings(PHPMD.Superglobals) + */ + private function generateUrl(int $layerId, ?Request $request = null): string + { + $params = ['layerId' => $layerId]; + + if ($request && ($filter = $request->getFilter())) { + $params['filter'] = $filter::getName(); + $params['values'] = $filter->toRequest(); + } + + if (isset($GLOBALS['objPage'])) { + $params['context'] = 'page'; + $params['contextId'] = $GLOBALS['objPage']->id; + } + + return $this->router->generate('leaflet_layer', $params, RouterInterface::ABSOLUTE_URL); + } }