xref: /webtrees/app/Module/FrequentlyAskedQuestionsModule.php (revision 59f2f229057fe08ec1b09bd435699190641eed6a)
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        /* I18N: Name of a module. Abbreviation for “Frequently Asked Questions” */
37        return I18N::translate('FAQ');
38    }
39
40    /** {@inheritdoc} */
41    public function getDescription()
42    {
43        /* I18N: Description of the “FAQ” module */
44        return 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 Tree $tree
104     *
105     * @return Response
106     */
107    public function getAdminAction(Tree $tree): Response
108    {
109        $this->layout = 'layouts/administration';
110
111        $faqs = Database::prepare(
112            "SELECT block_id, block_order, gedcom_id, bs1.setting_value AS header, bs2.setting_value AS faqbody" .
113            " FROM `##block` b" .
114            " JOIN `##block_setting` bs1 USING (block_id)" .
115            " JOIN `##block_setting` bs2 USING (block_id)" .
116            " WHERE module_name = :module_name" .
117            " AND bs1.setting_name = 'header'" .
118            " AND bs2.setting_name = 'faqbody'" .
119            " AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" .
120            " ORDER BY block_order"
121        )->execute([
122            'module_name' => $this->getName(),
123            'tree_id_1'   => $tree->getTreeId(),
124            'tree_id_2'   => $tree->getTreeId(),
125        ])->fetchAll();
126
127        $min_block_order = Database::prepare(
128            "SELECT MIN(block_order) FROM `##block` WHERE module_name = 'faq' AND (gedcom_id = :tree_id OR gedcom_id IS NULL)"
129        )->execute([
130            'tree_id' => $tree->getTreeId(),
131        ])->fetchOne();
132
133        $max_block_order = Database::prepare(
134            "SELECT MAX(block_order) FROM `##block` WHERE module_name = 'faq' AND (gedcom_id = :tree_id OR gedcom_id IS NULL)"
135        )->execute([
136            'tree_id' => $tree->getTreeId(),
137        ])->fetchOne();
138
139        $title = I18N::translate('Frequently asked questions') . ' — ' . $tree->getTitle();
140
141        return $this->viewResponse('modules/faq/config', [
142            'faqs'            => $faqs,
143            'max_block_order' => $max_block_order,
144            'min_block_order' => $min_block_order,
145            'title'           => $title,
146            'tree'            => $tree,
147            'tree_names'      => Tree::getNameList(),
148        ]);
149    }
150
151    /**
152     * @param Request $request
153     * @param Tree    $tree
154     *
155     * @return RedirectResponse
156     */
157    public function postAdminDeleteAction(Request $request, Tree $tree): RedirectResponse
158    {
159        $block_id = (int)$request->get('block_id');
160
161        Database::prepare(
162            "DELETE FROM `##block_setting` WHERE block_id = :block_id"
163        )->execute(['block_id' => $block_id]);
164
165        Database::prepare(
166            "DELETE FROM `##block` WHERE block_id = :block_id"
167        )->execute(['block_id' => $block_id]);
168
169
170        $url = route('module', [
171            'module' => 'faq',
172            'action' => 'Admin',
173            'ged'    => $tree->getName(),
174        ]);
175
176        return new RedirectResponse($url);
177    }
178
179    /**
180     * @param Request $request
181     * @param Tree    $tree
182     *
183     * @return RedirectResponse
184     */
185    public function postAdminMoveDownAction(Request $request, Tree $tree): RedirectResponse
186    {
187        $block_id = (int)$request->get('block_id');
188
189        $block_order = Database::prepare(
190            "SELECT block_order FROM `##block` WHERE block_id = :block_id"
191        )->execute([
192            'block_id' => $block_id,
193        ])->fetchOne();
194
195        $swap_block = Database::prepare(
196            "SELECT block_order, block_id" .
197            " FROM `##block`" .
198            " WHERE block_order=(" .
199            "  SELECT MIN(block_order) FROM `##block` WHERE block_order > :block_order AND module_name = :module_name_1" .
200            " ) AND module_name = :module_name_2" .
201            " LIMIT 1"
202        )->execute([
203            'block_order'   => $block_order,
204            'module_name_1' => $this->getName(),
205            'module_name_2' => $this->getName(),
206        ])->fetchOneRow();
207
208        if ($swap_block !== null) {
209            Database::prepare(
210                "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id"
211            )->execute([
212                'block_order' => $swap_block->block_order,
213                'block_id'    => $block_id,
214            ]);
215            Database::prepare(
216                "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id"
217            )->execute([
218                'block_order' => $block_order,
219                'block_id'    => $swap_block->block_id,
220            ]);
221        }
222
223        $url = route('module', [
224            'module' => 'faq',
225            'action' => 'Admin',
226            'ged'    => $tree->getName(),
227        ]);
228
229        return new RedirectResponse($url);
230    }
231
232    /**
233     * @param Request $request
234     * @param Tree    $tree
235     *
236     * @return RedirectResponse
237     */
238    public function postAdminMoveUpAction(Request $request, Tree $tree): RedirectResponse
239    {
240        $block_id = (int)$request->get('block_id');
241
242        $block_order = Database::prepare(
243            "SELECT block_order FROM `##block` WHERE block_id = :block_id"
244        )->execute([
245            'block_id' => $block_id,
246        ])->fetchOne();
247
248        $swap_block = Database::prepare(
249            "SELECT block_order, block_id" .
250            " FROM `##block`" .
251            " WHERE block_order = (" .
252            "  SELECT MAX(block_order) FROM `##block` WHERE block_order < :block_order AND module_name = :module_name_1" .
253            " ) AND module_name = :module_name_2" .
254            " LIMIT 1"
255        )->execute([
256            'block_order'   => $block_order,
257            'module_name_1' => $this->getName(),
258            'module_name_2' => $this->getName(),
259        ])->fetchOneRow();
260
261        if ($swap_block !== null) {
262            Database::prepare(
263                "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id"
264            )->execute([
265                'block_order' => $swap_block->block_order,
266                'block_id'    => $block_id,
267            ]);
268            Database::prepare(
269                "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id"
270            )->execute([
271                'block_order' => $block_order,
272                'block_id'    => $swap_block->block_id,
273            ]);
274        }
275
276        $url = route('module', [
277            'module' => 'faq',
278            'action' => 'Admin',
279            'ged'    => $tree->getName(),
280        ]);
281
282        return new RedirectResponse($url);
283    }
284
285    /**
286     * @param Request $request
287     * @param Tree    $tree
288     *
289     * @return Response
290     */
291    public function getAdminEditAction(Request $request, Tree $tree): Response
292    {
293        $this->layout = 'layouts/administration';
294
295        $block_id = (int)$request->get('block_id');
296
297        if ($block_id === 0) {
298            // Creating a new faq
299            $header      = '';
300            $faqbody     = '';
301            $block_order = Database::prepare(
302                "SELECT IFNULL(MAX(block_order)+1, 0) FROM `##block` WHERE module_name = :module_name"
303            )->execute([
304                'module_name' => $this->getName(),
305            ])->fetchOne();
306            $languages   = [];
307
308            $title = I18N::translate('Add an FAQ');
309        } else {
310            // Editing an existing faq
311            $header      = $this->getBlockSetting($block_id, 'header');
312            $faqbody     = $this->getBlockSetting($block_id, 'faqbody');
313            $block_order = Database::prepare(
314                "SELECT block_order FROM `##block` WHERE block_id = :block_id"
315            )->execute(['block_id' => $block_id])->fetchOne();
316            $languages   = explode(',', $this->getBlockSetting($block_id, 'languages'));
317
318            $title = I18N::translate('Edit the FAQ');
319        }
320
321        // @TODO enable CKEDITOR
322
323        return $this->viewResponse('modules/faq/edit', [
324            'block_id'    => $block_id,
325            'block_order' => $block_order,
326            'header'      => $header,
327            'faqbody'     => $faqbody,
328            'languages'   => $languages,
329            'title'       => $title,
330            'tree'        => $tree,
331            'tree_names'  => Tree::getNameList(),
332        ]);
333    }
334
335    /**
336     * @param Request $request
337     * @param Tree    $tree
338     *
339     * @return RedirectResponse
340     */
341    public function postAdminEditAction(Request $request, Tree $tree): RedirectResponse
342    {
343        $block_id  = (int)$request->get('block_id');
344        $faqbody   = $request->get('faqbody', '');
345        $header    = $request->get('header', '');
346        $languages = $request->get('languages', []);
347
348        if ($block_id !== 0) {
349            Database::prepare(
350                "UPDATE `##block` SET gedcom_id = NULLIF(:tree_id, '0'), block_order = :block_order WHERE block_id = :block_id"
351            )->execute([
352                'tree_id'     => Filter::postInteger('gedcom_id'),
353                'block_order' => Filter::postInteger('block_order'),
354                'block_id'    => $block_id,
355            ]);
356        } else {
357            Database::prepare(
358                "INSERT INTO `##block` (gedcom_id, module_name, block_order) VALUES (NULLIF(:tree_id, '0'), :module_name, :block_order)"
359            )->execute([
360                'tree_id'     => Filter::postInteger('gedcom_id'),
361                'module_name' => $this->getName(),
362                'block_order' => Filter::postInteger('block_order'),
363            ]);
364
365            $block_id = Database::getInstance()->lastInsertId();
366        }
367
368        $this->setBlockSetting($block_id, 'faqbody', $faqbody);
369        $this->setBlockSetting($block_id, 'header', $header);
370        $this->setBlockSetting($block_id, 'languages', implode(',', $languages));
371
372        $url = route('module', [
373            'module' => 'faq',
374            'action' => 'Admin',
375            'ged'    => $tree->getName(),
376        ]);
377
378        return new RedirectResponse($url);
379    }
380
381    /**
382     * @param Request $request
383     * @param Tree    $tree
384     *
385     * @return Response
386     */
387    public function getShowAction(Request $request, 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