xref: /webtrees/app/Module/RelativesTabModule.php (revision 3d7a8a4ca809135634f38216b734b15acff479f7)
1<?php
2namespace Fisharebest\Webtrees\Module;
3
4/**
5 * webtrees: online genealogy
6 * Copyright (C) 2015 webtrees development team
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
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	/** {@inheritdoc} */
32	public function getTitle() {
33		return /* I18N: Name of a module */ I18N::translate('Families');
34	}
35
36	/** {@inheritdoc} */
37	public function getDescription() {
38		return /* I18N: Description of the “Families” module */ I18N::translate('A tab showing the close relatives of an individual.');
39	}
40
41	/** {@inheritdoc} */
42	public function defaultTabOrder() {
43		return 20;
44	}
45
46	/**
47	 * @param Date $prev
48	 * @param Date $next
49	 * @param int  $child_number
50	 *
51	 * @return string
52	 */
53	private static function ageDifference(Date $prev, Date $next, $child_number = 0) {
54		if ($prev->isOK() && $next->isOK()) {
55			$days = $next->maximumJulianDay() - $prev->minimumJulianDay();
56			if ($days < 0) {
57				// Show warning triangle if dates in reverse order
58				$diff = '<i class="icon-warning"></i> ';
59			} elseif ($child_number > 1 && $days > 1 && $days < 240) {
60				// Show warning triangle if children born too close together
61				$diff = '<i class="icon-warning"></i> ';
62			} else {
63				$diff = '';
64			}
65
66			$months = round($days * 12 / 365.25); // Approximate - we do not know the calendar
67			if (abs($months) == 12 || abs($months) >= 24) {
68				$diff .= I18N::plural('%s year', '%s years', round($months / 12), I18N::number(round($months / 12)));
69			} elseif ($months != 0) {
70				$diff .= I18N::plural('%s month', '%s months', $months, I18N::number($months));
71			}
72
73			return '<div class="elderdate age">' . $diff . '</div>';
74		} else {
75			return '';
76		}
77	}
78
79	/**
80	 * @param Family $family
81	 * @param string $type
82	 * @param string $label
83	 */
84	private function printFamily(Family $family, $type, $label) {
85		global $controller;
86
87		if ($family->getTree()->getPreference('SHOW_PRIVATE_RELATIONSHIPS')) {
88			$access_level = Auth::PRIV_HIDE;
89		} else {
90			$access_level = Auth::accessLevel($family->getTree());
91		}
92
93		?>
94		<table>
95			<tr>
96				<td>
97					<i class="icon-cfamily"></i>
98				</td>
99				<td>
100					<span class="subheaders"> <?php echo $label; ?> </span> -
101					<a href="<?php echo $family->getHtmlUrl(); ?>"><?php echo I18N::translate('View family'); ?></a>
102				</td>
103			</tr>
104		</table>
105		<table class="facts_table">
106		<?php
107
108		///// HUSB /////
109		$found = false;
110		foreach ($family->getFacts('HUSB', false, $access_level) as $fact) {
111			$found |= !$fact->isPendingDeletion();
112			$person = $fact->getTarget();
113			if ($person instanceof Individual) {
114				if ($fact->isPendingAddition()) {
115					$class = 'facts_label new';
116				} elseif ($fact->isPendingDeletion()) {
117					$class = 'facts_label old';
118				} else {
119					$class = 'facts_label';
120				}
121				?>
122					<tr>
123					<td class="<?php echo $class; ?>">
124						<?php echo Functions::getCloseRelationshipName($controller->record, $person); ?>
125					</td>
126					<td class="<?php echo $controller->getPersonStyle($person); ?>">
127						<?php echo Theme::theme()->individualBoxLarge($person); ?>
128					</td>
129					</tr>
130				<?php
131			}
132		}
133		if (!$found && $family->canEdit()) {
134			?>
135			<tr>
136				<td class="facts_label"></td>
137				<td class="facts_value"><a href="#" onclick="return add_spouse_to_family('<?php echo $family->getXref(); ?>', 'HUSB');"><?php echo I18N::translate('Add a husband to this family'); ?></a></td>
138			</tr>
139			<?php
140		}
141
142		///// WIFE /////
143		$found = false;
144		foreach ($family->getFacts('WIFE', false, $access_level) as $fact) {
145			$person = $fact->getTarget();
146			if ($person instanceof Individual) {
147				$found |= !$fact->isPendingDeletion();
148				if ($fact->isPendingAddition()) {
149					$class = 'facts_label new';
150				} elseif ($fact->isPendingDeletion()) {
151					$class = 'facts_label old';
152				} else {
153					$class = 'facts_label';
154				}
155				?>
156				<tr>
157					<td class="<?php echo $class; ?>">
158						<?php echo Functions::getCloseRelationshipName($controller->record, $person); ?>
159					</td>
160					<td class="<?php echo $controller->getPersonStyle($person); ?>">
161						<?php echo Theme::theme()->individualBoxLarge($person); ?>
162					</td>
163				</tr>
164				<?php
165			}
166		}
167		if (!$found && $family->canEdit()) {
168			?>
169			<tr>
170				<td class="facts_label"></td>
171				<td class="facts_value"><a href="#" onclick="return add_spouse_to_family('<?php echo $family->getXref(); ?>', 'WIFE');"><?php echo I18N::translate('Add a wife to this family'); ?></a></td>
172			</tr>
173			<?php
174		}
175
176		///// MARR /////
177		$found = false;
178		$prev  = new Date('');
179		foreach ($family->getFacts(WT_EVENTS_MARR) as $fact) {
180			$found |= !$fact->isPendingDeletion();
181			if ($fact->isPendingAddition()) {
182				$class = ' new';
183			} elseif ($fact->isPendingDeletion()) {
184				$class = ' old';
185			} else {
186				$class = '';
187			}
188			?>
189			<tr>
190				<td class="facts_label">
191					&nbsp;
192				</td>
193				<td class="facts_value<?php echo $class; ?>">
194					<?php echo GedcomTag::getLabelValue($fact->getTag(), $fact->getDate()->display() . ' — ' . $fact->getPlace()->getFullName()); ?>
195				</td>
196			</tr>
197			<?php
198			if (!$prev->isOK() && $fact->getDate()->isOK()) {
199				$prev = $fact->getDate();
200			}
201		}
202		if (!$found && $family->canShow() && $family->canEdit()) {
203			// Add a new marriage
204			?>
205			<tr>
206				<td class="facts_label">
207					&nbsp;
208				</td>
209				<td class="facts_value">
210					<a href="#" onclick="return add_new_record('<?php echo $family->getXref(); ?>', 'MARR');">
211						<?php echo I18N::translate('Add marriage details'); ?>
212					</a>
213				</td>
214			</tr>
215			<?php
216		}
217
218		///// CHIL /////
219		$child_number = 0;
220		foreach ($family->getFacts('CHIL', false, $access_level) as $fact) {
221			$person = $fact->getTarget();
222			if ($person instanceof Individual) {
223				if ($fact->isPendingAddition()) {
224					$child_number++;
225					$class = 'facts_label new';
226				} elseif ($fact->isPendingDeletion()) {
227					$class = 'facts_label old';
228				} else {
229					$child_number++;
230					$class = 'facts_label';
231				}
232				$next = new Date('');
233				foreach ($person->getFacts(WT_EVENTS_BIRT) as $bfact) {
234					if ($bfact->getDate()->isOK()) {
235						$next = $bfact->getDate();
236						break;
237					}
238				}
239				?>
240				<tr>
241					<td class="<?php echo $class; ?>">
242						<?php echo self::ageDifference($prev, $next, $child_number); ?>
243						<?php echo Functions::getCloseRelationshipName($controller->record, $person); ?>
244					</td>
245					<td class="<?php echo $controller->getPersonStyle($person); ?>">
246						<?php echo Theme::theme()->individualBoxLarge($person); ?>
247					</td>
248				</tr>
249				<?php
250				$prev = $next;
251			}
252		}
253		// Re-order children / add a new child
254		if ($family->canEdit()) {
255			if ($type == 'FAMS') {
256				$add_child_text = I18N::translate('Add a new son or daughter');
257			} else {
258				$add_child_text = I18N::translate('Add a new brother or sister');
259			}
260			?>
261			<tr>
262				<td class="facts_label">
263					<?php if (count($family->getChildren()) > 1) { ?>
264					<a href="#" onclick="reorder_children('<?php echo $family->getXref(); ?>');tabswitch(5);"><i class="icon-media-shuffle"></i> <?php echo I18N::translate('Re-order children'); ?></a>
265					<?php } ?>
266				</td>
267				<td class="facts_value">
268					<a href="#" onclick="return add_child_to_family('<?php echo $family->getXref(); ?>');"><?php echo $add_child_text; ?></a>
269					<span style='white-space:nowrap;'>
270						<a href="#" class="icon-sex_m_15x15" onclick="return add_child_to_family('<?php echo $family->getXref(); ?>','M');"></a>
271						<a href="#" class="icon-sex_f_15x15" onclick="return add_child_to_family('<?php echo $family->getXref(); ?>','F');"></a>
272					</span>
273				</td>
274			</tr>
275			<?php
276		}
277
278		echo '</table>';
279
280		return;
281	}
282
283	/** {@inheritdoc} */
284	public function getTabContent() {
285		global $WT_TREE, $show_full, $controller;
286
287		if (isset($show_full)) {
288			$saved_show_full = $show_full;
289		}
290		// We always want to see full details here
291		$show_full = 1;
292
293		ob_start();
294		?>
295		<table class="facts_table"><tr><td class="descriptionbox rela">
296		<input id="checkbox_elder" type="checkbox" onclick="jQuery('div.elderdate').toggle();" <?php echo $WT_TREE->getPreference('SHOW_AGE_DIFF') ? 'checked' : ''; ?>>
297		<label for="checkbox_elder"><?php echo I18N::translate('Show date differences'); ?></label>
298		</td></tr></table>
299		<?php
300		$families = $controller->record->getChildFamilies();
301		if (!$families && $controller->record->canEdit()) {
302			?>
303			<table class="facts_table">
304				<tr>
305					<td class="facts_value"><a href="#" onclick="return add_parent_to_individual('<?php echo $controller->record->getXref(); ?>', 'M');"><?php echo I18N::translate('Add a new father'); ?></td>
306				</tr>
307				<tr>
308					<td class="facts_value"><a href="#" onclick="return add_parent_to_individual('<?php echo $controller->record->getXref(); ?>', 'F');"><?php echo I18N::translate('Add a new mother'); ?></a></td>
309				</tr>
310			</table>
311			<?php
312		}
313
314		// parents
315		foreach ($families as $family) {
316			$this->printFamily($family, 'FAMC', $controller->record->getChildFamilyLabel($family));
317		}
318
319		// step-parents
320		foreach ($controller->record->getChildStepFamilies() as $family) {
321			$this->printFamily($family, 'FAMC', $controller->record->getStepFamilyLabel($family));
322		}
323
324		// spouses
325		$families = $controller->record->getSpouseFamilies();
326		foreach ($families as $family) {
327			$this->printFamily($family, 'FAMS', $controller->getSpouseFamilyLabel($family, $controller->record));
328		}
329
330		// step-children
331		foreach ($controller->record->getSpouseStepFamilies() as $family) {
332			$this->printFamily($family, 'FAMS', $family->getFullName());
333		}
334
335		if (!$WT_TREE->getPreference('SHOW_AGE_DIFF')) {
336			echo '<script>jQuery("DIV.elderdate").toggle();</script>';
337		}
338
339		if ($controller->record->canEdit()) {
340		?>
341		<br><table class="facts_table">
342		<?php
343			if (count($families) > 1) { ?>
344			<tr>
345				<td class="facts_value">
346				<a href="#" onclick="return reorder_families('<?php echo $controller->record->getXref(); ?>');"><?php echo I18N::translate('Re-order families'); ?></a>
347				</td>
348			</tr>
349		<?php } ?>
350			<tr>
351				<td class="facts_value">
352				<a href="#" onclick="return add_famc('<?php echo $controller->record->getXref(); ?>');"><?php echo I18N::translate('Link this individual to an existing family as a child'); ?></a>
353				</td>
354			</tr>
355			<?php if ($controller->record->getSex() != "F") { ?>
356			<tr>
357				<td class="facts_value">
358				<a href="#" onclick="return add_spouse_to_individual('<?php echo $controller->record->getXref(); ?>','WIFE');"><?php echo I18N::translate('Add a new wife'); ?></a>
359				</td>
360			</tr>
361			<tr>
362				<td class="facts_value">
363				<a href="#" onclick="return linkspouse('<?php echo $controller->record->getXref(); ?>','WIFE');"><?php echo I18N::translate('Add a wife using an existing individual'); ?></a>
364				</td>
365			</tr>
366			<?php }
367			if ($controller->record->getSex() != "M") { ?>
368			<tr>
369				<td class="facts_value">
370				<a href="#" onclick="return add_spouse_to_individual('<?php echo $controller->record->getXref(); ?>','HUSB');"><?php echo I18N::translate('Add a new husband'); ?></a>
371				</td>
372			</tr>
373			<tr>
374				<td class="facts_value">
375				<a href="#" onclick="return linkspouse('<?php echo $controller->record->getXref(); ?>','HUSB');"><?php echo I18N::translate('Add a husband using an existing individual'); ?></a>
376				</td>
377			</tr>
378			<?php } ?>
379			<tr>
380				<td class="facts_value">
381				<a href="#" onclick="return add_child_to_individual('<?php echo $controller->record->getXref(); ?>','U');"><?php echo I18N::translate('Add a child to create a one-parent family'); ?></a>
382				</td>
383			</tr>
384		</table>
385		<?php } ?>
386		<br>
387		<?php
388
389		unset($show_full);
390		if (isset($saved_show_full)) {
391			$show_full = $saved_show_full;
392		}
393
394		return '<div id="' . $this->getName() . '_content">' . ob_get_clean() . '</div>';
395	}
396
397	/** {@inheritdoc} */
398	public function hasTabContent() {
399		return true;
400	}
401	/** {@inheritdoc} */
402	public function isGrayedOut() {
403		return false;
404	}
405	/** {@inheritdoc} */
406	public function canLoadAjax() {
407		return !Auth::isSearchEngine(); // Search engines cannot use AJAX
408	}
409
410	/** {@inheritdoc} */
411	public function getPreLoadContent() {
412		return '';
413	}
414}
415