Passed
Push — master ( 6d5769...1ab2f3 )
by Greg
07:36
created

HomePageController::updateUserBlocks()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 41
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 28
c 1
b 0
f 0
nc 12
nop 3
dl 0
loc 41
rs 8.5386
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\Webtrees\Auth;
23
use Fisharebest\Webtrees\Contracts\UserInterface;
24
use Fisharebest\Webtrees\Http\RequestHandlers\ControlPanel;
25
use Fisharebest\Webtrees\I18N;
26
use Fisharebest\Webtrees\Module\FamilyTreeFavoritesModule;
27
use Fisharebest\Webtrees\Module\FamilyTreeNewsModule;
28
use Fisharebest\Webtrees\Module\FamilyTreeStatisticsModule;
29
use Fisharebest\Webtrees\Module\LoggedInUsersModule;
30
use Fisharebest\Webtrees\Module\ModuleBlockInterface;
31
use Fisharebest\Webtrees\Module\ModuleInterface;
32
use Fisharebest\Webtrees\Module\OnThisDayModule;
33
use Fisharebest\Webtrees\Module\ReviewChangesModule;
34
use Fisharebest\Webtrees\Module\SlideShowModule;
35
use Fisharebest\Webtrees\Module\UpcomingAnniversariesModule;
36
use Fisharebest\Webtrees\Module\UserFavoritesModule;
37
use Fisharebest\Webtrees\Module\UserMessagesModule;
38
use Fisharebest\Webtrees\Module\UserWelcomeModule;
39
use Fisharebest\Webtrees\Module\WelcomeBlockModule;
40
use Fisharebest\Webtrees\Services\ModuleService;
41
use Fisharebest\Webtrees\Services\UserService;
42
use Fisharebest\Webtrees\Tree;
43
use Illuminate\Database\Capsule\Manager as DB;
44
use Illuminate\Database\Query\Builder;
45
use Illuminate\Database\Query\Expression;
46
use Illuminate\Support\Collection;
47
use Psr\Http\Message\ResponseInterface;
48
use Psr\Http\Message\ServerRequestInterface;
49
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
50
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
51
52
use function assert;
53
54
/**
55
 * Controller for the user/tree's home page.
56
 */
57
class HomePageController extends AbstractBaseController
58
{
59
    // We show blocks in two columns on the tree/user pages.
60
    private const MAIN_BLOCKS = 'main';
61
    private const SIDE_BLOCKS = 'side';
62
63
    private const DEFAULT_TREE_PAGE_BLOCKS = [
64
        self::MAIN_BLOCKS => [
65
            1 => FamilyTreeStatisticsModule::class,
66
            2 => FamilyTreeNewsModule::class,
67
            3 => FamilyTreeFavoritesModule::class,
68
            4 => ReviewChangesModule::class,
69
        ],
70
        self::SIDE_BLOCKS => [
71
            1 => WelcomeBlockModule::class,
72
            2 => SlideShowModule::class,
73
            3 => OnThisDayModule::class,
74
            4 => LoggedInUsersModule::class,
75
        ],
76
    ];
77
78
    private const DEFAULT_USER_PAGE_BLOCKS = [
79
        self::MAIN_BLOCKS => [
80
            1 => OnThisDayModule::class,
81
            2 => UserMessagesModule::class,
82
            3 => UserFavoritesModule::class,
83
        ],
84
        self::SIDE_BLOCKS => [
85
            1 => UserWelcomeModule::class,
86
            2 => SlideShowModule::class,
87
            3 => UpcomingAnniversariesModule::class,
88
            4 => LoggedInUsersModule::class,
89
        ],
90
    ];
91
92
    /**
93
     * @var ModuleService
94
     */
95
    private $module_service;
96
97
    /**
98
     * @var UserService
99
     */
100
    private $user_service;
101
102
    /**
103
     * HomePageController constructor.
104
     *
105
     * @param ModuleService $module_service
106
     * @param UserService   $user_service
107
     */
108
    public function __construct(ModuleService $module_service, UserService $user_service)
109
    {
110
        $this->module_service = $module_service;
111
        $this->user_service   = $user_service;
112
    }
113
114
    /**
115
     * Show a form to edit block config options.
116
     *
117
     * @param ServerRequestInterface $request
118
     *
119
     * @return ResponseInterface
120
     */
121
    public function treePageBlockEdit(ServerRequestInterface $request): ResponseInterface
122
    {
123
        $tree = $request->getAttribute('tree');
124
        assert($tree instanceof Tree);
125
126
        $user     = $request->getAttribute('user');
127
        $block_id = (int) $request->getQueryParams()['block_id'];
128
        $block    = $this->treeBlock($request, $user);
129
        $title    = $block->title() . ' — ' . I18N::translate('Preferences');
130
131
        return $this->viewResponse('modules/edit-block-config', [
132
            'block'      => $block,
133
            'block_id'   => $block_id,
134
            'cancel_url' => route('tree-page', ['tree' => $tree->name()]),
135
            'title'      => $title,
136
            'tree'       => $tree,
137
        ]);
138
    }
139
140
    /**
141
     * Update block config options.
142
     *
143
     * @param ServerRequestInterface $request
144
     *
145
     * @return ResponseInterface
146
     */
147
    public function treePageBlockUpdate(ServerRequestInterface $request): ResponseInterface
148
    {
149
        $tree = $request->getAttribute('tree');
150
        assert($tree instanceof Tree);
151
152
        $user     = $request->getAttribute('user');
153
        $block    = $this->treeBlock($request, $user);
154
        $block_id = (int) $request->getQueryParams()['block_id'];
155
156
        $block->saveBlockConfiguration($request, $block_id);
157
158
        return redirect(route('tree-page', ['tree' => $tree->name()]));
159
    }
160
161
    /**
162
     * Load a block and check we have permission to edit it.
163
     *
164
     * @param ServerRequestInterface $request
165
     * @param UserInterface          $user
166
     *
167
     * @return ModuleBlockInterface
168
     */
169
    private function treeBlock(ServerRequestInterface $request, UserInterface $user): ModuleBlockInterface
170
    {
171
        $tree = $request->getAttribute('tree');
172
        assert($tree instanceof Tree);
173
174
        $block_id = (int) $request->getQueryParams()['block_id'];
175
176
        $block = DB::table('block')
177
            ->where('block_id', '=', $block_id)
178
            ->where('gedcom_id', '=', $tree->id())
179
            ->whereNull('user_id')
180
            ->first();
181
182
        if ($block === null) {
183
            throw new NotFoundHttpException();
184
        }
185
186
        $module = $this->module_service->findByName($block->module_name);
187
188
        if (!$module instanceof ModuleBlockInterface) {
189
            throw new NotFoundHttpException();
190
        }
191
192
        if ($block->user_id !== $user->id() && !Auth::isAdmin()) {
193
            throw new AccessDeniedHttpException();
194
        }
195
196
        return $module;
197
    }
198
199
    /**
200
     * Show a form to edit block config options.
201
     *
202
     * @param ServerRequestInterface $request
203
     *
204
     * @return ResponseInterface
205
     */
206
    public function userPageBlockEdit(ServerRequestInterface $request): ResponseInterface
207
    {
208
        $tree = $request->getAttribute('tree');
209
        assert($tree instanceof Tree);
210
211
        $user     = $request->getAttribute('user');
212
        $block_id = (int) $request->getQueryParams()['block_id'];
213
        $block    = $this->userBlock($request, $user);
214
        $title    = $block->title() . ' — ' . I18N::translate('Preferences');
215
216
        return $this->viewResponse('modules/edit-block-config', [
217
            'block'      => $block,
218
            'block_id'   => $block_id,
219
            'cancel_url' => route('user-page', ['tree' => $tree->name()]),
220
            'title'      => $title,
221
            'tree'       => $tree,
222
        ]);
223
    }
224
225
    /**
226
     * Update block config options.
227
     *
228
     * @param ServerRequestInterface $request
229
     *
230
     * @return ResponseInterface
231
     */
232
    public function userPageBlockUpdate(ServerRequestInterface $request): ResponseInterface
233
    {
234
        $tree = $request->getAttribute('tree');
235
        assert($tree instanceof Tree);
236
237
        $user     = $request->getAttribute('user');
238
        $block    = $this->userBlock($request, $user);
239
        $block_id = (int) $request->getQueryParams()['block_id'];
240
241
        $block->saveBlockConfiguration($request, $block_id);
242
243
        return redirect(route('user-page', ['tree' => $tree->name()]));
244
    }
245
246
    /**
247
     * Load a block and check we have permission to edit it.
248
     *
249
     * @param ServerRequestInterface $request
250
     * @param UserInterface          $user
251
     *
252
     * @return ModuleBlockInterface
253
     */
254
    private function userBlock(ServerRequestInterface $request, UserInterface $user): ModuleBlockInterface
255
    {
256
        $block_id = (int) $request->getQueryParams()['block_id'];
257
258
        $block = DB::table('block')
259
            ->where('block_id', '=', $block_id)
260
            ->where('user_id', '=', $user->id())
261
            ->whereNull('gedcom_id')
262
            ->first();
263
264
        if ($block === null) {
265
            throw new NotFoundHttpException('This block does not exist');
266
        }
267
268
        $module = $this->module_service->findByName($block->module_name);
269
270
        if (!$module instanceof ModuleBlockInterface) {
271
            throw new NotFoundHttpException($block->module_name . ' is not a block');
272
        }
273
274
        $block_owner_id = (int) $block->user_id;
275
276
        if ($block_owner_id !== $user->id() && !Auth::isAdmin()) {
277
            throw new AccessDeniedHttpException('You are not allowed to edit this block');
278
        }
279
280
        return $module;
281
    }
282
283
    /**
284
     * Show a tree's page.
285
     *
286
     * @param ServerRequestInterface $request
287
     *
288
     * @return ResponseInterface
289
     */
290
    public function treePage(ServerRequestInterface $request): ResponseInterface
291
    {
292
        $tree = $request->getAttribute('tree');
293
        assert($tree instanceof Tree);
294
295
        $has_blocks = DB::table('block')
296
            ->where('gedcom_id', '=', $tree->id())
297
            ->exists();
298
299
        if (!$has_blocks) {
300
            $this->checkDefaultTreeBlocksExist();
301
302
            // Copy the defaults
303
            (new Builder(DB::connection()))->from('block')->insertUsing(
304
                ['gedcom_id', 'location', 'block_order', 'module_name'],
305
                static function (Builder $query) use ($tree): void {
306
                    $query
307
                        ->select([new Expression($tree->id()), 'location', 'block_order', 'module_name'])
308
                        ->from('block')
309
                        ->where('gedcom_id', '=', -1);
310
                }
311
            );
312
        }
313
314
        return $this->viewResponse('tree-page', [
315
            'main_blocks' => $this->treeBlocks($tree->id(), self::MAIN_BLOCKS),
316
            'side_blocks' => $this->treeBlocks($tree->id(), self::SIDE_BLOCKS),
317
            'title'       => e($tree->title()),
318
            'tree'        => $tree,
319
            'meta_robots' => 'index,follow',
320
        ]);
321
    }
322
323
    /**
324
     * Load block asynchronously.
325
     *
326
     * @param ServerRequestInterface $request
327
     *
328
     * @return ResponseInterface
329
     */
330
    public function treePageBlock(ServerRequestInterface $request): ResponseInterface
331
    {
332
        $tree = $request->getAttribute('tree');
333
        assert($tree instanceof Tree);
334
335
        $block_id = $request->getQueryParams()['block_id'];
336
337
        $block_id = (int) DB::table('block')
338
            ->where('block_id', '=', $block_id)
339
            ->where('gedcom_id', '=', $tree->id())
340
            ->value('block_id');
341
342
        $module = $this->getBlockModule($tree, $block_id);
343
344
        $html = view('layouts/ajax', [
345
            'content' => $module->getBlock($tree, $block_id, ModuleBlockInterface::CONTEXT_TREE_PAGE),
346
        ]);
347
348
        return response($html);
349
    }
350
351
    /**
352
     * Show a form to edit the default blocks for new trees.
353
     *
354
     * @param ServerRequestInterface $request
355
     *
356
     * @return ResponseInterface
357
     */
358
    public function treePageDefaultEdit(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

358
    public function treePageDefaultEdit(/** @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...
359
    {
360
        $this->layout = 'layouts/administration';
361
362
        $this->checkDefaultTreeBlocksExist();
363
364
        $main_blocks = $this->treeBlocks(-1, self::MAIN_BLOCKS);
365
        $side_blocks = $this->treeBlocks(-1, self::SIDE_BLOCKS);
366
367
        $all_blocks = $this->availableTreeBlocks();
368
        $title      = I18N::translate('Set the default blocks for new family trees');
369
        $url_cancel = route(ControlPanel::class);
370
        $url_save   = route('tree-page-default-update');
371
372
        return $this->viewResponse('edit-blocks-page', [
373
            'all_blocks'  => $all_blocks,
374
            'can_reset'   => false,
375
            'main_blocks' => $main_blocks,
376
            'side_blocks' => $side_blocks,
377
            'title'       => $title,
378
            'url_cancel'  => $url_cancel,
379
            'url_save'    => $url_save,
380
        ]);
381
    }
382
383
    /**
384
     * Save updated default blocks for new trees.
385
     *
386
     * @param ServerRequestInterface $request
387
     *
388
     * @return ResponseInterface
389
     */
390
    public function treePageDefaultUpdate(ServerRequestInterface $request): ResponseInterface
391
    {
392
        $main_blocks = $request->getParsedBody()[self::MAIN_BLOCKS] ?? [];
393
        $side_blocks = $request->getParsedBody()[self::SIDE_BLOCKS] ?? [];
394
395
        $this->updateTreeBlocks(-1, $main_blocks, $side_blocks);
396
397
        return redirect(route(ControlPanel::class));
398
    }
399
400
    /**
401
     * Show a form to edit the blocks on a tree's page.
402
     *
403
     * @param ServerRequestInterface $request
404
     *
405
     * @return ResponseInterface
406
     */
407
    public function treePageEdit(ServerRequestInterface $request): ResponseInterface
408
    {
409
        $tree = $request->getAttribute('tree');
410
        assert($tree instanceof Tree);
411
412
        $main_blocks = $this->treeBlocks($tree->id(), self::MAIN_BLOCKS);
413
        $side_blocks = $this->treeBlocks($tree->id(), self::SIDE_BLOCKS);
414
415
        $all_blocks = $this->availableTreeBlocks();
416
        $title      = I18N::translate('Change the “Home page” blocks');
417
        $url_cancel = route('tree-page', ['tree' => $tree->name()]);
418
        $url_save   = route('tree-page-update', ['tree' => $tree->name()]);
419
420
        return $this->viewResponse('edit-blocks-page', [
421
            'all_blocks'  => $all_blocks,
422
            'can_reset'   => true,
423
            'main_blocks' => $main_blocks,
424
            'side_blocks' => $side_blocks,
425
            'title'       => $title,
426
            'tree'        => $tree,
427
            'url_cancel'  => $url_cancel,
428
            'url_save'    => $url_save,
429
        ]);
430
    }
431
432
    /**
433
     * Save updated blocks on a tree's page.
434
     *
435
     * @param ServerRequestInterface $request
436
     *
437
     * @return ResponseInterface
438
     */
439
    public function treePageUpdate(ServerRequestInterface $request): ResponseInterface
440
    {
441
        $tree = $request->getAttribute('tree');
442
        assert($tree instanceof Tree);
443
444
        $params = $request->getParsedBody();
445
446
        $defaults = (bool) ($params['defaults'] ?? false);
447
448
        if ($defaults) {
449
            $main_blocks = $this->treeBlocks(-1, self::MAIN_BLOCKS)
450
                ->map(static function (ModuleBlockInterface $block) {
451
                    return $block->name();
452
                })
453
                ->all();
454
            $side_blocks = $this->treeBlocks(-1, self::SIDE_BLOCKS)
455
                ->map(static function (ModuleBlockInterface $block) {
456
                    return $block->name();
457
                })
458
                ->all();
459
        } else {
460
            $main_blocks = $params[self::MAIN_BLOCKS] ?? [];
461
            $side_blocks = $params[self::SIDE_BLOCKS] ?? [];
462
        }
463
464
        $this->updateTreeBlocks($tree->id(), $main_blocks, $side_blocks);
465
466
        return redirect(route('tree-page', ['tree' => $tree->name()]));
467
    }
468
469
    /**
470
     * Show a users's page.
471
     *
472
     * @param ServerRequestInterface $request
473
     *
474
     * @return ResponseInterface
475
     */
476
    public function userPage(ServerRequestInterface $request): ResponseInterface
477
    {
478
        $tree = $request->getAttribute('tree');
479
        assert($tree instanceof Tree);
480
481
        $user = $request->getAttribute('user');
482
483
        $has_blocks = DB::table('block')
484
            ->where('user_id', '=', $user->id())
485
            ->exists();
486
487
        if (!$has_blocks) {
488
            $this->checkDefaultUserBlocksExist();
489
490
            // Copy the defaults
491
            (new Builder(DB::connection()))->from('block')->insertUsing(
492
                ['user_id', 'location', 'block_order', 'module_name'],
493
                static function (Builder $query) use ($user): void {
494
                    $query
495
                        ->select([new Expression($user->id()), 'location', 'block_order', 'module_name'])
496
                        ->from('block')
497
                        ->where('user_id', '=', -1);
498
                }
499
            );
500
        }
501
502
        return $this->viewResponse('user-page', [
503
            'main_blocks' => $this->userBlocks($user->id(), self::MAIN_BLOCKS),
504
            'side_blocks' => $this->userBlocks($user->id(), self::SIDE_BLOCKS),
505
            'title'       => I18N::translate('My page'),
506
            'tree'        => $tree,
507
        ]);
508
    }
509
510
    /**
511
     * Load block asynchronously.
512
     *
513
     * @param ServerRequestInterface $request
514
     *
515
     * @return ResponseInterface
516
     */
517
    public function userPageBlock(ServerRequestInterface $request): ResponseInterface
518
    {
519
        $tree = $request->getAttribute('tree');
520
        assert($tree instanceof Tree);
521
522
        $user     = $request->getAttribute('user');
523
        $block_id = $request->getQueryParams()['block_id'];
524
525
        $block_id = (int) DB::table('block')
526
            ->where('block_id', '=', $block_id)
527
            ->where('user_id', '=', $user->id())
528
            ->value('block_id');
529
530
        $module = $this->getBlockModule($tree, $block_id);
531
532
        $html = view('layouts/ajax', [
533
            'content' => $module->getBlock($tree, $block_id, ModuleBlockInterface::CONTEXT_USER_PAGE),
534
        ]);
535
536
        return response($html);
537
    }
538
539
    /**
540
     * Show a form to edit the default blocks for new uesrs.
541
     *
542
     * @param ServerRequestInterface $request
543
     *
544
     * @return ResponseInterface
545
     */
546
    public function userPageDefaultEdit(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

546
    public function userPageDefaultEdit(/** @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...
547
    {
548
        $this->layout = 'layouts/administration';
549
550
        $this->checkDefaultUserBlocksExist();
551
552
        $main_blocks = $this->userBlocks(-1, self::MAIN_BLOCKS);
553
        $side_blocks = $this->userBlocks(-1, self::SIDE_BLOCKS);
554
        $all_blocks  = $this->availableUserBlocks();
555
        $title       = I18N::translate('Set the default blocks for new users');
556
        $url_cancel  = route('admin-users');
557
        $url_save    = route('user-page-default-update');
558
559
        return $this->viewResponse('edit-blocks-page', [
560
            'all_blocks'  => $all_blocks,
561
            'can_reset'   => false,
562
            'main_blocks' => $main_blocks,
563
            'side_blocks' => $side_blocks,
564
            'title'       => $title,
565
            'url_cancel'  => $url_cancel,
566
            'url_save'    => $url_save,
567
        ]);
568
    }
569
570
    /**
571
     * Save the updated default blocks for new users.
572
     *
573
     * @param ServerRequestInterface $request
574
     *
575
     * @return ResponseInterface
576
     */
577
    public function userPageDefaultUpdate(ServerRequestInterface $request): ResponseInterface
578
    {
579
        $main_blocks = $request->getParsedBody()[self::MAIN_BLOCKS] ?? [];
580
        $side_blocks = $request->getParsedBody()[self::SIDE_BLOCKS] ?? [];
581
582
        $this->updateUserBlocks(-1, $main_blocks, $side_blocks);
583
584
        return redirect(route(ControlPanel::class));
585
    }
586
587
    /**
588
     * Show a form to edit the blocks on the user's page.
589
     *
590
     * @param ServerRequestInterface $request
591
     *
592
     * @return ResponseInterface
593
     */
594
    public function userPageEdit(ServerRequestInterface $request): ResponseInterface
595
    {
596
        $tree = $request->getAttribute('tree');
597
        assert($tree instanceof Tree);
598
599
        $user        = $request->getAttribute('user');
600
        $main_blocks = $this->userBlocks($user->id(), self::MAIN_BLOCKS);
601
        $side_blocks = $this->userBlocks($user->id(), self::SIDE_BLOCKS);
602
        $all_blocks  = $this->availableUserBlocks();
603
        $title       = I18N::translate('Change the “My page” blocks');
604
        $url_cancel  = route('user-page', ['tree' => $tree->name()]);
605
        $url_save    = route('user-page-update', ['tree' => $tree->name()]);
606
607
        return $this->viewResponse('edit-blocks-page', [
608
            'all_blocks'  => $all_blocks,
609
            'can_reset'   => true,
610
            'main_blocks' => $main_blocks,
611
            'side_blocks' => $side_blocks,
612
            'title'       => $title,
613
            'tree'        => $tree,
614
            'url_cancel'  => $url_cancel,
615
            'url_save'    => $url_save,
616
        ]);
617
    }
618
619
    /**
620
     * Save the updated blocks on a user's page.
621
     *
622
     * @param ServerRequestInterface $request
623
     *
624
     * @return ResponseInterface
625
     */
626
    public function userPageUpdate(ServerRequestInterface $request): ResponseInterface
627
    {
628
        $tree = $request->getAttribute('tree');
629
        assert($tree instanceof Tree);
630
631
        $user     = $request->getAttribute('user');
632
        $params   = $request->getParsedBody();
633
        $defaults = (bool) ($params['defaults'] ?? false);
634
635
        if ($defaults) {
636
            $main_blocks = $this->userBlocks(-1, self::MAIN_BLOCKS)
637
                ->map(static function (ModuleBlockInterface $block) {
638
                    return $block->name();
639
                })
640
                ->all();
641
            $side_blocks = $this->userBlocks(-1, self::SIDE_BLOCKS)
642
                ->map(static function (ModuleBlockInterface $block) {
643
                    return $block->name();
644
                })
645
                ->all();
646
        } else {
647
            $main_blocks = $params[self::MAIN_BLOCKS] ?? [];
648
            $side_blocks = $params[self::SIDE_BLOCKS] ?? [];
649
        }
650
651
        $this->updateUserBlocks($user->id(), $main_blocks, $side_blocks);
652
653
        return redirect(route('user-page', ['tree' => $tree->name()]));
654
    }
655
656
    /**
657
     * Get a specific block.
658
     *
659
     * @param Tree $tree
660
     * @param int  $block_id
661
     *
662
     * @return ModuleBlockInterface
663
     */
664
    private function getBlockModule(Tree $tree, int $block_id): ModuleBlockInterface
665
    {
666
        $active_blocks = $this->module_service->findByComponent(ModuleBlockInterface::class, $tree, Auth::user());
667
668
        $module_name = DB::table('block')
669
            ->where('block_id', '=', $block_id)
670
            ->value('module_name');
671
672
        $block = $active_blocks->first(static function (ModuleInterface $module) use ($module_name): bool {
673
            return $module->name() === $module_name;
674
        });
675
676
        if ($block === null) {
677
            throw new NotFoundHttpException('Block not found');
678
        }
679
680
        return $block;
681
    }
682
683
    /**
684
     * Get all the available blocks for a tree page.
685
     *
686
     * @return Collection
687
     */
688
    private function availableTreeBlocks(): Collection
689
    {
690
        return $this->module_service->findByInterface(ModuleBlockInterface::class, false, true)
691
            ->filter(static function (ModuleBlockInterface $block): bool {
692
                return $block->isTreeBlock();
693
            })
694
            ->mapWithKeys(static function (ModuleInterface $block): array {
695
                return [$block->name() => $block];
696
            });
697
    }
698
699
    /**
700
     * Get all the available blocks for a user page.
701
     *
702
     * @return Collection
703
     */
704
    private function availableUserBlocks(): Collection
705
    {
706
        return $this->module_service->findByInterface(ModuleBlockInterface::class, false, true)
707
            ->filter(static function (ModuleBlockInterface $block): bool {
708
                return $block->isUserBlock();
709
            })
710
            ->mapWithKeys(static function (ModuleInterface $block): array {
711
                return [$block->name() => $block];
712
            });
713
    }
714
715
    /**
716
     * Get the blocks for a specified tree.
717
     *
718
     * @param int    $tree_id
719
     * @param string $location "main" or "side"
720
     *
721
     * @return Collection
722
     */
723
    private function treeBlocks(int $tree_id, string $location): Collection
724
    {
725
        $rows = DB::table('block')
726
            ->where('gedcom_id', '=', $tree_id)
727
            ->where('location', '=', $location)
728
            ->orderBy('block_order')
729
            ->pluck('module_name', 'block_id');
730
731
        return $this->filterActiveBlocks($rows, $this->availableTreeBlocks());
732
    }
733
734
    /**
735
     * Make sure that default blocks exist for a tree.
736
     *
737
     * @return void
738
     */
739
    private function checkDefaultTreeBlocksExist(): void
740
    {
741
        $has_blocks = DB::table('block')
742
            ->where('gedcom_id', '=', -1)
743
            ->exists();
744
745
        // No default settings?  Create them.
746
        if (!$has_blocks) {
747
            foreach ([self::MAIN_BLOCKS, self::SIDE_BLOCKS] as $location) {
748
                foreach (self::DEFAULT_TREE_PAGE_BLOCKS[$location] as $block_order => $class) {
749
                    $module_name = $this->module_service->findByInterface($class)->first()->name();
750
751
                    DB::table('block')->insert([
752
                        'gedcom_id'   => -1,
753
                        'location'    => $location,
754
                        'block_order' => $block_order,
755
                        'module_name' => $module_name,
756
                    ]);
757
                }
758
            }
759
        }
760
    }
761
762
    /**
763
     * Get the blocks for a specified user.
764
     *
765
     * @param int    $user_id
766
     * @param string $location "main" or "side"
767
     *
768
     * @return Collection
769
     */
770
    private function userBlocks(int $user_id, string $location): Collection
771
    {
772
        $rows = DB::table('block')
773
            ->where('user_id', '=', $user_id)
774
            ->where('location', '=', $location)
775
            ->orderBy('block_order')
776
            ->pluck('module_name', 'block_id');
777
778
        return $this->filterActiveBlocks($rows, $this->availableUserBlocks());
779
    }
780
781
    /**
782
     * Make sure that default blocks exist for a user.
783
     *
784
     * @return void
785
     */
786
    private function checkDefaultUserBlocksExist(): void
787
    {
788
        $has_blocks = DB::table('block')
789
            ->where('user_id', '=', -1)
790
            ->exists();
791
792
        // No default settings?  Create them.
793
        if (!$has_blocks) {
794
            foreach ([self::MAIN_BLOCKS, self::SIDE_BLOCKS] as $location) {
795
                foreach (self::DEFAULT_USER_PAGE_BLOCKS[$location] as $block_order => $class) {
796
                    $module_name = $this->module_service->findByInterface($class)->first()->name();
797
798
                    DB::table('block')->insert([
799
                        'user_id'     => -1,
800
                        'location'    => $location,
801
                        'block_order' => $block_order,
802
                        'module_name' => $module_name,
803
                    ]);
804
                }
805
            }
806
        }
807
    }
808
809
    /**
810
     * Save the updated blocks for a user.
811
     *
812
     * @param int   $user_id
813
     * @param array $main_blocks
814
     * @param array $side_blocks
815
     *
816
     * @return void
817
     */
818
    private function updateUserBlocks(int $user_id, array $main_blocks, array $side_blocks): void
819
    {
820
        $existing_block_ids = DB::table('block')
821
            ->where('user_id', '=', $user_id)
822
            ->pluck('block_id');
823
824
        // Deleted blocks
825
        foreach ($existing_block_ids as $existing_block_id) {
826
            if (!in_array($existing_block_id, $main_blocks, false) && !in_array($existing_block_id, $side_blocks, false)) {
827
                DB::table('block_setting')
828
                    ->where('block_id', '=', $existing_block_id)
829
                    ->delete();
830
831
                DB::table('block')
832
                    ->where('block_id', '=', $existing_block_id)
833
                    ->delete();
834
            }
835
        }
836
837
        $updates = [
838
            self::MAIN_BLOCKS => $main_blocks,
839
            self::SIDE_BLOCKS => $side_blocks,
840
        ];
841
842
        foreach ($updates as $location => $updated_blocks) {
843
            foreach ($updated_blocks as $block_order => $block_id) {
844
                if (is_numeric($block_id)) {
845
                    // Updated block
846
                    DB::table('block')
847
                        ->where('block_id', '=', $block_id)
848
                        ->update([
849
                            'block_order' => $block_order,
850
                            'location'    => $location,
851
                        ]);
852
                } else {
853
                    // New block
854
                    DB::table('block')->insert([
855
                        'user_id'     => $user_id,
856
                        'location'    => $location,
857
                        'block_order' => $block_order,
858
                        'module_name' => $block_id,
859
                    ]);
860
                }
861
            }
862
        }
863
    }
864
865
    /**
866
     * Save the updated blocks for a tree.
867
     *
868
     * @param int   $tree_id
869
     * @param array $main_blocks
870
     * @param array $side_blocks
871
     *
872
     * @return void
873
     */
874
    private function updateTreeBlocks(int $tree_id, array $main_blocks, array $side_blocks): void
875
    {
876
        $existing_block_ids = DB::table('block')
877
            ->where('gedcom_id', '=', $tree_id)
878
            ->pluck('block_id');
879
880
        // Deleted blocks
881
        foreach ($existing_block_ids as $existing_block_id) {
882
            if (!in_array($existing_block_id, $main_blocks, false) && !in_array($existing_block_id, $side_blocks, false)) {
883
                DB::table('block_setting')
884
                    ->where('block_id', '=', $existing_block_id)
885
                    ->delete();
886
887
                DB::table('block')
888
                    ->where('block_id', '=', $existing_block_id)
889
                    ->delete();
890
            }
891
        }
892
893
        $updates = [
894
            self::MAIN_BLOCKS => $main_blocks,
895
            self::SIDE_BLOCKS => $side_blocks,
896
        ];
897
898
        foreach ($updates as $location => $updated_blocks) {
899
            foreach ($updated_blocks as $block_order => $block_id) {
900
                if (is_numeric($block_id)) {
901
                    // Updated block
902
                    DB::table('block')
903
                        ->where('block_id', '=', $block_id)
904
                        ->update([
905
                            'block_order' => $block_order,
906
                            'location'    => $location,
907
                        ]);
908
                } else {
909
                    // New block
910
                    DB::table('block')->insert([
911
                        'gedcom_id'   => $tree_id,
912
                        'location'    => $location,
913
                        'block_order' => $block_order,
914
                        'module_name' => $block_id,
915
                    ]);
916
                }
917
            }
918
        }
919
    }
920
921
    /**
922
     * Take a list of block names, and return block (module) objects.
923
     *
924
     * @param Collection $blocks
925
     * @param Collection $active_blocks
926
     *
927
     * @return Collection
928
     */
929
    private function filterActiveBlocks(Collection $blocks, Collection $active_blocks): Collection
930
    {
931
        return $blocks->map(static function (string $block_name) use ($active_blocks): ?ModuleBlockInterface {
932
            return $active_blocks->filter(static function (ModuleInterface $block) use ($block_name): bool {
933
                return $block->name() === $block_name;
934
            })->first();
935
        })->filter();
936
    }
937
}
938