xref: /webtrees/app/Module/DescendancyModule.php (revision 911f5683b68340b4bad2bbca6e1ab7a3a15f46a7)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2018 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 */
16namespace Fisharebest\Webtrees\Module;
17
18use Fisharebest\Webtrees\Database;
19use Fisharebest\Webtrees\Family;
20use Fisharebest\Webtrees\FontAwesome;
21use Fisharebest\Webtrees\I18N;
22use Fisharebest\Webtrees\Individual;
23use Fisharebest\Webtrees\Tree;
24use Symfony\Component\HttpFoundation\Request;
25use Symfony\Component\HttpFoundation\Response;
26
27/**
28 * Class DescendancyModule
29 */
30class DescendancyModule extends AbstractModule implements ModuleSidebarInterface {
31	/** {@inheritdoc} */
32	public function getTitle() {
33		return /* I18N: Name of a module/sidebar */
34			I18N::translate('Descendants');
35	}
36
37	/** {@inheritdoc} */
38	public function getDescription() {
39		return /* I18N: Description of the “Descendants” module */
40			I18N::translate('A sidebar showing the descendants of an individual.');
41	}
42
43	/**
44	 * @param Request $request
45	 *
46	 * @return Response
47	 */
48	public function getSearchAction(Request $request): Response {
49		/** @var Tree $tree */
50		$tree = $request->attributes->get('tree');
51
52		$search = $request->get('search', '');
53
54		$html = '';
55
56		if (strlen($search) >= 2) {
57			$rows = Database::prepare(
58				"SELECT i_id AS xref" .
59				" FROM `##individuals`" .
60				" JOIN `##name` ON i_id = n_id AND i_file = n_file" .
61				" WHERE n_sort LIKE CONCAT('%', :query, '%') AND i_file = :tree_id" .
62				" ORDER BY n_sort"
63			)->execute([
64				'query'   => $search,
65				'tree_id' => $tree->getTreeId(),
66			])->fetchAll();
67
68			foreach ($rows as $row) {
69				$individual = Individual::getInstance($row->xref, $tree);
70				if ($individual !== null && $individual->canShow()) {
71					$html .= $this->getPersonLi($individual);
72				}
73			}
74		}
75
76		if ($html !== '') {
77			$html = '<ul>' . $html . '</ul>';
78		}
79
80		return new Response($html);
81	}
82
83	/**
84	 * @param Request $request
85	 *
86	 * @return Response
87	 */
88	public function getDescendantsAction(Request $request): Response {
89		/** @var Tree $tree */
90		$tree = $request->attributes->get('tree');
91
92		$xref = $request->get('xref');
93
94		$individual = Individual::getInstance($xref, $tree);
95
96		if ($individual !== null && $individual->canShow()) {
97			$html = $this->loadSpouses($individual, 1);
98		} else {
99			$html = '';
100		}
101
102		return new Response($html);
103	}
104
105	/** {@inheritdoc} */
106	public function defaultSidebarOrder() {
107		return 30;
108	}
109
110	/** {@inheritdoc} */
111	public function hasSidebarContent(Individual $individual) {
112		return true;
113	}
114
115	/**
116	 * Load this sidebar synchronously.
117	 *
118	 * @param Individual $individual
119	 *
120	 * @return string
121	 */
122	public function getSidebarContent(Individual $individual) {
123		return view('modules/descendancy/sidebar', [
124			'individual_list' => $this->getPersonLi($individual, 1),
125		]);
126	}
127
128	/**
129	 * Format an individual in a list.
130	 *
131	 * @param Individual $person
132	 * @param int        $generations
133	 *
134	 * @return string
135	 */
136	public function getPersonLi(Individual $person, $generations = 0) {
137		$icon     = $generations > 0 ? 'icon-minus' : 'icon-plus';
138		$lifespan = $person->canShow() ? '(' . $person->getLifeSpan() . ')' : '';
139		$spouses  = $generations > 0 ? $this->loadSpouses($person, 0) : '';
140
141		return
142			'<li class="sb_desc_indi_li">' .
143			'<a class="sb_desc_indi" href="' . e(route('module', ['module' => 'descendancy', 'action' => 'Descendants', 'ged' => $person->getTree()->getName(), 'xref' => $person->getXref()])) . '">' .
144			'<i class="plusminus ' . $icon . '"></i>' .
145			$person->getSexImage() . $person->getFullName() . $lifespan .
146			'</a>' .
147			FontAwesome::linkIcon('individual', $person->getFullName(), ['href' => $person->url()]) .
148			'<div>' . $spouses . '</div>' .
149			'</li>';
150	}
151
152	/**
153	 * Format a family in a list.
154	 *
155	 * @param Family     $family
156	 * @param Individual $person
157	 * @param int        $generations
158	 *
159	 * @return string
160	 */
161	public function getFamilyLi(Family $family, Individual $person, $generations = 0) {
162		$spouse = $family->getSpouse($person);
163		if ($spouse) {
164			$spouse_name = $spouse->getSexImage() . $spouse->getFullName();
165			$spouse_link = FontAwesome::linkIcon('individual', $spouse->getFullName(), ['href' => $person->url()]);
166		} else {
167			$spouse_name = '';
168			$spouse_link = '';
169		}
170
171		$marryear = $family->getMarriageYear();
172		$marr     = $marryear ? '<i class="icon-rings"></i>' . $marryear : '';
173
174		return
175			'<li class="sb_desc_indi_li">' .
176			'<a class="sb_desc_indi" href="#"><i class="plusminus icon-minus"></i>' . $spouse_name . $marr . '</a>' .
177			$spouse_link .
178			FontAwesome::linkIcon('family', $family->getFullName(), ['href' => $family->url()]) .
179		 '<div>' . $this->loadChildren($family, $generations) . '</div>' .
180			'</li>';
181	}
182
183	/**
184	 * Display spouses.
185	 *
186	 * @param Individual $person
187	 * @param int        $generations
188	 *
189	 * @return string
190	 */
191	public function loadSpouses(Individual $person, $generations) {
192		$out = '';
193		if ($person && $person->canShow()) {
194			foreach ($person->getSpouseFamilies() as $family) {
195				$out .= $this->getFamilyLi($family, $person, $generations - 1);
196			}
197		}
198		if ($out) {
199			return '<ul>' . $out . '</ul>';
200		} else {
201			return '';
202		}
203	}
204
205	/**
206	 * Display descendants.
207	 *
208	 * @param Family $family
209	 * @param int    $generations
210	 *
211	 * @return string
212	 */
213	public function loadChildren(Family $family, $generations) {
214		$out = '';
215		if ($family->canShow()) {
216			$children = $family->getChildren();
217			if ($children) {
218				foreach ($children as $child) {
219					$out .= $this->getPersonLi($child, $generations - 1);
220				}
221			} else {
222				$out .= '<li class="sb_desc_none">' . I18N::translate('No children') . '</li>';
223			}
224		}
225		if ($out) {
226			return '<ul>' . $out . '</ul>';
227		} else {
228			return '';
229		}
230	}
231}
232