Files
EIS/com_eis/administrator/src/Helper/TreeHelper.php
2025-08-26 20:28:35 +02:00

194 lines
7.1 KiB
PHP

<?php
namespace EIS\Component\EIS\Administrator\Helper;
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\Database\DatabaseDriver;
class TreeHelper
{
/** Lädt #__eis_documents, gruppiert nach parent_id und sortiert */
public static function getItems(): array
{
/** @var DatabaseDriver $db */
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select('*')
->from($db->quoteName('#__eis_documents'))
->order($db->quoteName('ordering') . ' ASC');
$rows = $db->setQuery($query)->loadAssocList();
$grouped = [];
foreach ($rows as $row) {
$pid = $row['parent_id'] === null ? null : (int) $row['parent_id'];
$grouped[$pid][] = $row;
}
// alphabetisch nach name
foreach ($grouped as &$group) {
usort($group, static fn($a, $b) => strcasecmp((string)$a['name'], (string)$b['name']));
}
return $grouped;
}
/**
* Kombiniert: Virtueller Ordner "Neue Dokumente" (falls $newIds vorhanden) + normaler Baum
*/
public static function renderTreeWithNew(array $items, array $newIds = []): string
{
$html = '';
// 1) Virtueller Ordner "Neue Dokumente"
$newFiles = self::collectFilesByIds($items, $newIds);
if (!empty($newFiles)) {
$label = Text::_('COM_EIS_VIRTUAL_NEW') ?: 'Neue Dokumente';
$html .= '<ul class="pdf-tree">';
$html .= '<li class="folder" data-id="new" data-title="' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . '" data-name="' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . '" data-count="' . count($newFiles) . '">';
$html .= '<span class="toggle" role="button" aria-label="Ordner umschalten" tabindex="0">▼</span> ';
$html .= '<span class="folder-label">🆕 ' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . ' <small class="count">(' . (int) count($newFiles) . ')</small></span>';
// Kinder sichtbar
$html .= '<ul style="display:block">';
foreach ($newFiles as $row) {
$html .= self::renderSingleFileLi($row);
}
$html .= '</ul>';
$html .= '</li>';
$html .= '</ul>';
}
// 2) Normaler Baum
$html .= self::renderTree($items, null);
return $html;
}
/** Baum rendern (nutzt title als alternativen Anzeigenamen) */
public static function renderTree(array $items, ?int $parentId = null): string
{
if (!isset($items[$parentId])) {
return '';
}
$html = '<ul class="pdf-tree">';
foreach ($items[$parentId] as $item) {
$isFolder = !empty($item['is_folder']);
$rawName = (string)($item['title'] ?? '') !== '' ? (string)$item['title'] : (string)$item['name'];
$display = preg_replace('/\.pdf$/i', '', $rawName);
$display = htmlspecialchars((string)$display, ENT_QUOTES, 'UTF-8');
$id = (int)$item['id'];
if ($isFolder) {
$fileCount = self::countFilesRecursive($items, $id);
$html .= '<li class="folder"'
. ' data-id="' . $id . '"'
. ' data-title="' . htmlspecialchars((string)($item['title'] ?? ''), ENT_QUOTES, 'UTF-8') . '"'
. ' data-name="' . htmlspecialchars((string)$item['name'], ENT_QUOTES, 'UTF-8') . '"'
. ' data-count="' . (int)$fileCount . '"'
. '>'
. '<span class="toggle" role="button" aria-label="Ordner umschalten" tabindex="0">▶</span> '
. '<span class="folder-label">📁 ' . $display . ' <small class="count">(' . (int)$fileCount . ')</small></span>'
. self::renderTree($items, $id)
. '</li>';
} else {
$html .= self::renderSingleFileLi($item, $display);
}
}
$html .= '</ul>';
return $html;
}
/** Einzelnes <li> für Datei (wird auch vom virtuellen Ordner genutzt) */
private static function renderSingleFileLi(array $item, ?string $displayAlreadyEscaped = null): string
{
$rawName = (string)($item['title'] ?? '') !== '' ? (string)$item['title'] : (string)$item['name'];
$display = $displayAlreadyEscaped ?? htmlspecialchars(preg_replace('/\.pdf$/i', '', $rawName), ENT_QUOTES, 'UTF-8');
$id = (int)$item['id'];
// Dateigröße ermitteln (optional)
$sizeStr = '';
$sizeBytes = '';
if (!empty($item['path']) && is_file((string)$item['path'])) {
$bytes = @filesize((string)$item['path']); // @ unterdrückt Warnungen bei Zugriffsproblemen
if ($bytes !== false) {
$sizeBytes = (string)(int)$bytes;
$sizeStr = self::formatFileSize((int)$bytes);
}
}
$html = '<li class="file"'
. ' data-id="' . $id . '"'
. ' data-title="' . htmlspecialchars((string)($item['title'] ?? ''), ENT_QUOTES, 'UTF-8') . '"'
. ' data-name="' . htmlspecialchars((string)$item['name'], ENT_QUOTES, 'UTF-8') . '"'
. ' data-size="' . htmlspecialchars($sizeStr, ENT_QUOTES, 'UTF-8') . '"'
. ' data-bytes="' . htmlspecialchars($sizeBytes, ENT_QUOTES, 'UTF-8') . '"'
. '>'
. '<span class="file-label">📄 ' . $display
. ($sizeStr !== '' ? ' <small class="meta">(' . $sizeStr . ')</small>' : '')
. '</span>'
. '</li>';
return $html;
}
/** Dateien zu gegebenen IDs einsammeln (nur is_folder = 0) */
private static function collectFilesByIds(array $items, array $ids): array
{
if (empty($ids)) {
return [];
}
$ids = array_map('intval', $ids);
// Flat-Index aller Rows
$idx = [];
foreach ($items as $group) {
foreach ($group as $row) {
$idx[(int)$row['id']] = $row;
}
}
$out = [];
foreach ($ids as $id) {
if (isset($idx[$id]) && empty($idx[$id]['is_folder'])) {
$out[] = $idx[$id];
}
}
return $out;
}
private static function countFilesRecursive(array $items, int $parentId): int
{
$count = 0;
if (!isset($items[$parentId])) {
return 0;
}
foreach ($items[$parentId] as $item) {
if (!empty($item['is_folder'])) {
$count += self::countFilesRecursive($items, (int)$item['id']);
} else {
$count++;
}
}
return $count;
}
private static function formatFileSize(int $bytes): string
{
if ($bytes >= 1073741824) return number_format($bytes / 1073741824, 2) . ' GB';
if ($bytes >= 1048576) return number_format($bytes / 1048576, 1) . ' MB';
if ($bytes >= 1024) return number_format($bytes / 1024, 0) . ' KB';
return $bytes . ' B';
}
}