Compare commits

...

3 Commits

Author SHA1 Message Date
833c3718cf Add summary field to knowledgecenter posts
- ALTER TABLE knowledgecenter_post_versions ADD COLUMN summary TEXT NULL AFTER content
- Add summary textarea to post edit form (after Quill editor)
- Include summary in insert_post_version() and update_post_version()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 11:59:31 +02:00
952b3e2efc Remove topic table query from post_cardform.php
The forum module (topic table) does not exist. Remove the comment count
query to prevent a fatal crash when loading a post detail page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 11:52:42 +02:00
887ac6a4bb Fix post save and file gallery for knowledgecenter
- Create module/shared_components/quill/ with toolbar_presets.js,
  quill_resize.js and quill_resize.css — missing files caused a JS crash
  in $(document).ready() before the save button's click handler was
  registered, so clicking save did nothing
- Fix filesgallery.php session detection to search any sid* cookie
  dynamically (site session name is 'sidawo', not the hardcoded
  'sidintranet'), fixing the popup showing the homepage instead of
  the gallery
- Revert categories_cardform.php from 3-tab layout back to original
  sidebar layout with full-width permissions section below

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 11:49:59 +02:00
8 changed files with 416 additions and 260 deletions

View File

@@ -707,6 +707,7 @@ function update_post_version()
$image = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['image'][$langId] ?? ''); $image = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['image'][$langId] ?? '');
$teaser = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['teaser'][$langId] ?? ''); $teaser = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['teaser'][$langId] ?? '');
$content = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['content'][$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; $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 = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['link'][$langId] ?? '');
$link_label = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['link_label'][$langId] ?? ''); $link_label = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['link_label'][$langId] ?? '');
@@ -752,11 +753,11 @@ function update_post_version()
$insertQuery = " $insertQuery = "
INSERT INTO knowledgecenter_post_versions INSERT INTO knowledgecenter_post_versions
(post_id, language_id, version_number, (post_id, language_id, version_number,
title, image, link, link_label, teaser, content, author_id, state, title, image, link, link_label, teaser, content, summary, author_id, state,
modified_at, valid_from, valid_until, background_color, text_color) modified_at, valid_from, valid_until, background_color, text_color)
VALUES VALUES
($postId, $langId, $newVersion, ($postId, $langId, $newVersion,
'$title', '$image', '$link', '$link_label', '$teaser', '$content', $author_id, $state, '$title', '$image', '$link', '$link_label', '$teaser', '$content', '$summary', $author_id, $state,
$insertModifiedAtSql, $valid_from_sql, $valid_until_sql, '$backgroundColor', '$textColor') $insertModifiedAtSql, $valid_from_sql, $valid_until_sql, '$backgroundColor', '$textColor')
"; ";
if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) { if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
@@ -775,6 +776,7 @@ function update_post_version()
link_label = '$link_label', link_label = '$link_label',
teaser = '$teaser', teaser = '$teaser',
content = '$content', content = '$content',
summary = '$summary',
author_id = $author_id, author_id = $author_id,
state = $state, state = $state,
valid_from = $valid_from_sql, valid_from = $valid_from_sql,
@@ -855,6 +857,7 @@ function insert_post_version()
$image = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['image'][$langId] ?? ''); $image = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['image'][$langId] ?? '');
$teaser = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['teaser'][$langId] ?? ''); $teaser = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['teaser'][$langId] ?? '');
$content = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['content'][$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 = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['link'][$langId] ?? '');
$link_label = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST['link_label'][$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; $author_id = isset($_POST['author_id'][$langId]) ? (int) $_POST['author_id'][$langId] : 0;
@@ -891,8 +894,8 @@ function insert_post_version()
} }
$insertQuery = "INSERT INTO knowledgecenter_post_versions $insertQuery = "INSERT INTO knowledgecenter_post_versions
(post_id, language_id, version_number, title, image, link, link_label, teaser, content, author_id, state, modified_at, valid_from, valid_until, background_color, text_color) (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', $author_id, $state, $insertModifiedAtSql, $valid_from_sql, $valid_until_sql, '$backgroundColor', '$textColor')"; 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)) { if (!mysqli_query($GLOBALS['mysql_con'], $insertQuery)) {
echo json_encode(['error' => "Fehler beim Einfügen für Sprache $langId: " . mysqli_error($GLOBALS['mysql_con'])]); echo json_encode(['error' => "Fehler beim Einfügen für Sprache $langId: " . mysqli_error($GLOBALS['mysql_con'])]);
exit; exit;

View File

@@ -1,13 +1,23 @@
<?php <?php
// Prüft, ob ein Session-Cookie mit bekanntem Namen existiert (sidintranet, mysyde oder PHPSESSID) // Find active session cookie: try any sid* cookie first, then fallbacks.
// und übernimmt diesen Namen für die aktuelle Session. $_kc_found = false;
foreach (['sidintranet', 'mysyde', 'PHPSESSID'] as $n) { foreach (array_keys($_COOKIE) as $_kc_n) {
if (isset($_COOKIE[$n])) { if (strncmp($_kc_n, 'sid', 3) === 0) {
if (session_name() !== $n) session_name($n); if (session_name() !== $_kc_n) session_name($_kc_n);
$_kc_found = true;
break; break;
} }
} }
if (!$_kc_found) {
foreach (['mysyde', 'PHPSESSID'] as $_kc_n) {
if (isset($_COOKIE[$_kc_n])) {
if (session_name() !== $_kc_n) session_name($_kc_n);
break;
}
}
}
unset($_kc_found, $_kc_n);
// Bestimmt, ob die Verbindung sicher (HTTPS) ist, um Cookie-Parameter korrekt zu setzen. // Bestimmt, ob die Verbindung sicher (HTTPS) ist, um Cookie-Parameter korrekt zu setzen.
$isSecure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (($_SERVER['SERVER_PORT'] ?? 80) == 443); $isSecure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (($_SERVER['SERVER_PORT'] ?? 80) == 443);

View File

@@ -232,62 +232,17 @@ while ($row = mysqli_fetch_assoc($resultParents)) {
$parentCount = count($parentCategories); $parentCount = count($parentCategories);
?> ?>
<style> <style>
.cf-layout{background:#fff;border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.1);overflow:hidden;} .perm-row { margin-bottom: 16px; }
.cf-topbar{display:flex;align-items:center;gap:10px;padding:12px 16px;border-bottom:2px solid #e5e7eb;} .perm-row label { display: block; font-weight: 500; font-size: 13px; color: #374151; margin-bottom: 6px; }
.cf-topbar-back{font-size:18px;text-decoration:none;color:#374151;padding:4px 8px;border-radius:4px;} .perm-row .tagify { width: 100%; }
.cf-topbar-back:hover{background:#f3f4f6;}
.cf-section-tabs{display:flex;gap:4px;flex:1;}
.cf-section-tab{padding:6px 18px;border:1px solid #d1d5db;border-radius:6px;background:#f9fafb;cursor:pointer;font-size:13px;color:#374151;font-weight:500;line-height:1.4;}
.cf-section-tab.active{background:#3B82F6;color:#fff;border-color:#3B82F6;}
.cf-topbar-actions{display:flex;gap:6px;align-items:center;margin-left:auto;}
.cf-tab-pane{display:none;padding:20px;}
.cf-tab-pane.active{display:block;}
.cf-general-layout{display:grid;grid-template-columns:1fr 260px;gap:24px;align-items:start;}
.cf-settings-col{border-left:1px solid #e5e7eb;padding-left:20px;display:flex;flex-direction:column;gap:12px;}
.cf-settings-col label{display:flex;justify-content:space-between;align-items:center;gap:8px;font-size:14px;color:#374151;cursor:pointer;}
.cf-settings-meta{font-size:12px;color:#6b7280;margin-top:8px;}
.cf-settings-meta ul{margin:4px 0;padding-left:16px;}
.cf-settings-meta a{color:#3B82F6;}
.cf-perm-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:20px;align-items:start;}
.cf-perm-sm{grid-column:span 2;}
.cf-perm-lg{grid-column:span 3;}
.cf-perm-block h4{font-size:13px;font-weight:600;margin:0 0 8px 0;color:#374151;}
</style> </style>
<div class="cf-layout"> <div class="cardform_containern">
<!-- Top bar: navigation, section tabs, save/delete -->
<div class="cf-topbar">
<a class="cf-topbar-back" href="?action=Categories&embedded=<?= $isEmbedded ?>">←</a>
<div class="cf-section-tabs">
<button class="cf-section-tab active" data-target="cf-tab-allgemein">Allgemein</button>
<?php if ($categoryId != 0): ?>
<button class="cf-section-tab" data-target="cf-tab-berechtigungen">Berechtigungen</button>
<button class="cf-section-tab" data-target="cf-tab-unterkategorien">Unterkategorien</button>
<?php endif; ?>
</div>
<div class="cf-topbar-actions">
<button id="message" style="opacity:0; pointer-events:none; min-width:1px;"></button>
<?php if (get_permission_state('r-wiki', $GLOBALS['main_contact']['master_mandant_id'], $GLOBALS['main_contact']['id']) == 1): ?>
<button class="post-action-btn" id="saveAllTranslations" style="background:#14b814;">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-check" viewBox="0 0 16 16">
<path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425z"/>
</svg>
</button>
<button class="post-action-btn red" id="deleteCategory">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-trash3-fill" viewBox="0 0 16 16">
<path d="M11 1.5v1h3.5a.5.5 0 0 1 0 1h-.538l-.853 10.66A2 2 0 0 1 11.115 16h-6.23a2 2 0 0 1-1.994-1.84L2.038 3.5H1.5a.5.5 0 0 1 0-1H5v-1A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5m-5 0v1h4v-1a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5M4.5 5.029l.5 8.5a.5.5 0 1 0 .998-.06l-.5-8.5a.5.5 0 1 0-.998.06m6.53-.528a.5.5 0 0 0-.528.47l-.5 8.5a.5.5 0 0 0 .998.058l.5-8.5a.5.5 0 0 0-.47-.528M8 4.5a.5.5 0 0 0-.5.5v8.5a.5.5 0 0 0 1 0V5a.5.5 0 0 0-.5-.5"/>
</svg>
</button>
<?php endif; ?>
</div>
</div>
<!-- Tab: Allgemein --> <div class="cardform">
<div id="cf-tab-allgemein" class="cf-tab-pane active"> <div class="card-body">
<div class="cf-general-layout"> <div class="tabbar_with_button">
<div> <a href="?action=Categories&embedded=<?= $isEmbedded ?>">←</a>
<ul class="nav nav-tabs" id="languageTabs" role="tablist"> <ul class="nav nav-tabs" id="languageTabs" role="tablist">
<?php foreach ($languages as $index => $lang): ?> <?php foreach ($languages as $index => $lang): ?>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
@@ -298,6 +253,10 @@ $parentCount = count($parentCategories);
</li> </li>
<?php endforeach; ?> <?php endforeach; ?>
</ul> </ul>
<div>
<button id="message" style="opacity:0;"></button>
</div>
</div>
<div class="tab-content-languages" id="languageTabsContent"> <div class="tab-content-languages" id="languageTabsContent">
<?php foreach ($languages as $index => $lang): <?php foreach ($languages as $index => $lang):
$trans = $translations[$lang['code']]; $trans = $translations[$lang['code']];
@@ -323,7 +282,8 @@ $parentCount = count($parentCategories);
<input type="hidden" name="image-<?= $lang['code'] ?>" id="image-<?= $lang['code'] ?>" <input type="hidden" name="image-<?= $lang['code'] ?>" id="image-<?= $lang['code'] ?>"
value="<?= htmlspecialchars($trans['image']) ?>"> value="<?= htmlspecialchars($trans['image']) ?>">
<div class="post-action-btn green openFileGallery" data-ajax-function="saveCategoryAjax" <div class="post-action-btn green openFileGallery" data-ajax-function="saveCategoryAjax"
data-target="image-<?= $lang['code'] ?>">+</div> data-target="image-<?= $lang['code'] ?>">
+</div>
<div class="post-action-btn clearImage" data-target="image-<?= $lang['code'] ?>">✕</div> <div class="post-action-btn clearImage" data-target="image-<?= $lang['code'] ?>">✕</div>
</div> </div>
<?php endif; ?> <?php endif; ?>
@@ -331,100 +291,8 @@ $parentCount = count($parentCategories);
</div> </div>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
</div>
<!-- Settings sidebar -->
<div class="cf-settings-col">
<label for="stateCheckbox">
<?= $translation->get("active_state") ?>
<input type="checkbox" id="stateCheckbox" name="state" value="1" <?= $state === 1 ? 'checked' : '' ?>>
</label>
<?php $is_navigation_item = $translations[$languages[0]['code']]['is_navigation_item'] ?? 0; ?>
<label for="isNavigationItem">
<?= $translation->get("create_navigation_menu") ?>
<input type="checkbox" id="isNavigationItem" name="is_navigation_item" value="1"
<?= $is_navigation_item == 1 ? 'checked' : '' ?>>
</label>
<label for="color_hex">
<?= $translation->get("color_hex") ?>
<input type="color" id="color_hex" name="color_hex" value="<?= $category['color_hex'] ?? '#3B82F6FF' ?>">
</label>
<?php $enable_post_color_pickers = isset($category['enable_post_color_pickers']) ? (int) $category['enable_post_color_pickers'] : 0; ?>
<label for="enablePostColorPickersCheckbox">
<?= $translation->get("enable_post_color_pickers") ?>
<input type="checkbox" id="enablePostColorPickersCheckbox" name="enable_post_color_pickers" value="1"
<?= $enable_post_color_pickers === 1 ? 'checked' : '' ?>>
</label>
<?php if (!empty($categoryId)): ?>
<?php
$formatter = new IntlDateFormatter(
'de_DE',
IntlDateFormatter::LONG,
IntlDateFormatter::SHORT
);
$timestamp = strtotime($category["modified_at"]);
?>
<div class="cf-settings-meta">
<small><?= $translation->get("connected_subcategories") ?> (<?= $parentCount ?>)</small>
<?php if ($parentCount > 0): ?>
<ul class="connected_categories">
<?php foreach ($parentCategories as $parent): ?>
<li>
<a target="_blank" href="?action=Categories&detail=<?= $parent['id'] ?>">
<?= htmlspecialchars($parent['title'] ?: "Kategorie-ID " . $parent['id']) ?> ↗
</a>
</li>
<?php endforeach; ?>
</ul>
<?php else: ?>
<p style="font-style: italic;"><?= $translation->get("not_connected_as_subcategory") ?></p>
<?php endif; ?>
<small><?= $translation->get("last_change_on") ?></small>
<p style="font-style: italic;"><?= $formatter->format($timestamp) ?> - <?= $category["modified_by"] ?></p>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php if ($categoryId != 0): ?> <?php if ($categoryId != 0): ?>
<!-- Tab: Berechtigungen & Filter -->
<div id="cf-tab-berechtigungen" class="cf-tab-pane">
<div class="cf-perm-grid">
<div class="cf-perm-block cf-perm-sm">
<h4><?= $translation->get("mandants") ?></h4>
<input type="text" id="selected_mandants" name="selected_mandants"
value='<?= htmlspecialchars(json_encode($savedMandantsData)); ?>'>
</div>
<div class="cf-perm-block cf-perm-sm">
<h4><?= $translation->get("departments") ?></h4>
<input type="text" id="selected_departments" name="selected_departments"
value='<?= htmlspecialchars(json_encode($savedDepartmentsData)); ?>'>
</div>
<div class="cf-perm-block cf-perm-sm">
<h4><?= $translation->get("roles") ?></h4>
<input type="text" id="selected_roles" name="selected_roles"
value='<?= htmlspecialchars(json_encode($savedRolesData)); ?>'>
</div>
<div class="cf-perm-block cf-perm-lg">
<h4>Einrichtungen</h4>
<input type="text" id="selected_einrichts" name="selected_einrichts"
value='<?= htmlspecialchars(json_encode($savedEinrichtsData)); ?>'>
</div>
<div class="cf-perm-block cf-perm-lg">
<h4>Fachbereiche</h4>
<input type="text" id="selected_fachbereiche" name="selected_fachbereiche"
value='<?= htmlspecialchars(json_encode($savedFachbereicheData)); ?>'>
</div>
</div>
</div>
<!-- Tab: Unterkategorien -->
<div id="cf-tab-unterkategorien" class="cf-tab-pane">
<div class="category_sub_select"> <div class="category_sub_select">
<div class="category_sub_select_container"> <div class="category_sub_select_container">
<h3><?= $translation->get("connected_subcategories") ?></h3> <h3><?= $translation->get("connected_subcategories") ?></h3>
@@ -441,7 +309,8 @@ $parentCount = count($parentCategories);
<div> <div>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-grip-vertical" viewBox="0 0 16 16"> class="bi bi-grip-vertical" viewBox="0 0 16 16">
<path d="M7 2a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0M7 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0M7 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0m-3 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0m-3 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0"/> <path
d="M7 2a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0M7 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0M7 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0m-3 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0m-3 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0" />
</svg> </svg>
<?= htmlspecialchars($sub['title'] ?? "Fehlende Übersetzung ID {$sub['id']}") ?> <?= htmlspecialchars($sub['title'] ?? "Fehlende Übersetzung ID {$sub['id']}") ?>
</div> </div>
@@ -468,7 +337,8 @@ $parentCount = count($parentCategories);
<div> <div>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-grip-vertical" viewBox="0 0 16 16"> class="bi bi-grip-vertical" viewBox="0 0 16 16">
<path d="M7 2a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0M7 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0M7 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0m-3 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0m-3 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0"/> <path
d="M7 2a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0M7 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0M7 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0m-3 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0m-3 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0" />
</svg> </svg>
<?= htmlspecialchars($row['title'] ?: "Fehlende Übersetzung ID {$row['id']}") ?> <?= htmlspecialchars($row['title'] ?: "Fehlende Übersetzung ID {$row['id']}") ?>
</div> </div>
@@ -478,9 +348,136 @@ $parentCount = count($parentCategories);
</ul> </ul>
</div> </div>
</div> </div>
<?php endif; ?>
</div>
</div>
<div class="aside">
<div class="aside_section">
<div class="aside_toolbar">
<?php if (get_permission_state('r-wiki', $GLOBALS['main_contact']['master_mandant_id'], $GLOBALS['main_contact']['id']) == 1) { ?>
<button class="post-action-btn" id="saveAllTranslations" style="background: #14b814;">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-check" viewBox="0 0 16 16">
<path
d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425z">
</path>
</svg>
</button>
<?php } ?>
<?php if (get_permission_state('r-wiki', $GLOBALS['main_contact']['master_mandant_id'], $GLOBALS['main_contact']['id']) == 1) { ?>
<button class="post-action-btn red" id="deleteCategory">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-trash3-fill" viewBox="0 0 16 16">
<path
d="M11 1.5v1h3.5a.5.5 0 0 1 0 1h-.538l-.853 10.66A2 2 0 0 1 11.115 16h-6.23a2 2 0 0 1-1.994-1.84L2.038 3.5H1.5a.5.5 0 0 1 0-1H5v-1A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5m-5 0v1h4v-1a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5M4.5 5.029l.5 8.5a.5.5 0 1 0 .998-.06l-.5-8.5a.5.5 0 1 0-.998.06m6.53-.528a.5.5 0 0 0-.528.47l-.5 8.5a.5.5 0 0 0 .998.058l.5-8.5a.5.5 0 0 0-.47-.528M8 4.5a.5.5 0 0 0-.5.5v8.5a.5.5 0 0 0 1 0V5a.5.5 0 0 0-.5-.5" />
</svg>
</button>
<?php } ?>
</div>
</div>
<div class="aside_section">
<label for="stateCheckbox">
<?= $translation->get("active_state") ?>
<input type="checkbox" id="stateCheckbox" name="state" value="1" <?= $state === 1 ? 'checked' : '' ?>>
</label>
</div>
<?php $is_navigation_item = $translations[$languages[0]['code']]['is_navigation_item'] ?? 0; ?>
<div class="aside_section">
<label for="isNavigationItem">
<?= $translation->get("create_navigation_menu") ?>
<input type="checkbox" id="isNavigationItem" name="is_navigation_item" value="1"
<?= $is_navigation_item == 1 ? 'checked' : '' ?>>
</label>
</div>
<div class="aside_section">
<label for="color_hex">
<?= $translation->get("color_hex") ?>
<input type="color" id="color_hex" name="color_hex" value="<?= $category['color_hex'] ?? '#3B82F6FF' ?>">
</label>
</div>
<?php $enable_post_color_pickers = isset($category['enable_post_color_pickers']) ? (int) $category['enable_post_color_pickers'] : 0; ?>
<div class="aside_section">
<label for="enablePostColorPickersCheckbox">
<?= $translation->get("enable_post_color_pickers") ?>
<input type="checkbox" id="enablePostColorPickersCheckbox" name="enable_post_color_pickers" value="1"
<?= $enable_post_color_pickers === 1 ? 'checked' : '' ?>>
</label>
</div>
<?php if (!empty($categoryId)): ?>
<?php
$formatter = new IntlDateFormatter(
'de_DE',
IntlDateFormatter::LONG,
IntlDateFormatter::SHORT
);
$timestamp = strtotime($category["modified_at"]);
?>
<div class="aside_section">
<small><?= $translation->get("connected_subcategories") ?> (<?= $parentCount ?>)</small>
<?php if ($parentCount > 0): ?>
<ul class="connected_categories">
<?php foreach ($parentCategories as $parent): ?>
<li>
<a target="_blank" href="?action=Categories&detail=<?= $parent['id'] ?>">
<?= htmlspecialchars($parent['title'] ?: "Kategorie-ID " . $parent['id']) ?> ↗
</a>
</li>
<?php endforeach; ?>
</ul>
<?php else: ?>
<p style="font-style: italic;"><?= $translation->get("not_connected_as_subcategory") ?></p>
<?php endif; ?>
</div>
<div class="aside_section">
<small><?= $translation->get("last_change_on") ?></small>
<p style="font-style: italic;">
<?= $formatter->format($timestamp) ?> - <?= $category["modified_by"] ?>
</p>
</div> </div>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div>
<?php if ($categoryId != 0): ?>
<div class="cardform_containern perm-fullwidth">
<div class="cardform" style="flex:1;">
<div class="card-body">
<h3><?= $translation->get("permissions_and_filters") ?></h3>
<div class="perm-row">
<label for="selected_mandants"><?= $translation->get("mandants") ?></label>
<input type="text" id="selected_mandants" name="selected_mandants"
value='<?= htmlspecialchars(json_encode($savedMandantsData)); ?>'>
</div>
<div class="perm-row">
<label for="selected_departments"><?= $translation->get("departments") ?></label>
<input type="text" id="selected_departments" name="selected_departments"
value='<?= htmlspecialchars(json_encode($savedDepartmentsData)); ?>'>
</div>
<div class="perm-row">
<label for="selected_roles"><?= $translation->get("roles") ?></label>
<input type="text" id="selected_roles" name="selected_roles"
value='<?= htmlspecialchars(json_encode($savedRolesData)); ?>'>
</div>
<div class="perm-row">
<label for="selected_einrichts">Einrichtungen</label>
<input type="text" id="selected_einrichts" name="selected_einrichts"
value='<?= htmlspecialchars(json_encode($savedEinrichtsData)); ?>'>
</div>
<div class="perm-row">
<label for="selected_fachbereiche">Fachbereiche</label>
<input type="text" id="selected_fachbereiche" name="selected_fachbereiche"
value='<?= htmlspecialchars(json_encode($savedFachbereicheData)); ?>'>
</div>
</div>
</div>
</div>
<?php endif; ?>
<input type="hidden" id="category_id" value="<?= $categoryId ?>"> <input type="hidden" id="category_id" value="<?= $categoryId ?>">
<input type="hidden" id="modified_by" <input type="hidden" id="modified_by"
@@ -488,15 +485,6 @@ $parentCount = count($parentCategories);
<script> <script>
$(function () { $(function () {
// Section tab switching
$('.cf-section-tab').on('click', function () {
var target = $(this).data('target');
$('.cf-section-tab').removeClass('active');
$(this).addClass('active');
$('.cf-tab-pane').removeClass('active').hide();
$('#' + target).addClass('active').show();
});
// Funktion zum sequentiellen Speichern der Übersetzungen // Funktion zum sequentiellen Speichern der Übersetzungen
window.saveCategoryAjax = function (callback) { window.saveCategoryAjax = function (callback) {
var forms = $(".translationForm"); var forms = $(".translationForm");

View File

@@ -156,7 +156,6 @@ if ($isNew) {
'id' => 0, 'id' => 0,
// Weitere Felder kannst du hier ggf. ergänzen // Weitere Felder kannst du hier ggf. ergänzen
]; ];
$commentCount = 0;
$versionCount = 0; $versionCount = 0;
$currentVersion = [ $currentVersion = [
'id' => 0, 'id' => 0,
@@ -199,16 +198,6 @@ if ($isNew) {
} }
// Anzahl Kommentare ermitteln
$sqlCountComments = "SELECT COUNT(*) AS commentCount FROM topic WHERE knowledgecenter_post_id = $postId";
$resCountComments = mysqli_query($GLOBALS['mysql_con'], $sqlCountComments);
if ($resCountComments && mysqli_num_rows($resCountComments) > 0) {
$rowCount = mysqli_fetch_assoc($resCountComments);
$commentCount = (int) $rowCount['commentCount'];
} else {
$commentCount = 0;
}
// Anzahl Versionen für die aktuell ausgewählte Sprache ermitteln // Anzahl Versionen für die aktuell ausgewählte Sprache ermitteln
$sqlCountVersions = "SELECT COUNT(*) AS versionCount FROM knowledgecenter_post_versions WHERE post_id = $postId AND language_id = $languageId"; $sqlCountVersions = "SELECT COUNT(*) AS versionCount FROM knowledgecenter_post_versions WHERE post_id = $postId AND language_id = $languageId";
$resCountVersions = mysqli_query($GLOBALS['mysql_con'], $sqlCountVersions); $resCountVersions = mysqli_query($GLOBALS['mysql_con'], $sqlCountVersions);

View File

@@ -34,7 +34,7 @@
<div class="edit-main"> <div class="edit-main">
<?php foreach ($allLanguages as $lang): <?php foreach ($allLanguages as $lang):
$langId = $lang['id']; $langId = $lang['id'];
$trans = isset($translations[$langId]) ? $translations[$langId] : ['title' => '', 'image' => '', 'teaser' => '', 'content' => '']; $trans = isset($translations[$langId]) ? $translations[$langId] : ['title' => '', 'image' => '', 'teaser' => '', 'content' => '', 'summary' => ''];
?> ?>
<div class="lang-form" id="lang_<?= $langId ?>" data-lang="<?= $langId ?>" <div class="lang-form" id="lang_<?= $langId ?>" data-lang="<?= $langId ?>"
style="<?= ($langId == $languageId) ? '' : 'display: none;' ?>"> style="<?= ($langId == $languageId) ? '' : 'display: none;' ?>">
@@ -65,6 +65,10 @@
<div id="editor_<?= $langId ?>" class="editor"> <div id="editor_<?= $langId ?>" class="editor">
<?= $translations[$langId]['content'] ?> <?= $translations[$langId]['content'] ?>
</div> </div>
<hr>
<label for="summary_<?= $langId ?>">Zusammenfassung (Summary)</label>
<textarea id="summary_<?= $langId ?>" name="summary[<?= $langId ?>]" rows="4"
placeholder="Kurze Zusammenfassung des Artikelinhalts..."><?= htmlspecialchars($trans['summary'] ?? '') ?></textarea>
</div> </div>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
@@ -80,7 +84,7 @@
<?php foreach ($allLanguages as $lang): <?php foreach ($allLanguages as $lang):
$langId = $lang['id']; $langId = $lang['id'];
$trans = isset($translations[$langId]) ? $translations[$langId] : ['title' => '', 'image' => '', 'teaser' => '', 'content' => '']; $trans = isset($translations[$langId]) ? $translations[$langId] : ['title' => '', 'image' => '', 'teaser' => '', 'content' => '', 'summary' => ''];
?> ?>
<div class="lang-form-meta" data-lang="<?= $langId ?>" <div class="lang-form-meta" data-lang="<?= $langId ?>"
style="<?= ($langId == $languageId) ? '' : 'display: none;' ?>"> style="<?= ($langId == $languageId) ? '' : 'display: none;' ?>">

View File

@@ -0,0 +1,20 @@
.ql-img-resize-overlay {
position: absolute;
border: 1px dashed #3C7DD9;
box-sizing: border-box;
pointer-events: none;
z-index: 100;
}
.ql-img-resize-handle {
position: absolute;
bottom: -5px;
right: -5px;
width: 10px;
height: 10px;
background: #3C7DD9;
border: 1px solid #fff;
border-radius: 2px;
cursor: nwse-resize;
pointer-events: all;
}

View File

@@ -0,0 +1,96 @@
(function () {
'use strict';
if (typeof Quill === 'undefined') return;
function ImageResizeModule(quill, options) {
this.quill = quill;
this.overlay = null;
this.activeImg = null;
this._startX = 0;
this._startW = 0;
this._onImgClick = this._onImgClick.bind(this);
this._onDocClick = this._onDocClick.bind(this);
this._onMouseMove = this._onMouseMove.bind(this);
this._onMouseUp = this._onMouseUp.bind(this);
quill.root.addEventListener('click', this._onImgClick);
document.addEventListener('click', this._onDocClick);
}
ImageResizeModule.prototype._onImgClick = function (e) {
if (e.target && e.target.tagName === 'IMG') {
e.stopPropagation();
this._select(e.target);
}
};
ImageResizeModule.prototype._onDocClick = function (e) {
if (this.overlay && e.target !== this.activeImg && !this.overlay.contains(e.target)) {
this._deselect();
}
};
ImageResizeModule.prototype._select = function (img) {
this._deselect();
this.activeImg = img;
var overlay = document.createElement('div');
overlay.className = 'ql-img-resize-overlay';
var handle = document.createElement('div');
handle.className = 'ql-img-resize-handle';
overlay.appendChild(handle);
this.quill.root.style.position = 'relative';
this.quill.root.appendChild(overlay);
this.overlay = overlay;
this._reposition();
var self = this;
handle.addEventListener('mousedown', function (e) {
e.preventDefault();
self._startX = e.clientX;
self._startW = self.activeImg.offsetWidth || self.activeImg.getBoundingClientRect().width;
document.addEventListener('mousemove', self._onMouseMove);
document.addEventListener('mouseup', self._onMouseUp);
});
};
ImageResizeModule.prototype._reposition = function () {
if (!this.overlay || !this.activeImg) return;
var r = this.activeImg.getBoundingClientRect();
var er = this.quill.root.getBoundingClientRect();
this.overlay.style.left = (r.left - er.left + this.quill.root.scrollLeft) + 'px';
this.overlay.style.top = (r.top - er.top + this.quill.root.scrollTop) + 'px';
this.overlay.style.width = r.width + 'px';
this.overlay.style.height = r.height + 'px';
};
ImageResizeModule.prototype._onMouseMove = function (e) {
if (!this.activeImg) return;
var dx = e.clientX - this._startX;
var newW = Math.max(40, this._startW + dx);
this.activeImg.style.width = newW + 'px';
this.activeImg.setAttribute('width', newW);
this._reposition();
};
ImageResizeModule.prototype._onMouseUp = function () {
document.removeEventListener('mousemove', this._onMouseMove);
document.removeEventListener('mouseup', this._onMouseUp);
if (this.quill) this.quill.update();
};
ImageResizeModule.prototype._deselect = function () {
if (this.overlay && this.overlay.parentNode) {
this.overlay.parentNode.removeChild(this.overlay);
}
this.overlay = null;
this.activeImg = null;
};
Quill.register('modules/imageResize', ImageResizeModule);
})();

View File

@@ -0,0 +1,46 @@
window.QuillToolbarPresets = {
full: function () {
return [
[{ header: [1, 2, 3, 4, 5, 6, false] }],
['bold', 'italic', 'underline', 'strike'],
[{ color: [] }, { background: [] }],
[{ list: 'ordered' }, { list: 'bullet' }],
[{ indent: '-1' }, { indent: '+1' }],
[{ align: [] }],
['blockquote', 'code-block'],
['link', 'image'],
['table'],
['clean']
];
},
compact: function () {
return [
[{ header: [1, 2, 3, false] }],
['bold', 'italic', 'underline'],
[{ list: 'ordered' }, { list: 'bullet' }],
['link', 'image'],
['table'],
['clean']
];
},
betterTableConfig: function () {
return {
operationMenu: {
items: {
insertColumnRight: { text: 'Spalte rechts' },
insertColumnLeft: { text: 'Spalte links' },
insertRowUp: { text: 'Zeile oben' },
insertRowDown: { text: 'Zeile unten' },
mergeCells: { text: 'Zellen verbinden' },
unmergeCells: { text: 'Zellen trennen' },
deleteColumn: { text: 'Spalte löschen' },
deleteRow: { text: 'Zeile löschen' },
deleteTable: { text: 'Tabelle löschen' }
}
}
};
}
};