xref: /webtrees/app/Module/StoriesModule.php (revision 36e597144bed95dcfa6934dbad5388fb701a64eb)
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) {
19672ac996dSGreg Roach            $story->individual = Individual::getInstance($story->xref, $tree);
19772ac996dSGreg Roach            $story->title      = $this->getBlockSetting($story->block_id, 'title');
19872ac996dSGreg Roach            $story->languages  = $this->getBlockSetting($story->block_id, 'languages');
19972ac996dSGreg Roach        }
20072ac996dSGreg Roach
20172ac996dSGreg Roach        return $this->viewResponse('modules/stories/config', [
20272ac996dSGreg Roach            'stories'    => $stories,
20372ac996dSGreg Roach            'title'      => $this->getTitle() . ' — ' . $tree->getTitle(),
20472ac996dSGreg Roach            'tree'       => $tree,
20572ac996dSGreg Roach            'tree_names' => Tree::getNameList(),
20672ac996dSGreg Roach        ]);
20772ac996dSGreg Roach    }
20872ac996dSGreg Roach
20972ac996dSGreg Roach    /**
21072ac996dSGreg Roach     * @param Request $request
211b6db7c1fSGreg Roach     * @param Tree    $tree
21272ac996dSGreg Roach     *
21372ac996dSGreg Roach     * @return Response
21472ac996dSGreg Roach     */
215b6db7c1fSGreg Roach    public function getAdminEditAction(Request $request, Tree $tree): Response
216c1010edaSGreg Roach    {
21772ac996dSGreg Roach        $this->layout = 'layouts/administration';
21872ac996dSGreg Roach
21972ac996dSGreg Roach        $block_id = (int) $request->get('block_id');
22072ac996dSGreg Roach
22172ac996dSGreg Roach        if ($block_id === 0) {
22272ac996dSGreg Roach            // Creating a new story
22372ac996dSGreg Roach            $individual  = Individual::getInstance($request->get('xref', ''), $tree);
22472ac996dSGreg Roach            $story_title = '';
22572ac996dSGreg Roach            $story_body  = '';
22672ac996dSGreg Roach            $languages   = [];
22772ac996dSGreg Roach
22872ac996dSGreg Roach            $title = I18N::translate('Add a story') . ' — ' . e($tree->getTitle());
22972ac996dSGreg Roach        } else {
23072ac996dSGreg Roach            // Editing an existing story
23172ac996dSGreg Roach            $xref = Database::prepare(
23272ac996dSGreg Roach                "SELECT xref FROM `##block` WHERE block_id = :block_id"
23372ac996dSGreg Roach            )->execute([
23472ac996dSGreg Roach                'block_id' => $block_id,
23572ac996dSGreg Roach            ])->fetchOne();
23672ac996dSGreg Roach
23772ac996dSGreg Roach            $individual  = Individual::getInstance($xref, $tree);
23872ac996dSGreg Roach            $story_title = $this->getBlockSetting($block_id, 'title', '');
23972ac996dSGreg Roach            $story_body  = $this->getBlockSetting($block_id, 'story_body', '');
24072ac996dSGreg Roach            $languages   = explode(',', $this->getBlockSetting($block_id, 'languages'));
24172ac996dSGreg Roach
24272ac996dSGreg Roach            $title = I18N::translate('Edit the story') . ' — ' . e($tree->getTitle());
24372ac996dSGreg Roach        }
24472ac996dSGreg Roach
24572ac996dSGreg Roach        return $this->viewResponse('modules/stories/edit', [
24672ac996dSGreg Roach            'block_id'    => $block_id,
24772ac996dSGreg Roach            'languages'   => $languages,
24872ac996dSGreg Roach            'story_body'  => $story_body,
24972ac996dSGreg Roach            'story_title' => $story_title,
25072ac996dSGreg Roach            'title'       => $title,
25172ac996dSGreg Roach            'tree'        => $tree,
25272ac996dSGreg Roach            'individual'  => $individual,
25372ac996dSGreg Roach        ]);
25472ac996dSGreg Roach    }
25572ac996dSGreg Roach
25672ac996dSGreg Roach    /**
25772ac996dSGreg Roach     * @param Request $request
258b6db7c1fSGreg Roach     * @param Tree    $tree
25972ac996dSGreg Roach     *
26072ac996dSGreg Roach     * @return RedirectResponse
26172ac996dSGreg Roach     */
262b6db7c1fSGreg Roach    public function postAdminEditAction(Request $request, Tree $tree): RedirectResponse
263c1010edaSGreg Roach    {
26472ac996dSGreg Roach        $block_id    = (int) $request->get('block_id');
26572ac996dSGreg Roach        $xref        = $request->get('xref', '');
26672ac996dSGreg Roach        $story_body  = $request->get('story_body', '');
26772ac996dSGreg Roach        $story_title = $request->get('story_title', '');
26872ac996dSGreg Roach        $languages   = $request->get('languages', []);
26972ac996dSGreg Roach
27072ac996dSGreg Roach        if ($block_id !== 0) {
27172ac996dSGreg Roach            Database::prepare(
27272ac996dSGreg Roach                "UPDATE `##block` SET gedcom_id = :tree_id, xref = :xref WHERE block_id = :block_id"
27372ac996dSGreg Roach            )->execute([
27472ac996dSGreg Roach                'tree_id'  => $tree->getTreeId(),
27572ac996dSGreg Roach                'xref'     => $xref,
27672ac996dSGreg Roach                'block_id' => $block_id,
27772ac996dSGreg Roach            ]);
27872ac996dSGreg Roach        } else {
27972ac996dSGreg Roach            Database::prepare(
28072ac996dSGreg Roach                "INSERT INTO `##block` (gedcom_id, xref, module_name, block_order) VALUES (:tree_id, :xref, 'stories', 0)"
28172ac996dSGreg Roach            )->execute([
28272ac996dSGreg Roach                'tree_id' => $tree->getTreeId(),
28372ac996dSGreg Roach                'xref'    => $xref,
28472ac996dSGreg Roach            ]);
28572ac996dSGreg Roach
286*36e59714SGreg Roach            $block_id = Database::lastInsertId();
28772ac996dSGreg Roach        }
28872ac996dSGreg Roach
28972ac996dSGreg Roach        $this->setBlockSetting($block_id, 'story_body', $story_body);
29072ac996dSGreg Roach        $this->setBlockSetting($block_id, 'title', $story_title);
29172ac996dSGreg Roach        $this->setBlockSetting($block_id, 'languages', implode(',', $languages));
29272ac996dSGreg Roach
293c1010edaSGreg Roach        $url = route('module', [
294c1010edaSGreg Roach            'module' => 'stories',
295c1010edaSGreg Roach            'action' => 'Admin',
296c1010edaSGreg Roach            'ged'    => $tree->getName(),
297c1010edaSGreg Roach        ]);
29872ac996dSGreg Roach
29972ac996dSGreg Roach        return new RedirectResponse($url);
30072ac996dSGreg Roach    }
30172ac996dSGreg Roach
30272ac996dSGreg Roach    /**
30372ac996dSGreg Roach     * @param Request $request
304b6db7c1fSGreg Roach     * @param Tree    $tree
30572ac996dSGreg Roach     *
30672ac996dSGreg Roach     * @return Response
30772ac996dSGreg Roach     */
308b6db7c1fSGreg Roach    public function postAdminDeleteAction(Request $request, Tree $tree): Response
309c1010edaSGreg Roach    {
31072ac996dSGreg Roach        $block_id = (int) $request->get('block_id');
31172ac996dSGreg Roach
31272ac996dSGreg Roach        Database::prepare(
31372ac996dSGreg Roach            "DELETE FROM `##block_setting` WHERE block_id = :block_id"
31472ac996dSGreg Roach        )->execute([
31572ac996dSGreg Roach            'block_id' => $block_id,
31672ac996dSGreg Roach        ]);
31772ac996dSGreg Roach
31872ac996dSGreg Roach        Database::prepare(
31972ac996dSGreg Roach            "DELETE FROM `##block` WHERE block_id = :block_id"
32072ac996dSGreg Roach        )->execute([
32172ac996dSGreg Roach            'block_id' => $block_id,
32272ac996dSGreg Roach        ]);
32372ac996dSGreg Roach
324c1010edaSGreg Roach        $url = route('module', [
325c1010edaSGreg Roach            'module' => 'stories',
326c1010edaSGreg Roach            'action' => 'Admin',
327c1010edaSGreg Roach            'ged'    => $tree->getName(),
328c1010edaSGreg Roach        ]);
32972ac996dSGreg Roach
33072ac996dSGreg Roach        return new RedirectResponse($url);
33172ac996dSGreg Roach    }
33272ac996dSGreg Roach
33372ac996dSGreg Roach    /**
334b6db7c1fSGreg Roach     * @param Tree $tree
33572ac996dSGreg Roach     *
33672ac996dSGreg Roach     * @return Response
33772ac996dSGreg Roach     */
338b6db7c1fSGreg Roach    public function getShowListAction(Tree $tree): Response
339c1010edaSGreg Roach    {
34072ac996dSGreg Roach        $stories = Database::prepare(
34172ac996dSGreg Roach            "SELECT block_id, xref" .
34272ac996dSGreg Roach            " FROM `##block` b" .
34372ac996dSGreg Roach            " WHERE module_name = :module_name" .
34472ac996dSGreg Roach            " AND gedcom_id = :tree_id" .
34572ac996dSGreg Roach            " ORDER BY xref"
34672ac996dSGreg Roach        )->execute([
34772ac996dSGreg Roach            'module_name' => $this->getName(),
34872ac996dSGreg Roach            'tree_id'     => $tree->getTreeId(),
34972ac996dSGreg Roach        ])->fetchAll();
35072ac996dSGreg Roach
35172ac996dSGreg Roach        foreach ($stories as $story) {
35272ac996dSGreg Roach            $story->individual = Individual::getInstance($story->xref, $tree);
35372ac996dSGreg Roach            $story->title      = $this->getBlockSetting($story->block_id, 'title');
35472ac996dSGreg Roach            $story->languages  = $this->getBlockSetting($story->block_id, 'languages');
35572ac996dSGreg Roach        }
35672ac996dSGreg Roach
35772ac996dSGreg Roach        // Filter non-existant and private individuals.
358492c7072SGreg Roach        $stories = array_filter($stories, function (stdClass $story): bool {
35972ac996dSGreg Roach            return $story->individual !== null && $story->individual->canShow();
36072ac996dSGreg Roach        });
36172ac996dSGreg Roach
36272ac996dSGreg Roach        // Filter foreign languages.
363492c7072SGreg Roach        $stories = array_filter($stories, function (stdClass $story): bool {
364a44b57a9SGreg Roach            return $story->languages === '' || in_array(WT_LOCALE, explode(',', $story->languages));
36572ac996dSGreg Roach        });
36672ac996dSGreg Roach
36772ac996dSGreg Roach        return $this->viewResponse('modules/stories/list', [
36872ac996dSGreg Roach            'stories' => $stories,
36972ac996dSGreg Roach            'title'   => $this->getTitle(),
37072ac996dSGreg Roach        ]);
37172ac996dSGreg Roach    }
3728c2e8227SGreg Roach}
373