get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s", 'ajbq_quote' ) ); foreach ( $post_ids as $id ) { wp_delete_post( (int) $id, true ); } // Safety net — direct delete in case wp_delete_post missed anything. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->posts} WHERE post_type = %s", 'ajbq_quote' ) ); $wpdb->query( "DELETE pm FROM {$wpdb->postmeta} pm LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id WHERE p.ID IS NULL" ); // ── 2. Delete all ajbq_category taxonomy terms ───────────────────────── $term_ids = $wpdb->get_col( $wpdb->prepare( "SELECT term_id FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s", 'ajbq_category' ) ); foreach ( $term_ids as $tid ) { wp_delete_term( (int) $tid, 'ajbq_category' ); } // Direct cleanup of any orphaned rows. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s", 'ajbq_category' ) ); // ── 3. Delete all plugin options ────────────────────────────────────── $options = [ 'ajbq_bg_color', 'ajbq_corners', 'ajbq_width', 'ajbq_height', 'ajbq_show_author', 'ajbq_show_link', 'ajbq_show_copy', 'ajbq_quote_font', 'ajbq_quote_size', 'ajbq_quote_color', 'ajbq_quote_style', 'ajbq_quote_weight', 'ajbq_author_font', 'ajbq_author_size', 'ajbq_author_color', 'ajbq_author_style', 'ajbq_author_weight', 'ajbq_author_prefix', 'ajbq_author_suffix', 'ajbq_link_font', 'ajbq_link_size', 'ajbq_link_color', 'ajbq_always_load_assets', 'ajbq_migration_v2_done', 'ajbq_sample_quotes_seeded', ]; foreach ( $options as $opt ) { delete_option( $opt ); } // ── 4. Delete all transients ────────────────────────────────────────── $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", '_transient_rtq_%' ) ); $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", '_transient_timeout_rtq_%' ) ); } // ========================================================================= // ACTIVATION — seed sample quotes on first install // ========================================================================= register_activation_hook( AJBQ_FILE, 'ajbq_on_activation' ); function ajbq_on_activation() { // Only seed once, and only if there are no quotes already. if ( get_option( 'ajbq_sample_quotes_seeded' ) ) { return; } // Register the CPT so wp_insert_post can use it immediately. ajbq_register_cpt(); $samples = [ [ 'quote' => 'The only way to do great work is to love what you do.', 'author' => 'Steve Jobs', 'category' => 'Inspiration', 'favorite' => '1', ], [ 'quote' => 'In the middle of every difficulty lies opportunity.', 'author' => 'Albert Einstein', 'category' => 'Inspiration', 'favorite' => '0', ], [ 'quote' => 'It does not matter how slowly you go as long as you do not stop.', 'author' => 'Confucius', 'category' => 'Perseverance', 'favorite' => '1', ], [ 'quote' => 'Life is what happens when you\'re busy making other plans.', 'author' => 'John Lennon', 'category' => 'Life', 'favorite' => '0', ], [ 'quote' => 'The future belongs to those who believe in the beauty of their dreams.', 'author' => 'Eleanor Roosevelt', 'category' => 'Inspiration', 'favorite' => '0', ], [ 'quote' => 'Spread love everywhere you go. Let no one ever come to you without leaving happier.', 'author' => 'Mother Teresa', 'category' => 'Kindness', 'favorite' => '1', ], [ 'quote' => 'When you reach the end of your rope, tie a knot in it and hang on.', 'author' => 'Franklin D. Roosevelt', 'category' => 'Perseverance', 'favorite' => '0', ], [ 'quote' => 'Always remember that you are absolutely unique. Just like everyone else.', 'author' => 'Margaret Mead', 'category' => 'Humour', 'favorite' => '0', ], [ 'quote' => 'Do not go where the path may lead, go instead where there is no path and leave a trail.', 'author' => 'Ralph Waldo Emerson', 'category' => 'Inspiration', 'favorite' => '0', ], [ 'quote' => 'You will face many defeats in life, but never let yourself be defeated.', 'author' => 'Maya Angelou', 'category' => 'Perseverance', 'favorite' => '1', ], [ 'quote' => 'The greatest glory in living lies not in never falling, but in rising every time we fall.', 'author' => 'Nelson Mandela', 'category' => 'Perseverance', 'favorite' => '0', ], [ 'quote' => 'In the end, it\'s not the years in your life that count. It\'s the life in your years.', 'author' => 'Abraham Lincoln', 'category' => 'Life', 'favorite' => '0', ], [ 'quote' => 'Never let the fear of striking out keep you from playing the game.', 'author' => 'Babe Ruth', 'category' => 'Courage', 'favorite' => '0', ], [ 'quote' => 'Life is either a daring adventure or nothing at all.', 'author' => 'Helen Keller', 'category' => 'Life', 'favorite' => '1', ], [ 'quote' => 'Many of life\'s failures are people who did not realise how close they were to success when they gave up.', 'author' => 'Thomas A. Edison', 'category' => 'Perseverance', 'favorite' => '0', ], ]; foreach ( $samples as $s ) { $post_id = wp_insert_post( [ 'post_title' => wp_trim_words( $s['quote'], 10, '…' ), 'post_content' => $s['quote'], 'post_status' => 'publish', 'post_type' => AJBQ_POST_TYPE, ] ); if ( ! $post_id || is_wp_error( $post_id ) ) { continue; } update_post_meta( $post_id, '_rtq_author', $s['author'] ); update_post_meta( $post_id, '_rtq_is_favorite', $s['favorite'] ); $term = term_exists( $s['category'], AJBQ_TAXONOMY ); if ( ! $term ) { $term = wp_insert_term( $s['category'], AJBQ_TAXONOMY ); } if ( ! is_wp_error( $term ) ) { wp_set_post_terms( $post_id, [ (int) $term['term_id'] ], AJBQ_TAXONOMY ); } } update_option( 'ajbq_sample_quotes_seeded', '1' ); } function ajbq_register_cpt() { register_post_type( AJBQ_POST_TYPE, [ 'labels' => [ 'name' => __( 'AJB Quotes', 'ajb-random-quote-cards' ), 'singular_name' => __( 'Quote', 'ajb-random-quote-cards' ), 'menu_name' => __( 'AJB Quotes', 'ajb-random-quote-cards' ), 'add_new' => __( 'Add New', 'ajb-random-quote-cards' ), 'add_new_item' => __( 'Add New Quote', 'ajb-random-quote-cards' ), 'edit_item' => __( 'Edit Quote', 'ajb-random-quote-cards' ), 'new_item' => __( 'New Quote', 'ajb-random-quote-cards' ), 'view_item' => __( 'View Quote', 'ajb-random-quote-cards' ), 'search_items' => __( 'Search Quotes', 'ajb-random-quote-cards' ), 'not_found' => __( 'No quotes found.', 'ajb-random-quote-cards' ), 'not_found_in_trash' => __( 'No quotes in trash.', 'ajb-random-quote-cards' ), 'all_items' => __( 'All Quotes', 'ajb-random-quote-cards' ), ], 'public' => false, 'show_ui' => true, 'show_in_rest' => true, // Keep true for the Gutenberg block to read categories. 'has_archive' => false, 'menu_icon' => 'dashicons-editor-quote', 'supports' => [ 'title' ], // Title only — quote text handled by our custom form via post_content. ] ); register_taxonomy( AJBQ_TAXONOMY, AJBQ_POST_TYPE, [ 'labels' => [ 'name' => __( 'Quote Categories', 'ajb-random-quote-cards' ), 'singular_name' => __( 'Quote Category', 'ajb-random-quote-cards' ), 'add_new_item' => __( 'Add New Category', 'ajb-random-quote-cards' ), 'edit_item' => __( 'Edit Category', 'ajb-random-quote-cards' ), ], 'hierarchical' => true, 'show_ui' => true, 'show_in_rest' => true, ] ); } add_action( 'init', 'ajbq_register_cpt' ); // Force the classic editor for ajbq_quote — our custom meta box form is the UI. add_filter( 'use_block_editor_for_post_type', function( $use, $post_type ) { if ( AJBQ_POST_TYPE === $post_type ) return false; return $use; }, 10, 2 ); // ========================================================================= // 2. DATA MIGRATION — post_title → post_content (runs once on upgrade) // ========================================================================= /** * Migrates existing quotes whose quote text is stored only in post_title to * post_content. Runs once per site, triggered on plugin load when the * migration flag is absent from wp_options. * * Safe to run multiple times: it checks each post individually and only * migrates posts where post_content is still empty. */ function ajbq_maybe_migrate_post_title_to_content() { if ( get_option( 'ajbq_migration_v2_done' ) ) { return; } $quotes = get_posts( [ 'post_type' => AJBQ_POST_TYPE, 'posts_per_page' => -1, 'post_status' => 'any', 'no_found_rows' => true, ] ); foreach ( $quotes as $quote ) { // Only migrate posts that still have an empty post_content. // If content is already set the migration was already done for this post. if ( '' === $quote->post_content && '' !== $quote->post_title ) { wp_update_post( [ 'ID' => $quote->ID, 'post_content' => $quote->post_title, // Give the post a short title for the admin list (truncated to 80 chars). 'post_title' => wp_trim_words( $quote->post_title, 12, '…' ), ] ); } } update_option( 'ajbq_migration_v2_done', '1' ); } add_action( 'init', 'ajbq_maybe_migrate_post_title_to_content', 20 ); // ========================================================================= // 3. ADD / EDIT QUOTE — custom meta box form // Replaces the default title+editor with a single clear form. // ========================================================================= add_action( 'add_meta_boxes', function () { add_meta_box( 'ajbq_quote_form', __( 'Quote Details', 'ajb-random-quote-cards' ), 'ajbq_render_quote_form', AJBQ_POST_TYPE, 'normal', 'high' ); } ); // Hide the default title field and style the quote form — enqueued properly via admin_enqueue_scripts. // (Inline styles must use wp_add_inline_style, not echo'd