class IpGeoLocViewsPluginStyle in IP Geolocation Views & Maps 8
Class IpGeoLocViewsPluginStyle.
Hierarchy
- class \Drupal\ip_geoloc\Services\IpGeoLocViewsPluginStyle
Expanded class hierarchy of IpGeoLocViewsPluginStyle
2 files declare their use of IpGeoLocViewsPluginStyle
- IpGeoLocPluginStyleLeaflet.php in src/
Plugin/ views/ style/ IpGeoLocPluginStyleLeaflet.php - IpGeoLocPluginStyleMap.php in src/
Plugin/ views/ style/ IpGeoLocPluginStyleMap.php
1 string reference to 'IpGeoLocViewsPluginStyle'
1 service uses IpGeoLocViewsPluginStyle
File
- src/
Services/ IpGeoLocViewsPluginStyle.php, line 27
Namespace
Drupal\ip_geoloc\ServicesView source
class IpGeoLocViewsPluginStyle {
protected $ipGeolocGlobal;
protected $stringTranslation;
protected $messenger;
protected $config;
protected $renderer;
/**
* Constructs a new IpGeoLocGlobal object.
*/
public function __construct(TranslationInterface $stringTranslation, MessengerInterface $messenger, IpGeoLocGlobal $ipGeolocGlobal, ConfigFactoryInterface $config_factory, Renderer $renderer) {
$this->stringTranslation = $stringTranslation;
$this->messenger = $messenger;
$this->ipGeolocGlobal = $ipGeolocGlobal;
$this->config = $config_factory
->getEditable('ip_geoloc.settings');
$this->renderer = $renderer;
}
/**
* The bulk of the plugin style form.
*/
public function pluginStyleBulkOfForm($views_plugin_style, &$form, FormStateInterface &$form_state) {
// We only ever want to offer at most 1 grouping field.
if (!empty($form['grouping']) && count($form['grouping']) > 1) {
$form['grouping'] = array_slice($form['grouping'], 0, 1);
}
$fields = ip_geoloc_get_display_fields($views_plugin_style->displayHandler, TRUE);
$field_options = $fields;
$field_options['---'] = '[' . t('Type a field name') . ']';
$selected_option = $views_plugin_style->options['ip_geoloc_views_plugin_latitude'];
if (!isset($field_options[$selected_option])) {
$selected_option = '---';
}
$form['ip_geoloc_views_plugin_latitude'] = [
'#title' => t('Name of latitude field in Views query'),
'#type' => 'select',
'#options' => $field_options,
'#default_value' => $selected_option,
'#weight' => 10,
];
$form['ip_geoloc_views_plugin_latitude_text'] = [
'#type' => 'textfield',
'#default_value' => $views_plugin_style->options['ip_geoloc_views_plugin_latitude'],
'#states' => [
'visible' => [
':input[name="style_options[ip_geoloc_views_plugin_latitude]"]' => [
'value' => '---',
],
],
],
'#weight' => 11,
];
$form['ip_geoloc_views_plugin_latitude_desc'] = [
'#type' => 'item',
'#markup' => t('Select the field holding the latitude from the dropdown above.<br/><strong>Special cases</strong> If the View has relationships and lat/lon are accessed via the relationships, select <em>[Type field name]</em> and enter the field name in the additional text box presented. If you use a view based on the <strong>Location</strong> module, enter <strong>location_latitude</strong>. If the latitude is stored in a <strong>field</strong>, then enter the field\'s machine name. For instance, when using the <strong>Geofield</strong>, <strong>Geolocation field</strong> or <strong>Get Locations</strong> modules enter <strong>field_<em>yourfieldname</em></strong>. If necessary, check the <a target="_field_names" href="!url">list of all field names</a>. <br/>For IP Geolocation Views & Maps <em>visitor</em> data, enter <strong>ip_geoloc_latitude</strong>.', [
'!url' => 'admin/reports/fields',
]),
'#weight' => 12,
];
$selected_option = $views_plugin_style->options['ip_geoloc_views_plugin_longitude'];
if (!isset($field_options[$selected_option])) {
$selected_option = '---';
}
$form['ip_geoloc_views_plugin_longitude'] = [
'#title' => t('Name of longitude field in Views query'),
'#type' => 'select',
'#options' => $field_options,
'#default_value' => $selected_option,
'#weight' => 15,
];
$form['ip_geoloc_views_plugin_longitude_text'] = [
'#type' => 'textfield',
'#default_value' => $views_plugin_style->options['ip_geoloc_views_plugin_longitude'],
'#states' => [
'visible' => [
':input[name="style_options[ip_geoloc_views_plugin_longitude]"]' => [
'value' => '---',
],
],
],
'#weight' => 16,
];
$form['ip_geoloc_views_plugin_longitude_desc'] = [
'#type' => 'item',
'#markup' => t('See comments above. When using the <strong>Geofield</strong>, <strong>Geolocation field</strong> or <strong>Get Locations</strong> modules select <em><none></em>.'),
'#weight' => 17,
];
$differentiator = $form_state
->getValue('differentiator') ? $form_state
->getValue('differentiator') : $views_plugin_style->options['differentiator']['differentiator_field'];
$user_input = $form_state
->getUserInput();
if (isset($user_input["color_table"])) {
$form_state
->set('num_associations', count($user_input["color_table"]));
}
//@TODO check on change even to see if this is necessary
if (isset($user_input["_triggering_element_name"]) && $user_input["_triggering_element_name"] == "style_options[differentiator][differentiator_field]") {
// Get here when any form element with #ajax was changed/clicked causing
// an auto-rebuild of the form. Can't put this in an ajax handler, as these
// are called AFTER the form rebuild, so too late.
// @TODO check how to migrate this
// if (strpos($form_state->getTriggeringElement()['#id'], 'differentiator-differentiator-field') > 0) {
// Get here when it was the differentiator drop-down that was changed.
$differentiator = $user_input['style_options']['differentiator']['differentiator_field'];
//$form_state->set('num_associations', NULL);
// }
}
$form_state
->set('differentiator', $differentiator);
// Or AJAX won't work!
$form_state
->disableCache();
$is_openlayers = TRUE;
$form_state
->set('renderer', 'openlayers');
$form['default_marker_color'] = [
'#title' => t('Default location marker'),
'#type' => 'select',
'#default_value' => $views_plugin_style->options['default_marker_color'],
'#options' => $is_openlayers ? $this->ipGeolocGlobal
->openlayersMarkerLayers() : $this->ipGeolocGlobal
->markerColors(),
'#description' => t('Select an image to use for all location markers whose images are not overridden by the <strong>location differentiator</strong> below.'),
'#attributes' => [
'class' => $is_openlayers ? [
'marker-color-ol',
] : [
'marker-color',
],
],
'#weight' => 20,
];
if (!$is_openlayers) {
$path = drupal_get_path('module', 'ip_geoloc');
$css_file = strpos($this->config
->get('ip_geoloc_marker_directory'), 'amarkers') ? 'ip_geoloc_admin_a.css' : 'ip_geoloc_admin.css';
$form['default_marker_color']['#attached']['css'] = [
"{$path}/css/{$css_file}",
];
}
// Add wrapper for differentiator drop-down, association table and buttons.
// The id in the prefix must match the AJAX submit handlers below.
$desc = t('You may designate one field from your view as a <strong>location differentiator</strong>. Locations that match the same corresponding differentiator value will have the same marker image on the map. Examples of location differentiators are content type and taxonomy term. You can enter numeric and alphabetic <em>ranges</em> too, e.g. price ranges, like 100--125.<br/><strong>Leaflet</strong> only: you may input a special character, eg. from <a target="_fsymbols" href="!url1">fsymbols</a> or <a target="_font_awesome" href="!url2">Font Awesome</a>, to be superimposed as a font-icon on the marker image. For <em>fsymbols</em> copy and paste the desired font-icon into the Special Char field. For <em>Font Awesome</em> icons leave the Special Char field empty. Instead type the <em>Font Awesome</em> icon name in the Special Char CSS Class field, optionally appending a color class. Example: <strong>icon-beer icon-light</strong>. <br/>All this works best with the markers from the <em>/amarkers</em> directory, configurable <a target="_ip_geoloc_config" href="!url3">here</a>.', [
/*'!url1' => url('http://fsymbols.com'),
'!url2' => url('http://fortawesome.github.io/Font-Awesome/cheatsheet'),
'!url3' => url('admin/config/system/ip_geoloc'),*/
'!url1' => 'http://fsymbols.com',
'!url2' => 'http://fortawesome.github.io/Font-Awesome/cheatsheet',
'!url3' => 'admin/config/system/ip_geoloc',
]);
$form['differentiator'] = [
'#type' => 'fieldset',
'#title' => t('Location differentiator and associated markers'),
'#description' => $desc,
'#prefix' => '<div id="differentiator-wrapper">',
'#suffix' => '</div>',
'#weight' => 30,
];
// @TODO Check this function implementaton to get the field name
if (empty($differentiator)) {
$description = t('Optionally select a location differentiator.');
}
else {
// $instances = ip_geoloc_get_field_instances($differentiator);
// $instance = reset($instances);
// @TODO migrate this field. Get entity type
// $field = field_info_field($differentiator);
// Remove node and get the info from related view entity type, take care of taxonomies
$field = FieldStorageConfig::loadByName('node', $differentiator);
$description = t('Below associate %differentiator values with marker images.', [
'%differentiator' => $field
->getLabel(),
]) . '<br/>';
if (!$field || $field
->getType() == 'text') {
$description .= t('You may enter a range of values by separating "from" and "to" by a <strong>double hyphen</strong>.<br/>Example: <strong>A--ZZ</strong><br/>You may omit "from" or "to" to create open-ended ranges.');
}
elseif ($field
->getType() == 'number') {
$description .= t('You may enter a numeric range of by separating "from" and "to" by a <strong>double hyphen</strong>.<br/>Example: <strong>2.5--7.95</strong><br/>You may omit "from" or "to" to create open-ended ranges.');
}
}
$form['differentiator']['differentiator_field'] = [
'#title' => t('Location differentiator'),
'#type' => 'select',
'#default_value' => $differentiator,
'#options' => $fields,
'#description' => $description,
'#ajax' => [
'callback' => [
$this,
'pluginStyleRefreshColorTableJs',
],
'wrapper' => 'differentiator-wrapper',
'prevent' => 'submit',
],
];
if (!empty($differentiator)) {
// Container for the differentiator color associations table.
$field = FieldStorageConfig::loadByName('node', $differentiator);
$differentiator_label = $field
->getLabel();
$header = [
$differentiator_label,
t('Associated marker image'),
t('Special char'),
t('Special char CSS class'),
];
$form['differentiator']['color_table'] = [
//'#theme' => 'ip_geoloc_plugin_style_differentiator_color_table',
'#type' => 'table',
'#header' => $header,
'#rows' => [],
'#tree' => TRUE,
'#attributes' => [
'id' => 'differentiator-color-table',
],
];
if (isset($user_input["_triggering_element_value"]) && !empty($user_input["_triggering_element_value"])) {
if ($user_input["_triggering_element_value"] == t("Add another association") || $user_input["_triggering_element_value"] == t("Add an association")) {
$this
->pluginStyleAddAssociationSubmit($form, $form_state);
$this
->pluginStyleDifferentiatorColorAssociationsSubmit($form, $form_state);
}
if ($user_input["_triggering_element_value"] == t("Remove bottom association")) {
$this
->pluginStyleRemoveAssociationSubmit($form, $form_state);
$this
->pluginStyleDifferentiatorColorAssociationsSubmit($form, $form_state);
}
}
// Saving color configuration data
if ($user_input["op"] == t("Apply")) {
$this
->pluginStyleDifferentiatorColorAssociationsSubmit($form, $form_state);
}
$this
->pluginStyleDifferentiatorColorTableForm($form, $form_state);
$form['differentiator']['add-another'] = [
'#type' => 'submit',
'#value' => empty($form_state
->get('num_associations')) ? t('Add an association') : t('Add another association'),
'#weight' => 1,
//'#submit' => [$this,'pluginStyleDifferentiatorColorAssociationsSubmit'],
'#ajax' => [
'callback' => [
$this,
'pluginStyleRefreshColorTableJs',
],
'wrapper' => 'differentiator-wrapper',
// Or 'none' or 'slide'.
'effect' => 'fade',
// Or 'slow' or number of millisec.
'speed' => 'fast',
'prevent' => 'submit',
],
];
if (!empty($form_state
->get('num_associations'))) {
$form['differentiator']['remove'] = [
'#type' => 'submit',
'#value' => t('Remove bottom association'),
'#weight' => 2,
'#ajax' => [
'callback' => [
$this,
'pluginStyleRefreshColorTableJs',
],
'wrapper' => 'differentiator-wrapper',
// Or 'fade' or 'slide'.
'effect' => 'none',
// Or 'slow' or number of millisec.
'speed' => 'fast',
'prevent' => 'submit',
],
];
}
}
if ($is_openlayers) {
$desc = t('*) If you want a fixed center, visit the "Center & Bounds" vertical tab on the <a target="_ol_map_edit" href="@url">map edit page</a>. This is also where you set the initial zoom level.', [
// '@url' => url('admin/structure/openlayers/maps/' . $views_plugin_style->options['map'] . '/edit'),.
'@url' => 'admin/structure/openlayers/maps/' . $views_plugin_style->options['map'] . '/edit',
]);
}
else {
$desc = t('*) If you choose the first option you may center the map via the special <strong>Map options</strong> <em>"centerLat"</em> and <em>"centerLng"</em> for example:<br/>%center_example', [
'%center_example' => '{ "mapTypeId":"terrain", "centerLat":-37.8, "centerLng":145 }',
]);
}
$form['center_option'] = [
'#title' => t('Map centering options'),
'#type' => 'radios',
'#default_value' => $views_plugin_style->options['center_option'],
'#options' => [
IP_GEOLOC_MAP_CENTER_FIXED => t('Fixed center; see note below *)'),
IP_GEOLOC_MAP_CENTER_ON_FIRST_LOCATION => t('Use the first location returned by the view as the center of the map.'),
IP_GEOLOC_MAP_CENTER_ON_VISITOR => t("Center the map on the visitor's current location."),
IP_GEOLOC_MAP_CENTER_OF_LOCATIONS => t('Use the center of the rectangle whose sides are defined by the left-most, right-most, top and bottom locations (this option is insensitive to location clusters).'),
IP_GEOLOC_MAP_CENTER_OF_LOCATIONS_WEIGHTED => t('Use the center of gravity based on all locations (this option is sensitive to location clusters)'),
],
'#description' => $desc,
'#weight' => 40,
];
$form['empty_map_center'] = [
'#title' => t('No locations behaviour'),
'#type' => 'textfield',
'#size' => 30,
'#default_value' => $views_plugin_style->options['empty_map_center'],
'#description' => t('a) Show empty map: enter <em>latitude, longitude</em> of the map center.<br/>b) Show empty map centered on visitor location: enter the word %visitor.<br/>c) Show nothing: if this field is left blank, the map canvas will be suppressed when there are no locations to show. If you wish, you may use the <strong>No results behavior</strong> panel in the Advanced section of the Views UI to show an alternative text or content.', [
'%visitor' => t('visitor'),
]),
'#weight' => 110,
];
return $form;
}
/**
* Validation for ip_geoloc_plugin_style_bulk_of_form().
*/
public function pluginStyleBulkOfFormValidate(&$form, FormStateInterface &$form_state) {
$style_options = $form_state
->getValue('style_options');
if ($style_options['ip_geoloc_views_plugin_latitude'] == '---') {
$style_options['ip_geoloc_views_plugin_latitude'] = $style_options['ip_geoloc_views_plugin_latitude_text'];
}
if ($style_options['ip_geoloc_views_plugin_longitude'] == '---') {
$style_options['ip_geoloc_views_plugin_longitude'] = $style_options['ip_geoloc_views_plugin_longitude_text'];
}
}
/**
* Submit handler for the "Add another association" button.
*
* Increments the counter and forces a form rebuild.
*/
public function pluginStyleAddAssociationSubmit($form, FormStateInterface &$form_state) {
$form_state
->set('num_associations', $form_state
->get('num_associations') + 1);
$form_state
->setRebuild();
}
/**
* Submit handler for the "Remove" button.
*
* Decrements the counter and forces a form rebuild.
*/
public function pluginStyleRemoveAssociationSubmit($form, FormStateInterface &$form_state) {
$form_state
->set('num_associations', $form_state
->get('num_associations') - 1);
$form_state
->setRebuild();
}
/**
* Ajax callback in response to new rows or the diff. drop-down being changed.
*
* At this point the $form has already been rebuilt. All we have to do here is
* tell AJAX what part of the browser form needs to be updated.
*/
public function pluginStyleRefreshColorTableJs(&$form, &$form_state) {
// Return the updated table, so that ajax.inc can issue commands to the
// browser to update only the targeted sections of the page.
return $form['options']['style_options']['differentiator'];
}
/**
* Submit handler as declared in ip_geoloc_form_alter().
*/
public function pluginStyleDifferentiatorColorAssociationsSubmit(&$form, &$form_state) {
if (empty($form_state
->get('view')) || empty($form_state
->get('display_id')) || empty($form_state
->get('differentiator'))) {
return;
}
$view_id = $form_state
->get('view')
->get('storage')
->get('id');
$display_id = $form_state
->get('display_id');
$differentiator = $form_state
->get('differentiator');
// Erase differentiator values for this display, then rebuild based on form.
$differentiator_color_associations = $this->config
->get('ip_geoloc_' . $view_id . '_color_mappings', []);
unset($differentiator_color_associations[$display_id][$differentiator]);
$user_input = $form_state
->getUserInput();
if (!empty($user_input['color_table'])) {
$i = 0;
foreach ($user_input['color_table'] as $association) {
if (is_array($association[$differentiator])) {
// E.g association['field_address']['und'][0]['postal_code'].
foreach ($association[$differentiator][LANGUAGE_NONE] as $values) {
$differentiator_value = [];
if (is_array($values)) {
foreach ($values as $name => $value) {
if (isset($value) && strpos($value, '|') === FALSE) {
$differentiator_value[$name] = $value;
}
}
}
else {
$differentiator_value[] = $values;
}
if (!empty($differentiator_value)) {
$differentiator_color_associations[$display_id][$differentiator][$i]['differentiator_value'] = $differentiator_value;
$differentiator_color_associations[$display_id][$differentiator][$i]['color'] = $association['color'];
$differentiator_color_associations[$display_id][$differentiator][$i]['special char'] = $association['special char'];
$differentiator_color_associations[$display_id][$differentiator][$i]['special char class'] = $association['special char class'];
$i++;
}
}
}
else {
// Plain text field or plain taxonomy select.
$differentiator_value = trim($association[$differentiator]);
if (!empty($differentiator_value)) {
$differentiator_color_associations[$display_id][$differentiator][$i]['differentiator_value'] = [
$differentiator_value,
];
$differentiator_color_associations[$display_id][$differentiator][$i]['color'] = $association['color'];
$differentiator_color_associations[$display_id][$differentiator][$i]['special char'] = $association['special char'];
$differentiator_color_associations[$display_id][$differentiator][$i]['special char class'] = $association['special char class'];
$i++;
}
}
}
}
$this->config
->set('ip_geoloc_' . $view_id . '_color_mappings', $differentiator_color_associations)
->save();
}
/**
* Plugin style color differ.
*/
public function pluginStyleDifferentiatorColorTableForm(&$form, FormStateInterface &$form_state) {
$is_openlayers = $form_state
->get('renderer') == 'openlayers';
// First the saved rows...
// @todo: if $field['cardinality'] > 1, compress multiple diff. values
// for the same color together in a single row.
$view_id = $form_state
->get('view')
->get('storage')
->get('id');
$display_id = $form_state
->get('display_id');
$differentiator = $form_state
->get('differentiator');
$differentiator_color_associations = $this->config
->get('ip_geoloc_' . $view_id . '_color_mappings', []);
if (empty($differentiator_color_associations[$display_id][$differentiator])) {
$differentiator_color_associations[$display_id] = [
$differentiator => [],
];
}
$row = 0;
foreach ($differentiator_color_associations[$display_id][$differentiator] as $association) {
if (!is_array($association)) {
// Data corrupt.
break;
}
if ($form_state
->has('num_associations') && $row >= $form_state
->get('num_associations')) {
break;
}
$form['differentiator']['color_table'][$row] = $this
->pluginStyleDiffColorTableRowForm($is_openlayers, $row, $differentiator, $association);
//$form['differentiator']['color_table']['#rows'][$row] = $this->pluginStyleDiffColorTableRowForm($is_openlayers, $row, $differentiator, $association);
$row++;
}
// ... then the empty rows.
if (!$form_state
->has('num_associations')) {
//$form_state->set('num_associations', count($differentiator_color_associations[$display_id][$differentiator]));
if (empty($differentiator_color_associations[$display_id][$differentiator])) {
$form_state
->set('num_associations', count($differentiator_color_associations[$display_id]));
}
else {
$form_state
->set('num_associations', count($differentiator_color_associations[$display_id][$differentiator]));
}
}
while ($row < $form_state
->get('num_associations')) {
$form['differentiator']['color_table'][$row] = $this
->pluginStyleDiffColorTableRowForm($is_openlayers, $row, $differentiator);
//$form['differentiator']['color_table']['#rows'][$row] = $this->pluginStyleDiffColorTableRowForm($is_openlayers, $row, $differentiator);
$row++;
}
}
/**
* Diffs the color table plugin style row.
*/
public function pluginStyleDiffColorTableRowForm($is_openlayers, $row, $differentiator, $association = NULL) {
$differentiator_value = empty($association) ? NULL : $association['differentiator_value'];
$attr_class = $is_openlayers ? [
'differentiator ol',
] : [
'differentiator',
];
if (strpos($differentiator, 'field_') === 0) {
// @TODO migrate this field. Get entity type
// $field = field_info_field($differentiator);
// Remove node and get the info from related view entity type, take care of taxonomies
$field = FieldStorageConfig::loadByName('node', $differentiator);
// $instances = ip_geoloc_get_field_instances($differentiator);
// $instance = reset($instances);
// @TODO find the correct function
$widget = $field
->getSettings();
if (strpos($widget['target_type'], 'taxonomy') !== FALSE) {
// Covers core and Autocomplete Deluxe.
$vid = '';
$bundle = 'page';
$vocs = [];
$field_confing = FieldConfig::loadByName('node', $bundle, $differentiator);
// Only using the first vocabulary
$vocabulary_name = reset($field_confing
->getSettings()['handler_settings']['target_bundles']);
$vocabularies = Vocabulary::loadMultiple();
foreach ($vocabularies as $vid => $vocabulary) {
if ($vid == $vocabulary_name) {
break;
}
}
$options = [];
$terms = \Drupal::entityTypeManager()
->getStorage('taxonomy_term')
->loadTree($vid);
foreach ($terms as $term) {
$options[$term->tid] = str_repeat('-', $term->depth) . $term->name;
}
$form[$differentiator] = [
'#type' => empty($options) ? 'textfield' : 'select',
'#options' => $options,
'#default_value' => $differentiator_value,
'#attributes' => [
'class' => $attr_class,
],
'#title' => $differentiator,
];
}
else {
// No label, unless other modules override this.
$instance['label'] = '';
// Don't want asterisk to appear.
$instance['required'] = FALSE;
// Make sure the text field is wide enough, especially for the case of a
// range, which needs to receive two values and a separator.
if (isset($instance['widget']['settings']['size']) && $instance['widget']['settings']['size'] < 15) {
$instance['widget']['settings']['size'] = 15;
}
$items[0] = empty($differentiator_value) ? [] : is_array($differentiator_value) ? $differentiator_value : [
$differentiator_value,
];
if (isset($instance['default_value'])) {
$items[0] += is_array($instance['default_value']) && !empty($instance['default_value']) ? reset($instance['default_value']) : $instance['default_value'];
}
$form['#parents'] = [];
$form_state = [];
$form = field_default_form($instance['entity_type'], NULL, $field, $instance, LANGUAGE_NONE, $items, $form, $form_state);
if ($field['module'] == 'number' || $field['module'] == 'text') {
$form[$differentiator][LANGUAGE_NONE][0][key($field['columns'])]['#element_validate'] = [
'ip_geoloc_range_widget_validate',
];
}
$form[$differentiator]['#attributes']['class'][] = $attr_class;
}
}
else {
// Not a field, maybe an expression.
$form[$differentiator] = [
'#type' => 'textfield',
'#size' => 40,
'#default_value' => $differentiator_value,
'#element_validate' => [
'ip_geoloc_range_widget_validate',
],
'#attributes' => [
'class' => $attr_class,
],
'#title' => $differentiator,
];
}
$form['color'] = [
'#type' => 'select',
'#default_value' => empty($association) ? NULL : $association['color'],
'#options' => $is_openlayers ? ip_geoloc_openlayers_marker_layers() : ip_geoloc_marker_colors(),
'#attributes' => [
'class' => $is_openlayers ? [
'marker-color-ol',
] : [
'marker-color',
],
],
];
$form['special char'] = [
'#type' => 'textfield',
'#size' => 8,
'#default_value' => empty($association) ? NULL : $association['special char'],
];
$form['special char class'] = [
'#type' => 'textfield',
'#size' => 25,
'#default_value' => empty($association) ? NULL : $association['special char class'],
];
// We'll manually set the #parents property of these fields so that their
// values appear in the $form_state->getValue('color_table') array.
$form[$differentiator]['#parents'] = [
'color_table',
$row,
$differentiator,
];
$form['color']['#parents'] = [
'color_table',
$row,
'color',
];
$form['special char']['#parents'] = [
'color_table',
$row,
'special char',
];
$form['special char class']['#parents'] = [
'color_table',
$row,
'special char class',
];
return $form;
}
/**
* Extract an array of locations from the supplied views_plugin_style.
*
* @param string $views_plugin_style
* The style of views plugin.
*
* @return array
* Array of location objects, each containing lat/long and balloon_text
*/
public function pluginStyleExtractLocations($views_plugin_style) {
$latitude = trim($views_plugin_style->options['ip_geoloc_views_plugin_latitude']);
$longitude = trim($views_plugin_style->options['ip_geoloc_views_plugin_longitude']);
$view =& $views_plugin_style->view;
$view_id = $view->storage
->getOriginalId();
$display_id = $view
->getDisplay()->display['id'];
$differentiator = FALSE;
if (!empty($views_plugin_style->options['differentiator']['differentiator_field'])) {
$differentiator = $views_plugin_style->options['differentiator']['differentiator_field'];
$differentiator_color_associations = $this->config
->get('ip_geoloc_' . $view_id . '_color_mappings') ? $this->config
->get('ip_geoloc_' . $view_id . '_color_mappings') : [];
if (empty($differentiator_color_associations[$display_id])) {
$differentiator_color_associations[$display_id] = [
$differentiator => [],
];
}
}
$tooltip_field = FALSE;
if (isset($views_plugin_style->options['tooltips']['marker_tooltip'])) {
$marker_tooltip = $views_plugin_style->options['tooltips']['marker_tooltip'];
if (isset($view->field[$marker_tooltip])) {
$tooltip_field = $view->field[$marker_tooltip];
}
}
$tag_field = FALSE;
if (isset($views_plugin_style->options['tags']['marker_tag'])) {
$marker_tag = $views_plugin_style->options['tags']['marker_tag'];
if (isset($view->field[$marker_tag])) {
$tag_field = $view->field[$marker_tag];
}
}
$locations = [];
$error_count = $first_error = 0;
$loc_field_name = $loc_field_alias = NULL;
if (isset($view->field[$latitude])) {
$loc_field_name = $view->field[$latitude]->definition['field_name'];
// @NEW
if ($view->field[$latitude]->field_alias == 'unknown') {
$loc_field_alias = $loc_field_name;
}
else {
$loc_field_alias = $view->field[$latitude]->field_alias;
}
// Example: $loc_field_name == 'field_geo'; $loc_field_alias == 'nid';.
}
// Group the rows according to the grouping instructions, if specified.
$sets = $views_plugin_style
->renderGrouping($views_plugin_style->view->result, $views_plugin_style->options['grouping'], TRUE);
if (!empty($views_plugin_style->options['grouping'])) {
// @todo - Add support for multiple grouping fields?
$group_field = $views_plugin_style->options['grouping'][0]['field'];
}
$rendered_fields = $views_plugin_style
->getRenderedFields();
foreach ($sets as $set) {
// Render as a grouping set.
if (!empty($set['group'])) {
// This is required for some Views functions.
$view->row_index = $i = key($set['rows']);
$row = reset($set['rows']);
$base = $this
->pluginStyleGetBase1($row, $loc_field_name, $loc_field_alias);
if ($location = $this
->pluginStyleExtractLatLng($row, $latitude, $longitude, $base)) {
// Remaining row values go into the balloon.
if (!empty($rendered_fields[$i])) {
$location->balloon_text = $rendered_fields[$i][$group_field];
foreach ($set['rows'] as $i => $row) {
$rendered_field = $rendered_fields[$i];
unset($rendered_field[$group_field]);
$location->balloon_text .= '<br />' . implode('<br/>', $rendered_field);
}
}
if (!empty($differentiator_color_associations[$display_id][$differentiator])) {
$this
->pluginStyleSetMarkerColor($location, $row, $differentiator, $differentiator_color_associations[$display_id][$differentiator]);
}
$this
->pluginStyleDecorate($location, $row, $tooltip_field, $tag_field);
$locations[] = $location;
}
elseif ($error_count++ == 0) {
$first_error = $i;
}
}
else {
foreach ($view->result as $i => $row) {
// This is required for some Views functions.
$view->row_index = $i;
$base = $this
->pluginStyleGetBase1($row, $loc_field_name, $loc_field_alias);
if ($location = $this
->pluginStyleExtractLatLng($row, $latitude, $longitude, $base)) {
// Remaining row values go into the balloon.
if (!empty($rendered_fields[$i])) {
$location->balloon_text = implode('<br/>', $rendered_fields[$i]);
}
if (!empty($differentiator_color_associations[$display_id][$differentiator])) {
$this
->pluginStyleSetMarkerColor($location, $row, $differentiator, $differentiator_color_associations[$display_id][$differentiator]);
}
$this
->pluginStyleDecorate($location, $row, $tooltip_field, $tag_field);
$locations[] = $location;
}
elseif ($error_count++ == 0) {
$first_error = $i;
}
}
}
}
$user = \Drupal::currentUser();
if ($error_count > 0 && $user
->id() == 1) {
$row_count = count($view->result);
$title = empty($views_plugin_style->display->display_options['title']) ? $view
->getTitle() . ' (' . $views_plugin_style->display->display_title . ')' : $views_plugin_style->display->display_options['title'];
$t_args = [
'%view_title' => $title,
'@total' => $row_count,
'@error_count' => $error_count,
'@first' => $first_error + 1,
'%latitude' => $latitude,
];
$msg = $error_count >= $row_count ? t('None of the @total result rows in view %view_title had their %latitude lat/lng set. Therefore the map could not be displayed. Is %latitude the correct field name?', $t_args) : t('Out of a total of @total result rows in view %view_title, @error_count rows did not have their %latitude lat/lng set and therefore could not be shown on the map. The first row that could not be located was row #@first. You can improve execution efficiency by using the Views UI to apply an "is not empty" filter criterion to %latitude.', $t_args);
$row_contents = t('First error row:') . '<br/>';
foreach ((array) $view->result[$first_error] as $field_name => $value) {
// @TODO review this print
// $row_contents .= "<strong>$field_name</strong> = " . print_r($value, TRUE) . '<br/>';
}
$this->messenger
->addMessage($msg, 'warning');
$this->messenger
->addMessage($row_contents, 'warning');
$this->messenger
->addMessage(t('The above messages are shown only to user 1.'), 'warning');
}
// Allow other modules to implement
// hook_ip_geoloc_marker_locations_alter(&$locations, &$view)
// @TODO check drupal_alter for drupal 8
// drupal_alter('ip_geoloc_marker_locations', $locations, $view);.
\Drupal::moduleHandler()
->alter('ip_geoloc_marker_locations', $locations, $view);
return $locations;
}
/**
* Pending doc.
*/
public function pluginStyleGetBase1($row, $location_field_name, $location_field_alias) {
$base = NULL;
if (isset($row->_entity)) {
// The lines below are stripped back fast versions of this call:
// $loc_field_value = $loc_field->get_value($row);
$entity = $row->_entity;
// The field value may not be there, i.e. no coordinates entered.
$location_field_value = $entity
->get($location_field_name)
->getValue();
if ($location_field_value) {
$base = is_array($location_field_value) ? reset($location_field_value) : $location_field_value;
}
}
return $base;
}
/**
* Plugin style base.
*/
public function pluginStyleGetBase2($row, $field_name, $delta) {
if (!isset($row) || !isset($field_name) || !isset($delta)) {
return NULL;
}
$field = $row
->get($field_name);
$field_value = $field
->getValue();
if (!$row
->get($field_name)
->isEmpty() && is_array($field_value)) {
if (!empty($field_value[$delta])) {
return $field_value[$delta];
// Geofield : field_<field_name>[0]['raw']['geom']
// Geolocation : field_<field_name>[0]['raw']['lat'] and ...]['lng']
// Get Locations: field_<field_name>[0]['raw']['latitude'].
}
// $lang = Language::LANGCODE_NOT_SPECIFIED;.
// @TODO Check the translation implementation
$lang = \Drupal::languageManager()
->getCurrentLanguage()
->getId();
if ($translated_entity = $row
->getTranslation($lang)) {
// EntityFieldQuery Views Backend.
// For Geofield module lat/long are as follows (note: no extra
// 'field_' prefix):
// <field_name>['und]['0']['wkt'], for example:
// POINT (144.976 -37.813) or MULTIPOINT ( (),(),() )
$field_value = $translated_entity
->getValue();
return $field_value[$delta];
}
// This doesnt apply for d8
// Last resort, retrieve from rendered, as opposed to raw, result.
/*if (!empty($row->{$field_name}[$delta]['rendered'])) {
$base = $row->{$field_name}[$delta]['rendered'];
return empty($base['#markup']) ? $base : $base['#markup'];
}*/
}
return NULL;
}
/**
* Extract coordinates from the View result row.
*
* @todo This function is in flux and a bit messy at the moment....
*
* @param array $row
* View result row.
* @param string $latitude
* Name of the row field that holds latitude.
* @param string $longitude
* Name of the row field that holds longitude.
* @param string $loc_field_value
* Pending doc.
*
* @return object|null
* location -- @todo return array for consistency with Leaflet?
*/
public function pluginStyleExtractLatLng($row, $latitude, $longitude, $loc_field_value = NULL) {
$delta =& drupal_static(__FUNCTION__);
if (isset($delta)) {
$delta++;
}
// Hack for http://drupal.org/node/1824538
// In the View, AddressField must have "Display all values in the same row"
// UNTICKED, while Geofield (and all other fields) must have it TICKED.
// @TODO review is this is needed for drupal 8
/*foreach ((array) $row as $key => $value) {
if (Unicode::substr($key, -6) == '_delta' && strpos($key, 'field_data_field_') === 0) {
$delta = $value;
break;
}
}*/
$row = $row->_entity;
$field_name = $latitude;
$field_value = $row
->get($field_name)
->getValue();
// @TODO check the count function for multivalued locations
if (!isset($delta) || !$row
->get($field_name)
->isEmpty() || $delta >= count($field_value)) {
$delta = 0;
}
$location = new \StdClass();
$base = $this
->pluginStyleGetBase2($row, $field_name, $delta);
if (empty($base)) {
// Drop the "field_" prefix and try again.
$base = $this
->pluginStyleGetBase2($row, $latitude, $delta);
}
if (empty($base)) {
$base = $loc_field_value;
}
if (empty($base)) {
// @TODO check location module
// Empty $base, e.g. Location module.
/*if (isset($row->{$latitude}) && isset($row->{$longitude})) {
// If not node fields then db table fields...
$location->latitude = $row->{$latitude};
$location->longitude = $row->{$longitude};
}*/
}
else {
if (is_string($base)) {
// Expect a rendered result like "Latitude: -37.8<br/>Longitude: 144.96".
$parts = explode(':', $base);
if (isset($parts[2])) {
$location->latitude = (double) $parts[1];
$location->longitude = (double) $parts[2];
}
}
elseif (isset($base['geo_type'])) {
// Geofield.
// Quick fix: take advantage of Leaflet module, if enabled.
// @todo generalise Leaflet code and incorporate here.
$moduleHandler = \Drupal::service('module_handler');
if ($moduleHandler
->moduleExists('leaflet')) {
$leafletSevice = \Drupal::service('leaflet.service');
// leaflet.service
// Support WKT lines and polygons in Leaflet. Call requires:
// $base['geo_type'] eg 'point' or 'linestring'
// $base['geom'] eg. 'POINT(145 -37)' or 'LINESTRING(145 -37, 146, -38)'.
if (!isset($base['wkt'])) {
// Until fixed in leaflet_process_geofield().
$base['wkt'] = $base['value'];
}
$location = $leafletSevice
->leafletProcessGeofield([
$base,
]);
$location = (object) reset($location);
// @todo get rid of this duality, use lat/lon throughout
if (isset($location->lat)) {
$location->latitude = $location->lat;
unset($location->lat);
}
if (isset($location->lon)) {
$location->longitude = $location->lon;
unset($location->lon);
}
}
else {
$is_point = $base['geo_type'] == 'point';
if ($is_point) {
// Wkt more accurate than lat,lon in Geofield 7.x-1.x.
$point = empty($base['geom']) ? $base['wkt'] : $base['geom'];
// @todo Consider using the following:
// $geometry = geoPHP::load($base['geom']);
// $location = json_decode($geometry->out('json'));
$is_point = Unicode::substr(trim($point), 0, 7) == 'POINT (';
if ($is_point) {
$parts = explode(' ', Unicode::substr($point, 7));
$is_point = count($parts) > 1;
}
}
// $is_point==FALSE may indicate a MULTIPOINT cluster, which has its
// averaged center on 'lat' and 'lon' indices.
$location->longitude = $is_point ? (double) $parts[0] : $base['lon'];
$location->latitude = $is_point ? (double) $parts[1] : $base['lat'];
}
}
elseif (isset($base['lng'])) {
// GeoLocation.
$location->latitude = $base['lat'];
$location->longitude = $base['lng'];
}
elseif (isset($base['longitude'])) {
// Get Locations.
$location->latitude = $base['latitude'];
$location->longitude = $base['longitude'];
}
elseif (isset($row->{'field_' . $latitude}[$delta]['raw']['value'])) {
// Other module.
// Field values tend to be inside ...[0]['raw']['value']:
$location->latitude = $row->{'field_' . $latitude}[$delta]['raw']['value'];
$location->longitude = $row->{'field_' . $longitude}[$delta]['raw']['value'];
}
}
return isset($location->type) || isset($location->latitude) ? $location : NULL;
}
/**
* Set the marker color.
*/
public function pluginStyleSetMarkerColor(&$location, $row, $differentiator, $differentiator_color_associations) {
if (!empty($differentiator)) {
if (!empty($row->{$differentiator})) {
$differentiator_value = $row->{$differentiator};
}
elseif (!empty($row->{'field_' . $differentiator})) {
$differentiator_values = $row->{'field_' . $differentiator};
if (is_array($differentiator_values)) {
// Count > 1 for taxonomy term with lineage.
$num_values = count($differentiator_values);
if (isset($differentiator_values[0]['raw'])) {
$differentiator_value = $differentiator_values[$num_values - 1]['raw'];
}
elseif (isset($differentiator_values[0]['rendered']['#markup'])) {
$differentiator_value = $differentiator_values[$num_values - 1]['rendered']['#markup'];
}
}
}
else {
$differentiator_value = '';
if (!isset($row->{'field_' . $differentiator})) {
$this->messenger
->addMessage($this->stringTranslation
->translate('IPGV&M: no differentiator values found for %diff. Cannot set marker images.', [
'%diff' => $differentiator,
]), 'warning', FALSE);
}
}
if (!empty($differentiator_value)) {
foreach ($differentiator_color_associations as $association) {
$match = TRUE;
if (is_array($differentiator_value)) {
if (empty($differentiator_value['tid'])) {
// Eg AddressField, has multiple subvalues. All must match the
// corresponding differentiator subvalues.
foreach ($association['differentiator_value'] as $name => $subvalue) {
if (!isset($differentiator_value[$name]) || !$this->ipGeolocGlobal
->isInRange($differentiator_value[$name], $subvalue)) {
$match = FALSE;
break;
}
}
}
else {
// 'tid' set: taxonomy widget (plain or autocomplete)
// $association['differentiator_value'] can have index 0 or 'tid'.
$range = reset($association['differentiator_value']);
$match = $this->ipGeolocGlobal
->isInRange($differentiator_value['tid'], $range);
}
}
else {
// Single value, either as simple value or array of length 1.
$range = $association['differentiator_value'];
$match = $this->ipGeolocGlobal
->isInRange($differentiator_value, $range);
}
if ($match) {
$location->marker_color = $association['color'];
if (!empty($association['special char'])) {
$location->marker_special_char = $association['special char'];
}
if (!empty($association['special char class'])) {
$location->marker_special_char_class = $association['special char class'];
}
}
}
}
}
if (isset($location->marker_color) && is_array($location->marker_color)) {
$location->marker_color = reset($location->marker_color);
}
}
/**
* Pending doc.
*/
public function pluginStyleDecorate(&$location, $row, $tooltip_field, $tag_field) {
if ($tooltip_field) {
$tooltip = $tooltip_field
->theme($row);
$location->marker_tooltip = strip_tags($this
->pluginStyleAddLabel($tooltip_field, $tooltip));
}
if ($tag_field) {
$tag = $tag_field
->theme($row);
$location->marker_tag = $this
->pluginStyleAddLabelAndStyling($tag_field, $tag);
}
if (isset($row->nid)) {
$location->id = $row->nid;
}
elseif (isset($row->tid)) {
$location->id = $row->tid;
}
}
/**
* Perform token replacement, convert timestamps to date strings etc.
*
* Store the rendered rows on the object passed in, which will typically be an
* instance of class views_plugin_style or subclass.
* Note that fields that have their Exclude box ticked, won't be rendered,
* Typical candidates for exclusion are the latitude and longitude fields.
*
* @param string $views_plugin_style
* The views plugin style.
*/
public function pluginStyleRenderFields($views_plugin_style) {
if (!$views_plugin_style
->usesFields() || isset($views_plugin_style->rendered_fields)) {
return;
}
$rendered_fields = [];
$field_ids = array_keys($views_plugin_style->view->field);
$differentiator = $views_plugin_style->options['differentiator']['differentiator_field'];
$latitude = trim($views_plugin_style->options['ip_geoloc_views_plugin_latitude']);
$longitude = trim($views_plugin_style->options['ip_geoloc_views_plugin_longitude']);
foreach ($views_plugin_style->view->result as $i => &$row) {
// God knows why we need this...
// $views_plugin_style->view->row_index = $i;.
foreach ($field_ids as $field_id) {
$field = $views_plugin_style->view->field[$field_id];
// theme() also renders the tokens that may be used in the balloon fields.
$field_value = $field
->theme($row);
// Add the special 'views_' fields that normally aren't in the results
// set to the row, if required for the $differentiator or lat/lon that
// have math expressions applied through the Views UI.
$special_field_id = 'views_' . $field_id;
if (($special_field_id == $differentiator || $special_field_id == $latitude || $special_field_id == $longitude) && empty($row->{$special_field_id})) {
$row->{$special_field_id}[] = $field_value;
}
// If Excluded is ticked, we don't style and add the field to the
// rendered_fields.
if (!$field->options['exclude']) {
$styled_field_value = $this
->pluginStyleAddLabelAndStyling($field, $field_value);
$rendered_fields[$i][$field_id] = $styled_field_value;
}
}
}
$views_plugin_style
->setRenderedFields($rendered_fields);
// unset($views_plugin_style->view->row_index);.
return $rendered_fields;
}
/**
* Prefix the field value with its label, where requested. No styling.
*
* @param string $field
* The field.
* @param string $field_content
* The content of the field.
*
* @return string
* $field_content prefixed with the label defined for the field
*/
public function pluginStyleAddLabel($field, $field_content) {
$label = $field
->label();
if ($label && $field->options['element_label_colon']) {
$label .= ': ';
}
return $label . $field_content;
}
/**
* Style field value, label and wrapper around both, where requested.
*
* Note: the "Add default classes" tickbox, options['element_default_classes'],
* is NOT supported to avoid the balloons having excessive bulky markup.
*
* @param string $field
* The field.
* @param string $field_content
* The content of the field.
*
* @return string
* $field_content wrapped in HTML with CSS classes
*/
public function pluginStyleAddLabelAndStyling($field, $field_content) {
if ($field_content_type = $field
->elementType(TRUE, TRUE)) {
$element = '<' . $field_content_type;
if ($field_content_class = $field
->elementClasses(NULL)) {
$element .= ' class="' . $field_content_class . '"';
}
$element .= '>';
$close_element = '</' . $field_content_type . '>';
$field_content = $element . $field_content . $close_element;
}
$label = $field
->label();
if ($label && $field->options['element_label_colon']) {
$label .= ': ';
}
$label_and_field_content = $label . $field_content;
if ($label_type = $field
->elementLabelType(TRUE, TRUE)) {
$element = '<' . $label_type;
if ($label_class = $field
->elementLabelClasses(NULL)) {
$element .= ' class="' . $label_class . '"';
}
$element .= '>';
$close_element = '</' . $label_type . '>';
$label_and_field_content = $element . $label . $close_element . $field_content;
}
if ($wrapper_type = $field
->elementWrapperType(TRUE, TRUE)) {
$element = '<' . $wrapper_type;
if ($wrapper_class = $field
->elementWrapperClasses(NULL)) {
$element .= ' class="' . $wrapper_class . '"';
}
$element .= '>';
$close_element = '</' . $wrapper_type . '>';
return $element . $label_and_field_content . $close_element;
}
return $label_and_field_content;
}
/**
* Returns as an array the raw value(s) of a field in the results set.
*
* @param object $views_plugin_style
* The Views plugin.
* @param mixed, field array or field name (string)
* The field or name of the field in the View.
* @param int index
* The row index into the Views result set.
*
* @return array
* The raw value(s) of the field in the specified row of the results set.
*/
function getViewResult($views_plugin_style, $field, $index) {
$field_name = $field
->getName();
if (!isset($views_plugin_style->view->result[$index])) {
$this
->messenger($this->stringTranslation
->translate("Field %name: no value found amongst view results.", array(
'%name' => $field_name,
)), 'warning', FALSE);
return NULL;
}
$row = $views_plugin_style->view->result[$index];
$row = $row->_entity
->getFields();
if (isset($row->{"field_{$field_name}"}) && $row->{"field_{$field_name}"} != array()) {
$values = $row->{"field_{$field_name}"};
}
elseif (isset($row->{"views_{$field_name}"})) {
$values = $row->{"views_{$field_name}"};
}
elseif (isset($row->{$field_name})) {
$values = $row->{$field_name};
}
elseif (isset($views_plugin_style->view->field[$field_name])) {
$values = $views_plugin_style
->getFieldValue($index, $field_name);
}
elseif (!empty($row->_entity_properties)) {
// Search API emergency fallback
$values = $this
->pluginStyleGetSearchApiValue($row, $field_name);
}
else {
return array(
'',
);
}
if (!is_array($values)) {
return array(
$values,
);
}
$single_values = array();
foreach ($values as $value) {
if (isset($value['raw'])) {
$value = $value['raw'];
}
if (isset($field['type'])) {
if ($field['type'] == 'addressfield') {
return $value;
}
if (is_array($value)) {
$value = $field['type'] == 'taxonomy_term_reference' ? $value['tid'] : reset($value);
}
}
elseif (is_array($value)) {
$value = reset($value);
}
$single_values[] = $value;
}
return $single_values;
}
function pluginStyleGetSearchApiValue($row, $field_name) {
$field_value = null;
if (isset($row->_entity_properties[$field_name])) {
//drupal_set_message(t('IPGV&M (1): Search API checking value for %name', array('%name' => $field_name)), 'warning', FALSE);
$field_value = $row->_entity_properties[$field_name];
}
elseif ($last_ = strrpos($field_name, '_')) {
$alt_field_name = Unicode::substr($field_name, $last_ + 1);
//drupal_set_message(t('IPGV&M (2): Search API checking value for %name', array('%name' => $alt_field_name)), 'warning', FALSE);
if (isset($row->_entity_properties[$alt_field_name])) {
$field_value = $row->_entity_properties[$alt_field_name];
}
}
else {
$alt_field_name = 'field_' . $field_name;
//drupal_set_message(t('IPGV&M (3): Search API checking value for %name', array('%name' => $alt_field_name)), 'warning', FALSE);
if (isset($row->_entity_properties[$alt_field_name])) {
$field_value = $row->_entity_properties[$alt_field_name];
}
}
if (isset($field_value)) {
//drupal_set_message(t('IPGV&M (success!): Search API %name=%value', array('%name' => $field_name, '%value' => $field_value)), 'warning', FALSE);
}
else {
$this
->messenger($this->stringTranslation
->translate('IPGV&M: Could not retrieve value of field %name, as its name does not appear among these Search API keys: %keys', array(
'%name' => $field_name,
'%keys' => implode(', ', array_keys($row->_entity_properties)),
)), 'warning', FALSE);
}
return isset($field_value) ? $field_value : '';
}
}