xref: /webtrees/app/Module/DescendancyChartModule.php (revision 0f1e0f10e36ff247d0dca153a69a8b8d622c6986)
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
2054452b04SGreg Roachuse Fisharebest\Webtrees\Auth;
21e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface;
2254452b04SGreg Roachuse Fisharebest\Webtrees\Family;
2354452b04SGreg Roachuse Fisharebest\Webtrees\Functions\FunctionsCharts;
2454452b04SGreg Roachuse Fisharebest\Webtrees\Functions\FunctionsPrint;
2554452b04SGreg Roachuse Fisharebest\Webtrees\Gedcom;
2654452b04SGreg Roachuse Fisharebest\Webtrees\GedcomTag;
27168ff6f3Sric2016use Fisharebest\Webtrees\I18N;
28168ff6f3Sric2016use Fisharebest\Webtrees\Individual;
29e46b0479SScrutinizer Auto-Fixeruse Fisharebest\Webtrees\Menu;
3054452b04SGreg Roachuse Fisharebest\Webtrees\Services\ChartService;
3154452b04SGreg Roachuse Fisharebest\Webtrees\Tree;
3254452b04SGreg Roachuse Illuminate\Support\Collection;
336ccdf4f0SGreg Roachuse Psr\Http\Message\ResponseInterface;
346ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface;
3554452b04SGreg Roachuse Ramsey\Uuid\Uuid;
36168ff6f3Sric2016
37168ff6f3Sric2016/**
38168ff6f3Sric2016 * Class DescendancyChartModule
39168ff6f3Sric2016 */
4037eb8894SGreg Roachclass DescendancyChartModule extends AbstractModule implements ModuleChartInterface
41c1010edaSGreg Roach{
4249a243cbSGreg Roach    use ModuleChartTrait;
4349a243cbSGreg Roach
4454452b04SGreg Roach    // Chart styles
45*0f1e0f10SGreg Roach    public const CHART_STYLE_TREE        = 0;
46677aaceaSGreg Roach    public const CHART_STYLE_BOOKLET     = 1;
47677aaceaSGreg Roach    public const CHART_STYLE_INDIVIDUALS = 2;
48677aaceaSGreg Roach    public const CHART_STYLE_FAMILIES    = 3;
4954452b04SGreg Roach
5054452b04SGreg Roach    // Defaults
51*0f1e0f10SGreg Roach    public const DEFAULT_STYLE               = self::CHART_STYLE_TREE;
52677aaceaSGreg Roach    public const DEFAULT_GENERATIONS         = '3';
53e759aebbSGreg Roach
54e759aebbSGreg Roach    // Limits
55e759aebbSGreg Roach    public const MINIMUM_GENERATIONS = 2;
56e759aebbSGreg Roach    public const MAXIMUM_GENERATIONS = 10;
5754452b04SGreg Roach
5854452b04SGreg Roach    /** @var int[] */
5954452b04SGreg Roach    protected $dabo_num = [];
6054452b04SGreg Roach
6154452b04SGreg Roach    /** @var string[] */
6254452b04SGreg Roach    protected $dabo_sex = [];
6354452b04SGreg Roach
64168ff6f3Sric2016    /**
650cfd6963SGreg Roach     * How should this module be identified in the control panel, etc.?
66168ff6f3Sric2016     *
67168ff6f3Sric2016     * @return string
68168ff6f3Sric2016     */
6949a243cbSGreg Roach    public function title(): string
70c1010edaSGreg Roach    {
71bbb76c12SGreg Roach        /* I18N: Name of a module/chart */
72bbb76c12SGreg Roach        return I18N::translate('Descendants');
73168ff6f3Sric2016    }
74168ff6f3Sric2016
75168ff6f3Sric2016    /**
76168ff6f3Sric2016     * A sentence describing what this module does.
77168ff6f3Sric2016     *
78168ff6f3Sric2016     * @return string
79168ff6f3Sric2016     */
8049a243cbSGreg Roach    public function description(): string
81c1010edaSGreg Roach    {
82bbb76c12SGreg Roach        /* I18N: Description of the “DescendancyChart” module */
83bbb76c12SGreg Roach        return I18N::translate('A chart of an individual’s descendants.');
84168ff6f3Sric2016    }
85168ff6f3Sric2016
86168ff6f3Sric2016    /**
87377a2979SGreg Roach     * CSS class for the URL.
88377a2979SGreg Roach     *
89377a2979SGreg Roach     * @return string
90377a2979SGreg Roach     */
91377a2979SGreg Roach    public function chartMenuClass(): string
92377a2979SGreg Roach    {
93377a2979SGreg Roach        return 'menu-chart-descendants';
94377a2979SGreg Roach    }
95377a2979SGreg Roach
96377a2979SGreg Roach    /**
974eb71cfaSGreg Roach     * Return a menu item for this chart - for use in individual boxes.
984eb71cfaSGreg Roach     *
9960bc3e3fSGreg Roach     * @param Individual $individual
10060bc3e3fSGreg Roach     *
1014eb71cfaSGreg Roach     * @return Menu|null
1024eb71cfaSGreg Roach     */
103377a2979SGreg Roach    public function chartBoxMenu(Individual $individual): ?Menu
104c1010edaSGreg Roach    {
105e6562982SGreg Roach        return $this->chartMenu($individual);
106e6562982SGreg Roach    }
107e6562982SGreg Roach
108e6562982SGreg Roach    /**
109e6562982SGreg Roach     * The title for a specific instance of this chart.
110e6562982SGreg Roach     *
111e6562982SGreg Roach     * @param Individual $individual
112e6562982SGreg Roach     *
113e6562982SGreg Roach     * @return string
114e6562982SGreg Roach     */
115e6562982SGreg Roach    public function chartTitle(Individual $individual): string
116e6562982SGreg Roach    {
117e6562982SGreg Roach        /* I18N: %s is an individual’s name */
11839ca88baSGreg Roach        return I18N::translate('Descendants of %s', $individual->fullName());
119e6562982SGreg Roach    }
120e6562982SGreg Roach
121e6562982SGreg Roach    /**
12254452b04SGreg Roach     * A form to request the chart parameters.
12354452b04SGreg Roach     *
1246ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
12554452b04SGreg Roach     * @param Tree                   $tree
126e5a6b4d4SGreg Roach     * @param UserInterface          $user
12754452b04SGreg Roach     * @param ChartService           $chart_service
12854452b04SGreg Roach     *
1296ccdf4f0SGreg Roach     * @return ResponseInterface
13054452b04SGreg Roach     */
1316ccdf4f0SGreg Roach    public function getChartAction(ServerRequestInterface $request, Tree $tree, UserInterface $user, ChartService $chart_service): ResponseInterface
13254452b04SGreg Roach    {
13316e8b6e8SGreg Roach        $ajax       = (bool) ($request->getQueryParams()['ajax'] ?? false);
13416e8b6e8SGreg Roach        $xref       = $request->getQueryParams()['xref'] ?? '';
13554452b04SGreg Roach        $individual = Individual::getInstance($xref, $tree);
13654452b04SGreg Roach
13754452b04SGreg Roach        Auth::checkIndividualAccess($individual);
1389867b2f0SGreg Roach        Auth::checkComponentAccess($this, 'chart', $tree, $user);
13954452b04SGreg Roach
14016e8b6e8SGreg Roach        $chart_style = (int) ($request->getQueryParams()['chart_style'] ?? self::DEFAULT_STYLE);
14116e8b6e8SGreg Roach        $generations = (int) ($request->getQueryParams()['generations'] ?? self::DEFAULT_GENERATIONS);
14254452b04SGreg Roach
143e759aebbSGreg Roach        $generations = min($generations, self::MAXIMUM_GENERATIONS);
144e759aebbSGreg Roach        $generations = max($generations, self::MINIMUM_GENERATIONS);
14554452b04SGreg Roach
1469b5537c3SGreg Roach        if ($ajax) {
14754452b04SGreg Roach            return $this->chart($request, $tree, $chart_service);
14854452b04SGreg Roach        }
14954452b04SGreg Roach
15054452b04SGreg Roach        $ajax_url = $this->chartUrl($individual, [
15154452b04SGreg Roach            'chart_style' => $chart_style,
15254452b04SGreg Roach            'generations' => $generations,
1539b5537c3SGreg Roach            'ajax'        => true,
15454452b04SGreg Roach        ]);
15554452b04SGreg Roach
1569b5537c3SGreg Roach        return $this->viewResponse('modules/descendancy_chart/page', [
15754452b04SGreg Roach            'ajax_url'            => $ajax_url,
15854452b04SGreg Roach            'chart_style'         => $chart_style,
15954452b04SGreg Roach            'chart_styles'        => $this->chartStyles(),
160e759aebbSGreg Roach            'default_generations' => self::DEFAULT_GENERATIONS,
16154452b04SGreg Roach            'generations'         => $generations,
16254452b04SGreg Roach            'individual'          => $individual,
163e759aebbSGreg Roach            'maximum_generations' => self::MAXIMUM_GENERATIONS,
164e759aebbSGreg Roach            'minimum_generations' => self::MINIMUM_GENERATIONS,
16526684e68SGreg Roach            'module_name'         => $this->name(),
16654452b04SGreg Roach            'title'               => $this->chartTitle($individual),
16754452b04SGreg Roach        ]);
16854452b04SGreg Roach    }
16954452b04SGreg Roach
17054452b04SGreg Roach    /**
1716ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
17254452b04SGreg Roach     * @param Tree                   $tree
17354452b04SGreg Roach     * @param ChartService           $chart_service
17454452b04SGreg Roach     *
1756ccdf4f0SGreg Roach     * @return ResponseInterface
17654452b04SGreg Roach     */
1776ccdf4f0SGreg Roach    public function chart(ServerRequestInterface $request, Tree $tree, ChartService $chart_service): ResponseInterface
17854452b04SGreg Roach    {
17954452b04SGreg Roach        $this->layout = 'layouts/ajax';
18054452b04SGreg Roach
18116e8b6e8SGreg Roach        $xref       = $request->getQueryParams()['xref'];
18254452b04SGreg Roach        $individual = Individual::getInstance($xref, $tree);
18354452b04SGreg Roach
18454452b04SGreg Roach        Auth::checkIndividualAccess($individual);
18554452b04SGreg Roach
18616e8b6e8SGreg Roach        $chart_style = (int) $request->getQueryParams()['chart_style'];
18716e8b6e8SGreg Roach        $generations = (int) $request->getQueryParams()['generations'];
18854452b04SGreg Roach
189e759aebbSGreg Roach        $generations = min($generations, self::MAXIMUM_GENERATIONS);
190e759aebbSGreg Roach        $generations = max($generations, self::MINIMUM_GENERATIONS);
19154452b04SGreg Roach
19254452b04SGreg Roach        switch ($chart_style) {
193*0f1e0f10SGreg Roach            case self::CHART_STYLE_TREE:
19454452b04SGreg Roach            default:
195*0f1e0f10SGreg Roach                return response(view('modules/descendancy_chart/tree', ['individual' => $individual, 'generations' => $generations, 'daboville' => '1']));
19654452b04SGreg Roach
19754452b04SGreg Roach            case self::CHART_STYLE_BOOKLET:
19854452b04SGreg Roach                return $this->descendantsBooklet($individual, $generations);
19954452b04SGreg Roach
20054452b04SGreg Roach            case self::CHART_STYLE_INDIVIDUALS:
20154452b04SGreg Roach                $individuals = $chart_service->descendants($individual, $generations - 1);
20254452b04SGreg Roach
20354452b04SGreg Roach                return $this->descendantsIndividuals($tree, $individuals);
20454452b04SGreg Roach
20554452b04SGreg Roach            case self::CHART_STYLE_FAMILIES:
20654452b04SGreg Roach                $families = $chart_service->descendantFamilies($individual, $generations - 1);
20754452b04SGreg Roach
20854452b04SGreg Roach                return $this->descendantsFamilies($tree, $families);
20954452b04SGreg Roach        }
21054452b04SGreg Roach    }
21154452b04SGreg Roach
21254452b04SGreg Roach    /**
21354452b04SGreg Roach     * Show a tabular list of individual descendants.
21454452b04SGreg Roach     *
21554452b04SGreg Roach     * @param Tree       $tree
21654452b04SGreg Roach     * @param Collection $individuals
21754452b04SGreg Roach     *
2186ccdf4f0SGreg Roach     * @return ResponseInterface
21954452b04SGreg Roach     */
2206ccdf4f0SGreg Roach    private function descendantsIndividuals(Tree $tree, Collection $individuals): ResponseInterface
22154452b04SGreg Roach    {
22254452b04SGreg Roach        $this->layout = 'layouts/ajax';
22354452b04SGreg Roach
22454452b04SGreg Roach        return $this->viewResponse('lists/individuals-table', [
22554452b04SGreg Roach            'individuals' => $individuals,
22654452b04SGreg Roach            'sosa'        => false,
22754452b04SGreg Roach            'tree'        => $tree,
22854452b04SGreg Roach        ]);
22954452b04SGreg Roach    }
23054452b04SGreg Roach
23154452b04SGreg Roach    /**
23254452b04SGreg Roach     * Show a tabular list of individual descendants.
23354452b04SGreg Roach     *
23454452b04SGreg Roach     * @param Tree       $tree
23554452b04SGreg Roach     * @param Collection $families
23654452b04SGreg Roach     *
2376ccdf4f0SGreg Roach     * @return ResponseInterface
23854452b04SGreg Roach     */
2396ccdf4f0SGreg Roach    private function descendantsFamilies(Tree $tree, Collection $families): ResponseInterface
24054452b04SGreg Roach    {
24154452b04SGreg Roach        $this->layout = 'layouts/ajax';
24254452b04SGreg Roach
24354452b04SGreg Roach        return $this->viewResponse('lists/families-table', [
24454452b04SGreg Roach            'families' => $families,
24554452b04SGreg Roach            'tree'     => $tree,
24654452b04SGreg Roach        ]);
24754452b04SGreg Roach    }
24854452b04SGreg Roach
24954452b04SGreg Roach    /**
25054452b04SGreg Roach     * Show a booklet view of descendants
25154452b04SGreg Roach     *
25254452b04SGreg Roach     * @TODO replace ob_start() with views.
25354452b04SGreg Roach     *
25454452b04SGreg Roach     * @param Individual $individual
25554452b04SGreg Roach     * @param int        $generations
25654452b04SGreg Roach     *
2576ccdf4f0SGreg Roach     * @return ResponseInterface
25854452b04SGreg Roach     */
2596ccdf4f0SGreg Roach    private function descendantsBooklet(Individual $individual, int $generations): ResponseInterface
26054452b04SGreg Roach    {
26154452b04SGreg Roach        ob_start();
26254452b04SGreg Roach
26354452b04SGreg Roach        $this->printChildFamily($individual, $generations);
26454452b04SGreg Roach
26554452b04SGreg Roach        $html = ob_get_clean();
26654452b04SGreg Roach
2676ccdf4f0SGreg Roach        return response($html);
26854452b04SGreg Roach    }
26954452b04SGreg Roach
27054452b04SGreg Roach    /**
27154452b04SGreg Roach     * Print a child family
27254452b04SGreg Roach     *
27354452b04SGreg Roach     * @param Individual $individual
27454452b04SGreg Roach     * @param int        $depth     - the descendancy depth to show
27554452b04SGreg Roach     * @param string     $daboville - d'Aboville number
27654452b04SGreg Roach     * @param string     $gpid
27754452b04SGreg Roach     *
27854452b04SGreg Roach     * @return void
27954452b04SGreg Roach     */
280e364afe4SGreg Roach    private function printChildFamily(Individual $individual, $depth, $daboville = '1.', $gpid = ''): void
28154452b04SGreg Roach    {
28254452b04SGreg Roach        if ($depth < 2) {
28354452b04SGreg Roach            return;
28454452b04SGreg Roach        }
28554452b04SGreg Roach
28654452b04SGreg Roach        $i = 1;
28754452b04SGreg Roach
28839ca88baSGreg Roach        foreach ($individual->spouseFamilies() as $family) {
28954452b04SGreg Roach            FunctionsCharts::printSosaFamily($family, '', -1, $daboville, $individual->xref(), $gpid, false);
29039ca88baSGreg Roach            foreach ($family->children() as $child) {
29154452b04SGreg Roach                $this->printChildFamily($child, $depth - 1, $daboville . ($i++) . '.', $individual->xref());
29254452b04SGreg Roach            }
29354452b04SGreg Roach        }
29454452b04SGreg Roach    }
29554452b04SGreg Roach
29654452b04SGreg Roach    /**
29754452b04SGreg Roach     * This chart can display its output in a number of styles
29854452b04SGreg Roach     *
2994db4b4a9SGreg Roach     * @return string[]
30054452b04SGreg Roach     */
30154452b04SGreg Roach    private function chartStyles(): array
30254452b04SGreg Roach    {
30354452b04SGreg Roach        return [
304*0f1e0f10SGreg Roach            self::CHART_STYLE_TREE        => I18N::translate('Tree'),
30554452b04SGreg Roach            self::CHART_STYLE_BOOKLET     => I18N::translate('Booklet'),
30654452b04SGreg Roach            self::CHART_STYLE_INDIVIDUALS => I18N::translate('Individuals'),
30754452b04SGreg Roach            self::CHART_STYLE_FAMILIES    => I18N::translate('Families'),
30854452b04SGreg Roach        ];
309e6562982SGreg Roach    }
310168ff6f3Sric2016}
311