xref: /webtrees/app/Module/DescendancyChartModule.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\I18N;
22use Fisharebest\Webtrees\Individual;
23use Fisharebest\Webtrees\Menu;
24use Fisharebest\Webtrees\Services\ChartService;
25use Fisharebest\Webtrees\Tree;
26use Illuminate\Support\Collection;
27use Psr\Http\Message\ResponseInterface;
28use Psr\Http\Message\ServerRequestInterface;
29
30/**
31 * Class DescendancyChartModule
32 */
33class DescendancyChartModule extends AbstractModule implements ModuleChartInterface
34{
35    use ModuleChartTrait;
36
37    // Chart styles
38    public const CHART_STYLE_TREE        = 'tree';
39    public const CHART_STYLE_INDIVIDUALS = 'individuals';
40    public const CHART_STYLE_FAMILIES    = 'families';
41
42    // Defaults
43    public const DEFAULT_STYLE               = self::CHART_STYLE_TREE;
44    public const DEFAULT_GENERATIONS         = '3';
45
46    // Limits
47    public const MINIMUM_GENERATIONS = 2;
48    public const MAXIMUM_GENERATIONS = 10;
49
50    /** @var int[] */
51    protected $dabo_num = [];
52
53    /** @var string[] */
54    protected $dabo_sex = [];
55
56    /** @var ChartService */
57    private $chart_service;
58
59    /**
60     * DescendancyChartModule constructor.
61     *
62     * @param ChartService $chart_service
63     */
64    public function __construct(ChartService $chart_service) {
65        $this->chart_service = $chart_service;
66    }
67
68    /**
69     * How should this module be identified in the control panel, etc.?
70     *
71     * @return string
72     */
73    public function title(): string
74    {
75        /* I18N: Name of a module/chart */
76        return I18N::translate('Descendants');
77    }
78
79    /**
80     * A sentence describing what this module does.
81     *
82     * @return string
83     */
84    public function description(): string
85    {
86        /* I18N: Description of the “DescendancyChart” module */
87        return I18N::translate('A chart of an individual’s descendants.');
88    }
89
90    /**
91     * CSS class for the URL.
92     *
93     * @return string
94     */
95    public function chartMenuClass(): string
96    {
97        return 'menu-chart-descendants';
98    }
99
100    /**
101     * Return a menu item for this chart - for use in individual boxes.
102     *
103     * @param Individual $individual
104     *
105     * @return Menu|null
106     */
107    public function chartBoxMenu(Individual $individual): ?Menu
108    {
109        return $this->chartMenu($individual);
110    }
111
112    /**
113     * The title for a specific instance of this chart.
114     *
115     * @param Individual $individual
116     *
117     * @return string
118     */
119    public function chartTitle(Individual $individual): string
120    {
121        /* I18N: %s is an individual’s name */
122        return I18N::translate('Descendants of %s', $individual->fullName());
123    }
124
125    /**
126     * A form to request the chart parameters.
127     *
128     * @param ServerRequestInterface $request
129     *
130     * @return ResponseInterface
131     */
132    public function getChartAction(ServerRequestInterface $request): ResponseInterface
133    {
134        $tree       = $request->getAttribute('tree');
135        $user       = $request->getAttribute('user');
136        $ajax       = $request->getQueryParams()['ajax'] ?? '';
137        $xref       = $request->getQueryParams()['xref'] ?? '';
138        $individual = Individual::getInstance($xref, $tree);
139
140        Auth::checkIndividualAccess($individual);
141        Auth::checkComponentAccess($this, 'chart', $tree, $user);
142
143        $chart_style = $request->getQueryParams()['chart_style'] ?? self::DEFAULT_STYLE;
144        $generations = (int) ($request->getQueryParams()['generations'] ?? self::DEFAULT_GENERATIONS);
145
146        $generations = min($generations, self::MAXIMUM_GENERATIONS);
147        $generations = max($generations, self::MINIMUM_GENERATIONS);
148
149        if ($ajax === '1') {
150            return $this->chart($request);
151        }
152
153        $ajax_url = $this->chartUrl($individual, [
154            'chart_style' => $chart_style,
155            'generations' => $generations,
156            'ajax'        => true,
157        ]);
158
159        return $this->viewResponse('modules/descendancy_chart/page', [
160            'ajax_url'            => $ajax_url,
161            'chart_style'         => $chart_style,
162            'chart_styles'        => $this->chartStyles(),
163            'default_generations' => self::DEFAULT_GENERATIONS,
164            'generations'         => $generations,
165            'individual'          => $individual,
166            'maximum_generations' => self::MAXIMUM_GENERATIONS,
167            'minimum_generations' => self::MINIMUM_GENERATIONS,
168            'module_name'         => $this->name(),
169            'title'               => $this->chartTitle($individual),
170        ]);
171    }
172
173    /**
174     * @param ServerRequestInterface $request
175     *
176     * @return ResponseInterface
177     */
178    public function chart(ServerRequestInterface $request): ResponseInterface
179    {
180        $this->layout = 'layouts/ajax';
181
182        $tree       = $request->getAttribute('tree');
183        $xref       = $request->getQueryParams()['xref'];
184        $individual = Individual::getInstance($xref, $tree);
185
186        Auth::checkIndividualAccess($individual);
187
188        $chart_style = $request->getQueryParams()['chart_style'];
189        $generations = (int) $request->getQueryParams()['generations'];
190
191        $generations = min($generations, self::MAXIMUM_GENERATIONS);
192        $generations = max($generations, self::MINIMUM_GENERATIONS);
193
194        switch ($chart_style) {
195            case self::CHART_STYLE_TREE:
196            default:
197                return response(view('modules/descendancy_chart/tree', ['individual' => $individual, 'generations' => $generations, 'daboville' => '1']));
198
199            case self::CHART_STYLE_INDIVIDUALS:
200                $individuals = $this->chart_service->descendants($individual, $generations - 1);
201
202                return $this->descendantsIndividuals($tree, $individuals);
203
204            case self::CHART_STYLE_FAMILIES:
205                $families = $this->chart_service->descendantFamilies($individual, $generations - 1);
206
207                return $this->descendantsFamilies($tree, $families);
208        }
209    }
210
211    /**
212     * Show a tabular list of individual descendants.
213     *
214     * @param Tree       $tree
215     * @param Collection $individuals
216     *
217     * @return ResponseInterface
218     */
219    private function descendantsIndividuals(Tree $tree, Collection $individuals): ResponseInterface
220    {
221        $this->layout = 'layouts/ajax';
222
223        return $this->viewResponse('lists/individuals-table', [
224            'individuals' => $individuals,
225            'sosa'        => false,
226            'tree'        => $tree,
227        ]);
228    }
229
230    /**
231     * Show a tabular list of individual descendants.
232     *
233     * @param Tree       $tree
234     * @param Collection $families
235     *
236     * @return ResponseInterface
237     */
238    private function descendantsFamilies(Tree $tree, Collection $families): ResponseInterface
239    {
240        $this->layout = 'layouts/ajax';
241
242        return $this->viewResponse('lists/families-table', [
243            'families' => $families,
244            'tree'     => $tree,
245        ]);
246    }
247
248    /**
249     * This chart can display its output in a number of styles
250     *
251     * @return string[]
252     */
253    private function chartStyles(): array
254    {
255        return [
256            self::CHART_STYLE_TREE        => I18N::translate('Tree'),
257            self::CHART_STYLE_INDIVIDUALS => I18N::translate('Individuals'),
258            self::CHART_STYLE_FAMILIES    => I18N::translate('Families'),
259        ];
260    }
261}
262