diff --git a/modules/edw_document/src/Services/DocumentsBulkManager.php b/modules/edw_document/src/Services/DocumentsBulkManager.php index 094d3cd..9398f52 100644 --- a/modules/edw_document/src/Services/DocumentsBulkManager.php +++ b/modules/edw_document/src/Services/DocumentsBulkManager.php @@ -83,7 +83,7 @@ public function loadEntityFromBulkFormKey($encodedKey, bool $isSearchApi = TRUE) * * @SuppressWarnings(PHPMD.ShortVariable) */ - private function bulkForm(array $keyParts) { + protected function bulkForm(array $keyParts) { // If there are 3 items, vid will be last. $revisionId = (count($keyParts) === 3) ? array_pop($keyParts) : NULL; // The first two items will always be langcode and ID. @@ -108,7 +108,7 @@ private function bulkForm(array $keyParts) { * * @see ViewsBulkOperationsBulkForm::calculateEntityBulkFormKey() */ - private function searchApiBulkForm(array $keyParts) { + protected function searchApiBulkForm(array $keyParts) { // Drop first element (the value of the base field for this view result). array_shift($keyParts); // The first three items will always be the entity type, langcode and ID. diff --git a/modules/edw_event/src/EventSubscriber/MeetingCloneSubscriber.php b/modules/edw_event/src/EventSubscriber/MeetingCloneSubscriber.php index c297139..4aa18e3 100644 --- a/modules/edw_event/src/EventSubscriber/MeetingCloneSubscriber.php +++ b/modules/edw_event/src/EventSubscriber/MeetingCloneSubscriber.php @@ -17,7 +17,7 @@ class MeetingCloneSubscriber implements EventSubscriberInterface { * * @var \Drupal\edw_event\Services\MeetingService */ - private $meetingService; + protected $meetingService; /** * Class constructor, injects the services. diff --git a/modules/edw_maps/.gitignore b/modules/edw_maps/.gitignore new file mode 100644 index 0000000..c69fb6b --- /dev/null +++ b/modules/edw_maps/.gitignore @@ -0,0 +1,5 @@ +vendor +composer.lock + +# GeoJson file is too big for GitHub. +/assets/country_boundaries/country_polygon.geojson diff --git a/modules/edw_maps/README.md b/modules/edw_maps/README.md new file mode 100644 index 0000000..9063c6b --- /dev/null +++ b/modules/edw_maps/README.md @@ -0,0 +1,113 @@ +# EDW Mapbox Integration for Drupal + +## Description +This module is designed to integrate Mapbox, a leading mapping platform, into your Drupal-powered websites. +This module provides a range of features to enhance your website with interactive and customizable maps using *views*. + +**Note:** This module requires a _Mapbox account and API key_. + +## Prerequisites +* Geofields on your contents of type latitue/logitude or Geofield WKT + +## Installation +Before enabling this module, make sure that the following modules are present in your codebase by adding them to your composer.json and running `composer update`: + +```php +"require": { + "drupal/core": "^9.4 || ^10", + "drupal/geofield": "^1.41", + "ext-zip": "*", + "itamair/geophp": "^1.5" +} +``` + +## How to use + +1. Enable the module. +2. Obtain a Mapbox API key [from your mapbox account](https://account.mapbox.com/), go to _/admin/config/system/edw_maps/settings_ and configure the module settings or add them in your _settings.local_ file: +```php + $config['edw_maps.settings']['token'] = 'pk.xxxxxxxxxxxxx'; + $config['edw_maps.settings']['default_style_url'] = 'mapbox://styles/ccac-secretariat/cljcyeoxg000601plehvh667v'; +``` +3. Create a view block, select *EDW Mapbox Map* for format and *fields* for show. +4. Configure format settings: you need to select a GeoField as a source for pins/polygons display. +5. Easily embed maps into your Drupal content using a content block. + +## How to style +1. To style markers use ``.edw-marker`` class in your css. You can choose a different icon or oen from the ``assets/icons`` folder: +```css +.edw-marker { + background-image: url('mapbox-icon.png'); + background-size: cover; + width: 50px; + height: 50px; + border-radius: 50%; + cursor: pointer; +} +``` +2. To style clusters use ``.edw-cluster-marker`` class in your css. + +## Altering tooltip data +You can display a rendered entity in a tooltip or any other field. +You can alter the contents of the _rendered entity_ using ``edw_maps`` hooks: +* to alter pin popup data: ``hook_edw_maps_pin_tooltip_data_alter()`` +* to alter country popup data: ``hook_edw_maps_country_tooltip_data_alter()`` +* to alter area popup data: ``hook_edw_maps_area_tooltip_data_alter()`` + +## GEOFields examples +For pins - point: +- WKT format: ``POINT (10.0 51.0)`` +- GeoJson format: +```JSON +{ + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [10.0, 51.0] + }, + "properties": {} +} +``` + +For areas - polygon: +- WKT format: ``POLYGON ((73.4 42.1, 79.0 42.1, 79.0 35.8, 73.4 35.8, 73.4 42.1))`` +- GeoJson format: +```JSON + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [73.4, 42.1], + [79.0, 42.1], + [79.0, 35.8], + [73.4, 35.8], + [73.4, 42.1] + ] + ] + }, + "properties": {} +} +``` + + +## Key Features + +1. **Interactive Maps:** Embed fully interactive maps on your Drupal site, allowing users to explore locations, zoom in/out, and interact with map features effortlessly. + +2. **Custom Map Styles:** Leverage the flexibility of Mapbox's styling capabilities to create visually stunning and branded maps that match your website's design aesthetics. + +3. **UN GIS support:** When displaying a map you can choose to render UN approved tiles: https://www.un.org/geospatial/mapsgeo/webservices. Keep in mind that "Carto Tile" cannot display pins. + +4. **Dynamic Marker Placement:** Easily add markers to the map to highlight key points of interest. Customize markers with icons, colors, and pop-up information to convey valuable details. + +5. **Marker clustering** + +6. **Responsive Design:** Ensure a seamless experience across devices with responsive map designs that adapt to various screen sizes, enhancing usability for both desktop and mobile users. + +7. **Drupal Configuration Integration:** Effortlessly configure and customize maps directly from the Drupal administration interface, providing administrators with a user-friendly experience. + +8. **Open Source Compatibility:** Aligning with the principles of open-source development, this module integrates seamlessly with Drupal's ecosystem, fostering collaboration and community-driven improvements. + + diff --git a/modules/edw_maps/assets/country_boundaries/country_polygon.zip b/modules/edw_maps/assets/country_boundaries/country_polygon.zip new file mode 100644 index 0000000..1a7eb4a Binary files /dev/null and b/modules/edw_maps/assets/country_boundaries/country_polygon.zip differ diff --git a/modules/edw_maps/assets/icons/filter-solid.svg b/modules/edw_maps/assets/icons/filter-solid.svg new file mode 100644 index 0000000..6cbe6d6 --- /dev/null +++ b/modules/edw_maps/assets/icons/filter-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-blue.png b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-blue.png new file mode 100644 index 0000000..8b686e2 Binary files /dev/null and b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-blue.png differ diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-gray.png b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-gray.png new file mode 100644 index 0000000..231d02f Binary files /dev/null and b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-gray.png differ diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-green.png b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-green.png new file mode 100644 index 0000000..0e557e6 Binary files /dev/null and b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-green.png differ diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-orange.png b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-orange.png new file mode 100644 index 0000000..b9ec899 Binary files /dev/null and b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-orange.png differ diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-pink.png b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-pink.png new file mode 100644 index 0000000..0d7a627 Binary files /dev/null and b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-pink.png differ diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-purple.png b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-purple.png new file mode 100644 index 0000000..db4358f Binary files /dev/null and b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-purple.png differ diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-red.png b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-red.png new file mode 100644 index 0000000..c22f07a Binary files /dev/null and b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-red.png differ diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-yellow.png b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-yellow.png new file mode 100644 index 0000000..98955c5 Binary files /dev/null and b/modules/edw_maps/assets/icons/mapbox-marker-icon-20px-yellow.png differ diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-blue.svg b/modules/edw_maps/assets/icons/mapbox-marker-icon-blue.svg new file mode 100644 index 0000000..4a9f2f2 --- /dev/null +++ b/modules/edw_maps/assets/icons/mapbox-marker-icon-blue.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-gray.svg b/modules/edw_maps/assets/icons/mapbox-marker-icon-gray.svg new file mode 100644 index 0000000..eeece86 --- /dev/null +++ b/modules/edw_maps/assets/icons/mapbox-marker-icon-gray.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-green.svg b/modules/edw_maps/assets/icons/mapbox-marker-icon-green.svg new file mode 100644 index 0000000..f181a40 --- /dev/null +++ b/modules/edw_maps/assets/icons/mapbox-marker-icon-green.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-orange.svg b/modules/edw_maps/assets/icons/mapbox-marker-icon-orange.svg new file mode 100644 index 0000000..b642d55 --- /dev/null +++ b/modules/edw_maps/assets/icons/mapbox-marker-icon-orange.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-pink.svg b/modules/edw_maps/assets/icons/mapbox-marker-icon-pink.svg new file mode 100644 index 0000000..9f9e99c --- /dev/null +++ b/modules/edw_maps/assets/icons/mapbox-marker-icon-pink.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-purple.svg b/modules/edw_maps/assets/icons/mapbox-marker-icon-purple.svg new file mode 100644 index 0000000..1f6d9c2 --- /dev/null +++ b/modules/edw_maps/assets/icons/mapbox-marker-icon-purple.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-red.svg b/modules/edw_maps/assets/icons/mapbox-marker-icon-red.svg new file mode 100644 index 0000000..3df32fa --- /dev/null +++ b/modules/edw_maps/assets/icons/mapbox-marker-icon-red.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/modules/edw_maps/assets/icons/mapbox-marker-icon-yellow.svg b/modules/edw_maps/assets/icons/mapbox-marker-icon-yellow.svg new file mode 100644 index 0000000..60c3f89 --- /dev/null +++ b/modules/edw_maps/assets/icons/mapbox-marker-icon-yellow.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/modules/edw_maps/composer.json b/modules/edw_maps/composer.json new file mode 100644 index 0000000..bbeb24a --- /dev/null +++ b/modules/edw_maps/composer.json @@ -0,0 +1,31 @@ +{ + "name": "drupal/edw_maps", + "description": "This module provides Mapbox integration with views for custom and UN tiles", + "type": "drupal-module", + "license": "GPL-2.0-or-later", + "authors": [ + { + "name": "Paula Iuga", + "email": "paula.iuga@eaudeweb.ro" + } + ], + "minimum-stability": "dev", + "homepage": "https://github.com/eaudeweb/edw_modules", + "repositories": [ + { + "type": "composer", + "url": "https://packages.drupal.org/8" + }, + { + "type": "composer", + "url": "https://asset-packagist.org" + } + ], + "require": { + "php": "^8", + "drupal/core": "^9.4 || ^10", + "drupal/geofield": "^1.41", + "ext-zip": "*", + "itamair/geophp": "^1.5" + } +} diff --git a/modules/edw_maps/config/install/edw_maps.settings.yml b/modules/edw_maps/config/install/edw_maps.settings.yml new file mode 100644 index 0000000..be8983e --- /dev/null +++ b/modules/edw_maps/config/install/edw_maps.settings.yml @@ -0,0 +1,2 @@ +token: '' +default_style_url: '' diff --git a/modules/edw_maps/config/schema/edw_maps.schema.yml b/modules/edw_maps/config/schema/edw_maps.schema.yml new file mode 100644 index 0000000..9f8e7a7 --- /dev/null +++ b/modules/edw_maps/config/schema/edw_maps.schema.yml @@ -0,0 +1,10 @@ +edw_maps.settings: + type: config_object + label: 'Edw maps settings' + mapping: + token: + type: string + label: 'Token' + default_style_url: + type: string + label: 'Default Mapbox Style URL' diff --git a/modules/edw_maps/css/edw_mapbox.css b/modules/edw_maps/css/edw_mapbox.css new file mode 100644 index 0000000..1128f34 --- /dev/null +++ b/modules/edw_maps/css/edw_mapbox.css @@ -0,0 +1,87 @@ +.map-container { + width: 100%; + height: 600px; +} + +@media screen and (max-width: 768px) { + .map-container { + height: 400px; + } +} + +@media screen and (max-width: 480px) { + .map-container { + height: 300px; + } +} + +.edw-marker { + background-image: url('../assets/icons/mapbox-marker-icon-yellow.svg'); + background-size: contain; + width: 20px; + height: 28px; + cursor: pointer; +} + +.edw-cluster-marker { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + background-color: #FFD700; + border-radius: 100%; + font-size: 14px; + font-weight: 420; + color: black; + border: 1px solid white; +} + +.view-mapbox { + position: relative; +} + +form.exposed-mapbox-filters { + display: none; + position: absolute; + top: 0; + right: 0; + z-index: 2; + box-shadow: 0 3px 10px rgb(0 0 0 / 0.2); + background: #ffff; + padding: 2.5rem 3.125rem; + margin: 1.5rem; +} + +form.exposed-mapbox-filters.form-visible { + display: block; +} + +form .close-button { + position: absolute; + top: 0; + right: 10px; + cursor: pointer; + background-color: transparent; + border: none; + padding: 0; + font-size: 32px; + margin-right: 10px; +} + +#toggleFiltersForm { + top: 0; + position: absolute; + z-index: 1; + right: 0; + margin: 1rem; + border: none; + background: #ffff; + border-radius: 2px; + display: none; + padding-left: 8px; + padding-right: 8px; +} + +#toggleFiltersForm.button-visible { + display: block; +} diff --git a/modules/edw_maps/edw_maps.info.yml b/modules/edw_maps/edw_maps.info.yml new file mode 100644 index 0000000..b7aedd3 --- /dev/null +++ b/modules/edw_maps/edw_maps.info.yml @@ -0,0 +1,8 @@ +name: EDW Maps +description: This module provides Mapbox integration with views for custom and UN tiles. +package: Eau de Web +type: module +core_version_requirement: ^9 || ^10 +dependencies: + - drupal:views + - drupal:geofield diff --git a/modules/edw_maps/edw_maps.install b/modules/edw_maps/edw_maps.install new file mode 100644 index 0000000..3955813 --- /dev/null +++ b/modules/edw_maps/edw_maps.install @@ -0,0 +1,15 @@ +unzipGeoJson(); +} diff --git a/modules/edw_maps/edw_maps.libraries.yml b/modules/edw_maps/edw_maps.libraries.yml new file mode 100644 index 0000000..44cc87c --- /dev/null +++ b/modules/edw_maps/edw_maps.libraries.yml @@ -0,0 +1,22 @@ +mapbox: + css: + theme: + https://api.mapbox.com/mapbox-gl-js/v3.0.0/mapbox-gl.css: { minified: true } + js: + https://api.mapbox.com/mapbox-gl-js/v3.0.0/mapbox-gl.js: { attributes: { defer: true }, minified: true } + +supercluster: + js: + https://unpkg.com/supercluster@8.0.0/dist/supercluster.min.js: { minified: true } + +edw_map: + css: + theme: + css/edw_mapbox.css: { } + js: + js/edw_mapbox.js: { preprocess: false } + dependencies: + - core/jquery + - core/drupal + - edw_maps/mapbox + - edw_maps/supercluster diff --git a/modules/edw_maps/edw_maps.module b/modules/edw_maps/edw_maps.module new file mode 100644 index 0000000..76527ef --- /dev/null +++ b/modules/edw_maps/edw_maps.module @@ -0,0 +1,73 @@ + [ + 'variables' => [ + 'mapContainerId' => NULL, + 'exposedFilters' => FALSE, + ], + ], + ]; +} + +/** + * Implements hook_preprocess_views_view(). + */ +function edw_maps_preprocess_views_view(&$variables) { + if (!isset($variables['view'])) { + return; + } + + if ($variables['view']->style_plugin instanceof MapboxMapStyle) { + if (!empty($variables['exposed'])) { + $variables['exposed']['#attributes']['class'][] = 'exposed-mapbox-filters'; + $variables['attributes']['class'][] = 'view-mapbox'; + } + } +} + +/** + * Implements hook_ajax_render_alter(). + */ +function edw_maps_ajax_render_alter(array &$data) { + foreach ($data as &$command) { + if ($command['command'] === 'settings' + && isset($command['settings']['edw_map']) + && isset($command['settings']['views'])) { + $command['merge'] = FALSE; + return; + } + } +} + +/** + * Implements hook_edw_maps_pin_tooltip_data_alter(). + */ +function edw_maps_edw_maps_pin_tooltip_data_alter(array &$variables) { + // Function to alter pin tooltip data if rendered entity is used. +} + +/** + * Implements hook_edw_maps_country_tooltip_data_alter(). + */ +function edw_maps_edw_maps_country_tooltip_data_alter(array &$variables) { + // Function to alter country tooltip data if rendered entity is used. +} + +/** + * Implements hook__edw_maps_area_tooltip_data_alter(). + */ +function edw_maps_edw_maps_area_tooltip_data_alter(array &$variables) { + // Function to alter area tooltip data if rendered entity is used. +} diff --git a/modules/edw_maps/edw_maps.routing.yml b/modules/edw_maps/edw_maps.routing.yml new file mode 100644 index 0000000..0baf1de --- /dev/null +++ b/modules/edw_maps/edw_maps.routing.yml @@ -0,0 +1,7 @@ +edw_maps.admin_settings_form: + path: '/admin/config/system/edw_maps' + defaults: + _form: '\Drupal\edw_maps\Form\EdwMapsSettingsForm' + _title: EDW Maps Settings + requirements: + _permission: 'administer modules' diff --git a/modules/edw_maps/edw_maps.services.yml b/modules/edw_maps/edw_maps.services.yml new file mode 100644 index 0000000..c86c04a --- /dev/null +++ b/modules/edw_maps/edw_maps.services.yml @@ -0,0 +1,4 @@ +services: + edw_maps.utils: + class: Drupal\edw_maps\Services\EdwMapsDataService + arguments: [ '@entity_type.manager', '@renderer', '@module_handler' ] diff --git a/modules/edw_maps/js/edw_mapbox.js b/modules/edw_maps/js/edw_mapbox.js new file mode 100644 index 0000000..3489819 --- /dev/null +++ b/modules/edw_maps/js/edw_mapbox.js @@ -0,0 +1,758 @@ +(function ($, Drupal) { + Drupal.behaviors.edw_map = { + attach: function (context, settings) { + $(function () { + const mapboxStyleUrl = settings.edw_map.mapboxStyleUrl; + const mapType = settings.edw_map.mapType; + const containerId = settings.edw_map.containerId; + const renderPins = settings.edw_map.renderPins; + const renderClusters = settings.edw_map.renderClusters; + const renderCountries = settings.edw_map.renderCountries; + const renderAreas = settings.edw_map.renderAreas; + const pinData = settings.edw_map.pinData; + const countryData = settings.edw_map.countryData; + const areaData = settings.edw_map.areaData; + const countryColor = settings.edw_map.countryColor; + const areaColor = settings.edw_map.areaColor; + const clearMapSource = settings.edw_map.clearMapSource; + const maxZoom = settings.edw_map.maxZoom; + let hoverCountryColor = settings.edw_map.countryHoverColor; + let hoverAreaColor = settings.edw_map.areaHoverColor; + const hoverPopups = settings.edw_map.hoverPopups; + const countryLinks = settings.edw_map.countryLinks; + const baseCountryCarto = ['rgb', 237, 237, 237]; + const lineCarto = ['rgb', 165, 165, 165]; + + // True when map finished rendering clusters. + let ready = false; + let clusters = []; + let markers = []; + let featurePoints = []; + let hoveredStateId = null; + let currentPopup = null; + let sourceLayer = mapType === 'custom' ? 'country_boundaries' : ''; + if (mapType === 'carto_tile') { + sourceLayer = 'bnda'; + } + + // Render map only once. + if (!$(`#${containerId}:empty`).length) { + return; + } + + exposedForm(); + + // Set access token. + mapboxgl.accessToken = settings.edw_map.mapboxToken; + + // Create map. + const map = new mapboxgl.Map({ + container: containerId, // container ID + style: getMapStyle(), // map style url + center: settings.edw_map.center, // starting position + zoom: settings.edw_map.zoom, // starting zoom + maxZoom: maxZoom, + pitch: settings.edw_map.pitch, // angle towards the horizon, + cooperativeGestures: true, + renderWorldCopies: settings.edw_map.worldCopies, + projection: settings.edw_map.projection + }); + + if (settings.edw_map.disableScrollZoom) { + map.scrollZoom.disable(); + } + + // Create navigation control. + const nav = new mapboxgl.NavigationControl({ + showCompass: false + }); + map.addControl(nav, 'bottom-left'); + + // Get feature points for cluster rendering. + if (renderPins && renderClusters) { + getPointsCoordinates(); + } + + // Load country boundaries layer. + map.on('load', () => { + addBoundariesSources(); + addMapExtraLayers(); + draw(); + + map.getCanvas().style.cursor = 'default'; + }); + + // Adds close button on exposed form. + function exposedForm() { + const form = $('.exposed-mapbox-filters'); + const filtersBtn = $('#toggleFiltersForm'); + if (form.length === 0) { + return; + } + const closeButton = $(' +{% endif %} + + +