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; 21389266c0SGreg Roachuse Fisharebest\Webtrees\Functions\FunctionsPrint; 22168ff6f3Sric2016use Fisharebest\Webtrees\I18N; 23168ff6f3Sric2016use Fisharebest\Webtrees\Individual; 24e46b0479SScrutinizer Auto-Fixeruse Fisharebest\Webtrees\Menu; 25389266c0SGreg Roachuse Fisharebest\Webtrees\Theme; 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 */ 3449a243cbSGreg Roachclass FamilyBookChartModule extends AbstractModule implements ModuleInterface, 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 43389266c0SGreg Roach /** @var stdClass */ 44389266c0SGreg Roach private $box; 45389266c0SGreg Roach 46389266c0SGreg Roach /** @var bool */ 47389266c0SGreg Roach private $show_spouse; 48389266c0SGreg Roach 49389266c0SGreg Roach /** @var int */ 50389266c0SGreg Roach private $descent; 51389266c0SGreg Roach 52389266c0SGreg Roach /** @var int */ 53389266c0SGreg Roach private $bhalfheight; 54389266c0SGreg Roach 55389266c0SGreg Roach /** @var int */ 56389266c0SGreg Roach private $generations; 57389266c0SGreg Roach 58389266c0SGreg Roach /** @var int */ 59389266c0SGreg Roach private $dgenerations; 60389266c0SGreg Roach 61168ff6f3Sric2016 /** 62168ff6f3Sric2016 * How should this module be labelled on tabs, menus, etc.? 63168ff6f3Sric2016 * 64168ff6f3Sric2016 * @return string 65168ff6f3Sric2016 */ 6649a243cbSGreg Roach public function title(): string 67c1010edaSGreg Roach { 68bbb76c12SGreg Roach /* I18N: Name of a module/chart */ 69bbb76c12SGreg Roach return I18N::translate('Family book'); 70168ff6f3Sric2016 } 71168ff6f3Sric2016 72168ff6f3Sric2016 /** 73168ff6f3Sric2016 * A sentence describing what this module does. 74168ff6f3Sric2016 * 75168ff6f3Sric2016 * @return string 76168ff6f3Sric2016 */ 7749a243cbSGreg Roach public function description(): string 78c1010edaSGreg Roach { 79bbb76c12SGreg Roach /* I18N: Description of the “FamilyBookChart” module */ 80bbb76c12SGreg Roach return I18N::translate('A chart of an individual’s ancestors and descendants, as a family book.'); 81168ff6f3Sric2016 } 82168ff6f3Sric2016 83168ff6f3Sric2016 /** 84377a2979SGreg Roach * CSS class for the URL. 85377a2979SGreg Roach * 86377a2979SGreg Roach * @return string 87377a2979SGreg Roach */ 88377a2979SGreg Roach public function chartMenuClass(): string 89377a2979SGreg Roach { 90377a2979SGreg Roach return 'menu-chart-familybook'; 91377a2979SGreg Roach } 92377a2979SGreg Roach 93377a2979SGreg Roach /** 944eb71cfaSGreg Roach * Return a menu item for this chart - for use in individual boxes. 954eb71cfaSGreg Roach * 9660bc3e3fSGreg Roach * @param Individual $individual 9760bc3e3fSGreg Roach * 984eb71cfaSGreg Roach * @return Menu|null 994eb71cfaSGreg Roach */ 100377a2979SGreg Roach public function chartBoxMenu(Individual $individual): ?Menu 101c1010edaSGreg Roach { 102e6562982SGreg Roach return $this->chartMenu($individual); 103e6562982SGreg Roach } 104e6562982SGreg Roach 105e6562982SGreg Roach /** 106e6562982SGreg Roach * The title for a specific instance of this chart. 107e6562982SGreg Roach * 108e6562982SGreg Roach * @param Individual $individual 109e6562982SGreg Roach * 110e6562982SGreg Roach * @return string 111e6562982SGreg Roach */ 112e6562982SGreg Roach public function chartTitle(Individual $individual): string 113e6562982SGreg Roach { 114e6562982SGreg Roach /* I18N: %s is an individual’s name */ 115e6562982SGreg Roach return I18N::translate('Family book of %s', $individual->getFullName()); 116e6562982SGreg Roach } 117e6562982SGreg Roach 118e6562982SGreg Roach /** 119389266c0SGreg Roach * A form to request the chart parameters. 120389266c0SGreg Roach * 121389266c0SGreg Roach * @param Request $request 122389266c0SGreg Roach * @param Tree $tree 123389266c0SGreg Roach * 124389266c0SGreg Roach * @return Response 125389266c0SGreg Roach */ 126389266c0SGreg Roach public function getChartAction(Request $request, Tree $tree): Response 127389266c0SGreg Roach { 128389266c0SGreg Roach $ajax = $request->get('ajax', ''); 129389266c0SGreg Roach $xref = $request->get('xref', ''); 130389266c0SGreg Roach $individual = Individual::getInstance($xref, $tree); 131389266c0SGreg Roach 132389266c0SGreg Roach Auth::checkIndividualAccess($individual); 133389266c0SGreg Roach 134389266c0SGreg Roach $minimum_generations = 2; 135389266c0SGreg Roach $maximum_generations = (int) $tree->getPreference('MAX_DESCENDANCY_GENERATIONS', self::DEFAULT_MAXIMUM_GENERATIONS); 136389266c0SGreg Roach $default_generations = (int) $tree->getPreference('DEFAULT_PEDIGREE_GENERATIONS', self::DEFAULT_GENERATIONS); 137389266c0SGreg Roach 138389266c0SGreg Roach $show_spouse = (bool) $request->get('show_spouse'); 139389266c0SGreg Roach $generations = (int) $request->get('generations', $default_generations); 140389266c0SGreg Roach $generations = min($generations, $maximum_generations); 141389266c0SGreg Roach $generations = max($generations, $minimum_generations); 142389266c0SGreg Roach 143389266c0SGreg Roach // Generations of ancestors/descendants in each mini-tree. 144389266c0SGreg Roach $book_size = (int) $request->get('book_size', 2); 145389266c0SGreg Roach $book_size = min($book_size, 5); 146389266c0SGreg Roach $book_size = max($book_size, 2); 147389266c0SGreg Roach 148389266c0SGreg Roach if ($ajax === '1') { 149389266c0SGreg Roach return $this->chart($individual, $generations, $book_size, $show_spouse); 150389266c0SGreg Roach } 151389266c0SGreg Roach 152389266c0SGreg Roach $ajax_url = $this->chartUrl($individual, [ 153389266c0SGreg Roach 'ajax' => '1', 154389266c0SGreg Roach 'book_size' => $book_size, 155389266c0SGreg Roach 'generations' => $generations, 156389266c0SGreg Roach 'show_spouse' => $show_spouse 157389266c0SGreg Roach ]); 158389266c0SGreg Roach 159389266c0SGreg Roach return $this->viewResponse('modules/family-book-chart/chart-page', [ 160389266c0SGreg Roach 'ajax_url' => $ajax_url, 161389266c0SGreg Roach 'book_size' => $book_size, 162389266c0SGreg Roach 'generations' => $generations, 163389266c0SGreg Roach 'individual' => $individual, 164389266c0SGreg Roach 'maximum_generations' => $maximum_generations, 165389266c0SGreg Roach 'minimum_generations' => $minimum_generations, 166*26684e68SGreg Roach 'module_name' => $this->name(), 167389266c0SGreg Roach 'show_spouse' => $show_spouse, 168389266c0SGreg Roach 'title' => $this->chartTitle($individual), 169389266c0SGreg Roach ]); 170389266c0SGreg Roach } 171389266c0SGreg Roach 172389266c0SGreg Roach /** 173389266c0SGreg Roach * @param Individual $individual 174389266c0SGreg Roach * @param int $generations 175389266c0SGreg Roach * @param int $book_size 176389266c0SGreg Roach * @param bool $show_spouse 177389266c0SGreg Roach * 178389266c0SGreg Roach * @return Response 179389266c0SGreg Roach */ 180389266c0SGreg Roach public function chart(Individual $individual, int $generations, int $book_size, bool $show_spouse): Response 181389266c0SGreg Roach { 182389266c0SGreg Roach $this->box = (object) [ 183389266c0SGreg Roach 'width' => Theme::theme()->parameter('chart-box-x'), 184389266c0SGreg Roach 'height' => Theme::theme()->parameter('chart-box-y'), 185389266c0SGreg Roach ]; 186389266c0SGreg Roach 187389266c0SGreg Roach $this->show_spouse = $show_spouse; 188389266c0SGreg Roach $this->descent = $generations; 189389266c0SGreg Roach $this->generations = $book_size; 190389266c0SGreg Roach 191389266c0SGreg Roach $this->bhalfheight = $this->box->height / 2; 192389266c0SGreg Roach $this->dgenerations = $this->maxDescendencyGenerations($individual, 0); 193389266c0SGreg Roach 194389266c0SGreg Roach if ($this->dgenerations < 1) { 195389266c0SGreg Roach $this->dgenerations = 1; 196389266c0SGreg Roach } 197389266c0SGreg Roach 198389266c0SGreg Roach // @TODO - this is just a wrapper around the old code. 199389266c0SGreg Roach ob_start(); 200389266c0SGreg Roach $this->printFamilyBook($individual, $generations); 201389266c0SGreg Roach $html = ob_get_clean(); 202389266c0SGreg Roach 203389266c0SGreg Roach return new Response($html); 204389266c0SGreg Roach } 205389266c0SGreg Roach 206389266c0SGreg Roach /** 207389266c0SGreg Roach * Prints descendency of passed in person 208389266c0SGreg Roach * 209389266c0SGreg Roach * @param int $generation 210389266c0SGreg Roach * @param Individual|null $person 211389266c0SGreg Roach * 212389266c0SGreg Roach * @return float 213389266c0SGreg Roach */ 214389266c0SGreg Roach private function printDescendency($generation, Individual $person = null): float 215389266c0SGreg Roach { 216389266c0SGreg Roach if ($generation > $this->dgenerations) { 217389266c0SGreg Roach return 0; 218389266c0SGreg Roach } 219389266c0SGreg Roach 220389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td>'; 221389266c0SGreg Roach $numkids = 0.0; 222389266c0SGreg Roach 223389266c0SGreg Roach // Load children 224389266c0SGreg Roach $children = []; 225389266c0SGreg Roach if ($person instanceof Individual) { 226389266c0SGreg Roach // Count is position from center to left, dgenerations is number of generations 227389266c0SGreg Roach if ($generation < $this->dgenerations) { 228389266c0SGreg Roach // All children, from all partners 229389266c0SGreg Roach foreach ($person->getSpouseFamilies() as $family) { 230389266c0SGreg Roach foreach ($family->getChildren() as $child) { 231389266c0SGreg Roach $children[] = $child; 232389266c0SGreg Roach } 233389266c0SGreg Roach } 234389266c0SGreg Roach } 235389266c0SGreg Roach } 236389266c0SGreg Roach if ($generation < $this->dgenerations) { 237389266c0SGreg Roach if (!empty($children)) { 238389266c0SGreg Roach // real people 239389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" >'; 240389266c0SGreg Roach foreach ($children as $i => $child) { 241389266c0SGreg Roach echo '<tr><td>'; 242389266c0SGreg Roach $kids = $this->printDescendency($generation + 1, $child); 243389266c0SGreg Roach $numkids += $kids; 244389266c0SGreg Roach echo '</td>'; 245389266c0SGreg Roach // Print the lines 246389266c0SGreg Roach if (count($children) > 1) { 247389266c0SGreg Roach if ($i === 0) { 248389266c0SGreg Roach // Adjust for the first column on left 249389266c0SGreg Roach $h = round(((($this->box->height) * $kids) + 8) / 2); // Assumes border = 1 and padding = 3 250389266c0SGreg Roach // Adjust for other vertical columns 251389266c0SGreg Roach if ($kids > 1) { 252389266c0SGreg Roach $h = ($kids - 1) * 4 + $h; 253389266c0SGreg Roach } 254389266c0SGreg Roach echo '<td class="align-bottom">', 255389266c0SGreg Roach '<img id="vline_', $child->xref(), '" src="', Theme::theme()->parameter('image-vline'), '" width="3" height="', $h - 4, '"></td>'; 256389266c0SGreg Roach } elseif ($i === count($children) - 1) { 257389266c0SGreg Roach // Adjust for the first column on left 258389266c0SGreg Roach $h = round(((($this->box->height) * $kids) + 8) / 2); 259389266c0SGreg Roach // Adjust for other vertical columns 260389266c0SGreg Roach if ($kids > 1) { 261389266c0SGreg Roach $h = ($kids - 1) * 4 + $h; 262389266c0SGreg Roach } 263389266c0SGreg Roach echo '<td class="align-top">', 264389266c0SGreg Roach '<img class="bvertline" width="3" id="vline_', $child->xref(), '" src="', Theme::theme()->parameter('image-vline'), '" height="', $h - 2, '"></td>'; 265389266c0SGreg Roach } else { 266389266c0SGreg Roach echo '<td class="align-bottomm"style="background: url(', Theme::theme()->parameter('image-vline'), ');">', 267389266c0SGreg Roach '<img class="spacer" width="3" src="', Theme::theme()->parameter('image-spacer'), '"></td>'; 268389266c0SGreg Roach } 269389266c0SGreg Roach } 270389266c0SGreg Roach echo '</tr>'; 271389266c0SGreg Roach } 272389266c0SGreg Roach echo '</table>'; 273389266c0SGreg Roach } else { 274389266c0SGreg Roach // Hidden/empty boxes - to preserve the layout 275389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td>'; 276389266c0SGreg Roach $numkids += $this->printDescendency($generation + 1, null); 277389266c0SGreg Roach echo '</td></tr></table>'; 278389266c0SGreg Roach } 279389266c0SGreg Roach echo '</td>'; 280389266c0SGreg Roach echo '<td>'; 281389266c0SGreg Roach } 282389266c0SGreg Roach 283389266c0SGreg Roach if ($numkids === 0.0) { 284389266c0SGreg Roach $numkids = 1; 285389266c0SGreg Roach } 286389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td>'; 287389266c0SGreg Roach if ($person instanceof Individual) { 288389266c0SGreg Roach echo FunctionsPrint::printPedigreePerson($person); 289389266c0SGreg Roach echo '</td><td>', 290389266c0SGreg Roach '<img class="linef1" src="', Theme::theme()->parameter('image-hline'), '" width="8" height="3">'; 291389266c0SGreg Roach } else { 292389266c0SGreg Roach echo '<div style="width:', $this->box->width + 19, 'px; height:', $this->box->height + 8, 'px;"></div>', 293389266c0SGreg Roach '</td><td>'; 294389266c0SGreg Roach } 295389266c0SGreg Roach 296389266c0SGreg Roach // Print the spouse 297389266c0SGreg Roach if ($generation === 1 && $person instanceof Individual) { 298389266c0SGreg Roach if ($this->show_spouse) { 299389266c0SGreg Roach foreach ($person->getSpouseFamilies() as $family) { 300389266c0SGreg Roach $spouse = $family->getSpouse($person); 301389266c0SGreg Roach echo '</td></tr><tr><td>'; 302389266c0SGreg Roach echo FunctionsPrint::printPedigreePerson($spouse); 303389266c0SGreg Roach $numkids += 0.95; 304389266c0SGreg Roach echo '</td><td>'; 305389266c0SGreg Roach } 306389266c0SGreg Roach } 307389266c0SGreg Roach } 308389266c0SGreg Roach echo '</td></tr></table>'; 309389266c0SGreg Roach echo '</td></tr>'; 310389266c0SGreg Roach echo '</table>'; 311389266c0SGreg Roach 312389266c0SGreg Roach return $numkids; 313389266c0SGreg Roach } 314389266c0SGreg Roach 315389266c0SGreg Roach /** 316389266c0SGreg Roach * Prints pedigree of the person passed in 317389266c0SGreg Roach * 318389266c0SGreg Roach * @param Individual $person 319389266c0SGreg Roach * @param int $count 320389266c0SGreg Roach * 321389266c0SGreg Roach * @return void 322389266c0SGreg Roach */ 323389266c0SGreg Roach private function printPersonPedigree($person, $count) 324389266c0SGreg Roach { 325389266c0SGreg Roach if ($count >= $this->generations) { 326389266c0SGreg Roach return; 327389266c0SGreg Roach } 328389266c0SGreg Roach 329389266c0SGreg Roach $genoffset = $this->generations; // handle pedigree n generations lines 330389266c0SGreg Roach //-- calculate how tall the lines should be 331389266c0SGreg Roach $lh = ($this->bhalfheight) * (2 ** ($genoffset - $count - 1)); 332389266c0SGreg Roach // 333389266c0SGreg Roach //Prints empty table columns for children w/o parents up to the max generation 334389266c0SGreg Roach //This allows vertical line spacing to be consistent 335389266c0SGreg Roach if (count($person->getChildFamilies()) == 0) { 336389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" >'; 337389266c0SGreg Roach $this->printEmptyBox(); 338389266c0SGreg Roach 339389266c0SGreg Roach //-- recursively get the father’s family 340389266c0SGreg Roach $this->printPersonPedigree($person, $count + 1); 341389266c0SGreg Roach echo '</td><td></tr>'; 342389266c0SGreg Roach $this->printEmptyBox(); 343389266c0SGreg Roach 344389266c0SGreg Roach //-- recursively get the mother’s family 345389266c0SGreg Roach $this->printPersonPedigree($person, $count + 1); 346389266c0SGreg Roach echo '</td><td></tr></table>'; 347389266c0SGreg Roach } 348389266c0SGreg Roach 349389266c0SGreg Roach // Empty box section done, now for regular pedigree 350389266c0SGreg Roach foreach ($person->getChildFamilies() as $family) { 351389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td class="align-bottom">'; 352389266c0SGreg Roach // Determine line height for two or more spouces 353389266c0SGreg Roach // And then adjust the vertical line for the root person only 354389266c0SGreg Roach $famcount = 0; 355389266c0SGreg Roach if ($this->show_spouse) { 356389266c0SGreg Roach // count number of spouses 357389266c0SGreg Roach $famcount += count($person->getSpouseFamilies()); 358389266c0SGreg Roach } 359389266c0SGreg Roach $savlh = $lh; // Save current line height 360389266c0SGreg Roach if ($count == 1 && $genoffset <= $famcount) { 361389266c0SGreg Roach $linefactor = 0; 362389266c0SGreg Roach // genoffset of 2 needs no adjustment 363389266c0SGreg Roach if ($genoffset > 2) { 364389266c0SGreg Roach $tblheight = $this->box->height + 8; 365389266c0SGreg Roach if ($genoffset == 3) { 366389266c0SGreg Roach if ($famcount == 3) { 367389266c0SGreg Roach $linefactor = $tblheight / 2; 368389266c0SGreg Roach } elseif ($famcount > 3) { 369389266c0SGreg Roach $linefactor = $tblheight; 370389266c0SGreg Roach } 371389266c0SGreg Roach } 372389266c0SGreg Roach if ($genoffset == 4) { 373389266c0SGreg Roach if ($famcount == 4) { 374389266c0SGreg Roach $linefactor = $tblheight; 375389266c0SGreg Roach } elseif ($famcount > 4) { 376389266c0SGreg Roach $linefactor = ($famcount - $genoffset) * ($tblheight * 1.5); 377389266c0SGreg Roach } 378389266c0SGreg Roach } 379389266c0SGreg Roach if ($genoffset == 5) { 380389266c0SGreg Roach if ($famcount == 5) { 381389266c0SGreg Roach $linefactor = 0; 382389266c0SGreg Roach } elseif ($famcount > 5) { 383389266c0SGreg Roach $linefactor = $tblheight * ($famcount - $genoffset); 384389266c0SGreg Roach } 385389266c0SGreg Roach } 386389266c0SGreg Roach } 387389266c0SGreg Roach $lh = (($famcount - 1) * ($this->box->height) - ($linefactor)); 388389266c0SGreg Roach if ($genoffset > 5) { 389389266c0SGreg Roach $lh = $savlh; 390389266c0SGreg Roach } 391389266c0SGreg Roach } 392389266c0SGreg Roach echo '<img class="line3 pvline" src="', Theme::theme()->parameter('image-vline'), '" width="3" height="', $lh, '"></td>', 393389266c0SGreg Roach '<td>', 394389266c0SGreg Roach '<img class="linef2" src="', Theme::theme()->parameter('image-hline'), '" height="3"></td>', 395389266c0SGreg Roach '<td>'; 396389266c0SGreg Roach $lh = $savlh; // restore original line height 397389266c0SGreg Roach //-- print the father box 398389266c0SGreg Roach echo FunctionsPrint::printPedigreePerson($family->getHusband()); 399389266c0SGreg Roach echo '</td>'; 400389266c0SGreg Roach if ($family->getHusband()) { 401389266c0SGreg Roach echo '<td>'; 402389266c0SGreg Roach //-- recursively get the father’s family 403389266c0SGreg Roach $this->printPersonPedigree($family->getHusband(), $count + 1); 404389266c0SGreg Roach echo '</td>'; 405389266c0SGreg Roach } else { 406389266c0SGreg Roach echo '<td>'; 407389266c0SGreg Roach if ($genoffset > $count) { 408389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" >'; 409389266c0SGreg Roach for ($i = 1; $i < (pow(2, ($genoffset) - $count) / 2); $i++) { 410389266c0SGreg Roach $this->printEmptyBox(); 411389266c0SGreg Roach echo '</tr>'; 412389266c0SGreg Roach } 413389266c0SGreg Roach echo '</table>'; 414389266c0SGreg Roach } 415389266c0SGreg Roach } 416389266c0SGreg Roach echo '</tr><tr>', 417389266c0SGreg Roach '<td class="align-top"><img class="pvline" alt="" role="presentation" src="', Theme::theme()->parameter('image-vline'), '" width="3" height="', $lh, '"></td>', 418389266c0SGreg Roach '<td><img class="linef3" alt="" role="presentation" src="', Theme::theme()->parameter('image-hline'), '" height="3"></td>', 419389266c0SGreg Roach '<td>'; 420389266c0SGreg Roach //-- print the mother box 421389266c0SGreg Roach echo FunctionsPrint::printPedigreePerson($family->getWife()); 422389266c0SGreg Roach echo '</td>'; 423389266c0SGreg Roach if ($family->getWife()) { 424389266c0SGreg Roach echo '<td>'; 425389266c0SGreg Roach //-- recursively print the mother’s family 426389266c0SGreg Roach $this->printPersonPedigree($family->getWife(), $count + 1); 427389266c0SGreg Roach echo '</td>'; 428389266c0SGreg Roach } else { 429389266c0SGreg Roach echo '<td>'; 430389266c0SGreg Roach if ($count < $genoffset - 1) { 431389266c0SGreg Roach echo '<table cellspacing="0" cellpadding="0" border="0" >'; 432389266c0SGreg Roach for ($i = 1; $i < (pow(2, ($genoffset - 1) - $count) / 2) + 1; $i++) { 433389266c0SGreg Roach $this->printEmptyBox(); 434389266c0SGreg Roach echo '</tr>'; 435389266c0SGreg Roach $this->printEmptyBox(); 436389266c0SGreg Roach echo '</tr>'; 437389266c0SGreg Roach } 438389266c0SGreg Roach echo '</table>'; 439389266c0SGreg Roach } 440389266c0SGreg Roach } 441389266c0SGreg Roach echo '</tr>', 442389266c0SGreg Roach '</table>'; 443389266c0SGreg Roach break; 444389266c0SGreg Roach } 445389266c0SGreg Roach } 446389266c0SGreg Roach 447389266c0SGreg Roach /** 448389266c0SGreg Roach * Calculates number of generations a person has 449e6562982SGreg Roach * 450e6562982SGreg Roach * @param Individual $individual 451389266c0SGreg Roach * @param int $depth 452e6562982SGreg Roach * 453389266c0SGreg Roach * @return int 454e6562982SGreg Roach */ 455389266c0SGreg Roach private function maxDescendencyGenerations(Individual $individual, $depth): int 456e6562982SGreg Roach { 457389266c0SGreg Roach if ($depth > $this->generations) { 458389266c0SGreg Roach return $depth; 459389266c0SGreg Roach } 460389266c0SGreg Roach $maxdc = $depth; 461389266c0SGreg Roach foreach ($individual->getSpouseFamilies() as $family) { 462389266c0SGreg Roach foreach ($family->getChildren() as $child) { 463389266c0SGreg Roach $dc = $this->maxDescendencyGenerations($child, $depth + 1); 464389266c0SGreg Roach if ($dc >= $this->generations) { 465389266c0SGreg Roach return $dc; 466389266c0SGreg Roach } 467389266c0SGreg Roach if ($dc > $maxdc) { 468389266c0SGreg Roach $maxdc = $dc; 469389266c0SGreg Roach } 470389266c0SGreg Roach } 471389266c0SGreg Roach } 472389266c0SGreg Roach $maxdc++; 473389266c0SGreg Roach if ($maxdc == 1) { 474389266c0SGreg Roach $maxdc++; 475389266c0SGreg Roach } 476389266c0SGreg Roach 477389266c0SGreg Roach return $maxdc; 478389266c0SGreg Roach } 479389266c0SGreg Roach 480389266c0SGreg Roach /** 481389266c0SGreg Roach * Print empty box 482389266c0SGreg Roach * 483389266c0SGreg Roach * @return void 484389266c0SGreg Roach */ 485389266c0SGreg Roach 486389266c0SGreg Roach private function printEmptyBox() 487389266c0SGreg Roach { 488389266c0SGreg Roach echo Theme::theme()->individualBoxEmpty(); 489389266c0SGreg Roach } 490389266c0SGreg Roach 491389266c0SGreg Roach /** 492389266c0SGreg Roach * Print a “Family Book” for an individual 493389266c0SGreg Roach * 494389266c0SGreg Roach * @param Individual $person 495389266c0SGreg Roach * @param int $descent_steps 496389266c0SGreg Roach * 497389266c0SGreg Roach * @return void 498389266c0SGreg Roach */ 499389266c0SGreg Roach private function printFamilyBook(Individual $person, $descent_steps) 500389266c0SGreg Roach { 501389266c0SGreg Roach if ($descent_steps == 0) { 502389266c0SGreg Roach return; 503389266c0SGreg Roach } 504389266c0SGreg Roach 505389266c0SGreg Roach echo 506389266c0SGreg Roach '<h3>', 507389266c0SGreg Roach /* I18N: %s is an individual’s name */ 508389266c0SGreg Roach I18N::translate('Family of %s', $person->getFullName()), 509389266c0SGreg Roach '</h3>', 510389266c0SGreg Roach '<table cellspacing="0" cellpadding="0" border="0" ><tr><td class="align-middle">'; 511389266c0SGreg Roach $this->dgenerations = $this->generations; 512389266c0SGreg Roach $this->printDescendency(1, $person); 513389266c0SGreg Roach echo '</td><td class="align-middle">'; 514389266c0SGreg Roach $this->printPersonPedigree($person, 1); 515389266c0SGreg Roach echo '</td></tr></table><br><br><hr class="family-break"><br><br>'; 516389266c0SGreg Roach foreach ($person->getSpouseFamilies() as $family) { 517389266c0SGreg Roach foreach ($family->getChildren() as $child) { 518389266c0SGreg Roach $this->printFamilyBook($child, $descent_steps - 1); 519389266c0SGreg Roach } 520389266c0SGreg Roach } 521e6562982SGreg Roach } 522168ff6f3Sric2016} 523