Online Quiz Application - Complete Project
Introduction to the Project
The Online Quiz Application is a comprehensive, interactive web-based platform designed for creating, managing, and taking quizzes online. This system provides a complete ecosystem for educators, trainers, and organizations to assess knowledge and skills effectively. With role-based access control, the application supports three user types: Admin, Instructors, and Students.
The platform features automated grading, instant feedback, detailed analytics, and a user-friendly interface that makes learning and assessment engaging and efficient. Whether used for academic institutions, corporate training, or casual learning, this quiz application offers all the essential features needed for modern online assessment.
Key Features
Admin Features
- Dashboard Overview: View platform statistics, active users, and quiz activities
- User Management: Create, edit, suspend, or delete user accounts
- Category Management: Create and manage quiz categories and subjects
- System Monitoring: Track overall platform performance and usage
- Report Generation: Access comprehensive reports and analytics
- Settings Management: Configure system parameters and global settings
Instructor Features
- Quiz Creation: Create unlimited quizzes with multiple question types
- Question Bank: Build and manage a repository of questions
- Quiz Scheduling: Set start and end times for quizzes
- Attempt Management: View student attempts and scores
- Grading Options: Automatic and manual grading capabilities
- Performance Analytics: View detailed statistics on student performance
- Feedback Provision: Provide feedback on quiz attempts
Student Features
- User Registration: Create account and manage profile
- Quiz Dashboard: View available, upcoming, and completed quizzes
- Take Quizzes: User-friendly interface for answering questions
- Instant Results: Immediate feedback and scoring
- Performance Tracking: View personal progress and history
- Certificate Generation: Download certificates for completed quizzes
- Leaderboards: Compare performance with peers
Technology Stack
- Frontend: HTML5, CSS3, JavaScript (Vanilla), Chart.js for analytics
- Backend: PHP (Core PHP, no frameworks)
- Database: MySQL
- Server: Apache (XAMPP/WAMP/MAMP recommended)
Project File Structure
online-quiz-application/ │ ├── assets/ │ ├── css/ │ │ ├── style.css │ │ └── responsive.css │ ├── js/ │ │ ├── main.js │ │ ├── quiz.js │ │ └── chart.js │ ├── images/ │ └── plugins/ │ └── chart.js/ │ ├── includes/ │ ├── config.php │ ├── functions.php │ ├── auth.php │ ├── register_process.php │ ├── login_process.php │ └── quiz_process.php │ ├── admin/ │ ├── dashboard.php │ ├── manage_users.php │ ├── manage_categories.php │ ├── manage_quizzes.php │ ├── system_reports.php │ └── settings.php │ ├── instructor/ │ ├── dashboard.php │ ├── create_quiz.php │ ├── edit_quiz.php │ ├── manage_questions.php │ ├── add_question.php │ ├── view_attempts.php │ ├── grade_quiz.php │ └── analytics.php │ ├── student/ │ ├── dashboard.php │ ├── available_quizzes.php │ ├── take_quiz.php │ ├── quiz_results.php │ ├── my_results.php │ ├── profile.php │ └── leaderboard.php │ ├── index.php ├── register.php ├── login.php ├── logout.php ├── forgot_password.php └── sql/ └── database.sql
Database Schema
File: sql/database.sql
-- Create Database
CREATE DATABASE IF NOT EXISTS `online_quiz_system`;
USE `online_quiz_system`;
-- Users Table
CREATE TABLE `users` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) UNIQUE NOT NULL,
`email` VARCHAR(100) UNIQUE NOT NULL,
`password` VARCHAR(255) NOT NULL,
`full_name` VARCHAR(100) NOT NULL,
`role` ENUM('admin', 'instructor', 'student') DEFAULT 'student',
`profile_picture` VARCHAR(255) DEFAULT 'default.jpg',
`bio` TEXT,
`institution` VARCHAR(255),
`is_active` BOOLEAN DEFAULT TRUE,
`email_verified` BOOLEAN DEFAULT FALSE,
`verification_token` VARCHAR(255),
`reset_token` VARCHAR(255),
`reset_expires` DATETIME,
`last_login` DATETIME,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
-- Categories Table
CREATE TABLE `categories` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`category_name` VARCHAR(100) NOT NULL,
`description` TEXT,
`icon` VARCHAR(50) DEFAULT 'fa-folder',
`created_by` INT(11),
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`created_by`) REFERENCES `users`(`id`)
);
-- Quizzes Table
CREATE TABLE `quizzes` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`title` VARCHAR(255) NOT NULL,
`description` TEXT,
`category_id` INT(11),
`instructor_id` INT(11) NOT NULL,
`duration_minutes` INT NOT NULL,
`total_marks` INT DEFAULT 0,
`passing_marks` INT DEFAULT 0,
`attempts_allowed` INT DEFAULT 1,
`shuffle_questions` BOOLEAN DEFAULT FALSE,
`show_results_immediately` BOOLEAN DEFAULT TRUE,
`randomize_options` BOOLEAN DEFAULT FALSE,
`status` ENUM('draft', 'published', 'archived') DEFAULT 'draft',
`start_date` DATETIME,
`end_date` DATETIME,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`category_id`) REFERENCES `categories`(`id`),
FOREIGN KEY (`instructor_id`) REFERENCES `users`(`id`)
);
-- Questions Table
CREATE TABLE `questions` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`quiz_id` INT(11) NOT NULL,
`question_text` TEXT NOT NULL,
`question_type` ENUM('multiple_choice', 'true_false', 'short_answer', 'essay') DEFAULT 'multiple_choice',
`points` INT DEFAULT 1,
`difficulty` ENUM('easy', 'medium', 'hard') DEFAULT 'medium',
`explanation` TEXT,
`order_number` INT,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`quiz_id`) REFERENCES `quizzes`(`id`) ON DELETE CASCADE
);
-- Options Table (for multiple choice questions)
CREATE TABLE `options` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`question_id` INT(11) NOT NULL,
`option_text` TEXT NOT NULL,
`is_correct` BOOLEAN DEFAULT FALSE,
`order_number` INT,
PRIMARY KEY (`id`),
FOREIGN KEY (`question_id`) REFERENCES `questions`(`id`) ON DELETE CASCADE
);
-- Quiz Attempts Table
CREATE TABLE `quiz_attempts` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`quiz_id` INT(11) NOT NULL,
`student_id` INT(11) NOT NULL,
`start_time` DATETIME NOT NULL,
`end_time` DATETIME,
`score` DECIMAL(5,2),
`percentage` DECIMAL(5,2),
`status` ENUM('in_progress', 'completed', 'graded') DEFAULT 'in_progress',
`ip_address` VARCHAR(45),
`user_agent` TEXT,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`quiz_id`) REFERENCES `quizzes`(`id`),
FOREIGN KEY (`student_id`) REFERENCES `users`(`id`)
);
-- Student Answers Table
CREATE TABLE `student_answers` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`attempt_id` INT(11) NOT NULL,
`question_id` INT(11) NOT NULL,
`selected_option_id` INT(11),
`answer_text` TEXT,
`is_correct` BOOLEAN,
`marks_obtained` DECIMAL(5,2),
`feedback` TEXT,
`graded_by` INT(11),
`graded_at` DATETIME,
PRIMARY KEY (`id`),
FOREIGN KEY (`attempt_id`) REFERENCES `quiz_attempts`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`question_id`) REFERENCES `questions`(`id`),
FOREIGN KEY (`selected_option_id`) REFERENCES `options`(`id`),
FOREIGN KEY (`graded_by`) REFERENCES `users`(`id`)
);
-- Certificates Table
CREATE TABLE `certificates` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`attempt_id` INT(11) NOT NULL,
`certificate_number` VARCHAR(50) UNIQUE NOT NULL,
`issued_date` DATE NOT NULL,
`pdf_path` VARCHAR(255),
PRIMARY KEY (`id`),
FOREIGN KEY (`attempt_id`) REFERENCES `quiz_attempts`(`id`)
);
-- Insert Default Admin
INSERT INTO `users` (`username`, `email`, `password`, `full_name`, `role`, `is_active`, `email_verified`)
VALUES ('admin', '[email protected]', '$2y$10$YourHashedPasswordHere', 'System Administrator', 'admin', TRUE, TRUE);
-- Insert Sample Categories
INSERT INTO `categories` (`category_name`, `description`, `icon`) VALUES
('Mathematics', 'Quizzes related to mathematics, algebra, geometry, and calculus', 'fa-calculator'),
('Science', 'Physics, Chemistry, Biology and general science quizzes', 'fa-flask'),
('History', 'World history, ancient civilizations, and modern events', 'fa-landmark'),
('Literature', 'Books, authors, poetry, and literary analysis', 'fa-book'),
('Computer Science', 'Programming, algorithms, databases, and IT concepts', 'fa-laptop'),
('General Knowledge', 'Mixed topics and trivia', 'fa-globe');
Core PHP Files
Database Configuration
File: includes/config.php
<?php
session_start();
// Database Configuration
define('DB_HOST', 'localhost');
define('DB_NAME', 'online_quiz_system');
define('DB_USER', 'root');
define('DB_PASS', '');
define('BASE_URL', 'http://localhost/online-quiz-application/');
define('SITE_NAME', 'Online Quiz Application');
try {
$pdo = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4", DB_USER, DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch(PDOException $e) {
die("Connection failed: " . $e->getMessage());
}
// Include functions file
require_once 'functions.php';
?>
Helper Functions
File: includes/functions.php
<?php
// Authentication Functions
function isLoggedIn() {
return isset($_SESSION['user_id']);
}
function isAdmin() {
return isset($_SESSION['user_role']) && $_SESSION['user_role'] == 'admin';
}
function isInstructor() {
return isset($_SESSION['user_role']) && $_SESSION['user_role'] == 'instructor';
}
function isStudent() {
return isset($_SESSION['user_role']) && $_SESSION['user_role'] == 'student';
}
function redirect($url) {
header("Location: " . BASE_URL . $url);
exit();
}
// Sanitization Functions
function sanitize($input) {
return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8');
}
function sanitizeArray($array) {
return array_map('sanitize', $array);
}
// Validation Functions
function validateEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
function validatePassword($password) {
// At least 8 characters, 1 uppercase, 1 lowercase, 1 number
return strlen($password) >= 8 &&
preg_match('/[A-Z]/', $password) &&
preg_match('/[a-z]/', $password) &&
preg_match('/[0-9]/', $password);
}
// Date Functions
function formatDate($date, $format = 'd M Y H:i') {
return date($format, strtotime($date));
}
function timeAgo($datetime) {
$time = strtotime($datetime);
$now = time();
$diff = $now - $time;
if ($diff < 60) {
return $diff . ' seconds ago';
} elseif ($diff < 3600) {
return floor($diff / 60) . ' minutes ago';
} elseif ($diff < 86400) {
return floor($diff / 3600) . ' hours ago';
} elseif ($diff < 2592000) {
return floor($diff / 86400) . ' days ago';
} else {
return formatDate($datetime);
}
}
// Quiz Functions
function calculatePercentage($obtained, $total) {
if ($total > 0) {
return round(($obtained / $total) * 100, 2);
}
return 0;
}
function isQuizAvailable($quiz) {
$now = date('Y-m-d H:i:s');
if ($quiz['start_date'] && $quiz['start_date'] > $now) {
return false;
}
if ($quiz['end_date'] && $quiz['end_date'] < $now) {
return false;
}
return $quiz['status'] == 'published';
}
function getUserAttempts($pdo, $quiz_id, $user_id) {
$stmt = $pdo->prepare("SELECT COUNT(*) FROM quiz_attempts WHERE quiz_id = ? AND student_id = ?");
$stmt->execute([$quiz_id, $user_id]);
return $stmt->fetchColumn();
}
// Display Functions
function showAlert($message, $type = 'success') {
return '<div class="alert alert-' . $type . '">' . htmlspecialchars($message) . '</div>';
}
function getAvatar($name) {
$initials = '';
$words = explode(' ', $name);
foreach ($words as $word) {
$initials .= strtoupper(substr($word, 0, 1));
}
return $initials;
}
// Pagination
function paginate($current_page, $total_pages, $url) {
$html = '<div class="pagination">';
if ($current_page > 1) {
$html .= '<a href="' . $url . '?page=' . ($current_page - 1) . '">« Previous</a>';
}
for ($i = 1; $i <= $total_pages; $i++) {
if ($i == $current_page) {
$html .= '<span class="active">' . $i . '</span>';
} else {
$html .= '<a href="' . $url . '?page=' . $i . '">' . $i . '</a>';
}
}
if ($current_page < $total_pages) {
$html .= '<a href="' . $url . '?page=' . ($current_page + 1) . '">Next »</a>';
}
$html .= '</div>';
return $html;
}
// Generate Random String
function generateRandomString($length = 32) {
return bin2hex(random_bytes($length / 2));
}
// File Upload
function uploadFile($file, $target_dir, $allowed_types = ['jpg', 'jpeg', 'png', 'gif']) {
$target_file = $target_dir . basename($file['name']);
$imageFileType = strtolower(pathinfo($target_file, PATHINFO_EXTENSION));
// Check if file is valid
if (!in_array($imageFileType, $allowed_types)) {
return ['error' => 'File type not allowed'];
}
// Generate unique filename
$new_filename = uniqid() . '.' . $imageFileType;
$target_path = $target_dir . $new_filename;
if (move_uploaded_file($file['tmp_name'], $target_path)) {
return ['success' => true, 'filename' => $new_filename];
} else {
return ['error' => 'Failed to upload file'];
}
}
?>
Authentication Check
File: includes/auth.php
<?php
require_once 'config.php';
function requireLogin() {
if (!isLoggedIn()) {
$_SESSION['error'] = 'Please login to access this page.';
redirect('login.php');
}
}
function requireAdmin() {
requireLogin();
if (!isAdmin()) {
$_SESSION['error'] = 'Access denied. Admin privileges required.';
redirect('index.php');
}
}
function requireInstructor() {
requireLogin();
if (!isInstructor() && !isAdmin()) {
$_SESSION['error'] = 'Access denied. Instructor privileges required.';
redirect('index.php');
}
}
function requireStudent() {
requireLogin();
if (!isStudent() && !isAdmin()) {
$_SESSION['error'] = 'Access denied. Student privileges required.';
redirect('index.php');
}
}
// Check quiz access
function canAccessQuiz($pdo, $quiz_id, $user_id) {
// Check if quiz exists and is published
$stmt = $pdo->prepare("SELECT * FROM quizzes WHERE id = ? AND status = 'published'");
$stmt->execute([$quiz_id]);
$quiz = $stmt->fetch();
if (!$quiz) {
return ['access' => false, 'message' => 'Quiz not found or not available.'];
}
// Check if within time limits
$now = date('Y-m-d H:i:s');
if ($quiz['start_date'] && $quiz['start_date'] > $now) {
return ['access' => false, 'message' => 'This quiz has not started yet.'];
}
if ($quiz['end_date'] && $quiz['end_date'] < $now) {
return ['access' => false, 'message' => 'This quiz has already ended.'];
}
// Check attempts
$attempts = getUserAttempts($pdo, $quiz_id, $user_id);
if ($attempts >= $quiz['attempts_allowed']) {
return ['access' => false, 'message' => 'You have used all your allowed attempts.'];
}
return ['access' => true, 'quiz' => $quiz];
}
?>
Registration Processing
File: includes/register_process.php
<?php
require_once 'config.php';
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username = sanitize($_POST['username']);
$email = sanitize($_POST['email']);
$password = $_POST['password'];
$confirm_password = $_POST['confirm_password'];
$full_name = sanitize($_POST['full_name']);
$institution = sanitize($_POST['institution'] ?? '');
$errors = [];
// Validation
if (empty($username) || empty($email) || empty($password) || empty($full_name)) {
$errors[] = "All fields are required.";
}
if (!preg_match('/^[a-zA-Z0-9_]{3,20}$/', $username)) {
$errors[] = "Username must be 3-20 characters and can only contain letters, numbers, and underscores.";
}
if (!validateEmail($email)) {
$errors[] = "Please enter a valid email address.";
}
if (!validatePassword($password)) {
$errors[] = "Password must be at least 8 characters with 1 uppercase, 1 lowercase, and 1 number.";
}
if ($password !== $confirm_password) {
$errors[] = "Passwords do not match.";
}
// Check if username/email exists
if (empty($errors)) {
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = ? OR email = ?");
$stmt->execute([$username, $email]);
if ($stmt->fetch()) {
$errors[] = "Username or Email already exists.";
}
}
// If no errors, create user
if (empty($errors)) {
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$verification_token = generateRandomString();
$sql = "INSERT INTO users (username, email, password, full_name, institution, verification_token, role)
VALUES (?, ?, ?, ?, ?, ?, 'student')";
$stmt = $pdo->prepare($sql);
if ($stmt->execute([$username, $email, $hashed_password, $full_name, $institution, $verification_token])) {
// Send verification email (simplified - in production, use PHPMailer)
$verification_link = BASE_URL . "verify.php?token=" . $verification_token;
$_SESSION['success'] = "Registration successful! Please check your email to verify your account.";
redirect("login.php");
} else {
$errors[] = "Registration failed. Please try again.";
}
}
// If errors exist
if (!empty($errors)) {
$_SESSION['errors'] = $errors;
$_SESSION['form_data'] = $_POST;
redirect("register.php");
}
} else {
redirect("register.php");
}
?>
Login Processing
File: includes/login_process.php
<?php
require_once 'config.php';
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username_email = sanitize($_POST['username_email']);
$password = $_POST['password'];
$remember = isset($_POST['remember']);
$errors = [];
if (empty($username_email) || empty($password)) {
$errors[] = "Both fields are required.";
}
if (empty($errors)) {
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? OR email = ?");
$stmt->execute([$username_email, $username_email]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
if (!$user['is_active']) {
$_SESSION['error'] = "Your account has been deactivated. Please contact administrator.";
redirect("login.php");
}
if (!$user['email_verified']) {
$_SESSION['error'] = "Please verify your email address before logging in.";
redirect("login.php");
}
// Set session
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['user_role'] = $user['role'];
$_SESSION['full_name'] = $user['full_name'];
// Update last login
$stmt = $pdo->prepare("UPDATE users SET last_login = NOW() WHERE id = ?");
$stmt->execute([$user['id']]);
// Remember me functionality
if ($remember) {
$token = generateRandomString(64);
$expires = date('Y-m-d H:i:s', strtotime('+30 days'));
// Store token in database (you'd need a remember_tokens table)
// For simplicity, we're setting a cookie directly
setcookie('remember_token', $token, time() + (86400 * 30), '/');
}
// Redirect based on role
switch($user['role']) {
case 'admin':
redirect("admin/dashboard.php");
break;
case 'instructor':
redirect("instructor/dashboard.php");
break;
default:
redirect("student/dashboard.php");
}
} else {
$errors[] = "Invalid username/email or password.";
}
}
if (!empty($errors)) {
$_SESSION['errors'] = $errors;
redirect("login.php");
}
} else {
redirect("login.php");
}
?>
Quiz Taking Processing
File: includes/quiz_process.php
<?php
require_once 'config.php';
require_once 'auth.php';
requireStudent();
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$action = $_POST['action'] ?? '';
// Start Quiz
if ($action == 'start_quiz') {
$quiz_id = $_POST['quiz_id'];
$student_id = $_SESSION['user_id'];
// Check access
$access = canAccessQuiz($pdo, $quiz_id, $student_id);
if (!$access['access']) {
$_SESSION['error'] = $access['message'];
redirect("student/available_quizzes.php");
}
$quiz = $access['quiz'];
// Create new attempt
$stmt = $pdo->prepare("INSERT INTO quiz_attempts (quiz_id, student_id, start_time, status) VALUES (?, ?, NOW(), 'in_progress')");
$stmt->execute([$quiz_id, $student_id]);
$attempt_id = $pdo->lastInsertId();
$_SESSION['current_attempt'] = $attempt_id;
$_SESSION['quiz_start_time'] = time();
$_SESSION['quiz_duration'] = $quiz['duration_minutes'] * 60;
redirect("student/take_quiz.php?attempt_id=" . $attempt_id);
}
// Submit Answer
elseif ($action == 'submit_answer') {
$attempt_id = $_POST['attempt_id'];
$question_id = $_POST['question_id'];
$answer = $_POST['answer'] ?? '';
$selected_option = $_POST['option_id'] ?? null;
// Verify attempt belongs to user
$stmt = $pdo->prepare("SELECT * FROM quiz_attempts WHERE id = ? AND student_id = ? AND status = 'in_progress'");
$stmt->execute([$attempt_id, $_SESSION['user_id']]);
$attempt = $stmt->fetch();
if (!$attempt) {
echo json_encode(['error' => 'Invalid attempt']);
exit();
}
// Get question details
$stmt = $pdo->prepare("SELECT * FROM questions WHERE id = ?");
$stmt->execute([$question_id]);
$question = $stmt->fetch();
// Check if answer already exists
$stmt = $pdo->prepare("SELECT id FROM student_answers WHERE attempt_id = ? AND question_id = ?");
$stmt->execute([$attempt_id, $question_id]);
$existing = $stmt->fetch();
$is_correct = null;
$marks = 0;
// Auto-grade multiple choice and true/false
if ($question['question_type'] == 'multiple_choice' || $question['question_type'] == 'true_false') {
if ($selected_option) {
$stmt = $pdo->prepare("SELECT is_correct FROM options WHERE id = ?");
$stmt->execute([$selected_option]);
$option = $stmt->fetch();
if ($option) {
$is_correct = $option['is_correct'];
$marks = $is_correct ? $question['points'] : 0;
}
}
}
// Save or update answer
if ($existing) {
$sql = "UPDATE student_answers SET selected_option_id = ?, answer_text = ?, is_correct = ?, marks_obtained = ? WHERE attempt_id = ? AND question_id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$selected_option, $answer, $is_correct, $marks, $attempt_id, $question_id]);
} else {
$sql = "INSERT INTO student_answers (attempt_id, question_id, selected_option_id, answer_text, is_correct, marks_obtained) VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $pdo->prepare($sql);
$stmt->execute([$attempt_id, $question_id, $selected_option, $answer, $is_correct, $marks]);
}
echo json_encode(['success' => true]);
exit();
}
// Finish Quiz
elseif ($action == 'finish_quiz') {
$attempt_id = $_POST['attempt_id'];
// Verify attempt
$stmt = $pdo->prepare("SELECT * FROM quiz_attempts WHERE id = ? AND student_id = ? AND status = 'in_progress'");
$stmt->execute([$attempt_id, $_SESSION['user_id']]);
$attempt = $stmt->fetch();
if (!$attempt) {
$_SESSION['error'] = 'Invalid attempt';
redirect("student/dashboard.php");
}
// Calculate total score
$stmt = $pdo->prepare("
SELECT SUM(marks_obtained) as total,
(SELECT SUM(points) FROM questions WHERE quiz_id = ?) as max_marks
FROM student_answers sa
JOIN questions q ON sa.question_id = q.id
WHERE sa.attempt_id = ?
");
$stmt->execute([$attempt['quiz_id'], $attempt_id]);
$result = $stmt->fetch();
$total_marks = $result['total'] ?? 0;
$max_marks = $result['max_marks'] ?? 0;
$percentage = calculatePercentage($total_marks, $max_marks);
// Update attempt
$stmt = $pdo->prepare("
UPDATE quiz_attempts
SET end_time = NOW(), score = ?, percentage = ?, status = 'completed'
WHERE id = ?
");
$stmt->execute([$total_marks, $percentage, $attempt_id]);
// Check for essay questions that need manual grading
$stmt = $pdo->prepare("
SELECT COUNT(*) FROM student_answers sa
JOIN questions q ON sa.question_id = q.id
WHERE sa.attempt_id = ? AND q.question_type IN ('short_answer', 'essay') AND sa.graded_by IS NULL
");
$stmt->execute([$attempt_id]);
$needs_grading = $stmt->fetchColumn();
if ($needs_grading > 0) {
// Mark attempt as needing grading
$stmt = $pdo->prepare("UPDATE quiz_attempts SET status = 'graded' WHERE id = ?");
$stmt->execute([$attempt_id]);
$_SESSION['info'] = "Your quiz has been submitted and is pending instructor grading.";
} else {
// Check if passed for certificate
$stmt = $pdo->prepare("SELECT passing_marks FROM quizzes WHERE id = ?");
$stmt->execute([$attempt['quiz_id']]);
$quiz = $stmt->fetch();
if ($percentage >= $quiz['passing_marks']) {
// Generate certificate
$cert_number = 'CERT-' . strtoupper(uniqid());
$stmt = $pdo->prepare("INSERT INTO certificates (attempt_id, certificate_number, issued_date) VALUES (?, ?, CURDATE())");
$stmt->execute([$attempt_id, $cert_number]);
}
$_SESSION['success'] = "Quiz completed! Your score: " . $percentage . "%";
}
unset($_SESSION['current_attempt']);
unset($_SESSION['quiz_start_time']);
redirect("student/quiz_results.php?attempt_id=" . $attempt_id);
}
} else {
redirect("index.php");
}
?>
Frontend Pages
Main Landing Page
File: index.php
<?php include 'includes/config.php'; ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo SITE_NAME; ?> - Test Your Knowledge</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<div class="wrapper">
<!-- Navigation -->
<nav class="navbar">
<div class="container">
<div class="navbar-brand">
<a href="index.php">
<i class="fas fa-graduation-cap"></i>
<span><?php echo SITE_NAME; ?></span>
</a>
</div>
<div class="navbar-menu">
<a href="#features">Features</a>
<a href="#how-it-works">How It Works</a>
<a href="#categories">Categories</a>
<?php if (isLoggedIn()): ?>
<a href="<?php
echo $_SESSION['user_role'] == 'admin' ? 'admin/dashboard.php' :
($_SESSION['user_role'] == 'instructor' ? 'instructor/dashboard.php' : 'student/dashboard.php');
?>" class="btn btn-outline">Dashboard</a>
<a href="logout.php" class="btn btn-danger">Logout</a>
<?php else: ?>
<a href="login.php" class="btn btn-primary">Login</a>
<a href="register.php" class="btn btn-secondary">Register</a>
<?php endif; ?>
</div>
<button class="navbar-toggle">
<i class="fas fa-bars"></i>
</button>
</div>
</nav>
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<h1>Test Your Knowledge with <span>Interactive Quizzes</span></h1>
<p>Create, share, and take quizzes on any topic. Perfect for teachers, students, and lifelong learners.</p>
<?php if (!isLoggedIn()): ?>
<div class="hero-buttons">
<a href="register.php" class="btn btn-primary btn-large">
<i class="fas fa-user-plus"></i> Get Started Free
</a>
<a href="#features" class="btn btn-outline btn-large">
<i class="fas fa-play"></i> Learn More
</a>
</div>
<?php endif; ?>
</div>
<div class="hero-image">
<img src="assets/images/hero-illustration.svg" alt="Quiz Illustration">
</div>
</div>
</section>
<!-- Stats Section -->
<section class="stats">
<div class="container">
<div class="stats-grid">
<?php
$total_quizzes = $pdo->query("SELECT COUNT(*) FROM quizzes WHERE status = 'published'")->fetchColumn();
$total_users = $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn();
$total_questions = $pdo->query("SELECT COUNT(*) FROM questions")->fetchColumn();
$total_attempts = $pdo->query("SELECT COUNT(*) FROM quiz_attempts")->fetchColumn();
?>
<div class="stat-item">
<div class="stat-icon">
<i class="fas fa-question-circle"></i>
</div>
<div class="stat-number"><?php echo number_format($total_quizzes); ?></div>
<div class="stat-label">Active Quizzes</div>
</div>
<div class="stat-item">
<div class="stat-icon">
<i class="fas fa-users"></i>
</div>
<div class="stat-number"><?php echo number_format($total_users); ?></div>
<div class="stat-label">Registered Users</div>
</div>
<div class="stat-item">
<div class="stat-icon">
<i class="fas fa-list"></i>
</div>
<div class="stat-number"><?php echo number_format($total_questions); ?></div>
<div class="stat-label">Questions</div>
</div>
<div class="stat-item">
<div class="stat-icon">
<i class="fas fa-check-circle"></i>
</div>
<div class="stat-number"><?php echo number_format($total_attempts); ?></div>
<div class="stat-label">Quizzes Taken</div>
</div>
</div>
</div>
</section>
<!-- Features Section -->
<section id="features" class="features">
<div class="container">
<div class="section-header">
<h2>Why Choose Our Quiz Platform?</h2>
<p>Everything you need for effective learning and assessment</p>
</div>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-pencil-alt"></i>
</div>
<h3>Easy Quiz Creation</h3>
<p>Create quizzes with multiple question types including multiple choice, true/false, and essay questions.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-chart-line"></i>
</div>
<h3>Detailed Analytics</h3>
<p>Track performance with comprehensive reports and insights into learning progress.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-certificate"></i>
</div>
<h3>Certificates</h3>
<p>Generate beautiful certificates for students who successfully complete quizzes.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-clock"></i>
</div>
<h3>Timed Quizzes</h3>
<p>Set time limits for quizzes to simulate real exam conditions.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-mobile-alt"></i>
</div>
<h3>Mobile Friendly</h3>
<p>Fully responsive design works perfectly on all devices.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-shield-alt"></i>
</div>
<h3>Secure & Reliable</h3>
<p>Enterprise-grade security to protect your data and prevent cheating.</p>
</div>
</div>
</div>
</section>
<!-- How It Works -->
<section id="how-it-works" class="how-it-works">
<div class="container">
<div class="section-header">
<h2>How It Works</h2>
<p>Get started in three simple steps</p>
</div>
<div class="steps">
<div class="step">
<div class="step-number">1</div>
<div class="step-icon">
<i class="fas fa-user-plus"></i>
</div>
<h3>Create Account</h3>
<p>Sign up as a student or instructor in less than a minute.</p>
</div>
<div class="step">
<div class="step-number">2</div>
<div class="step-icon">
<i class="fas fa-edit"></i>
</div>
<h3>Create or Take Quizzes</h3>
<p>Instructors create quizzes, students find and take them.</p>
</div>
<div class="step">
<div class="step-number">3</div>
<div class="step-icon">
<i class="fas fa-trophy"></i>
</div>
<h3>Track Progress</h3>
<p>Get instant results, earn certificates, and track improvement.</p>
</div>
</div>
</div>
</section>
<!-- Categories -->
<section id="categories" class="categories">
<div class="container">
<div class="section-header">
<h2>Quiz Categories</h2>
<p>Explore quizzes across various subjects</p>
</div>
<div class="categories-grid">
<?php
$stmt = $pdo->query("SELECT * FROM categories LIMIT 6");
$categories = $stmt->fetchAll();
foreach ($categories as $category):
$quiz_count = $pdo->prepare("SELECT COUNT(*) FROM quizzes WHERE category_id = ?");
$quiz_count->execute([$category['id']]);
$count = $quiz_count->fetchColumn();
?>
<a href="student/available_quizzes.php?category=<?php echo $category['id']; ?>" class="category-card">
<div class="category-icon">
<i class="fas <?php echo $category['icon']; ?>"></i>
</div>
<h3><?php echo htmlspecialchars($category['category_name']); ?></h3>
<p><?php echo $count; ?> Quizzes</p>
</a>
<?php endforeach; ?>
</div>
</div>
</section>
<!-- Call to Action -->
<section class="cta">
<div class="container">
<h2>Ready to Test Your Knowledge?</h2>
<p>Join thousands of learners and educators using our platform</p>
<?php if (!isLoggedIn()): ?>
<a href="register.php" class="btn btn-primary btn-large">
<i class="fas fa-rocket"></i> Start Learning Today
</a>
<?php else: ?>
<a href="<?php echo $_SESSION['user_role'] == 'student' ? 'student/dashboard.php' : 'instructor/dashboard.php'; ?>" class="btn btn-primary btn-large">
<i class="fas fa-tachometer-alt"></i> Go to Dashboard
</a>
<?php endif; ?>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-section">
<h4><?php echo SITE_NAME; ?></h4>
<p>Making learning fun and accessible through interactive quizzes.</p>
<div class="social-links">
<a href="#"><i class="fab fa-facebook"></i></a>
<a href="#"><i class="fab fa-twitter"></i></a>
<a href="#"><i class="fab fa-linkedin"></i></a>
<a href="#"><i class="fab fa-github"></i></a>
</div>
</div>
<div class="footer-section">
<h4>Quick Links</h4>
<ul>
<li><a href="#features">Features</a></li>
<li><a href="#how-it-works">How It Works</a></li>
<li><a href="#categories">Categories</a></li>
<li><a href="#">About Us</a></li>
<li><a href="#">Contact</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Support</h4>
<ul>
<li><a href="#">Help Center</a></li>
<li><a href="#">FAQ</a></li>
<li><a href="#">Terms of Service</a></li>
<li><a href="#">Privacy Policy</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Contact Us</h4>
<ul>
<li><i class="fas fa-envelope"></i> [email protected]</li>
<li><i class="fas fa-phone"></i> +1 (555) 123-4567</li>
<li><i class="fas fa-map-marker-alt"></i> 123 Learning St, Education City</li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>© 2024 <?php echo SITE_NAME; ?>. All rights reserved.</p>
</div>
</div>
</footer>
</div>
<script src="assets/js/main.js"></script>
</body>
</html>
Registration Page
File: register.php
<?php include 'includes/config.php'; ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register - <?php echo SITE_NAME; ?></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body class="auth-page">
<div class="auth-container">
<div class="auth-card">
<div class="auth-header">
<a href="index.php" class="auth-logo">
<i class="fas fa-graduation-cap"></i>
<span><?php echo SITE_NAME; ?></span>
</a>
<h2>Create Account</h2>
<p>Join our learning community today</p>
</div>
<?php if (isset($_SESSION['errors'])): ?>
<div class="alert alert-danger">
<ul>
<?php foreach ($_SESSION['errors'] as $error): ?>
<li><?php echo htmlspecialchars($error); ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php unset($_SESSION['errors']); ?>
<?php endif; ?>
<?php $form_data = $_SESSION['form_data'] ?? []; unset($_SESSION['form_data']); ?>
<form action="includes/register_process.php" method="POST" class="auth-form" onsubmit="return validateRegistration()">
<div class="form-group">
<label for="full_name">
<i class="fas fa-user"></i>
Full Name *
</label>
<input type="text" id="full_name" name="full_name"
value="<?php echo htmlspecialchars($form_data['full_name'] ?? ''); ?>"
placeholder="Enter your full name" required>
</div>
<div class="form-group">
<label for="username">
<i class="fas fa-at"></i>
Username *
</label>
<input type="text" id="username" name="username"
value="<?php echo htmlspecialchars($form_data['username'] ?? ''); ?>"
placeholder="Choose a username" required>
<small>3-20 characters, letters, numbers, and underscores only</small>
</div>
<div class="form-group">
<label for="email">
<i class="fas fa-envelope"></i>
Email Address *
</label>
<input type="email" id="email" name="email"
value="<?php echo htmlspecialchars($form_data['email'] ?? ''); ?>"
placeholder="Enter your email" required>
</div>
<div class="form-group">
<label for="institution">
<i class="fas fa-school"></i>
Institution/School
</label>
<input type="text" id="institution" name="institution"
value="<?php echo htmlspecialchars($form_data['institution'] ?? ''); ?>"
placeholder="Enter your school or organization">
</div>
<div class="form-row">
<div class="form-group">
<label for="password">
<i class="fas fa-lock"></i>
Password *
</label>
<div class="password-field">
<input type="password" id="password" name="password"
placeholder="Create password" required>
<button type="button" class="toggle-password" onclick="togglePassword('password', 'eye-icon1')">
<i class="fas fa-eye" id="eye-icon1"></i>
</button>
</div>
<small>Min 8 chars, 1 uppercase, 1 lowercase, 1 number</small>
</div>
<div class="form-group">
<label for="confirm_password">
<i class="fas fa-lock"></i>
Confirm Password *
</label>
<div class="password-field">
<input type="password" id="confirm_password" name="confirm_password"
placeholder="Confirm password" required>
<button type="button" class="toggle-password" onclick="togglePassword('confirm_password', 'eye-icon2')">
<i class="fas fa-eye" id="eye-icon2"></i>
</button>
</div>
</div>
</div>
<div class="form-group terms-group">
<label class="checkbox-label">
<input type="checkbox" name="terms" required>
<span>I agree to the <a href="#" target="_blank">Terms of Service</a> and <a href="#" target="_blank">Privacy Policy</a></span>
</label>
</div>
<button type="submit" class="btn btn-primary btn-block">
<i class="fas fa-user-plus"></i>
Create Account
</button>
</form>
<div class="auth-footer">
<p>Already have an account? <a href="login.php">Sign In</a></p>
</div>
<div class="auth-divider">
<span>or</span>
</div>
<div class="social-login">
<a href="#" class="btn-social btn-google">
<i class="fab fa-google"></i>
Continue with Google
</a>
<a href="#" class="btn-social btn-facebook">
<i class="fab fa-facebook-f"></i>
Continue with Facebook
</a>
</div>
</div>
<div class="auth-features">
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Free forever for students</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Create unlimited quizzes</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Detailed analytics</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Certificate generation</span>
</div>
</div>
</div>
<script src="assets/js/main.js"></script>
</body>
</html>
Student Dashboard
File: student/dashboard.php
```php
<?php
require_once '../includes/config.php';
require_once '../includes/auth.php';
requireStudent();
$student_id = $_SESSION['user_id'];
// Get student stats
$stats = [];
// Total quizzes taken
$stmt = $pdo->prepare("SELECT COUNT(*) FROM quiz_attempts WHERE student_id = ?");
$stmt->execute([$student_id]);
$stats['total_quizzes'] = $stmt->fetchColumn();
// Average score
$stmt = $pdo->prepare("SELECT AVG(percentage) FROM quiz_attempts WHERE student_id = ? AND status = 'completed'");
$stmt->execute([$student_id]);
$stats['avg_score'] = round($stmt->fetchColumn() ?? 0, 2);
// Certificates earned
$stmt = $pdo->prepare("
SELECT COUNT(*) FROM certificates c
JOIN quiz_attempts qa ON c.attempt_id = qa.id
WHERE qa.student_id = ?
");
$stmt->execute([$student_id]);
$stats['certificates'] = $stmt->fetchColumn();
// Quizzes in progress
$stmt = $pdo->prepare("SELECT COUNT(*) FROM quiz_attempts WHERE student_id = ? AND status = 'in_progress'");
$stmt->execute([$student_id]);
$stats['in_progress'] = $stmt->fetchColumn();
// Recent quizzes
$stmt = $pdo->prepare("
SELECT qa., q.title, q.description, q.total_marks, (SELECT COUNT() FROM questions WHERE quiz_id = q.id) as question_count
FROM quiz_attempts qa
JOIN quizzes q ON qa.quiz_id = q.id
WHERE qa.student_id = ?
ORDER BY qa.created_at DESC
LIMIT 5
");
$stmt->execute([$student_id]);
$recent_quizzes = $stmt->fetchAll();
// Available quizzes
$stmt = $pdo->prepare("
SELECT q., c.category_name, u.full_name as instructor_name, (SELECT COUNT() FROM questions WHERE quiz_id = q.id) as question_count,
(SELECT COUNT() FROM quiz_attempts WHERE quiz_id = q.id AND student_id = ?) as attempt_count FROM quizzes q JOIN categories c ON q.category_id = c.id JOIN users u ON q.instructor_id = u.id WHERE q.status = 'published' AND (q.start_date IS NULL OR q.start_date <= NOW()) AND (q.end_date IS NULL OR q.end_date >= NOW()) AND q.id NOT IN ( SELECT quiz_id FROM quiz_attempts WHERE student_id = ? AND status = 'completed' GROUP BY quiz_id HAVING COUNT() >= q.attempts_allowed
)
ORDER BY q.created_at DESC
LIMIT 3
");
$stmt->execute([$student_id, $student_id]);
$available_quizzes = $stmt->fetchAll();
?>
Student Dashboard - <?php echo SITE_NAME; ?>
<div class="sidebar-user">
<div class="user-avatar">
<?php echo getAvatar($_SESSION['full_name']); ?>
</div>
<div class="user-info">
<h4><?php echo htmlspecialchars($_SESSION['full_name']); ?></h4>
<p>Student</p>
</div>
</div>
<nav class="sidebar-nav">
<a href="dashboard.php" class="active">
<i class="fas fa-tachometer-alt"></i>
<span>Dashboard</span>
</a>
<a href="available_quizzes.php">
<i class="fas fa-list"></i>
<span>Available Quizzes</span>
</a>
<a href="my_results.php">
<i class="fas fa-chart-bar"></i>
<span>My Results</span>
</a>
<a href="leaderboard.php">
<i class="fas fa-trophy"></i>
<span>Leaderboard</span>
</a>
<a href="profile.php">
<i class="fas fa-user"></i>
<span>My Profile</span>
</a>
<a href="../logout.php">
<i class="fas fa-sign-out-alt"></i>
<span>Logout</span>
</a>
</nav>
</aside>
<!-- Main Content -->
<main class="main-content">
<header class="content-header">
<h1>Student Dashboard</h1>
<div class="header-actions">
<div class="notification-badge">
<i class="fas fa-bell"></i>
<span class="badge">3</span>
</div>
<div class="date-display">
<i class="fas fa-calendar"></i>
<?php echo date('F j, Y'); ?>
</div>
</div>
</header>
<!-- Stats Cards -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon" style="background: #e3f2fd; color: #1976d2;">
<i class="fas