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