Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: map widget #480

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
/Data/
/Web/_Resources
/Web/.htaccess
/Web/index.php
/bin/
/Packages
/flow
Expand Down
1 change: 1 addition & 0 deletions .localbeach.docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ services:
- CROWD_API_USERNAME=${CROWD_API_USERNAME}
- CROWD_API_PASSWORD=${CROWD_API_PASSWORD}
- GITHUB_API_TOKEN=${GITHUB_API_TOKEN}
- SANDSTORM_MAPS_API_KEY=${SANDSTORM_MAPS_API_KEY}

redis:
image: ${BEACH_REDIS_IMAGE:-flownative/redis}:${BEACH_REDIS_IMAGE_VERSION:-latest}
Expand Down
51 changes: 51 additions & 0 deletions DistributionPackages/Neos.NeosIo/NodeTypes/Content/Map.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'Neos.NeosIo:Content.Map':
superTypes:
'Neos.Neos:Content': true
ui:
label: 'Map'
icon: icon-map
inspector:
groups:
general:
label: i18n
properties:
label: 'Properties'
tab: default

properties:

lat:
type: string
defaultValue: '51.0567032'
ui:
label: 'Latitude'
reloadIfChanged: true
inspector:
group: properties

lng:
type: string
defaultValue: '13.7769583'
ui:
label: 'Longitude'
reloadIfChanged: true
inspector:
group: properties

zoom:
type: string
defaultValue: '14'
ui:
label: 'Default zoom level'
reloadIfChanged: true
inspector:
group: properties

popupText:
type: string
defaultValue: ''
ui:
label: 'Popup-Text'
reloadIfChanged: true
inspector:
group: properties
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
'Neos.NeosIo.FeatureList:FeatureList': true
'Neos.NeosIo.CaseStudies:Content.CaseList': true
'Neos.NeosIo:PostArchive': true
'Neos.NeosIo:Content.Map': true
ui:
label: Stage
icon: icon-tasks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
'Neos.NeosIo:Youtube': TRUE
'Neos.NeosIo:VideoEmbed': TRUE
'PunktDe.CodeView:Code': true
'Neos.NeosIo:Content.Map': TRUE
column1:
constraints: *twoColumnConstraints

Expand Down Expand Up @@ -116,6 +117,7 @@
'Neos.NeosIo:Icon': TRUE
'Neos.NeosIo:Youtube': TRUE
'Neos.NeosIo:VideoEmbed': TRUE
'Neos.NeosIo:Content.Map': TRUE
column1:
constraints: *threeColumnConstraints
column2:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
prototype(Neos.NeosIo:Content.Map) < prototype(Neos.Neos:ContentComponent) {
renderer = Neos.NeosIo:Map {
lat = ${q(node).property('lat')}
lng = ${q(node).property('lng')}
zoom = ${q(node).property('zoom')}
popupText = ${q(node).property('popupText')}
}
}

prototype(Neos.NeosIo:Map) < prototype(Neos.Fusion:Component) {
lat = 0
[email protected] = ${String.toFloat(value)}
lng = 0
[email protected] = ${String.toFloat(value)}
zoom = 14
[email protected] = ${String.toFloat(value)}

popupText = ''

@propTypes {
@strict = true
lat = ${PropTypes.float.isRequired}
lng = ${PropTypes.float.isRequired}
zoom = ${PropTypes.float.isRequired}
popupText = ${PropTypes.string}
}

renderer = afx`
<link rel="stylesheet" href="/_maptiles/frontend/v1/map-main.css" />
<div
data-component="Map"
style="height: 500px"
data-lat={props.lat}
data-lng={props.lng}
data-zoom={props.zoom}
data-popupText={props.popupText}
></div>
`
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ prototype(Neos.NeosIo:DefaultPage) < prototype(Neos.Neos:Page) {
}

javascripts.site = afx`
<script @children="attributes.src" async="">
<script @children="attributes.src" async="" type="module">
<Neos.Fusion:ResourceUri path="resource://Neos.NeosIo/Public/Scripts/Main.js"/>
</script>
`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import BaseComponent from "DistributionPackages/Neos.NeosIo/Resources/Private/JavaScript/Components/BaseComponent";
import inViewport from "in-viewport";

class Map extends BaseComponent {
constructor(el) {
super(el);
const isAlreadyVisible = inViewport(el);

//
// Now let's check initially for the visibility in the viewport.
//
if (isAlreadyVisible) {
this.loadMap();
} else {
inViewport(
el,
{
offset: 300
},
() => this.loadMap()
);
}
}

loadMap() {
import('/_maptiles/frontend/v1.1/map-main.js')
.then(async ({maplibregl, createMap}) => {
let map = await createMap(window.location.protocol + '//' + window.location.host + '/_maptiles', {
container: this.el, // HTML Element
center: [this.lng, this.lat], // starting position [lng, lat]
zoom: this.zoom, // starting zoom
});

map.addControl(new maplibregl.NavigationControl(), 'top-left');

new maplibregl.Marker()
.setLngLat([this.lng, this.lat])
.setPopup(new maplibregl.Popup({offset: 25}).setText(this.popupText))
.addTo(map);
});
}
}

Map.prototype.props = {
lat: '',
lng: '',
zoom: '',
popupText: '',
}

export default Map;
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ScrollTo from './ScrollTo';
import ProgressiveImage from './ProgressiveImage';
import ScrollClassToggler from './ScrollClassToggler';
import SentenceSwitcher from './SentenceSwitcher';
import Map from './Map';

export {
ClassToggler,
Expand All @@ -16,5 +17,6 @@ export {
SentenceSwitcher,
ProgressiveImage,
ScrollClassToggler,
EmptyClickHandler
EmptyClickHandler,
Map
};
104 changes: 104 additions & 0 deletions Web/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php
declare(strict_types=1);

/*
* This file is part of the Neos.Flow package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*
* The file has been amended with handling of requests to the
* sandstorm maps API to cache requests via a "proxy".
*/

use Neos\Cache\Backend\FileBackend;
use Neos\Cache\EnvironmentConfiguration;
use Neos\Cache\Frontend\StringFrontend;
use Neos\Flow\Core\Bootstrap;

const MAPS_CACHE_LIFETIME = 24 * 60 * 60; // 24 hours
const MAPS_BASE_URI = 'https://maps-api.sandstorm.de/';

$rootPath = $_SERVER['FLOW_ROOTPATH'] ?? false;
if ($rootPath === false && isset($_SERVER['REDIRECT_FLOW_ROOTPATH'])) {
$rootPath = $_SERVER['REDIRECT_FLOW_ROOTPATH'];
}
if ($rootPath === false) {
$rootPath = __DIR__ . '/../';
} elseif (substr($rootPath, -1) !== '/') {
$rootPath .= '/';
}

$composerAutoloader = require($rootPath . 'Packages/Libraries/autoload.php');

if ($_SERVER['REQUEST_METHOD'] === 'GET' && strpos($_SERVER['REQUEST_URI'], '/_maptiles/') === 0) {
proxyMap($_ENV['SANDSTORM_MAPS_API_KEY'] ?: 'unset');
exit();
}

$context = Bootstrap::getEnvironmentConfigurationSetting('FLOW_CONTEXT') ?: 'Development';
$bootstrap = new Bootstrap($context, $composerAutoloader);
$bootstrap->run();

/**
* Proxy requests to sandstorm map server
*/
function proxyMap(string $apiKey): void
{
$environment = new EnvironmentConfiguration('maptiles-proxy', '/tmp/maptiles/');
$backend = new FileBackend($environment);
$cache = new StringFrontend('maptiles', $backend);
$backend->setCache($cache);

// identify request headers
$requestHeaders = [];
foreach ($_SERVER as $key => $value) {
if (strpos($key, 'HTTP_') === 0) {
$headerName = str_replace('_', ' ', substr($key, 5));
$headerName = str_replace(' ', '-', ucwords(strtolower($headerName)));
if (in_array($headerName, ['Accept-Encoding', 'Accept-Language', 'Accept',])) {
$requestHeaders[] = "$headerName: $value";
}
}
}

// identify path from url
preg_match('/^\/_maptiles\/(?P<path>.*)$/', $_SERVER['REQUEST_URI'], $matches);
if ($matches['path']) {
$requestUrl = MAPS_BASE_URI . $matches['path'] . '?t=' . $apiKey;
} else {
header('HTTP/1.1 404');
die();
}

$cacheIdentifier = md5($requestUrl . implode('', $requestHeaders));
$response = $cache->get($cacheIdentifier);

if (!$response) {
$curlHandle = curl_init($requestUrl);
curl_setopt($curlHandle, CURLOPT_HTTPHEADER, $requestHeaders); // (re-)send headers
curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true); // return response
curl_setopt($curlHandle, CURLOPT_HEADER, true); // enabled response headers
$response = curl_exec($curlHandle);
curl_close($curlHandle);

$cache->set($cacheIdentifier, $response, [], MAPS_CACHE_LIFETIME);
}

// split response to header and content
[$responseHeaders, $responseContent] = preg_split('/(\r\n){2}/', $response, 2);

// (re-)send the headers
$responseHeaders = preg_split('/\r\n/', $responseHeaders);
foreach ($responseHeaders as $responseHeader) {
if (strpos($responseHeader, 'Transfer-Encoding:') !== 0) {
header($responseHeader, false);
}
}

// finally, output the content
echo $responseContent;
}
10 changes: 9 additions & 1 deletion webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,15 @@ function config(
extensions: ['*', '.js', '.jsx', '.ts', '.tsx', '.scss'],
// absolute paths for JS and SCSS related files
alias: alias
}
},
target: 'es11', // required for dynamic imports
externals: {
'/_maptiles/frontend/v1.1/map-main.js': '/_maptiles/frontend/v1.1/map-main.js',
},
externalsType: 'module',
experiments: {
outputModule: true, // required for externalsType: 'module'
},
};
}

Expand Down