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 AbstractModule 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 $this->setBlockSetting($block_id, 'header', Filter::post('header')); 94 $this->setBlockSetting($block_id, 'faqbody', Filter::post('faqbody')); 95 96 $languages = Filter::postArray('lang'); 97 $this->setBlockSetting($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 = $this->getBlockSetting($block_id, 'header'); 105 $faqbody = $this->getBlockSetting($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 131 href="module.php?mod=<?php echo $this->getName(); ?>&mod_action=admin_config"><?php echo I18N::translate('Frequently asked questions'); ?></a> 132 </li> 133 <li class="active"><?php echo $controller->getPageTitle(); ?></li> 134 </ol> 135 <h1><?php echo $controller->getPageTitle(); ?></h1> 136 137 <form name="faq" class="form-horizontal" method="post" action="module.php?mod=<?php echo $this->getName(); ?>&mod_action=admin_edit"> 138 <?php echo Filter::getCsrf(); ?> 139 <input type="hidden" name="save" value="1"> 140 <input type="hidden" name="block_id" value="<?php echo $block_id; ?>"> 141 142 <div class="form-group"> 143 <label for="header" class="col-sm-3 control-label"> 144 <?php echo I18N::translate('Question'); ?> 145 </label> 146 147 <div class="col-sm-9"> 148 <input type="text" class="form-control" name="header" id="header" 149 value="<?php echo Filter::escapeHtml($header); ?>"> 150 </div> 151 </div> 152 153 <div class="form-group"> 154 <label for="faqbody" class="col-sm-3 control-label"> 155 <?php echo I18N::translate('Answer'); ?> 156 </label> 157 158 <div class="col-sm-9"> 159 <textarea name="faqbody" id="faqbody" class="form-control" 160 rows="10"><?php echo Filter::escapeHtml($faqbody); ?></textarea> 161 </div> 162 </div> 163 164 <div class="form-group"> 165 <label for="xref" class="col-sm-3 control-label"> 166 <?php echo I18N::translate('Show this block for which languages?'); ?> 167 </label> 168 169 <div class="col-sm-9"> 170 <?php echo edit_language_checkboxes('lang', explode(',', $this->getBlockSetting($block_id, 'languages'))); ?> 171 </div> 172 </div> 173 174 <div class="form-group"> 175 <label for="block_order" class="col-sm-3 control-label"> 176 <?php echo I18N::translate('FAQ position'); ?> 177 </label> 178 179 <div class="col-sm-9"> 180 <input type="text" name="block_order" id="block_order" class="form-control" value="<?php echo $block_order; ?>"> 181 </div> 182 </div> 183 184 <div class="form-group"> 185 <label for="gedcom_id" class="col-sm-3 control-label"> 186 <?php echo I18N::translate('FAQ visibility'); ?> 187 </label> 188 189 <div class="col-sm-9"> 190 <?php echo select_edit_control('gedcom_id', Tree::getIdList(), I18N::translate('All'), $gedcom_id, 'class="form-control"'); ?> 191 <p class="small text-muted"> 192 <?php echo I18N::translate('A FAQ item can be displayed on just one of the family trees, or on all the family trees.'); ?> 193 </p> 194 </div> 195 </div> 196 197 <div class="form-group"> 198 <div class="col-sm-offset-3 col-sm-9"> 199 <button type="submit" class="btn btn-primary"> 200 <i class="fa fa-check"></i> 201 <?php echo I18N::translate('save'); ?> 202 </button> 203 </div> 204 </div> 205 206 </form> 207 <?php 208 } 209 } 210 211 /** 212 * Respond to a request to delete a FAQ. 213 */ 214 private function delete() { 215 $block_id = Filter::getInteger('block_id'); 216 217 Database::prepare( 218 "DELETE FROM `##block_setting` WHERE block_id = :block_id" 219 )->execute(array('block_id' => $block_id)); 220 221 Database::prepare( 222 "DELETE FROM `##block` WHERE block_id = :block_id" 223 )->execute(array('block_id' => $block_id)); 224 } 225 226 /** 227 * Respond to a request to move a FAQ up the list. 228 */ 229 private function moveup() { 230 $block_id = Filter::getInteger('block_id'); 231 232 $block_order = Database::prepare( 233 "SELECT block_order FROM `##block` WHERE block_id = :block_id" 234 )->execute(array('block_id' => $block_id))->fetchOne(); 235 236 $swap_block = Database::prepare( 237 "SELECT block_order, block_id" . 238 " FROM `##block`" . 239 " WHERE block_order = (" . 240 " SELECT MAX(block_order) FROM `##block` WHERE block_order < :block_order AND module_name = :module_name_1" . 241 " ) AND module_name = :module_name_2" . 242 " LIMIT 1" 243 )->execute(array( 244 'block_order' => $block_order, 245 'module_name_1' => $this->getName(), 246 'module_name_2' => $this->getName() 247 ))->fetchOneRow(); 248 if ($swap_block) { 249 Database::prepare( 250 "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id" 251 )->execute(array( 252 'block_order' => $swap_block->block_order, 253 'block_id' => $block_id, 254 )); 255 Database::prepare( 256 "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id" 257 )->execute(array( 258 'block_order' => $block_order, 259 'block_id' => $swap_block->block_id, 260 )); 261 } 262 } 263 264 /** 265 * Respond to a request to move a FAQ down the list. 266 */ 267 private function movedown() { 268 $block_id = Filter::get('block_id'); 269 270 $block_order = Database::prepare( 271 "SELECT block_order FROM `##block` WHERE block_id = :block_id" 272 )->execute(array( 273 'block_id' => $block_id, 274 ))->fetchOne(); 275 276 $swap_block = Database::prepare( 277 "SELECT block_order, block_id" . 278 " FROM `##block`" . 279 " WHERE block_order=(" . 280 " SELECT MIN(block_order) FROM `##block` WHERE block_order > :block_order AND module_name = :module_name_1" . 281 " ) AND module_name = :module_name_2" . 282 " LIMIT 1" 283 )->execute(array( 284 'block_order' => $block_order, 285 'module_name_1' => $this->getName(), 286 'module_name_2' => $this->getName(), 287 ))->fetchOneRow(); 288 if ($swap_block) { 289 Database::prepare( 290 "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id" 291 )->execute(array( 292 'block_order' => $swap_block->block_order, 293 'block_id' => $block_id, 294 )); 295 Database::prepare( 296 "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id" 297 )->execute(array( 298 'block_order' => $block_order, 299 'block_id' => $swap_block->block_id, 300 )); 301 } 302 } 303 304 /** 305 * Show a list of FAQs 306 */ 307 private function show() { 308 global $controller, $WT_TREE; 309 310 $controller = new PageController; 311 $controller 312 ->setPageTitle(I18N::translate('Frequently asked questions')) 313 ->pageHeader(); 314 315 $faqs = Database::prepare( 316 "SELECT block_id, bs1.setting_value AS header, bs2.setting_value AS body, bs3.setting_value AS languages" . 317 " FROM `##block` b" . 318 " JOIN `##block_setting` bs1 USING (block_id)" . 319 " JOIN `##block_setting` bs2 USING (block_id)" . 320 " JOIN `##block_setting` bs3 USING (block_id)" . 321 " WHERE module_name = :module_name" . 322 " AND bs1.setting_name = 'header'" . 323 " AND bs2.setting_name = 'faqbody'" . 324 " AND bs3.setting_name = 'languages'" . 325 " AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" . 326 " ORDER BY block_order" 327 )->execute(array( 328 'module_name' => $this->getName(), 329 'tree_id_1' => $WT_TREE->getTreeId(), 330 'tree_id_2' => $WT_TREE->getTreeId(), 331 ))->fetchAll(); 332 333 // Define your colors for the alternating rows 334 echo '<h2 class="center">', I18N::translate('Frequently asked questions'), '</h2>'; 335 // Instructions 336 echo '<div class="faq_italic">', I18N::translate('Click on a title to go straight to it, or scroll down to read them all.'); 337 if (Auth::isManager($WT_TREE)) { 338 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>'; 339 } 340 echo '</div>'; 341 $row_count = 0; 342 echo '<table class="faq">'; 343 // List of titles 344 foreach ($faqs as $id => $faq) { 345 if (!$faq->languages || in_array(WT_LOCALE, explode(',', $faq->languages))) { 346 $row_color = ($row_count % 2) ? 'odd' : 'even'; 347 // NOTE: Print the header of the current item 348 echo '<tr class="', $row_color, '"><td style="padding: 5px;">'; 349 echo '<a href="#faq', $id, '">', $faq->header, '</a>'; 350 echo '</td></tr>'; 351 $row_count++; 352 } 353 } 354 echo '</table><hr>'; 355 // Detailed entries 356 foreach ($faqs as $id => $faq) { 357 if (!$faq->languages || in_array(WT_LOCALE, explode(',', $faq->languages))) { 358 echo '<div class="faq_title" id="faq', $id, '">', $faq->header; 359 echo '<div class="faq_top faq_italic">'; 360 echo '<a href="#content">', I18N::translate('back to top'), '</a>'; 361 echo '</div>'; 362 echo '</div>'; 363 echo '<div class="faq_body">', substr($faq->body, 0, 1) == '<' ? $faq->body : nl2br($faq->body, false), '</div>'; 364 echo '<hr>'; 365 } 366 } 367 } 368 369 /** 370 * Provide a form to manage the FAQs. 371 */ 372 private function config() { 373 global $WT_TREE; 374 375 $controller = new PageController; 376 $controller 377 ->setPageTitle(I18N::translate('Frequently asked questions')) 378 ->pageHeader(); 379 380 $faqs = Database::prepare( 381 "SELECT block_id, block_order, gedcom_id, bs1.setting_value AS header, bs2.setting_value AS faqbody" . 382 " FROM `##block` b" . 383 " JOIN `##block_setting` bs1 USING (block_id)" . 384 " JOIN `##block_setting` bs2 USING (block_id)" . 385 " WHERE module_name = :module_name" . 386 " AND bs1.setting_name = 'header'" . 387 " AND bs2.setting_name = 'faqbody'" . 388 " AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" . 389 " ORDER BY block_order" 390 )->execute(array( 391 'module_name' => $this->getName(), 392 'tree_id_1' => $WT_TREE->getTreeId(), 393 'tree_id_2' => $WT_TREE->getTreeId(), 394 ))->fetchAll(); 395 396 $min_block_order = Database::prepare( 397 "SELECT MIN(block_order) FROM `##block` WHERE module_name = 'faq' AND (gedcom_id = :tree_id OR gedcom_id IS NULL)" 398 )->execute(array( 399 'tree_id' => $WT_TREE->getTreeId(), 400 ))->fetchOne(); 401 402 $max_block_order = Database::prepare( 403 "SELECT MAX(block_order) FROM `##block` WHERE module_name = 'faq' AND (gedcom_id = :tree_id OR gedcom_id IS NULL)" 404 )->execute(array( 405 'tree_id' => $WT_TREE->getTreeId(), 406 ))->fetchOne(); 407 408 ?> 409 <ol class="breadcrumb small"> 410 <li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li> 411 <li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li> 412 <li class="active"><?php echo $controller->getPageTitle(); ?></li> 413 </ol> 414 <h2><?php echo $controller->getPageTitle(); ?></h2> 415 <p> 416 <?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.'); ?> 417 <?php echo I18N::translate('You may use HTML to format the answer and to add links to other websites.'); ?> 418 </p> 419 <?php 420 421 echo 422 '<p><form>', 423 I18N::translate('Family tree'), ' ', 424 '<input type="hidden" name="mod", value="', $this->getName(), '">', 425 '<input type="hidden" name="mod_action" value="admin_config">', 426 select_edit_control('ged', Tree::getNameList(), null, $WT_TREE->getNameHtml()), 427 '<input type="submit" value="', I18N::translate('show'), '">', 428 '</form></p>'; 429 430 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_edit">', I18N::translate('Add an FAQ item'), '</a>'; 431 432 echo '<table class="table table-bordered">'; 433 if (empty($faqs)) { 434 echo '<tr><td class="error center" colspan="5">', I18N::translate('The FAQ list is empty.'), '</td></tr></table>'; 435 } else { 436 foreach ($faqs as $faq) { 437 // NOTE: Print the position of the current item 438 echo '<tr class="faq_edit_pos"><td>'; 439 echo I18N::translate('#%s', $faq->block_order + 1), ' '; 440 if ($faq->gedcom_id == null) { 441 echo I18N::translate('All'); 442 } else { 443 echo $WT_TREE->getTitleHtml(); 444 } 445 echo '</td>'; 446 // NOTE: Print the edit options of the current item 447 echo '<td>'; 448 if ($faq->block_order == $min_block_order) { 449 echo ' '; 450 } else { 451 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_moveup&block_id=', $faq->block_id, '"><i class="fa fa-arrow-up"></i></i> ', I18N::translate('Move up'), '</a>'; 452 } 453 echo '</td><td>'; 454 if ($faq->block_order == $max_block_order) { 455 echo ' '; 456 } else { 457 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_movedown&block_id=', $faq->block_id, '"><i class="fa fa-arrow-down"></i></i> ', I18N::translate('Move down'), '</a>'; 458 } 459 echo '</td><td>'; 460 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_edit&block_id=', $faq->block_id, '"><i class="fa fa-pencil"></i> ', I18N::translate('Edit'), '</a>'; 461 echo '</td><td>'; 462 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?'), '\');"><i class="fa fa-trash"></i> ', I18N::translate('Delete'), '</a>'; 463 echo '</td></tr>'; 464 // NOTE: Print the title text of the current item 465 echo '<tr><td colspan="5">'; 466 echo '<div class="faq_edit_item">'; 467 echo '<div class="faq_edit_title">', $faq->header, '</div>'; 468 // NOTE: Print the body text of the current item 469 echo '<div class="faq_edit_content">', substr($faq->faqbody, 0, 1) == '<' ? $faq->faqbody : nl2br($faq->faqbody, false), '</div></div></td></tr>'; 470 } 471 echo '</table>'; 472 } 473 } 474 475 /** {@inheritdoc} */ 476 public function defaultMenuOrder() { 477 return 40; 478 } 479 480 /** {@inheritdoc} */ 481 public function getMenu() { 482 global $WT_TREE; 483 484 if (Auth::isSearchEngine()) { 485 return null; 486 } 487 488 $faqs = Database::prepare( 489 "SELECT block_id FROM `##block` WHERE module_name = :module_name AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" 490 )->execute(array( 491 'module_name' => $this->getName(), 492 'tree_id_1' => $WT_TREE->getTreeId(), 493 'tree_id_2' => $WT_TREE->getTreeId(), 494 ))->fetchAll(); 495 496 if (!$faqs) { 497 return null; 498 } 499 500 $menu = new Menu(I18N::translate('FAQ'), 'module.php?mod=faq&mod_action=show', 'menu-help'); 501 502 return $menu; 503 } 504} 505