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