xref: /webtrees/app/Module/ClippingsCartModule.php (revision f566cb162e9535d2b6538d49af05befe2f4e0ea1)
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;
51f0448b68SGreg Roachuse League\Flysystem\FilesystemException;
52f32d77e6SGreg Roachuse League\Flysystem\ZipArchive\FilesystemZipArchiveProvider;
535a78cd34SGreg Roachuse League\Flysystem\ZipArchive\ZipArchiveAdapter;
5400c45d23SGreg Roachuse Psr\Http\Message\ResponseFactoryInterface;
556ccdf4f0SGreg Roachuse Psr\Http\Message\ResponseInterface;
566ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface;
5700c45d23SGreg Roachuse Psr\Http\Message\StreamFactoryInterface;
5869c05a6eSGreg Roachuse RuntimeException;
593976b470SGreg Roach
60eb235819SGreg Roachuse function app;
61bf80ec58SGreg Roachuse function array_filter;
62bf80ec58SGreg Roachuse function array_keys;
63bf80ec58SGreg Roachuse function array_map;
64fa695506SGreg Roachuse function array_search;
655229eadeSGreg Roachuse function assert;
6669c05a6eSGreg Roachuse function fopen;
67bf80ec58SGreg Roachuse function in_array;
68ddeb3354SGreg Roachuse function is_string;
69bf80ec58SGreg Roachuse function preg_match_all;
70bf80ec58SGreg Roachuse function redirect;
7169c05a6eSGreg Roachuse function rewind;
72bf80ec58SGreg Roachuse function route;
73e5a6b4d4SGreg Roachuse function str_replace;
74fa695506SGreg Roachuse function stream_get_meta_data;
75fa695506SGreg Roachuse function tmpfile;
76fa695506SGreg Roachuse function uasort;
77d45701ccSGreg Roachuse function view;
78fa695506SGreg Roach
79fa695506SGreg Roachuse const PREG_SET_ORDER;
808c2e8227SGreg Roach
818c2e8227SGreg Roach/**
828c2e8227SGreg Roach * Class ClippingsCartModule
838c2e8227SGreg Roach */
8437eb8894SGreg Roachclass ClippingsCartModule extends AbstractModule implements ModuleMenuInterface
85c1010edaSGreg Roach{
8649a243cbSGreg Roach    use ModuleMenuTrait;
8749a243cbSGreg Roach
8803f99a78SGreg Roach    // What to add to the cart?
8903f99a78SGreg Roach    private const ADD_RECORD_ONLY        = 'record';
9003f99a78SGreg Roach    private const ADD_CHILDREN           = 'children';
9103f99a78SGreg Roach    private const ADD_DESCENDANTS        = 'descendants';
9203f99a78SGreg Roach    private const ADD_PARENT_FAMILIES    = 'parents';
9303f99a78SGreg Roach    private const ADD_SPOUSE_FAMILIES    = 'spouses';
9403f99a78SGreg Roach    private const ADD_ANCESTORS          = 'ancestors';
9503f99a78SGreg Roach    private const ADD_ANCESTOR_FAMILIES  = 'families';
9603f99a78SGreg Roach    private const ADD_LINKED_INDIVIDUALS = 'linked';
9703f99a78SGreg Roach
985a78cd34SGreg Roach    // Routes that have a record which can be added to the clipboard
9916d6367aSGreg Roach    private const ROUTES_WITH_RECORDS = [
100f95e0480SGreg Roach        'Family'     => FamilyPage::class,
101f95e0480SGreg Roach        'Individual' => IndividualPage::class,
102f95e0480SGreg Roach        'Media'      => MediaPage::class,
103e8ded2caSGreg Roach        'Location'   => LocationPage::class,
104f95e0480SGreg Roach        'Note'       => NotePage::class,
105f95e0480SGreg Roach        'Repository' => RepositoryPage::class,
106f95e0480SGreg Roach        'Source'     => SourcePage::class,
107d45701ccSGreg Roach        'Submitter'  => SubmitterPage::class,
108c1010edaSGreg Roach    ];
1095a78cd34SGreg Roach
11049a243cbSGreg Roach    /** @var int The default access level for this module.  It can be changed in the control panel. */
11149a243cbSGreg Roach    protected $access_level = Auth::PRIV_USER;
11249a243cbSGreg Roach
113ca47b2adSGreg Roach    private GedcomExportService $gedcom_export_service;
11469c05a6eSGreg Roach
115ca47b2adSGreg Roach    private UserService $user_service;
116ca47b2adSGreg Roach
117ca47b2adSGreg Roach    private ResponseFactoryInterface $response_factory;
118ca47b2adSGreg Roach
119ca47b2adSGreg Roach    private StreamFactoryInterface $stream_factory;
120e5a6b4d4SGreg Roach
121e5a6b4d4SGreg Roach    /**
122e5a6b4d4SGreg Roach     * ClippingsCartModule constructor.
123e5a6b4d4SGreg Roach     *
12469c05a6eSGreg Roach     * @param GedcomExportService      $gedcom_export_service
125ca47b2adSGreg Roach     * @param ResponseFactoryInterface $response_factory
126ca47b2adSGreg Roach     * @param StreamFactoryInterface   $stream_factory
127e5a6b4d4SGreg Roach     * @param UserService              $user_service
128e5a6b4d4SGreg Roach     */
129ca47b2adSGreg Roach    public function __construct(
130ca47b2adSGreg Roach        GedcomExportService $gedcom_export_service,
131ca47b2adSGreg Roach        ResponseFactoryInterface $response_factory,
132ca47b2adSGreg Roach        StreamFactoryInterface $stream_factory,
133ca47b2adSGreg Roach        UserService $user_service
134ca47b2adSGreg Roach    ) {
13569c05a6eSGreg Roach        $this->gedcom_export_service = $gedcom_export_service;
136ca47b2adSGreg Roach        $this->response_factory      = $response_factory;
137ca47b2adSGreg Roach        $this->stream_factory        = $stream_factory;
138e5a6b4d4SGreg Roach        $this->user_service          = $user_service;
139e5a6b4d4SGreg Roach    }
140e5a6b4d4SGreg Roach
141e5a6b4d4SGreg Roach    /**
142961ec755SGreg Roach     * A sentence describing what this module does.
143961ec755SGreg Roach     *
144961ec755SGreg Roach     * @return string
145961ec755SGreg Roach     */
14649a243cbSGreg Roach    public function description(): string
147c1010edaSGreg Roach    {
148bbb76c12SGreg Roach        /* I18N: Description of the “Clippings cart” module */
149bbb76c12SGreg Roach        return I18N::translate('Select records from your family tree and save them as a GEDCOM file.');
1508c2e8227SGreg Roach    }
1518c2e8227SGreg Roach
1520ee13198SGreg Roach    /**
15349a243cbSGreg Roach     * The default position for this menu.  It can be changed in the control panel.
1540ee13198SGreg Roach     *
1550ee13198SGreg Roach     * @return int
1560ee13198SGreg Roach     */
1578f53f488SRico Sonntag    public function defaultMenuOrder(): int
158c1010edaSGreg Roach    {
159353b36abSGreg Roach        return 6;
1608c2e8227SGreg Roach    }
1618c2e8227SGreg Roach
1620ee13198SGreg Roach    /**
1630ee13198SGreg Roach     * A menu, to be added to the main application menu.
1640ee13198SGreg Roach     *
165aee13b6dSGreg Roach     * @param Tree $tree
166aee13b6dSGreg Roach     *
1670ee13198SGreg Roach     * @return Menu|null
1680ee13198SGreg Roach     */
16946295629SGreg Roach    public function getMenu(Tree $tree): ?Menu
170c1010edaSGreg Roach    {
171eb235819SGreg Roach        /** @var ServerRequestInterface $request */
1726ccdf4f0SGreg Roach        $request = app(ServerRequestInterface::class);
1738c2e8227SGreg Roach
174f7ab47b1SGreg Roach        $route = $request->getAttribute('route');
175de2aa325SGreg Roach        assert($route instanceof Route);
1765a78cd34SGreg Roach
177d45701ccSGreg Roach        $cart  = Session::get('cart', []);
178d45701ccSGreg Roach        $count = count($cart[$tree->name()] ?? []);
179d45701ccSGreg Roach        $badge = view('components/badge', ['count' => $count]);
180d45701ccSGreg Roach
1815a78cd34SGreg Roach        $submenus = [
182d45701ccSGreg Roach            new Menu($this->title() . ' ' . $badge, route('module', [
18326684e68SGreg Roach                'module' => $this->name(),
184c1010edaSGreg Roach                'action' => 'Show',
185d72b284aSGreg Roach                'tree'   => $tree->name(),
186c1010edaSGreg Roach            ]), 'menu-clippings-cart', ['rel' => 'nofollow']),
1875a78cd34SGreg Roach        ];
1885a78cd34SGreg Roach
1892b0d92b4SGreg Roach        $action = array_search($route->name, self::ROUTES_WITH_RECORDS, true);
190f95e0480SGreg Roach        if ($action !== false) {
1912b0d92b4SGreg Roach            $xref = $route->attributes['xref'];
192ddeb3354SGreg Roach            assert(is_string($xref));
193ddeb3354SGreg Roach
194c1010edaSGreg Roach            $add_route = route('module', [
19526684e68SGreg Roach                'module' => $this->name(),
196f95e0480SGreg Roach                'action' => 'Add' . $action,
197c1010edaSGreg Roach                'xref'   => $xref,
198d72b284aSGreg Roach                'tree'   => $tree->name(),
199c1010edaSGreg Roach            ]);
2005a78cd34SGreg Roach
20125b2dde3SGreg Roach            $submenus[] = new Menu(I18N::translate('Add to the clippings cart'), $add_route, 'menu-clippings-add', ['rel' => 'nofollow']);
2028c2e8227SGreg Roach        }
203cbc1590aSGreg Roach
2045a78cd34SGreg Roach        if (!$this->isCartEmpty($tree)) {
205c1010edaSGreg Roach            $submenus[] = new Menu(I18N::translate('Empty the clippings cart'), route('module', [
20626684e68SGreg Roach                'module' => $this->name(),
207c1010edaSGreg Roach                'action' => 'Empty',
208d72b284aSGreg Roach                'tree'   => $tree->name(),
209c1010edaSGreg Roach            ]), 'menu-clippings-empty', ['rel' => 'nofollow']);
210f95e0480SGreg Roach
211c1010edaSGreg Roach            $submenus[] = new Menu(I18N::translate('Download'), route('module', [
21226684e68SGreg Roach                'module' => $this->name(),
213c1010edaSGreg Roach                'action' => 'DownloadForm',
214d72b284aSGreg Roach                'tree'   => $tree->name(),
215c1010edaSGreg Roach            ]), 'menu-clippings-download', ['rel' => 'nofollow']);
2165a78cd34SGreg Roach        }
2175a78cd34SGreg Roach
21849a243cbSGreg Roach        return new Menu($this->title(), '#', 'menu-clippings', ['rel' => 'nofollow'], $submenus);
2198c2e8227SGreg Roach    }
2208c2e8227SGreg Roach
22176692c8bSGreg Roach    /**
222d45701ccSGreg Roach     * How should this module be identified in the control panel, etc.?
223d45701ccSGreg Roach     *
224d45701ccSGreg Roach     * @return string
225d45701ccSGreg Roach     */
226d45701ccSGreg Roach    public function title(): string
227d45701ccSGreg Roach    {
228d45701ccSGreg Roach        /* I18N: Name of a module */
229d45701ccSGreg Roach        return I18N::translate('Clippings cart');
230d45701ccSGreg Roach    }
231d45701ccSGreg Roach
232d45701ccSGreg Roach    /**
233d45701ccSGreg Roach     * @param Tree $tree
234d45701ccSGreg Roach     *
235d45701ccSGreg Roach     * @return bool
236d45701ccSGreg Roach     */
237d45701ccSGreg Roach    private function isCartEmpty(Tree $tree): bool
238d45701ccSGreg Roach    {
239d45701ccSGreg Roach        $cart     = Session::get('cart', []);
240d45701ccSGreg Roach        $contents = $cart[$tree->name()] ?? [];
241d45701ccSGreg Roach
242d45701ccSGreg Roach        return $contents === [];
243d45701ccSGreg Roach    }
244d45701ccSGreg Roach
245d45701ccSGreg Roach    /**
246d45701ccSGreg Roach     * @param ServerRequestInterface $request
247d45701ccSGreg Roach     *
248d45701ccSGreg Roach     * @return ResponseInterface
249d45701ccSGreg Roach     */
250d45701ccSGreg Roach    public function getDownloadFormAction(ServerRequestInterface $request): ResponseInterface
251d45701ccSGreg Roach    {
252d45701ccSGreg Roach        $tree = $request->getAttribute('tree');
253d45701ccSGreg Roach        assert($tree instanceof Tree);
254d45701ccSGreg Roach
255d45701ccSGreg Roach        $user  = $request->getAttribute('user');
256d45701ccSGreg Roach        $title = I18N::translate('Family tree clippings cart') . ' — ' . I18N::translate('Download');
257d45701ccSGreg Roach
258d45701ccSGreg Roach        return $this->viewResponse('modules/clippings/download', [
259d45701ccSGreg Roach            'is_manager' => Auth::isManager($tree, $user),
260d45701ccSGreg Roach            'is_member'  => Auth::isMember($tree, $user),
261d45701ccSGreg Roach            'module'     => $this->name(),
262d45701ccSGreg Roach            'title'      => $title,
263d45701ccSGreg Roach            'tree'       => $tree,
264d45701ccSGreg Roach        ]);
265d45701ccSGreg Roach    }
266d45701ccSGreg Roach
267d45701ccSGreg Roach    /**
2686ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
26976692c8bSGreg Roach     *
2706ccdf4f0SGreg Roach     * @return ResponseInterface
271f0448b68SGreg Roach     * @throws FilesystemException
27276692c8bSGreg Roach     */
273f95e0480SGreg Roach    public function postDownloadAction(ServerRequestInterface $request): ResponseInterface
274c1010edaSGreg Roach    {
27557ab2231SGreg Roach        $tree = $request->getAttribute('tree');
2764ea62551SGreg Roach        assert($tree instanceof Tree);
2774ea62551SGreg Roach
2786b9cb339SGreg Roach        $data_filesystem = Registry::filesystem()->data();
279a04bb9a2SGreg Roach
280b46c87bdSGreg Roach        $params = (array) $request->getParsedBody();
281b46c87bdSGreg Roach
282d45701ccSGreg Roach        $privatize_export = $params['privatize_export'] ?? 'none';
283e2ed7c79SGreg Roach
284e2ed7c79SGreg Roach        if ($privatize_export === 'none' && !Auth::isManager($tree)) {
285e2ed7c79SGreg Roach            $privatize_export = 'member';
286e2ed7c79SGreg Roach        }
287e2ed7c79SGreg Roach
288e2ed7c79SGreg Roach        if ($privatize_export === 'gedadmin' && !Auth::isManager($tree)) {
289e2ed7c79SGreg Roach            $privatize_export = 'member';
290e2ed7c79SGreg Roach        }
291e2ed7c79SGreg Roach
292e2ed7c79SGreg Roach        if ($privatize_export === 'user' && !Auth::isMember($tree)) {
293e2ed7c79SGreg Roach            $privatize_export = 'visitor';
294e2ed7c79SGreg Roach        }
295e2ed7c79SGreg Roach
296b46c87bdSGreg Roach        $convert = (bool) ($params['convert'] ?? false);
2978c2e8227SGreg Roach
29813abd6f3SGreg Roach        $cart = Session::get('cart', []);
2998c2e8227SGreg Roach
300aa6f03bbSGreg Roach        $xrefs = array_keys($cart[$tree->name()] ?? []);
301c8846facSGreg Roach        $xrefs = array_map('strval', $xrefs); // PHP converts numeric keys to integers.
3025a78cd34SGreg Roach
3035a78cd34SGreg Roach        // Create a new/empty .ZIP file
304a00baf47SGreg Roach        $temp_zip_file  = stream_get_meta_data(tmpfile())['uri'];
305f32d77e6SGreg Roach        $zip_provider   = new FilesystemZipArchiveProvider($temp_zip_file, 0755);
306f32d77e6SGreg Roach        $zip_adapter    = new ZipArchiveAdapter($zip_provider);
3077f996f6eSGreg Roach        $zip_filesystem = new Filesystem($zip_adapter);
3085a78cd34SGreg Roach
309fa695506SGreg Roach        $media_filesystem = $tree->mediaFilesystem($data_filesystem);
31061bf91b2SGreg Roach
3115a78cd34SGreg Roach        // Media file prefix
3125a78cd34SGreg Roach        $path = $tree->getPreference('MEDIA_DIRECTORY');
3135a78cd34SGreg Roach
31469c05a6eSGreg Roach        $encoding = $convert ? 'ANSI' : 'UTF-8';
31569c05a6eSGreg Roach
31669c05a6eSGreg Roach        $records = new Collection();
3175a78cd34SGreg Roach
3185a78cd34SGreg Roach        switch ($privatize_export) {
3195a78cd34SGreg Roach            case 'gedadmin':
3205a78cd34SGreg Roach                $access_level = Auth::PRIV_NONE;
3215a78cd34SGreg Roach                break;
3225a78cd34SGreg Roach            case 'user':
3235a78cd34SGreg Roach                $access_level = Auth::PRIV_USER;
3245a78cd34SGreg Roach                break;
3255a78cd34SGreg Roach            case 'visitor':
3265a78cd34SGreg Roach                $access_level = Auth::PRIV_PRIVATE;
3275a78cd34SGreg Roach                break;
3285a78cd34SGreg Roach            case 'none':
3295a78cd34SGreg Roach            default:
3305a78cd34SGreg Roach                $access_level = Auth::PRIV_HIDE;
3315a78cd34SGreg Roach                break;
3325a78cd34SGreg Roach        }
3335a78cd34SGreg Roach
3345a78cd34SGreg Roach        foreach ($xrefs as $xref) {
3356b9cb339SGreg Roach            $object = Registry::gedcomRecordFactory()->make($xref, $tree);
3365a78cd34SGreg Roach            // The object may have been deleted since we added it to the cart....
337bed27cedSGreg Roach            if ($object instanceof GedcomRecord) {
3385a78cd34SGreg Roach                $record = $object->privatizeGedcom($access_level);
3395a78cd34SGreg Roach                // Remove links to objects that aren't in the cart
3408d0ebef0SGreg Roach                preg_match_all('/\n1 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(\n[2-9].*)*/', $record, $matches, PREG_SET_ORDER);
3415a78cd34SGreg Roach                foreach ($matches as $match) {
342bf80ec58SGreg Roach                    if (!in_array($match[1], $xrefs, true)) {
3435a78cd34SGreg Roach                        $record = str_replace($match[0], '', $record);
3445a78cd34SGreg Roach                    }
3455a78cd34SGreg Roach                }
3468d0ebef0SGreg Roach                preg_match_all('/\n2 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(\n[3-9].*)*/', $record, $matches, PREG_SET_ORDER);
3475a78cd34SGreg Roach                foreach ($matches as $match) {
348bf80ec58SGreg Roach                    if (!in_array($match[1], $xrefs, true)) {
3495a78cd34SGreg Roach                        $record = str_replace($match[0], '', $record);
3505a78cd34SGreg Roach                    }
3515a78cd34SGreg Roach                }
3528d0ebef0SGreg Roach                preg_match_all('/\n3 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(\n[4-9].*)*/', $record, $matches, PREG_SET_ORDER);
3535a78cd34SGreg Roach                foreach ($matches as $match) {
354bf80ec58SGreg Roach                    if (!in_array($match[1], $xrefs, true)) {
3555a78cd34SGreg Roach                        $record = str_replace($match[0], '', $record);
3565a78cd34SGreg Roach                    }
3575a78cd34SGreg Roach                }
3585a78cd34SGreg Roach
359*f566cb16SGreg Roach                $records->add($record);
360*f566cb16SGreg Roach
361*f566cb16SGreg Roach                if ($object instanceof Media) {
36255167344SGreg Roach                    // Add the media files to the archive
3635a78cd34SGreg Roach                    foreach ($object->mediaFiles() as $media_file) {
364fa695506SGreg Roach                        $from = $media_file->filename();
365fa695506SGreg Roach                        $to   = $path . $media_file->filename();
366f0448b68SGreg Roach                        if (!$media_file->isExternal() && $media_filesystem->fileExists($from)) {
367fa695506SGreg Roach                            $zip_filesystem->writeStream($to, $media_filesystem->readStream($from));
3685a78cd34SGreg Roach                        }
3695a78cd34SGreg Roach                    }
3708c2e8227SGreg Roach                }
3718c2e8227SGreg Roach            }
3728c2e8227SGreg Roach        }
3738c2e8227SGreg Roach
37469c05a6eSGreg Roach        $stream = fopen('php://temp', 'wb+');
3755a78cd34SGreg Roach
37669c05a6eSGreg Roach        if ($stream === false) {
37769c05a6eSGreg Roach            throw new RuntimeException('Failed to create temporary stream');
3788c2e8227SGreg Roach        }
379cbc1590aSGreg Roach
38069c05a6eSGreg Roach        // We have already applied privacy filtering, so do not do it again.
38169c05a6eSGreg Roach        $this->gedcom_export_service->export($tree, $stream, false, $encoding, Auth::PRIV_HIDE, $path, $records);
38269c05a6eSGreg Roach        rewind($stream);
38369c05a6eSGreg Roach
3845a78cd34SGreg Roach        // Finally add the GEDCOM file to the .ZIP file.
38569c05a6eSGreg Roach        $zip_filesystem->writeStream('clippings.ged', $stream);
3865a78cd34SGreg Roach
3876ccdf4f0SGreg Roach        // Use a stream, so that we do not have to load the entire file into memory.
388ca47b2adSGreg Roach        $stream = $this->stream_factory->createStreamFromFile($temp_zip_file);
3895a78cd34SGreg Roach
390ca47b2adSGreg Roach        return $this->response_factory->createResponse()
3916ccdf4f0SGreg Roach            ->withBody($stream)
3921b3d4731SGreg Roach            ->withHeader('Content-Type', 'application/zip')
393bed27cedSGreg Roach            ->withHeader('Content-Disposition', 'attachment; filename="clippings.zip');
3948c2e8227SGreg Roach    }
3958c2e8227SGreg Roach
3968c2e8227SGreg Roach    /**
39757ab2231SGreg Roach     * @param ServerRequestInterface $request
39876692c8bSGreg Roach     *
3996ccdf4f0SGreg Roach     * @return ResponseInterface
4008c2e8227SGreg Roach     */
40157ab2231SGreg Roach    public function getEmptyAction(ServerRequestInterface $request): ResponseInterface
402c1010edaSGreg Roach    {
40357ab2231SGreg Roach        $tree = $request->getAttribute('tree');
4044ea62551SGreg Roach        assert($tree instanceof Tree);
4054ea62551SGreg Roach
4065a78cd34SGreg Roach        $cart                = Session::get('cart', []);
407aa6f03bbSGreg Roach        $cart[$tree->name()] = [];
4085a78cd34SGreg Roach        Session::put('cart', $cart);
4098c2e8227SGreg Roach
410c1010edaSGreg Roach        $url = route('module', [
41126684e68SGreg Roach            'module' => $this->name(),
412c1010edaSGreg Roach            'action' => 'Show',
413d72b284aSGreg Roach            'tree'   => $tree->name(),
414c1010edaSGreg Roach        ]);
4155a78cd34SGreg Roach
4166ccdf4f0SGreg Roach        return redirect($url);
4175a78cd34SGreg Roach    }
4185a78cd34SGreg Roach
4195a78cd34SGreg Roach    /**
4206ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
4215a78cd34SGreg Roach     *
4226ccdf4f0SGreg Roach     * @return ResponseInterface
4235a78cd34SGreg Roach     */
42457ab2231SGreg Roach    public function postRemoveAction(ServerRequestInterface $request): ResponseInterface
425c1010edaSGreg Roach    {
42657ab2231SGreg Roach        $tree = $request->getAttribute('tree');
42775964c75SGreg Roach        assert($tree instanceof Tree);
4285229eadeSGreg Roach
429d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
4305a78cd34SGreg Roach
4315a78cd34SGreg Roach        $cart = Session::get('cart', []);
432aa6f03bbSGreg Roach        unset($cart[$tree->name()][$xref]);
4335a78cd34SGreg Roach        Session::put('cart', $cart);
4345a78cd34SGreg Roach
435c1010edaSGreg Roach        $url = route('module', [
43626684e68SGreg Roach            'module' => $this->name(),
437c1010edaSGreg Roach            'action' => 'Show',
438d72b284aSGreg Roach            'tree'   => $tree->name(),
439c1010edaSGreg Roach        ]);
4405a78cd34SGreg Roach
4416ccdf4f0SGreg Roach        return redirect($url);
4425a78cd34SGreg Roach    }
4435a78cd34SGreg Roach
4445a78cd34SGreg Roach    /**
44557ab2231SGreg Roach     * @param ServerRequestInterface $request
4465a78cd34SGreg Roach     *
4476ccdf4f0SGreg Roach     * @return ResponseInterface
4485a78cd34SGreg Roach     */
44957ab2231SGreg Roach    public function getShowAction(ServerRequestInterface $request): ResponseInterface
450c1010edaSGreg Roach    {
45157ab2231SGreg Roach        $tree = $request->getAttribute('tree');
45275964c75SGreg Roach        assert($tree instanceof Tree);
45357ab2231SGreg Roach
4545a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/show', [
45577b78a63SRichard Cissée            'module'  => $this->name(),
4565a78cd34SGreg Roach            'records' => $this->allRecordsInCart($tree),
4575a78cd34SGreg Roach            'title'   => I18N::translate('Family tree clippings cart'),
4585a78cd34SGreg Roach            'tree'    => $tree,
4595a78cd34SGreg Roach        ]);
4605a78cd34SGreg Roach    }
4615a78cd34SGreg Roach
4625a78cd34SGreg Roach    /**
463d45701ccSGreg Roach     * Get all the records in the cart.
464d45701ccSGreg Roach     *
465d45701ccSGreg Roach     * @param Tree $tree
466d45701ccSGreg Roach     *
467d45701ccSGreg Roach     * @return GedcomRecord[]
468d45701ccSGreg Roach     */
469d45701ccSGreg Roach    private function allRecordsInCart(Tree $tree): array
470d45701ccSGreg Roach    {
471d45701ccSGreg Roach        $cart = Session::get('cart', []);
472d45701ccSGreg Roach
473d45701ccSGreg Roach        $xrefs = array_keys($cart[$tree->name()] ?? []);
474d45701ccSGreg Roach        $xrefs = array_map('strval', $xrefs); // PHP converts numeric keys to integers.
475d45701ccSGreg Roach
476d45701ccSGreg Roach        // Fetch all the records in the cart.
477d45701ccSGreg Roach        $records = array_map(static function (string $xref) use ($tree): ?GedcomRecord {
478d45701ccSGreg Roach            return Registry::gedcomRecordFactory()->make($xref, $tree);
479d45701ccSGreg Roach        }, $xrefs);
480d45701ccSGreg Roach
481d45701ccSGreg Roach        // Some records may have been deleted after they were added to the cart.
482d45701ccSGreg Roach        $records = array_filter($records);
483d45701ccSGreg Roach
484d45701ccSGreg Roach        // Group and sort.
485d45701ccSGreg Roach        uasort($records, static function (GedcomRecord $x, GedcomRecord $y): int {
486d45701ccSGreg Roach            return $x->tag() <=> $y->tag() ?: GedcomRecord::nameComparator()($x, $y);
487d45701ccSGreg Roach        });
488d45701ccSGreg Roach
489d45701ccSGreg Roach        return $records;
490d45701ccSGreg Roach    }
491d45701ccSGreg Roach
492d45701ccSGreg Roach    /**
4936ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
4945a78cd34SGreg Roach     *
4956ccdf4f0SGreg Roach     * @return ResponseInterface
4965a78cd34SGreg Roach     */
49757ab2231SGreg Roach    public function getAddFamilyAction(ServerRequestInterface $request): ResponseInterface
498c1010edaSGreg Roach    {
49957ab2231SGreg Roach        $tree = $request->getAttribute('tree');
50075964c75SGreg Roach        assert($tree instanceof Tree);
5015229eadeSGreg Roach
502d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
5035a78cd34SGreg Roach
5046b9cb339SGreg Roach        $family = Registry::familyFactory()->make($xref, $tree);
505d45701ccSGreg Roach        $family = Auth::checkFamilyAccess($family);
506d45701ccSGreg Roach        $name   = $family->fullName();
5075a78cd34SGreg Roach
508d45701ccSGreg Roach        $options = [
50903f99a78SGreg Roach            self::ADD_RECORD_ONLY => $name,
510bbb76c12SGreg Roach            /* I18N: %s is a family (husband + wife) */
51103f99a78SGreg Roach            self::ADD_CHILDREN    => I18N::translate('%s and their children', $name),
512bbb76c12SGreg Roach            /* I18N: %s is a family (husband + wife) */
51303f99a78SGreg Roach            self::ADD_DESCENDANTS => I18N::translate('%s and their descendants', $name),
5145a78cd34SGreg Roach        ];
515d45701ccSGreg Roach
516d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
517d45701ccSGreg Roach
518d45701ccSGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
519d45701ccSGreg Roach            'options' => $options,
520d45701ccSGreg Roach            'record'  => $family,
521d45701ccSGreg Roach            'title'   => $title,
522d45701ccSGreg Roach            'tree'    => $tree,
523d45701ccSGreg Roach        ]);
5245a78cd34SGreg Roach    }
5255a78cd34SGreg Roach
5265a78cd34SGreg Roach    /**
5276ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
5285a78cd34SGreg Roach     *
5296ccdf4f0SGreg Roach     * @return ResponseInterface
5305a78cd34SGreg Roach     */
53157ab2231SGreg Roach    public function postAddFamilyAction(ServerRequestInterface $request): ResponseInterface
532c1010edaSGreg Roach    {
53357ab2231SGreg Roach        $tree = $request->getAttribute('tree');
5344ea62551SGreg Roach        assert($tree instanceof Tree);
5354ea62551SGreg Roach
536b46c87bdSGreg Roach        $params = (array) $request->getParsedBody();
537b46c87bdSGreg Roach
538d45701ccSGreg Roach        $xref   = $params['xref'] ?? '';
539d45701ccSGreg Roach        $option = $params['option'] ?? '';
5405a78cd34SGreg Roach
5416b9cb339SGreg Roach        $family = Registry::familyFactory()->make($xref, $tree);
542d45701ccSGreg Roach        $family = Auth::checkFamilyAccess($family);
5435a78cd34SGreg Roach
5445a78cd34SGreg Roach        switch ($option) {
54503f99a78SGreg Roach            case self::ADD_RECORD_ONLY:
5465a78cd34SGreg Roach                $this->addFamilyToCart($family);
5475a78cd34SGreg Roach                break;
5485a78cd34SGreg Roach
54903f99a78SGreg Roach            case self::ADD_CHILDREN:
5505a78cd34SGreg Roach                $this->addFamilyAndChildrenToCart($family);
5515a78cd34SGreg Roach                break;
5525a78cd34SGreg Roach
55303f99a78SGreg Roach            case self::ADD_DESCENDANTS:
5545a78cd34SGreg Roach                $this->addFamilyAndDescendantsToCart($family);
5555a78cd34SGreg Roach                break;
5565a78cd34SGreg Roach        }
5575a78cd34SGreg Roach
5586ccdf4f0SGreg Roach        return redirect($family->url());
5595a78cd34SGreg Roach    }
5605a78cd34SGreg Roach
5615a78cd34SGreg Roach
5625a78cd34SGreg Roach    /**
5635a78cd34SGreg Roach     * @param Family $family
56418d7a90dSGreg Roach     *
56518d7a90dSGreg Roach     * @return void
5665a78cd34SGreg Roach     */
567d45701ccSGreg Roach    protected function addFamilyAndChildrenToCart(Family $family): void
568c1010edaSGreg Roach    {
569d45701ccSGreg Roach        $this->addFamilyToCart($family);
5705a78cd34SGreg Roach
57139ca88baSGreg Roach        foreach ($family->children() as $child) {
572d45701ccSGreg Roach            $this->addIndividualToCart($child);
5735a78cd34SGreg Roach        }
5745a78cd34SGreg Roach    }
5755a78cd34SGreg Roach
5765a78cd34SGreg Roach    /**
5775a78cd34SGreg Roach     * @param Family $family
57818d7a90dSGreg Roach     *
57918d7a90dSGreg Roach     * @return void
5805a78cd34SGreg Roach     */
581d45701ccSGreg Roach    protected function addFamilyAndDescendantsToCart(Family $family): void
582c1010edaSGreg Roach    {
583d45701ccSGreg Roach        $this->addFamilyAndChildrenToCart($family);
5845a78cd34SGreg Roach
58539ca88baSGreg Roach        foreach ($family->children() as $child) {
58639ca88baSGreg Roach            foreach ($child->spouseFamilies() as $child_family) {
5875a78cd34SGreg Roach                $this->addFamilyAndDescendantsToCart($child_family);
5885a78cd34SGreg Roach            }
5895a78cd34SGreg Roach        }
5905a78cd34SGreg Roach    }
5915a78cd34SGreg Roach
5925a78cd34SGreg Roach    /**
5936ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
5945a78cd34SGreg Roach     *
5956ccdf4f0SGreg Roach     * @return ResponseInterface
5965a78cd34SGreg Roach     */
59757ab2231SGreg Roach    public function getAddIndividualAction(ServerRequestInterface $request): ResponseInterface
598c1010edaSGreg Roach    {
59957ab2231SGreg Roach        $tree = $request->getAttribute('tree');
60075964c75SGreg Roach        assert($tree instanceof Tree);
6015229eadeSGreg Roach
602d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
6035a78cd34SGreg Roach
6046b9cb339SGreg Roach        $individual = Registry::individualFactory()->make($xref, $tree);
605d45701ccSGreg Roach        $individual = Auth::checkIndividualAccess($individual);
606d45701ccSGreg Roach        $name       = $individual->fullName();
6075a78cd34SGreg Roach
60839ca88baSGreg Roach        if ($individual->sex() === 'F') {
609d45701ccSGreg Roach            $options = [
61003f99a78SGreg Roach                self::ADD_RECORD_ONLY       => $name,
61103f99a78SGreg Roach                self::ADD_PARENT_FAMILIES   => I18N::translate('%s, her parents and siblings', $name),
61203f99a78SGreg Roach                self::ADD_SPOUSE_FAMILIES   => I18N::translate('%s, her spouses and children', $name),
61303f99a78SGreg Roach                self::ADD_ANCESTORS         => I18N::translate('%s and her ancestors', $name),
61403f99a78SGreg Roach                self::ADD_ANCESTOR_FAMILIES => I18N::translate('%s, her ancestors and their families', $name),
61503f99a78SGreg Roach                self::ADD_DESCENDANTS       => I18N::translate('%s, her spouses and descendants', $name),
6165a78cd34SGreg Roach            ];
617d45701ccSGreg Roach        } else {
618d45701ccSGreg Roach            $options = [
61903f99a78SGreg Roach                self::ADD_RECORD_ONLY       => $name,
62003f99a78SGreg Roach                self::ADD_PARENT_FAMILIES   => I18N::translate('%s, his parents and siblings', $name),
62103f99a78SGreg Roach                self::ADD_SPOUSE_FAMILIES   => I18N::translate('%s, his spouses and children', $name),
62203f99a78SGreg Roach                self::ADD_ANCESTORS         => I18N::translate('%s and his ancestors', $name),
62303f99a78SGreg Roach                self::ADD_ANCESTOR_FAMILIES => I18N::translate('%s, his ancestors and their families', $name),
62403f99a78SGreg Roach                self::ADD_DESCENDANTS       => I18N::translate('%s, his spouses and descendants', $name),
6255a78cd34SGreg Roach            ];
6265a78cd34SGreg Roach        }
6275a78cd34SGreg Roach
628d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
629d45701ccSGreg Roach
630d45701ccSGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
631d45701ccSGreg Roach            'options' => $options,
632d45701ccSGreg Roach            'record'  => $individual,
633d45701ccSGreg Roach            'title'   => $title,
634d45701ccSGreg Roach            'tree'    => $tree,
635d45701ccSGreg Roach        ]);
636d45701ccSGreg Roach    }
637d45701ccSGreg Roach
6385a78cd34SGreg Roach    /**
6396ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
6405a78cd34SGreg Roach     *
6416ccdf4f0SGreg Roach     * @return ResponseInterface
6425a78cd34SGreg Roach     */
64357ab2231SGreg Roach    public function postAddIndividualAction(ServerRequestInterface $request): ResponseInterface
644c1010edaSGreg Roach    {
64557ab2231SGreg Roach        $tree = $request->getAttribute('tree');
6464ea62551SGreg Roach        assert($tree instanceof Tree);
6474ea62551SGreg Roach
648b46c87bdSGreg Roach        $params = (array) $request->getParsedBody();
649b46c87bdSGreg Roach
650d45701ccSGreg Roach        $xref   = $params['xref'] ?? '';
651d45701ccSGreg Roach        $option = $params['option'] ?? '';
6525a78cd34SGreg Roach
6536b9cb339SGreg Roach        $individual = Registry::individualFactory()->make($xref, $tree);
654d45701ccSGreg Roach        $individual = Auth::checkIndividualAccess($individual);
6555a78cd34SGreg Roach
6565a78cd34SGreg Roach        switch ($option) {
65703f99a78SGreg Roach            case self::ADD_RECORD_ONLY:
658d45701ccSGreg Roach                $this->addIndividualToCart($individual);
6595a78cd34SGreg Roach                break;
6605a78cd34SGreg Roach
66103f99a78SGreg Roach            case self::ADD_PARENT_FAMILIES:
66239ca88baSGreg Roach                foreach ($individual->childFamilies() as $family) {
6635a78cd34SGreg Roach                    $this->addFamilyAndChildrenToCart($family);
6645a78cd34SGreg Roach                }
6655a78cd34SGreg Roach                break;
6665a78cd34SGreg Roach
66703f99a78SGreg Roach            case self::ADD_SPOUSE_FAMILIES:
66839ca88baSGreg Roach                foreach ($individual->spouseFamilies() as $family) {
6695a78cd34SGreg Roach                    $this->addFamilyAndChildrenToCart($family);
6705a78cd34SGreg Roach                }
6715a78cd34SGreg Roach                break;
6725a78cd34SGreg Roach
67303f99a78SGreg Roach            case self::ADD_ANCESTORS:
6745a78cd34SGreg Roach                $this->addAncestorsToCart($individual);
6755a78cd34SGreg Roach                break;
6765a78cd34SGreg Roach
67703f99a78SGreg Roach            case self::ADD_ANCESTOR_FAMILIES:
6785a78cd34SGreg Roach                $this->addAncestorFamiliesToCart($individual);
6795a78cd34SGreg Roach                break;
6805a78cd34SGreg Roach
68103f99a78SGreg Roach            case self::ADD_DESCENDANTS:
68239ca88baSGreg Roach                foreach ($individual->spouseFamilies() as $family) {
6835a78cd34SGreg Roach                    $this->addFamilyAndDescendantsToCart($family);
6845a78cd34SGreg Roach                }
6855a78cd34SGreg Roach                break;
6865a78cd34SGreg Roach        }
6875a78cd34SGreg Roach
6886ccdf4f0SGreg Roach        return redirect($individual->url());
6895a78cd34SGreg Roach    }
6905a78cd34SGreg Roach
6915a78cd34SGreg Roach    /**
6925a78cd34SGreg Roach     * @param Individual $individual
69318d7a90dSGreg Roach     *
69418d7a90dSGreg Roach     * @return void
6955a78cd34SGreg Roach     */
696d45701ccSGreg Roach    protected function addAncestorsToCart(Individual $individual): void
697c1010edaSGreg Roach    {
698d45701ccSGreg Roach        $this->addIndividualToCart($individual);
6995a78cd34SGreg Roach
70039ca88baSGreg Roach        foreach ($individual->childFamilies() as $family) {
701d45701ccSGreg Roach            $this->addFamilyToCart($family);
7028df4c68dSGreg Roach
70339ca88baSGreg Roach            foreach ($family->spouses() as $parent) {
7045a78cd34SGreg Roach                $this->addAncestorsToCart($parent);
7055a78cd34SGreg Roach            }
7065a78cd34SGreg Roach        }
7075a78cd34SGreg Roach    }
7085a78cd34SGreg Roach
7095a78cd34SGreg Roach    /**
7105a78cd34SGreg Roach     * @param Individual $individual
71118d7a90dSGreg Roach     *
71218d7a90dSGreg Roach     * @return void
7135a78cd34SGreg Roach     */
714d45701ccSGreg Roach    protected function addAncestorFamiliesToCart(Individual $individual): void
715c1010edaSGreg Roach    {
71639ca88baSGreg Roach        foreach ($individual->childFamilies() as $family) {
7175a78cd34SGreg Roach            $this->addFamilyAndChildrenToCart($family);
7188df4c68dSGreg Roach
71939ca88baSGreg Roach            foreach ($family->spouses() as $parent) {
720cad6d3f3SGreg Roach                $this->addAncestorFamiliesToCart($parent);
7215a78cd34SGreg Roach            }
7225a78cd34SGreg Roach        }
7235a78cd34SGreg Roach    }
7245a78cd34SGreg Roach
7255a78cd34SGreg Roach    /**
7266ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
7275a78cd34SGreg Roach     *
7286ccdf4f0SGreg Roach     * @return ResponseInterface
7295a78cd34SGreg Roach     */
730e8ded2caSGreg Roach    public function getAddLocationAction(ServerRequestInterface $request): ResponseInterface
731e8ded2caSGreg Roach    {
732e8ded2caSGreg Roach        $tree = $request->getAttribute('tree');
733e8ded2caSGreg Roach        assert($tree instanceof Tree);
734e8ded2caSGreg Roach
735e8ded2caSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
736e8ded2caSGreg Roach
737e8ded2caSGreg Roach        $location = Registry::locationFactory()->make($xref, $tree);
738e8ded2caSGreg Roach        $location = Auth::checkLocationAccess($location);
739e8ded2caSGreg Roach        $name     = $location->fullName();
740e8ded2caSGreg Roach
741e8ded2caSGreg Roach        $options = [
74203f99a78SGreg Roach            self::ADD_RECORD_ONLY => $name,
743e8ded2caSGreg Roach        ];
744e8ded2caSGreg Roach
745e8ded2caSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
746e8ded2caSGreg Roach
747e8ded2caSGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
748e8ded2caSGreg Roach            'options' => $options,
749e8ded2caSGreg Roach            'record'  => $location,
750e8ded2caSGreg Roach            'title'   => $title,
751e8ded2caSGreg Roach            'tree'    => $tree,
752e8ded2caSGreg Roach        ]);
753e8ded2caSGreg Roach    }
754e8ded2caSGreg Roach
755e8ded2caSGreg Roach    /**
756e8ded2caSGreg Roach     * @param ServerRequestInterface $request
757e8ded2caSGreg Roach     *
758e8ded2caSGreg Roach     * @return ResponseInterface
759e8ded2caSGreg Roach     */
760e8ded2caSGreg Roach    public function postAddLocationAction(ServerRequestInterface $request): ResponseInterface
761e8ded2caSGreg Roach    {
762e8ded2caSGreg Roach        $tree = $request->getAttribute('tree');
763e8ded2caSGreg Roach        assert($tree instanceof Tree);
764e8ded2caSGreg Roach
765e8ded2caSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
766e8ded2caSGreg Roach
767e8ded2caSGreg Roach        $location = Registry::locationFactory()->make($xref, $tree);
768e8ded2caSGreg Roach        $location = Auth::checkLocationAccess($location);
769e8ded2caSGreg Roach
770e8ded2caSGreg Roach        $this->addLocationToCart($location);
771e8ded2caSGreg Roach
772e8ded2caSGreg Roach        return redirect($location->url());
773e8ded2caSGreg Roach    }
774e8ded2caSGreg Roach
775e8ded2caSGreg Roach    /**
776e8ded2caSGreg Roach     * @param ServerRequestInterface $request
777e8ded2caSGreg Roach     *
778e8ded2caSGreg Roach     * @return ResponseInterface
779e8ded2caSGreg Roach     */
78057ab2231SGreg Roach    public function getAddMediaAction(ServerRequestInterface $request): ResponseInterface
781c1010edaSGreg Roach    {
78257ab2231SGreg Roach        $tree = $request->getAttribute('tree');
78375964c75SGreg Roach        assert($tree instanceof Tree);
7845229eadeSGreg Roach
785d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
7865a78cd34SGreg Roach
7876b9cb339SGreg Roach        $media = Registry::mediaFactory()->make($xref, $tree);
788d45701ccSGreg Roach        $media = Auth::checkMediaAccess($media);
789d45701ccSGreg Roach        $name  = $media->fullName();
7905a78cd34SGreg Roach
791d45701ccSGreg Roach        $options = [
79203f99a78SGreg Roach            self::ADD_RECORD_ONLY => $name,
793d45701ccSGreg Roach        ];
7945a78cd34SGreg Roach
795d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
7965a78cd34SGreg Roach
7975a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
7985a78cd34SGreg Roach            'options' => $options,
7995a78cd34SGreg Roach            'record'  => $media,
8005a78cd34SGreg Roach            'title'   => $title,
8015a78cd34SGreg Roach            'tree'    => $tree,
8025a78cd34SGreg Roach        ]);
8035a78cd34SGreg Roach    }
8045a78cd34SGreg Roach
8055a78cd34SGreg Roach    /**
8066ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
8075a78cd34SGreg Roach     *
8086ccdf4f0SGreg Roach     * @return ResponseInterface
8095a78cd34SGreg Roach     */
81057ab2231SGreg Roach    public function postAddMediaAction(ServerRequestInterface $request): ResponseInterface
811c1010edaSGreg Roach    {
81257ab2231SGreg Roach        $tree = $request->getAttribute('tree');
81375964c75SGreg Roach        assert($tree instanceof Tree);
8145229eadeSGreg Roach
815d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
8165a78cd34SGreg Roach
8176b9cb339SGreg Roach        $media = Registry::mediaFactory()->make($xref, $tree);
818d45701ccSGreg Roach        $media = Auth::checkMediaAccess($media);
8195a78cd34SGreg Roach
820d45701ccSGreg Roach        $this->addMediaToCart($media);
8215a78cd34SGreg Roach
8226ccdf4f0SGreg Roach        return redirect($media->url());
8235a78cd34SGreg Roach    }
8245a78cd34SGreg Roach
8255a78cd34SGreg Roach    /**
8266ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
8275a78cd34SGreg Roach     *
8286ccdf4f0SGreg Roach     * @return ResponseInterface
8295a78cd34SGreg Roach     */
83057ab2231SGreg Roach    public function getAddNoteAction(ServerRequestInterface $request): ResponseInterface
831c1010edaSGreg Roach    {
83257ab2231SGreg Roach        $tree = $request->getAttribute('tree');
83375964c75SGreg Roach        assert($tree instanceof Tree);
8345229eadeSGreg Roach
835d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
8365a78cd34SGreg Roach
8376b9cb339SGreg Roach        $note = Registry::noteFactory()->make($xref, $tree);
838d45701ccSGreg Roach        $note = Auth::checkNoteAccess($note);
839d45701ccSGreg Roach        $name = $note->fullName();
8405a78cd34SGreg Roach
841d45701ccSGreg Roach        $options = [
84203f99a78SGreg Roach            self::ADD_RECORD_ONLY => $name,
843d45701ccSGreg Roach        ];
8445a78cd34SGreg Roach
845d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
8465a78cd34SGreg Roach
8475a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
8485a78cd34SGreg Roach            'options' => $options,
8495a78cd34SGreg Roach            'record'  => $note,
8505a78cd34SGreg Roach            'title'   => $title,
8515a78cd34SGreg Roach            'tree'    => $tree,
8525a78cd34SGreg Roach        ]);
8535a78cd34SGreg Roach    }
8545a78cd34SGreg Roach
8555a78cd34SGreg Roach    /**
8566ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
8575a78cd34SGreg Roach     *
8586ccdf4f0SGreg Roach     * @return ResponseInterface
8595a78cd34SGreg Roach     */
86057ab2231SGreg Roach    public function postAddNoteAction(ServerRequestInterface $request): ResponseInterface
861c1010edaSGreg Roach    {
86257ab2231SGreg Roach        $tree = $request->getAttribute('tree');
86375964c75SGreg Roach        assert($tree instanceof Tree);
8645229eadeSGreg Roach
865d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
8665a78cd34SGreg Roach
8676b9cb339SGreg Roach        $note = Registry::noteFactory()->make($xref, $tree);
868d45701ccSGreg Roach        $note = Auth::checkNoteAccess($note);
8695a78cd34SGreg Roach
870d45701ccSGreg Roach        $this->addNoteToCart($note);
8715a78cd34SGreg Roach
8726ccdf4f0SGreg Roach        return redirect($note->url());
8735a78cd34SGreg Roach    }
8745a78cd34SGreg Roach
8755a78cd34SGreg Roach    /**
8766ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
8775a78cd34SGreg Roach     *
8786ccdf4f0SGreg Roach     * @return ResponseInterface
8795a78cd34SGreg Roach     */
88057ab2231SGreg Roach    public function getAddRepositoryAction(ServerRequestInterface $request): ResponseInterface
881c1010edaSGreg Roach    {
88257ab2231SGreg Roach        $tree = $request->getAttribute('tree');
88375964c75SGreg Roach        assert($tree instanceof Tree);
8845229eadeSGreg Roach
885d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
8865a78cd34SGreg Roach
8876b9cb339SGreg Roach        $repository = Registry::repositoryFactory()->make($xref, $tree);
888d45701ccSGreg Roach        $repository = Auth::checkRepositoryAccess($repository);
889d45701ccSGreg Roach        $name       = $repository->fullName();
8905a78cd34SGreg Roach
891d45701ccSGreg Roach        $options = [
89203f99a78SGreg Roach            self::ADD_RECORD_ONLY => $name,
893d45701ccSGreg Roach        ];
8945a78cd34SGreg Roach
895d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
8965a78cd34SGreg Roach
8975a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
8985a78cd34SGreg Roach            'options' => $options,
8995a78cd34SGreg Roach            'record'  => $repository,
9005a78cd34SGreg Roach            'title'   => $title,
9015a78cd34SGreg Roach            'tree'    => $tree,
9025a78cd34SGreg Roach        ]);
9035a78cd34SGreg Roach    }
9045a78cd34SGreg Roach
9055a78cd34SGreg Roach    /**
9066ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
9075a78cd34SGreg Roach     *
9086ccdf4f0SGreg Roach     * @return ResponseInterface
9095a78cd34SGreg Roach     */
91057ab2231SGreg Roach    public function postAddRepositoryAction(ServerRequestInterface $request): ResponseInterface
911c1010edaSGreg Roach    {
91257ab2231SGreg Roach        $tree = $request->getAttribute('tree');
91375964c75SGreg Roach        assert($tree instanceof Tree);
9145229eadeSGreg Roach
915d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
9165a78cd34SGreg Roach
9176b9cb339SGreg Roach        $repository = Registry::repositoryFactory()->make($xref, $tree);
918d45701ccSGreg Roach        $repository = Auth::checkRepositoryAccess($repository);
9195a78cd34SGreg Roach
920d45701ccSGreg Roach        $this->addRepositoryToCart($repository);
921d45701ccSGreg Roach
922d45701ccSGreg Roach        foreach ($repository->linkedSources('REPO') as $source) {
923d45701ccSGreg Roach            $this->addSourceToCart($source);
9245a78cd34SGreg Roach        }
9255a78cd34SGreg Roach
9266ccdf4f0SGreg Roach        return redirect($repository->url());
9275a78cd34SGreg Roach    }
9285a78cd34SGreg Roach
9295a78cd34SGreg Roach    /**
9306ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
9315a78cd34SGreg Roach     *
9326ccdf4f0SGreg Roach     * @return ResponseInterface
9335a78cd34SGreg Roach     */
93457ab2231SGreg Roach    public function getAddSourceAction(ServerRequestInterface $request): ResponseInterface
935c1010edaSGreg Roach    {
93657ab2231SGreg Roach        $tree = $request->getAttribute('tree');
93775964c75SGreg Roach        assert($tree instanceof Tree);
9385229eadeSGreg Roach
939d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
9405a78cd34SGreg Roach
9416b9cb339SGreg Roach        $source = Registry::sourceFactory()->make($xref, $tree);
942d45701ccSGreg Roach        $source = Auth::checkSourceAccess($source);
943d45701ccSGreg Roach        $name   = $source->fullName();
9445a78cd34SGreg Roach
945d45701ccSGreg Roach        $options = [
94603f99a78SGreg Roach            self::ADD_RECORD_ONLY        => $name,
94703f99a78SGreg Roach            self::ADD_LINKED_INDIVIDUALS => I18N::translate('%s and the individuals that reference it.', $name),
948d45701ccSGreg Roach        ];
9495a78cd34SGreg Roach
950d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
9515a78cd34SGreg Roach
9525a78cd34SGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
9535a78cd34SGreg Roach            'options' => $options,
9545a78cd34SGreg Roach            'record'  => $source,
9555a78cd34SGreg Roach            'title'   => $title,
9565a78cd34SGreg Roach            'tree'    => $tree,
9575a78cd34SGreg Roach        ]);
9585a78cd34SGreg Roach    }
9595a78cd34SGreg Roach
9605a78cd34SGreg Roach    /**
9616ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
9625a78cd34SGreg Roach     *
9636ccdf4f0SGreg Roach     * @return ResponseInterface
9645a78cd34SGreg Roach     */
96557ab2231SGreg Roach    public function postAddSourceAction(ServerRequestInterface $request): ResponseInterface
966c1010edaSGreg Roach    {
96757ab2231SGreg Roach        $tree = $request->getAttribute('tree');
96875964c75SGreg Roach        assert($tree instanceof Tree);
9695229eadeSGreg Roach
970b46c87bdSGreg Roach        $params = (array) $request->getParsedBody();
971b46c87bdSGreg Roach
972d45701ccSGreg Roach        $xref   = $params['xref'] ?? '';
973d45701ccSGreg Roach        $option = $params['option'] ?? '';
9745a78cd34SGreg Roach
9756b9cb339SGreg Roach        $source = Registry::sourceFactory()->make($xref, $tree);
976d45701ccSGreg Roach        $source = Auth::checkSourceAccess($source);
9775a78cd34SGreg Roach
978d45701ccSGreg Roach        $this->addSourceToCart($source);
9795a78cd34SGreg Roach
98003f99a78SGreg Roach        if ($option === self::ADD_LINKED_INDIVIDUALS) {
9815a78cd34SGreg Roach            foreach ($source->linkedIndividuals('SOUR') as $individual) {
982d45701ccSGreg Roach                $this->addIndividualToCart($individual);
9835a78cd34SGreg Roach            }
9845a78cd34SGreg Roach            foreach ($source->linkedFamilies('SOUR') as $family) {
985d45701ccSGreg Roach                $this->addFamilyToCart($family);
9865a78cd34SGreg Roach            }
9875a78cd34SGreg Roach        }
9885a78cd34SGreg Roach
9896ccdf4f0SGreg Roach        return redirect($source->url());
9905a78cd34SGreg Roach    }
9915a78cd34SGreg Roach
9925a78cd34SGreg Roach    /**
993d45701ccSGreg Roach     * @param ServerRequestInterface $request
9945a78cd34SGreg Roach     *
995d45701ccSGreg Roach     * @return ResponseInterface
9965a78cd34SGreg Roach     */
997d45701ccSGreg Roach    public function getAddSubmitterAction(ServerRequestInterface $request): ResponseInterface
998c1010edaSGreg Roach    {
999d45701ccSGreg Roach        $tree = $request->getAttribute('tree');
1000d45701ccSGreg Roach        assert($tree instanceof Tree);
10015a78cd34SGreg Roach
1002d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
10035a78cd34SGreg Roach
1004d45701ccSGreg Roach        $submitter = Registry::submitterFactory()->make($xref, $tree);
1005d45701ccSGreg Roach        $submitter = Auth::checkSubmitterAccess($submitter);
1006d45701ccSGreg Roach        $name      = $submitter->fullName();
10075a78cd34SGreg Roach
1008d45701ccSGreg Roach        $options = [
100903f99a78SGreg Roach            self::ADD_RECORD_ONLY => $name,
1010d45701ccSGreg Roach        ];
10115a78cd34SGreg Roach
1012d45701ccSGreg Roach        $title = I18N::translate('Add %s to the clippings cart', $name);
10135a78cd34SGreg Roach
1014d45701ccSGreg Roach        return $this->viewResponse('modules/clippings/add-options', [
1015d45701ccSGreg Roach            'options' => $options,
1016d45701ccSGreg Roach            'record'  => $submitter,
1017d45701ccSGreg Roach            'title'   => $title,
1018d45701ccSGreg Roach            'tree'    => $tree,
1019d45701ccSGreg Roach        ]);
10205a78cd34SGreg Roach    }
10215a78cd34SGreg Roach
10225a78cd34SGreg Roach    /**
1023d45701ccSGreg Roach     * @param ServerRequestInterface $request
10245a78cd34SGreg Roach     *
1025d45701ccSGreg Roach     * @return ResponseInterface
10265a78cd34SGreg Roach     */
1027d45701ccSGreg Roach    public function postAddSubmitterAction(ServerRequestInterface $request): ResponseInterface
1028d45701ccSGreg Roach    {
1029d45701ccSGreg Roach        $tree = $request->getAttribute('tree');
1030d45701ccSGreg Roach        assert($tree instanceof Tree);
1031d45701ccSGreg Roach
1032d45701ccSGreg Roach        $xref = $request->getQueryParams()['xref'] ?? '';
1033d45701ccSGreg Roach
1034d45701ccSGreg Roach        $submitter = Registry::submitterFactory()->make($xref, $tree);
1035d45701ccSGreg Roach        $submitter = Auth::checkSubmitterAccess($submitter);
1036d45701ccSGreg Roach
1037d45701ccSGreg Roach        $this->addSubmitterToCart($submitter);
1038d45701ccSGreg Roach
1039d45701ccSGreg Roach        return redirect($submitter->url());
1040d45701ccSGreg Roach    }
1041d45701ccSGreg Roach
1042d45701ccSGreg Roach    /**
1043d45701ccSGreg Roach     * @param Family $family
1044d45701ccSGreg Roach     */
1045d45701ccSGreg Roach    protected function addFamilyToCart(Family $family): void
1046c1010edaSGreg Roach    {
10475a78cd34SGreg Roach        $cart = Session::get('cart', []);
1048d45701ccSGreg Roach        $tree = $family->tree()->name();
1049d45701ccSGreg Roach        $xref = $family->xref();
10505a78cd34SGreg Roach
1051d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1052d45701ccSGreg Roach            $cart[$tree][$xref] = true;
10535a78cd34SGreg Roach
1054d45701ccSGreg Roach            Session::put('cart', $cart);
10555a78cd34SGreg Roach
1056d45701ccSGreg Roach            foreach ($family->spouses() as $spouse) {
1057d45701ccSGreg Roach                $this->addIndividualToCart($spouse);
10585a78cd34SGreg Roach            }
10595a78cd34SGreg Roach
1060e8ded2caSGreg Roach            $this->addLocationLinksToCart($family);
1061d45701ccSGreg Roach            $this->addMediaLinksToCart($family);
1062d45701ccSGreg Roach            $this->addNoteLinksToCart($family);
1063d45701ccSGreg Roach            $this->addSourceLinksToCart($family);
1064d45701ccSGreg Roach            $this->addSubmitterLinksToCart($family);
1065d45701ccSGreg Roach        }
1066d45701ccSGreg Roach    }
1067d45701ccSGreg Roach
1068d45701ccSGreg Roach    /**
1069d45701ccSGreg Roach     * @param Individual $individual
1070d45701ccSGreg Roach     */
1071d45701ccSGreg Roach    protected function addIndividualToCart(Individual $individual): void
1072d45701ccSGreg Roach    {
1073d45701ccSGreg Roach        $cart = Session::get('cart', []);
1074d45701ccSGreg Roach        $tree = $individual->tree()->name();
1075d45701ccSGreg Roach        $xref = $individual->xref();
1076d45701ccSGreg Roach
1077d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1078d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1079d45701ccSGreg Roach
1080d45701ccSGreg Roach            Session::put('cart', $cart);
1081d45701ccSGreg Roach
1082e8ded2caSGreg Roach            $this->addLocationLinksToCart($individual);
1083d45701ccSGreg Roach            $this->addMediaLinksToCart($individual);
1084d45701ccSGreg Roach            $this->addNoteLinksToCart($individual);
1085d45701ccSGreg Roach            $this->addSourceLinksToCart($individual);
1086d45701ccSGreg Roach        }
1087d45701ccSGreg Roach    }
1088d45701ccSGreg Roach
1089d45701ccSGreg Roach    /**
1090e8ded2caSGreg Roach     * @param Location $location
1091e8ded2caSGreg Roach     */
1092e8ded2caSGreg Roach    protected function addLocationToCart(Location $location): void
1093e8ded2caSGreg Roach    {
1094e8ded2caSGreg Roach        $cart = Session::get('cart', []);
1095e8ded2caSGreg Roach        $tree = $location->tree()->name();
1096e8ded2caSGreg Roach        $xref = $location->xref();
1097e8ded2caSGreg Roach
1098e8ded2caSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1099e8ded2caSGreg Roach            $cart[$tree][$xref] = true;
1100e8ded2caSGreg Roach
1101e8ded2caSGreg Roach            Session::put('cart', $cart);
1102e8ded2caSGreg Roach
1103e8ded2caSGreg Roach            $this->addLocationLinksToCart($location);
1104e8ded2caSGreg Roach            $this->addMediaLinksToCart($location);
1105e8ded2caSGreg Roach            $this->addNoteLinksToCart($location);
1106e8ded2caSGreg Roach            $this->addSourceLinksToCart($location);
1107e8ded2caSGreg Roach        }
1108e8ded2caSGreg Roach    }
1109e8ded2caSGreg Roach
1110e8ded2caSGreg Roach    /**
1111e8ded2caSGreg Roach     * @param GedcomRecord $record
1112e8ded2caSGreg Roach     */
1113e8ded2caSGreg Roach    protected function addLocationLinksToCart(GedcomRecord $record): void
1114e8ded2caSGreg Roach    {
1115e8ded2caSGreg Roach        preg_match_all('/\n\d _LOC @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1116e8ded2caSGreg Roach
1117e8ded2caSGreg Roach        foreach ($matches[1] as $xref) {
1118e8ded2caSGreg Roach            $location = Registry::locationFactory()->make($xref, $record->tree());
1119e8ded2caSGreg Roach
1120e8ded2caSGreg Roach            if ($location instanceof Location && $location->canShow()) {
1121e8ded2caSGreg Roach                $this->addLocationToCart($location);
1122e8ded2caSGreg Roach            }
1123e8ded2caSGreg Roach        }
1124e8ded2caSGreg Roach    }
1125e8ded2caSGreg Roach
1126e8ded2caSGreg Roach    /**
1127d45701ccSGreg Roach     * @param Media $media
1128d45701ccSGreg Roach     */
1129d45701ccSGreg Roach    protected function addMediaToCart(Media $media): void
1130d45701ccSGreg Roach    {
1131d45701ccSGreg Roach        $cart = Session::get('cart', []);
1132d45701ccSGreg Roach        $tree = $media->tree()->name();
1133d45701ccSGreg Roach        $xref = $media->xref();
1134d45701ccSGreg Roach
1135d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1136d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1137d45701ccSGreg Roach
1138d45701ccSGreg Roach            Session::put('cart', $cart);
1139d45701ccSGreg Roach
1140d45701ccSGreg Roach            $this->addNoteLinksToCart($media);
1141d45701ccSGreg Roach        }
1142d45701ccSGreg Roach    }
1143d45701ccSGreg Roach
1144d45701ccSGreg Roach    /**
1145d45701ccSGreg Roach     * @param GedcomRecord $record
1146d45701ccSGreg Roach     */
1147d45701ccSGreg Roach    protected function addMediaLinksToCart(GedcomRecord $record): void
1148d45701ccSGreg Roach    {
1149d45701ccSGreg Roach        preg_match_all('/\n\d OBJE @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1150d45701ccSGreg Roach
1151d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1152d45701ccSGreg Roach            $media = Registry::mediaFactory()->make($xref, $record->tree());
1153d45701ccSGreg Roach
1154d45701ccSGreg Roach            if ($media instanceof Media && $media->canShow()) {
1155d45701ccSGreg Roach                $this->addMediaToCart($media);
1156d45701ccSGreg Roach            }
1157d45701ccSGreg Roach        }
1158d45701ccSGreg Roach    }
1159d45701ccSGreg Roach
1160d45701ccSGreg Roach    /**
1161d45701ccSGreg Roach     * @param Note $note
1162d45701ccSGreg Roach     */
1163d45701ccSGreg Roach    protected function addNoteToCart(Note $note): void
1164d45701ccSGreg Roach    {
1165d45701ccSGreg Roach        $cart = Session::get('cart', []);
1166d45701ccSGreg Roach        $tree = $note->tree()->name();
1167d45701ccSGreg Roach        $xref = $note->xref();
1168d45701ccSGreg Roach
1169d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1170d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1171d45701ccSGreg Roach
11725a78cd34SGreg Roach            Session::put('cart', $cart);
11735a78cd34SGreg Roach        }
1174d45701ccSGreg Roach    }
11755a78cd34SGreg Roach
11765a78cd34SGreg Roach    /**
1177d45701ccSGreg Roach     * @param GedcomRecord $record
11785a78cd34SGreg Roach     */
1179d45701ccSGreg Roach    protected function addNoteLinksToCart(GedcomRecord $record): void
1180d45701ccSGreg Roach    {
1181d45701ccSGreg Roach        preg_match_all('/\n\d NOTE @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1182d45701ccSGreg Roach
1183d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1184d45701ccSGreg Roach            $note = Registry::noteFactory()->make($xref, $record->tree());
1185d45701ccSGreg Roach
1186d45701ccSGreg Roach            if ($note instanceof Note && $note->canShow()) {
1187d45701ccSGreg Roach                $this->addNoteToCart($note);
1188d45701ccSGreg Roach            }
1189d45701ccSGreg Roach        }
1190d45701ccSGreg Roach    }
1191d45701ccSGreg Roach
1192d45701ccSGreg Roach    /**
1193d45701ccSGreg Roach     * @param Source $source
1194d45701ccSGreg Roach     */
1195d45701ccSGreg Roach    protected function addSourceToCart(Source $source): void
1196c1010edaSGreg Roach    {
11975a78cd34SGreg Roach        $cart = Session::get('cart', []);
1198d45701ccSGreg Roach        $tree = $source->tree()->name();
1199d45701ccSGreg Roach        $xref = $source->xref();
12005a78cd34SGreg Roach
1201d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1202d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1203d45701ccSGreg Roach
1204d45701ccSGreg Roach            Session::put('cart', $cart);
1205d45701ccSGreg Roach
1206d45701ccSGreg Roach            $this->addNoteLinksToCart($source);
1207d45701ccSGreg Roach            $this->addRepositoryLinksToCart($source);
1208d45701ccSGreg Roach        }
1209d45701ccSGreg Roach    }
1210d45701ccSGreg Roach
1211d45701ccSGreg Roach    /**
1212d45701ccSGreg Roach     * @param GedcomRecord $record
1213d45701ccSGreg Roach     */
1214d45701ccSGreg Roach    protected function addSourceLinksToCart(GedcomRecord $record): void
1215d45701ccSGreg Roach    {
1216d45701ccSGreg Roach        preg_match_all('/\n\d SOUR @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1217d45701ccSGreg Roach
1218d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1219d45701ccSGreg Roach            $source = Registry::sourceFactory()->make($xref, $record->tree());
1220d45701ccSGreg Roach
1221d45701ccSGreg Roach            if ($source instanceof Source && $source->canShow()) {
1222d45701ccSGreg Roach                $this->addSourceToCart($source);
1223d45701ccSGreg Roach            }
1224d45701ccSGreg Roach        }
1225d45701ccSGreg Roach    }
1226d45701ccSGreg Roach
1227d45701ccSGreg Roach    /**
1228d45701ccSGreg Roach     * @param Repository $repository
1229d45701ccSGreg Roach     */
1230d45701ccSGreg Roach    protected function addRepositoryToCart(Repository $repository): void
1231d45701ccSGreg Roach    {
1232d45701ccSGreg Roach        $cart = Session::get('cart', []);
1233d45701ccSGreg Roach        $tree = $repository->tree()->name();
1234d45701ccSGreg Roach        $xref = $repository->xref();
1235d45701ccSGreg Roach
1236d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1237d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1238d45701ccSGreg Roach
1239d45701ccSGreg Roach            Session::put('cart', $cart);
1240d45701ccSGreg Roach
1241d45701ccSGreg Roach            $this->addNoteLinksToCart($repository);
1242d45701ccSGreg Roach        }
1243d45701ccSGreg Roach    }
1244d45701ccSGreg Roach
1245d45701ccSGreg Roach    /**
1246d45701ccSGreg Roach     * @param GedcomRecord $record
1247d45701ccSGreg Roach     */
1248d45701ccSGreg Roach    protected function addRepositoryLinksToCart(GedcomRecord $record): void
1249d45701ccSGreg Roach    {
1250d45701ccSGreg Roach        preg_match_all('/\n\d REPO @(' . Gedcom::REGEX_XREF . '@)/', $record->gedcom(), $matches);
1251d45701ccSGreg Roach
1252d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1253d45701ccSGreg Roach            $repository = Registry::repositoryFactory()->make($xref, $record->tree());
1254d45701ccSGreg Roach
1255d45701ccSGreg Roach            if ($repository instanceof Repository && $repository->canShow()) {
1256d45701ccSGreg Roach                $this->addRepositoryToCart($repository);
1257d45701ccSGreg Roach            }
1258d45701ccSGreg Roach        }
1259d45701ccSGreg Roach    }
1260d45701ccSGreg Roach
1261d45701ccSGreg Roach    /**
1262d45701ccSGreg Roach     * @param Submitter $submitter
1263d45701ccSGreg Roach     */
1264d45701ccSGreg Roach    protected function addSubmitterToCart(Submitter $submitter): void
1265d45701ccSGreg Roach    {
1266d45701ccSGreg Roach        $cart = Session::get('cart', []);
1267d45701ccSGreg Roach        $tree = $submitter->tree()->name();
1268d45701ccSGreg Roach        $xref = $submitter->xref();
1269d45701ccSGreg Roach
1270d45701ccSGreg Roach        if (($cart[$tree][$xref] ?? false) === false) {
1271d45701ccSGreg Roach            $cart[$tree][$xref] = true;
1272d45701ccSGreg Roach
1273d45701ccSGreg Roach            Session::put('cart', $cart);
1274d45701ccSGreg Roach
1275d45701ccSGreg Roach            $this->addNoteLinksToCart($submitter);
1276d45701ccSGreg Roach        }
1277d45701ccSGreg Roach    }
1278d45701ccSGreg Roach
1279d45701ccSGreg Roach    /**
1280d45701ccSGreg Roach     * @param GedcomRecord $record
1281d45701ccSGreg Roach     */
1282d45701ccSGreg Roach    protected function addSubmitterLinksToCart(GedcomRecord $record): void
1283d45701ccSGreg Roach    {
1284d45701ccSGreg Roach        preg_match_all('/\n\d SUBM @(' . Gedcom::REGEX_XREF . ')@/', $record->gedcom(), $matches);
1285d45701ccSGreg Roach
1286d45701ccSGreg Roach        foreach ($matches[1] as $xref) {
1287d45701ccSGreg Roach            $submitter = Registry::submitterFactory()->make($xref, $record->tree());
1288d45701ccSGreg Roach
1289d45701ccSGreg Roach            if ($submitter instanceof Submitter && $submitter->canShow()) {
1290d45701ccSGreg Roach                $this->addSubmitterToCart($submitter);
1291d45701ccSGreg Roach            }
1292d45701ccSGreg Roach        }
12935a78cd34SGreg Roach    }
12948c2e8227SGreg Roach}
1295