Test Failed
Branch master (4a3c5b)
by Greg
12:31
created

HourglassController::printEmptyBox()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
rs 10
c 0
b 0
f 0
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
 */
16
namespace Fisharebest\Webtrees\Controller;
17
18
use Fisharebest\Webtrees\Filter;
19
use Fisharebest\Webtrees\FontAwesome;
20
use Fisharebest\Webtrees\Functions\FunctionsPrint;
21
use Fisharebest\Webtrees\I18N;
22
use Fisharebest\Webtrees\Individual;
23
use Fisharebest\Webtrees\Theme;
24
25
/**
26
 * Controller for the hourglass chart
27
 */
28
class HourglassController extends ChartController {
29
	/** @var int Whether to show spouse details. */
30
	public $show_spouse;
31
32
	/** @var int Number of ascendancy generations to show. */
33
	public $generations;
34
35
	/** @var int Number of descendancy generations that exist. */
36
	public $dgenerations;
37
38
	/** @var int Half height of personbox. */
39
	public $bhalfheight;
40
41
	const SWITCH_LINK = "<a href='hourglass.php?rootid=%s&amp;show_spouse=%s&amp;generations=%s' class='name1'>%s</a>";
42
43
	/**
44
	 * Create the hourglass controller.
45
	 *
46
	 * @param string $rootid
47
	 */
48
	public function __construct($rootid = '') {
0 ignored issues
show
Unused Code introduced by
The parameter $rootid is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

48
	public function __construct(/** @scrutinizer ignore-unused */ $rootid = '') {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
49
		parent::__construct();
50
51
		// Extract parameters from
52
		$this->show_spouse = Filter::getInteger('show_spouse', 0, 1, 0);
53
		$this->generations = Filter::getInteger('generations', 2, $this->tree()->getPreference('MAX_DESCENDANCY_GENERATIONS'), 3);
0 ignored issues
show
Bug introduced by
$this->tree()->getPrefer...SCENDANCY_GENERATIONS') of type string is incompatible with the type integer expected by parameter $max of Fisharebest\Webtrees\Filter::getInteger(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

53
		$this->generations = Filter::getInteger('generations', 2, /** @scrutinizer ignore-type */ $this->tree()->getPreference('MAX_DESCENDANCY_GENERATIONS'), 3);
Loading history...
54
55
		$this->bhalfheight = (int) ($this->getBoxDimensions()->height / 2);
56
57
		//Checks how many generations of descendency is for the person for formatting purposes
58
		$this->dgenerations = $this->maxDescendencyGenerations($this->root, 0);
59
		if ($this->dgenerations < 1) {
60
			$this->dgenerations = 1;
61
		}
62
63
		$this->setPageTitle(/* I18N: %s is an individual’s name */ I18N::translate('Hourglass chart of %s', $this->root->getFullName()));
64
	}
65
66
	/**
67
	 * Prints pedigree of the person passed in. Which is the descendancy
68
	 *
69
	 * @param Individual $person ID of person to print the pedigree for
70
	 * @param int        $count  generation count, so it recursively calls itself
71
	 */
72
	public function printPersonPedigree(Individual $person, $count) {
73
		if ($count >= $this->generations) {
74
			return;
75
		}
76
77
		$genoffset = $this->generations; // handle pedigree n generations lines
78
79
		//
80
		//Prints empty table columns for children w/o parents up to the max generation
81
		//This allows vertical line spacing to be consistent
82
		//
83
		if (count($person->getChildFamilies()) == 0) {
84
			echo '<table><tr><td> ' . $this->printEmptyBox() . '</td>';
85
			echo '<td> ';
86
			// Recursively get the father’s family
87
			$this->printPersonPedigree($person, $count + 1);
88
			echo '</td></tr>';
89
			echo '<tr><td> ' . $this->printEmptyBox() . '</td>';
90
			echo '<td> ';
91
			// Recursively get the mother’s family
92
			$this->printPersonPedigree($person, $count + 1);
93
			echo '</td><td> </tr></table>';
94
		}
95
		foreach ($person->getChildFamilies() as $family) {
96
			echo '<table cellspacing="0" cellpadding="0" border="0"  class="hourglassChart">';
97
			echo '<tr>';
98
			echo '<td style="vertical-align:bottom"><img class="line3 pvline" src="' . Theme::theme()->parameter('image-vline') . '" width="3"></td>';
99
			echo '<td> <img class="lineh2" src="' . Theme::theme()->parameter('image-hline') . '" width="7" height="3"></td>';
100
			echo '<td class="myCharts"> ';
101
			//-- print the father box
102
			FunctionsPrint::printPedigreePerson($family->getHusband());
103
			echo '</td>';
104
			if ($family->getHusband()) {
105
				$ARID = $family->getHusband()->getXref();
106
				echo '<td id="td_' . $ARID . '">';
107
108
				//-- print an Ajax arrow on the last generation of the adult male
109 View Code Duplication
				if ($count == $this->generations - 1 && $family->getHusband()->getChildFamilies()) {
110
					echo FontAwesome::linkIcon('arrow-end', I18N::translate('Parents'), [
111
						'href'           => '#',
112
						'data-direction' => 'asc',
113
						'data-xref'      => $ARID,
114
						'data-spouses'   => $this->show_spouse,
115
					]);
116
				}
117
				//-- recursively get the father’s family
118
				$this->printPersonPedigree($family->getHusband(), $count + 1);
119
				echo '</td>';
120 View Code Duplication
			} else {
121
				echo '<td> ';
122
				if ($count < $genoffset - 1) {
123
					echo '<table>';
124
					for ($i = $count; $i < (pow(2, ($genoffset - 1) - $count) / 2) + 2; $i++) {
125
						$this->printEmptyBox();
126
						echo '</tr>';
127
						$this->printEmptyBox();
128
						echo '</tr>';
129
					}
130
					echo '</table>';
131
				}
132
			}
133
			echo
134
			'</tr><tr>',
135
			"<td style='vertical-align:top'><img class='pvline' src='" . Theme::theme()->parameter('image-vline') . "' width='3' alt=''></td>",
136
				'<td> <img class="lineh3" src="' . Theme::theme()->parameter('image-hline') . '" width="7" height="3"></td>',
137
			'<td class="myCharts"> ';
138
			//-- print the mother box
139
			FunctionsPrint::printPedigreePerson($family->getWife());
140
			echo '</td>';
141
			if ($family->getWife()) {
142
				$ARID = $family->getWife()->getXref();
143
				echo '<td id="td_' . $ARID . '">';
144
145
				//-- print an ajax arrow on the last generation of the adult female
146 View Code Duplication
				if ($count == $this->generations - 1 && $family->getWife()->getChildFamilies()) {
147
					echo FontAwesome::linkIcon('arrow-end', I18N::translate('Parents'), [
148
						'href'           => '#',
149
						'data-direction' => 'asc',
150
						'data-xref'      => $ARID,
151
						'data-spouses'   => $this->show_spouse,
152
					]);
153
				}
154
				//-- recursively print the mother’s family
155
				$this->printPersonPedigree($family->getWife(), $count + 1);
156
				echo '</td>';
157
			}
158
			echo '</tr></table>';
159
			break;
160
		}
161
	}
162
163
	/**
164
	 * Print empty box
165
	 *
166
	 * @return string
167
	 */
168
169
	private function printEmptyBox() {
170
		return Theme::theme()->individualBoxEmpty();
171
	}
172
173
	/**
174
	 * Prints descendency of passed in person
175
	 *
176
	 * @param Individual $person  person to print descendency for
177
	 * @param int        $count   count of generations to print
178
	 * @param bool       $showNav
179
	 *
180
	 * @return int
181
	 */
182
	public function printDescendency($person, $count, $showNav = true) {
183
		global $lastGenSecondFam;
184
185
		if ($count > $this->dgenerations) {
186
			return 0;
187
		}
188
		$pid         = $person->getXref();
189
		$tablealign  = 'right';
190
		$otablealign = 'left';
191
		if (I18N::direction() === 'rtl') {
192
			$tablealign  = 'left';
193
			$otablealign = 'right';
194
		}
195
196
		//-- put a space between families on the last generation
197
		if ($count == $this->dgenerations - 1) {
198
			if (isset($lastGenSecondFam)) {
199
				echo '<br>';
200
			}
201
			$lastGenSecondFam = true;
202
		}
203
		echo "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" id='table_$pid' class='hourglassChart' style='float:$tablealign'>";
204
		echo '<tr>';
205
		echo "<td style='text-align:$tablealign'>";
206
		$numkids  = 0;
207
		$families = $person->getSpouseFamilies();
208
		$famNum   = 0;
209
		$children = [];
210
		if ($count < $this->dgenerations) {
211
			// Put all of the children in a common array
212
			foreach ($families as $family) {
213
				$famNum++;
214
				foreach ($family->getChildren() as $child) {
215
					$children[] = $child;
216
				}
217
			}
218
219
			$ct = count($children);
220
			if ($ct > 0) {
221
				echo "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" style='position: relative; top: auto; float: $tablealign;'>";
222
				for ($i = 0; $i < $ct; $i++) {
223
					$person2 = $children[$i];
224
					$chil    = $person2->getXref();
225
					echo '<tr>';
226
					echo '<td id="td_', $chil, '" class="', I18N::direction(), '" style="text-align:', $otablealign, '">';
227
					$kids = $this->printDescendency($person2, $count + 1, $showNav);
228
					$numkids += $kids;
229
					echo '</td>';
230
231
					// Print the lines
232
					if ($ct > 1) {
233
						if ($i == 0) {
234
							// First child
235
							echo "<td style='vertical-align:bottom'><img alt='' class='line1 tvertline' id='vline_$chil' src='" . Theme::theme()->parameter('image-vline') . "' width='3'></td>";
236
						} elseif ($i == $ct - 1) {
237
							// Last child
238
							echo "<td style='vertical-align:top'><img alt='' class='bvertline' id='vline_$chil' src='" . Theme::theme()->parameter('image-vline') . "' width='3'></td>";
239
						} else {
240
							// Middle child
241
							echo '<td style="background: url(\'' . Theme::theme()->parameter('image-vline') . '\');"><img src=\'' . Theme::theme()->parameter('image-spacer') . '\' width="3"></td>';
242
						}
243
					}
244
					echo '</tr>';
245
				}
246
				echo '</table>';
247
			}
248
			echo '</td>';
249
			echo '<td class="myCharts" width="', $this->getBoxDimensions()->width, '">';
250
		}
251
252
		// Print the descendency expansion arrow
253
		if ($count == $this->dgenerations) {
254
			$numkids = 1;
255
			$tbwidth = $this->getBoxDimensions()->width + 16;
256
			for ($j = $count; $j < $this->dgenerations; $j++) {
257
				echo "<div style='width: ", $tbwidth, "px;'><br></div></td><td style='width:", $this->getBoxDimensions()->width, "px'>";
258
			}
259
			$kcount = 0;
260
			foreach ($families as $family) {
261
				$kcount += $family->getNumberOfChildren();
262
			}
263
			if ($kcount == 0) {
264
				echo "</td><td style='width:", $this->getBoxDimensions()->width, "px'>";
265
			} else {
266
				echo FontAwesome::linkIcon('arrow-start', I18N::translate('Children'), [
267
					'href'           => '#',
268
					'data-direction' => 'desc',
269
					'data-xref'      => $pid,
270
					'data-spouses'   => $this->show_spouse,
271
				]);
272
273
				//-- move the arrow up to line up with the correct box
274
				if ($this->show_spouse) {
275
					echo str_repeat('<br><br><br>', count($families));
276
				}
277
				echo "</td><td style='width:", $this->getBoxDimensions()->width, "px'>";
278
			}
279
		}
280
281
		echo '<table cellspacing="0" cellpadding="0" border="0" id="table2_' . $pid . '"><tr><td> ';
282
		FunctionsPrint::printPedigreePerson($person);
283
		echo '</td><td> <img class="lineh1" src="' . Theme::theme()->parameter('image-hline') . '" width="7" height="3">';
284
285
		//----- Print the spouse
286
		if ($this->show_spouse) {
287
			foreach ($families as $family) {
288
				echo "</td></tr><tr><td style='text-align:$otablealign'>";
289
				FunctionsPrint::printPedigreePerson($family->getSpouse($person));
290
				$numkids++;
291
				echo '</td><td> </td>';
292
			}
293
			//-- add offset divs to make things line up better
294
			if ($count == $this->dgenerations) {
295
				echo "<tr><td colspan '2'><div style='height:", ($this->bhalfheight / 2), 'px; width:', $this->getBoxDimensions()->width, "px;'><br></div>";
296
			}
297
		}
298
		echo '</td></tr></table>';
299
300
		// For the root person, print a down arrow that allows changing the root of tree
301
		if ($showNav && $count == 1) {
302
			if ($person->canShowName()) {
303
				// -- print left arrow for decendants so that we can move down the tree
304
				$famids = $person->getSpouseFamilies();
305
				//-- make sure there is more than 1 child in the family with parents
306
				$cfamids = $person->getChildFamilies();
307
				$num     = 0;
308
				foreach ($cfamids as $family) {
309
					$num += $family->getNumberOfChildren();
310
				}
311
				if ($num > 0) {
312
					echo '<div class="center" id="childarrow" style="position:absolute; width:', $this->getBoxDimensions()->width, 'px;">';
313
					echo FontAwesome::linkIcon('arrow-down', I18N::translate('Family'), ['href' => '#', 'id' => 'spouse-child-links']);
314
					echo '<div id="childbox">';
315
					echo '<table cellspacing="0" cellpadding="0" border="0" class="person_box"><tr><td> ';
316
317
					foreach ($famids as $family) {
318
						echo "<span class='name1'>" . I18N::translate('Family') . '</span>';
319
						$spouse = $family->getSpouse($person);
320
						if ($spouse) {
321
							printf(self::SWITCH_LINK, $spouse->getXref(), $this->show_spouse, $this->generations, $spouse->getFullName());
322
						}
323
						foreach ($family->getChildren() as $child) {
324
							printf(self::SWITCH_LINK, $child->getXref(), $this->show_spouse, $this->generations, $child->getFullName());
325
						}
326
					}
327
328
					//-- print the siblings
329
					foreach ($cfamids as $family) {
330
						if ($family->getHusband() || $family->getWife()) {
331
							echo "<span class='name1'>" . I18N::translate('Parents') . '</span>';
332
							$husb = $family->getHusband();
333
							if ($husb) {
334
								printf(self::SWITCH_LINK, $husb->getXref(), $this->show_spouse, $this->generations, $husb->getFullName());
335
							}
336
							$wife = $family->getWife();
337
							if ($wife) {
338
								printf(self::SWITCH_LINK, $wife->getXref(), $this->show_spouse, $this->generations, $wife->getFullName());
339
							}
340
						}
341
342
						// filter out root person from children array so only siblings remain
343
						$siblings = array_filter($family->getChildren(), function (Individual $item) use ($pid) {
344
							return $item->getXref() != $pid;
345
						});
346
						$num = count($siblings);
347
						if ($num) {
348
							echo "<span class='name1'>";
349
							echo $num > 1 ? I18N::translate('Siblings') : I18N::translate('Sibling');
350
							echo '</span>';
351
							foreach ($siblings as $child) {
352
								printf(self::SWITCH_LINK, $child->getXref(), $this->show_spouse, $this->generations, $child->getFullName());
353
							}
354
						}
355
					}
356
					echo '</td></tr></table>';
357
					echo '</div>';
358
					echo '</div>';
359
				}
360
			}
361
		}
362
		echo '</td></tr></table>';
363
364
		return $numkids;
365
	}
366
367
	/**
368
	 * Calculates number of generations a person has
369
	 *
370
	 * @param Individual $individual Start individual
371
	 * @param int        $depth      Pass in 0 and it calculates how far down descendency goes
372
	 *
373
	 * @return int Number of generations the descendency actually goes
374
	 */
375
	private function maxDescendencyGenerations(Individual $individual, $depth) {
376
		if ($depth > $this->generations) {
377
			return $depth;
378
		}
379
		$maxdc = $depth;
380 View Code Duplication
		foreach ($individual->getSpouseFamilies() as $family) {
381
			foreach ($family->getChildren() as $child) {
382
				$dc = $this->maxDescendencyGenerations($child, $depth + 1);
383
				if ($dc >= $this->generations) {
384
					return $dc;
385
				}
386
				if ($dc > $maxdc) {
387
					$maxdc = $dc;
388
				}
389
			}
390
		}
391
392
		$maxdc++;
393
		if ($maxdc == 1) {
394
			$maxdc++;
395
		}
396
397
		return $maxdc;
398
	}
399
400
	/**
401
	 * setup all of the javascript that is needed for the hourglass chart
402
	 *
403
	 * @return string
404
	 */
405
	public function setupJavascript() {
406
		return "
407
			(function() {
408
				function sizeLines() {
409
					$('.tvertline').each(function(i,e) {
410
						var pid = e.id.split('_').pop();
411
						e.style.height = Math.abs($('#table_' + pid)[0].offsetHeight - ($('#table2_' + pid)[0].offsetTop + {$this->bhalfheight})) + 'px';
412
					});
413
414
					$('.bvertline').each(function(i,e) {
415
						var pid = e.id.split('_').pop();
416
						e.style.height = $('#table_' + pid)[0].offsetTop + $('#table2_' + pid)[0].offsetTop + {$this->bhalfheight} + 'px';
417
					});
418
419
					$('.pvline').each(function(i,e) {
420
						var el = $(e);
421
						el.height(Math.floor(el.parent().height()/2));
422
					});
423
				}
424
425
				$('#spouse-child-links').on('click', function(e) {
426
					e.preventDefault();
427
					$('#childbox').slideToggle('fast');
428
				})
429
				$('.hourglassChart').on('click', '.wt-icon-arrow-start, .wt-icon-arrow-end', function (e) {
430
					e.preventDefault();
431
					e.stopPropagation();
432
433
					var direction = this.parentNode.dataset.direction;
434
					var xref      = this.parentNode.dataset.xref;
435
					var spouses   = this.parentNode.dataset.spouses;
436
					
437
					$('#td_' + xref).load('hourglass_ajax.php?rootid='+ xref +'&generations=1&type=' + direction + '&show_spouse=' + spouses, function() {
438
						sizeLines();
439
					});
440
				});
441
442
				sizeLines();
443
			})();
444
		";
445
	}
446
}
447