xref: /webtrees/app/Module/InteractiveTreeModule.php (revision 3976b4703df669696105ed6b024b96d433c8fbdb)
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\Exceptions\IndividualAccessDeniedException;
23use Fisharebest\Webtrees\Exceptions\IndividualNotFoundException;
24use Fisharebest\Webtrees\I18N;
25use Fisharebest\Webtrees\Individual;
26use Fisharebest\Webtrees\Menu;
27use Fisharebest\Webtrees\Module\InteractiveTree\TreeView;
28use Psr\Http\Message\ResponseInterface;
29use Psr\Http\Message\ServerRequestInterface;
30
31/**
32 * Class InteractiveTreeModule
33 * 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
34 */
35class InteractiveTreeModule extends AbstractModule implements ModuleChartInterface, ModuleTabInterface
36{
37    use ModuleChartTrait;
38    use ModuleTabTrait;
39
40    /**
41     * How should this module be identified in the control panel, etc.?
42     *
43     * @return string
44     */
45    public function title(): string
46    {
47        /* I18N: Name of a module */
48        return I18N::translate('Interactive tree');
49    }
50
51    /**
52     * A sentence describing what this module does.
53     *
54     * @return string
55     */
56    public function description(): string
57    {
58        /* I18N: Description of the “Interactive tree” module */
59        return I18N::translate('An interactive tree, showing all the ancestors and descendants of an individual.');
60    }
61
62    /**
63     * The default position for this tab.  It can be changed in the control panel.
64     *
65     * @return int
66     */
67    public function defaultTabOrder(): int
68    {
69        return 7;
70    }
71
72    /**
73     * Generate the HTML content of this tab.
74     *
75     * @param Individual $individual
76     *
77     * @return string
78     */
79    public function getTabContent(Individual $individual): string
80    {
81        $treeview = new TreeView('tvTab');
82
83        [$html, $js] = $treeview->drawViewport($individual, 3);
84
85        return view('modules/interactive-tree/tab', [
86            'html' => $html,
87            'js'   => $js,
88        ]);
89    }
90
91    /**
92     * Is this tab empty? If so, we don't always need to display it.
93     *
94     * @param Individual $individual
95     *
96     * @return bool
97     */
98    public function hasTabContent(Individual $individual): bool
99    {
100        return true;
101    }
102
103    /**
104     * A greyed out tab has no actual content, but may perhaps have
105     * options to create content.
106     *
107     * @param Individual $individual
108     *
109     * @return bool
110     */
111    public function isGrayedOut(Individual $individual): bool
112    {
113        return false;
114    }
115
116    /**
117     * Can this tab load asynchronously?
118     *
119     * @return bool
120     */
121    public function canLoadAjax(): bool
122    {
123        return true;
124    }
125
126    /**
127     * CSS class for the URL.
128     *
129     * @return string
130     */
131    public function chartMenuClass(): string
132    {
133        return 'menu-chart-tree';
134    }
135
136    /**
137     * Return a menu item for this chart - for use in individual boxes.
138     *
139     * @param Individual $individual
140     *
141     * @return Menu|null
142     */
143    public function chartBoxMenu(Individual $individual): ?Menu
144    {
145        return $this->chartMenu($individual);
146    }
147
148    /**
149     * The title for a specific instance of this chart.
150     *
151     * @param Individual $individual
152     *
153     * @return string
154     */
155    public function chartTitle(Individual $individual): string
156    {
157        /* I18N: %s is an individual’s name */
158        return I18N::translate('Interactive tree of %s', $individual->fullName());
159    }
160
161    /**
162     * The URL for this chart.
163     *
164     * @param Individual $individual
165     * @param string[]   $parameters
166     *
167     * @return string
168     */
169    public function chartUrl(Individual $individual, array $parameters = []): string
170    {
171        return route('module', [
172                'module' => $this->name(),
173                'action' => 'Chart',
174                'xref'   => $individual->xref(),
175                'ged'    => $individual->tree()->name(),
176            ] + $parameters);
177    }
178
179    /**
180     * @param ServerRequestInterface $request
181     *
182     * @return ResponseInterface
183     */
184    public function getChartAction(ServerRequestInterface $request): ResponseInterface
185    {
186        $tree = $request->getAttribute('tree');
187        $user = $request->getAttribute('user');
188        $xref = $request->getQueryParams()['xref'];
189
190        $individual = Individual::getInstance($xref, $tree);
191
192        Auth::checkIndividualAccess($individual);
193        Auth::checkComponentAccess($this, 'chart', $tree, $user);
194
195        $tv = new TreeView('tv');
196
197        [$html, $js] = $tv->drawViewport($individual, 4);
198
199        return $this->viewResponse('modules/interactive-tree/page', [
200            'html'       => $html,
201            'individual' => $individual,
202            'js'         => $js,
203            'title'      => $this->chartTitle($individual),
204            'tree'       => $tree,
205        ]);
206    }
207
208    /**
209     * @param ServerRequestInterface $request
210     *
211     * @return ResponseInterface
212     */
213    public function getDetailsAction(ServerRequestInterface $request): ResponseInterface
214    {
215        $tree       = $request->getAttribute('tree');
216        $pid        = $request->getQueryParams()['pid'];
217        $individual = Individual::getInstance($pid, $tree);
218
219        if ($individual === null) {
220            throw new IndividualNotFoundException();
221        }
222
223        if (!$individual->canShow()) {
224            throw new IndividualAccessDeniedException();
225        }
226
227        $instance = $request->getQueryParams()['instance'];
228        $treeview = new TreeView($instance);
229
230        return response($treeview->getDetails($individual));
231    }
232
233    /**
234     * @param ServerRequestInterface $request
235     *
236     * @return ResponseInterface
237     */
238    public function getPersonsAction(ServerRequestInterface $request): ResponseInterface
239    {
240        $tree     = $request->getAttribute('tree');
241        $q        = $request->getQueryParams()['q'];
242        $instance = $request->getQueryParams()['instance'];
243        $treeview = new TreeView($instance);
244
245        return response($treeview->getPersons($tree, $q));
246    }
247}
248