18c2e8227SGreg Roach<?php 28c2e8227SGreg Roach/** 38c2e8227SGreg Roach * webtrees: online genealogy 41062a142SGreg Roach * Copyright (C) 2018 webtrees development team 58c2e8227SGreg Roach * This program is free software: you can redistribute it and/or modify 68c2e8227SGreg Roach * it under the terms of the GNU General Public License as published by 78c2e8227SGreg Roach * the Free Software Foundation, either version 3 of the License, or 88c2e8227SGreg Roach * (at your option) any later version. 98c2e8227SGreg Roach * This program is distributed in the hope that it will be useful, 108c2e8227SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 118c2e8227SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 128c2e8227SGreg Roach * GNU General Public License for more details. 138c2e8227SGreg Roach * You should have received a copy of the GNU General Public License 148c2e8227SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 158c2e8227SGreg Roach */ 1676692c8bSGreg Roachnamespace Fisharebest\Webtrees\Module; 1776692c8bSGreg Roach 180e62c4b8SGreg Roachuse Fisharebest\Webtrees\Auth; 190e62c4b8SGreg Roachuse Fisharebest\Webtrees\Database; 200e62c4b8SGreg Roachuse Fisharebest\Webtrees\I18N; 210e62c4b8SGreg Roachuse Fisharebest\Webtrees\Individual; 220e62c4b8SGreg Roachuse Fisharebest\Webtrees\Menu; 230e62c4b8SGreg Roachuse Fisharebest\Webtrees\Tree; 24225e381fSGreg Roachuse stdClass; 2572ac996dSGreg Roachuse Symfony\Component\HttpFoundation\RedirectResponse; 2672ac996dSGreg Roachuse Symfony\Component\HttpFoundation\Request; 2772ac996dSGreg Roachuse Symfony\Component\HttpFoundation\Response; 288c2e8227SGreg Roach 298c2e8227SGreg Roach/** 308c2e8227SGreg Roach * Class StoriesModule 318c2e8227SGreg Roach */ 32e2a378d3SGreg Roachclass StoriesModule extends AbstractModule implements ModuleTabInterface, ModuleConfigInterface, ModuleMenuInterface { 338c2e8227SGreg Roach /** {@inheritdoc} */ 348c2e8227SGreg Roach public function getTitle() { 3572ac996dSGreg Roach return /* I18N: Name of a module */ 3672ac996dSGreg Roach I18N::translate('Stories'); 378c2e8227SGreg Roach } 388c2e8227SGreg Roach 398c2e8227SGreg Roach /** {@inheritdoc} */ 408c2e8227SGreg Roach public function getDescription() { 4172ac996dSGreg Roach return /* I18N: Description of the “Stories” module */ 4272ac996dSGreg Roach I18N::translate('Add narrative stories to individuals in the family tree.'); 438c2e8227SGreg Roach } 448c2e8227SGreg Roach 45*aee13b6dSGreg Roach /** 46*aee13b6dSGreg Roach * The URL to a page where the user can modify the configuration of this module. 47*aee13b6dSGreg Roach * 48*aee13b6dSGreg Roach * @return string 49*aee13b6dSGreg Roach */ 508c2e8227SGreg Roach public function getConfigLink() { 5172ac996dSGreg Roach return route('module', ['module' => $this->getName(), 'action' => 'Admin']); 528c2e8227SGreg Roach } 538c2e8227SGreg Roach 548c2e8227SGreg Roach /** {@inheritdoc} */ 558c2e8227SGreg Roach public function defaultTabOrder() { 568c2e8227SGreg Roach return 55; 578c2e8227SGreg Roach } 588c2e8227SGreg Roach 598c2e8227SGreg Roach /** {@inheritdoc} */ 60225e381fSGreg Roach public function getTabContent(Individual $individual) { 6172ac996dSGreg Roach return view('modules/stories/tab', [ 6272ac996dSGreg Roach 'is_admin' => Auth::isAdmin(), 63225e381fSGreg Roach 'individual' => $individual, 64225e381fSGreg Roach 'stories' => $this->getStoriesForIndividual($individual), 65225e381fSGreg Roach ]); 668c2e8227SGreg Roach } 678c2e8227SGreg Roach 688c2e8227SGreg Roach /** {@inheritdoc} */ 69225e381fSGreg Roach public function hasTabContent(Individual $individual) { 70225e381fSGreg Roach return Auth::isManager($individual->getTree()) || !empty($this->getStoriesForIndividual($individual)); 718c2e8227SGreg Roach } 728c2e8227SGreg Roach 738c2e8227SGreg Roach /** {@inheritdoc} */ 74225e381fSGreg Roach public function isGrayedOut(Individual $individual) { 75225e381fSGreg Roach return !empty($this->getStoriesForIndividual($individual)); 768c2e8227SGreg Roach } 778c2e8227SGreg Roach 788c2e8227SGreg Roach /** {@inheritdoc} */ 798c2e8227SGreg Roach public function canLoadAjax() { 808c2e8227SGreg Roach return false; 818c2e8227SGreg Roach } 828c2e8227SGreg Roach 83225e381fSGreg Roach /** 84225e381fSGreg Roach * @param Individual $individual 85225e381fSGreg Roach * 86225e381fSGreg Roach * @return stdClass[] 87225e381fSGreg Roach */ 88225e381fSGreg Roach private function getStoriesForIndividual(Individual $individual): array { 89225e381fSGreg Roach $block_ids = 90225e381fSGreg Roach Database::prepare( 91225e381fSGreg Roach "SELECT SQL_CACHE block_id" . 92225e381fSGreg Roach " FROM `##block`" . 93225e381fSGreg Roach " WHERE module_name = :module_name" . 94225e381fSGreg Roach " AND xref = :xref" . 95225e381fSGreg Roach " AND gedcom_id = :tree_id" 96225e381fSGreg Roach )->execute([ 97225e381fSGreg Roach 'module_name' => $this->getName(), 98225e381fSGreg Roach 'xref' => $individual->getXref(), 99225e381fSGreg Roach 'tree_id' => $individual->getTree()->getTreeId(), 100225e381fSGreg Roach ])->fetchOneColumn(); 101225e381fSGreg Roach 102225e381fSGreg Roach $stories = []; 103225e381fSGreg Roach foreach ($block_ids as $block_id) { 104225e381fSGreg Roach // Only show this block for certain languages 105225e381fSGreg Roach $languages = $this->getBlockSetting($block_id, 'languages', ''); 106225e381fSGreg Roach if ($languages === '' || in_array(WT_LOCALE, explode(',', $languages))) { 107225e381fSGreg Roach $stories[] = (object) [ 108225e381fSGreg Roach 'block_id' => $block_id, 109225e381fSGreg Roach 'title' => $this->getBlockSetting($block_id, 'title'), 11072ac996dSGreg Roach 'story_body' => $this->getBlockSetting($block_id, 'story_body'), 111225e381fSGreg Roach ]; 112225e381fSGreg Roach } 113225e381fSGreg Roach } 114225e381fSGreg Roach 115225e381fSGreg Roach return $stories; 1168c2e8227SGreg Roach } 1178c2e8227SGreg Roach 1188c2e8227SGreg Roach /** 1190ee13198SGreg Roach * The user can re-order menus. Until they do, they are shown in this order. 1200ee13198SGreg Roach * 1210ee13198SGreg Roach * @return int 1220ee13198SGreg Roach */ 1238c2e8227SGreg Roach public function defaultMenuOrder() { 1248c2e8227SGreg Roach return 30; 1258c2e8227SGreg Roach } 1268c2e8227SGreg Roach 1270ee13198SGreg Roach /** 1280ee13198SGreg Roach * What is the default access level for this module? 1290ee13198SGreg Roach * 1300ee13198SGreg Roach * Some modules are aimed at admins or managers, and are not generally shown to users. 1310ee13198SGreg Roach * 1320ee13198SGreg Roach * @return int 1330ee13198SGreg Roach */ 1348c2e8227SGreg Roach public function defaultAccessLevel() { 1354b9ff166SGreg Roach return Auth::PRIV_HIDE; 1368c2e8227SGreg Roach } 1378c2e8227SGreg Roach 1380ee13198SGreg Roach /** 1390ee13198SGreg Roach * A menu, to be added to the main application menu. 1400ee13198SGreg Roach * 141*aee13b6dSGreg Roach * @param Tree $tree 142*aee13b6dSGreg Roach * 1430ee13198SGreg Roach * @return Menu|null 1440ee13198SGreg Roach */ 145*aee13b6dSGreg Roach public function getMenu(Tree $tree) { 14672ac996dSGreg Roach $menu = new Menu($this->getTitle(), e(route('module', ['module' => $this->getName(), 'action' => 'ShowList'])), 'menu-story'); 1478c2e8227SGreg Roach 1488c2e8227SGreg Roach return $menu; 1498c2e8227SGreg Roach } 15072ac996dSGreg Roach 15172ac996dSGreg Roach /** 15272ac996dSGreg Roach * @param Request $request 15372ac996dSGreg Roach * 15472ac996dSGreg Roach * @return Response 15572ac996dSGreg Roach */ 15672ac996dSGreg Roach public function getAdminAction(Request $request): Response { 15772ac996dSGreg Roach /** @var Tree $tree */ 15872ac996dSGreg Roach $tree = $request->attributes->get('tree'); 15972ac996dSGreg Roach 16072ac996dSGreg Roach $this->layout = 'layouts/administration'; 16172ac996dSGreg Roach 16272ac996dSGreg Roach $stories = Database::prepare( 16372ac996dSGreg Roach "SELECT block_id, xref, gedcom_id" . 16472ac996dSGreg Roach " FROM `##block` b" . 16572ac996dSGreg Roach " WHERE module_name = :module_name" . 16672ac996dSGreg Roach " AND gedcom_id = :tree_id" . 16772ac996dSGreg Roach " ORDER BY gedcom_id, xref" 16872ac996dSGreg Roach )->execute([ 16972ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 17072ac996dSGreg Roach 'module_name' => $this->getName(), 17172ac996dSGreg Roach ])->fetchAll(); 17272ac996dSGreg Roach 17372ac996dSGreg Roach foreach ($stories as $story) { 17472ac996dSGreg Roach $story->individual = Individual::getInstance($story->xref, $tree); 17572ac996dSGreg Roach $story->title = $this->getBlockSetting($story->block_id, 'title'); 17672ac996dSGreg Roach $story->languages = $this->getBlockSetting($story->block_id, 'languages'); 17772ac996dSGreg Roach } 17872ac996dSGreg Roach 17972ac996dSGreg Roach return $this->viewResponse('modules/stories/config', [ 18072ac996dSGreg Roach 'stories' => $stories, 18172ac996dSGreg Roach 'title' => $this->getTitle() . ' — ' . $tree->getTitle(), 18272ac996dSGreg Roach 'tree' => $tree, 18372ac996dSGreg Roach 'tree_names' => Tree::getNameList(), 18472ac996dSGreg Roach ]); 18572ac996dSGreg Roach } 18672ac996dSGreg Roach 18772ac996dSGreg Roach /** 18872ac996dSGreg Roach * @param Request $request 18972ac996dSGreg Roach * 19072ac996dSGreg Roach * @return Response 19172ac996dSGreg Roach */ 19272ac996dSGreg Roach public function getAdminEditAction(Request $request): Response { 19372ac996dSGreg Roach /** @var Tree $tree */ 19472ac996dSGreg Roach $tree = $request->attributes->get('tree'); 19572ac996dSGreg Roach 19672ac996dSGreg Roach $this->layout = 'layouts/administration'; 19772ac996dSGreg Roach 19872ac996dSGreg Roach $block_id = (int) $request->get('block_id'); 19972ac996dSGreg Roach 20072ac996dSGreg Roach if ($block_id === 0) { 20172ac996dSGreg Roach // Creating a new story 20272ac996dSGreg Roach $individual = Individual::getInstance($request->get('xref', ''), $tree); 20372ac996dSGreg Roach $story_title = ''; 20472ac996dSGreg Roach $story_body = ''; 20572ac996dSGreg Roach $languages = []; 20672ac996dSGreg Roach 20772ac996dSGreg Roach $title = I18N::translate('Add a story') . ' — ' . e($tree->getTitle()); 20872ac996dSGreg Roach } else { 20972ac996dSGreg Roach // Editing an existing story 21072ac996dSGreg Roach $xref = Database::prepare( 21172ac996dSGreg Roach "SELECT xref FROM `##block` WHERE block_id = :block_id" 21272ac996dSGreg Roach )->execute([ 21372ac996dSGreg Roach 'block_id' => $block_id, 21472ac996dSGreg Roach ])->fetchOne(); 21572ac996dSGreg Roach 21672ac996dSGreg Roach $individual = Individual::getInstance($xref, $tree); 21772ac996dSGreg Roach $story_title = $this->getBlockSetting($block_id, 'title', ''); 21872ac996dSGreg Roach $story_body = $this->getBlockSetting($block_id, 'story_body', ''); 21972ac996dSGreg Roach $languages = explode(',', $this->getBlockSetting($block_id, 'languages')); 22072ac996dSGreg Roach 22172ac996dSGreg Roach $title = I18N::translate('Edit the story') . ' — ' . e($tree->getTitle()); 22272ac996dSGreg Roach } 22372ac996dSGreg Roach 22472ac996dSGreg Roach return $this->viewResponse('modules/stories/edit', [ 22572ac996dSGreg Roach 'block_id' => $block_id, 22672ac996dSGreg Roach 'languages' => $languages, 22772ac996dSGreg Roach 'story_body' => $story_body, 22872ac996dSGreg Roach 'story_title' => $story_title, 22972ac996dSGreg Roach 'title' => $title, 23072ac996dSGreg Roach 'tree' => $tree, 23172ac996dSGreg Roach 'individual' => $individual, 23272ac996dSGreg Roach ]); 23372ac996dSGreg Roach } 23472ac996dSGreg Roach 23572ac996dSGreg Roach /** 23672ac996dSGreg Roach * @param Request $request 23772ac996dSGreg Roach * 23872ac996dSGreg Roach * @return RedirectResponse 23972ac996dSGreg Roach */ 24072ac996dSGreg Roach public function postAdminEditAction(Request $request): RedirectResponse { 24172ac996dSGreg Roach /** @var Tree $tree */ 24272ac996dSGreg Roach $tree = $request->attributes->get('tree'); 24372ac996dSGreg Roach 24472ac996dSGreg Roach $block_id = (int) $request->get('block_id'); 24572ac996dSGreg Roach $xref = $request->get('xref', ''); 24672ac996dSGreg Roach $story_body = $request->get('story_body', ''); 24772ac996dSGreg Roach $story_title = $request->get('story_title', ''); 24872ac996dSGreg Roach $languages = $request->get('languages', []); 24972ac996dSGreg Roach 25072ac996dSGreg Roach if ($block_id !== 0) { 25172ac996dSGreg Roach Database::prepare( 25272ac996dSGreg Roach "UPDATE `##block` SET gedcom_id = :tree_id, xref = :xref WHERE block_id = :block_id" 25372ac996dSGreg Roach )->execute([ 25472ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 25572ac996dSGreg Roach 'xref' => $xref, 25672ac996dSGreg Roach 'block_id' => $block_id, 25772ac996dSGreg Roach ]); 25872ac996dSGreg Roach } else { 25972ac996dSGreg Roach Database::prepare( 26072ac996dSGreg Roach "INSERT INTO `##block` (gedcom_id, xref, module_name, block_order) VALUES (:tree_id, :xref, 'stories', 0)" 26172ac996dSGreg Roach )->execute([ 26272ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 26372ac996dSGreg Roach 'xref' => $xref, 26472ac996dSGreg Roach ]); 26572ac996dSGreg Roach 26672ac996dSGreg Roach $block_id = Database::getInstance()->lastInsertId(); 26772ac996dSGreg Roach } 26872ac996dSGreg Roach 26972ac996dSGreg Roach $this->setBlockSetting($block_id, 'story_body', $story_body); 27072ac996dSGreg Roach $this->setBlockSetting($block_id, 'title', $story_title); 27172ac996dSGreg Roach $this->setBlockSetting($block_id, 'languages', implode(',', $languages)); 27272ac996dSGreg Roach 27372ac996dSGreg Roach $url = route('module', ['module' => 'stories', 'action' => 'Admin', 'ged' => $tree->getName()]); 27472ac996dSGreg Roach 27572ac996dSGreg Roach return new RedirectResponse($url); 27672ac996dSGreg Roach } 27772ac996dSGreg Roach 27872ac996dSGreg Roach /** 27972ac996dSGreg Roach * @param Request $request 28072ac996dSGreg Roach * 28172ac996dSGreg Roach * @return Response 28272ac996dSGreg Roach */ 28372ac996dSGreg Roach public function postAdminDeleteAction(Request $request): Response { 28472ac996dSGreg Roach /** @var Tree $tree */ 28572ac996dSGreg Roach $tree = $request->attributes->get('tree'); 28672ac996dSGreg Roach 28772ac996dSGreg Roach $block_id = (int) $request->get('block_id'); 28872ac996dSGreg Roach 28972ac996dSGreg Roach Database::prepare( 29072ac996dSGreg Roach "DELETE FROM `##block_setting` WHERE block_id = :block_id" 29172ac996dSGreg Roach )->execute([ 29272ac996dSGreg Roach 'block_id' => $block_id, 29372ac996dSGreg Roach ]); 29472ac996dSGreg Roach 29572ac996dSGreg Roach Database::prepare( 29672ac996dSGreg Roach "DELETE FROM `##block` WHERE block_id = :block_id" 29772ac996dSGreg Roach )->execute([ 29872ac996dSGreg Roach 'block_id' => $block_id, 29972ac996dSGreg Roach ]); 30072ac996dSGreg Roach 30172ac996dSGreg Roach $url = route('module', ['module' => 'stories', 'action' => 'Admin', 'ged' => $tree->getName()]); 30272ac996dSGreg Roach 30372ac996dSGreg Roach return new RedirectResponse($url); 30472ac996dSGreg Roach } 30572ac996dSGreg Roach 30672ac996dSGreg Roach /** 30772ac996dSGreg Roach * @param Request $request 30872ac996dSGreg Roach * 30972ac996dSGreg Roach * @return Response 31072ac996dSGreg Roach */ 31172ac996dSGreg Roach public function getShowListAction(Request $request): Response { 31272ac996dSGreg Roach /** @var Tree $tree 31372ac996dSGreg Roach */ 31472ac996dSGreg Roach $tree = $request->attributes->get('tree'); 31572ac996dSGreg Roach 31672ac996dSGreg Roach $stories = Database::prepare( 31772ac996dSGreg Roach "SELECT block_id, xref" . 31872ac996dSGreg Roach " FROM `##block` b" . 31972ac996dSGreg Roach " WHERE module_name = :module_name" . 32072ac996dSGreg Roach " AND gedcom_id = :tree_id" . 32172ac996dSGreg Roach " ORDER BY xref" 32272ac996dSGreg Roach )->execute([ 32372ac996dSGreg Roach 'module_name' => $this->getName(), 32472ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 32572ac996dSGreg Roach ])->fetchAll(); 32672ac996dSGreg Roach 32772ac996dSGreg Roach foreach ($stories as $story) { 32872ac996dSGreg Roach $story->individual = Individual::getInstance($story->xref, $tree); 32972ac996dSGreg Roach $story->title = $this->getBlockSetting($story->block_id, 'title'); 33072ac996dSGreg Roach $story->languages = $this->getBlockSetting($story->block_id, 'languages'); 33172ac996dSGreg Roach } 33272ac996dSGreg Roach 33372ac996dSGreg Roach // Filter non-existant and private individuals. 33472ac996dSGreg Roach $stories = array_filter($stories, function (stdClass $story) { 33572ac996dSGreg Roach return $story->individual !== null && $story->individual->canShow(); 33672ac996dSGreg Roach }); 33772ac996dSGreg Roach 33872ac996dSGreg Roach // Filter foreign languages. 33972ac996dSGreg Roach $stories = array_filter($stories, function (stdClass $story) { 34072ac996dSGreg Roach return $story->language === '' || in_array(WT_LOCALE, explode(',', $story->language)); 34172ac996dSGreg Roach }); 34272ac996dSGreg Roach 34372ac996dSGreg Roach return $this->viewResponse('modules/stories/list', [ 34472ac996dSGreg Roach 'stories' => $stories, 34572ac996dSGreg Roach 'title' => $this->getTitle(), 34672ac996dSGreg Roach ]); 34772ac996dSGreg Roach } 3488c2e8227SGreg Roach} 349