xref: /webtrees/resources/views/individual-page.phtml (revision f7cf8a155e2743f3d124eef3d30a558ab062fa4b)
1<?php
2
3use Fisharebest\Webtrees\Auth;
4use Fisharebest\Webtrees\Fact;
5use Fisharebest\Webtrees\Http\RequestHandlers\AddNewFact;
6use Fisharebest\Webtrees\Http\RequestHandlers\PendingChangesAcceptRecord;
7use Fisharebest\Webtrees\Http\RequestHandlers\PendingChangesRejectRecord;
8use Fisharebest\Webtrees\I18N;
9use Fisharebest\Webtrees\Individual;
10use Fisharebest\Webtrees\Media;
11use Fisharebest\Webtrees\Module\ModuleSidebarInterface;
12use Fisharebest\Webtrees\Module\ModuleTabInterface;
13use Fisharebest\Webtrees\Tree;
14use Fisharebest\Webtrees\View;
15use Illuminate\Support\Collection;
16
17/**
18 * @var string                             $age
19 * @var Individual                         $individual
20 * @var string                             $user_link
21 * @var Collection<ModuleSidebarInterface> $sidebars
22 * @var Collection<Media>                  $individual_media
23 * @var Collection<Fact>                   $name_records
24 * @var Collection<Fact>                   $sex_records
25 * @var Collection<ModuleTabInterface>     $tabs
26 * @var Tree                               $tree
27 */
28?>
29
30<?php if ($individual->isPendingDeletion()) : ?>
31    <?php if (Auth::isModerator($individual->tree())) : ?>
32        <?= view('components/alert-warning-dismissible', [
33            'alert' => /* I18N: %1$s is “accept”, %2$s is “reject”. These are links. */
34                I18N::translate('This individual has been deleted. You should review the deletion and then %1$s or %2$s it.', '<a href="#" class="alert-link" data-post-url="' . e(route(PendingChangesAcceptRecord::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref()])) . '">' . I18N::translateContext('You should review the deletion and then accept or reject it.', 'accept') . '</a>', '<a href="#" class="alert-link" data-post-url="' . e(route(PendingChangesRejectRecord::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref()])) . '">' . I18N::translateContext('You should review the deletion and then accept or reject it.', 'reject') . '</a>') . ' ' . view('help/link', ['topic' => 'pending_changes']),
35        ]) ?>
36    <?php elseif (Auth::isEditor($individual->tree())) : ?>
37        <?= view('components/alert-warning-dismissible', ['alert' => I18N::translate('This individual has been deleted. The deletion will need to be reviewed by a moderator.') . ' ' . view('help/link', ['topic' => 'pending_changes'])]) ?>
38    <?php endif ?>
39<?php elseif ($individual->isPendingAddition()) : ?>
40    <?php if (Auth::isModerator($individual->tree())) : ?>
41        <?= view('components/alert-warning-dismissible', [
42            'alert' => /* I18N: %1$s is “accept”, %2$s is “reject”. These are links. */
43                I18N::translate('This individual has been edited. You should review the changes and then %1$s or %2$s them.', '<a href="#" class="alert-link" data-post-url="' . e(route(PendingChangesAcceptRecord::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref()])) . '">' . I18N::translateContext('You should review the changes and then accept or reject them.', 'accept') . '</a>', '<a href="#" class="alert-link" data-post-url="' . e(route(PendingChangesRejectRecord::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref()])) . '">' . I18N::translateContext('You should review the changes and then accept or reject them.', 'reject') . '</a>') . ' ' . view('help/link', ['topic' => 'pending_changes']),
44        ]) ?>
45    <?php elseif (Auth::isEditor($individual->tree())) : ?>
46        <?= view('components/alert-warning-dismissible', ['alert' => I18N::translate('This individual has been edited. The changes need to be reviewed by a moderator.') . ' ' . view('help/link', ['topic' => 'pending_changes'])]) ?>
47    <?php endif ?>
48<?php endif ?>
49
50<div class="d-flex mb-4">
51    <h2 class="wt-page-title mx-auto">
52        <?= $individual->fullName() ?><?= $user_link ?>, <?= $individual->lifespan() ?> <?= $age ?>
53    </h2>
54    <?php if ($individual->canEdit()) : ?>
55        <?= view('individual-page-menu', ['record' => $individual]) ?>
56    <?php endif ?>
57</div>
58
59<div class="row">
60    <div class="<?= $sidebars->isEmpty() ? 'col-sm-12' : 'col-sm-8' ?>">
61        <div class="row mb-4">
62            <!-- Individual images -->
63            <?php if ($individual_media->isNotEmpty() || $tree->getPreference('USE_SILHOUETTE') === '1') : ?>
64                <div class="col-sm-3">
65                    <?php if ($individual_media->isEmpty()) : ?>
66                        <div class="img-thumbnail">
67                            <i class="wt-individual-silhouette wt-individual-silhouette-<?= strtolower($individual->sex()) ?>"></i>
68                        </div>
69                    <?php elseif ($individual_media->count() === 1) : ?>
70                        <?= $individual_media->first()->displayImage(200, 260, 'crop', ['class' => 'img-thumbnail img-fluid w-100']) ?>
71                    <?php else : ?>
72                        <div id="individual-images" class="carousel slide" data-ride="carousel" data-interval="false">
73                            <div class="carousel-inner">
74                                <?php foreach ($individual_media as $n => $media_file) : ?>
75                                    <div class="carousel-item <?= $n === 0 ? 'active' : '' ?>">
76                                        <?= $media_file->displayImage(200, 260, 'crop', ['class' => 'img-thumbnail img-fluid w-100']) ?>
77                                    </div>
78                                <?php endforeach ?>
79                            </div>
80                            <a class="carousel-control-prev" href="#individual-images" role="button" data-slide="prev">
81                                <span class="carousel-control-prev-icon" aria-hidden="true"></span>
82                                <span class="sr-only"><?= I18N::translate('previous') ?></span>
83                            </a>
84                            <a class="carousel-control-next" href="#individual-images" role="button" data-slide="next">
85                                <span class="carousel-control-next-icon" aria-hidden="true"></span>
86                                <span class="sr-only"><?= I18N::translate('next') ?></span>
87                            </a>
88                        </div>
89                    <?php endif ?>
90
91                    <?php if (Auth::isEditor($individual->tree())) : ?>
92                        <div class="text-center">
93                            <a href="<?= e(route(AddNewFact::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref(), 'fact' => 'OBJE'])) ?>">
94                                <?= I18N::translate('Add a media object') ?>
95                            </a>
96                        </div>
97                    <?php endif ?>
98                </div>
99            <?php endif ?>
100
101            <!-- Name accordion -->
102            <div class="col-sm accordion" id="individual-names">
103                <?php foreach ($name_records as $name_record) : ?>
104                    <?= $name_record ?>
105                <?php endforeach ?>
106
107                <?php foreach ($sex_records as $sex_record) : ?>
108                    <?= $sex_record ?>
109                <?php endforeach ?>
110            </div>
111        </div>
112
113        <div class="wt-tabs-individual" id="individual-tabs">
114            <ul class="nav nav-tabs flex-wrap" role="tablist">
115                <?php foreach ($tabs as $tab) : ?>
116                    <li class="nav-item" role="presentation">
117                        <a class="nav-link<?= $tab->isGrayedOut($individual) ? ' text-muted' : '' ?>" data-toggle="tab" role="tab" data-href="<?= e(route('module', ['module' => $tab->name(), 'action' => 'Tab', 'tree' => $individual->tree()->name(), 'xref' => $individual->xref()])) ?>" href="#<?= $tab->name() ?>">
118                            <?= $tab->tabTitle() ?>
119                        </a>
120                    </li>
121                <?php endforeach ?>
122            </ul>
123
124            <div class="tab-content">
125                <?php foreach ($tabs as $tab) : ?>
126                    <div id="<?= $tab->name() ?>" class="tab-pane fade wt-ajax-load" role="tabpanel"><?php if (!$tab->canLoadAjax()) :
127                        ?><?= $tab->getTabContent($individual) ?><?php
128                             endif ?></div>
129                <?php endforeach ?>
130            </div>
131        </div>
132    </div>
133    <?php if ($sidebars->isNotEmpty()) : ?>
134    <div class="col-sm-4 accordion" id="sidebar">
135        <?php foreach ($sidebars as $sidebar) : ?>
136            <div class="card">
137                <div class="card-header" role="tab" id="sidebar-header-<?= $sidebar->name() ?>">
138                    <div class="card-title mb-0">
139                        <a data-toggle="collapse" href="#sidebar-content-<?= $sidebar->name() ?>" aria-expanded="<?= $sidebar->name() === 'family_nav' ? 'true' : 'false' ?>" aria-controls="sidebar-content-<?= $sidebar->name() ?>">
140                            <?= view('icons/expand') ?>
141                            <?= view('icons/collapse') ?>
142                            <?= $sidebar->sidebarTitle() ?>
143                        </a>
144                    </div>
145                </div>
146
147                <div id="sidebar-content-<?= $sidebar->name() ?>" class="collapse<?= $sidebar->name() === 'family_nav' ? ' show' : '' ?>" data-parent="#sidebar" aria-labelledby="sidebar-header-<?= $sidebar->name() ?>">
148                    <div class="card-body">
149                        <?= $sidebar->getSidebarContent($individual) ?>
150                    </div>
151                </div>
152            </div>
153        <?php endforeach ?>
154    </div>
155    <?php endif ?>
156</div>
157
158<?php View::push('javascript') ?>
159<script>
160  "use strict";
161
162  // Bootstrap tabs - load content dynamically using AJAX
163  $('a[data-toggle="tab"][data-href]').on('show.bs.tab', function () {
164      let target = $(this.hash + ':empty');
165      if (target.length > 0) {
166          // Start the download immediately...
167          let download = fetch(this.dataset.href);
168
169          // ...but don't insert it until the tab is ready.
170          $(this).one('shown.bs.tab', () => {
171              download
172                  .then(data => data.text())
173                  .then(data => target.html(data));
174          });
175      }
176  });
177
178  // If the URL contains a fragment, then activate the corresponding tab.
179  // Use a prefix on the fragment, to prevent scrolling to the element.
180  let target = window.location.hash.replace("tab-", "");
181  let tab = $("#individual-tabs .nav-link[href='" + target + "']");
182  // If not, then activate the first tab.
183  if (tab.length === 0) {
184    tab = $("#individual-tabs .nav-link:first");
185  }
186  tab.tab("show");
187
188  // If the user selects a tab, update the URL to reflect this
189  $('#individual-tabs a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
190    window.location.hash = "tab-" + e.target.href.substring(e.target.href.indexOf('#') + 1);
191  });
192</script>
193<?php View::endpush() ?>
194
195<?= view('modals/ajax') ?>
196