Passed
Push — master ( a91af2...e218f3 )
by Greg
06:06
created

StoriesModule::postAdminAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2019 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Module;
21
22
use Fisharebest\Webtrees\Auth;
23
use Fisharebest\Webtrees\Http\RequestHandlers\ControlPanel;
24
use Fisharebest\Webtrees\I18N;
25
use Fisharebest\Webtrees\Individual;
26
use Fisharebest\Webtrees\Menu;
27
use Fisharebest\Webtrees\Services\HtmlService;
28
use Fisharebest\Webtrees\Services\TreeService;
29
use Fisharebest\Webtrees\Tree;
30
use Illuminate\Database\Capsule\Manager as DB;
31
use InvalidArgumentException;
32
use Psr\Http\Message\ResponseInterface;
33
use Psr\Http\Message\ServerRequestInterface;
34
use stdClass;
35
36
use function assert;
37
use function redirect;
38
use function route;
39
40
/**
41
 * Class StoriesModule
42
 */
43
class StoriesModule extends AbstractModule implements ModuleConfigInterface, ModuleMenuInterface, ModuleTabInterface
44
{
45
    use ModuleTabTrait;
46
    use ModuleConfigTrait;
47
    use ModuleMenuTrait;
48
49
    /** @var HtmlService */
50
    private $html_service;
51
52
    /** @var TreeService */
53
    private $tree_service;
54
55
    /**
56
     * BatchUpdateModule constructor.
57
     *
58
     * @param HtmlService $html_service
59
     * @param TreeService $tree_service
60
     */
61
    public function __construct(HtmlService $html_service, TreeService $tree_service)
62
    {
63
        $this->html_service = $html_service;
64
        $this->tree_service = $tree_service;
65
    }
66
67
    /** @var int The default access level for this module.  It can be changed in the control panel. */
68
    protected $access_level = Auth::PRIV_HIDE;
69
70
    /**
71
     * A sentence describing what this module does.
72
     *
73
     * @return string
74
     */
75
    public function description(): string
76
    {
77
        /* I18N: Description of the “Stories” module */
78
        return I18N::translate('Add narrative stories to individuals in the family tree.');
79
    }
80
81
    /**
82
     * The default position for this menu.  It can be changed in the control panel.
83
     *
84
     * @return int
85
     */
86
    public function defaultMenuOrder(): int
87
    {
88
        return 7;
89
    }
90
91
    /**
92
     * The default position for this tab.  It can be changed in the control panel.
93
     *
94
     * @return int
95
     */
96
    public function defaultTabOrder(): int
97
    {
98
        return 9;
99
    }
100
101
    /**
102
     * Generate the HTML content of this tab.
103
     *
104
     * @param Individual $individual
105
     *
106
     * @return string
107
     */
108
    public function getTabContent(Individual $individual): string
109
    {
110
        return view('modules/stories/tab', [
111
            'is_admin'   => Auth::isAdmin(),
112
            'individual' => $individual,
113
            'stories'    => $this->getStoriesForIndividual($individual),
114
        ]);
115
    }
116
117
    /**
118
     * @param Individual $individual
119
     *
120
     * @return stdClass[]
121
     */
122
    private function getStoriesForIndividual(Individual $individual): array
123
    {
124
        $block_ids = DB::table('block')
125
            ->where('module_name', '=', $this->name())
126
            ->where('xref', '=', $individual->xref())
127
            ->where('gedcom_id', '=', $individual->tree()->id())
128
            ->pluck('block_id');
129
130
        $stories = [];
131
        foreach ($block_ids as $block_id) {
132
            $block_id = (int) $block_id;
133
134
            // Only show this block for certain languages
135
            $languages = $this->getBlockSetting($block_id, 'languages');
136
            if ($languages === '' || in_array(WT_LOCALE, explode(',', $languages), true)) {
137
                $stories[] = (object) [
138
                    'block_id'   => $block_id,
139
                    'title'      => $this->getBlockSetting($block_id, 'title'),
140
                    'story_body' => $this->getBlockSetting($block_id, 'story_body'),
141
                ];
142
            }
143
        }
144
145
        return $stories;
146
    }
147
148
    /**
149
     * Is this tab empty? If so, we don't always need to display it.
150
     *
151
     * @param Individual $individual
152
     *
153
     * @return bool
154
     */
155
    public function hasTabContent(Individual $individual): bool
156
    {
157
        return Auth::isManager($individual->tree()) || $this->getStoriesForIndividual($individual) !== [];
158
    }
159
160
    /**
161
     * A greyed out tab has no actual content, but may perhaps have
162
     * options to create content.
163
     *
164
     * @param Individual $individual
165
     *
166
     * @return bool
167
     */
168
    public function isGrayedOut(Individual $individual): bool
169
    {
170
        return $this->getStoriesForIndividual($individual) !== [];
171
    }
172
173
    /**
174
     * Can this tab load asynchronously?
175
     *
176
     * @return bool
177
     */
178
    public function canLoadAjax(): bool
179
    {
180
        return false;
181
    }
182
183
    /**
184
     * A menu, to be added to the main application menu.
185
     *
186
     * @param Tree $tree
187
     *
188
     * @return Menu|null
189
     */
190
    public function getMenu(Tree $tree): ?Menu
191
    {
192
        $menu = new Menu($this->title(), route('module', [
193
            'module' => $this->name(),
194
            'action' => 'ShowList',
195
            'tree'    => $tree->name(),
196
        ]), 'menu-story');
197
198
        return $menu;
199
    }
200
201
    /**
202
     * How should this module be identified in the control panel, etc.?
203
     *
204
     * @return string
205
     */
206
    public function title(): string
207
    {
208
        /* I18N: Name of a module */
209
        return I18N::translate('Stories');
210
    }
211
212
    /**
213
     * @param ServerRequestInterface $request
214
     *
215
     * @return ResponseInterface
216
     */
217
    public function getAdminAction(ServerRequestInterface $request): ResponseInterface
218
    {
219
        $this->layout = 'layouts/administration';
220
221
        // This module can't run without a tree
222
        $tree = $request->getAttribute('tree');
223
224
        if (!$tree instanceof Tree) {
225
            $tree = $this->tree_service->all()->first();
226
            if ($tree instanceof Tree) {
227
                return redirect(route('module', ['module' => $this->name(), 'action' => 'Admin', 'tree' => $tree->name()]));
228
            }
229
230
            return redirect(route(ControlPanel::class));
231
        }
232
233
        $stories = DB::table('block')
234
            ->where('module_name', '=', $this->name())
235
            ->where('gedcom_id', '=', $tree->id())
236
            ->orderBy('xref')
237
            ->get();
238
239
        foreach ($stories as $story) {
240
            $block_id = (int) $story->block_id;
241
242
            $story->individual = Individual::getInstance($story->xref, $tree);
243
            $story->title      = $this->getBlockSetting($block_id, 'title');
244
            $story->languages  = $this->getBlockSetting($block_id, 'languages');
245
        }
246
247
        return $this->viewResponse('modules/stories/config', [
248
            'module'     => $this->name(),
249
            'stories'    => $stories,
250
            'title'      => $this->title() . ' — ' . $tree->title(),
251
            'tree'       => $tree,
252
            'tree_names' => Tree::getNameList(),
253
        ]);
254
    }
255
256
    /**
257
     * @param ServerRequestInterface $request
258
     *
259
     * @return ResponseInterface
260
     */
261
    public function postAdminAction(ServerRequestInterface $request): ResponseInterface
262
    {
263
        return redirect(route('module', [
264
            'module' => $this->name(),
265
            'action' => 'Admin',
266
            'tree'   => $request->getParsedBody()['tree'] ?? '',
267
        ]));
268
    }
269
270
    /**
271
     * @param ServerRequestInterface $request
272
     *
273
     * @return ResponseInterface
274
     */
275
    public function getAdminEditAction(ServerRequestInterface $request): ResponseInterface
276
    {
277
        $this->layout = 'layouts/administration';
278
279
        $tree     = $request->getAttribute('tree');
280
        $block_id = (int) ($request->getQueryParams()['block_id'] ?? 0);
281
282
        if ($block_id === 0) {
283
            // Creating a new story
284
            $individual  = null;
285
            $story_title = '';
286
            $story_body  = '';
287
            $languages   = [];
288
289
            $title = I18N::translate('Add a story') . ' — ' . e($tree->title());
290
        } else {
291
            // Editing an existing story
292
            $xref = (string) DB::table('block')
293
                ->where('block_id', '=', $block_id)
294
                ->value('xref');
295
296
            $individual  = Individual::getInstance($xref, $tree);
297
            $story_title = $this->getBlockSetting($block_id, 'title');
298
            $story_body  = $this->getBlockSetting($block_id, 'story_body');
299
            $languages   = explode(',', $this->getBlockSetting($block_id, 'languages'));
300
301
            $title = I18N::translate('Edit the story') . ' — ' . e($tree->title());
302
        }
303
304
        return $this->viewResponse('modules/stories/edit', [
305
            'block_id'    => $block_id,
306
            'languages'   => $languages,
307
            'story_body'  => $story_body,
308
            'story_title' => $story_title,
309
            'title'       => $title,
310
            'tree'        => $tree,
311
            'individual'  => $individual,
312
        ]);
313
    }
314
315
    /**
316
     * @param ServerRequestInterface $request
317
     *
318
     * @return ResponseInterface
319
     */
320
    public function postAdminEditAction(ServerRequestInterface $request): ResponseInterface
321
    {
322
        $tree     = $request->getAttribute('tree');
323
        $block_id = (int) ($request->getQueryParams()['block_id'] ?? 0);
324
325
        $params = $request->getParsedBody();
326
327
        $xref        = $params['xref'];
328
        $story_body  = $params['story_body'];
329
        $story_title = $params['story_title'];
330
        $languages   = $params['languages'] ?? [];
331
332
        $story_body  = $this->html_service->sanitize($story_body);
333
        $story_title = $this->html_service->sanitize($story_title);
334
335
        if ($block_id !== 0) {
336
            DB::table('block')
337
                ->where('block_id', '=', $block_id)
338
                ->update([
339
                    'gedcom_id' => $tree->id(),
340
                    'xref'      => $xref,
341
                ]);
342
        } else {
343
            DB::table('block')->insert([
344
                'gedcom_id'   => $tree->id(),
345
                'xref'        => $xref,
346
                'module_name' => $this->name(),
347
                'block_order' => 0,
348
            ]);
349
350
            $block_id = (int) DB::connection()->getPdo()->lastInsertId();
351
        }
352
353
        $this->setBlockSetting($block_id, 'story_body', $story_body);
354
        $this->setBlockSetting($block_id, 'title', $story_title);
355
        $this->setBlockSetting($block_id, 'languages', implode(',', $languages));
356
357
        $url = route('module', [
358
            'module' => $this->name(),
359
            'action' => 'Admin',
360
            'tree'    => $tree->name(),
361
        ]);
362
363
        return redirect($url);
364
    }
365
366
    /**
367
     * @param ServerRequestInterface $request
368
     *
369
     * @return ResponseInterface
370
     */
371
    public function postAdminDeleteAction(ServerRequestInterface $request): ResponseInterface
372
    {
373
        $tree     = $request->getAttribute('tree');
374
        $block_id = $request->getQueryParams()['block_id'];
375
376
        DB::table('block_setting')
377
            ->where('block_id', '=', $block_id)
378
            ->delete();
379
380
        DB::table('block')
381
            ->where('block_id', '=', $block_id)
382
            ->delete();
383
384
        $url = route('module', [
385
            'module' => $this->name(),
386
            'action' => 'Admin',
387
            'tree'    => $tree->name(),
388
        ]);
389
390
        return redirect($url);
391
    }
392
393
    /**
394
     * @param ServerRequestInterface $request
395
     *
396
     * @return ResponseInterface
397
     */
398
    public function getShowListAction(ServerRequestInterface $request): ResponseInterface
399
    {
400
        $tree = $request->getAttribute('tree');
401
        assert($tree instanceof Tree, new InvalidArgumentException());
402
403
        $stories = DB::table('block')
404
            ->where('module_name', '=', $this->name())
405
            ->where('gedcom_id', '=', $tree->id())
406
            ->get()
407
            ->map(function (stdClass $story) use ($tree): stdClass {
408
                $block_id = (int) $story->block_id;
409
410
                $story->individual = Individual::getInstance($story->xref, $tree);
411
                $story->title      = $this->getBlockSetting($block_id, 'title');
412
                $story->languages  = $this->getBlockSetting($block_id, 'languages');
413
414
                return $story;
415
            })->filter(static function (stdClass $story): bool {
416
                // Filter non-existant and private individuals.
417
                return $story->individual instanceof Individual && $story->individual->canShow();
418
            })->filter(static function (stdClass $story): bool {
419
                // Filter foreign languages.
420
                return $story->languages === '' || in_array(WT_LOCALE, explode(',', $story->languages), true);
421
            });
422
423
        return $this->viewResponse('modules/stories/list', [
424
            'stories' => $stories,
425
            'title'   => $this->title(),
426
        ]);
427
    }
428
}
429