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