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} */ 35*8f53f488SRico Sonntag public function getTitle(): string 36c1010edaSGreg Roach { 37bbb76c12SGreg Roach /* I18N: Name of a module */ 38bbb76c12SGreg Roach return I18N::translate('Stories'); 398c2e8227SGreg Roach } 408c2e8227SGreg Roach 418c2e8227SGreg Roach /** {@inheritdoc} */ 42*8f53f488SRico Sonntag public function getDescription(): string 43c1010edaSGreg Roach { 44bbb76c12SGreg Roach /* I18N: Description of the “Stories” module */ 45bbb76c12SGreg Roach return 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 */ 53*8f53f488SRico Sonntag public function getConfigLink(): string 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 */ 136*8f53f488SRico Sonntag public function defaultMenuOrder(): int 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 */ 148*8f53f488SRico Sonntag public function defaultAccessLevel(): int 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', 1658765c3bdSGreg Roach 'ged' => $tree->getName(), 166c1010edaSGreg Roach ]), 'menu-story'); 1678c2e8227SGreg Roach 1688c2e8227SGreg Roach return $menu; 1698c2e8227SGreg Roach } 17072ac996dSGreg Roach 17172ac996dSGreg Roach /** 172b6db7c1fSGreg Roach * @param Tree $tree 17372ac996dSGreg Roach * 17472ac996dSGreg Roach * @return Response 17572ac996dSGreg Roach */ 176b6db7c1fSGreg Roach public function getAdminAction(Tree $tree): Response 177c1010edaSGreg Roach { 17872ac996dSGreg Roach $this->layout = 'layouts/administration'; 17972ac996dSGreg Roach 18072ac996dSGreg Roach $stories = Database::prepare( 18172ac996dSGreg Roach "SELECT block_id, xref, gedcom_id" . 18272ac996dSGreg Roach " FROM `##block` b" . 18372ac996dSGreg Roach " WHERE module_name = :module_name" . 18472ac996dSGreg Roach " AND gedcom_id = :tree_id" . 18572ac996dSGreg Roach " ORDER BY gedcom_id, xref" 18672ac996dSGreg Roach )->execute([ 18772ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 18872ac996dSGreg Roach 'module_name' => $this->getName(), 18972ac996dSGreg Roach ])->fetchAll(); 19072ac996dSGreg Roach 19172ac996dSGreg Roach foreach ($stories as $story) { 19272ac996dSGreg Roach $story->individual = Individual::getInstance($story->xref, $tree); 19372ac996dSGreg Roach $story->title = $this->getBlockSetting($story->block_id, 'title'); 19472ac996dSGreg Roach $story->languages = $this->getBlockSetting($story->block_id, 'languages'); 19572ac996dSGreg Roach } 19672ac996dSGreg Roach 19772ac996dSGreg Roach return $this->viewResponse('modules/stories/config', [ 19872ac996dSGreg Roach 'stories' => $stories, 19972ac996dSGreg Roach 'title' => $this->getTitle() . ' — ' . $tree->getTitle(), 20072ac996dSGreg Roach 'tree' => $tree, 20172ac996dSGreg Roach 'tree_names' => Tree::getNameList(), 20272ac996dSGreg Roach ]); 20372ac996dSGreg Roach } 20472ac996dSGreg Roach 20572ac996dSGreg Roach /** 20672ac996dSGreg Roach * @param Request $request 207b6db7c1fSGreg Roach * @param Tree $tree 20872ac996dSGreg Roach * 20972ac996dSGreg Roach * @return Response 21072ac996dSGreg Roach */ 211b6db7c1fSGreg Roach public function getAdminEditAction(Request $request, Tree $tree): Response 212c1010edaSGreg Roach { 21372ac996dSGreg Roach $this->layout = 'layouts/administration'; 21472ac996dSGreg Roach 21572ac996dSGreg Roach $block_id = (int)$request->get('block_id'); 21672ac996dSGreg Roach 21772ac996dSGreg Roach if ($block_id === 0) { 21872ac996dSGreg Roach // Creating a new story 21972ac996dSGreg Roach $individual = Individual::getInstance($request->get('xref', ''), $tree); 22072ac996dSGreg Roach $story_title = ''; 22172ac996dSGreg Roach $story_body = ''; 22272ac996dSGreg Roach $languages = []; 22372ac996dSGreg Roach 22472ac996dSGreg Roach $title = I18N::translate('Add a story') . ' — ' . e($tree->getTitle()); 22572ac996dSGreg Roach } else { 22672ac996dSGreg Roach // Editing an existing story 22772ac996dSGreg Roach $xref = Database::prepare( 22872ac996dSGreg Roach "SELECT xref FROM `##block` WHERE block_id = :block_id" 22972ac996dSGreg Roach )->execute([ 23072ac996dSGreg Roach 'block_id' => $block_id, 23172ac996dSGreg Roach ])->fetchOne(); 23272ac996dSGreg Roach 23372ac996dSGreg Roach $individual = Individual::getInstance($xref, $tree); 23472ac996dSGreg Roach $story_title = $this->getBlockSetting($block_id, 'title', ''); 23572ac996dSGreg Roach $story_body = $this->getBlockSetting($block_id, 'story_body', ''); 23672ac996dSGreg Roach $languages = explode(',', $this->getBlockSetting($block_id, 'languages')); 23772ac996dSGreg Roach 23872ac996dSGreg Roach $title = I18N::translate('Edit the story') . ' — ' . e($tree->getTitle()); 23972ac996dSGreg Roach } 24072ac996dSGreg Roach 24172ac996dSGreg Roach return $this->viewResponse('modules/stories/edit', [ 24272ac996dSGreg Roach 'block_id' => $block_id, 24372ac996dSGreg Roach 'languages' => $languages, 24472ac996dSGreg Roach 'story_body' => $story_body, 24572ac996dSGreg Roach 'story_title' => $story_title, 24672ac996dSGreg Roach 'title' => $title, 24772ac996dSGreg Roach 'tree' => $tree, 24872ac996dSGreg Roach 'individual' => $individual, 24972ac996dSGreg Roach ]); 25072ac996dSGreg Roach } 25172ac996dSGreg Roach 25272ac996dSGreg Roach /** 25372ac996dSGreg Roach * @param Request $request 254b6db7c1fSGreg Roach * @param Tree $tree 25572ac996dSGreg Roach * 25672ac996dSGreg Roach * @return RedirectResponse 25772ac996dSGreg Roach */ 258b6db7c1fSGreg Roach public function postAdminEditAction(Request $request, Tree $tree): RedirectResponse 259c1010edaSGreg Roach { 26072ac996dSGreg Roach $block_id = (int)$request->get('block_id'); 26172ac996dSGreg Roach $xref = $request->get('xref', ''); 26272ac996dSGreg Roach $story_body = $request->get('story_body', ''); 26372ac996dSGreg Roach $story_title = $request->get('story_title', ''); 26472ac996dSGreg Roach $languages = $request->get('languages', []); 26572ac996dSGreg Roach 26672ac996dSGreg Roach if ($block_id !== 0) { 26772ac996dSGreg Roach Database::prepare( 26872ac996dSGreg Roach "UPDATE `##block` SET gedcom_id = :tree_id, xref = :xref WHERE block_id = :block_id" 26972ac996dSGreg Roach )->execute([ 27072ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 27172ac996dSGreg Roach 'xref' => $xref, 27272ac996dSGreg Roach 'block_id' => $block_id, 27372ac996dSGreg Roach ]); 27472ac996dSGreg Roach } else { 27572ac996dSGreg Roach Database::prepare( 27672ac996dSGreg Roach "INSERT INTO `##block` (gedcom_id, xref, module_name, block_order) VALUES (:tree_id, :xref, 'stories', 0)" 27772ac996dSGreg Roach )->execute([ 27872ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 27972ac996dSGreg Roach 'xref' => $xref, 28072ac996dSGreg Roach ]); 28172ac996dSGreg Roach 28272ac996dSGreg Roach $block_id = Database::getInstance()->lastInsertId(); 28372ac996dSGreg Roach } 28472ac996dSGreg Roach 28572ac996dSGreg Roach $this->setBlockSetting($block_id, 'story_body', $story_body); 28672ac996dSGreg Roach $this->setBlockSetting($block_id, 'title', $story_title); 28772ac996dSGreg Roach $this->setBlockSetting($block_id, 'languages', implode(',', $languages)); 28872ac996dSGreg Roach 289c1010edaSGreg Roach $url = route('module', [ 290c1010edaSGreg Roach 'module' => 'stories', 291c1010edaSGreg Roach 'action' => 'Admin', 292c1010edaSGreg Roach 'ged' => $tree->getName(), 293c1010edaSGreg Roach ]); 29472ac996dSGreg Roach 29572ac996dSGreg Roach return new RedirectResponse($url); 29672ac996dSGreg Roach } 29772ac996dSGreg Roach 29872ac996dSGreg Roach /** 29972ac996dSGreg Roach * @param Request $request 300b6db7c1fSGreg Roach * @param Tree $tree 30172ac996dSGreg Roach * 30272ac996dSGreg Roach * @return Response 30372ac996dSGreg Roach */ 304b6db7c1fSGreg Roach public function postAdminDeleteAction(Request $request, Tree $tree): Response 305c1010edaSGreg Roach { 30672ac996dSGreg Roach $block_id = (int)$request->get('block_id'); 30772ac996dSGreg Roach 30872ac996dSGreg Roach Database::prepare( 30972ac996dSGreg Roach "DELETE FROM `##block_setting` WHERE block_id = :block_id" 31072ac996dSGreg Roach )->execute([ 31172ac996dSGreg Roach 'block_id' => $block_id, 31272ac996dSGreg Roach ]); 31372ac996dSGreg Roach 31472ac996dSGreg Roach Database::prepare( 31572ac996dSGreg Roach "DELETE FROM `##block` WHERE block_id = :block_id" 31672ac996dSGreg Roach )->execute([ 31772ac996dSGreg Roach 'block_id' => $block_id, 31872ac996dSGreg Roach ]); 31972ac996dSGreg Roach 320c1010edaSGreg Roach $url = route('module', [ 321c1010edaSGreg Roach 'module' => 'stories', 322c1010edaSGreg Roach 'action' => 'Admin', 323c1010edaSGreg Roach 'ged' => $tree->getName(), 324c1010edaSGreg Roach ]); 32572ac996dSGreg Roach 32672ac996dSGreg Roach return new RedirectResponse($url); 32772ac996dSGreg Roach } 32872ac996dSGreg Roach 32972ac996dSGreg Roach /** 330b6db7c1fSGreg Roach * @param Tree $tree 33172ac996dSGreg Roach * 33272ac996dSGreg Roach * @return Response 33372ac996dSGreg Roach */ 334b6db7c1fSGreg Roach public function getShowListAction(Tree $tree): Response 335c1010edaSGreg Roach { 33672ac996dSGreg Roach $stories = Database::prepare( 33772ac996dSGreg Roach "SELECT block_id, xref" . 33872ac996dSGreg Roach " FROM `##block` b" . 33972ac996dSGreg Roach " WHERE module_name = :module_name" . 34072ac996dSGreg Roach " AND gedcom_id = :tree_id" . 34172ac996dSGreg Roach " ORDER BY xref" 34272ac996dSGreg Roach )->execute([ 34372ac996dSGreg Roach 'module_name' => $this->getName(), 34472ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 34572ac996dSGreg Roach ])->fetchAll(); 34672ac996dSGreg Roach 34772ac996dSGreg Roach foreach ($stories as $story) { 34872ac996dSGreg Roach $story->individual = Individual::getInstance($story->xref, $tree); 34972ac996dSGreg Roach $story->title = $this->getBlockSetting($story->block_id, 'title'); 35072ac996dSGreg Roach $story->languages = $this->getBlockSetting($story->block_id, 'languages'); 35172ac996dSGreg Roach } 35272ac996dSGreg Roach 35372ac996dSGreg Roach // Filter non-existant and private individuals. 354492c7072SGreg Roach $stories = array_filter($stories, function (stdClass $story): bool { 35572ac996dSGreg Roach return $story->individual !== null && $story->individual->canShow(); 35672ac996dSGreg Roach }); 35772ac996dSGreg Roach 35872ac996dSGreg Roach // Filter foreign languages. 359492c7072SGreg Roach $stories = array_filter($stories, function (stdClass $story): bool { 360a44b57a9SGreg Roach return $story->languages === '' || in_array(WT_LOCALE, explode(',', $story->languages)); 36172ac996dSGreg Roach }); 36272ac996dSGreg Roach 36372ac996dSGreg Roach return $this->viewResponse('modules/stories/list', [ 36472ac996dSGreg Roach 'stories' => $stories, 36572ac996dSGreg Roach 'title' => $this->getTitle(), 36672ac996dSGreg Roach ]); 36772ac996dSGreg Roach } 3688c2e8227SGreg Roach} 369