register_document_cpt(); // Ensure CPT is registered before flushing.
$this->register_document_category_taxonomy(); // Ensure Taxonomy is registered before flushing.
flush_rewrite_rules(); // Essential for custom post types/taxonomies URLs to work.
}
/**
* What to do on plugin deactivation.
* Flushes rewrite rules and optionally cleans up data.
*/
public function deactivate_plugin() {
flush_rewrite_rules(); // Essential to remove custom post types/taxonomies URLs.
// For now, we won't delete documents or categories on deactivation.
// A full uninstall hook would handle data deletion.
}
/**
* Register the 'Document' Custom Post Type.
* CPT slug changed to 'ttdvr_document' for unique prefixing.
*/
public function register_document_cpt() {
$labels = array(
'name' => _x( 'Documents', 'Post Type General Name', '24tt-document-verifier' ),
'singular_name' => _x( 'Document', 'Post Type Singular Name', '24tt-document-verifier' ),
'menu_name' => esc_html__( 'Documents', '24tt-document-verifier' ), // Escaped.
'name_admin_bar' => esc_html__( 'Document', '24tt-document-verifier' ), // Escaped.
'archives' => esc_html__( 'Document Archives', '24tt-document-verifier' ), // Escaped.
'attributes' => esc_html__( 'Document Attributes', '24tt-document-verifier' ), // Escaped.
'parent_item_colon' => esc_html__( 'Parent Document:', '24tt-document-verifier' ), // Escaped.
'all_items' => esc_html__( 'All Documents', '24tt-document-verifier' ), // Escaped.
'add_new_item' => esc_html__( 'Add New Document', '24tt-document-verifier' ), // Escaped.
'add_new' => esc_html__( 'Add New', '24tt-document-verifier' ), // Escaped.
'new_item' => esc_html__( 'New Document', '24tt-document-verifier' ), // Escaped.
'edit_item' => esc_html__( 'Edit Document', '24tt-document-verifier' ), // Escaped.
'update_item' => esc_html__( 'Update Document', '24tt-document-verifier' ), // Escaped.
'view_item' => esc_html__( 'View Document', '24tt-document-verifier' ), // Escaped.
'view_items' => esc_html__( 'View Documents', '24tt-document-verifier' ), // Escaped.
'search_items' => esc_html__( 'Search Document', '24tt-document-verifier' ), // Escaped.
'not_found' => esc_html__( 'No documents found', '24tt-document-verifier' ), // Escaped.
'not_found_in_trash' => esc_html__( 'No documents found in Trash', '24tt-document-verifier' ), // Escaped.
'featured_image' => esc_html__( 'Document Image', '24tt-document-verifier' ), // Escaped.
'set_featured_image' => esc_html__( 'Set document image', '24tt-document-verifier' ), // Escaped.
'remove_featured_image' => esc_html__( 'Remove document image', '24tt-document-verifier' ), // Escaped.
'use_featured_image' => esc_html__( 'Use as document image', '24tt-document-verifier' ), // Escaped.
'insert_into_item' => esc_html__( 'Insert into document', '24tt-document-verifier' ), // Escaped.
'uploaded_to_this_item' => esc_html__( 'Uploaded to this document', '24tt-document-verifier' ), // Escaped.
'items_list' => esc_html__( 'Documents list', '24tt-document-verifier' ), // Escaped.
'items_list_navigation' => esc_html__( 'Documents list navigation', '24tt-document-verifier' ), // Escaped.
'filter_items_list' => esc_html__( 'Filter documents list', '24tt-document-verifier' ), // Escaped.
);
$args = array(
'label' => esc_html__( 'Document', '24tt-document-verifier' ), // Escaped.
'description' => esc_html__( 'Documents for verification', '24tt-document-verifier' ), // Escaped.
'labels' => $labels,
'supports' => array( 'title', 'editor' ), // Title for document name, editor for remarks/details.
'taxonomies' => array( 'ttdvr_doc_category' ), // Associate with our custom category taxonomy (new prefix).
'hierarchical' => false,
'public' => true,
'show_ui' => true,
'show_in_menu' => false, // Set to false, we'll add it as a submenu to our main plugin page.
'menu_position' => 5,
'menu_icon' => 'dashicons-media-document',
'show_in_admin_bar' => true,
'show_in_nav_menus' => true,
'can_export' => true,
'has_archive' => true,
'exclude_from_search' => false,
'publicly_queryable' => true,
'capability_type' => 'post',
'rewrite' => array( 'slug' => 'document-verification' ),
);
register_post_type( 'ttdvr_document', $args ); // CPT slug changed.
}
/**
* Register the 'Document Category' Custom Taxonomy.
* Taxonomy slug changed to 'ttdvr_doc_category' for unique prefixing.
*/
public function register_document_category_taxonomy() {
$labels = array(
'name' => _x( 'Document Categories', 'Taxonomy General Name', '24tt-document-verifier' ),
'singular_name' => _x( 'Document Category', 'Taxonomy Singular Name', '24tt-document-verifier' ),
'menu_name' => esc_html__( 'Categories', '24tt-document-verifier' ), // Escaped.
'all_items' => esc_html__( 'All Categories', '24tt-document-verifier' ), // Escaped.
'parent_item' => esc_html__( 'Parent Category', '24tt-document-verifier' ), // Escaped.
'parent_item_colon' => esc_html__( 'Parent Category:', '24tt-document-verifier' ), // Escaped.
'new_item_name' => esc_html__( 'New Document Category Name', '24tt-document-verifier' ), // Escaped.
'add_new_item' => esc_html__( 'Add New Document Category', '24tt-document-verifier' ), // Escaped.
'edit_item' => esc_html__( 'Edit Document Category', '24tt-document-verifier' ), // Escaped.
'update_item' => esc_html__( 'Update Document Category', '24tt-document-verifier' ), // Escaped.
'view_item' => esc_html__( 'View Document Category', '24tt-document-verifier' ), // Escaped.
'separate_items_with_commas' => esc_html__( 'Separate categories with commas', '24tt-document-verifier' ), // Escaped.
'add_or_remove_items' => esc_html__( 'Add or remove categories', '24tt-document-verifier' ), // Escaped.
'choose_from_most_used' => esc_html__( 'Choose from the most used', '24tt-document-verifier' ), // Escaped.
'popular_items' => esc_html__( 'Popular Categories', '24tt-document-verifier' ), // Escaped.
'search_items' => esc_html__( 'Search Categories', '24tt-document-verifier' ), // Escaped.
'not_found' => esc_html__( 'No Categories Found', '24tt-document-verifier' ), // Escaped.
'no_terms' => esc_html__( 'No categories', '24tt-document-verifier' ), // Escaped.
'items_list' => esc_html__( 'Categories list', '24tt-document-verifier' ), // Escaped.
'items_list_navigation' => esc_html__( 'Categories list navigation', '24tt-document-verifier' ), // Escaped.
'filter_items_list' => esc_html__( 'Filter categories list', '24tt-document-verifier' ), // Escaped.
);
$args = array(
'labels' => $labels,
'hierarchical' => true, // Allows parent-child relationships like post categories.
'public' => true,
'show_ui' => true,
'show_admin_column' => true, // Show column in document list table.
'query_var' => true,
'rewrite' => array( 'slug' => 'doc-category' ), // Custom slug for category URLs.
'show_in_rest' => true, // For Gutenberg compatibility.
);
register_taxonomy( 'ttdvr_doc_category', array( 'ttdvr_document' ), $args ); // Taxonomy slug changed.
}
/**
* Add custom meta boxes to the Document edit screen.
*/
public function add_document_meta_boxes() {
add_meta_box(
'ttdvr_document_details', // Unique ID (new prefix).
esc_html__( 'Document Details', '24tt-document-verifier' ), // Box title (escaped).
array( $this, 'render_document_details_meta_box' ), // Callback function to render the HTML.
'ttdvr_document', // Post type to display on (new prefix).
'normal', // Context (where on the screen).
'high' // Priority.
);
}
/**
* Render the HTML for the Document Details meta box.
* This is where we define all the input fields.
*/
public function render_document_details_meta_box( $post ) {
// Use nonce for verification. Nonce name changed to 'ttdvr_document_nonce'.
wp_nonce_field( basename( __FILE__ ), 'ttdvr_document_nonce' );
// Retrieve current metadata values if they exist (meta keys updated with new prefix).
$verification_id = get_post_meta( $post->ID, '_ttdvr_verification_id', true );
$recipient_name = get_post_meta( $post->ID, '_ttdvr_recipient_name', true );
$phone = get_post_meta( $post->ID, '_ttdvr_phone', true );
$email = get_post_meta( $post->ID, '_ttdvr_email', true );
$date_of_birth = get_post_meta( $post->ID, '_ttdvr_date_of_birth', true );
$address = get_post_meta( $post->ID, '_ttdvr_address', true );
$document_type = get_post_meta( $post->ID, '_ttdvr_document_type', true );
$document_type_other = get_post_meta( $post->ID, '_ttdvr_document_type_other', true ); // For "Other (specify)"
$awarding_body = get_post_meta( $post->ID, '_ttdvr_awarding_body', true );
$course_subject = get_post_meta( $post->ID, '_ttdvr_course_subject', true );
$grade = get_post_meta( $post->ID, '_ttdvr_grade', true );
$performance = get_post_meta( $post->ID, '_ttdvr_performance', true );
$issued_date = get_post_meta( $post->ID, '_ttdvr_issued_date', true );
$effective_until_date = get_post_meta( $post->ID, '_ttdvr_effective_until_date', true );
$document_pdf_url = get_post_meta( $post->ID, '_ttdvr_document_pdf_url', true );
$course_details_remarks = get_post_meta( $post->ID, '_ttdvr_course_details_remarks', true );
// Options for Document Type dropdown (translated strings escaped).
$document_type_options = array(
'' => esc_html__( 'Select Document Type', '24tt-document-verifier' ),
'Letter' => esc_html__( 'Letter', '24tt-document-verifier' ),
'Certificate of Merit' => esc_html__( 'Certificate of Merit', '24tt-document-verifier' ),
'Certificate of Completion' => esc_html__( 'Certificate of Completion', '24tt-document-verifier' ),
'Memorandum of Understanding' => esc_html__( 'Memorandum of Understanding', '24tt-document-verifier' ),
'Proposal' => esc_html__( 'Proposal', '24tt-document-verifier' ),
'Invoice' => esc_html__( 'Invoice', '24tt-document-verifier' ),
'Receipt' => esc_html__( 'Receipt', '24tt-document-verifier' ),
'Quotation' => esc_html__( 'Quotation', '24tt-document-verifier' ),
'Contract/Agreement' => esc_html__( 'Contract/Agreement', '24tt-document-verifier' ),
'Company Resolution' => esc_html__( 'Company Resolution', '24tt-document-verifier' ),
'Offer' => esc_html__( 'Offer', '24tt-document-verifier' ),
'Business Letter' => esc_html__( 'Business Letter', '24tt-document-verifier' ),
'Other' => esc_html__( 'Other (specify below)', '24tt-document-verifier' ), // This option will enable the 'Other' text field
);
?>
$label ) : ?>
>
>A
>B
>C
>D
>E
>Pass
>Distinction
>Merit
>Credit
>Participation
>Completion
>N/A
'_ttdvr_verification_id',
'ttdvr_recipient_name' => '_ttdvr_recipient_name',
'ttdvr_phone' => '_ttdvr_phone',
'ttdvr_email' => '_ttdvr_email',
'ttdvr_date_of_birth' => '_ttdvr_date_of_birth',
'ttdvr_address' => '_ttdvr_address',
'ttdvr_document_type' => '_ttdvr_document_type',
'ttdvr_document_type_other' => '_ttdvr_document_type_other',
'ttdvr_awarding_body' => '_ttdvr_awarding_body',
'ttdvr_course_subject' => '_ttdvr_course_subject',
'ttdvr_grade' => '_ttdvr_grade',
'ttdvr_performance' => '_ttdvr_performance',
'ttdvr_issued_date' => '_ttdvr_issued_date',
'ttdvr_effective_until_date' => '_ttdvr_effective_until_date',
'ttdvr_document_pdf_url' => '_ttdvr_document_pdf_url',
'ttdvr_course_details_remarks' => '_ttdvr_course_details_remarks',
);
foreach ( $fields as $input_name => $meta_key ) {
// Sanitize input using wp_unslash before sanitization functions.
// Ensure $_POST[$input_name] is unslashed immediately upon access.
$posted_value = isset( $_POST[ $input_name ] ) ? wp_unslash( $_POST[ $input_name ] ) : ''; // Fix for line 410.
// Special sanitization for specific fields.
if ( 'ttdvr_email' === $input_name ) {
$new_value = sanitize_email( $posted_value );
} elseif ( 'ttdvr_document_pdf_url' === $input_name ) {
$new_value = esc_url_raw( $posted_value );
} elseif ( 'ttdvr_address' === $input_name || 'ttdvr_course_details_remarks' === $input_name ) {
$new_value = sanitize_textarea_field( $posted_value );
} elseif ( 'ttdvr_performance' === $input_name ) {
$new_value = absint( $posted_value ); // Ensure it's a non-negative integer.
if ( $new_value > 100 ) {
$new_value = 100;
}
} else {
$new_value = sanitize_text_field( $posted_value ); // Default for other text fields.
}
update_post_meta( $post_id, $meta_key, $new_value );
}
}
/**
* Add admin menu page for plugin settings/dashboard.
* Menu slugs updated with new prefix.
*/
public function add_plugin_admin_menu() {
// Add a top-level menu item for the plugin's dashboard.
add_menu_page(
esc_html__( '24TT Document Verifier', '24tt-document-verifier' ), // Page title (escaped).
esc_html__( '24TT Document Verifier', '24tt-document-verifier' ), // Menu title (escaped).
'manage_options', // Capability required to access.
'ttdvr-dashboard', // Unique slug for this menu page (new prefix).
array( $this, 'render_plugin_dashboard_page' ), // Callback function to render content.
'dashicons-clipboard', // Icon.
6 // Position in the menu.
);
// Add submenu: All Documents (links to custom post type list).
add_submenu_page(
'ttdvr-dashboard', // Parent slug (new prefix).
esc_html__( 'All Documents', '24tt-document-verifier' ), // Page title (escaped).
esc_html__( 'All Documents', '24tt-document-verifier' ), // Menu title (escaped).
'edit_posts', // Capability.
'edit.php?post_type=ttdvr_document', // Slug (links to CPT list, new prefix).
'', // Callback (empty, as we're redirecting).
10 // Position.
);
// Add submenu: Add New Document (links to custom post type add new).
add_submenu_page(
'ttdvr-dashboard', // Parent slug (new prefix).
esc_html__( 'Add New Document', '24tt-document-verifier' ), // Page title (escaped).
esc_html__( 'Add New Document', '24tt-document-verifier' ), // Menu title (escaped).
'publish_posts', // Capability.
'post-new.php?post_type=ttdvr_document', // Slug (links to CPT add new, new prefix).
'', // Callback (empty).
20 // Position.
);
// Add submenu: Bulk Add Documents (New page for bulk entry).
add_submenu_page(
'ttdvr-dashboard', // Parent slug (new prefix).
esc_html__( 'Bulk Add Documents', '24tt-document-verifier' ), // Page title (escaped).
esc_html__( 'Bulk Add Documents', '24tt-document-verifier' ), // Menu title (escaped).
'publish_posts', // Capability.
'ttdvr-bulk-add-documents', // Unique slug for this page (new prefix).
array( $this, 'render_bulk_add_documents_page' ), // Callback function.
25 // Position.
);
// Add submenu: Document Categories (links to custom taxonomy UI).
add_submenu_page(
'ttdvr-dashboard', // Parent slug (new prefix).
esc_html__( 'Document Categories', '24tt-document-verifier' ), // Page title (escaped).
esc_html__( 'Categories', '24tt-document-verifier' ), // Menu title (escaped).
'manage_categories', // Capability.
'edit-tags.php?taxonomy=ttdvr_doc_category&post_type=ttdvr_document', // Slug (links to taxonomy UI, new prefix).
'', // Callback (empty).
30 // Position.
);
}
/**
* Renders the main plugin dashboard page.
* This is where the shortcode information will be displayed.
*/
public function render_plugin_dashboard_page() {
?>
%1$s > Categories to add categories for your documents (e.g., Certificate, Letter, Article).', '24tt-document-verifier' ), esc_html__( '24TT Document Verifier', '24tt-document-verifier' ) ) ); ?>
%1$s > Add New Document to add individual documents with their unique IDs and details.', '24tt-document-verifier' ), esc_html__( '24TT Document Verifier', '24tt-document-verifier' ) ) ); ?>
%1$s > Bulk Add Documents page.', '24tt-document-verifier' ), esc_html__( '24TT Document Verifier', '24tt-document-verifier' ) ) ); ?>
%1$s into its content.', '24tt-document-verifier' ), '[ttdvr_verify_document]' ) ); ?>
esc_html__( 'Select Document Type', '24tt-document-verifier' ),
'Letter' => esc_html__( 'Letter', '24tt-document-verifier' ),
'Certificate of Merit' => esc_html__( 'Certificate of Merit', '24tt-document-verifier' ),
'Certificate of Completion' => esc_html__( 'Certificate of Completion', '24tt-document-verifier' ),
'Memorandum of Understanding' => esc_html__( 'Memorandum of Understanding', '24tt-document-verifier' ),
'Proposal' => esc_html__( 'Proposal', '24tt-document-verifier' ),
'Invoice' => esc_html__( 'Invoice', '24tt-document-verifier' ),
'Receipt' => esc_html__( 'Receipt', '24tt-document-verifier' ),
'Quotation' => esc_html__( 'Quotation', '24tt-document-verifier' ),
'Contract/Agreement' => esc_html__( 'Contract/Agreement', '24tt-document-verifier' ),
'Company Resolution' => esc_html__( 'Company Resolution', '24tt-document-verifier' ),
'Offer' => esc_html__( 'Offer', '24tt-document-verifier' ),
'Business Letter' => esc_html__( 'Business Letter', '24tt-document-verifier' ),
'Other' => esc_html__( 'Other (specify below)', '24tt-document-verifier' ),
);
// Get existing categories to populate the dropdown (taxonomy slug updated).
$categories = get_terms( array(
'taxonomy' => 'ttdvr_doc_category',
'hide_empty' => false,
) );
?>
' . esc_html( $message ) . '
';
delete_transient( 'ttdvr_bulk_upload_message' );
delete_transient( 'ttdvr_bulk_upload_message_type' );
}
?>
$document ) {
// Basic validation: Document Title and Verification ID are required.
// If a row is entirely empty, skip it. This can happen if user adds and then clears rows.
if ( empty( $document['title'] ) && empty( $document['verification_id'] ) && empty( $document['recipient_name'] ) ) {
continue; // Skip entirely empty rows.
}
// If required fields are missing, treat as an error for this specific row.
if ( empty( $document['title'] ) || empty( $document['verification_id'] ) ) {
$error_count++;
continue;
}
// Sanitize and prepare data for wp_insert_post and update_post_meta.
$new_post_title = sanitize_text_field( $document['title'] );
$post_args = array(
'post_title' => $new_post_title,
'post_status' => 'publish', // Publish immediately.
'post_type' => 'ttdvr_document', // CPT slug updated.
'post_content' => '', // Content can be added here if needed, but not part of current bulk fields.
);
// Insert the post.
$post_id = wp_insert_post( $post_args, true ); // Set second argument to true to return WP_Error on failure.
if ( is_wp_error( $post_id ) ) {
$error_count++;
// Optionally log the error for debugging: error_log( '24TT Doc Verification: Failed to insert post - ' . $post_id->get_error_message() );
continue;
}
// Save custom meta data.
self::save_single_document_meta_from_bulk( $post_id, $document );
// Handle taxonomy assignment. Taxonomy slug updated.
if ( ! empty( $document['category_id'] ) ) {
wp_set_object_terms( $post_id, (int) $document['category_id'], 'ttdvr_doc_category' );
}
$success_count++;
}
if ( $success_count > 0 ) {
// Translators: %s: number of documents.
$message = sprintf(
_n( '%s document added successfully.', '%s documents added successfully.', $success_count, '24tt-document-verifier' ), // Fix for lines 521, 522.
$success_count
);
set_transient( 'ttdvr_bulk_upload_message', $message, 30 ); // Store message for 30 seconds (new prefix).
set_transient( 'ttdvr_bulk_upload_message_type', 'success', 30 ); // New prefix.
}
if ( $error_count > 0 ) {
// Translators: %s: number of documents.
$message = sprintf(
_n( '%s document could not be added due to missing required fields or an error.', '%s documents could not be added due to missing required fields or errors.', $error_count, '24tt-document-verifier' ), // Fix for lines 523, 524.
$error_count
);
set_transient( 'ttdvr_bulk_upload_message', $message, 30 ); // New prefix.
set_transient( 'ttdvr_bulk_upload_message_type', 'error', 30 ); // New prefix.
}
} else {
set_transient( 'ttdvr_bulk_upload_message', esc_html__( 'No documents submitted for bulk upload.', '24tt-document-verifier' ), 30 ); // New prefix, escaped.
set_transient( 'ttdvr_bulk_upload_message_type', 'warning', 30 ); // New prefix.
}
// Redirect back to the bulk add page to show messages.
wp_safe_redirect( admin_url( 'admin.php?page=ttdvr-bulk-add-documents' ) ); // Slug updated.
exit; // Always exit after a redirect.
}
/**
* Helper function to save meta data for a single document from bulk submission.
* This is a simplified version of save_document_meta_box_data for bulk data.
* @param int $post_id The ID of the post.
* @param array $data The array of document data.
*/
public static function save_single_document_meta_from_bulk( $post_id, $data ) {
// Define the fields we expect from the bulk form and their corresponding meta keys (updated with new prefix).
$meta_fields_map = array(
'verification_id' => '_ttdvr_verification_id',
'recipient_name' => '_ttdvr_recipient_name',
'document_type' => '_ttdvr_document_type',
'document_type_other' => '_ttdvr_document_type_other',
'course_subject' => '_ttdvr_course_subject',
'issued_date' => '_ttdvr_issued_date',
// Note: The bulk form currently only has these fields.
// If you add more fields to the bulk form later (e.g., phone, email, pdf url),
// you'll need to add them to this map and implement their sanitization.
);
foreach ( $meta_fields_map as $input_key => $meta_key ) {
if ( isset( $data[ $input_key ] ) ) {
$value = wp_unslash( $data[ $input_key ] ); // Unslash first.
$value = sanitize_text_field( $value ); // Default sanitization.
// Add specific sanitization if needed for bulk fields (e.g., email, url).
// if ( 'email' === $input_key ) { $value = sanitize_email( $value ); }
// if ( 'document_pdf_url' === $input_key ) { $value = esc_url_raw( $value ); }
update_post_meta( $post_id, $meta_key, $value );
} else {
// If a field isn't set, ensure it's deleted if it existed previously.
delete_post_meta( $post_id, $meta_key );
}
}
}
/**
* Shortcode to display the document verification form.
* Shortcode name changed to 'ttdvr_verify_document'.
*/
public static function ttdvr_verify_document_shortcode( $atts ) {
ob_start(); // Start output buffering.
?>
' . esc_html__( 'Security check failed. Please try again.', '24tt-document-verifier' ) . '';
return;
}
// Sanitize the input ID.
$search_id = sanitize_text_field( wp_unslash( $_POST['ttdvr_verification_id'] ) );
if ( empty( $search_id ) ) {
echo '' . esc_html__( 'Please enter a verification ID.', '24tt-document-verifier' ) . '
';
return;
}
// Query for documents based on the meta field. CPT and meta key updated.
$args = array(
'post_type' => 'ttdvr_document',
'posts_per_page' => 1, // We expect only one matching document.
'meta_query' => array(
array(
'key' => '_ttdvr_verification_id',
'value' => $search_id,
'compare' => '=',
),
),
);
$documents = new WP_Query( $args );
if ( $documents->have_posts() ) {
while ( $documents->have_posts() ) {
$documents->the_post();
$post_id = get_the_ID();
// Retrieve all document details (updated meta keys).
$verification_id = get_post_meta( $post_id, '_ttdvr_verification_id', true );
$recipient_name = get_post_meta( $post_id, '_ttdvr_recipient_name', true );
$phone = get_post_meta( $post_id, '_ttdvr_phone', true );
$email = get_post_meta( $post_id, '_ttdvr_email', true );
$date_of_birth = get_post_meta( $post_id, '_ttdvr_date_of_birth', true );
$address = get_post_meta( $post_id, '_ttdvr_address', true );
$document_type = get_post_meta( $post_id, '_ttdvr_document_type', true );
$document_type_other = get_post_meta( $post_id, '_ttdvr_document_type_other', true );
$awarding_body = get_post_meta( $post_id, '_ttdvr_awarding_body', true );
$course_subject = get_post_meta( $post_id, '_ttdvr_course_subject', true );
$grade = get_post_meta( $post_id, '_ttdvr_grade', true );
$performance = get_post_meta( $post_id, '_ttdvr_performance', true );
$issued_date = get_post_meta( $post_id, '_ttdvr_issued_date', true );
$effective_until_date = get_post_meta( $post_id, '_ttdvr_effective_until_date', true );
$document_pdf_url = get_post_meta( $post_id, '_ttdvr_document_pdf_url', true );
$course_details_remarks = get_post_meta( $post_id, '_ttdvr_course_details_remarks', true );
$document_title = get_the_title();
$document_categories = get_the_terms( $post_id, 'ttdvr_doc_category' ); // Get terms for the taxonomy (new prefix).
// Determine effective document type for display.
$display_document_type = ( 'Other' === $document_type && ! empty( $document_type_other ) ) ? $document_type_other : $document_type;
?>
name ); // Escaped each term name.
}
echo esc_html( implode( ', ', $category_names ) ); // Fix for line 865.
?>
' . esc_html__( 'Document not found or invalid ID.', '24tt-document-verifier' ) . '';
}
}
}
/**
* Defines custom columns for the 'ttdvr_document' post type list table.
*/
public function add_document_columns( $columns ) {
$new_columns = array(
'cb' => ' ', // Checkbox for bulk actions.
'title' => esc_html__( 'Document Title', '24tt-document-verifier' ), // Escaped.
'document_id' => esc_html__( 'Document ID', '24tt-document-verifier' ), // Escaped.
'recipient_name_col' => esc_html__( 'Recipient Name', '24tt-document-verifier' ), // Escaped.
'doc_type_col' => esc_html__( 'Document Type', '24tt-document-verifier' ), // Escaped.
'course_subject_col' => esc_html__( 'Course / Subject', '24tt-document-verifier' ), // Escaped.
'doc_category' => esc_html__( 'Category', '24tt-document-verifier' ), // Taxonomy column (escaped).
'issued_date_col' => esc_html__( 'Issued Date', '24tt-document-verifier' ), // Escaped.
'date' => esc_html__( 'Date Added', '24tt-document-verifier' ), // Default WordPress 'Date' column (escaped).
);
return $new_columns;
}
/**
* Renders content for custom columns in the 'ttdvr_document' list table.
*/
public function custom_document_column_content( $column, $post_id ) {
switch ( $column ) {
case 'document_id':
echo esc_html( get_post_meta( $post_id, '_ttdvr_verification_id', true ) ); // Meta key updated.
break;
case 'recipient_name_col':
echo esc_html( get_post_meta( $post_id, '_ttdvr_recipient_name', true ) ); // Meta key updated.
break;
case 'doc_type_col':
$doc_type = get_post_meta( $post_id, '_ttdvr_document_type', true ); // Meta key updated.
$doc_type_other = get_post_meta( $post_id, '_ttdvr_document_type_other', true ); // Meta key updated.
echo esc_html( ( 'Other' === $doc_type && ! empty( $doc_type_other ) ) ? $doc_type_other : $doc_type );
break;
case 'course_subject_col':
echo esc_html( get_post_meta( $post_id, '_ttdvr_course_subject', true ) ); // Meta key updated.
break;
case 'doc_category':
$terms = get_the_terms( $post_id, 'ttdvr_doc_category' ); // Taxonomy slug updated.
if ( $terms && ! is_wp_error( $terms ) ) {
$category_names = array();
foreach ( $terms as $term ) {
$category_names[] = esc_html( $term->name ); // Escaped each term name.
}
echo esc_html( implode( ', ', $category_names ) ); // Fix for line 944.
} else {
echo esc_html__( 'No Category', '24tt-document-verifier' ); // Escaped.
}
break;
case 'issued_date_col':
$issued_date = get_post_meta( $post_id, '_ttdvr_issued_date', true ); // Meta key updated.
if ( $issued_date ) {
echo esc_html( date_i18n( get_option( 'date_format' ), strtotime( $issued_date ) ) );
}
break;
}
}
/**
* Makes custom columns sortable in the 'ttdvr_document' list table.
*/
public static function make_document_columns_sortable( $columns ) {
$columns['document_id'] = 'document_id';
$columns['recipient_name_col'] = 'recipient_name_col';
$columns['doc_type_col'] = 'doc_type_col';
$columns['course_subject_col'] = 'course_subject_col';
$columns['issued_date_col'] = 'issued_date_col';
return $columns;
}
/**
* Modifies the WP_Query to handle sorting for custom columns.
*/
public static function sort_document_columns_query( $query ) {
if ( ! is_admin() || ! $query->is_main_query() || $query->get( 'post_type' ) !== 'ttdvr_document' ) { // CPT slug updated.
return;
}
$orderby = $query->get( 'orderby' );
if ( 'document_id' === $orderby ) {
$query->set( 'meta_key', '_ttdvr_verification_id' ); // Meta key updated.
$query->set( 'orderby', 'meta_value' );
} elseif ( 'recipient_name_col' === $orderby ) {
$query->set( 'meta_key', '_ttdvr_recipient_name' ); // Meta key updated.
$query->set( 'orderby', 'meta_value' );
} elseif ( 'doc_type_col' === $orderby ) {
$query->set( 'meta_key', '_ttdvr_document_type' ); // Meta key updated.
$query->set( 'orderby', 'meta_value' );
} elseif ( 'course_subject_col' === $orderby ) {
$query->set( 'meta_key', '_ttdvr_course_subject' ); // Meta key updated.
$query->set( 'orderby', 'meta_value' );
} elseif ( 'issued_date_col' === $orderby ) {
$query->set( 'meta_key', '_ttdvr_issued_date' ); // Meta key updated.
$query->set( 'orderby', 'meta_value_date' );
}
}
/**
* Enqueue admin scripts and styles.
* This handles all admin-side JS and CSS.
*
* @param string $hook_suffix The current admin page hook.
*/
public function enqueue_admin_scripts( $hook_suffix ) {
// Enqueue admin styles.
wp_enqueue_style( 'ttdvr-admin-style', plugin_dir_url( __FILE__ ) . 'assets/css/admin-style.css', array(), '1.1.2', 'all' ); // New handle.
// Only enqueue scripts on our plugin's specific admin pages.
$is_our_cpt_page = ( 'post.php' === $hook_suffix || 'post-new.php' === $hook_suffix ) && ( isset( $_GET['post_type'] ) && 'ttdvr_document' === $_GET['post_type'] );
$is_our_admin_page = ( 'admin.php' === $hook_suffix && isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'ttdvr-dashboard', 'ttdvr-bulk-add-documents' ), true ) );
$is_our_taxonomy_page = ( 'edit-tags.php' === $hook_suffix && isset( $_GET['taxonomy'] ) && 'ttdvr_doc_category' === $_GET['taxonomy'] );
if ( $is_our_cpt_page || $is_our_admin_page || $is_our_taxonomy_page ) {
// Enqueue jQuery as a dependency.
wp_enqueue_script( 'jquery' );
// Register and enqueue our main admin script.
// This file can be empty if all JS is inline, but it's good practice to have it.
wp_enqueue_script(
'ttdvr-admin-script', // Handle for our script.
plugin_dir_url( __FILE__ ) . 'assets/js/admin-script.js', // Path to your JS file.
array( 'jquery' ), // Dependencies.
'1.1.2', // Version.
true // In footer.
);
// Localize script data for all admin scripts.
// This is the correct way to pass PHP variables and translated strings to JavaScript.
$localized_data = array(
// Meta Box Strings.
'selectDocumentType' => esc_html__( 'Select Document Type', '24tt-document-verifier' ),
'specifyDocumentType' => esc_attr__( 'Specify Document Type', '24tt-document-verifier' ),
// Bulk Add Strings.
'documentTitle' => esc_attr__( 'Document Title', '24tt-document-verifier' ),
'documentId' => esc_attr__( 'Document ID', '24tt-document-verifier' ),
'recipientName' => esc_attr__( 'Recipient Name', '24tt-document-verifier' ),
'specifyOther' => esc_attr__( 'Specify Other', '24tt-document-verifier' ),
'courseSubject' => esc_attr__( 'Course / Subject', '24tt-document-verifier' ),
'selectCategory' => esc_attr__( 'Select Category', '24tt-document-verifier' ),
'remove' => esc_html__( 'Remove', '24tt-document-verifier' ),
'shortcodeCopied' => esc_html__( 'Shortcode copied!', '24tt-document-verifier' ),
// Data for bulk add dropdowns.
'documentTypeOptions' => array(
'' => esc_html__( 'Select Document Type', '24tt-document-verifier' ),
'Letter' => esc_html__( 'Letter', '24tt-document-verifier' ),
'Certificate of Merit' => esc_html__( 'Certificate of Merit', '24tt-document-verifier' ),
'Certificate of Completion' => esc_html__( 'Certificate of Completion', '24tt-document-verifier' ),
'Memorandum of Understanding' => esc_html__( 'Memorandum of Understanding', '24tt-document-verifier' ),
'Proposal' => esc_html__( 'Proposal', '24tt-document-verifier' ),
'Invoice' => esc_html__( 'Invoice', '24tt-document-verifier' ),
'Receipt' => esc_html__( 'Receipt', '24tt-document-verifier' ),
'Quotation' => esc_html__( 'Quotation', '24tt-document-verifier' ),
'Contract/Agreement' => esc_html__( 'Contract/Agreement', '24tt-document-verifier' ),
'Company Resolution' => esc_html__( 'Company Resolution', '24tt-document-verifier' ),
'Offer' => esc_html__( 'Offer', '24tt-document-verifier' ),
'Business Letter' => esc_html__( 'Business Letter', '24tt-document-verifier' ),
'Other' => esc_html__( 'Other (specify below)', '24tt-document-verifier' ),
),
'categories' => get_terms( array(
'taxonomy' => 'ttdvr_doc_category',
'hide_empty' => false,
'fields' => 'id=>name', // Get only ID and Name for JS.
) ),
);
wp_localize_script( 'ttdvr-admin-script', 'ttdvrAdminVars', $localized_data );
// Inline script for Document Details meta box (on single document edit screen).
if ( $is_our_cpt_page ) {
$meta_box_js = "
document.addEventListener('DOMContentLoaded', function() {
var docTypeSelect = document.getElementById('ttdvr_document_type');
var docTypeOtherInput = document.getElementById('ttdvr_document_type_other');
function toggleOtherInput() {
if (docTypeSelect.value === 'Other') {
docTypeOtherInput.style.display = 'inline-block';
docTypeOtherInput.setAttribute('required', 'required');
} else {
docTypeOtherInput.style.display = 'none';
docTypeOtherInput.removeAttribute('required');
docTypeOtherInput.value = '';
}
}
toggleOtherInput();
docTypeSelect.addEventListener('change', toggleOtherInput);
});
";
wp_add_inline_script( 'ttdvr-admin-script', $meta_box_js );
}
// Inline script for Bulk Add Documents page.
if ( 'admin.php' === $hook_suffix && isset( $_GET['page'] ) && 'ttdvr-bulk-add-documents' === $_GET['page'] ) {
$bulk_add_js = "
document.addEventListener('DOMContentLoaded', function() {
var documentsTableBody = document.getElementById('documents-table-body');
var addRowButton = document.getElementById('ttdvr-add-row');
var rowCounter = 3; // Start counter after initial rows.
// Function to generate a new row HTML using localized data
function generateNewRow(index) {
var documentTypeOptions = ttdvrAdminVars.documentTypeOptions;
var categories = ttdvrAdminVars.categories;
var docTypeOptionsHtml = '';
for (const value in documentTypeOptions) {
if (Object.prototype.hasOwnProperty.call(documentTypeOptions, value)) {
docTypeOptionsHtml += `\${documentTypeOptions[value]} `;
}
}
var categoryOptionsHtml = `\${ttdvrAdminVars.selectCategory} `;
if (categories) {
for (const term_id in categories) {
if (Object.prototype.hasOwnProperty.call(categories, term_id)) {
categoryOptionsHtml += `\${categories[term_id]} `;
}
}
}
var rowHtml = `
\${docTypeOptionsHtml}
\${categoryOptionsHtml}
\${ttdvrAdminVars.remove}
`;
return rowHtml;
}
// Add Row functionality
addRowButton.addEventListener('click', function() {
var newRowHtml = generateNewRow(rowCounter);
documentsTableBody.insertAdjacentHTML('beforeend', newRowHtml);
// Re-initialize event listeners for the new row's document type select
var newRow = documentsTableBody.lastElementChild;
setupDocumentTypeToggle(newRow);
rowCounter++;
});
// Remove Row functionality (event delegation)
documentsTableBody.addEventListener('click', function(e) {
if (e.target && e.target.classList.contains('ttdvr-remove-row')) {
e.target.closest('tr').remove();
}
});
// Function to set up the change listener for document type selects
function setupDocumentTypeToggle(rowElement) {
var selectElement = rowElement.querySelector('.ttdvr-document-type-select');
var otherInput = rowElement.querySelector('.ttdvr-document-type-other-input');
if (selectElement && otherInput) {
function toggleOther() {
if (selectElement.value === 'Other') {
otherInput.style.display = 'inline-block';
if (otherInput.style.display !== 'none') {
otherInput.setAttribute('required', 'required');
}
} else {
otherInput.style.display = 'none';
otherInput.removeAttribute('required');
otherInput.value = '';
}
}
toggleOther();
selectElement.addEventListener('change', toggleOther);
}
}
// Initialize document type toggles for existing/initial rows
documentsTableBody.querySelectorAll('tr').forEach(setupDocumentTypeToggle);
});
";
wp_add_inline_script( 'ttdvr-admin-script', $bulk_add_js );
}
// Inline script for copying shortcode on dashboard.
if ( 'admin.php' === $hook_suffix && isset( $_GET['page'] ) && 'ttdvr-dashboard' === $_GET['page'] ) {
$dashboard_js = "
document.addEventListener('DOMContentLoaded', function() {
var copyButton = document.querySelector('.ttdvr-copy-shortcode');
if (copyButton) {
copyButton.addEventListener('click', function() {
var shortcode = this.getAttribute('data-shortcode');
var tempInput = document.createElement('textarea');
tempInput.value = shortcode;
document.body.appendChild(tempInput);
tempInput.select();
document.execCommand('copy');
document.body.removeChild(tempInput);
var copyMessage = document.createElement('span');
copyMessage.textContent = ' ' + ttdvrAdminVars.shortcodeCopied;
copyMessage.style.marginLeft = '10px';
copyMessage.style.color = 'green';
this.parentNode.insertBefore(copyMessage, this.nextSibling);
setTimeout(function() { copyMessage.remove(); }, 3000);
});
}
});
";
wp_add_inline_script( 'ttdvr-admin-script', $dashboard_js );
}
}
}
/**
* Enqueue frontend styles for the verification form/results.
*/
public function enqueue_frontend_styles() {
wp_enqueue_style( 'ttdvr-frontend-style', plugin_dir_url( __FILE__ ) . 'assets/css/frontend-style.css', array(), '1.1.2', 'all' ); // Handle updated.
}
}
// Instantiate the plugin class.
$ttdvr_document_verification_plugin = new TTDVR_Document_Verification_Plugin();