1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2019 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17declare(strict_types=1); 18 19namespace Fisharebest\Webtrees\Module; 20 21use Fisharebest\Webtrees\Auth; 22use Fisharebest\Webtrees\Family; 23use Fisharebest\Webtrees\I18N; 24use Fisharebest\Webtrees\Individual; 25use Fisharebest\Webtrees\Menu; 26use Fisharebest\Webtrees\Tree; 27use Illuminate\Support\Collection; 28use Psr\Http\Message\ResponseInterface; 29use Psr\Http\Message\ServerRequestInterface; 30 31use function app; 32use function response; 33use function view; 34 35/** 36 * Class HourglassChartModule 37 */ 38class HourglassChartModule extends AbstractModule implements ModuleChartInterface 39{ 40 use ModuleChartTrait; 41 42 // Defaults 43 private const DEFAULT_GENERATIONS = '3'; 44 private const DEFAULT_MAXIMUM_GENERATIONS = '9'; 45 46 // Limits 47 private const MAXIMUM_GENERATIONS = 10; 48 private const MINIMUM_GENERATIONS = 2; 49 50 /** 51 * How should this module be identified in the control panel, etc.? 52 * 53 * @return string 54 */ 55 public function title(): string 56 { 57 /* I18N: Name of a module/chart */ 58 return I18N::translate('Hourglass chart'); 59 } 60 61 /** 62 * A sentence describing what this module does. 63 * 64 * @return string 65 */ 66 public function description(): string 67 { 68 /* I18N: Description of the “HourglassChart” module */ 69 return I18N::translate('An hourglass chart of an individual’s ancestors and descendants.'); 70 } 71 72 /** 73 * CSS class for the URL. 74 * 75 * @return string 76 */ 77 public function chartMenuClass(): string 78 { 79 return 'menu-chart-hourglass'; 80 } 81 82 /** 83 * Return a menu item for this chart - for use in individual boxes. 84 * 85 * @param Individual $individual 86 * 87 * @return Menu|null 88 */ 89 public function chartBoxMenu(Individual $individual): ?Menu 90 { 91 return $this->chartMenu($individual); 92 } 93 94 /** 95 * A form to request the chart parameters. 96 * 97 * @param ServerRequestInterface $request 98 * 99 * @return ResponseInterface 100 */ 101 public function getChartAction(ServerRequestInterface $request): ResponseInterface 102 { 103 $tree = $request->getAttribute('tree'); 104 $user = $request->getAttribute('user'); 105 $ajax = $request->getQueryParams()['ajax'] ?? ''; 106 $xref = $request->getQueryParams()['xref'] ?? ''; 107 $individual = Individual::getInstance($xref, $tree); 108 109 Auth::checkIndividualAccess($individual); 110 Auth::checkComponentAccess($this, 'chart', $tree, $user); 111 112 $generations = (int) ($request->getQueryParams()['generations'] ?? self::DEFAULT_GENERATIONS); 113 114 $generations = min($generations, self::MAXIMUM_GENERATIONS); 115 $generations = max($generations, self::MINIMUM_GENERATIONS); 116 117 $show_spouse = (bool) ($request->getQueryParams()['show_spouse'] ?? false); 118 119 if ($ajax === '1') { 120 return $this->chart($individual, $generations, $show_spouse); 121 } 122 123 $ajax_url = $this->chartUrl($individual, [ 124 'ajax' => true, 125 'generations' => $generations, 126 'show_spouse' => $show_spouse, 127 ]); 128 129 return $this->viewResponse('modules/hourglass-chart/page', [ 130 'ajax_url' => $ajax_url, 131 'generations' => $generations, 132 'individual' => $individual, 133 'maximum_generations' => self::MAXIMUM_GENERATIONS, 134 'minimum_generations' => self::MINIMUM_GENERATIONS, 135 'module_name' => $this->name(), 136 'show_spouse' => $show_spouse, 137 'title' => $this->chartTitle($individual), 138 ]); 139 } 140 141 /** 142 * Generate the initial generations of the chart 143 * 144 * @param Individual $individual 145 * @param int $generations 146 * @param bool $show_spouse 147 * 148 * @return ResponseInterface 149 */ 150 protected function chart(Individual $individual, int $generations, bool $show_spouse): ResponseInterface 151 { 152 $this->layout = 'layouts/ajax'; 153 154 return $this->viewResponse('modules/hourglass-chart/chart', [ 155 'generations' => $generations, 156 'individual' => $individual, 157 'show_spouse' => $show_spouse, 158 ]); 159 } 160 161 /** 162 * Generate an extension to the chart 163 * 164 * @param ServerRequestInterface $request 165 * 166 * @return ResponseInterface 167 */ 168 public function getAncestorsAction(ServerRequestInterface $request): ResponseInterface 169 { 170 $tree = $request->getAttribute('tree'); 171 $xref = $request->getQueryParams()['xref'] ?? ''; 172 173 $family = Family::getInstance($xref, $tree); 174 Auth::checkFamilyAccess($family); 175 176 return response(view('modules/hourglass-chart/parents', [ 177 'family' => $family, 178 'generations' => 1, 179 ])); 180 } 181 182 /** 183 * Generate an extension to the chart 184 * 185 * @param ServerRequestInterface $request 186 * 187 * @return ResponseInterface 188 */ 189 public function getDescendantsAction(ServerRequestInterface $request): ResponseInterface 190 { 191 $tree = $request->getAttribute('tree'); 192 $xref = $request->getQueryParams()['xref'] ?? ''; 193 194 $show_spouse = (bool) ($request->getQueryParams()['show_spouse'] ?? false); 195 $individual = Individual::getInstance($xref, $tree); 196 197 Auth::checkIndividualAccess($individual); 198 199 $children = $individual->spouseFamilies()->map(static function (Family $family): Collection { 200 return $family->children(); 201 })->flatten(); 202 203 return response(view('modules/hourglass-chart/children', [ 204 'children' => $children, 205 'generations' => 1, 206 'show_spouse' => $show_spouse, 207 ])); 208 } 209} 210