Passed
Push — master ( 60dbc8...203b6c )
by Greg
11:35
created

AutocompleteController::select2MediaObject()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 34
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 4
eloc 22
c 2
b 0
f 0
nc 2
nop 1
dl 0
loc 34
rs 9.568
1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Http\Controllers;
21
22
use Fisharebest\Localization\Locale\LocaleInterface;
23
use Fisharebest\Webtrees\Auth;
24
use Fisharebest\Webtrees\Family;
25
use Fisharebest\Webtrees\GedcomRecord;
26
use Fisharebest\Webtrees\Individual;
27
use Fisharebest\Webtrees\Services\SearchService;
28
use Fisharebest\Webtrees\Site;
29
use Fisharebest\Webtrees\Source;
30
use Fisharebest\Webtrees\Tree;
31
use Illuminate\Database\Capsule\Manager as DB;
32
use Illuminate\Database\Query\JoinClause;
33
use Illuminate\Support\Collection;
34
use Illuminate\Support\Str;
35
use Psr\Http\Message\ResponseInterface;
36
use Psr\Http\Message\ServerRequestInterface;
37
38
use function assert;
39
use function curl_close;
40
use function curl_exec;
41
use function curl_init;
42
use function curl_setopt;
43
use function file_get_contents;
44
use function function_exists;
45
use function ini_get;
46
use function is_array;
47
use function json_decode;
48
use function preg_match_all;
49
use function preg_quote;
50
use function rawurlencode;
51
use function response;
52
53
use const CURLOPT_RETURNTRANSFER;
54
use const CURLOPT_URL;
55
56
/**
57
 * Controller for the autocomplete callbacks
58
 */
59
class AutocompleteController extends AbstractBaseController
60
{
61
    /** @var SearchService */
62
    private $search_service;
63
64
    /**
65
     * AutocompleteController constructor.
66
     *
67
     * @param SearchService $search_service
68
     */
69
    public function __construct(SearchService $search_service)
70
    {
71
        $this->search_service = $search_service;
72
    }
73
74
    /**
75
     * Autocomplete for media folders.
76
     *
77
     * @param ServerRequestInterface $request
78
     *
79
     * @return ResponseInterface
80
     */
81
    public function folder(ServerRequestInterface $request): ResponseInterface
82
    {
83
        $tree = $request->getAttribute('tree');
84
        assert($tree instanceof Tree);
85
86
        $query = $request->getQueryParams()['query'] ?? '';
87
88
        $media_filesystem = $tree->mediaFilesystem();
89
90
        $contents = new Collection($media_filesystem->listContents('', true));
91
92
        $folders = $contents
93
            ->filter(static function (array $object) use ($query): bool {
94
                return $object['type'] === 'dir' && Str::contains($object['path'], $query);
95
            })
96
            ->map(static function (array $object): array {
97
                return ['value' => $object['path']];
98
            });
99
100
        return response($folders);
101
    }
102
103
    /**
104
     * Autocomplete for source citations.
105
     *
106
     * @param ServerRequestInterface $request
107
     *
108
     * @return ResponseInterface
109
     */
110
    public function page(ServerRequestInterface $request): ResponseInterface
111
    {
112
        $tree = $request->getAttribute('tree');
113
        assert($tree instanceof Tree);
114
115
        $query = $request->getQueryParams()['query'] ?? '';
116
        $xref  = $request->getQueryParams()['extra'] ?? '';
117
118
        $source = Source::getInstance($xref, $tree);
119
        $source = Auth::checkSourceAccess($source);
120
121
        $regex_query = preg_quote(strtr($query, [' ' => '.+']), '/');
122
123
        // Fetch all records with a link to this source
124
        $individuals = DB::table('individuals')
125
            ->join('link', static function (JoinClause $join): void {
126
                $join
127
                    ->on('l_file', '=', 'i_file')
128
                    ->on('l_from', '=', 'i_id');
129
            })
130
            ->where('i_file', '=', $tree->id())
131
            ->where('l_to', '=', $source->xref())
132
            ->where('l_type', '=', 'SOUR')
133
            ->distinct()
134
            ->select(['individuals.*'])
135
            ->get()
136
            ->map(Individual::rowMapper())
137
            ->filter(GedcomRecord::accessFilter());
138
139
        $families = DB::table('families')
140
            ->join('link', static function (JoinClause $join): void {
141
                $join
142
                    ->on('l_file', '=', 'f_file')
143
                    ->on('l_from', '=', 'f_id')
144
                    ->where('l_type', '=', 'SOUR');
145
            })
146
            ->where('f_file', '=', $tree->id())
147
            ->where('l_to', '=', $source->xref())
148
            ->where('l_type', '=', 'SOUR')
149
            ->distinct()
150
            ->select(['families.*'])
151
            ->get()
152
            ->map(Family::rowMapper())
153
            ->filter(GedcomRecord::accessFilter());
154
155
        $pages = new Collection();
156
157
        foreach ($individuals->merge($families) as $record) {
158
            if (preg_match_all('/\n1 SOUR @' . $source->xref() . '@(?:\n[2-9].*)*\n2 PAGE (.*' . $regex_query . '.*)/i', $record->gedcom(), $matches)) {
159
                $pages = $pages->concat($matches[1]);
160
            }
161
162
            if (preg_match_all('/\n2 SOUR @' . $source->xref() . '@(?:\n[3-9].*)*\n3 PAGE (.*' . $regex_query . '.*)/i', $record->gedcom(), $matches)) {
163
                $pages = $pages->concat($matches[1]);
164
            }
165
        }
166
167
        $pages = $pages
168
            ->unique()
169
            ->map(static function (string $page): array {
170
                return ['value' => $page];
171
            })
172
            ->all();
173
174
        return response($pages);
175
    }
176
177
    /**
178
     * /**
179
     * Autocomplete for place names.
180
     *
181
     * @param ServerRequestInterface $request
182
     *
183
     * @return ResponseInterface
184
     */
185
    public function place(ServerRequestInterface $request): ResponseInterface
186
    {
187
        $tree = $request->getAttribute('tree');
188
        assert($tree instanceof Tree);
189
190
        $locale = $request->getAttribute('locale');
191
        assert($locale instanceof LocaleInterface);
192
193
        $query = $request->getQueryParams()['query'] ?? '';
194
        $data  = [];
195
196
        foreach ($this->search_service->searchPlaces($tree, $query) as $place) {
197
            $data[] = ['value' => $place->gedcomName()];
198
        }
199
200
        $geonames = Site::getPreference('geonames');
201
202
        if ($data === [] && $geonames !== '') {
203
            // No place found? Use an external gazetteer
204
            $url =
205
                'http://api.geonames.org/searchJSON' .
206
                '?name_startsWith=' . rawurlencode($query) .
207
                '&lang=' . $locale->languageTag() .
208
                '&fcode=CMTY&fcode=ADM4&fcode=PPL&fcode=PPLA&fcode=PPLC' .
209
                '&style=full' .
210
                '&username=' . rawurlencode($geonames);
211
212
            // try to use curl when file_get_contents not allowed
213
            if (ini_get('allow_url_fopen')) {
214
                $json = file_get_contents($url);
215
            } elseif (function_exists('curl_init')) {
216
                $ch = curl_init();
217
                curl_setopt($ch, CURLOPT_URL, $url);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_setopt() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

217
                curl_setopt(/** @scrutinizer ignore-type */ $ch, CURLOPT_URL, $url);
Loading history...
218
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
219
                $json = curl_exec($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

219
                $json = curl_exec(/** @scrutinizer ignore-type */ $ch);
Loading history...
220
                curl_close($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_close() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

220
                curl_close(/** @scrutinizer ignore-type */ $ch);
Loading history...
221
            } else {
222
                return response([]);
223
            }
224
225
            $places = json_decode($json, true);
226
            if (isset($places['geonames']) && is_array($places['geonames'])) {
227
                foreach ($places['geonames'] as $k => $place) {
228
                    $data[] = ['value' => $place['name'] . ', ' . $place['adminName2'] . ', ' . $place['adminName1'] . ', ' . $place['countryName']];
229
                }
230
            }
231
        }
232
233
        return response($data);
234
    }
235
}
236