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 StoriesModule 21 */ 22class StoriesModule extends AbstractModule implements ModuleTabInterface, ModuleConfigInterface, ModuleMenuInterface { 23 /** {@inheritdoc} */ 24 public function getTitle() { 25 return /* I18N: Name of a module */ I18N::translate('Stories'); 26 } 27 28 /** {@inheritdoc} */ 29 public function getDescription() { 30 return /* I18N: Description of the “Stories” module */ I18N::translate('Add narrative stories to individuals in the family tree.'); 31 } 32 33 /** {@inheritdoc} */ 34 public function modAction($mod_action) { 35 switch ($mod_action) { 36 case 'admin_edit': 37 $this->edit(); 38 break; 39 case 'admin_delete': 40 $this->delete(); 41 $this->config(); 42 break; 43 case 'admin_config': 44 $this->config(); 45 break; 46 case 'show_list': 47 $this->showList(); 48 break; 49 default: 50 http_response_code(404); 51 } 52 } 53 54 /** {@inheritdoc} */ 55 public function getConfigLink() { 56 return 'module.php?mod=' . $this->getName() . '&mod_action=admin_config'; 57 } 58 59 /** {@inheritdoc} */ 60 public function defaultTabOrder() { 61 return 55; 62 } 63 64 /** {@inheritdoc} */ 65 public function getTabContent() { 66 global $controller, $WT_TREE; 67 68 $block_ids = 69 Database::prepare( 70 "SELECT block_id" . 71 " FROM `##block`" . 72 " WHERE module_name=?" . 73 " AND xref=?" . 74 " AND gedcom_id=?" 75 )->execute(array( 76 $this->getName(), 77 $controller->record->getXref(), 78 $controller->record->getTree()->getTreeId(), 79 ))->fetchOneColumn(); 80 81 $html = ''; 82 foreach ($block_ids as $block_id) { 83 // Only show this block for certain languages 84 $languages = $this->getBlockSetting($block_id, 'languages'); 85 if (!$languages || in_array(WT_LOCALE, explode(',', $languages))) { 86 $html .= '<div class="story_title descriptionbox center rela">' . $this->getBlockSetting($block_id, 'title') . '</div>'; 87 $html .= '<div class="story_body optionbox">' . $this->getBlockSetting($block_id, 'story_body') . '</div>'; 88 if (Auth::isEditor($WT_TREE)) { 89 $html .= '<div class="story_edit"><a href="module.php?mod=' . $this->getName() . '&mod_action=admin_edit&block_id=' . $block_id . '">'; 90 $html .= I18N::translate('Edit story') . '</a></div>'; 91 } 92 } 93 } 94 if (Auth::isManager($WT_TREE) && !$html) { 95 $html .= '<div class="news_title center">' . $this->getTitle() . '</div>'; 96 $html .= '<div><a href="module.php?mod=' . $this->getName() . '&mod_action=admin_edit&xref=' . $controller->record->getXref() . '">'; 97 $html .= I18N::translate('Add a story') . '</a></div><br>'; 98 } 99 100 return $html; 101 } 102 103 /** {@inheritdoc} */ 104 public function hasTabContent() { 105 return $this->getTabContent() != ''; 106 } 107 108 /** {@inheritdoc} */ 109 public function isGrayedOut() { 110 global $controller; 111 112 $count_of_stories = 113 Database::prepare( 114 "SELECT COUNT(block_id)" . 115 " FROM `##block`" . 116 " WHERE module_name=?" . 117 " AND xref=?" . 118 " AND gedcom_id=?" 119 )->execute(array( 120 $this->getName(), 121 $controller->record->getXref(), 122 $controller->record->getTree()->getTreeId(), 123 ))->fetchOne(); 124 125 return $count_of_stories == 0; 126 } 127 128 /** {@inheritdoc} */ 129 public function canLoadAjax() { 130 return false; 131 } 132 133 /** {@inheritdoc} */ 134 public function getPreLoadContent() { 135 return ''; 136 } 137 138 /** 139 * Show and process a form to edit a story. 140 */ 141 private function edit() { 142 global $WT_TREE; 143 144 if (Auth::isEditor($WT_TREE)) { 145 if (Filter::postBool('save') && Filter::checkCsrf()) { 146 $block_id = Filter::postInteger('block_id'); 147 if ($block_id) { 148 Database::prepare( 149 "UPDATE `##block` SET gedcom_id=?, xref=? WHERE block_id=?" 150 )->execute(array(Filter::postInteger('gedcom_id'), Filter::post('xref', WT_REGEX_XREF), $block_id)); 151 } else { 152 Database::prepare( 153 "INSERT INTO `##block` (gedcom_id, xref, module_name, block_order) VALUES (?, ?, ?, ?)" 154 )->execute(array( 155 Filter::postInteger('gedcom_id'), 156 Filter::post('xref', WT_REGEX_XREF), 157 $this->getName(), 158 0, 159 )); 160 $block_id = Database::getInstance()->lastInsertId(); 161 } 162 $this->setBlockSetting($block_id, 'title', Filter::post('title')); 163 $this->setBlockSetting($block_id, 'story_body', Filter::post('story_body')); 164 $languages = Filter::postArray('lang'); 165 $this->setBlockSetting($block_id, 'languages', implode(',', $languages)); 166 $this->config(); 167 } else { 168 $block_id = Filter::getInteger('block_id'); 169 170 $controller = new PageController; 171 if ($block_id) { 172 $controller->setPageTitle(I18N::translate('Edit story')); 173 $title = $this->getBlockSetting($block_id, 'title'); 174 $story_body = $this->getBlockSetting($block_id, 'story_body'); 175 $xref = Database::prepare( 176 "SELECT xref FROM `##block` WHERE block_id=?" 177 )->execute(array($block_id))->fetchOne(); 178 } else { 179 $controller->setPageTitle(I18N::translate('Add a story')); 180 $title = ''; 181 $story_body = ''; 182 $xref = Filter::get('xref', WT_REGEX_XREF); 183 } 184 $controller 185 ->pageHeader() 186 ->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL) 187 ->addInlineJavascript('autocomplete();'); 188 if (Module::getModuleByName('ckeditor')) { 189 CkeditorModule::enableEditor($controller); 190 } 191 192 $individual = Individual::getInstance($xref, $WT_TREE); 193 194 ?> 195 <ol class="breadcrumb small"> 196 <li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li> 197 <li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li> 198 <li><a href="module.php?mod=<?php echo $this->getName(); ?>&mod_action=admin_config"><?php echo $this->getTitle(); ?></a></li> 199 <li class="active"><?php echo $controller->getPageTitle(); ?></li> 200 </ol> 201 202 <h1><?php echo $controller->getPageTitle(); ?></h1> 203 204 <form class="form-horizontal" method="post" action="module.php?mod=<?php echo $this->getName(); ?>&mod_action=admin_edit"> 205 <?php echo Filter::getCsrf(); ?> 206 <input type="hidden" name="save" value="1"> 207 <input type="hidden" name="block_id" value="<?php echo $block_id; ?>"> 208 <input type="hidden" name="gedcom_id" value="<?php echo $WT_TREE->getTreeId(); ?>"> 209 210 <div class="form-group"> 211 <label for="title" class="col-sm-3 control-label"> 212 <?php echo I18N::translate('Story title'); ?> 213 </label> 214 <div class="col-sm-9"> 215 <input type="text" class="form-control" name="title" id="title" value="<?php echo Filter::escapeHtml($title); ?>"> 216 </div> 217 </div> 218 219 <div class="form-group"> 220 <label for="story_body" class="col-sm-3 control-label"> 221 <?php echo I18N::translate('Story'); ?> 222 </label> 223 <div class="col-sm-9"> 224 <textarea name="story_body" id="story_body" class="html-edit form-control" rows="10"><?php echo Filter::escapeHtml($story_body); ?></textarea> 225 </div> 226 </div> 227 228 <div class="form-group"> 229 <label for="xref" class="col-sm-3 control-label"> 230 <?php echo I18N::translate('Individual'); ?> 231 </label> 232 <div class="col-sm-9"> 233 <input data-autocomplete-type="INDI" type="text" name="xref" id="xref" size="4" value="<?php echo $xref; ?>"> 234 <?php echo print_findindi_link('xref'); ?> 235 <?php if ($individual): ?> 236 <?php echo $individual->formatList('span'); ?> 237 <?php endif; ?> 238 </div> 239 </div> 240 241 <div class="form-group"> 242 <label for="xref" class="col-sm-3 control-label"> 243 <?php echo I18N::translate('Show this block for which languages?'); ?> 244 </label> 245 <div class="col-sm-9"> 246 <?php echo edit_language_checkboxes('lang', explode(',', $this->getBlockSetting($block_id, 'languages'))); ?> 247 </div> 248 </div> 249 250 <div class="form-group"> 251 <div class="col-sm-offset-3 col-sm-9"> 252 <button type="submit" class="btn btn-primary"> 253 <i class="fa fa-check"></i> 254 <?php echo I18N::translate('save'); ?> 255 </button> 256 </div> 257 </div> 258 259 </form> 260 <?php 261 } 262 } else { 263 header('Location: ' . WT_BASE_URL); 264 } 265 } 266 267 /** 268 * Respond to a request to delete a story. 269 */ 270 private function delete() { 271 global $WT_TREE; 272 273 if (Auth::isEditor($WT_TREE)) { 274 $block_id = Filter::getInteger('block_id'); 275 276 Database::prepare( 277 "DELETE FROM `##block_setting` WHERE block_id=?" 278 )->execute(array($block_id)); 279 280 Database::prepare( 281 "DELETE FROM `##block` WHERE block_id=?" 282 )->execute(array($block_id)); 283 } else { 284 header('Location: ' . WT_BASE_URL); 285 exit; 286 } 287 } 288 289 /** 290 * The admin view - list, create, edit, delete stories. 291 */ 292 private function config() { 293 global $WT_TREE; 294 295 $controller = new PageController; 296 $controller 297 ->restrictAccess(Auth::isAdmin()) 298 ->setPageTitle($this->getTitle()) 299 ->pageHeader() 300 ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL) 301 ->addExternalJavascript(WT_DATATABLES_BOOTSTRAP_JS_URL) 302 ->addInlineJavascript(' 303 jQuery("#story_table").dataTable({ 304 ' . I18N::datatablesI18N() . ', 305 autoWidth: false, 306 paging: true, 307 pagingType: "full_numbers", 308 lengthChange: true, 309 filter: true, 310 info: true, 311 sorting: [[0,"asc"]], 312 columns: [ 313 /* 0-name */ null, 314 /* 1-NAME */ null, 315 /* 2-NAME */ { sortable:false }, 316 /* 3-NAME */ { sortable:false } 317 ] 318 }); 319 '); 320 321 $stories = Database::prepare( 322 "SELECT block_id, xref" . 323 " FROM `##block` b" . 324 " WHERE module_name=?" . 325 " AND gedcom_id=?" . 326 " ORDER BY xref" 327 )->execute(array($this->getName(), $WT_TREE->getTreeId()))->fetchAll(); 328 329 ?> 330 <ol class="breadcrumb small"> 331 <li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li> 332 <li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li> 333 <li class="active"><?php echo $controller->getPageTitle(); ?></li> 334 </ol> 335 336 <h1><?php echo $controller->getPageTitle(); ?></h1> 337 338 <form class="form form-inline"> 339 <label for="ged" class="sr-only"> 340 <?php echo I18N::translate('Family tree'); ?> 341 </label> 342 <input type="hidden" name="mod" value="<?php echo $this->getName(); ?>"> 343 <input type="hidden" name="mod_action" value="admin_config"> 344 <?php echo select_edit_control('ged', Tree::getNameList(), null, $WT_TREE->getName(), 'class="form-control"'); ?> 345 <input type="submit" class="btn btn-primary" value="<?php echo I18N::translate('show'); ?>"> 346 </form> 347 348 <p> 349 <a href="module.php?mod=<?php echo $this->getName(); ?>&mod_action=admin_edit" class="btn btn-default"> 350 <i class="fa fa-plus"></i> 351 <?php echo I18N::translate('Add a story'); ?> 352 </a> 353 </p> 354 355 <table class="table table-bordered table-condensed"> 356 <thead> 357 <tr> 358 <th><?php echo I18N::translate('Story title'); ?></th> 359 <th><?php echo I18N::translate('Individual'); ?></th> 360 <th><?php echo I18N::translate('Edit'); ?></th> 361 <th><?php echo I18N::translate('Delete'); ?></th> 362 </tr> 363 </thead> 364 <tbody> 365 <?php foreach ($stories as $story): ?> 366 <tr> 367 <td> 368 <?php echo Filter::escapeHtml($this->getBlockSetting($story->block_id, 'title')); ?> 369 </td> 370 <td> 371 <?php $individual = Individual::getInstance($story->xref, $WT_TREE); ?> 372 <?php if ($individual): ?> 373 <a href="<?php echo $individual->getHtmlUrl(); ?>#stories"> 374 <?php echo $individual->getFullName(); ?> 375 </a> 376 <?php else: ?> 377 <?php echo $story->xref; ?> 378 <?php endif; ?> 379 </td> 380 <td> 381 <a href="module.php?mod=<?php echo $this->getName(); ?>&mod_action=admin_edit&block_id=<?php echo $story->block_id; ?>"> 382 <div class="icon-edit"> </div> 383 </a> 384 </td> 385 <td> 386 <a 387 href="module.php?mod=<?php echo $this->getName(); ?>&mod_action=admin_delete&block_id=<?php echo $story->block_id; ?>" 388 onclick="return confirm('<?php echo I18N::translate('Are you sure you want to delete this story?'); ?>');" 389 > 390 <div class="icon-delete"> </div> 391 </a> 392 </td> 393 </tr> 394 <?php endforeach; ?> 395 </tbody> 396 </table> 397 <?php 398 } 399 400 /** 401 * Show the list of stories 402 */ 403 private function showList() { 404 global $controller, $WT_TREE; 405 406 $controller = new PageController; 407 $controller 408 ->setPageTitle($this->getTitle()) 409 ->pageHeader() 410 ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL) 411 ->addInlineJavascript(' 412 jQuery("#story_table").dataTable({ 413 dom: \'<"H"pf<"dt-clear">irl>t<"F"pl>\', 414 ' . I18N::datatablesI18N() . ', 415 autoWidth: false, 416 paging: true, 417 pagingType: "full_numbers", 418 lengthChange: true, 419 filter: true, 420 info: true, 421 jQueryUI: true, 422 sorting: [[0,"asc"]], 423 columns: [ 424 /* 0-name */ null, 425 /* 1-NAME */ null 426 ] 427 }); 428 '); 429 430 $stories = Database::prepare( 431 "SELECT block_id, xref" . 432 " FROM `##block` b" . 433 " WHERE module_name=?" . 434 " AND gedcom_id=?" . 435 " ORDER BY xref" 436 )->execute(array($this->getName(), $WT_TREE->getTreeId()))->fetchAll(); 437 438 echo '<h2 class="center">', I18N::translate('Stories'), '</h2>'; 439 if (count($stories) > 0) { 440 echo '<table id="story_table" class="width100">'; 441 echo '<thead><tr> 442 <th>', I18N::translate('Story title'), '</th> 443 <th>', I18N::translate('Individual'), '</th> 444 </tr></thead> 445 <tbody>'; 446 foreach ($stories as $story) { 447 $indi = Individual::getInstance($story->xref, $WT_TREE); 448 $story_title = $this->getBlockSetting($story->block_id, 'title'); 449 $languages = $this->getBlockSetting($story->block_id, 'languages'); 450 if (!$languages || in_array(WT_LOCALE, explode(',', $languages))) { 451 if ($indi) { 452 if ($indi->canShow()) { 453 echo '<tr><td><a href="' . $indi->getHtmlUrl() . '#stories">' . $story_title . '</a></td><td><a href="' . $indi->getHtmlUrl() . '#stories">' . $indi->getFullName() . '</a></td></tr>'; 454 } 455 } else { 456 echo '<tr><td>', $story_title, '</td><td class="error">', $story->xref, '</td></tr>'; 457 } 458 } 459 } 460 echo '</tbody></table>'; 461 } 462 } 463 464 /** {@inheritdoc} */ 465 public function defaultMenuOrder() { 466 return 30; 467 } 468 469 /** {@inheritdoc} */ 470 public function defaultAccessLevel() { 471 return Auth::PRIV_HIDE; 472 } 473 474 /** {@inheritdoc} */ 475 public function getMenu() { 476 if (Auth::isSearchEngine()) { 477 return null; 478 } 479 480 $menu = new Menu($this->getTitle(), 'module.php?mod=' . $this->getName() . '&mod_action=show_list', 'menu-story'); 481 482 return $menu; 483 } 484} 485