342 lines
12 KiB
Plaintext
342 lines
12 KiB
Plaintext
<?php
|
|
/**
|
|
* @file
|
|
* Farm Map Geofield integration.
|
|
*/
|
|
|
|
/**
|
|
* Implements hook_field_formatter_info().
|
|
*/
|
|
function farm_map_geofield_field_formatter_info() {
|
|
return array(
|
|
'farm_map_geofield' => array(
|
|
'label' => t('farmOS Map'),
|
|
'field types' => array('geofield'),
|
|
),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Implements hook_field_formatter_view().
|
|
*/
|
|
function farm_map_geofield_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
|
|
$element = array();
|
|
|
|
// First check to see if we have any value and remove any unset deltas.
|
|
foreach ($items as $delta => $item) {
|
|
if (empty($item['geom'])) {
|
|
unset($items[$delta]);
|
|
}
|
|
}
|
|
|
|
// If there are no items, stop here. We won't show anything.
|
|
if (empty($items)) {
|
|
return $element;
|
|
}
|
|
|
|
// Ensure GeoPHP is available.
|
|
geophp_load();
|
|
|
|
// Create array of features.
|
|
$features = array();
|
|
foreach ($items as $delta) {
|
|
if (array_key_exists('geom', $delta)) {
|
|
$geometry = geoPHP::load($delta['geom']);
|
|
}
|
|
else {
|
|
$geometry = geoPHP::load($delta);
|
|
}
|
|
$features[] = array(
|
|
'wkt' => $geometry->out('wkt'),
|
|
'projection' => 'EPSG:4326',
|
|
);
|
|
}
|
|
|
|
// If there are no features at this point, bail.
|
|
if (empty($features)) {
|
|
return $element;
|
|
}
|
|
|
|
// Build a map for each item.
|
|
$map_name = 'farm_map_geofield';
|
|
foreach ($features as $delta => $feature) {
|
|
$element[$delta] = array(
|
|
'#type' => 'farm_map',
|
|
'#map_name' => $map_name,
|
|
'#wkt' => $feature['wkt'],
|
|
);
|
|
}
|
|
|
|
return $element;
|
|
}
|
|
|
|
/**
|
|
* Implements hook_field_widget_info().
|
|
*/
|
|
function farm_map_geofield_field_widget_info() {
|
|
return array(
|
|
'farm_map_geofield' => array(
|
|
'label' => t('farmOS Map'),
|
|
'field types' => array('geofield'),
|
|
'behaviors' => array(
|
|
'multiple values' => FIELD_BEHAVIOR_CUSTOM,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Implements hook_field_widget_settings_form().
|
|
*/
|
|
function farm_map_geofield_field_widget_settings_form($field, $instance) {
|
|
$widget = $instance['widget'];
|
|
$settings = $widget['settings'];
|
|
$form = array();
|
|
|
|
// Add Geocoder support.
|
|
$use_geocoder = isset($settings['use_geocoder']) ? $settings['use_geocoder'] : 0;
|
|
$geocoder_form = array(
|
|
'#type' => 'fieldset',
|
|
'#title' => t('Geocoder settings'),
|
|
);
|
|
$geocoder_form['use_geocoder'] = array(
|
|
'#type' => 'checkbox',
|
|
'#title' => t('Enable geocoding of location data'),
|
|
'#default_value' => $use_geocoder,
|
|
// Can't nest this in a fieldset element without affecting data storage so
|
|
// instead hardcode one.
|
|
'#prefix' => '<fieldset><legend><span="fieldset-legend">' . t('Geocoder settings') . '</span></legend><div class="fieldset-wrapper">',
|
|
);
|
|
|
|
// Load the Geocoder widget settings.
|
|
module_load_include('inc', 'geocoder', 'geocoder.widget');
|
|
$new = geocoder_field_widget_settings_form($field, $instance);
|
|
$new['geocoder_field']['#options'][$field['field_name']] = t('Add extra field');
|
|
// If there are no options available search by ourselves to ensure the text
|
|
// field type was taken in account.
|
|
if (empty($new['geocoder_handler']['#options'])) {
|
|
$supported_field_types = geocoder_supported_field_types();
|
|
if (isset($supported_field_types['text'])) {
|
|
$processors = geocoder_handler_info();
|
|
foreach ($supported_field_types['text'] as $handler) {
|
|
$new['geocoder_handler']['#options'][$handler] = $processors[$handler]['title'];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Show the geocoder fields only if geocoder is selected.
|
|
farm_map_geofield_widget_add_states($new, ':input[name="instance[widget][settings][use_geocoder]"]');
|
|
|
|
// Close the fieldset we opened in the #prefix to use_geocoder.
|
|
$element_children = element_children($new);
|
|
$new[end($element_children)]['#suffix'] = '</div></fieldset>';
|
|
$geocoder_form += $new;
|
|
$form += $geocoder_form;
|
|
return $form;
|
|
}
|
|
|
|
/**
|
|
* Recurse through form elements adding a visibility #states selector
|
|
* and removing #required flags.
|
|
*/
|
|
function farm_map_geofield_widget_add_states(&$element, $selector) {
|
|
foreach (element_children($element) as $key) {
|
|
$element[$key]['#required'] = FALSE;
|
|
// Don't override any existing #states settings.
|
|
if (!isset($element[$key]['#states'])) {
|
|
$element[$key]['#states'] = array();
|
|
}
|
|
if (!isset($element[$key]['#states']['visible'])) {
|
|
$element[$key]['#states']['visible'] = array();
|
|
}
|
|
$element[$key]['#states']['visible'][$selector] = array('checked' => TRUE);
|
|
farm_map_geofield_widget_add_states($element[$key], $selector);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_field_widget_form().
|
|
*/
|
|
function farm_map_geofield_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
|
|
|
|
// Start with the Geofield WKT widget.
|
|
$instance['widget']['type'] = 'geofield_wkt';
|
|
$element = geofield_field_widget_form($form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
|
|
|
|
// Get the geometry (as WKT), from form state input (if available), or from
|
|
// the saved field value.
|
|
$wkt = '';
|
|
if (!empty($form_state['input'][$field['field_name']][$langcode][$delta]['geom'])) {
|
|
$wkt = $form_state['input'][$field['field_name']][$langcode][$delta]['geom'];
|
|
}
|
|
elseif (!empty($items[$delta]['geom'])) {
|
|
$wkt = $items[$delta]['geom'];
|
|
}
|
|
|
|
// Add a farmOS map instance with the WKT and drawing controls.
|
|
$element['map'] = array(
|
|
'#type' => 'farm_map',
|
|
'#map_name' => 'farm_map_geofield_widget',
|
|
'#wkt' => $wkt,
|
|
'#edit' => TRUE,
|
|
);
|
|
|
|
// Move the geometry field below the map.
|
|
$element['geom']['#weight'] = 100;
|
|
|
|
// Time to deal with optional geocoder integration
|
|
// Conditionally add geocoder button.
|
|
$parents = array_merge($element['#field_parents'], array($element['#field_name'], $langcode, $delta));
|
|
$field_name = $field['field_name'];
|
|
$id_prefix = implode('-', array_merge($parents, array($field_name, $delta)));
|
|
$wrapper_id = drupal_html_id($id_prefix . '-use-geocoder-wrapper');
|
|
$is_settings_form = isset($form['#title']) && $form['#title'] == t('Default value');
|
|
$settings = $instance['widget']['settings'];
|
|
if (!$is_settings_form && !empty($settings['use_geocoder']) && !empty($settings['geocoder_field'])) {
|
|
if ($settings['geocoder_field'] == $element['#field_name']) {
|
|
// Extra field.
|
|
$element['geocoder_input'] = array(
|
|
'#type' => 'textfield',
|
|
'#title' => t('Geocode'),
|
|
'#description' => t('Enter the place to geocode.'),
|
|
);
|
|
$label = $element['geocoder_input']['#title'];
|
|
}
|
|
elseif ($field = field_info_instance($instance['entity_type'], $settings['geocoder_field'], $instance['bundle'])) {
|
|
$label = $field['label'];
|
|
}
|
|
else {
|
|
switch ($settings['geocoder_field']) {
|
|
case 'title':
|
|
$label = t('Title');
|
|
break;
|
|
|
|
case 'name':
|
|
$label = t('Name');
|
|
break;
|
|
|
|
default:
|
|
$label = $settings['geocoder_field'];
|
|
}
|
|
}
|
|
|
|
$element['#prefix'] = '<div id="' . $wrapper_id . '">';
|
|
$element['#suffix'] = '</div>';
|
|
|
|
$element['use_geocoder'] = array(
|
|
'#type' => 'submit',
|
|
'#name' => strtr($id_prefix, '-', '_') . '_use_geocoder',
|
|
'#value' => t('Find using @field field', array('@field' => $label)),
|
|
'#attributes' => array('class' => array('field-use-geocoder-submit')),
|
|
// Avoid validation errors for e.g. required fields but do pass the value
|
|
// of the geocoder field.
|
|
'#limit_validation_errors' => array(),
|
|
'#ajax' => array(
|
|
'callback' => 'farm_map_geofield_geocode_ajax_callback',
|
|
'wrapper' => $wrapper_id,
|
|
'effect' => 'fade',
|
|
),
|
|
'#submit' => array('farm_map_geofield_use_geocoder_submit'),
|
|
'#weight' => 101,
|
|
);
|
|
}
|
|
|
|
// Add the element to an array, because it's the format that
|
|
// FIELD_BEHAVIOR_CUSTOM expects.
|
|
/**
|
|
* @todo
|
|
* This is necessary due to a legacy decision that was made in early farmOS
|
|
* development, when we were using the OpenLayers module. The farmOS Geofield
|
|
* (field_farm_geofield) was set up with a cardinality of -1 (allowing
|
|
* unlimited values), and the OpenLayers Geofield widget was configured to
|
|
* save all features as a combined geometry. So, even though the field was
|
|
* configured to allow unlimited values, only one value was ever saved. And
|
|
* only one value was/is ever needed. So in the future, when we move to Drupal
|
|
* 8, we should plan to change the cardinality to 1. The decision was made to
|
|
* leave it as -1 in 7.x-1.x, because changing it would cause a change to the
|
|
* REST API (which expects an array of geometries, even though only one should
|
|
* ever be provided).
|
|
*
|
|
* Using FIELD_BEHAVIOR_CUSTOM and wrapping the element in an array is the
|
|
* same approach that the openlayers_geofield module takes.
|
|
*/
|
|
$full_element = array($element);
|
|
|
|
// Return the widget element.
|
|
return $full_element;
|
|
}
|
|
|
|
/**
|
|
* Submit handler for the geocoder integration.
|
|
*/
|
|
function farm_map_geofield_use_geocoder_submit($form, &$form_state) {
|
|
$button = $form_state['triggering_element'];
|
|
|
|
// Go one level up in the form, to the widgets container.
|
|
$element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -1));
|
|
$field_name = $element['#field_name'];
|
|
$langcode = $element['#language'];
|
|
$delta = $element['#delta'];
|
|
$parents = $element['#field_parents'];
|
|
|
|
// Set the widget value based on geocoding results.
|
|
$field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
|
|
$geocoder_field = $field_state['instance']['widget']['settings']['geocoder_field'];
|
|
$geocoder_field_parents = array_merge(array_slice($button['#array_parents'], 0, -4), array($geocoder_field, $langcode));
|
|
// Since all validation errors are disabled we need to fetch the values for
|
|
// the geocoding as only validate values are sent to the values array of the
|
|
// form state.
|
|
$values = drupal_array_get_nested_value($form_state['input'], $geocoder_field_parents);
|
|
// Inject the values into the values array to ensure the geofield module is
|
|
// able to handle the data.
|
|
drupal_array_set_nested_value($form_state['values'], $geocoder_field_parents, $values);
|
|
|
|
if ($field_value = geocoder_widget_get_field_value($element['#entity_type'], $field_state['instance'], NULL, $values)) {
|
|
geophp_load();
|
|
$geometry = geoPHP::load($field_value[$langcode][$delta]['geom']);
|
|
// Openlayers can only use WKT, so translate.
|
|
$field_value[$langcode][$delta]['geom'] = $geometry->out('wkt');
|
|
$field_value[$langcode][$delta]['input_format'] = 'wkt';
|
|
// Override the field's value in the 'input' array to substitute the new
|
|
// field value for the one that was submitted.
|
|
drupal_array_set_nested_value($form_state, array_merge(array('input'), array_slice($button['#array_parents'], 0, -4), array($field_name)), $field_value);
|
|
}
|
|
|
|
// Rebuild the form to ensure that the map is recreated from scratch.
|
|
// See field_add_more_submit() in core field.form.inc for similar usage.
|
|
$form_state['rebuild'] = TRUE;
|
|
}
|
|
|
|
/**
|
|
* Return the altered form element from an AJAX request.
|
|
*
|
|
* @see farm_map_geofield_field_widget_form()
|
|
*/
|
|
function farm_map_geofield_geocode_ajax_callback($form, $form_state) {
|
|
$button = $form_state['triggering_element'];
|
|
|
|
// Go one level up in the form, to the widgets container.
|
|
$element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -1));
|
|
|
|
// Return the map, but remove the '_weight' element inserted
|
|
// by the field API.
|
|
unset($element['_weight']);
|
|
return $element;
|
|
}
|
|
|
|
/**
|
|
* Implements hook_geocoder_geocode_values_alter().
|
|
*/
|
|
function farm_map_geofield_geocoder_geocode_values_alter(&$source_field_values, &$field_info, $handler_settings, $field_instance) {
|
|
// If this is a farm_map_geofield pointing to its extra field adjust the
|
|
// field values and mock a text field.
|
|
if (isset($field_instance['widget']['settings']['geocoder_field']) && $field_instance['widget']['type'] == 'farm_map_geofield' && $field_instance['widget']['settings']['geocoder_field'] == $field_info['field_name']) {
|
|
if (isset($source_field_values[0]['geocoder_input'])) {
|
|
$source_field_values = array(
|
|
array('value' => $source_field_values[0]['geocoder_input']),
|
|
);
|
|
$field_info = array('type' => 'text');
|
|
}
|
|
}
|
|
}
|