xref: /webtrees/app/Module/StoriesModule.php (revision e9e853987811e8bd423dccf26f6ef57571f393eb)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2019 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 */
16declare(strict_types=1);
17
18namespace Fisharebest\Webtrees\Module;
19
20use Fisharebest\Webtrees\Auth;
21use Fisharebest\Webtrees\I18N;
22use Fisharebest\Webtrees\Individual;
23use Fisharebest\Webtrees\Menu;
24use Fisharebest\Webtrees\Tree;
25use Illuminate\Database\Capsule\Manager as DB;
26use stdClass;
27use Symfony\Component\HttpFoundation\RedirectResponse;
28use Symfony\Component\HttpFoundation\Request;
29use Symfony\Component\HttpFoundation\Response;
30
31/**
32 * Class StoriesModule
33 */
34class StoriesModule extends AbstractModule implements ModuleTabInterface, ModuleConfigInterface, ModuleMenuInterface
35{
36    /** {@inheritdoc} */
37    public function getTitle(): string
38    {
39        /* I18N: Name of a module */
40        return I18N::translate('Stories');
41    }
42
43    /** {@inheritdoc} */
44    public function getDescription(): string
45    {
46        /* I18N: Description of the “Stories” module */
47        return I18N::translate('Add narrative stories to individuals in the family tree.');
48    }
49
50    /**
51     * The URL to a page where the user can modify the configuration of this module.
52     *
53     * @return string
54     */
55    public function getConfigLink(): string
56    {
57        return route('module', [
58            'module' => $this->getName(),
59            'action' => 'Admin',
60        ]);
61    }
62
63    /** {@inheritdoc} */
64    public function defaultTabOrder(): int
65    {
66        return 55;
67    }
68
69    /** {@inheritdoc} */
70    public function getTabContent(Individual $individual): string
71    {
72        return view('modules/stories/tab', [
73            'is_admin'   => Auth::isAdmin(),
74            'individual' => $individual,
75            'stories'    => $this->getStoriesForIndividual($individual),
76        ]);
77    }
78
79    /** {@inheritdoc} */
80    public function hasTabContent(Individual $individual): bool
81    {
82        return Auth::isManager($individual->tree()) || !empty($this->getStoriesForIndividual($individual));
83    }
84
85    /** {@inheritdoc} */
86    public function isGrayedOut(Individual $individual): bool
87    {
88        return !empty($this->getStoriesForIndividual($individual));
89    }
90
91    /** {@inheritdoc} */
92    public function canLoadAjax(): bool
93    {
94        return false;
95    }
96
97    /**
98     * @param Individual $individual
99     *
100     * @return stdClass[]
101     */
102    private function getStoriesForIndividual(Individual $individual): array
103    {
104        $block_ids = DB::table('block')
105            ->where('module_name', '=', $this->getName())
106            ->where('xref', '=', $individual->xref())
107            ->where('gedcom_id', '=', $individual->tree()->id())
108            ->pluck('block_id');
109
110        $stories = [];
111        foreach ($block_ids as $block_id) {
112            $block_id = (int) $block_id;
113
114            // Only show this block for certain languages
115            $languages = $this->getBlockSetting($block_id, 'languages', '');
116            if ($languages === '' || in_array(WT_LOCALE, explode(',', $languages))) {
117                $stories[] = (object) [
118                    'block_id'   => $block_id,
119                    'title'      => $this->getBlockSetting($block_id, 'title'),
120                    'story_body' => $this->getBlockSetting($block_id, 'story_body'),
121                ];
122            }
123        }
124
125        return $stories;
126    }
127
128    /**
129     * The user can re-order menus. Until they do, they are shown in this order.
130     *
131     * @return int
132     */
133    public function defaultMenuOrder(): int
134    {
135        return 30;
136    }
137
138    /**
139     * What is the default access level for this module?
140     *
141     * Some modules are aimed at admins or managers, and are not generally shown to users.
142     *
143     * @return int
144     */
145    public function defaultAccessLevel(): int
146    {
147        return Auth::PRIV_HIDE;
148    }
149
150    /**
151     * A menu, to be added to the main application menu.
152     *
153     * @param Tree $tree
154     *
155     * @return Menu|null
156     */
157    public function getMenu(Tree $tree)
158    {
159        $menu = new Menu($this->getTitle(), route('module', [
160            'module' => $this->getName(),
161            'action' => 'ShowList',
162            'ged'    => $tree->name(),
163        ]), 'menu-story');
164
165        return $menu;
166    }
167
168    /**
169     * @param Tree $tree
170     *
171     * @return Response
172     */
173    public function getAdminAction(Tree $tree): Response
174    {
175        $this->layout = 'layouts/administration';
176
177        $stories = DB::table('block')
178            ->where('module_name', '=', $this->getName())
179            ->where('gedcom_id', '=', $tree->id())
180            ->orderBy('xref')
181            ->get();
182
183        foreach ($stories as $story) {
184            $block_id = (int) $story->block_id;
185
186            $story->individual = Individual::getInstance($story->xref, $tree);
187            $story->title      = $this->getBlockSetting($block_id, 'title');
188            $story->languages  = $this->getBlockSetting($block_id, 'languages');
189        }
190
191        return $this->viewResponse('modules/stories/config', [
192            'stories'    => $stories,
193            'title'      => $this->getTitle() . ' — ' . $tree->title(),
194            'tree'       => $tree,
195            'tree_names' => Tree::getNameList(),
196        ]);
197    }
198
199    /**
200     * @param Request $request
201     * @param Tree    $tree
202     *
203     * @return Response
204     */
205    public function getAdminEditAction(Request $request, Tree $tree): Response
206    {
207        $this->layout = 'layouts/administration';
208
209        $block_id = (int) $request->get('block_id');
210
211        if ($block_id === 0) {
212            // Creating a new story
213            $individual  = Individual::getInstance($request->get('xref', ''), $tree);
214            $story_title = '';
215            $story_body  = '';
216            $languages   = [];
217
218            $title = I18N::translate('Add a story') . ' — ' . e($tree->title());
219        } else {
220            // Editing an existing story
221            $xref = (string) DB::table('block')
222                ->where('block_id', '=', $block_id)
223                ->value('xref');
224
225            $individual  = Individual::getInstance($xref, $tree);
226            $story_title = $this->getBlockSetting($block_id, 'title', '');
227            $story_body  = $this->getBlockSetting($block_id, 'story_body', '');
228            $languages   = explode(',', $this->getBlockSetting($block_id, 'languages'));
229
230            $title = I18N::translate('Edit the story') . ' — ' . e($tree->title());
231        }
232
233        return $this->viewResponse('modules/stories/edit', [
234            'block_id'    => $block_id,
235            'languages'   => $languages,
236            'story_body'  => $story_body,
237            'story_title' => $story_title,
238            'title'       => $title,
239            'tree'        => $tree,
240            'individual'  => $individual,
241        ]);
242    }
243
244    /**
245     * @param Request $request
246     * @param Tree    $tree
247     *
248     * @return RedirectResponse
249     */
250    public function postAdminEditAction(Request $request, Tree $tree): RedirectResponse
251    {
252        $block_id    = (int) $request->get('block_id');
253        $xref        = $request->get('xref', '');
254        $story_body  = $request->get('story_body', '');
255        $story_title = $request->get('story_title', '');
256        $languages   = $request->get('languages', []);
257
258        if ($block_id !== 0) {
259            DB::table('block')
260                ->where('block_id', '=', $block_id)
261                ->update([
262                    'gedcom_id' => $tree->id(),
263                    'xref'      => $xref,
264                ]);
265        } else {
266            DB::table('block')->insert([
267                'gedcom_id'   => $tree->id(),
268                'xref'        => $xref,
269                'module_name' => $this->getName(),
270                'block_order' => 0,
271            ]);
272
273            $block_id = (int) DB::connection()->getPdo()->lastInsertId();
274        }
275
276        $this->setBlockSetting($block_id, 'story_body', $story_body);
277        $this->setBlockSetting($block_id, 'title', $story_title);
278        $this->setBlockSetting($block_id, 'languages', implode(',', $languages));
279
280        $url = route('module', [
281            'module' => 'stories',
282            'action' => 'Admin',
283            'ged'    => $tree->name(),
284        ]);
285
286        return new RedirectResponse($url);
287    }
288
289    /**
290     * @param Request $request
291     * @param Tree    $tree
292     *
293     * @return Response
294     */
295    public function postAdminDeleteAction(Request $request, Tree $tree): Response
296    {
297        $block_id = (int) $request->get('block_id');
298
299        DB::table('block_setting')
300            ->where('block_id', '=', $block_id)
301            ->delete();
302
303        DB::table('block')
304            ->where('block_id', '=', $block_id)
305            ->delete();
306
307        $url = route('module', [
308            'module' => 'stories',
309            'action' => 'Admin',
310            'ged'    => $tree->name(),
311        ]);
312
313        return new RedirectResponse($url);
314    }
315
316    /**
317     * @param Tree $tree
318     *
319     * @return Response
320     */
321    public function getShowListAction(Tree $tree): Response
322    {
323        $stories = DB::table('block')
324            ->where('module_name', '=', $this->getName())
325            ->where('gedcom_id', '=', $tree->id())
326            ->get()
327            ->map(function (stdClass $story) use ($tree): stdClass {
328                $block_id = (int) $story->block_id;
329
330                $story->individual = Individual::getInstance($story->xref, $tree);
331                $story->title      = $this->getBlockSetting($block_id, 'title');
332                $story->languages  = $this->getBlockSetting($block_id, 'languages');
333
334                return $story;
335            })->filter(function (stdClass $story): bool {
336                // Filter non-existant and private individuals.
337                return $story->individual instanceof Individual && $story->individual->canShow();
338            })->filter(function (stdClass $story): bool {
339                // Filter foreign languages.
340                return $story->languages === '' || in_array(WT_LOCALE, explode(',', $story->languages));
341            });
342
343        return $this->viewResponse('modules/stories/list', [
344            'stories' => $stories,
345            'title'   => $this->getTitle(),
346        ]);
347    }
348}
349