xref: /webtrees/app/Module/GeonamesAutocomplete.php (revision 2ebcf907ed34213f816592af04e6c160335d6311)
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\Module;
21
22use Fisharebest\Webtrees\FlashMessages;
23use Fisharebest\Webtrees\Gedcom;
24use Fisharebest\Webtrees\Html;
25use Fisharebest\Webtrees\I18N;
26use Fisharebest\Webtrees\Site;
27use GuzzleHttp\Psr7\Request;
28use Psr\Http\Message\RequestInterface;
29use Psr\Http\Message\ResponseInterface;
30use Psr\Http\Message\ServerRequestInterface;
31
32use function array_filter;
33use function implode;
34use function json_decode;
35use function redirect;
36use function usort;
37
38use const JSON_THROW_ON_ERROR;
39
40/**
41 * Class GeonamesAutocomplete - use geonames.org to search for place names
42 */
43class GeonamesAutocomplete extends AbstractModule implements ModuleConfigInterface, ModuleMapAutocompleteInterface
44{
45    use ModuleConfigTrait;
46    use ModuleMapAutocompleteTrait;
47
48    /**
49     * Name of the map provider.
50     *
51     * @return string
52     */
53    public function title(): string
54    {
55        // I18N: https://www.geonames.org
56        return I18N::translate('GeoNames');
57    }
58
59    /**
60     * Name of the map provider.
61     *
62     * @return string
63     */
64    public function description(): string
65    {
66        $link = '<a href="https://geonames.org">geonames.org</a>';
67
68        return I18N::translate('Search for place names using %s.', $link);
69    }
70
71    /**
72     * Should this module be enabled when it is first installed?
73     *
74     * @return bool
75     */
76    public function isEnabledByDefault(): bool
77    {
78        return false;
79    }
80
81    /**
82     * @return ResponseInterface
83     */
84    public function getAdminAction(): ResponseInterface
85    {
86        $this->layout = 'layouts/administration';
87
88        // This was a global setting before it became a module setting...
89        $default  = Site::getPreference('geonames');
90        $username = $this->getPreference('username', $default);
91
92        return $this->viewResponse('modules/geonames/config', [
93            'username' => $username,
94            'title'    => $this->title(),
95        ]);
96    }
97
98    /**
99     * @param ServerRequestInterface $request
100     *
101     * @return ResponseInterface
102     */
103    public function postAdminAction(ServerRequestInterface $request): ResponseInterface
104    {
105        $params = (array) $request->getParsedBody();
106
107        $this->setPreference('username', $params['username' ?? '']);
108
109        FlashMessages::addMessage(I18N::translate('The preferences for the module “%s” have been updated.', $this->title()), 'success');
110
111        return redirect($this->getConfigLink());
112    }
113
114    /**
115     * @param string $place
116     *
117     * @return RequestInterface
118     */
119    protected function createPlaceNameSearchRequest(string $place): RequestInterface
120    {
121        // This was a global setting before it became a module setting...
122        $default  = Site::getPreference('geonames');
123        $username = $this->getPreference('username', $default);
124
125        $uri = Html::url('https://secure.geonames.org/searchJSON', [
126            'name_startsWith' => $place,
127            'featureClass'    => 'P',
128            'lang'            => I18N::languageTag(),
129            'username'        => $username,
130        ]);
131
132        return new Request('GET', $uri);
133    }
134
135    /**
136     * @param ResponseInterface $response
137     *
138     * @return array<string>
139     */
140    protected function parsePlaceNameSearchResponse(ResponseInterface $response): array
141    {
142        $body    = $response->getBody()->getContents();
143        $places  = [];
144        $results = json_decode($body, false, 512, JSON_THROW_ON_ERROR);
145
146        foreach ($results->geonames as $result) {
147            if (($result->countryName ?? null) === 'United Kingdom') {
148                // adminName1 will be England, Scotland, etc.
149                $result->countryName = null;
150            }
151
152            $parts = [
153                $result->name,
154                $result->adminName2 ?? null,
155                $result->adminName1 ?? null,
156                $result->countryName ?? null,
157            ];
158
159            $places[] = implode(Gedcom::PLACE_SEPARATOR, array_filter($parts));
160        }
161
162        usort($places, I18N::comparator());
163
164        return $places;
165    }
166}
167