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; 266 $controller = new PageController; 267 $controller 268 ->setPageTitle(I18N::translate('Frequently asked questions')) 269 ->pageHeader(); 270 271 $faqs = Database::prepare( 272 "SELECT block_id, bs1.setting_value AS header, bs2.setting_value AS body, bs3.setting_value AS languages" . 273 " FROM `##block` b" . 274 " JOIN `##block_setting` bs1 USING (block_id)" . 275 " JOIN `##block_setting` bs2 USING (block_id)" . 276 " JOIN `##block_setting` bs3 USING (block_id)" . 277 " WHERE module_name = :module_name" . 278 " AND bs1.setting_name = 'header'" . 279 " AND bs2.setting_name = 'faqbody'" . 280 " AND bs3.setting_name = 'languages'" . 281 " AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" . 282 " ORDER BY block_order" 283 )->execute(array( 284 'module_name' => $this->getName(), 285 'tree_id_1' => WT_GED_ID, 286 'tree_id_2' => WT_GED_ID, 287 ))->fetchAll(); 288 289 // Define your colors for the alternating rows 290 echo '<h2 class="center">', I18N::translate('Frequently asked questions'), '</h2>'; 291 // Instructions 292 echo '<div class="faq_italic">', I18N::translate('Click on a title to go straight to it, or scroll down to read them all'); 293 if (WT_USER_GEDCOM_ADMIN) { 294 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>'; 295 } 296 echo '</div>'; 297 $row_count = 0; 298 echo '<table class="faq">'; 299 // List of titles 300 foreach ($faqs as $id => $faq) { 301 if (!$faq->languages || in_array(WT_LOCALE, explode(',', $faq->languages))) { 302 $row_color = ($row_count % 2) ? 'odd' : 'even'; 303 // NOTE: Print the header of the current item 304 echo '<tr class="', $row_color, '"><td style="padding: 5px;">'; 305 echo '<a href="#faq', $id, '">', $faq->header, '</a>'; 306 echo '</td></tr>'; 307 $row_count++; 308 } 309 } 310 echo '</table><hr>'; 311 // Detailed entries 312 foreach ($faqs as $id => $faq) { 313 if (!$faq->languages || in_array(WT_LOCALE, explode(',', $faq->languages))) { 314 echo '<div class="faq_title" id="faq', $id, '">', $faq->header; 315 echo '<div class="faq_top faq_italic">'; 316 echo '<a href="#content">', I18N::translate('back to top'), '</a>'; 317 echo '</div>'; 318 echo '</div>'; 319 echo '<div class="faq_body">', substr($faq->body, 0, 1) == '<' ? $faq->body : nl2br($faq->body, false), '</div>'; 320 echo '<hr>'; 321 } 322 } 323 } 324 325 /** 326 * Provide a form to manage the FAQs. 327 */ 328 private function config() { 329 $controller = new PageController; 330 $controller 331 ->setPageTitle(I18N::translate('Frequently asked questions')) 332 ->pageHeader(); 333 334 $faqs = Database::prepare( 335 "SELECT block_id, block_order, gedcom_id, bs1.setting_value AS header, bs2.setting_value AS faqbody" . 336 " FROM `##block` b" . 337 " JOIN `##block_setting` bs1 USING (block_id)" . 338 " JOIN `##block_setting` bs2 USING (block_id)" . 339 " WHERE module_name = :module_name" . 340 " AND bs1.setting_name = 'header'" . 341 " AND bs2.setting_name = 'faqbody'" . 342 " AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" . 343 " ORDER BY block_order" 344 )->execute(array( 345 'module_name' => $this->getName(), 346 'tree_id_1' => WT_GED_ID, 347 'tree_id_2' => WT_GED_ID, 348 ))->fetchAll(); 349 350 $min_block_order = Database::prepare( 351 "SELECT MIN(block_order) FROM `##block` WHERE module_name=?" 352 )->execute(array($this->getName()))->fetchOne(); 353 354 $max_block_order = Database::prepare( 355 "SELECT MAX(block_order) FROM `##block` WHERE module_name=?" 356 )->execute(array($this->getName()))->fetchOne(); 357 358 ?> 359 <ol class="breadcrumb small"> 360 <li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li> 361 <li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li> 362 <li class="active"><?php echo $controller->getPageTitle(); ?></li> 363 </ol> 364 <h2><?php echo $controller->getPageTitle(); ?></h2> 365 <p> 366 <?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.'); ?> 367 <?php echo I18N::translate('You may use HTML to format the answer and to add links to other websites.'); ?> 368 </p> 369 <?php 370 371 echo 372 '<p><form>', 373 I18N::translate('Family tree'), ' ', 374 '<input type="hidden" name="mod", value="', $this->getName(), '">', 375 '<input type="hidden" name="mod_action" value="admin_config">', 376 select_edit_control('ged', Tree::getNameList(), null, WT_GEDCOM), 377 '<input type="submit" value="', I18N::translate('show'), '">', 378 '</form></p>'; 379 380 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_edit">', I18N::translate('Add an FAQ item'), '</a>'; 381 382 echo '<table id="faq_edit">'; 383 if (empty($faqs)) { 384 echo '<tr><td class="error center" colspan="5">', I18N::translate('The FAQ list is empty.'), '</td></tr></table>'; 385 } else { 386 $trees = Tree::getAll(); 387 foreach ($faqs as $faq) { 388 // NOTE: Print the position of the current item 389 echo '<tr class="faq_edit_pos"><td>'; 390 echo I18N::translate('Position item'), ': ', ($faq->block_order + 1), ', '; 391 if ($faq->gedcom_id == null) { 392 echo I18N::translate('All'); 393 } else { 394 echo $trees[$faq->gedcom_id]->getTitleHtml(); 395 } 396 echo '</td>'; 397 // NOTE: Print the edit options of the current item 398 echo '<td>'; 399 if ($faq->block_order == $min_block_order) { 400 echo ' '; 401 } else { 402 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_moveup&block_id=', $faq->block_id, '" class="icon-uarrow"></a>'; 403 } 404 echo '</td><td>'; 405 if ($faq->block_order == $max_block_order) { 406 echo ' '; 407 } else { 408 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_movedown&block_id=', $faq->block_id, '" class="icon-darrow"></a>'; 409 } 410 echo '</td><td>'; 411 echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_edit&block_id=', $faq->block_id, '">', I18N::translate('Edit'), '</a>'; 412 echo '</td><td>'; 413 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>'; 414 echo '</td></tr>'; 415 // NOTE: Print the title text of the current item 416 echo '<tr><td colspan="5">'; 417 echo '<div class="faq_edit_item">'; 418 echo '<div class="faq_edit_title">', $faq->header, '</div>'; 419 // NOTE: Print the body text of the current item 420 echo '<div class="faq_edit_content">', substr($faq->faqbody, 0, 1) == '<' ? $faq->faqbody : nl2br($faq->faqbody, false), '</div></div></td></tr>'; 421 } 422 echo '</table>'; 423 } 424 } 425 426 /** {@inheritdoc} */ 427 public function defaultMenuOrder() { 428 return 40; 429 } 430 431 /** {@inheritdoc} */ 432 public function getMenu() { 433 if (Auth::isSearchEngine()) { 434 return null; 435 } 436 437 $faqs = Database::prepare( 438 "SELECT block_id FROM `##block` WHERE module_name = :module_name AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" 439 )->execute(array( 440 'module_name' => $this->getName(), 441 'tree_id_1' => WT_GED_ID, 442 'tree_id_2' => WT_GED_ID, 443 ))->fetchAll(); 444 445 if (!$faqs) { 446 return null; 447 } 448 449 $menu = new Menu(I18N::translate('FAQ'), 'module.php?mod=faq&mod_action=show', 'menu-help'); 450 451 return $menu; 452 } 453} 454