1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2021 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\Http\RequestHandlers; 21 22use Fisharebest\Webtrees\I18N; 23use Fisharebest\Webtrees\Place; 24use Fisharebest\Webtrees\Site; 25use Fisharebest\Webtrees\Tree; 26use GuzzleHttp\Client; 27use GuzzleHttp\Exception\RequestException; 28use Illuminate\Support\Collection; 29use Psr\Http\Message\ServerRequestInterface; 30 31use function assert; 32use function is_array; 33use function json_decode; 34use function rawurlencode; 35 36use const JSON_THROW_ON_ERROR; 37 38/** 39 * Autocomplete handler for places 40 */ 41class AutoCompletePlace extends AbstractAutocompleteHandler 42{ 43 // Options for fetching files using GuzzleHTTP 44 private const GUZZLE_OPTIONS = [ 45 'connect_timeout' => 3, 46 'read_timeout' => 3, 47 'timeout' => 3, 48 ]; 49 50 protected function search(ServerRequestInterface $request): Collection 51 { 52 $tree = $request->getAttribute('tree'); 53 assert($tree instanceof Tree); 54 55 $query = $request->getAttribute('query'); 56 57 $data = $this->search_service 58 ->searchPlaces($tree, $query, 0, static::LIMIT) 59 ->map(static function (Place $place): string { 60 return $place->gedcomName(); 61 }); 62 63 $geonames = Site::getPreference('geonames'); 64 65 if ($data->isEmpty() && $geonames !== '') { 66 // No place found? Use an external gazetteer 67 $url = 68 'https://secure.geonames.org/searchJSON' . 69 '?name_startsWith=' . rawurlencode($query) . 70 '&lang=' . I18N::languageTag() . 71 '&fcode=CMTY&fcode=ADM4&fcode=PPL&fcode=PPLA&fcode=PPLC' . 72 '&style=full' . 73 '&username=' . rawurlencode($geonames); 74 75 // Read from the URL 76 $client = new Client(); 77 try { 78 $json = $client->get($url, self::GUZZLE_OPTIONS)->getBody()->__toString(); 79 $places = json_decode($json, true, 512, JSON_THROW_ON_ERROR); 80 if (isset($places['geonames']) && is_array($places['geonames'])) { 81 foreach ($places['geonames'] as $k => $place) { 82 $data->add($place['name'] . ', ' . $place['adminName2'] . ', ' . $place['adminName1'] . ', ' . $place['countryName']); 83 } 84 } 85 } catch (RequestException $ex) { 86 // Service down? Quota exceeded? 87 } 88 } 89 90 return new Collection($data); 91 } 92} 93