xref: /webtrees/resources/views/admin/control-panel.phtml (revision 77c72ee5b2feabccd404f30f83ff89f5637dd0e0)
1<?php
2
3use Fisharebest\Webtrees\Http\RequestHandlers\BroadcastPage;
4use Fisharebest\Webtrees\Http\RequestHandlers\CleanDataFolder;
5use Fisharebest\Webtrees\Http\RequestHandlers\ControlPanel;
6use Fisharebest\Webtrees\Http\RequestHandlers\CreateTreePage;
7use Fisharebest\Webtrees\Http\RequestHandlers\EmailPreferencesPage;
8use Fisharebest\Webtrees\Http\RequestHandlers\FixLevel0MediaPage;
9use Fisharebest\Webtrees\Http\RequestHandlers\ImportThumbnailsPage;
10use Fisharebest\Webtrees\Http\RequestHandlers\ManageMediaPage;
11use Fisharebest\Webtrees\Http\RequestHandlers\ManageTrees;
12use Fisharebest\Webtrees\Http\RequestHandlers\MapDataList;
13use Fisharebest\Webtrees\Http\RequestHandlers\MergeTreesPage;
14use Fisharebest\Webtrees\Http\RequestHandlers\ModulesAllPage;
15use Fisharebest\Webtrees\Http\RequestHandlers\ModulesAnalyticsPage;
16use Fisharebest\Webtrees\Http\RequestHandlers\ModulesBlocksPage;
17use Fisharebest\Webtrees\Http\RequestHandlers\ModulesChartsPage;
18use Fisharebest\Webtrees\Http\RequestHandlers\ModulesCustomTagsPage;
19use Fisharebest\Webtrees\Http\RequestHandlers\ModulesDataFixesPage;
20use Fisharebest\Webtrees\Http\RequestHandlers\ModulesFootersPage;
21use Fisharebest\Webtrees\Http\RequestHandlers\ModulesHistoricEventsPage;
22use Fisharebest\Webtrees\Http\RequestHandlers\ModulesLanguagesPage;
23use Fisharebest\Webtrees\Http\RequestHandlers\ModulesListsPage;
24use Fisharebest\Webtrees\Http\RequestHandlers\ModulesMapAutocompletePage;
25use Fisharebest\Webtrees\Http\RequestHandlers\ModulesMapLinksPage;
26use Fisharebest\Webtrees\Http\RequestHandlers\ModulesMapGeoLocationsPage;
27use Fisharebest\Webtrees\Http\RequestHandlers\ModulesMapProvidersPage;
28use Fisharebest\Webtrees\Http\RequestHandlers\ModulesMenusPage;
29use Fisharebest\Webtrees\Http\RequestHandlers\ModulesReportsPage;
30use Fisharebest\Webtrees\Http\RequestHandlers\ModulesSharesPage;
31use Fisharebest\Webtrees\Http\RequestHandlers\ModulesSidebarsPage;
32use Fisharebest\Webtrees\Http\RequestHandlers\ModulesTabsPage;
33use Fisharebest\Webtrees\Http\RequestHandlers\ModulesThemesPage;
34use Fisharebest\Webtrees\Http\RequestHandlers\PendingChanges;
35use Fisharebest\Webtrees\Http\RequestHandlers\PhpInformation;
36use Fisharebest\Webtrees\Http\RequestHandlers\SiteLogsPage;
37use Fisharebest\Webtrees\Http\RequestHandlers\SitePreferencesPage;
38use Fisharebest\Webtrees\Http\RequestHandlers\SiteRegistrationPage;
39use Fisharebest\Webtrees\Http\RequestHandlers\TreePageDefaultEdit;
40use Fisharebest\Webtrees\Http\RequestHandlers\UpgradeWizardPage;
41use Fisharebest\Webtrees\Http\RequestHandlers\UploadMediaPage;
42use Fisharebest\Webtrees\Http\RequestHandlers\UserAddPage;
43use Fisharebest\Webtrees\Http\RequestHandlers\UserEditPage;
44use Fisharebest\Webtrees\Http\RequestHandlers\UserListPage;
45use Fisharebest\Webtrees\Http\RequestHandlers\UserPageDefaultEdit;
46use Fisharebest\Webtrees\Http\RequestHandlers\UsersCleanupPage;
47use Fisharebest\Webtrees\I18N;
48use Fisharebest\Webtrees\Module\FamilyListModule;
49use Fisharebest\Webtrees\Module\IndividualListModule;
50use Fisharebest\Webtrees\Module\MediaListModule;
51use Fisharebest\Webtrees\Module\ModuleAnalyticsInterface;
52use Fisharebest\Webtrees\Module\ModuleBlockInterface;
53use Fisharebest\Webtrees\Module\ModuleChartInterface;
54use Fisharebest\Webtrees\Module\ModuleConfigInterface;
55use Fisharebest\Webtrees\Module\ModuleCustomInterface;
56use Fisharebest\Webtrees\Module\ModuleCustomTagsInterface;
57use Fisharebest\Webtrees\Module\ModuleDataFixInterface;
58use Fisharebest\Webtrees\Module\ModuleFooterInterface;
59use Fisharebest\Webtrees\Module\ModuleHistoricEventsInterface;
60use Fisharebest\Webtrees\Module\ModuleInterface;
61use Fisharebest\Webtrees\Module\ModuleLanguageInterface;
62use Fisharebest\Webtrees\Module\ModuleListInterface;
63use Fisharebest\Webtrees\Module\ModuleMapAutocompleteInterface;
64use Fisharebest\Webtrees\Module\ModuleMapGeoLocationInterface;
65use Fisharebest\Webtrees\Module\ModuleMapLinkInterface;
66use Fisharebest\Webtrees\Module\ModuleMapProviderInterface;
67use Fisharebest\Webtrees\Module\ModuleMenuInterface;
68use Fisharebest\Webtrees\Module\ModuleReportInterface;
69use Fisharebest\Webtrees\Module\ModuleShareInterface;
70use Fisharebest\Webtrees\Module\ModuleSidebarInterface;
71use Fisharebest\Webtrees\Module\ModuleTabInterface;
72use Fisharebest\Webtrees\Module\ModuleThemeInterface;
73use Fisharebest\Webtrees\Module\NoteListModule;
74use Fisharebest\Webtrees\Module\RepositoryListModule;
75use Fisharebest\Webtrees\Module\SourceListModule;
76use Fisharebest\Webtrees\Module\SubmitterListModule;
77use Fisharebest\Webtrees\Tree;
78use Fisharebest\Webtrees\User;
79use Fisharebest\Webtrees\Webtrees;
80use Illuminate\Support\Collection;
81
82/**
83 * @var Collection<User>                           $administrators
84 * @var Collection<ModuleInterface>                $all_modules_disabled
85 * @var Collection<ModuleInterface>                $all_modules_enabled
86 * @var Collection<Tree>                           $all_trees
87 * @var Collection<User>                           $all_users
88 * @var Collection<ModuleAnalyticsInterface>       $analytics_modules_disabled
89 * @var Collection<ModuleAnalyticsInterface>       $analytics_modules_enabled
90 * @var Collection<ModuleBlockInterface>           $block_modules_disabled
91 * @var Collection<ModuleBlockInterface>           $block_modules_enabled
92 * @var array<string,int>                          $changes
93 * @var Collection<ModuleChartInterface>           $chart_modules_disabled
94 * @var Collection<ModuleChartInterface>           $chart_modules_enabled
95 * @var Collection<ModuleCustomInterface>          $custom_updates
96* @var Collection<ModuleCustomTagsInterface>     $custom_tags_modules_disabled
97 * @var Collection<ModuleCustomTagsInterface>     $custom_tags_modules_enabled
98 * @var Collection<ModuleDataFixInterface>         $data_fix_modules_disabled
99 * @var Collection<ModuleDataFixInterface>         $data_fix_modules_enabled
100 * @var Collection<string,int>                     $families
101 * @var FamilyListModule|null                      $family_list_module
102 * @var Collection<ModuleFooterInterface>          $footer_modules_disabled
103 * @var Collection<ModuleFooterInterface>          $footer_modules_enabled
104 * @var Collection<ModuleHistoricEventsInterface>  $history_modules_disabled
105 * @var Collection<ModuleHistoricEventsInterface>  $history_modules_enabled
106 * @var Collection<string,int>                     $individuals
107 * @var IndividualListModule|null                  $individual_list_module
108 * @var Collection<ModuleLanguageInterface>        $language_modules_disabled
109 * @var Collection<ModuleLanguageInterface>        $language_modules_enabled
110 * @var string                                     $latest_version
111 * @var Collection<ModuleListInterface>            $list_modules_disabled
112 * @var Collection<ModuleListInterface>            $list_modules_enabled
113 * @var Collection<ModuleMapLinkInterface>         $map_link_modules_disabled
114 * @var Collection<ModuleMapLinkInterface>         $map_link_modules_enabled
115 * @var Collection<ModuleMapAutocompleteInterface> $map_autocomplete_modules_disabled
116 * @var Collection<ModuleMapAutocompleteInterface> $map_autocomplete_modules_enabled
117 * @var Collection<ModuleMapProviderInterface>     $map_provider_modules_disabled
118 * @var Collection<ModuleMapProviderInterface>     $map_provider_modules_enabled
119 * @var Collection<ModuleMapGeoLocationInterface>  $map_search_modules_disabled
120 * @var Collection<ModuleMapGeoLocationInterface>  $map_search_modules_enabled
121 * @var Collection<User>                           $managers
122 * @var Collection<string,int>                     $media
123 * @var MediaListModule|null                       $media_list_module
124 * @var Collection<ModuleMenuInterface>            $menu_modules_disabled
125 * @var Collection<ModuleMenuInterface>            $menu_modules_enabled
126 * @var Collection<User>                           $moderators
127 * @var Collection<string,int>                     $notes
128 * @var NoteListModule|null                        $note_list_module
129 * @var Collection<ModuleInterface>                $other_modules
130 * @var Collection<ModuleReportInterface>          $report_modules_disabled
131 * @var Collection<ModuleReportInterface>          $report_modules_enabled
132 * @var Collection<string,int>                     $repositories
133 * @var RepositoryListModule|null                  $repository_list_module
134 * @var Collection<string>                         $server_errors
135 * @var Collection<string>                         $server_warnings
136 * @var bool                                       $show_synchronize
137 * @var Collection<ModuleShareInterface>           $share_modules_disabled
138 * @var Collection<ModuleShareInterface>           $share_modules_enabled
139 * @var Collection<ModuleSidebarInterface>         $sidebar_modules_disabled
140 * @var Collection<ModuleSidebarInterface>         $sidebar_modules_enabled
141 * @var Collection<string,int>                     $sources
142 * @var SourceListModule|null                      $source_list_module
143 * @var Collection<string,int>                     $submitters
144 * @var SubmitterListModule|null                   $submitter_list_module
145 * @var Collection<ModuleTabInterface>             $tab_modules_disabled
146 * @var Collection<ModuleTabInterface>             $tab_modules_enabled
147 * @var Collection<ModuleThemeInterface>           $theme_modules_disabled
148 * @var Collection<ModuleThemeInterface>           $theme_modules_enabled
149 * @var string                                     $title
150 * @var Collection<User>                           $unapproved
151 * @var Collection<User>                           $unverified
152 */
153
154?>
155
156<h1><?= $title ?></h1>
157
158<!-- WEBSITE / WEBTREES -->
159<div class="card mb-4">
160    <div class="card-header">
161        <h2 class="mb-0">
162            <?= I18N::translate('Website') ?>
163            <span class="badge badge-secondary" dir="ltr">
164                <?= e(Webtrees::VERSION) ?>
165            </span>
166        </h2>
167    </div>
168    <div class="card-body">
169        <?php foreach ($server_errors as $server_error) : ?>
170            <p class="alert alert-danger"><?= $server_error ?></p>
171        <?php endforeach ?>
172
173        <?php foreach ($server_warnings as $server_warning) : ?>
174            <p class="alert alert-warning"><?= $server_warning ?></p>
175        <?php endforeach ?>
176
177        <p class="card-text">
178            <?= /* I18N: %s is a URL/link to the project website */
179            I18N::translate('Support and documentation can be found at %s.', '<a href="https://webtrees.net/">webtrees.net</a>') ?>
180        </p>
181
182        <?php if ($latest_version === '') : ?>
183            <p class="card-text">
184                <?= I18N::translate('No upgrade information is available.') ?>
185            </p>
186        <?php elseif (version_compare(Webtrees::VERSION, $latest_version) < 0) : ?>
187            <p class="card-text alert alert-info">
188                <?= I18N::translate('A new version of webtrees is available.') ?>
189                <a href="<?= e(route(UpgradeWizardPage::class)) ?>" class="alert-link">
190                    <?= /* I18N: %s is a version number */
191                    I18N::translate('Upgrade to webtrees %s.', e($latest_version)) ?>
192                </a>
193            </p>
194        <?php else : ?>
195            <p class="card-text">
196                <?= I18N::translate('This is the latest version of webtrees. No upgrade is available.') ?>
197            </p>
198        <?php endif ?>
199
200        <?php foreach ($custom_updates as $module) : ?>
201        <div class="alert alert-info">
202            <?= view('icons/information') ?>
203            <?= I18N::translate('An upgrade is available.') ?>
204            <br>
205            <?= $module->title() ?><?= e($module->customModuleVersion()) ?><?= e($module->customModuleLatestVersion()) ?>
206        </div>
207        <?php endforeach ?>
208
209        <div class="row">
210            <div class="col-sm-6">
211                <ul class="fa-ul">
212                    <li>
213                        <span class="fa-li"><?= view('icons/preferences') ?></span>
214                        <a href="<?= e(route(SitePreferencesPage::class)) ?>">
215                            <?= I18N::translate('Website preferences') ?>
216                        </a>
217                    </li>
218                    <li>
219                        <span class="fa-li"><?= view('icons/email') ?></span>
220                        <a href="<?= e(route(EmailPreferencesPage::class)) ?>">
221                            <?= I18N::translate('Sending email') ?>
222                        </a>
223                    </li>
224                    <li>
225                        <span class="fa-li"><?= view('icons/user') ?></span>
226                        <a href="<?= e(route(SiteRegistrationPage::class)) ?>">
227                            <?= I18N::translate('Sign-in and registration') ?>
228                        </a>
229                    </li>
230                </ul>
231            </div>
232            <div class="col-sm-6">
233                <ul class="fa-ul">
234                    <li>
235                        <span class="fa-li"><?= view('icons/list') ?></span>
236                        <a href="<?= e(route(SiteLogsPage::class)) ?>">
237                            <?= I18N::translate('Website logs') ?>
238                        </a>
239                    </li>
240                    <li>
241                        <span class="fa-li"><?= view('icons/delete') ?></span>
242                        <a href="<?= e(route(CleanDataFolder::class)) ?>">
243                            <?= I18N::translate('Clean up data folder') ?>
244                        </a>
245                    </li>
246                    <li>
247                        <span class="fa-li"><?= view('icons/help') ?></span>
248                        <a href="<?= e(route(PhpInformation::class)) ?>">
249                            <?= I18N::translate('Server information') ?>
250                        </a>
251                    </li>
252                </ul>
253            </div>
254        </div>
255    </div>
256</div>
257
258<!-- FAMILY TREES -->
259<div class="card mb-4 <?= array_sum($changes) ? 'card-outline-danger' : '' ?>">
260    <div class="card-header">
261        <h2 class="mb-0">
262            <?= I18N::translate('Family trees') ?>
263            <span class="badge badge-secondary">
264                    <?= I18N::number($all_trees->count()) ?>
265                </span>
266        </h2>
267    </div>
268
269    <div class="card-body">
270        <?php if ($all_trees->isEmpty()) : ?>
271            <p class="alert alert-info">
272                <?= I18N::translate('You need to create a family tree.') ?>
273            </p>
274        <?php endif ?>
275
276        <ul class="fa-ul">
277            <li>
278                <span class="fa-li"><?= view('icons/add') ?></span>
279                <a href="<?= e(route(CreateTreePage::class)) ?>">
280                    <?= I18N::translate('Create a family tree') ?>
281                </a>
282            </li>
283            <li>
284                <span class="fa-li"><?= view('icons/block') ?></span>
285                <a href="<?= e(route(TreePageDefaultEdit::class)) ?>">
286                    <?= I18N::translate('Set the default blocks for new family trees') ?>
287                </a>
288            </li>
289            <?php if (count($all_trees) > 1) : ?>
290                <li>
291                    <span class="fa-li"><?= view('icons/merge') ?></span>
292                    <a href="<?= e(route(MergeTreesPage::class)) ?>">
293                        <?= I18N::translate('Merge family trees') ?>
294                    </a>
295                </li>
296            <?php endif ?>
297        </ul>
298
299        <table class="table table-sm">
300            <caption class="sr-only">
301                <?= I18N::translate('Family trees') ?>
302            </caption>
303
304            <thead>
305                <tr>
306                    <th><?= I18N::translate('Family tree') ?></th>
307                    <th class="align-digits"><?= I18N::translate('Pending changes') ?></th>
308                    <th class="d-none d-sm-table-cell align-digits"><?= I18N::translate('Individuals') ?></th>
309                    <th class="d-none d-lg-table-cell align-digits"><?= I18N::translate('Families') ?></th>
310                    <th class="d-none d-sm-table-cell align-digits"><?= I18N::translate('Sources') ?></th>
311                    <th class="d-none d-lg-table-cell align-digits"><?= I18N::translate('Repositories') ?></th>
312                    <th class="d-none d-sm-table-cell align-digits"><?= I18N::translate('Media') ?></th>
313                    <th class="d-none d-lg-table-cell align-digits"><?= I18N::translate('Notes') ?></th>
314                    <th class="d-none d-lg-table-cell align-digits"><?= I18N::translate('Submitters') ?></th>
315                </tr>
316            </thead>
317
318            <tbody>
319                <?php foreach ($all_trees as $tree) : ?>
320                    <tr class="<?= $changes[$tree->id()] ? 'danger' : '' ?>">
321                        <th scope="row">
322                            <?= view('icons/preferences') ?>
323                            <a href="<?= e(route(ManageTrees::class, ['tree' => $tree->name()])) ?>">
324                                <?= e($tree->name()) ?>
325                                -
326                                <?= e($tree->title()) ?>
327                            </a>
328                        </th>
329                        <td style="text-align: right;">
330                            <?php if ($changes[$tree->id()]) : ?>
331                                <a href="<?= e(route(PendingChanges::class, ['tree' => $tree->name(), 'url' => route(ControlPanel::class)])) ?>">
332                                    <?= I18N::number($changes[$tree->id()]) ?>
333                                    <span class="sr-only"><?= I18N::translate('Pending changes') ?> <?= e($tree->title()) ?></span>
334                                </a>
335                            <?php else : ?>
336                                -
337                            <?php endif ?>
338                        </td>
339                        <td class="d-none d-sm-table-cell align-digits">
340                            <?php if ($individuals[$tree->id()] > 0) : ?>
341                                <?php if ($individual_list_module instanceof IndividualListModule) : ?>
342                                    <a href="<?= e($individual_list_module->listUrl($tree)) ?>"><?= I18N::number($individuals[$tree->id()]) ?></a>
343                                <?php else : ?>
344                                    <?= I18N::number($individuals[$tree->id()]) ?>
345                                <?php endif ?>
346                            <?php else : ?>
347                                -
348                            <?php endif ?>
349                        </td>
350                        <td class="d-none d-lg-table-cell align-digits">
351                            <?php if ($families[$tree->id()] > 0) : ?>
352                                <?php if ($family_list_module instanceof FamilyListModule) : ?>
353                                    <a href="<?= e($family_list_module->listUrl($tree)) ?>"><?= I18N::number($families[$tree->id()]) ?></a>
354                                <?php else : ?>
355                                    <?= I18N::number($families[$tree->id()]) ?>
356                                <?php endif ?>
357                            <?php else : ?>
358                                -
359                            <?php endif ?>
360                        </td>
361                        <td class="d-none d-sm-table-cell align-digits">
362                            <?php if ($sources[$tree->id()] > 0) : ?>
363                                <?php if ($source_list_module instanceof SourceListModule) : ?>
364                                    <a href="<?= e($source_list_module->listUrl($tree)) ?>"><?= I18N::number($sources[$tree->id()]) ?></a>
365                                <?php else : ?>
366                                    <?= I18N::number($sources[$tree->id()]) ?>
367                                <?php endif ?>
368                            <?php else : ?>
369                                -
370                            <?php endif ?>
371                        </td>
372                        <td class="d-none d-lg-table-cell align-digits">
373                            <?php if ($repositories[$tree->id()] > 0) : ?>
374                                <?php if ($repository_list_module instanceof RepositoryListModule) : ?>
375                                    <a href="<?= e($repository_list_module->listUrl($tree)) ?>"><?= I18N::number($repositories[$tree->id()]) ?></a>
376                                <?php else : ?>
377                                    <?= I18N::number($repositories[$tree->id()]) ?>
378                                <?php endif ?>
379                            <?php else : ?>
380                                -
381                            <?php endif ?>
382                        </td>
383                        <td class="d-none d-sm-table-cell align-digits">
384                            <?php if ($media[$tree->id()] > 0) : ?>
385                                <?php if ($media_list_module instanceof MediaListModule) : ?>
386                                    <a href="<?= e($media_list_module->listUrl($tree)) ?>"><?= I18N::number($media[$tree->id()]) ?></a>
387                                <?php else : ?>
388                                    <?= I18N::number($media[$tree->id()]) ?>
389                                <?php endif ?>
390                            <?php else : ?>
391                                -
392                            <?php endif ?>
393                        </td>
394                        <td class="d-none d-lg-table-cell align-digits">
395                            <?php if ($notes[$tree->id()] > 0) : ?>
396                                <?php if ($note_list_module instanceof NoteListModule) : ?>
397                                    <a href="<?= e($note_list_module->listUrl($tree)) ?>"><?= I18N::number($notes[$tree->id()]) ?></a>
398                                <?php else : ?>
399                                    <?= I18N::number($notes[$tree->id()]) ?>
400                                <?php endif ?>
401                            <?php else : ?>
402                                -
403                            <?php endif ?>
404                        </td>
405                         <td class="d-none d-lg-table-cell align-digits">
406                            <?php if ($submitters[$tree->id()] > 0) : ?>
407                                <?php if ($submitter_list_module instanceof SubmitterListModule) : ?>
408                                    <a href="<?= e($submitter_list_module->listUrl($tree)) ?>"><?= I18N::number($submitters[$tree->id()]) ?></a>
409                                <?php else : ?>
410                                    <?= I18N::number($submitters[$tree->id()]) ?>
411                                <?php endif ?>
412                            <?php else : ?>
413                                -
414                            <?php endif ?>
415                        </td>
416                    </tr>
417                <?php endforeach ?>
418            </tbody>
419
420            <tfoot>
421                <tr>
422                    <th scope="row">
423                        <?= I18N::translate('Total') ?>
424                    </th>
425                    <td class="align-digits">
426                        <?= I18N::number(array_sum($changes)) ?>
427                    </td>
428                    <td class="d-none d-sm-table-cell align-digits">
429                        <?= I18N::number($individuals->sum()) ?>
430                    </td>
431                    <td class="d-none d-lg-table-cell align-digits">
432                        <?= I18N::number($families->sum()) ?>
433                    </td>
434                    <td class="d-none d-sm-table-cell align-digits">
435                        <?= I18N::number($sources->sum()) ?>
436                    </td>
437                    <td class="d-none d-lg-table-cell align-digits">
438                        <?= I18N::number($repositories->sum()) ?>
439                    </td>
440                    <td class="d-none d-sm-table-cell align-digits">
441                        <?= I18N::number($media->sum()) ?>
442                    </td>
443                    <td class="d-none d-lg-table-cell align-digits">
444                        <?= I18N::number($notes->sum()) ?>
445                    </td>
446                </tr>
447            </tfoot>
448        </table>
449    </div>
450</div>
451
452<?php if ($show_synchronize): ?>
453    <?= view('admin/synchronize-trees') ?>
454<?php endif ?>
455
456<!-- USERS -->
457<div class="card mb-4 <?= $unapproved || $unverified ? 'card-outline-danger' : '' ?>">
458    <div class="card-header">
459        <h2 class="mb-0">
460            <?= I18N::translate('Users') ?>
461            <span class="badge badge-secondary">
462                    <?= I18N::number(count($all_users)) ?>
463                </span>
464        </h2>
465    </div>
466    <div class="card-body">
467        <dl class="row">
468            <?php foreach ([I18N::translate('Administrators') => $administrators, I18N::translate('Managers') => $managers, I18N::translate('Moderators') => $moderators, I18N::translate('Not verified by the user') => $unverified, I18N::translate('Not approved by an administrator') => $unapproved] as $label => $list) : ?>
469                <?php if (!empty($list)) : ?>
470                    <dt class="col-sm-6">
471                        <?= $label ?>
472                    </dt>
473                    <dd class="col-sm-6">
474                        <?php foreach ($list as $n => $user) : ?>
475                            <?= $n ? I18N::$list_separator : '' ?>
476                            <a href="<?= e(route(UserEditPage::class, ['user_id' => $user->id()])) ?>" dir="auto">
477                                <?= e($user->realName()) ?>
478                            </a>
479                        <?php endforeach ?>
480                        <?php if ($list->isEmpty()) : ?>
481482                        <?php endif ?>
483                    </dd>
484                <?php endif ?>
485            <?php endforeach ?>
486        </dl>
487
488        <div class="row">
489            <div class="col-sm-6">
490                <ul class="fa-ul">
491                    <li>
492                        <span class="fa-li"><?= view('icons/preferences') ?></span>
493                        <a href="<?= e(route(UserListPage::class)) ?>">
494                            <?= I18N::translate('User administration') ?>
495                        </a>
496                    </li>
497                    <li>
498                        <span class="fa-li"><?= view('icons/add') ?></span>
499                        <a href="<?= e(route(UserAddPage::class)) ?>">
500                            <?= I18N::translate('Add a user') ?>
501                        </a>
502                    </li>
503                    <li>
504                        <span class="fa-li"><?= view('icons/delete') ?></span>
505                        <a href="<?= e(route(UsersCleanupPage::class)) ?>">
506                            <?= I18N::translate('Delete inactive users') ?>
507                        </a>
508                    </li>
509                    <li>
510                        <span class="fa-li"><?= view('icons/block') ?></span>
511                        <a href="<?= e(route(UserPageDefaultEdit::class)) ?>">
512                            <?= I18N::translate('Set the default blocks for new users') ?>
513                        </a>
514                    </li>
515                </ul>
516            </div>
517            <div class="col-sm-6">
518                <ul class="fa-ul">
519                    <li>
520                        <span class="fa-li"><?= view('icons/email') ?></span>
521                        <a href="<?= e(route(BroadcastPage::class, ['to' => 'all'])) ?>">
522                            <?= I18N::translate('Send a message to all users') ?>
523                        </a>
524                    </li>
525                    <li>
526                        <span class="fa-li"><?= view('icons/email') ?></span>
527                        <a href="<?= e(route(BroadcastPage::class, ['to' => 'never_logged'])) ?>">
528                            <?= I18N::translate('Send a message to users who have never signed in') ?>
529                        </a>
530                    </li>
531                    <li>
532                        <span class="fa-li"><?= view('icons/email') ?></span>
533                        <a href="<?= e(route(BroadcastPage::class, ['to' => 'last_6mo'])) ?>">
534                            <?= I18N::translate('Send a message to users who have not signed in for 6 months') ?>
535                        </a>
536                    </li>
537                </ul>
538            </div>
539        </div>
540    </div>
541</div>
542
543<!-- MODULES -->
544<div class="card mb-4">
545    <div class="card-header">
546        <h2 class="mb-0">
547            <?= I18N::translate('Modules') ?>
548            <?= view('components/badge', ['count' => $all_modules_enabled->count(), 'total' => $all_modules_disabled->count(), 'context' => 'secondary']) ?>
549        </h2>
550    </div>
551
552    <div class="card-body">
553        <ul class="fa-ul">
554            <li>
555                <span class="fa-li"><?= view('icons/module') ?></span>
556                <a href="<?= e(route(ModulesAllPage::class)) ?>">
557                    <?= I18N::translate('All modules') ?>
558                </a>
559                <?= view('components/badge', ['count' => $all_modules_enabled->count(), 'total' => $all_modules_disabled->count(), 'context' => 'primary']) ?>
560            </li>
561        </ul>
562
563        <div class="row">
564            <div class="col-sm-4">
565                <h3>
566                    <?= I18N::translate('Website') ?>
567                </h3>
568
569                <ul class="fa-ul">
570                    <li>
571                        <span class="fa-li"><?= view('icons/footer') ?></span>
572                        <a href="<?= e(route(ModulesFootersPage::class)) ?>">
573                            <?= I18N::translate('Footers') ?>
574                        </a>
575                        <?= view('components/badge', ['count' => $footer_modules_enabled->count(), 'total' => $footer_modules_disabled->count(), 'context' => 'primary']) ?>
576                    </li>
577                    <li>
578                        <span class="fa-li"><?= view('icons/analytics') ?></span>
579                        <a href="<?= e(route(ModulesAnalyticsPage::class)) ?>">
580                            <?= I18N::translate('Tracking and analytics') ?>
581                        </a>
582                        <?= view('components/badge', ['count' => $analytics_modules_enabled->count(), 'total' => $analytics_modules_disabled->count(), 'context' => 'primary']) ?>
583                    </li>
584                    <li>
585                        <span class="fa-li"><?= view('icons/theme') ?></span>
586                        <a href="<?= e(route(ModulesThemesPage::class)) ?>">
587                            <?= I18N::translate('Themes') ?>
588                        </a>
589                        <?= view('components/badge', ['count' => $theme_modules_enabled->count(), 'total' => $theme_modules_disabled->count(), 'context' => 'primary']) ?>
590                    </li>
591                    <li>
592                        <span class="fa-li"><?= view('icons/language') ?></span>
593                        <a href="<?= e(route(ModulesLanguagesPage::class)) ?>">
594                            <?= I18N::translate('Languages') ?>
595                        </a>
596                        <?= view('components/badge', ['count' => $language_modules_enabled->count(), 'total' => $language_modules_disabled->count(), 'context' => 'primary']) ?>
597                    </li>
598                </ul>
599
600                <h3>
601                    <?= I18N::translate('Home page') ?>
602                </h3>
603
604                <ul class="fa-ul">
605                    <li>
606                        <span class="fa-li"><?= view('icons/block') ?></span>
607                        <a href="<?= e(route(ModulesBlocksPage::class)) ?>">
608                            <?= I18N::translate('Blocks') ?>
609                        </a>
610                        <?= view('components/badge', ['count' => $block_modules_enabled->count(), 'total' => $block_modules_disabled->count(), 'context' => 'primary']) ?>
611                    </li>
612                </ul>
613
614                <h3>
615                    <?= I18N::translate('Geographic data') ?>
616                </h3>
617
618                <ul class="fa-ul">
619                    <li>
620                        <span class="fa-li"><?= view('icons/search') ?></span>
621                        <a href="<?= e(route(ModulesMapAutocompletePage::class)) ?>">
622                            <?= /* I18N: Automatic suggestions when you type */ I18N::translate('Autocomplete') ?>
623                        </a>
624                        <?= view('components/badge', ['count' => $map_autocomplete_modules_enabled->count(), 'total' => $map_autocomplete_modules_disabled->count(), 'context' => 'primary']) ?>
625                    </li>
626                    <li>
627                        <span class="fa-li"><?= view('icons/search-location') ?></span>
628                        <a href="<?= e(route(ModulesMapGeoLocationsPage::class)) ?>">
629                            <?= /* I18N: find latitude/longitude for a place */ I18N::translate('Geolocation') ?>
630                        </a>
631                        <?= view('components/badge', ['count' => $map_search_modules_enabled->count(), 'total' => $map_search_modules_disabled->count(), 'context' => 'primary']) ?>
632                    </li>
633                    <li>
634                        <span class="fa-li"><?= view('icons/location') ?></span>
635                        <a href="<?= e(route(ModulesMapLinksPage::class)) ?>">
636                            <?= /* I18N: Links to maps */ I18N::translate('Map links') ?>
637                        </a>
638                        <?= view('components/badge', ['count' => $map_link_modules_enabled->count(), 'total' => $map_link_modules_disabled->count(), 'context' => 'primary']) ?>
639                    </li>
640                    <li>
641                        <span class="fa-li"><?= view('icons/map') ?></span>
642                        <a href="<?= e(route(ModulesMapProvidersPage::class)) ?>">
643                            <?= I18N::translate('Map providers') ?>
644                        </a>
645                        <?= view('components/badge', ['count' => $map_provider_modules_enabled->count(), 'total' => $map_provider_modules_disabled->count(), 'context' => 'primary']) ?>
646                    </li>
647                </ul>
648            </div>
649
650            <div class="col-sm-4">
651                <h3>
652                    <?= I18N::translate('Genealogy') ?>
653                </h3>
654
655                <ul class="fa-ul">
656                    <li>
657                        <span class="fa-li"><?= view('icons/menu') ?></span>
658                        <a href="<?= e(route(ModulesMenusPage::class)) ?>">
659                            <?= I18N::translate('Menus') ?>
660                        </a>
661                        <?= view('components/badge', ['count' => $menu_modules_enabled->count(), 'total' => $menu_modules_disabled->count(), 'context' => 'primary']) ?>
662                    </li>
663                    <li>
664                        <span class="fa-li"><?= view('icons/chart') ?></span>
665                        <a href="<?= e(route(ModulesChartsPage::class)) ?>">
666                            <?= I18N::translate('Charts') ?>
667                        </a>
668                        <?= view('components/badge', ['count' => $chart_modules_enabled->count(), 'total' => $chart_modules_disabled->count(), 'context' => 'primary']) ?>
669                    </li>
670                    <li>
671                        <span class="fa-li"><?= view('icons/list') ?></span>
672                        <a href="<?= e(route(ModulesListsPage::class)) ?>">
673                            <?= I18N::translate('Lists') ?>
674                        </a>
675                        <?= view('components/badge', ['count' => $list_modules_enabled->count(), 'total' => $list_modules_disabled->count(), 'context' => 'primary']) ?>
676                    </li>
677                    <li>
678                        <span class="fa-li"><?= view('icons/report') ?></span>
679                        <a href="<?= e(route(ModulesReportsPage::class)) ?>">
680                            <?= I18N::translate('Reports') ?>
681                        </a>
682                        <?= view('components/badge', ['count' => $report_modules_enabled->count(), 'total' => $report_modules_disabled->count(), 'context' => 'primary']) ?>
683                    </li>
684                    <li>
685                        <span class="fa-li"><?= view('icons/data-fix') ?></span>
686                        <a href="<?= e(route(ModulesDataFixesPage::class)) ?>">
687                            <?= I18N::translate('Data fixes') ?>
688                        </a>
689                        <?= view('components/badge', ['count' => $data_fix_modules_enabled->count(), 'total' => $data_fix_modules_disabled->count(), 'context' => 'primary']) ?>
690                    </li>
691                    <li>
692                        <span class="fa-li"><?= view('icons/tag') ?></span>
693                        <a href="<?= e(route(ModulesCustomTagsPage::class)) ?>">
694                            <?= I18N::translate('Custom GEDCOM tags') ?>
695                        </a>
696                        <?= view('components/badge', ['count' => $custom_tags_modules_enabled->count(), 'total' => $custom_tags_modules_disabled->count(), 'context' => 'primary']) ?>
697                    </li>
698                </ul>
699
700                <h3>
701                    <?= I18N::translate('Individual page') ?>
702                </h3>
703
704                <ul class="fa-ul">
705                    <li>
706                        <span class="fa-li"><?= view('icons/tab') ?></span>
707                        <a href="<?= e(route(ModulesTabsPage::class)) ?>">
708                            <?= I18N::translate('Tabs') ?>
709                            <?= view('components/badge', ['count' => $tab_modules_enabled->count(), 'total' => $tab_modules_disabled->count(), 'context' => 'primary']) ?>
710                        </a>
711                    </li>
712                    <li>
713                        <span class="fa-li"><?= view('icons/sidebar') ?></span>
714                        <a href="<?= e(route(ModulesSidebarsPage::class)) ?>">
715                            <?= I18N::translate('Sidebars') ?>
716                        </a>
717                        <?= view('components/badge', ['count' => $sidebar_modules_enabled->count(), 'total' => $sidebar_modules_disabled->count(), 'context' => 'primary']) ?>
718                    </li>
719                    <li>
720                        <span class="fa-li"><?= view('icons/history') ?></span>
721                        <a href="<?= e(route(ModulesHistoricEventsPage::class)) ?>">
722                            <?= I18N::translate('Historic events') ?>
723                        </a>
724                        <?= view('components/badge', ['count' => $history_modules_enabled->count(), 'total' => $history_modules_disabled->count(), 'context' => 'primary']) ?>
725                    </li>
726                    <li>
727                        <span class="fa-li"><?= view('icons/share') ?></span>
728                        <a href="<?= e(route(ModulesSharesPage::class)) ?>">
729                            <?= /* I18N: plural noun - things that can be shared */  I18N::translate('Shares') ?>
730                        </a>
731                        <?= view('components/badge', ['count' => $share_modules_enabled->count(), 'total' => $share_modules_disabled->count(), 'context' => 'primary']) ?>
732                    </li>
733                </ul>
734            </div>
735
736            <div class="col-sm-4">
737
738                <h3>
739                    <?= I18N::translate('Other') ?>
740                </h3>
741
742                <ul class="fa-ul">
743                    <?php foreach ($other_modules as $module) : ?>
744                        <li>
745                            <?php if ($module->isEnabled()) : ?>
746                                <span class="fa-li"><?= view('icons/module') ?></span>
747                                <?php if ($module instanceof ModuleConfigInterface) : ?>
748                                    <a href="<?= e($module->getConfigLink()) ?>">
749                                    <?= $module->title() ?>
750                                    <?= view('icons/preferences') ?>
751                                    </a>
752                                <?php else : ?>
753                                    <?= $module->title() ?>
754                                <?php endif ?>
755                            <?php else : ?>
756                                <span class="text-muted">
757                                    <span class="fa-li"><?= view('icons/module') ?></span>
758                                    <?= $module->title() ?>
759                                    <?php if ($module instanceof ModuleConfigInterface) : ?>
760                                        <?= view('icons/preferences') ?>
761                                    <?php endif ?>
762                                    </span>
763                                </span>
764                            <?php endif ?>
765
766                            <?php if ($module instanceof ModuleCustomInterface) : ?>
767                                <?= view('admin/custom-module-info', ['module' => $module]) ?>
768                            <?php endif ?>
769                        </li>
770                    <?php endforeach ?>
771                </ul>
772            </div>
773        </div>
774    </div>
775</div>
776
777<!-- MEDIA -->
778<div class="card mb-4">
779    <div class="card-header">
780        <h2 class="mb-0">
781            <?= I18N::translate('Media') ?>
782        </h2>
783    </div>
784    <div class="card-body">
785        <ul class="fa-ul">
786            <li>
787                <span class="fa-li"><?= view('icons/preferences') ?></span>
788                <a href="<?= e(route(ManageMediaPage::class)) ?>">
789                    <?= I18N::translate('Manage media') ?>
790                </a>
791            </li>
792            <li>
793                <span class="fa-li"><?= view('icons/upload') ?></span>
794                <a href="<?= e(route(UploadMediaPage::class)) ?>">
795                    <?= I18N::translate('Upload media files') ?>
796                </a>
797            </li>
798            <li>
799                <span class="fa-li"><?= view('icons/link') ?></span>
800                <a href="<?= e(route(FixLevel0MediaPage::class)) ?>">
801                    <?= I18N::translate('Link media objects to facts and events') ?>
802                </a>
803            </li>
804            <li>
805                <span class="fa-li"><?= view('icons/upload') ?></span>
806                <a href="<?= e(route(ImportThumbnailsPage::class)) ?>">
807                    <?= I18N::translate('Import custom thumbnails from webtrees version 1') ?>
808                </a>
809            </li>
810        </ul>
811    </div>
812</div>
813
814<!-- MAPS -->
815<div class="card mb-4">
816    <div class="card-header">
817        <h2 class="mb-0">
818            <?= I18N::translate('Map') ?>
819        </h2>
820    </div>
821    <div class="card-body">
822        <div class="row">
823            <div class="col-sm-12">
824                <ul class="fa-ul">
825                    <li>
826                        <span class="fa-li"><?= view('icons/location') ?></span>
827                        <a href="<?= e(route(MapDataList::class)) ?>">
828                            <?= I18N::translate('Geographic data') ?>
829                        </a>
830                    </li>
831                </ul>
832            </div>
833        </div>
834    </div>
835</div>
836
837<!-- OLD FILES -->
838<?php if (!empty($files_to_delete)) : ?>
839    <div class="card mb-4 card-outline-danger">
840        <div class="card-header">
841            <h2 class="mb-0">
842                <?= I18N::translate('Old files found') ?>
843            </h2>
844        </div>
845        <div class="card-body">
846            <p>
847                <?= I18N::translate('Files have been found from a previous version of webtrees. Old files can sometimes be a security risk. You should delete them.') ?>
848            </p>
849            <ul class="list-unstyled">
850                <?php foreach ($files_to_delete as $file_to_delete) : ?>
851                    <li dir="ltr">
852                        <code><?= e($file_to_delete) ?></code>
853                    </li>
854                <?php endforeach ?>
855            </ul>
856        </div>
857    </div>
858<?php endif ?>
859