From 04bf4adfb0707a3e9b213adf22a826477e09163b Mon Sep 17 00:00:00 2001 From: Ruben Ramirez Date: Thu, 3 Apr 2025 23:58:15 -0500 Subject: [PATCH] Refactor: Move CPT metabox logic to dedicated Admin classes --- quiztech-assessment-platform.php | 25 ++- src/Admin/AssessmentMetaboxes.php | 109 ++++++++++++ src/Admin/JobMetaboxes.php | 97 +++++++++- src/Admin/QuestionMetaboxes.php | 232 ++++++++++++++++++++++++ src/Includes/post-types.php | 286 +----------------------------- 5 files changed, 460 insertions(+), 289 deletions(-) create mode 100644 src/Admin/AssessmentMetaboxes.php create mode 100644 src/Admin/QuestionMetaboxes.php diff --git a/quiztech-assessment-platform.php b/quiztech-assessment-platform.php index 9f3565e..5dd9979 100644 --- a/quiztech-assessment-platform.php +++ b/quiztech-assessment-platform.php @@ -39,6 +39,17 @@ require_once QUIZTECH_PLUGIN_DIR . 'src/Includes/roles.php'; require_once QUIZTECH_PLUGIN_DIR . 'src/Includes/post-types.php'; require_once QUIZTECH_PLUGIN_DIR . 'src/Includes/taxonomies.php'; +// Use statements for Admin classes +use Quiztech\AssessmentPlatform\Admin\AdminListTables; +use Quiztech\AssessmentPlatform\Admin\SettingsPage; +use Quiztech\AssessmentPlatform\Admin\JobMetaboxes; +use Quiztech\AssessmentPlatform\Admin\QuestionMetaboxes; +use Quiztech\AssessmentPlatform\Admin\AssessmentMetaboxes; +use Quiztech\AssessmentPlatform\Includes\FrontendHandler; +use Quiztech\AssessmentPlatform\Includes\Ajax\AssessmentAjaxHandler; +use Quiztech\AssessmentPlatform\Includes\Ajax\AssessmentBuilderAjaxHandler; + + /** * Load plugin dependencies. (Now handled by autoloading) */ @@ -195,14 +206,20 @@ function quiztech_init() { // Initialize Admin-specific features if ( is_admin() ) { - $admin_list_tables = new \Quiztech\AssessmentPlatform\Admin\AdminListTables(); + $admin_list_tables = new AdminListTables(); $admin_list_tables->register_hooks(); - $settings_page = new \Quiztech\AssessmentPlatform\Admin\SettingsPage(); + $settings_page = new SettingsPage(); $settings_page->register_hooks(); - $job_metaboxes = new \Quiztech\AssessmentPlatform\Admin\JobMetaboxes(); - $job_metaboxes->register_hooks(); + $job_metaboxes = new JobMetaboxes(); + $job_metaboxes->register_hooks(); + + $question_metaboxes = new QuestionMetaboxes(); + $question_metaboxes->register_hooks(); + + $assessment_metaboxes = new AssessmentMetaboxes(); + $assessment_metaboxes->register_hooks(); } // Future core classes (e.g., Shortcode handlers) should be instantiated here. diff --git a/src/Admin/AssessmentMetaboxes.php b/src/Admin/AssessmentMetaboxes.php new file mode 100644 index 0000000..27216bd --- /dev/null +++ b/src/Admin/AssessmentMetaboxes.php @@ -0,0 +1,109 @@ +ID, '_quiztech_linked_question_ids', true ); + if ( ! is_array( $linked_question_ids ) ) { + $linked_question_ids = []; + } + + $questions_query = new \WP_Query( [ + 'post_type' => 'question', + 'posts_per_page' => -1, + 'post_status' => 'publish', + 'orderby' => 'title', + 'order' => 'ASC', + ] ); + + echo '
'; + echo '

' . \esc_html__( 'Select the questions to include in this assessment:', 'quiztech' ) . '

'; + + if ( $questions_query->have_posts() ) { + echo ''; + \wp_reset_postdata(); + } else { + echo '

' . \esc_html__( 'No questions found. Please create some questions first.', 'quiztech' ) . '

'; + } + echo '
'; + } + + /** + * Saves the meta box data for the linked questions on an assessment. + * + * @param int $post_id The ID of the post being saved. + */ + public function save_linked_questions_meta( $post_id ) { + // Basic checks (nonce, autosave, permissions) + if ( ! isset( $_POST['quiztech_linked_questions_nonce'] ) || ! \wp_verify_nonce( \sanitize_key( $_POST['quiztech_linked_questions_nonce'] ), 'quiztech_save_linked_questions_meta' ) ) { return; } + if ( \defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } + if ( ! \current_user_can( 'edit_post', $post_id ) ) { return; } + // No need to check post type here, as the action is specific ('save_post_assessment') + + // Process submitted IDs + $submitted_ids = []; + if ( isset( $_POST['quiztech_linked_questions_field'] ) && is_array( $_POST['quiztech_linked_questions_field'] ) ) { + $submitted_ids = array_map( 'absint', $_POST['quiztech_linked_questions_field'] ); + $submitted_ids = array_filter( $submitted_ids ); // Remove zeros/invalid entries + } + + // Update meta (even if empty array, to clear previous selections) + \update_post_meta( $post_id, '_quiztech_linked_question_ids', $submitted_ids ); + } +} \ No newline at end of file diff --git a/src/Admin/JobMetaboxes.php b/src/Admin/JobMetaboxes.php index f4f4b80..e31ce22 100644 --- a/src/Admin/JobMetaboxes.php +++ b/src/Admin/JobMetaboxes.php @@ -13,13 +13,43 @@ class JobMetaboxes { * Register hooks for the metaboxes. */ public function register_hooks() { - add_action( 'add_meta_boxes', [ $this, 'add_send_invite_metabox' ] ); + add_action( 'add_meta_boxes', [ $this, 'add_job_metaboxes' ] ); // Consolidated add_meta_boxes hook + add_action( 'save_post_job', [ $this, 'save_linked_assessment_meta' ] ); // Hook for saving linked assessment add_action( 'wp_ajax_quiztech_send_job_invite', [ $this, 'handle_send_invite_ajax' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_scripts' ] ); } /** - * Adds the "Send Invite" metabox to the Job CPT. + * Adds all metaboxes for the Job CPT. + * + * @param string $post_type The current post type. + */ + public function add_job_metaboxes( $post_type ) { + if ( 'job' === $post_type ) { + // Send Invite Metabox (Side) + add_meta_box( + 'quiztech_send_invite_metabox', // Metabox ID + __( 'Send Assessment Invite', 'quiztech' ), // Title + [ $this, 'render_send_invite_metabox' ], // Callback function + 'job', // Post type + 'side', // Context + 'high' // Priority + ); + + // Linked Assessment Metabox (Normal) + add_meta_box( + 'quiztech_linked_assessment_metabox', + __( 'Linked Assessment', 'quiztech' ), + [ $this, 'render_linked_assessment_metabox' ], + 'job', + 'normal', // Context below editor + 'default' + ); + } + } + + /** + * Adds the "Send Invite" metabox to the Job CPT. - DEPRECATED (consolidated into add_job_metaboxes) * * @param string $post_type The current post type. */ @@ -136,4 +166,67 @@ class JobMetaboxes { ]); } } + + /** + * Renders the meta box content for selecting an assessment for a job. + * + * @param \WP_Post $post The post object. + */ + public function render_linked_assessment_metabox( $post ) { + \wp_nonce_field( 'quiztech_save_linked_assessment_meta', 'quiztech_linked_assessment_nonce' ); + $linked_assessment_id = \get_post_meta( $post->ID, '_quiztech_linked_assessment_id', true ); + + $assessments_query = new \WP_Query( [ + 'post_type' => 'assessment', + 'posts_per_page' => -1, + 'post_status' => 'publish', + 'orderby' => 'title', + 'order' => 'ASC', + ] ); + + echo '<label for="quiztech_linked_assessment_field">'; + \esc_html_e( 'Select the assessment for this job:', 'quiztech' ); + echo '</label><br />'; + + if ( $assessments_query->have_posts() ) { + echo '<select name="quiztech_linked_assessment_field" id="quiztech_linked_assessment_field" class="widefat">'; + echo '<option value="">' . \esc_html__( '-- None --', 'quiztech' ) . '</option>'; + while ( $assessments_query->have_posts() ) { + $assessments_query->the_post(); + $assessment_id = \get_the_ID(); + $assessment_title = \get_the_title(); + echo '<option value="' . \esc_attr( $assessment_id ) . '" ' . \selected( $linked_assessment_id, $assessment_id, false ) . '>' . \esc_html( $assessment_title ) . '</option>'; + } + echo '</select>'; + \wp_reset_postdata(); + } else { + echo '<p>' . \esc_html__( 'No assessments found. Please create an assessment first.', 'quiztech' ) . '</p>'; + } + } + + /** + * Saves the meta box data for the linked assessment on a job. + * + * @param int $post_id The ID of the post being saved. + */ + public function save_linked_assessment_meta( $post_id ) { + // Basic checks (nonce, autosave, permissions) + if ( ! isset( $_POST['quiztech_linked_assessment_nonce'] ) || ! \wp_verify_nonce( \sanitize_key( $_POST['quiztech_linked_assessment_nonce'] ), 'quiztech_save_linked_assessment_meta' ) ) { return; } + if ( \defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } + if ( ! \current_user_can( 'edit_post', $post_id ) ) { return; } + // No need to check post type here, as the action is specific ('save_post_job') + + // Field check and sanitize + if ( isset( $_POST['quiztech_linked_assessment_field'] ) ) { + $assessment_id = \sanitize_text_field( \wp_unslash( $_POST['quiztech_linked_assessment_field'] ) ); + $assessment_id = '' === $assessment_id ? '' : \absint( $assessment_id ); + \update_post_meta( $post_id, '_quiztech_linked_assessment_id', $assessment_id ); + } else { + // If the field isn't submitted (e.g., if the metabox wasn't rendered for some reason), + // we might want to delete the meta key to ensure no stale data persists. + // However, typically, if the field exists and is submitted empty, the above logic handles it (sets to ''). + // Let's delete it if it's not set in POST, assuming the metabox should always be present on save. + \delete_post_meta( $post_id, '_quiztech_linked_assessment_id' ); + } + } } \ No newline at end of file diff --git a/src/Admin/QuestionMetaboxes.php b/src/Admin/QuestionMetaboxes.php new file mode 100644 index 0000000..c4555e3 --- /dev/null +++ b/src/Admin/QuestionMetaboxes.php @@ -0,0 +1,232 @@ +ID, '_quiztech_question_type', true ); + $question_types = [ + 'text' => \__( 'Text (Single Line)', 'quiztech' ), + 'textarea' => \__( 'Text Area (Multi-line)', 'quiztech' ), + 'multiple-choice' => \__( 'Multiple Choice (Single Answer)', 'quiztech' ), + 'checkbox' => \__( 'Checkboxes (Multiple Answers)', 'quiztech' ), + 'numeric' => \__( 'Numeric', 'quiztech' ), + ]; + + echo ' '; + echo ''; + } + + /** + * Renders the meta box content for Correct Answer. + * + * @param \WP_Post $post The post object. + */ + public function render_correct_answer_metabox( $post ) { + \wp_nonce_field( 'quiztech_save_correct_answer_meta', 'quiztech_correct_answer_nonce' ); + $value = \get_post_meta( $post->ID, '_quiztech_correct_answer', true ); + + echo '
'; + echo ''; + echo '

' . \esc_html__( 'Note: How this field is interpreted depends on the Question Type selected above.', 'quiztech' ) . '

'; + } + + /** + * Renders the meta box content for Credit Cost. + * + * @param \WP_Post $post The post object. + */ + public function render_credit_cost_metabox( $post ) { + \wp_nonce_field( 'quiztech_save_credit_cost_meta', 'quiztech_credit_cost_nonce' ); + $value = \get_post_meta( $post->ID, '_quiztech_credit_cost', true ); + $value = $value ? \absint( $value ) : 0; // Ensure non-negative integer, default 0 + + echo '

'; + echo ' '; + echo ''; + echo '
' . \esc_html__( 'Number of credits required to use this question.', 'quiztech' ) . ''; + echo '

'; + } + + /** + * Renders the meta box content for Question Points. + * + * @param \WP_Post $post The post object. + */ + public function render_question_points_metabox( $post ) { + \wp_nonce_field( 'quiztech_save_question_points_meta', 'quiztech_question_points_nonce' ); + $value = \get_post_meta( $post->ID, '_quiztech_question_points', true ); + $value = $value ? \absint( $value ) : 0; // Ensure non-negative integer, default 0 + + echo '

'; + echo ' '; + echo ''; + echo '
' . \esc_html__( 'Points awarded for answering this question correctly.', 'quiztech' ) . ''; + echo '

'; + } + + /** + * Saves the meta box data for Question Type. + * + * @param int $post_id The ID of the post being saved. + */ + public function save_question_type_meta( $post_id ) { + // Basic checks (nonce, autosave, permissions) + if ( ! isset( $_POST['quiztech_question_type_nonce'] ) || ! \wp_verify_nonce( \sanitize_key( $_POST['quiztech_question_type_nonce'] ), 'quiztech_save_question_type_meta' ) ) { return; } + if ( \defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } + if ( ! \current_user_can( 'edit_post', $post_id ) ) { return; } + // No need to check post type here, as the action is specific ('save_post_question') + + // Field check and sanitize + if ( ! isset( $_POST['quiztech_question_type_field'] ) ) { return; } + $new_meta_value = \sanitize_text_field( \wp_unslash( $_POST['quiztech_question_type_field'] ) ); + + // Validation against allowed types + $allowed_types = ['text', 'textarea', 'multiple-choice', 'checkbox', 'numeric']; + if ( in_array( $new_meta_value, $allowed_types, true ) || '' === $new_meta_value ) { + \update_post_meta( $post_id, '_quiztech_question_type', $new_meta_value ); + } else { + \delete_post_meta( $post_id, '_quiztech_question_type' ); + } + } + + /** + * Saves the meta box data for Correct Answer. + * + * @param int $post_id The ID of the post being saved. + */ + public function save_correct_answer_meta( $post_id ) { + // Basic checks (nonce, autosave, permissions) + if ( ! isset( $_POST['quiztech_correct_answer_nonce'] ) || ! \wp_verify_nonce( \sanitize_key( $_POST['quiztech_correct_answer_nonce'] ), 'quiztech_save_correct_answer_meta' ) ) { return; } + if ( \defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } + if ( ! \current_user_can( 'edit_post', $post_id ) ) { return; } + + // Field check and sanitize + if ( isset( $_POST['quiztech_correct_answer_field'] ) ) { + $new_meta_value = \sanitize_textarea_field( \wp_unslash( $_POST['quiztech_correct_answer_field'] ) ); + \update_post_meta( $post_id, '_quiztech_correct_answer', $new_meta_value ); + } else { + // Delete if field is expected but not submitted + \delete_post_meta( $post_id, '_quiztech_correct_answer' ); + } + } + + /** + * Saves the meta box data for Credit Cost. + * + * @param int $post_id The ID of the post being saved. + */ + public function save_credit_cost_meta( $post_id ) { + // Basic checks (nonce, autosave, permissions) + if ( ! isset( $_POST['quiztech_credit_cost_nonce'] ) || ! \wp_verify_nonce( \sanitize_key( $_POST['quiztech_credit_cost_nonce'] ), 'quiztech_save_credit_cost_meta' ) ) { return; } + if ( \defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } + if ( ! \current_user_can( 'edit_post', $post_id ) ) { return; } + + // Field check and sanitize + if ( isset( $_POST['quiztech_credit_cost_field'] ) ) { + // Use absint to ensure it's a non-negative integer. + $new_meta_value = \absint( \wp_unslash( $_POST['quiztech_credit_cost_field'] ) ); + \update_post_meta( $post_id, '_quiztech_credit_cost', $new_meta_value ); + } + } + + /** + * Saves the meta box data for Question Points. + * + * @param int $post_id The ID of the post being saved. + */ + public function save_question_points_meta( $post_id ) { + // Basic checks (nonce, autosave, permissions) + if ( ! isset( $_POST['quiztech_question_points_nonce'] ) || ! \wp_verify_nonce( \sanitize_key( $_POST['quiztech_question_points_nonce'] ), 'quiztech_save_question_points_meta' ) ) { return; } + if ( \defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } + if ( ! \current_user_can( 'edit_post', $post_id ) ) { return; } + + // Field check and sanitize + if ( isset( $_POST['quiztech_question_points_field'] ) ) { + // Use absint to ensure it's a non-negative integer. + $new_meta_value = \absint( \wp_unslash( $_POST['quiztech_question_points_field'] ) ); + \update_post_meta( $post_id, '_quiztech_question_points', $new_meta_value ); + } + } +} \ No newline at end of file diff --git a/src/Includes/post-types.php b/src/Includes/post-types.php index 95a7ee0..e44fb7f 100644 --- a/src/Includes/post-types.php +++ b/src/Includes/post-types.php @@ -169,288 +169,8 @@ function quiztech_register_post_types() { } \add_action( 'init', __NAMESPACE__ . '\quiztech_register_post_types' ); - -// --- Meta Box Registration --- - -/** - * Adds meta boxes for various CPTs. - * Hooks into 'add_meta_boxes'. - * - * @param string $post_type The post type slug. - */ -function quiztech_add_all_meta_boxes( $post_type ) { - // Meta Boxes for 'question' CPT - if ( 'question' === $post_type ) { - \add_meta_box( - 'quiztech_question_type_metabox', - \__( 'Question Type', 'quiztech' ), - __NAMESPACE__ . '\quiztech_render_question_type_metabox', - 'question', - 'normal', // Context below editor - 'high' - ); - \add_meta_box( - 'quiztech_correct_answer_metabox', - \__( 'Correct Answer(s)', 'quiztech' ), - __NAMESPACE__ . '\quiztech_render_correct_answer_metabox', - 'question', - 'normal', // Context below editor - 'high' - ); - } - - // Meta Box for 'job' CPT - if ( 'job' === $post_type ) { - \add_meta_box( - 'quiztech_linked_assessment_metabox', - \__( 'Linked Assessment', 'quiztech' ), - __NAMESPACE__ . '\quiztech_render_linked_assessment_metabox', - 'job', - 'normal', // Context below editor - 'default' - ); - } - - // Meta Box for 'assessment' CPT - if ( 'assessment' === $post_type ) { - \add_meta_box( - 'quiztech_linked_questions_metabox', - \__( 'Linked Questions', 'quiztech' ), - __NAMESPACE__ . '\quiztech_render_linked_questions_metabox', - 'assessment', - 'normal', // Context below editor - 'high' - ); - } -} -\add_action( 'add_meta_boxes', __NAMESPACE__ . '\quiztech_add_all_meta_boxes' ); - - -// --- Meta Box Render Functions --- - -/** - * Renders the meta box content for Question Type. - * - * @param \WP_Post $post The post object. - */ -function quiztech_render_question_type_metabox( $post ) { - \wp_nonce_field( 'quiztech_save_question_type_meta', 'quiztech_question_type_nonce' ); - $value = \get_post_meta( $post->ID, '_quiztech_question_type', true ); - $question_types = [ - 'text' => \__( 'Text (Single Line)', 'quiztech' ), - 'textarea' => \__( 'Text Area (Multi-line)', 'quiztech' ), - 'multiple-choice' => \__( 'Multiple Choice (Single Answer)', 'quiztech' ), - 'checkbox' => \__( 'Checkboxes (Multiple Answers)', 'quiztech' ), - 'numeric' => \__( 'Numeric', 'quiztech' ), - ]; - - echo ' '; - echo ''; -} - -/** - * Renders the meta box content for Correct Answer. - * - * @param \WP_Post $post The post object. - */ -function quiztech_render_correct_answer_metabox( $post ) { - \wp_nonce_field( 'quiztech_save_correct_answer_meta', 'quiztech_correct_answer_nonce' ); - $value = \get_post_meta( $post->ID, '_quiztech_correct_answer', true ); - - echo '
'; - echo ''; - echo '

' . \esc_html__( 'Note: How this field is interpreted depends on the Question Type selected above.', 'quiztech' ) . '

'; -} - -/** - * Renders the meta box content for selecting an assessment for a job. - * - * @param \WP_Post $post The post object. - */ -function quiztech_render_linked_assessment_metabox( $post ) { - \wp_nonce_field( 'quiztech_save_linked_assessment_meta', 'quiztech_linked_assessment_nonce' ); - $linked_assessment_id = \get_post_meta( $post->ID, '_quiztech_linked_assessment_id', true ); - - $assessments_query = new \WP_Query( [ - 'post_type' => 'assessment', - 'posts_per_page' => -1, - 'post_status' => 'publish', - 'orderby' => 'title', - 'order' => 'ASC', - ] ); - - echo '
'; - - if ( $assessments_query->have_posts() ) { - echo ''; - \wp_reset_postdata(); - } else { - echo '

' . \esc_html__( 'No assessments found. Please create an assessment first.', 'quiztech' ) . '

'; - } -} - -/** - * Renders the meta box content for selecting questions for an assessment. - * - * @param \WP_Post $post The post object. - */ -function quiztech_render_linked_questions_metabox( $post ) { - \wp_nonce_field( 'quiztech_save_linked_questions_meta', 'quiztech_linked_questions_nonce' ); - $linked_question_ids = \get_post_meta( $post->ID, '_quiztech_linked_question_ids', true ); - if ( ! is_array( $linked_question_ids ) ) { - $linked_question_ids = []; - } - - $questions_query = new \WP_Query( [ - 'post_type' => 'question', - 'posts_per_page' => -1, - 'post_status' => 'publish', - 'orderby' => 'title', - 'order' => 'ASC', - ] ); - - echo '
'; - echo '

' . \esc_html__( 'Select the questions to include in this assessment:', 'quiztech' ) . '

'; - - if ( $questions_query->have_posts() ) { - echo ''; - \wp_reset_postdata(); - } else { - echo '

' . \esc_html__( 'No questions found. Please create some questions first.', 'quiztech' ) . '

'; - } - echo '
'; -} - - -// --- Meta Box Save Functions --- - -/** - * Saves the meta box data for Question Type. - * Hooks into 'save_post_question'. - * - * @param int $post_id The ID of the post being saved. - */ -function quiztech_save_question_type_meta( $post_id ) { - // Basic checks (nonce, autosave, permissions) - if ( ! isset( $_POST['quiztech_question_type_nonce'] ) || ! \wp_verify_nonce( \sanitize_key( $_POST['quiztech_question_type_nonce'] ), 'quiztech_save_question_type_meta' ) ) { return; } - if ( \defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } - if ( ! \current_user_can( 'edit_post', $post_id ) ) { return; } - - // Field check and sanitize - if ( ! isset( $_POST['quiztech_question_type_field'] ) ) { return; } - $new_meta_value = \sanitize_text_field( \wp_unslash( $_POST['quiztech_question_type_field'] ) ); - - // Validation against allowed types - $allowed_types = ['text', 'textarea', 'multiple-choice', 'checkbox', 'numeric']; - if ( in_array( $new_meta_value, $allowed_types, true ) || '' === $new_meta_value ) { - \update_post_meta( $post_id, '_quiztech_question_type', $new_meta_value ); - } else { - \delete_post_meta( $post_id, '_quiztech_question_type' ); - } -} -\add_action( 'save_post_question', __NAMESPACE__ . '\quiztech_save_question_type_meta' ); - -/** - * Saves the meta box data for Correct Answer. - * Hooks into 'save_post_question'. - * - * @param int $post_id The ID of the post being saved. - */ -function quiztech_save_correct_answer_meta( $post_id ) { - // Basic checks (nonce, autosave, permissions) - if ( ! isset( $_POST['quiztech_correct_answer_nonce'] ) || ! \wp_verify_nonce( \sanitize_key( $_POST['quiztech_correct_answer_nonce'] ), 'quiztech_save_correct_answer_meta' ) ) { return; } - if ( \defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } - if ( ! \current_user_can( 'edit_post', $post_id ) ) { return; } - - // Field check and sanitize - if ( isset( $_POST['quiztech_correct_answer_field'] ) ) { - $new_meta_value = \sanitize_textarea_field( \wp_unslash( $_POST['quiztech_correct_answer_field'] ) ); - \update_post_meta( $post_id, '_quiztech_correct_answer', $new_meta_value ); - } else { - // Delete if field is expected but not submitted - \delete_post_meta( $post_id, '_quiztech_correct_answer' ); - } -} -\add_action( 'save_post_question', __NAMESPACE__ . '\quiztech_save_correct_answer_meta' ); - -/** - * Saves the meta box data for the linked assessment on a job. - * Hooks into 'save_post_job'. - * - * @param int $post_id The ID of the post being saved. - */ -function quiztech_save_linked_assessment_meta( $post_id ) { - // Basic checks (nonce, autosave, permissions) - if ( ! isset( $_POST['quiztech_linked_assessment_nonce'] ) || ! \wp_verify_nonce( \sanitize_key( $_POST['quiztech_linked_assessment_nonce'] ), 'quiztech_save_linked_assessment_meta' ) ) { return; } - if ( \defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } - if ( ! \current_user_can( 'edit_post', $post_id ) ) { return; } - - // Field check and sanitize - if ( isset( $_POST['quiztech_linked_assessment_field'] ) ) { - $assessment_id = \sanitize_text_field( \wp_unslash( $_POST['quiztech_linked_assessment_field'] ) ); - $assessment_id = '' === $assessment_id ? '' : \absint( $assessment_id ); - \update_post_meta( $post_id, '_quiztech_linked_assessment_id', $assessment_id ); - } else { - \delete_post_meta( $post_id, '_quiztech_linked_assessment_id' ); - } -} -\add_action( 'save_post_job', __NAMESPACE__ . '\quiztech_save_linked_assessment_meta' ); - -/** - * Saves the meta box data for the linked questions on an assessment. - * Hooks into 'save_post_assessment'. - * - * @param int $post_id The ID of the post being saved. - */ -function quiztech_save_linked_questions_meta( $post_id ) { - // Basic checks (nonce, autosave, permissions) - if ( ! isset( $_POST['quiztech_linked_questions_nonce'] ) || ! \wp_verify_nonce( \sanitize_key( $_POST['quiztech_linked_questions_nonce'] ), 'quiztech_save_linked_questions_meta' ) ) { return; } - if ( \defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } - if ( ! \current_user_can( 'edit_post', $post_id ) ) { return; } - - // Process submitted IDs - $submitted_ids = []; - if ( isset( $_POST['quiztech_linked_questions_field'] ) && is_array( $_POST['quiztech_linked_questions_field'] ) ) { - $submitted_ids = array_map( 'absint', $_POST['quiztech_linked_questions_field'] ); - $submitted_ids = array_filter( $submitted_ids ); // Remove zeros/invalid entries - } - - // Update meta (even if empty array, to clear previous selections) - \update_post_meta( $post_id, '_quiztech_linked_question_ids', $submitted_ids ); -} -\add_action( 'save_post_assessment', __NAMESPACE__ . '\quiztech_save_linked_questions_meta' ); +// Metabox registration, rendering, and saving are now handled in dedicated classes +// within the src/Admin/ directory (e.g., QuestionMetaboxes.php, AssessmentMetaboxes.php, JobMetaboxes.php). +// These classes are instantiated and their hooks registered in the main plugin file. ?> \ No newline at end of file