Files
awo-hamburg-intranet/module/knowledgecenter_update/Ajax/Ajax.php
Moritz Weinmann a0698d621e feat(wiki): Betriebsrat-Berechtigung (r-wiki-br)
Neue Permission r-wiki-br ermöglicht eingeschränkte Wiki-Bearbeitung:

- Neue Permission in main_permission (ID 30: r-wiki-br)
- Neue Spalte betriebsrat_editable in knowledgecenter_categories_update
- Checkbox "Für Betriebsrat bearbeitbar" in der Kategorie-Verwaltung
  (nur für r-wiki-Redakteure sichtbar und speicherbar)

Berechtigungslogik:
- r-wiki schlägt immer: Wer beide Rechte hat, verhält sich wie Redakteur
- Betriebsrat sieht nur Kategorien mit betriebsrat_editable=1
- "Beitrag erstellen"-Button nur in freigegebenen Kategorien sichtbar
- Neue Posts/Bearbeitungen auf betriebsrat_editable-Kategorien beschränkt
- Settings-Tab und Kategorieverwaltung weiterhin r-wiki-only
- Ajax-Endpoints (save_post_files, delete_post_file, upload_inline_image)
  prüfen Kategorie-Berechtigung serverseitig

File Gallery:
- Betriebsrat ist per kc_lock_path auf den Post-eigenen Ordner beschränkt
- Redakteur kann frei navigieren (kein Lock)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 13:16:36 +02:00

2185 lines
86 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// ini_set('display_errors', 1);
// error_reporting(E_ALL);
$currDir = __DIR__;
$rootDir = dirname(dirname(dirname($currDir)));
$mysydeDir = $rootDir . '/mysyde';
require_once($rootDir . '/vendor/autoload.php');
require_once($rootDir . "/mysyde/frontend/frontend_functions.inc.php");
$envDir = $rootDir . '/config';
$envFilePath = $envDir . '/.env';
if (is_dir($envDir) && file_exists($envFilePath) && is_file($envFilePath) && is_readable($envFilePath)) {
$dotenv = new \Dotenv\Dotenv($envDir);
$dotenv->load();
}
$common = $mysydeDir . "/common/common_functions.inc.php";
require_once($common);
$connEnv = (local_environment()) ? $mysydeDir . "/dc.config.php" : $mysydeDir . "/dc-server.config.php";
require_once($connEnv);
db_connect();
// Prüft ob der aktuelle User Wiki-Schreibrechte hat (r-wiki oder r-wiki-br)
function ajax_has_wiki_edit(): bool
{
$mid = $GLOBALS['main_contact']['master_mandant_id'] ?? 0;
$uid = $GLOBALS['main_contact']['id'] ?? 0;
return get_permission_state('r-wiki', $mid, $uid) == 1
|| get_permission_state('r-wiki-br', $mid, $uid) == 1;
}
// Betriebsrat darf einen Post nur bearbeiten, wenn er in einer freigegebenen Kategorie liegt.
// Gibt false zurück wenn Bearbeitung verboten, true wenn erlaubt.
function ajax_can_edit_post(int $postId): bool
{
$mid = $GLOBALS['main_contact']['master_mandant_id'] ?? 0;
$uid = $GLOBALS['main_contact']['id'] ?? 0;
if (get_permission_state('r-wiki', $mid, $uid) == 1) {
return true; // Redakteur darf immer
}
if (get_permission_state('r-wiki-br', $mid, $uid) != 1) {
return false; // Kein Wiki-Recht
}
// Betriebsrat: Post muss in betriebsrat_editable Kategorie liegen
$res = mysqli_query($GLOBALS['mysql_con'],
"SELECT COUNT(*) AS cnt
FROM knowledgecenter_category_post kcp
JOIN knowledgecenter_categories_update kcu ON kcu.id = kcp.category_id
WHERE kcp.post_id = $postId AND kcu.betriebsrat_editable = 1");
$row = mysqli_fetch_assoc($res);
return $row && (int)$row['cnt'] > 0;
}
// AJAX-Handler: Falls per POST eine Aktion übergeben wurde, diese verarbeiten und das Skript beenden.
if (isset($_POST["action"])) {
header('Content-Type: application/json');
switch ($_POST["action"]) {
case 'update_categories_sorting':
update_categories_sorting();
break;
case 'update_category_detail':
update_category_detail();
break;
case 'update_subcategory_sorting':
update_subcategory_sorting();
break;
case 'link_subcategory':
link_subcategory();
break;
case 'unlink_subcategory':
unlink_subcategory();
break;
case 'update_category_links':
update_category_links();
break;
case 'delete_category':
delete_category();
break;
case 'update_post_version':
update_post_version();
break;
case 'insert_post_version':
insert_post_version();
break;
case 'create_post_draft':
create_post_draft();
break;
case 'ensure_post_gallery_folder':
ensure_post_gallery_folder();
break;
case 'delete_post_version':
delete_post_version();
break;
case 'get_post_comments':
get_post_comments();
break;
case 'insert_comment':
insert_comment();
break;
case 'delete_comment':
delete_comment();
break;
case 'update_post_categories':
update_post_categories();
break;
case 'get_post_list':
get_post_list();
break;
case 'get_post_files':
get_post_files();
break;
case 'update_post_contacts':
update_post_contacts();
break;
case 'get_contact_cards':
get_contact_cards();
break;
case 'search_posts':
search_posts();
break;
case 'update_post_pdf_inline':
update_post_pdf_inline();
break;
case 'update_post_colors':
update_post_colors();
break;
case 'delete_post':
delete_post();
break;
case 'update_post_mandants':
// Erwarte post_id und einen JSON-codierten Array für mandant IDs
$postId = (int) ($_POST['post_id'] ?? 0);
$mandantIds = json_decode($_POST['mandants'], true) ?: [];
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Post-ID']);
exit;
}
$result = update_post_mandants($postId, $mandantIds);
echo json_encode(is_bool($result) && $result ? ['success' => true] : ['error' => $result]);
exit;
case 'update_post_departments':
$postId = (int) ($_POST['post_id'] ?? 0);
$departmentIds = json_decode($_POST['departments'], true) ?: [];
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Post-ID']);
exit;
}
$result = update_post_departments($postId, $departmentIds);
echo json_encode(is_bool($result) && $result ? ['success' => true] : ['error' => $result]);
exit;
case 'update_post_roles':
$postId = (int) ($_POST['post_id'] ?? 0);
$roleIds = json_decode($_POST['roles'], true) ?: [];
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Post-ID']);
exit;
}
$result = update_post_roles($postId, $roleIds);
echo json_encode(is_bool($result) && $result ? ['success' => true] : ['error' => $result]);
exit;
case 'update_post_einrichts':
$postId = (int) ($_POST['post_id'] ?? 0);
$einrichtIds = json_decode($_POST['einrichts'], true) ?: [];
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Post-ID']);
exit;
}
$result = update_post_einrichts($postId, $einrichtIds);
echo json_encode(is_bool($result) && $result ? ['success' => true] : ['error' => $result]);
exit;
case 'update_post_fachbereiche':
$postId = (int) ($_POST['post_id'] ?? 0);
$fachbereichIds = json_decode($_POST['fachbereiche'], true) ?: [];
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Post-ID']);
exit;
}
$result = update_post_fachbereiche($postId, $fachbereichIds);
echo json_encode(is_bool($result) && $result ? ['success' => true] : ['error' => $result]);
exit;
case 'update_post_products':
$postId = (int) ($_POST['post_id'] ?? 0);
$productIds = json_decode($_POST['products'] ?? '[]', true) ?: [];
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Post-ID']);
exit;
}
$result = update_post_products($postId, $productIds);
echo json_encode(is_bool($result) && $result ? ['success' => true] : ['error' => $result]);
exit;
case 'save_post_files':
save_post_files();
break;
case 'delete_post_file':
delete_post_file();
break;
case 'update_post_certificate':
update_post_certificate();
break;
case 'update_post_form':
$postId = (int) ($_POST['post_id'] ?? 0);
$formId = (int) ($_POST['form_id'] ?? 0);
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Post-ID']);
exit;
}
$result = update_post_form($postId, $formId);
echo json_encode($result === true ? ['success' => true] : ['error' => $result]);
exit;
default:
echo json_encode(['error' => 'Ungültige Aktion']);
exit;
}
exit;
}
/**
* Aktualisiert die Sortierung der Kategorien in der Hauptliste.
*/
function update_categories_sorting()
{
if (!isset($_POST['order']) || !is_array($_POST['order'])) {
echo json_encode(['error' => 'Keine Sortierdaten übergeben']);
exit;
}
$order = $_POST['order'];
foreach ($order as $index => $categoryId) {
$sorting = $index + 1; // 1-indexiert
$categoryId = (int) $categoryId;
$query = "UPDATE knowledgecenter_categories_update SET sorting = $sorting WHERE id = $categoryId";
if (!mysqli_query($GLOBALS['mysql_con'], $query)) {
echo json_encode(['error' => "Datenbankfehler bei Kategorie-ID $categoryId"]);
exit;
}
}
echo json_encode(['success' => true]);
exit;
}
function update_post_form($postId, $formId)
{
$checkQuery = "SELECT id FROM knowledgecenter_post_form WHERE post_id = $postId";
$result = mysqli_query($GLOBALS['mysql_con'], $checkQuery);
if ($result && mysqli_num_rows($result) > 0) {
$query = "UPDATE knowledgecenter_post_form SET form_id = $formId WHERE post_id = $postId";
} else {
$query = "INSERT INTO knowledgecenter_post_form (post_id, form_id) VALUES ($postId, $formId)";
}
if (mysqli_query($GLOBALS['mysql_con'], $query)) {
return true;
} else {
return 'Fehler beim Speichern der Formular-Zuordnung: ' . mysqli_error($GLOBALS['mysql_con']);
}
}
/**
* Speichert oder aktualisiert die sprachspezifischen Inhalte (Translation) einer Kategorie.
*/
function update_category_detail()
{
$is_navigation_item = isset($_POST['is_navigation_item']) ? (int) $_POST['is_navigation_item'] : 0;
$enable_post_color_pickers = isset($_POST['enable_post_color_pickers']) ? (int) $_POST['enable_post_color_pickers'] : 0;
$betriebsrat_editable = isset($_POST['betriebsrat_editable']) ? (int) $_POST['betriebsrat_editable'] : 0;
$color_hex = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['color_hex'] ?? '');
$categoryId = (int) ($_POST['id'] ?? 0);
$languageId = (int) ($_POST['language_id'] ?? 0);
$modified_by = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['modified_by'] ?? '');
$state = isset($_POST['state']) ? (int) $_POST['state'] : 0;
if ($languageId <= 0) {
echo json_encode(['error' => 'Ungültige Parameter']);
exit;
}
$title = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['title'] ?? '');
$image = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['image'] ?? '');
if ($categoryId === 0) {
$insertMain = "INSERT INTO knowledgecenter_categories_update
(modified_by, created_at, state, color_hex, enable_post_color_pickers)
VALUES ('$modified_by', NOW(), $state, '$color_hex', $enable_post_color_pickers)";
if (!mysqli_query($GLOBALS['mysql_con'], $insertMain)) {
echo json_encode(['error' => 'Fehler beim Erstellen der Hauptkategorie: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
$categoryId = mysqli_insert_id($GLOBALS['mysql_con']);
}
$checkQuery = "SELECT id FROM knowledgecenter_category_translations
WHERE category_id = $categoryId AND language_id = $languageId LIMIT 1";
$checkResult = mysqli_query($GLOBALS['mysql_con'], $checkQuery);
if ($checkResult && mysqli_num_rows($checkResult) > 0) {
$query = "UPDATE knowledgecenter_category_translations
SET title = '$title', image = '$image', modified_at = NOW(), is_navigation_item = $is_navigation_item
WHERE category_id = $categoryId AND language_id = $languageId";
} else {
$query = "INSERT INTO knowledgecenter_category_translations
(category_id, language_id, title, image, created_at, is_navigation_item)
VALUES ($categoryId, $languageId, '$title', '$image', NOW(), $is_navigation_item)";
}
if (!mysqli_query($GLOBALS['mysql_con'], $query)) {
echo json_encode(['error' => 'Datenbankfehler: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
$mainUpdate = "UPDATE knowledgecenter_categories_update
SET modified_by = '$modified_by',
modified_at = NOW(),
state = $state,
enable_post_color_pickers = $enable_post_color_pickers,
betriebsrat_editable = $betriebsrat_editable,
color_hex = '$color_hex'
WHERE id = $categoryId";
if (!mysqli_query($GLOBALS['mysql_con'], $mainUpdate)) {
echo json_encode(['error' => 'Datenbankfehler beim Update der Hauptkategorie: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
$forwardUrl = "/intranet/de/wiki/?detail=$categoryId";
if ($is_navigation_item === 1) {
$existsNavQuery = "SELECT id FROM main_navigation
WHERE forward_url = '$forwardUrl'
LIMIT 1";
$navResult = mysqli_query($GLOBALS['mysql_con'], $existsNavQuery);
if (mysqli_num_rows($navResult) === 0) {
$menuName = mysqli_real_escape_string($GLOBALS['mysql_con'], $title);
$queryNav = "INSERT INTO main_navigation (
main_site_id, main_language_id, parent_id, sorting, level, code, menu_name,
title_name, active, validity_from, validity_to, modified_date, modified_admin_user_id,
forward_type, forward_navigation_id, forward_page_id, forward_url,
navigation_icon, hidden, is_landing_page, description
) VALUES (
1, $languageId, NULL, 9999, 1, 'kc_$categoryId', '$menuName',
'$menuName', 1, NULL, NULL, NOW(), 0,
5, 0, 0, '$forwardUrl',
'knowledgecenter', 0, 0, ''
)";
mysqli_query($GLOBALS['mysql_con'], $queryNav);
}
} else {
$deleteNavQuery = "DELETE FROM main_navigation WHERE forward_url = '$forwardUrl'";
mysqli_query($GLOBALS['mysql_con'], $deleteNavQuery);
}
$queryTimestamp = "SELECT modified_at, modified_by FROM knowledgecenter_categories_update WHERE id = $categoryId";
$resultTimestamp = mysqli_query($GLOBALS['mysql_con'], $queryTimestamp);
$row = mysqli_fetch_assoc($resultTimestamp);
$fmt = new IntlDateFormatter('de_DE', IntlDateFormatter::LONG, IntlDateFormatter::SHORT);
$timestamp = strtotime($row['modified_at']);
$formattedAt = $fmt->format($timestamp);
echo json_encode([
'success' => true,
'modified_at' => $formattedAt,
'modified_by' => $row['modified_by'],
'category_id' => $categoryId,
'is_navigation_item' => $is_navigation_item
]);
exit;
}
/**
* Aktualisiert die Sortierung der verknüpften Unterkategorien.
*/
function update_subcategory_sorting()
{
$parentId = (int) ($_POST['parent_id'] ?? 0);
$order = $_POST['order'] ?? [];
if ($parentId <= 0 || !is_array($order)) {
echo json_encode(['error' => 'Ungültige Parameter']);
exit;
}
foreach ($order as $index => $childId) {
$childId = (int) $childId;
$sorting = $index + 1;
$query = "UPDATE knowledgecenter_category_links
SET sorting = $sorting
WHERE parent_category_id = $parentId AND child_category_id = $childId";
if (!mysqli_query($GLOBALS['mysql_con'], $query)) {
echo json_encode(['error' => "Fehler beim Sortieren: " . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
}
echo json_encode(['success' => true]);
exit;
}
/**
* Verknüpft eine neue Unterkategorie mit der aktuellen Kategorie.
*/
function link_subcategory()
{
$parent_id = (int) ($_POST['parent_id'] ?? 0);
$child_id = (int) ($_POST['child_id'] ?? 0);
if ($parent_id <= 0 || $child_id <= 0) {
echo json_encode(['error' => 'Ungültige Parameter']);
exit;
}
// Bestimme den nächsten Sortierwert für die neue Verknüpfung
$query = "SELECT IFNULL(MAX(sorting), 0) + 1 AS next_sort
FROM knowledgecenter_category_links
WHERE parent_category_id = $parent_id";
$result = mysqli_query($GLOBALS['mysql_con'], $query);
$row = mysqli_fetch_assoc($result);
$nextSort = (int) $row['next_sort'];
$insertQuery = "INSERT INTO knowledgecenter_category_links (parent_category_id, child_category_id, sorting)
VALUES ($parent_id, $child_id, $nextSort)";
if (mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['error' => 'Datenbankfehler: ' . mysqli_error($GLOBALS['mysql_con'])]);
}
exit;
}
/**
* Entfernt die Verknüpfung einer Unterkategorie von der aktuellen Kategorie.
*/
function unlink_subcategory()
{
$parent_id = (int) ($_POST['parent_id'] ?? 0);
$child_id = (int) ($_POST['child_id'] ?? 0);
if ($parent_id <= 0 || $child_id <= 0) {
echo json_encode(['error' => 'Ungültige Parameter']);
exit;
}
$query = "DELETE FROM knowledgecenter_category_links WHERE parent_category_id = $parent_id AND child_category_id = $child_id";
if (mysqli_query($GLOBALS['mysql_con'], $query)) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['error' => 'Datenbankfehler: ' . mysqli_error($GLOBALS['mysql_con'])]);
}
exit;
}
/**
* Löscht alte Mandanten-Verknüpfungen und fügt neue ein.
*/
function update_category_mandants($categoryId, $mandantIds)
{
// Alte Verknüpfungen löschen
$deleteQuery = "DELETE FROM knowledgecenter_category_mandant WHERE category_id = $categoryId";
if (!mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
return "Fehler beim Löschen der Mandanten-Verknüpfungen: " . mysqli_error($GLOBALS['mysql_con']);
}
// Neue Verknüpfungen einfügen
foreach ($mandantIds as $mandantId) {
$mandantId = (int) $mandantId;
$insertQuery = "INSERT INTO knowledgecenter_category_mandant (category_id, mandant_id, created_at)
VALUES ($categoryId, $mandantId, NOW())";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
return "Fehler beim Einfügen der Mandant-ID $mandantId: " . mysqli_error($GLOBALS['mysql_con']);
}
}
return true;
}
/**
* Löscht alte Department-Verknüpfungen und fügt neue ein.
*/
function update_category_departments($categoryId, $departmentIds)
{
$deleteQuery = "DELETE FROM knowledgecenter_category_department WHERE category_id = $categoryId";
if (!mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
return "Fehler beim Löschen der Department-Verknüpfungen: " . mysqli_error($GLOBALS['mysql_con']);
}
foreach ($departmentIds as $departmentId) {
$departmentId = (int) $departmentId;
$insertQuery = "INSERT INTO knowledgecenter_category_department (category_id, department_id, created_at)
VALUES ($categoryId, $departmentId, NOW())";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
return "Fehler beim Einfügen der Department-ID $departmentId: " . mysqli_error($GLOBALS['mysql_con']);
}
}
return true;
}
/**
* Löscht alte Rollen-Verknüpfungen und fügt neue ein.
*/
function update_category_roles($categoryId, $roleIds)
{
$deleteQuery = "DELETE FROM knowledgecenter_category_role WHERE category_id = $categoryId";
if (!mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
return "Fehler beim Löschen der Rollen-Verknüpfungen: " . mysqli_error($GLOBALS['mysql_con']);
}
foreach ($roleIds as $roleId) {
$roleId = (int) $roleId;
$insertQuery = "INSERT INTO knowledgecenter_category_role (category_id, role_id, created_at)
VALUES ($categoryId, $roleId, NOW())";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
return "Fehler beim Einfügen der Rollen-ID $roleId: " . mysqli_error($GLOBALS['mysql_con']);
}
}
return true;
}
/**
* Löscht alte Einrichtungs-Verknüpfungen und fügt neue ein.
*/
function update_category_einrichts($categoryId, $einrichtIds)
{
$deleteQuery = "DELETE FROM knowledgecenter_category_einricht WHERE category_id = $categoryId";
if (!mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
return "Fehler beim Löschen der Einrichtungs-Verknüpfungen: " . mysqli_error($GLOBALS['mysql_con']);
}
foreach ($einrichtIds as $einrichtId) {
$einrichtId = (int) $einrichtId;
$insertQuery = "INSERT INTO knowledgecenter_category_einricht (category_id, einricht_id, created_at)
VALUES ($categoryId, $einrichtId, NOW())";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
return "Fehler beim Einfügen der Einrichtungs-ID $einrichtId: " . mysqli_error($GLOBALS['mysql_con']);
}
}
return true;
}
/**
* Löscht alte Fachbereich-Verknüpfungen und fügt neue ein.
*/
function update_category_fachbereiche($categoryId, $fachbereichIds)
{
$deleteQuery = "DELETE FROM knowledgecenter_category_fachbereich WHERE category_id = $categoryId";
if (!mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
return "Fehler beim Löschen der Fachbereich-Verknüpfungen: " . mysqli_error($GLOBALS['mysql_con']);
}
foreach ($fachbereichIds as $fachbereichId) {
$fachbereichId = (int) $fachbereichId;
$insertQuery = "INSERT INTO knowledgecenter_category_fachbereich (category_id, fachbereich_id, created_at)
VALUES ($categoryId, $fachbereichId, NOW())";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
return "Fehler beim Einfügen der Fachbereich-ID $fachbereichId: " . mysqli_error($GLOBALS['mysql_con']);
}
}
return true;
}
/**
* Kombinierte Funktion, die alle fünf Link-Tabellen aktualisiert.
*/
function update_category_links()
{
$categoryId = (int) ($_POST['id'] ?? 0);
$mandantIds = json_decode($_POST['mandants'], true) ?: [];
$departmentIds = json_decode($_POST['departments'], true) ?: [];
$roleIds = json_decode($_POST['roles'], true) ?: [];
$einrichtIds = json_decode($_POST['einrichts'] ?? '[]', true) ?: [];
$fachbereichIds = json_decode($_POST['fachbereiche'] ?? '[]', true) ?: [];
$errors = [];
$result = update_category_mandants($categoryId, $mandantIds);
if ($result !== true) {
$errors[] = $result;
}
$result = update_category_departments($categoryId, $departmentIds);
if ($result !== true) {
$errors[] = $result;
}
$result = update_category_roles($categoryId, $roleIds);
if ($result !== true) {
$errors[] = $result;
}
$result = update_category_einrichts($categoryId, $einrichtIds);
if ($result !== true) {
$errors[] = $result;
}
$result = update_category_fachbereiche($categoryId, $fachbereichIds);
if ($result !== true) {
$errors[] = $result;
}
if (empty($errors)) {
$savedMandants = [];
foreach ($mandantIds as $id) {
$id = (int) $id;
$sql = "SELECT id, description FROM main_mandant WHERE id = $id LIMIT 1";
$res = mysqli_query($GLOBALS['mysql_con'], $sql);
if ($res && mysqli_num_rows($res) > 0) {
$row = mysqli_fetch_assoc($res);
$savedMandants[] = ['value' => $row['id'], 'name' => $row['description']];
}
}
$savedDepartments = [];
foreach ($departmentIds as $id) {
$id = (int) $id;
$sql = "SELECT id, description FROM main_department WHERE id = $id LIMIT 1";
$res = mysqli_query($GLOBALS['mysql_con'], $sql);
if ($res && mysqli_num_rows($res) > 0) {
$row = mysqli_fetch_assoc($res);
$savedDepartments[] = ['value' => $row['id'], 'name' => $row['description']];
}
}
$savedRoles = [];
foreach ($roleIds as $id) {
$id = (int) $id;
$sql = "SELECT id, description FROM main_role WHERE id = $id LIMIT 1";
$res = mysqli_query($GLOBALS['mysql_con'], $sql);
if ($res && mysqli_num_rows($res) > 0) {
$row = mysqli_fetch_assoc($res);
$savedRoles[] = ['value' => $row['id'], 'name' => $row['description']];
}
}
$savedEinrichts = [];
foreach ($einrichtIds as $id) {
$id = (int) $id;
$sql = "SELECT id, description FROM organigramm_einricht WHERE id = $id LIMIT 1";
$res = mysqli_query($GLOBALS['mysql_con'], $sql);
if ($res && mysqli_num_rows($res) > 0) {
$row = mysqli_fetch_assoc($res);
$savedEinrichts[] = ['value' => $row['id'], 'name' => $row['description']];
}
}
$savedFachbereiche = [];
foreach ($fachbereichIds as $id) {
$id = (int) $id;
$sql = "SELECT id, description FROM organogramm_space WHERE id = $id LIMIT 1";
$res = mysqli_query($GLOBALS['mysql_con'], $sql);
if ($res && mysqli_num_rows($res) > 0) {
$row = mysqli_fetch_assoc($res);
$savedFachbereiche[] = ['value' => $row['id'], 'name' => $row['description']];
}
}
echo json_encode([
'success' => true,
'mandants' => $savedMandants,
'departments' => $savedDepartments,
'roles' => $savedRoles,
'einrichts' => $savedEinrichts,
'fachbereiche' => $savedFachbereiche,
]);
} else {
echo json_encode(['error' => implode("; ", $errors)]);
}
exit;
}
function delete_category()
{
$categoryId = (int) ($_POST['id'] ?? 0);
if ($categoryId <= 0) {
echo json_encode(['error' => 'Ungültige Kategorie-ID']);
exit;
}
// Lösche Navigationseintrag für diese Kategorie, falls vorhanden
$forwardUrl = "/intranet/de/wiki/?detail=" . $categoryId;
$navCheckQuery = "SELECT id FROM main_navigation WHERE forward_url = '$forwardUrl' LIMIT 1";
$navCheckResult = mysqli_query($GLOBALS['mysql_con'], $navCheckQuery);
if ($navCheckResult && mysqli_num_rows($navCheckResult) > 0) {
mysqli_query($GLOBALS['mysql_con'], "DELETE FROM main_navigation WHERE forward_url = '$forwardUrl'");
}
// Bei korrekter Fremdschlüsseldefinition (z. B. ON DELETE CASCADE) reicht oft ein DELETE aus.
$query = "DELETE FROM knowledgecenter_categories_update WHERE id = $categoryId";
if (mysqli_query($GLOBALS['mysql_con'], $query)) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['error' => 'Fehler beim Löschen: ' . mysqli_error($GLOBALS['mysql_con'])]);
}
exit;
}
function normalize_hex_color($color, $default = '#FFFFFF')
{
$color = trim((string) $color);
if (preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$/', $color)) {
return strtoupper($color);
}
return $default;
}
/**
* Aktualisiert eine bestehende Post-Version.
*/
function update_post_version()
{
// Stelle sicher, dass alle Sprachdaten als Array übermittelt wurden
if (!isset($_POST['title']) || !is_array($_POST['title'])) {
echo json_encode(['error' => 'Keine Sprachdaten übermittelt']);
exit;
}
// Hole die Beitrags-ID aus den POST-Daten (wichtig für den Insert-Fallback)
$postId = (int) ($_POST['post_id'] ?? 0);
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Beitrags-ID']);
exit;
}
$keepModifiedAt = isset($_POST['keep_modified_at']) && (int) $_POST['keep_modified_at'] === 1;
$backgroundColor = normalize_hex_color($_POST['background_color'] ?? '#FFFFFF', '#FFFFFF');
$textColor = normalize_hex_color($_POST['text_color'] ?? '#000000', '#000000');
$updatePostQuery = "UPDATE knowledgecenter_posts
SET background_color = '$backgroundColor',
text_color = '$textColor'" . ($keepModifiedAt ? "" : ", modified_at = NOW()") . "
WHERE id = $postId";
if (!mysqli_query($GLOBALS['mysql_con'], $updatePostQuery)) {
echo json_encode(['error' => 'Fehler beim Aktualisieren der Post-Farben: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
// Iteriere über alle übermittelten Sprachfelder
foreach ($_POST['title'] as $langId => $title) {
$langId = (int) $langId;
$title = mysqli_real_escape_string($GLOBALS['mysql_con'], $title);
$image = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['image'][$langId] ?? '');
$teaser = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['teaser'][$langId] ?? '');
$content = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['content'][$langId] ?? '');
$summary = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['summary'][$langId] ?? '');
$author_id = isset($_POST['author_id'][$langId]) ? (int) $_POST['author_id'][$langId] : 0;
$link = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['link'][$langId] ?? '');
$link_label = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['link_label'][$langId] ?? '');
// State-Rohwert (Default 0 = Inaktiv, falls nichts übergeben)
$stateRaw = $_POST['state'][$langId] ?? '0';
// In Integer casten
$state = (int) $stateRaw;
// Hier die neuen Felder:
$valid_from = isset($_POST['valid_from'][$langId]) ? mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['valid_from'][$langId]) : 'NULL';
$valid_until = isset($_POST['valid_until'][$langId]) ? mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['valid_until'][$langId]) : 'NULL';
// Wenn ein Datum übergeben wird, müssen wir es als Zeichenkette in SQL einbinden, andernfalls NULL
$valid_from_sql = ($valid_from !== 'NULL' && !empty($valid_from)) ? "'$valid_from'" : "NULL";
$valid_until_sql = ($valid_until !== 'NULL' && !empty($valid_until)) ? "'$valid_until'" : "NULL";
// Prüfe, ob es bereits eine Version gibt…
$versionId = isset($_POST['version_id'][$langId]) ? (int) $_POST['version_id'][$langId] : 0;
if ($versionId <= 0) {
// INSERT: Ermittle die neue Versionnummer…
$query = "SELECT MAX(version_number) AS max_ver
FROM knowledgecenter_post_versions
WHERE post_id = $postId AND language_id = $langId";
$result = mysqli_query($GLOBALS['mysql_con'], $query);
$row = mysqli_fetch_assoc($result);
$newVersion = ((int) $row['max_ver']) + 1;
$insertModifiedAtSql = "NOW()";
if ($keepModifiedAt) {
$prevQuery = "SELECT modified_at FROM knowledgecenter_post_versions
WHERE post_id = $postId AND language_id = $langId
ORDER BY version_number DESC
LIMIT 1";
$prevResult = mysqli_query($GLOBALS['mysql_con'], $prevQuery);
if ($prevResult && mysqli_num_rows($prevResult) > 0) {
$prevRow = mysqli_fetch_assoc($prevResult);
if (!empty($prevRow['modified_at'])) {
$prevModifiedAt = mysqli_real_escape_string($GLOBALS['mysql_con'], $prevRow['modified_at']);
$insertModifiedAtSql = "'$prevModifiedAt'";
}
}
}
$insertQuery = "
INSERT INTO knowledgecenter_post_versions
(post_id, language_id, version_number,
title, image, link, link_label, teaser, content, summary, author_id, state,
modified_at, valid_from, valid_until, background_color, text_color)
VALUES
($postId, $langId, $newVersion,
'$title', '$image', '$link', '$link_label', '$teaser', '$content', '$summary', $author_id, $state,
$insertModifiedAtSql, $valid_from_sql, $valid_until_sql, '$backgroundColor', '$textColor')
";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
echo json_encode(['error' => 'Fehler beim Einfügen für Sprache ' . $langId . ': ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
continue;
}
// UPDATE: Aktualisiere auch die Datumsfelder
$query = "
UPDATE knowledgecenter_post_versions
SET title = '$title',
image = '$image',
link = '$link',
link_label = '$link_label',
teaser = '$teaser',
content = '$content',
summary = '$summary',
author_id = $author_id,
state = $state,
valid_from = $valid_from_sql,
valid_until = $valid_until_sql,
background_color = '$backgroundColor',
text_color = '$textColor'" . ($keepModifiedAt ? "" : ",\n modified_at = NOW()") . "
WHERE id = $versionId AND language_id = $langId
";
if (!mysqli_query($GLOBALS['mysql_con'], $query)) {
echo json_encode(['error' => 'Fehler beim Aktualisieren für Sprache ' . $langId . ': ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
}
echo json_encode(['success' => true]);
exit;
}
/**
* Erstellt eine neue Post-Version basierend auf den übermittelten Feldern.
*/
function insert_post_version()
{
// Hole die Beitrags-ID aus den POST-Daten (0 = neuer Beitrag)
$postId = (int) ($_POST['post_id'] ?? 0);
// Initiale Kategorie (falls über Form übergeben)
$initialCategoryId = isset($_POST['initial_category_id']) ? (int) $_POST['initial_category_id'] : 0;
$initialProductId = isset($_POST['InitialProduct']) ? (int) $_POST['InitialProduct'] : 0;
$keepModifiedAt = isset($_POST['keep_modified_at']) && (int) $_POST['keep_modified_at'] === 1;
$backgroundColor = normalize_hex_color($_POST['background_color'] ?? '#FFFFFF', '#FFFFFF');
$textColor = normalize_hex_color($_POST['text_color'] ?? '#000000', '#000000');
// Wenn keine gültige Post-ID vorliegt, erstelle einen neuen Beitrag
if ($postId <= 0) {
$insertPostQuery = "INSERT INTO knowledgecenter_posts (created_at, background_color, text_color) VALUES (NOW(), '$backgroundColor', '$textColor')";
if (!mysqli_query($GLOBALS['mysql_con'], $insertPostQuery)) {
echo json_encode(['error' => 'Fehler beim Erstellen des neuen Beitrags: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
// Verwende die neu generierte Post-ID für den weiteren Insert
$postId = mysqli_insert_id($GLOBALS['mysql_con']);
} else {
$updatePostQuery = "UPDATE knowledgecenter_posts
SET background_color = '$backgroundColor',
text_color = '$textColor'" . ($keepModifiedAt ? "" : ",\n modified_at = NOW()") . "
WHERE id = $postId";
if (!mysqli_query($GLOBALS['mysql_con'], $updatePostQuery)) {
echo json_encode(['error' => 'Fehler beim Aktualisieren der Post-Farben: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
}
// Kategorie-Verknüpfung bei neu angelegtem Beitrag
if ($initialCategoryId > 0 && $postId > 0) {
$catInsert = "INSERT INTO knowledgecenter_category_post (post_id, category_id) VALUES ($postId, $initialCategoryId)";
mysqli_query($GLOBALS['mysql_con'], $catInsert);
}
if ($initialProductId > 0 && $postId > 0) {
$prodInsert = "INSERT INTO product_knowledgecenter_post_link (product_id, post_id) VALUES ($initialProductId, $postId)";
mysqli_query($GLOBALS['mysql_con'], $prodInsert);
}
$folderInfo = ensure_post_gallery_dir($postId);
if (!$folderInfo['success']) {
echo json_encode(['error' => 'Fehler beim Erstellen des Beitragsordners: ' . $folderInfo['error']]);
exit;
}
// Sicherstellen, dass Sprachdaten übermittelt wurden
if (!isset($_POST['title']) || !is_array($_POST['title'])) {
echo json_encode(['error' => 'Keine Sprachdaten übermittelt']);
exit;
}
$newIds = [];
foreach ($_POST['title'] as $langId => $title) {
$langId = (int) $langId;
$title = trim($title);
$title = mysqli_real_escape_string($GLOBALS['mysql_con'], $title);
$image = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['image'][$langId] ?? '');
$teaser = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['teaser'][$langId] ?? '');
$content = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['content'][$langId] ?? '');
$summary = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['summary'][$langId] ?? '');
$link = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['link'][$langId] ?? '');
$link_label = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['link_label'][$langId] ?? '');
$author_id = isset($_POST['author_id'][$langId]) ? (int) $_POST['author_id'][$langId] : 0;
// State-Rohwert (Default 0 = Inaktiv, falls nichts übergeben)
$stateRaw = $_POST['state'][$langId] ?? '0';
// In Integer casten
$state = (int) $stateRaw;
$valid_from = isset($_POST['valid_from'][$langId]) ? mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['valid_from'][$langId]) : 'NULL';
$valid_until = isset($_POST['valid_until'][$langId]) ? mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['valid_until'][$langId]) : 'NULL';
$valid_from_sql = ($valid_from !== 'NULL' && !empty($valid_from)) ? "'$valid_from'" : "NULL";
$valid_until_sql = ($valid_until !== 'NULL' && !empty($valid_until)) ? "'$valid_until'" : "NULL";
$query = "SELECT MAX(version_number) AS max_ver
FROM knowledgecenter_post_versions
WHERE post_id = $postId AND language_id = $langId";
$result = mysqli_query($GLOBALS['mysql_con'], $query);
$row = mysqli_fetch_assoc($result);
$newVersion = ((int) $row['max_ver']) + 1;
$insertModifiedAtSql = "NOW()";
if ($keepModifiedAt) {
$prevQuery = "SELECT modified_at FROM knowledgecenter_post_versions
WHERE post_id = $postId AND language_id = $langId
ORDER BY version_number DESC
LIMIT 1";
$prevResult = mysqli_query($GLOBALS['mysql_con'], $prevQuery);
if ($prevResult && mysqli_num_rows($prevResult) > 0) {
$prevRow = mysqli_fetch_assoc($prevResult);
if (!empty($prevRow['modified_at'])) {
$prevModifiedAt = mysqli_real_escape_string($GLOBALS['mysql_con'], $prevRow['modified_at']);
$insertModifiedAtSql = "'$prevModifiedAt'";
}
}
}
$insertQuery = "INSERT INTO knowledgecenter_post_versions
(post_id, language_id, version_number, title, image, link, link_label, teaser, content, summary, author_id, state, modified_at, valid_from, valid_until, background_color, text_color)
VALUES ($postId, $langId, $newVersion, '$title', '$image', '$link', '$link_label', '$teaser', '$content', '$summary', $author_id, $state, $insertModifiedAtSql, $valid_from_sql, $valid_until_sql, '$backgroundColor', '$textColor')";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
echo json_encode(['error' => "Fehler beim Einfügen für Sprache $langId: " . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
$newIds[$langId] = mysqli_insert_id($GLOBALS['mysql_con']);
}
echo json_encode([
'success' => true,
'new_post_id' => $postId,
'gallery_start_path' => $folderInfo['start_path'],
'new_ids' => $newIds
]);
exit;
}
function get_post_folder_hash($postId)
{
return substr(sha1('kc_post_' . (int) $postId), 0, 24);
}
function ensure_post_gallery_dir($postId)
{
$postId = (int) $postId;
if ($postId <= 0) {
return ['success' => false, 'error' => 'Ungültige Post-ID'];
}
$hash = get_post_folder_hash($postId);
$galleryRelativePath = 'knowledgecenter_update/posts/' . $hash;
$basePath = $_SERVER['DOCUMENT_ROOT'] . '/userdata/knowledgecenter_update/posts';
$postPath = $basePath . '/' . $hash;
if (!is_dir($basePath) && !@mkdir($basePath, 0775, true)) {
return ['success' => false, 'error' => 'Basisordner konnte nicht erstellt werden'];
}
if (!is_dir($postPath) && !@mkdir($postPath, 0775, true)) {
return ['success' => false, 'error' => 'Post-Ordner konnte nicht erstellt werden'];
}
// Globalen shared-Symlink idempotent anlegen. Ziel ist relativ, damit der
// Symlink bei Backups/Restores oder Mount-Point-Wechseln stabil bleibt.
// Fehler hier sind nicht-blockierend: ein fehlender shared-Symlink darf die
// Post-Erstellung nicht verhindern.
$sharedLink = $postPath . '/shared';
if (!is_link($sharedLink) && !file_exists($sharedLink)) {
@symlink('../../shared', $sharedLink);
}
return [
'success' => true,
'hash' => $hash,
'start_path' => $galleryRelativePath,
];
}
function delete_post_gallery_dir($postId)
{
$postId = (int) $postId;
if ($postId <= 0) {
return ['success' => false, 'error' => 'Ungültige Post-ID'];
}
$userdataRoot = realpath($_SERVER['DOCUMENT_ROOT'] . '/userdata');
if ($userdataRoot === false) {
return ['success' => false, 'error' => 'userdata-Verzeichnis nicht gefunden'];
}
$postsBasePath = $userdataRoot . '/knowledgecenter_update/posts';
$postsBaseReal = realpath($postsBasePath);
if ($postsBaseReal === false) {
return ['success' => true, 'skipped' => true];
}
$targetPath = $postsBaseReal . '/' . get_post_folder_hash($postId);
$targetReal = realpath($targetPath);
if ($targetReal === false) {
return ['success' => true, 'skipped' => true];
}
if ($targetReal !== $postsBaseReal && strpos($targetReal, $postsBaseReal . DIRECTORY_SEPARATOR) !== 0) {
return ['success' => false, 'error' => 'Ungültiger Zielpfad'];
}
// Symlinks im Top-Level zuerst entfernen, BEVOR RecursiveDirectoryIterator startet.
// unlink() auf einem Symlink löscht nur den Symlink selbst, nicht das Ziel — so
// bleiben referenzierte globale Ressourcen (z.B. shared/) garantiert unangetastet.
foreach (scandir($targetReal, SCANDIR_SORT_NONE) as $entry) {
if ($entry === '.' || $entry === '..') continue;
$entryPath = $targetReal . '/' . $entry;
if (is_link($entryPath)) {
if (!@unlink($entryPath)) {
return ['success' => false, 'error' => 'Symlink konnte nicht gelöscht werden: ' . $entry];
}
}
}
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($targetReal, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $item) {
$itemPath = $item->getPathname();
// Defense-in-Depth: falls trotz Top-Level-Bereinigung ein Symlink auftaucht
// (z.B. tief verschachtelt), niemals dem Ziel folgen.
if (is_link($itemPath)) {
if (!@unlink($itemPath)) {
return ['success' => false, 'error' => 'Symlink konnte nicht gelöscht werden'];
}
continue;
}
if ($item->isDir()) {
if (!@rmdir($itemPath)) {
return ['success' => false, 'error' => 'Unterordner konnte nicht gelöscht werden'];
}
} else {
if (!@unlink($itemPath)) {
return ['success' => false, 'error' => 'Datei konnte nicht gelöscht werden'];
}
}
}
if (!@rmdir($targetReal)) {
return ['success' => false, 'error' => 'Post-Ordner konnte nicht gelöscht werden'];
}
return ['success' => true];
}
function create_post_draft()
{
$insertPostQuery = "INSERT INTO knowledgecenter_posts (created_at) VALUES (NOW())";
if (!mysqli_query($GLOBALS['mysql_con'], $insertPostQuery)) {
echo json_encode(['error' => 'Fehler beim Erstellen des Entwurfs: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
$postId = (int) mysqli_insert_id($GLOBALS['mysql_con']);
$folderInfo = ensure_post_gallery_dir($postId);
if (!$folderInfo['success']) {
echo json_encode(['error' => 'Entwurf erstellt, aber Ordner fehlgeschlagen: ' . $folderInfo['error']]);
exit;
}
echo json_encode([
'success' => true,
'new_post_id' => $postId,
'post_hash' => $folderInfo['hash'],
'gallery_start_path' => $folderInfo['start_path'],
]);
exit;
}
function ensure_post_gallery_folder()
{
$postId = (int) ($_POST['post_id'] ?? 0);
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Post-ID']);
exit;
}
$folderInfo = ensure_post_gallery_dir($postId);
if (!$folderInfo['success']) {
echo json_encode(['error' => 'Fehler beim Erstellen des Beitragsordners: ' . $folderInfo['error']]);
exit;
}
echo json_encode([
'success' => true,
'post_id' => $postId,
'post_hash' => $folderInfo['hash'],
'gallery_start_path' => $folderInfo['start_path'],
]);
exit;
}
function delete_post_version()
{
$postId = (int) ($_POST['post_id'] ?? 0);
$versionNumber = (int) ($_POST['version_number'] ?? 0);
if ($postId <= 0 || $versionNumber <= 0) {
echo json_encode(['error' => 'Ungültige Parameter']);
exit;
}
$isEmbedded = (int) ($_POST['embedded'] ?? 0);
// Lösche alle Versionseinträge für diesen Beitrag und diese Version (alle Sprachvarianten)
$query = "DELETE FROM knowledgecenter_post_versions WHERE post_id = $postId AND version_number = $versionNumber";
if (!mysqli_query($GLOBALS['mysql_con'], $query)) {
echo json_encode(['error' => 'Fehler beim Löschen der Version: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
// Prüfe, ob noch Versionen zu diesem Beitrag existieren
$queryCount = "SELECT COUNT(*) AS cnt FROM knowledgecenter_post_versions WHERE post_id = $postId";
$resCount = mysqli_query($GLOBALS['mysql_con'], $queryCount);
if ($resCount && mysqli_num_rows($resCount) > 0) {
$row = mysqli_fetch_assoc($resCount);
$countVersions = (int) $row['cnt'];
} else {
$countVersions = 0;
}
$response = ['success' => true];
// Wenn keine Versionen mehr vorhanden sind: Beitrag löschen und Redirect-Pfad zurückgeben
if ($countVersions === 0) {
$deletePostQuery = "DELETE FROM knowledgecenter_posts WHERE id = $postId";
if (!mysqli_query($GLOBALS['mysql_con'], $deletePostQuery)) {
echo json_encode(['error' => 'Fehler beim Löschen des Beitrags: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
$deleteFolderResult = delete_post_gallery_dir($postId);
if (!$deleteFolderResult['success']) {
echo json_encode(['error' => 'Beitrag gelöscht, aber Ordner konnte nicht gelöscht werden: ' . $deleteFolderResult['error']]);
exit;
}
$action = ($isEmbedded) ? 'PostList' : 'Category';
$response['redirect'] = '?action=' . $action . '&embedded=' . $isEmbedded;
}
echo json_encode($response);
exit;
}
function get_post_comments()
{
$postId = (int) ($_POST['post_id'] ?? 0);
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Beitrags-ID']);
exit;
}
// Achte darauf, dass auch parent_id abgerufen wird.
$query = "SELECT kc.*, mc.name AS author
FROM knowledgecenter_post_comments AS kc
LEFT JOIN main_contact AS mc ON kc.author_id = mc.id
WHERE kc.post_id = $postId
ORDER BY kc.created_at ASC";
$result = mysqli_query($GLOBALS['mysql_con'], $query);
if (!$result) {
echo json_encode(['error' => 'Datenbankfehler: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
$comments = [];
while ($row = mysqli_fetch_assoc($result)) {
$comments[] = $row;
}
echo json_encode(['success' => true, 'comments' => $comments]);
exit;
}
function insert_comment()
{
$postId = (int) ($_POST['post_id'] ?? 0);
$text = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['text'] ?? '');
$author_id = (int) ($_POST['author_id'] ?? 0);
// Neuen Parameter für Antwort (optional)
$parentId = isset($_POST['parent_id']) ? (int) $_POST['parent_id'] : 0;
if ($postId <= 0 || empty($text) || $author_id <= 0) {
echo json_encode(['error' => 'Ungültige Eingaben: Post-ID, Text und Author-ID sind erforderlich.']);
exit;
}
// Falls kein Parent vorhanden, setze NULL
$parentValue = ($parentId > 0) ? $parentId : "NULL";
$query = "INSERT INTO knowledgecenter_post_comments (post_id, text, author_id, parent_id, created_at)
VALUES ($postId, '$text', $author_id, $parentValue, NOW())";
if (!mysqli_query($GLOBALS['mysql_con'], $query)) {
echo json_encode(['error' => 'Datenbankfehler: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
$newCommentId = mysqli_insert_id($GLOBALS['mysql_con']);
echo json_encode(['success' => true, 'id' => $newCommentId]);
exit;
}
function delete_comment()
{
// Kommentar-ID aus den POST-Daten
$commentId = (int) ($_POST['comment_id'] ?? 0);
if ($commentId <= 0) {
echo json_encode(['error' => 'Ungültige Kommentar-ID']);
exit;
}
// Kommentar auslesen
$query = "SELECT author_id FROM knowledgecenter_post_comments WHERE id = $commentId LIMIT 1";
$result = mysqli_query($GLOBALS['mysql_con'], $query);
if (!$result || mysqli_num_rows($result) == 0) {
echo json_encode(['error' => 'Kommentar nicht gefunden.']);
exit;
}
$row = mysqli_fetch_assoc($result);
$commentAuthorId = (int) $row['author_id'];
// Den aktuell angemeldeten Benutzer ermitteln (Sicherstellen, dass $GLOBALS['main_contact']['id'] gesetzt ist)
$currentUserId = (int) $_POST['main_contact_id'];
// Nur eigene Kommentare löschen
if ($commentAuthorId !== $currentUserId) {
echo json_encode(['error' => 'Sie dürfen diesen Kommentar nicht löschen.']);
exit;
}
// Kommentar löschen
$deleteQuery = "DELETE FROM knowledgecenter_post_comments WHERE id = $commentId";
if (mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['error' => 'Fehler beim Löschen: ' . mysqli_error($GLOBALS['mysql_con'])]);
}
exit;
}
/**
* Löscht alle bestehenden Kategorie-Zuordnungen in der Tabelle knowledgecenter_category_post
* für den gegebenen Post und fügt dann für jede ausgewählte Kategorie einen neuen Eintrag ein.
*/
function update_post_categories()
{
// post_id und die Kategorien-IDs auslesen (als JSON-codiertes Array)
$postId = (int) ($_POST['post_id'] ?? 0);
$categoriesJson = $_POST['categories'] ?? '[]';
$categories = json_decode($categoriesJson, true);
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Beitrags-ID']);
exit;
}
if (!is_array($categories)) {
echo json_encode(['error' => 'Fehlerhafte Kategorien-Daten']);
exit;
}
// Bestehende Einträge für diesen Beitrag löschen
$deleteQuery = "DELETE FROM knowledgecenter_category_post WHERE post_id = $postId";
if (!mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
echo json_encode(['error' => 'Fehler beim Löschen alter Einträge: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
// Für jede ausgewählte Kategorie einen neuen Eintrag einfügen
foreach ($categories as $catId) {
$catId = (int) $catId;
if ($catId > 0) {
$insertQuery = "INSERT INTO knowledgecenter_category_post (post_id, category_id) VALUES ($postId, $catId)";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
echo json_encode(['error' => "Fehler beim Einfügen der Kategorie $catId: " . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
}
}
echo json_encode(['success' => true]);
exit;
}
function get_post_list()
{
$languageId = isset($_POST['language_id']) ? $_POST['language_id'] : '1';
$languageCode = isset($_POST['language_code']) ? $_POST['language_code'] : 'de';
$categoryId = isset($_POST['category_id'])
? (int) $_POST['category_id']
: 0;
// Prüfe, ob ein redirectURL-Parameter übergeben wurde, sonst verwende $_SERVER['REQUEST_URI']
$passedRedirectURL = isset($_POST['redirectURL']) ? $_POST['redirectURL'] : $_SERVER['REQUEST_URI'];
$redirectUrl = urlencode($passedRedirectURL);
// Lese den Suchbegriff aus (optional)
$search = '';
if (isset($_POST['query'])) {
$search = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['query']);
}
// Erstelle einen zusätzlichen Filter wenn ein Suchbegriff vorhanden ist, filtere nach Titel ODER Kategorie
if ($search) {
$searchCondition = "
AND (
pv.title LIKE '%$search%'
OR EXISTS (
SELECT 1
FROM knowledgecenter_category_post AS cp
JOIN knowledgecenter_category_translations AS t
ON cp.category_id = t.category_id
AND t.language_id = $languageId
WHERE cp.post_id = kp.id
AND t.title LIKE '%$search%'
)
OR EXISTS (
SELECT 1
FROM knowledgecenter_post_versions AS kpv2
JOIN main_contact AS sc
ON kpv2.author_id = sc.id
WHERE kpv2.post_id = kp.id
AND sc.name LIKE '%$search%'
)
)";
} else {
$searchCondition = "";
}
if ($categoryId) {
// nur Posts, die in dieser Kategorie sind
$categoryCondition = "
AND EXISTS (
SELECT 1
FROM knowledgecenter_category_post AS cp
WHERE cp.post_id = kp.id
AND cp.category_id = $categoryId
)";
} else {
$categoryCondition = "";
}
// Beiträge abfragen (nur PostID und Titel der aktuellen Version)
$sqlPosts = "
SELECT
kp.id AS post_id,
pv.title AS current_title
FROM knowledgecenter_posts AS kp
LEFT JOIN knowledgecenter_post_versions AS pv
ON kp.id = pv.post_id
AND pv.language_id = $languageId
/* … Bedingungen für gültige Version und höchste version_number … */
WHERE 1=1
$searchCondition
$categoryCondition
GROUP BY kp.id
ORDER BY kp.modified_at DESC
";
$resPosts = mysqli_query($GLOBALS['mysql_con'], $sqlPosts);
$count = ($resPosts) ? mysqli_num_rows($resPosts) : 0;
$postsHtml = '';
$fmt = new IntlDateFormatter('de_DE', IntlDateFormatter::LONG, IntlDateFormatter::SHORT);
if ($resPosts && mysqli_num_rows($resPosts) > 0) {
$postsHtml .= "<div class='posts-container'>";
while ($row = mysqli_fetch_assoc($resPosts)) {
$postId = $row['post_id'];
$postTitle = htmlspecialchars($row['current_title'] ?: 'Kein Titel');
// Kategorien holen
$catTitles = [];
$sqlCat = "
SELECT t.title
FROM knowledgecenter_category_post AS cp
JOIN knowledgecenter_category_translations AS t
ON cp.category_id = t.category_id
AND t.language_id = $languageId
WHERE cp.post_id = $postId
";
$resCat = mysqli_query($GLOBALS['mysql_con'], $sqlCat);
if ($resCat) {
while ($c = mysqli_fetch_assoc($resCat)) {
$catTitles[] = htmlspecialchars($c['title']);
}
}
// URL zum Post
$postUrl = "?action=Post&id={$postId}&language=" . urlencode($languageCode)
. "&redirectURL={$redirectUrl}";
// 1) Post-Überschrift & klickbare Container-Div
$postsHtml .= "<div class='post-item' style='cursor:pointer;' onclick=\"window.location.href='{$postUrl}'\">";
// Titel + Kategorien
$postsHtml .= "<div class='post-item-title'>{$postTitle}";
if (!empty($catTitles)) {
$postsHtml .= "<div class='category-list'>";
foreach ($catTitles as $catName) {
$postsHtml .= "<small class='category-item'>{$catName}</small>";
}
$postsHtml .= "</div>";
}
$postsHtml .= "</div>"; // Ende post-item-title
// 2) Versionen-Liste
$sqlVers = "
SELECT
kpv.version_number,
kpv.title,
kpv.modified_at,
kpv.state,
sc.name AS author
FROM knowledgecenter_post_versions AS kpv
LEFT JOIN main_contact AS sc
ON kpv.author_id = sc.id
WHERE kpv.post_id = $postId
AND kpv.language_id = $languageId
ORDER BY kpv.version_number DESC
";
$resVers = mysqli_query($GLOBALS['mysql_con'], $sqlVers);
if ($resVers && mysqli_num_rows($resVers) > 0) {
$postsHtml .= "<ul class='version-list'>";
while ($v = mysqli_fetch_assoc($resVers)) {
$isActive = ((int) $v['state'] === 1);
$statusClass = $isActive ? 'active' : 'inactive';
$verState = " <span class='status {$statusClass}'></span>";
$verNum = (int) $v['version_number'];
$verTitle = htmlspecialchars($v['title'] ?: ' kein Titel ');
$verDate = $v['modified_at']
? $fmt->format(strtotime($v['modified_at']))
: '-';
$author = htmlspecialchars($v['author'] ?: 'Unbekannt');
$postsHtml .=
"<li>
<div>
{$verState} Version {$verNum}: {$verTitle}
</div>
<small>geändert am {$verDate} von {$author}</small>
</li>";
}
$postsHtml .= "</ul>";
} else {
$postsHtml .= "<p><em>Keine Versionen gefunden.</em></p>";
}
$postsHtml .= "</div>"; // Ende .post-item
}
$postsHtml .= "</div>"; // Ende .posts-container
} else {
$postsHtml = '<p>Keine Beiträge gefunden.</p>';
}
echo json_encode([
'success' => true,
'html' => $postsHtml,
'count' => (int)$count
]);
exit;
}
/**
* Löscht alte Mandanten-Verknüpfungen für einen Beitrag und fügt neue ein.
*/
function update_post_mandants($postId, $mandantIds)
{
// Alte Verknüpfungen löschen
$deleteQuery = "DELETE FROM knowledgecenter_post_mandant WHERE post_id = $postId";
if (!mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
return "Fehler beim Löschen der Post-Mandanten-Verknüpfungen: " . mysqli_error($GLOBALS['mysql_con']);
}
// Neue Verknüpfungen einfügen
foreach ($mandantIds as $mandantId) {
$mandantId = (int) $mandantId;
$insertQuery = "INSERT INTO knowledgecenter_post_mandant (post_id, mandant_id, created_at)
VALUES ($postId, $mandantId, NOW())";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
return "Fehler beim Einfügen der Mandant-ID $mandantId: " . mysqli_error($GLOBALS['mysql_con']);
}
}
return true;
}
/**
* Löscht alte Department-Verknüpfungen für einen Beitrag und fügt neue ein.
*/
function update_post_departments($postId, $departmentIds)
{
$deleteQuery = "DELETE FROM knowledgecenter_post_department WHERE post_id = $postId";
if (!mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
return "Fehler beim Löschen der Post-Department-Verknüpfungen: " . mysqli_error($GLOBALS['mysql_con']);
}
foreach ($departmentIds as $departmentId) {
$departmentId = (int) $departmentId;
$insertQuery = "INSERT INTO knowledgecenter_post_department (post_id, department_id, created_at)
VALUES ($postId, $departmentId, NOW())";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
return "Fehler beim Einfügen der Department-ID $departmentId: " . mysqli_error($GLOBALS['mysql_con']);
}
}
return true;
}
/**
* Löscht alte Rollen-Verknüpfungen für einen Beitrag und fügt neue ein.
*/
function update_post_roles($postId, $roleIds)
{
$deleteQuery = "DELETE FROM knowledgecenter_post_role WHERE post_id = $postId";
if (!mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
return "Fehler beim Löschen der Post-Rollen-Verknüpfungen: " . mysqli_error($GLOBALS['mysql_con']);
}
foreach ($roleIds as $roleId) {
$roleId = (int) $roleId;
$insertQuery = "INSERT INTO knowledgecenter_post_role (post_id, role_id, created_at)
VALUES ($postId, $roleId, NOW())";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
return "Fehler beim Einfügen der Rollen-ID $roleId: " . mysqli_error($GLOBALS['mysql_con']);
}
}
return true;
}
/**
* Löscht alte Einrichtungs-Verknüpfungen für einen Beitrag und fügt neue ein.
*/
function update_post_einrichts($postId, $einrichtIds)
{
$deleteQuery = "DELETE FROM knowledgecenter_post_einricht WHERE post_id = $postId";
if (!mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
return "Fehler beim Löschen der Post-Einrichtungs-Verknüpfungen: " . mysqli_error($GLOBALS['mysql_con']);
}
foreach ($einrichtIds as $einrichtId) {
$einrichtId = (int) $einrichtId;
$insertQuery = "INSERT INTO knowledgecenter_post_einricht (post_id, einricht_id, created_at)
VALUES ($postId, $einrichtId, NOW())";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
return "Fehler beim Einfügen der Einrichtungs-ID $einrichtId: " . mysqli_error($GLOBALS['mysql_con']);
}
}
return true;
}
/**
* Löscht alte Fachbereich-Verknüpfungen für einen Beitrag und fügt neue ein.
*/
function update_post_fachbereiche($postId, $fachbereichIds)
{
$deleteQuery = "DELETE FROM knowledgecenter_post_fachbereich WHERE post_id = $postId";
if (!mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
return "Fehler beim Löschen der Post-Fachbereich-Verknüpfungen: " . mysqli_error($GLOBALS['mysql_con']);
}
foreach ($fachbereichIds as $fachbereichId) {
$fachbereichId = (int) $fachbereichId;
$insertQuery = "INSERT INTO knowledgecenter_post_fachbereich (post_id, fachbereich_id, created_at)
VALUES ($postId, $fachbereichId, NOW())";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
return "Fehler beim Einfügen der Fachbereich-ID $fachbereichId: " . mysqli_error($GLOBALS['mysql_con']);
}
}
return true;
}
/**
* Löscht alte Produkt-Verknüpfungen und fügt neue in product_knowledgecenter_post_link ein.
*/
function update_post_products($postId, $productIds)
{
// Alte Verknüpfungen löschen
$deleteQuery = "DELETE FROM product_knowledgecenter_post_link WHERE post_id = $postId";
if (!mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
return "Fehler beim Löschen der Produkt-Verknüpfungen: " . mysqli_error($GLOBALS['mysql_con']);
}
// Neue Verknüpfungen einfügen
foreach ($productIds as $productId) {
$productId = (int) $productId;
if ($productId > 0) {
$insertQuery = "
INSERT INTO product_knowledgecenter_post_link
(product_id, post_id)
VALUES
($productId, $postId)
";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
return "Fehler beim Einfügen der Produkt-ID $productId: " . mysqli_error($GLOBALS['mysql_con']);
}
}
}
return true;
}
function save_post_files()
{
$postId = (int) ($_POST['post_id'] ?? 0);
$filesStr = trim($_POST['post_files'] ?? '');
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Post-ID']);
exit;
}
if (!ajax_can_edit_post($postId)) {
echo json_encode(['error' => 'Keine Berechtigung']);
exit;
}
if (!empty($filesStr)) {
// Zerlege den String in einzelne Pfade (getrennt durch Komma und optional Leerzeichen)
$files = preg_split('/,\s*/', $filesStr);
$errors = [];
foreach ($files as $file) {
$file = trim($file);
if (empty($file)) {
continue;
}
// Setze die Beschreibung standardmäßig auf den Dateinamen,
// der letzte Teil des Pfades (mithilfe von basename)
$description = basename($file);
// Füge "/userdata/" vor dem Dateipfad hinzu
// Mit ltrim entfernen wir einen eventuell vorhandenen führenden Slash
$fileWithPrefix = '/userdata/' . ltrim($file, '/');
// Pfad sicher escapen
$escapedFile = mysqli_real_escape_string($GLOBALS['mysql_con'], $fileWithPrefix);
$query = "INSERT INTO knowledgecenter_post_files (post_id, description, path, created_at)
VALUES ($postId, '$description', '$escapedFile', NOW())";
if (!mysqli_query($GLOBALS['mysql_con'], $query)) {
$errors[] = mysqli_error($GLOBALS['mysql_con']);
}
}
if (!empty($errors)) {
echo json_encode(['error' => implode("; ", $errors)]);
exit;
}
}
echo json_encode(['success' => true]);
exit;
}
function get_post_files()
{
$postId = (int) ($_POST['post_id'] ?? 0);
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Post-ID']);
exit;
}
$query = "SELECT id, description, path FROM knowledgecenter_post_files WHERE post_id = $postId ORDER BY created_at ASC";
$result = mysqli_query($GLOBALS['mysql_con'], $query);
if (!$result) {
echo json_encode(['error' => 'Datenbankfehler: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
$files = [];
while ($row = mysqli_fetch_assoc($result)) {
$files[] = $row;
}
echo json_encode(['success' => true, 'files' => $files]);
exit;
}
function delete_post_file()
{
$fileId = (int) ($_POST['file_id'] ?? 0);
if ($fileId <= 0) {
echo json_encode(['error' => 'Ungültige Datei-ID']);
exit;
}
$selectQuery = "SELECT path, post_id FROM knowledgecenter_post_files WHERE id = $fileId LIMIT 1";
$selectResult = mysqli_query($GLOBALS['mysql_con'], $selectQuery);
if (!$selectResult || mysqli_num_rows($selectResult) === 0) {
echo json_encode(['error' => 'Datei-Eintrag nicht gefunden']);
exit;
}
$row = mysqli_fetch_assoc($selectResult);
$storedPath = trim((string) ($row['path'] ?? ''));
if (!ajax_can_edit_post((int)($row['post_id'] ?? 0))) {
echo json_encode(['error' => 'Keine Berechtigung']);
exit;
}
$userdataRoot = realpath($_SERVER['DOCUMENT_ROOT'] . '/userdata');
if ($userdataRoot === false) {
echo json_encode(['error' => 'userdata-Verzeichnis nicht gefunden']);
exit;
}
$relativePath = ltrim(str_replace('\\', '/', $storedPath), '/');
if (strpos($relativePath, 'userdata/') === 0) {
$relativePath = substr($relativePath, strlen('userdata/'));
}
if ($relativePath === '' || strpos($relativePath, '..') !== false) {
echo json_encode(['error' => 'Ungültiger Dateipfad']);
exit;
}
$absolutePath = $userdataRoot . '/' . $relativePath;
$absoluteDir = realpath(dirname($absolutePath));
if ($absoluteDir === false || strpos($absoluteDir, $userdataRoot) !== 0) {
echo json_encode(['error' => 'Ungültiges Zielverzeichnis']);
exit;
}
if (is_file($absolutePath)) {
if (!@unlink($absolutePath)) {
echo json_encode(['error' => 'Datei konnte nicht vom Server gelöscht werden']);
exit;
}
}
$deleteQuery = "DELETE FROM knowledgecenter_post_files WHERE id = $fileId";
if (mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['error' => 'Fehler beim Löschen des Datenbankeintrags: ' . mysqli_error($GLOBALS['mysql_con'])]);
}
exit;
}
/**
* Aktualisiert die Kontakte (Ansprechpartner) eines Beitrags in der Tabelle knowledgecenter_post_contacts.
*/
function update_post_contacts()
{
// Hole die Post-ID und dekodiere den JSON-String für Ansprechpartner
$postId = (int) ($_POST['post_id'] ?? 0);
$contactIds = json_decode($_POST['contacts'] ?? '[]', true) ?: [];
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Beitrags-ID']);
exit;
}
// Lösche alle bestehenden Kontakte für diesen Beitrag
$deleteQuery = "DELETE FROM knowledgecenter_post_contacts WHERE post_id = $postId";
if (!mysqli_query($GLOBALS['mysql_con'], $deleteQuery)) {
echo json_encode(['error' => 'Fehler beim Löschen bestehender Kontakte: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
$errors = [];
// Füge jeden angegebenen Ansprechpartner ein
foreach ($contactIds as $contactId) {
$contactId = (int) $contactId;
if ($contactId > 0) {
$insertQuery = "INSERT INTO knowledgecenter_post_contacts (post_id, main_contact_id, created_at)
VALUES ($postId, $contactId, NOW())";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
$errors[] = "Fehler beim Einfügen der Kontakt-ID $contactId: " . mysqli_error($GLOBALS['mysql_con']);
}
}
}
if (!empty($errors)) {
echo json_encode(['error' => implode("; ", $errors)]);
exit;
}
echo json_encode(['success' => true]);
exit;
}
/**
* Gibt als HTML-String Visitenkarten der zugeordneten Kontakte zurück.
*/
function get_contact_cards()
{
$postId = (int) ($_POST['post_id'] ?? 0);
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Beitrags-ID']);
exit;
}
// Erweiterte Abfrage: Lade zusätzlich email und image
$query = "SELECT mc.id, mc.name, mc.email, mc.image
FROM knowledgecenter_post_contacts AS pc
JOIN main_contact AS mc ON pc.main_contact_id = mc.id
WHERE pc.post_id = $postId";
$result = mysqli_query($GLOBALS['mysql_con'], $query);
if (!$result) {
echo json_encode(['error' => 'Datenbankfehler: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
$html = '';
// Erstelle die HTML-Struktur für jeden Kontakt
while ($row = mysqli_fetch_assoc($result)) {
// Link zum Profil passe den Pfad an, falls nötig
$profileLink = "/intranet/de/profil/?contact=" . $row['id'];
// Falls kein Bild vorhanden, verwende einen Platzhalter
$image = !empty($row['image']) ? '/userdata/intranet/contact/' . $row['image'] : '/userdata/intranet/contact/default_avatar.webp';
$name = htmlspecialchars($row['name']);
$email = htmlspecialchars($row['email']);
$html .= '<a href="' . $profileLink . '" class="contact-card">';
$html .= ' <div class="contact-image">';
$html .= ' <img src="' . $image . '" alt="Profilbild von ' . $name . '">';
$html .= ' </div>';
$html .= ' <div class="contact-details">';
$html .= ' <h3>' . $name . '</h3>';
$html .= ' <p>' . $email . '</p>';
$html .= ' </div>';
$html .= '</a>';
}
echo json_encode(['success' => true, 'html' => $html]);
exit;
}
function search_posts()
{
// Parameter-Verarbeitung
$languageCode = $_POST['language_code'] ?? 'de';
$languageId = $_POST['language'] ?? '1';
$queryText = trim($_POST['query'] ?? '');
$page = isset($_POST['page']) ? (int) $_POST['page'] : 1;
$limit = 50;
$offset = ($page - 1) * $limit;
$limitPlusOne = $limit + 1; // um zusätzlich eine Zeile zu laden, falls mehr Ergebnisse existieren
/**
* Zunächst: Holen der User-Daten (Mandanten, Abteilungen, Rollen, Einrichtungen, Fachbereiche)
*/
$currentUserId = $_POST["main_contact_id"] ?? 0;
$userMandants = [];
$userDepartments = [];
$userRoles = [];
$userEinrichts = [];
$userFachbereiche = [];
if ($currentUserId) {
$query = "SELECT main_mandant_id, main_department_id, main_role_id, main_einricht_id, main_bereich_id
FROM main_contact_department
WHERE main_contact_id = $currentUserId";
$result = mysqli_query($GLOBALS['mysql_con'], $query);
while ($row = mysqli_fetch_assoc($result)) {
// Filtere 0 aus 0 gilt als "keine Zuordnung"
if ($row['main_mandant_id'] != 0 && !in_array($row['main_mandant_id'], $userMandants)) {
$userMandants[] = $row['main_mandant_id'];
}
if ($row['main_department_id'] != 0 && !in_array($row['main_department_id'], $userDepartments)) {
$userDepartments[] = $row['main_department_id'];
}
if ($row['main_role_id'] != 0 && !in_array($row['main_role_id'], $userRoles)) {
$userRoles[] = $row['main_role_id'];
}
if ($row['main_einricht_id'] != 0 && !in_array($row['main_einricht_id'], $userEinrichts)) {
$userEinrichts[] = $row['main_einricht_id'];
}
if ($row['main_bereich_id'] != 0 && !in_array($row['main_bereich_id'], $userFachbereiche)) {
$userFachbereiche[] = $row['main_bereich_id'];
}
}
}
/**
* Für den LEFT JOIN-Ansatz bereiten wir die erlaubten Werte als
* kommaseparierte Strings vor.
*/
$allowedMandants = !empty($userMandants) ? implode(',', $userMandants) : 'NULL';
$allowedDepartments = !empty($userDepartments) ? implode(',', $userDepartments) : 'NULL';
$allowedRoles = !empty($userRoles) ? implode(',', $userRoles) : 'NULL';
$allowedEinrichts = !empty($userEinrichts) ? implode(',', $userEinrichts) : 'NULL';
$allowedFachbereiche = !empty($userFachbereiche) ? implode(',', $userFachbereiche) : 'NULL';
// Suchbedingung: Auf title, teaser, content
$safeQuery = mysqli_real_escape_string($GLOBALS['mysql_con'], $queryText);
$searchCondition = "";
if ($safeQuery) {
$searchCondition = "AND (
pv.title LIKE '%$safeQuery%' OR
pv.teaser LIKE '%$safeQuery%' OR
pv.content LIKE '%$safeQuery%'
)";
}
// Hier bauen wir den Query mithilfe von LEFT JOINs:
// Wir joinen zunächst die Tabelle der Posts mit den Versionen (wie gehabt).
// Anschließend werden die Filtertabellen (Abteilung, Mandant, Rolle) via LEFT JOIN angehängt.
// In der WHERE-Klausel stellen wir sicher, dass in jedem dieser Bereiche entweder kein Datensatz
// vorliegt (NULL) oder der Wert in den erlaubten Werten enthalten ist.
$sql = "
SELECT DISTINCT
kp.id AS post_id,
kp.modified_at,
pv.title,
pv.image,
pv.teaser,
pv.content,
(
SELECT GROUP_CONCAT(mc.name SEPARATOR ', ')
FROM knowledgecenter_post_contacts AS pc
JOIN main_contact AS mc ON mc.id = pc.main_contact_id
WHERE pc.post_id = kp.id
) AS contacts,
(
SELECT GROUP_CONCAT(t.title SEPARATOR ', ')
FROM knowledgecenter_category_post AS cp
JOIN knowledgecenter_category_translations AS t
ON cp.category_id = t.category_id
WHERE cp.post_id = kp.id AND t.language_id = $languageId
) AS categories
FROM knowledgecenter_posts AS kp
LEFT JOIN knowledgecenter_post_versions AS pv
ON kp.id = pv.post_id
AND pv.language_id = $languageId
AND pv.state = 1
AND (pv.valid_from IS NULL OR pv.valid_from <= NOW())
AND (pv.valid_until IS NULL OR pv.valid_until >= NOW())
AND pv.version_number = (
SELECT MAX(version_number)
FROM knowledgecenter_post_versions
WHERE post_id = kp.id
AND language_id = $languageId
AND (valid_from IS NULL OR valid_from <= NOW())
AND (valid_until IS NULL OR valid_until >= NOW())
)
LEFT JOIN knowledgecenter_post_department AS pd
ON kp.id = pd.post_id
LEFT JOIN knowledgecenter_post_mandant AS pm
ON kp.id = pm.post_id
LEFT JOIN knowledgecenter_post_role AS pr
ON kp.id = pr.post_id
LEFT JOIN knowledgecenter_post_einricht AS pe
ON kp.id = pe.post_id
LEFT JOIN knowledgecenter_post_fachbereich AS pfb
ON kp.id = pfb.post_id
WHERE 1=1
$searchCondition
AND ( pd.department_id IS NULL OR pd.department_id IN ($allowedDepartments) )
AND ( pm.mandant_id IS NULL OR pm.mandant_id IN ($allowedMandants) )
AND ( pr.role_id IS NULL OR pr.role_id IN ($allowedRoles) )
AND ( pe.einricht_id IS NULL OR pe.einricht_id IN ($allowedEinrichts) )
AND ( pfb.fachbereich_id IS NULL OR pfb.fachbereich_id IN ($allowedFachbereiche) )
ORDER BY kp.modified_at DESC
LIMIT $offset, $limitPlusOne
";
$result = mysqli_query($GLOBALS['mysql_con'], $sql);
$html = '';
// Ergebnisse in ein Array übernehmen
$rows = [];
if ($result && mysqli_num_rows($result) > 0) {
while ($row = mysqli_fetch_assoc($result)) {
$rows[] = $row;
}
}
// Wenn mehr als $limit Ergebnisse gefunden wurden, dann gibt es weitere Seiten.
$hasMore = (count($rows) > $limit);
if ($hasMore) {
array_pop($rows); // letztes Element entfernen
}
// Ergebnisse rendern
if (count($rows) > 0) {
$fmt = new IntlDateFormatter('de_DE', IntlDateFormatter::LONG, IntlDateFormatter::SHORT);
$highlight = function ($text, $query) {
if (!$query)
return htmlspecialchars($text);
return preg_replace_callback(
"/" . preg_quote($query, "/") . "/i",
function ($match) {
return "<mark>{$match[0]}</mark>";
},
htmlspecialchars($text)
);
};
$html .= '<ul class="search-list">';
foreach ($rows as $row) {
$title = $highlight($row['title'] ?: 'Kein Titel', $queryText);
$teaser = $highlight($row['teaser'], $queryText);
$content = strip_tags($row['content']);
if (!$teaser && $queryText) {
if (stripos($content, $queryText) !== false) {
$pos = stripos($content, $queryText);
$snippet = substr($content, max(0, $pos - 30), 100);
$teaser = $highlight("{$snippet}", $queryText);
}
}
$img = !empty($row['image']) ? '/userdata/' . htmlspecialchars($row['image']) : '/userdata/knowledgecenter_update/platzhalter.jpg';
$link = "?action=Post&id={$row['post_id']}&language={$languageCode}";
// Kontakte
$contacts = $row['contacts'] ?? '';
$personSVG = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person-circle" viewBox="0 0 16 16">
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0"/>
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1"/>
</svg>';
$contactsHtml = $contacts ? "<small class='contacts'>$personSVG " . htmlspecialchars($contacts) . "</small><br>" : "";
// Kategorien als einzelne kleine Elemente in einem div
$categoriesHtml = "";
if (!empty($row['categories'])) {
// Den zurückgegebenen String in ein Array aufspalten
$catArray = explode(', ', $row['categories']);
$categoriesHtml .= "<div class='categories'>";
foreach ($catArray as $cat) {
$categoriesHtml .= "<small class='category-item'>" . htmlspecialchars($cat) . "</small> ";
}
$categoriesHtml .= "</div>";
}
$html .= "<li class='search-item'>
<a href='{$link}'>
<img src='{$img}' alt='Bild'>
<div>
<strong>{$title}</strong><br>
{$contactsHtml}
<small>{$teaser}</small><br>
{$categoriesHtml}
</div>
</a>
</li>";
}
$html .= '</ul>';
if ($hasMore) {
$nextPage = $page + 1;
$html .= "<button id='loadMoreBtn' data-page='{$nextPage}'>Weitere Ergebnisse laden</button>";
}
} else {
$html = '<p>Keine passenden Beiträge gefunden.</p>';
}
echo json_encode([
'success' => true,
'html' => $html
]);
exit;
}
/**
* Aktualisiert in der Tabelle main_certificate für ein gegebenes Zertifikat den zugeordneten post_id-Wert.
* Falls kein Zertifikat ausgewählt wurde (certificate_id leer oder 0), wird die Zuordnung entfernt,
* indem alle Zertifikate, die aktuell diesen Beitrag zugeordnet haben, auf NULL gesetzt werden.
*
* Erwartete POST-Parameter:
* - post_id: Die ID des Beitrags, der dem Zertifikat zugewiesen werden soll.
* - certificate_id: Die ID des Zertifikats, dessen post_id gesetzt werden soll oder leer/0, um die Zuordnung zu entfernen.
*
* @return void JSON-Antwort mit Erfolgsmeldung oder Fehler
*/
function update_post_certificate()
{
// Beitrags-ID extrahieren und in Integer umwandeln
$postId = (int) ($_POST['post_id'] ?? 0);
// certificate_id auswerten: Falls ein leerer Wert oder 0, gilt dies als "kein Zertifikat ausgewählt"
$certificateId = isset($_POST['certificate_id']) && $_POST['certificate_id'] !== "" ? (int) ($_POST['certificate_id']) : 0;
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Beitrags-ID']);
exit;
}
// Falls eine gültige certificate_id übergeben wurde (größer als 0)
if ($certificateId > 0) {
// Dem Zertifikat wird der Beitrag zugeordnet
$query = "UPDATE main_certificate SET post_id = $postId WHERE id = $certificateId";
if (mysqli_query($GLOBALS['mysql_con'], $query)) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['error' => 'Fehler beim Aktualisieren des Zertifikats: ' . mysqli_error($GLOBALS['mysql_con'])]);
}
} else {
// Kein Zertifikat ausgewählt entferne die Zuordnung zu diesem Beitrag
$query = "UPDATE main_certificate SET post_id = NULL WHERE post_id = $postId";
if (mysqli_query($GLOBALS['mysql_con'], $query)) {
echo json_encode(['success' => true, 'message' => 'Zertifikats-Zuordnung entfernt']);
} else {
echo json_encode(['error' => 'Fehler beim Entfernen der Zertifikats-Zuordnung: ' . mysqli_error($GLOBALS['mysql_con'])]);
}
}
exit;
}
function update_post_pdf_inline()
{
$postId = (int) ($_POST['post_id'] ?? 0);
// Wenn der Parameter pdf_inline im POST gesetzt ist, wird der Wert verwendet, ansonsten 0
$pdfInline = isset($_POST['pdf_inline']) ? (int) $_POST['pdf_inline'] : 0;
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Beitrags-ID']);
exit;
}
$query = "UPDATE knowledgecenter_posts SET preview_inline = $pdfInline WHERE id = $postId";
if (mysqli_query($GLOBALS['mysql_con'], $query)) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['error' => 'Fehler beim Aktualisieren der PDF Inline Vorschau: ' . mysqli_error($GLOBALS['mysql_con'])]);
}
exit;
}
function update_post_colors()
{
$postId = (int) ($_POST['post_id'] ?? 0);
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Beitrags-ID']);
exit;
}
$backgroundColor = normalize_hex_color($_POST['background_color'] ?? '#FFFFFF', '#FFFFFF');
$textColor = normalize_hex_color($_POST['text_color'] ?? '#000000', '#000000');
$query = "UPDATE knowledgecenter_posts
SET background_color = '$backgroundColor',
text_color = '$textColor',
modified_at = NOW()
WHERE id = $postId";
if (mysqli_query($GLOBALS['mysql_con'], $query)) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['error' => 'Fehler beim Aktualisieren der Post-Farben: ' . mysqli_error($GLOBALS['mysql_con'])]);
}
exit;
}
/**
* Löscht einen kompletten Beitrag (inkl. all seiner Versionen).
*/
function delete_post()
{
$postId = isset($_POST['post_id']) ? (int) $_POST['post_id'] : 0;
if ($postId <= 0) {
echo json_encode(['error' => 'Ungültige Beitrags-ID']);
exit;
}
// 2) Haupt-Datensatz löschen
$delPostQ = "DELETE FROM knowledgecenter_posts WHERE id = $postId";
if (!mysqli_query($GLOBALS['mysql_con'], $delPostQ)) {
echo json_encode(['error' => 'Fehler beim Löschen des Beitrags: ' . mysqli_error($GLOBALS['mysql_con'])]);
exit;
}
$deleteFolderResult = delete_post_gallery_dir($postId);
if (!$deleteFolderResult['success']) {
echo json_encode(['error' => 'Beitrag gelöscht, aber Ordner konnte nicht gelöscht werden: ' . $deleteFolderResult['error']]);
exit;
}
echo json_encode(['success' => true]);
exit;
}
?>
<!-- <script src="/plugins/fslightbox/fslightbox.js">
</script> -->