ClippingsCartModule::addIndividualToCart()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 11
nc 4
nop 1
dl 0
loc 17
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2023 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
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Module;
21
22
use Fisharebest\Webtrees\Auth;
23
use Fisharebest\Webtrees\Encodings\ANSEL;
24
use Fisharebest\Webtrees\Encodings\ASCII;
25
use Fisharebest\Webtrees\Encodings\UTF16BE;
26
use Fisharebest\Webtrees\Encodings\UTF8;
27
use Fisharebest\Webtrees\Encodings\Windows1252;
28
use Fisharebest\Webtrees\Family;
29
use Fisharebest\Webtrees\Gedcom;
30
use Fisharebest\Webtrees\GedcomRecord;
31
use Fisharebest\Webtrees\Http\RequestHandlers\FamilyPage;
32
use Fisharebest\Webtrees\Http\RequestHandlers\IndividualPage;
33
use Fisharebest\Webtrees\Http\RequestHandlers\LocationPage;
34
use Fisharebest\Webtrees\Http\RequestHandlers\MediaPage;
35
use Fisharebest\Webtrees\Http\RequestHandlers\NotePage;
36
use Fisharebest\Webtrees\Http\RequestHandlers\RepositoryPage;
37
use Fisharebest\Webtrees\Http\RequestHandlers\SourcePage;
38
use Fisharebest\Webtrees\Http\RequestHandlers\SubmitterPage;
39
use Fisharebest\Webtrees\I18N;
40
use Fisharebest\Webtrees\Individual;
41
use Fisharebest\Webtrees\Location;
42
use Fisharebest\Webtrees\Media;
43
use Fisharebest\Webtrees\Menu;
44
use Fisharebest\Webtrees\Note;
45
use Fisharebest\Webtrees\Registry;
46
use Fisharebest\Webtrees\Repository;
47
use Fisharebest\Webtrees\Services\GedcomExportService;
48
use Fisharebest\Webtrees\Services\LinkedRecordService;
49
use Fisharebest\Webtrees\Session;
50
use Fisharebest\Webtrees\Source;
51
use Fisharebest\Webtrees\Submitter;
52
use Fisharebest\Webtrees\Tree;
53
use Fisharebest\Webtrees\Validator;
54
use Illuminate\Support\Collection;
55
use Psr\Http\Message\ResponseInterface;
56
use Psr\Http\Message\ServerRequestInterface;
57
58
use function array_filter;
59
use function array_keys;
60
use function array_map;
61
use function array_search;
62
use function assert;
63
use function count;
64
use function date;
65
use function extension_loaded;
66
use function in_array;
67
use function is_array;
68
use function is_string;
69
use function preg_match_all;
70
use function redirect;
71
use function route;
72
use function str_replace;
73
use function uasort;
74
use function view;
75
76
use const PREG_SET_ORDER;
77
78
/**
79
 * Class ClippingsCartModule
80
 */
81
class ClippingsCartModule extends AbstractModule implements ModuleMenuInterface
82
{
83
    use ModuleMenuTrait;
84
85
    // What to add to the cart?
86
    private const ADD_RECORD_ONLY        = 'record';
87
    private const ADD_CHILDREN           = 'children';
88
    private const ADD_DESCENDANTS        = 'descendants';
89
    private const ADD_PARENT_FAMILIES    = 'parents';
90
    private const ADD_SPOUSE_FAMILIES    = 'spouses';
91
    private const ADD_ANCESTORS          = 'ancestors';
92
    private const ADD_ANCESTOR_FAMILIES  = 'families';
93
    private const ADD_LINKED_INDIVIDUALS = 'linked';
94
95
    // Routes that have a record which can be added to the clipboard
96
    private const ROUTES_WITH_RECORDS = [
97
        'Family'     => FamilyPage::class,
98
        'Individual' => IndividualPage::class,
99
        'Media'      => MediaPage::class,
100
        'Location'   => LocationPage::class,
101
        'Note'       => NotePage::class,
102
        'Repository' => RepositoryPage::class,
103
        'Source'     => SourcePage::class,
104
        'Submitter'  => SubmitterPage::class,
105
    ];
106
107
    /** @var int The default access level for this module.  It can be changed in the control panel. */
108
    protected int $access_level = Auth::PRIV_USER;
109
110
    private GedcomExportService $gedcom_export_service;
111
112
    private LinkedRecordService $linked_record_service;
113
114
    public function __construct(
115
        GedcomExportService $gedcom_export_service,
116
        LinkedRecordService $linked_record_service
117
    ) {
118
        $this->gedcom_export_service = $gedcom_export_service;
119
        $this->linked_record_service = $linked_record_service;
120
    }
121
122
    public function description(): string
123
    {
124
        /* I18N: Description of the “Clippings cart” module */
125
        return I18N::translate('Select records from your family tree and save them as a GEDCOM file.');
126
    }
127
128
    public function defaultMenuOrder(): int
129
    {
130
        return 6;
131
    }
132
133
    public function getMenu(Tree $tree): Menu|null
134
    {
135
        $request = Registry::container()->get(ServerRequestInterface::class);
136
        $route   = Validator::attributes($request)->route();
137
        $cart    = Session::get('cart');
138
        $cart    = is_array($cart) ? $cart : [];
139
        $count   = count($cart[$tree->name()] ?? []);
140
        $badge   = view('components/badge', ['count' => $count]);
141
142
        $submenus = [
143
            new Menu($this->title() . ' ' . $badge, route('module', [
144
                'module' => $this->name(),
145
                'action' => 'Show',
146
                'tree'   => $tree->name(),
147
            ]), 'menu-clippings-cart', ['rel' => 'nofollow']),
148
        ];
149
150
        $action = array_search($route->name, self::ROUTES_WITH_RECORDS, true);
151
        if ($action !== false) {
152
            $xref = $route->attributes['xref'];
153
            assert(is_string($xref));
154
155
            $add_route = route('module', [
156
                'module' => $this->name(),
157
                'action' => 'Add' . $action,
158
                'xref'   => $xref,
159
                'tree'   => $tree->name(),
160
            ]);
161
162
            $submenus[] = new Menu(I18N::translate('Add to the clippings cart'), $add_route, 'menu-clippings-add', ['rel' => 'nofollow']);
163
        }
164
165
        if (!$this->isCartEmpty($tree)) {
166
            $submenus[] = new Menu(I18N::translate('Empty the clippings cart'), route('module', [
167
                'module' => $this->name(),
168
                'action' => 'Empty',
169
                'tree'   => $tree->name(),
170
            ]), 'menu-clippings-empty', ['rel' => 'nofollow']);
171
172
            $submenus[] = new Menu(I18N::translate('Download'), route('module', [
173
                'module' => $this->name(),
174
                'action' => 'DownloadForm',
175
                'tree'   => $tree->name(),
176
            ]), 'menu-clippings-download', ['rel' => 'nofollow']);
177
        }
178
179
        return new Menu($this->title(), '#', 'menu-clippings', ['rel' => 'nofollow'], $submenus);
180
    }
181
182
    public function title(): string
183
    {
184
        /* I18N: Name of a module */
185
        return I18N::translate('Clippings cart');
186
    }
187
188
    private function isCartEmpty(Tree $tree): bool
189
    {
190
        $cart     = Session::get('cart');
191
        $cart     = is_array($cart) ? $cart : [];
192
        $contents = $cart[$tree->name()] ?? [];
193
194
        return $contents === [];
195
    }
196
197
    public function getDownloadFormAction(ServerRequestInterface $request): ResponseInterface
198
    {
199
        $tree = Validator::attributes($request)->tree();
200
201
        $title = I18N::translate('Family tree clippings cart') . ' — ' . I18N::translate('Download');
202
203
        $download_filenames = [
204
            'clippings'                  => 'clippings',
205
            'clippings-' . date('Y-m-d') => 'clippings-' . date('Y-m-d'),
206
        ];
207
208
        return $this->viewResponse('modules/clippings/download', [
209
            'download_filenames' => $download_filenames,
210
            'module'             => $this->name(),
211
            'title'              => $title,
212
            'tree'               => $tree,
213
            'zip_available'      => extension_loaded('zip'),
214
        ]);
215
    }
216
217
    public function postDownloadAction(ServerRequestInterface $request): ResponseInterface
218
    {
219
        $tree = Validator::attributes($request)->tree();
220
221
        if (Auth::isAdmin()) {
222
            $privacy_options = ['none', 'gedadmin', 'user', 'visitor'];
223
        } elseif (Auth::isManager($tree)) {
224
            $privacy_options = ['gedadmin', 'user', 'visitor'];
225
        } elseif (Auth::isMember($tree)) {
226
            $privacy_options = ['user', 'visitor'];
227
        } else {
228
            $privacy_options = ['visitor'];
229
        }
230
231
        $filename     = Validator::parsedBody($request)->string('filename');
232
        $format       = Validator::parsedBody($request)->isInArray(['gedcom', 'zip', 'zipmedia', 'gedzip'])->string('format');
233
        $privacy      = Validator::parsedBody($request)->isInArray($privacy_options)->string('privacy');
234
        $encoding     = Validator::parsedBody($request)->isInArray([UTF8::NAME, UTF16BE::NAME, ANSEL::NAME, ASCII::NAME, Windows1252::NAME])->string('encoding');
235
        $line_endings = Validator::parsedBody($request)->isInArray(['CRLF', 'LF'])->string('line_endings');
236
237
        $cart = Session::get('cart');
238
        $cart = is_array($cart) ? $cart : [];
239
240
        $xrefs = array_keys($cart[$tree->name()] ?? []);
241
        $xrefs = array_map('strval', $xrefs); // PHP converts numeric keys to integers.
242
243
        $records = new Collection();
244
245
        switch ($privacy) {
246
            case 'gedadmin':
247
                $access_level = Auth::PRIV_NONE;
248
                break;
249
            case 'user':
250
                $access_level = Auth::PRIV_USER;
251
                break;
252
            case 'visitor':
253
                $access_level = Auth::PRIV_PRIVATE;
254
                break;
255
            case 'none':
256
            default:
257
                $access_level = Auth::PRIV_HIDE;
258
                break;
259
        }
260
261
        foreach ($xrefs as $xref) {
262
            $object = Registry::gedcomRecordFactory()->make($xref, $tree);
263
            // The object may have been deleted since we added it to the cart....
264
            if ($object instanceof GedcomRecord && $object->canShow($access_level)) {
265
                $gedcom = $object->privatizeGedcom($access_level);
266
267
                // Remove links to objects that aren't in the cart
268
                $patterns = [
269
                    '/\n1 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(?:\n[2-9].*)*/',
270
                    '/\n2 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(?:\n[3-9].*)*/',
271
                    '/\n3 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(?:\n[4-9].*)*/',
272
                    '/\n4 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(?:\n[5-9].*)*/',
273
                ];
274
275
                foreach ($patterns as $pattern) {
276
                    preg_match_all($pattern, $gedcom, $matches, PREG_SET_ORDER);
277
278
                    foreach ($matches as $match) {
279
                        if (!in_array($match[1], $xrefs, true)) {
280
                            // Remove the reference to any object that isn't in the cart
281
                            $gedcom = str_replace($match[0], '', $gedcom);
282
                        }
283
                    }
284
                }
285
286
                $records->add($gedcom);
0 ignored issues
show
Bug introduced by
$gedcom of type string is incompatible with the type Illuminate\Support\TValue expected by parameter $item of Illuminate\Support\Collection::add(). ( Ignorable by Annotation )

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

286
                $records->add(/** @scrutinizer ignore-type */ $gedcom);
Loading history...
287
            }
288
        }
289
290
        // We have already applied privacy filtering, so do not do it again.
291
        return $this->gedcom_export_service->downloadResponse($tree, false, $encoding, 'none', $line_endings, $filename, $format, $records);
292
    }
293
294
    public function getEmptyAction(ServerRequestInterface $request): ResponseInterface
295
    {
296
        $tree = Validator::attributes($request)->tree();
297
298
        $cart = Session::get('cart');
299
        $cart = is_array($cart) ? $cart : [];
300
301
        $cart[$tree->name()] = [];
302
        Session::put('cart', $cart);
303
304
        $url = route('module', [
305
            'module' => $this->name(),
306
            'action' => 'Show',
307
            'tree'   => $tree->name(),
308
        ]);
309
310
        return redirect($url);
311
    }
312
313
    public function postRemoveAction(ServerRequestInterface $request): ResponseInterface
314
    {
315
        $tree = Validator::attributes($request)->tree();
316
        $xref = Validator::queryParams($request)->isXref()->string('xref');
317
        $cart = Session::get('cart');
318
        $cart = is_array($cart) ? $cart : [];
319
320
        unset($cart[$tree->name()][$xref]);
321
        Session::put('cart', $cart);
322
323
        $url = route('module', [
324
            'module' => $this->name(),
325
            'action' => 'Show',
326
            'tree'   => $tree->name(),
327
        ]);
328
329
        return redirect($url);
330
    }
331
332
    public function getShowAction(ServerRequestInterface $request): ResponseInterface
333
    {
334
        $tree = Validator::attributes($request)->tree();
335
336
        return $this->viewResponse('modules/clippings/show', [
337
            'module'  => $this->name(),
338
            'records' => $this->allRecordsInCart($tree),
339
            'title'   => I18N::translate('Family tree clippings cart'),
340
            'tree'    => $tree,
341
        ]);
342
    }
343
344
    /**
345
     * @return array<GedcomRecord>
346
     */
347
    private function allRecordsInCart(Tree $tree): array
348
    {
349
        $cart = Session::get('cart');
350
        $cart = is_array($cart) ? $cart : [];
351
352
        $xrefs = array_keys($cart[$tree->name()] ?? []);
353
        $xrefs = array_map('strval', $xrefs); // PHP converts numeric keys to integers.
354
355
        // Fetch all the records in the cart.
356
        $records = array_map(static fn (string $xref): GedcomRecord|null => Registry::gedcomRecordFactory()->make($xref, $tree), $xrefs);
357
358
        // Some records may have been deleted after they were added to the cart.
359
        $records = array_filter($records);
360
361
        // Group and sort.
362
        uasort($records, static fn (GedcomRecord $x, GedcomRecord $y): int => $x->tag() <=> $y->tag() ?: GedcomRecord::nameComparator()($x, $y));
363
364
        return $records;
365
    }
366
367
    public function getAddFamilyAction(ServerRequestInterface $request): ResponseInterface
368
    {
369
        $tree   = Validator::attributes($request)->tree();
370
        $xref   = Validator::queryParams($request)->isXref()->string('xref');
371
        $family = Registry::familyFactory()->make($xref, $tree);
372
        $family = Auth::checkFamilyAccess($family);
373
        $name   = $family->fullName();
374
375
        $options = [
376
            self::ADD_RECORD_ONLY => $name,
377
            /* I18N: %s is a family (husband + wife) */
378
            self::ADD_CHILDREN    => I18N::translate('%s and their children', $name),
379
            /* I18N: %s is a family (husband + wife) */
380
            self::ADD_DESCENDANTS => I18N::translate('%s and their descendants', $name),
381
        ];
382
383
        $title = I18N::translate('Add %s to the clippings cart', $name);
384
385
        return $this->viewResponse('modules/clippings/add-options', [
386
            'options' => $options,
387
            'record'  => $family,
388
            'title'   => $title,
389
            'tree'    => $tree,
390
        ]);
391
    }
392
393
    public function postAddFamilyAction(ServerRequestInterface $request): ResponseInterface
394
    {
395
        $tree   = Validator::attributes($request)->tree();
396
        $xref   = Validator::parsedBody($request)->isXref()->string('xref');
397
        $option = Validator::parsedBody($request)->string('option');
398
399
        $family = Registry::familyFactory()->make($xref, $tree);
400
        $family = Auth::checkFamilyAccess($family);
401
402
        switch ($option) {
403
            case self::ADD_RECORD_ONLY:
404
                $this->addFamilyToCart($family);
405
                break;
406
407
            case self::ADD_CHILDREN:
408
                $this->addFamilyAndChildrenToCart($family);
409
                break;
410
411
            case self::ADD_DESCENDANTS:
412
                $this->addFamilyAndDescendantsToCart($family);
413
                break;
414
        }
415
416
        return redirect($family->url());
417
    }
418
419
    protected function addFamilyAndChildrenToCart(Family $family): void
420
    {
421
        $this->addFamilyToCart($family);
422
423
        foreach ($family->children() as $child) {
424
            $this->addIndividualToCart($child);
425
        }
426
    }
427
428
    protected function addFamilyAndDescendantsToCart(Family $family): void
429
    {
430
        $this->addFamilyAndChildrenToCart($family);
431
432
        foreach ($family->children() as $child) {
433
            foreach ($child->spouseFamilies() as $child_family) {
434
                $this->addFamilyAndDescendantsToCart($child_family);
435
            }
436
        }
437
    }
438
439
    public function getAddIndividualAction(ServerRequestInterface $request): ResponseInterface
440
    {
441
        $tree       = Validator::attributes($request)->tree();
442
        $xref       = Validator::queryParams($request)->isXref()->string('xref');
443
        $individual = Registry::individualFactory()->make($xref, $tree);
444
        $individual = Auth::checkIndividualAccess($individual);
445
        $name       = $individual->fullName();
446
447
        if ($individual->sex() === 'F') {
448
            $options = [
449
                self::ADD_RECORD_ONLY       => $name,
450
                self::ADD_PARENT_FAMILIES   => I18N::translate('%s, her parents and siblings', $name),
451
                self::ADD_SPOUSE_FAMILIES   => I18N::translate('%s, her spouses and children', $name),
452
                self::ADD_ANCESTORS         => I18N::translate('%s and her ancestors', $name),
453
                self::ADD_ANCESTOR_FAMILIES => I18N::translate('%s, her ancestors and their families', $name),
454
                self::ADD_DESCENDANTS       => I18N::translate('%s, her spouses and descendants', $name),
455
            ];
456
        } else {
457
            $options = [
458
                self::ADD_RECORD_ONLY       => $name,
459
                self::ADD_PARENT_FAMILIES   => I18N::translate('%s, his parents and siblings', $name),
460
                self::ADD_SPOUSE_FAMILIES   => I18N::translate('%s, his spouses and children', $name),
461
                self::ADD_ANCESTORS         => I18N::translate('%s and his ancestors', $name),
462
                self::ADD_ANCESTOR_FAMILIES => I18N::translate('%s, his ancestors and their families', $name),
463
                self::ADD_DESCENDANTS       => I18N::translate('%s, his spouses and descendants', $name),
464
            ];
465
        }
466
467
        $title = I18N::translate('Add %s to the clippings cart', $name);
468
469
        return $this->viewResponse('modules/clippings/add-options', [
470
            'options' => $options,
471
            'record'  => $individual,
472
            'title'   => $title,
473
            'tree'    => $tree,
474
        ]);
475
    }
476
477
    public function postAddIndividualAction(ServerRequestInterface $request): ResponseInterface
478
    {
479
        $tree   = Validator::attributes($request)->tree();
480
        $xref   = Validator::parsedBody($request)->isXref()->string('xref');
481
        $option = Validator::parsedBody($request)->string('option');
482
483
        $individual = Registry::individualFactory()->make($xref, $tree);
484
        $individual = Auth::checkIndividualAccess($individual);
485
486
        switch ($option) {
487
            case self::ADD_RECORD_ONLY:
488
                $this->addIndividualToCart($individual);
489
                break;
490
491
            case self::ADD_PARENT_FAMILIES:
492
                foreach ($individual->childFamilies() as $family) {
493
                    $this->addFamilyAndChildrenToCart($family);
494
                }
495
                break;
496
497
            case self::ADD_SPOUSE_FAMILIES:
498
                foreach ($individual->spouseFamilies() as $family) {
499
                    $this->addFamilyAndChildrenToCart($family);
500
                }
501
                break;
502
503
            case self::ADD_ANCESTORS:
504
                $this->addAncestorsToCart($individual);
505
                break;
506
507
            case self::ADD_ANCESTOR_FAMILIES:
508
                $this->addAncestorFamiliesToCart($individual);
509
                break;
510
511
            case self::ADD_DESCENDANTS:
512
                foreach ($individual->spouseFamilies() as $family) {
513
                    $this->addFamilyAndDescendantsToCart($family);
514
                }
515
                break;
516
        }
517
518
        return redirect($individual->url());
519
    }
520
521
    protected function addAncestorsToCart(Individual $individual): void
522
    {
523
        $this->addIndividualToCart($individual);
524
525
        foreach ($individual->childFamilies() as $family) {
526
            $this->addFamilyToCart($family);
527
528
            foreach ($family->spouses() as $parent) {
529
                $this->addAncestorsToCart($parent);
530
            }
531
        }
532
    }
533
534
    protected function addAncestorFamiliesToCart(Individual $individual): void
535
    {
536
        foreach ($individual->childFamilies() as $family) {
537
            $this->addFamilyAndChildrenToCart($family);
538
539
            foreach ($family->spouses() as $parent) {
540
                $this->addAncestorFamiliesToCart($parent);
541
            }
542
        }
543
    }
544
545
    public function getAddLocationAction(ServerRequestInterface $request): ResponseInterface
546
    {
547
        $tree     = Validator::attributes($request)->tree();
548
        $xref     = Validator::queryParams($request)->isXref()->string('xref');
549
        $location = Registry::locationFactory()->make($xref, $tree);
550
        $location = Auth::checkLocationAccess($location);
551
        $name     = $location->fullName();
552
553
        $options = [
554
            self::ADD_RECORD_ONLY => $name,
555
        ];
556
557
        $title = I18N::translate('Add %s to the clippings cart', $name);
558
559
        return $this->viewResponse('modules/clippings/add-options', [
560
            'options' => $options,
561
            'record'  => $location,
562
            'title'   => $title,
563
            'tree'    => $tree,
564
        ]);
565
    }
566
567
    public function postAddLocationAction(ServerRequestInterface $request): ResponseInterface
568
    {
569
        $tree     = Validator::attributes($request)->tree();
570
        $xref     = Validator::queryParams($request)->isXref()->string('xref');
571
        $location = Registry::locationFactory()->make($xref, $tree);
572
        $location = Auth::checkLocationAccess($location);
573
574
        $this->addLocationToCart($location);
575
576
        return redirect($location->url());
577
    }
578
579
    public function getAddMediaAction(ServerRequestInterface $request): ResponseInterface
580
    {
581
        $tree  = Validator::attributes($request)->tree();
582
        $xref  = Validator::queryParams($request)->isXref()->string('xref');
583
        $media = Registry::mediaFactory()->make($xref, $tree);
584
        $media = Auth::checkMediaAccess($media);
585
        $name  = $media->fullName();
586
587
        $options = [
588
            self::ADD_RECORD_ONLY => $name,
589
        ];
590
591
        $title = I18N::translate('Add %s to the clippings cart', $name);
592
593
        return $this->viewResponse('modules/clippings/add-options', [
594
            'options' => $options,
595
            'record'  => $media,
596
            'title'   => $title,
597
            'tree'    => $tree,
598
        ]);
599
    }
600
601
    public function postAddMediaAction(ServerRequestInterface $request): ResponseInterface
602
    {
603
        $tree  = Validator::attributes($request)->tree();
604
        $xref  = Validator::queryParams($request)->isXref()->string('xref');
605
        $media = Registry::mediaFactory()->make($xref, $tree);
606
        $media = Auth::checkMediaAccess($media);
607
608
        $this->addMediaToCart($media);
609
610
        return redirect($media->url());
611
    }
612
613
    public function getAddNoteAction(ServerRequestInterface $request): ResponseInterface
614
    {
615
        $tree = Validator::attributes($request)->tree();
616
        $xref = Validator::queryParams($request)->isXref()->string('xref');
617
        $note = Registry::noteFactory()->make($xref, $tree);
618
        $note = Auth::checkNoteAccess($note);
619
        $name = $note->fullName();
620
621
        $options = [
622
            self::ADD_RECORD_ONLY => $name,
623
        ];
624
625
        $title = I18N::translate('Add %s to the clippings cart', $name);
626
627
        return $this->viewResponse('modules/clippings/add-options', [
628
            'options' => $options,
629
            'record'  => $note,
630
            'title'   => $title,
631
            'tree'    => $tree,
632
        ]);
633
    }
634
635
    public function postAddNoteAction(ServerRequestInterface $request): ResponseInterface
636
    {
637
        $tree = Validator::attributes($request)->tree();
638
        $xref = Validator::queryParams($request)->isXref()->string('xref');
639
        $note = Registry::noteFactory()->make($xref, $tree);
640
        $note = Auth::checkNoteAccess($note);
641
642
        $this->addNoteToCart($note);
643
644
        return redirect($note->url());
645
    }
646
647
    public function getAddRepositoryAction(ServerRequestInterface $request): ResponseInterface
648
    {
649
        $tree       = Validator::attributes($request)->tree();
650
        $xref       = Validator::queryParams($request)->isXref()->string('xref');
651
        $repository = Registry::repositoryFactory()->make($xref, $tree);
652
        $repository = Auth::checkRepositoryAccess($repository);
653
        $name       = $repository->fullName();
654
655
        $options = [
656
            self::ADD_RECORD_ONLY => $name,
657
        ];
658
659
        $title = I18N::translate('Add %s to the clippings cart', $name);
660
661
        return $this->viewResponse('modules/clippings/add-options', [
662
            'options' => $options,
663
            'record'  => $repository,
664
            'title'   => $title,
665
            'tree'    => $tree,
666
        ]);
667
    }
668
669
    public function postAddRepositoryAction(ServerRequestInterface $request): ResponseInterface
670
    {
671
        $tree       = Validator::attributes($request)->tree();
672
        $xref       = Validator::queryParams($request)->isXref()->string('xref');
673
        $repository = Registry::repositoryFactory()->make($xref, $tree);
674
        $repository = Auth::checkRepositoryAccess($repository);
675
676
        $this->addRepositoryToCart($repository);
677
678
        foreach ($this->linked_record_service->linkedSources($repository) as $source) {
679
            $this->addSourceToCart($source);
680
        }
681
682
        return redirect($repository->url());
683
    }
684
685
    public function getAddSourceAction(ServerRequestInterface $request): ResponseInterface
686
    {
687
        $tree   = Validator::attributes($request)->tree();
688
        $xref   = Validator::queryParams($request)->isXref()->string('xref');
689
        $source = Registry::sourceFactory()->make($xref, $tree);
690
        $source = Auth::checkSourceAccess($source);
691
        $name   = $source->fullName();
692
693
        $options = [
694
            self::ADD_RECORD_ONLY        => $name,
695
            self::ADD_LINKED_INDIVIDUALS => I18N::translate('%s and the individuals that reference it.', $name),
696
        ];
697
698
        $title = I18N::translate('Add %s to the clippings cart', $name);
699
700
        return $this->viewResponse('modules/clippings/add-options', [
701
            'options' => $options,
702
            'record'  => $source,
703
            'title'   => $title,
704
            'tree'    => $tree,
705
        ]);
706
    }
707
708
    public function postAddSourceAction(ServerRequestInterface $request): ResponseInterface
709
    {
710
        $tree   = Validator::attributes($request)->tree();
711
        $xref   = Validator::parsedBody($request)->isXref()->string('xref');
712
        $option = Validator::parsedBody($request)->string('option');
713
714
        $source = Registry::sourceFactory()->make($xref, $tree);
715
        $source = Auth::checkSourceAccess($source);
716
717
        $this->addSourceToCart($source);
718
719
        if ($option === self::ADD_LINKED_INDIVIDUALS) {
720
            foreach ($this->linked_record_service->linkedIndividuals($source) as $individual) {
721
                $this->addIndividualToCart($individual);
722
            }
723
            foreach ($this->linked_record_service->linkedFamilies($source) as $family) {
724
                $this->addFamilyToCart($family);
725
            }
726
        }
727
728
        return redirect($source->url());
729
    }
730
731
    public function getAddSubmitterAction(ServerRequestInterface $request): ResponseInterface
732
    {
733
        $tree      = Validator::attributes($request)->tree();
734
        $xref      = Validator::queryParams($request)->isXref()->string('xref');
735
        $submitter = Registry::submitterFactory()->make($xref, $tree);
736
        $submitter = Auth::checkSubmitterAccess($submitter);
737
        $name      = $submitter->fullName();
738
739
        $options = [
740
            self::ADD_RECORD_ONLY => $name,
741
        ];
742
743
        $title = I18N::translate('Add %s to the clippings cart', $name);
744
745
        return $this->viewResponse('modules/clippings/add-options', [
746
            'options' => $options,
747
            'record'  => $submitter,
748
            'title'   => $title,
749
            'tree'    => $tree,
750
        ]);
751
    }
752
753
    public function postAddSubmitterAction(ServerRequestInterface $request): ResponseInterface
754
    {
755
        $tree      = Validator::attributes($request)->tree();
756
        $xref      = Validator::queryParams($request)->isXref()->string('xref');
757
        $submitter = Registry::submitterFactory()->make($xref, $tree);
758
        $submitter = Auth::checkSubmitterAccess($submitter);
759
760
        $this->addSubmitterToCart($submitter);
761
762
        return redirect($submitter->url());
763
    }
764
765
    protected function addFamilyToCart(Family $family): void
766
    {
767
        $cart = Session::get('cart');
768
        $cart = is_array($cart) ? $cart : [];
769
770
        $tree = $family->tree()->name();
771
        $xref = $family->xref();
772
773
        if (($cart[$tree][$xref] ?? false) === false) {
774
            $cart[$tree][$xref] = true;
775
776
            Session::put('cart', $cart);
777
778
            foreach ($family->spouses() as $spouse) {
779
                $this->addIndividualToCart($spouse);
780
            }
781
782
            $this->addLocationLinksToCart($family);
783
            $this->addMediaLinksToCart($family);
784
            $this->addNoteLinksToCart($family);
785
            $this->addSourceLinksToCart($family);
786
            $this->addSubmitterLinksToCart($family);
787
        }
788
    }
789
790
    protected function addIndividualToCart(Individual $individual): void
791
    {
792
        $cart = Session::get('cart');
793
        $cart = is_array($cart) ? $cart : [];
794
795
        $tree = $individual->tree()->name();
796
        $xref = $individual->xref();
797
798
        if (($cart[$tree][$xref] ?? false) === false) {
799
            $cart[$tree][$xref] = true;
800
801
            Session::put('cart', $cart);
802
803
            $this->addLocationLinksToCart($individual);
804
            $this->addMediaLinksToCart($individual);
805
            $this->addNoteLinksToCart($individual);
806
            $this->addSourceLinksToCart($individual);
807
        }
808
    }
809
810
    protected function addLocationToCart(Location $location): void
811
    {
812
        $cart = Session::get('cart');
813
        $cart = is_array($cart) ? $cart : [];
814
815
        $tree = $location->tree()->name();
816
        $xref = $location->xref();
817
818
        if (($cart[$tree][$xref] ?? false) === false) {
819
            $cart[$tree][$xref] = true;
820
821
            Session::put('cart', $cart);
822
823
            $this->addLocationLinksToCart($location);
824
            $this->addMediaLinksToCart($location);
825
            $this->addNoteLinksToCart($location);
826
            $this->addSourceLinksToCart($location);
827
        }
828
    }
829
830
    protected function addLocationLinksToCart(GedcomRecord $record): void
831
    {
832
        preg_match_all('/\n\d _LOC @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
833
834
        foreach ($matches[1] as $xref) {
835
            $location = Registry::locationFactory()->make($xref, $record->tree());
836
837
            if ($location instanceof Location && $location->canShow()) {
838
                $this->addLocationToCart($location);
839
            }
840
        }
841
    }
842
843
    protected function addMediaToCart(Media $media): void
844
    {
845
        $cart = Session::get('cart');
846
        $cart = is_array($cart) ? $cart : [];
847
848
        $tree = $media->tree()->name();
849
        $xref = $media->xref();
850
851
        if (($cart[$tree][$xref] ?? false) === false) {
852
            $cart[$tree][$xref] = true;
853
854
            Session::put('cart', $cart);
855
856
            $this->addNoteLinksToCart($media);
857
        }
858
    }
859
860
    protected function addMediaLinksToCart(GedcomRecord $record): void
861
    {
862
        preg_match_all('/\n\d OBJE @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
863
864
        foreach ($matches[1] as $xref) {
865
            $media = Registry::mediaFactory()->make($xref, $record->tree());
866
867
            if ($media instanceof Media && $media->canShow()) {
868
                $this->addMediaToCart($media);
869
            }
870
        }
871
    }
872
873
    protected function addNoteToCart(Note $note): void
874
    {
875
        $cart = Session::get('cart');
876
        $cart = is_array($cart) ? $cart : [];
877
878
        $tree = $note->tree()->name();
879
        $xref = $note->xref();
880
881
        if (($cart[$tree][$xref] ?? false) === false) {
882
            $cart[$tree][$xref] = true;
883
884
            Session::put('cart', $cart);
885
        }
886
    }
887
888
    protected function addNoteLinksToCart(GedcomRecord $record): void
889
    {
890
        preg_match_all('/\n\d NOTE @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
891
892
        foreach ($matches[1] as $xref) {
893
            $note = Registry::noteFactory()->make($xref, $record->tree());
894
895
            if ($note instanceof Note && $note->canShow()) {
896
                $this->addNoteToCart($note);
897
            }
898
        }
899
    }
900
901
    protected function addSourceToCart(Source $source): void
902
    {
903
        $cart = Session::get('cart');
904
        $cart = is_array($cart) ? $cart : [];
905
906
        $tree = $source->tree()->name();
907
        $xref = $source->xref();
908
909
        if (($cart[$tree][$xref] ?? false) === false) {
910
            $cart[$tree][$xref] = true;
911
912
            Session::put('cart', $cart);
913
914
            $this->addNoteLinksToCart($source);
915
            $this->addRepositoryLinksToCart($source);
916
        }
917
    }
918
919
    protected function addSourceLinksToCart(GedcomRecord $record): void
920
    {
921
        preg_match_all('/\n\d SOUR @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
922
923
        foreach ($matches[1] as $xref) {
924
            $source = Registry::sourceFactory()->make($xref, $record->tree());
925
926
            if ($source instanceof Source && $source->canShow()) {
927
                $this->addSourceToCart($source);
928
            }
929
        }
930
    }
931
932
    protected function addRepositoryToCart(Repository $repository): void
933
    {
934
        $cart = Session::get('cart');
935
        $cart = is_array($cart) ? $cart : [];
936
937
        $tree = $repository->tree()->name();
938
        $xref = $repository->xref();
939
940
        if (($cart[$tree][$xref] ?? false) === false) {
941
            $cart[$tree][$xref] = true;
942
943
            Session::put('cart', $cart);
944
945
            $this->addNoteLinksToCart($repository);
946
        }
947
    }
948
949
    protected function addRepositoryLinksToCart(GedcomRecord $record): void
950
    {
951
        preg_match_all('/\n\d REPO @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
952
953
        foreach ($matches[1] as $xref) {
954
            $repository = Registry::repositoryFactory()->make($xref, $record->tree());
955
956
            if ($repository instanceof Repository && $repository->canShow()) {
957
                $this->addRepositoryToCart($repository);
958
            }
959
        }
960
    }
961
962
    protected function addSubmitterToCart(Submitter $submitter): void
963
    {
964
        $cart = Session::get('cart');
965
        $cart = is_array($cart) ? $cart : [];
966
        $tree = $submitter->tree()->name();
967
        $xref = $submitter->xref();
968
969
        if (($cart[$tree][$xref] ?? false) === false) {
970
            $cart[$tree][$xref] = true;
971
972
            Session::put('cart', $cart);
973
974
            $this->addNoteLinksToCart($submitter);
975
        }
976
    }
977
978
    protected function addSubmitterLinksToCart(GedcomRecord $record): void
979
    {
980
        preg_match_all('/\n\d SUBM @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
981
982
        foreach ($matches[1] as $xref) {
983
            $submitter = Registry::submitterFactory()->make($xref, $record->tree());
984
985
            if ($submitter instanceof Submitter && $submitter->canShow()) {
986
                $this->addSubmitterToCart($submitter);
987
            }
988
        }
989
    }
990
}
991