xref: /webtrees/app/Module/FrequentlyAskedQuestionsModule.php (revision 8136679edbff8170fb92bd6e8dd8df8eb645bb1a)
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 labelled on tabs, menus, 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(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(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', '=', 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', '=', 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        return $this->viewResponse('modules/faq/edit', [
292            'block_id'    => $block_id,
293            'block_order' => $block_order,
294            'header'      => $header,
295            'faqbody'     => $faqbody,
296            'languages'   => $languages,
297            'title'       => $title,
298            'tree'        => $tree,
299            'tree_names'  => Tree::getIdList(),
300        ]);
301    }
302
303    /**
304     * @param Request $request
305     * @param Tree    $tree
306     *
307     * @return RedirectResponse
308     */
309    public function postAdminEditAction(Request $request, Tree $tree): RedirectResponse
310    {
311        $block_id    = (int) $request->get('block_id');
312        $faqbody     = $request->get('faqbody', '');
313        $header      = $request->get('header', '');
314        $languages   = $request->get('languages', []);
315        $gedcom_id   = (int) $request->get('gedcom_id') ?: null;
316        $block_order = (int) $request->get('block_order');
317
318        if ($block_id !== 0) {
319            DB::table('block')
320                ->where('block_id', '=', $block_id)
321                ->update([
322                    'gedcom_id'   => $gedcom_id,
323                    'block_order' => $block_order,
324                ]);
325        } else {
326            DB::table('block')->insert([
327                'gedcom_id'   => $gedcom_id,
328                'module_name' => $this->name(),
329                'block_order' => $block_order,
330            ]);
331
332            $block_id = (int) DB::connection()->getPdo()->lastInsertId();
333        }
334
335        $this->setBlockSetting($block_id, 'faqbody', $faqbody);
336        $this->setBlockSetting($block_id, 'header', $header);
337        $this->setBlockSetting($block_id, 'languages', implode(',', $languages));
338
339        $url = route('module', [
340            'module' => $this->name(),
341            'action' => 'Admin',
342            'ged'    => $tree->name(),
343        ]);
344
345        return new RedirectResponse($url);
346    }
347
348    /**
349     * @param Tree $tree
350     *
351     * @return Response
352     */
353    public function getShowAction(Tree $tree): Response
354    {
355        // Filter foreign languages.
356        $faqs = $this->faqsForTree($tree)
357            ->filter(function (stdClass $faq): bool {
358                return $faq->languages === '' || in_array(WT_LOCALE, explode(',', $faq->languages));
359            });
360
361        return $this->viewResponse('modules/faq/show', [
362            'faqs'  => $faqs,
363            'title' => I18N::translate('Frequently asked questions'),
364            'tree'  => $tree,
365        ]);
366    }
367
368    /**
369     * @param Tree   $tree
370     *
371     * @return Collection
372     */
373    private function faqsForTree(Tree $tree): Collection
374    {
375        return DB::table('block')
376            ->join('block_setting AS bs1', 'bs1.block_id', '=', 'block.block_id')
377            ->join('block_setting AS bs2', 'bs2.block_id', '=', 'block.block_id')
378            ->join('block_setting AS bs3', 'bs3.block_id', '=', 'block.block_id')
379            ->where('module_name', '=', $this->name())
380            ->where('bs1.setting_name', '=', 'header')
381            ->where('bs2.setting_name', '=', 'faqbody')
382            ->where('bs3.setting_name', '=', 'languages')
383            ->where(function (Builder $query) use ($tree): void {
384                $query
385                    ->whereNull('gedcom_id')
386                    ->orWhere('gedcom_id', '=', $tree->id());
387            })
388            ->orderBy('block_order')
389            ->select(['block.block_id', 'block_order', 'gedcom_id', 'bs1.setting_value AS header', 'bs2.setting_value AS faqbody', 'bs3.setting_value AS languages'])
390            ->get();
391    }
392
393    /**
394     * @param Tree   $tree
395     * @param string $language
396     *
397     * @return bool
398     */
399    private function faqsExist(Tree $tree, string $language): bool
400    {
401        return DB::table('block')
402             ->join('block_setting', 'block_setting.block_id', '=', 'block.block_id')
403             ->where('module_name', '=', $this->name())
404             ->where('setting_name', '=', 'languages')
405             ->where(function (Builder $query) use ($tree): void {
406                 $query
407                     ->whereNull('gedcom_id')
408                     ->orWhere('gedcom_id', '=', $tree->id());
409             })
410             ->select(['setting_value AS languages'])
411             ->get()
412             ->filter(function (stdClass $faq) use ($language): bool {
413                 return $faq->languages === '' || in_array($language, explode(',', $faq->languages));
414             })
415            ->isNotEmpty();
416    }
417}
418