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