quiztech_theme/functions.php

732 lines
32 KiB
PHP

<?php
/**
* Basic logging function for debugging. Allows passing of an object or array for the $data paramater
*
* Set the CUSTOM_DEBUG_LOG file in wp-config.php
*
*/
function log_to_file($message, $data = false){
// Only proceed if a message is provided
if ($message) {
// Check if the custom log file constant is defined
// Linter fix: Check only if defined, assume non-empty if defined.
if (defined('CUSTOM_DEBUG_LOG')) {
// Linter fix: Use constant() function to avoid undefined constant error
$log_File = constant('CUSTOM_DEBUG_LOG');
$date = new DateTime();
$date = $date->format("Y/m/d h:i:s");
// Convert arrays and objects to JSON format
if (is_array($data) || is_object($data)) {
$data = json_encode($data, JSON_PRETTY_PRINT);
$message = $message . "\r\n" . $data;
} else if ($data) {
$message = $message . " " . $data;
}
// Log the message to the specified file
error_log("[$date] " . $message ."\r\n", 3, $log_File);
}
// If CUSTOM_DEBUG_LOG is not defined, do nothing (fail silently)
// Alternatively, could log to default PHP error log:
// else { error_log("Quiztech Debug: " . $message); }
}
}
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' ),
'save_job_nonce' => wp_create_nonce( 'quiztech_save_job_action' ), // Renamed from add_job_nonce
'get_job_nonce' => wp_create_nonce( 'quiztech_get_job_action' ), // Nonce for fetching job data
'send_invite_nonce' => wp_create_nonce( 'quiztech_send_job_invite_action' ),
'save_question_nonce' => wp_create_nonce( 'quiztech_save_question_action' ), // Nonce for saving question
'get_question_nonce' => wp_create_nonce( 'quiztech_get_question_action' ), // Nonce for fetching question
'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' ),
// Add translatable strings for JS
'l10n' => [
'addNewJobTitle' => esc_html__( 'Add New Job Details', 'quiztech' ),
'editJobTitle' => esc_html__( 'Edit Job Details', 'quiztech' ),
'saveJobButton' => esc_html__( 'Save Job', 'quiztech' ),
'updateJobButton'=> esc_html__( 'Update Job', 'quiztech' ),
'loading' => esc_html__( 'Loading...', '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', 'jquery-ui-draggable' ), // Added draggable dependency
$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 fetching job data for editing.
*/
function quiztech_ajax_get_job() {
// 1. Verify Nonce
check_ajax_referer( 'quiztech_get_job_action', 'nonce' );
// 2. Check Capabilities
$capability = 'manage_options'; // Or a more specific capability like 'edit_jobs'
if ( ! current_user_can( $capability ) ) {
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;
if ( ! $job_id ) {
wp_send_json_error( [ 'message' => esc_html__( 'Invalid job ID provided.', 'quiztech' ) ], 400 );
}
// 4. Fetch Post
$post = get_post( $job_id );
// 5. Validate Post
if ( ! $post || $post->post_type !== 'job' ) {
wp_send_json_error( [ 'message' => esc_html__( 'Job not found.', 'quiztech' ) ], 404 );
}
// Optional: Check if the current user is the author or has higher privileges
if ( $post->post_author != get_current_user_id() && ! current_user_can( 'edit_others_posts' ) ) { // Adjust capability check if needed
wp_send_json_error( [ 'message' => esc_html__( 'You do not have permission to view this job\'s details.', 'quiztech' ) ], 403 );
}
// 6. Send Response
wp_send_json_success( [
'id' => $post->ID,
'title' => $post->post_title,
'description' => $post->post_content,
'assessment_id' => get_post_meta( $post->ID, '_quiztech_associated_assessment_id', true ), // Fetch the linked assessment ID
] );
}
add_action( 'wp_ajax_quiztech_get_job', 'quiztech_ajax_get_job' );
/**
* AJAX handler for saving (adding or updating) a job post from the frontend.
*/
function quiztech_ajax_save_job() {
// 1. Verify Nonce
check_ajax_referer( 'quiztech_save_job_action', 'nonce' ); // Use new nonce action
// 2. Check Capabilities (adjust capability if needed)
$capability = 'manage_options'; // Or a more specific capability like 'edit_jobs'
if ( ! current_user_can( $capability ) ) {
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;
$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'] ) ) : '';
$current_user_id = get_current_user_id();
// Basic Validation
if ( empty( $job_title ) ) {
wp_send_json_error( [ 'message' => esc_html__( 'Job title cannot be empty.', 'quiztech' ) ], 400 );
}
// 4. Prepare Post Data
$post_data = [
'post_title' => $job_title,
'post_content' => $job_description,
'post_type' => 'job',
'post_status' => 'publish', // Default to publish, adjust if needed
];
$result = 0;
$is_update = false;
// 5. Determine if Update or Insert
if ( $job_id > 0 ) {
// --- Update Existing Job ---
$is_update = true;
$existing_post = get_post( $job_id );
// Validation for update
if ( ! $existing_post || $existing_post->post_type !== 'job' ) {
wp_send_json_error( [ 'message' => esc_html__( 'Job not found.', 'quiztech' ) ], 404 );
}
// Optional: Check if the current user is the author or has higher privileges
if ( $existing_post->post_author != $current_user_id && ! current_user_can( 'edit_others_posts' ) ) { // Adjust capability check if needed
wp_send_json_error( [ 'message' => esc_html__( 'You do not have permission to edit this job.', 'quiztech' ) ], 403 );
}
$post_data['ID'] = $job_id; // Set ID for update
$result = wp_update_post( $post_data, true );
} else {
// --- Insert New Job ---
$post_data['post_author'] = $current_user_id; // Set author only for new posts
$result = wp_insert_post( $post_data, true );
}
// 6. Send Response
if ( is_wp_error( $result ) ) {
wp_send_json_error( [ 'message' => $result->get_error_message() ], 500 );
} elseif ( $result === 0 && $is_update ) { // Check if it was an update with no changes
// wp_update_post returns 0 if nothing changed.
// We still need to potentially update the meta field even if core fields didn't change.
$new_or_updated_post_id = $job_id;
} else {
// Success (Insert or Update with changes)
$new_or_updated_post_id = $result; // $result is the post ID on success
}
// --- Update Meta Fields ---
if ( $new_or_updated_post_id > 0 ) {
// Save Linked Assessment ID
if ( isset( $_POST['quiztech_associated_assessment_id'] ) ) {
$assessment_id = sanitize_text_field( wp_unslash( $_POST['quiztech_associated_assessment_id'] ) );
$assessment_id = '' === $assessment_id ? '' : absint( $assessment_id );
update_post_meta( $new_or_updated_post_id, '_quiztech_associated_assessment_id', $assessment_id );
} else {
// If not set in POST, maybe delete it? Or assume it wasn't part of the form?
// For now, let's assume if it's not sent, we don't change it.
// delete_post_meta($new_or_updated_post_id, '_quiztech_associated_assessment_id');
}
// Add other meta field updates here if needed
}
// --- End Meta Fields Update ---
// Send success response
$success_message = $is_update
? ( $result === 0 ? esc_html__( 'Job details saved (no changes detected).', 'quiztech' ) : esc_html__( 'Job updated successfully.', 'quiztech' ) )
: esc_html__( 'Job created successfully.', 'quiztech' );
wp_send_json_success( [
'message' => $success_message,
'job_id' => $new_or_updated_post_id
] );
}
add_action( 'wp_ajax_quiztech_save_job', 'quiztech_ajax_save_job' ); // Use new action hook
/**
* 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 );
} elseif ( !is_array($result) || !isset($result['token']) ) {
// Handle case where array isn't returned or token is missing
\error_log('Quiztech Send Invite Error: create_invitation did not return expected array with token.');
wp_send_json_error( [ 'message' => esc_html__( 'Failed to retrieve invitation token after creation.', 'quiztech' ) ], 500 );
} else {
// Success! $result contains the array. Extract the token.
$token = $result['token'];
// $invitation_id = $result['id']; // ID is available if needed later
$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'] ) ) {
return;
}
if ($_POST['quiztech_action'] !== 'initiate_credit_purchase') {
return;
}
if (is_admin()) {
return;
}
// Get the Manage Credits page URL reliably
$manage_credits_page = get_page_by_path('manage-credits'); // Assumes page slug is 'manage-credits'
if (!$manage_credits_page) {
// Fallback or error if page doesn't exist
wp_die(esc_html__('Manage Credits page not found. Cannot process purchase.', 'quiztech'));
exit;
}
$manage_credits_url = get_permalink($manage_credits_page->ID);
// 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' ) )
), $manage_credits_url ); // Use the stored URL
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' ) )
), $manage_credits_url );
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' ) )
), $manage_credits_url );
wp_safe_redirect( $redirect_url );
exit;
}
// Check if the plugin function exists and call it
// Note: The plugin function expects (user_id, item_id, quantity)
if ( function_exists( '\Quiztech\AssessmentPlatform\Includes\quiztech_initiate_credit_purchase' ) ) {
// Pass package_id as item_id, quantity is 1 for these packages
$result = \Quiztech\AssessmentPlatform\Includes\quiztech_initiate_credit_purchase( $user_id, $package_id, 1 );
// 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() )
), $manage_credits_url );
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,
'points' => get_post_meta( $question_id, '_quiztech_question_points', true ), // Add points
] );
}
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() {
// Verify nonce using the correct field name from wp_nonce_field()
check_ajax_referer( 'quiztech_save_question_action', 'quiztech_question_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
}
// Update Question Points meta
$points = isset( $_POST['question_points'] ) ? absint( $_POST['question_points'] ) : 1; // Default to 1 if not set or invalid
update_post_meta( $new_or_updated_post_id, '_quiztech_question_points', $points );
// Set Credit Cost meta (always 3 for user-generated questions as per requirement)
update_post_meta( $new_or_updated_post_id, '_quiztech_credit_cost', 3 );
// --- 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' );
// --- Dashboard Helper Functions ---
/**
* Get URLs for dashboard quick links.
*
* @return array Associative array of 'Label' => 'URL'.
*/
function quiztech_get_dashboard_quick_links() {
$links = [];
$page_slugs = [
'manage-jobs' => __('Manage Jobs', 'quiztech'),
'assessment-builder' => __('Manage Assessments', 'quiztech'), // Link to builder/manager
'manage-questions' => __('Manage Questions', 'quiztech'),
'manage-credits' => __('Manage Credits', 'quiztech'),
// Add 'Create New' links if separate pages exist, otherwise link to managers
// 'create-job' => __('Create New Job', 'quiztech'),
];
foreach ($page_slugs as $slug => $label) {
$page = get_page_by_path($slug);
$links[$label] = $page ? get_permalink($page->ID) : ''; // Return empty string if page not found
}
// Add direct links for creating new content if possible (often handled by query args on manager pages)
// Example: $links[__('Create New Job', 'quiztech')] = add_query_arg('action', 'new', $links[__('Manage Jobs', 'quiztech')]);
return $links;
}
/**
* Get recent invitations sent.
*
* @param int $count Number of invitations to retrieve.
* @return array Array of invitation objects/arrays.
*/
function quiztech_get_dashboard_recent_invitations($count = 5) {
global $wpdb;
$invitations_table = $wpdb->prefix . 'quiztech_invitations';
$posts_table = $wpdb->posts;
// Ensure table exists before querying
if ($wpdb->get_var("SHOW TABLES LIKE '{$invitations_table}'") != $invitations_table) {
return []; // Table doesn't exist
}
$query = $wpdb->prepare(
"SELECT inv.applicant_email, inv.created_timestamp, p.post_title AS job_title
FROM {$invitations_table} inv
LEFT JOIN {$posts_table} p ON inv.job_id = p.ID
ORDER BY inv.created_timestamp DESC
LIMIT %d",
absint($count)
);
$results = $wpdb->get_results($query);
return $results ? $results : [];
}
/**
* Get recent completed assessments.
*
* @param int $count Number of completions to retrieve.
* @return array Array of completion data.
*/
function quiztech_get_dashboard_recent_completions($count = 5) {
global $wpdb;
$invitations_table = $wpdb->prefix . 'quiztech_invitations';
$completions = [];
$args = [
'post_type' => 'user_evaluation',
'post_status' => 'completed', // Assuming 'completed' status is used
'posts_per_page' => absint($count),
'orderby' => 'date',
'order' => 'DESC',
'meta_query' => [
[
'key' => 'quiztech_invitation_id',
'compare' => 'EXISTS' // Ensure it's linked
]
]
];
$query = new WP_Query($args);
if ($query->have_posts()) {
// Check if invitations table exists
$invitations_table_exists = ($wpdb->get_var("SHOW TABLES LIKE '{$invitations_table}'") == $invitations_table);
while ($query->have_posts()) {
$query->the_post();
$evaluation_id = get_the_ID();
$completion_date = get_the_date(); // Or post_modified if that's more relevant
$invitation_id = get_post_meta($evaluation_id, 'quiztech_invitation_id', true);
$applicant_email = 'N/A';
$job_title = 'N/A';
if ($invitation_id && $invitations_table_exists) {
$invitation_data = $wpdb->get_row($wpdb->prepare(
"SELECT inv.applicant_email, p.post_title AS job_title
FROM {$invitations_table} inv
LEFT JOIN {$wpdb->posts} p ON inv.job_id = p.ID
WHERE inv.id = %d",
absint($invitation_id)
));
if ($invitation_data) {
$applicant_email = $invitation_data->applicant_email;
$job_title = $invitation_data->job_title;
}
}
$completions[] = [
'applicant_email' => $applicant_email,
'job_title' => $job_title,
'completion_date' => $completion_date,
'evaluation_id' => $evaluation_id // For potential linking
];
}
wp_reset_postdata();
}
return $completions;
}
/**
* Get the count of active job posts.
*
* @return int Count of published jobs.
*/
function quiztech_get_dashboard_active_job_count() {
$args = [
'post_type' => 'job',
'post_status' => 'publish',
'posts_per_page' => -1, // Count all
'fields' => 'ids' // Only need IDs for counting
];
$query = new WP_Query($args);
return $query->post_count;
}
// --- End Dashboard Helper Functions ---
add_action( 'template_redirect', 'quiztech_handle_credit_purchase_submission', 1 ); // Hook earlier (priority 1)