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\Functions\FunctionsCharts; 23use Fisharebest\Webtrees\Functions\FunctionsPrint; 24use Fisharebest\Webtrees\Gedcom; 25use Fisharebest\Webtrees\I18N; 26use Fisharebest\Webtrees\Individual; 27use Fisharebest\Webtrees\Menu; 28use Fisharebest\Webtrees\Services\ChartService; 29use Fisharebest\Webtrees\Tree; 30use Illuminate\Support\Collection; 31use Psr\Http\Message\ResponseInterface; 32use Psr\Http\Message\ServerRequestInterface; 33 34/** 35 * Class AncestorsChartModule 36 */ 37class AncestorsChartModule extends AbstractModule implements ModuleChartInterface 38{ 39 use ModuleChartTrait; 40 41 // Chart styles 42 protected const CHART_STYLE_LIST = 'list'; 43 protected const CHART_STYLE_BOOKLET = 'booklet'; 44 protected const CHART_STYLE_INDIVIDUALS = 'individuals'; 45 protected const CHART_STYLE_FAMILIES = 'families'; 46 47 // Defaults 48 protected const DEFAULT_COUSINS = false; 49 protected const DEFAULT_STYLE = self::CHART_STYLE_LIST; 50 protected const DEFAULT_GENERATIONS = '4'; 51 52 // Limits 53 protected const MINIMUM_GENERATIONS = 2; 54 protected const MAXIMUM_GENERATIONS = 10; 55 56 /** 57 * How should this module be identified in the control panel, etc.? 58 * 59 * @return string 60 */ 61 public function title(): string 62 { 63 /* I18N: Name of a module/chart */ 64 return I18N::translate('Ancestors'); 65 } 66 67 /** 68 * A sentence describing what this module does. 69 * 70 * @return string 71 */ 72 public function description(): string 73 { 74 /* I18N: Description of the “AncestorsChart” module */ 75 return I18N::translate('A chart of an individual’s ancestors.'); 76 } 77 78 /** 79 * CSS class for the URL. 80 * 81 * @return string 82 */ 83 public function chartMenuClass(): string 84 { 85 return 'menu-chart-ancestry'; 86 } 87 88 /** 89 * Return a menu item for this chart - for use in individual boxes. 90 * 91 * @param Individual $individual 92 * 93 * @return Menu|null 94 */ 95 public function chartBoxMenu(Individual $individual): ?Menu 96 { 97 return $this->chartMenu($individual); 98 } 99 100 /** 101 * The title for a specific instance of this chart. 102 * 103 * @param Individual $individual 104 * 105 * @return string 106 */ 107 public function chartTitle(Individual $individual): string 108 { 109 /* I18N: %s is an individual’s name */ 110 return I18N::translate('Ancestors of %s', $individual->fullName()); 111 } 112 113 /** 114 * A form to request the chart parameters. 115 * 116 * @param ServerRequestInterface $request 117 * @param Tree $tree 118 * @param UserInterface $user 119 * @param ChartService $chart_service 120 * 121 * @return ResponseInterface 122 */ 123 public function getChartAction(ServerRequestInterface $request, Tree $tree, UserInterface $user, ChartService $chart_service): ResponseInterface 124 { 125 $ajax = (bool) $request->get('ajax'); 126 $xref = $request->get('xref', ''); 127 $individual = Individual::getInstance($xref, $tree); 128 129 Auth::checkIndividualAccess($individual); 130 Auth::checkComponentAccess($this, 'chart', $tree, $user); 131 132 $show_cousins = (bool) $request->get('show_cousins', self::DEFAULT_COUSINS); 133 $chart_style = $request->get('chart_style', self::DEFAULT_STYLE); 134 $generations = (int) $request->get('generations', self::DEFAULT_GENERATIONS); 135 136 $generations = min($generations, self::MAXIMUM_GENERATIONS); 137 $generations = max($generations, self::MINIMUM_GENERATIONS); 138 139 if ($ajax) { 140 $ancestors = $chart_service->sosaStradonitzAncestors($individual, $generations); 141 142 switch ($chart_style) { 143 default: 144 case self::CHART_STYLE_LIST: 145 return response(view('modules/ancestors-chart/list', ['individual' => $individual, 'parents' => $individual->primaryChildFamily(), 'generations' => $generations, 'sosa' => 1])); 146 147 case self::CHART_STYLE_BOOKLET: 148 return $this->ancestorsBooklet($ancestors, $show_cousins); 149 150 case self::CHART_STYLE_INDIVIDUALS: 151 return $this->ancestorsIndividuals($tree, $ancestors); 152 153 case self::CHART_STYLE_FAMILIES: 154 return $this->ancestorsFamilies($tree, $ancestors); 155 } 156 } 157 158 $ajax_url = $this->chartUrl($individual, [ 159 'generations' => $generations, 160 'chart_style' => $chart_style, 161 'show_cousins' => $show_cousins, 162 'ajax' => true, 163 ]); 164 165 return $this->viewResponse('modules/ancestors-chart/page', [ 166 'ajax_url' => $ajax_url, 167 'chart_style' => $chart_style, 168 'chart_styles' => $this->chartStyles(), 169 'default_generations' => self::DEFAULT_GENERATIONS, 170 'generations' => $generations, 171 'individual' => $individual, 172 'maximum_generations' => self::MAXIMUM_GENERATIONS, 173 'minimum_generations' => self::MINIMUM_GENERATIONS, 174 'module_name' => $this->name(), 175 'show_cousins' => $show_cousins, 176 'title' => $this->chartTitle($individual), 177 ]); 178 } 179 180 /** 181 * Show a tabular list of individual ancestors. 182 * 183 * @param Tree $tree 184 * @param Collection $ancestors 185 * 186 * @return ResponseInterface 187 */ 188 protected function ancestorsIndividuals(Tree $tree, Collection $ancestors): ResponseInterface 189 { 190 $this->layout = 'layouts/ajax'; 191 192 return $this->viewResponse('lists/individuals-table', [ 193 'individuals' => $ancestors, 194 'sosa' => true, 195 'tree' => $tree, 196 ]); 197 } 198 199 /** 200 * Show a tabular list of individual ancestors. 201 * 202 * @param Tree $tree 203 * @param Collection $ancestors 204 * 205 * @return ResponseInterface 206 */ 207 protected function ancestorsFamilies(Tree $tree, Collection $ancestors): ResponseInterface 208 { 209 $this->layout = 'layouts/ajax'; 210 211 $families = []; 212 foreach ($ancestors as $individual) { 213 foreach ($individual->childFamilies() as $family) { 214 $families[$family->xref()] = $family; 215 } 216 } 217 218 return $this->viewResponse('lists/families-table', [ 219 'families' => $families, 220 'tree' => $tree, 221 ]); 222 } 223 224 /** 225 * Show a booklet view of ancestors 226 * 227 * @TODO replace ob_start() with views. 228 * 229 * @param Collection $ancestors 230 * @param bool $show_cousins 231 * 232 * @return ResponseInterface 233 */ 234 protected function ancestorsBooklet(Collection $ancestors, bool $show_cousins): ResponseInterface 235 { 236 ob_start(); 237 238 echo FunctionsPrint::printPedigreePerson($ancestors[1]); 239 foreach ($ancestors as $sosa => $individual) { 240 foreach ($individual->childFamilies() as $family) { 241 FunctionsCharts::printSosaFamily($family, $individual->xref(), $sosa, '', '', '', $show_cousins); 242 } 243 } 244 245 $html = ob_get_clean(); 246 247 return response($html); 248 } 249 250 /** 251 * This chart can display its output in a number of styles 252 * 253 * @return array 254 */ 255 protected function chartStyles(): array 256 { 257 return [ 258 self::CHART_STYLE_LIST => I18N::translate('List'), 259 self::CHART_STYLE_BOOKLET => I18N::translate('Booklet'), 260 self::CHART_STYLE_INDIVIDUALS => I18N::translate('Individuals'), 261 self::CHART_STYLE_FAMILIES => I18N::translate('Families'), 262 ]; 263 } 264} 265