Initial commit - Typo3 11.5.41
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2023 Alexander Bigga <alexander@bigga.de>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project is
|
||||
* free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
namespace Bobosch\OdsOsm\Backend\FormDataProvider;
|
||||
|
||||
/**
|
||||
* This class is inspired by the FlexFormManipulation::class of the
|
||||
* news extension by Georg Ringer
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Backend\Form\FormDataProviderInterface;
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
|
||||
/**
|
||||
* Manipulate flexforms of ods_osm_pi to ensure only available
|
||||
* tables are allowed to select records.
|
||||
*/
|
||||
class FlexFormManipulation implements FormDataProviderInterface
|
||||
{
|
||||
/**
|
||||
* Manipulate data of ods_osm_pi plugin.
|
||||
*
|
||||
* @param array $result
|
||||
* @return array
|
||||
*/
|
||||
public function addData(array $result): array
|
||||
{
|
||||
if ($result['tableName'] === 'tt_content'
|
||||
&& $result['databaseRow']['CType'] === 'list'
|
||||
&& $result['databaseRow']['list_type'] === 'ods_osm_pi1'
|
||||
&& is_array($result['processedTca']['columns']['pi_flexform']['config']['ds'])
|
||||
) {
|
||||
// get flexform data structure
|
||||
$dataStructure = $result['processedTca']['columns']['pi_flexform']['config']['ds'];
|
||||
|
||||
// check / manipulate data structure
|
||||
$dataStructure = $this->fixAllowedTables($dataStructure);
|
||||
|
||||
// set checked data structure
|
||||
$result['processedTca']['columns']['pi_flexform']['config']['ds'] = $dataStructure;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for optionally installed extensions and add the
|
||||
* corresponding tables to the list of allowed tables for marker.
|
||||
*
|
||||
* @param array &$dataStructure flexform structure
|
||||
* @return array Modified structure
|
||||
*/
|
||||
protected function fixAllowedTables(array $dataStructure): array
|
||||
{
|
||||
$markerAllowedTables = [ 'fe_users', 'fe_groups', 'pages', 'sys_category', 'tx_odsosm_track', 'tx_odsosm_vector' ];
|
||||
$markerPopupInitialAllowedTables = [ 'fe_users' ];
|
||||
|
||||
$extensions = [
|
||||
'tt_address' => 'tt_address',
|
||||
'calendarize' => 'tx_calendarize_domain_model_event'
|
||||
];
|
||||
|
||||
foreach ($extensions as $extension => $table) {
|
||||
if (ExtensionManagementUtility::isLoaded($extension)) {
|
||||
$markerAllowedTables[] = $table;
|
||||
$markerPopupInitialAllowedTables[] = $table;
|
||||
}
|
||||
}
|
||||
|
||||
// set manipulated value for marker -> config -> allowed
|
||||
if ($dataStructure['sheets']['sDEF']['ROOT']['el']['marker']['config']['allowed'] ?? false) {
|
||||
$dataStructure['sheets']['sDEF']['ROOT']['el']['marker']['config']['allowed'] = implode(',', $markerAllowedTables);
|
||||
}
|
||||
|
||||
// set manipulated value for marker_popup_initial -> config -> allowed
|
||||
if ($dataStructure['sheets']['sDEF']['ROOT']['el']['marker_popup_initial']['config']['allowed'] ?? false) {
|
||||
$dataStructure['sheets']['sDEF']['ROOT']['el']['marker_popup_initial']['config']['allowed'] = implode(',', $markerPopupInitialAllowedTables);
|
||||
}
|
||||
|
||||
return $dataStructure;
|
||||
}
|
||||
|
||||
}
|
||||
663
typo3conf/ext/ods_osm/Classes/Controller/PluginController.php
Normal file
663
typo3conf/ext/ods_osm/Classes/Controller/PluginController.php
Normal file
@@ -0,0 +1,663 @@
|
||||
<?php
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010 Robert Heel <typo3@bobosch.de>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project is
|
||||
* free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
namespace Bobosch\OdsOsm\Controller;
|
||||
|
||||
use Bobosch\OdsOsm\Div;
|
||||
use Bobosch\OdsOsm\Provider\BaseProvider;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Page\PageRenderer;
|
||||
use TYPO3\CMS\Core\Resource\FileRepository;
|
||||
use TYPO3\CMS\Core\Utility\ArrayUtility;
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
use TYPO3\CMS\Frontend\Plugin\AbstractPlugin;
|
||||
|
||||
/**
|
||||
* Plugin 'OpenStreetMap' for the 'ods_osm' extension.
|
||||
*
|
||||
* @author Robert Heel <typo3@bobosch.de>
|
||||
* @package TYPO3
|
||||
* @subpackage tx_odsosm
|
||||
*/
|
||||
class PluginController extends AbstractPlugin
|
||||
{
|
||||
/**
|
||||
* Same as class name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $prefixId = 'tx_odsosm_pi1';
|
||||
|
||||
/**
|
||||
* The extension key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $extKey = 'ods_osm';
|
||||
|
||||
public $config;
|
||||
public $hooks;
|
||||
public $lats = [];
|
||||
public $lons = [];
|
||||
/** @var ConnectionPool */
|
||||
public $connectionPool = null;
|
||||
/** @var BaseProvider */
|
||||
protected $library;
|
||||
|
||||
public function init($conf): void
|
||||
{
|
||||
$this->pi_setPiVarDefaults();
|
||||
$this->pi_loadLL();
|
||||
$this->pi_initPIflexForm(); // Init FlexForm configuration for plugin
|
||||
|
||||
$this->hooks = [];
|
||||
if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ods_osm']['class.tx_odsosm_pi1.php'] ?? null)) {
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ods_osm']['class.tx_odsosm_pi1.php'] as $classRef) {
|
||||
$this->hooks[] = GeneralUtility::makeInstance($classRef);
|
||||
}
|
||||
}
|
||||
|
||||
$this->connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
|
||||
|
||||
/* --------------------------------------------------
|
||||
Configuration may be done in different places
|
||||
- FlexForm ($flex)
|
||||
- TypoScript ($conf)
|
||||
- extension settings
|
||||
-------------------------------------------------- */
|
||||
|
||||
$flex = [];
|
||||
$options = [
|
||||
'cluster',
|
||||
'cluster_radius',
|
||||
'height',
|
||||
'lat',
|
||||
'layer',
|
||||
'leaflet_layer',
|
||||
'library',
|
||||
'lon',
|
||||
'marker',
|
||||
'marker_popup_initial',
|
||||
'mouse_position',
|
||||
'openlayers_layer',
|
||||
'base_layer',
|
||||
'overlays',
|
||||
'overlays_active',
|
||||
'position',
|
||||
'show_layerswitcher',
|
||||
'layerswitcher_activationMode',
|
||||
'show_scalebar',
|
||||
'show_fullscreen',
|
||||
'show_popups',
|
||||
'staticmap_layer',
|
||||
'use_coords_only_nomarker',
|
||||
'width',
|
||||
'zoom',
|
||||
'enable_scrollwheelzoom',
|
||||
'enable_dragging',
|
||||
];
|
||||
// fill flex array, if there is a flexform data available
|
||||
if ($this->cObj->data['pi_flexform'] ?? null) {
|
||||
foreach ($options as $option) {
|
||||
$value = $this->pi_getFFvalue($this->cObj->data['pi_flexform'] ?? null, $option, 'sDEF');
|
||||
switch ($option) {
|
||||
case 'lat':
|
||||
case 'lon':
|
||||
if ($value != 0) {
|
||||
$flex[$option] = $value;
|
||||
}
|
||||
break;
|
||||
case 'marker':
|
||||
case 'marker_popup_initial':
|
||||
$flex[$option] = $this->splitGroup($value, 'tt_address');
|
||||
break;
|
||||
default:
|
||||
$flex[$option] = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($flex['library'] == 'staticmap' && !empty($flex['staticmap_layer'])) {
|
||||
$flex['layer'] = $flex['staticmap_layer'];
|
||||
} elseif (!empty($flex['base_layer'])) {
|
||||
$flex['layer'] = $flex['base_layer'];
|
||||
}
|
||||
}
|
||||
|
||||
// merge configs together into $this->config
|
||||
// 1. get extension configuration
|
||||
$this->config = Div::getConfig();
|
||||
// 2. get TypoScript settings
|
||||
ArrayUtility::mergeRecursiveWithOverrule($this->config, $conf);
|
||||
// 3. merge Flexform settings, but skip empty values.
|
||||
ArrayUtility::mergeRecursiveWithOverrule($this->config, $flex, true, false);
|
||||
|
||||
if (!is_array($this->config['marker'] ?? null)) {
|
||||
$this->config['marker'] = [];
|
||||
}
|
||||
if (is_array($conf['marker.'])) {
|
||||
foreach ($conf['marker.'] as $name => $value) {
|
||||
if (is_string($value) && !empty($value)) {
|
||||
if (!is_array($this->config['marker'][$name] ?? null)) {
|
||||
$this->config['marker'][$name] = [];
|
||||
}
|
||||
$this->config['marker'][$name] = array_merge($this->config['marker'][$name], explode(',', $value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->config['layer'] = explode(',', $this->config['layer'] . (!empty($this->config['overlays']) ? ',' . $this->config['overlays'] : ''));
|
||||
|
||||
if (is_numeric($this->config['height'])) {
|
||||
$this->config['height'] .= 'px';
|
||||
}
|
||||
if (is_numeric($this->config['width'])) {
|
||||
$this->config['width'] .= 'px';
|
||||
}
|
||||
|
||||
if ($this->config['show_layerswitcher'] ?? false) {
|
||||
$this->config['layers_visible'] = [];
|
||||
} else {
|
||||
$this->config['layers_visible'] = $this->config['layer'];
|
||||
}
|
||||
|
||||
if ($this->config['external_control'] ?? false) {
|
||||
if (GeneralUtility::_GP('lon')) {
|
||||
$this->config['lon'] = GeneralUtility::_GP('lon');
|
||||
}
|
||||
if (GeneralUtility::_GP('lat')) {
|
||||
$this->config['lat'] = GeneralUtility::_GP('lat');
|
||||
}
|
||||
if (GeneralUtility::_GP('zoom')) {
|
||||
$this->config['zoom'] = GeneralUtility::_GP('zoom');
|
||||
}
|
||||
if (GeneralUtility::_GP('layers')) {
|
||||
$this->config['layers_visible'] = explode(',', GeneralUtility::_GP('layers'));
|
||||
}
|
||||
if (GeneralUtility::_GP('records')) {
|
||||
$this->config['marker'] = $this->splitGroup(GeneralUtility::_GP('records'), 'tt_address');
|
||||
}
|
||||
}
|
||||
|
||||
// If EXT:calendarize is installed and the single view is called, we try to fetch the right event.
|
||||
if (ExtensionManagementUtility::isLoaded('calendarize')) {
|
||||
if (GeneralUtility::_GP('tx_calendarize_calendar')['index'] ?? false) {
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('tx_calendarize_domain_model_index');
|
||||
|
||||
$result = $queryBuilder
|
||||
->select('foreign_uid')
|
||||
->from('tx_calendarize_domain_model_index')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq(
|
||||
'tx_calendarize_domain_model_index.uid',
|
||||
$queryBuilder->createNamedParameter((int) GeneralUtility::_GP('tx_calendarize_calendar')['index'], Connection::PARAM_INT)
|
||||
)
|
||||
)
|
||||
->setMaxResults(1)
|
||||
->executeQuery();
|
||||
|
||||
if ($row = $result->fetchAssociative()) {
|
||||
$this->config['marker']['tx_calendarize_domain_model_event'][] = $row['foreign_uid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->config['id'] = 'osm_' . ($this->cObj->data['uid'] ?? uniqid());
|
||||
|
||||
$this->config['marker'] = $this->extractGroup($this->config['marker']);
|
||||
|
||||
// Show this marker's popup intially
|
||||
if (is_array($this->config['marker_popup_initial'])) {
|
||||
foreach ($this->config['marker_popup_initial'] as $table => $records) {
|
||||
foreach ($records as $uid) {
|
||||
if (isset($this->config['marker'][$table][$uid])) {
|
||||
$this->config['marker'][$table][$uid]['initial_popup'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Library
|
||||
if (empty($this->config['library'])) {
|
||||
$this->config['library'] = 'leaflet';
|
||||
}
|
||||
$this->library = GeneralUtility::makeInstance('Bobosch\\OdsOsm\\Provider\\' . GeneralUtility::underscoredToUpperCamelCase($this->config['library']));
|
||||
$this->library->init($this->config);
|
||||
$this->library->cObj = $this->cObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main method of the PlugIn
|
||||
*
|
||||
* @param string $content : The PlugIn content
|
||||
* @param array $conf : The PlugIn configuration
|
||||
*
|
||||
* @return string The content that is displayed on the website
|
||||
*/
|
||||
public function main($content, $conf)
|
||||
{
|
||||
$this->init($conf);
|
||||
|
||||
if ($this->config['marker'] || $this->config['no_marker']) {
|
||||
$content = $this->getMap();
|
||||
}
|
||||
|
||||
return $this->pi_wrapInBaseClass($content);
|
||||
}
|
||||
|
||||
public function splitGroup($group, $default = '')
|
||||
{
|
||||
$groups = explode(',', $group);
|
||||
foreach ($groups as $group) {
|
||||
$item = GeneralUtility::revExplode('_', $group, 2);
|
||||
if (count($item) == 1) {
|
||||
$record_ids[$default][] = $item[0];
|
||||
} else {
|
||||
$record_ids[$item[0]][] = $item[1];
|
||||
}
|
||||
}
|
||||
|
||||
return ($record_ids);
|
||||
}
|
||||
|
||||
private function extractGroup($record_ids)
|
||||
{
|
||||
$tables = Div::getTableConfig();
|
||||
|
||||
// if no markers are set, select current page to find records on it
|
||||
if (count($record_ids) == 0) {
|
||||
//@extensionScannerIgnoreLine
|
||||
$record_ids['pages'] = [$GLOBALS['TSFE']->id];
|
||||
}
|
||||
|
||||
// get all marker records on configured page.
|
||||
if (!empty($record_ids['pages'])) {
|
||||
foreach (array_keys($tables) as $table) {
|
||||
if ($table != 'tt_content') {
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable($table);
|
||||
|
||||
$result = $queryBuilder
|
||||
->select($table . '.uid')
|
||||
->from($table)->where($queryBuilder->expr()->in(
|
||||
$table . '.pid',
|
||||
$queryBuilder->createNamedParameter(
|
||||
$record_ids['pages'],
|
||||
Connection::PARAM_INT_ARRAY
|
||||
)
|
||||
))->executeQuery();
|
||||
|
||||
while ($resArray = $result->fetchAssociative()) {
|
||||
if (!in_array($resArray['uid'], $record_ids[$table] ?? [])) {
|
||||
$record_ids[$table][] = $resArray['uid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get marker records from db
|
||||
$records = [];
|
||||
foreach ($record_ids as $table => $items) {
|
||||
$tc = $tables[$table] ?? [];
|
||||
$connection = $this->connectionPool
|
||||
->getConnectionForTable($table)
|
||||
->createSchemaManager()
|
||||
->tablesExist([$table]);
|
||||
|
||||
// table seems not available --> break to avoid exceptions
|
||||
if ($connection === false) {
|
||||
break;
|
||||
}
|
||||
foreach ($items as $item) {
|
||||
$item = (int) $item;
|
||||
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable($table);
|
||||
|
||||
// hint: language overlay e.g. of tt_address records is done automatically
|
||||
$result = $queryBuilder
|
||||
->select('*')
|
||||
->from($table)
|
||||
->where(
|
||||
$queryBuilder->expr()->eq(
|
||||
$table . '.uid',
|
||||
$queryBuilder->createNamedParameter((int) $item, Connection::PARAM_INT)
|
||||
)
|
||||
)->setMaxResults(1)->executeQuery();
|
||||
|
||||
if ($row = $result->fetchAssociative()) {
|
||||
// Group with relation to a field
|
||||
if (is_array($tc['FIND_IN_SET'] ?? null)) {
|
||||
foreach ($tc['FIND_IN_SET'] as $t => $f) {
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable($table);
|
||||
|
||||
$result = $queryBuilder
|
||||
->select('*')
|
||||
->from($t)
|
||||
->where(
|
||||
$queryBuilder->expr()->inSet(
|
||||
$f,
|
||||
$queryBuilder->createNamedParameter(
|
||||
(int) $item,
|
||||
Connection::PARAM_INT
|
||||
)
|
||||
)
|
||||
)->executeQuery();
|
||||
|
||||
while ($resArray = $result->fetchAssociative()) {
|
||||
$records[$t][$resArray['uid']] = $resArray;
|
||||
$records[$t][$resArray['uid']]['group_uid'] = $table . '_' . $row['uid'];
|
||||
$records[$t][$resArray['uid']]['group_title'] = $row['title'];
|
||||
$records[$t][$resArray['uid']]['group_description'] = $row['description'];
|
||||
$records[$t][$resArray['uid']]['tx_odsosm_marker'] = $row['tx_odsosm_marker'];
|
||||
$records[$t][$resArray['uid']]['longitude'] = $resArray[$tables[$t]['lon']];
|
||||
$records[$t][$resArray['uid']]['latitude'] = $resArray[$tables[$t]['lat']];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Group with mm relation
|
||||
if (is_array($tc['MM'] ?? null)) {
|
||||
foreach ($tc['MM'] as $t => $f) {
|
||||
$local = $f['local'];
|
||||
$mm = $f['mm'];
|
||||
$foreign = $f['foreign'];
|
||||
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($foreign);
|
||||
$constraints = Div::getConstraintsForQueryBuilder($foreign, $this->cObj, $queryBuilder);
|
||||
|
||||
// set uid
|
||||
$constraints[] = $queryBuilder->expr()->eq($local . '.uid', $queryBuilder->createNamedParameter($item, Connection::PARAM_INT));
|
||||
|
||||
$rows = $queryBuilder
|
||||
->select($foreign . '.*')
|
||||
->from($foreign)
|
||||
->join(
|
||||
$foreign,
|
||||
$mm,
|
||||
$mm,
|
||||
$queryBuilder->expr()->eq($foreign . '.uid', $queryBuilder->quoteIdentifier($mm . '.uid_foreign'))
|
||||
)
|
||||
->join(
|
||||
$mm,
|
||||
$local,
|
||||
$local,
|
||||
$queryBuilder->expr()->eq($local . '.uid', $queryBuilder->quoteIdentifier($mm . '.uid_local'))
|
||||
)
|
||||
->where(...$constraints)
|
||||
->executeQuery()
|
||||
->fetchAllAssociative();
|
||||
|
||||
foreach ($rows as $r) {
|
||||
$records[$t][$r['uid']] = $r;
|
||||
$records[$t][$r['uid']]['group_uid'] = $table . '_' . $row['uid'];
|
||||
$records[$t][$r['uid']]['group_title'] = $row['title'];
|
||||
$records[$t][$r['uid']]['group_description'] = $row['description'];
|
||||
$records[$t][$r['uid']]['tx_odsosm_marker'] = $row['tx_odsosm_marker'];
|
||||
$records[$t][$r['uid']]['longitude'] = $r[$tables[$t]['lon']];
|
||||
$records[$t][$r['uid']]['latitude'] = $r[$tables[$t]['lat']];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Marker
|
||||
if (isset($tc['lon'])) {
|
||||
$records[$table][$item] = $row;
|
||||
$records[$table][$item]['longitude'] = $row[$tc['lon']];
|
||||
$records[$table][$item]['latitude'] = $row[$tc['lat']];
|
||||
}
|
||||
|
||||
// Special element
|
||||
if ($tc === true && $row) {
|
||||
$records[$table][$item] = $row;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hook to change records
|
||||
foreach ($this->hooks as $hook) {
|
||||
if (method_exists($hook, 'changeRecords')) {
|
||||
$hook->changeRecords($records, $record_ids, $this);
|
||||
}
|
||||
}
|
||||
|
||||
// get lon & lat
|
||||
foreach ($records as $table => $items) {
|
||||
foreach ($items as $uid => $row) {
|
||||
switch ($table) {
|
||||
case 'tx_odsosm_track':
|
||||
case 'tx_odsosm_vector':
|
||||
if ($row['min_lon']) {
|
||||
$this->lons[] = (float) $row['min_lon'];
|
||||
$this->lats[] = (float) $row['min_lat'];
|
||||
$this->lons[] = (float) $row['max_lon'];
|
||||
$this->lats[] = (float) $row['max_lat'];
|
||||
} else {
|
||||
unset($records[$table][$uid]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$this->lons[] = (float) $row['longitude'];
|
||||
$this->lats[] = (float) $row['latitude'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No markers
|
||||
if (count($this->lons) == 0) {
|
||||
if ($this->config['no_marker'] == 1) {
|
||||
if (($this->config['lon'] ?? false) && ($this->config['lat'] ?? false)) {
|
||||
$this->lons[] = $this->config['lon'];
|
||||
$this->lats[] = $this->config['lat'];
|
||||
} else {
|
||||
$this->lons[] = $this->config['default_lon'];
|
||||
$this->lats[] = $this->config['default_lat'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ($records);
|
||||
}
|
||||
|
||||
public function getMap()
|
||||
{
|
||||
/* ==================================================
|
||||
Marker
|
||||
================================================== */
|
||||
// Get icon records
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('tx_odsosm_marker');
|
||||
|
||||
$result = $queryBuilder
|
||||
->select('*')
|
||||
->from('tx_odsosm_marker')->where(1)->executeQuery();
|
||||
|
||||
$icons = [];
|
||||
while ($resArray = $result->fetchAssociative()) {
|
||||
$icons[$resArray['uid']] = $resArray;
|
||||
}
|
||||
|
||||
// Prepare markers
|
||||
$markers = $this->config['marker'];
|
||||
$local_cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
||||
foreach ($markers as $table => &$items) {
|
||||
foreach ($items as $key => &$item) {
|
||||
$popup = is_string($this->config['popup.'][$table] ?? null) && is_array($this->config['popup.'][$table . '.'] ?? null) && $this->config['show_popups'];
|
||||
$icon = is_string($this->config['icon.'][$table] ?? null) && is_array($this->config['icon.'][$table . '.'] ?? null);
|
||||
if ($popup || $icon) {
|
||||
$local_cObj->start($item, $table);
|
||||
}
|
||||
|
||||
// Add popup information
|
||||
if ($popup) {
|
||||
$item['popup'] = $local_cObj->cObjGetSingle($this->config['popup.'][$table], $this->config['popup.'][$table . '.']);
|
||||
}
|
||||
|
||||
// Add icon information
|
||||
if ($item['tx_odsosm_marker'] ?? null) {
|
||||
$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
|
||||
$fileObjects = $fileRepository->findByRelation('tx_odsosm_marker', 'icon', $item['tx_odsosm_marker']);
|
||||
if ($fileObjects) {
|
||||
$file = $fileObjects[0];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
$item['tx_odsosm_marker'] = $icons[$item['tx_odsosm_marker']];
|
||||
$item['tx_odsosm_marker']['icon'] = $file;
|
||||
$item['tx_odsosm_marker']['type'] = 'image';
|
||||
} elseif ($icon) {
|
||||
if ($this->config['icon.'][$table] == 'IMAGE') {
|
||||
$info = $this->cObj->getImgResource(
|
||||
$this->config['icon.'][$table . '.']['file'] ?? '',
|
||||
$this->config['icon.'][$table . '.']['file.'] ?? []
|
||||
);
|
||||
$item['tx_odsosm_marker'] = [
|
||||
'icon' => $info['processedFile'],
|
||||
'type' => 'image',
|
||||
'size_x' => $info[0],
|
||||
'size_y' => $info[1],
|
||||
'offset_x' => -$info[0] / 2,
|
||||
'offset_y' => -$info[1],
|
||||
];
|
||||
} elseif ($this->config['icon.'][$table] == 'TEXT') {
|
||||
$conf = $this->config['icon.'][$table . '.'];
|
||||
$html = $local_cObj->cObjGetSingle(
|
||||
$this->config['icon.'][$table],
|
||||
$this->config['icon.'][$table . '.']
|
||||
);
|
||||
$item['tx_odsosm_marker'] = [
|
||||
'icon' => $html,
|
||||
'type' => 'html',
|
||||
'size_x' => $conf['size_x'],
|
||||
'size_y' => $conf['size_y'],
|
||||
'offset_x' => $conf['offset_x'],
|
||||
'offset_y' => $conf['offset_y'],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ==================================================
|
||||
Layers
|
||||
================================================== */
|
||||
$layers = [];
|
||||
$baselayers = [];
|
||||
if (!empty(implode(',', $this->config['layer']))) {
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('tx_odsosm_layer');
|
||||
|
||||
$result = $queryBuilder
|
||||
->select('*')
|
||||
->from('tx_odsosm_layer')
|
||||
->where(
|
||||
$queryBuilder->expr()->in(
|
||||
'tx_odsosm_layer.uid',
|
||||
$queryBuilder->createNamedParameter(
|
||||
$this->config['layer'],
|
||||
Connection::PARAM_INT_ARRAY
|
||||
)
|
||||
),
|
||||
)
|
||||
->add('orderBy', 'FIELD(uid, ' . implode(',', $this->config['layer']) . ')', true)
|
||||
->executeQuery();
|
||||
|
||||
while ($resArray = $result->fetchAssociative()) {
|
||||
$baselayers[$resArray['uid']] = $resArray;
|
||||
$baselayers[$resArray['uid']]['visible'] = false;
|
||||
}
|
||||
|
||||
// set visible flag
|
||||
if (isset($this->config['layers_visible'])) {
|
||||
foreach ($this->config['layers_visible'] as $key) {
|
||||
if ($baselayers[$key] ?? false) {
|
||||
$baselayers[$key]['visible'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($this->config['overlays_active'])) {
|
||||
foreach (explode(',', $this->config['overlays_active']) as $key) {
|
||||
if ($baselayers[$key] ?? false) {
|
||||
$baselayers[$key]['visible'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($baselayers as $uid => $layer) {
|
||||
if ($layer['overlay'] == 1) {
|
||||
$layers[1][] = $layer;
|
||||
} else {
|
||||
$layers[0][] = $layer;
|
||||
}
|
||||
}
|
||||
// $layers[0] = $baselayers;
|
||||
// $layers[1] = $overlays;
|
||||
$layers[2] = []; // markers will be filled in provider classes
|
||||
|
||||
/* ==================================================
|
||||
Map center
|
||||
================================================== */
|
||||
if ($this->config['lon'] ?? false || $this->config['use_coords_only_nomarker'] ?? false) {
|
||||
$lon = array_sum($this->lons) / count($this->lons);
|
||||
$lat = array_sum($this->lats) / count($this->lats);
|
||||
} else {
|
||||
$lon = (float)($this->config['lon'] ?? $this->config['default_lon']);
|
||||
$lat = (float)($this->config['lat'] ?? $this->config['default_lat']);
|
||||
}
|
||||
$zoom = (int)$this->config['zoom'];
|
||||
|
||||
/* ==================================================
|
||||
Map
|
||||
================================================== */
|
||||
$content = $this->library->getMap($layers, $markers, $lon, $lat, $zoom);
|
||||
$script = $this->library->getScript();
|
||||
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
|
||||
if ($script) {
|
||||
switch ($this->config['JSlibrary']) {
|
||||
case 'jquery':
|
||||
$pageRenderer->addJsFooterInlineCode(
|
||||
$this->config['id'],
|
||||
'$(document).ready(function() {' . $script . '});'
|
||||
);
|
||||
break;
|
||||
default:
|
||||
$pageRenderer->addJsFooterInlineCode(
|
||||
$this->config['id'],
|
||||
'document.addEventListener("DOMContentLoaded", function(){' . $script . '}, false);'
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ($content);
|
||||
}
|
||||
}
|
||||
576
typo3conf/ext/ods_osm/Classes/Div.php
Normal file
576
typo3conf/ext/ods_osm/Classes/Div.php
Normal file
@@ -0,0 +1,576 @@
|
||||
<?php
|
||||
|
||||
namespace Bobosch\OdsOsm;
|
||||
|
||||
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
|
||||
use TYPO3\CMS\Core\Page\PageRenderer;
|
||||
use TYPO3\CMS\Core\Messaging\AbstractMessage;
|
||||
use TYPO3\CMS\Core\Log\Logger;
|
||||
use Doctrine\DBAL\FetchMode;
|
||||
use Doctrine\DBAL\ParameterType;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
|
||||
use TYPO3\CMS\Core\Http\RequestFactory;
|
||||
use TYPO3\CMS\Core\Log\LogManager;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessageService;
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
class Div
|
||||
{
|
||||
const RESOURCE_BASE_PATH = 'EXT:ods_osm/Resources/Public/';
|
||||
|
||||
public static function getConstraintsForQueryBuilder(
|
||||
$table,
|
||||
ContentObjectRenderer $cObj,
|
||||
QueryBuilder $queryBuilder
|
||||
): array {
|
||||
$constraints = [];
|
||||
|
||||
if (is_string($table)) {
|
||||
$ctrl = $GLOBALS['TCA'][$table]['ctrl'];
|
||||
// Enable fields
|
||||
$queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
|
||||
|
||||
// Version
|
||||
$constraints[] =
|
||||
$queryBuilder->expr()->gte($table . '.pid', $queryBuilder->createNamedParameter(0, Connection::PARAM_INT));
|
||||
|
||||
// Translation
|
||||
if ($ctrl['languageField'] ?? null) {
|
||||
$orConstraints = [
|
||||
$queryBuilder->expr()->eq(
|
||||
$table . '.' . $ctrl['languageField'],
|
||||
$queryBuilder->createNamedParameter(0, Connection::PARAM_INT)
|
||||
),
|
||||
$queryBuilder->expr()->eq(
|
||||
$table . '.' . $ctrl['languageField'],
|
||||
$queryBuilder->createNamedParameter(-1, Connection::PARAM_INT)
|
||||
),
|
||||
];
|
||||
|
||||
$languageAspect = GeneralUtility::makeInstance(Context::class)->getAspect('language');
|
||||
|
||||
if ($languageAspect->getContentId() && $ctrl['transOrigPointerField']) {
|
||||
$orConstraints[] = $queryBuilder->expr()->and($queryBuilder->expr()->eq($table . '.' . $ctrl['languageField'],
|
||||
$queryBuilder->createNamedParameter((int) $languageAspect->getContentId(), Connection::PARAM_INT)), $queryBuilder->expr()->eq($table . '.' . $ctrl['transOrigPointerField'],
|
||||
$queryBuilder->createNamedParameter(0, Connection::PARAM_INT)));
|
||||
}
|
||||
$constraints[] = $queryBuilder->expr()->or(...$orConstraints);
|
||||
}
|
||||
}
|
||||
return $constraints;
|
||||
}
|
||||
|
||||
public static function addJsFiles($scripts, $doc): void
|
||||
{
|
||||
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
|
||||
foreach ($scripts as $script) {
|
||||
$pageRenderer->addJsFooterFile(
|
||||
$script['src'],
|
||||
'text/javascript',
|
||||
true,
|
||||
false,
|
||||
'',
|
||||
false,
|
||||
'|',
|
||||
false,
|
||||
$script['sri'] ?? '',
|
||||
false,
|
||||
'anonymous'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the given address record with geo data from a geocoding service.
|
||||
*
|
||||
* Note that the record does not get update in database.
|
||||
*
|
||||
* @param array &$address Address record from database
|
||||
*
|
||||
* @return boolean True if the address got updated, false if not.
|
||||
*
|
||||
* @uses searchAddress()
|
||||
*/
|
||||
public static function updateAddress(&$address)
|
||||
{
|
||||
$config = self::getConfig(['cache_enabled', 'geo_service']);
|
||||
|
||||
self::splitAddressField($address);
|
||||
|
||||
// Use cache only when enabled
|
||||
if ($config['cache_enabled'] == 1) {
|
||||
$ll = self::searchAddress($address, 0);
|
||||
}
|
||||
|
||||
if (!$ll) {
|
||||
$search = $address;
|
||||
$ll = self::searchAddress($address, $config['geo_service']);
|
||||
// Update cache when enabled or needed for statistic
|
||||
if ($ll && $config['cache_enabled']) {
|
||||
self::updateCache($address, $search);
|
||||
}
|
||||
}
|
||||
|
||||
return $ll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the given address in one of the geocoding services and update
|
||||
* its data.
|
||||
*
|
||||
* Data lat, lon, zip and city may get updated.
|
||||
*
|
||||
* @param array &$address Address record from database
|
||||
* @param integer $service Geocoding service to use
|
||||
* - 0: internal caching database table
|
||||
* - 1: geonames.org
|
||||
* - 2: nominatim.openstreetmap.org
|
||||
*
|
||||
* @return boolean True if the address got updated, false if not.
|
||||
*/
|
||||
public static function searchAddress(&$address, $service = 0)
|
||||
{
|
||||
$config = self::getConfig(['default_country', 'geo_service_email', 'geo_service_user']);
|
||||
$ll = false;
|
||||
|
||||
$country = strtoupper(strlen($address['country'] ?? false) == 2 ? $address['country'] : $config['default_country']);
|
||||
$email = GeneralUtility::validEmail($config['geo_service_email']) ? $config['geo_service_email'] : ($_SERVER['SERVER_ADMIN'] ?? 'unkown@example.com');
|
||||
|
||||
if ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug']) {
|
||||
$service_names = [0 => 'cache', 1 => 'geonames', 2 => 'nominatim'];
|
||||
self::getLogger()->debug('Search address using ' . $service_names[$service], $address);
|
||||
}
|
||||
|
||||
/** @var ConnectionPool $connectionPool */
|
||||
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
|
||||
$connection = $connectionPool->getConnectionForTable('tx_odsosm_geocache');
|
||||
|
||||
switch ($service) {
|
||||
case 0: // cache
|
||||
$where = [];
|
||||
if ($country) {
|
||||
$where[] = 'country=' . $connection->quote($country, ParameterType::STRING);
|
||||
}
|
||||
if ($address['city'] ?? false) {
|
||||
$where[] = '(city=' . $connection->quote($address['city'], ParameterType::STRING) . ' OR search_city=' . $connection->quote($address['city'], ParameterType::STRING) . ')';
|
||||
}
|
||||
if ($address['zip'] ?? false) {
|
||||
$where[] = 'zip=' . $connection->quote($address['zip'], ParameterType::STRING);
|
||||
}
|
||||
if ($address['street'] ?? false) {
|
||||
$where[] = 'street=' . $connection->quote($address['street'], ParameterType::STRING);
|
||||
}
|
||||
if ($address['housenumber'] ?? false) {
|
||||
$where[] = 'housenumber=' . $connection->quote($address['housenumber'], ParameterType::STRING);
|
||||
}
|
||||
|
||||
if ($where) {
|
||||
$where[] = 'deleted=0';
|
||||
|
||||
$res = $connection->executeQuery(
|
||||
'SELECT * FROM tx_odsosm_geocache WHERE ' . implode(' AND ', $where)
|
||||
);
|
||||
$row = $res->fetchAssociative();
|
||||
|
||||
if ($row) {
|
||||
$ll = true;
|
||||
|
||||
$set = [
|
||||
'tstamp' => time(),
|
||||
'cache_hit' => $row['cache_hit'] + 1,
|
||||
];
|
||||
$connection->update('tx_odsosm_geocache', $set, ['uid' => (int) $row['uid']]);
|
||||
|
||||
$address['lat'] = $row['lat'];
|
||||
$address['lon'] = $row['lon'];
|
||||
if ($row['zip'] ?? false) {
|
||||
$address['zip'] = $row['zip'];
|
||||
}
|
||||
if ($row['city'] ?? false) {
|
||||
$address['city'] = $row['city'];
|
||||
}
|
||||
if ($row['state'] ?? false) {
|
||||
$address['state'] = $row['state'];
|
||||
}
|
||||
if (empty($address['country'] ?? false)) {
|
||||
$address['country'] = $row['country'];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: // http://www.geonames.org/
|
||||
if ($country) {
|
||||
$query['country'] = $country;
|
||||
}
|
||||
if ($address['city'] ?? false) {
|
||||
$query['placename'] = $address['city'];
|
||||
}
|
||||
if ($address['zip'] ?? false) {
|
||||
$query['postalcode'] = $address['zip'];
|
||||
}
|
||||
|
||||
if ($query) {
|
||||
$query['maxRows'] = 1;
|
||||
$query['username'] = $config['geo_service_user'];
|
||||
|
||||
/** @var RequestFactory $requestFactory */
|
||||
$requestFactory = GeneralUtility::makeInstance(RequestFactory::class);
|
||||
$configuration = [
|
||||
'timeout' => 60,
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'User-Agent' => 'TYPO3 extension ods_osm/' . ExtensionManagementUtility::getExtensionVersion('ods_osm')
|
||||
],
|
||||
];
|
||||
|
||||
// secure endpoint available, too: https://secure.geonames.org/postalCodeSearchJSON?
|
||||
$response = $requestFactory->request('http://api.geonames.org/postalCodeSearchJSON?' . http_build_query($query, '', '&'), 'GET', $configuration);
|
||||
$content = $response->getBody()->getContents();
|
||||
$result = json_decode($content, true);
|
||||
|
||||
if ($result) {
|
||||
if ($result['status'] ?? false) {
|
||||
if ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug']) {
|
||||
self::getLogger()->debug('GeoNames message', (array)$result['status']['message']);
|
||||
}
|
||||
self::flashMessage(
|
||||
(string)$result['status']['message'],
|
||||
'GeoNames message',
|
||||
AbstractMessage::WARNING
|
||||
);
|
||||
}
|
||||
|
||||
if ($result['postalCodes'][0] ?? false) {
|
||||
$ll = true;
|
||||
$address['lat'] = (string)$result['postalCodes'][0]['lat'];
|
||||
$address['lon'] = (string)$result['postalCodes'][0]['lng'];
|
||||
if ($result['postalCodes'][0]['postalCode'] ?? false) {
|
||||
$address['zip'] = (string)$result['postalCodes'][0]['postalCode'];
|
||||
}
|
||||
if ($result['postalCodes'][0]['placeName'] ?? false) {
|
||||
$address['city'] = (string)$result['postalCodes'][0]['placeName'];
|
||||
}
|
||||
if (empty($address['country'] ?? false)) {
|
||||
$address['country'] = (string)$result['postalCodes'][0]['countryCode'];
|
||||
}
|
||||
}
|
||||
} elseif ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] ?? false) {
|
||||
self::getLogger()->error('No valid response from GeoNames service.');
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // http://nominatim.openstreetmap.org/
|
||||
$query['country'] = $country;
|
||||
$query['email'] = $email;
|
||||
$query['addressdetails'] = 1;
|
||||
$query['format'] = 'jsonv2';
|
||||
|
||||
if ($address['type'] == 'structured') {
|
||||
if ($address['city'] ?? false) {
|
||||
$query['city'] = $address['city'];
|
||||
}
|
||||
if ($address['zip'] ?? false) {
|
||||
$query['postalcode'] = $address['zip'];
|
||||
}
|
||||
if ($address['street'] ?? false) {
|
||||
$query['street'] = $address['street'];
|
||||
}
|
||||
if ($address['housenumber'] ?? false) {
|
||||
$query['street'] = $address['housenumber'] . ' ' . $query['street'];
|
||||
}
|
||||
|
||||
if ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] ?? false) {
|
||||
self::getLogger()->debug('Nominatim structured', $query);
|
||||
}
|
||||
$ll = self::searchAddressNominatim($query, $address);
|
||||
|
||||
if (!$ll && ($query['postalcode'] ?? false)) {
|
||||
unset($query['postalcode']);
|
||||
|
||||
if ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] ?? false) {
|
||||
self::getLogger()->debug('Nominatim retrying without zip', $query);
|
||||
}
|
||||
$ll = self::searchAddressNominatim($query, $address);
|
||||
}
|
||||
}
|
||||
|
||||
if ($address['type'] == 'unstructured') {
|
||||
$query['q'] = $address['address'];
|
||||
|
||||
if ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] ?? false) {
|
||||
self::getLogger()->debug('Nominatim unstructured', $query);
|
||||
}
|
||||
$ll = self::searchAddressNominatim($query, $address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] ?? false) {
|
||||
if ($ll) {
|
||||
self::getLogger()->debug('Return address', $address);
|
||||
} else {
|
||||
self::getLogger()->debug('No address found.');
|
||||
}
|
||||
}
|
||||
|
||||
return $ll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the given address in Nominatim service.
|
||||
*
|
||||
* Data lat, lon, zip and city may get updated.
|
||||
*
|
||||
* @param array $query The query sent to the nominatim API
|
||||
* @param array &$address Address record from database
|
||||
*
|
||||
* @return boolean True if the address was found and got updated.
|
||||
*/
|
||||
protected static function searchAddressNominatim($query, &$address)
|
||||
{
|
||||
$ll = false;
|
||||
|
||||
/** @var RequestFactory $requestFactory */
|
||||
$requestFactory = GeneralUtility::makeInstance(RequestFactory::class);
|
||||
$configuration = [
|
||||
'timeout' => 60,
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'User-Agent' => 'TYPO3 extension ods_osm/' . ExtensionManagementUtility::getExtensionVersion('ods_osm')
|
||||
],
|
||||
];
|
||||
|
||||
$response = $requestFactory->request('https://nominatim.openstreetmap.org/search?' . http_build_query($query, '', '&'), 'GET', $configuration);
|
||||
$content = $response->getBody()->getContents();
|
||||
$result = json_decode($content, true);
|
||||
|
||||
// Save value in cache
|
||||
if ($result) {
|
||||
// take the first result
|
||||
if ($result[0] ?? false) {
|
||||
$ll = true;
|
||||
$address['lat'] = (string)$result[0]['lat'];
|
||||
$address['lon'] = (string)$result[0]['lon'];
|
||||
if ($result[0]['address']['road'] ?? false) {
|
||||
$address['street'] = (string)$result[0]['address']['road'];
|
||||
}
|
||||
if ($result[0]['address']['house_number'] ?? false) {
|
||||
$address['housenumber'] = (string)$result[0]['address']['house_number'];
|
||||
}
|
||||
if ($result[0]['address']['postcode'] ?? false) {
|
||||
$address['zip'] = (string)$result[0]['address']['postcode'];
|
||||
}
|
||||
if ($result[0]['address']['city'] ?? false) {
|
||||
$address['city'] = $result[0]['address']['city'];
|
||||
} else if ($result[0]['address']['village'] ?? false) {
|
||||
$address['city'] = (string)$result[0]['address']['village'];
|
||||
}
|
||||
if ($result[0]['address']['state'] ?? false) {
|
||||
$address['state'] = (string)$result[0]['address']['state'];
|
||||
}
|
||||
if (($result[0]['address']['country_code'] ?? false) && empty($address['country'] ?? false)) {
|
||||
$address['country'] = strtoupper((string)$result[0]['address']['country_code']);
|
||||
}
|
||||
}
|
||||
} elseif ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] ?? false) {
|
||||
self::getLogger()->error('No valid response from Nominatim service.');
|
||||
}
|
||||
|
||||
return $ll;
|
||||
}
|
||||
|
||||
public static function flashMessage($message, $title, $status): void
|
||||
{
|
||||
/** @var FlashMessage $flashMessage */
|
||||
$flashMessage = GeneralUtility::makeInstance(
|
||||
FlashMessage::class,
|
||||
$message,
|
||||
$title,
|
||||
$status
|
||||
);
|
||||
/** @var FlashMessageService $flashMessageService */
|
||||
$flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
|
||||
$flashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
|
||||
$flashMessageQueue->addMessage($flashMessage);
|
||||
}
|
||||
|
||||
public static function updateCache($address, $search = []): void
|
||||
{
|
||||
$set = [
|
||||
'search_city' => $search['city'] ?? '',
|
||||
'country' => $address['country'] ?? '',
|
||||
'state' => $address['state'] ?? '',
|
||||
'city' => $address['city'] ?? '',
|
||||
'zip' => $address['zip'] ?? '',
|
||||
'street' => $address['street'] ?? '',
|
||||
'housenumber' => $address['housenumber'] ?? '',
|
||||
];
|
||||
|
||||
/** @var ConnectionPool $connectionPool */
|
||||
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
|
||||
$connection = $connectionPool->getConnectionForTable('tx_odsosm_geocache');
|
||||
|
||||
$res = $connection->select(
|
||||
['*'], 'tx_odsosm_geocache', $set
|
||||
);
|
||||
$row = $res->fetchAssociative();
|
||||
if ($row) {
|
||||
$set = [
|
||||
'tstamp' => time(),
|
||||
'service_hit' => $row['service_hit'] + 1,
|
||||
];
|
||||
$connection->update('tx_odsosm_geocache', $set, ['uid' => (int) $row['uid']]);
|
||||
} else {
|
||||
$set['tstamp'] = time();
|
||||
$set['crdate'] = time();
|
||||
$set['service_hit'] = 1;
|
||||
$set['lat'] = $address['lat'];
|
||||
$set['lon'] = $address['lon'];
|
||||
$connection->insert('tx_odsosm_geocache', $set);
|
||||
}
|
||||
}
|
||||
|
||||
public static function splitAddressField(&$address): void
|
||||
{
|
||||
// Address field contains street if country, city or zip is set
|
||||
if (($address['country'] ?? false) || ($address['city'] ?? false) || ($address['zip'] ?? false)) {
|
||||
$address['type'] = 'structured';
|
||||
if ($address['address'] && !($address['street'] ?? false)) {
|
||||
$address['street'] = $address['address'];
|
||||
}
|
||||
if (!($address['housenumber'] ?? false) && ($address['street'] ?? false)) {
|
||||
// Split street and house number
|
||||
preg_match('/^(.+)\s(\d+(\s*[^\d\s]+)*)$/', $address['street'], $matches);
|
||||
if ($matches) {
|
||||
$address['street'] = $matches[1];
|
||||
$address['housenumber'] = $matches[2];
|
||||
}
|
||||
}
|
||||
} elseif ($address['address'] ?? false) {
|
||||
$address['type'] = 'unstructured';
|
||||
} else {
|
||||
$address['type'] = 'empty';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extension configuration, and if not available use
|
||||
* default configuration. Optional parameter checks if
|
||||
* single value is available.
|
||||
*
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getConfig($values = [])
|
||||
{
|
||||
$config = $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ods_osm'] ?? [];
|
||||
$getDefault = [];
|
||||
|
||||
if ($config && is_array($values) && count($values)) {
|
||||
foreach ($values as $value) {
|
||||
if (!isset($config[$value])) {
|
||||
$getDefault[] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($config) || count($getDefault)) {
|
||||
$default = parse_ini_file(ExtensionManagementUtility::extPath('ods_osm') . 'ext_conf_template.txt');
|
||||
if (empty($config)) {
|
||||
$config = $default;
|
||||
} else {
|
||||
foreach ($getDefault as $value) {
|
||||
$config[$value] = $default[$value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
public static function getTableConfig($table = false)
|
||||
{
|
||||
$tables = [
|
||||
'fe_groups' => [
|
||||
'FIND_IN_SET' => [
|
||||
'fe_users' => 'usergroup',
|
||||
],
|
||||
],
|
||||
'fe_users' => [
|
||||
'FORMAT' => '%01.6f',
|
||||
'lon' => 'tx_odsosm_lon',
|
||||
'lat' => 'tx_odsosm_lat',
|
||||
'address' => 'address',
|
||||
'zip' => 'zip',
|
||||
'city' => 'city',
|
||||
'country' => 'country',
|
||||
],
|
||||
'tt_content' => [
|
||||
'FORMAT' => '%01.6f',
|
||||
'lon' => 'lon',
|
||||
'lat' => 'lat',
|
||||
],
|
||||
'tx_odsosm_track' => true,
|
||||
'tx_odsosm_vector' => true,
|
||||
'sys_category' => [
|
||||
'MM' => [
|
||||
'tt_address' => [
|
||||
'local' => 'sys_category',
|
||||
'mm' => 'sys_category_record_mm',
|
||||
'foreign' => 'tt_address'
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// load configuration for calendarize only if extension is loaded
|
||||
if (ExtensionManagementUtility::isLoaded('calendarize')) {
|
||||
$tables['tx_calendarize_domain_model_event'] = [
|
||||
'FORMAT' => '%01.6f',
|
||||
'lon' => 'tx_odsosm_lon',
|
||||
'lat' => 'tx_odsosm_lat',
|
||||
'address' => 'location',
|
||||
];
|
||||
}
|
||||
// load configuration for tt_address only if extension is loaded
|
||||
if (ExtensionManagementUtility::isLoaded('tt_address')) {
|
||||
$tables['tt_address'] = [
|
||||
'FORMAT' => '%01.11f',
|
||||
'lon' => 'longitude',
|
||||
'lat' => 'latitude',
|
||||
'address' => 'address',
|
||||
'zip' => 'zip',
|
||||
'city' => 'city',
|
||||
'state' => 'region',
|
||||
'country' => 'country',
|
||||
];
|
||||
}
|
||||
|
||||
if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ods_osm']['tables'] ?? null)) {
|
||||
$tables = array_merge($tables, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ods_osm']['tables']);
|
||||
}
|
||||
|
||||
return $table ? ($tables[$table] ?? []) : $tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Logger
|
||||
*/
|
||||
protected static function getLogger()
|
||||
{
|
||||
/** @var $loggerManager LogManager */
|
||||
$loggerManager = GeneralUtility::makeInstance(LogManager::class);
|
||||
|
||||
return $loggerManager->getLogger(static::class);
|
||||
}
|
||||
|
||||
}
|
||||
16
typo3conf/ext/ods_osm/Classes/Evaluation/LonLat.php
Normal file
16
typo3conf/ext/ods_osm/Classes/Evaluation/LonLat.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Bobosch\OdsOsm\Evaluation;
|
||||
|
||||
class LonLat
|
||||
{
|
||||
public function returnFieldJS(): string
|
||||
{
|
||||
return "return value;";
|
||||
}
|
||||
|
||||
public function evaluateFieldValue($value, $is_in, &$set): string
|
||||
{
|
||||
return sprintf('%01.6f', $value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Bobosch\OdsOsm\EventListener;
|
||||
|
||||
use HDNET\Calendarize\Register;
|
||||
use TYPO3\CMS\Core\Database\Event\AlterTableDefinitionStatementsEvent;
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
|
||||
class CalendarizeOdsOsmSqlListener
|
||||
{
|
||||
public function __invoke(AlterTableDefinitionStatementsEvent $event): void
|
||||
{
|
||||
if (ExtensionManagementUtility::isLoaded('calendarize')) {
|
||||
$event->addSqlData($this->getCalendarizeDatabaseString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the calendarize string for the registered tables.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getCalendarizeDatabaseString()
|
||||
{
|
||||
$sql = [];
|
||||
foreach (Register::getRegister() as $configuration) {
|
||||
if ($configuration['tableName'] == 'tx_calendarize_domain_model_event') {
|
||||
$sql[] = "CREATE TABLE " . $configuration['tableName'] . " (
|
||||
tx_odsosm_lon decimal(9,6) NOT NULL DEFAULT '0.000000',
|
||||
tx_odsosm_lat decimal(8,6) NOT NULL DEFAULT '0.000000',
|
||||
);";
|
||||
}
|
||||
}
|
||||
|
||||
return implode(LF, $sql);
|
||||
}
|
||||
}
|
||||
215
typo3conf/ext/ods_osm/Classes/Provider/BaseProvider.php
Normal file
215
typo3conf/ext/ods_osm/Classes/Provider/BaseProvider.php
Normal file
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
namespace Bobosch\OdsOsm\Provider;
|
||||
|
||||
use Bobosch\OdsOsm\Div;
|
||||
use TYPO3\CMS\Core\Page\PageRenderer;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
|
||||
abstract class BaseProvider
|
||||
{
|
||||
/** @var ContentObjectRenderer */
|
||||
public $cObj; // Must set from instantiating class
|
||||
protected $config;
|
||||
protected $script;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected $pageRenderer;
|
||||
|
||||
/** @var array keeping all JavaScripts to be included */
|
||||
protected $scripts = [];
|
||||
|
||||
protected $layers = [
|
||||
0 => [], // Base
|
||||
1 => [], // Overlay
|
||||
2 => [], // Marker
|
||||
];
|
||||
|
||||
// Implement these functions
|
||||
public function getMapCore($backpath = '')
|
||||
{
|
||||
}
|
||||
|
||||
public function getMapMain()
|
||||
{
|
||||
}
|
||||
|
||||
public function getMapCenter($lat, $lon, $zoom)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getLayer($layer, $i, $backpath = '')
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getMarker($item, $table)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get JavaScript code for fulltext button
|
||||
*
|
||||
* @return string The JavaScript to add the fullscreen button
|
||||
*/
|
||||
protected function getFullScreen()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function init($config): void
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMap($layers, $markers, $lon, $lat, $zoom)
|
||||
{
|
||||
$this->getMapCore();
|
||||
|
||||
$this->layers = $layers;
|
||||
|
||||
$baselayers = $layers[0] ?? null;
|
||||
$overlays = $layers[1] ?? null;
|
||||
|
||||
$this->script = "
|
||||
" . $this->getMapMain() . "
|
||||
" . $this->getBaseLayers($baselayers) . "
|
||||
" . $this->getOverlayLayers($overlays) . "
|
||||
" . $this->getMapCenter($lat, $lon, $zoom) . "
|
||||
" . $this->getMarkers($markers);
|
||||
|
||||
if (($this->config['show_layerswitcher'] ?? null) && ($this->config['show_layerswitcher'] > 0)) {
|
||||
$this->script .= $this->getLayerSwitcher() . "\n";
|
||||
}
|
||||
|
||||
if ($this->config['show_fullscreen'] ?? null) {
|
||||
$this->script .= $this->getFullScreen() . "\n";
|
||||
}
|
||||
|
||||
Div::addJsFiles($this->scripts, null);
|
||||
|
||||
return $this->getHtml();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBaseLayers($layers, $backpath = '')
|
||||
{
|
||||
// Main layer
|
||||
$i = 0;
|
||||
$jsBaseLayer = [];
|
||||
if (is_array($layers) && !empty($layers)) {
|
||||
foreach ($layers as $layer) {
|
||||
$jsBaseLayer[] = $this->getLayer($layer, $i, $backpath);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
return implode("\n", ($jsBaseLayer));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOverlayLayers($layers, $backpath = '')
|
||||
{
|
||||
// Main layer
|
||||
$i = 0;
|
||||
$jsOverlayLayer = [];
|
||||
if (is_array($layers) && !empty($layers)) {
|
||||
foreach ($layers as $layer) {
|
||||
$jsOverlayLayer[] = $this->getLayer($layer, $i, $backpath);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
return implode("\n", ($jsOverlayLayer));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getScript()
|
||||
{
|
||||
return $this->script;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getMarkers($markers)
|
||||
{
|
||||
$jsMarker = '';
|
||||
foreach ($markers as $table => $items) {
|
||||
foreach ($items as $item) {
|
||||
$jsMarker .= $this->getMarker($item, $table);
|
||||
}
|
||||
}
|
||||
|
||||
return $jsMarker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getLayerSwitcher()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getHtml()
|
||||
{
|
||||
$mousePosition = '';
|
||||
$popupcode = '';
|
||||
if ($this->config['library'] == 'openlayers') {
|
||||
if ($this->config['mouse_position']) {
|
||||
$mousePosition = '<div id="mouse-position-' . $this->config['id'] . '">' . LocalizationUtility::translate('mouse_position', 'OdsOsm') . ': </div>';
|
||||
}
|
||||
$popupcode = '
|
||||
<div id="popup" class="ol-popup">
|
||||
<a href="#" id="popup-closer" class="ol-popup-closer"></a>
|
||||
<div id="popup-content"></div>
|
||||
</div>';
|
||||
}
|
||||
return '<div style="width:' . $this->config['width'] . '; height:' . $this->config['height'] . '; " id="' . $this->config['id'] . '"></div>' . $mousePosition . $popupcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getTileUrl($layer)
|
||||
{
|
||||
if (strpos($layer['tile_url'], '://') !== false) {
|
||||
return $layer['tile_url'];
|
||||
}
|
||||
// if protocoll is missing, we add http:// or https://
|
||||
if ($layer['tile_https'] == 1) {
|
||||
return 'https://' . $layer['tile_url'];
|
||||
}
|
||||
return 'http://' . $layer['tile_url'];
|
||||
}
|
||||
|
||||
public function setContentObjectRenderer(ContentObjectRenderer $cObj): void
|
||||
{
|
||||
$this->cObj = $cObj;
|
||||
}
|
||||
}
|
||||
355
typo3conf/ext/ods_osm/Classes/Provider/Leaflet.php
Normal file
355
typo3conf/ext/ods_osm/Classes/Provider/Leaflet.php
Normal file
@@ -0,0 +1,355 @@
|
||||
<?php
|
||||
|
||||
namespace Bobosch\OdsOsm\Provider;
|
||||
|
||||
use Bobosch\OdsOsm\Div;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Resource\FileRepository;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\PathUtility;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
|
||||
class Leaflet extends BaseProvider
|
||||
{
|
||||
protected $path_res;
|
||||
protected $path_leaflet;
|
||||
|
||||
public function getMapCore($backpath = ''): void
|
||||
{
|
||||
$this->path_res = ($backpath ? $backpath :
|
||||
PathUtility::getAbsoluteWebPath(
|
||||
GeneralUtility::getFileAbsFileName(Div::RESOURCE_BASE_PATH . 'JavaScript/Leaflet/')
|
||||
)
|
||||
);
|
||||
$this->path_leaflet = ($this->config['local_js'] ? $this->path_res . 'Core/' : 'https://unpkg.com/leaflet@1.9.4/dist/');
|
||||
$this->pageRenderer->addCssFile($this->path_leaflet . 'leaflet.css');
|
||||
$this->scripts['leaflet'] = [
|
||||
'src' => $this->path_leaflet . 'leaflet.js',
|
||||
'sri' => 'sha384-cxOPjt7s7Iz04uaHJceBmS+qpjv2JkIHNVcuOrM+YHwZOmJGBXI00mdUXEq65HTH'
|
||||
];
|
||||
}
|
||||
|
||||
public function getMapMain()
|
||||
{
|
||||
$controls = [];
|
||||
if ($this->config['show_scalebar']) {
|
||||
$controls['scalebar'] = 'new L.control.scale()';
|
||||
}
|
||||
|
||||
$vars = '';
|
||||
foreach ($controls as $var => $obj) {
|
||||
$vars .= "\n\t\t\t" . $this->config['id'] . '.addControl(' . $obj . ");";
|
||||
}
|
||||
|
||||
$jsMain =
|
||||
$this->config['id'] . "=new L.Map('" . $this->config['id'] . "',
|
||||
{scrollWheelZoom: " .((!isset($this->config['enable_scrollwheelzoom']) || $this->config['enable_scrollwheelzoom'] == '1') ? 'true' : 'false'). ",
|
||||
dragging: " .((!isset($this->config['enable_dragging']) || $this->config['enable_dragging'] == '1') ? 'true' : 'false'). "});
|
||||
L.Icon.Default.imagePath='" . $this->path_leaflet . "images/';"
|
||||
. $vars;
|
||||
if ($this->config['cluster']) {
|
||||
$this->pageRenderer->addCssFile($this->path_res . 'leaflet-markercluster/MarkerCluster.css');
|
||||
$this->pageRenderer->addCssFile($this->path_res . 'leaflet-markercluster/MarkerCluster.Default.css');
|
||||
$this->scripts['leaflet-markercluster'] = [
|
||||
'src' => $this->path_res . 'leaflet-markercluster/leaflet.markercluster.js'
|
||||
];
|
||||
}
|
||||
|
||||
return $jsMain;
|
||||
}
|
||||
|
||||
protected function getLayer($layer, $i, $backpath = '')
|
||||
{
|
||||
if ($layer['tile_url']) {
|
||||
$options = [];
|
||||
if ($layer['min_zoom']) {
|
||||
$options['minZoom'] = $layer['min_zoom'];
|
||||
}
|
||||
if ($layer['max_zoom']) {
|
||||
$options['maxZoom'] = $layer['max_zoom'];
|
||||
}
|
||||
if ($layer['subdomains']) {
|
||||
$options['subdomains'] = $layer['subdomains'];
|
||||
}
|
||||
if ($layer['attribution']) {
|
||||
$options['attribution'] = $layer['attribution'];
|
||||
}
|
||||
|
||||
$jsLayer = 'new L.TileLayer(\'' . $this->getTileUrl($layer) . '\',' . json_encode($options) . ');';
|
||||
}
|
||||
|
||||
$jsLayer = "\n\t\t\tvar layer_" . $layer['uid'] . ' = ' . $jsLayer;
|
||||
|
||||
// only show first base layer on the map
|
||||
if (($layer['overlay'] == 1 && $layer['visible']) || ($i == 0 && $layer['overlay'] == 0)) {
|
||||
$jsLayer .= "\n\t\t\t" . $this->config['id'] . '.addLayer(layer_' . $layer['uid'] . ');';
|
||||
}
|
||||
|
||||
return $jsLayer;
|
||||
}
|
||||
|
||||
protected function getLayerSwitcher()
|
||||
{
|
||||
$base = [];
|
||||
if (is_array($this->layers[0] ?? null) && count($this->layers[0]) > 1) {
|
||||
foreach ($this->layers[0] as $layer) {
|
||||
$base[] = '"' . $layer['title'] . '":' . ($layer['table'] ?? 'layer') . '_' . $layer['uid'];
|
||||
}
|
||||
}
|
||||
$overlay = [];
|
||||
if (is_array($this->layers[1] ?? null)) {
|
||||
foreach ($this->layers[1] as $layer) {
|
||||
if (!empty($layer['gid'])) {
|
||||
$overlay[] = '"' . $layer['title'] . '":' . $layer['gid'];
|
||||
} else {
|
||||
$overlay[] = '"' . $layer['title'] . '":' . ($layer['table'] ?? 'layer') . '_' . $layer['uid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($base) && empty($overlay)) {
|
||||
return '';
|
||||
}
|
||||
return 'var layersControl=new L.Control.Layers({' . implode(',', $base) . '},{' . implode(',', $overlay) . '}' . ($this->config['show_layerswitcher'] == 2 ? ',{collapsed:false}' : '') . ');
|
||||
' . $this->config['id'] . '.addControl(layersControl);';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fullscreen button
|
||||
*
|
||||
* @return string The JavaScript to add the fullscreen button
|
||||
*/
|
||||
public function getFullScreen()
|
||||
{
|
||||
// load leaflet.fullscreen plugin
|
||||
$this->scripts['leaflet-fullscreen'] = [
|
||||
'src' => $this->path_res . 'leaflet-fullscreen/Control.FullScreen.js',
|
||||
'sri' => 'sha384-TqFtkYBnYsrP2JCfIv/oLQxS9L6xpaIV9xnaI2UGMK25cJsTtQXZIU6WGQ7daT0Z'
|
||||
];
|
||||
$this->pageRenderer->addCssFile($this->path_res . 'leaflet-fullscreen/Control.FullScreen.css');
|
||||
|
||||
return "L.control.fullscreen({
|
||||
position: 'topleft',
|
||||
title: 'Full Screen',
|
||||
titleCancel: 'Exit fullscreen mode',
|
||||
forceSeparateButton: true,
|
||||
forcePseudoFullscreen: true, // force use of pseudo full screen even if full screen API is available, default false
|
||||
fullscreenElement: false // Dom element to render in full screen, false by default, fallback to map._container
|
||||
}).addTo(" . $this->config['id'] . ");";
|
||||
}
|
||||
|
||||
public function getMapCenter($lat, $lon, $zoom)
|
||||
{
|
||||
$return = 'var center = new L.LatLng(' . json_encode($lat) . ',' . json_encode($lon) . ');' . $this->config['id'] . '.setView(center,' . $zoom . ');';
|
||||
if ($this->config['position']) {
|
||||
$return .= $this->config['id'] . '.locate();' . $this->config['id'] . '.on("locationfound",function(e){var radius=e.accuracy/2;L.circle(e.latlng,radius).addTo(' . $this->config['id'] . ');});';
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
protected function getMarkers($markers)
|
||||
{
|
||||
$jsMarker = parent::getMarkers($markers);
|
||||
|
||||
foreach ($this->layers[2] as $group_uid => $group) {
|
||||
if ($this->config['cluster']) {
|
||||
$jsMarker .= 'var ' . $group_uid . ' = new L.MarkerClusterGroup({maxClusterRadius:' . (int)$this->config['cluster_radius'] . '});' . "\n";
|
||||
foreach ($group as $jsElementVar) {
|
||||
$jsMarker .= $group_uid . '.addLayer(' . $jsElementVar . ');' . "\n";
|
||||
}
|
||||
} else {
|
||||
$jsMarker .= 'var ' . $group_uid . ' = L.layerGroup([' . implode(',', $group) . ']);' . "\n";
|
||||
}
|
||||
$jsMarker .= $this->config['id'] . '.addLayer(' . $group_uid . ');' . "\n";
|
||||
}
|
||||
|
||||
return $jsMarker;
|
||||
}
|
||||
|
||||
protected function getMarker($item, $table)
|
||||
{
|
||||
$jsMarker = '';
|
||||
$jsElementVar = $table . '_' . $item['uid'];
|
||||
$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
|
||||
$jsElementVarsForPopup = [];
|
||||
|
||||
switch ($table) {
|
||||
case 'tx_odsosm_track':
|
||||
$fileObjects = $fileRepository->findByRelation('tx_odsosm_track', 'file', $item['uid']);
|
||||
if ($fileObjects) {
|
||||
$file = $fileObjects[0];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
// Add tracks to layerswitcher
|
||||
$this->layers[1][] = [
|
||||
'title' => $item['title'],
|
||||
'table' => $table,
|
||||
'uid' => $item['uid']
|
||||
];
|
||||
|
||||
switch (strtolower(pathinfo($file->getName(), PATHINFO_EXTENSION))) {
|
||||
case 'kml':
|
||||
// include javascript file for KML support
|
||||
$this->scripts['leaflet-plugins'] = [
|
||||
'src' => $this->path_res . 'leaflet-plugins/layer/vector/KML.js'
|
||||
];
|
||||
|
||||
$jsMarker .= 'var ' . $jsElementVar . ' = new L.KML(';
|
||||
$jsMarker .= '"' . $file->getPublicUrl() . '"';
|
||||
$jsMarker .= ");\n";
|
||||
$jsMarker .= $this->config['id'] . '.addLayer(' . $jsElementVar . ');' . "\n";
|
||||
break;
|
||||
case 'gpx':
|
||||
// include javascript file for GPX support
|
||||
$this->scripts['leaflet-gpx'] = [
|
||||
'src' => $this->path_res . 'leaflet-gpx/gpx.js'
|
||||
];
|
||||
$options = [
|
||||
'clickable' => 'false',
|
||||
'polyline_options' => [
|
||||
'color' => $item['color'],
|
||||
'weight' => $item['width'] ?: 1,
|
||||
],
|
||||
'marker_options' => [
|
||||
'startIconUrl' => $this->path_res . 'leaflet-gpx/pin-icon-start.png',
|
||||
'endIconUrl' => $this->path_res . 'leaflet-gpx/pin-icon-end.png',
|
||||
'shadowUrl' => $this->path_res . 'leaflet-gpx/pin-shadow.png',
|
||||
],
|
||||
];
|
||||
$jsMarker .= 'var ' . $jsElementVar . ' = new L.GPX("' . $file->getPublicUrl() . '",';
|
||||
|
||||
$jsMarker .= json_encode($options) . ");\n";
|
||||
$jsMarker .= $this->config['id'] . '.addLayer(' . $jsElementVar . ');' . "\n";
|
||||
break;
|
||||
}
|
||||
$jsElementVarsForPopup[] = $jsElementVar;
|
||||
break;
|
||||
case 'tx_odsosm_vector':
|
||||
// add styles from record if both are set - color and width
|
||||
if (!empty($item['color']) && !empty($item['width'])) {
|
||||
$jsMarker .= 'var myStyle = {
|
||||
"color": "'.$item['color'].'",
|
||||
"weight": '.$item['width'].',
|
||||
"opacity": 1
|
||||
};';
|
||||
} else {
|
||||
$jsMarker .= 'var myStyle = {};';
|
||||
}
|
||||
|
||||
$fileObjects = $fileRepository->findByRelation('tx_odsosm_vector', 'file', $item['uid']);
|
||||
if ($fileObjects) {
|
||||
$file = $fileObjects[0];
|
||||
$filename = Environment::getPublicPath() . '/' . $file->getPublicUrl();
|
||||
$jsMarker .= 'var ' . $jsElementVar . '_file = new L.geoJson(' . file_get_contents($filename) . ',
|
||||
{
|
||||
style: myStyle
|
||||
});' . "\n";
|
||||
$jsMarker .= $this->config['id'] . '.addLayer(' . $jsElementVar . '_file);' . "\n";
|
||||
|
||||
// Add vector file to layerswitcher
|
||||
$this->layers[1][] = [
|
||||
'title' => $item['title'] . ' ('. LocalizationUtility::translate('file', 'OdsOsm') .')',
|
||||
'table' => $table,
|
||||
'uid' => $item['uid'] . '_file'
|
||||
];
|
||||
$jsElementVarsForPopup[] = $jsElementVar . '_file';
|
||||
}
|
||||
|
||||
// add geojson from data field as well
|
||||
if ($item['data']) {
|
||||
$jsMarker .= 'var ' . $jsElementVar . '_data = new L.geoJson(' . $item['data'] . ',
|
||||
{
|
||||
style: myStyle
|
||||
});' . "\n";
|
||||
$jsMarker .= $this->config['id'] . '.addLayer(' . $jsElementVar . '_data);' . "\n";
|
||||
|
||||
// Add vector data to layerswitcher
|
||||
$this->layers[1][] = [
|
||||
'title' => $item['title'],
|
||||
'table' => $table,
|
||||
'uid' => $item['uid'] . '_data'
|
||||
];
|
||||
$jsElementVarsForPopup[] = $jsElementVar . '_data';
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
$markerOptions = [];
|
||||
if ($item['tx_odsosm_marker'] ?? false) {
|
||||
$marker = $item['tx_odsosm_marker'];
|
||||
$iconOptions = [
|
||||
'iconSize' => [(int)$marker['size_x'], (int)$marker['size_y']],
|
||||
'iconAnchor' => [-(int)$marker['offset_x'], -(int)$marker['offset_y']],
|
||||
'popupAnchor' => [0, (int)$marker['offset_y']]
|
||||
];
|
||||
if ($marker['type'] == 'html') {
|
||||
$iconOptions['html'] = $marker['icon'];
|
||||
$markerOptions['icon'] = 'icon: new L.divIcon(' . json_encode($iconOptions) . ')';
|
||||
} else {
|
||||
$icon = $marker['icon']->getPublicUrl();
|
||||
$iconOptions['iconUrl'] = $icon;
|
||||
$markerOptions['icon'] = 'icon: new L.Icon(' . json_encode($iconOptions) . ')';
|
||||
}
|
||||
} else {
|
||||
$marker = [ 'type' => 'image' ];
|
||||
$icon = $this->path_leaflet . 'images/marker-icon.png';
|
||||
}
|
||||
$jsMarker .= 'var ' . $jsElementVar . ' = new L.Marker([' . $item['latitude'] . ', ' . $item['longitude'] . '], {' . implode(',', $markerOptions) . "});\n";
|
||||
// Add group to layer switch
|
||||
if ($item['group_title'] ?? false) {
|
||||
$this->layers[1][] = [
|
||||
'title' => ($marker['type'] == 'html' ? $marker['icon'] : "<img class='marker-icon' src='" . $icon . "' />") . ' ' . $item['group_title'],
|
||||
'gid' => $item['group_uid']
|
||||
];
|
||||
$this->layers[2][$item['group_uid']][] = $jsElementVar;
|
||||
} else {
|
||||
$this->layers[2][$this->config['id'] . '_g'][] = $jsElementVar;
|
||||
}
|
||||
|
||||
$jsElementVarsForPopup[] = $jsElementVar;
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($jsElementVarsForPopup as $jsElementVar) {
|
||||
// is there a properties attribute from geoJSON? If so, we will show the given properties
|
||||
$popupJsCode = '';
|
||||
if ($item['properties'] ?? null) {
|
||||
$geojsonProperties = json_encode(explode(', ', $item['properties']));
|
||||
$popupJsCode = "
|
||||
function (layer) {
|
||||
var osm_popup = '" . ($item['popup'] ?? '') . "';
|
||||
|
||||
var feature = layer.feature,
|
||||
props = feature.properties,
|
||||
ll = Object.keys(props),
|
||||
attribute, value = '';
|
||||
|
||||
var osm_filter = " . $geojsonProperties . ";
|
||||
|
||||
osm_filter.forEach((osm_prop) => {
|
||||
if (typeof props[osm_prop] !== 'undefined') {
|
||||
value += '<dt>' + osm_prop + '</dt> <dd>' + props[osm_prop] + '</dd>';
|
||||
}
|
||||
});
|
||||
return osm_popup + '<dl>' + value + '</dl>';
|
||||
}
|
||||
";
|
||||
} elseif ($item['popup'] ?? null) {
|
||||
$popupJsCode = json_encode($item['popup'] ?? '');
|
||||
}
|
||||
if ($this->config['show_popups'] == 1) {
|
||||
$jsMarker .= $jsElementVar . '.bindPopup(' . $popupJsCode . '); ' . "\n";
|
||||
if ($item['initial_popup'] ?? null) {
|
||||
$jsMarker .= $jsElementVar . ".openPopup();\n";
|
||||
}
|
||||
} elseif ($this->config['show_popups'] == 2) {
|
||||
$jsMarker .= $jsElementVar . '.bindTooltip(' . $popupJsCode . ");\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $jsMarker;
|
||||
}
|
||||
}
|
||||
632
typo3conf/ext/ods_osm/Classes/Provider/Openlayers.php
Normal file
632
typo3conf/ext/ods_osm/Classes/Provider/Openlayers.php
Normal file
@@ -0,0 +1,632 @@
|
||||
<?php
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2022 Alexander Bigga <alexander@bigga.de>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project is
|
||||
* free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
namespace Bobosch\OdsOsm\Provider;
|
||||
|
||||
use Bobosch\OdsOsm\Div;
|
||||
use TYPO3\CMS\Core\Page\PageRenderer;
|
||||
use TYPO3\CMS\Core\Resource\FileRepository;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\PathUtility;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
|
||||
class Openlayers extends BaseProvider
|
||||
{
|
||||
protected $layers = [
|
||||
0 => [], // Base
|
||||
1 => [], // Overlay
|
||||
2 => [], // Marker
|
||||
];
|
||||
|
||||
public function getMapCore($backpath = ''): void
|
||||
{
|
||||
$path = ($backpath ? $backpath :
|
||||
PathUtility::getAbsoluteWebPath(
|
||||
GeneralUtility::getFileAbsFileName(Div::RESOURCE_BASE_PATH . 'OpenLayers/')
|
||||
)
|
||||
);
|
||||
$pathOl = ($this->config['local_js'] ? $path : 'https://cdn.jsdelivr.net/npm/ol@v8.1.0/');
|
||||
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
|
||||
$pageRenderer->addCssFile($pathOl . 'ol.css');
|
||||
$this->scripts['OpenLayers'] = [
|
||||
'src' => $pathOl . 'dist/ol.js',
|
||||
'sri' => 'sha512-7BxMviUlJVAJOF4l717SzPknm3Y5nLAm3PPtRdrWlCu4GLaW+RhBxYuOJ1MkVNAcPu+lRWn4gtWx0PAxvTzD0g=='
|
||||
];
|
||||
|
||||
// Do we need the layerswitcher? If so, some extra plugin is required.
|
||||
if ($this->config['show_layerswitcher']) {
|
||||
$pathContrib = ($this->config['local_js'] ? $path . 'Contrib/ol-layerswitcher/' : 'https://unpkg.com/ol-layerswitcher@4.1.1/dist/');
|
||||
$pathCustom = $path . 'Custom/';
|
||||
|
||||
$pageRenderer->addCssFile($pathContrib . 'ol-layerswitcher.css');
|
||||
$pageRenderer->addCssFile($pathCustom . 'ol-layerswitcher.css');
|
||||
|
||||
$this->scripts['OpenLayersSwitch'] = [
|
||||
'src' => $pathContrib . 'ol-layerswitcher.js',
|
||||
'sri' => 'sha512-HhCrrWOoQb5HSpRe1fsk9ugZQEOokbJsLioPuUhfXlr5ccRTZVg3UpnfRsTJzrdKLejmx7uvY62n2fp5qLdYQg=='
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public function getMapMain()
|
||||
{
|
||||
$controls = [
|
||||
'new ol.control.Attribution()',
|
||||
'new ol.control.Zoom()',
|
||||
'new ol.control.Rotate()'
|
||||
];
|
||||
if ($this->config['mouse_position']) {
|
||||
$controls[] = "new ol.control.MousePosition({
|
||||
coordinateFormat: ol.coordinate.createStringXY(2),
|
||||
projection: 'EPSG:4326',
|
||||
className: 'ods-osm-mouse-position',
|
||||
target: document.getElementById('mouse-position-" . $this->config['id'] . "')
|
||||
})";
|
||||
}
|
||||
if ($this->config['show_scalebar']) {
|
||||
$controls[] = "new ol.control.ScaleLine()";
|
||||
}
|
||||
|
||||
return "
|
||||
view = new ol.View({
|
||||
center: [0, 0],
|
||||
zoom: 1
|
||||
});
|
||||
|
||||
baselayergroup = new ol.layer.Group({
|
||||
name: 'baselayergroup',
|
||||
title: '" . LocalizationUtility::translate('base_layer', 'OdsOsm') . "',
|
||||
layers: [
|
||||
new ol.layer.Tile({
|
||||
type: 'base',
|
||||
source: new ol.source.OSM(),
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
overlaygroup = new ol.layer.Group({
|
||||
name: 'overlaygroup',
|
||||
title: '" . LocalizationUtility::translate('overlays', 'OdsOsm') . "',
|
||||
layers: []
|
||||
});
|
||||
|
||||
const styleCache = {};
|
||||
clusters = new ol.layer.Vector({
|
||||
name: 'clusters',
|
||||
title: '" . LocalizationUtility::translate('openlayers.clusterLayer', 'OdsOsm') . "',
|
||||
source: new ol.source.Cluster({
|
||||
distance: " . (int)$this->config['cluster_radius'] . ",
|
||||
minDistance: 10,
|
||||
source: new ol.source.Vector({
|
||||
name: 'source',
|
||||
features: [],
|
||||
})
|
||||
}),
|
||||
style: function (feature) {
|
||||
const size = feature.get('features').length;
|
||||
if (size > 1) {
|
||||
style = new ol.style.Style({
|
||||
image: new ol.style.Circle({
|
||||
radius: 20,
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#fff',
|
||||
}),
|
||||
fill: new ol.style.Fill({
|
||||
color: '#3399CC',
|
||||
}),
|
||||
}),
|
||||
text: new ol.style.Text({
|
||||
text: size.toString(),
|
||||
fill: new ol.style.Fill({
|
||||
color: '#fff',
|
||||
}),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
style = feature.get('features')[0].values_.style;
|
||||
}
|
||||
return style;
|
||||
},
|
||||
});
|
||||
|
||||
layers = [
|
||||
baselayergroup,
|
||||
overlaygroup
|
||||
];
|
||||
|
||||
var " . $this->config['id'] . " = new ol.Map({
|
||||
target: '" . $this->config['id'] . "',
|
||||
controls:[" . implode(',', $controls) . "],
|
||||
layers: layers,
|
||||
view: view
|
||||
});
|
||||
|
||||
// Popup showing the position the user clicked
|
||||
var container = document.getElementById('popup');
|
||||
var closer = document.getElementById('popup-closer');
|
||||
var content = document.getElementById('popup-content');
|
||||
var popup = new ol.Overlay({
|
||||
element: container,
|
||||
autoPan: true,
|
||||
autoPanAnimation: {
|
||||
duration: 100
|
||||
}
|
||||
});
|
||||
" . $this->config['id'] . ".addOverlay(popup);
|
||||
closer.onclick = function () {
|
||||
popup.setPosition(undefined);
|
||||
content.innerHTML = '';
|
||||
closer.blur();
|
||||
return false;
|
||||
};
|
||||
";
|
||||
}
|
||||
|
||||
/**
|
||||
* The center and zoom level of the map
|
||||
*
|
||||
* @param float $lat: latitude
|
||||
* @param float $lon: longitude
|
||||
* @param int $zoom: zoom level
|
||||
*
|
||||
* @return string The JavaScript to set the center and zoom level
|
||||
*/
|
||||
public function getMapCenter($lat, $lon, $zoom)
|
||||
{
|
||||
return '
|
||||
view.setCenter(ol.proj.transform([' . $lon . ', ' . $lat . '], \'EPSG:4326\', \'EPSG:3857\'));
|
||||
view.setZoom(' . $zoom . ');
|
||||
';
|
||||
}
|
||||
|
||||
protected function getLayer($layer, $i, $backpath = '')
|
||||
{
|
||||
if (empty($layer['subdomains'])) {
|
||||
$layer['subdomains'] = 'abc';
|
||||
}
|
||||
$layer['subdomains'] = substr($layer['subdomains'], 0, 1) . '-' . substr($layer['subdomains'], -1, 1);
|
||||
$layer['tile_url'] = strtr($this->getTileUrl($layer), ['{s}' => '{' . $layer['subdomains'] . '}']);
|
||||
|
||||
if ($layer['overlay'] == 1) {
|
||||
return $this->config['id'] . "_" . $i . "_overlayLayer =
|
||||
new ol.layer.Tile({
|
||||
visible: " . ($layer['visible'] == true ? 'true' : 'false') . ",
|
||||
opacity: 0.99,
|
||||
title: '" . $layer['title'] . "',
|
||||
source: new ol.source.OSM({
|
||||
url: '" . $layer['tile_url'] . "',
|
||||
attributions: [
|
||||
'" . $layer['attribution']. "'
|
||||
]
|
||||
})
|
||||
});
|
||||
overlaygroup.getLayers().push(" . $this->config['id'] . "_" . $i . "_overlayLayer);
|
||||
";
|
||||
}
|
||||
|
||||
return $this->config['id'] . "_" . $i . "_baselayergroup =
|
||||
new ol.layer.Tile({
|
||||
type: 'base',
|
||||
combine: 'true',
|
||||
visible: " . ($i == 0 ? 'true' : 'false') . ",
|
||||
title: '" . $layer['title'] . "',
|
||||
source: new ol.source.OSM({
|
||||
url: '" . $layer['tile_url'] . "',
|
||||
attributions: [
|
||||
'" . $layer['attribution']. "'
|
||||
]
|
||||
})
|
||||
});
|
||||
baselayergroup.getLayers().push(" . $this->config['id'] . "_" . $i . "_baselayergroup);
|
||||
";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the layer switcher
|
||||
*
|
||||
* @return string The JavaScript to add the layerswitcher
|
||||
*/
|
||||
protected function getLayerSwitcher()
|
||||
{
|
||||
return '
|
||||
var layerSwitcher = new ol.control.LayerSwitcher({
|
||||
activationMode: \'' . ($this->config['layerswitcher_activationMode'] == '1' ? 'click' : 'mouseover') . '\',
|
||||
startActive: ' . ($this->config['show_layerswitcher'] == '2' ? 'true' : 'false') . ',
|
||||
tipLabel: \'' . LocalizationUtility::translate('openlayers.showLayerList', 'OdsOsm') . '\',
|
||||
collapseTipLabel: \'' . LocalizationUtility::translate('openlayers.hideLayerList', 'OdsOsm') . '\',
|
||||
groupSelectStyle: \'children\',
|
||||
reverse: false
|
||||
});
|
||||
' . $this->config['id'] . '.addControl(layerSwitcher);
|
||||
';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fullscreen button
|
||||
*
|
||||
* @return string The JavaScript to add the fullscreen button
|
||||
*/
|
||||
protected function getFullScreen()
|
||||
{
|
||||
return '
|
||||
var fullScreen = new ol.control.FullScreen();
|
||||
' . $this->config['id'] . '.addControl(fullScreen);
|
||||
';
|
||||
}
|
||||
|
||||
protected function getMarkers($markers)
|
||||
{
|
||||
$jsMarker = parent::getMarkers($markers);
|
||||
|
||||
// open popup? If yes, with click or hover?
|
||||
switch ($this->config['show_popups']) {
|
||||
case 1:
|
||||
$eventMethod = 'singleclick';
|
||||
break;
|
||||
case 2:
|
||||
$eventMethod = 'pointermove';
|
||||
break;
|
||||
default:
|
||||
$eventMethod = false;
|
||||
}
|
||||
|
||||
if ($eventMethod !== false) {
|
||||
$jsMarker .= "
|
||||
" . $this->config['id'] . ".on('" . $eventMethod . "', function (event) {
|
||||
var feature = " . $this->config['id'] . ".forEachFeatureAtPixel(event.pixel, function (feat, layer) {
|
||||
return feat;
|
||||
});
|
||||
var layer = " . $this->config['id'] . ".forEachFeatureAtPixel(event.pixel, function (feat, layer) {
|
||||
return layer;
|
||||
});
|
||||
|
||||
if (feature === undefined) {
|
||||
return;
|
||||
}
|
||||
if (feature.get('features') === undefined) {
|
||||
var coordinate = event.coordinate;
|
||||
if (feature.get('desc') !== undefined) {
|
||||
content.innerHTML = feature.get('desc');
|
||||
} else {
|
||||
// this might be some geoJSON data with properties set
|
||||
|
||||
var osm_popup = layer.get('popup');
|
||||
var props = feature.values_,
|
||||
ll = Object.keys(props),
|
||||
attribute, value = '';
|
||||
|
||||
var osm_filter = layer.get('properties').split(',').map(item=>item.trim());
|
||||
|
||||
osm_filter.forEach((osm_prop) => {
|
||||
if (typeof feature.get(osm_prop) !== 'undefined') {
|
||||
value += '<dt>' + osm_prop + '</dt> <dd>' + feature.get(osm_prop) + '</dd>';
|
||||
}
|
||||
});
|
||||
content.innerHTML = osm_popup + '<dl>' + value + '</dl>';
|
||||
}
|
||||
popup.setPosition(coordinate);
|
||||
} else if (feature.get('features').length === 1) {
|
||||
var singleFeature = feature.get('features')[0];
|
||||
if (feature && singleFeature.get('type') == 'Point') {
|
||||
var coordinate = event.coordinate;
|
||||
|
||||
content.innerHTML = singleFeature.get('desc');
|
||||
popup.setPosition(coordinate);
|
||||
}
|
||||
} else if (feature && feature.get('type') == 'Point') {
|
||||
|
||||
var coordinate = event.coordinate;
|
||||
|
||||
content.innerHTML = feature.get('desc');
|
||||
popup.setPosition(coordinate);
|
||||
} else {
|
||||
if (feature.get('features').length > 0) {
|
||||
const clusterMembers = feature.get('features');
|
||||
if (clusterMembers.length > 1) {
|
||||
// Calculate the extent of the cluster members.
|
||||
const extent = new ol.extent.createEmpty();
|
||||
clusterMembers.forEach((feature) =>
|
||||
ol.extent.extend(extent, feature.getGeometry().getExtent())
|
||||
);
|
||||
const view = " . $this->config['id'] . ".getView();
|
||||
const resolution = " . $this->config['id'] . ".getView().getResolution();
|
||||
if (
|
||||
view.getZoom() === view.getMaxZoom() ||
|
||||
(ol.extent.getWidth(extent) < resolution && ol.extent.getHeight(extent) < resolution)
|
||||
) {
|
||||
// Show an expanded view of the cluster members.
|
||||
clickFeature = features[0];
|
||||
clickResolution = resolution;
|
||||
clusterCircles.setStyle(clusterCircleStyle);
|
||||
} else {
|
||||
// Zoom to the extent of the cluster members.
|
||||
view.fit(extent, {duration: 600, padding: [100, 100, 100, 100]});
|
||||
}
|
||||
}
|
||||
}
|
||||
popup.setPosition(undefined);
|
||||
}
|
||||
});
|
||||
";
|
||||
}
|
||||
|
||||
// grouped marker layer
|
||||
foreach ($this->layers[2] as $group_uid => $group) {
|
||||
|
||||
$jsMarker .= $group['layer'];
|
||||
$jsMarkerFeatureBatch = [];
|
||||
foreach ($group['jsMarkerFeatures'] as $id => $jsMarkerFeature) {
|
||||
$jsMarker .= 'var ' . $group_uid . $id . ' = ' . $jsMarkerFeature;
|
||||
$jsMarkerFeatureBatch[] = $group_uid . $id;
|
||||
}
|
||||
|
||||
if ($this->config['cluster']) {
|
||||
$jsMarker .= 'clusters.getSource().getSource().addFeatures([' . implode(',', $jsMarkerFeatureBatch) . ']);' . "\n";
|
||||
} else {
|
||||
$jsMarker .= $group_uid . '.getSource().addFeatures([' . implode(',', $jsMarkerFeatureBatch) . ']);' . "\n";
|
||||
$jsMarker .= 'overlaygroup.getLayers().push(' . $group_uid . ');' . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->config['cluster']) {
|
||||
// add cluster layer in overlaygroup
|
||||
$jsMarker .= 'overlaygroup.getLayers().push(clusters);' . "\n";
|
||||
}
|
||||
|
||||
return $jsMarker;
|
||||
}
|
||||
|
||||
protected function getMarker($item, $table)
|
||||
{
|
||||
$jsMarker = '';
|
||||
$jsElementVar = $table . '_' . $item['uid'];
|
||||
$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
|
||||
|
||||
// Convert item color hex value to rgba() as Openlayers doesn't have an opacity option.
|
||||
if (empty($item['color'])) {
|
||||
// set default blue, if nothing is given
|
||||
$item['color'] = '#0009ff';
|
||||
}
|
||||
if (strlen($item['color']) == 7) {
|
||||
$hex = [ $item['color'][1] . $item['color'][2], $item['color'][3] . $item['color'][4], $item['color'][5] . $item['color'][6] ];
|
||||
$rgb = array_map('hexdec', $hex);
|
||||
$opacity = '0.2';
|
||||
$item['rgba'] = 'rgba('.implode(",", $rgb).','.$opacity.')';
|
||||
}
|
||||
|
||||
switch ($table) {
|
||||
case 'tx_odsosm_track':
|
||||
$fileObjects = $fileRepository->findByRelation('tx_odsosm_track', 'file', $item['uid']);
|
||||
if ($fileObjects) {
|
||||
$file = $fileObjects[0];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
// Add tracks to layerswitcher
|
||||
$this->layers[1][] = [
|
||||
'title' => $item['title'],
|
||||
'table' => $table,
|
||||
'uid' => $item['uid']
|
||||
];
|
||||
|
||||
// define style from given color and width
|
||||
$jsMarker .= 'var ' . $jsElementVar . '_style = new ol.style.Style({
|
||||
stroke: new ol.style.Stroke({
|
||||
color: \''.$item['color'].'\',
|
||||
width: '.($item['width'] ?: 1).'
|
||||
}),
|
||||
fill: new ol.style.Fill({
|
||||
color: \''.$item['rgba'].'\'
|
||||
}),
|
||||
});';
|
||||
|
||||
switch (strtolower(pathinfo($file->getName(), PATHINFO_EXTENSION))) {
|
||||
case 'kml':
|
||||
$jsMarker .= 'var ' . $jsElementVar . '_gpx = new ol.layer.Vector({
|
||||
title: \'' .$item['title'] . '\',
|
||||
source: new ol.source.Vector({
|
||||
projection: \'EPSG:3857\',
|
||||
url: \'' . $file->getPublicUrl() . '\',
|
||||
format: new ol.format.KML()
|
||||
}),
|
||||
style: ' . $jsElementVar . '_style
|
||||
});' . "\n";
|
||||
|
||||
$jsMarker .= "overlaygroup.getLayers().push(" . $jsElementVar . "_gpx);";
|
||||
break;
|
||||
case 'gpx':
|
||||
$jsMarker .= 'var ' . $jsElementVar . '_gpx = new ol.layer.Vector({
|
||||
title: \'' .$item['title'] . '\',
|
||||
source: new ol.source.Vector({
|
||||
projection: \'EPSG:3857\',
|
||||
url: \'' . $file->getPublicUrl() . '\',
|
||||
format: new ol.format.GPX()
|
||||
}),
|
||||
style: ' . $jsElementVar . '_style
|
||||
});' . "\n";
|
||||
|
||||
$jsMarker .= "overlaygroup.getLayers().push(" . $jsElementVar . "_gpx);";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'tx_odsosm_vector':
|
||||
$fileObjects = $fileRepository->findByRelation('tx_odsosm_vector', 'file', $item['uid']);
|
||||
|
||||
// define style from given color and width
|
||||
$jsMarker .= 'var ' . $jsElementVar . '_style = new ol.style.Style({
|
||||
stroke: new ol.style.Stroke({
|
||||
color: \''.$item['color'].'\',
|
||||
width: '.($item['width'] ?: 1).'
|
||||
}),
|
||||
fill: new ol.style.Fill({
|
||||
color: \''.$item['rgba'].'\'
|
||||
}),
|
||||
});' . "\n";
|
||||
|
||||
if ($fileObjects) {
|
||||
$file = $fileObjects[0];
|
||||
$filename = $file->getPublicUrl();
|
||||
|
||||
$properties = [
|
||||
'popup' => $item['popup'] ?? '',
|
||||
'properties' => $item['properties'],
|
||||
];
|
||||
|
||||
$jsMarker .= 'var ' . $jsElementVar . '_file_properties = ' . json_encode($properties) . ';';
|
||||
$jsMarker .= 'var ' . $jsElementVar . '_file = new ol.layer.Vector({
|
||||
title: \'' .$item['title'] . ' ('. LocalizationUtility::translate('file', 'OdsOsm') .')\',
|
||||
source: new ol.source.Vector({
|
||||
projection: \'EPSG:3857\',
|
||||
url: \'' . $filename . '\',
|
||||
format: new ol.format.GeoJSON()
|
||||
}),
|
||||
style: ' . $jsElementVar . '_style,
|
||||
properties: ' . $jsElementVar . '_file_properties,
|
||||
});' . "\n";
|
||||
|
||||
$jsMarker .= $jsElementVar . "_file.getSource().setProperties(" . $jsElementVar . "_file_properties);";
|
||||
$jsMarker .= "overlaygroup.getLayers().push(" . $jsElementVar . "_file);";
|
||||
}
|
||||
|
||||
// add geojson from data field as well
|
||||
if ($item['data']) {
|
||||
$properties = [
|
||||
'popup' => $item['popup'] ? $item['popup'] . '<br />' : '',
|
||||
'properties' => $item['properties'],
|
||||
];
|
||||
|
||||
$jsMarker .= 'const ' . $jsElementVar . '_geojsonObject = '. $item['data'] . ';';
|
||||
|
||||
$jsMarker .= 'var ' . $jsElementVar . '_data_properties = ' . json_encode($properties) . ';';
|
||||
$jsMarker .= 'var ' . $jsElementVar . '_data = new ol.layer.Vector({
|
||||
title: \'' .$item['title'] . '\',
|
||||
source: new ol.source.Vector({
|
||||
features: new ol.format.GeoJSON({
|
||||
featureProjection:"EPSG:3857"
|
||||
}).readFeatures(' . $jsElementVar . '_geojsonObject)
|
||||
}),
|
||||
style: ' . $jsElementVar . '_style
|
||||
});';
|
||||
|
||||
$jsMarker .= $jsElementVar . "_data.setProperties(" . $jsElementVar . "_data_properties);";
|
||||
$jsMarker .= "overlaygroup.getLayers().push(" . $jsElementVar . "_data);";
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
$markerOptions = [];
|
||||
if ($item['tx_odsosm_marker'] ?? false) {
|
||||
$marker = $item['tx_odsosm_marker'];
|
||||
if ($marker['type'] == 'html') {
|
||||
$markerOptions['icon'] = 'icon: new L.divIcon(' . json_encode($marker['icon']) . ')';
|
||||
} else {
|
||||
$icon = $marker['icon']->getPublicUrl();
|
||||
$markerOptions['icon'] = 'icon: new L.Icon(' . json_encode($icon) . ')';
|
||||
}
|
||||
} else {
|
||||
$icon = '/typo3conf/ext/ods_osm/Resources/Public/Icons/marker-icon.png';
|
||||
$marker['size_x'] = 25;
|
||||
$marker['size_y'] = 41;
|
||||
}
|
||||
|
||||
$markerStyle = "const " . $jsElementVar . "_style = new ol.style.Style({
|
||||
image: new ol.style.Icon({
|
||||
anchor: [0.5, 46],
|
||||
anchorXUnits: 'fraction',
|
||||
anchorYUnits: 'pixels',
|
||||
src: '" . $icon ."',
|
||||
width: " . (int)$marker['size_x'] . ",
|
||||
height: " . (int)$marker['size_y'] . "
|
||||
}),
|
||||
});";
|
||||
|
||||
// It's a group of markers
|
||||
if ($item['group_title'] ?? false) {
|
||||
if (empty($jsMarkerGroup)) {
|
||||
$jsMarker .= $markerStyle;
|
||||
|
||||
$group_title = ($marker['type'] == 'html' ? $icon : "<img class='marker-icon' src='" . $icon . "' />") . ' ' . $item['group_title'];
|
||||
$jsMarkerGroup = "
|
||||
var " . $item['group_uid'] . " = new ol.layer.Vector({
|
||||
title: \"" . $group_title . "\",
|
||||
source: new ol.source.Vector({
|
||||
features: []
|
||||
}),
|
||||
style: " . $jsElementVar . "_style
|
||||
});";
|
||||
|
||||
$this->layers[2][$item['group_uid']]['layer'] = $jsMarkerGroup;
|
||||
}
|
||||
|
||||
$popupJsCode = "
|
||||
function (layer) {
|
||||
var osm_popup = '" . ($item['popup'] ?? '') . "';
|
||||
|
||||
var feature = layer.feature,
|
||||
props = feature.properties,
|
||||
ll = Object.keys(props),
|
||||
attribute, value = '';
|
||||
|
||||
for (attribute in props) {
|
||||
value += '<strong>' + attribute + '</strong>: ' + props[attribute] + '<br />';
|
||||
}
|
||||
return osm_popup + value;
|
||||
}
|
||||
";
|
||||
|
||||
$jsMarkerFeature = "
|
||||
new ol.Feature({
|
||||
geometry: new ol.geom.Point(ol.proj.fromLonLat([" . $item['longitude'] . ", " . $item['latitude'] . "])),
|
||||
type: 'Point',
|
||||
desc: " . json_encode($item['popup']) . ",
|
||||
style: " . $jsElementVar . "_style
|
||||
});";
|
||||
|
||||
$this->layers[2][$item['group_uid']]['jsMarkerFeatures'][] = $jsMarkerFeature;
|
||||
} else {
|
||||
$jsMarker .= $markerStyle;
|
||||
$jsMarker .= "var " . $jsElementVar . " = new ol.layer.Vector({
|
||||
title: '<img src=\"" .$icon . "\" class=\"marker-icon\" /> " . ($item['group_title'] ?? $item['name']) . "',
|
||||
source: new ol.source.Vector({
|
||||
features: [
|
||||
new ol.Feature({
|
||||
geometry: new ol.geom.Point(ol.proj.fromLonLat([" . $item['longitude'] . ", " . $item['latitude'] . "])),
|
||||
type: 'Point',
|
||||
desc: " . json_encode($item['popup']) . "
|
||||
})
|
||||
]
|
||||
}),
|
||||
style: " . $jsElementVar . "_style
|
||||
});";
|
||||
|
||||
$jsMarker .= 'overlaygroup.getLayers().push(' . $jsElementVar . ');' . "\n";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return $jsMarker;
|
||||
}
|
||||
}
|
||||
108
typo3conf/ext/ods_osm/Classes/Provider/Staticmap.php
Normal file
108
typo3conf/ext/ods_osm/Classes/Provider/Staticmap.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Bobosch\OdsOsm\Provider;
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
|
||||
class Staticmap extends BaseProvider
|
||||
{
|
||||
protected $uploadPath = 'fileadmin/tx_odsosm/staticmap';
|
||||
|
||||
public function getMap($layers, $markers, $lon, $lat, $zoom)
|
||||
{
|
||||
$marker = [];
|
||||
|
||||
foreach ($markers as $table => $items) {
|
||||
foreach ($items as $item) {
|
||||
switch ($table) {
|
||||
case 'tx_odsosm_track':
|
||||
case 'tx_odsosm_vector':
|
||||
break;
|
||||
default:
|
||||
$lon = $item['longitude'];
|
||||
$lat = $item['latitude'];
|
||||
if ($item['tx_odsosm_marker'] ?? false) {
|
||||
$marker = $item['tx_odsosm_marker'];
|
||||
$icon = $marker['icon'];
|
||||
} else {
|
||||
$marker = ['size_x' => 21, 'size_y' => 25, 'offset_x' => -11, 'offset_y' => -25];
|
||||
$icon = 'EXT:ods_osm/Resources/Public/Icons/marker-icon.png';
|
||||
}
|
||||
break 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set reasonable defaults for width and height (100% and vh/vw does not work with staticmap)
|
||||
if ((int)$this->config['width'] <= 100) {
|
||||
$this->config['width'] = 640;
|
||||
}
|
||||
if ((int)$this->config['height'] <= 100) {
|
||||
$this->config['height'] = 480;
|
||||
}
|
||||
|
||||
|
||||
$markerUrl = [
|
||||
'###lon###' => $lon,
|
||||
'###lat###' => $lat,
|
||||
'###zoom###' => $zoom,
|
||||
'###width###' => (int)$this->config['width'],
|
||||
'###height###' => (int)$this->config['height'],
|
||||
];
|
||||
|
||||
$layer = array_shift($layers);
|
||||
$url = strtr($layer[0]['static_url'], $markerUrl);
|
||||
|
||||
$this->uploadPath = Environment::getPublicPath() . '/' . $this->uploadPath;
|
||||
if (!is_dir($this->uploadPath)) {
|
||||
GeneralUtility::mkdir_deep($this->uploadPath);
|
||||
}
|
||||
|
||||
$filename = $this->uploadPath . '/' . md5($url) . '.png';
|
||||
|
||||
// Cache image
|
||||
$cache = false;
|
||||
if (file_exists($filename)) {
|
||||
$cache = filectime($filename) > time() - 7 * 24 * 60 * 60;
|
||||
}
|
||||
if (!$cache) {
|
||||
$referer = $_SERVER['HTTP_REFERER'];
|
||||
$opts = [
|
||||
'http'=>[
|
||||
'header'=>["Referer: $referer\r\n"]
|
||||
]
|
||||
];
|
||||
$context = stream_context_create($opts);
|
||||
$image = file_get_contents($url, false, $context);
|
||||
if ($image) {
|
||||
file_put_contents($filename, $image);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate image tag
|
||||
$config = [
|
||||
'file' => 'GIFBUILDER',
|
||||
'file.' => [
|
||||
'format' => 'png',
|
||||
'XY' => '[10.w],[10.h]',
|
||||
'10' => 'IMAGE',
|
||||
'10.' => [
|
||||
'file' => $filename,
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
if ($marker['offset_x'] ?? null) {
|
||||
$config['file.']['20'] = 'IMAGE';
|
||||
$config['file.']['20.'] = [
|
||||
'offset' => ((int)$this->config['width'] / 2 + (int)$marker['offset_x']) . ',' . ((int)$this->config['height'] / 2 + (int)$marker['offset_y']),
|
||||
'file' => $icon,
|
||||
];
|
||||
}
|
||||
|
||||
$content = $this->cObj->cObjGetSingle('IMAGE', $config);
|
||||
|
||||
return ($content);
|
||||
}
|
||||
}
|
||||
365
typo3conf/ext/ods_osm/Classes/TceMain.php
Normal file
365
typo3conf/ext/ods_osm/Classes/TceMain.php
Normal file
@@ -0,0 +1,365 @@
|
||||
<?php
|
||||
|
||||
namespace Bobosch\OdsOsm;
|
||||
|
||||
use TYPO3\CMS\Core\DataHandling\DataHandler;
|
||||
use \geoPHP\geoPHP;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Resource\FileRepository;
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
class TceMain
|
||||
{
|
||||
public $lon = [];
|
||||
public $lat = [];
|
||||
|
||||
// ['t3lib/class.t3lib_tcemain.php']['processDatamapClass']
|
||||
public function processDatamap_preProcessFieldArray(&$incomingFieldArray, $table, $id, $obj)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a different preview link *
|
||||
*
|
||||
* @param string $status status
|
||||
* @param string $table table name
|
||||
* @param int $id id of the record
|
||||
* @param array $fieldArray fieldArray
|
||||
* @param DataHandler $parentObject parent Object
|
||||
*/
|
||||
public function processDatamap_afterDatabaseOperations(
|
||||
$status,
|
||||
$table,
|
||||
$id,
|
||||
array $fieldArray,
|
||||
DataHandler $parentObject
|
||||
): void {
|
||||
// guard statement, abort here if no ods_osm table
|
||||
if (strpos($table, 'tx_odsosm_') !== 0) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* The id may be integer already or the temporary NEW id. This depends, how the record was created
|
||||
*
|
||||
* case 1:
|
||||
* - the user creates a tx_odsosm_track record
|
||||
* - the file is added to the not yet saved record
|
||||
* - the record is saved (status="new")
|
||||
*
|
||||
* case 2:
|
||||
* - the user creates a tx_odsosm_track record and saves it
|
||||
* - the user remains in the dialog and adds the file
|
||||
* - the user saves again (status="update")
|
||||
*
|
||||
* This hook is run for sys_file_reference and for tx_odsosm_track. We only do our work on tx_odsosm_track:
|
||||
* - in case 1 --> id is not integer yet but the temporary NEW id
|
||||
* - in case 2 --> id is integer
|
||||
*/
|
||||
|
||||
if ($status == "new") {
|
||||
$id = $parentObject->substNEWwithIDs[$id] ?? '';
|
||||
}
|
||||
|
||||
if (!is_int($id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If ods_osm is installed via composer, the class geoPHP is already known.
|
||||
* Otherwise we use the copy in the local folder which is only available in the TER package.
|
||||
*/
|
||||
if (!class_exists(geoPHP::class)) {
|
||||
require_once 'phar://' . ExtensionManagementUtility::extPath('ods_osm', 'Resources/Private/geophp.phar/vendor/autoload.php');
|
||||
}
|
||||
|
||||
switch ($table) {
|
||||
case 'tx_odsosm_track':
|
||||
if (is_int($id)) {
|
||||
$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
|
||||
$fileObjects = $fileRepository->findByRelation('tx_odsosm_track', 'file', $id);
|
||||
}
|
||||
if ($fileObjects) {
|
||||
$file = $fileObjects[0];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
$filename = Environment::getPublicPath() . '/' . $file->getPublicUrl();
|
||||
if (file_exists($filename)) {
|
||||
try {
|
||||
$polygon = geoPHP::load(file_get_contents($filename), pathinfo($filename, PATHINFO_EXTENSION));
|
||||
} catch (\Exception $e) {
|
||||
// silently ignore failure of parsing data
|
||||
break;
|
||||
}
|
||||
$box = $polygon->getBBox();
|
||||
|
||||
// unfortunately we cannot pass the new values by reference in this hook, because the database operation is already done.
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable($table);
|
||||
$queryBuilder
|
||||
->update('tx_odsosm_track')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('uid', $id)
|
||||
)
|
||||
->set('min_lon', sprintf('%01.6f', $box['minx']))
|
||||
->set('min_lat', sprintf('%01.6f', $box['miny']))
|
||||
->set('max_lon', sprintf('%01.6f', $box['maxx']))
|
||||
->set('max_lat', sprintf('%01.6f', $box['maxy']))
|
||||
->executeStatement();
|
||||
}
|
||||
break;
|
||||
case 'tx_odsosm_marker':
|
||||
if (is_int($id)) {
|
||||
$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
|
||||
$fileObjects = $fileRepository->findByRelation('tx_odsosm_marker', 'icon', $id);
|
||||
}
|
||||
if ($fileObjects) {
|
||||
$file = $fileObjects[0];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
$filename = Environment::getPublicPath() . '/' . $file->getPublicUrl();
|
||||
if (file_exists($filename)) {
|
||||
$size = getimagesize($filename);
|
||||
|
||||
if ($size) {
|
||||
// unfortunately we cannot pass the new values by reference in this hook, because the database operation is already done.
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable($table);
|
||||
$queryBuilder
|
||||
->update('tx_odsosm_marker')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('uid', $id)
|
||||
)
|
||||
->set('size_x', $size[0])
|
||||
->set('size_y', $size[1])
|
||||
->set('offset_x', -round($size[0] / 2))
|
||||
->set('offset_y', -$size[1])
|
||||
->executeStatement();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'tx_odsosm_vector':
|
||||
if (is_int($id)) {
|
||||
$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
|
||||
$fileObjects = $fileRepository->findByRelation('tx_odsosm_vector', 'file', $id);
|
||||
}
|
||||
if ($fileObjects) {
|
||||
$file = $fileObjects[0];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
$filename = Environment::getPublicPath() . '/' . $file->getPublicUrl();
|
||||
if (file_exists($filename)) {
|
||||
|
||||
try {
|
||||
$polygon = geoPHP::load(file_get_contents($filename), pathinfo($filename, PATHINFO_EXTENSION));
|
||||
} catch (\Exception $e) {
|
||||
// silently ignore failure of parsing geojson
|
||||
break;
|
||||
}
|
||||
|
||||
$box = $polygon->getBBox();
|
||||
if ($box) {
|
||||
// unfortunately we cannot pass the new values by reference in this hook, because the database operation is already done.
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable($table);
|
||||
|
||||
// write the bbox into database
|
||||
$queryBuilder
|
||||
->update('tx_odsosm_vector')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('uid', $id)
|
||||
)
|
||||
->set('min_lon', sprintf('%01.6f', $box['minx']))
|
||||
->set('min_lat', sprintf('%01.6f', $box['miny']))
|
||||
->set('max_lon', sprintf('%01.6f', $box['maxx']))
|
||||
->set('max_lat', sprintf('%01.6f', $box['maxy']))
|
||||
->executeStatement();
|
||||
|
||||
// handle properties
|
||||
$properties = (array)$polygon->getData();
|
||||
if (empty($properties)) {
|
||||
// seems to contain multiple polygones
|
||||
$components = $polygon->getComponents();
|
||||
// take the properties of the first polygon
|
||||
$properties = (array)$components[0]->getData();
|
||||
}
|
||||
|
||||
if (!empty($properties)) {
|
||||
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable($table);
|
||||
|
||||
$result = $queryBuilder
|
||||
->select('properties', 'properties_from_file')
|
||||
->from('tx_odsosm_vector')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('uid', $id)
|
||||
)
|
||||
->setMaxResults(1)
|
||||
->executeQuery();
|
||||
|
||||
if ($row = $result->fetchAssociative()) {
|
||||
if ($row['properties_from_file'] && !empty($properties)) {
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable($table);
|
||||
|
||||
$queryBuilder
|
||||
->update('tx_odsosm_vector')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('uid', $id)
|
||||
)
|
||||
->set('properties', implode(', ', array_keys($properties)))
|
||||
->set('properties_from_file', 0)
|
||||
->executeStatement();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ['t3lib/class.t3lib_tcemain.php']['processDatamapClass']
|
||||
public function processDatamap_postProcessFieldArray($status, $table, $id, &$fieldArray, $obj): void
|
||||
{
|
||||
switch ($table) {
|
||||
case 'tx_odsosm_vector':
|
||||
if (!empty($fieldArray['data'] ?? false)) {
|
||||
$this->lon = [];
|
||||
$this->lat = [];
|
||||
|
||||
/*
|
||||
* If ods_osm is installed via composer, the class geoPHP is already known.
|
||||
* Otherwise we use the copy in the local folder which is only available in the TER package.
|
||||
*/
|
||||
if (!class_exists(geoPHP::class)) {
|
||||
require_once 'phar://' . ExtensionManagementUtility::extPath('ods_osm', 'Resources/Private/geophp.phar/vendor/autoload.php');
|
||||
}
|
||||
|
||||
try {
|
||||
$polygon = geoPHP::load(($fieldArray['data']));
|
||||
} catch (\Exception $e) {
|
||||
// silently ignore failure of parsing geojson
|
||||
break;
|
||||
}
|
||||
|
||||
if ($polygon) {
|
||||
$box = $polygon->getBBox();
|
||||
|
||||
$fieldArray['min_lon'] = sprintf('%01.6f', $box['minx']);
|
||||
$fieldArray['min_lat'] = sprintf('%01.6f', $box['miny']);
|
||||
$fieldArray['max_lon'] = sprintf('%01.6f', $box['maxx']);
|
||||
$fieldArray['max_lat'] = sprintf('%01.6f', $box['maxy']);
|
||||
|
||||
// handle properties
|
||||
$properties = [];
|
||||
$properties = (array)$polygon->getData();
|
||||
if (empty($properties)) {
|
||||
// seems to contain multiple polygones
|
||||
$components = $polygon->getComponents();
|
||||
// take the properties of the first polygon
|
||||
$properties = (array)$components[0]->getData();
|
||||
}
|
||||
|
||||
if (!empty($properties)) {
|
||||
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable($table);
|
||||
|
||||
$result = $queryBuilder
|
||||
->select('properties', 'properties_from_file')
|
||||
->from('tx_odsosm_vector')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('uid', $id)
|
||||
)
|
||||
->setMaxResults(1)
|
||||
->executeQuery();
|
||||
|
||||
if ($row = $result->fetchAssociative()) {
|
||||
if ($row['properties_from_file'] && !empty($properties)) {
|
||||
|
||||
$fieldArray['properties'] = implode(', ', array_keys($properties));
|
||||
$fieldArray['properties_from_file'] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$fieldArray['min_lon'] = 0;
|
||||
$fieldArray['min_lat'] = 0;
|
||||
$fieldArray['max_lon'] = 0;
|
||||
$fieldArray['max_lat'] = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$tc = Div::getTableConfig($table);
|
||||
if ($tc['lon'] ?? false) {
|
||||
if (
|
||||
(isset($tc['address']) && ($fieldArray[$tc['address']] ?? null))
|
||||
|| (isset($tc['street']) && ($fieldArray[$tc['street']] ?? null))
|
||||
|| (isset($tc['zip']) && ($fieldArray[$tc['zip']] ?? null))
|
||||
|| (isset($tc['city']) && ($fieldArray[$tc['city']] ?? null))
|
||||
) {
|
||||
$config = Div::getConfig(['autocomplete']);
|
||||
// Search coordinates
|
||||
if ($config['autocomplete']) {
|
||||
// Generate address array with standard keys
|
||||
$address = [];
|
||||
foreach ($tc as $def => $field) {
|
||||
if ($def == strtolower($def)) {
|
||||
$address[$def] = $obj->datamap[$table][$id][$field];
|
||||
}
|
||||
}
|
||||
if ($config['autocomplete'] == 2 || (float) ($address['longitude'] ?? 0) == 0) {
|
||||
$ll = Div::updateAddress($address);
|
||||
if ($ll) {
|
||||
// Optimize address
|
||||
$address['lon'] = sprintf($tc['FORMAT'], $address['lon']);
|
||||
$address['lat'] = sprintf($tc['FORMAT'], $address['lat']);
|
||||
if (($address['type'] ?? false) == 'structured') {
|
||||
if (isset($tc['address']) && !isset($tc['street'])) {
|
||||
if ($address['street'] ?? false) {
|
||||
$address['address'] = $address['street'];
|
||||
if ($address['housenumber'] ?? false) {
|
||||
$address['address'] .= ' ' . $address['housenumber'];
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($tc['address'] ?? false) {
|
||||
if ($address['street'] ?? false) {
|
||||
$address['address'] = $address['street'];
|
||||
if ($address['housenumber'] ?? false) {
|
||||
$address['address'] .= ' ' . $address['housenumber'];
|
||||
}
|
||||
$address['address'] .= ', ' . $address['zip'] . ' ' . $address['city'];
|
||||
$address['address'] .= ', ' . $address['country'];
|
||||
}
|
||||
}
|
||||
|
||||
// Update fieldArray if address is set
|
||||
foreach ($tc as $def => $field) {
|
||||
if ($def != strtolower($def)) {
|
||||
continue;
|
||||
}
|
||||
if (!$address[$def]) {
|
||||
continue;
|
||||
}
|
||||
$fieldArray[$field] = $address[$def];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
374
typo3conf/ext/ods_osm/Classes/Updates/FileLocationUpdater.php
Normal file
374
typo3conf/ext/ods_osm/Classes/Updates/FileLocationUpdater.php
Normal file
@@ -0,0 +1,374 @@
|
||||
<?php
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2020 Alexander Bigga <alexander@bigga.de>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project is
|
||||
* free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
namespace Bobosch\OdsOsm\Updates;
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
|
||||
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
|
||||
use TYPO3\CMS\Install\Updates\ChattyInterface;
|
||||
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
use TYPO3\CMS\Core\Resource\ResourceStorage;
|
||||
use TYPO3\CMS\Core\Resource\StorageRepository;
|
||||
|
||||
/**
|
||||
* Migrate location of marker and track files to new, FAL-based location
|
||||
*/
|
||||
class FileLocationUpdater implements UpgradeWizardInterface, ChattyInterface, LoggerAwareInterface
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var OutputInterface
|
||||
*/
|
||||
protected $output;
|
||||
|
||||
/**
|
||||
* @var ResourceStorage
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* Array with table and fields to migrate
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldsToMigrate = [
|
||||
'tx_odsosm_marker' => 'icon',
|
||||
'tx_odsosm_track' => 'file'
|
||||
];
|
||||
|
||||
/**
|
||||
* the source file resides here
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $sourcePath = 'uploads/tx_odsosm/';
|
||||
|
||||
/**
|
||||
* target folder after migration
|
||||
* Relative to fileadmin
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $targetPath = '_migrated/tx_odsosm/';
|
||||
|
||||
/**
|
||||
* Return the identifier for this wizard
|
||||
* This must be the same string as used in the ext_localconf class registration
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return 'odsOsmFileLocationUpdater';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the speaking name of this wizard
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'EXT:ods_osm: Migrate used files to FAL';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get description
|
||||
*
|
||||
* @return string Longer description of this updater
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Move marker images and track files of EXT:ods_osm to fileadmin/_migrated/tx_odsosm/ and convert reference in records.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Is an update necessary?
|
||||
*
|
||||
* Is used to determine whether a wizard needs to be run.
|
||||
* Check if data for migration exists.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function updateNecessary(): bool
|
||||
{
|
||||
$numRecords = $this->falGetRecordsFromTable(true);
|
||||
if ($numRecords > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[] All new fields and tables must exist
|
||||
*/
|
||||
public function getPrerequisites(): array
|
||||
{
|
||||
return [
|
||||
DatabaseUpdatedPrerequisite::class
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OutputInterface $output
|
||||
*/
|
||||
public function setOutput(OutputInterface $output): void
|
||||
{
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the update
|
||||
*
|
||||
* Called when a wizard reports that an update is necessary
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function executeUpdate(): bool
|
||||
{
|
||||
$result = true;
|
||||
try {
|
||||
$numRecords = $this->falGetRecordsFromTable(true);
|
||||
if ($numRecords > 0) {
|
||||
$this->falPerformUpdate();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// If something goes wrong, migrateField() logs an error
|
||||
$result = false;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get records from table where the field to migrate is not empty (NOT NULL and != '')
|
||||
* and also not numeric (which means that it is migrated)
|
||||
*
|
||||
* Work based on BackendLayoutIconUpdateWizard::class
|
||||
*
|
||||
* @return array|int
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function falGetRecordsFromTable($countOnly = false)
|
||||
{
|
||||
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
|
||||
$allResults = [];
|
||||
$numResults = 0;
|
||||
foreach(array_keys($this->fieldsToMigrate) as $table) {
|
||||
$queryBuilder = $connectionPool->getQueryBuilderForTable($table);
|
||||
$queryBuilder->getRestrictions()->removeAll();
|
||||
try {
|
||||
$result = $queryBuilder
|
||||
->select('uid', 'pid', $this->fieldsToMigrate[$table])
|
||||
->from($table)
|
||||
->where(
|
||||
$queryBuilder->expr()->isNotNull($this->fieldsToMigrate[$table]),
|
||||
$queryBuilder->expr()->neq(
|
||||
$this->fieldsToMigrate[$table],
|
||||
$queryBuilder->createNamedParameter('', Connection::PARAM_STR)
|
||||
),
|
||||
$queryBuilder->expr()->comparison(
|
||||
'CAST(CAST(' . $queryBuilder->quoteIdentifier($this->fieldsToMigrate[$table]) . ' AS DECIMAL) AS CHAR)',
|
||||
ExpressionBuilder::NEQ,
|
||||
'CAST(' . $queryBuilder->quoteIdentifier($this->fieldsToMigrate[$table]) . ' AS CHAR)'
|
||||
)
|
||||
)
|
||||
->orderBy('uid')
|
||||
->executeQuery()
|
||||
->fetchAllAssociative();
|
||||
|
||||
if ($countOnly === true) {
|
||||
$numResults += count($result);
|
||||
} else {
|
||||
$allResults[$table] = $result;
|
||||
}
|
||||
} catch (DBALException $e) {
|
||||
throw new \RuntimeException(
|
||||
'Database query failed. Error was: ' . $e->getPrevious()->getMessage(),
|
||||
1511950673
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($countOnly === true) {
|
||||
return $numResults;
|
||||
}
|
||||
return $allResults;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs the database update.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on error
|
||||
*/
|
||||
protected function falPerformUpdate(): bool
|
||||
{
|
||||
$result = true;
|
||||
|
||||
try {
|
||||
$storages = GeneralUtility::makeInstance(StorageRepository::class)->findAll();
|
||||
$this->storage = $storages[0];
|
||||
|
||||
$records = $this->falGetRecordsFromTable();
|
||||
foreach ($records as $table => $recordsInTable) {
|
||||
foreach ($recordsInTable as $record) {
|
||||
$this->migrateField($table, $record);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$result = false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates a single field.
|
||||
*
|
||||
* @param string $table
|
||||
* @param array $row
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function migrateField($table, $row)
|
||||
{
|
||||
$fieldItem = trim($row[$this->fieldsToMigrate[$table]]);
|
||||
|
||||
if (empty($fieldItem) || is_numeric($fieldItem)) {
|
||||
return;
|
||||
}
|
||||
$fileadminDirectory = rtrim($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') . '/';
|
||||
$i = 0;
|
||||
|
||||
$storageUid = (int)$this->storage->getUid();
|
||||
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
|
||||
|
||||
$fileUid = null;
|
||||
$sourcePath = Environment::getPublicPath() . '/' . $this->sourcePath . $fieldItem;
|
||||
$targetDirectory = Environment::getPublicPath() . '/' . $fileadminDirectory . $this->targetPath;
|
||||
$targetPath = $targetDirectory . basename($fieldItem);
|
||||
|
||||
// maybe the file was already moved, so check if the original file still exists
|
||||
if (file_exists($sourcePath)) {
|
||||
if (!is_dir($targetDirectory)) {
|
||||
GeneralUtility::mkdir_deep($targetDirectory);
|
||||
}
|
||||
|
||||
// see if the file already exists in the storage
|
||||
$fileSha1 = sha1_file($sourcePath);
|
||||
|
||||
$queryBuilder = $connectionPool->getQueryBuilderForTable('sys_file');
|
||||
$existingFileRecord = $queryBuilder->select('uid')->from('sys_file')->where($queryBuilder->expr()->eq(
|
||||
'missing',
|
||||
$queryBuilder->createNamedParameter(0, Connection::PARAM_INT)
|
||||
), $queryBuilder->expr()->eq(
|
||||
'sha1',
|
||||
$queryBuilder->createNamedParameter($fileSha1, Connection::PARAM_STR)
|
||||
), $queryBuilder->expr()->eq(
|
||||
'storage',
|
||||
$queryBuilder->createNamedParameter($storageUid, Connection::PARAM_INT)
|
||||
))->executeQuery()->fetchAssociative();
|
||||
|
||||
// the file exists, the file does not have to be moved again
|
||||
if (is_array($existingFileRecord)) {
|
||||
$fileUid = $existingFileRecord['uid'];
|
||||
} else {
|
||||
// just move the file (no duplicate)
|
||||
rename($sourcePath, $targetPath);
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileUid === null) {
|
||||
// get the File object if it hasn't been fetched before
|
||||
try {
|
||||
// if the source file does not exist, we should just continue, but leave a message in the docs;
|
||||
// ideally, the user would be informed after the update as well.
|
||||
/** @var File $file */
|
||||
$file = $this->storage->getFile($this->targetPath . $fieldItem);
|
||||
$fileUid = $file->getUid();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
// no file found, no reference can be set
|
||||
$this->logger->notice(
|
||||
'File ' . $this->sourcePath . $fieldItem . ' does not exist. Reference was not migrated.',
|
||||
[
|
||||
'table' => $table,
|
||||
'record' => $row,
|
||||
'field' => $fieldItem,
|
||||
]
|
||||
);
|
||||
$format = 'File \'%s\' does not exist. Referencing field: %s.%d.%s. The reference was not migrated.';
|
||||
$this->output->writeln(sprintf(
|
||||
$format,
|
||||
$this->sourcePath . $fieldItem,
|
||||
$table,
|
||||
$row['uid'],
|
||||
$fieldItem
|
||||
));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileUid > 0) {
|
||||
$fields = [
|
||||
'fieldname' => $this->fieldsToMigrate[$table],
|
||||
'table_local' => 'sys_file',
|
||||
'pid' => ($table === 'pages' ? $row['uid'] : $row['pid']),
|
||||
'uid_foreign' => $row['uid'],
|
||||
'uid_local' => $fileUid,
|
||||
'tablenames' => $table,
|
||||
'crdate' => time(),
|
||||
'tstamp' => time(),
|
||||
'sorting_foreign' => $i,
|
||||
];
|
||||
|
||||
$queryBuilder = $connectionPool->getQueryBuilderForTable('sys_file_reference');
|
||||
$queryBuilder->insert('sys_file_reference')->values($fields)->executeStatement();
|
||||
|
||||
++$i;
|
||||
}
|
||||
|
||||
// Update referencing table's original field to now contain the count of references,
|
||||
// but only if all new references could be set
|
||||
if ($i === 1) {
|
||||
$queryBuilder = $connectionPool->getQueryBuilderForTable($table);
|
||||
$queryBuilder->update($table)->where(
|
||||
$queryBuilder->expr()->eq(
|
||||
'uid',
|
||||
$queryBuilder->createNamedParameter($row['uid'], Connection::PARAM_INT)
|
||||
)
|
||||
)->set($this->fieldsToMigrate[$table], $i)->executeStatement();
|
||||
}
|
||||
}
|
||||
}
|
||||
235
typo3conf/ext/ods_osm/Classes/Updates/MigrateSettings.php
Normal file
235
typo3conf/ext/ods_osm/Classes/Updates/MigrateSettings.php
Normal file
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2022 Alexander Bigga <alexander@bigga.de>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project is
|
||||
* free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
namespace Bobosch\OdsOsm\Updates;
|
||||
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
|
||||
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
|
||||
|
||||
/**
|
||||
* Migrate flexform settings to keep existing configuration valid.
|
||||
*/
|
||||
class MigrateSettings implements UpgradeWizardInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Return the identifier for this wizard
|
||||
* This must be the same string as used in the ext_localconf class registration
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return 'odsOsmMigrateSettings';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the speaking name of this wizard
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'EXT:ods_osm: Migrate plugin flexform settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the description for this wizard
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'This wizard migrates some flexform settings which has changed in ods_osm' .
|
||||
' extension. This makes the full reconfiguration of all used plugins obsolete.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the update
|
||||
*
|
||||
* Called when a wizard reports that an update is necessary
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function executeUpdate(): bool
|
||||
{
|
||||
// Get all tt_content data of ods_osm and update their flexforms settings
|
||||
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tt_content');
|
||||
|
||||
/** @var QueryBuilder $queryBuilder */
|
||||
$queryBuilder = $connection->createQueryBuilder();
|
||||
$statement = $queryBuilder->select('uid')
|
||||
->addSelect('pi_flexform')
|
||||
->from('tt_content')->where($queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('list')), $queryBuilder->expr()->like('list_type', $queryBuilder->createNamedParameter('ods_osm_%')))->executeQuery();
|
||||
|
||||
// Update the found record sets
|
||||
while ($record = $statement->fetchAssociative()) {
|
||||
// Robust error handling in case pi_flexform is NULL or empty
|
||||
if (!($record['pi_flexform'] ?? false)) {
|
||||
continue;
|
||||
}
|
||||
$oldXml = $record['pi_flexform'];
|
||||
$newXml = $this->migrateFlexformSettings($record['pi_flexform']);
|
||||
|
||||
if ($oldXml === $newXml) {
|
||||
// robust error handling:
|
||||
// if no change is necessary, this record was probably already converted and we skip the SQL UPDATE
|
||||
continue;
|
||||
}
|
||||
|
||||
$queryBuilder = $connection->createQueryBuilder();
|
||||
$updateResult = $queryBuilder->update('tt_content')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq(
|
||||
'uid',
|
||||
$queryBuilder->createNamedParameter($record['uid'], Connection::PARAM_INT)
|
||||
)
|
||||
)->set('pi_flexform', $newXml)->executeStatement();
|
||||
|
||||
// exit if at least one update statement is not successful
|
||||
if (!((bool) $updateResult)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is an update necessary?
|
||||
*
|
||||
* Looks for ods_osm plugins in tt_content table to be migrated
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function updateNecessary(): bool
|
||||
{
|
||||
$oldSettingsFound = false;
|
||||
|
||||
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tt_content');
|
||||
|
||||
/** @var QueryBuilder $queryBuilder */
|
||||
$queryBuilder = $connection->createQueryBuilder();
|
||||
$statement = $queryBuilder->select('uid')
|
||||
->addSelect('pi_flexform')
|
||||
->from('tt_content')->where($queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('list')), $queryBuilder->expr()->like('list_type', $queryBuilder->createNamedParameter('ods_osm_%')))->executeQuery();
|
||||
|
||||
// Update the found record sets
|
||||
while ($record = $statement->fetchAssociative()) {
|
||||
if (!($record['pi_flexform'] ?? false)) {
|
||||
continue;
|
||||
}
|
||||
$oldSettingsFound = $this->checkForOldSettings($record['pi_flexform']);
|
||||
if ($oldSettingsFound) {
|
||||
// We found at least one field to be updated --> break here
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $oldSettingsFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of class names of Prerequisite classes
|
||||
*
|
||||
* This way a wizard can define dependencies like "database up-to-date" or
|
||||
* "reference index updated"
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPrerequisites(): array
|
||||
{
|
||||
return [
|
||||
DatabaseUpdatedPrerequisite::class
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $oldValue
|
||||
* @return string|bool
|
||||
*/
|
||||
protected function migrateFlexformSettings(string $oldValue): ?string
|
||||
{
|
||||
$xml = simplexml_load_string($oldValue);
|
||||
|
||||
// if something went wrong, return.
|
||||
if ($xml === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get all field elements
|
||||
$library = $xml->xpath("//field[@index='library'][1]");
|
||||
|
||||
// get all field elements
|
||||
$fields = $xml->xpath("//field");
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if ($library[0]->value != 'staticmap' && $field['index'] == $library[0]->value . '_layer') {
|
||||
// rename base layer field to base_layer
|
||||
$field['index'] = 'base_layer';
|
||||
// Copy all layers into new 'overlays' field. This is easier here, doesn't hurt the
|
||||
// frontend and will be filtered to only real 'overlays' on next saving the plugin flexform.
|
||||
$overlays = $xml->data->sheet->language->addChild('field');
|
||||
$overlays->addAttribute('index', 'overlays');
|
||||
$overlays->addChild('value', $field->value)->addAttribute('index', 'vDEF');
|
||||
} elseif ($field['index'] != $library[0]->value . '_layer' && ($field['index'] == 'layer' ||
|
||||
$field['index'] == 'openlayers_layer' ||
|
||||
$field['index'] == 'openlayers3_layer' || $field['index'] == 'leaflet_layer')) {
|
||||
// remove all other, unused layer fields from flexform xml
|
||||
unset($field[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return $xml->asXML();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $flexFormXml
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkForOldSettings(string $flexFormXml): bool
|
||||
{
|
||||
$xml = simplexml_load_string($flexFormXml);
|
||||
|
||||
// if something went wrong, return.
|
||||
if ($xml === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for existing values of attribute "index"
|
||||
// * openlayers_layer
|
||||
// * leaflet_layer
|
||||
// * layer
|
||||
|
||||
$fields = $xml->xpath("//field[@index='openlayers_layer'] | //field[@index='leaflet_layer'] | //field[@index='layer']");
|
||||
|
||||
return (bool) $fields;
|
||||
}
|
||||
|
||||
}
|
||||
117
typo3conf/ext/ods_osm/Classes/Wizard/CoordinatepickerWizard.php
Normal file
117
typo3conf/ext/ods_osm/Classes/Wizard/CoordinatepickerWizard.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Bobosch\OdsOsm\Wizard;
|
||||
|
||||
/**
|
||||
* This file is part of the "ods_osm" Extension for TYPO3 CMS.
|
||||
* It's based on LocationMapWizard of "tt_address".
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Bobosch\OdsOsm\Div;
|
||||
use TYPO3\CMS\Backend\Form\AbstractNode;
|
||||
use TYPO3\CMS\Core\Information\Typo3Version;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\StringUtility;
|
||||
|
||||
/**
|
||||
* Adds a wizard for location selection via map
|
||||
*/
|
||||
class CoordinatepickerWizard extends AbstractNode
|
||||
{
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function render(): array
|
||||
{
|
||||
$row = $this->data['databaseRow'];
|
||||
$paramArray = $this->data['parameterArray'];
|
||||
$resultArray = $this->initializeResultArray();
|
||||
$extConfig = Div::getConfig();
|
||||
|
||||
$nameLongitude = $paramArray['itemFormElName'];
|
||||
|
||||
if (strpos($nameLongitude, '[pi_flexform]') > 0) {
|
||||
// it's a call inside a flexform
|
||||
$lon = $row["pi_flexform"]["data"]["sDEF"]["lDEF"]["lon"]["vDEF"] != '' ? htmlspecialchars($row["pi_flexform"]["data"]["sDEF"]["lDEF"]["lon"]["vDEF"]) : '';
|
||||
$lat = $row["pi_flexform"]["data"]["sDEF"]["lDEF"]["lat"]["vDEF"] != '' ? htmlspecialchars($row["pi_flexform"]["data"]["sDEF"]["lDEF"]["lat"]["vDEF"]) : '';
|
||||
} else {
|
||||
$lat = $row['tx_odsosm_lat'] != '' ? htmlspecialchars($row['tx_odsosm_lat']) : '';
|
||||
$lon = $row['tx_odsosm_lon'] != '' ? htmlspecialchars($row['tx_odsosm_lon']) : '';
|
||||
}
|
||||
|
||||
$nameLatitude = str_replace('lon', 'lat', $nameLongitude);
|
||||
$nameLatitudeActive = str_replace('data', 'control[active]', $nameLatitude);
|
||||
$geoCodeUrl = '';
|
||||
$geoCodeUrlShort = '';
|
||||
|
||||
if (empty((float)$lat) || empty((float)$lon)) {
|
||||
// remove all after first slash in address (top, floor ...)
|
||||
$address = preg_replace('/^([^\/]*).*$/', '$1', $row['address'] ?? '') . ' ';
|
||||
$address .= $row['city'] ?? '';
|
||||
// if we have at least some address part (saves geocoding calls)
|
||||
if (trim($address)) {
|
||||
// base url
|
||||
$geoCodeUrlBase = 'https://nominatim.openstreetmap.org/search/';
|
||||
$geoCodeUrlAddress = $address;
|
||||
$geoCodeUrlCityOnly = ($row['city'] ?? '');
|
||||
// urlparams for nominatim which are fixed.
|
||||
$geoCodeUrlQuery = '?format=json&addressdetails=1&limit=1&polygon_svg=1';
|
||||
// replace newlines with spaces; remove multiple spaces
|
||||
$geoCodeUrl = trim(preg_replace('/\s\s+/', ' ', $geoCodeUrlBase . $geoCodeUrlAddress . $geoCodeUrlQuery));
|
||||
$geoCodeUrlShort = trim(preg_replace('/\s\s+/', ' ', $geoCodeUrlBase . $geoCodeUrlCityOnly . $geoCodeUrlQuery));
|
||||
}
|
||||
}
|
||||
|
||||
$resultArray['iconIdentifier'] = 'coordinate-picker-wizard';
|
||||
$resultArray['title'] = $this->getLanguageService()->sL('LLL:EXT:ods_osm/Resources/Private/Language/locallang_db.xlf:coordinatepickerWizard');
|
||||
$resultArray['linkAttributes']['class'] = 'coordinatepickerWizard ';
|
||||
$resultArray['linkAttributes']['data-label-title'] = $this->getLanguageService()->sL('LLL:EXT:ods_osm/Resources/Private/Language/locallang_db.xlf:coordinatepickerWizard.title');
|
||||
$resultArray['linkAttributes']['data-label-close'] = $this->getLanguageService()->sL('LLL:EXT:ods_osm/Resources/Private/Language/locallang_db.xlf:coordinatepickerWizard.close');
|
||||
$resultArray['linkAttributes']['data-label-import'] = $this->getLanguageService()->sL('LLL:EXT:ods_osm/Resources/Private/Language/locallang_db.xlf:coordinatepickerWizard.import');
|
||||
$resultArray['linkAttributes']['data-lat'] = $lat;
|
||||
$resultArray['linkAttributes']['data-lon'] = $lon;
|
||||
$resultArray['linkAttributes']['data-default-lat'] = $extConfig['default_lat'];
|
||||
$resultArray['linkAttributes']['data-default-lon'] = $extConfig['default_lon'];
|
||||
$resultArray['linkAttributes']['data-default-zoom'] = $extConfig['default_zoom'];
|
||||
$resultArray['linkAttributes']['data-geocodeurl'] = $geoCodeUrl;
|
||||
$resultArray['linkAttributes']['data-geocodeurlshort'] = $geoCodeUrlShort;
|
||||
$resultArray['linkAttributes']['data-namelat'] = htmlspecialchars($nameLatitude);
|
||||
$resultArray['linkAttributes']['data-namelon'] = htmlspecialchars($nameLongitude);
|
||||
$resultArray['linkAttributes']['data-namelat-active'] = htmlspecialchars($nameLatitudeActive);
|
||||
$resultArray['linkAttributes']['data-tiles'] = htmlspecialchars('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png');
|
||||
$resultArray['linkAttributes']['data-copy'] = '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors';
|
||||
$resultArray['stylesheetFiles'][] = 'EXT:ods_osm/Resources/Public/JavaScript/Leaflet/Core/leaflet.css';
|
||||
$resultArray['stylesheetFiles'][] = 'EXT:ods_osm/Resources/Public/Css/Backend/leafletBackend.css';
|
||||
|
||||
$versionInformation = GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion();
|
||||
if ($versionInformation > 11) {
|
||||
$id = StringUtility::getUniqueId('t3js-formengine-fieldcontrol-');
|
||||
$resultArray['requireJsModules'][] = JavaScriptModuleInstruction::forRequireJS(
|
||||
'TYPO3/CMS/OdsOsm/Leaflet/Core/leaflet'
|
||||
)->instance($id);
|
||||
$resultArray['requireJsModules'][] = JavaScriptModuleInstruction::forRequireJS(
|
||||
'TYPO3/CMS/OdsOsm/Backend/LeafletBackend'
|
||||
)->instance($id);
|
||||
} else {
|
||||
$resultArray['requireJsModules'][] = 'TYPO3/CMS/OdsOsm/Leaflet/Core/leaflet';
|
||||
$resultArray['requireJsModules'][] = 'TYPO3/CMS/OdsOsm/Backend/LeafletBackend';
|
||||
}
|
||||
|
||||
return $resultArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LanguageService
|
||||
*/
|
||||
protected function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
95
typo3conf/ext/ods_osm/Classes/Wizard/VectordrawWizard.php
Normal file
95
typo3conf/ext/ods_osm/Classes/Wizard/VectordrawWizard.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Bobosch\OdsOsm\Wizard;
|
||||
|
||||
/**
|
||||
* This file is part of the "ods_osm" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Bobosch\OdsOsm\Div;
|
||||
use TYPO3\CMS\Backend\Form\AbstractNode;
|
||||
use TYPO3\CMS\Core\Information\Typo3Version;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\StringUtility;
|
||||
|
||||
/**
|
||||
* Adds a wizard for drawing vectors on a map
|
||||
*/
|
||||
class VectordrawWizard extends AbstractNode
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function render(): array
|
||||
{
|
||||
$row = $this->data['databaseRow'];
|
||||
$paramArray = $this->data['parameterArray'];
|
||||
$resultArray = $this->initializeResultArray();
|
||||
$extConfig = Div::getConfig();
|
||||
|
||||
$nameDataField = $paramArray['itemFormElName'];
|
||||
|
||||
// calculate center point or use Kopenhagen as fallback
|
||||
if (!empty((float)$row['max_lon']) && !empty((float)$row['min_lon'])) {
|
||||
$lon = ($row['max_lon'] + $row['min_lon']) / 2;
|
||||
} else {
|
||||
$lon = $extConfig['default_lon'];
|
||||
}
|
||||
|
||||
if (!empty((float)$row['max_lat']) && !empty((float)$row['min_lat'])) {
|
||||
$lat = ($row['max_lat'] + $row['min_lat']) / 2;
|
||||
} else {
|
||||
$lat = $extConfig['default_lat'];
|
||||
}
|
||||
|
||||
$resultArray['iconIdentifier'] = 'vectordraw-wizard';
|
||||
$resultArray['title'] = $this->getLanguageService()->sL('LLL:EXT:ods_osm/Resources/Private/Language/locallang_db.xlf:vectordrawWizard');
|
||||
$resultArray['linkAttributes']['class'] = 'vectordrawWizard ';
|
||||
$resultArray['linkAttributes']['data-label-title'] = $this->getLanguageService()->sL('LLL:EXT:ods_osm/Resources/Private/Language/locallang_db.xlf:vectordrawWizard.title');
|
||||
$resultArray['linkAttributes']['data-label-close'] = $this->getLanguageService()->sL('LLL:EXT:ods_osm/Resources/Private/Language/locallang_db.xlf:vectordrawWizard.close');
|
||||
$resultArray['linkAttributes']['data-label-import'] = $this->getLanguageService()->sL('LLL:EXT:ods_osm/Resources/Private/Language/locallang_db.xlf:vectordrawWizard.import');
|
||||
$resultArray['linkAttributes']['data-minlat'] = empty((float)$row['min_lat']) ? null : $row['min_lat'];
|
||||
$resultArray['linkAttributes']['data-maxlat'] = empty((float)$row['max_lat']) ? null : $row['max_lat'];
|
||||
$resultArray['linkAttributes']['data-minlon'] = empty((float)$row['min_lon']) ? null : $row['min_lon'];
|
||||
$resultArray['linkAttributes']['data-maxlon'] = empty((float)$row['max_lon']) ? null : $row['max_lon'];
|
||||
$resultArray['linkAttributes']['data-lat'] = $lat;
|
||||
$resultArray['linkAttributes']['data-lon'] = $lon;
|
||||
$resultArray['linkAttributes']['data-fieldName'] = htmlspecialchars($nameDataField);
|
||||
$resultArray['linkAttributes']['data-fieldValue'] = $row['data'];
|
||||
$resultArray['linkAttributes']['data-tiles'] = htmlspecialchars('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png');
|
||||
$resultArray['linkAttributes']['data-copy'] = '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors';
|
||||
$resultArray['stylesheetFiles'][] = 'EXT:ods_osm/Resources/Public/JavaScript/Leaflet/leaflet-draw/leaflet.draw.css';
|
||||
$resultArray['stylesheetFiles'][] = 'EXT:ods_osm/Resources/Public/JavaScript/Leaflet/Core/leaflet.css';
|
||||
$resultArray['stylesheetFiles'][] = 'EXT:ods_osm/Resources/Public/Css/Backend/drawvectorWizard.css';
|
||||
|
||||
$versionInformation = GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion();
|
||||
if ($versionInformation > 11) {
|
||||
$id = StringUtility::getUniqueId('t3js-formengine-fieldcontrol-');
|
||||
$resultArray['requireJsModules'][] = JavaScriptModuleInstruction::forRequireJS(
|
||||
'TYPO3/CMS/OdsOsm/Leaflet/Core/leaflet'
|
||||
)->instance($id);
|
||||
$resultArray['requireJsModules'][] = JavaScriptModuleInstruction::forRequireJS(
|
||||
'TYPO3/CMS/OdsOsm/Backend/Vectordraw'
|
||||
)->instance($id);
|
||||
} else {
|
||||
$resultArray['requireJsModules'][] = 'TYPO3/CMS/OdsOsm/Leaflet/Core/leaflet';
|
||||
$resultArray['requireJsModules'][] = 'TYPO3/CMS/OdsOsm/Backend/Vectordraw';
|
||||
}
|
||||
|
||||
return $resultArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LanguageService
|
||||
*/
|
||||
protected function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user