xref: /webtrees/app/Module/FamilyNavigatorModule.php (revision d2681c37325a35ab01be82034f4afd3b58010fb8)
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\Family;
19use Fisharebest\Webtrees\Functions\Functions;
20use Fisharebest\Webtrees\I18N;
21use Fisharebest\Webtrees\Individual;
22use Fisharebest\Webtrees\Menu;
23
24/**
25 * Class FamilyNavigatorModule
26 */
27class FamilyNavigatorModule extends AbstractModule implements ModuleSidebarInterface {
28	const TTL = "<div class='flyout2'>%s</div>";
29	const LNK = "<div class='flyout3' data-href='%s'>%s</div>";
30	const MSG = "<div class='flyout4'>(%s)</div>"; // class flyout4 not used in standard themes
31
32	/** {@inheritdoc} */
33	public function getTitle() {
34		return /* I18N: Name of a module/sidebar */ I18N::translate('Family navigator');
35	}
36
37	/** {@inheritdoc} */
38	public function getDescription() {
39		return /* I18N: Description of the “Family navigator” module */ I18N::translate('A sidebar showing an individual’s close families and relatives.');
40	}
41
42	/** {@inheritdoc} */
43	public function defaultSidebarOrder() {
44		return 20;
45	}
46
47	/** {@inheritdoc} */
48	public function hasSidebarContent(Individual $individual) {
49		return true;
50	}
51
52	/** {@inheritdoc} */
53	public function getSidebarAjaxContent() {
54		return '';
55	}
56
57	/**
58	 * Load this sidebar synchronously.
59	 *
60	 * @param Individual $individual
61	 *
62	 * @return string
63	 */
64	public function getSidebarContent(Individual $individual) {
65		global $controller;
66
67		$controller->addInlineJavascript('
68			$("#sb_family_nav_content")
69				.on("click", ".flyout a", function() {
70					return false;
71				})
72				.on("click", ".flyout3", function() {
73					window.location.href = $(this).data("href");
74					return false;
75				});
76		');
77
78		ob_start();
79
80		?>
81		<div id="sb_family_nav_content">
82			<div class="nav_content">
83
84		<?php
85		//-- parent families -------------------------------------------------------------
86		foreach ($individual->getChildFamilies() as $family) {
87			$this->drawFamily($individual, $family, $individual->getChildFamilyLabel($family));
88		}
89		//-- step parents ----------------------------------------------------------------
90		foreach ($individual->getChildStepFamilies() as $family) {
91			$this->drawFamily($individual, $family, $individual->getStepFamilyLabel($family));
92		}
93		//-- spouse and children --------------------------------------------------
94		foreach ($individual->getSpouseFamilies() as $family) {
95			$this->drawFamily($individual, $family, $individual->getSpouseFamilyLabel($family));
96		}
97		//-- step children ----------------------------------------------------------------
98		foreach ($individual->getSpouseStepFamilies() as $family) {
99			$this->drawFamily($individual, $family, $family->getFullName());
100		}
101		?>
102			</div>
103		</div>
104		<?php
105
106		return ob_get_clean();
107	}
108
109	/**
110	 * Format a family.
111	 *
112	 * @param Family $family
113	 * @param string $title
114	 */
115	private function drawFamily(Individual $individual, Family $family, $title) {
116		global $controller;
117
118		?>
119		<table class="table table-sm wt-facts-table">
120		<caption class="text-center">
121			<a class="famnav_title" href="<?= e($family->url()) ?>">
122				<?= $title ?>
123			</a>
124		</caption>
125		<tbody>
126			<?php
127				foreach ($family->getSpouses() as $spouse) {
128					$icon = $individual === $spouse ? '<i class="icon-selected"></i>' : '';
129					$menu = new Menu($icon . Functions::getCloseRelationshipName($individual, $spouse));
130					$menu->addSubmenu(new Menu($this->getParents($spouse)));
131					?>
132					<tr class="text-center wt-parent wt-gender-<?= $spouse->getSex() ?>">
133						<th scope="row">
134							<ul class="nav">
135								<?= $menu->bootstrap4() ?>
136							</ul>
137						</th>
138						<td>
139							<?php if ($spouse->canShow()): ?>
140							<a class="famnav_link" href="<?= e($spouse->url()) ?>">
141								<?= $spouse->getFullName() ?>
142							</a>
143							<div class="small">
144								<?= $spouse->getLifeSpan() ?>
145							</div>
146							<?php else: ?>
147								<?= $spouse->getFullName() ?>
148							<?php endif ?>
149						</td>
150					</tr>
151				<?php
152				}
153
154				foreach ($family->getChildren() as $child) {
155					$icon = $individual === $child ? '<i class="icon-selected"></i>' : '';
156					$menu = new Menu($icon . Functions::getCloseRelationshipName($individual, $child));
157					$menu->addSubmenu(new Menu($this->getFamily($child)));
158					?>
159					<tr class="text-center wt-child wt-gender-<?= $child->getSex() ?>">
160						<th scope="row">
161							<ul class="nav">
162								<?= $menu->bootstrap4() ?>
163							</ul>
164						</th>
165						<td>
166							<?php if ($child->canShow()): ?>
167							<a class="famnav_link" href="<?= e($child->url()) ?>">
168								<?= $child->getFullName() ?>
169							</a>
170							<div class="small">
171								<?= $child->getLifeSpan() ?>
172							</div>
173							<?php else: ?>
174								<?= $child->getFullName() ?>
175							<?php endif ?>
176						</td>
177					</tr>
178				<?php
179				}
180				?>
181			</tbody>
182		</table>
183		<?php
184	}
185
186	/**
187	 * Format an individual.
188	 *
189	 * @param      $person
190	 * @param bool $showUnknown
191	 *
192	 * @return string
193	 */
194	private function getHTML($person, $showUnknown = false) {
195		if ($person instanceof Individual) {
196			return sprintf(self::LNK, e($person->url()), $person->getFullName());
197		} elseif ($showUnknown) {
198			return sprintf(self::MSG, I18N::translate('unknown'));
199		} else {
200			return '';
201		}
202	}
203
204	/**
205	 * Forat the parents of an individual.
206	 *
207	 * @param Individual $person
208	 *
209	 * @return string
210	 */
211	private function getParents(Individual $person) {
212		$father = null;
213		$mother = null;
214		$html   = sprintf(self::TTL, I18N::translate('Parents'));
215		$family = $person->getPrimaryChildFamily();
216		if ($person->canShowName() && $family !== null) {
217			$father = $family->getHusband();
218			$mother = $family->getWife();
219			$html .= $this->getHTML($father) .
220					 $this->getHTML($mother);
221
222			// Can only have a step parent if one & only one parent found at this point
223			if ($father instanceof Individual xor $mother instanceof Individual) {
224				$stepParents = '';
225				foreach ($person->getChildStepFamilies() as $family) {
226					if (!$father instanceof Individual) {
227						$stepParents .= $this->getHTML($family->getHusband());
228					} else {
229						$stepParents .= $this->getHTML($family->getWife());
230					}
231				}
232				if ($stepParents) {
233					$relationship = $father instanceof Individual ?
234						I18N::translateContext('father’s wife', 'step-mother') : I18N::translateContext('mother’s husband', 'step-father');
235					$html .= sprintf(self::TTL, $relationship) . $stepParents;
236				}
237			}
238		}
239		if (!($father instanceof Individual || $mother instanceof Individual)) {
240			$html .= sprintf(self::MSG, I18N::translateContext('unknown family', 'unknown'));
241		}
242
243		return $html;
244	}
245
246	/**
247	 * Format a family.
248	 *
249	 * @param Individual $person
250	 *
251	 * @return string
252	 */
253	private function getFamily(Individual $person) {
254		$html = '';
255		if ($person->canShowName()) {
256			foreach ($person->getSpouseFamilies() as $family) {
257				$spouse = $family->getSpouse($person);
258				$html .= $this->getHTML($spouse, true);
259				$children = $family->getChildren();
260				if (count($children) > 0) {
261					$html .= "<ul class='clist'>";
262					foreach ($children as $child) {
263						$html .= '<li>' . $this->getHTML($child) . '</li>';
264					}
265					$html .= '</ul>';
266				}
267			}
268		}
269		if (!$html) {
270			$html = sprintf(self::MSG, I18N::translate('none'));
271		}
272
273		return sprintf(self::TTL, I18N::translate('Family')) . $html;
274	}
275}
276