public static function sourceTable($sources)
{
global $WT_TREE, $controller;
// Count the number of linked records. These numbers include private records.
// It is not good to bypass privacy, but many servers do not have the resources
// to process privacy for every record in the tree
$count_individuals = Database::prepare("SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##individuals` JOIN `##link` ON l_from = i_id AND l_file = i_file AND l_type = 'SOUR' GROUP BY l_to, l_file")->fetchAssoc();
$count_families = Database::prepare("SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##families` JOIN `##link` ON l_from = f_id AND l_file = f_file AND l_type = 'SOUR' GROUP BY l_to, l_file")->fetchAssoc();
$count_media = Database::prepare("SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##media` JOIN `##link` ON l_from = m_id AND l_file = m_file AND l_type = 'SOUR' GROUP BY l_to, l_file")->fetchAssoc();
$count_notes = Database::prepare("SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##other` JOIN `##link` ON l_from = o_id AND l_file = o_file AND o_type = 'NOTE' AND l_type = 'SOUR' GROUP BY l_to, l_file")->fetchAssoc();
$html = '';
$table_id = 'table-sour-' . Uuid::uuid4();
// lists requires a unique ID in case there are multiple lists per page
$controller->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)->addInlineJavascript('
jQuery.fn.dataTableExt.oSort["text-asc"] = textCompareAsc;
jQuery.fn.dataTableExt.oSort["text-desc"] = textCompareDesc;
jQuery("#' . $table_id . '").dataTable( {
dom: \'<"H"pf<"dt-clear">irl>t<"F"pl>\',
' . I18N::datatablesI18N() . ',
jQueryUI: true,
autoWidth: false,
processing: true,
columns: [
/* Title */ { type: "text" },
/* Author */ { type: "text" },
/* Individuals */ { type: "num" },
/* Families */ { type: "num" },
/* Media objects */ { type: "num" },
/* Notes */ { type: "num" },
/* Last change */ { visible: ' . ($WT_TREE->getPreference('SHOW_LAST_CHANGE') ? 'true' : 'false') . ' },
/* Delete */ { visible: ' . (Auth::isManager($WT_TREE) ? 'true' : 'false') . ', sortable: false }
],
displayLength: 20,
pagingType: "full_numbers"
});
jQuery(".source-list").css("visibility", "visible");
jQuery(".loading-image").css("display", "none");
');
$html .= '<div class="loading-image"></div>';
$html .= '<div class="source-list">';
$html .= '<table id="' . $table_id . '"><thead><tr>';
$html .= '<th>' . GedcomTag::getLabel('TITL') . '</th>';
$html .= '<th>' . GedcomTag::getLabel('AUTH') . '</th>';
$html .= '<th>' . I18N::translate('Individuals') . '</th>';
$html .= '<th>' . I18N::translate('Families') . '</th>';
$html .= '<th>' . I18N::translate('Media objects') . '</th>';
$html .= '<th>' . I18N::translate('Shared notes') . '</th>';
$html .= '<th>' . GedcomTag::getLabel('CHAN') . '</th>';
$html .= '<th>' . I18N::translate('Delete') . '</th>';
$html .= '</tr></thead>';
$html .= '<tbody>';
foreach ($sources as $source) {
if (!$source->canShow()) {
continue;
}
if ($source->isPendingAddtion()) {
$class = ' class="new"';
} elseif ($source->isPendingDeletion()) {
$class = ' class="old"';
} else {
$class = '';
}
$html .= '<tr' . $class . '>';
// Source name(s)
$html .= '<td data-sort="' . Filter::escapeHtml($source->getSortName()) . '">';
foreach ($source->getAllNames() as $n => $name) {
if ($n) {
$html .= '<br>';
}
if ($n == $source->getPrimaryName()) {
$html .= '<a class="name2" href="' . $source->getHtmlUrl() . '">' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>';
} else {
$html .= '<a href="' . $source->getHtmlUrl() . '">' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>';
}
}
$html .= '</td>';
// Author
$auth = $source->getFirstFact('AUTH');
if ($auth) {
$author = $auth->getValue();
} else {
$author = '';
}
$html .= '<td data-sort="' . Filter::escapeHtml($author) . '">' . FunctionsPrint::highlightSearchHits($author) . '</td>';
$key = $source->getXref() . '@' . $source->getTree()->getTreeId();
// Count of linked individuals
$num = array_key_exists($key, $count_individuals) ? $count_individuals[$key] : 0;
$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
// Count of linked families
$num = array_key_exists($key, $count_families) ? $count_families[$key] : 0;
$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
// Count of linked media objects
$num = array_key_exists($key, $count_media) ? $count_media[$key] : 0;
$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
// Count of linked notes
$num = array_key_exists($key, $count_notes) ? $count_notes[$key] : 0;
$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
// Last change
$html .= '<td data-sort="' . $source->lastChangeTimestamp(true) . '">' . $source->lastChangeTimestamp() . '</td>';
// Delete
$html .= '<td><a href="#" title="' . I18N::translate('Delete') . '" class="deleteicon" onclick="return delete_record(\'' . I18N::translate('Are you sure you want to delete ā%sā?', Filter::escapeJs(Filter::unescapeHtml($source->getFullName()))) . "', '" . $source->getXref() . '\');"><span class="link_text">' . I18N::translate('Delete') . '</span></a></td>';
$html .= '</tr>';
}
$html .= '</tbody></table></div>';
return $html;
}