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