xref: /webtrees/app/Module/StoriesModule.php (revision e218f363523a4bc74e0e636c21220b0954cff34d)
18c2e8227SGreg Roach<?php
23976b470SGreg Roach
38c2e8227SGreg Roach/**
48c2e8227SGreg Roach * webtrees: online genealogy
58fcd0d32SGreg Roach * Copyright (C) 2019 webtrees development team
68c2e8227SGreg Roach * This program is free software: you can redistribute it and/or modify
78c2e8227SGreg Roach * it under the terms of the GNU General Public License as published by
88c2e8227SGreg Roach * the Free Software Foundation, either version 3 of the License, or
98c2e8227SGreg Roach * (at your option) any later version.
108c2e8227SGreg Roach * This program is distributed in the hope that it will be useful,
118c2e8227SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
128c2e8227SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
138c2e8227SGreg Roach * GNU General Public License for more details.
148c2e8227SGreg Roach * You should have received a copy of the GNU General Public License
158c2e8227SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
168c2e8227SGreg Roach */
17fcfa147eSGreg Roach
18e7f56f2aSGreg Roachdeclare(strict_types=1);
19e7f56f2aSGreg Roach
2076692c8bSGreg Roachnamespace Fisharebest\Webtrees\Module;
2176692c8bSGreg Roach
220e62c4b8SGreg Roachuse Fisharebest\Webtrees\Auth;
23*e218f363SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\ControlPanel;
240e62c4b8SGreg Roachuse Fisharebest\Webtrees\I18N;
250e62c4b8SGreg Roachuse Fisharebest\Webtrees\Individual;
260e62c4b8SGreg Roachuse Fisharebest\Webtrees\Menu;
2750d6f48cSGreg Roachuse Fisharebest\Webtrees\Services\HtmlService;
28*e218f363SGreg Roachuse Fisharebest\Webtrees\Services\TreeService;
290e62c4b8SGreg Roachuse Fisharebest\Webtrees\Tree;
304b92b602SGreg Roachuse Illuminate\Database\Capsule\Manager as DB;
315229eadeSGreg Roachuse InvalidArgumentException;
326ccdf4f0SGreg Roachuse Psr\Http\Message\ResponseInterface;
336ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface;
34225e381fSGreg Roachuse stdClass;
3583d28054SGreg Roach
365229eadeSGreg Roachuse function assert;
37*e218f363SGreg Roachuse function redirect;
38*e218f363SGreg Roachuse function route;
398c2e8227SGreg Roach
408c2e8227SGreg Roach/**
418c2e8227SGreg Roach * Class StoriesModule
428c2e8227SGreg Roach */
4337eb8894SGreg Roachclass StoriesModule extends AbstractModule implements ModuleConfigInterface, ModuleMenuInterface, ModuleTabInterface
44c1010edaSGreg Roach{
4549a243cbSGreg Roach    use ModuleTabTrait;
4649a243cbSGreg Roach    use ModuleConfigTrait;
4749a243cbSGreg Roach    use ModuleMenuTrait;
4849a243cbSGreg Roach
4950d6f48cSGreg Roach    /** @var HtmlService */
5050d6f48cSGreg Roach    private $html_service;
5150d6f48cSGreg Roach
52*e218f363SGreg Roach    /** @var TreeService */
53*e218f363SGreg Roach    private $tree_service;
54*e218f363SGreg Roach
5550d6f48cSGreg Roach    /**
56*e218f363SGreg Roach     * BatchUpdateModule constructor.
5750d6f48cSGreg Roach     *
5850d6f48cSGreg Roach     * @param HtmlService $html_service
59*e218f363SGreg Roach     * @param TreeService $tree_service
6050d6f48cSGreg Roach     */
61*e218f363SGreg Roach    public function __construct(HtmlService $html_service, TreeService $tree_service)
6250d6f48cSGreg Roach    {
6350d6f48cSGreg Roach        $this->html_service = $html_service;
64*e218f363SGreg Roach        $this->tree_service = $tree_service;
6550d6f48cSGreg Roach    }
6650d6f48cSGreg Roach
6749a243cbSGreg Roach    /** @var int The default access level for this module.  It can be changed in the control panel. */
6849a243cbSGreg Roach    protected $access_level = Auth::PRIV_HIDE;
6949a243cbSGreg Roach
70961ec755SGreg Roach    /**
71961ec755SGreg Roach     * A sentence describing what this module does.
72961ec755SGreg Roach     *
73961ec755SGreg Roach     * @return string
74961ec755SGreg Roach     */
7549a243cbSGreg Roach    public function description(): string
76c1010edaSGreg Roach    {
77bbb76c12SGreg Roach        /* I18N: Description of the “Stories” module */
78bbb76c12SGreg Roach        return I18N::translate('Add narrative stories to individuals in the family tree.');
798c2e8227SGreg Roach    }
808c2e8227SGreg Roach
81aee13b6dSGreg Roach    /**
8249a243cbSGreg Roach     * The default position for this menu.  It can be changed in the control panel.
83aee13b6dSGreg Roach     *
8449a243cbSGreg Roach     * @return int
85aee13b6dSGreg Roach     */
8649a243cbSGreg Roach    public function defaultMenuOrder(): int
87c1010edaSGreg Roach    {
88353b36abSGreg Roach        return 7;
898c2e8227SGreg Roach    }
908c2e8227SGreg Roach
9149a243cbSGreg Roach    /**
9249a243cbSGreg Roach     * The default position for this tab.  It can be changed in the control panel.
9349a243cbSGreg Roach     *
9449a243cbSGreg Roach     * @return int
9549a243cbSGreg Roach     */
96cbf4b7faSGreg Roach    public function defaultTabOrder(): int
97cbf4b7faSGreg Roach    {
98fb7a0427SGreg Roach        return 9;
998c2e8227SGreg Roach    }
1008c2e8227SGreg Roach
1013caaa4d2SGreg Roach    /**
1023caaa4d2SGreg Roach     * Generate the HTML content of this tab.
1033caaa4d2SGreg Roach     *
1043caaa4d2SGreg Roach     * @param Individual $individual
1053caaa4d2SGreg Roach     *
1063caaa4d2SGreg Roach     * @return string
1073caaa4d2SGreg Roach     */
1089b34404bSGreg Roach    public function getTabContent(Individual $individual): string
109c1010edaSGreg Roach    {
11072ac996dSGreg Roach        return view('modules/stories/tab', [
11172ac996dSGreg Roach            'is_admin'   => Auth::isAdmin(),
112225e381fSGreg Roach            'individual' => $individual,
113225e381fSGreg Roach            'stories'    => $this->getStoriesForIndividual($individual),
114225e381fSGreg Roach        ]);
1158c2e8227SGreg Roach    }
1168c2e8227SGreg Roach
117225e381fSGreg Roach    /**
118225e381fSGreg Roach     * @param Individual $individual
119225e381fSGreg Roach     *
120225e381fSGreg Roach     * @return stdClass[]
121225e381fSGreg Roach     */
122c1010edaSGreg Roach    private function getStoriesForIndividual(Individual $individual): array
123c1010edaSGreg Roach    {
1244b92b602SGreg Roach        $block_ids = DB::table('block')
12526684e68SGreg Roach            ->where('module_name', '=', $this->name())
1264b92b602SGreg Roach            ->where('xref', '=', $individual->xref())
1274b92b602SGreg Roach            ->where('gedcom_id', '=', $individual->tree()->id())
1284b92b602SGreg Roach            ->pluck('block_id');
129225e381fSGreg Roach
130225e381fSGreg Roach        $stories = [];
131225e381fSGreg Roach        foreach ($block_ids as $block_id) {
1327d988ec3SGreg Roach            $block_id = (int) $block_id;
1337d988ec3SGreg Roach
134225e381fSGreg Roach            // Only show this block for certain languages
13550d6f48cSGreg Roach            $languages = $this->getBlockSetting($block_id, 'languages');
13622d65e5aSGreg Roach            if ($languages === '' || in_array(WT_LOCALE, explode(',', $languages), true)) {
137225e381fSGreg Roach                $stories[] = (object) [
138225e381fSGreg Roach                    'block_id'   => $block_id,
139225e381fSGreg Roach                    'title'      => $this->getBlockSetting($block_id, 'title'),
14072ac996dSGreg Roach                    'story_body' => $this->getBlockSetting($block_id, 'story_body'),
141225e381fSGreg Roach                ];
142225e381fSGreg Roach            }
143225e381fSGreg Roach        }
144225e381fSGreg Roach
145225e381fSGreg Roach        return $stories;
1468c2e8227SGreg Roach    }
1478c2e8227SGreg Roach
1483caaa4d2SGreg Roach    /**
1493caaa4d2SGreg Roach     * Is this tab empty? If so, we don't always need to display it.
1503caaa4d2SGreg Roach     *
1513caaa4d2SGreg Roach     * @param Individual $individual
1523caaa4d2SGreg Roach     *
1533caaa4d2SGreg Roach     * @return bool
1543caaa4d2SGreg Roach     */
1556ccdf4f0SGreg Roach    public function hasTabContent(Individual $individual): bool
1566ccdf4f0SGreg Roach    {
15754c1ab5eSGreg Roach        return Auth::isManager($individual->tree()) || $this->getStoriesForIndividual($individual) !== [];
1586ccdf4f0SGreg Roach    }
1596ccdf4f0SGreg Roach
1603caaa4d2SGreg Roach    /**
1613caaa4d2SGreg Roach     * A greyed out tab has no actual content, but may perhaps have
1623caaa4d2SGreg Roach     * options to create content.
1633caaa4d2SGreg Roach     *
1643caaa4d2SGreg Roach     * @param Individual $individual
1653caaa4d2SGreg Roach     *
1663caaa4d2SGreg Roach     * @return bool
1673caaa4d2SGreg Roach     */
1686ccdf4f0SGreg Roach    public function isGrayedOut(Individual $individual): bool
1696ccdf4f0SGreg Roach    {
170b2448a1bSGreg Roach        return $this->getStoriesForIndividual($individual) !== [];
1716ccdf4f0SGreg Roach    }
1726ccdf4f0SGreg Roach
1733caaa4d2SGreg Roach    /**
1743caaa4d2SGreg Roach     * Can this tab load asynchronously?
1753caaa4d2SGreg Roach     *
1763caaa4d2SGreg Roach     * @return bool
1773caaa4d2SGreg Roach     */
1786ccdf4f0SGreg Roach    public function canLoadAjax(): bool
1796ccdf4f0SGreg Roach    {
1806ccdf4f0SGreg Roach        return false;
1816ccdf4f0SGreg Roach    }
1826ccdf4f0SGreg Roach
1838c2e8227SGreg Roach    /**
1840ee13198SGreg Roach     * A menu, to be added to the main application menu.
1850ee13198SGreg Roach     *
186aee13b6dSGreg Roach     * @param Tree $tree
187aee13b6dSGreg Roach     *
1880ee13198SGreg Roach     * @return Menu|null
1890ee13198SGreg Roach     */
19046295629SGreg Roach    public function getMenu(Tree $tree): ?Menu
191c1010edaSGreg Roach    {
19249a243cbSGreg Roach        $menu = new Menu($this->title(), route('module', [
19326684e68SGreg Roach            'module' => $this->name(),
194c1010edaSGreg Roach            'action' => 'ShowList',
1959022ab66SGreg Roach            'tree'    => $tree->name(),
196c1010edaSGreg Roach        ]), 'menu-story');
1978c2e8227SGreg Roach
1988c2e8227SGreg Roach        return $menu;
1998c2e8227SGreg Roach    }
20072ac996dSGreg Roach
20172ac996dSGreg Roach    /**
2026ccdf4f0SGreg Roach     * How should this module be identified in the control panel, etc.?
2036ccdf4f0SGreg Roach     *
2046ccdf4f0SGreg Roach     * @return string
2056ccdf4f0SGreg Roach     */
2066ccdf4f0SGreg Roach    public function title(): string
2076ccdf4f0SGreg Roach    {
2086ccdf4f0SGreg Roach        /* I18N: Name of a module */
2096ccdf4f0SGreg Roach        return I18N::translate('Stories');
2106ccdf4f0SGreg Roach    }
2116ccdf4f0SGreg Roach
2126ccdf4f0SGreg Roach    /**
21357ab2231SGreg Roach     * @param ServerRequestInterface $request
21472ac996dSGreg Roach     *
2156ccdf4f0SGreg Roach     * @return ResponseInterface
21672ac996dSGreg Roach     */
21757ab2231SGreg Roach    public function getAdminAction(ServerRequestInterface $request): ResponseInterface
218c1010edaSGreg Roach    {
21972ac996dSGreg Roach        $this->layout = 'layouts/administration';
22072ac996dSGreg Roach
221*e218f363SGreg Roach        // This module can't run without a tree
22257ab2231SGreg Roach        $tree = $request->getAttribute('tree');
223*e218f363SGreg Roach
224*e218f363SGreg Roach        if (!$tree instanceof Tree) {
225*e218f363SGreg Roach            $tree = $this->tree_service->all()->first();
226*e218f363SGreg Roach            if ($tree instanceof Tree) {
227*e218f363SGreg Roach                return redirect(route('module', ['module' => $this->name(), 'action' => 'Admin', 'tree' => $tree->name()]));
228*e218f363SGreg Roach            }
229*e218f363SGreg Roach
230*e218f363SGreg Roach            return redirect(route(ControlPanel::class));
231*e218f363SGreg Roach        }
23257ab2231SGreg Roach
2334b92b602SGreg Roach        $stories = DB::table('block')
23426684e68SGreg Roach            ->where('module_name', '=', $this->name())
2354b92b602SGreg Roach            ->where('gedcom_id', '=', $tree->id())
2364b92b602SGreg Roach            ->orderBy('xref')
2374b92b602SGreg Roach            ->get();
23872ac996dSGreg Roach
23972ac996dSGreg Roach        foreach ($stories as $story) {
2405db543e1SGreg Roach            $block_id = (int) $story->block_id;
2415db543e1SGreg Roach
24272ac996dSGreg Roach            $story->individual = Individual::getInstance($story->xref, $tree);
2435db543e1SGreg Roach            $story->title      = $this->getBlockSetting($block_id, 'title');
2445db543e1SGreg Roach            $story->languages  = $this->getBlockSetting($block_id, 'languages');
24572ac996dSGreg Roach        }
24672ac996dSGreg Roach
24772ac996dSGreg Roach        return $this->viewResponse('modules/stories/config', [
24871378461SGreg Roach            'module'     => $this->name(),
24972ac996dSGreg Roach            'stories'    => $stories,
25049a243cbSGreg Roach            'title'      => $this->title() . ' — ' . $tree->title(),
25172ac996dSGreg Roach            'tree'       => $tree,
25272ac996dSGreg Roach            'tree_names' => Tree::getNameList(),
25372ac996dSGreg Roach        ]);
25472ac996dSGreg Roach    }
25572ac996dSGreg Roach
25672ac996dSGreg Roach    /**
2576ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
25872ac996dSGreg Roach     *
2596ccdf4f0SGreg Roach     * @return ResponseInterface
26072ac996dSGreg Roach     */
261*e218f363SGreg Roach    public function postAdminAction(ServerRequestInterface $request): ResponseInterface
262*e218f363SGreg Roach    {
263*e218f363SGreg Roach        return redirect(route('module', [
264*e218f363SGreg Roach            'module' => $this->name(),
265*e218f363SGreg Roach            'action' => 'Admin',
266*e218f363SGreg Roach            'tree'   => $request->getParsedBody()['tree'] ?? '',
267*e218f363SGreg Roach        ]));
268*e218f363SGreg Roach    }
269*e218f363SGreg Roach
270*e218f363SGreg Roach    /**
271*e218f363SGreg Roach     * @param ServerRequestInterface $request
272*e218f363SGreg Roach     *
273*e218f363SGreg Roach     * @return ResponseInterface
274*e218f363SGreg Roach     */
27557ab2231SGreg Roach    public function getAdminEditAction(ServerRequestInterface $request): ResponseInterface
276c1010edaSGreg Roach    {
27772ac996dSGreg Roach        $this->layout = 'layouts/administration';
27872ac996dSGreg Roach
27957ab2231SGreg Roach        $tree     = $request->getAttribute('tree');
280b6b9dcc9SGreg Roach        $block_id = (int) ($request->getQueryParams()['block_id'] ?? 0);
28172ac996dSGreg Roach
28272ac996dSGreg Roach        if ($block_id === 0) {
28372ac996dSGreg Roach            // Creating a new story
284b6b9dcc9SGreg Roach            $individual  = null;
28572ac996dSGreg Roach            $story_title = '';
28672ac996dSGreg Roach            $story_body  = '';
28772ac996dSGreg Roach            $languages   = [];
28872ac996dSGreg Roach
289cc13d6d8SGreg Roach            $title = I18N::translate('Add a story') . ' — ' . e($tree->title());
29072ac996dSGreg Roach        } else {
29172ac996dSGreg Roach            // Editing an existing story
2924b92b602SGreg Roach            $xref = (string) DB::table('block')
2934b92b602SGreg Roach                ->where('block_id', '=', $block_id)
2944b92b602SGreg Roach                ->value('xref');
29572ac996dSGreg Roach
29672ac996dSGreg Roach            $individual  = Individual::getInstance($xref, $tree);
29750d6f48cSGreg Roach            $story_title = $this->getBlockSetting($block_id, 'title');
29850d6f48cSGreg Roach            $story_body  = $this->getBlockSetting($block_id, 'story_body');
29972ac996dSGreg Roach            $languages   = explode(',', $this->getBlockSetting($block_id, 'languages'));
30072ac996dSGreg Roach
301cc13d6d8SGreg Roach            $title = I18N::translate('Edit the story') . ' — ' . e($tree->title());
30272ac996dSGreg Roach        }
30372ac996dSGreg Roach
30472ac996dSGreg Roach        return $this->viewResponse('modules/stories/edit', [
30572ac996dSGreg Roach            'block_id'    => $block_id,
30672ac996dSGreg Roach            'languages'   => $languages,
30772ac996dSGreg Roach            'story_body'  => $story_body,
30872ac996dSGreg Roach            'story_title' => $story_title,
30972ac996dSGreg Roach            'title'       => $title,
31072ac996dSGreg Roach            'tree'        => $tree,
31172ac996dSGreg Roach            'individual'  => $individual,
31272ac996dSGreg Roach        ]);
31372ac996dSGreg Roach    }
31472ac996dSGreg Roach
31572ac996dSGreg Roach    /**
3166ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
31772ac996dSGreg Roach     *
3186ccdf4f0SGreg Roach     * @return ResponseInterface
31972ac996dSGreg Roach     */
32057ab2231SGreg Roach    public function postAdminEditAction(ServerRequestInterface $request): ResponseInterface
321c1010edaSGreg Roach    {
32257ab2231SGreg Roach        $tree     = $request->getAttribute('tree');
323b6b9dcc9SGreg Roach        $block_id = (int) ($request->getQueryParams()['block_id'] ?? 0);
324b6b9dcc9SGreg Roach
325b6b9dcc9SGreg Roach        $params = $request->getParsedBody();
326b6b9dcc9SGreg Roach
327b6b9dcc9SGreg Roach        $xref        = $params['xref'];
328b6b9dcc9SGreg Roach        $story_body  = $params['story_body'];
329b6b9dcc9SGreg Roach        $story_title = $params['story_title'];
330b6b9dcc9SGreg Roach        $languages   = $params['languages'] ?? [];
33172ac996dSGreg Roach
33250d6f48cSGreg Roach        $story_body  = $this->html_service->sanitize($story_body);
33350d6f48cSGreg Roach        $story_title = $this->html_service->sanitize($story_title);
33450d6f48cSGreg Roach
33572ac996dSGreg Roach        if ($block_id !== 0) {
3364b92b602SGreg Roach            DB::table('block')
3374b92b602SGreg Roach                ->where('block_id', '=', $block_id)
3384b92b602SGreg Roach                ->update([
3394b92b602SGreg Roach                    'gedcom_id' => $tree->id(),
34072ac996dSGreg Roach                    'xref'      => $xref,
34172ac996dSGreg Roach                ]);
34272ac996dSGreg Roach        } else {
3434b92b602SGreg Roach            DB::table('block')->insert([
3444b92b602SGreg Roach                'gedcom_id'   => $tree->id(),
34572ac996dSGreg Roach                'xref'        => $xref,
34626684e68SGreg Roach                'module_name' => $this->name(),
3474b92b602SGreg Roach                'block_order' => 0,
34872ac996dSGreg Roach            ]);
34972ac996dSGreg Roach
3504b92b602SGreg Roach            $block_id = (int) DB::connection()->getPdo()->lastInsertId();
35172ac996dSGreg Roach        }
35272ac996dSGreg Roach
35372ac996dSGreg Roach        $this->setBlockSetting($block_id, 'story_body', $story_body);
35472ac996dSGreg Roach        $this->setBlockSetting($block_id, 'title', $story_title);
35572ac996dSGreg Roach        $this->setBlockSetting($block_id, 'languages', implode(',', $languages));
35672ac996dSGreg Roach
357c1010edaSGreg Roach        $url = route('module', [
35826684e68SGreg Roach            'module' => $this->name(),
359c1010edaSGreg Roach            'action' => 'Admin',
3609022ab66SGreg Roach            'tree'    => $tree->name(),
361c1010edaSGreg Roach        ]);
36272ac996dSGreg Roach
3636ccdf4f0SGreg Roach        return redirect($url);
36472ac996dSGreg Roach    }
36572ac996dSGreg Roach
36672ac996dSGreg Roach    /**
3676ccdf4f0SGreg Roach     * @param ServerRequestInterface $request
36872ac996dSGreg Roach     *
3696ccdf4f0SGreg Roach     * @return ResponseInterface
37072ac996dSGreg Roach     */
37157ab2231SGreg Roach    public function postAdminDeleteAction(ServerRequestInterface $request): ResponseInterface
372c1010edaSGreg Roach    {
37357ab2231SGreg Roach        $tree     = $request->getAttribute('tree');
374b6b9dcc9SGreg Roach        $block_id = $request->getQueryParams()['block_id'];
37572ac996dSGreg Roach
3764b92b602SGreg Roach        DB::table('block_setting')
3774b92b602SGreg Roach            ->where('block_id', '=', $block_id)
3784b92b602SGreg Roach            ->delete();
37972ac996dSGreg Roach
3804b92b602SGreg Roach        DB::table('block')
3814b92b602SGreg Roach            ->where('block_id', '=', $block_id)
3824b92b602SGreg Roach            ->delete();
38372ac996dSGreg Roach
384c1010edaSGreg Roach        $url = route('module', [
38526684e68SGreg Roach            'module' => $this->name(),
386c1010edaSGreg Roach            'action' => 'Admin',
3879022ab66SGreg Roach            'tree'    => $tree->name(),
388c1010edaSGreg Roach        ]);
38972ac996dSGreg Roach
3906ccdf4f0SGreg Roach        return redirect($url);
39172ac996dSGreg Roach    }
39272ac996dSGreg Roach
39372ac996dSGreg Roach    /**
39457ab2231SGreg Roach     * @param ServerRequestInterface $request
39572ac996dSGreg Roach     *
3966ccdf4f0SGreg Roach     * @return ResponseInterface
39772ac996dSGreg Roach     */
39857ab2231SGreg Roach    public function getShowListAction(ServerRequestInterface $request): ResponseInterface
399c1010edaSGreg Roach    {
40057ab2231SGreg Roach        $tree = $request->getAttribute('tree');
4015229eadeSGreg Roach        assert($tree instanceof Tree, new InvalidArgumentException());
40257ab2231SGreg Roach
4034b92b602SGreg Roach        $stories = DB::table('block')
40426684e68SGreg Roach            ->where('module_name', '=', $this->name())
4054b92b602SGreg Roach            ->where('gedcom_id', '=', $tree->id())
4064b92b602SGreg Roach            ->get()
4074b92b602SGreg Roach            ->map(function (stdClass $story) use ($tree): stdClass {
4085db543e1SGreg Roach                $block_id = (int) $story->block_id;
4095db543e1SGreg Roach
41072ac996dSGreg Roach                $story->individual = Individual::getInstance($story->xref, $tree);
4115db543e1SGreg Roach                $story->title      = $this->getBlockSetting($block_id, 'title');
4125db543e1SGreg Roach                $story->languages  = $this->getBlockSetting($block_id, 'languages');
41372ac996dSGreg Roach
4144b92b602SGreg Roach                return $story;
4150b5fd0a6SGreg Roach            })->filter(static function (stdClass $story): bool {
41672ac996dSGreg Roach                // Filter non-existant and private individuals.
4174b92b602SGreg Roach                return $story->individual instanceof Individual && $story->individual->canShow();
4180b5fd0a6SGreg Roach            })->filter(static function (stdClass $story): bool {
41972ac996dSGreg Roach                // Filter foreign languages.
42022d65e5aSGreg Roach                return $story->languages === '' || in_array(WT_LOCALE, explode(',', $story->languages), true);
42172ac996dSGreg Roach            });
42272ac996dSGreg Roach
42372ac996dSGreg Roach        return $this->viewResponse('modules/stories/list', [
42472ac996dSGreg Roach            'stories' => $stories,
42549a243cbSGreg Roach            'title'   => $this->title(),
42672ac996dSGreg Roach        ]);
42772ac996dSGreg Roach    }
4288c2e8227SGreg Roach}
429