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