xref: /webtrees/app/Module/FrequentlyAskedQuestionsModule.php (revision 76ab908c83a7ad14130d09d30c1d81543922dff3)
1<?php
2namespace Fisharebest\Webtrees;
3
4/**
5 * webtrees: online genealogy
6 * Copyright (C) 2015 webtrees development team
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * Class FrequentlyAskedQuestionsModule
21 */
22class FrequentlyAskedQuestionsModule extends Module implements ModuleMenuInterface, ModuleConfigInterface {
23	/** {@inheritdoc} */
24	public function getTitle() {
25		return /* I18N: Name of a module.  Abbreviation for “Frequently Asked Questions” */ I18N::translate('FAQ');
26	}
27
28	/** {@inheritdoc} */
29	public function getDescription() {
30		return /* I18N: Description of the “FAQ” module */ I18N::translate('A list of frequently asked questions and answers.');
31	}
32
33	/** {@inheritdoc} */
34	public function modAction($mod_action) {
35		switch ($mod_action) {
36		case 'admin_config':
37			$this->config();
38			break;
39		case 'admin_delete':
40			$this->delete();
41			$this->config();
42			break;
43		case 'admin_edit':
44			$this->edit();
45			break;
46		case 'admin_movedown':
47			$this->movedown();
48			$this->config();
49			break;
50		case 'admin_moveup':
51			$this->moveup();
52			$this->config();
53			break;
54		case 'show':
55			$this->show();
56			break;
57		default:
58			http_response_code(404);
59		}
60	}
61
62	/** {@inheritdoc} */
63	public function getConfigLink() {
64		return 'module.php?mod=' . $this->getName() . '&amp;mod_action=admin_config';
65	}
66
67	/**
68	 * Action from the configuration page
69	 */
70	private function edit() {
71		global $WT_TREE;
72
73		if (Filter::postBool('save') && Filter::checkCsrf()) {
74			$block_id = Filter::postInteger('block_id');
75			if ($block_id) {
76				Database::prepare(
77					"UPDATE `##block` SET gedcom_id = NULLIF(:tree_id, '0'), block_order = :block_order WHERE block_id = :block_id"
78				)->execute(array(
79					'tree_id'     => Filter::postInteger('gedcom_id'),
80					'block_order' => Filter::postInteger('block_order'),
81					'block_id'    => $block_id
82				));
83			} else {
84				Database::prepare(
85					"INSERT INTO `##block` (gedcom_id, module_name, block_order) VALUES (NULLIF(:tree_id, '0'), :module_name, :block_order)"
86				)->execute(array(
87					'tree_id'     => Filter::postInteger('gedcom_id'),
88					'module_name' => $this->getName(),
89					'block_order' => Filter::postInteger('block_order'),
90				));
91				$block_id = Database::getInstance()->lastInsertId();
92			}
93			set_block_setting($block_id, 'header', Filter::post('header'));
94			set_block_setting($block_id, 'faqbody', Filter::post('faqbody'));
95
96			$languages = Filter::postArray('lang');
97			set_block_setting($block_id, 'languages', implode(',', $languages));
98			$this->config();
99		} else {
100			$block_id   = Filter::getInteger('block_id');
101			$controller = new PageController;
102			if ($block_id) {
103				$controller->setPageTitle(I18N::translate('Edit FAQ item'));
104				$header      = get_block_setting($block_id, 'header');
105				$faqbody     = get_block_setting($block_id, 'faqbody');
106				$block_order = Database::prepare(
107					"SELECT block_order FROM `##block` WHERE block_id = :block_id"
108				)->execute(array('block_id' => $block_id))->fetchOne();
109				$gedcom_id   = Database::prepare(
110					"SELECT gedcom_id FROM `##block` WHERE block_id = :block_id"
111				)->execute(array('block_id' => $block_id))->fetchOne();
112			} else {
113				$controller->setPageTitle(I18N::translate('Add an FAQ item'));
114				$header      = '';
115				$faqbody     = '';
116				$block_order = Database::prepare(
117					"SELECT IFNULL(MAX(block_order)+1, 0) FROM `##block` WHERE module_name = :module_name"
118				)->execute(array('module_name' => $this->getName()))->fetchOne();
119				$gedcom_id   = $WT_TREE->getTreeId();
120			}
121			$controller->pageHeader();
122			if (Module::getModuleByName('ckeditor')) {
123				CkeditorModule::enableEditor($controller);
124			}
125
126			?>
127			<ol class="breadcrumb small">
128				<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
129				<li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li>
130				<li><a href="module.php?mod=<?php echo $this->getName(); ?>&mod_action=admin_config"><?php echo I18N::translate('Frequently asked questions'); ?></a></li>
131				<li class="active"><?php echo $controller->getPageTitle(); ?></li>
132			</ol>
133			<h2><?php echo $controller->getPageTitle(); ?></h2>
134			<?php
135
136			echo '<form name="faq" method="post" action="module.php?mod=', $this->getName(), '&amp;mod_action=admin_edit">';
137			echo Filter::getCsrf();
138			echo '<input type="hidden" name="save" value="1">';
139			echo '<input type="hidden" name="block_id" value="', $block_id, '">';
140			echo '<table id="faq_module">';
141			echo '<tr><th>';
142			echo I18N::translate('Question');
143			echo '</th></tr><tr><td><input type="text" name="header" size="90" tabindex="1" value="' . Filter::escapeHtml($header) . '"></td></tr>';
144			echo '<tr><th>';
145			echo I18N::translate('Answer');
146			echo '</th></tr><tr><td>';
147			echo '<textarea name="faqbody" class="html-edit" rows="10" cols="90" tabindex="2">', Filter::escapeHtml($faqbody), '</textarea>';
148			echo '</td></tr>';
149			echo '</table><table id="faq_module2">';
150			echo '<tr>';
151			echo '<th>', I18N::translate('Show this block for which languages?'), '</th>';
152			echo '<th>', I18N::translate('FAQ position'), '</th>';
153			echo '<th>', I18N::translate('FAQ visibility'), '<br><small>', I18N::translate('A FAQ item can be displayed on just one of the family trees, or on all the family trees.'), '</small></th>';
154			echo '</tr><tr>';
155			echo '<td>';
156			$languages = explode(',', get_block_setting($block_id, 'languages'));
157			echo edit_language_checkboxes('lang', $languages);
158			echo '</td><td>';
159			echo '<input type="text" name="block_order" size="3" tabindex="3" value="', $block_order, '"></td>';
160			echo '</td><td>';
161			echo select_edit_control('gedcom_id', Tree::getIdList(), I18N::translate('All'), $gedcom_id, 'tabindex="4"');
162			echo '</td></tr>';
163			echo '</table>';
164
165			echo '<p><input type="submit" value="', I18N::translate('save'), '" tabindex="5">';
166			echo '</form>';
167		}
168	}
169
170	/**
171	 * Respond to a request to delete a FAQ.
172	 */
173	private function delete() {
174		$block_id = Filter::getInteger('block_id');
175
176		Database::prepare(
177			"DELETE FROM `##block_setting` WHERE block_id = :block_id"
178		)->execute(array('block_id' => $block_id));
179
180		Database::prepare(
181			"DELETE FROM `##block` WHERE block_id = :block_id"
182		)->execute(array('block_id' => $block_id));
183	}
184
185	/**
186	 * Respond to a request to move a FAQ up the list.
187	 */
188	private function moveup() {
189		$block_id = Filter::getInteger('block_id');
190
191		$block_order = Database::prepare(
192			"SELECT block_order FROM `##block` WHERE block_id = :block_id"
193		)->execute(array('block_id' => $block_id))->fetchOne();
194
195		$swap_block = Database::prepare(
196			"SELECT block_order, block_id" .
197			" FROM `##block`" .
198			" WHERE block_order = (" .
199			"  SELECT MAX(block_order) FROM `##block` WHERE block_order < :block_order AND module_name = :module_name_1" .
200			" ) AND module_name = :module_name_2" .
201			" LIMIT 1"
202		)->execute(array(
203			'block_order'   => $block_order,
204			'module_name_1' => $this->getName(),
205			'module_name_2' => $this->getName()
206		))->fetchOneRow();
207		if ($swap_block) {
208			Database::prepare(
209				"UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id"
210			)->execute(array(
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(array(
217				'block_order' => $block_order,
218				'block_id'    => $swap_block->block_id,
219			));
220		}
221	}
222
223	/**
224	 * Respond to a request to move a FAQ down the list.
225	 */
226	private function movedown() {
227		$block_id = Filter::get('block_id');
228
229		$block_order = Database::prepare(
230			"SELECT block_order FROM `##block` WHERE block_id = :block_id"
231		)->execute(array(
232			'block_id' => $block_id,
233		))->fetchOne();
234
235		$swap_block = Database::prepare(
236			"SELECT block_order, block_id" .
237			" FROM `##block`" .
238			" WHERE block_order=(" .
239			"  SELECT MIN(block_order) FROM `##block` WHERE block_order > :block_order AND module_name = :module_name_1" .
240			" ) AND module_name = :module_name_2" .
241			" LIMIT 1"
242		)->execute(array(
243			'block_order'   => $block_order,
244			'module_name_1' => $this->getName(),
245			'module_name_2' => $this->getName(),
246			))->fetchOneRow();
247		if ($swap_block) {
248			Database::prepare(
249				"UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id"
250			)->execute(array(
251				'block_order' => $swap_block->block_order,
252				'block_id'    => $block_id,
253			));
254			Database::prepare(
255				"UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id"
256			)->execute(array(
257				'block_order' => $block_order,
258				'block_id'    => $swap_block->block_id,
259			));
260		}
261	}
262
263	/**
264	 * Show a list of FAQs
265	 */
266	private function show() {
267		global $controller, $WT_TREE;
268
269		$controller = new PageController;
270		$controller
271			->setPageTitle(I18N::translate('Frequently asked questions'))
272			->pageHeader();
273
274		$faqs = Database::prepare(
275			"SELECT block_id, bs1.setting_value AS header, bs2.setting_value AS body, bs3.setting_value AS languages" .
276			" FROM `##block` b" .
277			" JOIN `##block_setting` bs1 USING (block_id)" .
278			" JOIN `##block_setting` bs2 USING (block_id)" .
279			" JOIN `##block_setting` bs3 USING (block_id)" .
280			" WHERE module_name = :module_name" .
281			" AND bs1.setting_name = 'header'" .
282			" AND bs2.setting_name = 'faqbody'" .
283			" AND bs3.setting_name = 'languages'" .
284			" AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" .
285			" ORDER BY block_order"
286		)->execute(array(
287			'module_name' => $this->getName(),
288			'tree_id_1'   => $WT_TREE->getTreeId(),
289			'tree_id_2'   => $WT_TREE->getTreeId(),
290		))->fetchAll();
291
292		// Define your colors for the alternating rows
293		echo '<h2 class="center">', I18N::translate('Frequently asked questions'), '</h2>';
294		// Instructions
295		echo '<div class="faq_italic">', I18N::translate('Click on a title to go straight to it, or scroll down to read them all.');
296		if (Auth::isManager($WT_TREE)) {
297			echo '<div class="faq_edit"><a href="module.php?mod=', $this->getName(), '&amp;mod_action=admin_config">', I18N::translate('Click here to add, edit, or delete'), '</a></div>';
298		}
299		echo '</div>';
300		$row_count = 0;
301		echo '<table class="faq">';
302		// List of titles
303		foreach ($faqs as $id => $faq) {
304			if (!$faq->languages || in_array(WT_LOCALE, explode(',', $faq->languages))) {
305				$row_color = ($row_count % 2) ? 'odd' : 'even';
306				// NOTE: Print the header of the current item
307				echo '<tr class="', $row_color, '"><td style="padding: 5px;">';
308				echo '<a href="#faq', $id, '">', $faq->header, '</a>';
309				echo '</td></tr>';
310				$row_count++;
311			}
312		}
313		echo '</table><hr>';
314		// Detailed entries
315		foreach ($faqs as $id => $faq) {
316			if (!$faq->languages || in_array(WT_LOCALE, explode(',', $faq->languages))) {
317				echo '<div class="faq_title" id="faq', $id, '">', $faq->header;
318				echo '<div class="faq_top faq_italic">';
319				echo '<a href="#content">', I18N::translate('back to top'), '</a>';
320				echo '</div>';
321				echo '</div>';
322				echo '<div class="faq_body">', substr($faq->body, 0, 1) == '<' ? $faq->body : nl2br($faq->body, false), '</div>';
323				echo '<hr>';
324			}
325		}
326	}
327
328	/**
329	 * Provide a form to manage the FAQs.
330	 */
331	private function config() {
332		global $WT_TREE;
333
334		$controller = new PageController;
335		$controller
336			->setPageTitle(I18N::translate('Frequently asked questions'))
337			->pageHeader();
338
339		$faqs = Database::prepare(
340			"SELECT block_id, block_order, gedcom_id, bs1.setting_value AS header, bs2.setting_value AS faqbody" .
341			" FROM `##block` b" .
342			" JOIN `##block_setting` bs1 USING (block_id)" .
343			" JOIN `##block_setting` bs2 USING (block_id)" .
344			" WHERE module_name = :module_name" .
345			" AND bs1.setting_name = 'header'" .
346			" AND bs2.setting_name = 'faqbody'" .
347			" AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" .
348			" ORDER BY block_order"
349		)->execute(array(
350			'module_name' => $this->getName(),
351			'tree_id_1'   => $WT_TREE->getTreeId(),
352			'tree_id_2'   => $WT_TREE->getTreeId(),
353			))->fetchAll();
354
355		$min_block_order = Database::prepare(
356			"SELECT MIN(block_order) FROM `##block` WHERE module_name=?"
357		)->execute(array($this->getName()))->fetchOne();
358
359		$max_block_order = Database::prepare(
360			"SELECT MAX(block_order) FROM `##block` WHERE module_name=?"
361		)->execute(array($this->getName()))->fetchOne();
362
363		?>
364		<ol class="breadcrumb small">
365			<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
366			<li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li>
367			<li class="active"><?php echo $controller->getPageTitle(); ?></li>
368		</ol>
369		<h2><?php echo $controller->getPageTitle(); ?></h2>
370		<p>
371			<?php echo I18N::translate('FAQs are lists of questions and answers, which allow you to explain the site’s rules, policies, and procedures to your visitors.  Questions are typically concerned with privacy, copyright, user-accounts, unsuitable content, requirement for source-citations, etc.'); ?>
372			<?php echo I18N::translate('You may use HTML to format the answer and to add links to other websites.'); ?>
373		</p>
374		<?php
375
376		echo
377			'<p><form>',
378			I18N::translate('Family tree'), ' ',
379			'<input type="hidden" name="mod", value="', $this->getName(), '">',
380			'<input type="hidden" name="mod_action" value="admin_config">',
381			select_edit_control('ged', Tree::getNameList(), null, $WT_TREE->getNameHtml()),
382			'<input type="submit" value="', I18N::translate('show'), '">',
383			'</form></p>';
384
385		echo '<a href="module.php?mod=', $this->getName(), '&amp;mod_action=admin_edit">', I18N::translate('Add an FAQ item'), '</a>';
386
387		echo '<table id="faq_edit">';
388		if (empty($faqs)) {
389			echo '<tr><td class="error center" colspan="5">', I18N::translate('The FAQ list is empty.'), '</td></tr></table>';
390		} else {
391			$trees = Tree::getAll();
392			foreach ($faqs as $faq) {
393				// NOTE: Print the position of the current item
394				echo '<tr class="faq_edit_pos"><td>';
395				echo I18N::translate('Position item'), ': ', ($faq->block_order + 1), ', ';
396				if ($faq->gedcom_id == null) {
397					echo I18N::translate('All');
398				} else {
399					echo $trees[$faq->gedcom_id]->getTitleHtml();
400				}
401				echo '</td>';
402				// NOTE: Print the edit options of the current item
403				echo '<td>';
404				if ($faq->block_order == $min_block_order) {
405					echo '&nbsp;';
406				} else {
407					echo '<a href="module.php?mod=', $this->getName(), '&amp;mod_action=admin_moveup&amp;block_id=', $faq->block_id, '" class="icon-uarrow"></a>';
408				}
409				echo '</td><td>';
410				if ($faq->block_order == $max_block_order) {
411					echo '&nbsp;';
412				} else {
413					echo '<a href="module.php?mod=', $this->getName(), '&amp;mod_action=admin_movedown&amp;block_id=', $faq->block_id, '" class="icon-darrow"></a>';
414				}
415				echo '</td><td>';
416				echo '<a href="module.php?mod=', $this->getName(), '&amp;mod_action=admin_edit&amp;block_id=', $faq->block_id, '">', I18N::translate('Edit'), '</a>';
417				echo '</td><td>';
418				echo '<a href="module.php?mod=', $this->getName(), '&amp;mod_action=admin_delete&amp;block_id=', $faq->block_id, '" onclick="return confirm(\'', I18N::translate('Are you sure you want to delete this FAQ entry?'), '\');">', I18N::translate('Delete'), '</a>';
419				echo '</td></tr>';
420				// NOTE: Print the title text of the current item
421				echo '<tr><td colspan="5">';
422				echo '<div class="faq_edit_item">';
423				echo '<div class="faq_edit_title">', $faq->header, '</div>';
424				// NOTE: Print the body text of the current item
425				echo '<div class="faq_edit_content">', substr($faq->faqbody, 0, 1) == '<' ? $faq->faqbody : nl2br($faq->faqbody, false), '</div></div></td></tr>';
426			}
427			echo '</table>';
428		}
429	}
430
431	/** {@inheritdoc} */
432	public function defaultMenuOrder() {
433		return 40;
434	}
435
436	/** {@inheritdoc} */
437	public function getMenu() {
438		global $WT_TREE;
439
440		if (Auth::isSearchEngine()) {
441			return null;
442		}
443
444		$faqs = Database::prepare(
445			"SELECT block_id FROM `##block` WHERE module_name = :module_name AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2"
446		)->execute(array(
447			'module_name' => $this->getName(),
448			'tree_id_1'   => $WT_TREE->getTreeId(),
449			'tree_id_2'   => $WT_TREE->getTreeId(),
450		))->fetchAll();
451
452		if (!$faqs) {
453			return null;
454		}
455
456		$menu = new Menu(I18N::translate('FAQ'), 'module.php?mod=faq&amp;mod_action=show', 'menu-help');
457
458		return $menu;
459	}
460}
461