Completed
Push — develop ( fcf138...6e2abb )
by Greg
09:51 queued 04:00
created

ModuleThemeTrait::genealogyMenuContent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * webtrees: online genealogy
4
 * Copyright (C) 2019 webtrees development team
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
 * GNU General Public License for more details.
13
 * You should have received a copy of the GNU General Public License
14
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15
 */
16
declare(strict_types=1);
17
18
namespace Fisharebest\Webtrees\Module;
19
20
use Fisharebest\Webtrees\Auth;
21
use Fisharebest\Webtrees\Fact;
22
use Fisharebest\Webtrees\Gedcom;
23
use Fisharebest\Webtrees\GedcomTag;
24
use Fisharebest\Webtrees\I18N;
25
use Fisharebest\Webtrees\Individual;
26
use Fisharebest\Webtrees\Menu;
27
use Fisharebest\Webtrees\Services\ModuleService;
28
use Fisharebest\Webtrees\Tree;
29
use Symfony\Component\HttpFoundation\Request;
30
31
/**
32
 * Trait ModuleThemeTrait - default implementation of ModuleThemeInterface
33
 */
34
trait ModuleThemeTrait
35
{
36
    /** @var  Request */
37
    protected $request;
38
39
    /**
40
     * @param Request   $request
41
     */
42
    public function __construct(Request $request)
43
    {
44
        $this->request = $request;
45
    }
46
47
    /**
48
     * Add markup to the secondary menu.
49
     *
50
     * @param Tree|null $tree
51
     *
52
     * @return string
53
     */
54
    public function formatUserMenu(?Tree $tree): string
55
    {
56
        return
57
            '<ul class="nav wt-user-menu">' .
58
            implode('', array_map(function (Menu $menu) use ($tree): string {
59
                return $this->formatUserMenuItem($menu);
60
            }, $this->userMenu($tree))) .
61
            '</ul>';
62
    }
63
64
    /**
65
     * Add markup to an item in the secondary menu.
66
     *
67
     * @param Menu $menu
68
     *
69
     * @return string
70
     */
71
    public function formatUserMenuItem(Menu $menu): string
72
    {
73
        return $menu->bootstrap4();
74
    }
75
76
    /**
77
     * Display an icon for this fact.
78
     *
79
     * @TODO use CSS for this
80
     *
81
     * @param Fact $fact
82
     *
83
     * @return string
84
     */
85
    public function icon(Fact $fact): string
86
    {
87
        $asset = 'public/css/' . $this->name() . '/images/facts/' . $fact->getTag() . '.png';
88
        if (file_exists(WT_ROOT . 'public' . $asset)) {
89
            return '<img src="' . e(asset($asset)) . '" title="' . GedcomTag::getLabel($fact->getTag()) . '">';
90
        }
91
92
        // Spacer image - for alignment - until we move to a sprite.
93
        $asset = 'public/css/' . $this->name() . '/images/facts/NULL.png';
94
        if (file_exists(WT_ROOT . 'public' . $asset)) {
95
            return '<img src="' . e(asset($asset)) . '">';
96
        }
97
98
        return '';
99
    }
100
101
    /**
102
     * Display an individual in a box - for charts, etc.
103
     *
104
     * @param Individual $individual
105
     *
106
     * @return string
107
     */
108
    public function individualBox(Individual $individual): string
109
    {
110
        return view('chart-box', ['individual' => $individual]);
111
    }
112
113
    /**
114
     * Display an empty box - for a missing individual in a chart.
115
     *
116
     * @return string
117
     */
118
    public function individualBoxEmpty(): string
119
    {
120
        return '<div class="wt-chart-box"></div>';
121
    }
122
123
    /**
124
     * Display an individual in a box - for charts, etc.
125
     *
126
     * @param Individual $individual
127
     *
128
     * @return string
129
     */
130
    public function individualBoxLarge(Individual $individual): string
131
    {
132
        return $this->individualBox($individual);
133
    }
134
135
    /**
136
     * Display an individual in a box - for charts, etc.
137
     *
138
     * @param Individual $individual
139
     *
140
     * @return string
141
     */
142
    public function individualBoxSmall(Individual $individual): string
143
    {
144
        return $this->individualBox($individual);
145
    }
146
147
    /**
148
     * Display an individual in a box - for charts, etc.
149
     *
150
     * @return string
151
     */
152
    public function individualBoxSmallEmpty(): string
153
    {
154
        return '<div class="wt-chart-box"></div>';
155
    }
156
157
    /**
158
     * Generate the facts, for display in charts.
159
     *
160
     * @param Individual $individual
161
     *
162
     * @return string
163
     */
164
    public function individualBoxFacts(Individual $individual): string
165
    {
166
        $html = '';
167
168
        $opt_tags = preg_split('/\W/', $individual->tree()->getPreference('CHART_BOX_TAGS'), 0, PREG_SPLIT_NO_EMPTY);
169
        // Show BIRT or equivalent event
170
        foreach (Gedcom::BIRTH_EVENTS as $birttag) {
171
            if (!in_array($birttag, $opt_tags)) {
172
                $event = $individual->facts([$birttag])->first();
173
                if ($event instanceof Fact) {
174
                    $html .= $event->summary();
175
                    break;
176
                }
177
            }
178
        }
179
        // Show optional events (before death)
180
        foreach ($opt_tags as $key => $tag) {
181
            if (!in_array($tag, Gedcom::DEATH_EVENTS)) {
182
                $event = $individual->facts([$tag])->first();
183
                if ($event instanceof Fact) {
184
                    $html .= $event->summary();
185
                    unset($opt_tags[$key]);
186
                }
187
            }
188
        }
189
        // Show DEAT or equivalent event
190
        foreach (Gedcom::DEATH_EVENTS as $deattag) {
191
            $event = $individual->facts([$deattag])->first();
192
            if ($event instanceof Fact) {
193
                $html .= $event->summary();
194
                if (in_array($deattag, $opt_tags)) {
195
                    unset($opt_tags[array_search($deattag, $opt_tags)]);
196
                }
197
                break;
198
            }
199
        }
200
        // Show remaining optional events (after death)
201
        foreach ($opt_tags as $tag) {
202
            $event = $individual->facts([$tag])->first();
203
            if ($event instanceof Fact) {
204
                $html .= $event->summary();
205
            }
206
        }
207
208
        return $html;
209
    }
210
211
    /**
212
     * Links, to show in chart boxes;
213
     *
214
     * @param Individual $individual
215
     *
216
     * @return Menu[]
217
     */
218
    public function individualBoxMenu(Individual $individual): array
219
    {
220
        $menus = array_merge(
221
            $this->individualBoxMenuCharts($individual),
222
            $this->individualBoxMenuFamilyLinks($individual)
223
        );
224
225
        return $menus;
226
    }
227
228
    /**
229
     * Chart links, to show in chart boxes;
230
     *
231
     * @param Individual $individual
232
     *
233
     * @return Menu[]
234
     */
235
    public function individualBoxMenuCharts(Individual $individual): array
236
    {
237
        $menus = [];
238
        foreach (app(ModuleService::class)->findByComponent(ModuleChartInterface::class, $individual->tree, Auth::user()) as $chart) {
239
            $menu = $chart->chartBoxMenu($individual);
240
            if ($menu) {
241
                $menus[] = $menu;
242
            }
243
        }
244
245
        usort($menus, function (Menu $x, Menu $y) {
246
            return I18N::strcasecmp($x->getLabel(), $y->getLabel());
247
        });
248
249
        return $menus;
250
    }
251
252
    /**
253
     * Family links, to show in chart boxes.
254
     *
255
     * @param Individual $individual
256
     *
257
     * @return Menu[]
258
     */
259
    public function individualBoxMenuFamilyLinks(Individual $individual): array
260
    {
261
        $menus = [];
262
263
        foreach ($individual->spouseFamilies() as $family) {
264
            $menus[] = new Menu('<strong>' . I18N::translate('Family with spouse') . '</strong>', $family->url());
265
            $spouse  = $family->spouse($individual);
266
            if ($spouse && $spouse->canShowName()) {
267
                $menus[] = new Menu($spouse->fullName(), $spouse->url());
268
            }
269
            foreach ($family->children() as $child) {
270
                if ($child->canShowName()) {
271
                    $menus[] = new Menu($child->fullName(), $child->url());
272
                }
273
            }
274
        }
275
276
        return $menus;
277
    }
278
279
    /**
280
     * Generate a menu item to change the blocks on the current (index.php) page.
281
     *
282
     * @param Tree $tree
283
     *
284
     * @return Menu|null
285
     */
286
    public function menuChangeBlocks(Tree $tree)
287
    {
288
        if (Auth::check() && $this->request->get('route') === 'user-page') {
289
            return new Menu(I18N::translate('Customize this page'), route('user-page-edit', ['ged' => $tree->name()]), 'menu-change-blocks');
290
        }
291
292
        if (Auth::isManager($tree) && $this->request->get('route') === 'tree-page') {
293
            return new Menu(I18N::translate('Customize this page'), route('tree-page-edit', ['ged' => $tree->name()]), 'menu-change-blocks');
294
        }
295
296
        return null;
297
    }
298
299
    /**
300
     * Generate a menu item for the control panel.
301
     *
302
     * @param Tree $tree
303
     *
304
     * @return Menu|null
305
     */
306
    public function menuControlPanel(Tree $tree)
307
    {
308
        if (Auth::isAdmin()) {
309
            return new Menu(I18N::translate('Control panel'), route('admin-control-panel'), 'menu-admin');
310
        }
311
312
        if (Auth::isManager($tree)) {
313
            return new Menu(I18N::translate('Control panel'), route('admin-control-panel-manager'), 'menu-admin');
314
        }
315
316
        return null;
317
    }
318
319
    /**
320
     * A menu to show a list of available languages.
321
     *
322
     * @return Menu|null
323
     */
324
    public function menuLanguages()
325
    {
326
        $menu = new Menu(I18N::translate('Language'), '#', 'menu-language');
327
328
        foreach (I18N::activeLocales() as $locale) {
329
            $language_tag = $locale->languageTag();
330
            $class        = 'menu-language-' . $language_tag . (WT_LOCALE === $language_tag ? ' active' : '');
331
            $menu->addSubmenu(new Menu($locale->endonym(), '#', $class, [
332
                'onclick'       => 'return false;',
333
                'data-language' => $language_tag,
334
            ]));
335
        }
336
337
        if (count($menu->getSubmenus()) > 1) {
338
            return $menu;
339
        }
340
341
        return null;
342
    }
343
344
    /**
345
     * A login menu option (or null if we are already logged in).
346
     *
347
     * @return Menu|null
348
     */
349
    public function menuLogin()
350
    {
351
        if (Auth::check()) {
352
            return null;
353
        }
354
355
        // Return to this page after login...
356
        $url = $this->request->getRequestUri();
357
358
        // ...but switch from the tree-page to the user-page
359
        $url = str_replace('route=tree-page', 'route=user-page', $url);
360
361
        return new Menu(I18N::translate('Sign in'), route('login', ['url' => $url]), 'menu-login', ['rel' => 'nofollow']);
362
    }
363
364
    /**
365
     * A logout menu option (or null if we are already logged out).
366
     *
367
     * @return Menu|null
368
     */
369
    public function menuLogout()
370
    {
371
        if (Auth::check()) {
372
            return new Menu(I18N::translate('Sign out'), route('logout'), 'menu-logout');
373
        }
374
375
        return null;
376
    }
377
378
    /**
379
     * A link to allow users to edit their account settings.
380
     *
381
     * @return Menu|null
382
     */
383
    public function menuMyAccount()
384
    {
385
        if (Auth::check()) {
386
            return new Menu(I18N::translate('My account'), route('my-account'));
387
        }
388
389
        return null;
390
    }
391
392
    /**
393
     * A link to the user's individual record (individual.php).
394
     *
395
     * @param Tree $tree
396
     *
397
     * @return Menu|null
398
     */
399
    public function menuMyIndividualRecord(Tree $tree)
400
    {
401
        $record = Individual::getInstance($tree->getUserPreference(Auth::user(), 'gedcomid'), $tree);
402
403
        if ($record) {
404
            return new Menu(I18N::translate('My individual record'), $record->url(), 'menu-myrecord');
405
        }
406
407
        return null;
408
    }
409
410
    /**
411
     * A link to the user's personal home page.
412
     *
413
     * @param Tree $tree
414
     *
415
     * @return Menu
416
     */
417
    public function menuMyPage(Tree $tree): Menu
418
    {
419
        return new Menu(I18N::translate('My page'), route('user-page', ['ged' => $tree->name()]), 'menu-mypage');
420
    }
421
422
    /**
423
     * A menu for the user's personal pages.
424
     *
425
     * @param Tree|null $tree
426
     *
427
     * @return Menu|null
428
     */
429
    public function menuMyPages(?Tree $tree)
430
    {
431
        if ($tree instanceof Tree && Auth::id()) {
432
            return new Menu(I18N::translate('My pages'), '#', 'menu-mymenu', [], array_filter([
433
                $this->menuMyPage($tree),
434
                $this->menuMyIndividualRecord($tree),
435
                $this->menuMyPedigree($tree),
436
                $this->menuMyAccount(),
437
                $this->menuControlPanel($tree),
438
                $this->menuChangeBlocks($tree),
439
            ]));
440
        }
441
442
        return null;
443
    }
444
445
    /**
446
     * A link to the user's individual record.
447
     *
448
     * @param Tree $tree
449
     *
450
     * @return Menu|null
451
     */
452
    public function menuMyPedigree(Tree $tree)
453
    {
454
        $gedcomid = $tree->getUserPreference(Auth::user(), 'gedcomid');
455
456
        $pedigree_chart = app(ModuleService::class)->findByComponent(ModuleChartInterface::class, $tree, Auth::user())
457
            ->filter(function (ModuleInterface $module): bool {
458
                return $module instanceof PedigreeChartModule;
459
            });
460
461
        if ($gedcomid !== '' && $pedigree_chart instanceof PedigreeChartModule) {
462
            return new Menu(
463
                I18N::translate('My pedigree'),
464
                route('pedigree', [
465
                    'xref' => $gedcomid,
466
                    'ged'  => $tree->name(),
467
                ]),
468
                'menu-mypedigree'
469
            );
470
        }
471
472
        return null;
473
    }
474
475
    /**
476
     * Create a pending changes menu.
477
     *
478
     * @param Tree|null $tree
479
     *
480
     * @return Menu|null
481
     */
482
    public function menuPendingChanges(?Tree $tree)
483
    {
484
        if ($tree instanceof Tree && $tree->hasPendingEdit() && Auth::isModerator($tree)) {
485
            $url = route('show-pending', [
486
                'ged' => $tree->name(),
487
                'url' => $this->request->getRequestUri(),
488
            ]);
489
490
            return new Menu(I18N::translate('Pending changes'), $url, 'menu-pending');
491
        }
492
493
        return null;
494
    }
495
496
    /**
497
     * Themes menu.
498
     *
499
     * @return Menu|null
500
     */
501
    public function menuThemes()
502
    {
503
        $themes = app(ModuleService::class)->findByInterface(ModuleThemeInterface::class);
504
505
        $current_theme = app()->make(ModuleThemeInterface::class);
506
507
        if ($themes->count() > 1) {
508
            $submenus = $themes->map(function (ModuleThemeInterface $theme) use ($current_theme): Menu {
509
                $active     = $theme->name() === $current_theme->name();
510
                $class      = 'menu-theme-' . $theme->name() . ($active ? ' active' : '');
511
512
                return new Menu($theme->title(), '#', $class, [
513
                    'onclick'    => 'return false;',
514
                    'data-theme' => $theme->name(),
515
                ]);
516
            });
517
518
            return  new Menu(I18N::translate('Theme'), '#', 'menu-theme', [], $submenus->all());
519
        }
520
521
        return null;
522
    }
523
524
    /**
525
     * Misecellaneous dimensions, fonts, styles, etc.
526
     *
527
     * @param string $parameter_name
528
     *
529
     * @return string|int|float
530
     */
531
    public function parameter($parameter_name)
532
    {
533
        return '';
534
    }
535
536
    /**
537
     * Generate a list of items for the main menu.
538
     *
539
     * @param Tree|null $tree
540
     *
541
     * @return Menu[]
542
     */
543
    public function genealogyMenu(?Tree $tree): array
544
    {
545
        if ($tree === null) {
546
            return [];
547
        }
548
549
        return app(ModuleService::class)->findByComponent(ModuleMenuInterface::class, $tree, Auth::user())
550
            ->map(function (ModuleMenuInterface $menu) use ($tree): ?Menu {
551
                return $menu->getMenu($tree);
552
            })
553
            ->filter()
554
            ->all();
555
    }
556
557
    /**
558
     * Create the genealogy menu.
559
     *
560
     * @param Menu[] $menus
561
     *
562
     * @return string
563
     */
564
    public function genealogyMenuContent(array $menus): string
565
    {
566
        return implode('', array_map(function (Menu $menu): string {
567
            return $menu->bootstrap4();
568
        }, $menus));
569
    }
570
571
    /**
572
     * Generate a list of items for the user menu.
573
     *
574
     * @param Tree|null $tree
575
     *
576
     * @return Menu[]
577
     */
578
    public function userMenu(?Tree $tree): array
579
    {
580
        return array_filter([
581
            $this->menuPendingChanges($tree),
582
            $this->menuMyPages($tree),
583
            $this->menuThemes(),
584
            $this->menuLanguages(),
585
            $this->menuLogin(),
586
            $this->menuLogout(),
587
        ]);
588
    }
589
590
    /**
591
     * A list of CSS files to include for this page.
592
     *
593
     * @return string[]
594
     */
595
    public function stylesheets(): array
596
    {
597
        return [];
598
    }
599
}
600