1<?php 2/** 3 * webtrees: online genealogy 4 * Copyright (C) 2019 webtrees development team 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16declare(strict_types=1); 17 18namespace Fisharebest\Webtrees\Module; 19 20use Fisharebest\Webtrees\Auth; 21use Fisharebest\Webtrees\Contracts\UserInterface; 22use Fisharebest\Webtrees\I18N; 23use Fisharebest\Webtrees\Individual; 24use Fisharebest\Webtrees\Menu; 25use Fisharebest\Webtrees\Services\ChartService; 26use Fisharebest\Webtrees\Tree; 27use Illuminate\Support\Collection; 28use Psr\Http\Message\ResponseInterface; 29use Psr\Http\Message\ServerRequestInterface; 30 31/** 32 * Class DescendancyChartModule 33 */ 34class DescendancyChartModule extends AbstractModule implements ModuleChartInterface 35{ 36 use ModuleChartTrait; 37 38 // Chart styles 39 public const CHART_STYLE_TREE = 'tree'; 40 public const CHART_STYLE_INDIVIDUALS = 'individuals'; 41 public const CHART_STYLE_FAMILIES = 'families'; 42 43 // Defaults 44 public const DEFAULT_STYLE = self::CHART_STYLE_TREE; 45 public const DEFAULT_GENERATIONS = '3'; 46 47 // Limits 48 public const MINIMUM_GENERATIONS = 2; 49 public const MAXIMUM_GENERATIONS = 10; 50 51 /** @var int[] */ 52 protected $dabo_num = []; 53 54 /** @var string[] */ 55 protected $dabo_sex = []; 56 57 /** 58 * How should this module be identified in the control panel, etc.? 59 * 60 * @return string 61 */ 62 public function title(): string 63 { 64 /* I18N: Name of a module/chart */ 65 return I18N::translate('Descendants'); 66 } 67 68 /** 69 * A sentence describing what this module does. 70 * 71 * @return string 72 */ 73 public function description(): string 74 { 75 /* I18N: Description of the “DescendancyChart” module */ 76 return I18N::translate('A chart of an individual’s descendants.'); 77 } 78 79 /** 80 * CSS class for the URL. 81 * 82 * @return string 83 */ 84 public function chartMenuClass(): string 85 { 86 return 'menu-chart-descendants'; 87 } 88 89 /** 90 * Return a menu item for this chart - for use in individual boxes. 91 * 92 * @param Individual $individual 93 * 94 * @return Menu|null 95 */ 96 public function chartBoxMenu(Individual $individual): ?Menu 97 { 98 return $this->chartMenu($individual); 99 } 100 101 /** 102 * The title for a specific instance of this chart. 103 * 104 * @param Individual $individual 105 * 106 * @return string 107 */ 108 public function chartTitle(Individual $individual): string 109 { 110 /* I18N: %s is an individual’s name */ 111 return I18N::translate('Descendants of %s', $individual->fullName()); 112 } 113 114 /** 115 * A form to request the chart parameters. 116 * 117 * @param ServerRequestInterface $request 118 * @param Tree $tree 119 * @param UserInterface $user 120 * @param ChartService $chart_service 121 * 122 * @return ResponseInterface 123 */ 124 public function getChartAction(ServerRequestInterface $request, Tree $tree, UserInterface $user, ChartService $chart_service): ResponseInterface 125 { 126 $ajax = (bool) ($request->getQueryParams()['ajax'] ?? false); 127 $xref = $request->getQueryParams()['xref'] ?? ''; 128 $individual = Individual::getInstance($xref, $tree); 129 130 Auth::checkIndividualAccess($individual); 131 Auth::checkComponentAccess($this, 'chart', $tree, $user); 132 133 $chart_style = $request->getQueryParams()['chart_style'] ?? self::DEFAULT_STYLE; 134 $generations = (int) ($request->getQueryParams()['generations'] ?? self::DEFAULT_GENERATIONS); 135 136 $generations = min($generations, self::MAXIMUM_GENERATIONS); 137 $generations = max($generations, self::MINIMUM_GENERATIONS); 138 139 if ($ajax) { 140 return $this->chart($request, $tree, $chart_service); 141 } 142 143 $ajax_url = $this->chartUrl($individual, [ 144 'chart_style' => $chart_style, 145 'generations' => $generations, 146 'ajax' => true, 147 ]); 148 149 return $this->viewResponse('modules/descendancy_chart/page', [ 150 'ajax_url' => $ajax_url, 151 'chart_style' => $chart_style, 152 'chart_styles' => $this->chartStyles(), 153 'default_generations' => self::DEFAULT_GENERATIONS, 154 'generations' => $generations, 155 'individual' => $individual, 156 'maximum_generations' => self::MAXIMUM_GENERATIONS, 157 'minimum_generations' => self::MINIMUM_GENERATIONS, 158 'module_name' => $this->name(), 159 'title' => $this->chartTitle($individual), 160 ]); 161 } 162 163 /** 164 * @param ServerRequestInterface $request 165 * @param Tree $tree 166 * @param ChartService $chart_service 167 * 168 * @return ResponseInterface 169 */ 170 public function chart(ServerRequestInterface $request, Tree $tree, ChartService $chart_service): ResponseInterface 171 { 172 $this->layout = 'layouts/ajax'; 173 174 $xref = $request->getQueryParams()['xref']; 175 $individual = Individual::getInstance($xref, $tree); 176 177 Auth::checkIndividualAccess($individual); 178 179 $chart_style = $request->getQueryParams()['chart_style']; 180 $generations = (int) $request->getQueryParams()['generations']; 181 182 $generations = min($generations, self::MAXIMUM_GENERATIONS); 183 $generations = max($generations, self::MINIMUM_GENERATIONS); 184 185 switch ($chart_style) { 186 case self::CHART_STYLE_TREE: 187 default: 188 return response(view('modules/descendancy_chart/tree', ['individual' => $individual, 'generations' => $generations, 'daboville' => '1'])); 189 190 case self::CHART_STYLE_INDIVIDUALS: 191 $individuals = $chart_service->descendants($individual, $generations - 1); 192 193 return $this->descendantsIndividuals($tree, $individuals); 194 195 case self::CHART_STYLE_FAMILIES: 196 $families = $chart_service->descendantFamilies($individual, $generations - 1); 197 198 return $this->descendantsFamilies($tree, $families); 199 } 200 } 201 202 /** 203 * Show a tabular list of individual descendants. 204 * 205 * @param Tree $tree 206 * @param Collection $individuals 207 * 208 * @return ResponseInterface 209 */ 210 private function descendantsIndividuals(Tree $tree, Collection $individuals): ResponseInterface 211 { 212 $this->layout = 'layouts/ajax'; 213 214 return $this->viewResponse('lists/individuals-table', [ 215 'individuals' => $individuals, 216 'sosa' => false, 217 'tree' => $tree, 218 ]); 219 } 220 221 /** 222 * Show a tabular list of individual descendants. 223 * 224 * @param Tree $tree 225 * @param Collection $families 226 * 227 * @return ResponseInterface 228 */ 229 private function descendantsFamilies(Tree $tree, Collection $families): ResponseInterface 230 { 231 $this->layout = 'layouts/ajax'; 232 233 return $this->viewResponse('lists/families-table', [ 234 'families' => $families, 235 'tree' => $tree, 236 ]); 237 } 238 239 /** 240 * This chart can display its output in a number of styles 241 * 242 * @return string[] 243 */ 244 private function chartStyles(): array 245 { 246 return [ 247 self::CHART_STYLE_TREE => I18N::translate('Tree'), 248 self::CHART_STYLE_INDIVIDUALS => I18N::translate('Individuals'), 249 self::CHART_STYLE_FAMILIES => I18N::translate('Families'), 250 ]; 251 } 252} 253