diff --git a/functions.php b/functions.php
index 212a449..bee98af 100644
--- a/functions.php
+++ b/functions.php
@@ -40,6 +40,8 @@ function quiztech_theme_enqueue_scripts() {
'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' ),
@@ -189,3 +191,223 @@ function quiztech_ajax_send_job_invite() {
}
}
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' => '
...
' // 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
diff --git a/template-manage-credits.php b/template-manage-credits.php
index bed4f95..dfdd375 100644
--- a/template-manage-credits.php
+++ b/template-manage-credits.php
@@ -8,9 +8,10 @@
*/
// Ensure the user is logged in and has the appropriate capability.
-// Using 'manage_options' as a placeholder capability for Quiz Managers.
-// Replace with a more specific capability like 'manage_quiztech_credits' or similar if defined.
-if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
+// Using basic 'read' capability for now, as specific credit management caps aren't defined.
+$required_capability = 'read';
+
+if ( ! is_user_logged_in() || ! current_user_can( $required_capability ) ) {
// Redirect to login page or show an error message.
wp_safe_redirect( wp_login_url( get_permalink() ) );
exit;
@@ -18,6 +19,10 @@ if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
// wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'quiztech' ), 403 );
}
+// Get current user's credit balance
+$user_id = get_current_user_id();
+$current_balance = function_exists('quiztech_get_credit_balance') ? quiztech_get_credit_balance( $user_id ) : __( 'N/A (Function not found)', 'quiztech' );
+
get_header(); ?>
@@ -29,8 +34,60 @@ get_header(); ?>
-
-
+
+
+
+
+
+
+
+
+
+
+
+ array( 'amount' => 10, 'price' => '10.00', 'currency' => 'USD' ),
+ '50_credits' => array( 'amount' => 50, 'price' => '45.00', 'currency' => 'USD' ),
+ '100_credits' => array( 'amount' => 100, 'price' => '80.00', 'currency' => 'USD' ),
+ );
+ ?>
+
+
+
+
' . esc_html__( 'Redirecting to payment gateway...', 'quiztech' ) . '
';
+ } elseif ( $_GET['purchase_status'] === 'error' ) {
+ $error_message = isset( $_GET['message'] ) ? sanitize_text_field( urldecode( $_GET['message'] ) ) : __( 'An unknown error occurred.', 'quiztech' );
+ echo '
' . esc_html( $error_message ) . '
';
+ } elseif ( $_GET['purchase_status'] === 'success' ) {
+ echo '
' . esc_html__( 'Credits successfully added!', 'quiztech' ) . '
';
+ } elseif ( $_GET['purchase_status'] === 'cancelled' ) {
+ echo '
' . esc_html__( 'Payment cancelled.', 'quiztech' ) . '
';
+ }
+ }
+ ?>
+
diff --git a/template-manage-questions.php b/template-manage-questions.php
index 009bdfc..04c199c 100644
--- a/template-manage-questions.php
+++ b/template-manage-questions.php
@@ -146,23 +146,53 @@ get_header(); ?>
diff --git a/template-view-results.php b/template-view-results.php
index f072ca8..b06af69 100644
--- a/template-view-results.php
+++ b/template-view-results.php
@@ -8,9 +8,10 @@
*/
// Ensure the user is logged in and has the appropriate capability.
-// Using 'manage_options' as a placeholder capability for Quiz Managers.
-// Replace with a more specific capability like 'view_quiztech_results' or similar if defined.
-if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
+// Capability required to view results (tied to managing jobs).
+$required_capability = 'edit_jobs'; // Or a more specific 'view_results' capability if created
+
+if ( ! is_user_logged_in() || ! current_user_can( $required_capability ) ) {
// Redirect to login page or show an error message.
wp_safe_redirect( wp_login_url( get_permalink() ) );
exit;
@@ -18,6 +19,26 @@ if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
// wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'quiztech' ), 403 );
}
+// Get Job ID from query parameter
+$job_id = isset( $_GET['job_id'] ) ? absint( $_GET['job_id'] ) : 0;
+$job_post = null;
+$error_message = '';
+
+if ( ! $job_id ) {
+ $error_message = __( 'No Job ID specified.', 'quiztech' );
+} else {
+ $job_post = get_post( $job_id );
+ if ( ! $job_post || $job_post->post_type !== 'job' ) {
+ $error_message = __( 'Invalid Job ID specified.', 'quiztech' );
+ $job_post = null; // Ensure job_post is null if invalid
+ }
+ // Optional: Check if the current user owns the job or has higher privileges
+ // if ( $job_post && $job_post->post_author != get_current_user_id() && ! current_user_can('edit_others_jobs') ) {
+ // $error_message = __( 'You do not have permission to view results for this job.', 'quiztech' );
+ // $job_post = null;
+ // }
+}
+
get_header(); ?>
@@ -29,8 +50,77 @@ get_header(); ?>
-
-
+
+
+
+
+
+
+
+ 'user_evaluation',
+ 'post_status' => 'any', // Show completed, in-progress, etc.
+ 'posts_per_page' => -1, // Show all for this job
+ 'meta_query' => array(
+ array(
+ 'key' => '_quiztech_job_id', // Make sure this meta key is correct
+ 'value' => $job_id,
+ 'compare' => '=',
+ 'type' => 'NUMERIC',
+ ),
+ ),
+ 'orderby' => 'date', // Order by submission date
+ 'order' => 'DESC',
+ );
+ $evaluations_query = new WP_Query( $args );
+ ?>
+
+ have_posts() ) : ?>
+
+
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+ have_posts() ) : $evaluations_query->the_post(); ?>
+ label : get_post_status( $evaluation_id );
+ $edit_link = get_edit_post_link( $evaluation_id );
+ ?>
+
+ |
+ |
+ |
+ |
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+