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() . '&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', null, array_keys(I18N::installedLanguages())); 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(), '&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(), '&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(), '&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 ' '; 406 } else { 407 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_moveup&block_id=', $faq->block_id, '" class="icon-uarrow"></a>'; 408 } 409 echo '</td><td>'; 410 if ($faq->block_order == $max_block_order) { 411 echo ' '; 412 } else { 413 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_movedown&block_id=', $faq->block_id, '" class="icon-darrow"></a>'; 414 } 415 echo '</td><td>'; 416 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_edit&block_id=', $faq->block_id, '">', I18N::translate('Edit'), '</a>'; 417 echo '</td><td>'; 418 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_delete&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&mod_action=show', 'menu-help'); 457 458 return $menu; 459 } 460} 461