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