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 */ 32c1010edaSGreg Roachclass StoriesModule extends AbstractModule implements ModuleTabInterface, ModuleConfigInterface, ModuleMenuInterface 33c1010edaSGreg Roach{ 348c2e8227SGreg Roach /** {@inheritdoc} */ 35c1010edaSGreg Roach public function getTitle() 36c1010edaSGreg Roach { 3772ac996dSGreg Roach return /* I18N: Name of a module */ 3872ac996dSGreg Roach I18N::translate('Stories'); 398c2e8227SGreg Roach } 408c2e8227SGreg Roach 418c2e8227SGreg Roach /** {@inheritdoc} */ 42c1010edaSGreg Roach public function getDescription() 43c1010edaSGreg Roach { 4472ac996dSGreg Roach return /* I18N: Description of the “Stories” module */ 4572ac996dSGreg Roach I18N::translate('Add narrative stories to individuals in the family tree.'); 468c2e8227SGreg Roach } 478c2e8227SGreg Roach 48aee13b6dSGreg Roach /** 49aee13b6dSGreg Roach * The URL to a page where the user can modify the configuration of this module. 50aee13b6dSGreg Roach * 51aee13b6dSGreg Roach * @return string 52aee13b6dSGreg Roach */ 53c1010edaSGreg Roach public function getConfigLink() 54c1010edaSGreg Roach { 55c1010edaSGreg Roach return route('module', [ 56c1010edaSGreg Roach 'module' => $this->getName(), 57c1010edaSGreg Roach 'action' => 'Admin', 58c1010edaSGreg Roach ]); 598c2e8227SGreg Roach } 608c2e8227SGreg Roach 618c2e8227SGreg Roach /** {@inheritdoc} */ 62c1010edaSGreg Roach public function defaultTabOrder() 63c1010edaSGreg Roach { 648c2e8227SGreg Roach return 55; 658c2e8227SGreg Roach } 668c2e8227SGreg Roach 678c2e8227SGreg Roach /** {@inheritdoc} */ 68c1010edaSGreg Roach public function getTabContent(Individual $individual) 69c1010edaSGreg Roach { 7072ac996dSGreg Roach return view('modules/stories/tab', [ 7172ac996dSGreg Roach 'is_admin' => Auth::isAdmin(), 72225e381fSGreg Roach 'individual' => $individual, 73225e381fSGreg Roach 'stories' => $this->getStoriesForIndividual($individual), 74225e381fSGreg Roach ]); 758c2e8227SGreg Roach } 768c2e8227SGreg Roach 778c2e8227SGreg Roach /** {@inheritdoc} */ 78c1010edaSGreg Roach public function hasTabContent(Individual $individual) 79c1010edaSGreg Roach { 80225e381fSGreg Roach return Auth::isManager($individual->getTree()) || !empty($this->getStoriesForIndividual($individual)); 818c2e8227SGreg Roach } 828c2e8227SGreg Roach 838c2e8227SGreg Roach /** {@inheritdoc} */ 84c1010edaSGreg Roach public function isGrayedOut(Individual $individual) 85c1010edaSGreg Roach { 86225e381fSGreg Roach return !empty($this->getStoriesForIndividual($individual)); 878c2e8227SGreg Roach } 888c2e8227SGreg Roach 898c2e8227SGreg Roach /** {@inheritdoc} */ 90c1010edaSGreg Roach public function canLoadAjax() 91c1010edaSGreg Roach { 928c2e8227SGreg Roach return false; 938c2e8227SGreg Roach } 948c2e8227SGreg Roach 95225e381fSGreg Roach /** 96225e381fSGreg Roach * @param Individual $individual 97225e381fSGreg Roach * 98225e381fSGreg Roach * @return stdClass[] 99225e381fSGreg Roach */ 100c1010edaSGreg Roach private function getStoriesForIndividual(Individual $individual): array 101c1010edaSGreg Roach { 102225e381fSGreg Roach $block_ids = 103225e381fSGreg Roach Database::prepare( 104e5588fb0SGreg Roach "SELECT block_id" . 105225e381fSGreg Roach " FROM `##block`" . 106225e381fSGreg Roach " WHERE module_name = :module_name" . 107225e381fSGreg Roach " AND xref = :xref" . 108225e381fSGreg Roach " AND gedcom_id = :tree_id" 109225e381fSGreg Roach )->execute([ 110225e381fSGreg Roach 'module_name' => $this->getName(), 111225e381fSGreg Roach 'xref' => $individual->getXref(), 112225e381fSGreg Roach 'tree_id' => $individual->getTree()->getTreeId(), 113225e381fSGreg Roach ])->fetchOneColumn(); 114225e381fSGreg Roach 115225e381fSGreg Roach $stories = []; 116225e381fSGreg Roach foreach ($block_ids as $block_id) { 117225e381fSGreg Roach // Only show this block for certain languages 118225e381fSGreg Roach $languages = $this->getBlockSetting($block_id, 'languages', ''); 119225e381fSGreg Roach if ($languages === '' || in_array(WT_LOCALE, explode(',', $languages))) { 120225e381fSGreg Roach $stories[] = (object)[ 121225e381fSGreg Roach 'block_id' => $block_id, 122225e381fSGreg Roach 'title' => $this->getBlockSetting($block_id, 'title'), 12372ac996dSGreg Roach 'story_body' => $this->getBlockSetting($block_id, 'story_body'), 124225e381fSGreg Roach ]; 125225e381fSGreg Roach } 126225e381fSGreg Roach } 127225e381fSGreg Roach 128225e381fSGreg Roach return $stories; 1298c2e8227SGreg Roach } 1308c2e8227SGreg Roach 1318c2e8227SGreg Roach /** 1320ee13198SGreg Roach * The user can re-order menus. Until they do, they are shown in this order. 1330ee13198SGreg Roach * 1340ee13198SGreg Roach * @return int 1350ee13198SGreg Roach */ 136c1010edaSGreg Roach public function defaultMenuOrder() 137c1010edaSGreg Roach { 1388c2e8227SGreg Roach return 30; 1398c2e8227SGreg Roach } 1408c2e8227SGreg Roach 1410ee13198SGreg Roach /** 1420ee13198SGreg Roach * What is the default access level for this module? 1430ee13198SGreg Roach * 1440ee13198SGreg Roach * Some modules are aimed at admins or managers, and are not generally shown to users. 1450ee13198SGreg Roach * 1460ee13198SGreg Roach * @return int 1470ee13198SGreg Roach */ 148c1010edaSGreg Roach public function defaultAccessLevel() 149c1010edaSGreg Roach { 1504b9ff166SGreg Roach return Auth::PRIV_HIDE; 1518c2e8227SGreg Roach } 1528c2e8227SGreg Roach 1530ee13198SGreg Roach /** 1540ee13198SGreg Roach * A menu, to be added to the main application menu. 1550ee13198SGreg Roach * 156aee13b6dSGreg Roach * @param Tree $tree 157aee13b6dSGreg Roach * 1580ee13198SGreg Roach * @return Menu|null 1590ee13198SGreg Roach */ 160c1010edaSGreg Roach public function getMenu(Tree $tree) 161c1010edaSGreg Roach { 162c1010edaSGreg Roach $menu = new Menu($this->getTitle(), route('module', [ 163c1010edaSGreg Roach 'module' => $this->getName(), 164c1010edaSGreg Roach 'action' => 'ShowList', 165c1010edaSGreg Roach ]), 'menu-story'); 1668c2e8227SGreg Roach 1678c2e8227SGreg Roach return $menu; 1688c2e8227SGreg Roach } 16972ac996dSGreg Roach 17072ac996dSGreg Roach /** 171b6db7c1fSGreg Roach * @param Tree $tree 17272ac996dSGreg Roach * 17372ac996dSGreg Roach * @return Response 17472ac996dSGreg Roach */ 175b6db7c1fSGreg Roach public function getAdminAction(Tree $tree): Response 176c1010edaSGreg Roach { 17772ac996dSGreg Roach $this->layout = 'layouts/administration'; 17872ac996dSGreg Roach 17972ac996dSGreg Roach $stories = Database::prepare( 18072ac996dSGreg Roach "SELECT block_id, xref, gedcom_id" . 18172ac996dSGreg Roach " FROM `##block` b" . 18272ac996dSGreg Roach " WHERE module_name = :module_name" . 18372ac996dSGreg Roach " AND gedcom_id = :tree_id" . 18472ac996dSGreg Roach " ORDER BY gedcom_id, xref" 18572ac996dSGreg Roach )->execute([ 18672ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 18772ac996dSGreg Roach 'module_name' => $this->getName(), 18872ac996dSGreg Roach ])->fetchAll(); 18972ac996dSGreg Roach 19072ac996dSGreg Roach foreach ($stories as $story) { 19172ac996dSGreg Roach $story->individual = Individual::getInstance($story->xref, $tree); 19272ac996dSGreg Roach $story->title = $this->getBlockSetting($story->block_id, 'title'); 19372ac996dSGreg Roach $story->languages = $this->getBlockSetting($story->block_id, 'languages'); 19472ac996dSGreg Roach } 19572ac996dSGreg Roach 19672ac996dSGreg Roach return $this->viewResponse('modules/stories/config', [ 19772ac996dSGreg Roach 'stories' => $stories, 19872ac996dSGreg Roach 'title' => $this->getTitle() . ' — ' . $tree->getTitle(), 19972ac996dSGreg Roach 'tree' => $tree, 20072ac996dSGreg Roach 'tree_names' => Tree::getNameList(), 20172ac996dSGreg Roach ]); 20272ac996dSGreg Roach } 20372ac996dSGreg Roach 20472ac996dSGreg Roach /** 20572ac996dSGreg Roach * @param Request $request 206b6db7c1fSGreg Roach * @param Tree $tree 20772ac996dSGreg Roach * 20872ac996dSGreg Roach * @return Response 20972ac996dSGreg Roach */ 210b6db7c1fSGreg Roach public function getAdminEditAction(Request $request, Tree $tree): Response 211c1010edaSGreg Roach { 21272ac996dSGreg Roach $this->layout = 'layouts/administration'; 21372ac996dSGreg Roach 21472ac996dSGreg Roach $block_id = (int)$request->get('block_id'); 21572ac996dSGreg Roach 21672ac996dSGreg Roach if ($block_id === 0) { 21772ac996dSGreg Roach // Creating a new story 21872ac996dSGreg Roach $individual = Individual::getInstance($request->get('xref', ''), $tree); 21972ac996dSGreg Roach $story_title = ''; 22072ac996dSGreg Roach $story_body = ''; 22172ac996dSGreg Roach $languages = []; 22272ac996dSGreg Roach 22372ac996dSGreg Roach $title = I18N::translate('Add a story') . ' — ' . e($tree->getTitle()); 22472ac996dSGreg Roach } else { 22572ac996dSGreg Roach // Editing an existing story 22672ac996dSGreg Roach $xref = Database::prepare( 22772ac996dSGreg Roach "SELECT xref FROM `##block` WHERE block_id = :block_id" 22872ac996dSGreg Roach )->execute([ 22972ac996dSGreg Roach 'block_id' => $block_id, 23072ac996dSGreg Roach ])->fetchOne(); 23172ac996dSGreg Roach 23272ac996dSGreg Roach $individual = Individual::getInstance($xref, $tree); 23372ac996dSGreg Roach $story_title = $this->getBlockSetting($block_id, 'title', ''); 23472ac996dSGreg Roach $story_body = $this->getBlockSetting($block_id, 'story_body', ''); 23572ac996dSGreg Roach $languages = explode(',', $this->getBlockSetting($block_id, 'languages')); 23672ac996dSGreg Roach 23772ac996dSGreg Roach $title = I18N::translate('Edit the story') . ' — ' . e($tree->getTitle()); 23872ac996dSGreg Roach } 23972ac996dSGreg Roach 24072ac996dSGreg Roach return $this->viewResponse('modules/stories/edit', [ 24172ac996dSGreg Roach 'block_id' => $block_id, 24272ac996dSGreg Roach 'languages' => $languages, 24372ac996dSGreg Roach 'story_body' => $story_body, 24472ac996dSGreg Roach 'story_title' => $story_title, 24572ac996dSGreg Roach 'title' => $title, 24672ac996dSGreg Roach 'tree' => $tree, 24772ac996dSGreg Roach 'individual' => $individual, 24872ac996dSGreg Roach ]); 24972ac996dSGreg Roach } 25072ac996dSGreg Roach 25172ac996dSGreg Roach /** 25272ac996dSGreg Roach * @param Request $request 253b6db7c1fSGreg Roach * @param Tree $tree 25472ac996dSGreg Roach * 25572ac996dSGreg Roach * @return RedirectResponse 25672ac996dSGreg Roach */ 257b6db7c1fSGreg Roach public function postAdminEditAction(Request $request, Tree $tree): RedirectResponse 258c1010edaSGreg Roach { 25972ac996dSGreg Roach $block_id = (int)$request->get('block_id'); 26072ac996dSGreg Roach $xref = $request->get('xref', ''); 26172ac996dSGreg Roach $story_body = $request->get('story_body', ''); 26272ac996dSGreg Roach $story_title = $request->get('story_title', ''); 26372ac996dSGreg Roach $languages = $request->get('languages', []); 26472ac996dSGreg Roach 26572ac996dSGreg Roach if ($block_id !== 0) { 26672ac996dSGreg Roach Database::prepare( 26772ac996dSGreg Roach "UPDATE `##block` SET gedcom_id = :tree_id, xref = :xref WHERE block_id = :block_id" 26872ac996dSGreg Roach )->execute([ 26972ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 27072ac996dSGreg Roach 'xref' => $xref, 27172ac996dSGreg Roach 'block_id' => $block_id, 27272ac996dSGreg Roach ]); 27372ac996dSGreg Roach } else { 27472ac996dSGreg Roach Database::prepare( 27572ac996dSGreg Roach "INSERT INTO `##block` (gedcom_id, xref, module_name, block_order) VALUES (:tree_id, :xref, 'stories', 0)" 27672ac996dSGreg Roach )->execute([ 27772ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 27872ac996dSGreg Roach 'xref' => $xref, 27972ac996dSGreg Roach ]); 28072ac996dSGreg Roach 28172ac996dSGreg Roach $block_id = Database::getInstance()->lastInsertId(); 28272ac996dSGreg Roach } 28372ac996dSGreg Roach 28472ac996dSGreg Roach $this->setBlockSetting($block_id, 'story_body', $story_body); 28572ac996dSGreg Roach $this->setBlockSetting($block_id, 'title', $story_title); 28672ac996dSGreg Roach $this->setBlockSetting($block_id, 'languages', implode(',', $languages)); 28772ac996dSGreg Roach 288c1010edaSGreg Roach $url = route('module', [ 289c1010edaSGreg Roach 'module' => 'stories', 290c1010edaSGreg Roach 'action' => 'Admin', 291c1010edaSGreg Roach 'ged' => $tree->getName(), 292c1010edaSGreg Roach ]); 29372ac996dSGreg Roach 29472ac996dSGreg Roach return new RedirectResponse($url); 29572ac996dSGreg Roach } 29672ac996dSGreg Roach 29772ac996dSGreg Roach /** 29872ac996dSGreg Roach * @param Request $request 299b6db7c1fSGreg Roach * @param Tree $tree 30072ac996dSGreg Roach * 30172ac996dSGreg Roach * @return Response 30272ac996dSGreg Roach */ 303b6db7c1fSGreg Roach public function postAdminDeleteAction(Request $request, Tree $tree): Response 304c1010edaSGreg Roach { 30572ac996dSGreg Roach $block_id = (int)$request->get('block_id'); 30672ac996dSGreg Roach 30772ac996dSGreg Roach Database::prepare( 30872ac996dSGreg Roach "DELETE FROM `##block_setting` WHERE block_id = :block_id" 30972ac996dSGreg Roach )->execute([ 31072ac996dSGreg Roach 'block_id' => $block_id, 31172ac996dSGreg Roach ]); 31272ac996dSGreg Roach 31372ac996dSGreg Roach Database::prepare( 31472ac996dSGreg Roach "DELETE FROM `##block` WHERE block_id = :block_id" 31572ac996dSGreg Roach )->execute([ 31672ac996dSGreg Roach 'block_id' => $block_id, 31772ac996dSGreg Roach ]); 31872ac996dSGreg Roach 319c1010edaSGreg Roach $url = route('module', [ 320c1010edaSGreg Roach 'module' => 'stories', 321c1010edaSGreg Roach 'action' => 'Admin', 322c1010edaSGreg Roach 'ged' => $tree->getName(), 323c1010edaSGreg Roach ]); 32472ac996dSGreg Roach 32572ac996dSGreg Roach return new RedirectResponse($url); 32672ac996dSGreg Roach } 32772ac996dSGreg Roach 32872ac996dSGreg Roach /** 329b6db7c1fSGreg Roach * @param Tree $tree 33072ac996dSGreg Roach * 33172ac996dSGreg Roach * @return Response 33272ac996dSGreg Roach */ 333b6db7c1fSGreg Roach public function getShowListAction(Tree $tree): Response 334c1010edaSGreg Roach { 33572ac996dSGreg Roach $stories = Database::prepare( 33672ac996dSGreg Roach "SELECT block_id, xref" . 33772ac996dSGreg Roach " FROM `##block` b" . 33872ac996dSGreg Roach " WHERE module_name = :module_name" . 33972ac996dSGreg Roach " AND gedcom_id = :tree_id" . 34072ac996dSGreg Roach " ORDER BY xref" 34172ac996dSGreg Roach )->execute([ 34272ac996dSGreg Roach 'module_name' => $this->getName(), 34372ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 34472ac996dSGreg Roach ])->fetchAll(); 34572ac996dSGreg Roach 34672ac996dSGreg Roach foreach ($stories as $story) { 34772ac996dSGreg Roach $story->individual = Individual::getInstance($story->xref, $tree); 34872ac996dSGreg Roach $story->title = $this->getBlockSetting($story->block_id, 'title'); 34972ac996dSGreg Roach $story->languages = $this->getBlockSetting($story->block_id, 'languages'); 35072ac996dSGreg Roach } 35172ac996dSGreg Roach 35272ac996dSGreg Roach // Filter non-existant and private individuals. 353*492c7072SGreg Roach $stories = array_filter($stories, function (stdClass $story): bool { 35472ac996dSGreg Roach return $story->individual !== null && $story->individual->canShow(); 35572ac996dSGreg Roach }); 35672ac996dSGreg Roach 35772ac996dSGreg Roach // Filter foreign languages. 358*492c7072SGreg Roach $stories = array_filter($stories, function (stdClass $story): bool { 359a44b57a9SGreg Roach return $story->languages === '' || in_array(WT_LOCALE, explode(',', $story->languages)); 36072ac996dSGreg Roach }); 36172ac996dSGreg Roach 36272ac996dSGreg Roach return $this->viewResponse('modules/stories/list', [ 36372ac996dSGreg Roach 'stories' => $stories, 36472ac996dSGreg Roach 'title' => $this->getTitle(), 36572ac996dSGreg Roach ]); 36672ac996dSGreg Roach } 3678c2e8227SGreg Roach} 368