xref: /webtrees/app/Module/ClippingsCartModule.php (revision f32d77e63d71326973bfe0496adeec694ca90ecf)
18c2e8227SGreg Roach<?php
23976b470SGreg Roach
38c2e8227SGreg Roach/**
48c2e8227SGreg Roach * webtrees: online genealogy
589f7189bSGreg Roach * Copyright (C) 2021 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
22de2aa325SGreg Roachuse Aura\Router\Route;
230e62c4b8SGreg Roachuse Fisharebest\Webtrees\Auth;
240e62c4b8SGreg Roachuse Fisharebest\Webtrees\Family;
255a78cd34SGreg Roachuse Fisharebest\Webtrees\Gedcom;
260e62c4b8SGreg Roachuse Fisharebest\Webtrees\GedcomRecord;
27f95e0480SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\FamilyPage;
28f95e0480SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\IndividualPage;
29e8ded2caSGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\LocationPage;
30f95e0480SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\MediaPage;
31f95e0480SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\NotePage;
32f95e0480SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\RepositoryPage;
33f95e0480SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\SourcePage;
34d45701ccSGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\SubmitterPage;
350e62c4b8SGreg Roachuse Fisharebest\Webtrees\I18N;
360e62c4b8SGreg Roachuse Fisharebest\Webtrees\Individual;
37e8ded2caSGreg Roachuse Fisharebest\Webtrees\Location;
385a78cd34SGreg Roachuse Fisharebest\Webtrees\Media;
390e62c4b8SGreg Roachuse Fisharebest\Webtrees\Menu;
405a78cd34SGreg Roachuse Fisharebest\Webtrees\Note;
41d45701ccSGreg Roachuse Fisharebest\Webtrees\Registry;
425a78cd34SGreg Roachuse Fisharebest\Webtrees\Repository;
4369c05a6eSGreg Roachuse Fisharebest\Webtrees\Services\GedcomExportService;
44e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Services\UserService;
450e62c4b8SGreg Roachuse Fisharebest\Webtrees\Session;
465a78cd34SGreg Roachuse Fisharebest\Webtrees\Source;
47d45701ccSGreg Roachuse Fisharebest\Webtrees\Submitter;
48aee13b6dSGreg Roachuse Fisharebest\Webtrees\Tree;
4969c05a6eSGreg Roachuse Illuminate\Support\Collection;
505a78cd34SGreg Roachuse League\Flysystem\Filesystem;
51*f32d77e6SGreg Roachuse League\Flysystem\ZipArchive\FilesystemZipArchiveProvider;
525a78cd34SGreg Roachuse League\Flysystem\ZipArchive\ZipArchiveAdapter;
53bed27cedSGreg Roachuse Psr\Http\Message\ResponseFactoryInterface;
546ccdf4f0SGreg Roachuse Psr\Http\Message\ResponseInterface;
556ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface;
566ccdf4f0SGreg Roachuse Psr\Http\Message\StreamFactoryInterface;
5769c05a6eSGreg Roachuse RuntimeException;
583976b470SGreg Roach
59eb235819SGreg Roachuse function app;
60bf80ec58SGreg Roachuse function array_filter;
61bf80ec58SGreg Roachuse function array_keys;
62bf80ec58SGreg Roachuse function array_map;
63fa695506SGreg Roachuse function array_search;
645229eadeSGreg Roachuse function assert;
6569c05a6eSGreg Roachuse function fopen;
66bf80ec58SGreg Roachuse function in_array;
67ddeb3354SGreg Roachuse function is_string;
68bf80ec58SGreg Roachuse function preg_match_all;
69bf80ec58SGreg Roachuse function redirect;
7069c05a6eSGreg Roachuse function rewind;
71bf80ec58SGreg Roachuse function route;
72e5a6b4d4SGreg Roachuse function str_replace;
73fa695506SGreg Roachuse function stream_get_meta_data;
74fa695506SGreg Roachuse function tmpfile;
75fa695506SGreg Roachuse function uasort;
76d45701ccSGreg Roachuse function view;
77fa695506SGreg Roach
78fa695506SGreg Roachuse const PREG_SET_ORDER;
798c2e8227SGreg Roach
808c2e8227SGreg Roach/**
818c2e8227SGreg Roach * Class ClippingsCartModule
828c2e8227SGreg Roach */
8337eb8894SGreg Roachclass ClippingsCartModule extends AbstractModule implements ModuleMenuInterface
84c1010edaSGreg Roach{
8549a243cbSGreg Roach    use ModuleMenuTrait;
8649a243cbSGreg Roach
875a78cd34SGreg Roach    // Routes that have a record which can be added to the clipboard
8816d6367aSGreg Roach    private const ROUTES_WITH_RECORDS = [
89f95e0480SGreg Roach        'Family'     => FamilyPage::class,
90f95e0480SGreg Roach        'Individual' => IndividualPage::class,
91f95e0480SGreg Roach        'Media'      => MediaPage::class,
92e8ded2caSGreg Roach        'Location'   => LocationPage::class,
93f95e0480SGreg Roach        'Note'       => NotePage::class,
94f95e0480SGreg Roach        'Repository' => RepositoryPage::class,
95f95e0480SGreg Roach        'Source'     => SourcePage::class,
96d45701ccSGreg Roach        'Submitter'  => SubmitterPage::class,
97c1010edaSGreg Roach    ];
985a78cd34SGreg Roach
9949a243cbSGreg Roach    /** @var int The default access level for this module.  It can be changed in the control panel. */
10049a243cbSGreg Roach    protected $access_level = Auth::PRIV_USER;
10149a243cbSGreg Roach
10269c05a6eSGreg Roach    /** @var GedcomExportService */
10369c05a6eSGreg Roach    private $gedcom_export_service;
10469c05a6eSGreg Roach
10569c05a6eSGreg Roach    /** @var UserService */
106e5a6b4d4SGreg Roach    private $user_service;
107e5a6b4d4SGreg Roach
108e5a6b4d4SGreg Roach    /**
109e5a6b4d4SGreg Roach     * ClippingsCartModule constructor.
110e5a6b4d4SGreg Roach     *
11169c05a6eSGreg Roach     * @param GedcomExportService $gedcom_export_service
112e5a6b4d4SGreg Roach     * @param UserService         $user_service
113e5a6b4d4SGreg Roach     */
11469c05a6eSGreg Roach    public function __construct(GedcomExportService $gedcom_export_service, UserService $user_service)
115e5a6b4d4SGreg Roach    {
11669c05a6eSGreg Roach        $this->gedcom_export_service = $gedcom_export_service;
117e5a6b4d4SGreg Roach        $this->user_service          = $user_service;
118e5a6b4d4SGreg Roach    }
119e5a6b4d4SGreg Roach
120e5a6b4d4SGreg Roach    /**
121961ec755SGreg Roach     * A sentence describing what this module does.
122961ec755SGreg Roach     *
123961ec755SGreg Roach     * @return string
124961ec755SGreg Roach     */
12549a243cbSGreg Roach    public function description(): string
126c1010edaSGreg Roach    {
127bbb76c12SGreg Roach        /* I18N: Description of the “Clippings cart” module */
128bbb76c12SGreg Roach        return I18N::translate('Select records from your family tree and save them as a GEDCOM file.');
1298c2e8227SGreg Roach    }
1308c2e8227SGreg Roach
1310ee13198SGreg Roach    /**
13249a243cbSGreg Roach     * The default position for this menu.  It can be changed in the control panel.
1330ee13198SGreg Roach     *
1340ee13198SGreg Roach     * @return int
1350ee13198SGreg Roach     */
1368f53f488SRico Sonntag    public function defaultMenuOrder(): int
137c1010edaSGreg Roach    {
138353b36abSGreg Roach        return 6;
1398c2e8227SGreg Roach    }
1408c2e8227SGreg Roach
1410ee13198SGreg Roach    /**
1420ee13198SGreg Roach     * A menu, to be added to the main application menu.
1430ee13198SGreg Roach     *
144aee13b6dSGreg Roach     * @param Tree $tree
145aee13b6dSGreg Roach     *
1460ee13198SGreg Roach     * @return Menu|null
1470ee13198SGreg Roach     */
14846295629SGreg Roach    public function getMenu(Tree $tree): ?Menu
149c1010edaSGreg Roach    {
150eb235819SGreg Roach        /** @var ServerRequestInterface $request */
1516ccdf4f0SGreg Roach        $request = app(ServerRequestInterface::class);
1528c2e8227SGreg Roach
153f7ab47b1SGreg Roach        $route = $request->getAttribute('route');
154de2aa325SGreg Roach        assert($route instanceof Route);
1555a78cd34SGreg Roach
156d45701ccSGreg Roach        $cart  = Session::get('cart', []);
157d45701ccSGreg Roach        $count = count($cart[$tree->name()] ?? []);
158d45701ccSGreg Roach        $badge = view('components/badge', ['count' => $count]);
159d45701ccSGreg Roach
1605a78cd34SGreg Roach        $submenus = [
161d45701ccSGreg Roach            new Menu($this->title() . ' ' . $badge, route('module', [
16226684e68SGreg Roach                'module' => $this->name(),
163c1010edaSGreg Roach                'action' => 'Show',
164d72b284aSGreg Roach                'tree'   => $tree->name(),
165c1010edaSGreg Roach            ]), 'menu-clippings-cart', ['rel' => 'nofollow']),
1665a78cd34SGreg Roach        ];
1675a78cd34SGreg Roach
1682b0d92b4SGreg Roach        $action = array_search($route->name, self::ROUTES_WITH_RECORDS, true);
169f95e0480SGreg Roach        if ($action !== false) {
1702b0d92b4SGreg Roach            $xref = $route->attributes['xref'];
171ddeb3354SGreg Roach            assert(is_string($xref));
172ddeb3354SGreg Roach
173c1010edaSGreg Roach            $add_route = route('module', [
17426684e68SGreg Roach                'module' => $this->name(),
175f95e0480SGreg Roach                'action' => 'Add' . $action,
176c1010edaSGreg Roach                'xref'   => $xref,
177d72b284aSGreg Roach                'tree'   => $tree->name(),
178c1010edaSGreg Roach            ]);
1795a78cd34SGreg Roach
18025b2dde3SGreg Roach            $submenus[] = new Menu(I18N::translate('Add to the clippings cart'), $add_route, 'menu-clippings-add', ['rel' => 'nofollow']);
1818c2e8227SGreg Roach        }
182cbc1590aSGreg Roach
1835a78cd34SGreg Roach        if (!$this->isCartEmpty($tree)) {
184c1010edaSGreg Roach            $submenus[] = new Menu(I18N::translate('Empty the clippings cart'), route('module', [
18526684e68SGreg Roach                'module' => $this->name(),
186c1010edaSGreg Roach                'action' => 'Empty',
187d72b284aSGreg Roach                'tree'   => $tree->name(),
188c1010edaSGreg Roach            ]), 'menu-clippings-empty', ['rel' => 'nofollow']);
189f95e0480SGreg Roach
190c1010edaSGreg Roach            $submenus[] = new Menu(I18N::translate('Download'), route('module', [
19126684e68SGreg Roach                'module' => $this->name(),
192c1010edaSGreg Roach                'action' => 'DownloadForm',
193d72b284aSGreg Roach                'tree'   => $tree->name(),
194c1010edaSGreg Roach            ]), 'menu-clippings-download', ['rel' => 'nofollow']);
1955a78cd34SGreg Roach        }
1965a78cd34SGreg Roach
19749a243cbSGreg Roach        return new Menu($this->title(), '#', 'menu-clippings', ['rel' => 'nofollow'], $submenus);
1988c2e8227SGreg Roach    }
1998c2e8227SGreg Roach
20076692c8bSGreg Roach    /**
201d45701ccSGreg Roach     * How should this module be identified in the control panel, etc.?
202d45701ccSGreg Roach     *
203d45701ccSGreg Roach     * @return string
204d45701ccSGreg Roach     */
205d45701ccSGreg Roach    public function title(): string
206d45701ccSGreg Roach    {
207d45701ccSGreg Roach        /* I18N: Name of a module */
208d45701ccSGreg Roach        return I18N::translate('Clippings cart');
209d45701ccSGreg Roach    }
210d45701ccSGreg Roach
211d45701ccSGreg Roach    /**
212d45701ccSGreg Roach     * @param Tree $tree
213d45701ccSGreg Roach     *
214d45701ccSGreg Roach     * @return bool
215d45701ccSGreg Roach     */
216d45701ccSGreg Roach    private function isCartEmpty(Tree $tree): bool
217d45701ccSGreg Roach    {
218d45701ccSGreg Roach        $cart     = Session::get('cart', []);
219d45701ccSGreg Roach        $contents = $cart[$tree->name()] ?? [];
220d45701ccSGreg Roach
221d45701ccSGreg Roach        return $contents === [];
222d45701ccSGreg Roach    }
223d45701ccSGreg Roach
224d45701ccSGreg Roach    /**
225d45701ccSGreg Roach     * @param ServerRequestInterface $request
226d45701ccSGreg Roach     *
227d45701ccSGreg Roach     * @return ResponseInterface
228d45701ccSGreg Roach     */
229d45701ccSGreg Roach    public function getDownloadFormAction(ServerRequestInterface $request): ResponseInterface
230d45701ccSGreg Roach    {
231d45701ccSGreg Roach        $tree = $request->getAttribute('tree');
232d45701ccSGreg Roach        assert($tree instanceof Tree);
233d45701ccSGreg Roach
234d45701ccSGreg Roach        $user  = $request->getAttribute('user');
235d45701ccSGreg Roach        $title = I18N::translate('Family tree clippings cart') . ' — ' . I18N::translate('Download');
236d45701ccSGreg Roach
237d45701ccSGreg Roach        return $this->viewResponse('modules/clippings/download', [
238d45701ccSGreg Roach            'is_manager' => Auth::isManager($tree, $user),
239d45701ccSGreg Roach            'is_member'  => Auth::isMember($tree, $user),
240d45701ccSGreg Roach            'module'     => $this->name(),
241d45701ccSGreg Roach            'title'      => $title,
242d45701ccSGreg Roach            'tree'       => $tree,
243d45701ccSGreg Roach        ]);
244d45701ccSGreg Roach    }
245d45701ccSGreg Roach
246d45701ccSGreg Roach    /**
2476ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
24876692c8bSGreg Roach     *
2496ccdf4f0SGreg Roach     * @return ResponseInterface
25076692c8bSGreg Roach     */
251f95e0480SGreg Roach    public function postDownloadAction(ServerRequestInterface $request): ResponseInterface
252c1010edaSGreg Roach    {
25357ab2231SGreg Roach        $tree = $request->getAttribute('tree');
2544ea62551SGreg Roach        assert($tree instanceof Tree);
2554ea62551SGreg Roach
2566b9cb339SGreg Roach        $data_filesystem = Registry::filesystem()->data();
257a04bb9a2SGreg Roach
258b46c87bdSGreg Roach        $params = (array) $request->getParsedBody();
259b46c87bdSGreg Roach
260d45701ccSGreg Roach        $privatize_export = $params['privatize_export'] ?? 'none';
261e2ed7c79SGreg Roach
262e2ed7c79SGreg Roach        if ($privatize_export === 'none' && !Auth::isManager($tree)) {
263e2ed7c79SGreg Roach            $privatize_export = 'member';
264e2ed7c79SGreg Roach        }
265e2ed7c79SGreg Roach
266e2ed7c79SGreg Roach        if ($privatize_export === 'gedadmin' && !Auth::isManager($tree)) {
267e2ed7c79SGreg Roach            $privatize_export = 'member';
268e2ed7c79SGreg Roach        }
269e2ed7c79SGreg Roach
270e2ed7c79SGreg Roach        if ($privatize_export === 'user' && !Auth::isMember($tree)) {
271e2ed7c79SGreg Roach            $privatize_export = 'visitor';
272e2ed7c79SGreg Roach        }
273e2ed7c79SGreg Roach
274b46c87bdSGreg Roach        $convert = (bool) ($params['convert'] ?? false);
2758c2e8227SGreg Roach
27613abd6f3SGreg Roach        $cart = Session::get('cart', []);
2778c2e8227SGreg Roach
278aa6f03bbSGreg Roach        $xrefs = array_keys($cart[$tree->name()] ?? []);
279c8846facSGreg Roach        $xrefs = array_map('strval', $xrefs); // PHP converts numeric keys to integers.
2805a78cd34SGreg Roach
2815a78cd34SGreg Roach        // Create a new/empty .ZIP file
282a00baf47SGreg Roach        $temp_zip_file  = stream_get_meta_data(tmpfile())['uri'];
283*f32d77e6SGreg Roach        $zip_provider   = new FilesystemZipArchiveProvider($temp_zip_file, 0755);
284*f32d77e6SGreg Roach        $zip_adapter    = new ZipArchiveAdapter($zip_provider);
2857f996f6eSGreg Roach        $zip_filesystem = new Filesystem($zip_adapter);
2865a78cd34SGreg Roach
287fa695506SGreg Roach        $media_filesystem = $tree->mediaFilesystem($data_filesystem);
28861bf91b2SGreg Roach
2895a78cd34SGreg Roach        // Media file prefix
2905a78cd34SGreg Roach        $path = $tree->getPreference('MEDIA_DIRECTORY');
2915a78cd34SGreg Roach
29269c05a6eSGreg Roach        $encoding = $convert ? 'ANSI' : 'UTF-8';
29369c05a6eSGreg Roach
29469c05a6eSGreg Roach        $records = new Collection();
2955a78cd34SGreg Roach
2965a78cd34SGreg Roach        switch ($privatize_export) {
2975a78cd34SGreg Roach            case 'gedadmin':
2985a78cd34SGreg Roach                $access_level = Auth::PRIV_NONE;
2995a78cd34SGreg Roach                break;
3005a78cd34SGreg Roach            case 'user':
3015a78cd34SGreg Roach                $access_level = Auth::PRIV_USER;
3025a78cd34SGreg Roach                break;
3035a78cd34SGreg Roach            case 'visitor':
3045a78cd34SGreg Roach                $access_level = Auth::PRIV_PRIVATE;
3055a78cd34SGreg Roach                break;
3065a78cd34SGreg Roach            case 'none':
3075a78cd34SGreg Roach            default:
3085a78cd34SGreg Roach                $access_level = Auth::PRIV_HIDE;
3095a78cd34SGreg Roach                break;
3105a78cd34SGreg Roach        }
3115a78cd34SGreg Roach
3125a78cd34SGreg Roach        foreach ($xrefs as $xref) {
3136b9cb339SGreg Roach            $object = Registry::gedcomRecordFactory()->make($xref, $tree);
3145a78cd34SGreg Roach            // The object may have been deleted since we added it to the cart....
315bed27cedSGreg Roach            if ($object instanceof GedcomRecord) {
3165a78cd34SGreg Roach                $record = $object->privatizeGedcom($access_level);
3175a78cd34SGreg Roach                // Remove links to objects that aren't in the cart
3188d0ebef0SGreg Roach                preg_match_all('/\n1 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(\n[2-9].*)*/', $record, $matches, PREG_SET_ORDER);
3195a78cd34SGreg Roach                foreach ($matches as $match) {
320bf80ec58SGreg Roach                    if (!in_array($match[1], $xrefs, true)) {
3215a78cd34SGreg Roach                        $record = str_replace($match[0], '', $record);
3225a78cd34SGreg Roach                    }
3235a78cd34SGreg Roach                }
3248d0ebef0SGreg Roach                preg_match_all('/\n2 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(\n[3-9].*)*/', $record, $matches, PREG_SET_ORDER);
3255a78cd34SGreg Roach                foreach ($matches as $match) {
326bf80ec58SGreg Roach                    if (!in_array($match[1], $xrefs, true)) {
3275a78cd34SGreg Roach                        $record = str_replace($match[0], '', $record);
3285a78cd34SGreg Roach                    }
3295a78cd34SGreg Roach                }
3308d0ebef0SGreg Roach                preg_match_all('/\n3 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(\n[4-9].*)*/', $record, $matches, PREG_SET_ORDER);
3315a78cd34SGreg Roach                foreach ($matches as $match) {
332bf80ec58SGreg Roach                    if (!in_array($match[1], $xrefs, true)) {
3335a78cd34SGreg Roach                        $record = str_replace($match[0], '', $record);
3345a78cd34SGreg Roach                    }
3355a78cd34SGreg Roach                }
3365a78cd34SGreg Roach
33755167344SGreg Roach                if ($object instanceof Individual || $object instanceof Family) {
33869c05a6eSGreg Roach                    $records->add($record . "\n1 SOUR @WEBTREES@\n2 PAGE " . $object->url());
33955167344SGreg Roach                } elseif ($object instanceof Source) {
34069c05a6eSGreg Roach                    $records->add($record . "\n1 NOTE " . $object->url());
34155167344SGreg Roach                } elseif ($object instanceof Media) {
34255167344SGreg Roach                    // Add the media files to the archive
3435a78cd34SGreg Roach                    foreach ($object->mediaFiles() as $media_file) {
344fa695506SGreg Roach                        $from = $media_file->filename();
345fa695506SGreg Roach                        $to   = $path . $media_file->filename();
346f7cf8a15SGreg Roach                        if (!$media_file->isExternal() && $media_filesystem->fileExists($from) && !$zip_filesystem->fileExists($to)) {
347fa695506SGreg Roach                            $zip_filesystem->writeStream($to, $media_filesystem->readStream($from));
3485a78cd34SGreg Roach                        }
3495a78cd34SGreg Roach                    }
35069c05a6eSGreg Roach                    $records->add($record);
35155167344SGreg Roach                } else {
35269c05a6eSGreg Roach                    $records->add($record);
3538c2e8227SGreg Roach                }
3548c2e8227SGreg Roach            }
3558c2e8227SGreg Roach        }
3568c2e8227SGreg Roach
3579b93b7c3SGreg Roach        $base_url = $request->getAttribute('base_url');
3589b93b7c3SGreg Roach
3595a78cd34SGreg Roach        // Create a source, to indicate the source of the data.
36069c05a6eSGreg Roach        $record = "0 @WEBTREES@ SOUR\n1 TITL " . $base_url;
361e5a6b4d4SGreg Roach        $author = $this->user_service->find((int) $tree->getPreference('CONTACT_USER_ID'));
3625a78cd34SGreg Roach        if ($author !== null) {
36369c05a6eSGreg Roach            $record .= "\n1 AUTH " . $author->realName();
3645a78cd34SGreg Roach        }
36569c05a6eSGreg Roach        $records->add($record);
3665a78cd34SGreg Roach
36769c05a6eSGreg Roach        $stream = fopen('php://temp', 'wb+');
3685a78cd34SGreg Roach
36969c05a6eSGreg Roach        if ($stream === false) {
37069c05a6eSGreg Roach            throw new RuntimeException('Failed to create temporary stream');
3718c2e8227SGreg Roach        }
372cbc1590aSGreg Roach
37369c05a6eSGreg Roach        // We have already applied privacy filtering, so do not do it again.
37469c05a6eSGreg Roach        $this->gedcom_export_service->export($tree, $stream, false, $encoding, Auth::PRIV_HIDE, $path, $records);
37569c05a6eSGreg Roach        rewind($stream);
37669c05a6eSGreg Roach
3775a78cd34SGreg Roach        // Finally add the GEDCOM file to the .ZIP file.
37869c05a6eSGreg Roach        $zip_filesystem->writeStream('clippings.ged', $stream);
3795a78cd34SGreg Roach
3806ccdf4f0SGreg Roach        // Use a stream, so that we do not have to load the entire file into memory.
3816ccdf4f0SGreg Roach        $stream = app(StreamFactoryInterface::class)->createStreamFromFile($temp_zip_file);
3825a78cd34SGreg Roach
383bed27cedSGreg Roach        /** @var ResponseFactoryInterface $response_factory */
384bed27cedSGreg Roach        $response_factory = app(ResponseFactoryInterface::class);
385bed27cedSGreg Roach
386bed27cedSGreg Roach        return $response_factory->createResponse()
3876ccdf4f0SGreg Roach            ->withBody($stream)
3881b3d4731SGreg Roach            ->withHeader('Content-Type', 'application/zip')
389bed27cedSGreg Roach            ->withHeader('Content-Disposition', 'attachment; filename="clippings.zip');
3908c2e8227SGreg Roach    }
3918c2e8227SGreg Roach
3928c2e8227SGreg Roach    /**
39357ab2231SGreg Roach     * @param ServerRequestInterface $request
39476692c8bSGreg Roach     *
3956ccdf4f0SGreg Roach     * @return ResponseInterface
3968c2e8227SGreg Roach     */
39757ab2231SGreg Roach    public function getEmptyAction(ServerRequestInterface $request): ResponseInterface
398c1010edaSGreg Roach    {
39957ab2231SGreg Roach        $tree = $request->getAttribute('tree');
4004ea62551SGreg Roach        assert($tree instanceof Tree);
4014ea62551SGreg Roach
4025a78cd34SGreg Roach        $cart                = Session::get('cart', []);
403aa6f03bbSGreg Roach        $cart[$tree->name()] = [];
4045a78cd34SGreg Roach        Session::put('cart', $cart);
4058c2e8227SGreg Roach
406c1010edaSGreg Roach        $url = route('module', [
40726684e68SGreg Roach            'module' => $this->name(),
408c1010edaSGreg Roach            'action' => 'Show',
409d72b284aSGreg Roach            'tree'   => $tree->name(),
410c1010edaSGreg Roach        ]);
4115a78cd34SGreg Roach
4126ccdf4f0SGreg Roach        return redirect($url);
4135a78cd34SGreg Roach    }
4145a78cd34SGreg Roach
4155a78cd34SGreg Roach    /**
4166ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
4175a78cd34SGreg Roach     *
4186ccdf4f0SGreg Roach     * @return ResponseInterface
4195a78cd34SGreg Roach     */
42057ab2231SGreg Roach    public function postRemoveAction(ServerRequestInterface $request): ResponseInterface
421c1010edaSGreg Roach    {
42257ab2231SGreg Roach        $tree = $request->getAttribute('tree');
42375964c75SGreg Roach        assert($tree instanceof Tree);
4245229eadeSGreg Roach
425d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
4265a78cd34SGreg Roach
4275a78cd34SGreg Roach        $cart = Session::get('cart', []);
428aa6f03bbSGreg Roach        unset($cart[$tree->name()][$xref]);
4295a78cd34SGreg Roach        Session::put('cart', $cart);
4305a78cd34SGreg Roach
431c1010edaSGreg Roach        $url = route('module', [
43226684e68SGreg Roach            'module' => $this->name(),
433c1010edaSGreg Roach            'action' => 'Show',
434d72b284aSGreg Roach            'tree'   => $tree->name(),
435c1010edaSGreg Roach        ]);
4365a78cd34SGreg Roach
4376ccdf4f0SGreg Roach        return redirect($url);
4385a78cd34SGreg Roach    }
4395a78cd34SGreg Roach
4405a78cd34SGreg Roach    /**
44157ab2231SGreg Roach     * @param ServerRequestInterface $request
4425a78cd34SGreg Roach     *
4436ccdf4f0SGreg Roach     * @return ResponseInterface
4445a78cd34SGreg Roach     */
44557ab2231SGreg Roach    public function getShowAction(ServerRequestInterface $request): ResponseInterface
446c1010edaSGreg Roach    {
44757ab2231SGreg Roach        $tree = $request->getAttribute('tree');
44875964c75SGreg Roach        assert($tree instanceof Tree);
44957ab2231SGreg Roach
4505a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/show', [
45177b78a63SRichard Cissée            'module'  => $this->name(),
4525a78cd34SGreg Roach            'records' => $this->allRecordsInCart($tree),
4535a78cd34SGreg Roach            'title'   => I18N::translate('Family tree clippings cart'),
4545a78cd34SGreg Roach            'tree'    => $tree,
4555a78cd34SGreg Roach        ]);
4565a78cd34SGreg Roach    }
4575a78cd34SGreg Roach
4585a78cd34SGreg Roach    /**
459d45701ccSGreg Roach     * Get all the records in the cart.
460d45701ccSGreg Roach     *
461d45701ccSGreg Roach     * @param Tree $tree
462d45701ccSGreg Roach     *
463d45701ccSGreg Roach     * @return GedcomRecord[]
464d45701ccSGreg Roach     */
465d45701ccSGreg Roach    private function allRecordsInCart(Tree $tree): array
466d45701ccSGreg Roach    {
467d45701ccSGreg Roach        $cart = Session::get('cart', []);
468d45701ccSGreg Roach
469d45701ccSGreg Roach        $xrefs = array_keys($cart[$tree->name()] ?? []);
470d45701ccSGreg Roach        $xrefs = array_map('strval', $xrefs); // PHP converts numeric keys to integers.
471d45701ccSGreg Roach
472d45701ccSGreg Roach        // Fetch all the records in the cart.
473d45701ccSGreg Roach        $records = array_map(static function (string $xref) use ($tree): ?GedcomRecord {
474d45701ccSGreg Roach            return Registry::gedcomRecordFactory()->make($xref, $tree);
475d45701ccSGreg Roach        }, $xrefs);
476d45701ccSGreg Roach
477d45701ccSGreg Roach        // Some records may have been deleted after they were added to the cart.
478d45701ccSGreg Roach        $records = array_filter($records);
479d45701ccSGreg Roach
480d45701ccSGreg Roach        // Group and sort.
481d45701ccSGreg Roach        uasort($records, static function (GedcomRecord $x, GedcomRecord $y): int {
482d45701ccSGreg Roach            return $x->tag() <=> $y->tag() ?: GedcomRecord::nameComparator()($x, $y);
483d45701ccSGreg Roach        });
484d45701ccSGreg Roach
485d45701ccSGreg Roach        return $records;
486d45701ccSGreg Roach    }
487d45701ccSGreg Roach
488d45701ccSGreg Roach    /**
4896ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
4905a78cd34SGreg Roach     *
4916ccdf4f0SGreg Roach     * @return ResponseInterface
4925a78cd34SGreg Roach     */
49357ab2231SGreg Roach    public function getAddFamilyAction(ServerRequestInterface $request): ResponseInterface
494c1010edaSGreg Roach    {
49557ab2231SGreg Roach        $tree = $request->getAttribute('tree');
49675964c75SGreg Roach        assert($tree instanceof Tree);
4975229eadeSGreg Roach
498d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
4995a78cd34SGreg Roach
5006b9cb339SGreg Roach        $family = Registry::familyFactory()->make($xref, $tree);
501d45701ccSGreg Roach        $family = Auth::checkFamilyAccess($family);
502d45701ccSGreg Roach        $name   = $family->fullName();
5035a78cd34SGreg Roach
504d45701ccSGreg Roach        $options = [
505d45701ccSGreg Roach            'record'      => $name,
506bbb76c12SGreg Roach            /* I18N: %s is a family (husband + wife) */
507bbb76c12SGreg Roach            'members'     => I18N::translate('%s and their children', $name),
508bbb76c12SGreg Roach            /* I18N: %s is a family (husband + wife) */
509bbb76c12SGreg Roach            'descendants' => I18N::translate('%s and their descendants', $name),
5105a78cd34SGreg Roach        ];
511d45701ccSGreg Roach
512d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
513d45701ccSGreg Roach
514d45701ccSGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
515d45701ccSGreg Roach            'options' => $options,
516d45701ccSGreg Roach            'record'  => $family,
517d45701ccSGreg Roach            'title'   => $title,
518d45701ccSGreg Roach            'tree'    => $tree,
519d45701ccSGreg Roach        ]);
5205a78cd34SGreg Roach    }
5215a78cd34SGreg Roach
5225a78cd34SGreg Roach    /**
5236ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
5245a78cd34SGreg Roach     *
5256ccdf4f0SGreg Roach     * @return ResponseInterface
5265a78cd34SGreg Roach     */
52757ab2231SGreg Roach    public function postAddFamilyAction(ServerRequestInterface $request): ResponseInterface
528c1010edaSGreg Roach    {
52957ab2231SGreg Roach        $tree = $request->getAttribute('tree');
5304ea62551SGreg Roach        assert($tree instanceof Tree);
5314ea62551SGreg Roach
532b46c87bdSGreg Roach        $params = (array) $request->getParsedBody();
533b46c87bdSGreg Roach
534d45701ccSGreg Roach        $xref   = $params['xref'] ?? '';
535d45701ccSGreg Roach        $option = $params['option'] ?? '';
5365a78cd34SGreg Roach
5376b9cb339SGreg Roach        $family = Registry::familyFactory()->make($xref, $tree);
538d45701ccSGreg Roach        $family = Auth::checkFamilyAccess($family);
5395a78cd34SGreg Roach
5405a78cd34SGreg Roach        switch ($option) {
541d45701ccSGreg Roach            case 'self':
5425a78cd34SGreg Roach                $this->addFamilyToCart($family);
5435a78cd34SGreg Roach                break;
5445a78cd34SGreg Roach
5455a78cd34SGreg Roach            case 'members':
5465a78cd34SGreg Roach                $this->addFamilyAndChildrenToCart($family);
5475a78cd34SGreg Roach                break;
5485a78cd34SGreg Roach
5495a78cd34SGreg Roach            case 'descendants':
5505a78cd34SGreg Roach                $this->addFamilyAndDescendantsToCart($family);
5515a78cd34SGreg Roach                break;
5525a78cd34SGreg Roach        }
5535a78cd34SGreg Roach
5546ccdf4f0SGreg Roach        return redirect($family->url());
5555a78cd34SGreg Roach    }
5565a78cd34SGreg Roach
5575a78cd34SGreg Roach
5585a78cd34SGreg Roach    /**
5595a78cd34SGreg Roach     * @param Family $family
56018d7a90dSGreg Roach     *
56118d7a90dSGreg Roach     * @return void
5625a78cd34SGreg Roach     */
563d45701ccSGreg Roach    protected function addFamilyAndChildrenToCart(Family $family): void
564c1010edaSGreg Roach    {
565d45701ccSGreg Roach        $this->addFamilyToCart($family);
5665a78cd34SGreg Roach
56739ca88baSGreg Roach        foreach ($family->children() as $child) {
568d45701ccSGreg Roach            $this->addIndividualToCart($child);
5695a78cd34SGreg Roach        }
5705a78cd34SGreg Roach    }
5715a78cd34SGreg Roach
5725a78cd34SGreg Roach    /**
5735a78cd34SGreg Roach     * @param Family $family
57418d7a90dSGreg Roach     *
57518d7a90dSGreg Roach     * @return void
5765a78cd34SGreg Roach     */
577d45701ccSGreg Roach    protected function addFamilyAndDescendantsToCart(Family $family): void
578c1010edaSGreg Roach    {
579d45701ccSGreg Roach        $this->addFamilyAndChildrenToCart($family);
5805a78cd34SGreg Roach
58139ca88baSGreg Roach        foreach ($family->children() as $child) {
58239ca88baSGreg Roach            foreach ($child->spouseFamilies() as $child_family) {
5835a78cd34SGreg Roach                $this->addFamilyAndDescendantsToCart($child_family);
5845a78cd34SGreg Roach            }
5855a78cd34SGreg Roach        }
5865a78cd34SGreg Roach    }
5875a78cd34SGreg Roach
5885a78cd34SGreg Roach    /**
5896ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
5905a78cd34SGreg Roach     *
5916ccdf4f0SGreg Roach     * @return ResponseInterface
5925a78cd34SGreg Roach     */
59357ab2231SGreg Roach    public function getAddIndividualAction(ServerRequestInterface $request): ResponseInterface
594c1010edaSGreg Roach    {
59557ab2231SGreg Roach        $tree = $request->getAttribute('tree');
59675964c75SGreg Roach        assert($tree instanceof Tree);
5975229eadeSGreg Roach
598d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
5995a78cd34SGreg Roach
6006b9cb339SGreg Roach        $individual = Registry::individualFactory()->make($xref, $tree);
601d45701ccSGreg Roach        $individual = Auth::checkIndividualAccess($individual);
602d45701ccSGreg Roach        $name       = $individual->fullName();
6035a78cd34SGreg Roach
60439ca88baSGreg Roach        if ($individual->sex() === 'F') {
605d45701ccSGreg Roach            $options = [
606d45701ccSGreg Roach                'record'            => $name,
6075a78cd34SGreg Roach                'parents'           => I18N::translate('%s, her parents and siblings', $name),
6085a78cd34SGreg Roach                'spouses'           => I18N::translate('%s, her spouses and children', $name),
6095a78cd34SGreg Roach                'ancestors'         => I18N::translate('%s and her ancestors', $name),
6105a78cd34SGreg Roach                'ancestor_families' => I18N::translate('%s, her ancestors and their families', $name),
6115a78cd34SGreg Roach                'descendants'       => I18N::translate('%s, her spouses and descendants', $name),
6125a78cd34SGreg Roach            ];
613d45701ccSGreg Roach        } else {
614d45701ccSGreg Roach            $options = [
615d45701ccSGreg Roach                'record'            => $name,
6165a78cd34SGreg Roach                'parents'           => I18N::translate('%s, his parents and siblings', $name),
6175a78cd34SGreg Roach                'spouses'           => I18N::translate('%s, his spouses and children', $name),
6185a78cd34SGreg Roach                'ancestors'         => I18N::translate('%s and his ancestors', $name),
6195a78cd34SGreg Roach                'ancestor_families' => I18N::translate('%s, his ancestors and their families', $name),
6205a78cd34SGreg Roach                'descendants'       => I18N::translate('%s, his spouses and descendants', $name),
6215a78cd34SGreg Roach            ];
6225a78cd34SGreg Roach        }
6235a78cd34SGreg Roach
624d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
625d45701ccSGreg Roach
626d45701ccSGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
627d45701ccSGreg Roach            'options' => $options,
628d45701ccSGreg Roach            'record'  => $individual,
629d45701ccSGreg Roach            'title'   => $title,
630d45701ccSGreg Roach            'tree'    => $tree,
631d45701ccSGreg Roach        ]);
632d45701ccSGreg Roach    }
633d45701ccSGreg Roach
6345a78cd34SGreg Roach    /**
6356ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
6365a78cd34SGreg Roach     *
6376ccdf4f0SGreg Roach     * @return ResponseInterface
6385a78cd34SGreg Roach     */
63957ab2231SGreg Roach    public function postAddIndividualAction(ServerRequestInterface $request): ResponseInterface
640c1010edaSGreg Roach    {
64157ab2231SGreg Roach        $tree = $request->getAttribute('tree');
6424ea62551SGreg Roach        assert($tree instanceof Tree);
6434ea62551SGreg Roach
644b46c87bdSGreg Roach        $params = (array) $request->getParsedBody();
645b46c87bdSGreg Roach
646d45701ccSGreg Roach        $xref   = $params['xref'] ?? '';
647d45701ccSGreg Roach        $option = $params['option'] ?? '';
6485a78cd34SGreg Roach
6496b9cb339SGreg Roach        $individual = Registry::individualFactory()->make($xref, $tree);
650d45701ccSGreg Roach        $individual = Auth::checkIndividualAccess($individual);
6515a78cd34SGreg Roach
6525a78cd34SGreg Roach        switch ($option) {
6535a78cd34SGreg Roach            case 'self':
654d45701ccSGreg Roach                $this->addIndividualToCart($individual);
6555a78cd34SGreg Roach                break;
6565a78cd34SGreg Roach
6575a78cd34SGreg Roach            case 'parents':
65839ca88baSGreg Roach                foreach ($individual->childFamilies() as $family) {
6595a78cd34SGreg Roach                    $this->addFamilyAndChildrenToCart($family);
6605a78cd34SGreg Roach                }
6615a78cd34SGreg Roach                break;
6625a78cd34SGreg Roach
6635a78cd34SGreg Roach            case 'spouses':
66439ca88baSGreg Roach                foreach ($individual->spouseFamilies() as $family) {
6655a78cd34SGreg Roach                    $this->addFamilyAndChildrenToCart($family);
6665a78cd34SGreg Roach                }
6675a78cd34SGreg Roach                break;
6685a78cd34SGreg Roach
6695a78cd34SGreg Roach            case 'ancestors':
6705a78cd34SGreg Roach                $this->addAncestorsToCart($individual);
6715a78cd34SGreg Roach                break;
6725a78cd34SGreg Roach
6735a78cd34SGreg Roach            case 'ancestor_families':
6745a78cd34SGreg Roach                $this->addAncestorFamiliesToCart($individual);
6755a78cd34SGreg Roach                break;
6765a78cd34SGreg Roach
6775a78cd34SGreg Roach            case 'descendants':
67839ca88baSGreg Roach                foreach ($individual->spouseFamilies() as $family) {
6795a78cd34SGreg Roach                    $this->addFamilyAndDescendantsToCart($family);
6805a78cd34SGreg Roach                }
6815a78cd34SGreg Roach                break;
6825a78cd34SGreg Roach        }
6835a78cd34SGreg Roach
6846ccdf4f0SGreg Roach        return redirect($individual->url());
6855a78cd34SGreg Roach    }
6865a78cd34SGreg Roach
6875a78cd34SGreg Roach    /**
6885a78cd34SGreg Roach     * @param Individual $individual
68918d7a90dSGreg Roach     *
69018d7a90dSGreg Roach     * @return void
6915a78cd34SGreg Roach     */
692d45701ccSGreg Roach    protected function addAncestorsToCart(Individual $individual): void
693c1010edaSGreg Roach    {
694d45701ccSGreg Roach        $this->addIndividualToCart($individual);
6955a78cd34SGreg Roach
69639ca88baSGreg Roach        foreach ($individual->childFamilies() as $family) {
697d45701ccSGreg Roach            $this->addFamilyToCart($family);
6988df4c68dSGreg Roach
69939ca88baSGreg Roach            foreach ($family->spouses() as $parent) {
7005a78cd34SGreg Roach                $this->addAncestorsToCart($parent);
7015a78cd34SGreg Roach            }
7025a78cd34SGreg Roach        }
7035a78cd34SGreg Roach    }
7045a78cd34SGreg Roach
7055a78cd34SGreg Roach    /**
7065a78cd34SGreg Roach     * @param Individual $individual
70718d7a90dSGreg Roach     *
70818d7a90dSGreg Roach     * @return void
7095a78cd34SGreg Roach     */
710d45701ccSGreg Roach    protected function addAncestorFamiliesToCart(Individual $individual): void
711c1010edaSGreg Roach    {
71239ca88baSGreg Roach        foreach ($individual->childFamilies() as $family) {
7135a78cd34SGreg Roach            $this->addFamilyAndChildrenToCart($family);
7148df4c68dSGreg Roach
71539ca88baSGreg Roach            foreach ($family->spouses() as $parent) {
716cad6d3f3SGreg Roach                $this->addAncestorFamiliesToCart($parent);
7175a78cd34SGreg Roach            }
7185a78cd34SGreg Roach        }
7195a78cd34SGreg Roach    }
7205a78cd34SGreg Roach
7215a78cd34SGreg Roach    /**
7226ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
7235a78cd34SGreg Roach     *
7246ccdf4f0SGreg Roach     * @return ResponseInterface
7255a78cd34SGreg Roach     */
726e8ded2caSGreg Roach    public function getAddLocationAction(ServerRequestInterface $request): ResponseInterface
727e8ded2caSGreg Roach    {
728e8ded2caSGreg Roach        $tree = $request->getAttribute('tree');
729e8ded2caSGreg Roach        assert($tree instanceof Tree);
730e8ded2caSGreg Roach
731e8ded2caSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
732e8ded2caSGreg Roach
733e8ded2caSGreg Roach        $location = Registry::locationFactory()->make($xref, $tree);
734e8ded2caSGreg Roach        $location = Auth::checkLocationAccess($location);
735e8ded2caSGreg Roach        $name     = $location->fullName();
736e8ded2caSGreg Roach
737e8ded2caSGreg Roach        $options = [
738e8ded2caSGreg Roach            'record' => $name,
739e8ded2caSGreg Roach        ];
740e8ded2caSGreg Roach
741e8ded2caSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
742e8ded2caSGreg Roach
743e8ded2caSGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
744e8ded2caSGreg Roach            'options' => $options,
745e8ded2caSGreg Roach            'record'  => $location,
746e8ded2caSGreg Roach            'title'   => $title,
747e8ded2caSGreg Roach            'tree'    => $tree,
748e8ded2caSGreg Roach        ]);
749e8ded2caSGreg Roach    }
750e8ded2caSGreg Roach
751e8ded2caSGreg Roach    /**
752e8ded2caSGreg Roach     * @param ServerRequestInterface $request
753e8ded2caSGreg Roach     *
754e8ded2caSGreg Roach     * @return ResponseInterface
755e8ded2caSGreg Roach     */
756e8ded2caSGreg Roach    public function postAddLocationAction(ServerRequestInterface $request): ResponseInterface
757e8ded2caSGreg Roach    {
758e8ded2caSGreg Roach        $tree = $request->getAttribute('tree');
759e8ded2caSGreg Roach        assert($tree instanceof Tree);
760e8ded2caSGreg Roach
761e8ded2caSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
762e8ded2caSGreg Roach
763e8ded2caSGreg Roach        $location = Registry::locationFactory()->make($xref, $tree);
764e8ded2caSGreg Roach        $location = Auth::checkLocationAccess($location);
765e8ded2caSGreg Roach
766e8ded2caSGreg Roach        $this->addLocationToCart($location);
767e8ded2caSGreg Roach
768e8ded2caSGreg Roach        return redirect($location->url());
769e8ded2caSGreg Roach    }
770e8ded2caSGreg Roach
771e8ded2caSGreg Roach    /**
772e8ded2caSGreg Roach     * @param ServerRequestInterface $request
773e8ded2caSGreg Roach     *
774e8ded2caSGreg Roach     * @return ResponseInterface
775e8ded2caSGreg Roach     */
77657ab2231SGreg Roach    public function getAddMediaAction(ServerRequestInterface $request): ResponseInterface
777c1010edaSGreg Roach    {
77857ab2231SGreg Roach        $tree = $request->getAttribute('tree');
77975964c75SGreg Roach        assert($tree instanceof Tree);
7805229eadeSGreg Roach
781d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
7825a78cd34SGreg Roach
7836b9cb339SGreg Roach        $media = Registry::mediaFactory()->make($xref, $tree);
784d45701ccSGreg Roach        $media = Auth::checkMediaAccess($media);
785d45701ccSGreg Roach        $name  = $media->fullName();
7865a78cd34SGreg Roach
787d45701ccSGreg Roach        $options = [
788d45701ccSGreg Roach            'record' => $name,
789d45701ccSGreg Roach        ];
7905a78cd34SGreg Roach
791d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
7925a78cd34SGreg Roach
7935a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
7945a78cd34SGreg Roach            'options' => $options,
7955a78cd34SGreg Roach            'record'  => $media,
7965a78cd34SGreg Roach            'title'   => $title,
7975a78cd34SGreg Roach            'tree'    => $tree,
7985a78cd34SGreg Roach        ]);
7995a78cd34SGreg Roach    }
8005a78cd34SGreg Roach
8015a78cd34SGreg Roach    /**
8026ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
8035a78cd34SGreg Roach     *
8046ccdf4f0SGreg Roach     * @return ResponseInterface
8055a78cd34SGreg Roach     */
80657ab2231SGreg Roach    public function postAddMediaAction(ServerRequestInterface $request): ResponseInterface
807c1010edaSGreg Roach    {
80857ab2231SGreg Roach        $tree = $request->getAttribute('tree');
80975964c75SGreg Roach        assert($tree instanceof Tree);
8105229eadeSGreg Roach
811d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
8125a78cd34SGreg Roach
8136b9cb339SGreg Roach        $media = Registry::mediaFactory()->make($xref, $tree);
814d45701ccSGreg Roach        $media = Auth::checkMediaAccess($media);
8155a78cd34SGreg Roach
816d45701ccSGreg Roach        $this->addMediaToCart($media);
8175a78cd34SGreg Roach
8186ccdf4f0SGreg Roach        return redirect($media->url());
8195a78cd34SGreg Roach    }
8205a78cd34SGreg Roach
8215a78cd34SGreg Roach    /**
8226ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
8235a78cd34SGreg Roach     *
8246ccdf4f0SGreg Roach     * @return ResponseInterface
8255a78cd34SGreg Roach     */
82657ab2231SGreg Roach    public function getAddNoteAction(ServerRequestInterface $request): ResponseInterface
827c1010edaSGreg Roach    {
82857ab2231SGreg Roach        $tree = $request->getAttribute('tree');
82975964c75SGreg Roach        assert($tree instanceof Tree);
8305229eadeSGreg Roach
831d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
8325a78cd34SGreg Roach
8336b9cb339SGreg Roach        $note = Registry::noteFactory()->make($xref, $tree);
834d45701ccSGreg Roach        $note = Auth::checkNoteAccess($note);
835d45701ccSGreg Roach        $name = $note->fullName();
8365a78cd34SGreg Roach
837d45701ccSGreg Roach        $options = [
838d45701ccSGreg Roach            'record' => $name,
839d45701ccSGreg Roach        ];
8405a78cd34SGreg Roach
841d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
8425a78cd34SGreg Roach
8435a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
8445a78cd34SGreg Roach            'options' => $options,
8455a78cd34SGreg Roach            'record'  => $note,
8465a78cd34SGreg Roach            'title'   => $title,
8475a78cd34SGreg Roach            'tree'    => $tree,
8485a78cd34SGreg Roach        ]);
8495a78cd34SGreg Roach    }
8505a78cd34SGreg Roach
8515a78cd34SGreg Roach    /**
8526ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
8535a78cd34SGreg Roach     *
8546ccdf4f0SGreg Roach     * @return ResponseInterface
8555a78cd34SGreg Roach     */
85657ab2231SGreg Roach    public function postAddNoteAction(ServerRequestInterface $request): ResponseInterface
857c1010edaSGreg Roach    {
85857ab2231SGreg Roach        $tree = $request->getAttribute('tree');
85975964c75SGreg Roach        assert($tree instanceof Tree);
8605229eadeSGreg Roach
861d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
8625a78cd34SGreg Roach
8636b9cb339SGreg Roach        $note = Registry::noteFactory()->make($xref, $tree);
864d45701ccSGreg Roach        $note = Auth::checkNoteAccess($note);
8655a78cd34SGreg Roach
866d45701ccSGreg Roach        $this->addNoteToCart($note);
8675a78cd34SGreg Roach
8686ccdf4f0SGreg Roach        return redirect($note->url());
8695a78cd34SGreg Roach    }
8705a78cd34SGreg Roach
8715a78cd34SGreg Roach    /**
8726ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
8735a78cd34SGreg Roach     *
8746ccdf4f0SGreg Roach     * @return ResponseInterface
8755a78cd34SGreg Roach     */
87657ab2231SGreg Roach    public function getAddRepositoryAction(ServerRequestInterface $request): ResponseInterface
877c1010edaSGreg Roach    {
87857ab2231SGreg Roach        $tree = $request->getAttribute('tree');
87975964c75SGreg Roach        assert($tree instanceof Tree);
8805229eadeSGreg Roach
881d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
8825a78cd34SGreg Roach
8836b9cb339SGreg Roach        $repository = Registry::repositoryFactory()->make($xref, $tree);
884d45701ccSGreg Roach        $repository = Auth::checkRepositoryAccess($repository);
885d45701ccSGreg Roach        $name       = $repository->fullName();
8865a78cd34SGreg Roach
887d45701ccSGreg Roach        $options = [
888d45701ccSGreg Roach            'record' => $name,
889d45701ccSGreg Roach        ];
8905a78cd34SGreg Roach
891d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
8925a78cd34SGreg Roach
8935a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
8945a78cd34SGreg Roach            'options' => $options,
8955a78cd34SGreg Roach            'record'  => $repository,
8965a78cd34SGreg Roach            'title'   => $title,
8975a78cd34SGreg Roach            'tree'    => $tree,
8985a78cd34SGreg Roach        ]);
8995a78cd34SGreg Roach    }
9005a78cd34SGreg Roach
9015a78cd34SGreg Roach    /**
9026ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
9035a78cd34SGreg Roach     *
9046ccdf4f0SGreg Roach     * @return ResponseInterface
9055a78cd34SGreg Roach     */
90657ab2231SGreg Roach    public function postAddRepositoryAction(ServerRequestInterface $request): ResponseInterface
907c1010edaSGreg Roach    {
90857ab2231SGreg Roach        $tree = $request->getAttribute('tree');
90975964c75SGreg Roach        assert($tree instanceof Tree);
9105229eadeSGreg Roach
911d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
9125a78cd34SGreg Roach
9136b9cb339SGreg Roach        $repository = Registry::repositoryFactory()->make($xref, $tree);
914d45701ccSGreg Roach        $repository = Auth::checkRepositoryAccess($repository);
9155a78cd34SGreg Roach
916d45701ccSGreg Roach        $this->addRepositoryToCart($repository);
917d45701ccSGreg Roach
918d45701ccSGreg Roach        foreach ($repository->linkedSources('REPO') as $source) {
919d45701ccSGreg Roach            $this->addSourceToCart($source);
9205a78cd34SGreg Roach        }
9215a78cd34SGreg Roach
9226ccdf4f0SGreg Roach        return redirect($repository->url());
9235a78cd34SGreg Roach    }
9245a78cd34SGreg Roach
9255a78cd34SGreg Roach    /**
9266ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
9275a78cd34SGreg Roach     *
9286ccdf4f0SGreg Roach     * @return ResponseInterface
9295a78cd34SGreg Roach     */
93057ab2231SGreg Roach    public function getAddSourceAction(ServerRequestInterface $request): ResponseInterface
931c1010edaSGreg Roach    {
93257ab2231SGreg Roach        $tree = $request->getAttribute('tree');
93375964c75SGreg Roach        assert($tree instanceof Tree);
9345229eadeSGreg Roach
935d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
9365a78cd34SGreg Roach
9376b9cb339SGreg Roach        $source = Registry::sourceFactory()->make($xref, $tree);
938d45701ccSGreg Roach        $source = Auth::checkSourceAccess($source);
939d45701ccSGreg Roach        $name   = $source->fullName();
9405a78cd34SGreg Roach
941d45701ccSGreg Roach        $options = [
942d45701ccSGreg Roach            'record' => $name,
943d45701ccSGreg Roach            'linked' => I18N::translate('%s and the individuals that reference it.', $name),
944d45701ccSGreg Roach        ];
9455a78cd34SGreg Roach
946d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
9475a78cd34SGreg Roach
9485a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
9495a78cd34SGreg Roach            'options' => $options,
9505a78cd34SGreg Roach            'record'  => $source,
9515a78cd34SGreg Roach            'title'   => $title,
9525a78cd34SGreg Roach            'tree'    => $tree,
9535a78cd34SGreg Roach        ]);
9545a78cd34SGreg Roach    }
9555a78cd34SGreg Roach
9565a78cd34SGreg Roach    /**
9576ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
9585a78cd34SGreg Roach     *
9596ccdf4f0SGreg Roach     * @return ResponseInterface
9605a78cd34SGreg Roach     */
96157ab2231SGreg Roach    public function postAddSourceAction(ServerRequestInterface $request): ResponseInterface
962c1010edaSGreg Roach    {
96357ab2231SGreg Roach        $tree = $request->getAttribute('tree');
96475964c75SGreg Roach        assert($tree instanceof Tree);
9655229eadeSGreg Roach
966b46c87bdSGreg Roach        $params = (array) $request->getParsedBody();
967b46c87bdSGreg Roach
968d45701ccSGreg Roach        $xref   = $params['xref'] ?? '';
969d45701ccSGreg Roach        $option = $params['option'] ?? '';
9705a78cd34SGreg Roach
9716b9cb339SGreg Roach        $source = Registry::sourceFactory()->make($xref, $tree);
972d45701ccSGreg Roach        $source = Auth::checkSourceAccess($source);
9735a78cd34SGreg Roach
974d45701ccSGreg Roach        $this->addSourceToCart($source);
9755a78cd34SGreg Roach
9765a78cd34SGreg Roach        if ($option === 'linked') {
9775a78cd34SGreg Roach            foreach ($source->linkedIndividuals('SOUR') as $individual) {
978d45701ccSGreg Roach                $this->addIndividualToCart($individual);
9795a78cd34SGreg Roach            }
9805a78cd34SGreg Roach            foreach ($source->linkedFamilies('SOUR') as $family) {
981d45701ccSGreg Roach                $this->addFamilyToCart($family);
9825a78cd34SGreg Roach            }
9835a78cd34SGreg Roach        }
9845a78cd34SGreg Roach
9856ccdf4f0SGreg Roach        return redirect($source->url());
9865a78cd34SGreg Roach    }
9875a78cd34SGreg Roach
9885a78cd34SGreg Roach    /**
989d45701ccSGreg Roach     * @param ServerRequestInterface $request
9905a78cd34SGreg Roach     *
991d45701ccSGreg Roach     * @return ResponseInterface
9925a78cd34SGreg Roach     */
993d45701ccSGreg Roach    public function getAddSubmitterAction(ServerRequestInterface $request): ResponseInterface
994c1010edaSGreg Roach    {
995d45701ccSGreg Roach        $tree = $request->getAttribute('tree');
996d45701ccSGreg Roach        assert($tree instanceof Tree);
9975a78cd34SGreg Roach
998d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
9995a78cd34SGreg Roach
1000d45701ccSGreg Roach        $submitter = Registry::submitterFactory()->make($xref, $tree);
1001d45701ccSGreg Roach        $submitter = Auth::checkSubmitterAccess($submitter);
1002d45701ccSGreg Roach        $name      = $submitter->fullName();
10035a78cd34SGreg Roach
1004d45701ccSGreg Roach        $options = [
1005d45701ccSGreg Roach            'record' => $name,
1006d45701ccSGreg Roach        ];
10075a78cd34SGreg Roach
1008d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
10095a78cd34SGreg Roach
1010d45701ccSGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
1011d45701ccSGreg Roach            'options' => $options,
1012d45701ccSGreg Roach            'record'  => $submitter,
1013d45701ccSGreg Roach            'title'   => $title,
1014d45701ccSGreg Roach            'tree'    => $tree,
1015d45701ccSGreg Roach        ]);
10165a78cd34SGreg Roach    }
10175a78cd34SGreg Roach
10185a78cd34SGreg Roach    /**
1019d45701ccSGreg Roach     * @param ServerRequestInterface $request
10205a78cd34SGreg Roach     *
1021d45701ccSGreg Roach     * @return ResponseInterface
10225a78cd34SGreg Roach     */
1023d45701ccSGreg Roach    public function postAddSubmitterAction(ServerRequestInterface $request): ResponseInterface
1024d45701ccSGreg Roach    {
1025d45701ccSGreg Roach        $tree = $request->getAttribute('tree');
1026d45701ccSGreg Roach        assert($tree instanceof Tree);
1027d45701ccSGreg Roach
1028d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
1029d45701ccSGreg Roach
1030d45701ccSGreg Roach        $submitter = Registry::submitterFactory()->make($xref, $tree);
1031d45701ccSGreg Roach        $submitter = Auth::checkSubmitterAccess($submitter);
1032d45701ccSGreg Roach
1033d45701ccSGreg Roach        $this->addSubmitterToCart($submitter);
1034d45701ccSGreg Roach
1035d45701ccSGreg Roach        return redirect($submitter->url());
1036d45701ccSGreg Roach    }
1037d45701ccSGreg Roach
1038d45701ccSGreg Roach    /**
1039d45701ccSGreg Roach     * @param Family $family
1040d45701ccSGreg Roach     */
1041d45701ccSGreg Roach    protected function addFamilyToCart(Family $family): void
1042c1010edaSGreg Roach    {
10435a78cd34SGreg Roach        $cart = Session::get('cart', []);
1044d45701ccSGreg Roach        $tree = $family->tree()->name();
1045d45701ccSGreg Roach        $xref = $family->xref();
10465a78cd34SGreg Roach
1047d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1048d45701ccSGreg Roach            $cart[$tree][$xref] = true;
10495a78cd34SGreg Roach
1050d45701ccSGreg Roach            Session::put('cart', $cart);
10515a78cd34SGreg Roach
1052d45701ccSGreg Roach            foreach ($family->spouses() as $spouse) {
1053d45701ccSGreg Roach                $this->addIndividualToCart($spouse);
10545a78cd34SGreg Roach            }
10555a78cd34SGreg Roach
1056e8ded2caSGreg Roach            $this->addLocationLinksToCart($family);
1057d45701ccSGreg Roach            $this->addMediaLinksToCart($family);
1058d45701ccSGreg Roach            $this->addNoteLinksToCart($family);
1059d45701ccSGreg Roach            $this->addSourceLinksToCart($family);
1060d45701ccSGreg Roach            $this->addSubmitterLinksToCart($family);
1061d45701ccSGreg Roach        }
1062d45701ccSGreg Roach    }
1063d45701ccSGreg Roach
1064d45701ccSGreg Roach    /**
1065d45701ccSGreg Roach     * @param Individual $individual
1066d45701ccSGreg Roach     */
1067d45701ccSGreg Roach    protected function addIndividualToCart(Individual $individual): void
1068d45701ccSGreg Roach    {
1069d45701ccSGreg Roach        $cart = Session::get('cart', []);
1070d45701ccSGreg Roach        $tree = $individual->tree()->name();
1071d45701ccSGreg Roach        $xref = $individual->xref();
1072d45701ccSGreg Roach
1073d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1074d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1075d45701ccSGreg Roach
1076d45701ccSGreg Roach            Session::put('cart', $cart);
1077d45701ccSGreg Roach
1078e8ded2caSGreg Roach            $this->addLocationLinksToCart($individual);
1079d45701ccSGreg Roach            $this->addMediaLinksToCart($individual);
1080d45701ccSGreg Roach            $this->addNoteLinksToCart($individual);
1081d45701ccSGreg Roach            $this->addSourceLinksToCart($individual);
1082d45701ccSGreg Roach        }
1083d45701ccSGreg Roach    }
1084d45701ccSGreg Roach
1085d45701ccSGreg Roach    /**
1086e8ded2caSGreg Roach     * @param Location $location
1087e8ded2caSGreg Roach     */
1088e8ded2caSGreg Roach    protected function addLocationToCart(Location $location): void
1089e8ded2caSGreg Roach    {
1090e8ded2caSGreg Roach        $cart = Session::get('cart', []);
1091e8ded2caSGreg Roach        $tree = $location->tree()->name();
1092e8ded2caSGreg Roach        $xref = $location->xref();
1093e8ded2caSGreg Roach
1094e8ded2caSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1095e8ded2caSGreg Roach            $cart[$tree][$xref] = true;
1096e8ded2caSGreg Roach
1097e8ded2caSGreg Roach            Session::put('cart', $cart);
1098e8ded2caSGreg Roach
1099e8ded2caSGreg Roach            $this->addLocationLinksToCart($location);
1100e8ded2caSGreg Roach            $this->addMediaLinksToCart($location);
1101e8ded2caSGreg Roach            $this->addNoteLinksToCart($location);
1102e8ded2caSGreg Roach            $this->addSourceLinksToCart($location);
1103e8ded2caSGreg Roach        }
1104e8ded2caSGreg Roach    }
1105e8ded2caSGreg Roach
1106e8ded2caSGreg Roach    /**
1107e8ded2caSGreg Roach     * @param GedcomRecord $record
1108e8ded2caSGreg Roach     */
1109e8ded2caSGreg Roach    protected function addLocationLinksToCart(GedcomRecord $record): void
1110e8ded2caSGreg Roach    {
1111e8ded2caSGreg Roach        preg_match_all('/\n\d _LOC @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1112e8ded2caSGreg Roach
1113e8ded2caSGreg Roach        foreach ($matches[1] as $xref) {
1114e8ded2caSGreg Roach            $location = Registry::locationFactory()->make($xref, $record->tree());
1115e8ded2caSGreg Roach
1116e8ded2caSGreg Roach            if ($location instanceof Location && $location->canShow()) {
1117e8ded2caSGreg Roach                $this->addLocationToCart($location);
1118e8ded2caSGreg Roach            }
1119e8ded2caSGreg Roach        }
1120e8ded2caSGreg Roach    }
1121e8ded2caSGreg Roach
1122e8ded2caSGreg Roach    /**
1123d45701ccSGreg Roach     * @param Media $media
1124d45701ccSGreg Roach     */
1125d45701ccSGreg Roach    protected function addMediaToCart(Media $media): void
1126d45701ccSGreg Roach    {
1127d45701ccSGreg Roach        $cart = Session::get('cart', []);
1128d45701ccSGreg Roach        $tree = $media->tree()->name();
1129d45701ccSGreg Roach        $xref = $media->xref();
1130d45701ccSGreg Roach
1131d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1132d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1133d45701ccSGreg Roach
1134d45701ccSGreg Roach            Session::put('cart', $cart);
1135d45701ccSGreg Roach
1136d45701ccSGreg Roach            $this->addNoteLinksToCart($media);
1137d45701ccSGreg Roach        }
1138d45701ccSGreg Roach    }
1139d45701ccSGreg Roach
1140d45701ccSGreg Roach    /**
1141d45701ccSGreg Roach     * @param GedcomRecord $record
1142d45701ccSGreg Roach     */
1143d45701ccSGreg Roach    protected function addMediaLinksToCart(GedcomRecord $record): void
1144d45701ccSGreg Roach    {
1145d45701ccSGreg Roach        preg_match_all('/\n\d OBJE @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1146d45701ccSGreg Roach
1147d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1148d45701ccSGreg Roach            $media = Registry::mediaFactory()->make($xref, $record->tree());
1149d45701ccSGreg Roach
1150d45701ccSGreg Roach            if ($media instanceof Media && $media->canShow()) {
1151d45701ccSGreg Roach                $this->addMediaToCart($media);
1152d45701ccSGreg Roach            }
1153d45701ccSGreg Roach        }
1154d45701ccSGreg Roach    }
1155d45701ccSGreg Roach
1156d45701ccSGreg Roach    /**
1157d45701ccSGreg Roach     * @param Note $note
1158d45701ccSGreg Roach     */
1159d45701ccSGreg Roach    protected function addNoteToCart(Note $note): void
1160d45701ccSGreg Roach    {
1161d45701ccSGreg Roach        $cart = Session::get('cart', []);
1162d45701ccSGreg Roach        $tree = $note->tree()->name();
1163d45701ccSGreg Roach        $xref = $note->xref();
1164d45701ccSGreg Roach
1165d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1166d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1167d45701ccSGreg Roach
11685a78cd34SGreg Roach            Session::put('cart', $cart);
11695a78cd34SGreg Roach        }
1170d45701ccSGreg Roach    }
11715a78cd34SGreg Roach
11725a78cd34SGreg Roach    /**
1173d45701ccSGreg Roach     * @param GedcomRecord $record
11745a78cd34SGreg Roach     */
1175d45701ccSGreg Roach    protected function addNoteLinksToCart(GedcomRecord $record): void
1176d45701ccSGreg Roach    {
1177d45701ccSGreg Roach        preg_match_all('/\n\d NOTE @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1178d45701ccSGreg Roach
1179d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1180d45701ccSGreg Roach            $note = Registry::noteFactory()->make($xref, $record->tree());
1181d45701ccSGreg Roach
1182d45701ccSGreg Roach            if ($note instanceof Note && $note->canShow()) {
1183d45701ccSGreg Roach                $this->addNoteToCart($note);
1184d45701ccSGreg Roach            }
1185d45701ccSGreg Roach        }
1186d45701ccSGreg Roach    }
1187d45701ccSGreg Roach
1188d45701ccSGreg Roach    /**
1189d45701ccSGreg Roach     * @param Source $source
1190d45701ccSGreg Roach     */
1191d45701ccSGreg Roach    protected function addSourceToCart(Source $source): void
1192c1010edaSGreg Roach    {
11935a78cd34SGreg Roach        $cart = Session::get('cart', []);
1194d45701ccSGreg Roach        $tree = $source->tree()->name();
1195d45701ccSGreg Roach        $xref = $source->xref();
11965a78cd34SGreg Roach
1197d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1198d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1199d45701ccSGreg Roach
1200d45701ccSGreg Roach            Session::put('cart', $cart);
1201d45701ccSGreg Roach
1202d45701ccSGreg Roach            $this->addNoteLinksToCart($source);
1203d45701ccSGreg Roach            $this->addRepositoryLinksToCart($source);
1204d45701ccSGreg Roach        }
1205d45701ccSGreg Roach    }
1206d45701ccSGreg Roach
1207d45701ccSGreg Roach    /**
1208d45701ccSGreg Roach     * @param GedcomRecord $record
1209d45701ccSGreg Roach     */
1210d45701ccSGreg Roach    protected function addSourceLinksToCart(GedcomRecord $record): void
1211d45701ccSGreg Roach    {
1212d45701ccSGreg Roach        preg_match_all('/\n\d SOUR @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1213d45701ccSGreg Roach
1214d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1215d45701ccSGreg Roach            $source = Registry::sourceFactory()->make($xref, $record->tree());
1216d45701ccSGreg Roach
1217d45701ccSGreg Roach            if ($source instanceof Source && $source->canShow()) {
1218d45701ccSGreg Roach                $this->addSourceToCart($source);
1219d45701ccSGreg Roach            }
1220d45701ccSGreg Roach        }
1221d45701ccSGreg Roach    }
1222d45701ccSGreg Roach
1223d45701ccSGreg Roach    /**
1224d45701ccSGreg Roach     * @param Repository $repository
1225d45701ccSGreg Roach     */
1226d45701ccSGreg Roach    protected function addRepositoryToCart(Repository $repository): void
1227d45701ccSGreg Roach    {
1228d45701ccSGreg Roach        $cart = Session::get('cart', []);
1229d45701ccSGreg Roach        $tree = $repository->tree()->name();
1230d45701ccSGreg Roach        $xref = $repository->xref();
1231d45701ccSGreg Roach
1232d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1233d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1234d45701ccSGreg Roach
1235d45701ccSGreg Roach            Session::put('cart', $cart);
1236d45701ccSGreg Roach
1237d45701ccSGreg Roach            $this->addNoteLinksToCart($repository);
1238d45701ccSGreg Roach        }
1239d45701ccSGreg Roach    }
1240d45701ccSGreg Roach
1241d45701ccSGreg Roach    /**
1242d45701ccSGreg Roach     * @param GedcomRecord $record
1243d45701ccSGreg Roach     */
1244d45701ccSGreg Roach    protected function addRepositoryLinksToCart(GedcomRecord $record): void
1245d45701ccSGreg Roach    {
1246d45701ccSGreg Roach        preg_match_all('/\n\d REPO @(' . Gedcom::REGEX_XREF . '@)/', $record->gedcom(), $matches);
1247d45701ccSGreg Roach
1248d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1249d45701ccSGreg Roach            $repository = Registry::repositoryFactory()->make($xref, $record->tree());
1250d45701ccSGreg Roach
1251d45701ccSGreg Roach            if ($repository instanceof Repository && $repository->canShow()) {
1252d45701ccSGreg Roach                $this->addRepositoryToCart($repository);
1253d45701ccSGreg Roach            }
1254d45701ccSGreg Roach        }
1255d45701ccSGreg Roach    }
1256d45701ccSGreg Roach
1257d45701ccSGreg Roach    /**
1258d45701ccSGreg Roach     * @param Submitter $submitter
1259d45701ccSGreg Roach     */
1260d45701ccSGreg Roach    protected function addSubmitterToCart(Submitter $submitter): void
1261d45701ccSGreg Roach    {
1262d45701ccSGreg Roach        $cart = Session::get('cart', []);
1263d45701ccSGreg Roach        $tree = $submitter->tree()->name();
1264d45701ccSGreg Roach        $xref = $submitter->xref();
1265d45701ccSGreg Roach
1266d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1267d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1268d45701ccSGreg Roach
1269d45701ccSGreg Roach            Session::put('cart', $cart);
1270d45701ccSGreg Roach
1271d45701ccSGreg Roach            $this->addNoteLinksToCart($submitter);
1272d45701ccSGreg Roach        }
1273d45701ccSGreg Roach    }
1274d45701ccSGreg Roach
1275d45701ccSGreg Roach    /**
1276d45701ccSGreg Roach     * @param GedcomRecord $record
1277d45701ccSGreg Roach     */
1278d45701ccSGreg Roach    protected function addSubmitterLinksToCart(GedcomRecord $record): void
1279d45701ccSGreg Roach    {
1280d45701ccSGreg Roach        preg_match_all('/\n\d SUBM @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1281d45701ccSGreg Roach
1282d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1283d45701ccSGreg Roach            $submitter = Registry::submitterFactory()->make($xref, $record->tree());
1284d45701ccSGreg Roach
1285d45701ccSGreg Roach            if ($submitter instanceof Submitter && $submitter->canShow()) {
1286d45701ccSGreg Roach                $this->addSubmitterToCart($submitter);
1287d45701ccSGreg Roach            }
1288d45701ccSGreg Roach        }
12895a78cd34SGreg Roach    }
12908c2e8227SGreg Roach}
1291