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; 250c0910bfSGreg 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 /** 47*962ead51SGreg Roach * @return string 48*962ead51SGreg Roach */ 49*962ead51SGreg Roach abstract public function name(): string; 50*962ead51SGreg Roach 51*962ead51SGreg Roach /** 52*962ead51SGreg Roach * @return string 53*962ead51SGreg Roach */ 54*962ead51SGreg Roach abstract public function title(): string; 55*962ead51SGreg Roach 56*962ead51SGreg Roach /** 573d8b2a8eSGreg Roach * A sentence describing what this module does. 583d8b2a8eSGreg Roach * 593d8b2a8eSGreg Roach * @return string 603d8b2a8eSGreg Roach */ 613d8b2a8eSGreg Roach public function description(): string 623d8b2a8eSGreg Roach { 633d8b2a8eSGreg Roach return I18N::translate('Theme') . ' — ' . $this->title(); 643d8b2a8eSGreg Roach } 653d8b2a8eSGreg Roach 663d8b2a8eSGreg Roach /** 67ade503dfSGreg Roach * Display an icon for this fact. 68ade503dfSGreg Roach * 69e837ff07SGreg Roach * @TODO use CSS for this 70e837ff07SGreg Roach * 71ade503dfSGreg Roach * @param Fact $fact 72ade503dfSGreg Roach * 73ade503dfSGreg Roach * @return string 74ade503dfSGreg Roach */ 75ade503dfSGreg Roach public function icon(Fact $fact): string 76ade503dfSGreg Roach { 77e837ff07SGreg Roach $asset = 'public/css/' . $this->name() . '/images/facts/' . $fact->getTag() . '.png'; 78f397d0fdSGreg Roach if (file_exists(Webtrees::ROOT_DIR . 'public' . $asset)) { 79e837ff07SGreg Roach return '<img src="' . e(asset($asset)) . '" title="' . GedcomTag::getLabel($fact->getTag()) . '">'; 80ade503dfSGreg Roach } 81ade503dfSGreg Roach 82ade503dfSGreg Roach // Spacer image - for alignment - until we move to a sprite. 83e837ff07SGreg Roach $asset = 'public/css/' . $this->name() . '/images/facts/NULL.png'; 84f397d0fdSGreg Roach if (file_exists(Webtrees::ROOT_DIR . 'public' . $asset)) { 85e837ff07SGreg Roach return '<img src="' . e(asset($asset)) . '">'; 86ade503dfSGreg Roach } 87ade503dfSGreg Roach 88ade503dfSGreg Roach return ''; 89ade503dfSGreg Roach } 90ade503dfSGreg Roach 91ade503dfSGreg Roach /** 92ade503dfSGreg Roach * Generate the facts, for display in charts. 93ade503dfSGreg Roach * 94ade503dfSGreg Roach * @param Individual $individual 95ade503dfSGreg Roach * 96ade503dfSGreg Roach * @return string 97ade503dfSGreg Roach */ 98ade503dfSGreg Roach public function individualBoxFacts(Individual $individual): string 99ade503dfSGreg Roach { 100ade503dfSGreg Roach $html = ''; 101ade503dfSGreg Roach 102ade503dfSGreg Roach $opt_tags = preg_split('/\W/', $individual->tree()->getPreference('CHART_BOX_TAGS'), 0, PREG_SPLIT_NO_EMPTY); 103ade503dfSGreg Roach // Show BIRT or equivalent event 104ade503dfSGreg Roach foreach (Gedcom::BIRTH_EVENTS as $birttag) { 10522d65e5aSGreg Roach if (!in_array($birttag, $opt_tags, true)) { 106820b62dfSGreg Roach $event = $individual->facts([$birttag])->first(); 107820b62dfSGreg Roach if ($event instanceof Fact) { 108ade503dfSGreg Roach $html .= $event->summary(); 109ade503dfSGreg Roach break; 110ade503dfSGreg Roach } 111ade503dfSGreg Roach } 112ade503dfSGreg Roach } 113ade503dfSGreg Roach // Show optional events (before death) 114ade503dfSGreg Roach foreach ($opt_tags as $key => $tag) { 11522d65e5aSGreg Roach if (!in_array($tag, Gedcom::DEATH_EVENTS, true)) { 116820b62dfSGreg Roach $event = $individual->facts([$tag])->first(); 117820b62dfSGreg Roach if ($event instanceof Fact) { 118ade503dfSGreg Roach $html .= $event->summary(); 119ade503dfSGreg Roach unset($opt_tags[$key]); 120ade503dfSGreg Roach } 121ade503dfSGreg Roach } 122ade503dfSGreg Roach } 123ade503dfSGreg Roach // Show DEAT or equivalent event 124ade503dfSGreg Roach foreach (Gedcom::DEATH_EVENTS as $deattag) { 125820b62dfSGreg Roach $event = $individual->facts([$deattag])->first(); 126820b62dfSGreg Roach if ($event instanceof Fact) { 127ade503dfSGreg Roach $html .= $event->summary(); 12822d65e5aSGreg Roach if (in_array($deattag, $opt_tags, true)) { 12922d65e5aSGreg Roach unset($opt_tags[array_search($deattag, $opt_tags, true)]); 130ade503dfSGreg Roach } 131ade503dfSGreg Roach break; 132ade503dfSGreg Roach } 133ade503dfSGreg Roach } 134ade503dfSGreg Roach // Show remaining optional events (after death) 135ade503dfSGreg Roach foreach ($opt_tags as $tag) { 136820b62dfSGreg Roach $event = $individual->facts([$tag])->first(); 137820b62dfSGreg Roach if ($event instanceof Fact) { 138ade503dfSGreg Roach $html .= $event->summary(); 139ade503dfSGreg Roach } 140ade503dfSGreg Roach } 141ade503dfSGreg Roach 142ade503dfSGreg Roach return $html; 143ade503dfSGreg Roach } 144ade503dfSGreg Roach 145ade503dfSGreg Roach /** 146ade503dfSGreg Roach * Links, to show in chart boxes; 147ade503dfSGreg Roach * 148ade503dfSGreg Roach * @param Individual $individual 149ade503dfSGreg Roach * 150ade503dfSGreg Roach * @return Menu[] 151ade503dfSGreg Roach */ 152ade503dfSGreg Roach public function individualBoxMenu(Individual $individual): array 153ade503dfSGreg Roach { 154ade503dfSGreg Roach $menus = array_merge( 155ade503dfSGreg Roach $this->individualBoxMenuCharts($individual), 156ade503dfSGreg Roach $this->individualBoxMenuFamilyLinks($individual) 157ade503dfSGreg Roach ); 158ade503dfSGreg Roach 159ade503dfSGreg Roach return $menus; 160ade503dfSGreg Roach } 161ade503dfSGreg Roach 162ade503dfSGreg Roach /** 163ade503dfSGreg Roach * Chart links, to show in chart boxes; 164ade503dfSGreg Roach * 165ade503dfSGreg Roach * @param Individual $individual 166ade503dfSGreg Roach * 167ade503dfSGreg Roach * @return Menu[] 168ade503dfSGreg Roach */ 169ade503dfSGreg Roach public function individualBoxMenuCharts(Individual $individual): array 170ade503dfSGreg Roach { 171ade503dfSGreg Roach $menus = []; 172f39638cfSGreg Roach foreach (app(ModuleService::class)->findByComponent(ModuleChartInterface::class, $individual->tree(), Auth::user()) as $chart) { 173ade503dfSGreg Roach $menu = $chart->chartBoxMenu($individual); 174ade503dfSGreg Roach if ($menu) { 175ade503dfSGreg Roach $menus[] = $menu; 176ade503dfSGreg Roach } 177ade503dfSGreg Roach } 178ade503dfSGreg Roach 1790b93976aSGreg Roach usort($menus, static function (Menu $x, Menu $y): int { 180ade503dfSGreg Roach return I18N::strcasecmp($x->getLabel(), $y->getLabel()); 181ade503dfSGreg Roach }); 182ade503dfSGreg Roach 183ade503dfSGreg Roach return $menus; 184ade503dfSGreg Roach } 185ade503dfSGreg Roach 186ade503dfSGreg Roach /** 187ade503dfSGreg Roach * Family links, to show in chart boxes. 188ade503dfSGreg Roach * 189ade503dfSGreg Roach * @param Individual $individual 190ade503dfSGreg Roach * 191ade503dfSGreg Roach * @return Menu[] 192ade503dfSGreg Roach */ 193ade503dfSGreg Roach public function individualBoxMenuFamilyLinks(Individual $individual): array 194ade503dfSGreg Roach { 195ade503dfSGreg Roach $menus = []; 196ade503dfSGreg Roach 19739ca88baSGreg Roach foreach ($individual->spouseFamilies() as $family) { 198ade503dfSGreg Roach $menus[] = new Menu('<strong>' . I18N::translate('Family with spouse') . '</strong>', $family->url()); 19939ca88baSGreg Roach $spouse = $family->spouse($individual); 200ade503dfSGreg Roach if ($spouse && $spouse->canShowName()) { 20139ca88baSGreg Roach $menus[] = new Menu($spouse->fullName(), $spouse->url()); 202ade503dfSGreg Roach } 20339ca88baSGreg Roach foreach ($family->children() as $child) { 204ade503dfSGreg Roach if ($child->canShowName()) { 20539ca88baSGreg Roach $menus[] = new Menu($child->fullName(), $child->url()); 206ade503dfSGreg Roach } 207ade503dfSGreg Roach } 208ade503dfSGreg Roach } 209ade503dfSGreg Roach 210ade503dfSGreg Roach return $menus; 211ade503dfSGreg Roach } 212ade503dfSGreg Roach 213ade503dfSGreg Roach /** 214f567c3d8SGreg Roach * Generate a menu item to change the blocks on the current tree/user page. 215ade503dfSGreg Roach * 2160c8c69d4SGreg Roach * @param Tree $tree 2170c8c69d4SGreg Roach * 218ade503dfSGreg Roach * @return Menu|null 219ade503dfSGreg Roach */ 220e364afe4SGreg Roach public function menuChangeBlocks(Tree $tree): ?Menu 221ade503dfSGreg Roach { 222eb235819SGreg Roach /** @var ServerRequestInterface $request */ 2236ccdf4f0SGreg Roach $request = app(ServerRequestInterface::class); 224e6bcfa02SGreg Roach 2250d7461faSGreg Roach $route = $request->getAttribute('route'); 226eb235819SGreg Roach 227eb235819SGreg Roach if (Auth::check() && $route === 'user-page') { 228d72b284aSGreg Roach return new Menu(I18N::translate('Customize this page'), route('user-page-edit', ['tree' => $tree->name()]), 'menu-change-blocks'); 229ade503dfSGreg Roach } 230ade503dfSGreg Roach 231eb235819SGreg Roach if (Auth::isManager($tree) && $route === 'tree-page') { 232d72b284aSGreg Roach return new Menu(I18N::translate('Customize this page'), route('tree-page-edit', ['tree' => $tree->name()]), 'menu-change-blocks'); 233ade503dfSGreg Roach } 234ade503dfSGreg Roach 235ade503dfSGreg Roach return null; 236ade503dfSGreg Roach } 237ade503dfSGreg Roach 238ade503dfSGreg Roach /** 239ade503dfSGreg Roach * Generate a menu item for the control panel. 240ade503dfSGreg Roach * 2410c8c69d4SGreg Roach * @param Tree $tree 2420c8c69d4SGreg Roach * 243ade503dfSGreg Roach * @return Menu|null 244ade503dfSGreg Roach */ 245e364afe4SGreg Roach public function menuControlPanel(Tree $tree): ?Menu 246ade503dfSGreg Roach { 247ade503dfSGreg Roach if (Auth::isAdmin()) { 2480c0910bfSGreg Roach return new Menu(I18N::translate('Control panel'), route(ControlPanel::class), 'menu-admin'); 249ade503dfSGreg Roach } 250ade503dfSGreg Roach 2510c8c69d4SGreg Roach if (Auth::isManager($tree)) { 2520c0910bfSGreg Roach return new Menu(I18N::translate('Control panel'), route('manage-trees'), 'menu-admin'); 253ade503dfSGreg Roach } 254ade503dfSGreg Roach 255ade503dfSGreg Roach return null; 256ade503dfSGreg Roach } 257ade503dfSGreg Roach 258ade503dfSGreg Roach /** 259ade503dfSGreg Roach * A menu to show a list of available languages. 260ade503dfSGreg Roach * 261ade503dfSGreg Roach * @return Menu|null 262ade503dfSGreg Roach */ 263e364afe4SGreg Roach public function menuLanguages(): ?Menu 264ade503dfSGreg Roach { 265ade503dfSGreg Roach $menu = new Menu(I18N::translate('Language'), '#', 'menu-language'); 266ade503dfSGreg Roach 267ade503dfSGreg Roach foreach (I18N::activeLocales() as $locale) { 268ade503dfSGreg Roach $language_tag = $locale->languageTag(); 269ade503dfSGreg Roach $class = 'menu-language-' . $language_tag . (WT_LOCALE === $language_tag ? ' active' : ''); 270ade503dfSGreg Roach $menu->addSubmenu(new Menu($locale->endonym(), '#', $class, [ 2717adfb8e5SGreg Roach 'data-post-url' => route(SelectLanguage::class, ['language' => $language_tag]), 272ade503dfSGreg Roach ])); 273ade503dfSGreg Roach } 274ade503dfSGreg Roach 275ade503dfSGreg Roach if (count($menu->getSubmenus()) > 1) { 276ade503dfSGreg Roach return $menu; 277ade503dfSGreg Roach } 278ade503dfSGreg Roach 279ade503dfSGreg Roach return null; 280ade503dfSGreg Roach } 281ade503dfSGreg Roach 282ade503dfSGreg Roach /** 283ade503dfSGreg Roach * A login menu option (or null if we are already logged in). 284ade503dfSGreg Roach * 285ade503dfSGreg Roach * @return Menu|null 286ade503dfSGreg Roach */ 287e364afe4SGreg Roach public function menuLogin(): ?Menu 288ade503dfSGreg Roach { 289ade503dfSGreg Roach if (Auth::check()) { 290ade503dfSGreg Roach return null; 291ade503dfSGreg Roach } 292ade503dfSGreg Roach 293ade503dfSGreg Roach // Return to this page after login... 294f567c3d8SGreg Roach $url = app(ServerRequestInterface::class)->getUri(); 295ade503dfSGreg Roach 296ade503dfSGreg Roach // ...but switch from the tree-page to the user-page 297ade503dfSGreg Roach $url = str_replace('route=tree-page', 'route=user-page', $url); 298ade503dfSGreg Roach 29956f9a9c1SGreg Roach return new Menu(I18N::translate('Sign in'), route(LoginPage::class, ['url' => $url]), 'menu-login', ['rel' => 'nofollow']); 300ade503dfSGreg Roach } 301ade503dfSGreg Roach 302ade503dfSGreg Roach /** 303ade503dfSGreg Roach * A logout menu option (or null if we are already logged out). 304ade503dfSGreg Roach * 305ade503dfSGreg Roach * @return Menu|null 306ade503dfSGreg Roach */ 307e364afe4SGreg Roach public function menuLogout(): ?Menu 308ade503dfSGreg Roach { 309ade503dfSGreg Roach if (Auth::check()) { 3100d7461faSGreg Roach return new Menu(I18N::translate('Sign out'), '#', 'menu-logout', ['data-post-url' => route(Logout::class)]); 311ade503dfSGreg Roach } 312ade503dfSGreg Roach 313ade503dfSGreg Roach return null; 314ade503dfSGreg Roach } 315ade503dfSGreg Roach 316ade503dfSGreg Roach /** 317ade503dfSGreg Roach * A link to allow users to edit their account settings. 318ade503dfSGreg Roach * 319ade503dfSGreg Roach * @return Menu|null 320ade503dfSGreg Roach */ 321e364afe4SGreg Roach public function menuMyAccount(): ?Menu 322ade503dfSGreg Roach { 323ade503dfSGreg Roach if (Auth::check()) { 324ade503dfSGreg Roach return new Menu(I18N::translate('My account'), route('my-account')); 325ade503dfSGreg Roach } 326ade503dfSGreg Roach 327ade503dfSGreg Roach return null; 328ade503dfSGreg Roach } 329ade503dfSGreg Roach 330ade503dfSGreg Roach /** 331ade503dfSGreg Roach * A link to the user's individual record (individual.php). 332ade503dfSGreg Roach * 3330c8c69d4SGreg Roach * @param Tree $tree 3340c8c69d4SGreg Roach * 335ade503dfSGreg Roach * @return Menu|null 336ade503dfSGreg Roach */ 337e364afe4SGreg Roach public function menuMyIndividualRecord(Tree $tree): ?Menu 338ade503dfSGreg Roach { 3390c8c69d4SGreg Roach $record = Individual::getInstance($tree->getUserPreference(Auth::user(), 'gedcomid'), $tree); 340ade503dfSGreg Roach 341ade503dfSGreg Roach if ($record) { 342ade503dfSGreg Roach return new Menu(I18N::translate('My individual record'), $record->url(), 'menu-myrecord'); 343ade503dfSGreg Roach } 344ade503dfSGreg Roach 345ade503dfSGreg Roach return null; 346ade503dfSGreg Roach } 347ade503dfSGreg Roach 348ade503dfSGreg Roach /** 349ade503dfSGreg Roach * A link to the user's personal home page. 350ade503dfSGreg Roach * 3510c8c69d4SGreg Roach * @param Tree $tree 3520c8c69d4SGreg Roach * 353ade503dfSGreg Roach * @return Menu 354ade503dfSGreg Roach */ 3550c8c69d4SGreg Roach public function menuMyPage(Tree $tree): Menu 356ade503dfSGreg Roach { 357d72b284aSGreg Roach return new Menu(I18N::translate('My page'), route('user-page', ['tree' => $tree->name()]), 'menu-mypage'); 358ade503dfSGreg Roach } 359ade503dfSGreg Roach 360ade503dfSGreg Roach /** 361ade503dfSGreg Roach * A menu for the user's personal pages. 362ade503dfSGreg Roach * 3630c8c69d4SGreg Roach * @param Tree|null $tree 3640c8c69d4SGreg Roach * 365ade503dfSGreg Roach * @return Menu|null 366ade503dfSGreg Roach */ 367e364afe4SGreg Roach public function menuMyPages(?Tree $tree): ?Menu 368ade503dfSGreg Roach { 3690c8c69d4SGreg Roach if ($tree instanceof Tree && Auth::id()) { 370ade503dfSGreg Roach return new Menu(I18N::translate('My pages'), '#', 'menu-mymenu', [], array_filter([ 3710c8c69d4SGreg Roach $this->menuMyPage($tree), 3720c8c69d4SGreg Roach $this->menuMyIndividualRecord($tree), 3730c8c69d4SGreg Roach $this->menuMyPedigree($tree), 374ade503dfSGreg Roach $this->menuMyAccount(), 3750c8c69d4SGreg Roach $this->menuControlPanel($tree), 3760c8c69d4SGreg Roach $this->menuChangeBlocks($tree), 377ade503dfSGreg Roach ])); 378ade503dfSGreg Roach } 379ade503dfSGreg Roach 380ade503dfSGreg Roach return null; 381ade503dfSGreg Roach } 382ade503dfSGreg Roach 383ade503dfSGreg Roach /** 384ade503dfSGreg Roach * A link to the user's individual record. 385ade503dfSGreg Roach * 3860c8c69d4SGreg Roach * @param Tree $tree 3870c8c69d4SGreg Roach * 388ade503dfSGreg Roach * @return Menu|null 389ade503dfSGreg Roach */ 390e364afe4SGreg Roach public function menuMyPedigree(Tree $tree): ?Menu 391ade503dfSGreg Roach { 3920c8c69d4SGreg Roach $gedcomid = $tree->getUserPreference(Auth::user(), 'gedcomid'); 393ade503dfSGreg Roach 3940c8c69d4SGreg Roach $pedigree_chart = app(ModuleService::class)->findByComponent(ModuleChartInterface::class, $tree, Auth::user()) 3950b5fd0a6SGreg Roach ->filter(static function (ModuleInterface $module): bool { 396ade503dfSGreg Roach return $module instanceof PedigreeChartModule; 397ade503dfSGreg Roach }); 398ade503dfSGreg Roach 399ade503dfSGreg Roach if ($gedcomid !== '' && $pedigree_chart instanceof PedigreeChartModule) { 400ade503dfSGreg Roach return new Menu( 401ade503dfSGreg Roach I18N::translate('My pedigree'), 402ade503dfSGreg Roach route('pedigree', [ 403ade503dfSGreg Roach 'xref' => $gedcomid, 4049022ab66SGreg Roach 'tree' => $tree->name(), 405ade503dfSGreg Roach ]), 406ade503dfSGreg Roach 'menu-mypedigree' 407ade503dfSGreg Roach ); 408ade503dfSGreg Roach } 409ade503dfSGreg Roach 410ade503dfSGreg Roach return null; 411ade503dfSGreg Roach } 412ade503dfSGreg Roach 413ade503dfSGreg Roach /** 414ade503dfSGreg Roach * Create a pending changes menu. 415ade503dfSGreg Roach * 4160c8c69d4SGreg Roach * @param Tree|null $tree 4170c8c69d4SGreg Roach * 418ade503dfSGreg Roach * @return Menu|null 419ade503dfSGreg Roach */ 420e364afe4SGreg Roach public function menuPendingChanges(?Tree $tree): ?Menu 421ade503dfSGreg Roach { 4220c8c69d4SGreg Roach if ($tree instanceof Tree && $tree->hasPendingEdit() && Auth::isModerator($tree)) { 423ade503dfSGreg Roach $url = route('show-pending', [ 4249022ab66SGreg Roach 'tree' => $tree->name(), 425cf8c0692SGreg Roach 'url' => (string) app(ServerRequestInterface::class)->getUri(), 426ade503dfSGreg Roach ]); 427ade503dfSGreg Roach 428ade503dfSGreg Roach return new Menu(I18N::translate('Pending changes'), $url, 'menu-pending'); 429ade503dfSGreg Roach } 430ade503dfSGreg Roach 431ade503dfSGreg Roach return null; 432ade503dfSGreg Roach } 433ade503dfSGreg Roach 434ade503dfSGreg Roach /** 435ade503dfSGreg Roach * Themes menu. 436ade503dfSGreg Roach * 437ade503dfSGreg Roach * @return Menu|null 438ade503dfSGreg Roach */ 439e364afe4SGreg Roach public function menuThemes(): ?Menu 440ade503dfSGreg Roach { 441b668782fSGreg Roach $themes = app(ModuleService::class)->findByInterface(ModuleThemeInterface::class, false, true); 442df8baf00SGreg Roach 443cab242e7SGreg Roach $current_theme = app(ModuleThemeInterface::class); 4448136679eSGreg Roach 4458136679eSGreg Roach if ($themes->count() > 1) { 4460b5fd0a6SGreg Roach $submenus = $themes->map(static function (ModuleThemeInterface $theme) use ($current_theme): Menu { 4478136679eSGreg Roach $active = $theme->name() === $current_theme->name(); 4488136679eSGreg Roach $class = 'menu-theme-' . $theme->name() . ($active ? ' active' : ''); 4498136679eSGreg Roach 4508136679eSGreg Roach return new Menu($theme->title(), '#', $class, [ 4517adfb8e5SGreg Roach 'data-post-url' => route(SelectTheme::class, ['theme' => $theme->name()]), 452ade503dfSGreg Roach ]); 453ade503dfSGreg Roach }); 454ade503dfSGreg Roach 4558136679eSGreg Roach return new Menu(I18N::translate('Theme'), '#', 'menu-theme', [], $submenus->all()); 456ade503dfSGreg Roach } 457ade503dfSGreg Roach 458ade503dfSGreg Roach return null; 459ade503dfSGreg Roach } 460ade503dfSGreg Roach 461ade503dfSGreg Roach /** 462ade503dfSGreg Roach * Misecellaneous dimensions, fonts, styles, etc. 463ade503dfSGreg Roach * 464ade503dfSGreg Roach * @param string $parameter_name 465ade503dfSGreg Roach * 466ade503dfSGreg Roach * @return string|int|float 467ade503dfSGreg Roach */ 468ade503dfSGreg Roach public function parameter($parameter_name) 469ade503dfSGreg Roach { 470ade503dfSGreg Roach return ''; 471ade503dfSGreg Roach } 472ade503dfSGreg Roach 473ade503dfSGreg Roach /** 474ade503dfSGreg Roach * Generate a list of items for the main menu. 475ade503dfSGreg Roach * 4760c8c69d4SGreg Roach * @param Tree|null $tree 4770c8c69d4SGreg Roach * 478ade503dfSGreg Roach * @return Menu[] 479ade503dfSGreg Roach */ 4800c8c69d4SGreg Roach public function genealogyMenu(?Tree $tree): array 481ade503dfSGreg Roach { 4820c8c69d4SGreg Roach if ($tree === null) { 4830c8c69d4SGreg Roach return []; 4840c8c69d4SGreg Roach } 4850c8c69d4SGreg Roach 4860c8c69d4SGreg Roach return app(ModuleService::class)->findByComponent(ModuleMenuInterface::class, $tree, Auth::user()) 4870b5fd0a6SGreg Roach ->map(static function (ModuleMenuInterface $menu) use ($tree): ?Menu { 4880c8c69d4SGreg Roach return $menu->getMenu($tree); 489ade503dfSGreg Roach }) 490ade503dfSGreg Roach ->filter() 491ade503dfSGreg Roach ->all(); 492ade503dfSGreg Roach } 493ade503dfSGreg Roach 494ade503dfSGreg Roach /** 4950c8c69d4SGreg Roach * Create the genealogy menu. 496ade503dfSGreg Roach * 497ade503dfSGreg Roach * @param Menu[] $menus 498ade503dfSGreg Roach * 499ade503dfSGreg Roach * @return string 500ade503dfSGreg Roach */ 5010c8c69d4SGreg Roach public function genealogyMenuContent(array $menus): string 502ade503dfSGreg Roach { 5030b5fd0a6SGreg Roach return implode('', array_map(static function (Menu $menu): string { 504ade503dfSGreg Roach return $menu->bootstrap4(); 505ade503dfSGreg Roach }, $menus)); 506ade503dfSGreg Roach } 507ade503dfSGreg Roach 508ade503dfSGreg Roach /** 509ade503dfSGreg Roach * Generate a list of items for the user menu. 510ade503dfSGreg Roach * 5110c8c69d4SGreg Roach * @param Tree|null $tree 5120c8c69d4SGreg Roach * 513ade503dfSGreg Roach * @return Menu[] 514ade503dfSGreg Roach */ 5150c8c69d4SGreg Roach public function userMenu(?Tree $tree): array 516ade503dfSGreg Roach { 517ade503dfSGreg Roach return array_filter([ 5180c8c69d4SGreg Roach $this->menuPendingChanges($tree), 5190c8c69d4SGreg Roach $this->menuMyPages($tree), 520ade503dfSGreg Roach $this->menuThemes(), 521ade503dfSGreg Roach $this->menuLanguages(), 522ade503dfSGreg Roach $this->menuLogin(), 523ade503dfSGreg Roach $this->menuLogout(), 524ade503dfSGreg Roach ]); 525ade503dfSGreg Roach } 526ade503dfSGreg Roach 527ade503dfSGreg Roach /** 528ade503dfSGreg Roach * A list of CSS files to include for this page. 529ade503dfSGreg Roach * 530ade503dfSGreg Roach * @return string[] 531ade503dfSGreg Roach */ 532ade503dfSGreg Roach public function stylesheets(): array 533ade503dfSGreg Roach { 534ade503dfSGreg Roach return []; 535ade503dfSGreg Roach } 53649a243cbSGreg Roach} 537