xref: /webtrees/app/Services/ClipboardService.php (revision 5bc5bf805eeeaf271674e6a1ea666230dcb73c3d)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2020 webtrees development team
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees\Services;
21
22use Fisharebest\Webtrees\Fact;
23use Fisharebest\Webtrees\GedcomRecord;
24use Fisharebest\Webtrees\Session;
25use Illuminate\Support\Collection;
26
27/**
28 * Copy and past facts between records.
29 */
30class ClipboardService
31{
32    // Maximum number of entries in the clipboard.
33    private const CLIPBOARD_SIZE = 10;
34
35    /**
36     * Copy a fact to the clipboard.
37     *
38     * @param Fact $fact
39     */
40    public function copyFact(Fact $fact): void
41    {
42        $clipboard   = Session::get('clipboard', []);
43        $record_type = $fact->record()->tag();
44        $fact_id     = $fact->id();
45
46        // If we are copying the same fact twice, make sure the new one is at the end.
47        unset($clipboard[$record_type][$fact_id]);
48
49        $clipboard[$record_type][$fact_id] = [
50            'factrec' => $fact->gedcom(),
51            'fact'    => $fact->getTag(),
52        ];
53
54        // The clipboard only holds a limited number of facts.
55        $clipboard[$record_type] = array_slice($clipboard[$record_type], -self::CLIPBOARD_SIZE);
56
57        Session::put('clipboard', $clipboard);
58    }
59
60    /**
61     * Copy a fact from the clipboard to a record.
62     *
63     * @param string       $fact_id
64     * @param GedcomRecord $record
65     *
66     * @return bool
67     */
68    public function pasteFact(string $fact_id, GedcomRecord $record): bool
69    {
70        $clipboard = Session::get('clipboard');
71
72        $record_type = $record->tag();
73
74        if (isset($clipboard[$record_type][$fact_id])) {
75            $record->createFact($clipboard[$record_type][$fact_id]['factrec'], true);
76
77            return true;
78        }
79
80        return false;
81    }
82
83    /**
84     * Create a list of facts that can be pasted into a given record
85     *
86     * @param GedcomRecord $record
87     * @param Collection   $exclude_types
88     *
89     * @return Collection<Fact>
90     */
91    public function pastableFacts(GedcomRecord $record, Collection $exclude_types): Collection
92    {
93        // The facts are stored in the session.
94        return (new Collection(Session::get('clipboard', [])[$record->tag()] ?? []))
95            // Put the most recently copied fact at the top of the list.
96            ->reverse()
97            // Create facts for the record.
98            ->map(static function (array $clipping) use ($record): Fact {
99                return new Fact($clipping['factrec'], $record, md5($clipping['factrec']));
100            })->filter(static function (Fact $fact) use ($exclude_types): bool {
101                return $exclude_types->isEmpty() || !$exclude_types->contains($fact->getTag());
102            });
103    }
104
105    /**
106     * Find facts of a given type, from all records.
107     *
108     * @param GedcomRecord $record
109     * @param Collection   $types
110     *
111     * @return Collection<Fact>
112     */
113    public function pastableFactsOfType(GedcomRecord $record, Collection $types): Collection
114    {
115        // The facts are stored in the session.
116        return (new Collection(Session::get('clipboard', [])))
117            ->flatten(1)
118            ->reverse()
119            ->map(static function (array $clipping) use ($record): Fact {
120                return new Fact($clipping['factrec'], $record, md5($clipping['factrec']));
121            })
122            ->filter(static function (Fact $fact) use ($types): bool {
123                return $types->contains($fact->getTag());
124            });
125    }
126}
127