xref: /webtrees/app/Module/ModuleThemeTrait.php (revision e837ff071ec04bc68a539c2c68fa4964e1c2bd2e)
149a243cbSGreg Roach<?php
249a243cbSGreg Roach/**
349a243cbSGreg Roach * webtrees: online genealogy
449a243cbSGreg Roach * Copyright (C) 2019 webtrees development team
549a243cbSGreg Roach * This program is free software: you can redistribute it and/or modify
649a243cbSGreg Roach * it under the terms of the GNU General Public License as published by
749a243cbSGreg Roach * the Free Software Foundation, either version 3 of the License, or
849a243cbSGreg Roach * (at your option) any later version.
949a243cbSGreg Roach * This program is distributed in the hope that it will be useful,
1049a243cbSGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
1149a243cbSGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1249a243cbSGreg Roach * GNU General Public License for more details.
1349a243cbSGreg Roach * You should have received a copy of the GNU General Public License
1449a243cbSGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
1549a243cbSGreg Roach */
1649a243cbSGreg Roachdeclare(strict_types=1);
1749a243cbSGreg Roach
1849a243cbSGreg Roachnamespace Fisharebest\Webtrees\Module;
1949a243cbSGreg Roach
20ade503dfSGreg Roachuse Fisharebest\Webtrees\Auth;
21ade503dfSGreg Roachuse Fisharebest\Webtrees\Fact;
22ade503dfSGreg Roachuse Fisharebest\Webtrees\Gedcom;
23ade503dfSGreg Roachuse Fisharebest\Webtrees\GedcomTag;
24ade503dfSGreg Roachuse Fisharebest\Webtrees\I18N;
25ade503dfSGreg Roachuse Fisharebest\Webtrees\Individual;
26ade503dfSGreg Roachuse Fisharebest\Webtrees\Menu;
274ca7e03cSGreg Roachuse Fisharebest\Webtrees\Services\ModuleService;
28ade503dfSGreg Roachuse Fisharebest\Webtrees\Tree;
29ade503dfSGreg Roachuse Symfony\Component\HttpFoundation\Request;
30ade503dfSGreg Roach
3149a243cbSGreg Roach/**
3249a243cbSGreg Roach * Trait ModuleThemeTrait - default implementation of ModuleThemeInterface
3349a243cbSGreg Roach */
3449a243cbSGreg Roachtrait ModuleThemeTrait
3549a243cbSGreg Roach{
36ade503dfSGreg Roach    /** @var  Request */
37ade503dfSGreg Roach    protected $request;
38ade503dfSGreg Roach
39ade503dfSGreg Roach    /** @var Tree|null */
40ade503dfSGreg Roach    protected $tree;
41ade503dfSGreg Roach
42ade503dfSGreg Roach    /**
43ade503dfSGreg Roach     * @param Request   $request
44ade503dfSGreg Roach     * @param Tree|null $tree The current tree (if there is one).
45ade503dfSGreg Roach     */
46ade503dfSGreg Roach    public function __construct(Request $request, ?Tree $tree)
47ade503dfSGreg Roach    {
48ade503dfSGreg Roach        $this->request = $request;
49ade503dfSGreg Roach        $this->tree    = $tree;
50ade503dfSGreg Roach    }
51ade503dfSGreg Roach
52ade503dfSGreg Roach    /**
53ade503dfSGreg Roach     * Add markup to the secondary menu.
54ade503dfSGreg Roach     *
55ade503dfSGreg Roach     * @return string
56ade503dfSGreg Roach     */
57ade503dfSGreg Roach    public function formatSecondaryMenu(): string
58ade503dfSGreg Roach    {
59ade503dfSGreg Roach        return
60ade503dfSGreg Roach            '<ul class="nav wt-secondary-menu">' .
61ade503dfSGreg Roach            implode('', array_map(function (Menu $menu): string {
62ade503dfSGreg Roach                return $this->formatSecondaryMenuItem($menu);
63ade503dfSGreg Roach            }, $this->secondaryMenu())) .
64ade503dfSGreg Roach            '</ul>';
65ade503dfSGreg Roach    }
66ade503dfSGreg Roach
67ade503dfSGreg Roach    /**
68ade503dfSGreg Roach     * Add markup to an item in the secondary menu.
69ade503dfSGreg Roach     *
70ade503dfSGreg Roach     * @param Menu $menu
71ade503dfSGreg Roach     *
72ade503dfSGreg Roach     * @return string
73ade503dfSGreg Roach     */
74ade503dfSGreg Roach    public function formatSecondaryMenuItem(Menu $menu): string
75ade503dfSGreg Roach    {
76ade503dfSGreg Roach        return $menu->bootstrap4();
77ade503dfSGreg Roach    }
78ade503dfSGreg Roach
79ade503dfSGreg Roach    /**
80ade503dfSGreg Roach     * Display an icon for this fact.
81ade503dfSGreg Roach     *
82*e837ff07SGreg Roach     * @TODO use CSS for this
83*e837ff07SGreg Roach     *
84ade503dfSGreg Roach     * @param Fact $fact
85ade503dfSGreg Roach     *
86ade503dfSGreg Roach     * @return string
87ade503dfSGreg Roach     */
88ade503dfSGreg Roach    public function icon(Fact $fact): string
89ade503dfSGreg Roach    {
90*e837ff07SGreg Roach        $asset = 'public/css/' . $this->name() . '/images/facts/' . $fact->getTag() . '.png';
91*e837ff07SGreg Roach        if (file_exists(WT_ROOT . 'public' . $asset)) {
92*e837ff07SGreg Roach            return '<img src="' . e(asset($asset)) . '" title="' . GedcomTag::getLabel($fact->getTag()) . '">';
93ade503dfSGreg Roach        }
94ade503dfSGreg Roach
95ade503dfSGreg Roach        // Spacer image - for alignment - until we move to a sprite.
96*e837ff07SGreg Roach        $asset = 'public/css/' . $this->name() . '/images/facts/NULL.png';
97*e837ff07SGreg Roach        if (file_exists(WT_ROOT . 'public' . $asset)) {
98*e837ff07SGreg Roach            return '<img src="' . e(asset($asset)) . '">';
99ade503dfSGreg Roach        }
100ade503dfSGreg Roach
101ade503dfSGreg Roach        return '';
102ade503dfSGreg Roach    }
103ade503dfSGreg Roach
104ade503dfSGreg Roach    /**
105ade503dfSGreg Roach     * Display an individual in a box - for charts, etc.
106ade503dfSGreg Roach     *
107ade503dfSGreg Roach     * @param Individual $individual
108ade503dfSGreg Roach     *
109ade503dfSGreg Roach     * @return string
110ade503dfSGreg Roach     */
111ade503dfSGreg Roach    public function individualBox(Individual $individual): string
112ade503dfSGreg Roach    {
113ade503dfSGreg Roach        $person_box_class = self::PERSON_BOX_CLASSES[$individual->getSex()];
114ade503dfSGreg Roach
115ade503dfSGreg Roach        if ($individual->canShow() && $individual->tree()->getPreference('SHOW_HIGHLIGHT_IMAGES')) {
116ade503dfSGreg Roach            $thumbnail = $individual->displayImage(40, 50, 'crop', []);
117ade503dfSGreg Roach        } else {
118ade503dfSGreg Roach            $thumbnail = '';
119ade503dfSGreg Roach        }
120ade503dfSGreg Roach
121ade503dfSGreg Roach        $content = '<span class="namedef name1">' . $individual->getFullName() . '</span>';
122ade503dfSGreg Roach        $icons   = '';
123ade503dfSGreg Roach        if ($individual->canShow()) {
124ade503dfSGreg Roach            $content = '<a href="' . e($individual->url()) . '">' . $content . '</a>' .
125ade503dfSGreg Roach                '<div class="namedef name1">' . $individual->getAddName() . '</div>';
126ade503dfSGreg Roach            $icons   = '<div class="icons">' .
127ade503dfSGreg Roach                '<span class="iconz icon-zoomin" title="' . I18N::translate('Zoom in/out on this box.') . '"></span>' .
128ade503dfSGreg Roach                '<div class="itr"><i class="icon-pedigree"></i><div class="popup">' .
129ade503dfSGreg Roach                '<ul class="' . $person_box_class . '">' . implode('', array_map(function (Menu $menu): string {
130ade503dfSGreg Roach                    return $menu->bootstrap4();
131ade503dfSGreg Roach                }, $this->individualBoxMenu($individual))) . '</ul>' .
132ade503dfSGreg Roach                '</div>' .
133ade503dfSGreg Roach                '</div>' .
134ade503dfSGreg Roach                '</div>';
135ade503dfSGreg Roach        }
136ade503dfSGreg Roach
137ade503dfSGreg Roach        return
138ade503dfSGreg Roach            '<div data-xref="' . e($individual->xref()) . '" data-tree="' . e($individual->tree()->name()) . '" class="person_box_template ' . $person_box_class . ' box-style1" style="width: ' . $this->parameter('chart-box-x') . 'px; height: ' . $this->parameter('chart-box-y') . 'px">' .
139ade503dfSGreg Roach            $icons .
140ade503dfSGreg Roach            '<div class="chart_textbox" style="max-height:' . $this->parameter('chart-box-y') . 'px;">' .
141ade503dfSGreg Roach            $thumbnail .
142ade503dfSGreg Roach            $content .
143ade503dfSGreg Roach            '<div class="inout2 details1">' . $this->individualBoxFacts($individual) . '</div>' .
144ade503dfSGreg Roach            '</div>' .
145ade503dfSGreg Roach            '<div class="inout"></div>' .
146ade503dfSGreg Roach            '</div>';
147ade503dfSGreg Roach    }
148ade503dfSGreg Roach
149ade503dfSGreg Roach    /**
150ade503dfSGreg Roach     * Display an empty box - for a missing individual in a chart.
151ade503dfSGreg Roach     *
152ade503dfSGreg Roach     * @return string
153ade503dfSGreg Roach     */
154ade503dfSGreg Roach    public function individualBoxEmpty(): string
155ade503dfSGreg Roach    {
156ade503dfSGreg Roach        return '<div class="person_box_template person_boxNN box-style1" style="width: ' . $this->parameter('chart-box-x') . 'px; min-height: ' . $this->parameter('chart-box-y') . 'px"></div>';
157ade503dfSGreg Roach    }
158ade503dfSGreg Roach
159ade503dfSGreg Roach    /**
160ade503dfSGreg Roach     * Display an individual in a box - for charts, etc.
161ade503dfSGreg Roach     *
162ade503dfSGreg Roach     * @param Individual $individual
163ade503dfSGreg Roach     *
164ade503dfSGreg Roach     * @return string
165ade503dfSGreg Roach     */
166ade503dfSGreg Roach    public function individualBoxLarge(Individual $individual): string
167ade503dfSGreg Roach    {
168ade503dfSGreg Roach        $person_box_class = self::PERSON_BOX_CLASSES[$individual->getSex()];
169ade503dfSGreg Roach
170ade503dfSGreg Roach        if ($individual->tree()->getPreference('SHOW_HIGHLIGHT_IMAGES')) {
171ade503dfSGreg Roach            $thumbnail = $individual->displayImage(40, 50, 'crop', []);
172ade503dfSGreg Roach        } else {
173ade503dfSGreg Roach            $thumbnail = '';
174ade503dfSGreg Roach        }
175ade503dfSGreg Roach
176ade503dfSGreg Roach        $content = '<span class="namedef name1">' . $individual->getFullName() . '</span>';
177ade503dfSGreg Roach        $icons   = '';
178ade503dfSGreg Roach        if ($individual->canShow()) {
179ade503dfSGreg Roach            $content = '<a href="' . e($individual->url()) . '">' . $content . '</a>' .
180ade503dfSGreg Roach                '<div class="namedef name2">' . $individual->getAddName() . '</div>';
181ade503dfSGreg Roach            $icons   = '<div class="icons">' .
182ade503dfSGreg Roach                '<span class="iconz icon-zoomin" title="' . I18N::translate('Zoom in/out on this box.') . '"></span>' .
183ade503dfSGreg Roach                '<div class="itr"><i class="icon-pedigree"></i><div class="popup">' .
184ade503dfSGreg Roach                '<ul class="' . $person_box_class . '">' . implode('', array_map(function (Menu $menu): string {
185ade503dfSGreg Roach                    return $menu->bootstrap4();
186ade503dfSGreg Roach                }, $this->individualBoxMenu($individual))) . '</ul>' .
187ade503dfSGreg Roach                '</div>' .
188ade503dfSGreg Roach                '</div>' .
189ade503dfSGreg Roach                '</div>';
190ade503dfSGreg Roach        }
191ade503dfSGreg Roach
192ade503dfSGreg Roach        return
193ade503dfSGreg Roach            '<div data-xref="' . e($individual->xref()) . '" data-tree="' . e($individual->tree()->name()) . '" class="person_box_template ' . $person_box_class . ' box-style2">' .
194ade503dfSGreg Roach            $icons .
195ade503dfSGreg Roach            '<div class="chart_textbox" style="max-height:' . $this->parameter('chart-box-y') . 'px;">' .
196ade503dfSGreg Roach            $thumbnail .
197ade503dfSGreg Roach            $content .
198ade503dfSGreg Roach            '<div class="inout2 details2">' . $this->individualBoxFacts($individual) . '</div>' .
199ade503dfSGreg Roach            '</div>' .
200ade503dfSGreg Roach            '<div class="inout"></div>' .
201ade503dfSGreg Roach            '</div>';
202ade503dfSGreg Roach    }
203ade503dfSGreg Roach
204ade503dfSGreg Roach    /**
205ade503dfSGreg Roach     * Display an individual in a box - for charts, etc.
206ade503dfSGreg Roach     *
207ade503dfSGreg Roach     * @param Individual $individual
208ade503dfSGreg Roach     *
209ade503dfSGreg Roach     * @return string
210ade503dfSGreg Roach     */
211ade503dfSGreg Roach    public function individualBoxSmall(Individual $individual): string
212ade503dfSGreg Roach    {
213ade503dfSGreg Roach        $person_box_class = self::PERSON_BOX_CLASSES[$individual->getSex()];
214ade503dfSGreg Roach
215ade503dfSGreg Roach        if ($individual->tree()->getPreference('SHOW_HIGHLIGHT_IMAGES')) {
216ade503dfSGreg Roach            $thumbnail = $individual->displayImage(40, 50, 'crop', []);
217ade503dfSGreg Roach        } else {
218ade503dfSGreg Roach            $thumbnail = '';
219ade503dfSGreg Roach        }
220ade503dfSGreg Roach
221ade503dfSGreg Roach        return
222ade503dfSGreg Roach            '<div data-xref="' . $individual->xref() . '" class="person_box_template ' . $person_box_class . ' iconz box-style0" style="width: ' . $this->parameter('compact-chart-box-x') . 'px; min-height: ' . $this->parameter('compact-chart-box-y') . 'px">' .
223ade503dfSGreg Roach            '<div class="compact_view">' .
224ade503dfSGreg Roach            $thumbnail .
225ade503dfSGreg Roach            '<a href="' . e($individual->url()) . '">' .
226ade503dfSGreg Roach            '<span class="namedef name0">' . $individual->getFullName() . '</span>' .
227ade503dfSGreg Roach            '</a>' .
228ade503dfSGreg Roach            '<div class="inout2 details0">' . $individual->getLifeSpan() . '</div>' .
229ade503dfSGreg Roach            '</div>' .
230ade503dfSGreg Roach            '<div class="inout"></div>' .
231ade503dfSGreg Roach            '</div>';
232ade503dfSGreg Roach    }
233ade503dfSGreg Roach
234ade503dfSGreg Roach    /**
235ade503dfSGreg Roach     * Display an individual in a box - for charts, etc.
236ade503dfSGreg Roach     *
237ade503dfSGreg Roach     * @return string
238ade503dfSGreg Roach     */
239ade503dfSGreg Roach    public function individualBoxSmallEmpty(): string
240ade503dfSGreg Roach    {
241ade503dfSGreg Roach        return '<div class="person_box_template person_boxNN box-style1" style="width: ' . $this->parameter('compact-chart-box-x') . 'px; min-height: ' . $this->parameter('compact-chart-box-y') . 'px"></div>';
242ade503dfSGreg Roach    }
243ade503dfSGreg Roach
244ade503dfSGreg Roach    /**
245ade503dfSGreg Roach     * Generate the facts, for display in charts.
246ade503dfSGreg Roach     *
247ade503dfSGreg Roach     * @param Individual $individual
248ade503dfSGreg Roach     *
249ade503dfSGreg Roach     * @return string
250ade503dfSGreg Roach     */
251ade503dfSGreg Roach    public function individualBoxFacts(Individual $individual): string
252ade503dfSGreg Roach    {
253ade503dfSGreg Roach        $html = '';
254ade503dfSGreg Roach
255ade503dfSGreg Roach        $opt_tags = preg_split('/\W/', $individual->tree()->getPreference('CHART_BOX_TAGS'), 0, PREG_SPLIT_NO_EMPTY);
256ade503dfSGreg Roach        // Show BIRT or equivalent event
257ade503dfSGreg Roach        foreach (Gedcom::BIRTH_EVENTS as $birttag) {
258ade503dfSGreg Roach            if (!in_array($birttag, $opt_tags)) {
259ade503dfSGreg Roach                $event = $individual->getFirstFact($birttag);
260ade503dfSGreg Roach                if ($event) {
261ade503dfSGreg Roach                    $html .= $event->summary();
262ade503dfSGreg Roach                    break;
263ade503dfSGreg Roach                }
264ade503dfSGreg Roach            }
265ade503dfSGreg Roach        }
266ade503dfSGreg Roach        // Show optional events (before death)
267ade503dfSGreg Roach        foreach ($opt_tags as $key => $tag) {
268ade503dfSGreg Roach            if (!in_array($tag, Gedcom::DEATH_EVENTS)) {
269ade503dfSGreg Roach                $event = $individual->getFirstFact($tag);
270ade503dfSGreg Roach                if ($event !== null) {
271ade503dfSGreg Roach                    $html .= $event->summary();
272ade503dfSGreg Roach                    unset($opt_tags[$key]);
273ade503dfSGreg Roach                }
274ade503dfSGreg Roach            }
275ade503dfSGreg Roach        }
276ade503dfSGreg Roach        // Show DEAT or equivalent event
277ade503dfSGreg Roach        foreach (Gedcom::DEATH_EVENTS as $deattag) {
278ade503dfSGreg Roach            $event = $individual->getFirstFact($deattag);
279ade503dfSGreg Roach            if ($event) {
280ade503dfSGreg Roach                $html .= $event->summary();
281ade503dfSGreg Roach                if (in_array($deattag, $opt_tags)) {
282ade503dfSGreg Roach                    unset($opt_tags[array_search($deattag, $opt_tags)]);
283ade503dfSGreg Roach                }
284ade503dfSGreg Roach                break;
285ade503dfSGreg Roach            }
286ade503dfSGreg Roach        }
287ade503dfSGreg Roach        // Show remaining optional events (after death)
288ade503dfSGreg Roach        foreach ($opt_tags as $tag) {
289ade503dfSGreg Roach            $event = $individual->getFirstFact($tag);
290ade503dfSGreg Roach            if ($event) {
291ade503dfSGreg Roach                $html .= $event->summary();
292ade503dfSGreg Roach            }
293ade503dfSGreg Roach        }
294ade503dfSGreg Roach
295ade503dfSGreg Roach        return $html;
296ade503dfSGreg Roach    }
297ade503dfSGreg Roach
298ade503dfSGreg Roach    /**
299ade503dfSGreg Roach     * Links, to show in chart boxes;
300ade503dfSGreg Roach     *
301ade503dfSGreg Roach     * @param Individual $individual
302ade503dfSGreg Roach     *
303ade503dfSGreg Roach     * @return Menu[]
304ade503dfSGreg Roach     */
305ade503dfSGreg Roach    public function individualBoxMenu(Individual $individual): array
306ade503dfSGreg Roach    {
307ade503dfSGreg Roach        $menus = array_merge(
308ade503dfSGreg Roach            $this->individualBoxMenuCharts($individual),
309ade503dfSGreg Roach            $this->individualBoxMenuFamilyLinks($individual)
310ade503dfSGreg Roach        );
311ade503dfSGreg Roach
312ade503dfSGreg Roach        return $menus;
313ade503dfSGreg Roach    }
314ade503dfSGreg Roach
315ade503dfSGreg Roach    /**
316ade503dfSGreg Roach     * Chart links, to show in chart boxes;
317ade503dfSGreg Roach     *
318ade503dfSGreg Roach     * @param Individual $individual
319ade503dfSGreg Roach     *
320ade503dfSGreg Roach     * @return Menu[]
321ade503dfSGreg Roach     */
322ade503dfSGreg Roach    public function individualBoxMenuCharts(Individual $individual): array
323ade503dfSGreg Roach    {
324ade503dfSGreg Roach        $menus = [];
3254ca7e03cSGreg Roach        foreach (app(ModuleService::class)->findByComponent('chart', $this->tree, Auth::user()) as $chart) {
326ade503dfSGreg Roach            $menu = $chart->chartBoxMenu($individual);
327ade503dfSGreg Roach            if ($menu) {
328ade503dfSGreg Roach                $menus[] = $menu;
329ade503dfSGreg Roach            }
330ade503dfSGreg Roach        }
331ade503dfSGreg Roach
332ade503dfSGreg Roach        usort($menus, function (Menu $x, Menu $y) {
333ade503dfSGreg Roach            return I18N::strcasecmp($x->getLabel(), $y->getLabel());
334ade503dfSGreg Roach        });
335ade503dfSGreg Roach
336ade503dfSGreg Roach        return $menus;
337ade503dfSGreg Roach    }
338ade503dfSGreg Roach
339ade503dfSGreg Roach    /**
340ade503dfSGreg Roach     * Family links, to show in chart boxes.
341ade503dfSGreg Roach     *
342ade503dfSGreg Roach     * @param Individual $individual
343ade503dfSGreg Roach     *
344ade503dfSGreg Roach     * @return Menu[]
345ade503dfSGreg Roach     */
346ade503dfSGreg Roach    public function individualBoxMenuFamilyLinks(Individual $individual): array
347ade503dfSGreg Roach    {
348ade503dfSGreg Roach        $menus = [];
349ade503dfSGreg Roach
350ade503dfSGreg Roach        foreach ($individual->getSpouseFamilies() as $family) {
351ade503dfSGreg Roach            $menus[] = new Menu('<strong>' . I18N::translate('Family with spouse') . '</strong>', $family->url());
352ade503dfSGreg Roach            $spouse  = $family->getSpouse($individual);
353ade503dfSGreg Roach            if ($spouse && $spouse->canShowName()) {
354ade503dfSGreg Roach                $menus[] = new Menu($spouse->getFullName(), $spouse->url());
355ade503dfSGreg Roach            }
356ade503dfSGreg Roach            foreach ($family->getChildren() as $child) {
357ade503dfSGreg Roach                if ($child->canShowName()) {
358ade503dfSGreg Roach                    $menus[] = new Menu($child->getFullName(), $child->url());
359ade503dfSGreg Roach                }
360ade503dfSGreg Roach            }
361ade503dfSGreg Roach        }
362ade503dfSGreg Roach
363ade503dfSGreg Roach        return $menus;
364ade503dfSGreg Roach    }
365ade503dfSGreg Roach
366ade503dfSGreg Roach    /**
367ade503dfSGreg Roach     * Generate a menu item to change the blocks on the current (index.php) page.
368ade503dfSGreg Roach     *
369ade503dfSGreg Roach     * @return Menu|null
370ade503dfSGreg Roach     */
371ade503dfSGreg Roach    public function menuChangeBlocks()
372ade503dfSGreg Roach    {
373ade503dfSGreg Roach        if (Auth::check() && $this->request->get('route') === 'user-page') {
374ade503dfSGreg Roach            return new Menu(I18N::translate('Customize this page'), route('user-page-edit', ['ged' => $this->tree->name()]), 'menu-change-blocks');
375ade503dfSGreg Roach        }
376ade503dfSGreg Roach
377ade503dfSGreg Roach        if (Auth::isManager($this->tree) && $this->request->get('route') === 'tree-page') {
378ade503dfSGreg Roach            return new Menu(I18N::translate('Customize this page'), route('tree-page-edit', ['ged' => $this->tree->name()]), 'menu-change-blocks');
379ade503dfSGreg Roach        }
380ade503dfSGreg Roach
381ade503dfSGreg Roach        return null;
382ade503dfSGreg Roach    }
383ade503dfSGreg Roach
384ade503dfSGreg Roach    /**
385ade503dfSGreg Roach     * Generate a menu item for the control panel.
386ade503dfSGreg Roach     *
387ade503dfSGreg Roach     * @return Menu|null
388ade503dfSGreg Roach     */
389ade503dfSGreg Roach    public function menuControlPanel()
390ade503dfSGreg Roach    {
391ade503dfSGreg Roach        if (Auth::isAdmin()) {
392ade503dfSGreg Roach            return new Menu(I18N::translate('Control panel'), route('admin-control-panel'), 'menu-admin');
393ade503dfSGreg Roach        }
394ade503dfSGreg Roach
395ade503dfSGreg Roach        if (Auth::isManager($this->tree)) {
396ade503dfSGreg Roach            return new Menu(I18N::translate('Control panel'), route('admin-control-panel-manager'), 'menu-admin');
397ade503dfSGreg Roach        }
398ade503dfSGreg Roach
399ade503dfSGreg Roach        return null;
400ade503dfSGreg Roach    }
401ade503dfSGreg Roach
402ade503dfSGreg Roach    /**
403ade503dfSGreg Roach     * A menu to show a list of available languages.
404ade503dfSGreg Roach     *
405ade503dfSGreg Roach     * @return Menu|null
406ade503dfSGreg Roach     */
407ade503dfSGreg Roach    public function menuLanguages()
408ade503dfSGreg Roach    {
409ade503dfSGreg Roach        $menu = new Menu(I18N::translate('Language'), '#', 'menu-language');
410ade503dfSGreg Roach
411ade503dfSGreg Roach        foreach (I18N::activeLocales() as $locale) {
412ade503dfSGreg Roach            $language_tag = $locale->languageTag();
413ade503dfSGreg Roach            $class        = 'menu-language-' . $language_tag . (WT_LOCALE === $language_tag ? ' active' : '');
414ade503dfSGreg Roach            $menu->addSubmenu(new Menu($locale->endonym(), '#', $class, [
415ade503dfSGreg Roach                'onclick'       => 'return false;',
416ade503dfSGreg Roach                'data-language' => $language_tag,
417ade503dfSGreg Roach            ]));
418ade503dfSGreg Roach        }
419ade503dfSGreg Roach
420ade503dfSGreg Roach        if (count($menu->getSubmenus()) > 1) {
421ade503dfSGreg Roach            return $menu;
422ade503dfSGreg Roach        }
423ade503dfSGreg Roach
424ade503dfSGreg Roach        return null;
425ade503dfSGreg Roach    }
426ade503dfSGreg Roach
427ade503dfSGreg Roach    /**
428ade503dfSGreg Roach     * A login menu option (or null if we are already logged in).
429ade503dfSGreg Roach     *
430ade503dfSGreg Roach     * @return Menu|null
431ade503dfSGreg Roach     */
432ade503dfSGreg Roach    public function menuLogin()
433ade503dfSGreg Roach    {
434ade503dfSGreg Roach        if (Auth::check()) {
435ade503dfSGreg Roach            return null;
436ade503dfSGreg Roach        }
437ade503dfSGreg Roach
438ade503dfSGreg Roach        // Return to this page after login...
439ade503dfSGreg Roach        $url = $this->request->getRequestUri();
440ade503dfSGreg Roach
441ade503dfSGreg Roach        // ...but switch from the tree-page to the user-page
442ade503dfSGreg Roach        $url = str_replace('route=tree-page', 'route=user-page', $url);
443ade503dfSGreg Roach
444ade503dfSGreg Roach        return new Menu(I18N::translate('Sign in'), route('login', ['url' => $url]), 'menu-login', ['rel' => 'nofollow']);
445ade503dfSGreg Roach    }
446ade503dfSGreg Roach
447ade503dfSGreg Roach    /**
448ade503dfSGreg Roach     * A logout menu option (or null if we are already logged out).
449ade503dfSGreg Roach     *
450ade503dfSGreg Roach     * @return Menu|null
451ade503dfSGreg Roach     */
452ade503dfSGreg Roach    public function menuLogout()
453ade503dfSGreg Roach    {
454ade503dfSGreg Roach        if (Auth::check()) {
455ade503dfSGreg Roach            return new Menu(I18N::translate('Sign out'), route('logout'), 'menu-logout');
456ade503dfSGreg Roach        }
457ade503dfSGreg Roach
458ade503dfSGreg Roach        return null;
459ade503dfSGreg Roach    }
460ade503dfSGreg Roach
461ade503dfSGreg Roach    /**
462ade503dfSGreg Roach     * A link to allow users to edit their account settings.
463ade503dfSGreg Roach     *
464ade503dfSGreg Roach     * @return Menu|null
465ade503dfSGreg Roach     */
466ade503dfSGreg Roach    public function menuMyAccount()
467ade503dfSGreg Roach    {
468ade503dfSGreg Roach        if (Auth::check()) {
469ade503dfSGreg Roach            return new Menu(I18N::translate('My account'), route('my-account'));
470ade503dfSGreg Roach        }
471ade503dfSGreg Roach
472ade503dfSGreg Roach        return null;
473ade503dfSGreg Roach    }
474ade503dfSGreg Roach
475ade503dfSGreg Roach    /**
476ade503dfSGreg Roach     * A link to the user's individual record (individual.php).
477ade503dfSGreg Roach     *
478ade503dfSGreg Roach     * @return Menu|null
479ade503dfSGreg Roach     */
480ade503dfSGreg Roach    public function menuMyIndividualRecord()
481ade503dfSGreg Roach    {
482ade503dfSGreg Roach        $record = Individual::getInstance($this->tree->getUserPreference(Auth::user(), 'gedcomid'), $this->tree);
483ade503dfSGreg Roach
484ade503dfSGreg Roach        if ($record) {
485ade503dfSGreg Roach            return new Menu(I18N::translate('My individual record'), $record->url(), 'menu-myrecord');
486ade503dfSGreg Roach        }
487ade503dfSGreg Roach
488ade503dfSGreg Roach        return null;
489ade503dfSGreg Roach    }
490ade503dfSGreg Roach
491ade503dfSGreg Roach    /**
492ade503dfSGreg Roach     * A link to the user's personal home page.
493ade503dfSGreg Roach     *
494ade503dfSGreg Roach     * @return Menu
495ade503dfSGreg Roach     */
496ade503dfSGreg Roach    public function menuMyPage(): Menu
497ade503dfSGreg Roach    {
498ade503dfSGreg Roach        return new Menu(I18N::translate('My page'), route('user-page', ['ged' => $this->tree->name()]), 'menu-mypage');
499ade503dfSGreg Roach    }
500ade503dfSGreg Roach
501ade503dfSGreg Roach    /**
502ade503dfSGreg Roach     * A menu for the user's personal pages.
503ade503dfSGreg Roach     *
504ade503dfSGreg Roach     * @return Menu|null
505ade503dfSGreg Roach     */
506ade503dfSGreg Roach    public function menuMyPages()
507ade503dfSGreg Roach    {
508ade503dfSGreg Roach        if (Auth::id() && $this->tree !== null) {
509ade503dfSGreg Roach            return new Menu(I18N::translate('My pages'), '#', 'menu-mymenu', [], array_filter([
510ade503dfSGreg Roach                $this->menuMyPage(),
511ade503dfSGreg Roach                $this->menuMyIndividualRecord(),
512ade503dfSGreg Roach                $this->menuMyPedigree(),
513ade503dfSGreg Roach                $this->menuMyAccount(),
514ade503dfSGreg Roach                $this->menuControlPanel(),
515ade503dfSGreg Roach                $this->menuChangeBlocks(),
516ade503dfSGreg Roach            ]));
517ade503dfSGreg Roach        }
518ade503dfSGreg Roach
519ade503dfSGreg Roach        return null;
520ade503dfSGreg Roach    }
521ade503dfSGreg Roach
522ade503dfSGreg Roach    /**
523ade503dfSGreg Roach     * A link to the user's individual record.
524ade503dfSGreg Roach     *
525ade503dfSGreg Roach     * @return Menu|null
526ade503dfSGreg Roach     */
527ade503dfSGreg Roach    public function menuMyPedigree()
528ade503dfSGreg Roach    {
529ade503dfSGreg Roach        $gedcomid = $this->tree->getUserPreference(Auth::user(), 'gedcomid');
530ade503dfSGreg Roach
5314ca7e03cSGreg Roach        $pedigree_chart = app(ModuleService::class)->findByComponent('chart', $this->tree, Auth::user())
532ade503dfSGreg Roach            ->filter(function (ModuleInterface $module): bool {
533ade503dfSGreg Roach                return $module instanceof PedigreeChartModule;
534ade503dfSGreg Roach            });
535ade503dfSGreg Roach
536ade503dfSGreg Roach        if ($gedcomid !== '' && $pedigree_chart instanceof PedigreeChartModule) {
537ade503dfSGreg Roach            return new Menu(
538ade503dfSGreg Roach                I18N::translate('My pedigree'),
539ade503dfSGreg Roach                route('pedigree', [
540ade503dfSGreg Roach                    'xref' => $gedcomid,
541ade503dfSGreg Roach                    'ged'  => $this->tree->name(),
542ade503dfSGreg Roach                ]),
543ade503dfSGreg Roach                'menu-mypedigree'
544ade503dfSGreg Roach            );
545ade503dfSGreg Roach        }
546ade503dfSGreg Roach
547ade503dfSGreg Roach        return null;
548ade503dfSGreg Roach    }
549ade503dfSGreg Roach
550ade503dfSGreg Roach    /**
551ade503dfSGreg Roach     * Create a pending changes menu.
552ade503dfSGreg Roach     *
553ade503dfSGreg Roach     * @return Menu|null
554ade503dfSGreg Roach     */
555ade503dfSGreg Roach    public function menuPendingChanges()
556ade503dfSGreg Roach    {
557ade503dfSGreg Roach        if ($this->pendingChangesExist()) {
558ade503dfSGreg Roach            $url = route('show-pending', [
559ade503dfSGreg Roach                'ged' => $this->tree ? $this->tree->name() : '',
560ade503dfSGreg Roach                'url' => $this->request->getRequestUri(),
561ade503dfSGreg Roach            ]);
562ade503dfSGreg Roach
563ade503dfSGreg Roach            return new Menu(I18N::translate('Pending changes'), $url, 'menu-pending');
564ade503dfSGreg Roach        }
565ade503dfSGreg Roach
566ade503dfSGreg Roach        return null;
567ade503dfSGreg Roach    }
568ade503dfSGreg Roach
569ade503dfSGreg Roach    /**
570ade503dfSGreg Roach     * Themes menu.
571ade503dfSGreg Roach     *
572ade503dfSGreg Roach     * @return Menu|null
573ade503dfSGreg Roach     */
574ade503dfSGreg Roach    public function menuThemes()
575ade503dfSGreg Roach    {
5764ca7e03cSGreg Roach        $themes = app(ModuleService::class)->findByInterface(ModuleThemeInterface::class);
577df8baf00SGreg Roach
5788136679eSGreg Roach        $current_theme = app()->make(ModuleThemeInterface::class);
5798136679eSGreg Roach
5808136679eSGreg Roach        if ($themes->count() > 1) {
5818136679eSGreg Roach            $submenus = $themes->map(function (ModuleThemeInterface $theme) use ($current_theme): Menu {
5828136679eSGreg Roach                $active     = $theme->name() === $current_theme->name();
5838136679eSGreg Roach                $class      = 'menu-theme-' . $theme->name() . ($active ? ' active' : '');
5848136679eSGreg Roach
5858136679eSGreg Roach                return new Menu($theme->title(), '#', $class, [
586ade503dfSGreg Roach                    'onclick'    => 'return false;',
587ade503dfSGreg Roach                    'data-theme' => $theme->name(),
588ade503dfSGreg Roach                ]);
589ade503dfSGreg Roach            });
590ade503dfSGreg Roach
5918136679eSGreg Roach            return  new Menu(I18N::translate('Theme'), '#', 'menu-theme', [], $submenus->all());
592ade503dfSGreg Roach        }
593ade503dfSGreg Roach
594ade503dfSGreg Roach        return null;
595ade503dfSGreg Roach    }
596ade503dfSGreg Roach
597ade503dfSGreg Roach    /**
598ade503dfSGreg Roach     * Misecellaneous dimensions, fonts, styles, etc.
599ade503dfSGreg Roach     *
600ade503dfSGreg Roach     * @param string $parameter_name
601ade503dfSGreg Roach     *
602ade503dfSGreg Roach     * @return string|int|float
603ade503dfSGreg Roach     */
604ade503dfSGreg Roach    public function parameter($parameter_name)
605ade503dfSGreg Roach    {
606ade503dfSGreg Roach        return '';
607ade503dfSGreg Roach    }
608ade503dfSGreg Roach
609ade503dfSGreg Roach    /**
610ade503dfSGreg Roach     * Are there any pending changes for us to approve?
611ade503dfSGreg Roach     *
612ade503dfSGreg Roach     * @return bool
613ade503dfSGreg Roach     */
614ade503dfSGreg Roach    public function pendingChangesExist(): bool
615ade503dfSGreg Roach    {
616ade503dfSGreg Roach        return $this->tree && $this->tree->hasPendingEdit() && Auth::isModerator($this->tree);
617ade503dfSGreg Roach    }
618ade503dfSGreg Roach
619ade503dfSGreg Roach    /**
620ade503dfSGreg Roach     * Generate a list of items for the main menu.
621ade503dfSGreg Roach     *
622ade503dfSGreg Roach     * @return Menu[]
623ade503dfSGreg Roach     */
624ade503dfSGreg Roach    public function primaryMenu(): array
625ade503dfSGreg Roach    {
6264ca7e03cSGreg Roach        return app(ModuleService::class)->findByComponent('menu', $this->tree, Auth::user())
627ade503dfSGreg Roach            ->map(function (ModuleMenuInterface $menu): ?Menu {
628ade503dfSGreg Roach                return $menu->getMenu($this->tree);
629ade503dfSGreg Roach            })
630ade503dfSGreg Roach            ->filter()
631ade503dfSGreg Roach            ->all();
632ade503dfSGreg Roach    }
633ade503dfSGreg Roach
634ade503dfSGreg Roach    /**
635ade503dfSGreg Roach     * Create the primary menu.
636ade503dfSGreg Roach     *
637ade503dfSGreg Roach     * @param Menu[] $menus
638ade503dfSGreg Roach     *
639ade503dfSGreg Roach     * @return string
640ade503dfSGreg Roach     */
641ade503dfSGreg Roach    public function primaryMenuContent(array $menus): string
642ade503dfSGreg Roach    {
643ade503dfSGreg Roach        return implode('', array_map(function (Menu $menu): string {
644ade503dfSGreg Roach            return $menu->bootstrap4();
645ade503dfSGreg Roach        }, $menus));
646ade503dfSGreg Roach    }
647ade503dfSGreg Roach
648ade503dfSGreg Roach    /**
649ade503dfSGreg Roach     * Generate a list of items for the user menu.
650ade503dfSGreg Roach     *
651ade503dfSGreg Roach     * @return Menu[]
652ade503dfSGreg Roach     */
653ade503dfSGreg Roach    public function secondaryMenu(): array
654ade503dfSGreg Roach    {
655ade503dfSGreg Roach        return array_filter([
656ade503dfSGreg Roach            $this->menuPendingChanges(),
657ade503dfSGreg Roach            $this->menuMyPages(),
658ade503dfSGreg Roach            $this->menuThemes(),
659ade503dfSGreg Roach            $this->menuLanguages(),
660ade503dfSGreg Roach            $this->menuLogin(),
661ade503dfSGreg Roach            $this->menuLogout(),
662ade503dfSGreg Roach        ]);
663ade503dfSGreg Roach    }
664ade503dfSGreg Roach
665ade503dfSGreg Roach    /**
666ade503dfSGreg Roach     * A list of CSS files to include for this page.
667ade503dfSGreg Roach     *
668ade503dfSGreg Roach     * @return string[]
669ade503dfSGreg Roach     */
670ade503dfSGreg Roach    public function stylesheets(): array
671ade503dfSGreg Roach    {
672ade503dfSGreg Roach        return [];
673ade503dfSGreg Roach    }
67449a243cbSGreg Roach}
675