xref: /webtrees/app/Module/StoriesModule.php (revision da83637ca6236094f5a00d6e54530cd25ac7aa0e)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2018 webtrees development team
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16namespace Fisharebest\Webtrees\Module;
17
18use Fisharebest\Webtrees\Auth;
19use Fisharebest\Webtrees\Database;
20use Fisharebest\Webtrees\I18N;
21use Fisharebest\Webtrees\Individual;
22use Fisharebest\Webtrees\Menu;
23use Fisharebest\Webtrees\Tree;
24use stdClass;
25use Symfony\Component\HttpFoundation\RedirectResponse;
26use Symfony\Component\HttpFoundation\Request;
27use Symfony\Component\HttpFoundation\Response;
28
29/**
30 * Class StoriesModule
31 */
32class StoriesModule extends AbstractModule implements ModuleTabInterface, ModuleConfigInterface, ModuleMenuInterface
33{
34    /** {@inheritdoc} */
35    public function getTitle()
36    {
37        return /* I18N: Name of a module */
38            I18N::translate('Stories');
39    }
40
41    /** {@inheritdoc} */
42    public function getDescription()
43    {
44        return /* I18N: Description of the “Stories” module */
45            I18N::translate('Add narrative stories to individuals in the family tree.');
46    }
47
48    /**
49     * The URL to a page where the user can modify the configuration of this module.
50     *
51     * @return string
52     */
53    public function getConfigLink()
54    {
55        return route('module', [
56            'module' => $this->getName(),
57            'action' => 'Admin',
58        ]);
59    }
60
61    /** {@inheritdoc} */
62    public function defaultTabOrder()
63    {
64        return 55;
65    }
66
67    /** {@inheritdoc} */
68    public function getTabContent(Individual $individual)
69    {
70        return view('modules/stories/tab', [
71            'is_admin'   => Auth::isAdmin(),
72            'individual' => $individual,
73            'stories'    => $this->getStoriesForIndividual($individual),
74        ]);
75    }
76
77    /** {@inheritdoc} */
78    public function hasTabContent(Individual $individual)
79    {
80        return Auth::isManager($individual->getTree()) || !empty($this->getStoriesForIndividual($individual));
81    }
82
83    /** {@inheritdoc} */
84    public function isGrayedOut(Individual $individual)
85    {
86        return !empty($this->getStoriesForIndividual($individual));
87    }
88
89    /** {@inheritdoc} */
90    public function canLoadAjax()
91    {
92        return false;
93    }
94
95    /**
96     * @param Individual $individual
97     *
98     * @return stdClass[]
99     */
100    private function getStoriesForIndividual(Individual $individual): array
101    {
102        $block_ids =
103            Database::prepare(
104                "SELECT block_id" .
105                " FROM `##block`" .
106                " WHERE module_name = :module_name" .
107                " AND xref          = :xref" .
108                " AND gedcom_id     = :tree_id"
109            )->execute([
110                'module_name' => $this->getName(),
111                'xref'        => $individual->getXref(),
112                'tree_id'     => $individual->getTree()->getTreeId(),
113            ])->fetchOneColumn();
114
115        $stories = [];
116        foreach ($block_ids as $block_id) {
117            // Only show this block for certain languages
118            $languages = $this->getBlockSetting($block_id, 'languages', '');
119            if ($languages === '' || in_array(WT_LOCALE, explode(',', $languages))) {
120                $stories[] = (object)[
121                    'block_id'   => $block_id,
122                    'title'      => $this->getBlockSetting($block_id, 'title'),
123                    'story_body' => $this->getBlockSetting($block_id, 'story_body'),
124                ];
125            }
126        }
127
128        return $stories;
129    }
130
131    /**
132     * The user can re-order menus. Until they do, they are shown in this order.
133     *
134     * @return int
135     */
136    public function defaultMenuOrder()
137    {
138        return 30;
139    }
140
141    /**
142     * What is the default access level for this module?
143     *
144     * Some modules are aimed at admins or managers, and are not generally shown to users.
145     *
146     * @return int
147     */
148    public function defaultAccessLevel()
149    {
150        return Auth::PRIV_HIDE;
151    }
152
153    /**
154     * A menu, to be added to the main application menu.
155     *
156     * @param Tree $tree
157     *
158     * @return Menu|null
159     */
160    public function getMenu(Tree $tree)
161    {
162        $menu = new Menu($this->getTitle(), route('module', [
163            'module' => $this->getName(),
164            'action' => 'ShowList',
165        ]), 'menu-story');
166
167        return $menu;
168    }
169
170    /**
171     * @param Request $request
172     *
173     * @return Response
174     */
175    public function getAdminAction(Request $request): Response
176    {
177        /** @var Tree $tree */
178        $tree = $request->attributes->get('tree');
179
180        $this->layout = 'layouts/administration';
181
182        $stories = Database::prepare(
183            "SELECT block_id, xref, gedcom_id" .
184            " FROM `##block` b" .
185            " WHERE module_name = :module_name" .
186            " AND gedcom_id = :tree_id" .
187            " ORDER BY gedcom_id, xref"
188        )->execute([
189            'tree_id'     => $tree->getTreeId(),
190            'module_name' => $this->getName(),
191        ])->fetchAll();
192
193        foreach ($stories as $story) {
194            $story->individual = Individual::getInstance($story->xref, $tree);
195            $story->title      = $this->getBlockSetting($story->block_id, 'title');
196            $story->languages  = $this->getBlockSetting($story->block_id, 'languages');
197        }
198
199        return $this->viewResponse('modules/stories/config', [
200            'stories'    => $stories,
201            'title'      => $this->getTitle() . ' — ' . $tree->getTitle(),
202            'tree'       => $tree,
203            'tree_names' => Tree::getNameList(),
204        ]);
205    }
206
207    /**
208     * @param Request $request
209     *
210     * @return Response
211     */
212    public function getAdminEditAction(Request $request): Response
213    {
214        /** @var Tree $tree */
215        $tree = $request->attributes->get('tree');
216
217        $this->layout = 'layouts/administration';
218
219        $block_id = (int)$request->get('block_id');
220
221        if ($block_id === 0) {
222            // Creating a new story
223            $individual  = Individual::getInstance($request->get('xref', ''), $tree);
224            $story_title = '';
225            $story_body  = '';
226            $languages   = [];
227
228            $title = I18N::translate('Add a story') . ' — ' . e($tree->getTitle());
229        } else {
230            // Editing an existing story
231            $xref = Database::prepare(
232                "SELECT xref FROM `##block` WHERE block_id = :block_id"
233            )->execute([
234                'block_id' => $block_id,
235            ])->fetchOne();
236
237            $individual  = Individual::getInstance($xref, $tree);
238            $story_title = $this->getBlockSetting($block_id, 'title', '');
239            $story_body  = $this->getBlockSetting($block_id, 'story_body', '');
240            $languages   = explode(',', $this->getBlockSetting($block_id, 'languages'));
241
242            $title = I18N::translate('Edit the story') . ' — ' . e($tree->getTitle());
243        }
244
245        return $this->viewResponse('modules/stories/edit', [
246            'block_id'    => $block_id,
247            'languages'   => $languages,
248            'story_body'  => $story_body,
249            'story_title' => $story_title,
250            'title'       => $title,
251            'tree'        => $tree,
252            'individual'  => $individual,
253        ]);
254    }
255
256    /**
257     * @param Request $request
258     *
259     * @return RedirectResponse
260     */
261    public function postAdminEditAction(Request $request): RedirectResponse
262    {
263        /** @var Tree $tree */
264        $tree = $request->attributes->get('tree');
265
266        $block_id    = (int)$request->get('block_id');
267        $xref        = $request->get('xref', '');
268        $story_body  = $request->get('story_body', '');
269        $story_title = $request->get('story_title', '');
270        $languages   = $request->get('languages', []);
271
272        if ($block_id !== 0) {
273            Database::prepare(
274                "UPDATE `##block` SET gedcom_id = :tree_id, xref = :xref WHERE block_id = :block_id"
275            )->execute([
276                'tree_id'  => $tree->getTreeId(),
277                'xref'     => $xref,
278                'block_id' => $block_id,
279            ]);
280        } else {
281            Database::prepare(
282                "INSERT INTO `##block` (gedcom_id, xref, module_name, block_order) VALUES (:tree_id, :xref, 'stories', 0)"
283            )->execute([
284                'tree_id' => $tree->getTreeId(),
285                'xref'    => $xref,
286            ]);
287
288            $block_id = Database::getInstance()->lastInsertId();
289        }
290
291        $this->setBlockSetting($block_id, 'story_body', $story_body);
292        $this->setBlockSetting($block_id, 'title', $story_title);
293        $this->setBlockSetting($block_id, 'languages', implode(',', $languages));
294
295        $url = route('module', [
296            'module' => 'stories',
297            'action' => 'Admin',
298            'ged'    => $tree->getName(),
299        ]);
300
301        return new RedirectResponse($url);
302    }
303
304    /**
305     * @param Request $request
306     *
307     * @return Response
308     */
309    public function postAdminDeleteAction(Request $request): Response
310    {
311        /** @var Tree $tree */
312        $tree = $request->attributes->get('tree');
313
314        $block_id = (int)$request->get('block_id');
315
316        Database::prepare(
317            "DELETE FROM `##block_setting` WHERE block_id = :block_id"
318        )->execute([
319            'block_id' => $block_id,
320        ]);
321
322        Database::prepare(
323            "DELETE FROM `##block` WHERE block_id = :block_id"
324        )->execute([
325            'block_id' => $block_id,
326        ]);
327
328        $url = route('module', [
329            'module' => 'stories',
330            'action' => 'Admin',
331            'ged'    => $tree->getName(),
332        ]);
333
334        return new RedirectResponse($url);
335    }
336
337    /**
338     * @param Request $request
339     *
340     * @return Response
341     */
342    public function getShowListAction(Request $request): Response
343    {
344        /** @var Tree $tree
345         */
346        $tree = $request->attributes->get('tree');
347
348        $stories = Database::prepare(
349            "SELECT block_id, xref" .
350            " FROM `##block` b" .
351            " WHERE module_name = :module_name" .
352            " AND gedcom_id = :tree_id" .
353            " ORDER BY xref"
354        )->execute([
355            'module_name' => $this->getName(),
356            'tree_id'     => $tree->getTreeId(),
357        ])->fetchAll();
358
359        foreach ($stories as $story) {
360            $story->individual = Individual::getInstance($story->xref, $tree);
361            $story->title      = $this->getBlockSetting($story->block_id, 'title');
362            $story->languages  = $this->getBlockSetting($story->block_id, 'languages');
363        }
364
365        // Filter non-existant and private individuals.
366        $stories = array_filter($stories, function (stdClass $story) {
367            return $story->individual !== null && $story->individual->canShow();
368        });
369
370        // Filter foreign languages.
371        $stories = array_filter($stories, function (stdClass $story) {
372            return $story->languages === '' || in_array(WT_LOCALE, explode(',', $story->languages));
373        });
374
375        return $this->viewResponse('modules/stories/list', [
376            'stories' => $stories,
377            'title'   => $this->getTitle(),
378        ]);
379    }
380}
381