xref: /webtrees/app/Module/DescendancyModule.php (revision 31bc7874190e33336a5ff742eac1246d7473e530)
1<?php
2namespace Fisharebest\Webtrees;
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 */
18
19/**
20 * Class DescendancyModule
21 */
22class DescendancyModule extends AbstractModule implements ModuleSidebarInterface {
23	/** {@inheritdoc} */
24	public function getTitle() {
25		return /* I18N: Name of a module/sidebar */
26			I18N::translate('Descendants');
27	}
28
29	/** {@inheritdoc} */
30	public function getDescription() {
31		return /* I18N: Description of the “Descendants” module */
32			I18N::translate('A sidebar showing the descendants of an individual.');
33	}
34
35	/** {@inheritdoc} */
36	public function modAction($modAction) {
37		global $WT_TREE;
38
39		header('Content-Type: text/html; charset=UTF-8');
40
41		switch ($modAction) {
42		case 'search':
43			$search = Filter::get('search');
44			echo $this->search($search);
45			break;
46		case 'descendants':
47			$individual = Individual::getInstance(Filter::get('xref', WT_REGEX_XREF), $WT_TREE);
48			if ($individual) {
49				echo $this->loadSpouses($individual, 1);
50			}
51			break;
52		default:
53			http_response_code(404);
54			break;
55		}
56	}
57
58	/** {@inheritdoc} */
59	public function defaultSidebarOrder() {
60		return 30;
61	}
62
63	/** {@inheritdoc} */
64	public function hasSidebarContent() {
65		return !Auth::isSearchEngine();
66	}
67
68	/** {@inheritdoc} */
69	public function getSidebarAjaxContent() {
70		return '';
71	}
72
73	/** {@inheritdoc} */
74	public function getSidebarContent() {
75		global $controller;
76
77		$controller->addInlineJavascript('
78			function dsearchQ() {
79				var query = jQuery("#sb_desc_name").val();
80				if (query.length>1) {
81					jQuery("#sb_desc_content").load("module.php?mod=' . $this->getName() . '&mod_action=search&search="+query);
82				}
83			}
84
85			jQuery("#sb_desc_name").focus(function(){this.select();});
86			jQuery("#sb_desc_name").blur(function(){if (this.value=="") this.value="' . I18N::translate('Search') . '";});
87			var dtimerid = null;
88			jQuery("#sb_desc_name").keyup(function(e) {
89				if (dtimerid) window.clearTimeout(dtimerid);
90				dtimerid = window.setTimeout("dsearchQ()", 500);
91			});
92
93			jQuery("#sb_desc_content").on("click", ".sb_desc_indi", function() {
94				var self = jQuery(this),
95					state = self.children(".plusminus"),
96					target = self.siblings("div");
97				if(state.hasClass("icon-plus")) {
98					if (jQuery.trim(target.html())) {
99						target.show("fast"); // already got content so just show it
100					} else {
101						target
102							.hide()
103							.load(self.attr("href"), function(response, status, xhr) {
104								if(status == "success" && response !== "") {
105									target.show("fast");
106								}
107							})
108					}
109				} else {
110					target.hide("fast");
111				}
112				state.toggleClass("icon-minus icon-plus");
113				return false;
114			});
115		');
116
117		return
118			'<form method="post" action="module.php?mod=' . $this->getName() . '&amp;mod_action=search" onsubmit="return false;">' .
119			'<input type="search" name="sb_desc_name" id="sb_desc_name" placeholder="' . I18N::translate('Search') . '">' .
120			'</form>' .
121			'<div id="sb_desc_content">' .
122			'<ul>' . $this->getPersonLi($controller->record, 1) . '</ul>' .
123			'</div>';
124	}
125
126	/**
127	 * @param Individual $person
128	 * @param integer       $generations
129	 *
130	 * @return string
131	 */
132	public function getPersonLi(Individual $person, $generations = 0) {
133		$icon = $generations > 0 ? 'icon-minus' : 'icon-plus';
134		$lifespan = $person->canShow() ? '(' . $person->getLifeSpan() . ')' : '';
135		$spouses = $generations > 0 ? $this->loadSpouses($person, 0) : '';
136		return sprintf('<li class="sb_desc_indi_li">
137		                  <a class="sb_desc_indi" href="module.php?mod=%s&amp;mod_action=descendants&amp;xref=%s"><i class="plusminus %s"></i>%s %s %s</a>
138		                  <a class="icon-button_indi" href="%s"></a>
139		                  %s
140		                  <div>%s</div>
141		                </li>', $this->getName(), $person->getXref(), $icon, $person->getSexImage(), $person->getFullName(), $lifespan, $person->getHtmlUrl(), '', $spouses);
142	}
143
144	/**
145	 * @param Family     $family
146	 * @param Individual $person
147	 * @param integer       $generations
148	 *
149	 * @return string
150	 */
151	public function getFamilyLi(Family $family, Individual $person, $generations = 0) {
152		$marryear = $family->getMarriageYear();
153		$marr = $marryear ? '<i class="icon-rings"></i>' . $marryear : '';
154		$fam = '<a href="' . $family->getHtmlUrl() . '" class="icon-button_family"></a>';
155		$kids = $this->loadChildren($family, $generations);
156		return sprintf('<li class="sb_desc_indi_li">
157		                  <a class="sb_desc_indi" href="#"><i class="plusminus icon-minus"></i>%s %s %s</a>
158		                  <a class="icon-button_indi" href="%s"></a>
159		                  %s
160		                  <div>%s</div>
161		                </li>', $person->getSexImage(), $person->getFullName(), $marr, $person->getHtmlUrl(), $fam, $kids);
162	}
163
164	/**
165	 * @param string $query
166	 *
167	 * @return string
168	 */
169	public function search($query) {
170		global $WT_TREE;
171
172		if (strlen($query) < 2) {
173			return '';
174		}
175		$rows = Database::prepare(
176			"SELECT i_id AS xref" .
177			" FROM `##individuals`, `##name`" .
178			" WHERE (i_id LIKE ? OR n_sort LIKE ?)" .
179			" AND i_id=n_id AND i_file=n_file AND i_file=?" .
180			" ORDER BY n_sort"
181		)
182			->execute(array("%{$query}%", "%{$query}%", $WT_TREE->getTreeId()))
183			->fetchAll();
184
185		$out = '';
186		foreach ($rows as $row) {
187			$person = Individual::getInstance($row->xref, $WT_TREE);
188			if ($person->canShowName()) {
189				$out .= $this->getPersonLi($person);
190			}
191		}
192		if ($out) {
193			return '<ul>' . $out . '</ul>';
194		} else {
195			return '';
196		}
197	}
198
199	/**
200	 * @param Individual $person
201	 * @param integer       $generations
202	 *
203	 * @return string
204	 */
205	public function loadSpouses(Individual $person, $generations) {
206		$out = '';
207		if ($person && $person->canShow()) {
208			foreach ($person->getSpouseFamilies() as $family) {
209				$spouse = $family->getSpouse($person);
210				if ($spouse) {
211					$out .= $this->getFamilyLi($family, $spouse, $generations - 1);
212				}
213			}
214			if (!$out) {
215				$out = '<li class="sb_desc_none">' . I18N::translate('No children') . '</li>';
216			}
217		}
218		if ($out) {
219			return '<ul>' . $out . '</ul>';
220		} else {
221			return '';
222		}
223	}
224
225	/**
226	 * @param Family $family
227	 * @param integer   $generations
228	 *
229	 * @return string
230	 */
231	public function loadChildren(Family $family, $generations) {
232		$out = '';
233		if ($family->canShow()) {
234			$children = $family->getChildren();
235			if ($children) {
236				foreach ($children as $child) {
237					$out .= $this->getPersonLi($child, $generations - 1);
238				}
239			} else {
240				$out .= '<li class="sb_desc_none">' . I18N::translate('No children') . '</li>';
241			}
242		}
243		if ($out) {
244			return '<ul>' . $out . '</ul>';
245		} else {
246			return '';
247		}
248	}
249}
250