diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5661c24..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/com_eis/.DS_Store b/com_eis/.DS_Store deleted file mode 100644 index 1efc6c6..0000000 Binary files a/com_eis/.DS_Store and /dev/null differ diff --git a/com_eis/Archiv.zip b/com_eis/Archiv.zip index 8321fab..ff67252 100644 Binary files a/com_eis/Archiv.zip and b/com_eis/Archiv.zip differ diff --git a/com_eis/administrator/.DS_Store b/com_eis/administrator/.DS_Store deleted file mode 100644 index 2431bac..0000000 Binary files a/com_eis/administrator/.DS_Store and /dev/null differ diff --git a/com_eis/administrator/language/.DS_Store b/com_eis/administrator/language/.DS_Store deleted file mode 100644 index 30f1ba4..0000000 Binary files a/com_eis/administrator/language/.DS_Store and /dev/null differ diff --git a/com_eis/administrator/language/de-DE/de-DE.com_eis.ini b/com_eis/administrator/language/de-DE/de-DE.com_eis.ini index 3a089a4..25dff04 100644 --- a/com_eis/administrator/language/de-DE/de-DE.com_eis.ini +++ b/com_eis/administrator/language/de-DE/de-DE.com_eis.ini @@ -42,3 +42,7 @@ COM_EIS_TOOLBAR_CLOSE="Schließen" COM_EIS_MSG_PATH_NOT_EXISTS="Hinweis: Das Verzeichnis „%s“ existiert nicht." COM_EIS_MSG_PATH_NOT_READABLE="Hinweis: Das Verzeichnis „%s“ ist nicht lesbar." +COM_EIS_ACCESS_TITLE="Zugriff (Top-Level-Ordner)" +COM_EIS_FIELD_VIEWLEVEL="Zugriffsebene" +COM_EIS_ACCESS_HELP="Nur für Ordner der ersten Ebene. Unterordner und Dateien erben die Einstellung." + diff --git a/com_eis/administrator/sql/install.mysql.utf8.sql b/com_eis/administrator/sql/install.mysql.utf8.sql index b1a42e7..4ff7464 100644 --- a/com_eis/administrator/sql/install.mysql.utf8.sql +++ b/com_eis/administrator/sql/install.mysql.utf8.sql @@ -27,6 +27,16 @@ CREATE TABLE IF NOT EXISTS `#__eis_settings` ( UNIQUE KEY `uniq_param` (`param`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE IF NOT EXISTS `#__eis_folder_access` ( + `folder_id` INT UNSIGNED NOT NULL, + `viewlevel_id` INT UNSIGNED NOT NULL, + `modified` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`folder_id`), + KEY `idx_viewlevel` (`viewlevel_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + + -- Standardwert für den PDF-Pfad (leer = noch nicht gesetzt) INSERT IGNORE INTO `#__eis_settings` (`param`, `value`, `created`, `modified`) diff --git a/com_eis/administrator/sql/updates/1.1.2.sql b/com_eis/administrator/sql/updates/1.1.2.sql index 55cbf1f..d1e2428 100644 --- a/com_eis/administrator/sql/updates/1.1.2.sql +++ b/com_eis/administrator/sql/updates/1.1.2.sql @@ -34,6 +34,15 @@ CREATE TABLE IF NOT EXISTS `#__eis_documents` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE IF NOT EXISTS `#__eis_folder_access` ( + `folder_id` INT UNSIGNED NOT NULL, + `viewlevel_id` INT UNSIGNED NOT NULL, + `modified` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`folder_id`), + KEY `idx_viewlevel` (`viewlevel_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + -- Falls es Altbestände ohne neue Spalten gab, Spalten idempotent ergänzen ALTER TABLE `#__eis_documents` ADD COLUMN IF NOT EXISTS `title` VARCHAR(255) DEFAULT NULL, diff --git a/com_eis/administrator/src/.DS_Store b/com_eis/administrator/src/.DS_Store deleted file mode 100644 index 889bb74..0000000 Binary files a/com_eis/administrator/src/.DS_Store and /dev/null differ diff --git a/com_eis/administrator/src/Controller/DisplayController.php b/com_eis/administrator/src/Controller/DisplayController.php index faa372f..bd615e6 100644 --- a/com_eis/administrator/src/Controller/DisplayController.php +++ b/com_eis/administrator/src/Controller/DisplayController.php @@ -164,6 +164,7 @@ class DisplayController extends BaseController $query = $db->getQuery(true) ->update($db->quoteName('#__eis_documents')) ->set($db->quoteName('title') . ' = ' . ($title === '' ? 'NULL' : $db->quote($title))) + ->set($db->quoteName('modified') . ' = ' . $db->quote(\Joomla\CMS\Factory::getDate()->toSql())) ->where($db->quoteName('id') . ' = ' . (int)$id); try { @@ -175,4 +176,63 @@ class DisplayController extends BaseController $this->setRedirect(Route::_('index.php?option=com_eis&view=main', false)); } + + public function saveAccess(): void +{ + if (!\Joomla\CMS\Session\Session::checkToken('post')) { + throw new \RuntimeException(\JText::_('JINVALID_TOKEN'), 403); + } + + $app = \Joomla\CMS\Factory::getApplication(); + $db = \Joomla\CMS\Factory::getDbo(); + + $folderId = (int) $app->input->post->get('folder_id', 0); + $viewlevelId = (int) $app->input->post->get('viewlevel_id', 0); + + // Validierung: folderId muss Top-Level-Ordner sein + if ($folderId <= 0) { + $app->enqueueMessage('Ungültiger Ordner.', 'error'); + $this->setRedirect(\Joomla\CMS\Router\Route::_('index.php?option=com_eis&view=main', false)); + return; + } + + // prüfen, ob Top-Level-Ordner + $q = $db->getQuery(true) + ->select($db->quoteName(['id','is_folder','parent_id'])) + ->from($db->quoteName('#__eis_documents')) + ->where($db->quoteName('id') . ' = ' . (int)$folderId); + $db->setQuery($q); + $row = $db->loadAssoc(); + + if (!$row || (int)$row['is_folder'] !== 1 || $row['parent_id'] !== null) { + $app->enqueueMessage('Nur Top-Level-Ordner können berechtigt werden.', 'warning'); + $this->setRedirect(\Joomla\CMS\Router\Route::_('index.php?option=com_eis&view=main', false)); + return; + } + + // Viewlevel validieren + if ($viewlevelId <= 0) { + // löschen (öffentlich) + $db->setQuery( + $db->getQuery(true) + ->delete($db->quoteName('#__eis_folder_access')) + ->where($db->quoteName('folder_id') . ' = ' . (int)$folderId) + )->execute(); + + $app->enqueueMessage('Berechtigung entfernt (öffentlich).', 'message'); + $this->setRedirect(\Joomla\CMS\Router\Route::_('index.php?option=com_eis&view=main', false)); + return; + } + + // upsert + $sql = 'INSERT INTO ' . $db->quoteName('#__eis_folder_access') + . ' (' . $db->quoteName('folder_id') . ', ' . $db->quoteName('viewlevel_id') . ') VALUES (' + . (int)$folderId . ', ' . (int)$viewlevelId . ')' + . ' ON DUPLICATE KEY UPDATE ' . $db->quoteName('viewlevel_id') . ' = VALUES(' . $db->quoteName('viewlevel_id') . ')'; + $db->setQuery($sql)->execute(); + + $app->enqueueMessage('Berechtigung gespeichert.', 'message'); + $this->setRedirect(\Joomla\CMS\Router\Route::_('index.php?option=com_eis&view=main', false)); +} + } diff --git a/com_eis/administrator/src/Service/DocumentScanner.php b/com_eis/administrator/src/Service/DocumentScanner.php new file mode 100644 index 0000000..347ce70 --- /dev/null +++ b/com_eis/administrator/src/Service/DocumentScanner.php @@ -0,0 +1,107 @@ + danach ist alles "neu" + $db->truncateTable('#__eis_documents'); + + $newIds = self::saveToDb($data, null, $db); + + // Dauerhaft speichern (Frontend-Modul nutzt das ja schon) + SettingsHelper::setSetting('last_new_ids', json_encode($newIds, JSON_UNESCAPED_SLASHES)); + SettingsHelper::setSetting('last_scan_at', date('Y-m-d H:i:s')); + + return $newIds; + } + + private static function scanFolder(string $dir): array + { + $result = []; + if (!is_dir($dir)) { + return $result; + } + + $entries = @scandir($dir); + if ($entries === false) { + return $result; + } + + foreach ($entries as $file) { + if ($file === '.' || $file === '..') { + continue; + } + + $fullPath = $dir . DIRECTORY_SEPARATOR . $file; + + if (is_dir($fullPath)) { + $result[] = [ + 'name' => $file, + 'children' => self::scanFolder($fullPath) + ]; + } elseif (is_file($fullPath) && strtolower(pathinfo($file, PATHINFO_EXTENSION)) === 'pdf') { + $result[] = [ + 'name' => $file, + 'path' => $fullPath + ]; + } + } + + usort($result, static fn($a, $b) => strcasecmp((string)$a['name'], (string)$b['name'])); + return $result; + } + + private static function saveToDb(array $items, ?int $parentId, DatabaseDriver $db): array + { + $insertedFileIds = []; + + foreach ($items as $item) { + $name = $db->quote($item['name']); + $path = $db->quote($item['path'] ?? ''); + $parent = $parentId !== null ? (int)$parentId : 'NULL'; + $isFolder = isset($item['children']) ? 1 : 0; + + $query = $db->getQuery(true) + ->insert($db->quoteName('#__eis_documents')) + ->columns(['name', 'path', 'parent_id', 'is_folder']) + ->values("$name, $path, $parent, $isFolder"); + + $db->setQuery($query)->execute(); + $insertedId = (int) $db->insertid(); + + if ($isFolder && !empty($item['children'])) { + $childIds = self::saveToDb($item['children'], $insertedId, $db); + $insertedFileIds = array_merge($insertedFileIds, $childIds); + } else { + $insertedFileIds[] = $insertedId; + } + } + + return $insertedFileIds; + } +} diff --git a/com_eis/administrator/src/View/.DS_Store b/com_eis/administrator/src/View/.DS_Store deleted file mode 100644 index d3f08b2..0000000 Binary files a/com_eis/administrator/src/View/.DS_Store and /dev/null differ diff --git a/com_eis/administrator/src/View/Main/HtmlView.php b/com_eis/administrator/src/View/Main/HtmlView.php index 74c5486..d444f20 100644 --- a/com_eis/administrator/src/View/Main/HtmlView.php +++ b/com_eis/administrator/src/View/Main/HtmlView.php @@ -4,38 +4,114 @@ namespace EIS\Component\EIS\Administrator\View\Main; \defined('_JEXEC') or die; use Joomla\CMS\Factory; -use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; +use Joomla\Database\DatabaseDriver; use EIS\Component\EIS\Administrator\Helper\TreeHelper; class HtmlView extends BaseHtmlView { - /** @var string HTML des Baums (inkl. virtuellem Ordner „Neue Dokumente“, falls vorhanden) */ - protected $treeHtml = ''; + /** HTML des Baums (inkl. virtuellem Ordner „Neue Dokumente“, falls vorhanden) */ + protected string $treeHtml = ''; + + /** Liste aller Viewlevels: [ ['id'=>int, 'title'=>string], ... ] */ + protected array $viewLevels = []; + + /** + * Map der gesetzten ACLs für Top-Level-Ordner: + * [ folder_id (int) => viewlevel_id (int), ... ] + */ + protected array $folderAccess = []; public function display($tpl = null): void { - $app = Factory::getApplication(); + $app = Factory::getApplication(); + /** @var DatabaseDriver $db */ + $db = Factory::getDbo(); - // Daten für den Baum holen + // 1) Baum-Daten $items = TreeHelper::getItems(); - // Neu hinzugekommene IDs aus dem letzten Scan (können leer sein) + // neu hinzugekommene IDs aus letztem Scan (können leer sein) $newIds = (array) $app->getUserState('com_eis.new_ids', []); - // Baum rendern (virtueller Ordner „Neue Dokumente“ + normaler Baum) + // Baum rendern (virtueller Ordner „Neue Dokumente“ + regulärer Baum) $this->treeHtml = TreeHelper::renderTreeWithNew($items, $newIds); - // Template rendern + // 2) Viewlevels laden + $this->viewLevels = $this->loadViewLevels($db); + + // 3) gesetzte ACLs für Top-Level-Ordner laden + $this->folderAccess = $this->loadFolderAccess($db); + + // Template ausgeben parent::display($tpl); - // Optional: nur einmal anzeigen -> State leeren + // „Neue Dokumente“ einmalig zeigen → State leeren $app->setUserState('com_eis.new_ids', []); } - /** Ermöglicht im Template: $this->treeHtml */ + /** Für Template: HTML des Baums */ public function getTreeHtml(): string { - return (string) $this->treeHtml; + return $this->treeHtml; + } + + /** Für Template: Liste der Viewlevels */ + public function getViewLevels(): array + { + return $this->viewLevels; + } + + /** + * Für Template/JS: Map folder_id => viewlevel_id (nur Top-Level-Ordner) + * Beispiel: [ 12 => 3, 15 => 5 ] + */ + public function getFolderAccess(): array + { + return $this->folderAccess; + } + + /** Lädt alle Viewlevels sortiert nach Titel */ + private function loadViewLevels(DatabaseDriver $db): array + { + $q = $db->getQuery(true) + ->select($db->quoteName(['id', 'title'])) + ->from($db->quoteName('#__viewlevels')) + ->order($db->quoteName('title') . ' ASC'); + + $db->setQuery($q); + $rows = (array) $db->loadAssocList(); + + // Safety: Normalisieren der Typen + foreach ($rows as &$r) { + $r['id'] = (int) ($r['id'] ?? 0); + $r['title'] = (string) ($r['title'] ?? ''); + } + + return $rows; + } + + /** + * Lädt ACL-Zuweisungen aus #__eis_folder_access. + * Es werden nur Top-Level-Ordner-IDs erwartet (per Controller validiert). + */ + private function loadFolderAccess(DatabaseDriver $db): array + { + $q = $db->getQuery(true) + ->select($db->quoteName(['folder_id', 'viewlevel_id'])) + ->from($db->quoteName('#__eis_folder_access')); + + $db->setQuery($q); + $rows = (array) $db->loadAssocList(); + + $map = []; + foreach ($rows as $r) { + $fid = (int) ($r['folder_id'] ?? 0); + $vid = (int) ($r['viewlevel_id'] ?? 0); + if ($fid > 0 && $vid > 0) { + $map[$fid] = $vid; + } + } + return $map; } } diff --git a/com_eis/administrator/tmpl/.DS_Store b/com_eis/administrator/tmpl/.DS_Store deleted file mode 100644 index cad045a..0000000 Binary files a/com_eis/administrator/tmpl/.DS_Store and /dev/null differ diff --git a/com_eis/administrator/tmpl/main/default.php b/com_eis/administrator/tmpl/main/default.php index e2c1adf..b66c213 100644 --- a/com_eis/administrator/tmpl/main/default.php +++ b/com_eis/administrator/tmpl/main/default.php @@ -6,6 +6,10 @@ use Joomla\CMS\Router\Route; use Joomla\CMS\Language\Text; $hasTree = !empty($this->treeHtml); + +// ACL-Daten aus der View (für Vorbelegung im Formular) +$viewLevels = method_exists($this, 'getViewLevels') ? (array) $this->getViewLevels() : []; +$aclMap = method_exists($this, 'getFolderAccess') ? (array) $this->getFolderAccess() : []; ?>