xref: /webtrees/app/Module/DescendancyModule.php (revision cb0eb4a9463291c5ba2c9deef129f29c38ed947e)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2019 webtrees development team
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16declare(strict_types=1);
17
18namespace Fisharebest\Webtrees\Module;
19
20use Fisharebest\Webtrees\Family;
21use Fisharebest\Webtrees\I18N;
22use Fisharebest\Webtrees\Individual;
23use Fisharebest\Webtrees\Services\SearchService;
24use Fisharebest\Webtrees\Tree;
25use Psr\Http\Message\ResponseInterface;
26use Psr\Http\Message\ServerRequestInterface;
27
28/**
29 * Class DescendancyModule
30 */
31class DescendancyModule extends AbstractModule implements ModuleSidebarInterface
32{
33    use ModuleSidebarTrait;
34
35    /**
36     * How should this module be identified in the control panel, etc.?
37     *
38     * @return string
39     */
40    public function title(): string
41    {
42        /* I18N: Name of a module/sidebar */
43        return I18N::translate('Descendants');
44    }
45
46    /**
47     * A sentence describing what this module does.
48     *
49     * @return string
50     */
51    public function description(): string
52    {
53        /* I18N: Description of the “Descendants” module */
54        return I18N::translate('A sidebar showing the descendants of an individual.');
55    }
56
57    /**
58     * The default position for this sidebar.  It can be changed in the control panel.
59     *
60     * @return int
61     */
62    public function defaultSidebarOrder(): int
63    {
64        return 3;
65    }
66
67    /**
68     * @param ServerRequestInterface $request
69     * @param Tree                   $tree
70     * @param SearchService          $search_service
71     *
72     * @return ResponseInterface
73     */
74    public function getSearchAction(ServerRequestInterface $request, Tree $tree, SearchService $search_service): ResponseInterface
75    {
76        $search = $request->get('search', '');
77
78        $html = '';
79
80        if (strlen($search) >= 2) {
81            $html = $search_service
82                ->searchIndividualNames([$tree], [$search])
83                ->map(function (Individual $individual): string {
84                    return $this->getPersonLi($individual);
85                })
86                ->implode('');
87        }
88
89        if ($html !== '') {
90            $html = '<ul>' . $html . '</ul>';
91        }
92
93        return response($html);
94    }
95
96    /**
97     * @param ServerRequestInterface $request
98     * @param Tree                   $tree
99     *
100     * @return ResponseInterface
101     */
102    public function getDescendantsAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
103    {
104        $xref = $request->get('xref', '');
105
106        $individual = Individual::getInstance($xref, $tree);
107
108        if ($individual !== null && $individual->canShow()) {
109            $html = $this->loadSpouses($individual, 1);
110        } else {
111            $html = '';
112        }
113
114        return response($html);
115    }
116
117    /** {@inheritdoc} */
118    public function hasSidebarContent(Individual $individual): bool
119    {
120        return true;
121    }
122
123    /**
124     * Load this sidebar synchronously.
125     *
126     * @param Individual $individual
127     *
128     * @return string
129     */
130    public function getSidebarContent(Individual $individual): string
131    {
132        return view('modules/descendancy/sidebar', [
133            'individual_list' => $this->getPersonLi($individual, 1),
134        ]);
135    }
136
137    /**
138     * Format an individual in a list.
139     *
140     * @param Individual $person
141     * @param int        $generations
142     *
143     * @return string
144     */
145    public function getPersonLi(Individual $person, $generations = 0): string
146    {
147        $icon     = $generations > 0 ? 'icon-minus' : 'icon-plus';
148        $lifespan = $person->canShow() ? '(' . $person->getLifeSpan() . ')' : '';
149        $spouses  = $generations > 0 ? $this->loadSpouses($person, 0) : '';
150
151        return
152            '<li class="sb_desc_indi_li">' .
153            '<a class="sb_desc_indi" href="' . e(route('module', [
154                'module' => $this->name(),
155                'action' => 'Descendants',
156                'ged'    => $person->tree()->name(),
157                'xref'   => $person->xref(),
158            ])) . '">' .
159            '<i class="plusminus ' . $icon . '"></i>' .
160            $person->getSexImage() . $person->fullName() . $lifespan .
161            '</a>' .
162            '<a href="' . e($person->url()) . '" title="' . strip_tags($person->fullName()) . '">' . view('icons/individual') . '</a>' .
163            '<div>' . $spouses . '</div>' .
164            '</li>';
165    }
166
167    /**
168     * Format a family in a list.
169     *
170     * @param Family     $family
171     * @param Individual $person
172     * @param int        $generations
173     *
174     * @return string
175     */
176    public function getFamilyLi(Family $family, Individual $person, $generations = 0): string
177    {
178        $spouse = $family->spouse($person);
179        if ($spouse) {
180            $spouse_name = $spouse->getSexImage() . $spouse->fullName();
181            $spouse_link = '<a href="' . e($person->url()) . '" title="' . strip_tags($person->fullName()) . '">' . view('icons/individual') . '</a>';
182        } else {
183            $spouse_name = '';
184            $spouse_link = '';
185        }
186
187        $family_link = '<a href="' . e($family->url()) . '" title="' . strip_tags($family->fullName()) . '">' . view('icons/family') . '</a>';
188
189        $marryear = $family->getMarriageYear();
190        $marr     = $marryear ? '<i class="icon-rings"></i>' . $marryear : '';
191
192        return
193            '<li class="sb_desc_indi_li">' .
194            '<a class="sb_desc_indi" href="#"><i class="plusminus icon-minus"></i>' .
195            $spouse_name .
196            $marr .
197            '</a>' .
198            $spouse_link .
199            $family_link .
200            '<div>' . $this->loadChildren($family, $generations) . '</div>' .
201            '</li>';
202    }
203
204    /**
205     * Display spouses.
206     *
207     * @param Individual $individual
208     * @param int        $generations
209     *
210     * @return string
211     */
212    public function loadSpouses(Individual $individual, $generations): string
213    {
214        $out = '';
215        if ($individual->canShow()) {
216            foreach ($individual->spouseFamilies() as $family) {
217                $out .= $this->getFamilyLi($family, $individual, $generations - 1);
218            }
219        }
220        if ($out) {
221            return '<ul>' . $out . '</ul>';
222        }
223
224        return '';
225    }
226
227    /**
228     * Display descendants.
229     *
230     * @param Family $family
231     * @param int    $generations
232     *
233     * @return string
234     */
235    public function loadChildren(Family $family, $generations): string
236    {
237        $out = '';
238        if ($family->canShow()) {
239            $children = $family->children();
240
241            if ($children->isNotEmpty()) {
242                foreach ($children as $child) {
243                    $out .= $this->getPersonLi($child, $generations - 1);
244                }
245            } else {
246                $out .= '<li class="sb_desc_none">' . I18N::translate('No children') . '</li>';
247            }
248        }
249        if ($out) {
250            return '<ul>' . $out . '</ul>';
251        }
252
253        return '';
254    }
255}
256