1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2020 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 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees\Module; 21 22use Fisharebest\Webtrees\Auth; 23use Fisharebest\Webtrees\Factory; 24use Fisharebest\Webtrees\I18N; 25use Fisharebest\Webtrees\Individual; 26use Fisharebest\Webtrees\Module\InteractiveTree\TreeView; 27use Fisharebest\Webtrees\Services\ModuleService; 28use Fisharebest\Webtrees\Tree; 29use Fisharebest\Webtrees\User; 30use Illuminate\Support\Str; 31use Psr\Http\Message\ServerRequestInterface; 32 33/** 34 * Class ChartsBlockModule 35 */ 36class ChartsBlockModule extends AbstractModule implements ModuleBlockInterface 37{ 38 use ModuleBlockTrait; 39 40 /** 41 * @var ModuleService 42 */ 43 private $module_service; 44 45 /** 46 * ChartsBlockModule constructor. 47 * 48 * @param ModuleService $module_service 49 */ 50 public function __construct(ModuleService $module_service) 51 { 52 $this->module_service = $module_service; 53 } 54 55 /** 56 * How should this module be identified in the control panel, etc.? 57 * 58 * @return string 59 */ 60 public function title(): string 61 { 62 /* I18N: Name of a module/block */ 63 return I18N::translate('Charts'); 64 } 65 66 /** 67 * A sentence describing what this module does. 68 * 69 * @return string 70 */ 71 public function description(): string 72 { 73 /* I18N: Description of the “Charts” module */ 74 return I18N::translate('An alternative way to display charts.'); 75 } 76 77 /** 78 * Generate the HTML content of this block. 79 * 80 * @param Tree $tree 81 * @param int $block_id 82 * @param string $context 83 * @param string[] $config 84 * 85 * @return string 86 */ 87 public function getBlock(Tree $tree, int $block_id, string $context, array $config = []): string 88 { 89 $PEDIGREE_ROOT_ID = $tree->getPreference('PEDIGREE_ROOT_ID'); 90 $gedcomid = $tree->getUserPreference(Auth::user(), User::PREF_TREE_ACCOUNT_XREF); 91 $default_xref = $gedcomid ?: $PEDIGREE_ROOT_ID; 92 93 $type = $this->getBlockSetting($block_id, 'type', 'pedigree'); 94 $xref = $this->getBlockSetting($block_id, 'pid', $default_xref); 95 96 extract($config, EXTR_OVERWRITE); 97 98 $individual = Factory::individual()->make($xref, $tree); 99 100 $title = $this->title(); 101 102 if ($individual instanceof Individual) { 103 switch ($type) { 104 default: 105 case 'pedigree': 106 /** @var PedigreeChartModule $module */ 107 $module = $this->module_service->findByInterface(PedigreeChartModule::class)->first(); 108 $title = $module->chartTitle($individual); 109 $chart_url = $module->chartUrl($individual, [ 110 'ajax' => true, 111 'generations' => 3, 112 'layout' => PedigreeChartModule::STYLE_RIGHT, 113 ]); 114 $content = view('modules/charts/chart', [ 115 'block_id' => $block_id, 116 'chart_url' => $chart_url, 117 'class' => 'wt-chart-pedigree', 118 ]); 119 break; 120 121 case 'descendants': 122 /** @var DescendancyChartModule $module */ 123 $module = $this->module_service->findByInterface(DescendancyChartModule::class)->first(); 124 $title = $module->chartTitle($individual); 125 $chart_url = $module->chartUrl($individual, [ 126 'ajax' => true, 127 'generations' => 2, 128 'chart_style' => DescendancyChartModule::CHART_STYLE_TREE, 129 ]); 130 $content = view('modules/charts/chart', [ 131 'block_id' => $block_id, 132 'chart_url' => $chart_url, 133 'class' => 'wt-chart-descendants', 134 ]); 135 break; 136 137 case 'hourglass': 138 /** @var HourglassChartModule $module */ 139 $module = $this->module_service->findByInterface(HourglassChartModule::class)->first(); 140 $title = $module->chartTitle($individual); 141 $chart_url = $module->chartUrl($individual, [ 142 'ajax' => true, 143 'generations' => 2, 144 ]); 145 $content = view('modules/charts/chart', [ 146 'block_id' => $block_id, 147 'chart_url' => $chart_url, 148 'class' => 'wt-chart-hourglass', 149 ]); 150 break; 151 152 case 'treenav': 153 /** @var InteractiveTreeModule $module */ 154 $module = $this->module_service->findByInterface(InteractiveTreeModule::class)->first(); 155 $title = I18N::translate('Interactive tree of %s', $individual->fullName()); 156 $tv = new TreeView(); 157 [$html, $js] = $tv->drawViewport($individual, 2); 158 $content = $html . '<script>' . $js . '</script>'; 159 break; 160 } 161 } else { 162 $content = I18N::translate('You must select an individual and a chart type in the block preferences'); 163 } 164 165 if ($context !== self::CONTEXT_EMBED) { 166 return view('modules/block-template', [ 167 'block' => Str::kebab($this->name()), 168 'id' => $block_id, 169 'config_url' => $this->configUrl($tree, $context, $block_id), 170 'title' => strip_tags($title), 171 'content' => $content, 172 ]); 173 } 174 175 return $content; 176 } 177 178 /** 179 * Should this block load asynchronously using AJAX? 180 * 181 * Simple blocks are faster in-line, more complex ones can be loaded later. 182 * 183 * @return bool 184 */ 185 public function loadAjax(): bool 186 { 187 return true; 188 } 189 190 /** 191 * Can this block be shown on the user’s home page? 192 * 193 * @return bool 194 */ 195 public function isUserBlock(): bool 196 { 197 return true; 198 } 199 200 /** 201 * Can this block be shown on the tree’s home page? 202 * 203 * @return bool 204 */ 205 public function isTreeBlock(): bool 206 { 207 return true; 208 } 209 210 /** 211 * Update the configuration for a block. 212 * 213 * @param ServerRequestInterface $request 214 * @param int $block_id 215 * 216 * @return void 217 */ 218 public function saveBlockConfiguration(ServerRequestInterface $request, int $block_id): void 219 { 220 $params = (array) $request->getParsedBody(); 221 222 $this->setBlockSetting($block_id, 'type', $params['type'] ?? 'pedigree'); 223 $this->setBlockSetting($block_id, 'pid', $params['xref'] ?? ''); 224 } 225 226 /** 227 * An HTML form to edit block settings 228 * 229 * @param Tree $tree 230 * @param int $block_id 231 * 232 * @return string 233 */ 234 public function editBlockConfiguration(Tree $tree, int $block_id): string 235 { 236 $PEDIGREE_ROOT_ID = $tree->getPreference('PEDIGREE_ROOT_ID'); 237 $gedcomid = $tree->getUserPreference(Auth::user(), User::PREF_TREE_ACCOUNT_XREF); 238 $default_xref = $gedcomid ?: $PEDIGREE_ROOT_ID; 239 240 $type = $this->getBlockSetting($block_id, 'type', 'pedigree'); 241 $xref = $this->getBlockSetting($block_id, 'pid', $default_xref); 242 243 $charts = [ 244 'pedigree' => I18N::translate('Pedigree'), 245 'descendants' => I18N::translate('Descendants'), 246 'hourglass' => I18N::translate('Hourglass chart'), 247 'treenav' => I18N::translate('Interactive tree'), 248 ]; 249 uasort($charts, 'Fisharebest\Webtrees\I18N::strcasecmp'); 250 251 $individual = Factory::individual()->make($xref, $tree); 252 253 return view('modules/charts/config', [ 254 'charts' => $charts, 255 'individual' => $individual, 256 'tree' => $tree, 257 'type' => $type, 258 ]); 259 } 260} 261