413 lines
19 KiB
PHP
413 lines
19 KiB
PHP
<?php
|
|
|
|
function quiztech_theme_enqueue_scripts() {
|
|
// List of Quiztech custom page templates
|
|
$quiztech_templates = [
|
|
'template-quiztech-dashboard.php',
|
|
'template-manage-jobs.php',
|
|
'template-manage-assessments.php',
|
|
'template-manage-questions.php',
|
|
'template-manage-credits.php',
|
|
'template-view-results.php',
|
|
'template-assessment-builder.php', // Add Assessment Builder template
|
|
];
|
|
|
|
// Check if the current page is using one of our general Quiztech templates
|
|
$is_quiztech_template = false;
|
|
foreach ( $quiztech_templates as $template ) {
|
|
if ( is_page_template( $template ) ) {
|
|
$is_quiztech_template = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Only enqueue the script on our specific template pages
|
|
if ( $is_quiztech_template ) {
|
|
$script_path = get_stylesheet_directory() . '/js/quiztech-theme.js';
|
|
$script_url = get_stylesheet_directory_uri() . '/js/quiztech-theme.js';
|
|
$version = file_exists( $script_path ) ? filemtime( $script_path ) : '1.0';
|
|
|
|
wp_enqueue_script(
|
|
'quiztech-theme-script',
|
|
$script_url,
|
|
array( 'jquery' ), // Depends on jQuery
|
|
$version,
|
|
true // Load in footer
|
|
);
|
|
|
|
// Localize script with necessary data for AJAX calls
|
|
wp_localize_script( 'quiztech-theme-script', 'quiztechThemeData', [
|
|
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
|
'add_job_nonce' => wp_create_nonce( 'quiztech_add_new_job_action' ),
|
|
'send_invite_nonce' => wp_create_nonce( 'quiztech_send_job_invite_action' ),
|
|
'save_question_nonce' => wp_create_nonce( 'quiztech_save_question_action' ), // Nonce for saving
|
|
'get_question_nonce' => wp_create_nonce( 'quiztech_get_question_action' ), // Nonce for fetching
|
|
'error_generic' => esc_html__( 'An error occurred. Please try again.', 'quiztech' ),
|
|
'error_permissions' => esc_html__( 'You do not have permission to perform this action.', 'quiztech' ),
|
|
'error_missing_assessment' => esc_html__( 'Error: No assessment is linked to this job.', 'quiztech' ),
|
|
'invite_sent_success' => esc_html__( 'Invite sent successfully!', 'quiztech' ),
|
|
'job_added_success' => esc_html__( 'Job added successfully!', 'quiztech' ),
|
|
]);
|
|
}
|
|
|
|
// --- Enqueue script specifically for Assessment Builder ---
|
|
if ( is_page_template( 'template-assessment-builder.php' ) ) {
|
|
$builder_script_path = get_stylesheet_directory() . '/js/quiztech-assessment-builder.js';
|
|
$builder_script_url = get_stylesheet_directory_uri() . '/js/quiztech-assessment-builder.js';
|
|
$builder_version = file_exists( $builder_script_path ) ? filemtime( $builder_script_path ) : '1.0';
|
|
|
|
wp_enqueue_script(
|
|
'quiztech-builder-script', // Unique handle
|
|
$builder_script_url,
|
|
array( 'jquery', 'jquery-ui-sortable' ), // Depends on jQuery and Sortable for drag/drop later
|
|
$builder_version,
|
|
true // Load in footer
|
|
);
|
|
|
|
// Localize data specifically for the builder script
|
|
wp_localize_script( 'quiztech-builder-script', 'quiztechBuilderData', [
|
|
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
|
'fetch_nonce' => wp_create_nonce( 'quiztech_fetch_library_questions_action' ), // TODO: Verify action name in AJAX handler
|
|
'save_nonce' => wp_create_nonce( 'quiztech_save_assessment_action' ), // TODO: Verify action name in AJAX handler
|
|
'error_generic' => esc_html__( 'An error occurred. Please try again.', 'quiztech' ),
|
|
'loading_questions' => esc_html__( 'Loading questions...', 'quiztech' ),
|
|
'saving_assessment' => esc_html__( 'Saving assessment...', 'quiztech' ),
|
|
'assessment_saved' => esc_html__( 'Assessment saved successfully!', 'quiztech' ),
|
|
// Add more localized strings as needed
|
|
]);
|
|
}
|
|
}
|
|
add_action( 'wp_enqueue_scripts', 'quiztech_theme_enqueue_scripts' );
|
|
|
|
|
|
/**
|
|
* AJAX handler for adding a new job post from the frontend.
|
|
*/
|
|
function quiztech_ajax_add_new_job() {
|
|
// 1. Verify Nonce
|
|
check_ajax_referer( 'quiztech_add_new_job_action', 'nonce' );
|
|
|
|
// 2. Check Capabilities (adjust capability if needed)
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( [ 'message' => esc_html__( 'Insufficient permissions.', 'quiztech' ) ], 403 );
|
|
}
|
|
|
|
// 3. Sanitize Input
|
|
$job_title = isset( $_POST['job_title'] ) ? sanitize_text_field( wp_unslash( $_POST['job_title'] ) ) : '';
|
|
$job_description = isset( $_POST['job_description'] ) ? sanitize_textarea_field( wp_unslash( $_POST['job_description'] ) ) : '';
|
|
|
|
if ( empty( $job_title ) ) {
|
|
wp_send_json_error( [ 'message' => esc_html__( 'Job title cannot be empty.', 'quiztech' ) ], 400 );
|
|
}
|
|
|
|
// 4. Create Post
|
|
$post_data = [
|
|
'post_title' => $job_title,
|
|
'post_content' => $job_description,
|
|
'post_status' => 'publish', // Or 'draft' if preferred
|
|
'post_author' => get_current_user_id(),
|
|
'post_type' => 'job',
|
|
];
|
|
|
|
$post_id = wp_insert_post( $post_data, true ); // Pass true to return WP_Error on failure
|
|
|
|
// 5. Send Response
|
|
if ( is_wp_error( $post_id ) ) {
|
|
wp_send_json_error( [ 'message' => $post_id->get_error_message() ], 500 );
|
|
} else {
|
|
// Optionally return HTML for the new row, or just success and let JS handle refresh/update
|
|
wp_send_json_success( [
|
|
'message' => esc_html__( 'Job created successfully.', 'quiztech' ),
|
|
'post_id' => $post_id
|
|
] );
|
|
}
|
|
}
|
|
add_action( 'wp_ajax_add_new_job', 'quiztech_ajax_add_new_job' );
|
|
|
|
|
|
/**
|
|
* AJAX handler for sending a job invite from the frontend.
|
|
*/
|
|
function quiztech_ajax_send_job_invite() {
|
|
// 1. Verify Nonce
|
|
check_ajax_referer( 'quiztech_send_job_invite_action', 'nonce' );
|
|
|
|
// 2. Check Capabilities (adjust capability if needed)
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( [ 'message' => esc_html__( 'Insufficient permissions.', 'quiztech' ) ], 403 );
|
|
}
|
|
|
|
// 3. Sanitize Input
|
|
$job_id = isset( $_POST['job_id'] ) ? absint( $_POST['job_id'] ) : 0;
|
|
$applicant_email = isset( $_POST['applicant_email'] ) ? sanitize_email( wp_unslash( $_POST['applicant_email'] ) ) : '';
|
|
|
|
if ( ! $job_id || ! is_email( $applicant_email ) ) {
|
|
wp_send_json_error( [ 'message' => esc_html__( 'Invalid job ID or email address.', 'quiztech' ) ], 400 );
|
|
}
|
|
|
|
// 4. Check if Job exists and belongs to user (optional extra check)
|
|
$job_post = get_post( $job_id );
|
|
if ( ! $job_post || $job_post->post_type !== 'job' || $job_post->post_author != get_current_user_id() ) {
|
|
wp_send_json_error( [ 'message' => esc_html__( 'Invalid job specified.', 'quiztech' ) ], 404 );
|
|
}
|
|
|
|
// 5. Get Linked Assessment ID
|
|
$assessment_id = get_post_meta( $job_id, '_quiztech_linked_assessment_id', true );
|
|
if ( empty( $assessment_id ) ) {
|
|
wp_send_json_error( [ 'message' => esc_html__( 'No assessment is linked to this job. Please edit the job and link an assessment.', 'quiztech' ) ], 400 );
|
|
}
|
|
$assessment_id = absint( $assessment_id );
|
|
|
|
// 6. Use the Invitation Class (ensure plugin is active and class exists)
|
|
if ( ! class_exists( 'Quiztech\\AssessmentPlatform\\Includes\\Invitations' ) ) {
|
|
wp_send_json_error( [ 'message' => esc_html__( 'Invitation system is unavailable.', 'quiztech' ) ], 500 );
|
|
}
|
|
|
|
try {
|
|
$invitations = new \Quiztech\AssessmentPlatform\Includes\Invitations();
|
|
$result = $invitations->create_invitation( $job_id, $assessment_id, $applicant_email );
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
wp_send_json_error( [ 'message' => $result->get_error_message() ], 500 );
|
|
} elseif ( $result === false ) {
|
|
// Generic failure from create_invitation if no WP_Error
|
|
wp_send_json_error( [ 'message' => esc_html__( 'Failed to create invitation.', 'quiztech' ) ], 500 );
|
|
} else {
|
|
// Success! $result contains the token. Now send the email.
|
|
$token = $result;
|
|
$email_sent = $invitations->send_invitation_email( $applicant_email, $token, [ 'job_title' => get_the_title( $job_id ) ] );
|
|
|
|
if ( $email_sent ) {
|
|
wp_send_json_success( [ 'message' => esc_html__( 'Invitation sent successfully.', 'quiztech' ) ] );
|
|
} else {
|
|
// Invitation created, but email failed.
|
|
wp_send_json_error( [ 'message' => esc_html__( 'Invitation created, but the email could not be sent. Please check email settings.', 'quiztech' ) ], 500 );
|
|
}
|
|
}
|
|
} catch ( \Exception $e ) {
|
|
// Catch any unexpected exceptions
|
|
error_log( 'Quiztech Send Invite Error: ' . $e->getMessage() );
|
|
wp_send_json_error( [ 'message' => esc_html__( 'An unexpected error occurred while sending the invitation.', 'quiztech' ) ], 500 );
|
|
}
|
|
}
|
|
add_action( 'wp_ajax_send_job_invite', 'quiztech_ajax_send_job_invite' );
|
|
|
|
|
|
|
|
/**
|
|
* Handle the submission from the "Buy Credits" forms on the Manage Credits page.
|
|
*/
|
|
function quiztech_handle_credit_purchase_submission() {
|
|
// Only process if our action is set and we are on the frontend
|
|
if ( ! isset( $_POST['quiztech_action'] ) || $_POST['quiztech_action'] !== 'initiate_credit_purchase' || is_admin() ) {
|
|
return;
|
|
}
|
|
|
|
// Verify nonce
|
|
if ( ! isset( $_POST['quiztech_buy_credits_nonce'] ) || ! wp_verify_nonce( $_POST['quiztech_buy_credits_nonce'], 'quiztech_buy_credits_action' ) ) {
|
|
// Nonce is invalid, redirect back with error
|
|
$redirect_url = add_query_arg( array(
|
|
'purchase_status' => 'error',
|
|
'message' => urlencode( __( 'Security check failed. Please try again.', 'quiztech' ) )
|
|
), get_permalink() ); // Assumes this runs when get_permalink() refers to the Manage Credits page
|
|
wp_safe_redirect( $redirect_url );
|
|
exit;
|
|
}
|
|
|
|
// Check capability (basic read for now)
|
|
if ( ! current_user_can( 'read' ) ) {
|
|
$redirect_url = add_query_arg( array(
|
|
'purchase_status' => 'error',
|
|
'message' => urlencode( __( 'You do not have permission to purchase credits.', 'quiztech' ) )
|
|
), get_permalink() );
|
|
wp_safe_redirect( $redirect_url );
|
|
exit;
|
|
}
|
|
|
|
// Sanitize inputs
|
|
$user_id = get_current_user_id();
|
|
$package_id = isset( $_POST['package_id'] ) ? sanitize_key( $_POST['package_id'] ) : '';
|
|
$credit_amount = isset( $_POST['credit_amount'] ) ? absint( $_POST['credit_amount'] ) : 0;
|
|
$price = isset( $_POST['price'] ) ? sanitize_text_field( $_POST['price'] ) : '0.00';
|
|
$currency = isset( $_POST['currency'] ) ? sanitize_key( $_POST['currency'] ) : 'USD';
|
|
|
|
if ( $credit_amount <= 0 || empty($package_id) ) {
|
|
$redirect_url = add_query_arg( array(
|
|
'purchase_status' => 'error',
|
|
'message' => urlencode( __( 'Invalid credit package selected.', 'quiztech' ) )
|
|
), get_permalink() );
|
|
wp_safe_redirect( $redirect_url );
|
|
exit;
|
|
}
|
|
|
|
// Check if the plugin function exists and call it
|
|
if ( function_exists( 'quiztech_initiate_credit_purchase' ) ) {
|
|
$result = quiztech_initiate_credit_purchase( $user_id, $credit_amount, $price, $currency, $package_id );
|
|
|
|
// If the function returns an error, redirect back with the message
|
|
if ( is_wp_error( $result ) ) {
|
|
$redirect_url = add_query_arg( array(
|
|
'purchase_status' => 'error',
|
|
'message' => urlencode( $result->get_error_message() )
|
|
), get_permalink() );
|
|
wp_safe_redirect( $redirect_url );
|
|
exit;
|
|
}
|
|
// If the function didn't return an error, it should have handled the redirect to the payment gateway.
|
|
// If it somehow didn't redirect AND didn't return an error, we might end up here.
|
|
// We could add a fallback redirect with 'initiated' status, but ideally the plugin handles it.
|
|
// For now, assume the plugin handles the success redirect or returns WP_Error.
|
|
|
|
} else {
|
|
// Function doesn't exist - plugin likely inactive or issue
|
|
$redirect_url = add_query_arg( array(
|
|
'purchase_status' => 'error',
|
|
'message' => urlencode( __( 'Credit purchase functionality is currently unavailable.', 'quiztech' ) )
|
|
), get_permalink() );
|
|
wp_safe_redirect( $redirect_url );
|
|
exit;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* AJAX handler for fetching question data for editing.
|
|
*/
|
|
function quiztech_ajax_get_question() {
|
|
check_ajax_referer( 'quiztech_get_question_action', 'nonce' );
|
|
|
|
if ( ! current_user_can( 'edit_questions' ) ) {
|
|
wp_send_json_error( [ 'message' => esc_html__( 'Insufficient permissions.', 'quiztech' ) ], 403 );
|
|
}
|
|
|
|
$question_id = isset( $_POST['question_id'] ) ? absint( $_POST['question_id'] ) : 0;
|
|
|
|
if ( ! $question_id ) {
|
|
wp_send_json_error( [ 'message' => esc_html__( 'Invalid question ID.', 'quiztech' ) ], 400 );
|
|
}
|
|
|
|
$post = get_post( $question_id );
|
|
if ( ! $post || $post->post_type !== 'question' ) {
|
|
wp_send_json_error( [ 'message' => esc_html__( 'Question not found.', 'quiztech' ) ], 404 );
|
|
}
|
|
|
|
$question_type = get_post_meta( $question_id, '_quiztech_question_type', true );
|
|
$options_meta = get_post_meta( $question_id, '_quiztech_question_options', true ); // Expects array: [['text' => '...', 'correct' => bool], ...]
|
|
$terms = get_the_terms( $question_id, 'quiztech_category' );
|
|
$category_id = ! empty( $terms ) && ! is_wp_error( $terms ) ? $terms[0]->term_id : '';
|
|
|
|
// Format options back to string for textarea
|
|
$options_string = '';
|
|
if ( is_array( $options_meta ) ) {
|
|
foreach ( $options_meta as $option ) {
|
|
$options_string .= $option['text'] . ( $option['correct'] ? '*' : '' ) . "\n";
|
|
}
|
|
$options_string = trim( $options_string );
|
|
}
|
|
|
|
wp_send_json_success( [
|
|
'id' => $question_id,
|
|
'title' => $post->post_title,
|
|
'content' => $post->post_content,
|
|
'type' => $question_type,
|
|
'options' => $options_string,
|
|
'category' => $category_id,
|
|
] );
|
|
}
|
|
add_action( 'wp_ajax_quiztech_get_question', 'quiztech_ajax_get_question' );
|
|
|
|
|
|
/**
|
|
* AJAX handler for saving (creating or updating) a question.
|
|
*/
|
|
function quiztech_ajax_save_question() {
|
|
check_ajax_referer( 'quiztech_save_question_action', 'nonce' );
|
|
|
|
if ( ! current_user_can( 'edit_questions' ) ) {
|
|
wp_send_json_error( [ 'message' => esc_html__( 'Insufficient permissions.', 'quiztech' ) ], 403 );
|
|
}
|
|
|
|
// Sanitize inputs
|
|
$question_id = isset( $_POST['question_id'] ) ? absint( $_POST['question_id'] ) : 0;
|
|
$title = isset( $_POST['question_title'] ) ? sanitize_text_field( wp_unslash( $_POST['question_title'] ) ) : '';
|
|
$content = isset( $_POST['question_content'] ) ? sanitize_textarea_field( wp_unslash( $_POST['question_content'] ) ) : '';
|
|
$type = isset( $_POST['question_type'] ) ? sanitize_key( $_POST['question_type'] ) : '';
|
|
$options_string = isset( $_POST['question_options'] ) ? sanitize_textarea_field( wp_unslash( $_POST['question_options'] ) ) : '';
|
|
$category_id = isset( $_POST['question_category'] ) ? absint( $_POST['question_category'] ) : 0;
|
|
|
|
// Basic validation
|
|
if ( empty( $title ) || empty( $content ) || empty( $type ) ) {
|
|
wp_send_json_error( [ 'message' => esc_html__( 'Title, Content, and Type are required.', 'quiztech' ) ], 400 );
|
|
}
|
|
// Validate type against known types
|
|
$valid_types = ['multiple-choice', 'true-false', 'short-answer']; // Keep in sync with template
|
|
if ( ! in_array( $type, $valid_types ) ) {
|
|
wp_send_json_error( [ 'message' => esc_html__( 'Invalid question type selected.', 'quiztech' ) ], 400 );
|
|
}
|
|
|
|
// Prepare post data
|
|
$post_data = [
|
|
'post_title' => $title,
|
|
'post_content' => $content,
|
|
'post_status' => 'publish',
|
|
'post_type' => 'question',
|
|
'post_author' => get_current_user_id(),
|
|
];
|
|
|
|
if ( $question_id > 0 ) {
|
|
// Update existing post
|
|
$post_data['ID'] = $question_id;
|
|
$result = wp_update_post( $post_data, true );
|
|
} else {
|
|
// Insert new post
|
|
$result = wp_insert_post( $post_data, true );
|
|
}
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
wp_send_json_error( [ 'message' => $result->get_error_message() ], 500 );
|
|
}
|
|
|
|
// If insert/update was successful, $result is the post ID
|
|
$new_or_updated_post_id = $result;
|
|
|
|
// --- Update Meta and Taxonomy ---
|
|
|
|
// Update Question Type meta
|
|
update_post_meta( $new_or_updated_post_id, '_quiztech_question_type', $type );
|
|
|
|
// Parse and Update Options meta (only for relevant types)
|
|
$options_meta = [];
|
|
if ( $type === 'multiple-choice' || $type === 'true-false' ) {
|
|
$lines = explode( "\n", $options_string );
|
|
foreach ( $lines as $line ) {
|
|
$line = trim( $line );
|
|
if ( ! empty( $line ) ) {
|
|
$is_correct = ( substr( $line, -1 ) === '*' );
|
|
$text = $is_correct ? trim( substr( $line, 0, -1 ) ) : $line;
|
|
if (!empty($text)) {
|
|
$options_meta[] = [ 'text' => $text, 'correct' => $is_correct ];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
update_post_meta( $new_or_updated_post_id, '_quiztech_question_options', $options_meta );
|
|
|
|
// Set Category term
|
|
if ( $category_id > 0 ) {
|
|
wp_set_post_terms( $new_or_updated_post_id, [ $category_id ], 'quiztech_category', false ); // Replace existing terms
|
|
} else {
|
|
wp_set_post_terms( $new_or_updated_post_id, [], 'quiztech_category', false ); // Remove terms if none selected
|
|
}
|
|
|
|
// --- Prepare Success Response ---
|
|
// Optionally, generate HTML for the updated/new table row to send back
|
|
// For simplicity now, just send success and let JS reload or handle update
|
|
wp_send_json_success( [
|
|
'message' => $question_id > 0 ? esc_html__( 'Question updated successfully.', 'quiztech' ) : esc_html__( 'Question added successfully.', 'quiztech' ),
|
|
'question_id' => $new_or_updated_post_id,
|
|
// 'row_html' => '<tr>...</tr>' // TODO: Optionally generate row HTML
|
|
] );
|
|
}
|
|
add_action( 'wp_ajax_quiztech_save_question', 'quiztech_ajax_save_question' );
|
|
|
|
add_action( 'init', 'quiztech_handle_credit_purchase_submission' ); // Hook into init
|