Files
awo-hamburg-intranet/module/knowledgecenter_update/Ajax/Ajax.php

2256 lines
89 KiB
PHP
Raw Normal View History

<?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;
}
// Prüft ob eine Kategorie (oder ein Vorfahre) betriebsrat_editable=1 hat.
function ajax_is_category_editable_for_br(int $categoryId): bool
{
$visited = [];
$queue = [$categoryId];
while (!empty($queue)) {
$current = array_shift($queue);
if (isset($visited[$current])) {
continue;
}
$visited[$current] = true;
$res = mysqli_query($GLOBALS['mysql_con'],
"SELECT betriebsrat_editable FROM knowledgecenter_categories_update WHERE id = $current LIMIT 1");
$row = mysqli_fetch_assoc($res);
if ($row && (int)$row['betriebsrat_editable'] === 1) {
return true;
}
$pRes = mysqli_query($GLOBALS['mysql_con'],
"SELECT parent_category_id FROM knowledgecenter_category_links WHERE child_category_id = $current");
while ($pRow = mysqli_fetch_assoc($pRes)) {
$pid = (int)$pRow['parent_category_id'];
if (!isset($visited[$pid])) {
$queue[] = $pid;
}
}
}
return false;
}
// Betriebsrat darf einen Post nur bearbeiten, wenn er in einer freigegebenen
// Kategorie (oder Unterkategorie einer freigegebenen Kategorie) liegt.
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;
}
if (get_permission_state('r-wiki-br', $mid, $uid) != 1) {
return false;
}
// Betriebsrat: prüfe alle dem Post zugeordneten Kategorien inkl. Vorfahren
$res = mysqli_query($GLOBALS['mysql_con'],
"SELECT category_id FROM knowledgecenter_category_post WHERE post_id = $postId");
while ($row = mysqli_fetch_assoc($res)) {
if (ajax_is_category_editable_for_br((int)$row['category_id'])) {
return true;
}
}
return false;
}
// 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;
}
if (!ajax_has_wiki_edit()) {
echo json_encode(['error' => 'Keine Berechtigung']);
exit;
}
// Betriebsrat darf bestehende Posts nur bearbeiten wenn der Post in einer freigegebenen Kategorie liegt.
// Neue Drafts haben noch keine Kategorien → Prüfung überspringen (Zugang wurde schon bei create_post_draft gesichert).
if ($postId > 0) {
$catCountRes = mysqli_query($GLOBALS['mysql_con'],
"SELECT COUNT(*) AS cnt FROM knowledgecenter_category_post WHERE post_id = $postId");
$catCountRow = mysqli_fetch_assoc($catCountRes);
if ((int)$catCountRow['cnt'] > 0 && !ajax_can_edit_post($postId)) {
echo json_encode(['error' => 'Keine Berechtigung für diesen Beitrag']);
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()
{
if (!ajax_has_wiki_edit()) {
echo json_encode(['error' => 'Keine Berechtigung']);
exit;
}
$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;
}
$mid = $GLOBALS['main_contact']['master_mandant_id'] ?? 0;
$uid = $GLOBALS['main_contact']['id'] ?? 0;
$isRedakteur = get_permission_state('r-wiki', $mid, $uid) == 1;
$isBetriebsrat = get_permission_state('r-wiki-br', $mid, $uid) == 1;
if (!$isRedakteur && !$isBetriebsrat) {
echo json_encode(['error' => 'Keine Berechtigung']);
exit;
}
// Betriebsrat darf nur Kategorien zuweisen, die für ihn freigegeben sind (inkl. Vererbung)
if (!$isRedakteur && $isBetriebsrat) {
foreach ($categories as $catId) {
$catId = (int)$catId;
if ($catId > 0 && !ajax_is_category_editable_for_br($catId)) {
echo json_encode(['error' => "Kategorie $catId ist nicht für den Betriebsrat freigegeben"]);
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> -->