1<?php 2 3declare(strict_types=1); 4 5use Fisharebest\Webtrees\I18N; 6use Fisharebest\Webtrees\Registry; 7use Fisharebest\Webtrees\Source; 8use Fisharebest\Webtrees\Tree; 9use Illuminate\Database\Capsule\Manager as DB; 10use Illuminate\Database\Query\Expression; 11use Illuminate\Database\Query\JoinClause; 12use Illuminate\Support\Collection; 13 14/** 15 * @var Collection<int,Source> $sources 16 * @var Tree $tree 17 */ 18 19?> 20 21<?php 22// Count the number of linked records. These numbers include private records. 23// It is not good to bypass privacy, but many servers do not have the resources 24// to process privacy for every record in the tree 25$count_individuals = DB::table('individuals') 26 ->join('link', static function (JoinClause $join): void { 27 $join->on('l_from', '=', 'i_id'); 28 $join->on('l_file', '=', 'i_file'); 29 }) 30 ->where('l_type', '=', 'SOUR') 31 ->where('l_file', '=', $tree->id()) 32 ->groupBy(['l_to']) 33 ->select(['l_to', new Expression('COUNT(*) AS total')]) 34 ->pluck('total', 'l_to') 35 ->map(static fn (string $n) => (int) $n) 36 ->all(); 37 38$count_families = DB::table('families') 39 ->join('link', static function (JoinClause $join): void { 40 $join->on('l_from', '=', 'f_id'); 41 $join->on('l_file', '=', 'f_file'); 42 }) 43 ->where('l_type', '=', 'SOUR') 44 ->where('l_file', '=', $tree->id()) 45 ->groupBy(['l_to']) 46 ->select(['l_to', new Expression('COUNT(*) AS total')]) 47 ->pluck('total', 'l_to') 48 ->map(static fn (string $n) => (int) $n) 49 ->all(); 50 51$count_media = DB::table('media') 52 ->join('link', static function (JoinClause $join): void { 53 $join->on('l_from', '=', 'm_id'); 54 $join->on('l_file', '=', 'm_file'); 55 }) 56 ->where('l_type', '=', 'SOUR') 57 ->where('l_file', '=', $tree->id()) 58 ->groupBy(['l_to']) 59 ->select(['l_to', new Expression('COUNT(*) AS total')]) 60 ->pluck('total', 'l_to') 61 ->map(static fn (string $n) => (int) $n) 62 ->all(); 63 64$count_notes = DB::table('other') 65 ->join('link', static function (JoinClause $join): void { 66 $join->on('l_from', '=', 'o_id'); 67 $join->on('l_file', '=', 'o_file'); 68 }) 69 ->where('o_type', '=', 'NOTE') 70 ->where('l_type', '=', 'SOUR') 71 ->where('l_file', '=', $tree->id()) 72 ->groupBy(['l_to']) 73 ->select(['l_to', new Expression('COUNT(*) AS total')]) 74 ->pluck('total', 'l_to') 75 ->map(static fn (string $n) => (int) $n) 76 ->all(); 77?> 78 79<table 80 class="table table-bordered table-sm wt-table-source datatables d-none" 81 <?= view('lists/datatables-attributes') ?> 82 data-columns="<?= e(json_encode([ 83 ['type' => 'html'], 84 null, 85 null, 86 null, 87 ['visible' => array_sum($count_individuals) > 0], 88 ['visible' => array_sum($count_families) > 0], 89 ['visible' => array_sum($count_media) > 0], 90 ['visible' => array_sum($count_notes) > 0], 91 ['visible' => (bool) $tree->getPreference('SHOW_LAST_CHANGE'), 'searchable' => false], 92 ], JSON_THROW_ON_ERROR)) ?>" 93> 94 <caption class="visually-hidden"> 95 <?= $caption ?? I18N::translate('Sources') ?> 96 </caption> 97 98 <thead> 99 <tr> 100 <th><?= I18N::translate('Title') ?></th> 101 <th class="d-none d-md-table-cell"><?= I18N::translate('Abbreviation') ?></th> 102 <th class="d-none d-md-table-cell"><?= I18N::translate('Author') ?></th> 103 <th class="d-none d-md-table-cell"><?= I18N::translate('Publication') ?></th> 104 <th><?= I18N::translate('Individuals') ?></th> 105 <th><?= I18N::translate('Families') ?></th> 106 <th><?= I18N::translate('Media objects') ?></th> 107 <th><?= I18N::translate('Shared notes') ?></th> 108 <th><?= I18N::translate('Last change') ?></th> 109 </tr> 110 </thead> 111 112 <tbody> 113 <?php foreach ($sources as $source) : ?> 114 <tr class="<?= $source->isPendingAddition() ? 'wt-new' : '' ?> <?= $source->isPendingDeletion() ? 'wt-old' : '' ?>"> 115 <!-- Title --> 116 <td data-sort="<?= e($source->sortName()) ?>"> 117 <a href="<?= e($source->url()) ?>"> 118 <?= $source->fullName() ?> 119 </a> 120 </td> 121 122 <!-- Abbreviation --> 123 <td class="d-none d-md-table-cell"> 124 <?= e($source->facts(['ABBR'])->isNotEmpty() ? $source->facts(['ABBR'])->first()->value() : '') ?> 125 </td> 126 127 <!-- Author --> 128 <td class="d-none d-md-table-cell"> 129 <?= e($source->facts(['AUTH'])->isNotEmpty() ? $source->facts(['AUTH'])->first()->value() : '') ?> 130 </td> 131 132 <!-- Publisher --> 133 <td class="d-none d-md-table-cell"> 134 <?= Registry::elementFactory()->make('SOUR:PUBL')->value($source->facts(['PUBL'])->isNotEmpty() ? $source->facts(['PUBL'])->first()->value() : '', $tree) ?> 135 </td> 136 137 <!-- Count of linked individuals --> 138 <td class="text-center" data-sort="<?= $count_individuals[$source->xref()] ?? 0 ?>"> 139 <?= I18N::number($count_individuals[$source->xref()] ?? 0) ?> 140 </td> 141 142 <!-- Count of linked families --> 143 <td class="text-center" data-sort="<?= $count_families[$source->xref()] ?? 0 ?>"> 144 <?= I18N::number($count_families[$source->xref()] ?? 0) ?> 145 </td> 146 147 <!-- Count of linked media objects --> 148 <td class="text-center" data-sort="<?= $count_media[$source->xref()] ?? 0 ?>"> 149 <?= I18N::number($count_media[$source->xref()] ?? 0) ?> 150 </td> 151 152 <!-- Count of linked notes --> 153 <td class="text-center" data-sort="<?= $count_notes[$source->xref()] ?? 0 ?>"> 154 <?= I18N::number($count_notes[$source->xref()] ?? 0) ?> 155 </td> 156 157 <!-- Last change --> 158 <td data-sort="<?= $source->lastChangeTimestamp()->timestamp() ?>"> 159 <?= view('components/datetime', ['timestamp' => $source->lastChangeTimestamp()]) ?> 160 </td> 161 </tr> 162 <?php endforeach ?> 163 </tbody> 164</table> 165