cforge-2.0.x-dev/modules/cforge_network/cforge_network.module
modules/cforge_network/cforge_network.module
<?php
use Drupal\user\Entity\User;
use Drupal\user\UserInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\Core\Entity\ContentEntityInterface;
const NOMINATIM_PATH = "https://nominatim.openstreetmap.org/search";
/**
* Implements hook_cron().
*
* Geocode ten existing users who haven't been coded yet.
* @temp most migrated fields should already be geocoded.
*/
function cforge_network_cron() {
$uids = \Drupal::entityQuery('user')
->condition('coordinates', NULL, 'IS NULL')
->condition('uid', 1, '>')
->range(0, 10)
->addTag('sort_by_random')
->execute();
foreach ($uids as $uid) {
// Take a random one to avoid getting stuck on the same one failing.
$account = User::load($uid);
cforge_geolocate_entity($account);
$account->save();
}
}
/**
* Implements hook_query_TAG_alter
* @param \Drupal\Core\Database\Query\AlterableInterface $query
*/
function cforge_network_query_sort_by_random_alter($query) {
$query->orderRandom();
}
/**
* Implements hook_user_presave().
*/
function cforge_network_user_presave($account) {
if ($account->isNew() or $account->address->getValue() != $account->original->address->getValue()) {
// limit the frequency of requests
if (!batch_get() and !\Drupal::moduleHandler()->moduleExists('migrate_upgrade')) {
cforge_geolocate_entity($account);
}
}
}
/**
*
* @param UserInterface $account
*/
function cforge_geolocate_entity(ContentEntityInterface $entity, $source = 'address', $dest = 'coordinates') {
if ($address = $entity->{$source}->first()) {
if ($value = $address->getValue()) {
try {
$point = cforge_geocode_quick($value);
}
catch (RequestException $e) {// From Guzzle
\Drupal::messenger()->addWarning('There was a problem looking up the coordinates from your address.');
throw $e;
}
catch (\Exception $e) {
\Drupal::logger('cforge')->error(
'@address failed to geocode: @reason ',
['@address' => NOMINATIM_PATH, '@reason' => $e->getMessage()]
);
}
}
if (isset($point)) {
$entity->{$dest}->value = $point;
}
}
}
function cforge_geolocate_field_widget_single_element_form_alter(array &$element, \Drupal\Core\Form\FormStateInterface $form_state, array $context) {
$element['#description'] = 'blah';
exit;
return;
\Drupal::messenger()->addStatus(
t(
'Thanks to @osm_link for putting the address on the map',
['@osm_link' => Link::fromTextAndUrl('OpenStreetMap', Url::fromUri('http://openstreetmap.org'))->toString()]
)
);
}
/**
* Encode the addressField using openstreetmap.
*
* Wasted too much time fucking about with the geocoder module
*
* @param array $address_value
* the value from the address field
*
* @return POINT|NULL
*
* @note No heavy uses (an absolute maximum of 1 request per second). More
* details at https://operations.osmfoundation.org/policies/nominatim Provide a
* valid HTTP Referer or User-Agent identifying the application (stock
* User-Agents as set by http libraries will not do). Clearly display
* attribution as suitable for your medium.
*
* @see https://wiki.openstreetmap.org/wiki/Nominatim
*/
function cforge_geocode_quick($address_value) {
$fields = [
'street' => 'address_line1',
'county' => 'locality',
'state' => 'administrative_area',
'country' => 'country_code',
'postalcode' => ['postal_code', 'sorting_code'],
];
// Ensure the house number is first
if ($line1 = &$address_value['address_line1']) {
if (preg_match('/[0-9]+/', $line1, $matches)) {
$line1 = str_replace($matches[0], '', $line1);
$line1 = $matches[0] .' '.trim($line1);
}
}
$query = ['format' => 'json'];
foreach ($fields as $osmField => $dFields) {
foreach ((array)$dFields as $dField) {
if (isset($address_value[$dField])) {
$query[$osmField] = $address_value[$dField];
}
}
}
/** var $httpClient = Guzzle */
$httpClient = \Drupal::httpClient();
$dest_url = NOMINATIM_PATH . '?'.http_build_query($query);
$result = $httpClient->get(
$dest_url,
['headers' => ['User-Agent' => 'http://drupal.org/project/cforge']]
);
if ($result->getStatusCode() <> '200') {
throw new Exception("$dest_url returned empty body: ".$result->getReasonPhrase());
}
$body = $result->getBody();
if ($content = json_decode($body->getContents())) {
return \Drupal::service('geofield.wkt_generator')
->wktBuildPoint([$content[0]->lon, $content[0]->lat]);
}
throw new \Exception('Nominatim returned empty');
}
