1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2023 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees\Module; 21 22use Fisharebest\Webtrees\FlashMessages; 23use Fisharebest\Webtrees\Gedcom; 24use Fisharebest\Webtrees\Html; 25use Fisharebest\Webtrees\I18N; 26use Fisharebest\Webtrees\Site; 27use Fisharebest\Webtrees\Validator; 28use GuzzleHttp\Psr7\Request; 29use Psr\Http\Message\RequestInterface; 30use Psr\Http\Message\ResponseInterface; 31use Psr\Http\Message\ServerRequestInterface; 32 33use function array_filter; 34use function implode; 35use function json_decode; 36use function redirect; 37use function usort; 38 39use const JSON_THROW_ON_ERROR; 40 41/** 42 * Class GeonamesAutocomplete - use geonames.org to search for place names 43 */ 44class GeonamesAutocomplete extends AbstractModule implements ModuleConfigInterface, ModuleMapAutocompleteInterface 45{ 46 use ModuleConfigTrait; 47 use ModuleMapAutocompleteTrait; 48 49 /** 50 * Name of the map provider. 51 * 52 * @return string 53 */ 54 public function title(): string 55 { 56 // I18N: https://www.geonames.org 57 return I18N::translate('GeoNames'); 58 } 59 60 /** 61 * Name of the map provider. 62 * 63 * @return string 64 */ 65 public function description(): string 66 { 67 $link = '<a href="https://geonames.org">geonames.org</a>'; 68 69 return I18N::translate('Search for place names using %s.', $link); 70 } 71 72 /** 73 * Should this module be enabled when it is first installed? 74 * 75 * @return bool 76 */ 77 public function isEnabledByDefault(): bool 78 { 79 return false; 80 } 81 82 /** 83 * @return ResponseInterface 84 */ 85 public function getAdminAction(): ResponseInterface 86 { 87 $this->layout = 'layouts/administration'; 88 89 // This was a global setting before it became a module setting... 90 $default = Site::getPreference('geonames'); 91 $username = $this->getPreference('username', $default); 92 93 return $this->viewResponse('modules/geonames/config', [ 94 'username' => $username, 95 'title' => $this->title(), 96 ]); 97 } 98 99 /** 100 * @param ServerRequestInterface $request 101 * 102 * @return ResponseInterface 103 */ 104 public function postAdminAction(ServerRequestInterface $request): ResponseInterface 105 { 106 $username = Validator::parsedBody($request)->string('username'); 107 108 $this->setPreference('username', $username); 109 110 FlashMessages::addMessage(I18N::translate('The preferences for the module “%s” have been updated.', $this->title()), 'success'); 111 112 return redirect($this->getConfigLink()); 113 } 114 115 /** 116 * @param string $place 117 * 118 * @return RequestInterface 119 */ 120 protected function createPlaceNameSearchRequest(string $place): RequestInterface 121 { 122 // This was a global setting before it became a module setting... 123 $default = Site::getPreference('geonames'); 124 $username = $this->getPreference('username', $default); 125 126 $uri = Html::url('https://secure.geonames.org/searchJSON', [ 127 'name_startsWith' => $place, 128 'featureClass' => 'P', 129 'lang' => I18N::languageTag(), 130 'username' => $username, 131 ]); 132 133 return new Request('GET', $uri); 134 } 135 136 /** 137 * @param ResponseInterface $response 138 * 139 * @return array<string> 140 */ 141 protected function parsePlaceNameSearchResponse(ResponseInterface $response): array 142 { 143 $body = $response->getBody()->getContents(); 144 $places = []; 145 $results = json_decode($body, false, 512, JSON_THROW_ON_ERROR); 146 147 foreach ($results->geonames as $result) { 148 if (($result->countryName ?? null) === 'United Kingdom') { 149 // adminName1 will be England, Scotland, etc. 150 $result->countryName = null; 151 } 152 153 $parts = [ 154 $result->name, 155 $result->adminName2 ?? null, 156 $result->adminName1 ?? null, 157 $result->countryName ?? null, 158 ]; 159 160 $places[] = implode(Gedcom::PLACE_SEPARATOR, array_filter($parts)); 161 } 162 163 usort($places, I18N::comparator()); 164 165 return $places; 166 } 167} 168