3
0
Fork 0
mirror of https://github.com/farmOS/farmOS.git synced 2024-02-23 11:37:38 +01:00

Merge branch '2.x-map_asset_behavior' into 2.x

Issue #3188579 by paul121: Create map behaviors to view Locations
Issue #3188582 by paul121: Create map behavior for current asset location
This commit is contained in:
Michael Stenta 2021-04-08 15:48:26 -04:00
commit 05c677a4fe
33 changed files with 1304 additions and 78 deletions

View file

@ -28,7 +28,7 @@
"drupal/entity_reference_revisions": "^1.8",
"drupal/entity_reference_validators": "^1.0@alpha",
"drupal/fraction": "2.x-dev",
"drupal/geofield": "^1.15",
"drupal/geofield": "^1.22",
"drupal/gin": "3.x-dev",
"drupal/inline_entity_form": "^1.0@RC",
"drupal/jsonapi_extras": "^3.15",
@ -39,6 +39,7 @@
"drupal/simple_oauth": "^5.0",
"drupal/state_machine": "^1.0",
"drupal/token": "^1.7",
"drupal/views_geojson": "^1.1",
"drush/drush": "^10.3",
"npm-asset/farmos.org--farmos-map": "^1.4"
},
@ -64,6 +65,9 @@
"Issue #3167287: Always load clients through the Client Repository service": "https://www.drupal.org/files/issues/2020-09-29/3167287-9.patch",
"Issue #3174572: Passing a third argument to headers->get() is deprecated": "https://www.drupal.org/files/issues/2020-10-02/3174572-2.patch",
"Issue #3186301: Make $modules variable protected in tests": "https://www.drupal.org/files/issues/2020-12-03/3186301-2.patch"
},
"drupal/views_geojson": {
"Issue #3177013: Missing configuration schema.": "https://www.drupal.org/files/issues/2020-10-15/views_geojson-missing-config-schema-3177013.patch"
}
}
}

View file

@ -35,6 +35,9 @@ http {
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
proxy_pass http://www;
}
}

View file

@ -5,6 +5,9 @@
* Land asset module.
*/
use Drupal\farm_land\Entity\FarmLandType;
use Drupal\views\ViewExecutable;
/**
* Allowed values callback function for the land type field.
*
@ -20,3 +23,54 @@ function farm_land_type_field_allowed_values() {
}
return $allowed_values;
}
/**
* Implements hook_views_pre_render().
*/
function farm_land_views_pre_render(ViewExecutable $view) {
// Add land type map layers to the land assets map.
if ($view->id() == 'farm_asset' && $view->current_display == 'page_type' && !empty($view->args[0]) && $view->args[0] == 'land') {
// If the asset_map has not been added, bail.
if (empty($view->attachment_before['asset_map'])) {
return;
}
$map = &$view->attachment_before['asset_map'];
// Load all land types.
$land_types = FarmLandType::loadMultiple();
// Get exposed filters.
$exposed_filters = $view->getExposedInput();
// If the land type exposed filter is already in use remove land types
// that are not included.
if (!empty($exposed_filters['land_type_value'])) {
$land_types = array_filter($land_types, function ($key) use ($exposed_filters) {
return in_array($key, $exposed_filters['land_type_value']);
}, ARRAY_FILTER_USE_KEY);
}
// Create a layer for each land type.
$asset_layers = [];
foreach ($land_types as $land_type) {
$asset_layers['land_' . $land_type->id()] = [
'group' => t('Land types'),
'label' => $land_type->label(),
'asset_type' => 'land',
'filters' => $exposed_filters + ['land_type_value[]' => $land_type->id()],
// @todo Color each differently.
// This was previously provided with hook_farm_area_type_info.
'color' => 'orange',
'zoom' => TRUE,
];
}
// Add layers to the map settings.
$map['#map_settings']['asset_type_layers'] = array_merge($map['#map_settings']['asset_type_layers'], $asset_layers);
// Remove the land asset layer.
unset($map['#map_settings']['asset_type_layers']['full_land']);
}
}

View file

@ -5,6 +5,9 @@
* Structure asset module.
*/
use Drupal\farm_structure\Entity\FarmStructureType;
use Drupal\views\ViewExecutable;
/**
* Allowed values callback function for the structure type field.
*
@ -20,3 +23,54 @@ function farm_structure_type_field_allowed_values() {
}
return $allowed_values;
}
/**
* Implements hook_views_pre_render().
*/
function farm_structure_views_pre_render(ViewExecutable $view) {
// Add structure type map layers to the structure assets map.
if ($view->id() == 'farm_asset' && $view->current_display == 'page_type' && !empty($view->args[0]) && $view->args[0] == 'structure') {
// If the asset_map has not been added, bail.
if (empty($view->attachment_before['asset_map'])) {
return;
}
$map = &$view->attachment_before['asset_map'];
// Load all structure types.
$structure_types = FarmStructureType::loadMultiple();
// Get exposed filters.
$exposed_filters = $view->getExposedInput();
// If the structure type exposed filter is already in use remove structure
// types that are not included.
if (!empty($exposed_filters['structure_type_value'])) {
$structure_types = array_filter($structure_types, function ($key) use ($exposed_filters) {
return in_array($key, $exposed_filters['structure_type_value']);
}, ARRAY_FILTER_USE_KEY);
}
// Create a layer for each structure type.
$asset_layers = [];
foreach ($structure_types as $structure_type) {
$asset_layers['structure_' . $structure_type->id()] = [
'group' => t('Structure types'),
'label' => $structure_type->label(),
'asset_type' => 'structure',
'filters' => $exposed_filters + ['structure_type_value[]' => $structure_type->id()],
// @todo Color each differently.
// This was previously provided with hook_farm_area_type_info.
'color' => 'orange',
'zoom' => TRUE,
];
}
// Add layers to the map settings.
$map['#map_settings']['asset_type_layers'] = array_merge($map['#map_settings']['asset_type_layers'], $asset_layers);
// Remove the structure asset layer.
unset($map['#map_settings']['asset_type_layers']['full_structure']);
}
}

View file

@ -0,0 +1,607 @@
langcode: en
status: true
dependencies:
enforced:
module:
- farm_location
module:
- asset
- farm_location
- geofield
- rest
- user
- views_geojson
id: farm_asset_geojson
label: 'Farm Asset GeoJSON'
module: views
description: ''
tag: ''
base_table: asset_field_data
base_field: id
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: perm
options:
perm: 'view any asset'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: mini
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous:
next:
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
id:
id: id
table: asset_field_data
field: id
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: number_integer
settings:
thousand_separator: ''
prefix_suffix: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: asset
entity_field: id
plugin_id: field
name:
table: asset_field_data
field: name
id: name
entity_type: null
entity_field: name
plugin_id: field
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings: { }
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
geometry:
id: geometry
table: asset
field: geometry
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: geofield_default
settings:
output_format: wkt
output_escape: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: asset
plugin_id: asset_geometry
filters: { }
sorts: { }
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
type:
id: type
table: asset_field_data
field: type
relationship: none
group_type: group
admin_label: ''
default_action: default
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: all
default_argument_skip_url: false
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: true
validate:
type: 'entity:asset_type'
fail: 'not found'
validate_options:
operation: view
multiple: 1
access: false
bundles: { }
glossary: false
limit: 0
case: none
path_case: none
transform_dash: false
break_phrase: true
entity_type: asset
entity_field: type
plugin_id: string
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- user.permissions
tags: { }
centroid:
display_plugin: geojson_export
id: centroid
display_title: 'Centroid (GeoJSON export)'
position: 2
display_options:
display_extenders: { }
display_description: ''
path: assets/geojson/centroid/%
pager:
type: none
options:
offset: 0
style:
type: geojson
options:
data_source:
value: wkt
latitude: geometry
longitude: geometry
geofield: geometry
geolocation: id
wkt: geometry
name_field: name
description_field: ''
jsonp_prefix: ''
fields:
id:
id: id
table: asset_field_data
field: id
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: number_integer
settings:
thousand_separator: ''
prefix_suffix: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: asset
entity_field: id
plugin_id: field
name:
table: asset_field_data
field: name
id: name
entity_type: null
entity_field: name
plugin_id: field
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings: { }
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
geometry:
id: geometry
table: asset
field: geometry
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: geofield_latlon
settings:
output_format: wkt
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: asset
plugin_id: asset_geometry
defaults:
fields: false
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- user.permissions
tags: { }
full:
display_plugin: geojson_export
id: full
display_title: 'Full (GeoJSON export)'
position: 1
display_options:
display_extenders: { }
display_description: ''
path: assets/geojson/full/%
pager:
type: none
options:
offset: 0
style:
type: geojson
options:
data_source:
value: geofield
latitude: id
longitude: id
geofield: geometry
geolocation: id
wkt: id
name_field: name
description_field: ''
jsonp_prefix: ''
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- user.permissions
tags: { }

View file

@ -1,4 +1,7 @@
# Schema for the asset location views field handler.
# Schema for the asset location/geometry views field handlers.
views.field.asset_location:
type: views.field.field
label: 'Asset location field handler'
views.field.asset_geometry:
type: views.field.field
label: 'Asset geometry field handler'

View file

@ -8,3 +8,4 @@ dependencies:
- farm:farm_field
- farm:farm_log
- geofield:geofield
- views_geojson:views_geojson

View file

@ -55,15 +55,29 @@ function farm_location_entity_base_field_info(EntityTypeInterface $entity_type)
*/
function farm_location_views_data_alter(array &$data) {
// Add the current asset location computed field to Views.
if (isset($data['asset'])) {
$data['asset']['location'] = [
// Define the asset computed fields.
$asset_computed_fields = [
'geometry' => [
'title' => t('Geometry'),
'field' => [
'id' => 'asset_geometry',
'field_name' => 'geometry',
],
],
'location' => [
'title' => t('Current location'),
'field' => [
'id' => 'asset_location',
'field_name' => 'location',
],
];
],
];
// Add asset computed fields.
if (isset($data['asset'])) {
foreach ($asset_computed_fields as $field_id => $field_data) {
$data['asset'][$field_id] = $field_data;
}
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace Drupal\farm_location\Plugin\views\field;
use Drupal\views\Plugin\views\field\EntityField;
/**
* A field that displays asset geometry.
*
* @ingroup views_field_handlers
*
* @ViewsField("asset_geometry")
*/
class AssetGeometry extends EntityField {
}

View file

@ -7,4 +7,5 @@ dependencies:
id: default
label: Default
description: 'The default farmOS map.'
behaviors: { }
options: { }

View file

@ -7,4 +7,5 @@ dependencies:
id: geofield
label: Geofield
description: 'Renders geofield using farmOS-map.'
behaviors: { }
options: { }

View file

@ -7,4 +7,5 @@ dependencies:
id: geofield_widget
label: Geofield widget
description: 'Renders a geofield widget using farmOS-map.'
behaviors: { }
options: { }

View file

@ -12,6 +12,13 @@ farm_map.map_type.*:
description:
type: text
label: 'Description'
behaviors:
type: sequence
label: 'Map behaviors'
nullable: true
sequence:
type: string
label: 'Map behavior id'
options:
type: sequence
label: 'farmOS-map options'

View file

@ -1,5 +1,4 @@
farmOS-map:
version: 1.4.1
remote: https://github.com/farmOS/farmOS-map
license:
name: MIT

View file

@ -17,14 +17,3 @@ function farm_map_theme($existing, $type, $theme, $path) {
],
];
}
/**
* Implements hook_farm_dashboard_panes().
*/
function farm_map_farm_dashboard_panes() {
return [
'dashboard_map' => [
'block' => 'dashboard_map',
],
];
}

View file

@ -28,7 +28,7 @@
var description = feature.get('description') || '';
var measurement = instance.measureGeometry(feature.getGeometry(), instance.units);
if (name !== '' || measurement !== '' || description !== '') {
content = '<div class="ol-popup-content"><h4 class="ol-popup-name">' + name + '</h4><div class="ol-popup-measurement"><small>' + measurement + '</small></div></div><div class="ol-popup-description">' + description + '</div></div>';
content = '<h4 class="ol-popup-name">' + name + '</h4><div class="ol-popup-measurement"><small>' + measurement + '</small></div><div class="ol-popup-description">' + description + '</div>';
}
}
return content;

View file

@ -33,10 +33,12 @@
// Enable the line/polygon measure behavior.
instance.addBehavior('measure', { layer: layer });
// If zoom is true and the layer has features, zoom to them.
// If the layer has features, zoom to them.
// Otherwise, zoom to all vectors.
if (drupalSettings.farm_map[instance.target].behaviors.wkt.zoom && layer !== undefined) {
if (layer !== undefined) {
instance.zoomToLayer(layer);
} else {
instance.zoomToVectors();
}
},
weight: 100,

View file

@ -21,6 +21,7 @@ use Drupal\Core\Config\Entity\ConfigEntityBase;
* "id",
* "label",
* "description",
* "behaviors",
* "options",
* },
* )
@ -50,6 +51,13 @@ class MapType extends ConfigEntityBase implements MapTypeInterface {
*/
protected $description;
/**
* Behaviors to add to the map.
*
* @var string[]
*/
protected $behaviors;
/**
* The options to pass to farmOS-map.
*
@ -78,6 +86,13 @@ class MapType extends ConfigEntityBase implements MapTypeInterface {
return $this->set('description', $description);
}
/**
* {@inheritdoc}
*/
public function getMapBehaviors() {
return $this->behaviors ?? [];
}
/**
* {@inheritdoc}
*/

View file

@ -10,6 +10,14 @@ use Drupal\Core\Entity\EntityDescriptionInterface;
*/
interface MapTypeInterface extends ConfigEntityInterface, EntityDescriptionInterface {
/**
* Returns behaviors to add to the map.
*
* @return string[]
* The list of map behaviors.
*/
public function getMapBehaviors();
/**
* Returns the options to pass to farmOS-map.
*

View file

@ -41,6 +41,16 @@ class MapRenderEvent extends Event {
$this->mapType = $map_type;
}
/**
* Getter method to get the map target ID.
*
* @return string
* The map target ID.
*/
public function getMapTargetId() {
return $this->element['#attributes']['id'];
}
/**
* Getter method to get the map type being rendered.
*
@ -92,7 +102,7 @@ class MapRenderEvent extends Event {
if (!empty($this->element['#attached']['drupalSettings']['farm_map'])) {
$existing = $this->element['#attached']['drupalSettings']['farm_map'];
}
$this->element['#attached']['drupalSettings']['farm_map'] = array_merge_recursive($settings, $existing);
$this->element['#attached']['drupalSettings']['farm_map'] = array_replace_recursive($existing, $settings);
}
/**

View file

@ -8,7 +8,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* An event subscriber for the MapRenderEvent.
*
* Adds the wkt and geofield behaviors to necessary maps.
* Adds default behaviors to maps.
*/
class MapRenderEventSubscriber implements EventSubscriberInterface {
@ -29,6 +29,15 @@ class MapRenderEventSubscriber implements EventSubscriberInterface {
*/
public function onMapRender(MapRenderEvent $event) {
// Add the map type cache tags.
$event->addCacheTags($event->getMapType()->getCacheTags());
// Include map behaviors defined by the map type.
$map_behaviors = $event->getMapType()->getMapBehaviors();
foreach ($map_behaviors as $behavior) {
$event->addBehavior($behavior);
}
// Add the WKT behavior if the render element has WKT.
if (!empty($event->element['#map_settings']['wkt'])) {
$event->addBehavior('wkt');

View file

@ -5,6 +5,7 @@ package: farmOS UI
core_version_requirement: ^9
dependencies:
- farm:farm_ui_action
- farm:farm_ui_map
- farm:farm_ui_menu
- farm:farm_ui_theme
- farm:farm_ui_user

View file

@ -1,6 +1,9 @@
langcode: en
status: false
dependencies:
enforced:
module:
- farm_dashboard
module:
- farm_map
theme:
@ -15,6 +18,6 @@ settings:
id: dashboard_map
map_type: dashboard
label: 'Dashboard map'
provider: farm_map
provider: farm_dashboard
label_display: '0'
visibility: { }

View file

@ -0,0 +1,11 @@
langcode: en
status: true
dependencies:
enforced:
module:
- farm_ui_map
id: asset_type_layers
label: Asset type layers
description: 'Displays asset geometries in layers by asset type.'
library: 'farm_ui_map/behavior_asset_type_layers'
settings: { }

View file

@ -0,0 +1,12 @@
langcode: en
status: true
dependencies:
enforced:
module:
- farm_ui_map
id: asset_list
label: Asset list
description: 'The map displayed on lists of assets.'
behaviors:
- asset_type_layers
options: { }

View file

@ -3,8 +3,9 @@ status: true
dependencies:
enforced:
module:
- farm_map
- farm_dashboard
id: dashboard
label: Dashboard
description: 'The farmOS dashboard map.'
options: { }
behaviors: { }
options: { }

View file

@ -0,0 +1,11 @@
name: farmOS UI Map
description: Provides default farmOS maps and behaviors.
type: module
package: farmOS UI
core_version_requirement: ^9
dependencies:
- farm:asset
- farm:farm_dashboard
- farm:farm_location
- farm:farm_map
- farm:farm_ui_views

View file

@ -0,0 +1,5 @@
behavior_asset_type_layers:
js:
js/farmOS.map.behaviors.asset_type_layers.js: { }
dependencies:
- farm_map/farm_map

View file

@ -0,0 +1,156 @@
<?php
/**
* @file
* The farmOS UI Map module.
*/
use Drupal\views\Entity\View;
use Drupal\views\ViewExecutable;
/**
* Implements hook_farm_dashboard_panes().
*/
function farm_ui_map_farm_dashboard_panes() {
return [
'dashboard_map' => [
'block' => 'dashboard_map',
],
];
}
/**
* Implements hook_views_pre_view().
*/
function farm_ui_map_views_pre_view(ViewExecutable $view, $display_id, array &$args) {
// Alter the farm_asset_geojson View's full and centroid displays to add all
// exposed filters that are present in the farm_asset View.
if ($view->id() == 'farm_asset_geojson' && in_array($display_id, ['full', 'centroid'])) {
// Load the farm_asset View. Bail if unavailable.
$farm_asset_view = View::load('farm_asset');
if (empty($farm_asset_view)) {
return;
}
// Copy all exposed filters from the default display.
$display = $farm_asset_view->getDisplay('default');
if (!empty($display['display_options']['filters'])) {
foreach ($display['display_options']['filters'] as $field => $filter) {
$view->addHandler($display_id, 'filter', $filter['table'], $field, $filter, $filter['id']);
}
}
// If a type argument is present, add bundle-specific exposed filters.
if (!empty($args[0]) && $args[0] != 'all') {
farm_ui_views_add_bundle_handlers($view, $display_id, $args[0], 'filter');
}
}
}
/**
* Implements hook_views_pre_render().
*/
function farm_ui_map_views_pre_render(ViewExecutable $view) {
// Render a map attachment above views of assets.
if ($view->id() == 'farm_asset' && in_array($view->current_display, ['page', 'page_type'])) {
// Get all asset bundles.
$asset_bundles = \Drupal::service('entity_type.bundle.info')->getBundleInfo($view->getBaseEntityType()->id());
// Start array of filtered bundles.
$filtered_bundles = $asset_bundles;
// Start array of asset layers to add.
$asset_layers = [
'full' => [],
];
// Save the group labels.
$layer_group = $view->getBaseEntityType()->getCollectionLabel();
// Add multiple asset layers to the page of all assets.
if ($view->current_display == 'page') {
// Limit to filtered asset types.
if (!empty($exposed_filters['type'])) {
$filtered_bundles = array_intersect_key($asset_bundles, array_flip($exposed_filters['type']));
}
}
// Get exposed filters.
$exposed_filters = $view->getExposedInput();
// Determine if we are filtering by bundle.
// This may happen via contextual filter on the "page_type" display, or via
// the exposed "type" filter.
$bundle_filters = [];
if ($view->current_display == 'page_type' && !empty($view->args[0])) {
$bundle_filters[] = $view->args[0];
}
elseif (!empty($exposed_filters['type'])) {
foreach ($exposed_filters['type'] as $bundle) {
$bundle_filters[] = $bundle;
}
}
// Filter by bundle, if desired.
if (!empty($bundle_filters)) {
$filtered_bundles = [];
foreach ($bundle_filters as $bundle) {
$filtered_bundles[$bundle] = $asset_bundles[$bundle];
}
}
// Add a cluster layer for summarizing asset counts.
$asset_layers['cluster']['all'] = [
'label' => t('Asset counts'),
'cluster' => TRUE,
'filters' => $exposed_filters,
];
if (!empty($bundle_filters)) {
$asset_layers['cluster']['all']['asset_type'] = implode('+', $bundle_filters);
}
// Add a full asset geometry layer for each asset type.
foreach ($filtered_bundles as $bundle => $bundle_info) {
$asset_layers['full']['full_' . $bundle] = [
'group' => $layer_group,
'label' => $bundle_info['label'],
'asset_type' => $bundle,
'filters' => $exposed_filters,
// @todo Color each asset type differently.
// This was previously provided with hook_farm_area_type_info.
'color' => 'orange',
'zoom' => TRUE,
];
}
// Build the map render array.
$map = [
'#type' => 'farm_map',
'#map_type' => 'asset_list',
];
$all_layers = array_merge($asset_layers['cluster'], $asset_layers['full']);
$map['#map_settings']['asset_type_layers'] = $all_layers;
// Render the map.
$view->attachment_before['asset_map'] = $map;
}
}
/**
* Implements hook_module_implements_alter().
*/
function farm_ui_map_module_implements_alter(&$implementations, $hook) {
// Ensure that this module's hook_views_pre_render() runs first.
if ($hook == 'views_pre_render') {
$module = 'farm_ui_map';
$group = $implementations[$module];
unset($implementations[$module]);
$implementations = array_merge([$module => $group], $implementations);
}
}

View file

@ -0,0 +1,7 @@
services:
farm_ui_map.map_render_event_subscriber:
class: Drupal\farm_ui_map\EventSubscriber\MapRenderEventSubscriber
arguments:
[ '@entity_type.manager' ]
tags:
- { name: 'event_subscriber' }

View file

@ -0,0 +1,88 @@
(function () {
farmOS.map.behaviors.asset_type_layers = {
attach: function (instance) {
// Check if there are asset type layers to add.
if (drupalSettings.farm_map[instance.target].asset_type_layers !== undefined) {
// Add layers for each area type.
var layers = drupalSettings.farm_map[instance.target].asset_type_layers;
Object.values(layers).reverse().forEach( layer => {
// Determine if the layer should display full geometry or centroids.
let geomType = 'full';
let layerType = 'geojson';
if (!!layer.cluster && layer.cluster) {
geomType = 'centroid';
layerType = 'cluster';
}
// Build a url to the asset type geojson, default to all.
const assetType = layer.asset_type ?? 'all';
const url = new URL('/assets/geojson/' + geomType + '/' + assetType, window.location.origin + drupalSettings.path.baseUrl);
// Include provided filters.
const filters = layer.filters ?? {};
Object.entries(filters).forEach( ([key, value]) => {
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
url.searchParams.append(key + '[]', value[i]);
}
}
else {
url.searchParams.append(key, value);
}
});
// Build the layer.
var opts = {
title: layer.label,
url,
color: layer.color,
};
// Add the group if specified.
if (!!layer.group) {
opts.group = layer.group;
}
var newLayer = instance.addLayer(layerType, opts);
// If zoom is true, zoom to the layer vectors.
// Do not zoom to cluster layers.
if (layerType !== 'cluster' && layer.zoom !== undefined && layer.zoom) {
var source = newLayer.getSource();
source.on('change', function () {
instance.zoomToVectors();
});
}
});
}
// @todo: Display area details in popup.
// Load area details via AJAX when an area popup is displayed.
// instance.popup.on('farmOS-map.popup', function (event) {
// var area_details = jQuery('.ol-popup-description .area-details');
// if (area_details.attr('id') === undefined) {
// return;
// }
// area_details.html('test!');
// var area_id = area_details.attr('id').replace('area-details-', '');
// if (area_id) {
// area_details.html('Loading area details...');
// jQuery.getJSON(Drupal.settings.farm_map.base_path + 'farm/area/' + area_id + '/details', function(data) {
// if (data) {
// area_details.html(data);
// var position = event.target.getPosition();
// event.target.setPosition();
// event.target.setPosition(position);
// }
// else {
// area_details.html('');
// }
// });
// }
// });
}
};
}());

View file

@ -0,0 +1,118 @@
<?php
namespace Drupal\farm_ui_map\EventSubscriber;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\farm_map\Event\MapRenderEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* An event subscriber for the MapRenderEvent.
*
* Adds the wkt and geofield behaviors to necessary maps.
*/
class MapRenderEventSubscriber implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* Asset types.
*
* @var \Drupal\asset\Entity\AssetTypeInterface[]
*/
protected $assetTypes;
/**
* MapRenderEventSubscriber Constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->assetTypes = $entity_type_manager->getStorage('asset_type')->loadMultiple();
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
return [
MapRenderEvent::EVENT_NAME => 'onMapRender',
];
}
/**
* React to the MapRenderEvent.
*
* @param \Drupal\farm_map\Event\MapRenderEvent $event
* The MapRenderEvent.
*/
public function onMapRender(MapRenderEvent $event) {
// Get the map ID.
$map_id = $event->getmapType()->id();
// Add behaviors/settings to default and geofield maps.
if (in_array($map_id, ['default', 'geofield', 'geofield_widget'])) {
// Add "All locations" layers.
$event->addBehavior('asset_type_layers');
$settings[$event->getMapTargetId()]['asset_type_layers']['all_locations'] = [
'label' => $this->t('All locations'),
'filters' => [
'is_location' => 1,
],
'color' => 'grey',
'zoom' => TRUE,
];
$event->addSettings($settings);
// Prevent zooming to the "All locations" layer if WKT is provided.
if (!empty($event->element['#map_settings']['wkt'])) {
$settings[$event->getMapTargetId()]['asset_type_layers']['all_locations']['zoom'] = FALSE;
$event->addSettings($settings);
}
}
// Add asset layers to dashboard map.
elseif ($map_id == 'dashboard') {
$layers = [];
// Define common layer properties.
$group = $this->t('Location assets');
$filters = [
'is_location' => 1,
];
// Add layer for all asset types are locations by default.
foreach ($this->assetTypes as $type) {
// Only add a layer if the asset type is a location by default.
if ($type->getThirdPartySetting('farm_location', 'is_location', FALSE)) {
// Add layer for the asset type.
$layers[$type->id()] = [
'group' => $group,
'label' => $type->label(),
'asset_type' => $type->id(),
'filters' => $filters,
// @todo Color each asset type differently.
// This was previously provided with hook_farm_area_type_info.
'color' => 'orange',
'zoom' => TRUE,
];
}
}
// Add the asset_type_layers behavior.
$event->addBehavior('asset_type_layers');
// Add map specific settings.
$settings[$event->getMapTargetId()]['asset_type_layers'] = $layers;
$event->addSettings($settings);
}
}
}

View file

@ -98,60 +98,11 @@ function farm_ui_views_views_pre_view(ViewExecutable $view, $display_id, array &
$view->removeHandler($display_id, 'field', 'type');
$view->removeHandler($display_id, 'filter', 'type');
// Get the entity and bundle.
$base_entity = $view->getBaseEntityType();
$bundle = $args[0];
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
$entity_type_manager = \Drupal::entityTypeManager();
// If the entity type has a bundle_plugin manager, add all of its
// bundle fields and filters to the page_type view.
if ($entity_type_manager->hasHandler($base_entity->id(), 'bundle_plugin')) {
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
$table_mapping = \Drupal::entityTypeManager()->getStorage($base_entity->id())->getTableMapping();
// Load bundle fields.
$bundle_fields = $entity_type_manager->getHandler($base_entity->id(), 'bundle_plugin')->getFieldDefinitions($bundle);
foreach ($bundle_fields as $field_name => $field_definition) {
// Get the field's table column (main property name).
$table = $table_mapping->getFieldTableName($field_name);
$property_name = $field_definition->getFieldStorageDefinition()->getMainPropertyName();
// Build the column and table names.
$column_name = $field_name . '_' . $property_name;
$views_option_name = $table . '.' . $column_name;
// Add a field handler if a views data field definition exists.
$field_options = Views::viewsDataHelper()->fetchFields($table, 'field');
if (isset($field_options[$views_option_name])) {
// Add bundle fields to the view.
$view->addHandler('page_type', 'field', $table, $column_name);
}
// Add a filter handler if a views data filter definition exists.
$filter_options = Views::viewsDataHelper()->fetchFields($table, 'filter');
if (isset($filter_options[$views_option_name])) {
// Add bundle filters to the view.
$filter = [
'id' => $column_name,
'table' => $table,
'field' => $column_name,
'exposed' => TRUE,
'expose' => [
'operator_id' => $column_name . '_op',
'label' => $filter_options[$views_option_name]['title'],
'identifier' => $column_name,
'multiple' => TRUE,
],
'entity_type' => $base_entity,
'entity_field' => $column_name,
];
$view->addHandler('page_type', 'filter', $table, $column_name, $filter);
}
}
if (\Drupal::entityTypeManager()->hasHandler($view->getBaseEntityType()->id(), 'bundle_plugin')) {
farm_ui_views_add_bundle_handlers($view, 'page_type', $view->args[0], 'field');
farm_ui_views_add_bundle_handlers($view, 'page_type', $view->args[0], 'filter');
}
}
@ -173,6 +124,70 @@ function farm_ui_views_views_pre_view(ViewExecutable $view, $display_id, array &
}
}
/**
* Helper function for adding bundle-specific field and filter handlers.
*
* @param \Drupal\views\ViewExecutable $view
* The View to add handlers to.
* @param string $display_id
* The ID of the View display to add handlers to.
* @param string $bundle
* The bundle name.
* @param string $type
* The handler type ('field' or 'filter').
*/
function farm_ui_views_add_bundle_handlers(ViewExecutable $view, string $display_id, string $bundle, string $type) {
// Get the entity and bundle.
$base_entity = $view->getBaseEntityType();
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
$table_mapping = \Drupal::entityTypeManager()->getStorage($base_entity->id())->getTableMapping();
// Load bundle fields.
$bundle_fields = \Drupal::entityTypeManager()->getHandler($base_entity->id(), 'bundle_plugin')->getFieldDefinitions($bundle);
foreach ($bundle_fields as $field_name => $field_definition) {
// Get the field's table column (main property name).
$table = $table_mapping->getFieldTableName($field_name);
$property_name = $field_definition->getFieldStorageDefinition()->getMainPropertyName();
// Build the column and table names.
$column_name = $field_name . '_' . $property_name;
$views_option_name = $table . '.' . $column_name;
// Add a field handler if a views data field definition exists.
if ($type == 'field') {
$field_options = Views::viewsDataHelper()->fetchFields($table, 'field');
if (isset($field_options[$views_option_name])) {
$view->addHandler($display_id, 'field', $table, $column_name, [], $field_name);
}
}
// Add a filter handler if a views data filter definition exists.
elseif ($type == 'filter') {
$filter_options = Views::viewsDataHelper()->fetchFields($table, 'filter');
if (isset($filter_options[$views_option_name])) {
$filter = [
'id' => $field_name,
'table' => $table,
'field' => $column_name,
'exposed' => TRUE,
'expose' => [
'operator_id' => $column_name . '_op',
'label' => $filter_options[$views_option_name]['title'],
'identifier' => $column_name,
'multiple' => TRUE,
],
'entity_type' => $base_entity->id(),
'entity_field' => $field_name,
];
$view->addHandler($display_id, 'filter', $table, $column_name, $filter, $filter['id']);
}
}
}
}
/**
* Implements hook_views_pre_render().
*/