xref: /webtrees/app/Module/IndividualFactsTabModule.php (revision 47f790f035261bd01fe664c28da0d51cd82c3abe)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2022 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\Module;
21
22use Fisharebest\Webtrees\Auth;
23use Fisharebest\Webtrees\Date;
24use Fisharebest\Webtrees\Elements\CustomElement;
25use Fisharebest\Webtrees\Fact;
26use Fisharebest\Webtrees\Family;
27use Fisharebest\Webtrees\I18N;
28use Fisharebest\Webtrees\Individual;
29use Fisharebest\Webtrees\Registry;
30use Fisharebest\Webtrees\Services\ClipboardService;
31use Fisharebest\Webtrees\Services\IndividualFactsService;
32use Fisharebest\Webtrees\Services\LinkedRecordService;
33use Fisharebest\Webtrees\Services\ModuleService;
34use Illuminate\Support\Collection;
35
36use function explode;
37use function preg_match;
38use function preg_replace;
39use function str_contains;
40use function str_replace;
41use function view;
42
43/**
44 * Class IndividualFactsTabModule
45 */
46class IndividualFactsTabModule extends AbstractModule implements ModuleTabInterface
47{
48    use ModuleTabTrait;
49
50    private ClipboardService $clipboard_service;
51
52    private LinkedRecordService $linked_record_service;
53
54    private ModuleService $module_service;
55
56    /**
57     * @param ClipboardService       $clipboard_service
58     * @param IndividualFactsService $individual_facts_service
59     * @param LinkedRecordService    $linked_record_service
60     * @param ModuleService          $module_service
61     */
62    public function __construct(
63        ClipboardService $clipboard_service,
64        IndividualFactsService $individual_facts_service,
65        LinkedRecordService $linked_record_service,
66        ModuleService $module_service
67    ) {
68        $this->clipboard_service        = $clipboard_service;
69        $this->individual_facts_service = $individual_facts_service;
70        $this->linked_record_service    = $linked_record_service;
71        $this->module_service           = $module_service;
72    }
73
74    /**
75     * How should this module be identified in the control panel, etc.?
76     *
77     * @return string
78     */
79    public function title(): string
80    {
81        /* I18N: Name of a module/tab on the individual page. */
82        return I18N::translate('Facts and events');
83    }
84
85    /**
86     * A sentence describing what this module does.
87     *
88     * @return string
89     */
90    public function description(): string
91    {
92        /* I18N: Description of the “Facts and events” module */
93        return I18N::translate('A tab showing the facts and events of an individual.');
94    }
95
96    /**
97     * The default position for this tab.  It can be changed in the control panel.
98     *
99     * @return int
100     */
101    public function defaultTabOrder(): int
102    {
103        return 1;
104    }
105
106    /**
107     * A greyed out tab has no actual content, but may perhaps have
108     * options to create content.
109     *
110     * @param Individual $individual
111     *
112     * @return bool
113     */
114    public function isGrayedOut(Individual $individual): bool
115    {
116        return false;
117    }
118
119    /**
120     * Generate the HTML content of this tab.
121     *
122     * @param Individual $individual
123     *
124     * @return string
125     */
126    public function getTabContent(Individual $individual): string
127    {
128        // Which facts and events are handled by other modules?
129        $sidebar_facts = $this->module_service
130            ->findByComponent(ModuleSidebarInterface::class, $individual->tree(), Auth::user())
131            ->map(fn (ModuleSidebarInterface $sidebar): Collection => $sidebar->supportedFacts())
132            ->flatten();
133
134        $tab_facts = $this->module_service
135            ->findByComponent(ModuleTabInterface::class, $individual->tree(), Auth::user())
136            ->map(fn (ModuleTabInterface $tab): Collection => $tab->supportedFacts())
137            ->flatten();
138
139        $indi_exclude_facts = $sidebar_facts->merge($tab_facts);
140        $fam_exclude_facts  = new Collection(['FAM:CHAN', 'FAM:_UID']);
141
142        $individual_facts = $this->individual_facts_service->individualFacts($individual, $indi_exclude_facts);
143        $family_facts     = $this->individual_facts_service->familyFacts($individual, $fam_exclude_facts);
144        $relative_facts   = $this->individual_facts_service->relativeFacts($individual);
145        $associate_facts  = $this->individual_facts_service->associateFacts($individual);
146        $historic_facts   = $this->individual_facts_service->historicFacts($individual);
147
148        $individual_facts = $individual_facts
149            ->merge($family_facts)
150            ->merge($relative_facts)
151            ->merge($associate_facts)
152            ->merge($historic_facts);
153
154        $individual_facts = Fact::sortFacts($individual_facts);
155
156        // Facts of relatives take the form 1 EVEN / 2 TYPE Event of Individual
157        // Ensure custom tags from there are recognised
158        Registry::elementFactory()->registerTags(['INDI:EVEN:CEME' => new CustomElement('Cemetery')]);
159
160        return view('modules/personal_facts/tab', [
161            'can_edit'            => $individual->canEdit(),
162            'clipboard_facts'     => $this->clipboard_service->pastableFacts($individual),
163            'has_associate_facts' => $associate_facts->isNotEmpty(),
164            'has_historic_facts'  => $historic_facts->isNotEmpty(),
165            'has_relative_facts'  => $relative_facts->isNotEmpty(),
166            'individual'          => $individual,
167            'facts'               => $individual_facts,
168        ]);
169    }
170
171    /**
172     * Is this tab empty? If so, we don't always need to display it.
173     *
174     * @param Individual $individual
175     *
176     * @return bool
177     */
178    public function hasTabContent(Individual $individual): bool
179    {
180        return true;
181    }
182
183    /**
184     * Can this tab load asynchronously?
185     *
186     * @return bool
187     */
188    public function canLoadAjax(): bool
189    {
190        return false;
191    }
192
193    /**
194     * This module handles the following facts - so don't show them on the "Facts and events" tab.
195     *
196     * @return Collection<int,string>
197     */
198    public function supportedFacts(): Collection
199    {
200        // We don't actually displaye these facts, but they are displayed
201        // outside the tabs/sidebar systems. This just forces them to be excluded here.
202        return new Collection(['INDI:NAME', 'INDI:SEX', 'INDI:OBJE']);
203    }
204}
205