xref: /webtrees/app/Module/FrequentlyAskedQuestionsModule.php (revision 84586c024c68dbfa3b15df636ed2b74037428442)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2018 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\Database;
21use Fisharebest\Webtrees\I18N;
22use Fisharebest\Webtrees\Menu;
23use Fisharebest\Webtrees\Tree;
24use stdClass;
25use Symfony\Component\HttpFoundation\RedirectResponse;
26use Symfony\Component\HttpFoundation\Request;
27use Symfony\Component\HttpFoundation\Response;
28
29/**
30 * Class FrequentlyAskedQuestionsModule
31 */
32class FrequentlyAskedQuestionsModule extends AbstractModule implements ModuleMenuInterface, ModuleConfigInterface
33{
34    /** {@inheritdoc} */
35    public function getTitle(): string
36    {
37        /* I18N: Name of a module. Abbreviation for “Frequently Asked Questions” */
38        return I18N::translate('FAQ');
39    }
40
41    /** {@inheritdoc} */
42    public function getDescription(): string
43    {
44        /* I18N: Description of the “FAQ” module */
45        return I18N::translate('A list of frequently asked questions and answers.');
46    }
47
48    /**
49     * The URL to a page where the user can modify the configuration of this module.
50     *
51     * @return string
52     */
53    public function getConfigLink(): string
54    {
55        return route('module', [
56            'module' => $this->getName(),
57            'action' => 'Admin',
58        ]);
59    }
60
61    /**
62     * The user can re-order menus. Until they do, they are shown in this order.
63     *
64     * @return int
65     */
66    public function defaultMenuOrder(): int
67    {
68        return 40;
69    }
70
71    /**
72     * A menu, to be added to the main application menu.
73     *
74     * @param Tree $tree
75     *
76     * @return Menu|null
77     */
78    public function getMenu(Tree $tree)
79    {
80        $faqs = Database::prepare(
81            "SELECT block_id FROM `##block`" .
82            " JOIN `##block_setting` USING (block_id)" .
83            " WHERE module_name = :module_name AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" .
84            " AND setting_name='languages' AND (setting_value LIKE CONCAT('%', :locale, '%') OR setting_value='')"
85        )->execute([
86            'module_name' => $this->getName(),
87            'tree_id_1'   => $tree->getTreeId(),
88            'tree_id_2'   => $tree->getTreeId(),
89            'locale'      => WT_LOCALE,
90        ])->fetchAll();
91
92        if (!empty($faqs)) {
93            return new Menu($this->getTitle(), route('module', [
94                'module' => 'faq',
95                'action' => 'Show',
96                'ged'    => $tree->getName(),
97            ]), 'menu-help');
98        }
99
100        return null;
101    }
102
103    /**
104     * @param Tree $tree
105     *
106     * @return Response
107     */
108    public function getAdminAction(Tree $tree): Response
109    {
110        $this->layout = 'layouts/administration';
111
112        $faqs = Database::prepare(
113            "SELECT block_id, block_order, gedcom_id, bs1.setting_value AS header, bs2.setting_value AS faqbody" .
114            " FROM `##block` b" .
115            " JOIN `##block_setting` bs1 USING (block_id)" .
116            " JOIN `##block_setting` bs2 USING (block_id)" .
117            " WHERE module_name = :module_name" .
118            " AND bs1.setting_name = 'header'" .
119            " AND bs2.setting_name = 'faqbody'" .
120            " AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" .
121            " ORDER BY block_order"
122        )->execute([
123            'module_name' => $this->getName(),
124            'tree_id_1'   => $tree->getTreeId(),
125            'tree_id_2'   => $tree->getTreeId(),
126        ])->fetchAll();
127
128        $min_block_order = Database::prepare(
129            "SELECT MIN(block_order) FROM `##block` WHERE module_name = 'faq' AND (gedcom_id = :tree_id OR gedcom_id IS NULL)"
130        )->execute([
131            'tree_id' => $tree->getTreeId(),
132        ])->fetchOne();
133
134        $max_block_order = Database::prepare(
135            "SELECT MAX(block_order) FROM `##block` WHERE module_name = 'faq' AND (gedcom_id = :tree_id OR gedcom_id IS NULL)"
136        )->execute([
137            'tree_id' => $tree->getTreeId(),
138        ])->fetchOne();
139
140        $title = I18N::translate('Frequently asked questions') . ' — ' . $tree->getTitle();
141
142        return $this->viewResponse('modules/faq/config', [
143            'faqs'            => $faqs,
144            'max_block_order' => $max_block_order,
145            'min_block_order' => $min_block_order,
146            'title'           => $title,
147            'tree'            => $tree,
148            'tree_names'      => Tree::getNameList(),
149        ]);
150    }
151
152    /**
153     * @param Request $request
154     * @param Tree    $tree
155     *
156     * @return RedirectResponse
157     */
158    public function postAdminDeleteAction(Request $request, Tree $tree): RedirectResponse
159    {
160        $block_id = (int) $request->get('block_id');
161
162        Database::prepare(
163            "DELETE FROM `##block_setting` WHERE block_id = :block_id"
164        )->execute(['block_id' => $block_id]);
165
166        Database::prepare(
167            "DELETE FROM `##block` WHERE block_id = :block_id"
168        )->execute(['block_id' => $block_id]);
169
170
171        $url = route('module', [
172            'module' => 'faq',
173            'action' => 'Admin',
174            'ged'    => $tree->getName(),
175        ]);
176
177        return new RedirectResponse($url);
178    }
179
180    /**
181     * @param Request $request
182     * @param Tree    $tree
183     *
184     * @return RedirectResponse
185     */
186    public function postAdminMoveDownAction(Request $request, Tree $tree): RedirectResponse
187    {
188        $block_id = (int) $request->get('block_id');
189
190        $block_order = Database::prepare(
191            "SELECT block_order FROM `##block` WHERE block_id = :block_id"
192        )->execute([
193            'block_id' => $block_id,
194        ])->fetchOne();
195
196        $swap_block = Database::prepare(
197            "SELECT block_order, block_id" .
198            " FROM `##block`" .
199            " WHERE block_order=(" .
200            "  SELECT MIN(block_order) FROM `##block` WHERE block_order > :block_order AND module_name = :module_name_1" .
201            " ) AND module_name = :module_name_2" .
202            " LIMIT 1"
203        )->execute([
204            'block_order'   => $block_order,
205            'module_name_1' => $this->getName(),
206            'module_name_2' => $this->getName(),
207        ])->fetchOneRow();
208
209        if ($swap_block !== null) {
210            Database::prepare(
211                "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id"
212            )->execute([
213                'block_order' => $swap_block->block_order,
214                'block_id'    => $block_id,
215            ]);
216            Database::prepare(
217                "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id"
218            )->execute([
219                'block_order' => $block_order,
220                'block_id'    => $swap_block->block_id,
221            ]);
222        }
223
224        $url = route('module', [
225            'module' => 'faq',
226            'action' => 'Admin',
227            'ged'    => $tree->getName(),
228        ]);
229
230        return new RedirectResponse($url);
231    }
232
233    /**
234     * @param Request $request
235     * @param Tree    $tree
236     *
237     * @return RedirectResponse
238     */
239    public function postAdminMoveUpAction(Request $request, Tree $tree): RedirectResponse
240    {
241        $block_id = (int) $request->get('block_id');
242
243        $block_order = Database::prepare(
244            "SELECT block_order FROM `##block` WHERE block_id = :block_id"
245        )->execute([
246            'block_id' => $block_id,
247        ])->fetchOne();
248
249        $swap_block = Database::prepare(
250            "SELECT block_order, block_id" .
251            " FROM `##block`" .
252            " WHERE block_order = (" .
253            "  SELECT MAX(block_order) FROM `##block` WHERE block_order < :block_order AND module_name = :module_name_1" .
254            " ) AND module_name = :module_name_2" .
255            " LIMIT 1"
256        )->execute([
257            'block_order'   => $block_order,
258            'module_name_1' => $this->getName(),
259            'module_name_2' => $this->getName(),
260        ])->fetchOneRow();
261
262        if ($swap_block !== null) {
263            Database::prepare(
264                "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id"
265            )->execute([
266                'block_order' => $swap_block->block_order,
267                'block_id'    => $block_id,
268            ]);
269            Database::prepare(
270                "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id"
271            )->execute([
272                'block_order' => $block_order,
273                'block_id'    => $swap_block->block_id,
274            ]);
275        }
276
277        $url = route('module', [
278            'module' => 'faq',
279            'action' => 'Admin',
280            'ged'    => $tree->getName(),
281        ]);
282
283        return new RedirectResponse($url);
284    }
285
286    /**
287     * @param Request $request
288     * @param Tree    $tree
289     *
290     * @return Response
291     */
292    public function getAdminEditAction(Request $request, Tree $tree): Response
293    {
294        $this->layout = 'layouts/administration';
295
296        $block_id = (int) $request->get('block_id');
297
298        if ($block_id === 0) {
299            // Creating a new faq
300            $header      = '';
301            $faqbody     = '';
302            $block_order = Database::prepare(
303                "SELECT IFNULL(MAX(block_order)+1, 0) FROM `##block` WHERE module_name = :module_name"
304            )->execute([
305                'module_name' => $this->getName(),
306            ])->fetchOne();
307            $languages = [];
308
309            $title = I18N::translate('Add an FAQ');
310        } else {
311            // Editing an existing faq
312            $header      = $this->getBlockSetting($block_id, 'header');
313            $faqbody     = $this->getBlockSetting($block_id, 'faqbody');
314            $block_order = Database::prepare(
315                "SELECT block_order FROM `##block` WHERE block_id = :block_id"
316            )->execute(['block_id' => $block_id])->fetchOne();
317            $languages   = explode(',', $this->getBlockSetting($block_id, 'languages'));
318
319            $title = I18N::translate('Edit the FAQ');
320        }
321
322        // @TODO enable CKEDITOR
323
324        return $this->viewResponse('modules/faq/edit', [
325            'block_id'    => $block_id,
326            'block_order' => $block_order,
327            'header'      => $header,
328            'faqbody'     => $faqbody,
329            'languages'   => $languages,
330            'title'       => $title,
331            'tree'        => $tree,
332            'tree_names'  => Tree::getNameList(),
333        ]);
334    }
335
336    /**
337     * @param Request $request
338     * @param Tree    $tree
339     *
340     * @return RedirectResponse
341     */
342    public function postAdminEditAction(Request $request, Tree $tree): RedirectResponse
343    {
344        $block_id  = (int) $request->get('block_id');
345        $faqbody   = $request->get('faqbody', '');
346        $header    = $request->get('header', '');
347        $languages = $request->get('languages', []);
348
349        if ($block_id !== 0) {
350            Database::prepare(
351                "UPDATE `##block` SET gedcom_id = NULLIF(:tree_id, '0'), block_order = :block_order WHERE block_id = :block_id"
352            )->execute([
353                'tree_id'     => (int) $request->get('gedcom_id'),
354                'block_order' => (int) $request->get('block_order'),
355                'block_id'    => $block_id,
356            ]);
357        } else {
358            Database::prepare(
359                "INSERT INTO `##block` (gedcom_id, module_name, block_order) VALUES (NULLIF(:tree_id, '0'), :module_name, :block_order)"
360            )->execute([
361                'tree_id'     => (int) $request->get('gedcom_id'),
362                'module_name' => $this->getName(),
363                'block_order' => (int) $request->get('block_order'),
364            ]);
365
366            $block_id = Database::lastInsertId();
367        }
368
369        $this->setBlockSetting($block_id, 'faqbody', $faqbody);
370        $this->setBlockSetting($block_id, 'header', $header);
371        $this->setBlockSetting($block_id, 'languages', implode(',', $languages));
372
373        $url = route('module', [
374            'module' => 'faq',
375            'action' => 'Admin',
376            'ged'    => $tree->getName(),
377        ]);
378
379        return new RedirectResponse($url);
380    }
381
382    /**
383     * @param Tree $tree
384     *
385     * @return Response
386     */
387    public function getShowAction(Tree $tree): Response
388    {
389        $faqs = Database::prepare(
390            "SELECT block_id, bs1.setting_value AS header, bs2.setting_value AS body, bs3.setting_value AS languages" .
391            " FROM `##block` b" .
392            " JOIN `##block_setting` bs1 USING (block_id)" .
393            " JOIN `##block_setting` bs2 USING (block_id)" .
394            " JOIN `##block_setting` bs3 USING (block_id)" .
395            " WHERE module_name = :module_name" .
396            " AND bs1.setting_name = 'header'" .
397            " AND bs2.setting_name = 'faqbody'" .
398            " AND bs3.setting_name = 'languages'" .
399            " AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" .
400            " ORDER BY block_order"
401        )->execute([
402            'module_name' => $this->getName(),
403            'tree_id_1'   => $tree->getTreeId(),
404            'tree_id_2'   => $tree->getTreeId(),
405        ])->fetchAll();
406
407        // Filter foreign languages.
408        $faqs = array_filter($faqs, function (stdClass $faq): bool {
409            return $faq->languages === '' || in_array(WT_LOCALE, explode(',', $faq->languages));
410        });
411
412        return $this->viewResponse('modules/faq/show', [
413            'faqs'  => $faqs,
414            'title' => I18N::translate('Frequently asked questions'),
415            'tree'  => $tree,
416        ]);
417    }
418}
419