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 Module 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; 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 WT_GED_ID 79 ))->fetchOneColumn(); 80 81 $html = ''; 82 foreach ($block_ids as $block_id) { 83 // Only show this block for certain languages 84 $languages = get_block_setting($block_id, 'languages'); 85 if (!$languages || in_array(WT_LOCALE, explode(',', $languages))) { 86 $html .= '<div class="story_title descriptionbox center rela">' . get_block_setting($block_id, 'title') . '</div>'; 87 $html .= '<div class="story_body optionbox">' . get_block_setting($block_id, 'story_body') . '</div>'; 88 if (WT_USER_CAN_EDIT) { 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 (WT_USER_GEDCOM_ADMIN && !$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 WT_GED_ID 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 if (WT_USER_CAN_EDIT) { 143 if (Filter::postBool('save') && Filter::checkCsrf()) { 144 $block_id = Filter::postInteger('block_id'); 145 if ($block_id) { 146 Database::prepare( 147 "UPDATE `##block` SET gedcom_id=?, xref=? WHERE block_id=?" 148 )->execute(array(Filter::postInteger('gedcom_id'), Filter::post('xref', WT_REGEX_XREF), $block_id)); 149 } else { 150 Database::prepare( 151 "INSERT INTO `##block` (gedcom_id, xref, module_name, block_order) VALUES (?, ?, ?, ?)" 152 )->execute(array( 153 Filter::postInteger('gedcom_id'), 154 Filter::post('xref', WT_REGEX_XREF), 155 $this->getName(), 156 0 157 )); 158 $block_id = Database::getInstance()->lastInsertId(); 159 } 160 set_block_setting($block_id, 'title', Filter::post('title')); 161 set_block_setting($block_id, 'story_body', Filter::post('story_body')); 162 $languages = Filter::postArray('lang', null, array_keys(I18N::installed_languages())); 163 set_block_setting($block_id, 'languages', implode(',', $languages)); 164 $this->config(); 165 } else { 166 $block_id = Filter::getInteger('block_id'); 167 168 $controller = new PageController; 169 if ($block_id) { 170 $controller->setPageTitle(I18N::translate('Edit story')); 171 $title = get_block_setting($block_id, 'title'); 172 $story_body = get_block_setting($block_id, 'story_body'); 173 $xref = Database::prepare( 174 "SELECT xref FROM `##block` WHERE block_id=?" 175 )->execute(array($block_id))->fetchOne(); 176 } else { 177 $controller->setPageTitle(I18N::translate('Add a story')); 178 $title = ''; 179 $story_body = ''; 180 $xref = Filter::get('xref', WT_REGEX_XREF); 181 } 182 $controller 183 ->pageHeader() 184 ->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL) 185 ->addInlineJavascript('autocomplete();'); 186 if (Module::getModuleByName('ckeditor')) { 187 CkeditorModule::enableEditor($controller); 188 } 189 190 ?> 191 <ol class="breadcrumb small"> 192 <li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li> 193 <li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li> 194 <li><a href="module.php?mod=<?php echo $this->getName(); ?>&mod_action=admin_config"><?php echo $this->getTitle(); ?></a></li> 195 <li class="active"><?php echo $controller->getPageTitle(); ?></li> 196 </ol> 197 198 <h1><?php echo $controller->getPageTitle(); ?></h1> 199 <?php 200 201 echo '<form name="story" method="post" action="module.php?mod=', $this->getName(), '&mod_action=admin_edit">'; 202 echo Filter::getCsrf(); 203 echo '<input type="hidden" name="save" value="1">'; 204 echo '<input type="hidden" name="block_id" value="', $block_id, '">'; 205 echo '<input type="hidden" name="gedcom_id" value="', WT_GED_ID, '">'; 206 echo '<table id="story_module">'; 207 echo '<tr><th>'; 208 echo I18N::translate('Story title'); 209 echo '</th></tr><tr><td><textarea name="title" rows="1" cols="90" tabindex="2">', Filter::escapeHtml($title), '</textarea></td></tr>'; 210 echo '<tr><th>'; 211 echo I18N::translate('Story'); 212 echo '</th></tr><tr><td>'; 213 echo '<textarea name="story_body" class="html-edit" rows="10" cols="90" tabindex="2">', Filter::escapeHtml($story_body), '</textarea>'; 214 echo '</td></tr>'; 215 echo '</table><table id="story_module2">'; 216 echo '<tr>'; 217 echo '<th>', I18N::translate('Individual'), '</th>'; 218 echo '<th>', I18N::translate('Show this block for which languages?'), '</th>'; 219 echo '</tr>'; 220 echo '<tr>'; 221 echo '<td class="optionbox">'; 222 echo '<input data-autocomplete-type="INDI" type="text" name="xref" id="pid" size="4" value="' . $xref . '">'; 223 echo print_findindi_link('pid'); 224 if ($xref) { 225 $person = Individual::getInstance($xref); 226 if ($person) { 227 echo ' ', $person->format_list('span'); 228 } 229 } 230 echo '</td>'; 231 $languages = explode(',', get_block_setting($block_id, 'languages')); 232 echo '<td class="optionbox">'; 233 echo edit_language_checkboxes('lang', $languages); 234 echo '</td></tr></table>'; 235 echo '<p><input type="submit" value="', I18N::translate('save'), '" tabindex="5">'; 236 echo '</p>'; 237 echo '</form>'; 238 } 239 } else { 240 header('Location: ' . WT_BASE_URL); 241 } 242 } 243 244 /** 245 * Respond to a request to delete a story. 246 */ 247 private function delete() { 248 if (WT_USER_CAN_EDIT) { 249 $block_id = Filter::getInteger('block_id'); 250 251 Database::prepare( 252 "DELETE FROM `##block_setting` WHERE block_id=?" 253 )->execute(array($block_id)); 254 255 Database::prepare( 256 "DELETE FROM `##block` WHERE block_id=?" 257 )->execute(array($block_id)); 258 } else { 259 header('Location: ' . WT_BASE_URL); 260 exit; 261 } 262 } 263 264 /** 265 * The admin view - list, create, edit, delete stories. 266 */ 267 private function config() { 268 $controller = new PageController; 269 $controller 270 ->restrictAccess(WT_USER_GEDCOM_ADMIN) 271 ->setPageTitle($this->getTitle()) 272 ->pageHeader() 273 ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL) 274 ->addExternalJavascript(WT_DATATABLES_BOOTSTRAP_JS_URL) 275 ->addInlineJavascript(' 276 jQuery("#story_table").dataTable({ 277 ' . I18N::datatablesI18N() . ', 278 autoWidth: false, 279 paging: true, 280 pagingType: "full_numbers", 281 lengthChange: true, 282 filter: true, 283 info: true, 284 sorting: [[0,"asc"]], 285 columns: [ 286 /* 0-name */ null, 287 /* 1-NAME */ null, 288 /* 2-NAME */ { sortable:false }, 289 /* 3-NAME */ { sortable:false } 290 ] 291 }); 292 '); 293 294 $stories = Database::prepare( 295 "SELECT block_id, xref" . 296 " FROM `##block` b" . 297 " WHERE module_name=?" . 298 " AND gedcom_id=?" . 299 " ORDER BY xref" 300 )->execute(array($this->getName(), WT_GED_ID))->fetchAll(); 301 302 ?> 303 <ol class="breadcrumb small"> 304 <li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li> 305 <li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li> 306 <li class="active"><?php echo $controller->getPageTitle(); ?></li> 307 </ol> 308 309 <h1><?php echo $controller->getPageTitle(); ?></h1> 310 311 <form class="form form-inline"> 312 <label for="ged" class="sr-only"> 313 <?php echo I18N::translate('Family tree'); ?> 314 </label> 315 <input type="hidden" name="mod" value="<?php echo $this->getName(); ?>"> 316 <input type="hidden" name="mod_action" value="admin_config"> 317 <?php echo select_edit_control('ged', Tree::getNameList(), null, WT_GEDCOM, 'class="form-control"'); ?> 318 <input type="submit" class="btn btn-primary" value="<?php echo I18N::translate('show'); ?>"> 319 </form> 320 321 <p> 322 <a href="module.php?mod=<?php echo $this->getName(); ?>&mod_action=admin_edit" class="btn btn-default"> 323 <i class="fa fa-plus"></i> 324 <?php echo I18N::translate('Add a story'); ?> 325 </a> 326 </p> 327 328 <table class="table table-bordered table-condensed"> 329 <thead> 330 <tr> 331 <th><?php echo I18N::translate('Story title'); ?></th> 332 <th><?php echo I18N::translate('Individual'); ?></th> 333 <th><?php echo I18N::translate('Edit'); ?></th> 334 <th><?php echo I18N::translate('Delete'); ?></th> 335 </tr> 336 </thead> 337 <tbody> 338 <?php foreach ($stories as $story): ?> 339 <tr> 340 <td> 341 <?php echo Filter::escapeHtml(get_block_setting($story->block_id, 'title')); ?> 342 </td> 343 <td> 344 <?php if ($indi = Individual::getInstance($story->xref)): ?> 345 <a href="<?php echo $indi->getHtmlUrl(); ?>#stories"> 346 <?php echo $indi->getFullName(); ?> 347 </a> 348 <?php else: ?> 349 <?php echo $story->xref; ?> 350 <?php endif; ?> 351 </td> 352 <td> 353 <a href="module.php?mod=<?php echo $this->getName(); ?>&mod_action=admin_edit&block_id=<?php echo $story->block_id; ?>"> 354 <div class="icon-edit"> </div> 355 </a> 356 </td> 357 <td> 358 <a 359 href="module.php?mod=<?php echo $this->getName(); ?>&mod_action=admin_delete&block_id=<?php echo $story->block_id; ?>" 360 onclick="return confirm('<?php echo I18N::translate('Are you sure you want to delete this story?'); ?>');" 361 > 362 <div class="icon-delete"> </div> 363 </a> 364 </td> 365 </tr> 366 <?php endforeach; ?> 367 </tbody> 368 </table> 369 <?php 370 } 371 372 /** 373 * Show the list of stories 374 */ 375 private function showList() { 376 global $controller; 377 378 $controller = new PageController; 379 $controller 380 ->setPageTitle($this->getTitle()) 381 ->pageHeader() 382 ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL) 383 ->addInlineJavascript(' 384 jQuery("#story_table").dataTable({ 385 dom: \'<"H"pf<"dt-clear">irl>t<"F"pl>\', 386 ' . I18N::datatablesI18N() . ', 387 autoWidth: false, 388 paging: true, 389 pagingType: "full_numbers", 390 lengthChange: true, 391 filter: true, 392 info: true, 393 jQueryUI: true, 394 sorting: [[0,"asc"]], 395 columns: [ 396 /* 0-name */ null, 397 /* 1-NAME */ null 398 ] 399 }); 400 '); 401 402 $stories = Database::prepare( 403 "SELECT block_id, xref" . 404 " FROM `##block` b" . 405 " WHERE module_name=?" . 406 " AND gedcom_id=?" . 407 " ORDER BY xref" 408 )->execute(array($this->getName(), WT_GED_ID))->fetchAll(); 409 410 echo '<h2 class="center">', I18N::translate('Stories'), '</h2>'; 411 if (count($stories) > 0) { 412 echo '<table id="story_table" class="width100">'; 413 echo '<thead><tr> 414 <th>', I18N::translate('Story title'), '</th> 415 <th>', I18N::translate('Individual'), '</th> 416 </tr></thead> 417 <tbody>'; 418 foreach ($stories as $story) { 419 $indi = Individual::getInstance($story->xref); 420 $story_title = get_block_setting($story->block_id, 'title'); 421 $languages = get_block_setting($story->block_id, 'languages'); 422 if (!$languages || in_array(WT_LOCALE, explode(',', $languages))) { 423 if ($indi) { 424 if ($indi->canShow()) { 425 echo '<tr><td><a href="' . $indi->getHtmlUrl() . '#stories">' . $story_title . '</a></td><td><a href="' . $indi->getHtmlUrl() . '#stories">' . $indi->getFullName() . '</a></td></tr>'; 426 } 427 } else { 428 echo '<tr><td>', $story_title, '</td><td class="error">', $story->xref, '</td></tr>'; 429 } 430 } 431 } 432 echo '</tbody></table>'; 433 } 434 } 435 436 /** {@inheritdoc} */ 437 public function defaultMenuOrder() { 438 return 30; 439 } 440 441 /** {@inheritdoc} */ 442 public function defaultAccessLevel() { 443 return WT_PRIV_HIDE; 444 } 445 446 /** {@inheritdoc} */ 447 public function getMenu() { 448 if (Auth::isSearchEngine()) { 449 return null; 450 } 451 452 $menu = new Menu($this->getTitle(), 'module.php?mod=' . $this->getName() . '&mod_action=show_list', 'menu-story'); 453 454 return $menu; 455 } 456} 457