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 */ 16e7f56f2aSGreg Roachdeclare(strict_types=1); 17e7f56f2aSGreg Roach 1876692c8bSGreg Roachnamespace Fisharebest\Webtrees\Module; 1976692c8bSGreg Roach 200e62c4b8SGreg Roachuse Fisharebest\Webtrees\Auth; 210e62c4b8SGreg Roachuse Fisharebest\Webtrees\Database; 220e62c4b8SGreg Roachuse Fisharebest\Webtrees\I18N; 230e62c4b8SGreg Roachuse Fisharebest\Webtrees\Individual; 240e62c4b8SGreg Roachuse Fisharebest\Webtrees\Menu; 250e62c4b8SGreg Roachuse Fisharebest\Webtrees\Tree; 26225e381fSGreg Roachuse stdClass; 2772ac996dSGreg Roachuse Symfony\Component\HttpFoundation\RedirectResponse; 2872ac996dSGreg Roachuse Symfony\Component\HttpFoundation\Request; 2972ac996dSGreg Roachuse Symfony\Component\HttpFoundation\Response; 308c2e8227SGreg Roach 318c2e8227SGreg Roach/** 328c2e8227SGreg Roach * Class StoriesModule 338c2e8227SGreg Roach */ 34c1010edaSGreg Roachclass StoriesModule extends AbstractModule implements ModuleTabInterface, ModuleConfigInterface, ModuleMenuInterface 35c1010edaSGreg Roach{ 368c2e8227SGreg Roach /** {@inheritdoc} */ 378f53f488SRico Sonntag public function getTitle(): string 38c1010edaSGreg Roach { 39bbb76c12SGreg Roach /* I18N: Name of a module */ 40bbb76c12SGreg Roach return I18N::translate('Stories'); 418c2e8227SGreg Roach } 428c2e8227SGreg Roach 438c2e8227SGreg Roach /** {@inheritdoc} */ 448f53f488SRico Sonntag public function getDescription(): string 45c1010edaSGreg Roach { 46bbb76c12SGreg Roach /* I18N: Description of the “Stories” module */ 47bbb76c12SGreg Roach return I18N::translate('Add narrative stories to individuals in the family tree.'); 488c2e8227SGreg Roach } 498c2e8227SGreg Roach 50aee13b6dSGreg Roach /** 51aee13b6dSGreg Roach * The URL to a page where the user can modify the configuration of this module. 52aee13b6dSGreg Roach * 53aee13b6dSGreg Roach * @return string 54aee13b6dSGreg Roach */ 558f53f488SRico Sonntag public function getConfigLink(): string 56c1010edaSGreg Roach { 57c1010edaSGreg Roach return route('module', [ 58c1010edaSGreg Roach 'module' => $this->getName(), 59c1010edaSGreg Roach 'action' => 'Admin', 60c1010edaSGreg Roach ]); 618c2e8227SGreg Roach } 628c2e8227SGreg Roach 638c2e8227SGreg Roach /** {@inheritdoc} */ 649b34404bSGreg Roach public function defaultTabOrder(): int 65c1010edaSGreg Roach { 668c2e8227SGreg Roach return 55; 678c2e8227SGreg Roach } 688c2e8227SGreg Roach 698c2e8227SGreg Roach /** {@inheritdoc} */ 709b34404bSGreg Roach public function getTabContent(Individual $individual): string 71c1010edaSGreg Roach { 7272ac996dSGreg Roach return view('modules/stories/tab', [ 7372ac996dSGreg Roach 'is_admin' => Auth::isAdmin(), 74225e381fSGreg Roach 'individual' => $individual, 75225e381fSGreg Roach 'stories' => $this->getStoriesForIndividual($individual), 76225e381fSGreg Roach ]); 778c2e8227SGreg Roach } 788c2e8227SGreg Roach 798c2e8227SGreg Roach /** {@inheritdoc} */ 809b34404bSGreg Roach public function hasTabContent(Individual $individual): bool 81c1010edaSGreg Roach { 82225e381fSGreg Roach return Auth::isManager($individual->getTree()) || !empty($this->getStoriesForIndividual($individual)); 838c2e8227SGreg Roach } 848c2e8227SGreg Roach 858c2e8227SGreg Roach /** {@inheritdoc} */ 869b34404bSGreg Roach public function isGrayedOut(Individual $individual): bool 87c1010edaSGreg Roach { 88225e381fSGreg Roach return !empty($this->getStoriesForIndividual($individual)); 898c2e8227SGreg Roach } 908c2e8227SGreg Roach 918c2e8227SGreg Roach /** {@inheritdoc} */ 929b34404bSGreg Roach public function canLoadAjax(): bool 93c1010edaSGreg Roach { 948c2e8227SGreg Roach return false; 958c2e8227SGreg Roach } 968c2e8227SGreg Roach 97225e381fSGreg Roach /** 98225e381fSGreg Roach * @param Individual $individual 99225e381fSGreg Roach * 100225e381fSGreg Roach * @return stdClass[] 101225e381fSGreg Roach */ 102c1010edaSGreg Roach private function getStoriesForIndividual(Individual $individual): array 103c1010edaSGreg Roach { 104225e381fSGreg Roach $block_ids = 105225e381fSGreg Roach Database::prepare( 106e5588fb0SGreg Roach "SELECT block_id" . 107225e381fSGreg Roach " FROM `##block`" . 108225e381fSGreg Roach " WHERE module_name = :module_name" . 109225e381fSGreg Roach " AND xref = :xref" . 110225e381fSGreg Roach " AND gedcom_id = :tree_id" 111225e381fSGreg Roach )->execute([ 112225e381fSGreg Roach 'module_name' => $this->getName(), 113225e381fSGreg Roach 'xref' => $individual->getXref(), 114225e381fSGreg Roach 'tree_id' => $individual->getTree()->getTreeId(), 115225e381fSGreg Roach ])->fetchOneColumn(); 116225e381fSGreg Roach 117225e381fSGreg Roach $stories = []; 118225e381fSGreg Roach foreach ($block_ids as $block_id) { 1197d988ec3SGreg Roach $block_id = (int) $block_id; 1207d988ec3SGreg Roach 121225e381fSGreg Roach // Only show this block for certain languages 122225e381fSGreg Roach $languages = $this->getBlockSetting($block_id, 'languages', ''); 123225e381fSGreg Roach if ($languages === '' || in_array(WT_LOCALE, explode(',', $languages))) { 124225e381fSGreg Roach $stories[] = (object) [ 125225e381fSGreg Roach 'block_id' => $block_id, 126225e381fSGreg Roach 'title' => $this->getBlockSetting($block_id, 'title'), 12772ac996dSGreg Roach 'story_body' => $this->getBlockSetting($block_id, 'story_body'), 128225e381fSGreg Roach ]; 129225e381fSGreg Roach } 130225e381fSGreg Roach } 131225e381fSGreg Roach 132225e381fSGreg Roach return $stories; 1338c2e8227SGreg Roach } 1348c2e8227SGreg Roach 1358c2e8227SGreg Roach /** 1360ee13198SGreg Roach * The user can re-order menus. Until they do, they are shown in this order. 1370ee13198SGreg Roach * 1380ee13198SGreg Roach * @return int 1390ee13198SGreg Roach */ 1408f53f488SRico Sonntag public function defaultMenuOrder(): int 141c1010edaSGreg Roach { 1428c2e8227SGreg Roach return 30; 1438c2e8227SGreg Roach } 1448c2e8227SGreg Roach 1450ee13198SGreg Roach /** 1460ee13198SGreg Roach * What is the default access level for this module? 1470ee13198SGreg Roach * 1480ee13198SGreg Roach * Some modules are aimed at admins or managers, and are not generally shown to users. 1490ee13198SGreg Roach * 1500ee13198SGreg Roach * @return int 1510ee13198SGreg Roach */ 1528f53f488SRico Sonntag public function defaultAccessLevel(): int 153c1010edaSGreg Roach { 1544b9ff166SGreg Roach return Auth::PRIV_HIDE; 1558c2e8227SGreg Roach } 1568c2e8227SGreg Roach 1570ee13198SGreg Roach /** 1580ee13198SGreg Roach * A menu, to be added to the main application menu. 1590ee13198SGreg Roach * 160aee13b6dSGreg Roach * @param Tree $tree 161aee13b6dSGreg Roach * 1620ee13198SGreg Roach * @return Menu|null 1630ee13198SGreg Roach */ 164c1010edaSGreg Roach public function getMenu(Tree $tree) 165c1010edaSGreg Roach { 166c1010edaSGreg Roach $menu = new Menu($this->getTitle(), route('module', [ 167c1010edaSGreg Roach 'module' => $this->getName(), 168c1010edaSGreg Roach 'action' => 'ShowList', 1698765c3bdSGreg Roach 'ged' => $tree->getName(), 170c1010edaSGreg Roach ]), 'menu-story'); 1718c2e8227SGreg Roach 1728c2e8227SGreg Roach return $menu; 1738c2e8227SGreg Roach } 17472ac996dSGreg Roach 17572ac996dSGreg Roach /** 176b6db7c1fSGreg Roach * @param Tree $tree 17772ac996dSGreg Roach * 17872ac996dSGreg Roach * @return Response 17972ac996dSGreg Roach */ 180b6db7c1fSGreg Roach public function getAdminAction(Tree $tree): Response 181c1010edaSGreg Roach { 18272ac996dSGreg Roach $this->layout = 'layouts/administration'; 18372ac996dSGreg Roach 18472ac996dSGreg Roach $stories = Database::prepare( 18572ac996dSGreg Roach "SELECT block_id, xref, gedcom_id" . 18672ac996dSGreg Roach " FROM `##block` b" . 18772ac996dSGreg Roach " WHERE module_name = :module_name" . 18872ac996dSGreg Roach " AND gedcom_id = :tree_id" . 18972ac996dSGreg Roach " ORDER BY gedcom_id, xref" 19072ac996dSGreg Roach )->execute([ 19172ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 19272ac996dSGreg Roach 'module_name' => $this->getName(), 19372ac996dSGreg Roach ])->fetchAll(); 19472ac996dSGreg Roach 19572ac996dSGreg Roach foreach ($stories as $story) { 196*5db543e1SGreg Roach $block_id = (int) $story->block_id; 197*5db543e1SGreg Roach 19872ac996dSGreg Roach $story->individual = Individual::getInstance($story->xref, $tree); 199*5db543e1SGreg Roach $story->title = $this->getBlockSetting($block_id, 'title'); 200*5db543e1SGreg Roach $story->languages = $this->getBlockSetting($block_id, 'languages'); 20172ac996dSGreg Roach } 20272ac996dSGreg Roach 20372ac996dSGreg Roach return $this->viewResponse('modules/stories/config', [ 20472ac996dSGreg Roach 'stories' => $stories, 20572ac996dSGreg Roach 'title' => $this->getTitle() . ' — ' . $tree->getTitle(), 20672ac996dSGreg Roach 'tree' => $tree, 20772ac996dSGreg Roach 'tree_names' => Tree::getNameList(), 20872ac996dSGreg Roach ]); 20972ac996dSGreg Roach } 21072ac996dSGreg Roach 21172ac996dSGreg Roach /** 21272ac996dSGreg Roach * @param Request $request 213b6db7c1fSGreg Roach * @param Tree $tree 21472ac996dSGreg Roach * 21572ac996dSGreg Roach * @return Response 21672ac996dSGreg Roach */ 217b6db7c1fSGreg Roach public function getAdminEditAction(Request $request, Tree $tree): Response 218c1010edaSGreg Roach { 21972ac996dSGreg Roach $this->layout = 'layouts/administration'; 22072ac996dSGreg Roach 22172ac996dSGreg Roach $block_id = (int) $request->get('block_id'); 22272ac996dSGreg Roach 22372ac996dSGreg Roach if ($block_id === 0) { 22472ac996dSGreg Roach // Creating a new story 22572ac996dSGreg Roach $individual = Individual::getInstance($request->get('xref', ''), $tree); 22672ac996dSGreg Roach $story_title = ''; 22772ac996dSGreg Roach $story_body = ''; 22872ac996dSGreg Roach $languages = []; 22972ac996dSGreg Roach 23072ac996dSGreg Roach $title = I18N::translate('Add a story') . ' — ' . e($tree->getTitle()); 23172ac996dSGreg Roach } else { 23272ac996dSGreg Roach // Editing an existing story 23372ac996dSGreg Roach $xref = Database::prepare( 23472ac996dSGreg Roach "SELECT xref FROM `##block` WHERE block_id = :block_id" 23572ac996dSGreg Roach )->execute([ 23672ac996dSGreg Roach 'block_id' => $block_id, 23772ac996dSGreg Roach ])->fetchOne(); 23872ac996dSGreg Roach 23972ac996dSGreg Roach $individual = Individual::getInstance($xref, $tree); 24072ac996dSGreg Roach $story_title = $this->getBlockSetting($block_id, 'title', ''); 24172ac996dSGreg Roach $story_body = $this->getBlockSetting($block_id, 'story_body', ''); 24272ac996dSGreg Roach $languages = explode(',', $this->getBlockSetting($block_id, 'languages')); 24372ac996dSGreg Roach 24472ac996dSGreg Roach $title = I18N::translate('Edit the story') . ' — ' . e($tree->getTitle()); 24572ac996dSGreg Roach } 24672ac996dSGreg Roach 24772ac996dSGreg Roach return $this->viewResponse('modules/stories/edit', [ 24872ac996dSGreg Roach 'block_id' => $block_id, 24972ac996dSGreg Roach 'languages' => $languages, 25072ac996dSGreg Roach 'story_body' => $story_body, 25172ac996dSGreg Roach 'story_title' => $story_title, 25272ac996dSGreg Roach 'title' => $title, 25372ac996dSGreg Roach 'tree' => $tree, 25472ac996dSGreg Roach 'individual' => $individual, 25572ac996dSGreg Roach ]); 25672ac996dSGreg Roach } 25772ac996dSGreg Roach 25872ac996dSGreg Roach /** 25972ac996dSGreg Roach * @param Request $request 260b6db7c1fSGreg Roach * @param Tree $tree 26172ac996dSGreg Roach * 26272ac996dSGreg Roach * @return RedirectResponse 26372ac996dSGreg Roach */ 264b6db7c1fSGreg Roach public function postAdminEditAction(Request $request, Tree $tree): RedirectResponse 265c1010edaSGreg Roach { 26672ac996dSGreg Roach $block_id = (int) $request->get('block_id'); 26772ac996dSGreg Roach $xref = $request->get('xref', ''); 26872ac996dSGreg Roach $story_body = $request->get('story_body', ''); 26972ac996dSGreg Roach $story_title = $request->get('story_title', ''); 27072ac996dSGreg Roach $languages = $request->get('languages', []); 27172ac996dSGreg Roach 27272ac996dSGreg Roach if ($block_id !== 0) { 27372ac996dSGreg Roach Database::prepare( 27472ac996dSGreg Roach "UPDATE `##block` SET gedcom_id = :tree_id, xref = :xref WHERE block_id = :block_id" 27572ac996dSGreg Roach )->execute([ 27672ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 27772ac996dSGreg Roach 'xref' => $xref, 27872ac996dSGreg Roach 'block_id' => $block_id, 27972ac996dSGreg Roach ]); 28072ac996dSGreg Roach } else { 28172ac996dSGreg Roach Database::prepare( 28272ac996dSGreg Roach "INSERT INTO `##block` (gedcom_id, xref, module_name, block_order) VALUES (:tree_id, :xref, 'stories', 0)" 28372ac996dSGreg Roach )->execute([ 28472ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 28572ac996dSGreg Roach 'xref' => $xref, 28672ac996dSGreg Roach ]); 28772ac996dSGreg Roach 28836e59714SGreg Roach $block_id = Database::lastInsertId(); 28972ac996dSGreg Roach } 29072ac996dSGreg Roach 29172ac996dSGreg Roach $this->setBlockSetting($block_id, 'story_body', $story_body); 29272ac996dSGreg Roach $this->setBlockSetting($block_id, 'title', $story_title); 29372ac996dSGreg Roach $this->setBlockSetting($block_id, 'languages', implode(',', $languages)); 29472ac996dSGreg Roach 295c1010edaSGreg Roach $url = route('module', [ 296c1010edaSGreg Roach 'module' => 'stories', 297c1010edaSGreg Roach 'action' => 'Admin', 298c1010edaSGreg Roach 'ged' => $tree->getName(), 299c1010edaSGreg Roach ]); 30072ac996dSGreg Roach 30172ac996dSGreg Roach return new RedirectResponse($url); 30272ac996dSGreg Roach } 30372ac996dSGreg Roach 30472ac996dSGreg Roach /** 30572ac996dSGreg Roach * @param Request $request 306b6db7c1fSGreg Roach * @param Tree $tree 30772ac996dSGreg Roach * 30872ac996dSGreg Roach * @return Response 30972ac996dSGreg Roach */ 310b6db7c1fSGreg Roach public function postAdminDeleteAction(Request $request, Tree $tree): Response 311c1010edaSGreg Roach { 31272ac996dSGreg Roach $block_id = (int) $request->get('block_id'); 31372ac996dSGreg Roach 31472ac996dSGreg Roach Database::prepare( 31572ac996dSGreg Roach "DELETE FROM `##block_setting` WHERE block_id = :block_id" 31672ac996dSGreg Roach )->execute([ 31772ac996dSGreg Roach 'block_id' => $block_id, 31872ac996dSGreg Roach ]); 31972ac996dSGreg Roach 32072ac996dSGreg Roach Database::prepare( 32172ac996dSGreg Roach "DELETE FROM `##block` WHERE block_id = :block_id" 32272ac996dSGreg Roach )->execute([ 32372ac996dSGreg Roach 'block_id' => $block_id, 32472ac996dSGreg Roach ]); 32572ac996dSGreg Roach 326c1010edaSGreg Roach $url = route('module', [ 327c1010edaSGreg Roach 'module' => 'stories', 328c1010edaSGreg Roach 'action' => 'Admin', 329c1010edaSGreg Roach 'ged' => $tree->getName(), 330c1010edaSGreg Roach ]); 33172ac996dSGreg Roach 33272ac996dSGreg Roach return new RedirectResponse($url); 33372ac996dSGreg Roach } 33472ac996dSGreg Roach 33572ac996dSGreg Roach /** 336b6db7c1fSGreg Roach * @param Tree $tree 33772ac996dSGreg Roach * 33872ac996dSGreg Roach * @return Response 33972ac996dSGreg Roach */ 340b6db7c1fSGreg Roach public function getShowListAction(Tree $tree): Response 341c1010edaSGreg Roach { 34272ac996dSGreg Roach $stories = Database::prepare( 34372ac996dSGreg Roach "SELECT block_id, xref" . 34472ac996dSGreg Roach " FROM `##block` b" . 34572ac996dSGreg Roach " WHERE module_name = :module_name" . 34672ac996dSGreg Roach " AND gedcom_id = :tree_id" . 34772ac996dSGreg Roach " ORDER BY xref" 34872ac996dSGreg Roach )->execute([ 34972ac996dSGreg Roach 'module_name' => $this->getName(), 35072ac996dSGreg Roach 'tree_id' => $tree->getTreeId(), 35172ac996dSGreg Roach ])->fetchAll(); 35272ac996dSGreg Roach 35372ac996dSGreg Roach foreach ($stories as $story) { 354*5db543e1SGreg Roach $block_id = (int) $story->block_id; 355*5db543e1SGreg Roach 35672ac996dSGreg Roach $story->individual = Individual::getInstance($story->xref, $tree); 357*5db543e1SGreg Roach $story->title = $this->getBlockSetting($block_id, 'title'); 358*5db543e1SGreg Roach $story->languages = $this->getBlockSetting($block_id, 'languages'); 35972ac996dSGreg Roach } 36072ac996dSGreg Roach 36172ac996dSGreg Roach // Filter non-existant and private individuals. 362492c7072SGreg Roach $stories = array_filter($stories, function (stdClass $story): bool { 36372ac996dSGreg Roach return $story->individual !== null && $story->individual->canShow(); 36472ac996dSGreg Roach }); 36572ac996dSGreg Roach 36672ac996dSGreg Roach // Filter foreign languages. 367492c7072SGreg Roach $stories = array_filter($stories, function (stdClass $story): bool { 368a44b57a9SGreg Roach return $story->languages === '' || in_array(WT_LOCALE, explode(',', $story->languages)); 36972ac996dSGreg Roach }); 37072ac996dSGreg Roach 37172ac996dSGreg Roach return $this->viewResponse('modules/stories/list', [ 37272ac996dSGreg Roach 'stories' => $stories, 37372ac996dSGreg Roach 'title' => $this->getTitle(), 37472ac996dSGreg Roach ]); 37572ac996dSGreg Roach } 3768c2e8227SGreg Roach} 377