<?php
// Start session
session_start();
// Include database configuration
require_once '../config/database.php';
// Check if user is logged in and has admin role
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
header("Location: ../login.php");
exit();
}
// Redirect if no question bank exists
$bank_count = $conn->query("SELECT COUNT(*) FROM question_banks")->fetch_row()[0];
if ($bank_count == 0) {
$_SESSION['error_message'] = "No question banks found. Create a question bank first.";
header("Location: banks.php");
exit;
}
// Check if course_id is provided in the URL
$course_id = isset($_GET['course_id']) ? intval($_GET['course_id']) : 0;
// Check if bank_id is provided
if (!isset($_GET['bank_id']) || empty($_GET['bank_id'])) {
// If course_id is provided, redirect to filtered banks page
if ($course_id > 0) {
$_SESSION['error_message'] = "Please select a question bank for this course.";
header("Location: banks.php?course_id=" . $course_id);
} else {
$_SESSION['error_message'] = "Please select a question bank.";
header("Location: banks.php");
}
exit;
}
$bank_id = $_GET['bank_id'];
// Get question bank details
$bank_query = "SELECT qb.*, c.title as course_title
FROM question_banks qb
LEFT JOIN courses c ON qb.course_id = c.id
WHERE qb.id = ?";
$stmt = $conn->prepare($bank_query);
$stmt->bind_param("i", $bank_id);
$stmt->execute();
$bank_result = $stmt->get_result();
if ($bank_result->num_rows === 0) {
$_SESSION['error_message'] = "Question bank not found.";
header("Location: banks.php");
exit();
}
$question_bank = $bank_result->fetch_assoc();
// Process form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Get common question data
$question_text = trim($_POST['question_text']);
$question_type = $_POST['question_type'];
$difficulty = $_POST['difficulty'];
$marks = $_POST['marks'];
$is_practice = isset($_POST['is_practice']) ? 1 : 0;
$explanation = trim($_POST['explanation']);
// Validate inputs
$errors = [];
if (empty($question_text)) {
$errors[] = "Question text is required";
}
if (empty($question_type)) {
$errors[] = "Question type is required";
}
if (empty($difficulty)) {
$errors[] = "Difficulty level is required";
}
if (!is_numeric($marks) || $marks <= 0) {
$errors[] = "Marks must be a positive number";
}
// Question type specific validation
if ($question_type === 'multiple_choice') {
$options = $_POST['options'] ?? [];
$correct_options = $_POST['correct_options'] ?? [];
if (count($options) < 2) {
$errors[] = "Multiple choice questions must have at least 2 options";
}
if (empty($correct_options)) {
$errors[] = "At least one correct option must be selected";
}
// Check for empty options
foreach ($options as $option) {
if (trim($option) === '') {
$errors[] = "All options must have text";
break;
}
}
} elseif ($question_type === 'true_false') {
if (!isset($_POST['true_false_answer'])) {
$errors[] = "Please select True or False as the correct answer";
}
} elseif ($question_type === 'short_answer') {
if (empty(trim($_POST['answer_key'] ?? ''))) {
$errors[] = "Answer key is required for short answer questions";
}
}
// If no errors, proceed with inserting the question
if (empty($errors)) {
// Start transaction
$conn->begin_transaction();
try {
// Insert question
$insert_question = "INSERT INTO questions (question_bank_id, question_text, question_type, difficulty, marks, is_practice, explanation)
VALUES (?, ?, ?, ?, ?, ?, ?)";
$stmt = $conn->prepare($insert_question);
$stmt->bind_param("isssdis", $bank_id, $question_text, $question_type, $difficulty, $marks, $is_practice, $explanation);
$stmt->execute();
// Get the inserted question ID
$question_id = $conn->insert_id;
// Handle options based on question type
if ($question_type === 'multiple_choice') {
$options = $_POST['options'] ?? [];
$correct_options = $_POST['correct_options'] ?? [];
// Insert options
foreach ($options as $index => $option_text) {
$option_text = trim($option_text);
if (empty($option_text)) continue;
$is_correct = in_array((string)$index, $correct_options) ? 1 : 0;
$insert_option = "INSERT INTO question_options (question_id, option_text, is_correct) VALUES (?, ?, ?)";
$stmt = $conn->prepare($insert_option);
$stmt->bind_param("isi", $question_id, $option_text, $is_correct);
$stmt->execute();
}
} elseif ($question_type === 'true_false') {
$options = ['True', 'False'];
$true_false_answer = $_POST['true_false_answer'] ?? '';
foreach ($options as $index => $option_text) {
$is_correct = 0;
// If true is selected, the first option (True) is correct
// If false is selected, the second option (False) is correct
if (($true_false_answer === 'true' && $index === 0) ||
($true_false_answer === 'false' && $index === 1)) {
$is_correct = 1;
}
$insert_option = "INSERT INTO question_options (question_id, option_text, is_correct) VALUES (?, ?, ?)";
$stmt = $conn->prepare($insert_option);
$stmt->bind_param("isi", $question_id, $option_text, $is_correct);
$stmt->execute();
}
} elseif ($question_type === 'short_answer') {
// For short answer, require an answer key
$answer_key = trim($_POST['answer_key'] ?? '');
$insert_option = "INSERT INTO question_options (question_id, option_text, is_correct) VALUES (?, ?, 1)";
$stmt = $conn->prepare($insert_option);
$stmt->bind_param("is", $question_id, $answer_key);
$stmt->execute();
}
// Commit transaction
$conn->commit();
// Set success message and redirect
$_SESSION['success_message'] = "Question added successfully!";
header("Location: questions.php?bank_id=" . $bank_id);
exit();
} catch (Exception $e) {
// Roll back transaction
$conn->rollback();
$error_message = "Error: " . $e->getMessage();
}
} else {
$error_message = "Please fix the following errors:<br>" . implode("<br>", $errors);
}
}
// Include header
include 'includes/header.php';
?>
<div class="container-fluid">
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<div>
<h1 class="h3 mb-0 text-gray-800">Add Question</h1>
<?php if (!empty($question_bank['course_id'])): ?>
<p class="text-muted mb-0">
<i class="fas fa-graduation-cap me-1"></i> Course: <?php echo htmlspecialchars($question_bank['course_title']); ?>
</p>
<?php endif; ?>
</div>
<div>
<a href="questions.php?bank_id=<?php echo $bank_id; ?>" class="btn btn-secondary btn-sm shadow-sm me-2">
<i class="fas fa-arrow-left fa-sm text-white-50"></i> Back to Questions
</a>
<a href="banks.php" class="btn btn-primary btn-sm shadow-sm">
<i class="fas fa-list fa-sm text-white-50"></i> All Question Banks
</a>
</div>
</div>
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
Adding Question to: <?php echo htmlspecialchars($question_bank['title']); ?>
</h6>
</div>
<div class="card-body">
<?php if (isset($error_message)): ?>
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<?php echo $error_message; ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<?php if (!isset($_POST['question_text'])): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<strong><i class="fas fa-lightbulb me-2"></i>Getting Started:</strong> Select a question type first, then fill in the details and options. For multiple choice questions, check the box next to correct answers.
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<div class="alert alert-info mb-4">
<h5><i class="fas fa-info-circle me-2"></i> Question Type Guide</h5>
<hr>
<ul class="mb-0">
<li><strong>Multiple Choice:</strong> Create questions with multiple options where one or more can be correct. Select checkbox(es) next to the correct option(s).</li>
<li><strong>True/False:</strong> Simple questions with True or False as the only possible answers.</li>
<li><strong>Short Answer:</strong> Questions where students must type a specific answer. Define the exact expected answer.</li>
<li><strong>Essay:</strong> Open-ended questions that require manual grading.</li>
</ul>
</div>
<div class="accordion mb-4" id="examplesAccordion">
<div class="accordion-item">
<h2 class="accordion-header" id="examplesHeading">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#examplesCollapse" aria-expanded="false" aria-controls="examplesCollapse">
<i class="fas fa-lightbulb me-2"></i> Examples and Best Practices
</button>
</h2>
<div id="examplesCollapse" class="accordion-collapse collapse" aria-labelledby="examplesHeading" data-bs-parent="#examplesAccordion">
<div class="accordion-body">
<div class="row">
<div class="col-md-6 mb-3">
<div class="card h-100">
<div class="card-header bg-primary text-white">
<h5 class="mb-0"><i class="fas fa-check-square me-2"></i> Multiple Choice Example</h5>
</div>
<div class="card-body">
<p><strong>Question:</strong> Which of the following are programming languages?</p>
<ul>
<li><strong>Option 1:</strong> Python <span class="badge bg-success">Correct</span></li>
<li><strong>Option 2:</strong> Java <span class="badge bg-success">Correct</span></li>
<li><strong>Option 3:</strong> Microsoft Word <span class="badge bg-danger">Incorrect</span></li>
<li><strong>Option 4:</strong> HTML <span class="badge bg-success">Correct</span></li>
</ul>
<p><strong>Tip:</strong> For multiple choice, ensure distractors (incorrect options) are plausible but clearly wrong.</p>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card h-100">
<div class="card-header bg-primary text-white">
<h5 class="mb-0"><i class="fas fa-toggle-on me-2"></i> True/False Example</h5>
</div>
<div class="card-body">
<p><strong>Question:</strong> HTML is a programming language.</p>
<ul>
<li><strong>Answer:</strong> False <span class="badge bg-success">Correct</span></li>
</ul>
<p><strong>Explanation:</strong> HTML is a markup language, not a programming language.</p>
<p><strong>Tip:</strong> Avoid ambiguous statements. The answer should be clearly true or false.</p>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card h-100">
<div class="card-header bg-primary text-white">
<h5 class="mb-0"><i class="fas fa-keyboard me-2"></i> Short Answer Example</h5>
</div>
<div class="card-body">
<p><strong>Question:</strong> What does CSS stand for?</p>
<p><strong>Answer Key:</strong> Cascading Style Sheets</p>
<p><strong>Tip:</strong> For short answers, be specific about the expected answer format. Consider alternative spellings or formats that should be accepted.</p>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card h-100">
<div class="card-header bg-primary text-white">
<h5 class="mb-0"><i class="fas fa-pen me-2"></i> Essay Example</h5>
</div>
<div class="card-body">
<p><strong>Question:</strong> Explain the differences between procedural and object-oriented programming paradigms.</p>
<p><strong>Tip:</strong> Provide clear criteria for grading in the explanation field to ensure consistent evaluation.</p>
</div>
</div>
</div>
</div>
<div class="alert alert-secondary mt-2">
<h6><i class="fas fa-star me-2"></i> Best Practices:</h6>
<ol class="mb-0">
<li>Keep questions clear and concise</li>
<li>Use proper grammar and spelling</li>
<li>Avoid giving away the answer in the question text</li>
<li>Make sure options are mutually exclusive (for multiple choice)</li>
<li>Include an explanation for complex questions</li>
<li>Make sure difficulty level matches the actual difficulty of the question</li>
</ol>
</div>
</div>
</div>
</div>
</div>
<form method="post" action="" id="questionForm">
<div class="form-group">
<label for="question_text"><strong>Question Text *</strong></label>
<textarea class="form-control" id="question_text" name="question_text" rows="4" required><?php echo isset($_POST['question_text']) ? htmlspecialchars($_POST['question_text']) : ''; ?></textarea>
<small class="form-text text-muted">
Enter the full text of your question. You can use basic HTML for formatting.
</small>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label for="question_type"><strong>Question Type *</strong></label>
<select class="form-control" id="question_type" name="question_type" required>
<option value="">Select Type</option>
<option value="multiple_choice" <?php echo isset($_POST['question_type']) && $_POST['question_type'] === 'multiple_choice' ? 'selected' : ''; ?>>Multiple Choice</option>
<option value="true_false" <?php echo isset($_POST['question_type']) && $_POST['question_type'] === 'true_false' ? 'selected' : ''; ?>>True/False</option>
<option value="short_answer" <?php echo isset($_POST['question_type']) && $_POST['question_type'] === 'short_answer' ? 'selected' : ''; ?>>Short Answer</option>
<option value="essay" <?php echo isset($_POST['question_type']) && $_POST['question_type'] === 'essay' ? 'selected' : ''; ?>>Essay</option>
</select>
</div>
<div class="form-group col-md-4">
<label for="difficulty"><strong>Difficulty *</strong></label>
<select class="form-control" id="difficulty" name="difficulty" required>
<option value="">Select Difficulty</option>
<option value="easy" <?php echo isset($_POST['difficulty']) && $_POST['difficulty'] === 'easy' ? 'selected' : ''; ?>>Easy</option>
<option value="medium" <?php echo isset($_POST['difficulty']) && $_POST['difficulty'] === 'medium' ? 'selected' : ''; ?>>Medium</option>
<option value="hard" <?php echo isset($_POST['difficulty']) && $_POST['difficulty'] === 'hard' ? 'selected' : ''; ?>>Hard</option>
</select>
</div>
<div class="form-group col-md-4">
<label for="marks"><strong>Marks *</strong></label>
<input type="number" class="form-control" id="marks" name="marks" min="1" max="100" required value="<?php echo isset($_POST['marks']) ? htmlspecialchars($_POST['marks']) : '1'; ?>">
</div>
</div>
<div class="form-group">
<div class="form-check mb-3">
<input type="checkbox" class="form-check-input" id="is_practice" name="is_practice" <?php echo isset($_POST['is_practice']) ? 'checked' : ''; ?>>
<label class="form-check-label" for="is_practice">Include in practice questions</label>
<small class="form-text text-muted">
Check this if you want to include this question in practice sessions.
</small>
</div>
</div>
<!-- Options for Multiple Choice -->
<div id="multiple_choice_options" class="options-section" style="display: none;">
<h5 class="mt-4 mb-3">Answer Options</h5>
<div class="alert alert-info">
<i class="fas fa-info-circle"></i> Add at least 2 options and select the correct answer(s). You can select multiple correct answers.
</div>
<div id="options_container" class="mb-3">
<!-- Options will be added here dynamically -->
<!-- Default options will be generated by JavaScript -->
<div class="option-row mb-2">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<input type="checkbox" name="correct_options[]" value="0">
</div>
</div>
<input type="text" class="form-control" name="options[]" placeholder="Option 1" required>
<div class="input-group-append">
<button type="button" class="btn btn-danger remove-option" disabled>
<i class="fas fa-times"></i>
</button>
</div>
</div>
</div>
<div class="option-row mb-2">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<input type="checkbox" name="correct_options[]" value="1">
</div>
</div>
<input type="text" class="form-control" name="options[]" placeholder="Option 2" required>
<div class="input-group-append">
<button type="button" class="btn btn-danger remove-option" disabled>
<i class="fas fa-times"></i>
</button>
</div>
</div>
</div>
</div>
<div class="text-start mb-3">
<button type="button" id="add_option" class="btn btn-secondary btn-sm">
<i class="fas fa-plus"></i> Add Option
</button>
</div>
<div class="alert alert-warning small">
<i class="fas fa-exclamation-triangle"></i> Remember to select at least one correct option by checking the box next to it.
</div>
</div>
<!-- Options for True/False -->
<div id="true_false_options" class="options-section" style="display: none;">
<h5 class="mt-4 mb-3">Answer Options</h5>
<div class="alert alert-info">
<i class="fas fa-info-circle"></i> Select the correct answer for this True/False question.
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="radio" name="true_false_answer" id="true_option" value="true">
<label class="form-check-label" for="true_option">
True
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="true_false_answer" id="false_option" value="false">
<label class="form-check-label" for="false_option">
False
</label>
</div>
</div>
<!-- Options for Short Answer -->
<div id="short_answer_options" class="options-section" style="display: none;">
<h5 class="mt-4 mb-3">Answer Key</h5>
<div class="alert alert-info">
<i class="fas fa-info-circle"></i> Enter the exact answer that will be accepted as correct.
</div>
<div class="form-group">
<label for="answer_key">Correct Answer <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="answer_key" name="answer_key" placeholder="Enter the correct answer">
<small class="form-text text-muted">
The student's answer must match this exactly. Case sensitivity will be considered.
</small>
</div>
</div>
<!-- No options for Essay -->
<div id="essay_options" class="options-section" style="display: none;">
<div class="alert alert-info mt-4">
<i class="fas fa-info-circle"></i> Essay questions require manual grading. No answer options needed.
</div>
</div>
<div class="form-group mt-4">
<label for="explanation">Explanation (Optional)</label>
<textarea class="form-control" id="explanation" name="explanation" rows="3" placeholder="Provide an explanation for this question and its answer"><?php echo isset($_POST['explanation']) ? htmlspecialchars($_POST['explanation']) : ''; ?></textarea>
<small class="form-text text-muted">
This will be shown to students after they answer or in review mode.
</small>
</div>
<div class="mt-4">
<button type="submit" class="btn btn-primary">Save Question</button>
<a href="questions.php?bank_id=<?php echo $bank_id; ?>" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
</div>
</div>
<!-- This script needs to be after jQuery is loaded, so put it right before the footer include -->
<script>
// We'll initialize this function after the page loads
function initializeQuestionOptions() {
console.log('Initializing question options script');
// Function to add a new option
function addOption() {
var optionCount = $('#options_container .option-row').length;
console.log('Adding option #' + (optionCount + 1));
var newOption = `
<div class="option-row mb-2">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<input type="checkbox" name="correct_options[]" value="${optionCount}">
</div>
</div>
<input type="text" class="form-control" name="options[]" placeholder="Option ${optionCount + 1}" required>
<div class="input-group-append">
<button type="button" class="btn btn-danger remove-option" ${optionCount < 2 ? 'disabled' : ''}>
<i class="fas fa-times"></i>
</button>
</div>
</div>
</div>
`;
$('#options_container').append(newOption);
// Enable remove buttons if we have more than 2 options
if (optionCount + 1 > 2) {
$('.remove-option').prop('disabled', false);
}
}
// Function to initialize options container
function initializeOptionsContainer() {
console.log('Initializing options container');
// We no longer need to clear and add default options since they're in the HTML
// Just make sure any events are attached
// Update value attributes for correct_options checkboxes just in case
$('.option-row').each(function(index) {
$(this).find('input[type="checkbox"]').val(index);
$(this).find('input[type="text"]').attr('placeholder', 'Option ' + (index + 1));
});
}
// Function to set up true/false options
function setupTrueFalseOptions() {
// Ensure the true/false hidden inputs exist
if ($('#true_false_hidden').length === 0) {
$('#true_false_options').append(`
<div id="true_false_hidden" style="display: none;">
<input type="hidden" name="options[]" value="True">
<input type="hidden" name="options[]" value="False">
</div>
`);
}
// Set correct option based on selected radio button
$('input[name="true_false_answer"]').change(function() {
$('#true_false_hidden').html('');
if ($(this).val() === 'true') {
$('#true_false_hidden').html(`
<input type="hidden" name="options[]" value="True">
<input type="hidden" name="options[]" value="False">
<input type="hidden" name="correct_options[]" value="0">
`);
} else {
$('#true_false_hidden').html(`
<input type="hidden" name="options[]" value="True">
<input type="hidden" name="options[]" value="False">
<input type="hidden" name="correct_options[]" value="1">
`);
}
});
// Trigger change to set default if a radio is checked
if ($('input[name="true_false_answer"]:checked').length) {
$('input[name="true_false_answer"]:checked').trigger('change');
} else {
// Default to first option if none selected
$('#true_option').prop('checked', true).trigger('change');
}
}
// Handle question type change
$('#question_type').change(function() {
console.log('Question type changed to:', $(this).val());
// Hide all options sections
$('.options-section').hide();
// Show the relevant section based on the selected type
var selectedType = $(this).val();
switch(selectedType) {
case 'multiple_choice':
// Initialize the options container before showing it
initializeOptionsContainer();
$('#multiple_choice_options').show();
break;
case 'true_false':
$('#true_false_options').show();
// Set default true/false options
setupTrueFalseOptions();
break;
case 'short_answer':
$('#short_answer_options').show();
break;
case 'essay':
$('#essay_options').show();
break;
}
});
// Add option button click handler
$('#add_option').click(addOption);
// Remove option button (delegated event for dynamically added elements)
$('#options_container').on('click', '.remove-option', function() {
if ($('.option-row').length <= 2) {
alert("Multiple choice questions must have at least 2 options.");
return false;
}
$(this).closest('.option-row').remove();
// Update value attributes for correct_options checkboxes
$('.option-row').each(function(index) {
$(this).find('input[type="checkbox"]').val(index);
$(this).find('input[type="text"]').attr('placeholder', 'Option ' + (index + 1));
});
// Disable remove buttons if we have 2 or fewer options
if ($('.option-row').length <= 2) {
$('.remove-option').prop('disabled', true);
}
});
// Form validation before submit
$('#questionForm').submit(function(e) {
var isValid = true;
var questionType = $('#question_type').val();
// Check if question type specific validations pass
if (questionType === 'multiple_choice') {
if ($('.option-row').length < 2) {
alert('Multiple choice questions must have at least 2 options');
isValid = false;
}
if ($('input[name="correct_options[]"]:checked').length === 0) {
alert('Please select at least one correct option');
isValid = false;
}
// Check if all options have text
var emptyOptions = false;
$('.option-row input[type="text"]').each(function() {
if ($(this).val().trim() === '') {
emptyOptions = true;
return false; // break the loop
}
});
if (emptyOptions) {
alert('All options must have text');
isValid = false;
}
} else if (questionType === 'true_false') {
if (!$('input[name="true_false_answer"]:checked').val()) {
alert('Please select True or False as the correct answer');
isValid = false;
}
} else if (questionType === 'short_answer') {
if ($('#answer_key').val().trim() === '') {
alert('Please provide an answer key for the short answer question');
isValid = false;
}
}
if (!isValid) {
e.preventDefault();
}
});
// Initialize form based on current selection
$('#question_type').trigger('change');
}
// This ensures the script runs after jQuery is loaded
document.addEventListener('DOMContentLoaded', function() {
// Check if jQuery is loaded
if (typeof jQuery !== 'undefined') {
console.log('jQuery is loaded, version:', $.fn.jquery);
// Initialize our form
initializeQuestionOptions();
} else {
console.error('jQuery is not loaded yet!');
// Try again after a short delay
setTimeout(function() {
if (typeof jQuery !== 'undefined') {
console.log('jQuery loaded after delay, initializing now');
initializeQuestionOptions();
} else {
console.error('jQuery could not be loaded. Please check your script includes.');
alert('Error: jQuery could not be loaded. Please contact the administrator.');
}
}, 500);
}
});
</script>
<?php
// Include footer
include 'includes/footer.php';
?>