Passed
Push — master ( e1f140...fd6c00 )
by Greg
05:58
created

AdminSiteController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 5
dl 0
loc 7
rs 10
c 0
b 0
f 0
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
declare(strict_types=1);
18
19
namespace Fisharebest\Webtrees\Http\Controllers;
20
21
use Exception;
22
use Fig\Http\Message\StatusCodeInterface;
23
use Fisharebest\Webtrees\Carbon;
24
use Fisharebest\Webtrees\FlashMessages;
25
use Fisharebest\Webtrees\Http\RequestHandlers\ControlPanel;
26
use Fisharebest\Webtrees\I18N;
27
use Fisharebest\Webtrees\Module\ModuleThemeInterface;
28
use Fisharebest\Webtrees\Services\DatatablesService;
29
use Fisharebest\Webtrees\Services\MailService;
30
use Fisharebest\Webtrees\Services\ModuleService;
31
use Fisharebest\Webtrees\Services\TreeService;
32
use Fisharebest\Webtrees\Services\UserService;
33
use Fisharebest\Webtrees\Site;
34
use Fisharebest\Webtrees\Tree;
35
use Fisharebest\Webtrees\User;
36
use Illuminate\Database\Capsule\Manager as DB;
37
use Illuminate\Database\Query\Builder;
38
use Illuminate\Database\Query\Expression;
39
use Illuminate\Support\Collection;
40
use League\Flysystem\FilesystemInterface;
41
use Psr\Http\Message\ResponseInterface;
42
use Psr\Http\Message\ServerRequestInterface;
43
use stdClass;
44
45
use function filter_var;
46
47
use const FILTER_VALIDATE_DOMAIN;
48
49
/**
50
 * Controller for site administration.
51
 */
52
class AdminSiteController extends AbstractBaseController
53
{
54
    /** @var string */
55
    protected $layout = 'layouts/administration';
56
57
    /** @var DatatablesService */
58
    private $datatables_service;
59
60
    /** @var MailService */
61
    private $mail_service;
62
63
    /** @var ModuleService */
64
    private $module_service;
65
66
    /** @var TreeService */
67
    private $tree_service;
68
69
    /** @var UserService */
70
    private $user_service;
71
72
    /**
73
     * AdminSiteController constructor.
74
     *
75
     * @param DatatablesService   $datatables_service
76
     * @param MailService         $mail_service
77
     * @param ModuleService       $module_service
78
     * @param UserService         $user_service
79
     */
80
    public function __construct(DatatablesService $datatables_service, MailService $mail_service, ModuleService $module_service, TreeService $tree_service, UserService $user_service)
81
    {
82
        $this->mail_service       = $mail_service;
83
        $this->datatables_service = $datatables_service;
84
        $this->module_service     = $module_service;
85
        $this->tree_service       = $tree_service;
86
        $this->user_service       = $user_service;
87
    }
88
89
    /**
90
     * @param ServerRequestInterface $request
91
     *
92
     * @return ResponseInterface
93
     */
94
    public function logs(ServerRequestInterface $request): ResponseInterface
95
    {
96
        $earliest = DB::table('log')->min('log_time');
97
        $latest   = DB::table('log')->max('log_time');
98
99
        $earliest = $earliest ? Carbon::make($earliest) : Carbon::now();
100
        $latest   = $latest ? Carbon::make($latest) : Carbon::now();
101
102
        $earliest = $earliest->toDateString();
0 ignored issues
show
Bug introduced by
The method toDateString() does not exist on null. ( Ignorable by Annotation )

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

102
        /** @scrutinizer ignore-call */ 
103
        $earliest = $earliest->toDateString();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
103
        $latest   = $latest->toDateString();
0 ignored issues
show
Bug introduced by
The method toDateString() does not exist on null. ( Ignorable by Annotation )

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

103
        /** @scrutinizer ignore-call */ 
104
        $latest   = $latest->toDateString();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
104
105
        $params   = $request->getQueryParams();
106
        $action   = $params['action'] ?? '';
107
        $from     = $params['from'] ?? $earliest;
108
        $to       = $params['to'] ?? $latest;
109
        $type     = $params['type'] ?? '';
110
        $text     = $params['text'] ?? '';
111
        $ip       = $params['ip'] ?? '';
112
        $username = $params['username'] ?? '';
113
        $gedc     = $params['gedc'] ?? '';
114
115
        $from = max($from, $earliest);
116
        $to   = min(max($from, $to), $latest);
117
118
        $user_options = $this->user_service->all()->mapWithKeys(static function (User $user): array {
119
            return [$user->userName() => $user->userName()];
120
        });
121
        $user_options = (new Collection(['' => '']))->merge($user_options);
122
123
        $tree_options = $this->tree_service->all()->mapWithKeys(static function (Tree $tree): array {
124
            return [$tree->name() => $tree->title()];
125
        });
126
        $tree_options = (new Collection(['' => '']))->merge($tree_options);
127
128
        $title = I18N::translate('Website logs');
129
130
        return $this->viewResponse('admin/site-logs', [
131
            'action'       => $action,
132
            'earliest'     => $earliest,
133
            'from'         => $from,
134
            'gedc'         => $gedc,
135
            'ip'           => $ip,
136
            'latest'       => $latest,
137
            'tree_options' => $tree_options,
138
            'title'        => $title,
139
            'to'           => $to,
140
            'text'         => $text,
141
            'type'         => $type,
142
            'username'     => $username,
143
            'user_options' => $user_options,
144
        ]);
145
    }
146
147
    /**
148
     * @param ServerRequestInterface $request
149
     *
150
     * @return ResponseInterface
151
     */
152
    public function logsData(ServerRequestInterface $request): ResponseInterface
153
    {
154
        $query = $this->logsQuery($request->getQueryParams());
155
156
        return $this->datatables_service->handle($request, $query, [], [], static function (stdClass $row): array {
157
            return [
158
                $row->log_id,
159
                Carbon::make($row->log_time)->local()->format('Y-m-d H:i:s'),
160
                $row->log_type,
161
                '<span dir="auto">' . e($row->log_message) . '</span>',
162
                '<span dir="auto">' . e($row->ip_address) . '</span>',
163
                '<span dir="auto">' . e($row->user_name) . '</span>',
164
                '<span dir="auto">' . e($row->gedcom_name) . '</span>',
165
            ];
166
        });
167
    }
168
169
    /**
170
     * Generate a query for filtering the site log.
171
     *
172
     * @param string[] $params
173
     *
174
     * @return Builder
175
     */
176
    private function logsQuery(array $params): Builder
177
    {
178
        $from     = $params['from'];
179
        $to       = $params['to'];
180
        $type     = $params['type'];
181
        $text     = $params['text'];
182
        $ip       = $params['ip'];
183
        $username = $params['username'];
184
        $gedc     = $params['gedc'];
185
186
        $query = DB::table('log')
187
            ->leftJoin('user', 'user.user_id', '=', 'log.user_id')
188
            ->leftJoin('gedcom', 'gedcom.gedcom_id', '=', 'log.gedcom_id')
189
            ->select(['log.*', new Expression("COALESCE(user_name, '<none>') AS user_name"), new Expression("COALESCE(gedcom_name, '<none>') AS gedcom_name")]);
190
191
        if ($from !== '') {
192
            $query->where('log_time', '>=', $from);
193
        }
194
195
        if ($to !== '') {
196
            // before end of the day
197
            $query->where('log_time', '<', Carbon::make($to)->addDay());
198
        }
199
200
        if ($type !== '') {
201
            $query->where('log_type', '=', $type);
202
        }
203
204
        if ($text) {
205
            $query->whereContains('log_message', $text);
206
        }
207
208
        if ($ip) {
209
            $query->whereContains('ip_address', $ip);
210
        }
211
212
        if ($username) {
213
            $query->whereContains('user_name', $ip);
214
        }
215
216
        if ($gedc) {
217
            $query->where('gedcom_name', '=', $gedc);
218
        }
219
220
        return $query;
221
    }
222
223
    /**
224
     * @param ServerRequestInterface $request
225
     *
226
     * @return ResponseInterface
227
     */
228
    public function logsDelete(ServerRequestInterface $request): ResponseInterface
229
    {
230
        $this->logsQuery($request->getParsedBody())->delete();
0 ignored issues
show
Bug introduced by
It seems like $request->getParsedBody() can also be of type null and object; however, parameter $params of Fisharebest\Webtrees\Htt...Controller::logsQuery() does only seem to accept array, 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

230
        $this->logsQuery(/** @scrutinizer ignore-type */ $request->getParsedBody())->delete();
Loading history...
231
232
        return response();
233
    }
234
235
    /**
236
     * @param ServerRequestInterface $request
237
     *
238
     * @return ResponseInterface
239
     */
240
    public function logsExport(ServerRequestInterface $request): ResponseInterface
241
    {
242
        $content = $this->logsQuery($request->getQueryParams())
243
            ->orderBy('log_id')
244
            ->get()
245
            ->map(static function (stdClass $row): string {
246
                return
247
                    '"' . $row->log_time . '",' .
248
                    '"' . $row->log_type . '",' .
249
                    '"' . str_replace('"', '""', $row->log_message) . '",' .
250
                    '"' . $row->ip_address . '",' .
251
                    '"' . str_replace('"', '""', $row->user_name) . '",' .
252
                    '"' . str_replace('"', '""', $row->gedcom_name) . '"' .
253
                    "\n";
254
            })
255
            ->implode('');
256
257
        return response($content, StatusCodeInterface::STATUS_OK, [
258
            'Content-Type'        => 'text/csv; charset=utf-8',
259
            'Content-Disposition' => 'attachment; filename="webtrees-logs.csv"',
260
        ]);
261
    }
262
263
    /**
264
     * @param ServerRequestInterface $request
265
     *
266
     * @return ResponseInterface
267
     */
268
    public function mailForm(ServerRequestInterface $request): ResponseInterface
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

268
    public function mailForm(/** @scrutinizer ignore-unused */ ServerRequestInterface $request): ResponseInterface

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
269
    {
270
        $mail_ssl_options       = $this->mail_service->mailSslOptions();
271
        $mail_transport_options = $this->mail_service->mailTransportOptions();
272
273
        $title = I18N::translate('Sending email');
274
275
        $SMTP_ACTIVE    = Site::getPreference('SMTP_ACTIVE');
276
        $SMTP_AUTH      = Site::getPreference('SMTP_AUTH');
277
        $SMTP_AUTH_USER = Site::getPreference('SMTP_AUTH_USER');
278
        $SMTP_FROM_NAME = $this->mail_service->senderEmail();
279
        $SMTP_HELO      = $this->mail_service->localDomain();
280
        $SMTP_HOST      = Site::getPreference('SMTP_HOST');
281
        $SMTP_PORT      = Site::getPreference('SMTP_PORT');
282
        $SMTP_SSL       = Site::getPreference('SMTP_SSL');
283
        $DKIM_DOMAIN    = Site::getPreference('DKIM_DOMAIN');
284
        $DKIM_SELECTOR  = Site::getPreference('DKIM_SELECTOR');
285
        $DKIM_KEY       = Site::getPreference('DKIM_KEY');
286
287
        $smtp_from_name_valid = $this->mail_service->isValidEmail($SMTP_FROM_NAME);
288
        $smtp_helo_valid      = filter_var($SMTP_HELO, FILTER_VALIDATE_DOMAIN);
289
290
        return $this->viewResponse('admin/site-mail', [
291
            'mail_ssl_options'       => $mail_ssl_options,
292
            'mail_transport_options' => $mail_transport_options,
293
            'title'                  => $title,
294
            'smtp_helo_valid'        => $smtp_helo_valid,
295
            'smtp_from_name_valid'   => $smtp_from_name_valid,
296
            'SMTP_ACTIVE'            => $SMTP_ACTIVE,
297
            'SMTP_AUTH'              => $SMTP_AUTH,
298
            'SMTP_AUTH_USER'         => $SMTP_AUTH_USER,
299
            'SMTP_FROM_NAME'         => $SMTP_FROM_NAME,
300
            'SMTP_HELO'              => $SMTP_HELO,
301
            'SMTP_HOST'              => $SMTP_HOST,
302
            'SMTP_PORT'              => $SMTP_PORT,
303
            'SMTP_SSL'               => $SMTP_SSL,
304
            'DKIM_DOMAIN'            => $DKIM_DOMAIN,
305
            'DKIM_SELECTOR'          => $DKIM_SELECTOR,
306
            'DKIM_KEY'               => $DKIM_KEY,
307
        ]);
308
    }
309
310
    /**
311
     * @param ServerRequestInterface $request
312
     *
313
     * @return ResponseInterface
314
     */
315
    public function mailSave(ServerRequestInterface $request): ResponseInterface
316
    {
317
        $params = $request->getParsedBody();
318
319
        Site::setPreference('SMTP_ACTIVE', $params['SMTP_ACTIVE']);
320
        Site::setPreference('SMTP_FROM_NAME', $params['SMTP_FROM_NAME']);
321
        Site::setPreference('SMTP_HOST', $params['SMTP_HOST']);
322
        Site::setPreference('SMTP_PORT', $params['SMTP_PORT']);
323
        Site::setPreference('SMTP_AUTH', $params['SMTP_AUTH']);
324
        Site::setPreference('SMTP_AUTH_USER', $params['SMTP_AUTH_USER']);
325
        Site::setPreference('SMTP_SSL', $params['SMTP_SSL']);
326
        Site::setPreference('SMTP_HELO', $params['SMTP_HELO']);
327
        Site::setPreference('DKIM_DOMAIN', $params['DKIM_DOMAIN']);
328
        Site::setPreference('DKIM_SELECTOR', $params['DKIM_SELECTOR']);
329
        Site::setPreference('DKIM_KEY', $params['DKIM_KEY']);
330
331
        if ($params['SMTP_AUTH_PASS'] !== '') {
332
            Site::setPreference('SMTP_AUTH_PASS', $params['SMTP_AUTH_PASS']);
333
        }
334
335
        FlashMessages::addMessage(I18N::translate('The website preferences have been updated.'), 'success');
336
        $url = route(ControlPanel::class);
337
338
        return redirect($url);
339
    }
340
341
    /**
342
     * @param ServerRequestInterface $request
343
     *
344
     * @return ResponseInterface
345
     */
346
    public function preferencesForm(ServerRequestInterface $request): ResponseInterface
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

346
    public function preferencesForm(/** @scrutinizer ignore-unused */ ServerRequestInterface $request): ResponseInterface

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
347
    {
348
        $all_themes = $this->themeOptions();
349
350
        $title = I18N::translate('Website preferences');
351
352
        return $this->viewResponse('admin/site-preferences', [
353
            'all_themes'         => $all_themes,
354
            'max_execution_time' => (int) get_cfg_var('max_execution_time'),
355
            'title'              => $title,
356
        ]);
357
    }
358
359
    /**
360
     * @return Collection
361
     */
362
    private function themeOptions(): Collection
363
    {
364
        return $this->module_service
365
            ->findByInterface(ModuleThemeInterface::class)
366
            ->map($this->module_service->titleMapper());
367
    }
368
369
    /**
370
     * @param ServerRequestInterface $request
371
     *
372
     * @return ResponseInterface
373
     */
374
    public function preferencesSave(ServerRequestInterface $request): ResponseInterface
375
    {
376
        $params = $request->getParsedBody();
377
378
        $INDEX_DIRECTORY = $params['INDEX_DIRECTORY'];
379
        if (substr($INDEX_DIRECTORY, -1) !== '/') {
380
            $INDEX_DIRECTORY .= '/';
381
        }
382
        if (is_dir($INDEX_DIRECTORY)) {
383
            Site::setPreference('INDEX_DIRECTORY', $INDEX_DIRECTORY);
384
        } else {
385
            FlashMessages::addMessage(I18N::translate('The folder “%s” does not exist.', e($INDEX_DIRECTORY)), 'danger');
386
        }
387
388
        Site::setPreference('THEME_DIR', $params['THEME_DIR']);
389
        Site::setPreference('ALLOW_CHANGE_GEDCOM', $params['ALLOW_CHANGE_GEDCOM']);
390
        Site::setPreference('TIMEZONE', $params['TIMEZONE']);
391
392
        FlashMessages::addMessage(I18N::translate('The website preferences have been updated.'), 'success');
393
        $url = route(ControlPanel::class);
394
395
        return redirect($url);
396
    }
397
398
    /**
399
     * @param ServerRequestInterface $request
400
     *
401
     * @return ResponseInterface
402
     */
403
    public function registrationForm(ServerRequestInterface $request): ResponseInterface
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

403
    public function registrationForm(/** @scrutinizer ignore-unused */ ServerRequestInterface $request): ResponseInterface

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
404
    {
405
        $title = I18N::translate('Sign-in and registration');
406
407
        $registration_text_options = $this->registrationTextOptions();
408
409
        return $this->viewResponse('admin/site-registration', [
410
            'registration_text_options' => $registration_text_options,
411
            'title'                     => $title,
412
        ]);
413
    }
414
415
    /**
416
     * A list of registration rules (e.g. for an edit control).
417
     *
418
     * @return string[]
419
     */
420
    private function registrationTextOptions(): array
421
    {
422
        return [
423
            0 => I18N::translate('No predefined text'),
424
            1 => I18N::translate('Predefined text that states all users can request a user account'),
425
            2 => I18N::translate('Predefined text that states admin will decide on each request for a user account'),
426
            3 => I18N::translate('Predefined text that states only family members can request a user account'),
427
            4 => I18N::translate('Choose user defined welcome text typed below'),
428
        ];
429
    }
430
431
    /**
432
     * @param ServerRequestInterface $request
433
     *
434
     * @return ResponseInterface
435
     */
436
    public function registrationSave(ServerRequestInterface $request): ResponseInterface
437
    {
438
        $params = $request->getParsedBody();
439
440
        Site::setPreference('WELCOME_TEXT_AUTH_MODE', $params['WELCOME_TEXT_AUTH_MODE']);
441
        Site::setPreference('WELCOME_TEXT_AUTH_MODE_' . WT_LOCALE, $params['WELCOME_TEXT_AUTH_MODE_4']);
442
        Site::setPreference('USE_REGISTRATION_MODULE', $params['USE_REGISTRATION_MODULE']);
443
        Site::setPreference('SHOW_REGISTER_CAUTION', $params['SHOW_REGISTER_CAUTION']);
444
445
        FlashMessages::addMessage(I18N::translate('The website preferences have been updated.'), 'success');
446
        $url = route(ControlPanel::class);
447
448
        return redirect($url);
449
    }
450
}
451