xref: /webtrees/app/Module/RelativesTabModule.php (revision 15d603e7c7c15d20f055d3d9c38d6b133453c5be)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2017 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\Auth;
19use Fisharebest\Webtrees\Date;
20use Fisharebest\Webtrees\Family;
21use Fisharebest\Webtrees\Functions\Functions;
22use Fisharebest\Webtrees\GedcomTag;
23use Fisharebest\Webtrees\I18N;
24use Fisharebest\Webtrees\Individual;
25use Fisharebest\Webtrees\Theme;
26
27/**
28 * Class RelativesTabModule
29 */
30class RelativesTabModule extends AbstractModule implements ModuleTabInterface {
31	/**
32	 * How should this module be labelled on tabs, menus, etc.?
33	 *
34	 * @return string
35	 */
36	public function getTitle() {
37		return /* I18N: Name of a module */ I18N::translate('Families');
38	}
39
40	/**
41	 * A sentence describing what this module does.
42	 *
43	 * @return string
44	 */
45	public function getDescription() {
46		return /* I18N: Description of the “Families” module */ I18N::translate('A tab showing the close relatives of an individual.');
47	}
48
49	/**
50	 * The user can re-arrange the tab order, but until they do, this
51	 * is the order in which tabs are shown.
52	 *
53	 * @return int
54	 */
55	public function defaultTabOrder() {
56		return 20;
57	}
58
59	/**
60	 * Display the age difference between marriages and the births of children.
61	 *
62	 * @param Date $prev
63	 * @param Date $next
64	 * @param int  $child_number
65	 *
66	 * @return string
67	 */
68	private static function ageDifference(Date $prev, Date $next, $child_number = 0) {
69		if ($prev->isOK() && $next->isOK()) {
70			$days = $next->maximumJulianDay() - $prev->minimumJulianDay();
71			if ($days < 0) {
72				// Show warning triangle if dates in reverse order
73				$diff = '<i class="icon-warning"></i> ';
74			} elseif ($child_number > 1 && $days > 1 && $days < 240) {
75				// Show warning triangle if children born too close together
76				$diff = '<i class="icon-warning"></i> ';
77			} else {
78				$diff = '';
79			}
80
81			$months = round($days * 12 / 365.25); // Approximate - we do not know the calendar
82			if (abs($months) == 12 || abs($months) >= 24) {
83				$diff .= I18N::plural('%s year', '%s years', round($months / 12), I18N::number(round($months / 12)));
84			} elseif ($months != 0) {
85				$diff .= I18N::plural('%s month', '%s months', $months, I18N::number($months));
86			}
87
88			return '<div class="elderdate age">' . $diff . '</div>';
89		} else {
90			return '';
91		}
92	}
93
94	/**
95	 * Print a family group.
96	 *
97	 * @param Family $family
98	 * @param string $type
99	 * @param string $label
100	 */
101	private function printFamily(Family $family, $type, $label) {
102		global $controller;
103
104		if ($family->getTree()->getPreference('SHOW_PRIVATE_RELATIONSHIPS')) {
105			$access_level = Auth::PRIV_HIDE;
106		} else {
107			$access_level = Auth::accessLevel($family->getTree());
108		}
109
110		?>
111		<table>
112			<tr>
113				<td>
114					<i class="icon-cfamily"></i>
115				</td>
116				<td>
117					<span class="subheaders"> <?= $label ?></span>
118					<a href="<?= $family->getHtmlUrl() ?>"> - <?= I18N::translate('View this family') ?></a>
119				</td>
120			</tr>
121		</table>
122		<table class="facts_table">
123		<?php
124
125		///// HUSB /////
126		$found = false;
127		foreach ($family->getFacts('HUSB', false, $access_level) as $fact) {
128			$found |= !$fact->isPendingDeletion();
129			$person = $fact->getTarget();
130			if ($person instanceof Individual) {
131				if ($fact->isPendingAddition()) {
132					$class = 'facts_label new';
133				} elseif ($fact->isPendingDeletion()) {
134					$class = 'facts_label old';
135				} else {
136					$class = 'facts_label';
137				}
138				?>
139					<tr>
140					<td class="<?= $class ?>">
141						<?= Functions::getCloseRelationshipName($controller->record, $person) ?>
142					</td>
143					<td class="<?= $controller->getPersonStyle($person) ?>">
144						<?= Theme::theme()->individualBoxLarge($person) ?>
145					</td>
146					</tr>
147				<?php
148			}
149		}
150		if (!$found && $family->canEdit()) {
151			?>
152			<tr>
153				<td class="facts_label"></td>
154				<td class="facts_value">
155					<a href="edit_interface.php?action=add_spouse_to_family&amp;ged=<?= $family->getTree()->getNameHtml() ?>&amp;xref=<?= $family->getXref() ?>&amp;famtag=HUSB">
156						<?= I18N::translate('Add a husband to this family') ?>
157					</a>
158					</td>
159			</tr>
160			<?php
161		}
162
163		///// WIFE /////
164		$found = false;
165		foreach ($family->getFacts('WIFE', false, $access_level) as $fact) {
166			$person = $fact->getTarget();
167			if ($person instanceof Individual) {
168				$found |= !$fact->isPendingDeletion();
169				if ($fact->isPendingAddition()) {
170					$class = 'facts_label new';
171				} elseif ($fact->isPendingDeletion()) {
172					$class = 'facts_label old';
173				} else {
174					$class = 'facts_label';
175				}
176				?>
177				<tr>
178					<td class="<?= $class ?>">
179						<?= Functions::getCloseRelationshipName($controller->record, $person) ?>
180					</td>
181					<td class="<?= $controller->getPersonStyle($person) ?>">
182						<?= Theme::theme()->individualBoxLarge($person) ?>
183					</td>
184				</tr>
185				<?php
186			}
187		}
188		if (!$found && $family->canEdit()) {
189			?>
190			<tr>
191				<td class="facts_label"></td>
192				<td class="facts_value">
193					<a href="edit_interface.php?action=add_spouse_to_family&amp;ged=<?= $family->getTree()->getNameHtml() ?>&amp;xref=<?= $family->getXref() ?>&amp;famtag=WIFE">
194						<?= I18N::translate('Add a wife to this family') ?>
195					</a>
196				</td>
197			</tr>
198			<?php
199		}
200
201		///// MARR /////
202		$found = false;
203		$prev  = new Date('');
204		foreach ($family->getFacts(WT_EVENTS_MARR . '|' . WT_EVENTS_DIV, true) as $fact) {
205			$found |= !$fact->isPendingDeletion();
206			if ($fact->isPendingAddition()) {
207				$class = ' new';
208			} elseif ($fact->isPendingDeletion()) {
209				$class = ' old';
210			} else {
211				$class = '';
212			}
213			?>
214			<tr>
215				<td class="facts_label">
216				</td>
217				<td class="facts_value<?= $class ?>">
218					<?= GedcomTag::getLabelValue($fact->getTag(), $fact->getDate()->display() . ' — ' . $fact->getPlace()->getFullName()) ?>
219				</td>
220			</tr>
221			<?php
222			if (!$prev->isOK() && $fact->getDate()->isOK()) {
223				$prev = $fact->getDate();
224			}
225		}
226		if (!$found && $family->canShow() && $family->canEdit()) {
227			// Add a new marriage
228			?>
229			<tr>
230				<td class="facts_label">
231				</td>
232				<td class="facts_value">
233					<a href="edit_interface.php?action=add&amp;ged=<?= $family->getTree()->getNameHtml() ?>&amp;xref=<?= $family->getXref() ?>&amp;fact=MARR">
234						<?= I18N::translate('Add marriage details') ?>
235					</a>
236				</td>
237			</tr>
238			<?php
239		}
240
241		///// CHIL /////
242		$child_number = 0;
243		foreach ($family->getFacts('CHIL', false, $access_level) as $fact) {
244			$person = $fact->getTarget();
245			if ($person instanceof Individual) {
246				if ($fact->isPendingAddition()) {
247					$child_number++;
248					$class = 'facts_label new';
249				} elseif ($fact->isPendingDeletion()) {
250					$class = 'facts_label old';
251				} else {
252					$child_number++;
253					$class = 'facts_label';
254				}
255				$next = new Date('');
256				foreach ($person->getFacts(WT_EVENTS_BIRT, true) as $bfact) {
257					if ($bfact->getDate()->isOK()) {
258						$next = $bfact->getDate();
259						break;
260					}
261				}
262				?>
263				<tr>
264					<td class="<?= $class ?>">
265						<?= self::ageDifference($prev, $next, $child_number) ?>
266						<?= Functions::getCloseRelationshipName($controller->record, $person) ?>
267					</td>
268					<td class="<?= $controller->getPersonStyle($person) ?>">
269						<?= Theme::theme()->individualBoxLarge($person) ?>
270					</td>
271				</tr>
272				<?php
273				$prev = $next;
274			}
275		}
276		// Re-order children / add a new child
277		if ($family->canEdit()) {
278			if ($type == 'FAMS') {
279				$add_child_text = I18N::translate('Add a son or daughter');
280			} else {
281				$add_child_text = I18N::translate('Add a brother or sister');
282			}
283			?>
284			<tr>
285				<td class="facts_label">
286					<?php if (count($family->getChildren()) > 1): ?>
287					<a href="edit_interface.php?action=reorder_children&amp;ged=<?= $family->getTree()->getNameHtml() ?>&amp;xref=<?= $family->getXref() ?>">
288						<i class="icon-media-shuffle"></i> <?= I18N::translate('Re-order children') ?>
289					</a>
290					<?php endif; ?>
291				</td>
292				<td class="facts_value">
293					<a href="edit_interface.php?action=add_child_to_family&amp;ged=<?= $family->getTree()->getNameHtml() ?>&amp;xref=<?= $family->getXref() ?>&amp;gender=U">
294						<?= $add_child_text ?>
295					</a>
296					<span style='white-space:nowrap;'>
297						<a href="edit_interface.php?action=add_child_to_family&amp;ged=<?= $family->getTree()->getNameHtml() ?>&amp;xref=<?= $family->getXref() ?>&amp;gender=M" class="icon-sex_m_15x15"></a>
298						<a href="edit_interface.php?action=add_child_to_family&amp;ged=<?= $family->getTree()->getNameHtml() ?>&amp;xref=<?= $family->getXref() ?>&amp;gender=F" class="icon-sex_f_15x15"></a>
299					</span>
300				</td>
301			</tr>
302			<?php
303		}
304
305		echo '</table>';
306	}
307
308	/** {@inheritdoc} */
309	public function getTabContent() {
310		global $controller;
311
312		ob_start();
313		?>
314		<table class="facts_table">
315			<tr>
316				<td class="descriptionbox rela">
317					<label>
318						<input id="show-date-differences" type="checkbox" checked>
319						<?= I18N::translate('Date differences') ?>
320					</label>
321				</td>
322			</tr>
323		</table>
324		<?php
325		$families = $controller->record->getChildFamilies();
326		if (!$families && $controller->record->canEdit()) {
327			?>
328			<table class="facts_table">
329				<tr>
330					<td class="facts_value">
331						<a href="edit_interface.php?action=add_parent_to_individual&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>&amp;gender=M">
332							<?= I18N::translate('Add a father') ?>
333						</a>
334					</td>
335				</tr>
336				<tr>
337					<td class="facts_value">
338						<a href="edit_interface.php?action=add_parent_to_individual&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>&amp;gender=F">
339							<?= I18N::translate('Add a mother') ?>
340						</a>
341					</td>
342				</tr>
343			</table>
344			<?php
345		}
346
347		// parents
348		foreach ($families as $family) {
349			$this->printFamily($family, 'FAMC', $controller->record->getChildFamilyLabel($family));
350		}
351
352		// step-parents
353		foreach ($controller->record->getChildStepFamilies() as $family) {
354			$this->printFamily($family, 'FAMC', $controller->record->getStepFamilyLabel($family));
355		}
356
357		// spouses
358		$families = $controller->record->getSpouseFamilies();
359		foreach ($families as $family) {
360			$this->printFamily($family, 'FAMS', $controller->getSpouseFamilyLabel($family, $controller->record));
361		}
362
363		// step-children
364		foreach ($controller->record->getSpouseStepFamilies() as $family) {
365			$this->printFamily($family, 'FAMS', $family->getFullName());
366		}
367
368		if ($controller->record->canEdit()) {
369		?>
370		<br><table class="facts_table">
371		<?php
372			if (count($families) > 1) { ?>
373			<tr>
374				<td class="facts_value">
375					<a href="edit_interface.php?action=reorder_fams&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>">
376					<?= I18N::translate('Re-order families') ?>
377					</a>
378				</td>
379			</tr>
380		<?php } ?>
381			<tr>
382				<td class="facts_value">
383				<a href="edit_interface.php?action=addfamlink&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>"><?= I18N::translate('Link this individual to an existing family as a child') ?></a>
384				</td>
385			</tr>
386			<?php if ($controller->record->getSex() !== 'F') { ?>
387			<tr>
388				<td class="facts_value">
389				<a href="edit_interface.php?action=add_spouse_to_individual&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>&amp;sex=F"><?= I18N::translate('Add a wife') ?></a>
390				</td>
391			</tr>
392			<tr>
393				<td class="facts_value">
394				<a href="edit_interface.php?action=linkspouse&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>&amp;famtag=WIFE"><?= I18N::translate('Add a wife using an existing individual') ?></a>
395				</td>
396			</tr>
397			<?php }
398			if ($controller->record->getSex() !== 'M') { ?>
399			<tr>
400				<td class="facts_value">
401				<a href="edit_interface.php?action=add_spouse_to_individual&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>&amp;sex=M"><?= I18N::translate('Add a husband') ?></a>
402				</td>
403			</tr>
404			<tr>
405				<td class="facts_value">
406				<a href="edit_interface.php?action=linkspouse&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>&amp;famtag=HUSB"><?= I18N::translate('Add a husband using an existing individual') ?></a>
407				</td>
408			</tr>
409			<?php } ?>
410			<tr>
411				<td class="facts_value">
412					<a href="edit_interface.php?action=add_child_to_individual&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>&amp;gender=U">
413						<?= I18N::translate('Add a child to create a one-parent family') ?>
414					</a>
415				</td>
416			</tr>
417		</table>
418		<?php } ?>
419		<br>
420		<script>
421			//persistent_toggle("show-date-differences", ".elderdate");
422		</script>
423		<?php
424
425		return '<div id="' . $this->getName() . '_content">' . ob_get_clean() . '</div>';
426	}
427
428	/** {@inheritdoc} */
429	public function hasTabContent() {
430		return true;
431	}
432	/** {@inheritdoc} */
433	public function isGrayedOut() {
434		return false;
435	}
436	/** {@inheritdoc} */
437	public function canLoadAjax() {
438		return false;
439	}
440
441	/** {@inheritdoc} */
442	public function getPreLoadContent() {
443		return '';
444	}
445}
446