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