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