Files
EIS/mod_pdf_tree/helper.php
2026-02-01 12:46:12 +01:00

247 lines
8.8 KiB
PHP

<?php
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Router\Route;
use Joomla\Database\DatabaseDriver;
class ModEisAnzeigeHelper
{
/**
* Holt alle PDF-Elemente aus der Datenbank
*/
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();
// Nach parent_id gruppieren und alphabetisch sortieren
$grouped = [];
foreach ($rows as $row) {
$pid = $row['parent_id'] === null ? null : (int) $row['parent_id'];
$grouped[$pid][] = $row;
}
// Sortieren nach name (case-insensitive)
foreach ($grouped as &$group) {
usort($group, fn($a, $b) => strcasecmp((string)$a['name'], (string)$b['name']));
}
return $grouped;
}
/** Liest param/value aus #__eis_settings (Key/Value) */
private static function getSetting(string $param, $default = '')
{
$db = Factory::getDbo();
try {
$q = $db->getQuery(true)
->select($db->quoteName('value'))
->from($db->quoteName('#__eis_settings'))
->where($db->quoteName('param') . ' = ' . $db->quote($param))
->setLimit(1);
$db->setQuery($q);
$val = $db->loadResult();
return ($val !== null && $val !== '') ? $val : $default;
} catch (\Throwable $e) {
return $default;
}
}
/** Holt die zuletzt gespeicherten neuen Datei-IDs (vom Backend-Scan) */
private static function getLastNewIds(): array
{
$json = (string) self::getSetting('last_new_ids', '[]');
$arr = json_decode($json, true);
if (!is_array($arr)) return [];
return array_values(array_unique(array_map('intval', $arr)));
}
/** Baut einen flachen Index id => row über alle Items */
private static function buildFlatIndex(array $grouped): array
{
$idx = [];
foreach ($grouped as $group) {
foreach ($group as $row) {
$idx[(int) $row['id']] = $row;
}
}
return $idx;
}
/**
* Hauptfunktion zum Rendern des Baums (klassisch, ohne „Neue Dokumente“)
* - Nutzt title als alternativen Anzeigenamen (Fallback name)
* - Fügt für Ordner eine klickbare Label-Zeile hinzu
* - Liefert data-* Attribute für spätere Inline-Edits / Preview
*/
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 = (bool) $item['is_folder'];
$rawName = $item['title'] ?: $item['name'];
$fileId = (int) $item['id'];
// Dateiendung .pdf entfernen und escapen
$displayName = preg_replace('/\.pdf$/i', '', (string) $rawName);
$displayName = htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8');
if ($isFolder) {
$fileCount = self::countFilesRecursive($items, (int)$item['id']);
// Ordner-<li> inkl. Data-Attribute + große Tap-Ziele (toggle + folder-label)
$html .= '<li class="folder"'
. ' data-id="' . (int) $item['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 . '"'
. '>';
$html .= '<span class="toggle" role="button" aria-label="Ordner umschalten" tabindex="0">▶</span> ';
$html .= '<span class="folder-label">📁 ' . $displayName . ' <small class="count">(' . (int) $fileCount . ')</small></span>';
// Kindknoten
$html .= self::renderTree($items, (int)$item['id']);
$html .= '</li>';
} else {
$html .= self::renderSingleFileLi($item, $displayName);
}
}
$html .= '</ul>';
return $html;
}
/**
* NEU: Rendert (falls vorhanden) zuerst einen virtuellen Ordner „Neue Dokumente“,
* danach den normalen Baum.
*/
public static function renderTreeWithNew(array $items): string
{
/*
$html = '';
// 1) Virtueller Ordner „Neue Dokumente“
$newIds = self::getLastNewIds();
if (!empty($newIds)) {
$idx = self::buildFlatIndex($items);
$files = [];
foreach ($newIds as $id) {
if (!isset($idx[$id])) continue;
$row = $idx[$id];
if (!empty($row['is_folder'])) continue; // nur Dateien
$files[] = $row;
}
if (!empty($files)) {
$label = 'Neue Dokumente'; // optional via Sprachschlüssel ersetzen
$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($files) . '">';
$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($files) . ')</small></span>';
// Kinder direkt sichtbar
$html .= '<ul style="display:block">';
foreach ($files as $fileRow) {
$html .= self::renderSingleFileLi($fileRow);
}
$html .= '</ul>';
$html .= '</li>';
$html .= '</ul>';
}
}
*/
// 2) Normaler Baum
$html .= self::renderTree($items, null);
return $html;
}
/** Einzelnes <li> für Datei (wiederverwendbar) */
private static function renderSingleFileLi(array $item, ?string $displayNameEscaped = null): string
{
$fileId = (int) $item['id'];
$rawName = $item['title'] ?: $item['name'];
$display = $displayNameEscaped ?? htmlspecialchars(preg_replace('/\.pdf$/i', '', (string)$rawName), ENT_QUOTES, 'UTF-8');
// Tooltip + (optional) inline-Anzeige der Dateigröße
$tooltip = '';
$sizeStr = '';
if (!empty($item['path']) && is_file((string)$item['path'])) {
$bytes = @filesize((string)$item['path']);
if ($bytes !== false) {
$formatted = self::formatFileSize((int)$bytes);
$tooltip = ' title="Größe: ' . $formatted . '"';
// $sizeStr = ' <small class="meta">(' . $formatted . ')</small>';
}
}
$link = Route::_('index.php?option=com_eis&task=download.download&id=' . $fileId);
$html = '<li class="file"'
. ' data-id="' . $fileId . '"'
. ' data-title="' . htmlspecialchars((string)($item['title'] ?? ''), ENT_QUOTES, 'UTF-8') . '"'
. ' data-name="' . htmlspecialchars((string)$item['name'], ENT_QUOTES, 'UTF-8') . '"'
. '>';
$html .= '📄 <a class="file-link" href="' . $link . '"' . $tooltip
. ' data-filename="' . $display . '">'
. $display . '</a>' . $sizeStr;
$html .= '</li>';
return $html;
}
/**
* Zählt alle PDF-Dateien unterhalb eines Ordners rekursiv
*/
private static function countFilesRecursive(array $items, int $parentId): int
{
$count = 0;
if (!isset($items[$parentId])) {
return 0;
}
foreach ($items[$parentId] as $item) {
if ((bool) $item['is_folder']) {
$count += self::countFilesRecursive($items, (int) $item['id']);
} else {
$count++;
}
}
return $count;
}
/**
* Formatierte Dateigröße für Tooltip / Anzeige
*/
private static function formatFileSize(int $bytes): string
{
if ($bytes >= 1073741824) {
return number_format($bytes / 1073741824, 2) . ' GB';
} elseif ($bytes >= 1048576) {
return number_format($bytes / 1048576, 1) . ' MB';
} elseif ($bytes >= 1024) {
return number_format($bytes / 1024, 0) . ' KB';
} else {
return $bytes . ' B';
}
}
}