xref: /webtrees/app/Module/FrequentlyAskedQuestionsModule.php (revision 0b5fd0a636fa959f5279ee28ebd2f27e921c091e)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2019 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\I18N;
21use Fisharebest\Webtrees\Menu;
22use Fisharebest\Webtrees\Tree;
23use Illuminate\Database\Capsule\Manager as DB;
24use Illuminate\Database\Query\Builder;
25use Illuminate\Support\Collection;
26use stdClass;
27use Symfony\Component\HttpFoundation\RedirectResponse;
28use Symfony\Component\HttpFoundation\Request;
29use Symfony\Component\HttpFoundation\Response;
30
31/**
32 * Class FrequentlyAskedQuestionsModule
33 */
34class FrequentlyAskedQuestionsModule extends AbstractModule implements ModuleConfigInterface, ModuleMenuInterface
35{
36    use ModuleConfigTrait;
37    use ModuleMenuTrait;
38
39    /**
40     * How should this module be identified in the control panel, etc.?
41     *
42     * @return string
43     */
44    public function title(): string
45    {
46        /* I18N: Name of a module. Abbreviation for “Frequently Asked Questions” */
47        return I18N::translate('FAQ');
48    }
49
50    /**
51     * A sentence describing what this module does.
52     *
53     * @return string
54     */
55    public function description(): string
56    {
57        /* I18N: Description of the “FAQ” module */
58        return I18N::translate('A list of frequently asked questions and answers.');
59    }
60
61    /**
62     * The default position for this menu.  It can be changed in the control panel.
63     *
64     * @return int
65     */
66    public function defaultMenuOrder(): int
67    {
68        return 8;
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): ?Menu
79    {
80        if ($this->faqsExist($tree, WT_LOCALE)) {
81            return new Menu($this->title(), route('module', [
82                'module' => $this->name(),
83                'action' => 'Show',
84                'ged'    => $tree->name(),
85            ]), 'menu-help');
86        }
87
88        return null;
89    }
90
91    /**
92     * @param Tree $tree
93     *
94     * @return Response
95     */
96    public function getAdminAction(Tree $tree): Response
97    {
98        $this->layout = 'layouts/administration';
99
100        $faqs = $this->faqsForTree($tree);
101
102        $min_block_order = DB::table('block')
103            ->where('module_name', '=', $this->name())
104            ->where(static function (Builder $query) use ($tree): void {
105                $query
106                    ->whereNull('gedcom_id')
107                    ->orWhere('gedcom_id', '=', $tree->id());
108            })
109            ->min('block_order');
110
111        $max_block_order = DB::table('block')
112            ->where('module_name', '=', $this->name())
113            ->where(static function (Builder $query) use ($tree): void {
114                $query
115                    ->whereNull('gedcom_id')
116                    ->orWhere('gedcom_id', '=', $tree->id());
117            })
118            ->max('block_order');
119
120        $title = I18N::translate('Frequently asked questions') . ' — ' . $tree->title();
121
122        return $this->viewResponse('modules/faq/config', [
123            'faqs'            => $faqs,
124            'max_block_order' => $max_block_order,
125            'min_block_order' => $min_block_order,
126            'title'           => $title,
127            'tree'            => $tree,
128            'tree_names'      => Tree::getNameList(),
129        ]);
130    }
131
132    /**
133     * @param Request $request
134     * @param Tree    $tree
135     *
136     * @return RedirectResponse
137     */
138    public function postAdminDeleteAction(Request $request, Tree $tree): RedirectResponse
139    {
140        $block_id = (int) $request->get('block_id');
141
142        DB::table('block_setting')->where('block_id', '=', $block_id)->delete();
143
144        DB::table('block')->where('block_id', '=', $block_id)->delete();
145
146        $url = route('module', [
147            'module' => $this->name(),
148            'action' => 'Admin',
149            'ged'    => $tree->name(),
150        ]);
151
152        return new RedirectResponse($url);
153    }
154
155    /**
156     * @param Request $request
157     * @param Tree    $tree
158     *
159     * @return RedirectResponse
160     */
161    public function postAdminMoveDownAction(Request $request, Tree $tree): RedirectResponse
162    {
163        $block_id = (int) $request->get('block_id');
164
165        $block_order = DB::table('block')
166            ->where('block_id', '=', $block_id)
167            ->value('block_order');
168
169        $swap_block = DB::table('block')
170            ->where('module_name', '=', $this->name())
171            ->where('block_order', '=', static function (Builder $query) use ($block_order): void {
172                $query
173                    ->from('block')
174                    ->where('module_name', '=', $this->name())
175                    ->where('block_order', '>', $block_order)
176                    ->select(DB::raw('MIN(block_order)'));
177            })
178            ->select(['block_order', 'block_id'])
179            ->first();
180
181        if ($swap_block !== null) {
182            DB::table('block')
183                ->where('block_id', '=', $block_id)
184                ->update([
185                    'block_order' => $swap_block->block_order,
186                ]);
187
188            DB::table('block')
189                ->where('block_id', '=', $swap_block->block_id)
190                ->update([
191                    'block_order' => $block_order,
192                ]);
193        }
194
195        $url = route('module', [
196            'module' => $this->name(),
197            'action' => 'Admin',
198            'ged'    => $tree->name(),
199        ]);
200
201        return new RedirectResponse($url);
202    }
203
204    /**
205     * @param Request $request
206     * @param Tree    $tree
207     *
208     * @return RedirectResponse
209     */
210    public function postAdminMoveUpAction(Request $request, Tree $tree): RedirectResponse
211    {
212        $block_id = (int) $request->get('block_id');
213
214        $block_order = DB::table('block')
215            ->where('block_id', '=', $block_id)
216            ->value('block_order');
217
218        $swap_block = DB::table('block')
219            ->where('module_name', '=', $this->name())
220            ->where('block_order', '=', static function (Builder $query) use ($block_order): void {
221                $query
222                    ->from('block')
223                    ->where('module_name', '=', $this->name())
224                    ->where('block_order', '<', $block_order)
225                    ->select(DB::raw('MAX(block_order)'));
226            })
227            ->select(['block_order', 'block_id'])
228            ->first();
229
230        if ($swap_block !== null) {
231            DB::table('block')
232                ->where('block_id', '=', $block_id)
233                ->update([
234                    'block_order' => $swap_block->block_order,
235                ]);
236
237            DB::table('block')
238                ->where('block_id', '=', $swap_block->block_id)
239                ->update([
240                    'block_order' => $block_order,
241                ]);
242        }
243
244        $url = route('module', [
245            'module' => $this->name(),
246            'action' => 'Admin',
247            'ged'    => $tree->name(),
248        ]);
249
250        return new RedirectResponse($url);
251    }
252
253    /**
254     * @param Request $request
255     * @param Tree    $tree
256     *
257     * @return Response
258     */
259    public function getAdminEditAction(Request $request, Tree $tree): Response
260    {
261        $this->layout = 'layouts/administration';
262
263        $block_id = (int) $request->get('block_id');
264
265        if ($block_id === 0) {
266            // Creating a new faq
267            $header      = '';
268            $faqbody     = '';
269
270            $block_order = 1 + (int) DB::table('block')
271                ->where('module_name', '=', $this->name())
272                ->max('block_order');
273
274            $languages = [];
275
276            $title = I18N::translate('Add an FAQ');
277        } else {
278            // Editing an existing faq
279            $header  = $this->getBlockSetting($block_id, 'header');
280            $faqbody = $this->getBlockSetting($block_id, 'faqbody');
281
282            $block_order = DB::table('block')
283                ->where('block_id', '=', $block_id)
284                ->value('block_order');
285
286            $languages = explode(',', $this->getBlockSetting($block_id, 'languages'));
287
288            $title = I18N::translate('Edit the FAQ');
289        }
290
291        $tree_names = ['' => I18N::translate('All')] + Tree::getIdList();
292
293        return $this->viewResponse('modules/faq/edit', [
294            'block_id'    => $block_id,
295            'block_order' => $block_order,
296            'header'      => $header,
297            'faqbody'     => $faqbody,
298            'languages'   => $languages,
299            'title'       => $title,
300            'tree'        => $tree,
301            'tree_names'  => $tree_names,
302        ]);
303    }
304
305    /**
306     * @param Request $request
307     * @param Tree    $tree
308     *
309     * @return RedirectResponse
310     */
311    public function postAdminEditAction(Request $request, Tree $tree): RedirectResponse
312    {
313        $block_id    = (int) $request->get('block_id');
314        $faqbody     = $request->get('faqbody', '');
315        $header      = $request->get('header', '');
316        $languages   = $request->get('languages', []);
317        $gedcom_id   = (int) $request->get('gedcom_id') ?: null;
318        $block_order = (int) $request->get('block_order');
319
320        if ($block_id !== 0) {
321            DB::table('block')
322                ->where('block_id', '=', $block_id)
323                ->update([
324                    'gedcom_id'   => $gedcom_id,
325                    'block_order' => $block_order,
326                ]);
327        } else {
328            DB::table('block')->insert([
329                'gedcom_id'   => $gedcom_id,
330                'module_name' => $this->name(),
331                'block_order' => $block_order,
332            ]);
333
334            $block_id = (int) DB::connection()->getPdo()->lastInsertId();
335        }
336
337        $this->setBlockSetting($block_id, 'faqbody', $faqbody);
338        $this->setBlockSetting($block_id, 'header', $header);
339        $this->setBlockSetting($block_id, 'languages', implode(',', $languages));
340
341        $url = route('module', [
342            'module' => $this->name(),
343            'action' => 'Admin',
344            'ged'    => $tree->name(),
345        ]);
346
347        return new RedirectResponse($url);
348    }
349
350    /**
351     * @param Tree $tree
352     *
353     * @return Response
354     */
355    public function getShowAction(Tree $tree): Response
356    {
357        // Filter foreign languages.
358        $faqs = $this->faqsForTree($tree)
359            ->filter(static function (stdClass $faq): bool {
360                return $faq->languages === '' || in_array(WT_LOCALE, explode(',', $faq->languages));
361            });
362
363        return $this->viewResponse('modules/faq/show', [
364            'faqs'  => $faqs,
365            'title' => I18N::translate('Frequently asked questions'),
366            'tree'  => $tree,
367        ]);
368    }
369
370    /**
371     * @param Tree   $tree
372     *
373     * @return Collection
374     */
375    private function faqsForTree(Tree $tree): Collection
376    {
377        return DB::table('block')
378            ->join('block_setting AS bs1', 'bs1.block_id', '=', 'block.block_id')
379            ->join('block_setting AS bs2', 'bs2.block_id', '=', 'block.block_id')
380            ->join('block_setting AS bs3', 'bs3.block_id', '=', 'block.block_id')
381            ->where('module_name', '=', $this->name())
382            ->where('bs1.setting_name', '=', 'header')
383            ->where('bs2.setting_name', '=', 'faqbody')
384            ->where('bs3.setting_name', '=', 'languages')
385            ->where(static function (Builder $query) use ($tree): void {
386                $query
387                    ->whereNull('gedcom_id')
388                    ->orWhere('gedcom_id', '=', $tree->id());
389            })
390            ->orderBy('block_order')
391            ->select(['block.block_id', 'block_order', 'gedcom_id', 'bs1.setting_value AS header', 'bs2.setting_value AS faqbody', 'bs3.setting_value AS languages'])
392            ->get();
393    }
394
395    /**
396     * @param Tree   $tree
397     * @param string $language
398     *
399     * @return bool
400     */
401    private function faqsExist(Tree $tree, string $language): bool
402    {
403        return DB::table('block')
404             ->join('block_setting', 'block_setting.block_id', '=', 'block.block_id')
405             ->where('module_name', '=', $this->name())
406             ->where('setting_name', '=', 'languages')
407             ->where(static function (Builder $query) use ($tree): void {
408                 $query
409                     ->whereNull('gedcom_id')
410                     ->orWhere('gedcom_id', '=', $tree->id());
411             })
412             ->select(['setting_value AS languages'])
413             ->get()
414             ->filter(static function (stdClass $faq) use ($language): bool {
415                 return $faq->languages === '' || in_array($language, explode(',', $faq->languages), true);
416             })
417            ->isNotEmpty();
418    }
419}
420