<?php
// Start session
session_start();
// Include database configuration
require_once '../config/database.php';
// Check if user is logged in and has admin or director role
if (!isset($_SESSION['user_id']) || ($_SESSION['role'] !== 'admin' && $_SESSION['role'] !== 'director')) {
header('Location: login.php');
exit;
}
// Check if exam_id is provided
if (!isset($_GET['exam_id']) || !is_numeric($_GET['exam_id'])) {
header('Location: manage_exams.php');
exit;
}
$exam_id = $_GET['exam_id'];
// Get exam details
$exam_query = "SELECT es.*, c.title as course_title
FROM exam_schedules es
JOIN courses c ON es.course_id = c.id
WHERE es.id = ?";
$stmt = $conn->prepare($exam_query);
$stmt->bind_param("i", $exam_id);
$stmt->execute();
$exam_result = $stmt->get_result();
if ($exam_result->num_rows === 0) {
header('Location: manage_exams.php');
exit;
}
$exam = $exam_result->fetch_assoc();
$course_id = $exam['course_id'];
// Verify that exam_question_maps table exists
$check_table = $conn->query("SHOW TABLES LIKE 'exam_question_maps'");
$exam_question_maps_exists = $check_table->num_rows > 0;
if (!$exam_question_maps_exists) {
$_SESSION['error'] = "The exam_question_maps table does not exist. Please run the 'Fix Database Tables' script first.";
header('Location: map_questions.php?exam_id=' . $exam_id);
exit;
}
// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_questions'])) {
// Get selected questions
$selected_questions = isset($_POST['selected_questions']) ? $_POST['selected_questions'] : [];
if (empty($selected_questions)) {
$_SESSION['error'] = "Please select at least one question to add to the exam.";
} else {
// Start transaction
$conn->begin_transaction();
try {
$questions_added = 0;
$questions_already_mapped = 0;
// Add each selected question to the exam
foreach ($selected_questions as $question_id) {
// Check if the question is already in the exam
$check_query = "SELECT id FROM exam_question_maps WHERE exam_id = ? AND question_id = ?";
$stmt = $conn->prepare($check_query);
$stmt->bind_param("ii", $exam_id, $question_id);
$stmt->execute();
$check_result = $stmt->get_result();
if ($check_result->num_rows === 0) {
// If not already in the exam, add it
$insert_query = "INSERT INTO exam_question_maps (exam_id, question_id) VALUES (?, ?)";
$stmt = $conn->prepare($insert_query);
$stmt->bind_param("ii", $exam_id, $question_id);
if ($stmt->execute()) {
$questions_added++;
}
} else {
$questions_already_mapped++;
}
}
// Commit transaction
$conn->commit();
if ($questions_added > 0) {
$_SESSION['success'] = "$questions_added question(s) added to the exam successfully.";
if ($questions_already_mapped > 0) {
$_SESSION['success'] .= " $questions_already_mapped question(s) were already in the exam.";
}
} else if ($questions_already_mapped > 0) {
$_SESSION['info'] = "All selected questions were already in the exam.";
}
header('Location: map_questions.php?exam_id=' . $exam_id);
exit;
} catch (Exception $e) {
// Roll back transaction
$conn->rollback();
$_SESSION['error'] = "Error adding questions: " . $e->getMessage();
}
}
}
// Handle mapping entire question bank at once
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['map_entire_bank'])) {
$bank_id = isset($_POST['bank_id']) ? intval($_POST['bank_id']) : 0;
if (empty($bank_id)) {
$_SESSION['error'] = "Please select a question bank.";
} else {
// Start transaction
$conn->begin_transaction();
try {
// Get all questions from the selected bank that aren't already mapped to this exam
$questions_query = "SELECT q.id FROM questions q
WHERE q.question_bank_id = ?
AND q.id NOT IN (
SELECT question_id FROM exam_question_maps WHERE exam_id = ?
)";
$stmt = $conn->prepare($questions_query);
$stmt->bind_param("ii", $bank_id, $exam_id);
$stmt->execute();
$questions_result = $stmt->get_result();
$questions_added = 0;
// Add each question to the exam
while ($question = $questions_result->fetch_assoc()) {
$insert_query = "INSERT INTO exam_question_maps (exam_id, question_id) VALUES (?, ?)";
$stmt = $conn->prepare($insert_query);
$stmt->bind_param("ii", $exam_id, $question['id']);
if ($stmt->execute()) {
$questions_added++;
}
}
// Commit transaction
$conn->commit();
if ($questions_added > 0) {
$_SESSION['success'] = "$questions_added question(s) from the bank added to the exam successfully.";
} else {
$_SESSION['info'] = "No new questions to add from this bank. All questions may already be in the exam.";
}
header('Location: map_questions.php?exam_id=' . $exam_id);
exit;
} catch (Exception $e) {
// Roll back transaction
$conn->rollback();
$_SESSION['error'] = "Error adding questions from bank: " . $e->getMessage();
}
}
}
// Get all available questions from question banks related to this course
$available_questions = [];
$total_available = 0;
// Get question banks for this course
$banks_query = "SELECT qb.id, qb.title,
(SELECT COUNT(*) FROM questions q WHERE q.question_bank_id = qb.id) AS question_count,
(SELECT COUNT(*) FROM questions q
JOIN exam_question_maps eqm ON q.id = eqm.question_id
WHERE q.question_bank_id = qb.id AND eqm.exam_id = ?) AS mapped_count
FROM question_banks qb
WHERE (qb.course_id = ? OR qb.course_id IS NULL)
ORDER BY
CASE WHEN qb.course_id = ? THEN 0 ELSE 1 END,
qb.title";
$stmt = $conn->prepare($banks_query);
$stmt->bind_param("iii", $exam_id, $course_id, $course_id);
$stmt->execute();
$banks_result = $stmt->get_result();
$question_banks = [];
while ($bank = $banks_result->fetch_assoc()) {
$question_banks[$bank['id']] = [
'title' => $bank['title'],
'question_count' => $bank['question_count'],
'mapped_count' => $bank['mapped_count']
];
}
// Get all questions not already in the exam
$questions_query = "
SELECT q.*, b.title as bank_title
FROM questions q
JOIN question_banks b ON q.question_bank_id = b.id
WHERE (b.course_id = ? OR b.course_id IS NULL)
AND q.id NOT IN (
SELECT question_id FROM exam_question_maps WHERE exam_id = ?
)
ORDER BY b.title, q.question_type, q.difficulty";
$stmt = $conn->prepare($questions_query);
$stmt->bind_param("ii", $course_id, $exam_id);
$stmt->execute();
$questions_result = $stmt->get_result();
$questions_by_bank = [];
while ($question = $questions_result->fetch_assoc()) {
$bank_id = $question['question_bank_id'];
if (!isset($questions_by_bank[$bank_id])) {
$questions_by_bank[$bank_id] = [];
}
$questions_by_bank[$bank_id][] = $question;
$total_available++;
}
// Get existing exam questions count
$mapped_count_query = "SELECT COUNT(*) as count FROM exam_question_maps WHERE exam_id = ?";
$stmt = $conn->prepare($mapped_count_query);
$stmt->bind_param("i", $exam_id);
$stmt->execute();
$mapped_count = $stmt->get_result()->fetch_assoc()['count'];
// Include header
include_once 'includes/header.php';
?>
<div class="content-wrapper">
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0">Add Questions to Exam</h1>
<p class="text-muted">
<i class="fas fa-book"></i> <?php echo htmlspecialchars($exam['title']); ?> -
<i class="fas fa-graduation-cap"></i> <?php echo htmlspecialchars($exam['course_title']); ?>
</p>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="index.php">Home</a></li>
<li class="breadcrumb-item"><a href="manage_exams.php">Manage Exams</a></li>
<li class="breadcrumb-item"><a href="map_questions.php?exam_id=<?php echo $exam_id; ?>">Manage Questions</a></li>
<li class="breadcrumb-item active">Add Questions</li>
</ol>
</div>
</div>
</div>
</div>
<section class="content">
<div class="container-fluid">
<?php if (isset($_SESSION['error'])): ?>
<div class="alert alert-danger alert-dismissible fade show">
<?php echo $_SESSION['error']; unset($_SESSION['error']); ?>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<?php endif; ?>
<?php if (isset($_SESSION['success'])): ?>
<div class="alert alert-success alert-dismissible fade show">
<?php echo $_SESSION['success']; unset($_SESSION['success']); ?>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<?php endif; ?>
<?php if (isset($_SESSION['info'])): ?>
<div class="alert alert-info alert-dismissible fade show">
<?php echo $_SESSION['info']; unset($_SESSION['info']); ?>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<?php endif; ?>
<div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Available Questions</h5>
<a href="map_questions.php?exam_id=<?php echo $exam_id; ?>" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Back to Exam Questions
</a>
</div>
<div class="card-body">
<?php if ($total_available === 0): ?>
<div class="alert alert-info">
<p><i class="fas fa-info-circle"></i> No more questions available to add to this exam.</p>
<p>You can:</p>
<ul>
<li><a href="add_question.php?course_id=<?php echo $course_id; ?>">Create new questions</a></li>
<li><a href="banks.php?course_id=<?php echo $course_id; ?>">Manage question banks</a></li>
</ul>
</div>
<?php else: ?>
<div class="alert alert-info">
<i class="fas fa-info-circle"></i>
This exam currently has <strong><?php echo $mapped_count; ?></strong> questions.
There are <strong><?php echo $total_available; ?></strong> more questions available to add.
<br>
Use the checkboxes to select questions and click "Add Selected Questions" at the bottom of the page.
</div>
<!-- Quick Map Entire Bank Section -->
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">Map Entire Question Bank</h5>
</div>
<div class="card-body">
<form action="" method="POST" class="row g-3 align-items-end">
<div class="col-md-6">
<label for="quick_bank_select" class="form-label">Select Question Bank to Map</label>
<select class="form-control" id="quick_bank_select" name="bank_id" required>
<option value="">-- Select a Question Bank --</option>
<?php foreach ($question_banks as $bank_id => $bank): ?>
<?php
$unmapped_count = $bank['question_count'] - $bank['mapped_count'];
$disabled = ($unmapped_count <= 0) ? 'disabled' : '';
?>
<option value="<?php echo $bank_id; ?>" <?php echo $disabled; ?>>
<?php echo htmlspecialchars($bank['title']); ?>
(<?php echo $unmapped_count; ?> of <?php echo $bank['question_count']; ?> questions available)
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<input type="hidden" name="map_entire_bank" value="1">
<button type="submit" class="btn btn-success" onclick="return confirm('Are you sure you want to add ALL questions from this bank to the exam?');">
<i class="fas fa-plus-circle"></i> Add All Questions from Selected Bank
</button>
</div>
<div class="col-12">
<div class="form-text text-muted">
This will add all questions from the selected bank that aren't already in the exam.
</div>
</div>
</form>
</div>
</div>
<form action="" method="POST" id="add-questions-form">
<input type="hidden" name="add_questions" value="1">
<div class="mb-3">
<button type="button" class="btn btn-sm btn-outline-primary" id="select-all-btn">Select All</button>
<button type="button" class="btn btn-sm btn-outline-secondary" id="deselect-all-btn">Deselect All</button>
<span class="ml-2" id="selection-counter">0 questions selected</span>
</div>
<?php foreach ($questions_by_bank as $bank_id => $questions): ?>
<div class="card mb-4">
<div class="card-header bg-light">
<h5 class="mb-0"><?php echo htmlspecialchars($question_banks[$bank_id]['title']); ?></h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-striped mb-0">
<thead>
<tr>
<th style="width:50px;">
<div class="form-check">
<input class="form-check-input select-bank" type="checkbox" data-bank="<?php echo $bank_id; ?>">
</div>
</th>
<th>Question</th>
<th>Type</th>
<th>Difficulty</th>
<th>Marks</th>
<th>Preview</th>
</tr>
</thead>
<tbody>
<?php foreach ($questions as $question): ?>
<tr>
<td>
<div class="form-check">
<input class="form-check-input question-checkbox" type="checkbox" name="selected_questions[]" value="<?php echo $question['id']; ?>" data-bank="<?php echo $bank_id; ?>">
</div>
</td>
<td><?php echo htmlspecialchars(substr($question['question_text'], 0, 100)) . (strlen($question['question_text']) > 100 ? '...' : ''); ?></td>
<td>
<?php
switch ($question['question_type']) {
case 'multiple_choice':
echo '<span class="badge badge-primary">Multiple Choice</span>';
break;
case 'true_false':
echo '<span class="badge badge-info">True/False</span>';
break;
case 'short_answer':
echo '<span class="badge badge-secondary">Short Answer</span>';
break;
case 'essay':
echo '<span class="badge badge-warning">Essay</span>';
break;
default:
echo '<span class="badge badge-secondary">' . ucfirst($question['question_type']) . '</span>';
}
?>
</td>
<td>
<?php
$difficulty_field = isset($question['difficulty']) ? 'difficulty' :
(isset($question['difficulty_level']) ? 'difficulty_level' : null);
if ($difficulty_field && isset($question[$difficulty_field])) {
$difficulty = $question[$difficulty_field];
$class = '';
switch ($difficulty) {
case 'easy': $class = 'success'; break;
case 'medium': $class = 'warning'; break;
case 'hard': $class = 'danger'; break;
default: $class = 'secondary';
}
echo '<span class="badge badge-' . $class . '">' . ucfirst($difficulty) . '</span>';
} else {
echo '<span class="badge badge-secondary">Unknown</span>';
}
?>
</td>
<td><?php echo $question['marks']; ?></td>
<td>
<button type="button" class="btn btn-sm btn-info preview-btn" data-toggle="modal" data-target="#previewModal" data-question-id="<?php echo $question['id']; ?>">
<i class="fas fa-eye"></i>
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php endforeach; ?>
<div class="text-center mt-4">
<button type="submit" class="btn btn-primary btn-lg">
<i class="fas fa-plus"></i> Add Selected Questions to Exam
</button>
</div>
</form>
<?php endif; ?>
</div>
</div>
</div>
</section>
</div>
<!-- Question Preview Modal -->
<div class="modal fade" id="previewModal" tabindex="-1" aria-labelledby="previewModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="previewModalLabel">Question Preview</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div id="question-preview-content">
<div class="text-center">
<div class="spinner-border text-primary" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
// Select all questions
$('#select-all-btn').click(function() {
$('.question-checkbox').prop('checked', true);
updateSelectedCount();
});
// Deselect all questions
$('#deselect-all-btn').click(function() {
$('.question-checkbox').prop('checked', false);
updateSelectedCount();
});
// Select all questions in a bank
$('.select-bank').change(function() {
var bankId = $(this).data('bank');
var isChecked = $(this).prop('checked');
$('.question-checkbox[data-bank="' + bankId + '"]').prop('checked', isChecked);
updateSelectedCount();
});
// Update counter when individual checkboxes change
$('.question-checkbox').change(function() {
updateSelectedCount();
// Check if all questions in the bank are selected
var bankId = $(this).data('bank');
var totalInBank = $('.question-checkbox[data-bank="' + bankId + '"]').length;
var checkedInBank = $('.question-checkbox[data-bank="' + bankId + '"]:checked').length;
$('.select-bank[data-bank="' + bankId + '"]').prop('checked', totalInBank === checkedInBank);
});
// Update the selection counter
function updateSelectedCount() {
var count = $('.question-checkbox:checked').length;
$('#selection-counter').text(count + ' question' + (count !== 1 ? 's' : '') + ' selected');
}
// Preview question
$('.preview-btn').click(function() {
var questionId = $(this).data('question-id');
// Show loading spinner
$('#question-preview-content').html('<div class="text-center"><div class="spinner-border text-primary" role="status"><span class="sr-only">Loading...</span></div></div>');
// Load question details via AJAX
$.ajax({
url: 'ajax/get_question_details.php',
type: 'POST',
data: { question_id: questionId },
success: function(response) {
$('#question-preview-content').html(response);
},
error: function() {
$('#question-preview-content').html('<div class="alert alert-danger">Error loading question details.</div>');
}
});
});
});
</script>
<?php include_once 'includes/footer.php'; ?>