ip_geoloc-2.0.0-alpha0/src/Form/SetLocationForm.php
src/Form/SetLocationForm.php
<?php
namespace Drupal\ip_geoloc\Form;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Url;
use Drupal\ip_geoloc\Services\IpGeoLocSession;
use Drupal\ip_geoloc\Services\IpGeoLocAPI;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Peding doc.
*/
class SetLocationForm extends FormBase {
/**
* The Messenger service.
*
* @var \Drupal\Core\Messenger\MessengerInterface
*/
protected $messenger;
/**
* The IP Geolocation API service.
*
* @var \Drupal\ip_geoloc\Services\IpGeoLocAPI
*/
protected $api;
/**
* The IP Geolocation Session service.
*
* @var \Drupal\ip_geoloc\Services\IpGeoLocSession
*/
protected $session;
/**
* The Module Handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The Request Stack service.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* The Entity Type Manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The Entity Field Manager service.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $entityFieldManager;
/**
* {@inheritdoc}
*/
public function __construct(MessengerInterface $messenger, ConfigFactoryInterface $config_factory, IpGeoLocAPI $api, IpGeoLocSession $session, ModuleHandlerInterface $module_handler, RequestStack $request_stack, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager) {
parent::__construct($config_factory);
$this->messenger = $messenger;
$this->api = $api;
$this->session = $session;
$this->moduleHandler = $module_handler;
$this->requestStack = $request_stack;
$this->entityTypeManager = $entity_type_manager;
$this->entityFieldManager = $entity_field_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('messenger'),
$container->get('config.factory'),
$container->get('ip_geoloc.api'),
$container->get('ip_geoloc.session'),
$container->get('module_handler'),
$container->get('request_stack'),
$container->get('entity_type.manager'),
$container->get('entity_field.manager')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'ip_geoloc_set_location_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Migration comment: Part of ip_geoloc_set_location_form definition.
$ip_geoloc_config = $this->config('ip_geoloc.settings');
$has_find_visitor = $ip_geoloc_config->get('ip_geoloc_visitor_find') ? $ip_geoloc_config->get('ip_geoloc_visitor_find') : TRUE;
$is_address_editable = $ip_geoloc_config->get('ip_geoloc_visitor_address_editable') ? $ip_geoloc_config->get('ip_geoloc_visitor_address_editable') : TRUE;
$geo_vocabulary_id = $ip_geoloc_config->get('ip_geoloc_geo_vocabulary_id') ? $ip_geoloc_config->get('ip_geoloc_geo_vocabulary_id') : 0;
if (!$has_find_visitor && !$is_address_editable && !$geo_vocabulary_id) {
$this->messenger->addError($this->t('You should select at least one of the three widgets available for the "Set my location" block.'));
return $form;
}
$location = $this->api->getVisitorLocation();
$form['#attributes']['id'] = $ajax_wrapper_id = Html::getUniqueId('set-location-form');
if ($has_find_visitor) {
$this->setMyLocationAddFindMe($form, $ajax_wrapper_id);
}
$options = $this->setMyLocationAddSelector($form, $location, $is_address_editable, $geo_vocabulary_id);
$is_reverse_geocode = $has_find_visitor && $ip_geoloc_config->get('ip_geoloc_visitor_reverse_geocode') ? $ip_geoloc_config->get('ip_geoloc_visitor_reverse_geocode') : TRUE;
if ($is_reverse_geocode || $is_address_editable) {
$this->setMyLocationAddAddress($form, $location);
}
if ($geo_vocabulary_id) {
$this->setMyLocationAddRegion($form, $location, $geo_vocabulary_id);
}
$this->setMyLocationAddLogic($form, $location, $options, $is_address_editable, $geo_vocabulary_id);
if ($is_address_editable || $geo_vocabulary_id) {
$form['submit_address'] = [
'#type' => 'submit',
'#value' => t('Go'),
'#submit' => ['_ip_geoloc_process_go_to_submit'],
'#weight' => 15,
];
}
$form['#attributes']['class'][] = 'ip-geoloc-address';
$form['#attached']['library'][] = 'ip_geoloc/ip_geoloc.client';
return $form;
}
/*
* The value of the 'fixed_address' selector is stored on the location, as
* stored on the session. Its value is coded as follows:
* 0: "Find me" button pressed
* 1: "Move to" selected and "Go" pressed
* 2: "Region" selected and "Go" pressed
*/
function setMyLocationAddLogic(&$form, $location, $options, $is_address_editable, $geo_vocabulary_id) {
$fixed_address = empty($location['fixed_address']) ? 0 : (int)$location['fixed_address'];
// Check if we have a "Region/Move to" drop-down ...
if (isset($form['fixed_address'])) {
// Default to "Move to", unless we do not have a location at all, or when
// "Region" was selected and "Go" pressed.
$selected = '1';
if (isset($options['2']) && !isset($location['fixed_address']) || $fixed_address == 2) {
$selected = '2';
}
$form['fixed_address']['#default_value'] = $selected;
}
// Disable the address box when it isn't editable.
$form['street_address']['#disabled'] = !$is_address_editable;
// Special logic for when there's a Region selector
if ($geo_vocabulary_id) {
// If "Move to" is NOT there, remove Address box when Go was pressed.
if (!isset($options['1']) && $fixed_address == 2) {
unset($form['street_address']);
}
elseif (count($options) > 1) {
// If 2 or more options, then hide Address box when Region pressed.
$form['street_address']['#states'] = array(
'invisible' => array('select[name="fixed_address"]' => array('value' => '2')),
);
}
}
}
function setMyLocationAddAddress(&$form, $location) {
$ip_geoloc_config = $this->config('ip_geoloc.settings');
$textarea = $ip_geoloc_config->get('ip_geoloc_address_element', 2);
$default_address = Html::escape($ip_geoloc_config->get('ip_geoloc_visitor_address_default', ''));
$form['street_address'] = array(
'#type' => $textarea ? 'textarea' : 'textfield',
'#rows' => $textarea,
'#default_value' => empty($location['formatted_address']) ? $default_address : $location['formatted_address'],
'#weight' => 10,
);
}
function setMyLocationAddSelector(&$form, &$location, $is_address_editable, $geo_vocabulary_id) {
$ip_geoloc_config = $this->config('ip_geoloc.settings');
$options = array();
$editable_address_label = $ip_geoloc_config->get('ip_geoloc_visitor_address_label', '');
$editable_region_label = $ip_geoloc_config->get('ip_geoloc_visitor_region_label', '');
if ($is_address_editable && $editable_address_label != '<none>') {
$options['1'] = $editable_address_label ? Xss::filterAdmin($editable_address_label) : IP_GEOLOC_VISITOR_DEFAULT_ADDRESS_LABEL;
}
if ($geo_vocabulary_id && $editable_region_label != '<none>') {
$options['2'] = $editable_region_label ? Xss::filterAdmin($editable_region_label) : IP_GEOLOC_VISITOR_DEFAULT_REGION_LABEL;
}
if (!empty($options)) {
$form['fixed_address'] = array(
'#type' => count($options) <= 1 ? 'markup' : 'select',
'#options' => $options,
'#default_value' => isset($options['2']) ? '2' : '1',
'#markup' => isset($options['1']) ? $options['1'] : $options['2'],
'#weight' => 5,
);
}
return $options;
}
function setMyLocationAddFindMe(&$form, $ajax_wrapper_id) {
$ip_geoloc_config = $this->config('ip_geoloc.settings');
$find_visitor_label = $ip_geoloc_config->get('ip_geoloc_visitor_find_label', '');
$find_visitor_label = empty($find_visitor_label) ? IP_GEOLOC_VISITOR_DEFAULT_FIND_LABEL : Xss::filterAdmin($find_visitor_label);
// See _ip_geoloc_process_find_me_ajax() for why this is done here.
if ($ip_geoloc_config->get('ip_geoloc_visitor_reverse_geocode')) {
$form['#attached']['js'][] = IP_GEOLOC_GOOGLE_MAPS;
}
$form['find'] = array(
// Use 'submit' to go with '#submit'. For #ajax 'button' may be used too.
'#type' => 'submit',
'#value' => $find_visitor_label,
'#submit' => array('_ip_geoloc_process_find_me_submit'),
'#ajax' => array(
'callback' => '_ip_geoloc_process_find_me_ajax',
'wrapper' => $ajax_wrapper_id,
'progress' => FALSE,
),
'#weight' => $ip_geoloc_config->get('ip_geoloc_visitor_find_position', 0),
);
$throbber_text = $ip_geoloc_config->get('ip_geoloc_throbber_text', '');
if (empty($throbber_text)) {
$throbber_text = IP_GEOLOC_THROBBER_DEFAULT_TEXT;
}
if ($throbber_text != '<none>') {
$form['find']['#ajax']['progress'] = array(
'type' => 'throbber',
'message' => Xss::filterAdmin($throbber_text),
);
}
}
function setMyLocationAddRegion(&$form, $location, $geo_vocabulary_id) {
$ip_geoloc_config = $this->config('ip_geoloc.settings');
$regions = array(0 => '--' . t('none') . '--');
foreach ($this->entityTypeManager->getStorage('taxonomy_term')->loadTree($geo_vocabulary_id) as $term) {
$regions[$term->tid] = str_repeat('-', $term->depth) . $term->name;
if (isset($location['region']) && $location['region'] == $term->tid) {
$current_region_name = $term->name;
}
}
if ($ip_geoloc_config->get('ip_geoloc_region_autocomplete')) {
// Use an autocomplete textfield, instead of a drop-down selector.
$form['region'] = array(
'#type' => 'textfield',
'#size' => 29,
'#default_value' => isset($current_region_name) ? $current_region_name : '',
'#description' => t('Type the first letters of a region.'),
// Refer to hook_menu().
'#autocomplete_path' => 'ip_geoloc/region_autocomplete',
'#executes_submit_callback' => TRUE,
'#weight' => 10,
);
}
else {
$form['region'] = array(
'#type' => 'select',
'#options' => $regions,
'#default_value' => isset($location['region']) ? $location['region'] : 0,
'#weight' => 10,
);
}
$form['region']['#states'] = array(
'visible' => array('select[name="fixed_address"]' => array('value' => '2')),
);
}
/**
* Returns the supplied string into an array with various address components.
*
* @param string $partial_address_or_landmark
* @return array, a location holding, latitude, longitude and address fields
*/
function setLocationFromAddress($partial_address_or_landmark) {
if (empty($partial_address_or_landmark)) {
$location = array(
'provider' => 'user',
'ip_address' => $this->requestStack->getCurrentRequest()->getClientIp(),
);
}
else {
if ($this->moduleHandler->moduleExists('geocoder')) {
// Use Google server-side API to retrieve lat/long from entered text, as
// well as all components of the full address.
$geocoder = \Drupal::service('geocoder');
$point = $geocoder->geocode($partial_address_or_landmark, ['google']);
}
else {
$this->messenger->addMessage(
t('<a target="project_page" href="@geocoder">Geocoder</a> module must be enabled to geocode an address.', [
'@geocoder' => Url::fromUri('http://drupal.org/project/geocoder')->toString()]),
'warning');
}
if (empty($point)) {
$this->messenger->addMessage(t('The address %address could not be geocoded to a location.', array(
'%address' => $partial_address_or_landmark)), 'warning');
}
else {
$location = array(
'provider' => 'user+google',
'is_updated' => TRUE,
'ip_address' => $this->requestStack->getCurrentRequest()->getClientIp(),
'latitude' => $point->coords[1],
'longitude' => $point->coords[0],
'formatted_address' => $point->data['geocoder_formatted_address'],
'accuracy' => $point->data['geocoder_accuracy'],
);
// Flatten the point object into a straight location array.
foreach ($point->data['geocoder_address_components'] as $component) {
if (!empty($component->long_name)) {
$type = $component->types[0];
$location[$type] = $component->long_name;
if ($type == 'country' && !empty($component->short_name)) {
$location['country_code'] = $component->short_name;
}
}
}
}
}
$location['fixed_address'] = 1;
return $location;
}
function setLocationFromRegion($region_name_or_tid) {
$ip_geoloc_config = $this->config('ip_geoloc.settings');
if (!is_numeric($region_name_or_tid)) {
$geo_vocabulary_id = $ip_geoloc_config->get('ip_geoloc_geo_vocabulary_id', 0);
foreach ($this->entityTypeManager->getStorage('taxonomy_term')->loadTree($geo_vocabulary_id) as $term) {
if (strcasecmp($term->name, $region_name_or_tid) === 0) {
$region_name_or_tid = $term->tid;
break;
}
}
}
$parent = $ip_geoloc_config->get('ip_geoloc_region_parent', 0);
$location = $this->getLocationFromTerm($region_name_or_tid, $parent);
$location['fixed_address'] = 2;
return $location;
}
/**
* Returns the location object belonging to supplied region taxonomy term id.
*
* @param int $tid
* Taxonomy term identifier
* @param int $return_parent
* To return location of the parent (1) or grand-parent (2) rather than the
* supplied region term.
*
* @return array, location info
*/
function getLocationFromTerm($tid, $parent = 0) {
$ip_geoloc_config = $this->config('ip_geoloc.settings');
$term = $this->entityTypeManager->getStorage('taxonomy_term')->load($tid);
$parents = $term->getParents();
if (empty($parents)) {
return array('provider' => 'taxonomy', 'ip_address' => $this->requestStack->getCurrentRequest()->getClientIp());
}
$term = $parents[min($parent, count($parents) - 1)];
// Get lat,lng from the Geofield on this term
$geo_vocabulary_id = $ip_geoloc_config->get('ip_geoloc_geo_vocabulary_id', 0);
$vocabulary = $this->entityTypeManager->getStorage('taxonomy_vocabulary')->load($geo_vocabulary_id);
$fields_definition = $this->entityFieldManager->getFieldDefinitions('taxonomy_term', $vocabulary->machine_name);
foreach ($fields_definition as $field_name => $field_instance) {
$field = $field_instance->getType();
if ($field['type'] == 'geofield') {
$value = reset($term->{$field_name});
$value = is_array($value) ? reset($value) : array();
if (empty($value['lat'])) {
$this->messenger->addMessage(t('The latitude and longitude of the term %name are not known.', array('%name' => $field_name)), 'warning');
$value = array('lat' => NULL, 'lon' => NULL);
}
return array(
'provider' => 'taxonomy',
'is_updated' => TRUE,
'ip_address' => $this->requestStack->getCurrentRequest()->getClientIp(),
'region' => $term->tid,
'latitude' => $value['lat'],
'longitude' => $value['lon'],
//'formatted_address' => $term->name,
);
}
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array $form, FormStateInterface $form_state) {
// Migration comment: Part of _ip_geoloc_process_go_to_submit for submiting ip_geoloc_set_location_form.
// Clear any pending location retrieval process that may be in process.
$this->session->setSessionValue('last_position_check', time());
$this->session->setSessionValue('position_pending_since', NULL);
$geo_vocabulary_id = \Drupal::state()->get('ip_geoloc_geo_vocabulary_id', 0);
if ($form_state->isValueEmpty('fixed_address')) {
// Nothing slected. As 'Go' was pressed we need to choose 1 or 2.
$form_state->setValue('fixed_address', $geo_vocabulary_id ? '2' : '1');
}
if ($form_state->getValue('fixed_address') == '1') {
// Using new 'input' rather than current 'values'.
$input = $form_state->get('input');
if(!empty($input['street_address'])) {
$entered_address = Html::escape($input['street_address']);
}else {
$entered_address = NULL;
}
$location = $this->setLocationFromAddress($entered_address);
}
elseif ($geo_vocabulary_id) {
// "Region" selected.
$location = $this->setLocationFromRegion($form_state->getValue('region'));
}
// Wipe old location before setting the new one (to avoid merging).
$this->session->setSessionValue('location', NULL);
$this->session->setSessionValue('location', $location);
$redirect = \Drupal::state()->get('ip_geoloc_address_redirect');
if (!empty($redirect)) {
$form_state->set('redirect',$redirect);
}
// No need to remember the form state. It's all kept on the session.
// Also, if set to TRUE, content is rendered before new location is set
// and Region selector will be one step behind.
$form_state->setRebuild();
}
}
