Refactor: Move CPT metabox logic to dedicated Admin classes
This commit is contained in:
parent
e26f9ec54f
commit
04bf4adfb0
5 changed files with 460 additions and 289 deletions
|
|
@ -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.
|
||||
|
|
|
|||
109
src/Admin/AssessmentMetaboxes.php
Normal file
109
src/Admin/AssessmentMetaboxes.php
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
namespace Quiztech\AssessmentPlatform\Admin;
|
||||
|
||||
// If this file is called directly, abort.
|
||||
if ( ! \defined( 'WPINC' ) ) {
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles metaboxes for the Assessment CPT edit screen.
|
||||
*/
|
||||
class AssessmentMetaboxes {
|
||||
|
||||
/**
|
||||
* Register hooks for the metaboxes.
|
||||
*/
|
||||
public function register_hooks() {
|
||||
add_action( 'add_meta_boxes', [ $this, 'add_assessment_metaboxes' ] );
|
||||
add_action( 'save_post_assessment', [ $this, 'save_linked_questions_meta' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the metaboxes to the Assessment CPT.
|
||||
*
|
||||
* @param string $post_type The current post type.
|
||||
*/
|
||||
public function add_assessment_metaboxes( $post_type ) {
|
||||
if ( 'assessment' === $post_type ) {
|
||||
\add_meta_box(
|
||||
'quiztech_linked_questions_metabox',
|
||||
\__( 'Linked Questions', 'quiztech' ),
|
||||
[ $this, 'render_linked_questions_metabox' ],
|
||||
'assessment',
|
||||
'normal', // Context below editor
|
||||
'high'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the meta box content for selecting questions for an assessment.
|
||||
*
|
||||
* @param \WP_Post $post The post object.
|
||||
*/
|
||||
public function 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 '<div style="max-height: 300px; overflow-y: auto; border: 1px solid #ccd0d4; padding: 10px;">';
|
||||
echo '<p>' . \esc_html__( 'Select the questions to include in this assessment:', 'quiztech' ) . '</p>';
|
||||
|
||||
if ( $questions_query->have_posts() ) {
|
||||
echo '<ul>';
|
||||
while ( $questions_query->have_posts() ) {
|
||||
$questions_query->the_post();
|
||||
$question_id = \get_the_ID();
|
||||
$question_title = \get_the_title();
|
||||
$is_checked = in_array( $question_id, $linked_question_ids, true ); // Use strict comparison
|
||||
|
||||
echo '<li>';
|
||||
echo '<label>';
|
||||
echo '<input type="checkbox" name="quiztech_linked_questions_field[]" value="' . \esc_attr( $question_id ) . '" ' . \checked( $is_checked, true, false ) . '>';
|
||||
echo ' ' . \esc_html( $question_title );
|
||||
echo '</label>';
|
||||
echo '</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
\wp_reset_postdata();
|
||||
} else {
|
||||
echo '<p>' . \esc_html__( 'No questions found. Please create some questions first.', 'quiztech' ) . '</p>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
}
|
||||
|
|
@ -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' );
|
||||
}
|
||||
}
|
||||
}
|
||||
232
src/Admin/QuestionMetaboxes.php
Normal file
232
src/Admin/QuestionMetaboxes.php
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
<?php
|
||||
|
||||
namespace Quiztech\AssessmentPlatform\Admin;
|
||||
|
||||
// If this file is called directly, abort.
|
||||
if ( ! \defined( 'WPINC' ) ) {
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles metaboxes for the Question CPT edit screen.
|
||||
*/
|
||||
class QuestionMetaboxes {
|
||||
|
||||
/**
|
||||
* Register hooks for the metaboxes.
|
||||
*/
|
||||
public function register_hooks() {
|
||||
add_action( 'add_meta_boxes', [ $this, 'add_question_metaboxes' ] );
|
||||
add_action( 'save_post_question', [ $this, 'save_question_type_meta' ] );
|
||||
add_action( 'save_post_question', [ $this, 'save_correct_answer_meta' ] );
|
||||
add_action( 'save_post_question', [ $this, 'save_credit_cost_meta' ] );
|
||||
add_action( 'save_post_question', [ $this, 'save_question_points_meta' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the metaboxes to the Question CPT.
|
||||
*
|
||||
* @param string $post_type The current post type.
|
||||
*/
|
||||
public function add_question_metaboxes( $post_type ) {
|
||||
if ( 'question' === $post_type ) {
|
||||
\add_meta_box(
|
||||
'quiztech_question_type_metabox',
|
||||
\__( 'Question Type', 'quiztech' ),
|
||||
[ $this, 'render_question_type_metabox' ],
|
||||
'question',
|
||||
'normal', // Context below editor
|
||||
'high'
|
||||
);
|
||||
\add_meta_box(
|
||||
'quiztech_correct_answer_metabox',
|
||||
\__( 'Correct Answer(s)', 'quiztech' ),
|
||||
[ $this, 'render_correct_answer_metabox' ],
|
||||
'question',
|
||||
'normal', // Context below editor
|
||||
'high'
|
||||
);
|
||||
\add_meta_box(
|
||||
'quiztech_credit_cost_metabox',
|
||||
\__( 'Credit Cost', 'quiztech' ),
|
||||
[ $this, 'render_credit_cost_metabox' ],
|
||||
'question',
|
||||
'normal', // Context below editor
|
||||
'default'
|
||||
);
|
||||
\add_meta_box(
|
||||
'quiztech_question_points_metabox',
|
||||
\__( 'Question Points', 'quiztech' ),
|
||||
[ $this, 'render_question_points_metabox' ],
|
||||
'question',
|
||||
'normal', // Context below editor
|
||||
'default'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the meta box content for Question Type.
|
||||
*
|
||||
* @param \WP_Post $post The post object.
|
||||
*/
|
||||
public function 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 '<label for="quiztech_question_type_field">';
|
||||
\esc_html_e( 'Select the type of question:', 'quiztech' );
|
||||
echo '</label> ';
|
||||
echo '<select name="quiztech_question_type_field" id="quiztech_question_type_field" class="postbox">';
|
||||
echo '<option value="">' . \esc_html__( '-- Select Type --', 'quiztech' ) . '</option>';
|
||||
foreach ( $question_types as $type_key => $type_label ) {
|
||||
echo '<option value="' . \esc_attr( $type_key ) . '" ' . \selected( $value, $type_key, false ) . '>' . \esc_html( $type_label ) . '</option>';
|
||||
}
|
||||
echo '</select>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 '<label for="quiztech_correct_answer_field">';
|
||||
\esc_html_e( 'Enter the correct answer(s). For multiple choice/checkboxes, enter each correct option on a new line.', 'quiztech' );
|
||||
echo '</label><br />';
|
||||
echo '<textarea name="quiztech_correct_answer_field" id="quiztech_correct_answer_field" class="large-text" rows="5">' . \esc_textarea( $value ) . '</textarea>';
|
||||
echo '<p class="description">' . \esc_html__( 'Note: How this field is interpreted depends on the Question Type selected above.', 'quiztech' ) . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 '<p>';
|
||||
echo '<label for="quiztech_credit_cost_field">';
|
||||
\esc_html_e( 'Credit Cost:', 'quiztech' );
|
||||
echo '</label> ';
|
||||
echo '<input type="number" id="quiztech_credit_cost_field" name="quiztech_credit_cost_field" value="' . \esc_attr( $value ) . '" min="0" step="1" size="4" />';
|
||||
echo '<br><span class="description">' . \esc_html__( 'Number of credits required to use this question.', 'quiztech' ) . '</span>';
|
||||
echo '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 '<p>';
|
||||
echo '<label for="quiztech_question_points_field">';
|
||||
\esc_html_e( 'Question Points:', 'quiztech' );
|
||||
echo '</label> ';
|
||||
echo '<input type="number" id="quiztech_question_points_field" name="quiztech_question_points_field" value="' . \esc_attr( $value ) . '" min="0" step="1" size="4" />';
|
||||
echo '<br><span class="description">' . \esc_html__( 'Points awarded for answering this question correctly.', 'quiztech' ) . '</span>';
|
||||
echo '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 '<label for="quiztech_question_type_field">';
|
||||
\esc_html_e( 'Select the type of question:', 'quiztech' );
|
||||
echo '</label> ';
|
||||
echo '<select name="quiztech_question_type_field" id="quiztech_question_type_field" class="postbox">';
|
||||
echo '<option value="">' . \esc_html__( '-- Select Type --', 'quiztech' ) . '</option>';
|
||||
foreach ( $question_types as $type_key => $type_label ) {
|
||||
echo '<option value="' . \esc_attr( $type_key ) . '" ' . \selected( $value, $type_key, false ) . '>' . \esc_html( $type_label ) . '</option>';
|
||||
}
|
||||
echo '</select>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 '<label for="quiztech_correct_answer_field">';
|
||||
\esc_html_e( 'Enter the correct answer(s). For multiple choice/checkboxes, enter each correct option on a new line.', 'quiztech' );
|
||||
echo '</label><br />';
|
||||
echo '<textarea name="quiztech_correct_answer_field" id="quiztech_correct_answer_field" class="large-text" rows="5">' . \esc_textarea( $value ) . '</textarea>';
|
||||
echo '<p class="description">' . \esc_html__( 'Note: How this field is interpreted depends on the Question Type selected above.', 'quiztech' ) . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 '<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>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 '<div style="max-height: 300px; overflow-y: auto; border: 1px solid #ccd0d4; padding: 10px;">';
|
||||
echo '<p>' . \esc_html__( 'Select the questions to include in this assessment:', 'quiztech' ) . '</p>';
|
||||
|
||||
if ( $questions_query->have_posts() ) {
|
||||
echo '<ul>';
|
||||
while ( $questions_query->have_posts() ) {
|
||||
$questions_query->the_post();
|
||||
$question_id = \get_the_ID();
|
||||
$question_title = \get_the_title();
|
||||
$is_checked = in_array( $question_id, $linked_question_ids, true ); // Use strict comparison
|
||||
|
||||
echo '<li>';
|
||||
echo '<label>';
|
||||
echo '<input type="checkbox" name="quiztech_linked_questions_field[]" value="' . \esc_attr( $question_id ) . '" ' . \checked( $is_checked, true, false ) . '>';
|
||||
echo ' ' . \esc_html( $question_title );
|
||||
echo '</label>';
|
||||
echo '</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
\wp_reset_postdata();
|
||||
} else {
|
||||
echo '<p>' . \esc_html__( 'No questions found. Please create some questions first.', 'quiztech' ) . '</p>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
|
||||
// --- 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.
|
||||
|
||||
?>
|
||||
Loading…
Add table
Reference in a new issue