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\Exceptions\IndividualAccessDeniedException; 21use Fisharebest\Webtrees\Exceptions\IndividualNotFoundException; 22use Fisharebest\Webtrees\Gedcom; 23use Fisharebest\Webtrees\I18N; 24use Fisharebest\Webtrees\Individual; 25use Fisharebest\Webtrees\Menu; 26use Fisharebest\Webtrees\Module\InteractiveTree\TreeView; 27use Fisharebest\Webtrees\Tree; 28use Fisharebest\Webtrees\Webtrees; 29use Symfony\Component\HttpFoundation\Request; 30use Symfony\Component\HttpFoundation\Response; 31 32/** 33 * Class InteractiveTreeModule 34 * Tip : you could change the number of generations loaded before ajax calls both in individual page and in treeview page to optimize speed and server load 35 */ 36class InteractiveTreeModule extends AbstractModule implements ModuleInterface, ModuleChartInterface, ModuleTabInterface 37{ 38 use ModuleChartTrait; 39 use ModuleTabTrait; 40 41 /** {@inheritdoc} */ 42 public function title(): string 43 { 44 /* I18N: Name of a module */ 45 return I18N::translate('Interactive tree'); 46 } 47 48 /** {@inheritdoc} */ 49 public function description(): string 50 { 51 /* I18N: Description of the “Interactive tree” module */ 52 return I18N::translate('An interactive tree, showing all the ancestors and descendants of an individual.'); 53 } 54 55 /** 56 * The default position for this tab. It can be changed in the control panel. 57 * 58 * @return int 59 */ 60 public function defaultTabOrder(): int { 61 return 90; 62 } 63 64 /** {@inheritdoc} */ 65 public function getTabContent(Individual $individual): string 66 { 67 $treeview = new TreeView('tvTab'); 68 [$html, $js] = $treeview->drawViewport($individual, 3); 69 70 return view('modules/tree/tab', [ 71 'html' => $html, 72 'js' => $js, 73 'treeview_css' => $this->css(), 74 'treeview_js' => $this->js(), 75 ]); 76 } 77 78 /** 79 * @return string 80 */ 81 public function css(): string 82 { 83 return Webtrees::MODULES_PATH . $this->getName() . '/css/treeview.css'; 84 } 85 86 /** 87 * @return string 88 */ 89 public function js(): string 90 { 91 return Webtrees::MODULES_PATH . $this->getName() . '/js/treeview.js'; 92 } 93 94 /** {@inheritdoc} */ 95 public function hasTabContent(Individual $individual): bool 96 { 97 return true; 98 } 99 100 /** {@inheritdoc} */ 101 public function isGrayedOut(Individual $individual): bool 102 { 103 return false; 104 } 105 106 /** {@inheritdoc} */ 107 public function canLoadAjax(): bool 108 { 109 return true; 110 } 111 112 /** 113 * Return a menu item for this chart. 114 * 115 * @param Individual $individual 116 * 117 * @return Menu|null 118 */ 119 public function getChartMenu(Individual $individual): ?Menu 120 { 121 return new Menu( 122 $this->title(), 123 route('module', [ 124 'module' => $this->getName(), 125 'action' => 'Treeview', 126 'xref' => $individual->xref(), 127 'ged' => $individual->tree()->name(), 128 ]), 129 'menu-chart-tree', 130 ['rel' => 'nofollow'] 131 ); 132 } 133 134 /** 135 * Return a menu item for this chart - for use in individual boxes. 136 * 137 * @param Individual $individual 138 * 139 * @return Menu|null 140 */ 141 public function getBoxChartMenu(Individual $individual): ?Menu 142 { 143 return $this->getChartMenu($individual); 144 } 145 146 /** 147 * @param Request $request 148 * @param Tree $tree 149 * 150 * @return Response 151 */ 152 public function getTreeviewAction(Request $request, Tree $tree): Response 153 { 154 $xref = $request->get('xref', ''); 155 156 $individual = Individual::getInstance($xref, $tree); 157 158 if ($individual === null) { 159 throw new IndividualNotFoundException(); 160 } 161 162 if (!$individual->canShow()) { 163 throw new IndividualAccessDeniedException(); 164 } 165 166 $tv = new TreeView('tv'); 167 168 [$html, $js] = $tv->drawViewport($individual, 4); 169 170 $title = I18N::translate('Interactive tree of %s', $individual->getFullName()); 171 172 return $this->viewResponse('interactive-tree-page', [ 173 'title' => $title, 174 'individual' => $individual, 175 'js' => $js, 176 'html' => $html, 177 'tree' => $tree, 178 ]); 179 } 180 181 /** 182 * @param Request $request 183 * @param Tree $tree 184 * 185 * @return Response 186 */ 187 public function getDetailsAction(Request $request, Tree $tree): Response 188 { 189 $pid = $request->get('pid', Gedcom::REGEX_XREF); 190 $individual = Individual::getInstance($pid, $tree); 191 192 if ($individual === null) { 193 throw new IndividualNotFoundException(); 194 } 195 196 if (!$individual->canShow()) { 197 throw new IndividualAccessDeniedException(); 198 } 199 200 $instance = $request->get('instance', ''); 201 $treeview = new TreeView($instance); 202 203 return new Response($treeview->getDetails($individual)); 204 } 205 206 /** 207 * @param Request $request 208 * @param Tree $tree 209 * 210 * @return Response 211 */ 212 public function getPersonsAction(Request $request, Tree $tree): Response 213 { 214 $q = $request->get('q', ''); 215 $instance = $request->get('instance', ''); 216 $treeview = new TreeView($instance); 217 218 return new Response($treeview->getPersons($tree, $q)); 219 } 220} 221