xref: /webtrees/app/Module/ModuleThemeTrait.php (revision 0c0910bf0f275a14f35d2ccdf698f91f79e269d4)
149a243cbSGreg Roach<?php
23976b470SGreg Roach
349a243cbSGreg Roach/**
449a243cbSGreg Roach * webtrees: online genealogy
549a243cbSGreg Roach * Copyright (C) 2019 webtrees development team
649a243cbSGreg Roach * This program is free software: you can redistribute it and/or modify
749a243cbSGreg Roach * it under the terms of the GNU General Public License as published by
849a243cbSGreg Roach * the Free Software Foundation, either version 3 of the License, or
949a243cbSGreg Roach * (at your option) any later version.
1049a243cbSGreg Roach * This program is distributed in the hope that it will be useful,
1149a243cbSGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
1249a243cbSGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1349a243cbSGreg Roach * GNU General Public License for more details.
1449a243cbSGreg Roach * You should have received a copy of the GNU General Public License
1549a243cbSGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
1649a243cbSGreg Roach */
1749a243cbSGreg Roachdeclare(strict_types=1);
1849a243cbSGreg Roach
1949a243cbSGreg Roachnamespace Fisharebest\Webtrees\Module;
2049a243cbSGreg Roach
21ade503dfSGreg Roachuse Fisharebest\Webtrees\Auth;
22ade503dfSGreg Roachuse Fisharebest\Webtrees\Fact;
23ade503dfSGreg Roachuse Fisharebest\Webtrees\Gedcom;
24ade503dfSGreg Roachuse Fisharebest\Webtrees\GedcomTag;
25*0c0910bfSGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\ControlPanel;
2656f9a9c1SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\LoginPage;
2756f9a9c1SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\Logout;
287adfb8e5SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\SelectLanguage;
297adfb8e5SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\SelectTheme;
30ade503dfSGreg Roachuse Fisharebest\Webtrees\I18N;
31ade503dfSGreg Roachuse Fisharebest\Webtrees\Individual;
32ade503dfSGreg Roachuse Fisharebest\Webtrees\Menu;
334ca7e03cSGreg Roachuse Fisharebest\Webtrees\Services\ModuleService;
34ade503dfSGreg Roachuse Fisharebest\Webtrees\Tree;
35f397d0fdSGreg Roachuse Fisharebest\Webtrees\Webtrees;
366ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface;
373976b470SGreg Roach
386ccdf4f0SGreg Roachuse function app;
397adfb8e5SGreg Roachuse function route;
40ade503dfSGreg Roach
4149a243cbSGreg Roach/**
4249a243cbSGreg Roach * Trait ModuleThemeTrait - default implementation of ModuleThemeInterface
4349a243cbSGreg Roach */
4449a243cbSGreg Roachtrait ModuleThemeTrait
4549a243cbSGreg Roach{
46ade503dfSGreg Roach    /**
473d8b2a8eSGreg Roach     * A sentence describing what this module does.
483d8b2a8eSGreg Roach     *
493d8b2a8eSGreg Roach     * @return string
503d8b2a8eSGreg Roach     */
513d8b2a8eSGreg Roach    public function description(): string
523d8b2a8eSGreg Roach    {
533d8b2a8eSGreg Roach        return I18N::translate('Theme') . ' — ' . $this->title();
543d8b2a8eSGreg Roach    }
553d8b2a8eSGreg Roach
563d8b2a8eSGreg Roach    /**
57ade503dfSGreg Roach     * Display an icon for this fact.
58ade503dfSGreg Roach     *
59e837ff07SGreg Roach     * @TODO use CSS for this
60e837ff07SGreg Roach     *
61ade503dfSGreg Roach     * @param Fact $fact
62ade503dfSGreg Roach     *
63ade503dfSGreg Roach     * @return string
64ade503dfSGreg Roach     */
65ade503dfSGreg Roach    public function icon(Fact $fact): string
66ade503dfSGreg Roach    {
67e837ff07SGreg Roach        $asset = 'public/css/' . $this->name() . '/images/facts/' . $fact->getTag() . '.png';
68f397d0fdSGreg Roach        if (file_exists(Webtrees::ROOT_DIR . 'public' . $asset)) {
69e837ff07SGreg Roach            return '<img src="' . e(asset($asset)) . '" title="' . GedcomTag::getLabel($fact->getTag()) . '">';
70ade503dfSGreg Roach        }
71ade503dfSGreg Roach
72ade503dfSGreg Roach        // Spacer image - for alignment - until we move to a sprite.
73e837ff07SGreg Roach        $asset = 'public/css/' . $this->name() . '/images/facts/NULL.png';
74f397d0fdSGreg Roach        if (file_exists(Webtrees::ROOT_DIR . 'public' . $asset)) {
75e837ff07SGreg Roach            return '<img src="' . e(asset($asset)) . '">';
76ade503dfSGreg Roach        }
77ade503dfSGreg Roach
78ade503dfSGreg Roach        return '';
79ade503dfSGreg Roach    }
80ade503dfSGreg Roach
81ade503dfSGreg Roach    /**
82ade503dfSGreg Roach     * Generate the facts, for display in charts.
83ade503dfSGreg Roach     *
84ade503dfSGreg Roach     * @param Individual $individual
85ade503dfSGreg Roach     *
86ade503dfSGreg Roach     * @return string
87ade503dfSGreg Roach     */
88ade503dfSGreg Roach    public function individualBoxFacts(Individual $individual): string
89ade503dfSGreg Roach    {
90ade503dfSGreg Roach        $html = '';
91ade503dfSGreg Roach
92ade503dfSGreg Roach        $opt_tags = preg_split('/\W/', $individual->tree()->getPreference('CHART_BOX_TAGS'), 0, PREG_SPLIT_NO_EMPTY);
93ade503dfSGreg Roach        // Show BIRT or equivalent event
94ade503dfSGreg Roach        foreach (Gedcom::BIRTH_EVENTS as $birttag) {
9522d65e5aSGreg Roach            if (!in_array($birttag, $opt_tags, true)) {
96820b62dfSGreg Roach                $event = $individual->facts([$birttag])->first();
97820b62dfSGreg Roach                if ($event instanceof Fact) {
98ade503dfSGreg Roach                    $html .= $event->summary();
99ade503dfSGreg Roach                    break;
100ade503dfSGreg Roach                }
101ade503dfSGreg Roach            }
102ade503dfSGreg Roach        }
103ade503dfSGreg Roach        // Show optional events (before death)
104ade503dfSGreg Roach        foreach ($opt_tags as $key => $tag) {
10522d65e5aSGreg Roach            if (!in_array($tag, Gedcom::DEATH_EVENTS, true)) {
106820b62dfSGreg Roach                $event = $individual->facts([$tag])->first();
107820b62dfSGreg Roach                if ($event instanceof Fact) {
108ade503dfSGreg Roach                    $html .= $event->summary();
109ade503dfSGreg Roach                    unset($opt_tags[$key]);
110ade503dfSGreg Roach                }
111ade503dfSGreg Roach            }
112ade503dfSGreg Roach        }
113ade503dfSGreg Roach        // Show DEAT or equivalent event
114ade503dfSGreg Roach        foreach (Gedcom::DEATH_EVENTS as $deattag) {
115820b62dfSGreg Roach            $event = $individual->facts([$deattag])->first();
116820b62dfSGreg Roach            if ($event instanceof Fact) {
117ade503dfSGreg Roach                $html .= $event->summary();
11822d65e5aSGreg Roach                if (in_array($deattag, $opt_tags, true)) {
11922d65e5aSGreg Roach                    unset($opt_tags[array_search($deattag, $opt_tags, true)]);
120ade503dfSGreg Roach                }
121ade503dfSGreg Roach                break;
122ade503dfSGreg Roach            }
123ade503dfSGreg Roach        }
124ade503dfSGreg Roach        // Show remaining optional events (after death)
125ade503dfSGreg Roach        foreach ($opt_tags as $tag) {
126820b62dfSGreg Roach            $event = $individual->facts([$tag])->first();
127820b62dfSGreg Roach            if ($event instanceof Fact) {
128ade503dfSGreg Roach                $html .= $event->summary();
129ade503dfSGreg Roach            }
130ade503dfSGreg Roach        }
131ade503dfSGreg Roach
132ade503dfSGreg Roach        return $html;
133ade503dfSGreg Roach    }
134ade503dfSGreg Roach
135ade503dfSGreg Roach    /**
136ade503dfSGreg Roach     * Links, to show in chart boxes;
137ade503dfSGreg Roach     *
138ade503dfSGreg Roach     * @param Individual $individual
139ade503dfSGreg Roach     *
140ade503dfSGreg Roach     * @return Menu[]
141ade503dfSGreg Roach     */
142ade503dfSGreg Roach    public function individualBoxMenu(Individual $individual): array
143ade503dfSGreg Roach    {
144ade503dfSGreg Roach        $menus = array_merge(
145ade503dfSGreg Roach            $this->individualBoxMenuCharts($individual),
146ade503dfSGreg Roach            $this->individualBoxMenuFamilyLinks($individual)
147ade503dfSGreg Roach        );
148ade503dfSGreg Roach
149ade503dfSGreg Roach        return $menus;
150ade503dfSGreg Roach    }
151ade503dfSGreg Roach
152ade503dfSGreg Roach    /**
153ade503dfSGreg Roach     * Chart links, to show in chart boxes;
154ade503dfSGreg Roach     *
155ade503dfSGreg Roach     * @param Individual $individual
156ade503dfSGreg Roach     *
157ade503dfSGreg Roach     * @return Menu[]
158ade503dfSGreg Roach     */
159ade503dfSGreg Roach    public function individualBoxMenuCharts(Individual $individual): array
160ade503dfSGreg Roach    {
161ade503dfSGreg Roach        $menus = [];
162f39638cfSGreg Roach        foreach (app(ModuleService::class)->findByComponent(ModuleChartInterface::class, $individual->tree(), Auth::user()) as $chart) {
163ade503dfSGreg Roach            $menu = $chart->chartBoxMenu($individual);
164ade503dfSGreg Roach            if ($menu) {
165ade503dfSGreg Roach                $menus[] = $menu;
166ade503dfSGreg Roach            }
167ade503dfSGreg Roach        }
168ade503dfSGreg Roach
1690b93976aSGreg Roach        usort($menus, static function (Menu $x, Menu $y): int {
170ade503dfSGreg Roach            return I18N::strcasecmp($x->getLabel(), $y->getLabel());
171ade503dfSGreg Roach        });
172ade503dfSGreg Roach
173ade503dfSGreg Roach        return $menus;
174ade503dfSGreg Roach    }
175ade503dfSGreg Roach
176ade503dfSGreg Roach    /**
177ade503dfSGreg Roach     * Family links, to show in chart boxes.
178ade503dfSGreg Roach     *
179ade503dfSGreg Roach     * @param Individual $individual
180ade503dfSGreg Roach     *
181ade503dfSGreg Roach     * @return Menu[]
182ade503dfSGreg Roach     */
183ade503dfSGreg Roach    public function individualBoxMenuFamilyLinks(Individual $individual): array
184ade503dfSGreg Roach    {
185ade503dfSGreg Roach        $menus = [];
186ade503dfSGreg Roach
18739ca88baSGreg Roach        foreach ($individual->spouseFamilies() as $family) {
188ade503dfSGreg Roach            $menus[] = new Menu('<strong>' . I18N::translate('Family with spouse') . '</strong>', $family->url());
18939ca88baSGreg Roach            $spouse  = $family->spouse($individual);
190ade503dfSGreg Roach            if ($spouse && $spouse->canShowName()) {
19139ca88baSGreg Roach                $menus[] = new Menu($spouse->fullName(), $spouse->url());
192ade503dfSGreg Roach            }
19339ca88baSGreg Roach            foreach ($family->children() as $child) {
194ade503dfSGreg Roach                if ($child->canShowName()) {
19539ca88baSGreg Roach                    $menus[] = new Menu($child->fullName(), $child->url());
196ade503dfSGreg Roach                }
197ade503dfSGreg Roach            }
198ade503dfSGreg Roach        }
199ade503dfSGreg Roach
200ade503dfSGreg Roach        return $menus;
201ade503dfSGreg Roach    }
202ade503dfSGreg Roach
203ade503dfSGreg Roach    /**
204f567c3d8SGreg Roach     * Generate a menu item to change the blocks on the current tree/user page.
205ade503dfSGreg Roach     *
2060c8c69d4SGreg Roach     * @param Tree $tree
2070c8c69d4SGreg Roach     *
208ade503dfSGreg Roach     * @return Menu|null
209ade503dfSGreg Roach     */
210e364afe4SGreg Roach    public function menuChangeBlocks(Tree $tree): ?Menu
211ade503dfSGreg Roach    {
212eb235819SGreg Roach        /** @var ServerRequestInterface $request */
2136ccdf4f0SGreg Roach        $request = app(ServerRequestInterface::class);
214e6bcfa02SGreg Roach
2150d7461faSGreg Roach        $route = $request->getAttribute('route');
216eb235819SGreg Roach
217eb235819SGreg Roach        if (Auth::check() && $route === 'user-page') {
218d72b284aSGreg Roach            return new Menu(I18N::translate('Customize this page'), route('user-page-edit', ['tree' => $tree->name()]), 'menu-change-blocks');
219ade503dfSGreg Roach        }
220ade503dfSGreg Roach
221eb235819SGreg Roach        if (Auth::isManager($tree) && $route === 'tree-page') {
222d72b284aSGreg Roach            return new Menu(I18N::translate('Customize this page'), route('tree-page-edit', ['tree' => $tree->name()]), 'menu-change-blocks');
223ade503dfSGreg Roach        }
224ade503dfSGreg Roach
225ade503dfSGreg Roach        return null;
226ade503dfSGreg Roach    }
227ade503dfSGreg Roach
228ade503dfSGreg Roach    /**
229ade503dfSGreg Roach     * Generate a menu item for the control panel.
230ade503dfSGreg Roach     *
2310c8c69d4SGreg Roach     * @param Tree $tree
2320c8c69d4SGreg Roach     *
233ade503dfSGreg Roach     * @return Menu|null
234ade503dfSGreg Roach     */
235e364afe4SGreg Roach    public function menuControlPanel(Tree $tree): ?Menu
236ade503dfSGreg Roach    {
237ade503dfSGreg Roach        if (Auth::isAdmin()) {
238*0c0910bfSGreg Roach            return new Menu(I18N::translate('Control panel'), route(ControlPanel::class), 'menu-admin');
239ade503dfSGreg Roach        }
240ade503dfSGreg Roach
2410c8c69d4SGreg Roach        if (Auth::isManager($tree)) {
242*0c0910bfSGreg Roach            return new Menu(I18N::translate('Control panel'), route('manage-trees'), 'menu-admin');
243ade503dfSGreg Roach        }
244ade503dfSGreg Roach
245ade503dfSGreg Roach        return null;
246ade503dfSGreg Roach    }
247ade503dfSGreg Roach
248ade503dfSGreg Roach    /**
249ade503dfSGreg Roach     * A menu to show a list of available languages.
250ade503dfSGreg Roach     *
251ade503dfSGreg Roach     * @return Menu|null
252ade503dfSGreg Roach     */
253e364afe4SGreg Roach    public function menuLanguages(): ?Menu
254ade503dfSGreg Roach    {
255ade503dfSGreg Roach        $menu = new Menu(I18N::translate('Language'), '#', 'menu-language');
256ade503dfSGreg Roach
257ade503dfSGreg Roach        foreach (I18N::activeLocales() as $locale) {
258ade503dfSGreg Roach            $language_tag = $locale->languageTag();
259ade503dfSGreg Roach            $class        = 'menu-language-' . $language_tag . (WT_LOCALE === $language_tag ? ' active' : '');
260ade503dfSGreg Roach            $menu->addSubmenu(new Menu($locale->endonym(), '#', $class, [
2617adfb8e5SGreg Roach                'data-post-url' => route(SelectLanguage::class, ['language' => $language_tag]),
262ade503dfSGreg Roach            ]));
263ade503dfSGreg Roach        }
264ade503dfSGreg Roach
265ade503dfSGreg Roach        if (count($menu->getSubmenus()) > 1) {
266ade503dfSGreg Roach            return $menu;
267ade503dfSGreg Roach        }
268ade503dfSGreg Roach
269ade503dfSGreg Roach        return null;
270ade503dfSGreg Roach    }
271ade503dfSGreg Roach
272ade503dfSGreg Roach    /**
273ade503dfSGreg Roach     * A login menu option (or null if we are already logged in).
274ade503dfSGreg Roach     *
275ade503dfSGreg Roach     * @return Menu|null
276ade503dfSGreg Roach     */
277e364afe4SGreg Roach    public function menuLogin(): ?Menu
278ade503dfSGreg Roach    {
279ade503dfSGreg Roach        if (Auth::check()) {
280ade503dfSGreg Roach            return null;
281ade503dfSGreg Roach        }
282ade503dfSGreg Roach
283ade503dfSGreg Roach        // Return to this page after login...
284f567c3d8SGreg Roach        $url = app(ServerRequestInterface::class)->getUri();
285ade503dfSGreg Roach
286ade503dfSGreg Roach        // ...but switch from the tree-page to the user-page
287ade503dfSGreg Roach        $url = str_replace('route=tree-page', 'route=user-page', $url);
288ade503dfSGreg Roach
28956f9a9c1SGreg Roach        return new Menu(I18N::translate('Sign in'), route(LoginPage::class, ['url' => $url]), 'menu-login', ['rel' => 'nofollow']);
290ade503dfSGreg Roach    }
291ade503dfSGreg Roach
292ade503dfSGreg Roach    /**
293ade503dfSGreg Roach     * A logout menu option (or null if we are already logged out).
294ade503dfSGreg Roach     *
295ade503dfSGreg Roach     * @return Menu|null
296ade503dfSGreg Roach     */
297e364afe4SGreg Roach    public function menuLogout(): ?Menu
298ade503dfSGreg Roach    {
299ade503dfSGreg Roach        if (Auth::check()) {
3000d7461faSGreg Roach            return new Menu(I18N::translate('Sign out'), '#', 'menu-logout', ['data-post-url' => route(Logout::class)]);
301ade503dfSGreg Roach        }
302ade503dfSGreg Roach
303ade503dfSGreg Roach        return null;
304ade503dfSGreg Roach    }
305ade503dfSGreg Roach
306ade503dfSGreg Roach    /**
307ade503dfSGreg Roach     * A link to allow users to edit their account settings.
308ade503dfSGreg Roach     *
309ade503dfSGreg Roach     * @return Menu|null
310ade503dfSGreg Roach     */
311e364afe4SGreg Roach    public function menuMyAccount(): ?Menu
312ade503dfSGreg Roach    {
313ade503dfSGreg Roach        if (Auth::check()) {
314ade503dfSGreg Roach            return new Menu(I18N::translate('My account'), route('my-account'));
315ade503dfSGreg Roach        }
316ade503dfSGreg Roach
317ade503dfSGreg Roach        return null;
318ade503dfSGreg Roach    }
319ade503dfSGreg Roach
320ade503dfSGreg Roach    /**
321ade503dfSGreg Roach     * A link to the user's individual record (individual.php).
322ade503dfSGreg Roach     *
3230c8c69d4SGreg Roach     * @param Tree $tree
3240c8c69d4SGreg Roach     *
325ade503dfSGreg Roach     * @return Menu|null
326ade503dfSGreg Roach     */
327e364afe4SGreg Roach    public function menuMyIndividualRecord(Tree $tree): ?Menu
328ade503dfSGreg Roach    {
3290c8c69d4SGreg Roach        $record = Individual::getInstance($tree->getUserPreference(Auth::user(), 'gedcomid'), $tree);
330ade503dfSGreg Roach
331ade503dfSGreg Roach        if ($record) {
332ade503dfSGreg Roach            return new Menu(I18N::translate('My individual record'), $record->url(), 'menu-myrecord');
333ade503dfSGreg Roach        }
334ade503dfSGreg Roach
335ade503dfSGreg Roach        return null;
336ade503dfSGreg Roach    }
337ade503dfSGreg Roach
338ade503dfSGreg Roach    /**
339ade503dfSGreg Roach     * A link to the user's personal home page.
340ade503dfSGreg Roach     *
3410c8c69d4SGreg Roach     * @param Tree $tree
3420c8c69d4SGreg Roach     *
343ade503dfSGreg Roach     * @return Menu
344ade503dfSGreg Roach     */
3450c8c69d4SGreg Roach    public function menuMyPage(Tree $tree): Menu
346ade503dfSGreg Roach    {
347d72b284aSGreg Roach        return new Menu(I18N::translate('My page'), route('user-page', ['tree' => $tree->name()]), 'menu-mypage');
348ade503dfSGreg Roach    }
349ade503dfSGreg Roach
350ade503dfSGreg Roach    /**
351ade503dfSGreg Roach     * A menu for the user's personal pages.
352ade503dfSGreg Roach     *
3530c8c69d4SGreg Roach     * @param Tree|null $tree
3540c8c69d4SGreg Roach     *
355ade503dfSGreg Roach     * @return Menu|null
356ade503dfSGreg Roach     */
357e364afe4SGreg Roach    public function menuMyPages(?Tree $tree): ?Menu
358ade503dfSGreg Roach    {
3590c8c69d4SGreg Roach        if ($tree instanceof Tree && Auth::id()) {
360ade503dfSGreg Roach            return new Menu(I18N::translate('My pages'), '#', 'menu-mymenu', [], array_filter([
3610c8c69d4SGreg Roach                $this->menuMyPage($tree),
3620c8c69d4SGreg Roach                $this->menuMyIndividualRecord($tree),
3630c8c69d4SGreg Roach                $this->menuMyPedigree($tree),
364ade503dfSGreg Roach                $this->menuMyAccount(),
3650c8c69d4SGreg Roach                $this->menuControlPanel($tree),
3660c8c69d4SGreg Roach                $this->menuChangeBlocks($tree),
367ade503dfSGreg Roach            ]));
368ade503dfSGreg Roach        }
369ade503dfSGreg Roach
370ade503dfSGreg Roach        return null;
371ade503dfSGreg Roach    }
372ade503dfSGreg Roach
373ade503dfSGreg Roach    /**
374ade503dfSGreg Roach     * A link to the user's individual record.
375ade503dfSGreg Roach     *
3760c8c69d4SGreg Roach     * @param Tree $tree
3770c8c69d4SGreg Roach     *
378ade503dfSGreg Roach     * @return Menu|null
379ade503dfSGreg Roach     */
380e364afe4SGreg Roach    public function menuMyPedigree(Tree $tree): ?Menu
381ade503dfSGreg Roach    {
3820c8c69d4SGreg Roach        $gedcomid = $tree->getUserPreference(Auth::user(), 'gedcomid');
383ade503dfSGreg Roach
3840c8c69d4SGreg Roach        $pedigree_chart = app(ModuleService::class)->findByComponent(ModuleChartInterface::class, $tree, Auth::user())
3850b5fd0a6SGreg Roach            ->filter(static function (ModuleInterface $module): bool {
386ade503dfSGreg Roach                return $module instanceof PedigreeChartModule;
387ade503dfSGreg Roach            });
388ade503dfSGreg Roach
389ade503dfSGreg Roach        if ($gedcomid !== '' && $pedigree_chart instanceof PedigreeChartModule) {
390ade503dfSGreg Roach            return new Menu(
391ade503dfSGreg Roach                I18N::translate('My pedigree'),
392ade503dfSGreg Roach                route('pedigree', [
393ade503dfSGreg Roach                    'xref' => $gedcomid,
3940c8c69d4SGreg Roach                    'ged'  => $tree->name(),
395ade503dfSGreg Roach                ]),
396ade503dfSGreg Roach                'menu-mypedigree'
397ade503dfSGreg Roach            );
398ade503dfSGreg Roach        }
399ade503dfSGreg Roach
400ade503dfSGreg Roach        return null;
401ade503dfSGreg Roach    }
402ade503dfSGreg Roach
403ade503dfSGreg Roach    /**
404ade503dfSGreg Roach     * Create a pending changes menu.
405ade503dfSGreg Roach     *
4060c8c69d4SGreg Roach     * @param Tree|null $tree
4070c8c69d4SGreg Roach     *
408ade503dfSGreg Roach     * @return Menu|null
409ade503dfSGreg Roach     */
410e364afe4SGreg Roach    public function menuPendingChanges(?Tree $tree): ?Menu
411ade503dfSGreg Roach    {
4120c8c69d4SGreg Roach        if ($tree instanceof Tree && $tree->hasPendingEdit() && Auth::isModerator($tree)) {
413ade503dfSGreg Roach            $url = route('show-pending', [
4140c8c69d4SGreg Roach                'ged' => $tree->name(),
415cf8c0692SGreg Roach                'url' => (string) app(ServerRequestInterface::class)->getUri(),
416ade503dfSGreg Roach            ]);
417ade503dfSGreg Roach
418ade503dfSGreg Roach            return new Menu(I18N::translate('Pending changes'), $url, 'menu-pending');
419ade503dfSGreg Roach        }
420ade503dfSGreg Roach
421ade503dfSGreg Roach        return null;
422ade503dfSGreg Roach    }
423ade503dfSGreg Roach
424ade503dfSGreg Roach    /**
425ade503dfSGreg Roach     * Themes menu.
426ade503dfSGreg Roach     *
427ade503dfSGreg Roach     * @return Menu|null
428ade503dfSGreg Roach     */
429e364afe4SGreg Roach    public function menuThemes(): ?Menu
430ade503dfSGreg Roach    {
431b668782fSGreg Roach        $themes = app(ModuleService::class)->findByInterface(ModuleThemeInterface::class, false, true);
432df8baf00SGreg Roach
433cab242e7SGreg Roach        $current_theme = app(ModuleThemeInterface::class);
4348136679eSGreg Roach
4358136679eSGreg Roach        if ($themes->count() > 1) {
4360b5fd0a6SGreg Roach            $submenus = $themes->map(static function (ModuleThemeInterface $theme) use ($current_theme): Menu {
4378136679eSGreg Roach                $active     = $theme->name() === $current_theme->name();
4388136679eSGreg Roach                $class      = 'menu-theme-' . $theme->name() . ($active ? ' active' : '');
4398136679eSGreg Roach
4408136679eSGreg Roach                return new Menu($theme->title(), '#', $class, [
4417adfb8e5SGreg Roach                    'data-post-url' => route(SelectTheme::class, ['theme' => $theme->name()]),
442ade503dfSGreg Roach                ]);
443ade503dfSGreg Roach            });
444ade503dfSGreg Roach
4458136679eSGreg Roach            return new Menu(I18N::translate('Theme'), '#', 'menu-theme', [], $submenus->all());
446ade503dfSGreg Roach        }
447ade503dfSGreg Roach
448ade503dfSGreg Roach        return null;
449ade503dfSGreg Roach    }
450ade503dfSGreg Roach
451ade503dfSGreg Roach    /**
452ade503dfSGreg Roach     * Misecellaneous dimensions, fonts, styles, etc.
453ade503dfSGreg Roach     *
454ade503dfSGreg Roach     * @param string $parameter_name
455ade503dfSGreg Roach     *
456ade503dfSGreg Roach     * @return string|int|float
457ade503dfSGreg Roach     */
458ade503dfSGreg Roach    public function parameter($parameter_name)
459ade503dfSGreg Roach    {
460ade503dfSGreg Roach        return '';
461ade503dfSGreg Roach    }
462ade503dfSGreg Roach
463ade503dfSGreg Roach    /**
464ade503dfSGreg Roach     * Generate a list of items for the main menu.
465ade503dfSGreg Roach     *
4660c8c69d4SGreg Roach     * @param Tree|null $tree
4670c8c69d4SGreg Roach     *
468ade503dfSGreg Roach     * @return Menu[]
469ade503dfSGreg Roach     */
4700c8c69d4SGreg Roach    public function genealogyMenu(?Tree $tree): array
471ade503dfSGreg Roach    {
4720c8c69d4SGreg Roach        if ($tree === null) {
4730c8c69d4SGreg Roach            return [];
4740c8c69d4SGreg Roach        }
4750c8c69d4SGreg Roach
4760c8c69d4SGreg Roach        return app(ModuleService::class)->findByComponent(ModuleMenuInterface::class, $tree, Auth::user())
4770b5fd0a6SGreg Roach            ->map(static function (ModuleMenuInterface $menu) use ($tree): ?Menu {
4780c8c69d4SGreg Roach                return $menu->getMenu($tree);
479ade503dfSGreg Roach            })
480ade503dfSGreg Roach            ->filter()
481ade503dfSGreg Roach            ->all();
482ade503dfSGreg Roach    }
483ade503dfSGreg Roach
484ade503dfSGreg Roach    /**
4850c8c69d4SGreg Roach     * Create the genealogy menu.
486ade503dfSGreg Roach     *
487ade503dfSGreg Roach     * @param Menu[] $menus
488ade503dfSGreg Roach     *
489ade503dfSGreg Roach     * @return string
490ade503dfSGreg Roach     */
4910c8c69d4SGreg Roach    public function genealogyMenuContent(array $menus): string
492ade503dfSGreg Roach    {
4930b5fd0a6SGreg Roach        return implode('', array_map(static function (Menu $menu): string {
494ade503dfSGreg Roach            return $menu->bootstrap4();
495ade503dfSGreg Roach        }, $menus));
496ade503dfSGreg Roach    }
497ade503dfSGreg Roach
498ade503dfSGreg Roach    /**
499ade503dfSGreg Roach     * Generate a list of items for the user menu.
500ade503dfSGreg Roach     *
5010c8c69d4SGreg Roach     * @param Tree|null $tree
5020c8c69d4SGreg Roach     *
503ade503dfSGreg Roach     * @return Menu[]
504ade503dfSGreg Roach     */
5050c8c69d4SGreg Roach    public function userMenu(?Tree $tree): array
506ade503dfSGreg Roach    {
507ade503dfSGreg Roach        return array_filter([
5080c8c69d4SGreg Roach            $this->menuPendingChanges($tree),
5090c8c69d4SGreg Roach            $this->menuMyPages($tree),
510ade503dfSGreg Roach            $this->menuThemes(),
511ade503dfSGreg Roach            $this->menuLanguages(),
512ade503dfSGreg Roach            $this->menuLogin(),
513ade503dfSGreg Roach            $this->menuLogout(),
514ade503dfSGreg Roach        ]);
515ade503dfSGreg Roach    }
516ade503dfSGreg Roach
517ade503dfSGreg Roach    /**
518ade503dfSGreg Roach     * A list of CSS files to include for this page.
519ade503dfSGreg Roach     *
520ade503dfSGreg Roach     * @return string[]
521ade503dfSGreg Roach     */
522ade503dfSGreg Roach    public function stylesheets(): array
523ade503dfSGreg Roach    {
524ade503dfSGreg Roach        return [];
525ade503dfSGreg Roach    }
52649a243cbSGreg Roach}
527