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 if (Filter::postBool('save') && Filter::checkCsrf()) { 72 $block_id = Filter::postInteger('block_id'); 73 if ($block_id) { 74 Database::prepare( 75 "UPDATE `##block` SET gedcom_id = NULLIF(:tree_id, '0'), block_order = :block_order WHERE block_id = :block_id" 76 )->execute(array( 77 'tree_id' => Filter::postInteger('gedcom_id'), 78 'block_order' => Filter::postInteger('block_order'), 79 'block_id' => $block_id 80 )); 81 } else { 82 Database::prepare( 83 "INSERT INTO `##block` (gedcom_id, module_name, block_order) VALUES (NULLIF(:tree_id, '0'), :module_name, :block_order)" 84 )->execute(array( 85 'tree_id' => Filter::postInteger('gedcom_id'), 86 'module_name' => $this->getName(), 87 'block_order' => Filter::postInteger('block_order'), 88 )); 89 $block_id = Database::getInstance()->lastInsertId(); 90 } 91 set_block_setting($block_id, 'header', Filter::post('header')); 92 set_block_setting($block_id, 'faqbody', Filter::post('faqbody')); 93 94 $languages = Filter::postArray('lang', null, array_keys(I18N::installedLanguages())); 95 set_block_setting($block_id, 'languages', implode(',', $languages)); 96 $this->config(); 97 } else { 98 $block_id = Filter::getInteger('block_id'); 99 $controller = new PageController; 100 if ($block_id) { 101 $controller->setPageTitle(I18N::translate('Edit FAQ item')); 102 $header = get_block_setting($block_id, 'header'); 103 $faqbody = get_block_setting($block_id, 'faqbody'); 104 $block_order = Database::prepare( 105 "SELECT block_order FROM `##block` WHERE block_id = :block_id" 106 )->execute(array('block_id' => $block_id))->fetchOne(); 107 $gedcom_id = Database::prepare( 108 "SELECT gedcom_id FROM `##block` WHERE block_id = :block_id" 109 )->execute(array('block_id' => $block_id))->fetchOne(); 110 } else { 111 $controller->setPageTitle(I18N::translate('Add an FAQ item')); 112 $header = ''; 113 $faqbody = ''; 114 $block_order = Database::prepare( 115 "SELECT IFNULL(MAX(block_order)+1, 0) FROM `##block` WHERE module_name = :module_name" 116 )->execute(array('module_name' => $this->getName()))->fetchOne(); 117 $gedcom_id = WT_GED_ID; 118 } 119 $controller->pageHeader(); 120 if (Module::getModuleByName('ckeditor')) { 121 CkeditorModule::enableEditor($controller); 122 } 123 124 ?> 125 <ol class="breadcrumb small"> 126 <li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li> 127 <li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li> 128 <li><a href="module.php?mod=<?php echo $this->getName(); ?>&mod_action=admin_config"><?php echo I18N::translate('Frequently asked questions'); ?></a></li> 129 <li class="active"><?php echo $controller->getPageTitle(); ?></li> 130 </ol> 131 <h2><?php echo $controller->getPageTitle(); ?></h2> 132 <?php 133 134 echo '<form name="faq" method="post" action="module.php?mod=', $this->getName(), '&mod_action=admin_edit">'; 135 echo Filter::getCsrf(); 136 echo '<input type="hidden" name="save" value="1">'; 137 echo '<input type="hidden" name="block_id" value="', $block_id, '">'; 138 echo '<table id="faq_module">'; 139 echo '<tr><th>'; 140 echo I18N::translate('Question'); 141 echo '</th></tr><tr><td><input type="text" name="header" size="90" tabindex="1" value="' . Filter::escapeHtml($header) . '"></td></tr>'; 142 echo '<tr><th>'; 143 echo I18N::translate('Answer'); 144 echo '</th></tr><tr><td>'; 145 echo '<textarea name="faqbody" class="html-edit" rows="10" cols="90" tabindex="2">', Filter::escapeHtml($faqbody), '</textarea>'; 146 echo '</td></tr>'; 147 echo '</table><table id="faq_module2">'; 148 echo '<tr>'; 149 echo '<th>', I18N::translate('Show this block for which languages?'), '</th>'; 150 echo '<th>', I18N::translate('FAQ position'), '</th>'; 151 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>'; 152 echo '</tr><tr>'; 153 echo '<td>'; 154 $languages = explode(',', get_block_setting($block_id, 'languages')); 155 echo edit_language_checkboxes('lang', $languages); 156 echo '</td><td>'; 157 echo '<input type="text" name="block_order" size="3" tabindex="3" value="', $block_order, '"></td>'; 158 echo '</td><td>'; 159 echo select_edit_control('gedcom_id', Tree::getIdList(), I18N::translate('All'), $gedcom_id, 'tabindex="4"'); 160 echo '</td></tr>'; 161 echo '</table>'; 162 163 echo '<p><input type="submit" value="', I18N::translate('save'), '" tabindex="5">'; 164 echo '</form>'; 165 } 166 } 167 168 /** 169 * Respond to a request to delete a FAQ. 170 */ 171 private function delete() { 172 $block_id = Filter::getInteger('block_id'); 173 174 Database::prepare( 175 "DELETE FROM `##block_setting` WHERE block_id = :block_id" 176 )->execute(array('block_id' => $block_id)); 177 178 Database::prepare( 179 "DELETE FROM `##block` WHERE block_id = :block_id" 180 )->execute(array('block_id' => $block_id)); 181 } 182 183 /** 184 * Respond to a request to move a FAQ up the list. 185 */ 186 private function moveup() { 187 $block_id = Filter::getInteger('block_id'); 188 189 $block_order = Database::prepare( 190 "SELECT block_order FROM `##block` WHERE block_id = :block_id" 191 )->execute(array('block_id' => $block_id))->fetchOne(); 192 193 $swap_block = Database::prepare( 194 "SELECT block_order, block_id" . 195 " FROM `##block`" . 196 " WHERE block_order = (" . 197 " SELECT MAX(block_order) FROM `##block` WHERE block_order < :block_order AND module_name = :module_name_1" . 198 " ) AND module_name = :module_name_2" . 199 " LIMIT 1" 200 )->execute(array( 201 'block_order' => $block_order, 202 'module_name_1' => $this->getName(), 203 'module_name_2' => $this->getName() 204 ))->fetchOneRow(); 205 if ($swap_block) { 206 Database::prepare( 207 "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id" 208 )->execute(array( 209 'block_order' => $swap_block->block_order, 210 'block_id' => $block_id, 211 )); 212 Database::prepare( 213 "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id" 214 )->execute(array( 215 'block_order' => $block_order, 216 'block_id' => $swap_block->block_id, 217 )); 218 } 219 } 220 221 /** 222 * Respond to a request to move a FAQ down the list. 223 */ 224 private function movedown() { 225 $block_id = Filter::get('block_id'); 226 227 $block_order = Database::prepare( 228 "SELECT block_order FROM `##block` WHERE block_id = :block_id" 229 )->execute(array( 230 'block_id' => $block_id, 231 ))->fetchOne(); 232 233 $swap_block = Database::prepare( 234 "SELECT block_order, block_id" . 235 " FROM `##block`" . 236 " WHERE block_order=(" . 237 " SELECT MIN(block_order) FROM `##block` WHERE block_order > :block_order AND module_name = :module_name_1" . 238 " ) AND module_name = :module_name_2" . 239 " LIMIT 1" 240 )->execute(array( 241 'block_order' => $block_order, 242 'module_name_1' => $this->getName(), 243 'module_name_2' => $this->getName(), 244 ))->fetchOneRow(); 245 if ($swap_block) { 246 Database::prepare( 247 "UPDATE `##block` SET block_order = :block_order WHERE block_id = :block_id" 248 )->execute(array( 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(array( 255 'block_order' => $block_order, 256 'block_id' => $swap_block->block_id, 257 )); 258 } 259 } 260 261 /** 262 * Show a list of FAQs 263 */ 264 private function show() { 265 global $controller, $WT_TREE; 266 267 $controller = new PageController; 268 $controller 269 ->setPageTitle(I18N::translate('Frequently asked questions')) 270 ->pageHeader(); 271 272 $faqs = Database::prepare( 273 "SELECT block_id, bs1.setting_value AS header, bs2.setting_value AS body, bs3.setting_value AS languages" . 274 " FROM `##block` b" . 275 " JOIN `##block_setting` bs1 USING (block_id)" . 276 " JOIN `##block_setting` bs2 USING (block_id)" . 277 " JOIN `##block_setting` bs3 USING (block_id)" . 278 " WHERE module_name = :module_name" . 279 " AND bs1.setting_name = 'header'" . 280 " AND bs2.setting_name = 'faqbody'" . 281 " AND bs3.setting_name = 'languages'" . 282 " AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" . 283 " ORDER BY block_order" 284 )->execute(array( 285 'module_name' => $this->getName(), 286 'tree_id_1' => WT_GED_ID, 287 'tree_id_2' => WT_GED_ID, 288 ))->fetchAll(); 289 290 // Define your colors for the alternating rows 291 echo '<h2 class="center">', I18N::translate('Frequently asked questions'), '</h2>'; 292 // Instructions 293 echo '<div class="faq_italic">', I18N::translate('Click on a title to go straight to it, or scroll down to read them all'); 294 if (Auth::isManager($WT_TREE)) { 295 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>'; 296 } 297 echo '</div>'; 298 $row_count = 0; 299 echo '<table class="faq">'; 300 // List of titles 301 foreach ($faqs as $id => $faq) { 302 if (!$faq->languages || in_array(WT_LOCALE, explode(',', $faq->languages))) { 303 $row_color = ($row_count % 2) ? 'odd' : 'even'; 304 // NOTE: Print the header of the current item 305 echo '<tr class="', $row_color, '"><td style="padding: 5px;">'; 306 echo '<a href="#faq', $id, '">', $faq->header, '</a>'; 307 echo '</td></tr>'; 308 $row_count++; 309 } 310 } 311 echo '</table><hr>'; 312 // Detailed entries 313 foreach ($faqs as $id => $faq) { 314 if (!$faq->languages || in_array(WT_LOCALE, explode(',', $faq->languages))) { 315 echo '<div class="faq_title" id="faq', $id, '">', $faq->header; 316 echo '<div class="faq_top faq_italic">'; 317 echo '<a href="#content">', I18N::translate('back to top'), '</a>'; 318 echo '</div>'; 319 echo '</div>'; 320 echo '<div class="faq_body">', substr($faq->body, 0, 1) == '<' ? $faq->body : nl2br($faq->body, false), '</div>'; 321 echo '<hr>'; 322 } 323 } 324 } 325 326 /** 327 * Provide a form to manage the FAQs. 328 */ 329 private function config() { 330 $controller = new PageController; 331 $controller 332 ->setPageTitle(I18N::translate('Frequently asked questions')) 333 ->pageHeader(); 334 335 $faqs = Database::prepare( 336 "SELECT block_id, block_order, gedcom_id, bs1.setting_value AS header, bs2.setting_value AS faqbody" . 337 " FROM `##block` b" . 338 " JOIN `##block_setting` bs1 USING (block_id)" . 339 " JOIN `##block_setting` bs2 USING (block_id)" . 340 " WHERE module_name = :module_name" . 341 " AND bs1.setting_name = 'header'" . 342 " AND bs2.setting_name = 'faqbody'" . 343 " AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" . 344 " ORDER BY block_order" 345 )->execute(array( 346 'module_name' => $this->getName(), 347 'tree_id_1' => WT_GED_ID, 348 'tree_id_2' => WT_GED_ID, 349 ))->fetchAll(); 350 351 $min_block_order = Database::prepare( 352 "SELECT MIN(block_order) FROM `##block` WHERE module_name=?" 353 )->execute(array($this->getName()))->fetchOne(); 354 355 $max_block_order = Database::prepare( 356 "SELECT MAX(block_order) FROM `##block` WHERE module_name=?" 357 )->execute(array($this->getName()))->fetchOne(); 358 359 ?> 360 <ol class="breadcrumb small"> 361 <li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li> 362 <li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li> 363 <li class="active"><?php echo $controller->getPageTitle(); ?></li> 364 </ol> 365 <h2><?php echo $controller->getPageTitle(); ?></h2> 366 <p> 367 <?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.'); ?> 368 <?php echo I18N::translate('You may use HTML to format the answer and to add links to other websites.'); ?> 369 </p> 370 <?php 371 372 echo 373 '<p><form>', 374 I18N::translate('Family tree'), ' ', 375 '<input type="hidden" name="mod", value="', $this->getName(), '">', 376 '<input type="hidden" name="mod_action" value="admin_config">', 377 select_edit_control('ged', Tree::getNameList(), null, WT_GEDCOM), 378 '<input type="submit" value="', I18N::translate('show'), '">', 379 '</form></p>'; 380 381 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_edit">', I18N::translate('Add an FAQ item'), '</a>'; 382 383 echo '<table id="faq_edit">'; 384 if (empty($faqs)) { 385 echo '<tr><td class="error center" colspan="5">', I18N::translate('The FAQ list is empty.'), '</td></tr></table>'; 386 } else { 387 $trees = Tree::getAll(); 388 foreach ($faqs as $faq) { 389 // NOTE: Print the position of the current item 390 echo '<tr class="faq_edit_pos"><td>'; 391 echo I18N::translate('Position item'), ': ', ($faq->block_order + 1), ', '; 392 if ($faq->gedcom_id == null) { 393 echo I18N::translate('All'); 394 } else { 395 echo $trees[$faq->gedcom_id]->getTitleHtml(); 396 } 397 echo '</td>'; 398 // NOTE: Print the edit options of the current item 399 echo '<td>'; 400 if ($faq->block_order == $min_block_order) { 401 echo ' '; 402 } else { 403 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_moveup&block_id=', $faq->block_id, '" class="icon-uarrow"></a>'; 404 } 405 echo '</td><td>'; 406 if ($faq->block_order == $max_block_order) { 407 echo ' '; 408 } else { 409 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_movedown&block_id=', $faq->block_id, '" class="icon-darrow"></a>'; 410 } 411 echo '</td><td>'; 412 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_edit&block_id=', $faq->block_id, '">', I18N::translate('Edit'), '</a>'; 413 echo '</td><td>'; 414 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>'; 415 echo '</td></tr>'; 416 // NOTE: Print the title text of the current item 417 echo '<tr><td colspan="5">'; 418 echo '<div class="faq_edit_item">'; 419 echo '<div class="faq_edit_title">', $faq->header, '</div>'; 420 // NOTE: Print the body text of the current item 421 echo '<div class="faq_edit_content">', substr($faq->faqbody, 0, 1) == '<' ? $faq->faqbody : nl2br($faq->faqbody, false), '</div></div></td></tr>'; 422 } 423 echo '</table>'; 424 } 425 } 426 427 /** {@inheritdoc} */ 428 public function defaultMenuOrder() { 429 return 40; 430 } 431 432 /** {@inheritdoc} */ 433 public function getMenu() { 434 if (Auth::isSearchEngine()) { 435 return null; 436 } 437 438 $faqs = Database::prepare( 439 "SELECT block_id FROM `##block` WHERE module_name = :module_name AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" 440 )->execute(array( 441 'module_name' => $this->getName(), 442 'tree_id_1' => WT_GED_ID, 443 'tree_id_2' => WT_GED_ID, 444 ))->fetchAll(); 445 446 if (!$faqs) { 447 return null; 448 } 449 450 $menu = new Menu(I18N::translate('FAQ'), 'module.php?mod=faq&mod_action=show', 'menu-help'); 451 452 return $menu; 453 } 454} 455