diff --git a/assets/course_sidebar.js b/assets/course_sidebar.js index b52862b..4d11ed3 100644 --- a/assets/course_sidebar.js +++ b/assets/course_sidebar.js @@ -454,11 +454,14 @@ const existingNav = document.getElementById('courseModuleNavigator'); if (existingNav) existingNav.remove(); - // Legacy-Buttons ausblenden - const legacyActionDivs = mainEl.querySelectorAll('.card > div[style*="flex"]:last-child, main > div[style*="flex"]:last-child'); - legacyActionDivs.forEach(div => { - if (div.querySelector('a[href*="workspace.html"]') || div.querySelector('a[href*="index.html"]')) { - div.style.display = 'none'; + // Legacy-Buttons ausblenden / entfernen + const legacyCandidates = mainEl.querySelectorAll( + '.card > div[style*="flex"]:last-child, main > div[style*="flex"]:last-child, div[style*="flex"]:last-child, ' + + '.border-t, div[class*="border-t"], div[class*="justify-between"], div.mt-8.pt-6' + ); + legacyCandidates.forEach(el => { + if (el.querySelector('a[href*="index.html"]') || el.querySelector('a[href*="workspace.html"]') || el.querySelector('.ihk-btn') || el.innerText.includes('Nächstes Modul') || el.innerText.includes('Vorheriges Modul')) { + el.remove(); } }); @@ -566,10 +569,13 @@ const existingPaneNav = theoriePane.querySelector('.course-module-navigator'); if (existingPaneNav) existingPaneNav.remove(); - const legacyActionDivs = theoriePane.querySelectorAll('.card > div[style*="flex"]:last-child, div[style*="flex"]:last-child'); - legacyActionDivs.forEach(div => { - if (div.querySelector('a[href*="workspace.html"]') || div.querySelector('a[href*="index.html"]')) { - div.style.display = 'none'; + const legacyCandidates = theoriePane.querySelectorAll( + '.card > div[style*="flex"]:last-child, div[style*="flex"]:last-child, ' + + '.border-t, div[class*="border-t"], div[class*="justify-between"], div.mt-8.pt-6' + ); + legacyCandidates.forEach(el => { + if (el.querySelector('a[href*="index.html"]') || el.querySelector('a[href*="workspace.html"]') || el.querySelector('.ihk-btn') || el.innerText.includes('Nächstes Modul') || el.innerText.includes('Vorheriges Modul')) { + el.remove(); } }); diff --git a/server.py b/server.py index 9e7c516..7173cfc 100644 --- a/server.py +++ b/server.py @@ -44,6 +44,41 @@ def is_trusted_source(ip: str) -> bool: except ValueError: return False +# ------------------------------------------------------------------------------ +# 0. AUTOMATISCHES ROUTING FÜR ALLE 264 KURS-KAPITEL +# ------------------------------------------------------------------------------ +CHAPTER_ROUTES = {} + +def load_chapter_routes(): + global CHAPTER_ROUTES + manifest_path = BASE_DIR / "assets" / "courses_manifest.js" + if not manifest_path.exists(): + return + try: + content = manifest_path.read_text(encoding="utf-8") + idx = content.find("{") + if idx != -1: + json_str = content[idx:].strip() + if json_str.endswith(";"): + json_str = json_str[:-1] + manifest = json.loads(json_str) + routes = {} + for cid, c in manifest.items(): + for tr in c.get("tracks", []): + tid = tr.get("id", "") + for ch in tr.get("chapters", []): + f = ch.get("folder", "") + if f: + if cid == "python": + routes[f] = f"/{tid}/{f}" + else: + routes[f] = f"/courses/{cid}/{f}" + CHAPTER_ROUTES = routes + except Exception as e: + print(f"Warnung beim Laden der Kapitel-Routen: {e}") + +load_chapter_routes() + # ------------------------------------------------------------------------------ # 1. PERSISTENTER SECRET KEY # ------------------------------------------------------------------------------ @@ -357,6 +392,25 @@ class PlatformRequestHandler(http.server.SimpleHTTPRequestHandler): if self.is_path_blocked(path): return self.send_json({"error": "Zugriff verweigert (Geschützte Datei)"}, 403) + # Automatische Weiterleitung für Direktaufrufe von Kapitel-Ordnern an der Root-Ebene + # (z. B. /02_lf2_arbeitsplaetze_ausstatten/index.html -> /courses/ihk_ap1/02_lf2_arbeitsplaetze_ausstatten/index.html) + path_parts = [p for p in path.split("/") if p] + if path_parts and not path.startswith("/api/"): + first_part = path_parts[0] + if first_part in CHAPTER_ROUTES: + target_base = CHAPTER_ROUTES[first_part] + sub_path = "/".join(path_parts[1:]) + if not sub_path: + sub_path = "index.html" + target_url = f"{target_base}/{sub_path}" + if parsed.query: + target_url += f"?{parsed.query}" + self.send_response(302) + self.send_header("Location", target_url) + self.send_header("Cache-Control", "no-cache") + self.end_headers() + return + # 1. ÖFFENTLICHER STATUS-ENDPUNKT (Wartungsmodus, Ankündigung, Registrierung) if path == "/api/platform/status": return self.send_json({ diff --git a/workspace.html b/workspace.html index 09d17c9..8744893 100644 --- a/workspace.html +++ b/workspace.html @@ -533,8 +533,35 @@ if (htmlDoc) { const parser = new DOMParser(); const doc = parser.parseFromString(htmlDoc, "text/html"); - const mainContent = doc.querySelector("main.container") || doc.body; - document.getElementById("tab-content-theorie").innerHTML = mainContent.innerHTML; + const mainContent = doc.querySelector("main.container") || doc.querySelector("main") || doc.querySelector(".container") || doc.body; + + // Legacy-Navigationsleisten und Dashboard-Breadcrumbs im IDE-Theorietext entfernen + const legacyBars = mainContent.querySelectorAll( + '.border-t, div[class*="border-t"], div[class*="justify-between"], div.mt-8.pt-6, .course-module-navigator, .course-sidebar-mobile-btn' + ); + legacyBars.forEach(el => { + if (el.querySelector('a[href*="index.html"]') || el.querySelector('a[href*="dashboard.html"]') || el.querySelector('.ihk-btn') || el.innerText.includes('Nächstes Modul') || el.innerText.includes('Vorheriges Modul') || el.innerText.includes('← Zurück zum Dashboard')) { + el.remove(); + } + }); + + const theorieContainer = document.getElementById("tab-content-theorie"); + theorieContainer.innerHTML = mainContent.innerHTML; + + // Alle Links innerhalb des Theorie-Panels absichern: + // Kapitel-Links schalten die Web-IDE um, anstatt die IDE zu verlassen + theorieContainer.querySelectorAll("a").forEach(link => { + const rawHref = link.getAttribute("href") || ""; + const chapMatch = rawHref.match(/([0-9]{2}_[a-zA-Z0-9_-]+)/); + if (chapMatch && !rawHref.startsWith("http") && !rawHref.includes("workspace.html")) { + link.addEventListener("click", (e) => { + e.preventDefault(); + const targetChapter = chapMatch[1]; + window.location.href = `workspace.html?course=${encodeURIComponent(courseId)}&chapter=${encodeURIComponent(targetChapter)}`; + }); + } + }); + if (window.renderWorkspaceTheoryNavigator) { window.renderWorkspaceTheoryNavigator(); }