xref: /webtrees/app/Module/HourglassChartModule.php (revision e5a6b4d4f6f6e7ff2fba7ae2cf27546ae68a79cc)
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
2026684e68SGreg Roachuse Fisharebest\Webtrees\Auth;
21*e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface;
2226684e68SGreg Roachuse Fisharebest\Webtrees\FontAwesome;
2326684e68SGreg Roachuse Fisharebest\Webtrees\Functions\FunctionsPrint;
24168ff6f3Sric2016use Fisharebest\Webtrees\I18N;
25168ff6f3Sric2016use Fisharebest\Webtrees\Individual;
26e46b0479SScrutinizer Auto-Fixeruse Fisharebest\Webtrees\Menu;
2726684e68SGreg Roachuse Fisharebest\Webtrees\Tree;
2826684e68SGreg Roachuse Symfony\Component\HttpFoundation\Request;
2926684e68SGreg Roachuse Symfony\Component\HttpFoundation\Response;
30168ff6f3Sric2016
31168ff6f3Sric2016/**
32168ff6f3Sric2016 * Class HourglassChartModule
33168ff6f3Sric2016 */
3437eb8894SGreg Roachclass HourglassChartModule extends AbstractModule implements ModuleChartInterface
35c1010edaSGreg Roach{
3649a243cbSGreg Roach    use ModuleChartTrait;
3749a243cbSGreg Roach
3826684e68SGreg Roach    // Defaults
3926684e68SGreg Roach    private const DEFAULT_GENERATIONS         = '3';
4026684e68SGreg Roach    private const DEFAULT_MAXIMUM_GENERATIONS = '9';
4126684e68SGreg Roach
4226684e68SGreg Roach    // Limits
4326684e68SGreg Roach    private const MINIMUM_GENERATIONS = 2;
4426684e68SGreg Roach
45168ff6f3Sric2016    /**
46168ff6f3Sric2016     * How should this module be labelled on tabs, menus, etc.?
47168ff6f3Sric2016     *
48168ff6f3Sric2016     * @return string
49168ff6f3Sric2016     */
5049a243cbSGreg Roach    public function title(): string
51c1010edaSGreg Roach    {
52bbb76c12SGreg Roach        /* I18N: Name of a module/chart */
53bbb76c12SGreg Roach        return I18N::translate('Hourglass chart');
54168ff6f3Sric2016    }
55168ff6f3Sric2016
56168ff6f3Sric2016    /**
57168ff6f3Sric2016     * A sentence describing what this module does.
58168ff6f3Sric2016     *
59168ff6f3Sric2016     * @return string
60168ff6f3Sric2016     */
6149a243cbSGreg Roach    public function description(): string
62c1010edaSGreg Roach    {
63bbb76c12SGreg Roach        /* I18N: Description of the “HourglassChart” module */
64bbb76c12SGreg Roach        return I18N::translate('An hourglass chart of an individual’s ancestors and descendants.');
65168ff6f3Sric2016    }
66168ff6f3Sric2016
67168ff6f3Sric2016    /**
68377a2979SGreg Roach     * CSS class for the URL.
69377a2979SGreg Roach     *
70377a2979SGreg Roach     * @return string
71377a2979SGreg Roach     */
72377a2979SGreg Roach    public function chartMenuClass(): string
73377a2979SGreg Roach    {
74377a2979SGreg Roach        return 'menu-chart-hourglass';
75377a2979SGreg Roach    }
76377a2979SGreg Roach
77377a2979SGreg Roach    /**
784eb71cfaSGreg Roach     * Return a menu item for this chart - for use in individual boxes.
794eb71cfaSGreg Roach     *
8060bc3e3fSGreg Roach     * @param Individual $individual
8160bc3e3fSGreg Roach     *
824eb71cfaSGreg Roach     * @return Menu|null
834eb71cfaSGreg Roach     */
84377a2979SGreg Roach    public function chartBoxMenu(Individual $individual): ?Menu
85c1010edaSGreg Roach    {
86e6562982SGreg Roach        return $this->chartMenu($individual);
87e6562982SGreg Roach    }
88e6562982SGreg Roach
89e6562982SGreg Roach    /**
9026684e68SGreg Roach     * A form to request the chart parameters.
91e6562982SGreg Roach     *
9226684e68SGreg Roach     * @param Request       $request
9326684e68SGreg Roach     * @param Tree          $tree
94*e5a6b4d4SGreg Roach     * @param UserInterface $user
95e6562982SGreg Roach     *
9626684e68SGreg Roach     * @return Response
97e6562982SGreg Roach     */
98*e5a6b4d4SGreg Roach    public function getChartAction(Request $request, Tree $tree, UserInterface $user): Response
99e6562982SGreg Roach    {
1009b5537c3SGreg Roach        $ajax       = (bool) $request->get('ajax');
10126684e68SGreg Roach        $xref       = $request->get('xref', '');
10226684e68SGreg Roach        $individual = Individual::getInstance($xref, $tree);
10326684e68SGreg Roach
10426684e68SGreg Roach        Auth::checkIndividualAccess($individual);
1059867b2f0SGreg Roach        Auth::checkComponentAccess($this, 'chart', $tree, $user);
10626684e68SGreg Roach
10726684e68SGreg Roach        $maximum_generations = (int) $tree->getPreference('MAX_DESCENDANCY_GENERATIONS', self::DEFAULT_MAXIMUM_GENERATIONS);
10826684e68SGreg Roach        $default_generations = (int) $tree->getPreference('DEFAULT_PEDIGREE_GENERATIONS', self::DEFAULT_GENERATIONS);
10926684e68SGreg Roach
11026684e68SGreg Roach        $generations = (int) $request->get('generations', $default_generations);
11126684e68SGreg Roach
11226684e68SGreg Roach        $generations = min($generations, $maximum_generations);
11326684e68SGreg Roach        $generations = max($generations, self::MINIMUM_GENERATIONS);
11426684e68SGreg Roach
11526684e68SGreg Roach        $show_spouse = (bool) $request->get('show_spouse');
11626684e68SGreg Roach
1179b5537c3SGreg Roach        if ($ajax) {
11826684e68SGreg Roach            return $this->chart($individual, $generations, $show_spouse);
11926684e68SGreg Roach        }
12026684e68SGreg Roach
12126684e68SGreg Roach        $ajax_url = $this->chartUrl($individual, [
1229b5537c3SGreg Roach            'ajax' => true,
12326684e68SGreg Roach        ]);
12426684e68SGreg Roach
1259b5537c3SGreg Roach        return $this->viewResponse('modules/hourglass-chart/page', [
12626684e68SGreg Roach            'ajax_url'            => $ajax_url,
12726684e68SGreg Roach            'generations'         => $generations,
12826684e68SGreg Roach            'individual'          => $individual,
12926684e68SGreg Roach            'maximum_generations' => $maximum_generations,
13026684e68SGreg Roach            'minimum_generations' => self::MINIMUM_GENERATIONS,
13126684e68SGreg Roach            'module_name'         => $this->name(),
13226684e68SGreg Roach            'show_spouse'         => $show_spouse,
13326684e68SGreg Roach            'title'               => $this->chartTitle($individual),
13426684e68SGreg Roach        ]);
13526684e68SGreg Roach    }
13626684e68SGreg Roach
13726684e68SGreg Roach    /**
13826684e68SGreg Roach     * Generate the initial generations of the chart
13926684e68SGreg Roach     *
140*e5a6b4d4SGreg Roach     * @param Individual $individual
141*e5a6b4d4SGreg Roach     * @param int        $generations
142*e5a6b4d4SGreg Roach     * @param bool       $show_spouse
14326684e68SGreg Roach     *
14426684e68SGreg Roach     * @return Response
14526684e68SGreg Roach     */
14626684e68SGreg Roach    protected function chart(Individual $individual, int $generations, bool $show_spouse): Response
14726684e68SGreg Roach    {
14826684e68SGreg Roach        ob_start();
14926684e68SGreg Roach        $this->printDescendency($individual, 1, $generations, $show_spouse, true);
15026684e68SGreg Roach        $descendants = ob_get_clean();
15126684e68SGreg Roach
15226684e68SGreg Roach        ob_start();
15326684e68SGreg Roach        $this->printPersonPedigree($individual, 1, $generations, $show_spouse);
15426684e68SGreg Roach        $ancestors = ob_get_clean();
15526684e68SGreg Roach
15626684e68SGreg Roach        return new Response(view('modules/hourglass-chart/chart', [
15726684e68SGreg Roach            'descendants' => $descendants,
15826684e68SGreg Roach            'ancestors'   => $ancestors,
1598136679eSGreg Roach            'bhalfheight' => (int) (app()->make(ModuleThemeInterface::class)->parameter('chart-box-y') / 2),
16026684e68SGreg Roach            'module_name' => $this->name(),
16126684e68SGreg Roach        ]));
16226684e68SGreg Roach    }
16326684e68SGreg Roach
16426684e68SGreg Roach    /**
16526684e68SGreg Roach     * @param Request $request
16626684e68SGreg Roach     * @param Tree    $tree
16726684e68SGreg Roach     *
16826684e68SGreg Roach     * @return Response
16926684e68SGreg Roach     */
17026684e68SGreg Roach    public function postAncestorsAction(Request $request, Tree $tree): Response
17126684e68SGreg Roach    {
17226684e68SGreg Roach        $xref       = $request->get('xref', '');
17326684e68SGreg Roach        $individual = Individual::getInstance($xref, $tree);
17426684e68SGreg Roach
17526684e68SGreg Roach        Auth::checkIndividualAccess($individual);
17626684e68SGreg Roach
17726684e68SGreg Roach        $show_spouse = (bool) $request->get('show_spouse');
17826684e68SGreg Roach
17926684e68SGreg Roach        ob_start();
18026684e68SGreg Roach        $this->printPersonPedigree($individual, 0, 1, $show_spouse);
18126684e68SGreg Roach        $html = ob_get_clean();
18226684e68SGreg Roach
18326684e68SGreg Roach        return new Response($html);
18426684e68SGreg Roach    }
18526684e68SGreg Roach
18626684e68SGreg Roach    /**
18726684e68SGreg Roach     * @param Request $request
18826684e68SGreg Roach     * @param Tree    $tree
18926684e68SGreg Roach     *
19026684e68SGreg Roach     * @return Response
19126684e68SGreg Roach     */
19226684e68SGreg Roach    public function postDescendantsAction(Request $request, Tree $tree): Response
19326684e68SGreg Roach    {
19426684e68SGreg Roach        $xref       = $request->get('xref', '');
19526684e68SGreg Roach        $individual = Individual::getInstance($xref, $tree);
19626684e68SGreg Roach
19726684e68SGreg Roach        $show_spouse = (bool) $request->get('show_spouse');
19826684e68SGreg Roach
19926684e68SGreg Roach        ob_start();
20026684e68SGreg Roach        $this->printDescendency($individual, 1, 2, $show_spouse, false);
20126684e68SGreg Roach        $html = ob_get_clean();
20226684e68SGreg Roach
20326684e68SGreg Roach        return new Response($html);
20426684e68SGreg Roach    }
20526684e68SGreg Roach
20626684e68SGreg Roach    /**
20726684e68SGreg Roach     * Prints descendency of passed in person
20826684e68SGreg Roach     *
20926684e68SGreg Roach     * @param Individual $individual  Show descendants of this individual
21026684e68SGreg Roach     * @param int        $generation  The current generation number
21126684e68SGreg Roach     * @param int        $generations Show this number of generations
21226684e68SGreg Roach     * @param bool       $show_spouse
21326684e68SGreg Roach     * @param bool       $show_menu
21426684e68SGreg Roach     *
21526684e68SGreg Roach     * @return void
21626684e68SGreg Roach     */
21726684e68SGreg Roach    private function printDescendency(Individual $individual, int $generation, int $generations, bool $show_spouse, bool $show_menu)
21826684e68SGreg Roach    {
21926684e68SGreg Roach        static $lastGenSecondFam = false;
22026684e68SGreg Roach
22126684e68SGreg Roach        if ($generation > $generations) {
22226684e68SGreg Roach            return;
22326684e68SGreg Roach        }
22426684e68SGreg Roach        $pid         = $individual->xref();
22526684e68SGreg Roach        $tablealign  = 'right';
22626684e68SGreg Roach        $otablealign = 'left';
22726684e68SGreg Roach        if (I18N::direction() === 'rtl') {
22826684e68SGreg Roach            $tablealign  = 'left';
22926684e68SGreg Roach            $otablealign = 'right';
23026684e68SGreg Roach        }
23126684e68SGreg Roach
23226684e68SGreg Roach        //-- put a space between families on the last generation
23326684e68SGreg Roach        if ($generation == $generations - 1) {
23426684e68SGreg Roach            if ($lastGenSecondFam) {
23526684e68SGreg Roach                echo '<br>';
23626684e68SGreg Roach            }
23726684e68SGreg Roach            $lastGenSecondFam = true;
23826684e68SGreg Roach        }
23926684e68SGreg Roach        echo '<table cellspacing="0" cellpadding="0" border="0" id="table_' . e($pid) . '" class="hourglassChart" style="float:' . $tablealign . '">';
24026684e68SGreg Roach        echo '<tr>';
24126684e68SGreg Roach        echo '<td style="text-align:' . $tablealign . '">';
24226684e68SGreg Roach        $families = $individual->getSpouseFamilies();
24326684e68SGreg Roach        $children = [];
24426684e68SGreg Roach        if ($generation < $generations) {
24526684e68SGreg Roach            // Put all of the children in a common array
24626684e68SGreg Roach            foreach ($families as $family) {
24726684e68SGreg Roach                foreach ($family->getChildren() as $child) {
24826684e68SGreg Roach                    $children[] = $child;
24926684e68SGreg Roach                }
25026684e68SGreg Roach            }
25126684e68SGreg Roach
25226684e68SGreg Roach            $ct = count($children);
25326684e68SGreg Roach            if ($ct > 0) {
25426684e68SGreg Roach                echo '<table cellspacing="0" cellpadding="0" border="0" style="position: relative; top: auto; float: ' . $tablealign . ';">';
25526684e68SGreg Roach                for ($i = 0; $i < $ct; $i++) {
25626684e68SGreg Roach                    $individual2 = $children[$i];
25726684e68SGreg Roach                    $chil        = $individual2->xref();
25826684e68SGreg Roach                    echo '<tr>';
25926684e68SGreg Roach                    echo '<td id="td_', e($chil), '" class="', I18N::direction(), '" style="text-align:', $otablealign, '">';
26026684e68SGreg Roach                    $this->printDescendency($individual2, $generation + 1, $generations, $show_spouse, false);
26126684e68SGreg Roach                    echo '</td>';
26226684e68SGreg Roach
26326684e68SGreg Roach                    // Print the lines
26426684e68SGreg Roach                    if ($ct > 1) {
26526684e68SGreg Roach                        if ($i == 0) {
26626684e68SGreg Roach                            // First child
2678136679eSGreg Roach                            echo '<td style="vertical-align:bottom"><img alt="" role="presentation" class="line1 tvertline" id="vline_' . $chil . '" src="' . app()->make(ModuleThemeInterface::class)->parameter('image-vline') . '" width="3"></td>';
26826684e68SGreg Roach                        } elseif ($i == $ct - 1) {
26926684e68SGreg Roach                            // Last child
2708136679eSGreg Roach                            echo '<td style="vertical-align:top"><img alt="" role="presentation" class="bvertline" id="vline_' . $chil . '" src="' . app()->make(ModuleThemeInterface::class)->parameter('image-vline') . '" width="3"></td>';
27126684e68SGreg Roach                        } else {
27226684e68SGreg Roach                            // Middle child
2738136679eSGreg Roach                            echo '<td style="background: url(\'' . app()->make(ModuleThemeInterface::class)->parameter('image-vline') . '\');"><img alt="" role="presentation" src="' . app()->make(ModuleThemeInterface::class)->parameter('image-spacer') . '" width="3"></td>';
27426684e68SGreg Roach                        }
27526684e68SGreg Roach                    }
27626684e68SGreg Roach                    echo '</tr>';
27726684e68SGreg Roach                }
27826684e68SGreg Roach                echo '</table>';
27926684e68SGreg Roach            }
28026684e68SGreg Roach            echo '</td>';
2818136679eSGreg Roach            echo '<td class="myCharts" width="', app()->make(ModuleThemeInterface::class)->parameter('chart-box-x'), '">';
28226684e68SGreg Roach        }
28326684e68SGreg Roach
28426684e68SGreg Roach        // Print the descendency expansion arrow
28526684e68SGreg Roach        if ($generation == $generations) {
2868136679eSGreg Roach            $tbwidth = app()->make(ModuleThemeInterface::class)->parameter('chart-box-x') + 16;
28726684e68SGreg Roach            for ($j = $generation; $j < $generations; $j++) {
2888136679eSGreg Roach                echo "<div style='width: ", $tbwidth, "px;'><br></div></td><td style='width:", app()->make(ModuleThemeInterface::class)->parameter('chart-box-x'), "px'>";
28926684e68SGreg Roach            }
29026684e68SGreg Roach            $kcount = 0;
29126684e68SGreg Roach            foreach ($families as $family) {
29226684e68SGreg Roach                $kcount += $family->getNumberOfChildren();
29326684e68SGreg Roach            }
29426684e68SGreg Roach            if ($kcount == 0) {
2958136679eSGreg Roach                echo "</td><td style='width:", app()->make(ModuleThemeInterface::class)->parameter('chart-box-x'), "px'>";
29626684e68SGreg Roach            } else {
29726684e68SGreg Roach                echo FontAwesome::linkIcon('arrow-start', I18N::translate('Children'), [
29826684e68SGreg Roach                    'href'         => '#',
29926684e68SGreg Roach                    'data-route'   => 'Descendants',
30026684e68SGreg Roach                    'data-xref'    => $pid,
30126684e68SGreg Roach                    'data-spouses' => $show_spouse,
30226684e68SGreg Roach                    'data-tree'    => $individual->tree()->name(),
30326684e68SGreg Roach                ]);
30426684e68SGreg Roach
30526684e68SGreg Roach                //-- move the arrow up to line up with the correct box
30626684e68SGreg Roach                if ($show_spouse) {
30726684e68SGreg Roach                    echo str_repeat('<br><br><br>', count($families));
30826684e68SGreg Roach                }
3098136679eSGreg Roach                echo "</td><td style='width:", app()->make(ModuleThemeInterface::class)->parameter('chart-box-x'), "px'>";
31026684e68SGreg Roach            }
31126684e68SGreg Roach        }
31226684e68SGreg Roach
31326684e68SGreg Roach        echo '<table cellspacing="0" cellpadding="0" border="0" id="table2_' . $pid . '"><tr><td> ';
31426684e68SGreg Roach        echo FunctionsPrint::printPedigreePerson($individual);
3158136679eSGreg Roach        echo '</td><td> <img alt="" role="presentation" class="lineh1" src="' . app()->make(ModuleThemeInterface::class)->parameter('image-hline') . '" width="7" height="3">';
31626684e68SGreg Roach
31726684e68SGreg Roach        //----- Print the spouse
31826684e68SGreg Roach        if ($show_spouse) {
31926684e68SGreg Roach            foreach ($families as $family) {
32026684e68SGreg Roach                echo "</td></tr><tr><td style='text-align:$otablealign'>";
32126684e68SGreg Roach                echo FunctionsPrint::printPedigreePerson($family->getSpouse($individual));
32226684e68SGreg Roach                echo '</td><td> </td>';
32326684e68SGreg Roach            }
32426684e68SGreg Roach            //-- add offset divs to make things line up better
32526684e68SGreg Roach            if ($generation == $generations) {
3268136679eSGreg Roach                echo "<tr><td colspan '2'><div style='height:", (app()->make(ModuleThemeInterface::class)->parameter('chart-box-y') / 4), 'px; width:', app()->make(ModuleThemeInterface::class)->parameter('chart-box-x'), "px;'><br></div>";
32726684e68SGreg Roach            }
32826684e68SGreg Roach        }
32926684e68SGreg Roach        echo '</td></tr></table>';
33026684e68SGreg Roach
33126684e68SGreg Roach        // For the root individual, print a down arrow that allows changing the root of tree
33226684e68SGreg Roach        if ($show_menu && $generation == 1) {
3338136679eSGreg Roach            echo '<div class="center" id="childarrow" style="position:absolute; width:', app()->make(ModuleThemeInterface::class)->parameter('chart-box-x'), 'px;">';
33426684e68SGreg Roach            echo FontAwesome::linkIcon('arrow-down', I18N::translate('Family'), [
33526684e68SGreg Roach                'href' => '#',
33626684e68SGreg Roach                'id'   => 'spouse-child-links',
33726684e68SGreg Roach            ]);
33826684e68SGreg Roach            echo '<div id="childbox">';
33926684e68SGreg Roach            echo '<table cellspacing="0" cellpadding="0" border="0" class="person_box"><tr><td> ';
34026684e68SGreg Roach
34126684e68SGreg Roach            foreach ($individual->getSpouseFamilies() as $family) {
34226684e68SGreg Roach                echo "<span class='name1'>" . I18N::translate('Family') . '</span>';
34326684e68SGreg Roach                $spouse = $family->getSpouse($individual);
34426684e68SGreg Roach                if ($spouse !== null) {
34526684e68SGreg Roach                    echo '<a href="' . e(route('hourglass', [
34626684e68SGreg Roach                            'xref'        => $spouse->xref(),
34726684e68SGreg Roach                            'generations' => $generations,
34826684e68SGreg Roach                            'show_spouse' => (int) $show_spouse,
34926684e68SGreg Roach                            'ged'         => $spouse->tree()->name(),
35026684e68SGreg Roach                        ])) . '" class="name1">' . $spouse->getFullName() . '</a>';
35126684e68SGreg Roach                }
35226684e68SGreg Roach                foreach ($family->getChildren() as $child) {
35326684e68SGreg Roach                    echo '<a href="' . e(route('hourglass', [
35426684e68SGreg Roach                            'xref'        => $child->xref(),
35526684e68SGreg Roach                            'generations' => $generations,
35626684e68SGreg Roach                            'show_spouse' => (int) $show_spouse,
35726684e68SGreg Roach                            'ged'         => $child->tree()->name(),
35826684e68SGreg Roach                        ])) . '" class="name1">' . $child->getFullName() . '</a>';
35926684e68SGreg Roach                }
36026684e68SGreg Roach            }
36126684e68SGreg Roach
36226684e68SGreg Roach            //-- print the siblings
36326684e68SGreg Roach            foreach ($individual->getChildFamilies() as $family) {
36426684e68SGreg Roach                if ($family->getHusband() || $family->getWife()) {
36526684e68SGreg Roach                    echo "<span class='name1'>" . I18N::translate('Parents') . '</span>';
36626684e68SGreg Roach                    $husb = $family->getHusband();
36726684e68SGreg Roach                    if ($husb) {
36826684e68SGreg Roach                        echo '<a href="' . e(route('hourglass', [
36926684e68SGreg Roach                                'xref'        => $husb->xref(),
37026684e68SGreg Roach                                'generations' => $generations,
37126684e68SGreg Roach                                'show_spouse' => (int) $show_spouse,
37226684e68SGreg Roach                                'ged'         => $husb->tree()->name(),
37326684e68SGreg Roach                            ])) . '" class="name1">' . $husb->getFullName() . '</a>';
37426684e68SGreg Roach                    }
37526684e68SGreg Roach                    $wife = $family->getWife();
37626684e68SGreg Roach                    if ($wife) {
37726684e68SGreg Roach                        echo '<a href="' . e(route('hourglass', [
37826684e68SGreg Roach                                'xref'        => $wife->xref(),
37926684e68SGreg Roach                                'generations' => $generations,
38026684e68SGreg Roach                                'show_spouse' => (int) $show_spouse,
38126684e68SGreg Roach                                'ged'         => $wife->tree()->name(),
38226684e68SGreg Roach                            ])) . '" class="name1">' . $wife->getFullName() . '</a>';
38326684e68SGreg Roach                    }
38426684e68SGreg Roach                }
38526684e68SGreg Roach
38626684e68SGreg Roach                // filter out root person from children array so only siblings remain
38726684e68SGreg Roach                $siblings       = array_filter($family->getChildren(), function (Individual $x) use ($individual): bool {
38826684e68SGreg Roach                    return $x !== $individual;
38926684e68SGreg Roach                });
39026684e68SGreg Roach                $count_siblings = count($siblings);
39126684e68SGreg Roach                if ($count_siblings > 0) {
39226684e68SGreg Roach                    echo '<span class="name1">';
39326684e68SGreg Roach                    echo $count_siblings > 1 ? I18N::translate('Siblings') : I18N::translate('Sibling');
39426684e68SGreg Roach                    echo '</span>';
39526684e68SGreg Roach                    foreach ($siblings as $child) {
39626684e68SGreg Roach                        echo '<a href="' . e(route('hourglass', [
39726684e68SGreg Roach                                'xref'        => $child->xref(),
39826684e68SGreg Roach                                'generations' => $generations,
39926684e68SGreg Roach                                'show_spouse' => (int) $show_spouse,
40026684e68SGreg Roach                                'ged'         => $child->tree()->name(),
40126684e68SGreg Roach                            ])) . '" class="name1">' . $child->getFullName() . '</a>';
40226684e68SGreg Roach                    }
40326684e68SGreg Roach                }
40426684e68SGreg Roach            }
40526684e68SGreg Roach            echo '</td></tr></table>';
40626684e68SGreg Roach            echo '</div>';
40726684e68SGreg Roach            echo '</div>';
40826684e68SGreg Roach        }
40926684e68SGreg Roach        echo '</td></tr></table>';
41026684e68SGreg Roach    }
41126684e68SGreg Roach
41226684e68SGreg Roach    /**
41326684e68SGreg Roach     * Prints pedigree of the person passed in. Which is the descendancy
41426684e68SGreg Roach     *
41526684e68SGreg Roach     * @param Individual $individual  Show the pedigree of this individual
41626684e68SGreg Roach     * @param int        $generation  Current generation number
41726684e68SGreg Roach     * @param int        $generations Show this number of generations
41826684e68SGreg Roach     * @param bool       $show_spouse
41926684e68SGreg Roach     *
42026684e68SGreg Roach     * @return void
42126684e68SGreg Roach     */
42226684e68SGreg Roach    private function printPersonPedigree(Individual $individual, int $generation, int $generations, bool $show_spouse)
42326684e68SGreg Roach    {
42426684e68SGreg Roach        if ($generation >= $generations) {
42526684e68SGreg Roach            return;
42626684e68SGreg Roach        }
42726684e68SGreg Roach
42826684e68SGreg Roach        // handle pedigree n generations lines
42926684e68SGreg Roach        $genoffset = $generations;
43026684e68SGreg Roach
43126684e68SGreg Roach        $family = $individual->getPrimaryChildFamily();
43226684e68SGreg Roach
43326684e68SGreg Roach        if ($family === null) {
43426684e68SGreg Roach            // Prints empty table columns for children w/o parents up to the max generation
43526684e68SGreg Roach            // This allows vertical line spacing to be consistent
4368136679eSGreg Roach            echo '<table><tr><td> ' . app()->make(ModuleThemeInterface::class)->individualBoxEmpty() . '</td>';
43726684e68SGreg Roach            echo '<td> ';
43826684e68SGreg Roach            // Recursively get the father’s family
43926684e68SGreg Roach            $this->printPersonPedigree($individual, $generation + 1, $generations, $show_spouse);
44026684e68SGreg Roach            echo '</td></tr>';
4418136679eSGreg Roach            echo '<tr><td> ' . app()->make(ModuleThemeInterface::class)->individualBoxEmpty() . '</td>';
44226684e68SGreg Roach            echo '<td> ';
44326684e68SGreg Roach            // Recursively get the mother’s family
44426684e68SGreg Roach            $this->printPersonPedigree($individual, $generation + 1, $generations, $show_spouse);
44526684e68SGreg Roach            echo '</td><td> </tr></table>';
44626684e68SGreg Roach        } else {
44726684e68SGreg Roach            echo '<table cellspacing="0" cellpadding="0" border="0"  class="hourglassChart">';
44826684e68SGreg Roach            echo '<tr>';
4498136679eSGreg Roach            echo '<td style="vertical-align:bottom"><img alt="" role="presnentation" class="line3 pvline" src="' . app()->make(ModuleThemeInterface::class)->parameter('image-vline') . '" width="3"></td>';
4508136679eSGreg Roach            echo '<td> <img alt="" role="presentation" class="lineh2" src="' . app()->make(ModuleThemeInterface::class)->parameter('image-hline') . '" width="7" height="3"></td>';
45126684e68SGreg Roach            echo '<td class="myCharts"> ';
45226684e68SGreg Roach            //-- print the father box
45326684e68SGreg Roach            echo FunctionsPrint::printPedigreePerson($family->getHusband());
45426684e68SGreg Roach            echo '</td>';
45526684e68SGreg Roach            if ($family->getHusband()) {
45626684e68SGreg Roach                $ARID = $family->getHusband()->xref();
45726684e68SGreg Roach                echo '<td id="td_' . e($ARID) . '">';
45826684e68SGreg Roach
45926684e68SGreg Roach                if ($generation == $generations - 1 && $family->getHusband()->getChildFamilies()) {
46026684e68SGreg Roach                    echo FontAwesome::linkIcon('arrow-end', I18N::translate('Parents'), [
46126684e68SGreg Roach                        'href'         => '#',
46226684e68SGreg Roach                        'data-route'   => 'Ancestors',
46326684e68SGreg Roach                        'data-xref'    => $ARID,
46426684e68SGreg Roach                        'data-spouses' => (int) $show_spouse,
46526684e68SGreg Roach                        'data-tree'    => $family->getHusband()->tree()->name(),
46626684e68SGreg Roach                    ]);
46726684e68SGreg Roach                }
46826684e68SGreg Roach
46926684e68SGreg Roach                $this->printPersonPedigree($family->getHusband(), $generation + 1, $generations, $show_spouse);
47026684e68SGreg Roach                echo '</td>';
47126684e68SGreg Roach            } else {
47226684e68SGreg Roach                echo '<td> ';
47326684e68SGreg Roach                if ($generation < $genoffset - 1) {
47426684e68SGreg Roach                    echo '<table>';
47526684e68SGreg Roach                    for ($i = $generation; $i < ((2 ** (($genoffset - 1) - $generation)) / 2) + 2; $i++) {
4768136679eSGreg Roach                        echo app()->make(ModuleThemeInterface::class)->individualBoxEmpty();
47726684e68SGreg Roach                        echo '</tr>';
4788136679eSGreg Roach                        echo app()->make(ModuleThemeInterface::class)->individualBoxEmpty();
47926684e68SGreg Roach                        echo '</tr>';
48026684e68SGreg Roach                    }
48126684e68SGreg Roach                    echo '</table>';
48226684e68SGreg Roach                }
48326684e68SGreg Roach            }
48426684e68SGreg Roach            echo
48526684e68SGreg Roach            '</tr><tr>',
4868136679eSGreg Roach                '<td style="vertical-align:top"><img alt="" role="presentation" class="pvline" src="' . app()->make(ModuleThemeInterface::class)->parameter('image-vline') . '" width="3"></td>',
4878136679eSGreg Roach                '<td> <img alt="" role="presentation" class="lineh3" src="' . app()->make(ModuleThemeInterface::class)->parameter('image-hline') . '" width="7" height="3"></td>',
48826684e68SGreg Roach            '<td class="myCharts"> ';
48926684e68SGreg Roach
49026684e68SGreg Roach            echo FunctionsPrint::printPedigreePerson($family->getWife());
49126684e68SGreg Roach            echo '</td>';
49226684e68SGreg Roach            if ($family->getWife()) {
49326684e68SGreg Roach                $ARID = $family->getWife()->xref();
49426684e68SGreg Roach                echo '<td id="td_' . e($ARID) . '">';
49526684e68SGreg Roach
49626684e68SGreg Roach                if ($generation == $generations - 1 && $family->getWife()->getChildFamilies()) {
49726684e68SGreg Roach                    echo FontAwesome::linkIcon('arrow-end', I18N::translate('Parents'), [
49826684e68SGreg Roach                        'href'         => '#',
49926684e68SGreg Roach                        'data-route'   => 'Ancestors',
50026684e68SGreg Roach                        'data-xref'    => $ARID,
50126684e68SGreg Roach                        'data-spouses' => (int) $show_spouse,
50226684e68SGreg Roach                        'data-tree'    => $family->getWife()->tree()->name(),
50326684e68SGreg Roach                    ]);
50426684e68SGreg Roach                }
50526684e68SGreg Roach
50626684e68SGreg Roach                $this->printPersonPedigree($family->getWife(), $generation + 1, $generations, $show_spouse);
50726684e68SGreg Roach                echo '</td>';
50826684e68SGreg Roach            }
50926684e68SGreg Roach            echo '</tr></table>';
51026684e68SGreg Roach        }
511e6562982SGreg Roach    }
512168ff6f3Sric2016}
513