Fix activation errors and address initial TODOs

- Resolved fatal errors during plugin activation related to Composer autoloading, procedural function loading ('\die' vs 'die'), and 'dbDelta' SQL formatting.
- Defined and documented required post meta fields in 'memory-bank/project-knowledge/meta-fields.md'.
- Implemented logic for custom columns in 'AdminListTables.php' using defined meta keys.
- Made assessment title dynamic in 'assessment-shell.php'.
- Implemented check for pre-screening status in 'FrontendHandler.php'.
- Replaced placeholder or future implementation TODOs with descriptive notes in core plugin file, settings page, AJAX handler, frontend handler, credits, and payments files.
This commit is contained in:
Ruben Ramirez 2025-04-03 19:08:41 -05:00
parent 18f0dc57d3
commit 6beb1c2251
8 changed files with 57 additions and 48 deletions

View file

@ -34,7 +34,7 @@ if ( ! $invitation_data || ! $current_step ) {
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?php esc_html_e( 'Assessment', 'quiztech' ); // TODO: Make title dynamic ?></title>
<title><?php echo esc_html( get_the_title( $invitation_data->assessment_id ) ?: __( 'Assessment', 'quiztech' ) ); ?></title>
<?php // wp_head(); // Consider if theme/WP styles are needed or if it should be isolated ?>
<style>
/* Basic styling for isolation */
@ -108,7 +108,7 @@ if ( ! $invitation_data || ! $current_step ) {
if ( is_array( $question_ids ) && ! empty( $question_ids ) ) :
?>
<form id="quiztech-assessment-form" method="post" action=""> <?php // TODO: Add action URL/AJAX handling ?>
<form id="quiztech-assessment-form" method="post" action=""> <?php // Action handled by AJAX ?>
<?php wp_nonce_field( 'quiztech_submit_assessment_' . $invitation_data->token, 'quiztech_assessment_nonce' ); ?>
<input type="hidden" name="quiztech_invitation_token" value="<?php echo esc_attr( $invitation_data->token ); ?>">
<input type="hidden" name="quiztech_assessment_id" value="<?php echo esc_attr( $assessment_id ); ?>">

View file

@ -136,7 +136,6 @@ function quiztech_init() {
$settings_page->register_hooks();
}
// TODO: Instantiate other core classes and call their init methods here
// e.g., Shortcode handlers etc.
// Future core classes (e.g., Shortcode handlers) should be instantiated here.
}
add_action( 'plugins_loaded', 'quiztech_init' );

View file

@ -100,13 +100,13 @@ class AdminListTables {
public function render_assessment_custom_columns( $column_name, $post_id ) {
switch ( $column_name ) {
case 'num_questions':
// TODO: Implement logic to get question count (e.g., from '_quiztech_question_ids' meta)
$question_ids = get_post_meta( $post_id, '_quiztech_question_ids', true );
echo is_array( $question_ids ) ? count( $question_ids ) : '0';
break;
case 'credit_cost':
// TODO: Implement logic to calculate credit cost based on associated questions
echo 'N/A'; // Placeholder
// Display the pre-calculated total credit cost stored in meta.
$total_credits = get_post_meta( $post_id, '_quiztech_total_credits', true );
echo is_numeric( $total_credits ) ? esc_html( $total_credits ) : 'N/A';
break;
}
}
@ -148,7 +148,6 @@ class AdminListTables {
switch ( $column_name ) {
case 'associated_assessment':
// TODO: Need meta field '_quiztech_associated_assessment_id'
$assessment_id = get_post_meta( $post_id, '_quiztech_associated_assessment_id', true );
if ( $assessment_id && $assessment_title = get_the_title( $assessment_id ) ) {
// Optional: Link to assessment edit screen
@ -163,7 +162,6 @@ class AdminListTables {
}
break;
case 'job_status':
// TODO: Need meta field '_quiztech_job_status'
$status = get_post_meta( $post_id, '_quiztech_job_status', true );
echo esc_html( ucwords( $status ?: 'N/A' ) );
break;

View file

@ -95,7 +95,7 @@ class SettingsPage {
]
);
// TODO: Add more sections/fields as needed (e.g., email settings, default credit costs)
// Future settings sections/fields (e.g., email, defaults) should be registered here.
}
/**
@ -163,7 +163,7 @@ class SettingsPage {
$sanitized_input['stripe_secret_key'] = sanitize_text_field( $input['stripe_secret_key'] );
}
// TODO: Sanitize other settings as they are added
// Future settings added should have their sanitization logic implemented here.
return $sanitized_input;
}

View file

@ -287,7 +287,8 @@ class AssessmentAjaxHandler {
// 5. Send Response
// TODO: Consider adding a redirect URL or specific completion message to the response data
// Future Enhancement: Consider adding a redirect URL or specific completion message/HTML
// to the response data based on plugin settings or other logic.
wp_send_json_success(['message' => __('Assessment submitted successfully!', 'quiztech')]);
// Ensure script execution stops

View file

@ -38,11 +38,12 @@ class FrontendHandler {
// Prevent caching of this dynamic page.
nocache_headers();
// TODO: Store necessary invitation details for the assessment process.
// Options: PHP Session, WP Transients, custom cookie?
// Example data needed: $invitation_data->id, $invitation_data->assessment_id, $invitation_data->job_id
// Note: Persisting invitation state across page loads/refreshes (e.g., if user leaves mid-assessment)
// requires a persistence mechanism like PHP Sessions, Transients, or potentially custom DB storage.
// This needs further architectural decision based on requirements and server environment.
// Currently, data is passed via query vars and localized script vars for the initial load.
$job_id = $invitation_data->job_id;
$assessment_id = $invitation_data->assessment_id;
@ -51,8 +52,9 @@ class FrontendHandler {
// Determine the current step (pre-screening or assessment)
$current_step = 'assessment'; // Default to assessment
if ( ! empty( $pre_screening_questions ) ) {
// TODO: Add logic to check if pre-screening was already completed (e.g., check session/transient)
// If pre-screening questions exist AND the invitation status is still 'pending' (or similar initial state), show pre-screening.
// Assumes 'pending' is the initial status before viewing/pre-screening. Adjust if needed.
if ( ! empty( $pre_screening_questions ) && $invitation_data->status === 'pending' ) {
$current_step = 'pre_screening';
}
@ -99,7 +101,8 @@ class FrontendHandler {
// Token is invalid, expired, or already used.
\error_log( "Quiztech Frontend: Invalid or expired token received: $token" );
// TODO: Redirect to a specific error page or display a user-friendly message.
// Future Enhancement: Consider adding a plugin setting for an "Invalid Invitation Page"
// and redirecting there using wp_redirect() instead of wp_die().
wp_die( esc_html__( 'Sorry, this invitation link is invalid, expired, or has already been used.', 'quiztech' ), esc_html__( 'Invalid Invitation', 'quiztech' ), [ 'response' => 403 ] );
}
}

View file

@ -10,7 +10,7 @@ namespace Quiztech\AssessmentPlatform\Includes;
// If this file is called directly, abort.
if ( ! \defined( 'WPINC' ) ) {
\die;
die; // It's a language construct, not a function in the global namespace
}
/**
@ -77,14 +77,12 @@ function quiztech_calculate_assessment_cost( $assessment_id ) {
return 0;
}
// TODO: Get the array of question IDs associated with the assessment (from post meta).
$question_ids = \get_post_meta( $assessment_id, '_quiztech_question_ids', true ); // Assuming meta key '_quiztech_question_ids'
$question_ids = \get_post_meta( $assessment_id, '_quiztech_question_ids', true );
$question_ids = \is_array( $question_ids ) ? $question_ids : array();
$total_cost = 0;
foreach ( $question_ids as $question_id ) {
// TODO: Get the 'credit_cost' meta for each question.
$cost = \get_post_meta( $question_id, '_quiztech_credit_cost', true ); // Assuming meta key '_quiztech_credit_cost'
$cost = \get_post_meta( $question_id, '_quiztech_credit_cost', true );
$total_cost += $cost ? (int) $cost : 0;
}
@ -128,14 +126,12 @@ function quiztech_deduct_credits_for_invite( $user_id, $job_id ) {
return false;
}
// TODO: Get the assessment ID associated with the job.
$assessment_id = \get_post_meta( $job_id, '_quiztech_assessment_id', true ); // Assuming meta key '_quiztech_assessment_id'
$assessment_id = \get_post_meta( $job_id, '_quiztech_associated_assessment_id', true );
if ( ! $assessment_id ) {
// No assessment assigned to the job.
return false; // Or maybe true if invites are free without an assessment? Decide later.
}
// TODO: Get the required credits (should be stored on assessment meta).
$required_credits = \get_post_meta( $assessment_id, '_quiztech_total_credits', true );
$required_credits = $required_credits ? (int) $required_credits : 0;
@ -148,7 +144,8 @@ function quiztech_deduct_credits_for_invite( $user_id, $job_id ) {
if ( $current_balance < $required_credits ) {
// Insufficient funds.
// TODO: Maybe set a transient for an admin notice?
// Future Enhancement: Consider setting a transient here to display an admin notice
// to the user who attempted the action (e.g., sending invites).
return false;
}

View file

@ -11,7 +11,7 @@ use Quiztech\AssessmentPlatform\Gateways\StripeGateway;
// If this file is called directly, abort.
if ( ! \defined( 'WPINC' ) ) {
\die;
die; // It's a language construct, not a function in the global namespace
}
/**
@ -25,17 +25,19 @@ if ( ! \defined( 'WPINC' ) ) {
* @return mixed Gateway-specific response (e.g., redirect URL, session ID) or WP_Error on failure.
*/
function quiztech_initiate_credit_purchase( $user_id, $item_id, $quantity = 1 ) {
// TODO: Validate input ($user_id, $item_id, $quantity)
// TODO: Get details about the item (price, credits amount) based on $item_id
// TODO: Determine active payment gateway (e.g., Stripe from settings). For now, assume Stripe.
$active_gateway = 'stripe'; // Replace with dynamic logic later
// --- Implementation Required ---
// 1. Validate input: Ensure $user_id is valid, $item_id exists, $quantity is positive integer.
// 2. Get Item Details: Fetch price and credits amount for $item_id (requires defining how items are stored - e.g., CPT, options).
// 3. Get Active Gateway: Read plugin settings (e.g., get_option('quiztech_settings')) to find the configured gateway slug.
$active_gateway = 'stripe'; // Placeholder: Replace with dynamic logic reading settings.
if ( 'stripe' === $active_gateway ) {
$gateway = new StripeGateway();
// TODO: Pass necessary metadata (e.g., item details for Stripe session)
$result = $gateway->initiatePayment( $user_id, $item_id, $quantity );
return $result; // Return whatever the gateway method returns (e.g., session ID, URL, WP_Error)
}
if ( 'stripe' === $active_gateway ) {
$gateway = new StripeGateway();
// 4. Pass Metadata: Gather necessary data (user_id, item_id, quantity, credits_amount, price)
// and pass it appropriately to the gateway's initiatePayment method, likely for Stripe metadata.
$result = $gateway->initiatePayment( $user_id, $item_id, $quantity /*, $metadata_array */ ); // Example modification
return $result; // Return whatever the gateway method returns (e.g., session ID, URL, WP_Error)
}
// If no active or supported gateway found
return new \WP_Error( 'no_gateway', \__( 'No active or supported payment gateway configured.', 'quiztech' ) );
@ -46,11 +48,13 @@ function quiztech_initiate_credit_purchase( $user_id, $item_id, $quantity = 1 )
* This needs to be registered as a REST endpoint or admin-ajax handler.
*/
function quiztech_handle_payment_webhook() {
// TODO: Identify the gateway from the request (e.g., URL parameter, request header). Assume Stripe for now.
$gateway_slug = 'stripe'; // Replace with dynamic logic later
// --- Implementation Required ---
// Identify the gateway. This typically requires unique endpoints per gateway.
// Example: Register '/webhook/stripe' and '/webhook/paypal'. The endpoint itself identifies the gateway.
$gateway_slug = 'stripe'; // Placeholder: Determine dynamically based on the requested endpoint.
if ( 'stripe' === $gateway_slug ) {
$payload = @file_get_contents('php://input');
if ( 'stripe' === $gateway_slug ) {
$payload = @file_get_contents('php://input');
$signature = isset($_SERVER['HTTP_STRIPE_SIGNATURE']) ? $_SERVER['HTTP_STRIPE_SIGNATURE'] : '';
if ( empty($payload) || empty($signature) ) {
@ -73,10 +77,12 @@ function quiztech_handle_payment_webhook() {
// Handle other gateways or errors if no matching gateway found
\wp_send_json_error( 'Invalid or unsupported webhook request.', 400 );
}
// TODO: Register quiztech_handle_payment_webhook as a REST endpoint or AJAX action.
// Example REST endpoint registration:
// --- Implementation Required ---
// Register the webhook handler(s) using WordPress REST API (recommended for webhooks).
// Example (needs proper implementation, likely in a dedicated class or main plugin file):
// add_action( 'rest_api_init', function () {
// register_rest_route( 'quiztech/v1', '/webhook/payment', array(
// register_rest_route( 'quiztech/v1', '/webhook/stripe', array( // Use gateway-specific endpoint
// 'methods' => 'POST',
// 'callback' => 'quiztech_handle_payment_webhook',
// 'permission_callback' => '__return_true' // IMPORTANT: Implement proper permission checks!
@ -100,7 +106,12 @@ function quiztech_process_successful_payment( $user_id, $credits_purchased, $tra
return false;
}
// TODO: Add checks to prevent processing the same transaction ID multiple times.
// --- Implementation Required ---
// Add checks to prevent processing the same transaction ID multiple times.
// This typically involves:
// 1. Storing processed transaction IDs (e.g., in post meta on the user_evaluation, a dedicated log table, or user meta).
// 2. Querying this storage before updating the balance. If the ID exists, return true (or log) without updating again.
// 3. Saving the transaction ID after successfully updating the balance.
// Update the user's credit balance
$result = quiztech_update_user_credit_balance(