1168ff6f3Sric2016<?php 2168ff6f3Sric2016/** 3168ff6f3Sric2016 * webtrees: online genealogy 48fcd0d32SGreg Roach * Copyright (C) 2019 webtrees development team 5168ff6f3Sric2016 * This program is free software: you can redistribute it and/or modify 6168ff6f3Sric2016 * it under the terms of the GNU General Public License as published by 7168ff6f3Sric2016 * the Free Software Foundation, either version 3 of the License, or 8168ff6f3Sric2016 * (at your option) any later version. 9168ff6f3Sric2016 * This program is distributed in the hope that it will be useful, 10168ff6f3Sric2016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11168ff6f3Sric2016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12168ff6f3Sric2016 * GNU General Public License for more details. 13168ff6f3Sric2016 * You should have received a copy of the GNU General Public License 14168ff6f3Sric2016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15168ff6f3Sric2016 */ 16e7f56f2aSGreg Roachdeclare(strict_types=1); 17e7f56f2aSGreg Roach 18168ff6f3Sric2016namespace Fisharebest\Webtrees\Module; 19168ff6f3Sric2016 20389266c0SGreg Roachuse Fisharebest\Webtrees\Auth; 21e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface; 22389266c0SGreg Roachuse Fisharebest\Webtrees\Functions\FunctionsPrint; 23168ff6f3Sric2016use Fisharebest\Webtrees\I18N; 24168ff6f3Sric2016use Fisharebest\Webtrees\Individual; 25e46b0479SScrutinizer Auto-Fixeruse Fisharebest\Webtrees\Menu; 26389266c0SGreg Roachuse Fisharebest\Webtrees\Tree; 27389266c0SGreg Roachuse stdClass; 28389266c0SGreg Roachuse Symfony\Component\HttpFoundation\Request; 29389266c0SGreg Roachuse Symfony\Component\HttpFoundation\Response; 30168ff6f3Sric2016 31168ff6f3Sric2016/** 32168ff6f3Sric2016 * Class FamilyBookChartModule 33168ff6f3Sric2016 */ 3437eb8894SGreg Roachclass FamilyBookChartModule extends AbstractModule implements ModuleChartInterface 35c1010edaSGreg Roach{ 3649a243cbSGreg Roach use ModuleChartTrait; 3749a243cbSGreg Roach 38389266c0SGreg Roach // Defaults 39389266c0SGreg Roach private const DEFAULT_GENERATIONS = '2'; 40389266c0SGreg Roach private const DEFAULT_DESCENDANT_GENERATIONS = '5'; 41389266c0SGreg Roach private const DEFAULT_MAXIMUM_GENERATIONS = '9'; 42389266c0SGreg Roach 43e759aebbSGreg Roach // Limits 44e759aebbSGreg Roach public const MINIMUM_GENERATIONS = 2; 45e759aebbSGreg Roach public const MAXIMUM_GENERATIONS = 10; 46e759aebbSGreg Roach 47389266c0SGreg Roach /** @var stdClass */ 48389266c0SGreg Roach private $box; 49389266c0SGreg Roach 50389266c0SGreg Roach /** @var bool */ 51389266c0SGreg Roach private $show_spouse; 52389266c0SGreg Roach 53389266c0SGreg Roach /** @var int */ 54389266c0SGreg Roach private $descent; 55389266c0SGreg Roach 56389266c0SGreg Roach /** @var int */ 57389266c0SGreg Roach private $bhalfheight; 58389266c0SGreg Roach 59389266c0SGreg Roach /** @var int */ 60389266c0SGreg Roach private $generations; 61389266c0SGreg Roach 62389266c0SGreg Roach /** @var int */ 63389266c0SGreg Roach private $dgenerations; 64389266c0SGreg Roach 65168ff6f3Sric2016 /** 660cfd6963SGreg Roach * How should this module be identified in the control panel, etc.? 67168ff6f3Sric2016 * 68168ff6f3Sric2016 * @return string 69168ff6f3Sric2016 */ 7049a243cbSGreg Roach public function title(): string 71c1010edaSGreg Roach { 72bbb76c12SGreg Roach /* I18N: Name of a module/chart */ 73bbb76c12SGreg Roach return I18N::translate('Family book'); 74168ff6f3Sric2016 } 75168ff6f3Sric2016 76168ff6f3Sric2016 /** 77168ff6f3Sric2016 * A sentence describing what this module does. 78168ff6f3Sric2016 * 79168ff6f3Sric2016 * @return string 80168ff6f3Sric2016 */ 8149a243cbSGreg Roach public function description(): string 82c1010edaSGreg Roach { 83bbb76c12SGreg Roach /* I18N: Description of the “FamilyBookChart” module */ 84bbb76c12SGreg Roach return I18N::translate('A chart of an individual’s ancestors and descendants, as a family book.'); 85168ff6f3Sric2016 } 86168ff6f3Sric2016 87168ff6f3Sric2016 /** 88377a2979SGreg Roach * CSS class for the URL. 89377a2979SGreg Roach * 90377a2979SGreg Roach * @return string 91377a2979SGreg Roach */ 92377a2979SGreg Roach public function chartMenuClass(): string 93377a2979SGreg Roach { 94377a2979SGreg Roach return 'menu-chart-familybook'; 95377a2979SGreg Roach } 96377a2979SGreg Roach 97377a2979SGreg Roach /** 984eb71cfaSGreg Roach * Return a menu item for this chart - for use in individual boxes. 994eb71cfaSGreg Roach * 10060bc3e3fSGreg Roach * @param Individual $individual 10160bc3e3fSGreg Roach * 1024eb71cfaSGreg Roach * @return Menu|null 1034eb71cfaSGreg Roach */ 104377a2979SGreg Roach public function chartBoxMenu(Individual $individual): ?Menu 105c1010edaSGreg Roach { 106e6562982SGreg Roach return $this->chartMenu($individual); 107e6562982SGreg Roach } 108e6562982SGreg Roach 109e6562982SGreg Roach /** 110e6562982SGreg Roach * The title for a specific instance of this chart. 111e6562982SGreg Roach * 112e6562982SGreg Roach * @param Individual $individual 113e6562982SGreg Roach * 114e6562982SGreg Roach * @return string 115e6562982SGreg Roach */ 116e6562982SGreg Roach public function chartTitle(Individual $individual): string 117e6562982SGreg Roach { 118e6562982SGreg Roach /* I18N: %s is an individual’s name */ 11939ca88baSGreg Roach return I18N::translate('Family book of %s', $individual->fullName()); 120e6562982SGreg Roach } 121e6562982SGreg Roach 122e6562982SGreg Roach /** 123389266c0SGreg Roach * A form to request the chart parameters. 124389266c0SGreg Roach * 125389266c0SGreg Roach * @param Request $request 126389266c0SGreg Roach * @param Tree $tree 127e5a6b4d4SGreg Roach * @param UserInterface $user 128389266c0SGreg Roach * 129389266c0SGreg Roach * @return Response 130389266c0SGreg Roach */ 131e5a6b4d4SGreg Roach public function getChartAction(Request $request, Tree $tree, UserInterface $user): Response 132389266c0SGreg Roach { 1339b5537c3SGreg Roach $ajax = (bool) $request->get('ajax'); 134389266c0SGreg Roach $xref = $request->get('xref', ''); 135389266c0SGreg Roach $individual = Individual::getInstance($xref, $tree); 136389266c0SGreg Roach 137389266c0SGreg Roach Auth::checkIndividualAccess($individual); 1389867b2f0SGreg Roach Auth::checkComponentAccess($this, 'chart', $tree, $user); 139389266c0SGreg Roach 140389266c0SGreg Roach $show_spouse = (bool) $request->get('show_spouse'); 141e759aebbSGreg Roach $generations = (int) $request->get('generations', self::DEFAULT_GENERATIONS); 142e759aebbSGreg Roach $generations = min($generations, self::MAXIMUM_GENERATIONS); 143e759aebbSGreg Roach $generations = max($generations, self::MINIMUM_GENERATIONS); 144389266c0SGreg Roach 145389266c0SGreg Roach // Generations of ancestors/descendants in each mini-tree. 146389266c0SGreg Roach $book_size = (int) $request->get('book_size', 2); 147389266c0SGreg Roach $book_size = min($book_size, 5); 148389266c0SGreg Roach $book_size = max($book_size, 2); 149389266c0SGreg Roach 1509b5537c3SGreg Roach if ($ajax) { 151389266c0SGreg Roach return $this->chart($individual, $generations, $book_size, $show_spouse); 152389266c0SGreg Roach } 153389266c0SGreg Roach 154389266c0SGreg Roach $ajax_url = $this->chartUrl($individual, [ 1559b5537c3SGreg Roach 'ajax' => true, 156389266c0SGreg Roach 'book_size' => $book_size, 157389266c0SGreg Roach 'generations' => $generations, 158e5a6b4d4SGreg Roach 'show_spouse' => $show_spouse, 159389266c0SGreg Roach ]); 160389266c0SGreg Roach 1619b5537c3SGreg Roach return $this->viewResponse('modules/family-book-chart/page', [ 162389266c0SGreg Roach 'ajax_url' => $ajax_url, 163389266c0SGreg Roach 'book_size' => $book_size, 164389266c0SGreg Roach 'generations' => $generations, 165389266c0SGreg Roach 'individual' => $individual, 166e759aebbSGreg Roach 'maximum_generations' => self::MAXIMUM_GENERATIONS, 167e759aebbSGreg Roach 'minimum_generations' => self::MINIMUM_GENERATIONS, 16826684e68SGreg Roach 'module_name' => $this->name(), 169389266c0SGreg Roach 'show_spouse' => $show_spouse, 170389266c0SGreg Roach 'title' => $this->chartTitle($individual), 171389266c0SGreg Roach ]); 172389266c0SGreg Roach } 173389266c0SGreg Roach 174389266c0SGreg Roach /** 175389266c0SGreg Roach * @param Individual $individual 176389266c0SGreg Roach * @param int $generations 177389266c0SGreg Roach * @param int $book_size 178389266c0SGreg Roach * @param bool $show_spouse 179389266c0SGreg Roach * 180389266c0SGreg Roach * @return Response 181389266c0SGreg Roach */ 182389266c0SGreg Roach public function chart(Individual $individual, int $generations, int $book_size, bool $show_spouse): Response 183389266c0SGreg Roach { 184389266c0SGreg Roach $this->box = (object) [ 185cab242e7SGreg Roach 'width' => app(ModuleThemeInterface::class)->parameter('chart-box-x'), 186cab242e7SGreg Roach 'height' => app(ModuleThemeInterface::class)->parameter('chart-box-y'), 187389266c0SGreg Roach ]; 188389266c0SGreg Roach 189389266c0SGreg Roach $this->show_spouse = $show_spouse; 190389266c0SGreg Roach $this->descent = $generations; 191389266c0SGreg Roach $this->generations = $book_size; 192389266c0SGreg Roach 193389266c0SGreg Roach $this->bhalfheight = $this->box->height / 2; 194389266c0SGreg Roach $this->dgenerations = $this->maxDescendencyGenerations($individual, 0); 195389266c0SGreg Roach 196389266c0SGreg Roach if ($this->dgenerations < 1) { 197389266c0SGreg Roach $this->dgenerations = 1; 198389266c0SGreg Roach } 199389266c0SGreg Roach 200389266c0SGreg Roach // @TODO - this is just a wrapper around the old code. 201389266c0SGreg Roach ob_start(); 202389266c0SGreg Roach $this->printFamilyBook($individual, $generations); 203389266c0SGreg Roach $html = ob_get_clean(); 204389266c0SGreg Roach 205389266c0SGreg Roach return new Response($html); 206389266c0SGreg Roach } 207389266c0SGreg Roach 208389266c0SGreg Roach /** 209389266c0SGreg Roach * Prints descendency of passed in person 210389266c0SGreg Roach * 211389266c0SGreg Roach * @param int $generation 212389266c0SGreg Roach * @param Individual|null $person 213389266c0SGreg Roach * 214389266c0SGreg Roach * @return float 215389266c0SGreg Roach */ 216389266c0SGreg Roach private function printDescendency($generation, Individual $person = null): float 217389266c0SGreg Roach { 218389266c0SGreg Roach if ($generation > $this->dgenerations) { 219389266c0SGreg Roach return 0; 220389266c0SGreg Roach } 221389266c0SGreg Roach 222389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td>'; 223389266c0SGreg Roach $numkids = 0.0; 224389266c0SGreg Roach 225389266c0SGreg Roach // Load children 226389266c0SGreg Roach $children = []; 227389266c0SGreg Roach if ($person instanceof Individual) { 228389266c0SGreg Roach // Count is position from center to left, dgenerations is number of generations 229389266c0SGreg Roach if ($generation < $this->dgenerations) { 230389266c0SGreg Roach // All children, from all partners 23139ca88baSGreg Roach foreach ($person->spouseFamilies() as $family) { 23239ca88baSGreg Roach foreach ($family->children() as $child) { 233389266c0SGreg Roach $children[] = $child; 234389266c0SGreg Roach } 235389266c0SGreg Roach } 236389266c0SGreg Roach } 237389266c0SGreg Roach } 238389266c0SGreg Roach if ($generation < $this->dgenerations) { 239389266c0SGreg Roach if (!empty($children)) { 240389266c0SGreg Roach // real people 241389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" >'; 242389266c0SGreg Roach foreach ($children as $i => $child) { 243389266c0SGreg Roach echo '<tr><td>'; 244389266c0SGreg Roach $kids = $this->printDescendency($generation + 1, $child); 245389266c0SGreg Roach $numkids += $kids; 246389266c0SGreg Roach echo '</td>'; 247389266c0SGreg Roach // Print the lines 248389266c0SGreg Roach if (count($children) > 1) { 249389266c0SGreg Roach if ($i === 0) { 250389266c0SGreg Roach // Adjust for the first column on left 251e364afe4SGreg Roach $h = round((($this->box->height * $kids) + 8) / 2); // Assumes border = 1 and padding = 3 252389266c0SGreg Roach // Adjust for other vertical columns 253389266c0SGreg Roach if ($kids > 1) { 254389266c0SGreg Roach $h = ($kids - 1) * 4 + $h; 255389266c0SGreg Roach } 256389266c0SGreg Roach echo '<td class="align-bottom">', 257e837ff07SGreg Roach '<img id="vline_', $child->xref(), '" src="', e(asset('css/images/vline.png')), '" width="3" height="', $h - 4, '"></td>'; 258389266c0SGreg Roach } elseif ($i === count($children) - 1) { 259389266c0SGreg Roach // Adjust for the first column on left 260e364afe4SGreg Roach $h = round((($this->box->height * $kids) + 8) / 2); 261389266c0SGreg Roach // Adjust for other vertical columns 262389266c0SGreg Roach if ($kids > 1) { 263389266c0SGreg Roach $h = ($kids - 1) * 4 + $h; 264389266c0SGreg Roach } 265389266c0SGreg Roach echo '<td class="align-top">', 266e837ff07SGreg Roach '<img class="bvertline" width="3" id="vline_', $child->xref(), '" src="', e(asset('css/images/vline.png')), '" height="', $h - 2, '"></td>'; 267389266c0SGreg Roach } else { 268e837ff07SGreg Roach echo '<td class="align-bottomm"style="background: url(', e(asset('css/images/vline.png')), ');">', 269e837ff07SGreg Roach '<img class="spacer" width="3" src="', e(asset('css/images/spacer.png')), '"></td>'; 270389266c0SGreg Roach } 271389266c0SGreg Roach } 272389266c0SGreg Roach echo '</tr>'; 273389266c0SGreg Roach } 274389266c0SGreg Roach echo '</table>'; 275389266c0SGreg Roach } else { 276389266c0SGreg Roach // Hidden/empty boxes - to preserve the layout 277389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td>'; 278389266c0SGreg Roach $numkids += $this->printDescendency($generation + 1, null); 279389266c0SGreg Roach echo '</td></tr></table>'; 280389266c0SGreg Roach } 281389266c0SGreg Roach echo '</td>'; 282389266c0SGreg Roach echo '<td>'; 283389266c0SGreg Roach } 284389266c0SGreg Roach 285389266c0SGreg Roach if ($numkids === 0.0) { 286389266c0SGreg Roach $numkids = 1; 287389266c0SGreg Roach } 288389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td>'; 289389266c0SGreg Roach if ($person instanceof Individual) { 290389266c0SGreg Roach echo FunctionsPrint::printPedigreePerson($person); 291389266c0SGreg Roach echo '</td><td>', 292e837ff07SGreg Roach '<img class="linef1" src="', e(asset('css/images/hline.png')), '" width="8" height="3">'; 293389266c0SGreg Roach } else { 294389266c0SGreg Roach echo '<div style="width:', $this->box->width + 19, 'px; height:', $this->box->height + 8, 'px;"></div>', 295389266c0SGreg Roach '</td><td>'; 296389266c0SGreg Roach } 297389266c0SGreg Roach 298389266c0SGreg Roach // Print the spouse 299389266c0SGreg Roach if ($generation === 1 && $person instanceof Individual) { 300389266c0SGreg Roach if ($this->show_spouse) { 30139ca88baSGreg Roach foreach ($person->spouseFamilies() as $family) { 30239ca88baSGreg Roach $spouse = $family->spouse($person); 303389266c0SGreg Roach echo '</td></tr><tr><td>'; 304389266c0SGreg Roach echo FunctionsPrint::printPedigreePerson($spouse); 305389266c0SGreg Roach $numkids += 0.95; 306389266c0SGreg Roach echo '</td><td>'; 307389266c0SGreg Roach } 308389266c0SGreg Roach } 309389266c0SGreg Roach } 310389266c0SGreg Roach echo '</td></tr></table>'; 311389266c0SGreg Roach echo '</td></tr>'; 312389266c0SGreg Roach echo '</table>'; 313389266c0SGreg Roach 314389266c0SGreg Roach return $numkids; 315389266c0SGreg Roach } 316389266c0SGreg Roach 317389266c0SGreg Roach /** 318389266c0SGreg Roach * Prints pedigree of the person passed in 319389266c0SGreg Roach * 320389266c0SGreg Roach * @param Individual $person 321389266c0SGreg Roach * @param int $count 322389266c0SGreg Roach * 323389266c0SGreg Roach * @return void 324389266c0SGreg Roach */ 325e364afe4SGreg Roach private function printPersonPedigree($person, $count): void 326389266c0SGreg Roach { 327389266c0SGreg Roach if ($count >= $this->generations) { 328389266c0SGreg Roach return; 329389266c0SGreg Roach } 330389266c0SGreg Roach 331389266c0SGreg Roach $genoffset = $this->generations; // handle pedigree n generations lines 332389266c0SGreg Roach //-- calculate how tall the lines should be 333e364afe4SGreg Roach $lh = $this->bhalfheight * (2 ** ($genoffset - $count - 1)); 334389266c0SGreg Roach // 335389266c0SGreg Roach //Prints empty table columns for children w/o parents up to the max generation 336389266c0SGreg Roach //This allows vertical line spacing to be consistent 337*3b092cb5SGreg Roach if ($person->childFamilies()->isEmpty()) { 338389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" >'; 339389266c0SGreg Roach $this->printEmptyBox(); 340389266c0SGreg Roach 341389266c0SGreg Roach //-- recursively get the father’s family 342389266c0SGreg Roach $this->printPersonPedigree($person, $count + 1); 343389266c0SGreg Roach echo '</td><td></tr>'; 344389266c0SGreg Roach $this->printEmptyBox(); 345389266c0SGreg Roach 346389266c0SGreg Roach //-- recursively get the mother’s family 347389266c0SGreg Roach $this->printPersonPedigree($person, $count + 1); 348389266c0SGreg Roach echo '</td><td></tr></table>'; 349389266c0SGreg Roach } 350389266c0SGreg Roach 351389266c0SGreg Roach // Empty box section done, now for regular pedigree 35239ca88baSGreg Roach foreach ($person->childFamilies() as $family) { 353389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td class="align-bottom">'; 354389266c0SGreg Roach // Determine line height for two or more spouces 355389266c0SGreg Roach // And then adjust the vertical line for the root person only 356389266c0SGreg Roach $famcount = 0; 357389266c0SGreg Roach if ($this->show_spouse) { 358389266c0SGreg Roach // count number of spouses 359*3b092cb5SGreg Roach $famcount += $person->spouseFamilies()->count(); 360389266c0SGreg Roach } 361389266c0SGreg Roach $savlh = $lh; // Save current line height 362389266c0SGreg Roach if ($count == 1 && $genoffset <= $famcount) { 363389266c0SGreg Roach $linefactor = 0; 364389266c0SGreg Roach // genoffset of 2 needs no adjustment 365389266c0SGreg Roach if ($genoffset > 2) { 366389266c0SGreg Roach $tblheight = $this->box->height + 8; 367389266c0SGreg Roach if ($genoffset == 3) { 368389266c0SGreg Roach if ($famcount == 3) { 369389266c0SGreg Roach $linefactor = $tblheight / 2; 370389266c0SGreg Roach } elseif ($famcount > 3) { 371389266c0SGreg Roach $linefactor = $tblheight; 372389266c0SGreg Roach } 373389266c0SGreg Roach } 374389266c0SGreg Roach if ($genoffset == 4) { 375389266c0SGreg Roach if ($famcount == 4) { 376389266c0SGreg Roach $linefactor = $tblheight; 377389266c0SGreg Roach } elseif ($famcount > 4) { 378389266c0SGreg Roach $linefactor = ($famcount - $genoffset) * ($tblheight * 1.5); 379389266c0SGreg Roach } 380389266c0SGreg Roach } 381389266c0SGreg Roach if ($genoffset == 5) { 382389266c0SGreg Roach if ($famcount == 5) { 383389266c0SGreg Roach $linefactor = 0; 384389266c0SGreg Roach } elseif ($famcount > 5) { 385389266c0SGreg Roach $linefactor = $tblheight * ($famcount - $genoffset); 386389266c0SGreg Roach } 387389266c0SGreg Roach } 388389266c0SGreg Roach } 389e364afe4SGreg Roach $lh = (($famcount - 1) * $this->box->height - $linefactor); 390389266c0SGreg Roach if ($genoffset > 5) { 391389266c0SGreg Roach $lh = $savlh; 392389266c0SGreg Roach } 393389266c0SGreg Roach } 394e837ff07SGreg Roach echo '<img class="line3 pvline" src="', e(asset('css/images/vline.png')), '" width="3" height="', $lh, '"></td>', 395389266c0SGreg Roach '<td>', 396e837ff07SGreg Roach '<img class="linef2" src="', e(asset('css/images/hline.png')), '" height="3"></td>', 397389266c0SGreg Roach '<td>'; 398389266c0SGreg Roach $lh = $savlh; // restore original line height 399389266c0SGreg Roach //-- print the father box 40039ca88baSGreg Roach echo FunctionsPrint::printPedigreePerson($family->husband()); 401389266c0SGreg Roach echo '</td>'; 40239ca88baSGreg Roach if ($family->husband()) { 403389266c0SGreg Roach echo '<td>'; 404389266c0SGreg Roach //-- recursively get the father’s family 40539ca88baSGreg Roach $this->printPersonPedigree($family->husband(), $count + 1); 406389266c0SGreg Roach echo '</td>'; 407389266c0SGreg Roach } else { 408389266c0SGreg Roach echo '<td>'; 409389266c0SGreg Roach if ($genoffset > $count) { 410389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" >'; 411e364afe4SGreg Roach for ($i = 1; $i < ((2 ** ($genoffset - $count)) / 2); $i++) { 412389266c0SGreg Roach $this->printEmptyBox(); 413389266c0SGreg Roach echo '</tr>'; 414389266c0SGreg Roach } 415389266c0SGreg Roach echo '</table>'; 416389266c0SGreg Roach } 417389266c0SGreg Roach } 418389266c0SGreg Roach echo '</tr><tr>', 419e837ff07SGreg Roach '<td class="align-top"><img class="pvline" alt="" role="presentation" src="', e(asset('css/images/vline.png')), '" width="3" height="', $lh, '"></td>', 420e837ff07SGreg Roach '<td><img class="linef3" alt="" role="presentation" src="', e(asset('css/images/hline.png')), '" height="3"></td>', 421389266c0SGreg Roach '<td>'; 422389266c0SGreg Roach //-- print the mother box 42339ca88baSGreg Roach echo FunctionsPrint::printPedigreePerson($family->wife()); 424389266c0SGreg Roach echo '</td>'; 42539ca88baSGreg Roach if ($family->wife()) { 426389266c0SGreg Roach echo '<td>'; 427389266c0SGreg Roach //-- recursively print the mother’s family 42839ca88baSGreg Roach $this->printPersonPedigree($family->wife(), $count + 1); 429389266c0SGreg Roach echo '</td>'; 430389266c0SGreg Roach } else { 431389266c0SGreg Roach echo '<td>'; 432389266c0SGreg Roach if ($count < $genoffset - 1) { 433389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" >'; 434e364afe4SGreg Roach for ($i = 1; $i < ((2 ** (($genoffset - 1) - $count)) / 2) + 1; $i++) { 435389266c0SGreg Roach $this->printEmptyBox(); 436389266c0SGreg Roach echo '</tr>'; 437389266c0SGreg Roach $this->printEmptyBox(); 438389266c0SGreg Roach echo '</tr>'; 439389266c0SGreg Roach } 440389266c0SGreg Roach echo '</table>'; 441389266c0SGreg Roach } 442389266c0SGreg Roach } 443389266c0SGreg Roach echo '</tr>', 444389266c0SGreg Roach '</table>'; 445389266c0SGreg Roach break; 446389266c0SGreg Roach } 447389266c0SGreg Roach } 448389266c0SGreg Roach 449389266c0SGreg Roach /** 450389266c0SGreg Roach * Calculates number of generations a person has 451e6562982SGreg Roach * 452e6562982SGreg Roach * @param Individual $individual 453389266c0SGreg Roach * @param int $depth 454e6562982SGreg Roach * 455389266c0SGreg Roach * @return int 456e6562982SGreg Roach */ 457389266c0SGreg Roach private function maxDescendencyGenerations(Individual $individual, $depth): int 458e6562982SGreg Roach { 459389266c0SGreg Roach if ($depth > $this->generations) { 460389266c0SGreg Roach return $depth; 461389266c0SGreg Roach } 462389266c0SGreg Roach $maxdc = $depth; 46339ca88baSGreg Roach foreach ($individual->spouseFamilies() as $family) { 46439ca88baSGreg Roach foreach ($family->children() as $child) { 465389266c0SGreg Roach $dc = $this->maxDescendencyGenerations($child, $depth + 1); 466389266c0SGreg Roach if ($dc >= $this->generations) { 467389266c0SGreg Roach return $dc; 468389266c0SGreg Roach } 469389266c0SGreg Roach if ($dc > $maxdc) { 470389266c0SGreg Roach $maxdc = $dc; 471389266c0SGreg Roach } 472389266c0SGreg Roach } 473389266c0SGreg Roach } 474389266c0SGreg Roach $maxdc++; 475389266c0SGreg Roach if ($maxdc == 1) { 476389266c0SGreg Roach $maxdc++; 477389266c0SGreg Roach } 478389266c0SGreg Roach 479389266c0SGreg Roach return $maxdc; 480389266c0SGreg Roach } 481389266c0SGreg Roach 482389266c0SGreg Roach /** 483389266c0SGreg Roach * Print empty box 484389266c0SGreg Roach * 485389266c0SGreg Roach * @return void 486389266c0SGreg Roach */ 487389266c0SGreg Roach 488e364afe4SGreg Roach private function printEmptyBox(): void 489389266c0SGreg Roach { 490cab242e7SGreg Roach echo app(ModuleThemeInterface::class)->individualBoxEmpty(); 491389266c0SGreg Roach } 492389266c0SGreg Roach 493389266c0SGreg Roach /** 494389266c0SGreg Roach * Print a “Family Book” for an individual 495389266c0SGreg Roach * 496389266c0SGreg Roach * @param Individual $person 497389266c0SGreg Roach * @param int $descent_steps 498389266c0SGreg Roach * 499389266c0SGreg Roach * @return void 500389266c0SGreg Roach */ 501e364afe4SGreg Roach private function printFamilyBook(Individual $person, $descent_steps): void 502389266c0SGreg Roach { 503389266c0SGreg Roach if ($descent_steps == 0) { 504389266c0SGreg Roach return; 505389266c0SGreg Roach } 506389266c0SGreg Roach 507389266c0SGreg Roach echo 508389266c0SGreg Roach '<h3>', 509389266c0SGreg Roach /* I18N: %s is an individual’s name */ 51039ca88baSGreg Roach I18N::translate('Family of %s', $person->fullName()), 511389266c0SGreg Roach '</h3>', 512389266c0SGreg Roach '<table cellspacing="0" cellpadding="0" border="0" ><tr><td class="align-middle">'; 513389266c0SGreg Roach $this->dgenerations = $this->generations; 514389266c0SGreg Roach $this->printDescendency(1, $person); 515389266c0SGreg Roach echo '</td><td class="align-middle">'; 516389266c0SGreg Roach $this->printPersonPedigree($person, 1); 517242a7862SGreg Roach echo '</td></tr></table><br><br><hr class="wt-family-break"><br><br>'; 51839ca88baSGreg Roach foreach ($person->spouseFamilies() as $family) { 51939ca88baSGreg Roach foreach ($family->children() as $child) { 520389266c0SGreg Roach $this->printFamilyBook($child, $descent_steps - 1); 521389266c0SGreg Roach } 522389266c0SGreg Roach } 523e6562982SGreg Roach } 524168ff6f3Sric2016} 525