Mit dem Dreamcodes Quiz Master liefern wir dir eine vollständige Quiz Plattform. Diese vereint Benutzerverwaltung, OAuth-Authentifizierung, JWT-Session, mehrsprachige Oberfläche in sechs Sprachen, Kategorien- und Fragenverwaltung mit bereits 10 eingebauten Kategorien und 100 Fragen und Antworten, sowie ein interaktives Quiz mit Zeit-Challenges und Fortschrittsstatistiken. Geil oder? 😉
Hauptfunktionen
- Benutzerregistrierung, Login und Logout mit bcrypt-Passworthashing
- OAuth-Login via Google und JWT-basierte API-Authentifizierung
- Rollenbasiertes System für
Admins
und jeweiligeUser
- Mehrsprachige Oberfläche (Deutsch, Englisch, Französisch, Italienisch, Spanisch, Türkisch)
- Nutzerprofile mit individuellem Sprachprofil und Fortschrittsverlauf
- Kategorienverwaltung im Admin-Dashboard
- Fragenverwaltung: Anlegen, Löschen, Anzeigen aller Fragen
- Quiz-Modus mit Filter nach Kategorie, Schwierigkeit und Fragezahl
- Challenge-Modi: Standard, zeitbasiert pro Kategorie und zeitbasiert pro Schwierigkeit
- Echtzeit-Timer, Fortschrittsbalken und AJAX-Submission
- Speicherung aller Quiz-Ergebnisse in SQLite mit Zeitstempel
- CSV- und JSON-Export aller Statistiken für Admins
<?php
session_start();
require __DIR__ . '/vendor/autoload.php'; // Composer-Autoloader für firebase/php-jwt
use Firebase\JWT\JWT;
// --- KONSTANTEN & OAUTH‐CONFIG ------------------------------------------------
const JWT_SECRET = 'DEIN_SECRET_KEY_HIER_EINFÜGEN';
const OAUTH_GOOGLE_CLIENT_ID = 'DEINE_GOOGLE_CLIENT_ID';
const OAUTH_GOOGLE_CLIENT_SECRET = 'DEIN_GOOGLE_CLIENT_SECRET';
const OAUTH_REDIRECT_URI = 'https://deinedomain.de/index.php?oauth=google';
// --- DATENBANK‐SETUP & MIGRATION ---------------------------------------------
$pdo = new PDO('sqlite:' . __DIR__ . '/quiz.sqlite');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec('PRAGMA foreign_keys = ON');
// Tabellen anlegen
$pdo->exec("
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE,
password TEXT,
role TEXT NOT NULL CHECK(role IN('admin','user')) DEFAULT 'user',
provider TEXT,
oauth_id TEXT
);
CREATE TABLE IF NOT EXISTS profiles (
user_id INTEGER PRIMARY KEY,
lang TEXT NOT NULL DEFAULT 'de',
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL
);
CREATE TABLE IF NOT EXISTS questions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
question TEXT NOT NULL,
option_a TEXT NOT NULL,
option_b TEXT NOT NULL,
option_c TEXT NOT NULL,
option_d TEXT NOT NULL,
correct_option TEXT NOT NULL CHECK(correct_option IN('a','b','c','d')),
category_id INTEGER NOT NULL,
difficulty TEXT NOT NULL CHECK(difficulty IN('Easy','Medium','Hard')),
FOREIGN KEY(category_id) REFERENCES categories(id) ON DELETE CASCADE
);
INSERT OR IGNORE INTO categories (name) VALUES
('Allgemeinwissen'),
('Wissenschaft'),
('Geschichte'),
('Geographie'),
('Sport'),
('Unterhaltung'),
('Literatur'),
('Technologie'),
('Mathematik'),
('Kunst');
INSERT OR IGNORE INTO questions
(question, option_a, option_b, option_c, option_d, correct_option, category_id, difficulty)
VALUES
('Wie viele Kontinente gibt es auf der Erde?', '5', '6', '7', '8', 'c',
(SELECT id FROM categories WHERE name='Allgemeinwissen'), 'Easy'),
('Welches Tier ist das größte lebende Landtier?', 'Elefant', 'Giraffe', 'Flusspferd', 'Nashorn', 'a',
(SELECT id FROM categories WHERE name='Allgemeinwissen'), 'Easy'),
('Welches Element macht den größten Teil der Erdatmosphäre aus?', 'Sauerstoff', 'Stickstoff', 'Kohlendioxid', 'Argon', 'b',
(SELECT id FROM categories WHERE name='Allgemeinwissen'), 'Easy'),
('Welche Farbe entsteht durch Mischen von Blau und Gelb?', 'Grün', 'Lila', 'Orange', 'Braun', 'a',
(SELECT id FROM categories WHERE name='Allgemeinwissen'), 'Easy'),
('Welches Land hat die größte Bevölkerung?', 'China', 'Indien', 'USA', 'Indonesien', 'a',
(SELECT id FROM categories WHERE name='Allgemeinwissen'), 'Medium'),
('Wie viele Elemente hat das Periodensystem (Stand 2021)?', '118', '120', '115', '113', 'a',
(SELECT id FROM categories WHERE name='Allgemeinwissen'), 'Medium'),
('Welches Organ pumpt Blut durch den Körper?', 'Leber', 'Lunge', 'Herz', 'Niere', 'c',
(SELECT id FROM categories WHERE name='Allgemeinwissen'), 'Medium'),
('Wann endete der Zweite Weltkrieg?', '1945', '1944', '1939', '1950', 'a',
(SELECT id FROM categories WHERE name='Allgemeinwissen'), 'Medium'),
('Wer schrieb die „Kritik der reinen Vernunft“?', 'Immanuel Kant', 'Georg Hegel', 'Friedrich Nietzsche', 'Arthur Schopenhauer', 'a',
(SELECT id FROM categories WHERE name='Allgemeinwissen'), 'Hard'),
('Welche Sprache ist Amtssprache in Brasilien?', 'Spanisch', 'Portugiesisch', 'Französisch', 'Englisch', 'b',
(SELECT id FROM categories WHERE name='Allgemeinwissen'), 'Hard'),
('Was ist die Einheit der elektrischen Spannung?', 'Volt', 'Ampere', 'Ohm', 'Watt', 'a',
(SELECT id FROM categories WHERE name='Wissenschaft'), 'Easy'),
('Welches Gas atmen wir am meisten ein?', 'Sauerstoff', 'Stickstoff', 'Kohlendioxid', 'Helium', 'b',
(SELECT id FROM categories WHERE name='Wissenschaft'), 'Easy'),
('Wie heißt die Lehre von den Pflanzen?', 'Zoologie', 'Botanik', 'Geologie', 'Meteorologie', 'b',
(SELECT id FROM categories WHERE name='Wissenschaft'), 'Easy'),
('Welche Grundkraft wirkt zwischen allen Massen?', 'Gravitation', 'Magnetismus', 'Elektrizität', 'Reibung', 'a',
(SELECT id FROM categories WHERE name='Wissenschaft'), 'Easy'),
('Was beschreibt die Formel E=mc²?', 'Energie-Äquivalenz', 'Geschwindigkeit', 'Elektrische Feldstärke', 'Dichte', 'a',
(SELECT id FROM categories WHERE name='Wissenschaft'), 'Medium'),
('Wie viele Chromosomen hat die Zelle des Menschen?', '46', '23', '44', '92', 'a',
(SELECT id FROM categories WHERE name='Wissenschaft'), 'Medium'),
('Welches Vitamin wird durch Sonnenlicht gebildet?', 'Vitamin C', 'Vitamin D', 'Vitamin B12', 'Vitamin A', 'b',
(SELECT id FROM categories WHERE name='Wissenschaft'), 'Medium'),
('Welcher pH-Wert gilt als neutral?', '7', '1', '14', '0', 'a',
(SELECT id FROM categories WHERE name='Wissenschaft'), 'Medium'),
('Wer entdeckte die Röntgenstrahlen?', 'Wilhelm Röntgen', 'Marie Curie', 'Albert Einstein', 'Max Planck', 'a',
(SELECT id FROM categories WHERE name='Wissenschaft'), 'Hard'),
('Welche Teilchen vermitteln die starke Kernkraft?', 'Photon', 'Gluon', 'W-Boson', 'Neutron', 'b',
(SELECT id FROM categories WHERE name='Wissenschaft'), 'Hard'),
('Wer war der erste Bundeskanzler der Bundesrepublik Deutschland?', 'Konrad Adenauer', 'Helmut Schmidt', 'Helmut Kohl', 'Willy Brandt', 'a',
(SELECT id FROM categories WHERE name='Geschichte'), 'Easy'),
('In welchem Jahr fiel die Berliner Mauer?', '1989', '1990', '1987', '1991', 'a',
(SELECT id FROM categories WHERE name='Geschichte'), 'Easy'),
('Wer „entdeckte“ Amerika 1492?', 'Christoph Kolumbus', 'Ferdinand Magellan', 'Vasco da Gama', 'Amerigo Vespucci', 'a',
(SELECT id FROM categories WHERE name='Geschichte'), 'Easy'),
('Welche Zivilisation baute die Pyramiden von Gizeh?', 'Ägypter', 'Maya', 'Azteken', 'Römer', 'a',
(SELECT id FROM categories WHERE name='Geschichte'), 'Easy'),
('Wer komponierte Beethovens „Neunte Symphonie“?', 'Ludwig van Beethoven', 'Wolfgang Amadeus Mozart', 'Johann Sebastian Bach', 'Joseph Haydn', 'a',
(SELECT id FROM categories WHERE name='Geschichte'), 'Medium'),
('Was bezeichnet die Reformation im 16. Jahrhundert?', 'Kirchenspaltung', 'Kriegserklärung', 'Industrielle Revolution', 'Französische Revolution', 'a',
(SELECT id FROM categories WHERE name='Geschichte'), 'Medium'),
('Welche Dynastie herrschte während der Han-Zeit in China?', 'Han', 'Ming', 'Qing', 'Song', 'a',
(SELECT id FROM categories WHERE name='Geschichte'), 'Medium'),
('Wann begann der Erste Weltkrieg?', '1914', '1918', '1939', '1920', 'a',
(SELECT id FROM categories WHERE name='Geschichte'), 'Medium'),
('Wer war Pharao zum Bau der Cheops-Pyramide?', 'Cheops', 'Ramses II.', 'Tutanchamun', 'Mykerinos', 'a',
(SELECT id FROM categories WHERE name='Geschichte'), 'Hard'),
('In welchem Jahr endete das Weströmische Reich?', '476', '1453', '1492', '395', 'a',
(SELECT id FROM categories WHERE name='Geschichte'), 'Hard'),
('Welcher Fluss gilt traditionell als längster der Welt?', 'Nil', 'Amazonas', 'Jangtse', 'Mississippi', 'a',
(SELECT id FROM categories WHERE name='Geographie'), 'Easy'),
('Welcher Kontinent liegt südlich von Europa?', 'Afrika', 'Asien', 'Nordamerika', 'Australien', 'a',
(SELECT id FROM categories WHERE name='Geographie'), 'Easy'),
('Was ist die Hauptstadt von Frankreich?', 'Berlin', 'London', 'Paris', 'Rom', 'c',
(SELECT id FROM categories WHERE name='Geographie'), 'Easy'),
('Welches Land hat die größte Fläche?', 'Russland', 'Kanada', 'China', 'USA', 'a',
(SELECT id FROM categories WHERE name='Geographie'), 'Easy'),
('In welchem Land liegt der Kilimandscharo?', 'Kenia', 'Tansania', 'Südafrika', 'Angola', 'b',
(SELECT id FROM categories WHERE name='Geographie'), 'Medium'),
('Welcher Staat ist der kleinste der Welt?', 'Vatikanstadt', 'Monaco', 'Nauru', 'San Marino', 'a',
(SELECT id FROM categories WHERE name='Geographie'), 'Medium'),
('Welche Wüste ist flächenmäßig die größte?', 'Sahara', 'Gobi', 'Kalahari', 'Antarktis', 'd',
(SELECT id FROM categories WHERE name='Geographie'), 'Medium'),
('Wo liegt der tiefste Punkt der Erdoberfläche?', 'Marianengraben', 'Totes Meer', 'Baikalsee', 'Grand Canyon', 'a',
(SELECT id FROM categories WHERE name='Geographie'), 'Medium'),
('Wie hoch ist der Mount Everest?', '8848 m', '8636 m', '8000 m', '8950 m', 'a',
(SELECT id FROM categories WHERE name='Geographie'), 'Hard'),
('Welche Insel ist die größte der Welt?', 'Grönland', 'Neuguinea', 'Borneo', 'Madagaskar', 'a',
(SELECT id FROM categories WHERE name='Geographie'), 'Hard'),
('Wie viele Spieler stehen auf dem Feld bei einer Fußballmannschaft?', '11', '9', '7', '5', 'a',
(SELECT id FROM categories WHERE name='Sport'), 'Easy'),
('Welcher Sport ist kein Teil der Olympischen Winterspiele?', 'Beachvolleyball', 'Ski Alpin', 'Eishockey', 'Biathlon', 'a',
(SELECT id FROM categories WHERE name='Sport'), 'Easy'),
('Wie lang ist ein Marathon in Kilometern?', '42,195 km', '40 km', '45 km', '50 km', 'a',
(SELECT id FROM categories WHERE name='Sport'), 'Easy'),
('Wie oft hat Deutschland die Fußball-WM gewonnen (Stand 2021)?', '4', '3', '5', '2', 'a',
(SELECT id FROM categories WHERE name='Sport'), 'Easy'),
('Wer hält den Rekord für die meisten Grand-Slam-Titel im Herrentennis?', 'Roger Federer', 'Rafael Nadal', 'Novak Djokovic', 'Pete Sampras', 'c',
(SELECT id FROM categories WHERE name='Sport'), 'Medium'),
('Wie viele Ringe sind auf der Olympischen Flagge?', '5', '4', '6', '7', 'a',
(SELECT id FROM categories WHERE name='Sport'), 'Medium'),
('Welches Land gewann die erste Fußball-WM 1930?', 'Uruguay', 'Argentinien', 'Brasilien', 'Italien', 'a',
(SELECT id FROM categories WHERE name='Sport'), 'Medium'),
('Wie viele Minuten dauert ein NBA-Spiel?', '48', '40', '60', '36', 'a',
(SELECT id FROM categories WHERE name='Sport'), 'Medium'),
('Wer gewann die Tour de France 2020?', 'Tadej Pogačar', 'Egan Bernal', 'Primož Roglič', 'Chris Froome', 'a',
(SELECT id FROM categories WHERE name='Sport'), 'Hard'),
('Wie viele olympische Medaillen hat Michael Phelps insgesamt gewonnen?', '28', '22', '26', '24', 'a',
(SELECT id FROM categories WHERE name='Sport'), 'Hard'),
('Wer spielte den Joker in „The Dark Knight“?', 'Heath Ledger', 'Jack Nicholson', 'Jared Leto', 'Joaquin Phoenix', 'a',
(SELECT id FROM categories WHERE name='Unterhaltung'), 'Easy'),
('In welcher Serie ist Walter White die Hauptfigur?', 'Breaking Bad', 'Narcos', 'The Wire', 'Ozark', 'a',
(SELECT id FROM categories WHERE name='Unterhaltung'), 'Easy'),
('Wer sang den Hit „Thriller“?', 'Michael Jackson', 'Prince', 'Madonna', 'Elton John', 'a',
(SELECT id FROM categories WHERE name='Unterhaltung'), 'Easy'),
('Welches Studio produziert die Original-Star-Wars-Filme?', 'Lucasfilm', 'Marvel', 'Pixar', 'Warner Bros.', 'a',
(SELECT id FROM categories WHERE name='Unterhaltung'), 'Easy'),
('Wer schrieb die „Harry Potter“-Romane?', 'J.K. Rowling', 'Stephen King', 'J.R.R. Tolkien', 'C.S. Lewis', 'a',
(SELECT id FROM categories WHERE name='Unterhaltung'), 'Medium'),
('Welcher Film gewann 2020 den Oscar für den Besten Film?', 'Parasite', '1917', 'Joker', 'Once Upon a Time in Hollywood', 'a',
(SELECT id FROM categories WHERE name='Unterhaltung'), 'Medium'),
('Welche Band veröffentlichte „Bohemian Rhapsody“?', 'Queen', 'The Beatles', 'Pink Floyd', 'Led Zeppelin', 'a',
(SELECT id FROM categories WHERE name='Unterhaltung'), 'Medium'),
('Welche Schauspielerin verkörpert „Black Widow“?', 'Scarlett Johansson', 'Gal Gadot', 'Brie Larson', 'Zoe Saldana', 'a',
(SELECT id FROM categories WHERE name='Unterhaltung'), 'Medium'),
('Wer führte Regie bei „2001: A Space Odyssey“?', 'Stanley Kubrick', 'Steven Spielberg', 'George Lucas', 'Ridley Scott', 'a',
(SELECT id FROM categories WHERE name='Unterhaltung'), 'Hard'),
('Welcher Film hat laut IMDb die höchste Bewertung (Stand 2021)?', 'The Shawshank Redemption', 'The Godfather', 'The Dark Knight', 'Pulp Fiction', 'a',
(SELECT id FROM categories WHERE name='Unterhaltung'), 'Hard'),
('Wer schrieb „Die Leiden des jungen Werther“?', 'Johann Wolfgang von Goethe', 'Friedrich Schiller', 'Franz Kafka', 'Thomas Mann', 'a',
(SELECT id FROM categories WHERE name='Literatur'), 'Easy'),
('Wer ist Autor von „Der kleine Prinz“?', 'Antoine de Saint-Exupéry', 'Jules Verne', 'Hermann Hesse', 'Albert Camus', 'a',
(SELECT id FROM categories WHERE name='Literatur'), 'Easy'),
('Welche Romanfigur stammt von William Shakespeare?', 'Hamlet', 'Moby Dick', 'Faust', 'Odyssee', 'a',
(SELECT id FROM categories WHERE name='Literatur'), 'Easy'),
('Wer schrieb „Der Herr der Ringe“?', 'J.R.R. Tolkien', 'J.K. Rowling', 'George R.R. Martin', 'C.S. Lewis', 'a',
(SELECT id FROM categories WHERE name='Literatur'), 'Easy'),
('Wer schrieb „Hundert Jahre Einsamkeit“?', 'Gabriel García Márquez', 'Pablo Neruda', 'Jorge Luis Borges', 'Octavio Paz', 'a',
(SELECT id FROM categories WHERE name='Literatur'), 'Medium'),
('In welcher Epoche lebte Goethe?', 'Aufklärung', 'Romantik', 'Renaissance', 'Barock', 'a',
(SELECT id FROM categories WHERE name='Literatur'), 'Medium'),
('Wer ist Protagonist in „1984“?', 'Winston Smith', 'John Doe', 'Guy Montag', 'Holden Caulfield', 'a',
(SELECT id FROM categories WHERE name='Literatur'), 'Medium'),
('Welches Buch beginnt mit „Es war die beste aller Zeiten“?', 'A Tale of Two Cities', 'Great Expectations', 'War and Peace', 'Jane Eyre', 'a',
(SELECT id FROM categories WHERE name='Literatur'), 'Medium'),
('Wer übersetzte Homers „Ilias“ ins Deutsche?', 'Johann Heinrich Voss', 'Goethe', 'Schiller', 'Gottfried Herder', 'a',
(SELECT id FROM categories WHERE name='Literatur'), 'Hard'),
('Welches Werk schrieb Dante Alighieri?', 'Die göttliche Komödie', 'Paradise Lost', 'Inferno', 'La Vita Nuova', 'a',
(SELECT id FROM categories WHERE name='Literatur'), 'Hard'),
('Wofür steht die Abkürzung CPU?', 'Central Processing Unit', 'Computer Personal Unit', 'Central Performance Unit', 'Computer Processing Unit', 'a',
(SELECT id FROM categories WHERE name='Technologie'), 'Easy'),
('Welche Sprache ist allgegenwärtig in der Webentwicklung?', 'JavaScript', 'Fortran', 'COBOL', 'Pascal', 'a',
(SELECT id FROM categories WHERE name='Technologie'), 'Easy'),
('Was ist ein Betriebssystem?', 'Software zur Hardwareverwaltung', 'Programmiersprache', 'Datenbank', 'Browser', 'a',
(SELECT id FROM categories WHERE name='Technologie'), 'Easy'),
('Welcher Dienst ist bekannt für Cloud-Speicher?', 'Dropbox', 'VLC', 'WinRAR', 'Flash', 'a',
(SELECT id FROM categories WHERE name='Technologie'), 'Easy'),
('Welcher Standard definiert WLAN-Netze?', 'IEEE 802.11', 'IEEE 802.3', 'IEEE 802.5', 'IEEE 803.2', 'a',
(SELECT id FROM categories WHERE name='Technologie'), 'Medium'),
('Welcher Algorithmus sortiert durchschnittlich sehr effizient?', 'Quicksort', 'Bubblesort', 'Insertionsort', 'Selectionsort', 'a',
(SELECT id FROM categories WHERE name='Technologie'), 'Medium'),
('Aus welchem Speichertyp bestehen SSDs hauptsächlich?', 'Flash-Speicher', 'Magnetplatten', 'Bandlaufwerke', 'Optische Scheiben', 'a',
(SELECT id FROM categories WHERE name='Technologie'), 'Medium'),
('Was beschreibt REST in Web-APIs?', 'Architekturprinzip', 'Programmiersprache', 'Datenbank', 'Verschlüsselung', 'a',
(SELECT id FROM categories WHERE name='Technologie'), 'Medium'),
('Was besagt das CAP-Theorem?', 'Konsistenz-Verfügbarkeit-Partitionstoleranz', 'Konsistenz-Parallelität-Performance', 'Cache-Availability-Partitioning', 'Consistency-Atomicity-Performance', 'a',
(SELECT id FROM categories WHERE name='Technologie'), 'Hard'),
('Wofür steht JSON?', 'JavaScript Object Notation', 'Java Source Open Network', 'JavaScript Oriented Notation', 'Just Some Object Notation', 'a',
(SELECT id FROM categories WHERE name='Technologie'), 'Hard'),
('Was ist 2 + 2?', '3', '4', '5', '6', 'b',
(SELECT id FROM categories WHERE name='Mathematik'), 'Easy'),
('Wie nennt man ein Viereck mit vier gleichen Seiten?', 'Quadrat', 'Rechteck', 'Raute', 'Kreis', 'a',
(SELECT id FROM categories WHERE name='Mathematik'), 'Easy'),
('Wie viele Grad hat ein rechter Winkel?', '90', '180', '45', '60', 'a',
(SELECT id FROM categories WHERE name='Mathematik'), 'Easy'),
('Wert von π (ungefähr)?', '3.14', '2.72', '1.62', '4.13', 'a',
(SELECT id FROM categories WHERE name='Mathematik'), 'Easy'),
('Lösung der Gleichung x + 3 = 7?', '4', '5', '3', '10', 'a',
(SELECT id FROM categories WHERE name='Mathematik'), 'Medium'),
('Ableitung von x²?', '2x', 'x', 'x²', '1', 'a',
(SELECT id FROM categories WHERE name='Mathematik'), 'Medium'),
('Formel für Kreisfläche?', 'π·r²', '2·π·r', 'π·d', 'π·r', 'a',
(SELECT id FROM categories WHERE name='Mathematik'), 'Medium'),
('Nullstelle einer Funktion f(x)?', 'Wert von x bei f(x)=0', 'Maximum', 'Minimum', 'Durchschnitt', 'a',
(SELECT id FROM categories WHERE name='Mathematik'), 'Medium'),
('Binomische Formel (a+b)²?', 'a² + 2ab + b²', 'a² − 2ab + b²', 'a² + b²', '2a² + 2b² + ab', 'a',
(SELECT id FROM categories WHERE name='Mathematik'), 'Hard'),
('limₓ→0 sin(x)/x?', '1', '0', '∞', '-1', 'a',
(SELECT id FROM categories WHERE name='Mathematik'), 'Hard'),
('Wer malte die Mona Lisa?', 'Leonardo da Vinci', 'Michelangelo', 'Raphael', 'Pablo Picasso', 'a',
(SELECT id FROM categories WHERE name='Kunst'), 'Easy'),
('Welche Farbe erhält man aus Rot + Weiß?', 'Rosa', 'Lila', 'Orange', 'Braun', 'a',
(SELECT id FROM categories WHERE name='Kunst'), 'Easy'),
('Was ist eine Skulptur?', 'Dreidimensionales Kunstwerk', 'Gemälde', 'Musikstück', 'Gedicht', 'a',
(SELECT id FROM categories WHERE name='Kunst'), 'Easy'),
('Wer komponierte die „Mondscheinsonate“?', 'Ludwig van Beethoven', 'Wolfgang Amadeus Mozart', 'Johann Sebastian Bach', 'Frédéric Chopin', 'a',
(SELECT id FROM categories WHERE name='Kunst'), 'Easy'),
('Welcher Stil wird häufig mit Pablo Picasso verbunden?', 'Kubismus', 'Impressionismus', 'Surrealismus', 'Barock', 'a',
(SELECT id FROM categories WHERE name='Kunst'), 'Medium'),
('Wer schuf die Skulptur „David“?', 'Michelangelo', 'Donatello', 'Bernini', 'Rodin', 'a',
(SELECT id FROM categories WHERE name='Kunst'), 'Medium'),
('Welches Kunstwerk malte Edvard Munch?', 'Der Schrei', 'Die Sternennacht', 'Die Erschaffung Adams', 'Guernica', 'a',
(SELECT id FROM categories WHERE name='Kunst'), 'Medium'),
('In welcher Epoche entstanden die Höhlenmalereien von Lascaux?', 'Prähistorie', 'Renaissance', 'Barock', 'Moderne', 'a',
(SELECT id FROM categories WHERE name='Kunst'), 'Medium'),
('Welcher Maler war ein Hauptvertreter des Surrealismus?', 'Salvador Dalí', 'Claude Monet', 'Caravaggio', 'Vincent van Gogh', 'a',
(SELECT id FROM categories WHERE name='Kunst'), 'Hard'),
('Wer schuf das Gemälde „Die Schule von Athen“?', 'Raffael', 'Leonardo da Vinci', 'Hieronymus Bosch', 'Peter Paul Rubens', 'a',
(SELECT id FROM categories WHERE name='Kunst'), 'Hard');
CREATE TABLE IF NOT EXISTS stats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER,
score INTEGER NOT NULL,
total INTEGER NOT NULL,
challenge_type TEXT,
challenge_ref INTEGER,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE SET NULL
);
");
// Ersten Admin anlegen, falls keiner existiert
$adminCount = $pdo->query("SELECT COUNT(*) FROM users WHERE role='admin'")->fetchColumn();
if ($adminCount == 0) {
$hash = password_hash('admin123', PASSWORD_DEFAULT);
$pdo->prepare("INSERT INTO users(username,password,role) VALUES('admin',?, 'admin')")
->execute([$hash]);
}
// Standard‐Kategorie anlegen, falls keine existiert
$catCount = $pdo->query("SELECT COUNT(*) FROM categories")->fetchColumn();
if ($catCount == 0) {
$pdo->exec("INSERT INTO categories(name) VALUES('Allgemein')");
}
// --- INTERNATIONALISIERUNG ---------------------------------------------------
$available = ['de','en','fr','it','es','tr'];
if (isset($_GET['lang']) && in_array($_GET['lang'],$available)) {
$_SESSION['lang'] = $_GET['lang'];
}
$lang = $_SESSION['lang'] ?? 'de';
$T = [
'de'=>[
'welcome'=>'Willkommen',
'login'=>'Login',
'register'=>'Registrieren',
'logout'=>'Logout',
'start_quiz'=>'Quiz starten',
'admin_dashboard'=>'Admin-Dashboard',
'categories'=>'Kategorien verwalten',
'profile'=>'Profil',
'progress'=>'Fortschritt',
'question_management'=>'Fragenverwaltung',
'stats'=>'Statistiken',
'export'=>'Exportieren',
'challenge_mode'=>'Challenge-Modus',
'standard'=>'Standard',
'time_category'=>'Zeit-Kategorie',
'time_difficulty'=>'Zeit-Schwierigkeit',
'category'=>'Kategorie',
'difficulty'=>'Schwierigkeit',
'limit'=>'Fragenanzahl',
'fill_all'=>'Bitte alle Felder ausfüllen.',
'user_exists'=>'Benutzername existiert bereits.',
'register_success'=>'Registrierung erfolgreich.',
'login_failed'=>'Login fehlgeschlagen.'
],
'en'=>[
'welcome'=>'Welcome',
'login'=>'Login',
'register'=>'Register',
'logout'=>'Logout',
'start_quiz'=>'Start Quiz',
'admin_dashboard'=>'Admin Dashboard',
'categories'=>'Manage Categories',
'profile'=>'Profile',
'progress'=>'Progress',
'question_management'=>'Manage Questions',
'stats'=>'Statistics',
'export'=>'Export',
'challenge_mode'=>'Challenge Mode',
'standard'=>'Standard',
'time_category'=>'Time by Category',
'time_difficulty'=>'Time by Difficulty',
'category'=>'Category',
'difficulty'=>'Difficulty',
'limit'=>'Number of Questions',
'fill_all'=>'Please fill all fields.',
'user_exists'=>'Username already exists.',
'register_success'=>'Registration successful.',
'login_failed'=>'Login failed.'
],
'fr'=>[
'welcome'=>'Bienvenue',
'login'=>'Connexion',
'register'=>'S’inscrire',
'logout'=>'Déconnexion',
'start_quiz'=>'Commencer le quiz',
'admin_dashboard'=>'Tableau de bord',
'categories'=>'Gérer les catégories',
'profile'=>'Profil',
'progress'=>'Progrès',
'question_management'=>'Gérer les questions',
'stats'=>'Statistiques',
'export'=>'Exporter',
'challenge_mode'=>'Mode défi',
'standard'=>'Standard',
'time_category'=>'Temps-par-catégorie',
'time_difficulty'=>'Temps-par-difficulté',
'category'=>'Catégorie',
'difficulty'=>'Difficulté',
'limit'=>'Nombre de questions',
'fill_all'=>'Veuillez remplir tous les champs.',
'user_exists'=>'Nom d’utilisateur déjà existant.',
'register_success'=>'Inscription réussie.',
'login_failed'=>'Échec de la connexion.'
],
'it'=>[
'welcome'=>'Benvenuto',
'login'=>'Accesso',
'register'=>'Registrati',
'logout'=>'Esci',
'start_quiz'=>'Inizia quiz',
'admin_dashboard'=>'Pannello Admin',
'categories'=>'Gestisci categorie',
'profile'=>'Profilo',
'progress'=>'Progresso',
'question_management'=>'Gestione domande',
'stats'=>'Statistiche',
'export'=>'Esporta',
'challenge_mode'=>'Modalità sfida',
'standard'=>'Standard',
'time_category'=>'Tempo-per-categoria',
'time_difficulty'=>'Tempo-per-difficoltà',
'category'=>'Categoria',
'difficulty'=>'Difficoltà',
'limit'=>'Numero di domande',
'fill_all'=>'Compila tutti i campi.',
'user_exists'=>'Nome utente già esistente.',
'register_success'=>'Registrazione avvenuta con successo.',
'login_failed'=>'Accesso fallito.'
],
'es'=>[
'welcome'=>'Bienvenido',
'login'=>'Iniciar sesión',
'register'=>'Registrarse',
'logout'=>'Cerrar sesión',
'start_quiz'=>'Iniciar cuestionario',
'admin_dashboard'=>'Panel Admin',
'categories'=>'Gestionar categorías',
'profile'=>'Perfil',
'progress'=>'Progreso',
'question_management'=>'Gestionar preguntas',
'stats'=>'Estadísticas',
'export'=>'Exportar',
'challenge_mode'=>'Modo desafío',
'standard'=>'Estándar',
'time_category'=>'Tiempo-por-categoría',
'time_difficulty'=>'Tiempo-por-dificultad',
'category'=>'Categoría',
'difficulty'=>'Dificultad',
'limit'=>'Número de preguntas',
'fill_all'=>'Por favor completa todos los campos.',
'user_exists'=>'El nombre de usuario ya existe.',
'register_success'=>'Registro exitoso.',
'login_failed'=>'Error de inicio de sesión.'
],
'tr'=>[
'welcome'=>'Hoş geldiniz',
'login'=>'Giriş',
'register'=>'Kayıt ol',
'logout'=>'Çıkış',
'start_quiz'=>'Quiz’i Başlat',
'admin_dashboard'=>'Admin Paneli',
'categories'=>'Kategorileri Yönet',
'profile'=>'Profil',
'progress'=>'İlerleme',
'question_management'=>'Soruları Yönet',
'stats'=>'İstatistikler',
'export'=>'Dışa Aktar',
'challenge_mode'=>'Meydan Okuma Modu',
'standard'=>'Standart',
'time_category'=>'Kategoriye Göre Süre',
'time_difficulty'=>'Zorluk’a Göre Süre',
'category'=>'Kategori',
'difficulty'=>'Zorluk',
'limit'=>'Soru Sayısı',
'fill_all'=>'Lütfen tüm alanları doldurun.',
'user_exists'=>'Kullanıcı adı zaten var.',
'register_success'=>'Kayıt başarılı.',
'login_failed'=>'Giriş başarısız.'
]
];
function t($key){ global $T,$lang; return $T[$lang][$key] ?? $key; }
// --- JWT‐HELPER --------------------------------------------------------------
function generateJWT($uid){
$payload = ['sub'=>$uid, 'iat'=>time(), 'exp'=>time()+3600];
return JWT::encode($payload, JWT_SECRET, 'HS256');
}
function verifyJWT(){
if (!isset($_SERVER['HTTP_AUTHORIZATION'])) return false;
list(,$jwt)=explode(' ', $_SERVER['HTTP_AUTHORIZATION']);
try {
$p = JWT::decode($jwt, JWT_SECRET, ['HS256']);
return $p->sub;
} catch(Exception $e){
return false;
}
}
// --- OAUTH GOOGLE FLOW -------------------------------------------------------
if (isset($_GET['oauth']) && $_GET['oauth'] === 'google') {
// Callback
if (isset($_GET['code'])) {
$post = http_build_query([
'code'=>$_GET['code'],
'client_id'=>OAUTH_GOOGLE_CLIENT_ID,
'client_secret'=>OAUTH_GOOGLE_CLIENT_SECRET,
'redirect_uri'=>OAUTH_REDIRECT_URI,
'grant_type'=>'authorization_code'
]);
$opts = ['http'=>[
'method'=>'POST',
'header'=>"Content-Type: application/x-www-form-urlencoded",
'content'=>$post
]];
$tokenRes = file_get_contents('https://oauth2.googleapis.com/token', false, stream_context_create($opts));
$tok = json_decode($tokenRes, true);
// Userinfo
$info = json_decode(file_get_contents(
"https://openidconnect.googleapis.com/v1/userinfo?access_token=".$tok['access_token']
), true);
// In DB anlegen oder laden
$stmt = $pdo->prepare("SELECT * FROM users WHERE provider='google' AND oauth_id=?");
$stmt->execute([$info['sub']]);
if (!$user = $stmt->fetch(PDO::FETCH_ASSOC)) {
$pdo->prepare("INSERT INTO users(username,provider,oauth_id,role) VALUES(?,?,?,'user')")
->execute([$info['email'],'google',$info['sub']]);
$uid = $pdo->lastInsertId();
$pdo->prepare("INSERT INTO profiles(user_id) VALUES(?)")->execute([$uid]);
} else {
$uid = $user['id'];
}
$_SESSION['user_id']=$uid;
$_SESSION['username']=$info['email'];
$_SESSION['role']='user';
$_SESSION['jwt']=generateJWT($uid);
header('Location:index.php'); exit;
}
// Redirect zu Google
$authUrl = 'https://accounts.google.com/o/oauth2/v2/auth?'. http_build_query([
'client_id'=>OAUTH_GOOGLE_CLIENT_ID,
'redirect_uri'=>OAUTH_REDIRECT_URI,
'response_type'=>'code',
'scope'=>'openid email profile'
]);
header("Location: $authUrl"); exit;
}
// --- API‐ENDPOINTS ------------------------------------------------------------
if (isset($_GET['api'])) {
header('Content-Type: application/json');
$uid = verifyJWT();
switch($_GET['api']) {
// Fragen abrufen
case 'questions':
$cat = intval($_GET['category'] ?? 0);
$diff = $_GET['difficulty'] ?? '';
$lim = intval($_GET['limit'] ?? 5);
$sql = "SELECT q.id,q.question,q.option_a,q.option_b,q.option_c,q.option_d,
c.name AS category,q.difficulty
FROM questions q
JOIN categories c ON c.id=q.category_id WHERE 1";
$params = [];
if ($cat) { $sql .= " AND q.category_id=?"; $params[]=$cat; }
if ($diff) { $sql .= " AND q.difficulty=?"; $params[]=$diff; }
$sql .= " ORDER BY RANDOM() LIMIT $lim";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC));
break;
// Antworten auswerten
case 'submit':
$data = json_decode(file_get_contents('php://input'), true);
$answers = $data['answers'] ?? [];
$challenge = $data['challenge'] ?? 'standard';
$category = intval($data['category'] ?? 0);
$score = 0; $total = count($answers);
foreach ($answers as $qid => $ans) {
$c = $pdo->prepare("SELECT correct_option FROM questions WHERE id=?");
$c->execute([$qid]);
if ($c->fetchColumn() === $ans) $score++;
}
$pdo->prepare("
INSERT INTO stats(user_id,score,total,challenge_type,challenge_ref)
VALUES(?,?,?,?,?)
")->execute([ $uid, $score, $total, $challenge, $category ]);
echo json_encode(['score'=>$score,'total'=>$total]);
break;
// Export CSV/JSON (nur Admin)
case 'export':
if (($_SESSION['role'] ?? '') !== 'admin') {
echo json_encode([]); exit;
}
$fmt = $_GET['format'] ?? 'json';
$rows = $pdo->query("
SELECT s.id,u.username,s.score,s.total,s.challenge_type,s.challenge_ref,s.timestamp
FROM stats s
LEFT JOIN users u ON u.id=s.user_id
ORDER BY s.timestamp DESC
")->fetchAll(PDO::FETCH_ASSOC);
if ($fmt === 'csv') {
header('Content-Type: text/csv');
header('Content-Disposition: attachment;filename=stats.csv');
$out = fopen('php://output', 'w');
fputcsv($out, array_keys($rows[0] ?? []));
foreach ($rows as $r) fputcsv($out, $r);
fclose($out);
} else {
echo json_encode($rows);
}
break;
}
exit;
}
// --- FORMULAR‐HANDLER --------------------------------------------------------
// Registrierung
if (isset($_POST['do_register'])) {
$u = trim($_POST['username']); $p = $_POST['password'];
if ($u && $p) {
$h = password_hash($p, PASSWORD_DEFAULT);
try {
$pdo->prepare("INSERT INTO users(username,password) VALUES(?,?)")
->execute([$u,$h]);
$uid = $pdo->lastInsertId();
$pdo->prepare("INSERT INTO profiles(user_id) VALUES(?)")
->execute([$uid]);
$msg = "✅ " . t('register_success');
} catch(Exception $e) {
$msg = "⚠️ " . t('user_exists');
}
} else {
$msg = "⚠️ " . t('fill_all');
}
}
// Login
if (isset($_POST['do_login'])) {
$u = trim($_POST['username']); $p = $_POST['password'];
$st = $pdo->prepare("SELECT * FROM users WHERE username=?");
$st->execute([$u]); $usr = $st->fetch(PDO::FETCH_ASSOC);
if ($usr && password_verify($p, $usr['password'])) {
$_SESSION['user_id'] = $usr['id'];
$_SESSION['username'] = $usr['username'];
$_SESSION['role'] = $usr['role'];
$_SESSION['jwt'] = generateJWT($usr['id']);
header("Location:index.php"); exit;
} else {
$msg = "⚠️ " . t('login_failed');
}
}
// Logout
if (isset($_GET['action']) && $_GET['action'] === 'logout') {
session_destroy(); header("Location:index.php"); exit;
}
// Admin: Kategorie CRUD
if (isset($_POST['do_add_cat']) && ($_SESSION['role'] ?? '')==='admin') {
$name = trim($_POST['cat_name']);
if ($name) {
$pdo->prepare("INSERT INTO categories(name) VALUES(?)")
->execute([$name]);
$msg = "✅ " . t('categories') . " angelegt.";
} else {
$msg = "⚠️ " . t('fill_all');
}
}
if (isset($_POST['do_del_cat']) && ($_SESSION['role'] ?? '')==='admin') {
$pdo->prepare("DELETE FROM categories WHERE id=?")
->execute([ $_POST['cat_id'] ]);
$msg = "🗑️ Kategorie gelöscht.";
}
// Admin: Frage CRUD
if (isset($_POST['do_add_q']) && ($_SESSION['role'] ?? '')==='admin') {
$f = ['question','option_a','option_b','option_c','option_d','correct_option','category_id','difficulty'];
foreach($f as $k) $$k = $_POST[$k] ?? '';
if ($question && $option_a && $option_b && $option_c && $option_d && $correct_option && $category_id && $difficulty) {
$pdo->prepare("
INSERT INTO questions
(question,option_a,option_b,option_c,option_d,correct_option,category_id,difficulty)
VALUES (?,?,?,?,?,?,?,?)
")->execute([$question,$option_a,$option_b,$option_c,$option_d,$correct_option,$category_id,$difficulty]);
$msg = "✅ Frage gespeichert.";
} else {
$msg = "⚠️ " . t('fill_all');
}
}
if (isset($_POST['do_del_q']) && ($_SESSION['role'] ?? '')==='admin') {
$pdo->prepare("DELETE FROM questions WHERE id=?")
->execute([ $_POST['qid'] ]);
$msg = "🗑️ Frage gelöscht.";
}
// --- PAGE OUTPUT ------------------------------------------------------------
$logged = isset($_SESSION['user_id']);
?>
<!DOCTYPE html>
<html lang="<?=$lang?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Dreamcodes - Quiz Master</title>
<style>
* { box-sizing:border-box; margin:0; padding:0 }
body { font-family:'Segoe UI',sans-serif; background:#1f1c2c; color:#eee; }
header, main, footer { max-width:900px; margin:auto; padding:1rem }
a { color:#0af; text-decoration:none }
button,input,select,textarea { font:inherit }
header { display:flex; justify-content:space-between; align-items:center }
nav select { background:#2c2c3c; color:#eee; border:none; padding:.5rem; }
.msg { margin:1rem 0; padding:.75rem 1rem; border-radius:5px }
.success { background:#184f2e; color:#b5f5c1 }
.error { background:#4f1e1e; color:#f5b5b5 }
form { background:#2c2c3c; padding:1rem; border-radius:8px; margin:1rem 0 }
form > * + * { margin-top:.75rem }
input,select,textarea { width:100%; padding:.5rem; border:none; border-radius:4px }
button { padding:.6rem 1rem; background:#0af; color:#1f1c2c; border:none; border-radius:4px; cursor:pointer }
button:hover { background:#06f }
table { width:100%; border-collapse:collapse; margin:1rem 0 }
th,td { padding:.5rem; border:1px solid #444; text-align:left }
th { background:#333 }
section { margin:2rem 0 }
.grid { display:grid; gap:1rem; }
@media(min-width:600px){ .grid-2 { grid-template-columns:1fr 1fr } }
</style>
</head>
<body>
<header>
<h1>🧠 Quiz Master</h1>
<nav>
<?php if($logged): ?>
<span><?=htmlspecialchars($_SESSION['username'])?></span>
<a href="?action=logout"><?=t('logout')?></a>
<select onchange="location='?lang='+this.value">
<?php foreach($available as $l): ?>
<option <?=$l===$lang?'selected':''?> value="<?=$l?>"><?=$l?></option>
<?php endforeach; ?>
</select>
<?php else: ?>
<select onchange="location='?lang='+this.value">
<?php foreach($available as $l): ?>
<option <?=$l===$lang?'selected':''?> value="<?=$l?>"><?=$l?></option>
<?php endforeach; ?>
</select>
<?php endif; ?>
</nav>
</header>
<main>
<?php if(!empty($msg)): ?>
<div class="msg <?=strpos($msg,'✅')===0?'success':'error'?>">
<?=$msg?>
</div>
<?php endif; ?>
<?php if(!$logged): ?>
<!-- PUBLIC: Registrieren & Login -->
<div class="grid grid-2">
<form method="post">
<h2><?=t('register')?></h2>
<input name="username" placeholder="<?=t('register')?>" required>
<input name="password" type="password" placeholder="<?=t('register')?>" required>
<button name="do_register"><?=t('register')?></button>
</form>
<form method="post">
<h2><?=t('login')?></h2>
<input name="username" placeholder="<?=t('login')?>" required>
<input name="password" type="password" placeholder="<?=t('login')?>" required>
<button name="do_login"><?=t('login')?></button>
</form>
</div>
<p>— oder —</p>
<a href="?oauth=google">Mit Google anmelden</a>
<?php else: ?>
<!-- USER PROFILE & PROGRESS -->
<section id="profile">
<h2><?=t('profile')?> — <?=t('welcome')?>, <?=htmlspecialchars($_SESSION['username'])?></h2>
<h3><?=t('progress')?></h3>
<table>
<tr><th><?=t('stats')?></th><th><?=t('progress')?></th><th><?=t('challenge_mode')?></th><th><?=t('start_quiz')?></th></tr>
<?php
$ps = $pdo->prepare("
SELECT score,total,challenge_type,challenge_ref,timestamp
FROM stats WHERE user_id=? ORDER BY timestamp DESC LIMIT 10
"); $ps->execute([$_SESSION['user_id']]);
while($r=$ps->fetch(PDO::FETCH_ASSOC)){
echo "<tr>
<td>{$r['timestamp']}</td>
<td>{$r['score']}/{$r['total']}</td>
<td>{$r['challenge_type']}</td>
<td></td>
</tr>";
}
?>
</table>
</section>
<?php if($_SESSION['role']==='admin'): ?>
<!-- ADMIN DASHBOARD -->
<section id="admin">
<h2><?=t('admin_dashboard')?></h2>
<!-- Kategorienverwaltung -->
<h3><?=t('categories')?></h3>
<form method="post" class="grid grid-2">
<input name="cat_name" placeholder="<?=t('category')?>" required>
<button name="do_add_cat"><?=t('categories')?></button>
</form>
<table>
<tr><th>ID</th><th><?=t('category')?></th><th>Aktion</th></tr>
<?php
$cats = $pdo->query("SELECT * FROM categories")->fetchAll(PDO::FETCH_ASSOC);
foreach($cats as $c){
echo "<tr>
<td>{$c['id']}</td>
<td>{$c['name']}</td>
<td>
<form method='post' style='display:inline'>
<input type='hidden' name='cat_id' value='{$c['id']}'>
<button name='do_del_cat'>🗑️</button>
</form>
</td>
</tr>";
}
?>
</table>
<!-- Fragenverwaltung -->
<h3><?=t('question_management')?></h3>
<form method="post" class="grid grid-2">
<textarea name="question" placeholder="Frage" required></textarea>
<div>
<input name="option_a" placeholder="Antwort A" required>
<input name="option_b" placeholder="Antwort B" required>
<input name="option_c" placeholder="Antwort C" required>
<input name="option_d" placeholder="Antwort D" required>
<select name="correct_option" required>
<option value=""><?=t('correct_option')?></option>
<option value="a">A</option><option value="b">B</option>
<option value="c">C</option><option value="d">D</option>
</select>
<select name="category_id" required>
<option value=""><?=t('category')?></option>
<?php foreach($cats as $c) echo "<option value='{$c['id']}'>{$c['name']}</option>"; ?>
</select>
<select name="difficulty" required>
<option value=""><?=t('difficulty')?></option>
<option>Easy</option><option>Medium</option><option>Hard</option>
</select>
</div>
<button name="do_add_q">✓ Frage anlegen</button>
</form>
<table>
<tr><th>ID</th><th>Frage (Kurz)</th><th>Kategorie</th><th>Diff.</th><th>Aktion</th></tr>
<?php
$allQ = $pdo->query("
SELECT q.id,q.question,c.name AS cat,q.difficulty
FROM questions q JOIN categories c ON c.id=q.category_id
ORDER BY q.id DESC
")->fetchAll(PDO::FETCH_ASSOC);
foreach($allQ as $q){
$short = mb_substr($q['question'],0,50) . (mb_strlen($q['question'])>50?'…':'');
echo "<tr>
<td>{$q['id']}</td>
<td>{$short}</td>
<td>{$q['cat']}</td>
<td>{$q['difficulty']}</td>
<td>
<form method='post' style='display:inline'>
<input type='hidden' name='qid' value='{$q['id']}'>
<button name='do_del_q'>🗑️</button>
</form>
</td>
</tr>";
}
?>
</table>
<!-- Statistiken & Export -->
<h3><?=t('stats')?></h3>
<a href="?api=export&format=json"><?=t('export')?> JSON</a> |
<a href="?api=export&format=csv"><?=t('export')?> CSV</a>
</section>
<?php endif; ?>
<!-- QUIZ‐INTERFACE -->
<section id="quiz">
<h2><?=t('start_quiz')?></h2>
<div class="grid grid-2">
<label>
<?=t('category')?>:
<select id="quiz-cat">
<option value="0"><?=t('all')?></option>
<?php foreach($cats as $c) echo "<option value='{$c['id']}'>{$c['name']}</option>"; ?>
</select>
</label>
<label>
<?=t('difficulty')?>:
<select id="quiz-diff">
<option value=""><?=t('all')?></option>
<option>Easy</option><option>Medium</option><option>Hard</option>
</select>
</label>
<label>
<?=t('limit')?>:
<select id="quiz-limit">
<option value="5">5</option><option value="10">10</option>
<option value="15">15</option>
</select>
</label>
<label>
<?=t('challenge_mode')?>:
<select id="quiz-challenge">
<option value="standard"><?=t('standard')?></option>
<option value="time_category"><?=t('time_category')?></option>
<option value="time_difficulty"><?=t('time_difficulty')?></option>
</select>
</label>
</div>
<button id="start-quiz"><?=t('start_quiz')?></button>
<div id="quiz-area" style="display:none; margin-top:1rem">
<div class="timer">⏱ <span id="time">0</span>s</div>
<div class="progress"><div id="prog-bar" class="progress-bar"></div></div>
<form id="quiz-form">
<div id="questions-container"></div>
<button type="submit"><?=t('submit')?></button>
</form>
<div id="quiz-result"></div>
</div>
</section>
<?php endif; ?>
</main>
<footer>
<p style="text-align:center; padding:1rem 0; font-size:.9em">
© <?= date('Y') ?> <a href="http://www.dreamcodes.net" target="_blank">
Dreamcodes
</a>
</p>
</footer>
<script>
const JWT = "<?= $_SESSION['jwt'] ?? '' ?>";
function apiFetch(path, opts={}){
opts.headers = opts.headers || {};
opts.headers['Authorization'] = 'Bearer ' + JWT;
return fetch(path, opts);
}
// Quiz‐Logik
let questions = [], timerInt;
document.getElementById('start-quiz').addEventListener('click', ()=>{
const cat = document.getElementById('quiz-cat').value;
const diff = document.getElementById('quiz-diff').value;
const lim = document.getElementById('quiz-limit').value;
const ch = document.getElementById('quiz-challenge').value;
apiFetch(`?api=questions&category=${cat}&difficulty=${diff}&limit=${lim}`)
.then(r=>r.json()).then(data=>{
questions = data;
renderQuiz(data, ch, cat);
});
});
function renderQuiz(data, challenge, category){
document.getElementById('quiz-area').style.display = 'block';
const container = document.getElementById('questions-container');
container.innerHTML = '';
data.forEach((q,i)=>{
const div = document.createElement('div');
div.className = 'question';
div.innerHTML = `
<p><strong>${i+1}.</strong> ${q.question}
<br><em>${q.category} / ${q.difficulty}</em></p>
${['a','b','c','d'].map(opt=>`
<label>
<input type="radio" name="ans_${q.id}" value="${opt}" required>
${q['option_'+opt]}
</label><br>
`).join('')}
`;
container.appendChild(div);
});
const timeSpan = document.getElementById('time');
const progBar = document.getElementById('prog-bar');
let t = 60;
if (challenge==='time_category' || challenge==='time_difficulty') {
t = (challenge==='time_category')
? 15 * data.length
: Math.max(...data.map(q=> ({Easy:30,Medium:45,Hard:60}[q.difficulty] )));
}
timeSpan.textContent = t;
progBar.style.width = '0%';
clearInterval(timerInt);
timerInt = setInterval(()=>{
t--;
timeSpan.textContent = t;
progBar.style.width = ((timerSpan=>(((parseInt(timerSpan.textContent)||0) / (t+1))*100)+'%')(timeSpan)) ;
if (t<=0) {
clearInterval(timerInt);
submitQuiz(challenge, category);
}
},1000);
document.getElementById('quiz-form').onsubmit = e => {
e.preventDefault();
clearInterval(timerInt);
submitQuiz(challenge, category);
};
}
function submitQuiz(challenge, category){
const answers = {};
document.querySelectorAll('#questions-container input:checked').forEach(inp=>{
answers[inp.name.replace('ans_','')] = inp.value;
});
apiFetch('?api=submit', {
method:'POST',
headers:{ 'Content-Type':'application/json' },
body: JSON.stringify({answers, challenge, category})
})
.then(r=>r.json())
.then(res=>{
document.getElementById('quiz-result').innerHTML = `
<div class="msg success">
Du hast <strong>${res.score}</strong> von <strong>${res.total}</strong> Punkten!
</div>`;
});
}
</script>
</body>
</html>