xref: /webtrees/resources/views/lists/individuals-table.phtml (revision 43cb7f92010f0c9c0026f7ec677b8380b2d1d0e6)
1<?php use Fisharebest\Webtrees\Date; ?>
2<?php use Fisharebest\Webtrees\GedcomTag; ?>
3<?php use Fisharebest\Webtrees\I18N; ?>
4<?php use Fisharebest\Webtrees\Individual; ?>
5<?php use Fisharebest\Webtrees\View; ?>
6<?php use Ramsey\Uuid\Uuid; ?>
7
8
9<?php
10// lists requires a unique ID in case there are multiple lists per page
11$table_id = 'table-indi-' . Uuid::uuid4()->toString();
12
13$hundred_years_ago = new Date(date('Y') - 100);
14$unique_indis      = []; // Don't double-count indis with multiple names.
15?>
16
17<?php View::push('javascript') ?>
18<script>
19$("#<?= e($table_id) ?>").dataTable( {
20    dom: '<"H"<"filtersH_<?= e($table_id) ?>">T<"dt-clear">pf<"dt-clear">irl>t<"F"pl<"dt-clear"><"filtersF_<?= e($table_id) ?>">>',
21    <?= I18N::datatablesI18N() ?>,
22    autoWidth: false,
23    processing: true,
24    retrieve: true,
25    columns: [
26        /* Given names  */ { type: "text" },
27        /* Surnames     */ { type: "text" },
28        /* SOSA numnber */ { type: "num", visible: <?= json_encode($sosa) ?> },
29        /* Birth date   */ { type: "num" },
30        /* Anniversary  */ { type: "num" },
31        /* Birthplace   */ { type: "text" },
32        /* Children     */ { type: "num" },
33        /* Deate date   */ { type: "num" },
34        /* Anniversary  */ { type: "num" },
35        /* Age          */ { type: "num" },
36        /* Death place  */ { type: "text" },
37        /* Last change  */ { visible: <?= json_encode($tree->getPreference('SHOW_LAST_CHANGE')) ?> },
38        /* Filter sex   */ { sortable: false },
39        /* Filter birth */ { sortable: false },
40        /* Filter death */ { sortable: false },
41        /* Filter tree  */ { sortable: false }
42    ],
43    sorting: <?= json_encode($sosa ? [[4, "asc"]] : [[1, "asc"]]) ?>,
44    displayLength: 20,
45    pagingType: "full_numbers"
46});
47
48$("#<?= e($table_id) ?>")
49/* Hide/show parents */
50.on("click", ".btn-toggle-parents", function() {
51    $(this).toggleClass("ui-state-active");
52    $(".parents", $(this).closest("table").DataTable().rows().nodes()).slideToggle();
53})
54/* Hide/show statistics */
55.on("click", ".btn-toggle-statistics", function() {
56    $(this).toggleClass("ui-state-active");
57    $("#individual-charts-<?= e($table_id) ?>").slideToggle();
58})
59/* Filter buttons in table header */
60.on("click", "button[data-filter-column]", function() {
61    var btn = $(this);
62    // De-activate the other buttons in this button group
63    btn.siblings().removeClass("active");
64    // Apply (or clear) this filter
65    var col = $("#<?= e($table_id) ?>").DataTable().column(btn.data("filter-column"));
66    if (btn.hasClass("active")) {
67        col.search("").draw();
68    } else {
69        col.search(btn.data("filter-value")).draw();
70    }
71});
72</script>
73<?php View::endpush() ?>
74
75<?php
76$max_age = (int) $tree->getPreference('MAX_ALIVE_AGE');
77
78// Inititialise chart data
79$deat_by_age = [];
80for ($age = 0; $age <= $max_age; $age++) {
81    $deat_by_age[$age] = '';
82}
83$birt_by_decade = [];
84$deat_by_decade = [];
85for ($year = 1550; $year < 2030; $year += 10) {
86    $birt_by_decade[$year] = '';
87    $deat_by_decade[$year] = '';
88}
89?>
90
91<div class="indi-list">
92    <table id="<?= e($table_id) ?>">
93        <thead>
94            <tr>
95                <th colspan="16">
96                    <div class="btn-toolbar d-flex justify-content-between mb-2" role="toolbar">
97                        <div class="btn-group" data-toggle="buttons">
98                            <button
99                                class="btn btn-secondary"
100                                data-filter-column="12"
101                                data-filter-value="M"
102                                title="<?= I18N::translate('Show only males.') ?>"
103                            >
104                                <?= Individual::sexImage('M', 'large') ?>
105                            </button>
106                            <button
107                                class="btn btn-secondary"
108                                data-filter-column="12"
109                                data-filter-value="F"
110                                title="<?= I18N::translate('Show only females.') ?>"
111                            >
112                                <?= Individual::sexImage('F', 'large') ?>
113                            </button>
114                            <button
115                                class="btn btn-secondary"
116                                data-filter-column="12"
117                                data-filter-value="U"
118                                title="<?= I18N::translate('Show only individuals for whom the gender is not known.') ?>"
119                            >
120                                <?= Individual::sexImage('U', 'large') ?>
121                            </button>
122                        </div>
123                        <div class="btn-group" data-toggle="buttons">
124                            <button
125                                class="btn btn-secondary"
126                                data-filter-column="14"
127                                data-filter-value="N"
128                                title="<?= I18N::translate('Show individuals who are alive or couples where both partners are alive.') ?>"
129                            >
130                                <?= I18N::translate('Alive') ?>
131                            </button>
132                            <button
133                                class="btn btn-secondary"
134                                data-filter-column="14"
135                                data-filter-value="Y"
136                                title="<?= I18N::translate('Show individuals who are dead or couples where both partners are dead.') ?>"
137                            >
138                                <?= I18N::translate('Dead') ?>
139                            </button>
140                            <button
141                                class="btn btn-secondary"
142                                data-filter-column="14"
143                                data-filter-value="YES"
144                                title="<?= I18N::translate('Show individuals who died more than 100 years ago.') ?>"
145                            >
146                                <?= I18N::translate('Death') ?>&gt;100
147                            </button>
148                            <button
149                                class="btn btn-secondary"
150                                data-filter-column="14"
151                                data-filter-value="Y100"
152                                title="<?= I18N::translate('Show individuals who died within the last 100 years.') ?>"
153                            >
154                                <?= I18N::translate('Death') ?>&lt&lt;=100
155                            </button>
156                        </div>
157                        <div class="btn-group" data-toggle="buttons">
158                            <button
159                                class="btn btn-secondary"
160                                data-filter-column="13"
161                                data-filter-value="YES"
162                                title="<?= I18N::translate('Show individuals born more than 100 years ago.') ?>"
163                            >
164                                <?= I18N::translate('Birth') ?>&gt;100
165                            </button>
166                            <button
167                                class="btn btn-secondary"
168                                data-filter-column="13"
169                                data-filter-value="Y100"
170                                title="<?= I18N::translate('Show individuals born within the last 100 years.') ?>"
171                            >
172                                <?= I18N::translate('Birth') ?>&lt;=100
173                            </button>
174                        </div>
175                        <div class="btn-group" data-toggle="buttons">
176                            <button
177                                class="btn btn-secondary"
178                                data-filter-column="15"
179                                data-filter-value="R"
180                                title="<?= I18N::translate('Show “roots” couples or individuals. These individuals may also be called “patriarchs”. They are individuals who have no parents recorded in the database.') ?>"
181                            >
182                                <?= I18N::translate('Roots') ?>
183                            </button>
184                            <button
185                                class="btn btn-secondary"
186                                data-filter-column="15"
187                                data-filter-value="L"
188                                title="<?= I18N::translate('Show “leaves” couples or individuals. These are individuals who are alive but have no children recorded in the database.') ?>"
189                            >
190                                <?= I18N::translate('Leaves') ?>
191                            </button>
192                        </div>
193                    </div>
194                </th>
195            </tr>
196            <tr>
197                <th><?= I18N::translate('Given names') ?></th>
198                <th><?= I18N::translate('Surname') ?></th>
199                <th><?= /* I18N: Abbreviation for “Sosa-Stradonitz number”. This is an individual’s surname, so may need transliterating into non-latin alphabets. */
200                    I18N::translate('Sosa') ?></th>
201                <th><?= I18N::translate('Birth') ?></th>
202                <th>
203                    <i class="icon-reminder" title="<?= I18N::translate('Anniversary') ?>"></i>
204                </th>
205                <th><?= I18N::translate('Place') ?></th>
206                <th>
207                    <i class="icon-children" title="<?= I18N::translate('Children') ?>"></i>
208                </th>
209                <th><?= I18N::translate('Death') ?></th>
210                <th>
211                    <i class="icon-reminder" title="<?= I18N::translate('Anniversary') ?>"></i>
212                </th>
213                <th><?= I18N::translate('Age') ?></th>
214                <th><?= I18N::translate('Place') ?></th>
215                <th><?= I18N::translate('Last change') ?></th>
216                <th hidden></th>
217                <th hidden></th>
218                <th hidden></th>
219                <th hidden></th>
220            </tr>
221        </thead>
222        <tfoot>
223            <tr>
224                <th colspan="16">
225                    <div class="btn-toolbar">
226                        <div class="btn-group">
227                            <button class="ui-state-default btn-toggle-parents">
228                                <?= I18N::translate('Show parents') ?>
229                            </button>
230                            <button class="ui-state-default btn-toggle-statistics">
231                                <?= I18N::translate('Show statistics charts') ?>
232                            </button>
233                        </div>
234                    </div>
235                </th>
236            </tr>
237        </tfoot>
238
239        <tbody>
240            <?php foreach ($individuals as $key => $individual) : ?>
241            <tr class="<?= $individual->isPendingDeletion() ? 'old' : ($individual->isPendingAddition() ? 'new' : '') ?>">
242                <td colspan="2" data-sort="<?= e(str_replace([',', '@P.N.', '@N.N.'], 'AAAA', implode(',', array_reverse(explode(',', $individual->getSortName()))))) ?>">
243                    <?php foreach ($individual->getAllNames() as $num => $name) : ?>
244                        <a title="<?= $name['type'] === 'NAME' ? '' : GedcomTag::getLabel($name['type'], $individual) ?>" href="<?= e($individual->url()) ?>" class="<?= $num === $individual->getPrimaryName() ? 'name2' : '' ?>">
245                            <?= $name['full'] ?>
246                        </a>
247                        <?php if ($num === $individual->getPrimaryName()) : ?>
248                            <?= $individual->getSexImage() ?>
249                        <?php endif ?>
250                        <br>
251                    <?php endforeach ?>
252                    <?= $individual->getPrimaryParentsNames('parents details1', 'none') ?>
253                </td>
254
255                <td hidden data-sort="<?= e(str_replace([',', '@P.N.', '@N.N.'], 'AAAA', $individual->getSortName())) ?>"></td>
256
257                <td class="center" data-sort="<?= $key ?>">
258                    <?php if ($sosa) : ?>
259                        <a href="<?= e(route('relationships', ['xref1' => $individuals[1]->xref(), 'xref2' => $individual->xref(), 'ged' => $individual->tree()->name()])) ?>" title="<?= I18N::translate('Relationships') ?>" rel="nofollow">
260                            <?= I18N::number($key) ?>
261                        </a>
262                    <?php endif ?>
263                </td>
264
265                <!-- Birth date -->
266                <td data-sort="<?= $individual->getEstimatedBirthDate()->julianDay() ?>">
267                    <?php $birth_dates = $individual->getAllBirthDates(); ?>
268
269                    <?php foreach ($birth_dates as $n => $birth_date) : ?>
270                        <?= $birth_date->display(true) ?>
271                        <br>
272                    <?php endforeach ?>
273                </td>
274
275                <!-- Birth anniversary -->
276                <td class="center" data-sort="<?= -$individual->getEstimatedBirthDate()->julianDay() ?>">
277                    <?php if (isset($birth_dates[0]) && $birth_dates[0]->gregorianYear() >= 1550 && $birth_dates[0]->gregorianYear() < 2030 && !isset($unique_indis[$individual->xref()])) : ?>
278                        <?php $birt_by_decade[(int) ($birth_dates[0]->gregorianYear() / 10) * 10] .= $individual->getSex() ?>
279                        <?= Date::getAge($birth_dates[0], null) ?>
280                    <?php endif ?>
281                </td>
282
283                <!-- Birth place -->
284                <td>
285                    <?php foreach ($individual->getAllBirthPlaces() as $n => $birth_place) : ?>
286                        <a href="<?= e($birth_place->url()) ?>" title="<?= strip_tags($birth_place->getFullName()) ?>">
287                            <?= $birth_place->getShortName() ?>
288                        </a>
289                        <br>
290                    <?php endforeach ?>
291                </td>
292
293                <!-- Number of children -->
294                <td class="center" data-sort="<?= $individual->getNumberOfChildren() ?>">
295                    <?= I18N::number($individual->getNumberOfChildren()) ?>
296                </td>
297
298                <!--    Death date -->
299                <?php $death_dates = $individual->getAllDeathDates() ?>
300                <td data-sort="<?= $individual->getEstimatedDeathDate()->julianDay() ?>">
301                    <?php foreach ($death_dates as $num => $death_date) : ?>
302                        <?= $death_date->display(true) ?>
303                    <br>
304                    <?php endforeach ?>
305                </td>
306
307                <!-- Death anniversary -->
308                <td class="center" data-sort="<?= -$individual->getEstimatedDeathDate()->julianDay() ?>">
309                    <?php if (isset($death_dates[0]) && $death_dates[0]->gregorianYear() >= 1550 && $death_dates[0]->gregorianYear() < 2030 && !isset($unique_indis[$individual->xref()])) : ?>
310                        <?php $deat_by_decade[(int) ($death_dates[0]->gregorianYear() / 10) * 10] .= $individual->getSex() ?>
311                        <?= Date::getAge($death_dates[0], null) ?>
312                    <?php endif ?>
313                </td>
314
315                <!-- Age at death -->
316                <?php if (isset($birth_dates[0]) && isset($death_dates[0])) : ?>
317                    <?php $age_at_death = I18N::number((int) Date::getAgeYears($birth_dates[0], $death_dates[0])); ?>
318                    <?php $age_at_death_sort = Date::getAge($birth_dates[0], $death_dates[0]); ?>
319                    <?php if (!isset($unique_indis[$individual->xref()]) && $age_at_death >= 0 && $age_at_death <= $max_age) : ?>
320                        <?php $deat_by_age[$age_at_death] .= $individual->getSex(); ?>
321                    <?php endif ?>
322                <?php else : ?>
323                    <?php $age_at_death = ''; ?>
324                    <?php $age_at_death_sort = PHP_INT_MAX; ?>
325                <?php endif ?>
326                <td class="center" data-sort="<?= e($age_at_death_sort) ?>">
327                    <?= e($age_at_death) ?>
328                </td>
329
330                <!-- Death place -->
331                <td>
332                    <?php foreach ($individual->getAllDeathPlaces() as $n => $death_place) : ?>
333                        <a href="<?= e($death_place->url()) ?>" title="<?= e(strip_tags($death_place->getFullName())) ?>">
334                            <?= $death_place->getShortName() ?>
335                        </a>
336                        <br>
337                    <?php endforeach ?>
338                </td>
339
340                <!-- Last change -->
341                <td data-sort="<?= $individual->lastChangeTimestamp(true) ?>">
342                    <?= $individual->lastChangeTimestamp() ?>
343                </td>
344
345                <!-- Filter by sex -->
346                <td hidden>
347                    <?= $individual->getSex() ?>
348                </td>
349
350                <!-- Filter by birth date -->
351                <td hidden>
352                    <?php if (!$individual->canShow() || Date::compare($individual->getEstimatedBirthDate(), $hundred_years_ago) > 0) : ?>
353                        Y100
354                    <?php else : ?>
355                        YES
356                    <?php endif ?>
357                </td>
358
359                <!-- Filter by death date -->
360                <td hidden>
361                    <?php if (isset($death_dates[0]) && Date::compare($death_dates[0], $hundred_years_ago) > 0) : ?>
362                        Y100
363                    <?php elseif ($individual->isDead()) : ?>
364                        YES
365                    <?php else : ?>
366                        N
367                    <?php endif ?>
368                </td>
369
370                <!-- Filter by roots/leaves -->
371                <td hidden>
372                    <?php if (!$individual->getChildFamilies()) : ?>
373                        R
374                    <?php elseif (!$individual->isDead() && $individual->getNumberOfChildren() < 1) : ?>
375                        L
376                    <?php endif ?>
377                </td>
378            </tr>
379
380                <?php $unique_indis[$individual->xref()] = true ?>
381            <?php endforeach ?>
382        </tbody>
383    </table>
384
385    <div id="individual-charts-<?= e($table_id) ?>" style="display:none">
386        <table class="list-charts">
387            <tr>
388                <td>
389                    <?= view('lists/chart-by-decade', ['data' => $birt_by_decade, 'title' => I18N::translate('Decade of birth')]) ?>
390                </td>
391                <td>
392                    <?= view('lists/chart-by-decade', ['data' => $deat_by_decade, 'title' => I18N::translate('Decade of death')]) ?>
393                </td>
394            </tr>
395            <tr>
396                <td colspan="2">
397                    <?= view('lists/chart-by-age', ['data' => $deat_by_age, 'title' => I18N::translate('Age related to death year')]) ?>
398                </td>
399            </tr>
400        </table>
401    </div>
402</div>
403