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