xref: /webtrees/app/Module/ClippingsCartModule.php (revision d11be7027e34e3121be11cc025421873364403f9)
18c2e8227SGreg Roach<?php
23976b470SGreg Roach
38c2e8227SGreg Roach/**
48c2e8227SGreg Roach * webtrees: online genealogy
5*d11be702SGreg Roach * Copyright (C) 2023 webtrees development team
68c2e8227SGreg Roach * This program is free software: you can redistribute it and/or modify
78c2e8227SGreg Roach * it under the terms of the GNU General Public License as published by
88c2e8227SGreg Roach * the Free Software Foundation, either version 3 of the License, or
98c2e8227SGreg Roach * (at your option) any later version.
108c2e8227SGreg Roach * This program is distributed in the hope that it will be useful,
118c2e8227SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
128c2e8227SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
138c2e8227SGreg Roach * GNU General Public License for more details.
148c2e8227SGreg Roach * You should have received a copy of the GNU General Public License
1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>.
168c2e8227SGreg Roach */
17fcfa147eSGreg Roach
18e7f56f2aSGreg Roachdeclare(strict_types=1);
19e7f56f2aSGreg Roach
2076692c8bSGreg Roachnamespace Fisharebest\Webtrees\Module;
2176692c8bSGreg Roach
220e62c4b8SGreg Roachuse Fisharebest\Webtrees\Auth;
231c6adce8SGreg Roachuse Fisharebest\Webtrees\Encodings\ANSEL;
241c6adce8SGreg Roachuse Fisharebest\Webtrees\Encodings\ASCII;
251c6adce8SGreg Roachuse Fisharebest\Webtrees\Encodings\UTF16BE;
261c6adce8SGreg Roachuse Fisharebest\Webtrees\Encodings\UTF8;
271c6adce8SGreg Roachuse Fisharebest\Webtrees\Encodings\Windows1252;
280e62c4b8SGreg Roachuse Fisharebest\Webtrees\Family;
295a78cd34SGreg Roachuse Fisharebest\Webtrees\Gedcom;
300e62c4b8SGreg Roachuse Fisharebest\Webtrees\GedcomRecord;
31f95e0480SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\FamilyPage;
32f95e0480SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\IndividualPage;
33e8ded2caSGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\LocationPage;
34f95e0480SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\MediaPage;
35f95e0480SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\NotePage;
36f95e0480SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\RepositoryPage;
37f95e0480SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\SourcePage;
38d45701ccSGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\SubmitterPage;
390e62c4b8SGreg Roachuse Fisharebest\Webtrees\I18N;
400e62c4b8SGreg Roachuse Fisharebest\Webtrees\Individual;
41e8ded2caSGreg Roachuse Fisharebest\Webtrees\Location;
425a78cd34SGreg Roachuse Fisharebest\Webtrees\Media;
430e62c4b8SGreg Roachuse Fisharebest\Webtrees\Menu;
445a78cd34SGreg Roachuse Fisharebest\Webtrees\Note;
45d45701ccSGreg Roachuse Fisharebest\Webtrees\Registry;
465a78cd34SGreg Roachuse Fisharebest\Webtrees\Repository;
4769c05a6eSGreg Roachuse Fisharebest\Webtrees\Services\GedcomExportService;
484991f205SGreg Roachuse Fisharebest\Webtrees\Services\LinkedRecordService;
490e62c4b8SGreg Roachuse Fisharebest\Webtrees\Session;
505a78cd34SGreg Roachuse Fisharebest\Webtrees\Source;
51d45701ccSGreg Roachuse Fisharebest\Webtrees\Submitter;
52aee13b6dSGreg Roachuse Fisharebest\Webtrees\Tree;
531c6adce8SGreg Roachuse Fisharebest\Webtrees\Validator;
5469c05a6eSGreg Roachuse Illuminate\Support\Collection;
556ccdf4f0SGreg Roachuse Psr\Http\Message\ResponseInterface;
566ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface;
573976b470SGreg Roach
58eb235819SGreg Roachuse function app;
59bf80ec58SGreg Roachuse function array_filter;
60bf80ec58SGreg Roachuse function array_keys;
61bf80ec58SGreg Roachuse function array_map;
62fa695506SGreg Roachuse function array_search;
635229eadeSGreg Roachuse function assert;
6410e06497SGreg Roachuse function count;
6516ecfcafSGreg Roachuse function date;
6616ecfcafSGreg Roachuse function extension_loaded;
67bf80ec58SGreg Roachuse function in_array;
68d8809d62SGreg Roachuse function is_array;
69ddeb3354SGreg Roachuse function is_string;
70bf80ec58SGreg Roachuse function preg_match_all;
71bf80ec58SGreg Roachuse function redirect;
72bf80ec58SGreg Roachuse function route;
73e5a6b4d4SGreg Roachuse function str_replace;
74fa695506SGreg Roachuse function uasort;
75d45701ccSGreg Roachuse function view;
76fa695506SGreg Roach
77fa695506SGreg Roachuse const PREG_SET_ORDER;
788c2e8227SGreg Roach
798c2e8227SGreg Roach/**
808c2e8227SGreg Roach * Class ClippingsCartModule
818c2e8227SGreg Roach */
8237eb8894SGreg Roachclass ClippingsCartModule extends AbstractModule implements ModuleMenuInterface
83c1010edaSGreg Roach{
8449a243cbSGreg Roach    use ModuleMenuTrait;
8549a243cbSGreg Roach
8603f99a78SGreg Roach    // What to add to the cart?
8703f99a78SGreg Roach    private const ADD_RECORD_ONLY        = 'record';
8803f99a78SGreg Roach    private const ADD_CHILDREN           = 'children';
8903f99a78SGreg Roach    private const ADD_DESCENDANTS        = 'descendants';
9003f99a78SGreg Roach    private const ADD_PARENT_FAMILIES    = 'parents';
9103f99a78SGreg Roach    private const ADD_SPOUSE_FAMILIES    = 'spouses';
9203f99a78SGreg Roach    private const ADD_ANCESTORS          = 'ancestors';
9303f99a78SGreg Roach    private const ADD_ANCESTOR_FAMILIES  = 'families';
9403f99a78SGreg Roach    private const ADD_LINKED_INDIVIDUALS = 'linked';
9503f99a78SGreg Roach
965a78cd34SGreg Roach    // Routes that have a record which can be added to the clipboard
9716d6367aSGreg Roach    private const ROUTES_WITH_RECORDS = [
98f95e0480SGreg Roach        'Family'     => FamilyPage::class,
99f95e0480SGreg Roach        'Individual' => IndividualPage::class,
100f95e0480SGreg Roach        'Media'      => MediaPage::class,
101e8ded2caSGreg Roach        'Location'   => LocationPage::class,
102f95e0480SGreg Roach        'Note'       => NotePage::class,
103f95e0480SGreg Roach        'Repository' => RepositoryPage::class,
104f95e0480SGreg Roach        'Source'     => SourcePage::class,
105d45701ccSGreg Roach        'Submitter'  => SubmitterPage::class,
106c1010edaSGreg Roach    ];
1075a78cd34SGreg Roach
10849a243cbSGreg Roach    /** @var int The default access level for this module.  It can be changed in the control panel. */
10933c746f1SGreg Roach    protected int $access_level = Auth::PRIV_USER;
11049a243cbSGreg Roach
111ca47b2adSGreg Roach    private GedcomExportService $gedcom_export_service;
11269c05a6eSGreg Roach
1134991f205SGreg Roach    private LinkedRecordService $linked_record_service;
1144991f205SGreg Roach
115e5a6b4d4SGreg Roach    /**
116e5a6b4d4SGreg Roach     * ClippingsCartModule constructor.
117e5a6b4d4SGreg Roach     *
11869c05a6eSGreg Roach     * @param GedcomExportService $gedcom_export_service
1194991f205SGreg Roach     * @param LinkedRecordService $linked_record_service
120e5a6b4d4SGreg Roach     */
121ca47b2adSGreg Roach    public function __construct(
122ca47b2adSGreg Roach        GedcomExportService $gedcom_export_service,
12316ecfcafSGreg Roach        LinkedRecordService $linked_record_service
124ca47b2adSGreg Roach    ) {
12569c05a6eSGreg Roach        $this->gedcom_export_service = $gedcom_export_service;
1264991f205SGreg Roach        $this->linked_record_service = $linked_record_service;
127e5a6b4d4SGreg Roach    }
128e5a6b4d4SGreg Roach
129e5a6b4d4SGreg Roach    /**
130961ec755SGreg Roach     * A sentence describing what this module does.
131961ec755SGreg Roach     *
132961ec755SGreg Roach     * @return string
133961ec755SGreg Roach     */
13449a243cbSGreg Roach    public function description(): string
135c1010edaSGreg Roach    {
136bbb76c12SGreg Roach        /* I18N: Description of the “Clippings cart” module */
137bbb76c12SGreg Roach        return I18N::translate('Select records from your family tree and save them as a GEDCOM file.');
1388c2e8227SGreg Roach    }
1398c2e8227SGreg Roach
1400ee13198SGreg Roach    /**
14149a243cbSGreg Roach     * The default position for this menu.  It can be changed in the control panel.
1420ee13198SGreg Roach     *
1430ee13198SGreg Roach     * @return int
1440ee13198SGreg Roach     */
1458f53f488SRico Sonntag    public function defaultMenuOrder(): int
146c1010edaSGreg Roach    {
147353b36abSGreg Roach        return 6;
1488c2e8227SGreg Roach    }
1498c2e8227SGreg Roach
1500ee13198SGreg Roach    /**
1510ee13198SGreg Roach     * A menu, to be added to the main application menu.
1520ee13198SGreg Roach     *
153aee13b6dSGreg Roach     * @param Tree $tree
154aee13b6dSGreg Roach     *
1550ee13198SGreg Roach     * @return Menu|null
1560ee13198SGreg Roach     */
15746295629SGreg Roach    public function getMenu(Tree $tree): ?Menu
158c1010edaSGreg Roach    {
1596ccdf4f0SGreg Roach        $request = app(ServerRequestInterface::class);
160b55cbc6bSGreg Roach        assert($request instanceof ServerRequestInterface);
1618c2e8227SGreg Roach
162b55cbc6bSGreg Roach        $route = Validator::attributes($request)->route();
163d8809d62SGreg Roach        $cart  = Session::get('cart');
164d8809d62SGreg Roach        $cart  = is_array($cart) ? $cart : [];
165d45701ccSGreg Roach        $count = count($cart[$tree->name()] ?? []);
166d45701ccSGreg Roach        $badge = view('components/badge', ['count' => $count]);
167d45701ccSGreg Roach
1685a78cd34SGreg Roach        $submenus = [
169d45701ccSGreg Roach            new Menu($this->title() . ' ' . $badge, route('module', [
17026684e68SGreg Roach                'module' => $this->name(),
171c1010edaSGreg Roach                'action' => 'Show',
172d72b284aSGreg Roach                'tree'   => $tree->name(),
173c1010edaSGreg Roach            ]), 'menu-clippings-cart', ['rel' => 'nofollow']),
1745a78cd34SGreg Roach        ];
1755a78cd34SGreg Roach
1762b0d92b4SGreg Roach        $action = array_search($route->name, self::ROUTES_WITH_RECORDS, true);
177f95e0480SGreg Roach        if ($action !== false) {
1782b0d92b4SGreg Roach            $xref = $route->attributes['xref'];
179ddeb3354SGreg Roach            assert(is_string($xref));
180ddeb3354SGreg Roach
181c1010edaSGreg Roach            $add_route = route('module', [
18226684e68SGreg Roach                'module' => $this->name(),
183f95e0480SGreg Roach                'action' => 'Add' . $action,
184c1010edaSGreg Roach                'xref'   => $xref,
185d72b284aSGreg Roach                'tree'   => $tree->name(),
186c1010edaSGreg Roach            ]);
1875a78cd34SGreg Roach
18825b2dde3SGreg Roach            $submenus[] = new Menu(I18N::translate('Add to the clippings cart'), $add_route, 'menu-clippings-add', ['rel' => 'nofollow']);
1898c2e8227SGreg Roach        }
190cbc1590aSGreg Roach
1915a78cd34SGreg Roach        if (!$this->isCartEmpty($tree)) {
192c1010edaSGreg Roach            $submenus[] = new Menu(I18N::translate('Empty the clippings cart'), route('module', [
19326684e68SGreg Roach                'module' => $this->name(),
194c1010edaSGreg Roach                'action' => 'Empty',
195d72b284aSGreg Roach                'tree'   => $tree->name(),
196c1010edaSGreg Roach            ]), 'menu-clippings-empty', ['rel' => 'nofollow']);
197f95e0480SGreg Roach
198c1010edaSGreg Roach            $submenus[] = new Menu(I18N::translate('Download'), route('module', [
19926684e68SGreg Roach                'module' => $this->name(),
200c1010edaSGreg Roach                'action' => 'DownloadForm',
201d72b284aSGreg Roach                'tree'   => $tree->name(),
202c1010edaSGreg Roach            ]), 'menu-clippings-download', ['rel' => 'nofollow']);
2035a78cd34SGreg Roach        }
2045a78cd34SGreg Roach
20549a243cbSGreg Roach        return new Menu($this->title(), '#', 'menu-clippings', ['rel' => 'nofollow'], $submenus);
2068c2e8227SGreg Roach    }
2078c2e8227SGreg Roach
20876692c8bSGreg Roach    /**
209d45701ccSGreg Roach     * How should this module be identified in the control panel, etc.?
210d45701ccSGreg Roach     *
211d45701ccSGreg Roach     * @return string
212d45701ccSGreg Roach     */
213d45701ccSGreg Roach    public function title(): string
214d45701ccSGreg Roach    {
215d45701ccSGreg Roach        /* I18N: Name of a module */
216d45701ccSGreg Roach        return I18N::translate('Clippings cart');
217d45701ccSGreg Roach    }
218d45701ccSGreg Roach
219d45701ccSGreg Roach    /**
220d45701ccSGreg Roach     * @param Tree $tree
221d45701ccSGreg Roach     *
222d45701ccSGreg Roach     * @return bool
223d45701ccSGreg Roach     */
224d45701ccSGreg Roach    private function isCartEmpty(Tree $tree): bool
225d45701ccSGreg Roach    {
226d8809d62SGreg Roach        $cart     = Session::get('cart');
227d8809d62SGreg Roach        $cart     = is_array($cart) ? $cart : [];
228d45701ccSGreg Roach        $contents = $cart[$tree->name()] ?? [];
229d45701ccSGreg Roach
230d45701ccSGreg Roach        return $contents === [];
231d45701ccSGreg Roach    }
232d45701ccSGreg Roach
233d45701ccSGreg Roach    /**
234d45701ccSGreg Roach     * @param ServerRequestInterface $request
235d45701ccSGreg Roach     *
236d45701ccSGreg Roach     * @return ResponseInterface
237d45701ccSGreg Roach     */
238d45701ccSGreg Roach    public function getDownloadFormAction(ServerRequestInterface $request): ResponseInterface
239d45701ccSGreg Roach    {
240b55cbc6bSGreg Roach        $tree = Validator::attributes($request)->tree();
241d45701ccSGreg Roach
242d45701ccSGreg Roach        $title = I18N::translate('Family tree clippings cart') . ' — ' . I18N::translate('Download');
243d45701ccSGreg Roach
24416ecfcafSGreg Roach        $download_filenames = [
24516ecfcafSGreg Roach            'clippings'                  => 'clippings',
24616ecfcafSGreg Roach            'clippings-' . date('Y-m-d') => 'clippings-' . date('Y-m-d'),
24716ecfcafSGreg Roach        ];
24816ecfcafSGreg Roach
249d45701ccSGreg Roach        return $this->viewResponse('modules/clippings/download', [
25016ecfcafSGreg Roach            'download_filenames' => $download_filenames,
251d45701ccSGreg Roach            'module'             => $this->name(),
252d45701ccSGreg Roach            'title'              => $title,
253d45701ccSGreg Roach            'tree'               => $tree,
25416ecfcafSGreg Roach            'zip_available'      => extension_loaded('zip'),
255d45701ccSGreg Roach        ]);
256d45701ccSGreg Roach    }
257d45701ccSGreg Roach
258d45701ccSGreg Roach    /**
2596ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
26076692c8bSGreg Roach     *
2616ccdf4f0SGreg Roach     * @return ResponseInterface
26276692c8bSGreg Roach     */
263f95e0480SGreg Roach    public function postDownloadAction(ServerRequestInterface $request): ResponseInterface
264c1010edaSGreg Roach    {
265b55cbc6bSGreg Roach        $tree = Validator::attributes($request)->tree();
2664ea62551SGreg Roach
26716ecfcafSGreg Roach        if (Auth::isAdmin()) {
26816ecfcafSGreg Roach            $privacy_options = ['none', 'gedadmin', 'user', 'visitor'];
26916ecfcafSGreg Roach        } elseif (Auth::isManager($tree)) {
27016ecfcafSGreg Roach            $privacy_options = ['gedadmin', 'user', 'visitor'];
27116ecfcafSGreg Roach        } elseif (Auth::isMember($tree)) {
27216ecfcafSGreg Roach            $privacy_options = ['user', 'visitor'];
27316ecfcafSGreg Roach        } else {
27416ecfcafSGreg Roach            $privacy_options = ['visitor'];
27516ecfcafSGreg Roach        }
276a04bb9a2SGreg Roach
27716ecfcafSGreg Roach        $filename     = Validator::parsedBody($request)->string('filename');
27816ecfcafSGreg Roach        $format       = Validator::parsedBody($request)->isInArray(['gedcom', 'zip', 'zipmedia', 'gedzip'])->string('format');
27916ecfcafSGreg Roach        $privacy      = Validator::parsedBody($request)->isInArray($privacy_options)->string('privacy');
280b55cbc6bSGreg Roach        $encoding     = Validator::parsedBody($request)->isInArray([UTF8::NAME, UTF16BE::NAME, ANSEL::NAME, ASCII::NAME, Windows1252::NAME])->string('encoding');
281b55cbc6bSGreg Roach        $line_endings = Validator::parsedBody($request)->isInArray(['CRLF', 'LF'])->string('line_endings');
282b46c87bdSGreg Roach
283d8809d62SGreg Roach        $cart = Session::get('cart');
284d8809d62SGreg Roach        $cart = is_array($cart) ? $cart : [];
2858c2e8227SGreg Roach
286aa6f03bbSGreg Roach        $xrefs = array_keys($cart[$tree->name()] ?? []);
287c8846facSGreg Roach        $xrefs = array_map('strval', $xrefs); // PHP converts numeric keys to integers.
2885a78cd34SGreg Roach
28969c05a6eSGreg Roach        $records = new Collection();
2905a78cd34SGreg Roach
2911c6adce8SGreg Roach        switch ($privacy) {
2925a78cd34SGreg Roach            case 'gedadmin':
2935a78cd34SGreg Roach                $access_level = Auth::PRIV_NONE;
2945a78cd34SGreg Roach                break;
2955a78cd34SGreg Roach            case 'user':
2965a78cd34SGreg Roach                $access_level = Auth::PRIV_USER;
2975a78cd34SGreg Roach                break;
2985a78cd34SGreg Roach            case 'visitor':
2995a78cd34SGreg Roach                $access_level = Auth::PRIV_PRIVATE;
3005a78cd34SGreg Roach                break;
3015a78cd34SGreg Roach            case 'none':
3025a78cd34SGreg Roach            default:
3035a78cd34SGreg Roach                $access_level = Auth::PRIV_HIDE;
3045a78cd34SGreg Roach                break;
3055a78cd34SGreg Roach        }
3065a78cd34SGreg Roach
3075a78cd34SGreg Roach        foreach ($xrefs as $xref) {
3086b9cb339SGreg Roach            $object = Registry::gedcomRecordFactory()->make($xref, $tree);
3095a78cd34SGreg Roach            // The object may have been deleted since we added it to the cart....
310bed27cedSGreg Roach            if ($object instanceof GedcomRecord) {
3115a78cd34SGreg Roach                $record = $object->privatizeGedcom($access_level);
3125a78cd34SGreg Roach                // Remove links to objects that aren't in the cart
3138d0ebef0SGreg Roach                preg_match_all('/\n1 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(\n[2-9].*)*/', $record, $matches, PREG_SET_ORDER);
3145a78cd34SGreg Roach                foreach ($matches as $match) {
315bf80ec58SGreg Roach                    if (!in_array($match[1], $xrefs, true)) {
3165a78cd34SGreg Roach                        $record = str_replace($match[0], '', $record);
3175a78cd34SGreg Roach                    }
3185a78cd34SGreg Roach                }
3198d0ebef0SGreg Roach                preg_match_all('/\n2 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(\n[3-9].*)*/', $record, $matches, PREG_SET_ORDER);
3205a78cd34SGreg Roach                foreach ($matches as $match) {
321bf80ec58SGreg Roach                    if (!in_array($match[1], $xrefs, true)) {
3225a78cd34SGreg Roach                        $record = str_replace($match[0], '', $record);
3235a78cd34SGreg Roach                    }
3245a78cd34SGreg Roach                }
3258d0ebef0SGreg Roach                preg_match_all('/\n3 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(\n[4-9].*)*/', $record, $matches, PREG_SET_ORDER);
3265a78cd34SGreg Roach                foreach ($matches as $match) {
327bf80ec58SGreg Roach                    if (!in_array($match[1], $xrefs, true)) {
3285a78cd34SGreg Roach                        $record = str_replace($match[0], '', $record);
3295a78cd34SGreg Roach                    }
3305a78cd34SGreg Roach                }
3315a78cd34SGreg Roach
332f566cb16SGreg Roach                $records->add($record);
3331c6adce8SGreg Roach            }
3341c6adce8SGreg Roach        }
335f566cb16SGreg Roach
3361c6adce8SGreg Roach        // We have already applied privacy filtering, so do not do it again.
33716ecfcafSGreg Roach        return $this->gedcom_export_service->downloadResponse($tree, false, $encoding, 'none', $line_endings, $filename, $format, $records);
3388c2e8227SGreg Roach    }
3398c2e8227SGreg Roach
3408c2e8227SGreg Roach    /**
34157ab2231SGreg Roach     * @param ServerRequestInterface $request
34276692c8bSGreg Roach     *
3436ccdf4f0SGreg Roach     * @return ResponseInterface
3448c2e8227SGreg Roach     */
34557ab2231SGreg Roach    public function getEmptyAction(ServerRequestInterface $request): ResponseInterface
346c1010edaSGreg Roach    {
347b55cbc6bSGreg Roach        $tree = Validator::attributes($request)->tree();
3484ea62551SGreg Roach
349d8809d62SGreg Roach        $cart = Session::get('cart');
350d8809d62SGreg Roach        $cart = is_array($cart) ? $cart : [];
351d8809d62SGreg Roach
352aa6f03bbSGreg Roach        $cart[$tree->name()] = [];
3535a78cd34SGreg Roach        Session::put('cart', $cart);
3548c2e8227SGreg Roach
355c1010edaSGreg Roach        $url = route('module', [
35626684e68SGreg Roach            'module' => $this->name(),
357c1010edaSGreg Roach            'action' => 'Show',
358d72b284aSGreg Roach            'tree'   => $tree->name(),
359c1010edaSGreg Roach        ]);
3605a78cd34SGreg Roach
3616ccdf4f0SGreg Roach        return redirect($url);
3625a78cd34SGreg Roach    }
3635a78cd34SGreg Roach
3645a78cd34SGreg Roach    /**
3656ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
3665a78cd34SGreg Roach     *
3676ccdf4f0SGreg Roach     * @return ResponseInterface
3685a78cd34SGreg Roach     */
36957ab2231SGreg Roach    public function postRemoveAction(ServerRequestInterface $request): ResponseInterface
370c1010edaSGreg Roach    {
371b55cbc6bSGreg Roach        $tree = Validator::attributes($request)->tree();
372748dbe15SGreg Roach        $xref = Validator::queryParams($request)->isXref()->string('xref');
373d8809d62SGreg Roach        $cart = Session::get('cart');
374d8809d62SGreg Roach        $cart = is_array($cart) ? $cart : [];
375d8809d62SGreg Roach
376aa6f03bbSGreg Roach        unset($cart[$tree->name()][$xref]);
3775a78cd34SGreg Roach        Session::put('cart', $cart);
3785a78cd34SGreg Roach
379c1010edaSGreg Roach        $url = route('module', [
38026684e68SGreg Roach            'module' => $this->name(),
381c1010edaSGreg Roach            'action' => 'Show',
382d72b284aSGreg Roach            'tree'   => $tree->name(),
383c1010edaSGreg Roach        ]);
3845a78cd34SGreg Roach
3856ccdf4f0SGreg Roach        return redirect($url);
3865a78cd34SGreg Roach    }
3875a78cd34SGreg Roach
3885a78cd34SGreg Roach    /**
38957ab2231SGreg Roach     * @param ServerRequestInterface $request
3905a78cd34SGreg Roach     *
3916ccdf4f0SGreg Roach     * @return ResponseInterface
3925a78cd34SGreg Roach     */
39357ab2231SGreg Roach    public function getShowAction(ServerRequestInterface $request): ResponseInterface
394c1010edaSGreg Roach    {
395b55cbc6bSGreg Roach        $tree = Validator::attributes($request)->tree();
39657ab2231SGreg Roach
3975a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/show', [
39877b78a63SRichard Cissée            'module'  => $this->name(),
3995a78cd34SGreg Roach            'records' => $this->allRecordsInCart($tree),
4005a78cd34SGreg Roach            'title'   => I18N::translate('Family tree clippings cart'),
4015a78cd34SGreg Roach            'tree'    => $tree,
4025a78cd34SGreg Roach        ]);
4035a78cd34SGreg Roach    }
4045a78cd34SGreg Roach
4055a78cd34SGreg Roach    /**
406d45701ccSGreg Roach     * Get all the records in the cart.
407d45701ccSGreg Roach     *
408d45701ccSGreg Roach     * @param Tree $tree
409d45701ccSGreg Roach     *
41009482a55SGreg Roach     * @return array<GedcomRecord>
411d45701ccSGreg Roach     */
412d45701ccSGreg Roach    private function allRecordsInCart(Tree $tree): array
413d45701ccSGreg Roach    {
414d8809d62SGreg Roach        $cart = Session::get('cart');
415d8809d62SGreg Roach        $cart = is_array($cart) ? $cart : [];
416d45701ccSGreg Roach
417d45701ccSGreg Roach        $xrefs = array_keys($cart[$tree->name()] ?? []);
418d45701ccSGreg Roach        $xrefs = array_map('strval', $xrefs); // PHP converts numeric keys to integers.
419d45701ccSGreg Roach
420d45701ccSGreg Roach        // Fetch all the records in the cart.
421d45701ccSGreg Roach        $records = array_map(static function (string $xref) use ($tree): ?GedcomRecord {
422d45701ccSGreg Roach            return Registry::gedcomRecordFactory()->make($xref, $tree);
423d45701ccSGreg Roach        }, $xrefs);
424d45701ccSGreg Roach
425d45701ccSGreg Roach        // Some records may have been deleted after they were added to the cart.
426d45701ccSGreg Roach        $records = array_filter($records);
427d45701ccSGreg Roach
428d45701ccSGreg Roach        // Group and sort.
429d45701ccSGreg Roach        uasort($records, static function (GedcomRecord $x, GedcomRecord $y): int {
430d45701ccSGreg Roach            return $x->tag() <=> $y->tag() ?: GedcomRecord::nameComparator()($x, $y);
431d45701ccSGreg Roach        });
432d45701ccSGreg Roach
433d45701ccSGreg Roach        return $records;
434d45701ccSGreg Roach    }
435d45701ccSGreg Roach
436d45701ccSGreg Roach    /**
4376ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
4385a78cd34SGreg Roach     *
4396ccdf4f0SGreg Roach     * @return ResponseInterface
4405a78cd34SGreg Roach     */
44157ab2231SGreg Roach    public function getAddFamilyAction(ServerRequestInterface $request): ResponseInterface
442c1010edaSGreg Roach    {
443b55cbc6bSGreg Roach        $tree   = Validator::attributes($request)->tree();
444748dbe15SGreg Roach        $xref   = Validator::queryParams($request)->isXref()->string('xref');
4456b9cb339SGreg Roach        $family = Registry::familyFactory()->make($xref, $tree);
446d45701ccSGreg Roach        $family = Auth::checkFamilyAccess($family);
447d45701ccSGreg Roach        $name   = $family->fullName();
4485a78cd34SGreg Roach
449d45701ccSGreg Roach        $options = [
45003f99a78SGreg Roach            self::ADD_RECORD_ONLY => $name,
451bbb76c12SGreg Roach            /* I18N: %s is a family (husband + wife) */
45203f99a78SGreg Roach            self::ADD_CHILDREN    => I18N::translate('%s and their children', $name),
453bbb76c12SGreg Roach            /* I18N: %s is a family (husband + wife) */
45403f99a78SGreg Roach            self::ADD_DESCENDANTS => I18N::translate('%s and their descendants', $name),
4555a78cd34SGreg Roach        ];
456d45701ccSGreg Roach
457d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
458d45701ccSGreg Roach
459d45701ccSGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
460d45701ccSGreg Roach            'options' => $options,
461d45701ccSGreg Roach            'record'  => $family,
462d45701ccSGreg Roach            'title'   => $title,
463d45701ccSGreg Roach            'tree'    => $tree,
464d45701ccSGreg Roach        ]);
4655a78cd34SGreg Roach    }
4665a78cd34SGreg Roach
4675a78cd34SGreg Roach    /**
4686ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
4695a78cd34SGreg Roach     *
4706ccdf4f0SGreg Roach     * @return ResponseInterface
4715a78cd34SGreg Roach     */
47257ab2231SGreg Roach    public function postAddFamilyAction(ServerRequestInterface $request): ResponseInterface
473c1010edaSGreg Roach    {
474b55cbc6bSGreg Roach        $tree   = Validator::attributes($request)->tree();
475748dbe15SGreg Roach        $xref   = Validator::parsedBody($request)->isXref()->string('xref');
476748dbe15SGreg Roach        $option = Validator::parsedBody($request)->string('option');
4775a78cd34SGreg Roach
4786b9cb339SGreg Roach        $family = Registry::familyFactory()->make($xref, $tree);
479d45701ccSGreg Roach        $family = Auth::checkFamilyAccess($family);
4805a78cd34SGreg Roach
4815a78cd34SGreg Roach        switch ($option) {
48203f99a78SGreg Roach            case self::ADD_RECORD_ONLY:
4835a78cd34SGreg Roach                $this->addFamilyToCart($family);
4845a78cd34SGreg Roach                break;
4855a78cd34SGreg Roach
48603f99a78SGreg Roach            case self::ADD_CHILDREN:
4875a78cd34SGreg Roach                $this->addFamilyAndChildrenToCart($family);
4885a78cd34SGreg Roach                break;
4895a78cd34SGreg Roach
49003f99a78SGreg Roach            case self::ADD_DESCENDANTS:
4915a78cd34SGreg Roach                $this->addFamilyAndDescendantsToCart($family);
4925a78cd34SGreg Roach                break;
4935a78cd34SGreg Roach        }
4945a78cd34SGreg Roach
4956ccdf4f0SGreg Roach        return redirect($family->url());
4965a78cd34SGreg Roach    }
4975a78cd34SGreg Roach
4985a78cd34SGreg Roach
4995a78cd34SGreg Roach    /**
5005a78cd34SGreg Roach     * @param Family $family
50118d7a90dSGreg Roach     *
50218d7a90dSGreg Roach     * @return void
5035a78cd34SGreg Roach     */
504d45701ccSGreg Roach    protected function addFamilyAndChildrenToCart(Family $family): void
505c1010edaSGreg Roach    {
506d45701ccSGreg Roach        $this->addFamilyToCart($family);
5075a78cd34SGreg Roach
50839ca88baSGreg Roach        foreach ($family->children() as $child) {
509d45701ccSGreg Roach            $this->addIndividualToCart($child);
5105a78cd34SGreg Roach        }
5115a78cd34SGreg Roach    }
5125a78cd34SGreg Roach
5135a78cd34SGreg Roach    /**
5145a78cd34SGreg Roach     * @param Family $family
51518d7a90dSGreg Roach     *
51618d7a90dSGreg Roach     * @return void
5175a78cd34SGreg Roach     */
518d45701ccSGreg Roach    protected function addFamilyAndDescendantsToCart(Family $family): void
519c1010edaSGreg Roach    {
520d45701ccSGreg Roach        $this->addFamilyAndChildrenToCart($family);
5215a78cd34SGreg Roach
52239ca88baSGreg Roach        foreach ($family->children() as $child) {
52339ca88baSGreg Roach            foreach ($child->spouseFamilies() as $child_family) {
5245a78cd34SGreg Roach                $this->addFamilyAndDescendantsToCart($child_family);
5255a78cd34SGreg Roach            }
5265a78cd34SGreg Roach        }
5275a78cd34SGreg Roach    }
5285a78cd34SGreg Roach
5295a78cd34SGreg Roach    /**
5306ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
5315a78cd34SGreg Roach     *
5326ccdf4f0SGreg Roach     * @return ResponseInterface
5335a78cd34SGreg Roach     */
53457ab2231SGreg Roach    public function getAddIndividualAction(ServerRequestInterface $request): ResponseInterface
535c1010edaSGreg Roach    {
536b55cbc6bSGreg Roach        $tree       = Validator::attributes($request)->tree();
537748dbe15SGreg Roach        $xref       = Validator::queryParams($request)->isXref()->string('xref');
5386b9cb339SGreg Roach        $individual = Registry::individualFactory()->make($xref, $tree);
539d45701ccSGreg Roach        $individual = Auth::checkIndividualAccess($individual);
540d45701ccSGreg Roach        $name       = $individual->fullName();
5415a78cd34SGreg Roach
54239ca88baSGreg Roach        if ($individual->sex() === 'F') {
543d45701ccSGreg Roach            $options = [
54403f99a78SGreg Roach                self::ADD_RECORD_ONLY       => $name,
54503f99a78SGreg Roach                self::ADD_PARENT_FAMILIES   => I18N::translate('%s, her parents and siblings', $name),
54603f99a78SGreg Roach                self::ADD_SPOUSE_FAMILIES   => I18N::translate('%s, her spouses and children', $name),
54703f99a78SGreg Roach                self::ADD_ANCESTORS         => I18N::translate('%s and her ancestors', $name),
54803f99a78SGreg Roach                self::ADD_ANCESTOR_FAMILIES => I18N::translate('%s, her ancestors and their families', $name),
54903f99a78SGreg Roach                self::ADD_DESCENDANTS       => I18N::translate('%s, her spouses and descendants', $name),
5505a78cd34SGreg Roach            ];
551d45701ccSGreg Roach        } else {
552d45701ccSGreg Roach            $options = [
55303f99a78SGreg Roach                self::ADD_RECORD_ONLY       => $name,
55403f99a78SGreg Roach                self::ADD_PARENT_FAMILIES   => I18N::translate('%s, his parents and siblings', $name),
55503f99a78SGreg Roach                self::ADD_SPOUSE_FAMILIES   => I18N::translate('%s, his spouses and children', $name),
55603f99a78SGreg Roach                self::ADD_ANCESTORS         => I18N::translate('%s and his ancestors', $name),
55703f99a78SGreg Roach                self::ADD_ANCESTOR_FAMILIES => I18N::translate('%s, his ancestors and their families', $name),
55803f99a78SGreg Roach                self::ADD_DESCENDANTS       => I18N::translate('%s, his spouses and descendants', $name),
5595a78cd34SGreg Roach            ];
5605a78cd34SGreg Roach        }
5615a78cd34SGreg Roach
562d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
563d45701ccSGreg Roach
564d45701ccSGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
565d45701ccSGreg Roach            'options' => $options,
566d45701ccSGreg Roach            'record'  => $individual,
567d45701ccSGreg Roach            'title'   => $title,
568d45701ccSGreg Roach            'tree'    => $tree,
569d45701ccSGreg Roach        ]);
570d45701ccSGreg Roach    }
571d45701ccSGreg Roach
5725a78cd34SGreg Roach    /**
5736ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
5745a78cd34SGreg Roach     *
5756ccdf4f0SGreg Roach     * @return ResponseInterface
5765a78cd34SGreg Roach     */
57757ab2231SGreg Roach    public function postAddIndividualAction(ServerRequestInterface $request): ResponseInterface
578c1010edaSGreg Roach    {
579b55cbc6bSGreg Roach        $tree   = Validator::attributes($request)->tree();
580748dbe15SGreg Roach        $xref   = Validator::parsedBody($request)->isXref()->string('xref');
581748dbe15SGreg Roach        $option = Validator::parsedBody($request)->string('option');
5825a78cd34SGreg Roach
5836b9cb339SGreg Roach        $individual = Registry::individualFactory()->make($xref, $tree);
584d45701ccSGreg Roach        $individual = Auth::checkIndividualAccess($individual);
5855a78cd34SGreg Roach
5865a78cd34SGreg Roach        switch ($option) {
58703f99a78SGreg Roach            case self::ADD_RECORD_ONLY:
588d45701ccSGreg Roach                $this->addIndividualToCart($individual);
5895a78cd34SGreg Roach                break;
5905a78cd34SGreg Roach
59103f99a78SGreg Roach            case self::ADD_PARENT_FAMILIES:
59239ca88baSGreg Roach                foreach ($individual->childFamilies() as $family) {
5935a78cd34SGreg Roach                    $this->addFamilyAndChildrenToCart($family);
5945a78cd34SGreg Roach                }
5955a78cd34SGreg Roach                break;
5965a78cd34SGreg Roach
59703f99a78SGreg Roach            case self::ADD_SPOUSE_FAMILIES:
59839ca88baSGreg Roach                foreach ($individual->spouseFamilies() as $family) {
5995a78cd34SGreg Roach                    $this->addFamilyAndChildrenToCart($family);
6005a78cd34SGreg Roach                }
6015a78cd34SGreg Roach                break;
6025a78cd34SGreg Roach
60303f99a78SGreg Roach            case self::ADD_ANCESTORS:
6045a78cd34SGreg Roach                $this->addAncestorsToCart($individual);
6055a78cd34SGreg Roach                break;
6065a78cd34SGreg Roach
60703f99a78SGreg Roach            case self::ADD_ANCESTOR_FAMILIES:
6085a78cd34SGreg Roach                $this->addAncestorFamiliesToCart($individual);
6095a78cd34SGreg Roach                break;
6105a78cd34SGreg Roach
61103f99a78SGreg Roach            case self::ADD_DESCENDANTS:
61239ca88baSGreg Roach                foreach ($individual->spouseFamilies() as $family) {
6135a78cd34SGreg Roach                    $this->addFamilyAndDescendantsToCart($family);
6145a78cd34SGreg Roach                }
6155a78cd34SGreg Roach                break;
6165a78cd34SGreg Roach        }
6175a78cd34SGreg Roach
6186ccdf4f0SGreg Roach        return redirect($individual->url());
6195a78cd34SGreg Roach    }
6205a78cd34SGreg Roach
6215a78cd34SGreg Roach    /**
6225a78cd34SGreg Roach     * @param Individual $individual
62318d7a90dSGreg Roach     *
62418d7a90dSGreg Roach     * @return void
6255a78cd34SGreg Roach     */
626d45701ccSGreg Roach    protected function addAncestorsToCart(Individual $individual): void
627c1010edaSGreg Roach    {
628d45701ccSGreg Roach        $this->addIndividualToCart($individual);
6295a78cd34SGreg Roach
63039ca88baSGreg Roach        foreach ($individual->childFamilies() as $family) {
631d45701ccSGreg Roach            $this->addFamilyToCart($family);
6328df4c68dSGreg Roach
63339ca88baSGreg Roach            foreach ($family->spouses() as $parent) {
6345a78cd34SGreg Roach                $this->addAncestorsToCart($parent);
6355a78cd34SGreg Roach            }
6365a78cd34SGreg Roach        }
6375a78cd34SGreg Roach    }
6385a78cd34SGreg Roach
6395a78cd34SGreg Roach    /**
6405a78cd34SGreg Roach     * @param Individual $individual
64118d7a90dSGreg Roach     *
64218d7a90dSGreg Roach     * @return void
6435a78cd34SGreg Roach     */
644d45701ccSGreg Roach    protected function addAncestorFamiliesToCart(Individual $individual): void
645c1010edaSGreg Roach    {
64639ca88baSGreg Roach        foreach ($individual->childFamilies() as $family) {
6475a78cd34SGreg Roach            $this->addFamilyAndChildrenToCart($family);
6488df4c68dSGreg Roach
64939ca88baSGreg Roach            foreach ($family->spouses() as $parent) {
650cad6d3f3SGreg Roach                $this->addAncestorFamiliesToCart($parent);
6515a78cd34SGreg Roach            }
6525a78cd34SGreg Roach        }
6535a78cd34SGreg Roach    }
6545a78cd34SGreg Roach
6555a78cd34SGreg Roach    /**
6566ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
6575a78cd34SGreg Roach     *
6586ccdf4f0SGreg Roach     * @return ResponseInterface
6595a78cd34SGreg Roach     */
660e8ded2caSGreg Roach    public function getAddLocationAction(ServerRequestInterface $request): ResponseInterface
661e8ded2caSGreg Roach    {
662b55cbc6bSGreg Roach        $tree     = Validator::attributes($request)->tree();
663748dbe15SGreg Roach        $xref     = Validator::queryParams($request)->isXref()->string('xref');
664e8ded2caSGreg Roach        $location = Registry::locationFactory()->make($xref, $tree);
665e8ded2caSGreg Roach        $location = Auth::checkLocationAccess($location);
666e8ded2caSGreg Roach        $name     = $location->fullName();
667e8ded2caSGreg Roach
668e8ded2caSGreg Roach        $options = [
66903f99a78SGreg Roach            self::ADD_RECORD_ONLY => $name,
670e8ded2caSGreg Roach        ];
671e8ded2caSGreg Roach
672e8ded2caSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
673e8ded2caSGreg Roach
674e8ded2caSGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
675e8ded2caSGreg Roach            'options' => $options,
676e8ded2caSGreg Roach            'record'  => $location,
677e8ded2caSGreg Roach            'title'   => $title,
678e8ded2caSGreg Roach            'tree'    => $tree,
679e8ded2caSGreg Roach        ]);
680e8ded2caSGreg Roach    }
681e8ded2caSGreg Roach
682e8ded2caSGreg Roach    /**
683e8ded2caSGreg Roach     * @param ServerRequestInterface $request
684e8ded2caSGreg Roach     *
685e8ded2caSGreg Roach     * @return ResponseInterface
686e8ded2caSGreg Roach     */
687e8ded2caSGreg Roach    public function postAddLocationAction(ServerRequestInterface $request): ResponseInterface
688e8ded2caSGreg Roach    {
689b55cbc6bSGreg Roach        $tree     = Validator::attributes($request)->tree();
690748dbe15SGreg Roach        $xref     = Validator::queryParams($request)->isXref()->string('xref');
691e8ded2caSGreg Roach        $location = Registry::locationFactory()->make($xref, $tree);
692e8ded2caSGreg Roach        $location = Auth::checkLocationAccess($location);
693e8ded2caSGreg Roach
694e8ded2caSGreg Roach        $this->addLocationToCart($location);
695e8ded2caSGreg Roach
696e8ded2caSGreg Roach        return redirect($location->url());
697e8ded2caSGreg Roach    }
698e8ded2caSGreg Roach
699e8ded2caSGreg Roach    /**
700e8ded2caSGreg Roach     * @param ServerRequestInterface $request
701e8ded2caSGreg Roach     *
702e8ded2caSGreg Roach     * @return ResponseInterface
703e8ded2caSGreg Roach     */
70457ab2231SGreg Roach    public function getAddMediaAction(ServerRequestInterface $request): ResponseInterface
705c1010edaSGreg Roach    {
706b55cbc6bSGreg Roach        $tree  = Validator::attributes($request)->tree();
707748dbe15SGreg Roach        $xref  = Validator::queryParams($request)->isXref()->string('xref');
7086b9cb339SGreg Roach        $media = Registry::mediaFactory()->make($xref, $tree);
709d45701ccSGreg Roach        $media = Auth::checkMediaAccess($media);
710d45701ccSGreg Roach        $name  = $media->fullName();
7115a78cd34SGreg Roach
712d45701ccSGreg Roach        $options = [
71303f99a78SGreg Roach            self::ADD_RECORD_ONLY => $name,
714d45701ccSGreg Roach        ];
7155a78cd34SGreg Roach
716d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
7175a78cd34SGreg Roach
7185a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
7195a78cd34SGreg Roach            'options' => $options,
7205a78cd34SGreg Roach            'record'  => $media,
7215a78cd34SGreg Roach            'title'   => $title,
7225a78cd34SGreg Roach            'tree'    => $tree,
7235a78cd34SGreg Roach        ]);
7245a78cd34SGreg Roach    }
7255a78cd34SGreg Roach
7265a78cd34SGreg Roach    /**
7276ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
7285a78cd34SGreg Roach     *
7296ccdf4f0SGreg Roach     * @return ResponseInterface
7305a78cd34SGreg Roach     */
73157ab2231SGreg Roach    public function postAddMediaAction(ServerRequestInterface $request): ResponseInterface
732c1010edaSGreg Roach    {
733b55cbc6bSGreg Roach        $tree  = Validator::attributes($request)->tree();
734748dbe15SGreg Roach        $xref  = Validator::queryParams($request)->isXref()->string('xref');
7356b9cb339SGreg Roach        $media = Registry::mediaFactory()->make($xref, $tree);
736d45701ccSGreg Roach        $media = Auth::checkMediaAccess($media);
7375a78cd34SGreg Roach
738d45701ccSGreg Roach        $this->addMediaToCart($media);
7395a78cd34SGreg Roach
7406ccdf4f0SGreg Roach        return redirect($media->url());
7415a78cd34SGreg Roach    }
7425a78cd34SGreg Roach
7435a78cd34SGreg Roach    /**
7446ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
7455a78cd34SGreg Roach     *
7466ccdf4f0SGreg Roach     * @return ResponseInterface
7475a78cd34SGreg Roach     */
74857ab2231SGreg Roach    public function getAddNoteAction(ServerRequestInterface $request): ResponseInterface
749c1010edaSGreg Roach    {
750b55cbc6bSGreg Roach        $tree = Validator::attributes($request)->tree();
751748dbe15SGreg Roach        $xref = Validator::queryParams($request)->isXref()->string('xref');
7526b9cb339SGreg Roach        $note = Registry::noteFactory()->make($xref, $tree);
753d45701ccSGreg Roach        $note = Auth::checkNoteAccess($note);
754d45701ccSGreg Roach        $name = $note->fullName();
7555a78cd34SGreg Roach
756d45701ccSGreg Roach        $options = [
75703f99a78SGreg Roach            self::ADD_RECORD_ONLY => $name,
758d45701ccSGreg Roach        ];
7595a78cd34SGreg Roach
760d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
7615a78cd34SGreg Roach
7625a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
7635a78cd34SGreg Roach            'options' => $options,
7645a78cd34SGreg Roach            'record'  => $note,
7655a78cd34SGreg Roach            'title'   => $title,
7665a78cd34SGreg Roach            'tree'    => $tree,
7675a78cd34SGreg Roach        ]);
7685a78cd34SGreg Roach    }
7695a78cd34SGreg Roach
7705a78cd34SGreg Roach    /**
7716ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
7725a78cd34SGreg Roach     *
7736ccdf4f0SGreg Roach     * @return ResponseInterface
7745a78cd34SGreg Roach     */
77557ab2231SGreg Roach    public function postAddNoteAction(ServerRequestInterface $request): ResponseInterface
776c1010edaSGreg Roach    {
777b55cbc6bSGreg Roach        $tree = Validator::attributes($request)->tree();
778748dbe15SGreg Roach        $xref = Validator::queryParams($request)->isXref()->string('xref');
7796b9cb339SGreg Roach        $note = Registry::noteFactory()->make($xref, $tree);
780d45701ccSGreg Roach        $note = Auth::checkNoteAccess($note);
7815a78cd34SGreg Roach
782d45701ccSGreg Roach        $this->addNoteToCart($note);
7835a78cd34SGreg Roach
7846ccdf4f0SGreg Roach        return redirect($note->url());
7855a78cd34SGreg Roach    }
7865a78cd34SGreg Roach
7875a78cd34SGreg Roach    /**
7886ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
7895a78cd34SGreg Roach     *
7906ccdf4f0SGreg Roach     * @return ResponseInterface
7915a78cd34SGreg Roach     */
79257ab2231SGreg Roach    public function getAddRepositoryAction(ServerRequestInterface $request): ResponseInterface
793c1010edaSGreg Roach    {
794b55cbc6bSGreg Roach        $tree       = Validator::attributes($request)->tree();
795748dbe15SGreg Roach        $xref       = Validator::queryParams($request)->isXref()->string('xref');
7966b9cb339SGreg Roach        $repository = Registry::repositoryFactory()->make($xref, $tree);
797d45701ccSGreg Roach        $repository = Auth::checkRepositoryAccess($repository);
798d45701ccSGreg Roach        $name       = $repository->fullName();
7995a78cd34SGreg Roach
800d45701ccSGreg Roach        $options = [
80103f99a78SGreg Roach            self::ADD_RECORD_ONLY => $name,
802d45701ccSGreg Roach        ];
8035a78cd34SGreg Roach
804d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
8055a78cd34SGreg Roach
8065a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
8075a78cd34SGreg Roach            'options' => $options,
8085a78cd34SGreg Roach            'record'  => $repository,
8095a78cd34SGreg Roach            'title'   => $title,
8105a78cd34SGreg Roach            'tree'    => $tree,
8115a78cd34SGreg Roach        ]);
8125a78cd34SGreg Roach    }
8135a78cd34SGreg Roach
8145a78cd34SGreg Roach    /**
8156ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
8165a78cd34SGreg Roach     *
8176ccdf4f0SGreg Roach     * @return ResponseInterface
8185a78cd34SGreg Roach     */
81957ab2231SGreg Roach    public function postAddRepositoryAction(ServerRequestInterface $request): ResponseInterface
820c1010edaSGreg Roach    {
821b55cbc6bSGreg Roach        $tree       = Validator::attributes($request)->tree();
822748dbe15SGreg Roach        $xref       = Validator::queryParams($request)->isXref()->string('xref');
8236b9cb339SGreg Roach        $repository = Registry::repositoryFactory()->make($xref, $tree);
824d45701ccSGreg Roach        $repository = Auth::checkRepositoryAccess($repository);
8255a78cd34SGreg Roach
826d45701ccSGreg Roach        $this->addRepositoryToCart($repository);
827d45701ccSGreg Roach
8284991f205SGreg Roach        foreach ($this->linked_record_service->linkedSources($repository) as $source) {
829d45701ccSGreg Roach            $this->addSourceToCart($source);
8305a78cd34SGreg Roach        }
8315a78cd34SGreg Roach
8326ccdf4f0SGreg Roach        return redirect($repository->url());
8335a78cd34SGreg Roach    }
8345a78cd34SGreg Roach
8355a78cd34SGreg Roach    /**
8366ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
8375a78cd34SGreg Roach     *
8386ccdf4f0SGreg Roach     * @return ResponseInterface
8395a78cd34SGreg Roach     */
84057ab2231SGreg Roach    public function getAddSourceAction(ServerRequestInterface $request): ResponseInterface
841c1010edaSGreg Roach    {
842b55cbc6bSGreg Roach        $tree   = Validator::attributes($request)->tree();
843748dbe15SGreg Roach        $xref   = Validator::queryParams($request)->isXref()->string('xref');
8446b9cb339SGreg Roach        $source = Registry::sourceFactory()->make($xref, $tree);
845d45701ccSGreg Roach        $source = Auth::checkSourceAccess($source);
846d45701ccSGreg Roach        $name   = $source->fullName();
8475a78cd34SGreg Roach
848d45701ccSGreg Roach        $options = [
84903f99a78SGreg Roach            self::ADD_RECORD_ONLY        => $name,
85003f99a78SGreg Roach            self::ADD_LINKED_INDIVIDUALS => I18N::translate('%s and the individuals that reference it.', $name),
851d45701ccSGreg Roach        ];
8525a78cd34SGreg Roach
853d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
8545a78cd34SGreg Roach
8555a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
8565a78cd34SGreg Roach            'options' => $options,
8575a78cd34SGreg Roach            'record'  => $source,
8585a78cd34SGreg Roach            'title'   => $title,
8595a78cd34SGreg Roach            'tree'    => $tree,
8605a78cd34SGreg Roach        ]);
8615a78cd34SGreg Roach    }
8625a78cd34SGreg Roach
8635a78cd34SGreg Roach    /**
8646ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
8655a78cd34SGreg Roach     *
8666ccdf4f0SGreg Roach     * @return ResponseInterface
8675a78cd34SGreg Roach     */
86857ab2231SGreg Roach    public function postAddSourceAction(ServerRequestInterface $request): ResponseInterface
869c1010edaSGreg Roach    {
870b55cbc6bSGreg Roach        $tree   = Validator::attributes($request)->tree();
871748dbe15SGreg Roach        $xref   = Validator::parsedBody($request)->isXref()->string('xref');
872748dbe15SGreg Roach        $option = Validator::parsedBody($request)->string('option');
8735a78cd34SGreg Roach
8746b9cb339SGreg Roach        $source = Registry::sourceFactory()->make($xref, $tree);
875d45701ccSGreg Roach        $source = Auth::checkSourceAccess($source);
8765a78cd34SGreg Roach
877d45701ccSGreg Roach        $this->addSourceToCart($source);
8785a78cd34SGreg Roach
87903f99a78SGreg Roach        if ($option === self::ADD_LINKED_INDIVIDUALS) {
8804991f205SGreg Roach            foreach ($this->linked_record_service->linkedIndividuals($source) as $individual) {
881d45701ccSGreg Roach                $this->addIndividualToCart($individual);
8825a78cd34SGreg Roach            }
8834991f205SGreg Roach            foreach ($this->linked_record_service->linkedFamilies($source) as $family) {
884d45701ccSGreg Roach                $this->addFamilyToCart($family);
8855a78cd34SGreg Roach            }
8865a78cd34SGreg Roach        }
8875a78cd34SGreg Roach
8886ccdf4f0SGreg Roach        return redirect($source->url());
8895a78cd34SGreg Roach    }
8905a78cd34SGreg Roach
8915a78cd34SGreg Roach    /**
892d45701ccSGreg Roach     * @param ServerRequestInterface $request
8935a78cd34SGreg Roach     *
894d45701ccSGreg Roach     * @return ResponseInterface
8955a78cd34SGreg Roach     */
896d45701ccSGreg Roach    public function getAddSubmitterAction(ServerRequestInterface $request): ResponseInterface
897c1010edaSGreg Roach    {
898b55cbc6bSGreg Roach        $tree      = Validator::attributes($request)->tree();
899748dbe15SGreg Roach        $xref      = Validator::queryParams($request)->isXref()->string('xref');
900d45701ccSGreg Roach        $submitter = Registry::submitterFactory()->make($xref, $tree);
901d45701ccSGreg Roach        $submitter = Auth::checkSubmitterAccess($submitter);
902d45701ccSGreg Roach        $name      = $submitter->fullName();
9035a78cd34SGreg Roach
904d45701ccSGreg Roach        $options = [
90503f99a78SGreg Roach            self::ADD_RECORD_ONLY => $name,
906d45701ccSGreg Roach        ];
9075a78cd34SGreg Roach
908d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
9095a78cd34SGreg Roach
910d45701ccSGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
911d45701ccSGreg Roach            'options' => $options,
912d45701ccSGreg Roach            'record'  => $submitter,
913d45701ccSGreg Roach            'title'   => $title,
914d45701ccSGreg Roach            'tree'    => $tree,
915d45701ccSGreg Roach        ]);
9165a78cd34SGreg Roach    }
9175a78cd34SGreg Roach
9185a78cd34SGreg Roach    /**
919d45701ccSGreg Roach     * @param ServerRequestInterface $request
9205a78cd34SGreg Roach     *
921d45701ccSGreg Roach     * @return ResponseInterface
9225a78cd34SGreg Roach     */
923d45701ccSGreg Roach    public function postAddSubmitterAction(ServerRequestInterface $request): ResponseInterface
924d45701ccSGreg Roach    {
925b55cbc6bSGreg Roach        $tree      = Validator::attributes($request)->tree();
926748dbe15SGreg Roach        $xref      = Validator::queryParams($request)->isXref()->string('xref');
927d45701ccSGreg Roach        $submitter = Registry::submitterFactory()->make($xref, $tree);
928d45701ccSGreg Roach        $submitter = Auth::checkSubmitterAccess($submitter);
929d45701ccSGreg Roach
930d45701ccSGreg Roach        $this->addSubmitterToCart($submitter);
931d45701ccSGreg Roach
932d45701ccSGreg Roach        return redirect($submitter->url());
933d45701ccSGreg Roach    }
934d45701ccSGreg Roach
935d45701ccSGreg Roach    /**
936d45701ccSGreg Roach     * @param Family $family
937d45701ccSGreg Roach     */
938d45701ccSGreg Roach    protected function addFamilyToCart(Family $family): void
939c1010edaSGreg Roach    {
940d8809d62SGreg Roach        $cart = Session::get('cart');
941d8809d62SGreg Roach        $cart = is_array($cart) ? $cart : [];
942d8809d62SGreg Roach
943d45701ccSGreg Roach        $tree = $family->tree()->name();
944d45701ccSGreg Roach        $xref = $family->xref();
9455a78cd34SGreg Roach
946d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
947d45701ccSGreg Roach            $cart[$tree][$xref] = true;
9485a78cd34SGreg Roach
949d45701ccSGreg Roach            Session::put('cart', $cart);
9505a78cd34SGreg Roach
951d45701ccSGreg Roach            foreach ($family->spouses() as $spouse) {
952d45701ccSGreg Roach                $this->addIndividualToCart($spouse);
9535a78cd34SGreg Roach            }
9545a78cd34SGreg Roach
955e8ded2caSGreg Roach            $this->addLocationLinksToCart($family);
956d45701ccSGreg Roach            $this->addMediaLinksToCart($family);
957d45701ccSGreg Roach            $this->addNoteLinksToCart($family);
958d45701ccSGreg Roach            $this->addSourceLinksToCart($family);
959d45701ccSGreg Roach            $this->addSubmitterLinksToCart($family);
960d45701ccSGreg Roach        }
961d45701ccSGreg Roach    }
962d45701ccSGreg Roach
963d45701ccSGreg Roach    /**
964d45701ccSGreg Roach     * @param Individual $individual
965d45701ccSGreg Roach     */
966d45701ccSGreg Roach    protected function addIndividualToCart(Individual $individual): void
967d45701ccSGreg Roach    {
968d8809d62SGreg Roach        $cart = Session::get('cart');
969d8809d62SGreg Roach        $cart = is_array($cart) ? $cart : [];
970d8809d62SGreg Roach
971d45701ccSGreg Roach        $tree = $individual->tree()->name();
972d45701ccSGreg Roach        $xref = $individual->xref();
973d45701ccSGreg Roach
974d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
975d45701ccSGreg Roach            $cart[$tree][$xref] = true;
976d45701ccSGreg Roach
977d45701ccSGreg Roach            Session::put('cart', $cart);
978d45701ccSGreg Roach
979e8ded2caSGreg Roach            $this->addLocationLinksToCart($individual);
980d45701ccSGreg Roach            $this->addMediaLinksToCart($individual);
981d45701ccSGreg Roach            $this->addNoteLinksToCart($individual);
982d45701ccSGreg Roach            $this->addSourceLinksToCart($individual);
983d45701ccSGreg Roach        }
984d45701ccSGreg Roach    }
985d45701ccSGreg Roach
986d45701ccSGreg Roach    /**
987e8ded2caSGreg Roach     * @param Location $location
988e8ded2caSGreg Roach     */
989e8ded2caSGreg Roach    protected function addLocationToCart(Location $location): void
990e8ded2caSGreg Roach    {
991d8809d62SGreg Roach        $cart = Session::get('cart');
992d8809d62SGreg Roach        $cart = is_array($cart) ? $cart : [];
993d8809d62SGreg Roach
994e8ded2caSGreg Roach        $tree = $location->tree()->name();
995e8ded2caSGreg Roach        $xref = $location->xref();
996e8ded2caSGreg Roach
997e8ded2caSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
998e8ded2caSGreg Roach            $cart[$tree][$xref] = true;
999e8ded2caSGreg Roach
1000e8ded2caSGreg Roach            Session::put('cart', $cart);
1001e8ded2caSGreg Roach
1002e8ded2caSGreg Roach            $this->addLocationLinksToCart($location);
1003e8ded2caSGreg Roach            $this->addMediaLinksToCart($location);
1004e8ded2caSGreg Roach            $this->addNoteLinksToCart($location);
1005e8ded2caSGreg Roach            $this->addSourceLinksToCart($location);
1006e8ded2caSGreg Roach        }
1007e8ded2caSGreg Roach    }
1008e8ded2caSGreg Roach
1009e8ded2caSGreg Roach    /**
1010e8ded2caSGreg Roach     * @param GedcomRecord $record
1011e8ded2caSGreg Roach     */
1012e8ded2caSGreg Roach    protected function addLocationLinksToCart(GedcomRecord $record): void
1013e8ded2caSGreg Roach    {
1014e8ded2caSGreg Roach        preg_match_all('/\n\d _LOC @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1015e8ded2caSGreg Roach
1016e8ded2caSGreg Roach        foreach ($matches[1] as $xref) {
1017e8ded2caSGreg Roach            $location = Registry::locationFactory()->make($xref, $record->tree());
1018e8ded2caSGreg Roach
1019e8ded2caSGreg Roach            if ($location instanceof Location && $location->canShow()) {
1020e8ded2caSGreg Roach                $this->addLocationToCart($location);
1021e8ded2caSGreg Roach            }
1022e8ded2caSGreg Roach        }
1023e8ded2caSGreg Roach    }
1024e8ded2caSGreg Roach
1025e8ded2caSGreg Roach    /**
1026d45701ccSGreg Roach     * @param Media $media
1027d45701ccSGreg Roach     */
1028d45701ccSGreg Roach    protected function addMediaToCart(Media $media): void
1029d45701ccSGreg Roach    {
1030d8809d62SGreg Roach        $cart = Session::get('cart');
1031d8809d62SGreg Roach        $cart = is_array($cart) ? $cart : [];
1032d8809d62SGreg Roach
1033d45701ccSGreg Roach        $tree = $media->tree()->name();
1034d45701ccSGreg Roach        $xref = $media->xref();
1035d45701ccSGreg Roach
1036d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1037d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1038d45701ccSGreg Roach
1039d45701ccSGreg Roach            Session::put('cart', $cart);
1040d45701ccSGreg Roach
1041d45701ccSGreg Roach            $this->addNoteLinksToCart($media);
1042d45701ccSGreg Roach        }
1043d45701ccSGreg Roach    }
1044d45701ccSGreg Roach
1045d45701ccSGreg Roach    /**
1046d45701ccSGreg Roach     * @param GedcomRecord $record
1047d45701ccSGreg Roach     */
1048d45701ccSGreg Roach    protected function addMediaLinksToCart(GedcomRecord $record): void
1049d45701ccSGreg Roach    {
1050d45701ccSGreg Roach        preg_match_all('/\n\d OBJE @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1051d45701ccSGreg Roach
1052d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1053d45701ccSGreg Roach            $media = Registry::mediaFactory()->make($xref, $record->tree());
1054d45701ccSGreg Roach
1055d45701ccSGreg Roach            if ($media instanceof Media && $media->canShow()) {
1056d45701ccSGreg Roach                $this->addMediaToCart($media);
1057d45701ccSGreg Roach            }
1058d45701ccSGreg Roach        }
1059d45701ccSGreg Roach    }
1060d45701ccSGreg Roach
1061d45701ccSGreg Roach    /**
1062d45701ccSGreg Roach     * @param Note $note
1063d45701ccSGreg Roach     */
1064d45701ccSGreg Roach    protected function addNoteToCart(Note $note): void
1065d45701ccSGreg Roach    {
1066d8809d62SGreg Roach        $cart = Session::get('cart');
1067d8809d62SGreg Roach        $cart = is_array($cart) ? $cart : [];
1068d8809d62SGreg Roach
1069d45701ccSGreg Roach        $tree = $note->tree()->name();
1070d45701ccSGreg Roach        $xref = $note->xref();
1071d45701ccSGreg Roach
1072d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1073d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1074d45701ccSGreg Roach
10755a78cd34SGreg Roach            Session::put('cart', $cart);
10765a78cd34SGreg Roach        }
1077d45701ccSGreg Roach    }
10785a78cd34SGreg Roach
10795a78cd34SGreg Roach    /**
1080d45701ccSGreg Roach     * @param GedcomRecord $record
10815a78cd34SGreg Roach     */
1082d45701ccSGreg Roach    protected function addNoteLinksToCart(GedcomRecord $record): void
1083d45701ccSGreg Roach    {
1084d45701ccSGreg Roach        preg_match_all('/\n\d NOTE @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1085d45701ccSGreg Roach
1086d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1087d45701ccSGreg Roach            $note = Registry::noteFactory()->make($xref, $record->tree());
1088d45701ccSGreg Roach
1089d45701ccSGreg Roach            if ($note instanceof Note && $note->canShow()) {
1090d45701ccSGreg Roach                $this->addNoteToCart($note);
1091d45701ccSGreg Roach            }
1092d45701ccSGreg Roach        }
1093d45701ccSGreg Roach    }
1094d45701ccSGreg Roach
1095d45701ccSGreg Roach    /**
1096d45701ccSGreg Roach     * @param Source $source
1097d45701ccSGreg Roach     */
1098d45701ccSGreg Roach    protected function addSourceToCart(Source $source): void
1099c1010edaSGreg Roach    {
1100d8809d62SGreg Roach        $cart = Session::get('cart');
1101d8809d62SGreg Roach        $cart = is_array($cart) ? $cart : [];
1102d8809d62SGreg Roach
1103d45701ccSGreg Roach        $tree = $source->tree()->name();
1104d45701ccSGreg Roach        $xref = $source->xref();
11055a78cd34SGreg Roach
1106d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1107d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1108d45701ccSGreg Roach
1109d45701ccSGreg Roach            Session::put('cart', $cart);
1110d45701ccSGreg Roach
1111d45701ccSGreg Roach            $this->addNoteLinksToCart($source);
1112d45701ccSGreg Roach            $this->addRepositoryLinksToCart($source);
1113d45701ccSGreg Roach        }
1114d45701ccSGreg Roach    }
1115d45701ccSGreg Roach
1116d45701ccSGreg Roach    /**
1117d45701ccSGreg Roach     * @param GedcomRecord $record
1118d45701ccSGreg Roach     */
1119d45701ccSGreg Roach    protected function addSourceLinksToCart(GedcomRecord $record): void
1120d45701ccSGreg Roach    {
1121d45701ccSGreg Roach        preg_match_all('/\n\d SOUR @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1122d45701ccSGreg Roach
1123d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1124d45701ccSGreg Roach            $source = Registry::sourceFactory()->make($xref, $record->tree());
1125d45701ccSGreg Roach
1126d45701ccSGreg Roach            if ($source instanceof Source && $source->canShow()) {
1127d45701ccSGreg Roach                $this->addSourceToCart($source);
1128d45701ccSGreg Roach            }
1129d45701ccSGreg Roach        }
1130d45701ccSGreg Roach    }
1131d45701ccSGreg Roach
1132d45701ccSGreg Roach    /**
1133d45701ccSGreg Roach     * @param Repository $repository
1134d45701ccSGreg Roach     */
1135d45701ccSGreg Roach    protected function addRepositoryToCart(Repository $repository): void
1136d45701ccSGreg Roach    {
1137d8809d62SGreg Roach        $cart = Session::get('cart');
1138d8809d62SGreg Roach        $cart = is_array($cart) ? $cart : [];
1139d8809d62SGreg Roach
1140d45701ccSGreg Roach        $tree = $repository->tree()->name();
1141d45701ccSGreg Roach        $xref = $repository->xref();
1142d45701ccSGreg Roach
1143d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1144d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1145d45701ccSGreg Roach
1146d45701ccSGreg Roach            Session::put('cart', $cart);
1147d45701ccSGreg Roach
1148d45701ccSGreg Roach            $this->addNoteLinksToCart($repository);
1149d45701ccSGreg Roach        }
1150d45701ccSGreg Roach    }
1151d45701ccSGreg Roach
1152d45701ccSGreg Roach    /**
1153d45701ccSGreg Roach     * @param GedcomRecord $record
1154d45701ccSGreg Roach     */
1155d45701ccSGreg Roach    protected function addRepositoryLinksToCart(GedcomRecord $record): void
1156d45701ccSGreg Roach    {
1157d45701ccSGreg Roach        preg_match_all('/\n\d REPO @(' . Gedcom::REGEX_XREF . '@)/', $record->gedcom(), $matches);
1158d45701ccSGreg Roach
1159d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1160d45701ccSGreg Roach            $repository = Registry::repositoryFactory()->make($xref, $record->tree());
1161d45701ccSGreg Roach
1162d45701ccSGreg Roach            if ($repository instanceof Repository && $repository->canShow()) {
1163d45701ccSGreg Roach                $this->addRepositoryToCart($repository);
1164d45701ccSGreg Roach            }
1165d45701ccSGreg Roach        }
1166d45701ccSGreg Roach    }
1167d45701ccSGreg Roach
1168d45701ccSGreg Roach    /**
1169d45701ccSGreg Roach     * @param Submitter $submitter
1170d45701ccSGreg Roach     */
1171d45701ccSGreg Roach    protected function addSubmitterToCart(Submitter $submitter): void
1172d45701ccSGreg Roach    {
1173d8809d62SGreg Roach        $cart = Session::get('cart');
1174d8809d62SGreg Roach        $cart = is_array($cart) ? $cart : [];
1175d45701ccSGreg Roach        $tree = $submitter->tree()->name();
1176d45701ccSGreg Roach        $xref = $submitter->xref();
1177d45701ccSGreg Roach
1178d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1179d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1180d45701ccSGreg Roach
1181d45701ccSGreg Roach            Session::put('cart', $cart);
1182d45701ccSGreg Roach
1183d45701ccSGreg Roach            $this->addNoteLinksToCart($submitter);
1184d45701ccSGreg Roach        }
1185d45701ccSGreg Roach    }
1186d45701ccSGreg Roach
1187d45701ccSGreg Roach    /**
1188d45701ccSGreg Roach     * @param GedcomRecord $record
1189d45701ccSGreg Roach     */
1190d45701ccSGreg Roach    protected function addSubmitterLinksToCart(GedcomRecord $record): void
1191d45701ccSGreg Roach    {
1192d45701ccSGreg Roach        preg_match_all('/\n\d SUBM @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1193d45701ccSGreg Roach
1194d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1195d45701ccSGreg Roach            $submitter = Registry::submitterFactory()->make($xref, $record->tree());
1196d45701ccSGreg Roach
1197d45701ccSGreg Roach            if ($submitter instanceof Submitter && $submitter->canShow()) {
1198d45701ccSGreg Roach                $this->addSubmitterToCart($submitter);
1199d45701ccSGreg Roach            }
1200d45701ccSGreg Roach        }
12015a78cd34SGreg Roach    }
12028c2e8227SGreg Roach}
1203