Plugin Directory

Changeset 3470017


Ignore:
Timestamp:
02/26/2026 08:45:57 AM (2 weeks ago)
Author:
codingbunny
Message:

v.1.2.1

Location:
coding-bunny-llms-generator
Files:
32 added
7 edited

Legend:

Unmodified
Added
Removed
  • coding-bunny-llms-generator/trunk/admin/class-cbllms-fields-render.php

    r3467642 r3470017  
    357357
    358358/**
    359 * Renders the custom disallowed paths field.
     359* Renders the custom disallowed paths/IDs field.
    360360*/
    361361public function render_disallowed_paths_field() {
     
    363363$value   = isset( $options['custom_disallowed_paths'] ) ? (string) $options['custom_disallowed_paths'] : '';
    364364printf(
    365 '<textarea name="%s[custom_disallowed_paths]" rows="6" class="large-text code" placeholder="/wp-content/uploads/2024/&#10;/private/">%s</textarea>',
     365'<textarea name="%s[custom_disallowed_paths]" rows="6" class="large-text code" placeholder="/wp-content/uploads/2024/&#10;/private/&#10;42&#10;128">%s</textarea>',
    366366esc_attr( $this->option_name ),
    367367esc_textarea( $value )
    368368);
    369 echo '<p class="description">' . esc_html__( 'One path per line. They will be added after defaults (cart, checkout, admin, etc.). Must start with "/".', 'coding-bunny-llms-generator' ) . '</p>';
     369echo '<p class="description">' . esc_html__( 'One entry per line. Paths must start with "/". You can also enter post/page/product IDs (numeric) to exclude specific content from crawling.', 'coding-bunny-llms-generator' ) . '</p>';
    370370}
    371371
  • coding-bunny-llms-generator/trunk/admin/class-cbllms-sanitize.php

    r3467642 r3470017  
    88
    99    /**
    10     * AI bots handler instance.
    11     *
    1210    * @var CodingBunny_LLMs_AI_Bots
    1311    */
     
    1513
    1614    /**
    17     * Constructor.
    18     *
    1915    * @param CodingBunny_LLMs_AI_Bots $ai AI bots handler instance.
    2016    */
     
    3228        $san = array();
    3329
    34         // Sanitize post types.
    3530        $all_pts = get_post_types( array( 'public' => true ), 'names' );
    3631        if ( isset( $input['include_post_types'] ) && is_array( $input['include_post_types'] ) ) {
     
    4540        }
    4641
    47         // Sanitize max urls.
    4842        $san['max_urls']         = isset( $input['max_urls'] ) ? absint( $input['max_urls'] ) : 0;
    4943        $san['max_attachments']  = isset( $input['max_attachments'] ) ? absint( $input['max_attachments'] ) : 20;
    5044
    51         // Sanitize update frequency.
    5245        $allowed_freq            = array( 'none', 'immediate', 'daily', 'weekly' );
    5346        $san['update_frequency'] = ( isset( $input['update_frequency'] ) && in_array( $input['update_frequency'], $allowed_freq, true ) )
     
    5548                : 'daily';
    5649
    57         // Sanitize all checkboxes.
    5850        $checkboxes = array(
    5951            'include_disallow',
     
    7971        }
    8072
    81         // Sanitize priority sort.
    8273        $allowed_sort = array( 'date_desc', 'date_asc', 'relevance' );
    8374        $san['priority_sort'] = ( isset( $input['priority_sort'] ) && in_array( $input['priority_sort'], $allowed_sort, true ) )
     
    8576                : 'relevance';
    8677
    87         // Sanitize priority default.
    8878        if ( isset( $input['priority_default'] ) ) {
    8979            $val = (float) $input['priority_default'];
     
    9484        }
    9585
    96         // Sanitize daily time.
    9786        if ( isset( $input['daily_time'] ) && preg_match( '/^\d{2}:\d{2}$/', $input['daily_time'] ) ) {
    9887            $san['daily_time'] = $input['daily_time'];
     
    10190        }
    10291
    103         // Sanitize weekly day & time.
    10492        $allowed_days = array( 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun' );
    10593        if ( isset( $input['weekly_day'] ) && in_array( $input['weekly_day'], $allowed_days, true ) ) {
     
    114102        }
    115103
    116         // Sanitize custom disallowed paths.
    117104        if ( ! empty( $input['custom_disallowed_paths'] ) ) {
    118105            $lines = explode( "\n", (string) $input['custom_disallowed_paths'] );
     
    120107            foreach ( $lines as $line ) {
    121108                $line = trim( sanitize_text_field( $line ) );
    122                 if ( '' !== $line && '/' === $line[0] ) {
     109                if ( '' === $line ) {
     110                    continue;
     111                }
     112                if ( '/' === $line[0] || ctype_digit( $line ) ) {
    123113                    $clean[] = $line;
    124114                }
     
    129119        }
    130120
    131         // Sanitize custom taxonomies.
    132121        $all_taxes = get_taxonomies( array( 'public' => true, '_builtin' => false ), 'names' );
    133122        if ( isset( $input['custom_taxonomies'] ) && is_array( $input['custom_taxonomies'] ) ) {
     
    142131        }
    143132
    144         // Sanitize additional content (textarea).
    145133        if ( isset( $input['additional_content'] ) ) {
    146134            $san['additional_content'] = sanitize_textarea_field( $input['additional_content'] );
     
    149137        }
    150138
    151         // Sanitize allowed AI bots.
    152139        $known = $this->ai->get_known_ai_bots();
    153140        $uas   = array();
     
    168155        }
    169156
    170         // Sanitize custom site description.
    171157        if ( isset( $input['custom_site_description'] ) ) {
    172158            $san['custom_site_description'] = sanitize_textarea_field( $input['custom_site_description'] );
     
    175161        }
    176162
    177         // Sanitize AI license.
    178163        $allowed_licenses = array_keys( $this->ai->get_ai_license_options() );
    179164        if ( isset( $input['ai_license'] ) && in_array( $input['ai_license'], $allowed_licenses, true ) ) {
     
    183168        }
    184169
    185         // Sanitize attachment license.
    186170        if ( isset( $input['attachment_license'] ) && in_array( $input['attachment_license'], $allowed_licenses, true ) ) {
    187171            $san['attachment_license'] = sanitize_text_field( $input['attachment_license'] );
     
    190174        }
    191175
    192         // Sanitize summary length (words).
    193176        if ( isset( $input['summary_length'] ) ) {
    194177            $len = absint( $input['summary_length'] );
  • coding-bunny-llms-generator/trunk/admin/class-cbllms-settings.php

    r3467642 r3470017  
    264264add_settings_field(
    265265'custom_disallowed_paths',
    266 __( 'Custom blocked paths', 'coding-bunny-llms-generator' ),
     266__( 'Custom blocked paths/IDs', 'coding-bunny-llms-generator' ),
    267267array( $this->fields, 'render_disallowed_paths_field' ),
    268268'coding-bunny-llms-generator',
  • coding-bunny-llms-generator/trunk/coding-bunny-llms-generator.php

    r3467642 r3470017  
    44* Plugin URI: https://coding-bunny.com/llms-generator/
    55* Description: Generates and maintains an llms.txt file at site root to guide LLM/AI crawlers about your content structure and priorities.
    6 * Version: 1.2.0
     6* Version: 1.2.1
    77* Requires at least: 6.0
    88* Requires PHP: 8.0
     
    2121}
    2222
    23 define( 'CBLLMS_VERSION', '1.2.0' );
     23define( 'CBLLMS_VERSION', '1.2.1' );
    2424define( 'CBLLMS_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    2525define( 'CBLLMS_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
  • coding-bunny-llms-generator/trunk/includes/generator/class-cbllms-items.php

    r3467642 r3470017  
    2828        }
    2929
     30        $excluded_ids = CodingBunny_LLMs_Generator_Metadata_Builder::get_excluded_post_ids( $options );
     31
    3032        $args = array(
    3133            'post_type'      => $post_types,
     
    4951        }
    5052
    51         if ( ! empty( $items ) ) {
    52             $items = self::sort_items_by_relevance( $items );
    53         }
    54 
    55         return apply_filters( 'cbllms_items', $items );
     53        if ( ! empty( $excluded_ids ) ) {
     54            $items = array_filter(
     55            $items,
     56            function ( $item ) use ( $excluded_ids ) {
     57                return ! in_array( $item['post_id'], $excluded_ids, true );
     58            }
     59        );
     60        $items = array_values( $items );
    5661    }
    5762
    58     /**
    59     * Gets non-media (non-attachment) items.
    60     *
    61     * @param array $args       WP_Query args.
    62     * @param array $post_types Post types.
    63     * @return array
    64     */
    65     public static function get_non_media_items( $args, $post_types ) {
    66         $items                      = array();
    67         $args_normal                = $args;
    68         $args_normal['post_type']   = $post_types;
    69         $args_normal['post_status'] = 'publish';
    70 
    71         $options        = CodingBunny_LLMs_Generator::instance()->get_options();
    72         $summary_length = isset( $options['summary_length'] ) ? absint( $options['summary_length'] ) : 40;
    73 
    74         $query = new WP_Query( $args_normal );
    75         if ( $query->have_posts() ) {
    76             foreach ( $query->posts as $post_id ) {
    77                 $permalink = get_permalink( $post_id );
    78                 if ( ! $permalink ) {
    79                     continue;
    80                 }
    81 
    82                 $raw_title = get_the_title( $post_id );
    83                 $title     = CodingBunny_LLMs_Generator_Utils::normalize_text( $raw_title );
    84 
    85                 $raw_excerpt = get_the_excerpt( $post_id );
    86                 if ( empty( $raw_excerpt ) ) {
    87                     $raw_excerpt = get_post_field( 'post_content', $post_id );
    88                 }
    89 
    90                 $summary = CodingBunny_LLMs_Generator_Utils::build_summary( $post_id, (string) $raw_excerpt, $summary_length );
    91 
    92                 $items[] = array(
    93                     'post_id'    => $post_id,
    94                     'url'        => $permalink,
    95                     'path'       => (string) wp_parse_url( $permalink, PHP_URL_PATH ),
    96                     'title'      => $title,
    97                     'excerpt'    => $summary,
    98                     'date'       => get_the_date( 'c', $post_id ),
    99                     'post_type'  => get_post_type( $post_id ),
    100                     'menu_order' => absint( get_post_field( 'menu_order', $post_id ) ),
    101                 );
    102             }
    103             wp_reset_postdata();
    104         }
    105 
    106         return $items;
     63    if ( ! empty( $items ) ) {
     64        $items = self::sort_items_by_relevance( $items );
    10765    }
    10866
    109     /**
    110     * Sorts items by relevance (page > post > product > attachment).
    111     *
    112     * @param array $items Items array.
    113     * @return array
    114     */
    115     public static function sort_items_by_relevance( $items ) {
    116         usort(
    117         $items,
    118         function ( $a, $b ) {
    119             $priority_order = array(
    120                 'page'       => 1,
    121                 'post'       => 2,
    122                 'product'    => 3,
    123                 'attachment' => 4,
    124             );
     67    return apply_filters( 'cbllms_items', $items );
     68}
    12569
    126             $a_priority = isset( $priority_order[ $a['post_type'] ] ) ? $priority_order[ $a['post_type'] ] : 99;
    127             $b_priority = isset( $priority_order[ $b['post_type'] ] ) ? $priority_order[ $b['post_type'] ] : 99;
     70/**
     71* Gets non-media (non-attachment) items.
     72*
     73* @param array $args       WP_Query args.
     74* @param array $post_types Post types.
     75* @return array
     76*/
     77public static function get_non_media_items( $args, $post_types ) {
     78    $items                      = array();
     79    $args_normal                = $args;
     80    $args_normal['post_type']   = $post_types;
     81    $args_normal['post_status'] = 'publish';
    12882
    129             if ( $a_priority !== $b_priority ) {
    130                 return $a_priority <=> $b_priority;
     83    $options        = CodingBunny_LLMs_Generator::instance()->get_options();
     84    $summary_length = isset( $options['summary_length'] ) ? absint( $options['summary_length'] ) : 40;
     85
     86    $query = new WP_Query( $args_normal );
     87    if ( $query->have_posts() ) {
     88        foreach ( $query->posts as $post_id ) {
     89            $permalink = get_permalink( $post_id );
     90            if ( ! $permalink ) {
     91                continue;
    13192            }
    13293
    133             if ( $a['post_type'] === 'page' && $b['post_type'] === 'page' ) {
    134                 if ( $a['menu_order'] !== $b['menu_order'] ) {
    135                     return $a['menu_order'] <=> $b['menu_order'];
    136                 }
     94            $raw_title = get_the_title( $post_id );
     95            $title     = CodingBunny_LLMs_Generator_Utils::normalize_text( $raw_title );
     96
     97            $raw_excerpt = get_the_excerpt( $post_id );
     98            if ( empty( $raw_excerpt ) ) {
     99                $raw_excerpt = get_post_field( 'post_content', $post_id );
    137100            }
    138101
    139             return strcmp( $b['date'], $a['date'] );
     102            $summary = CodingBunny_LLMs_Generator_Utils::build_summary( $post_id, (string) $raw_excerpt, $summary_length );
     103
     104            $items[] = array(
     105                'post_id'    => $post_id,
     106                'url'        => $permalink,
     107                'path'       => (string) wp_parse_url( $permalink, PHP_URL_PATH ),
     108                'title'      => $title,
     109                'excerpt'    => $summary,
     110                'date'       => get_the_date( 'c', $post_id ),
     111                'post_type'  => get_post_type( $post_id ),
     112                'menu_order' => absint( get_post_field( 'menu_order', $post_id ) ),
     113            );
    140114        }
    141     );
     115        wp_reset_postdata();
     116    }
    142117
    143118    return $items;
    144119}
     120
     121/**
     122* Sorts items by relevance (page > post > product > attachment).
     123*
     124* @param array $items Items array.
     125* @return array
     126*/
     127public static function sort_items_by_relevance( $items ) {
     128    usort(
     129    $items,
     130    function ( $a, $b ) {
     131        $priority_order = array(
     132            'page'       => 1,
     133            'post'       => 2,
     134            'product'    => 3,
     135            'attachment' => 4,
     136        );
     137
     138        $a_priority = isset( $priority_order[ $a['post_type'] ] ) ? $priority_order[ $a['post_type'] ] : 99;
     139        $b_priority = isset( $priority_order[ $b['post_type'] ] ) ? $priority_order[ $b['post_type'] ] : 99;
     140
     141        if ( $a_priority !== $b_priority ) {
     142            return $a_priority <=> $b_priority;
     143        }
     144
     145        if ( $a['post_type'] === 'page' && $b['post_type'] === 'page' ) {
     146            if ( $a['menu_order'] !== $b['menu_order'] ) {
     147                return $a['menu_order'] <=> $b['menu_order'];
     148            }
     149        }
     150
     151        return strcmp( $b['date'], $a['date'] );
     152    }
     153);
     154
     155return $items;
    145156}
     157}
  • coding-bunny-llms-generator/trunk/includes/generator/class-cbllms-metadata-builder.php

    r3393019 r3470017  
    3434        $lines[] = '';
    3535
     36        $excluded_ids = self::get_excluded_post_ids( $options );
     37
    3638        foreach ( $items as $item ) {
     39            if ( ! empty( $excluded_ids ) && isset( $item['post_id'] ) && in_array( $item['post_id'], $excluded_ids, true ) ) {
     40                continue;
     41            }
     42
    3743            if ( ! empty( $disallowed ) && self::is_path_disallowed( $item['path'], $disallowed ) ) {
    3844                continue;
     
    518524    /**
    519525     * Parses custom disallowed paths from textarea input.
     526     * Supports both paths (starting with /) and post IDs (numeric).
    520527     *
    521528     * @param string $custom_paths Custom paths input.
     
    528535        foreach ( $lines as $line ) {
    529536            $line = trim( $line );
    530             if ( '' !== $line ) {
     537            if ( '' === $line ) {
     538                continue;
     539            }
     540
     541            if ( ctype_digit( $line ) ) {
     542                $post_id   = absint( $line );
     543                $permalink = get_permalink( $post_id );
     544                if ( $permalink ) {
     545                    $path = wp_parse_url( $permalink, PHP_URL_PATH );
     546                    if ( $path ) {
     547                        $paths[] = trailingslashit( $path );
     548                    }
     549                }
     550            } else {
    531551                $paths[] = $line;
    532552            }
     
    534554
    535555        return $paths;
     556    }
     557
     558    /**
     559     * Gets excluded post IDs from custom disallowed paths.
     560     *
     561     * @param array $options Plugin options.
     562     * @return array Array of excluded post IDs.
     563     */
     564    public static function get_excluded_post_ids( $options ) {
     565        $ids = array();
     566
     567        if ( empty( $options['custom_disallowed_paths'] ) ) {
     568            return $ids;
     569        }
     570
     571        $lines = explode( "\n", (string) $options['custom_disallowed_paths'] );
     572
     573        foreach ( $lines as $line ) {
     574            $line = trim( $line );
     575            if ( '' !== $line && ctype_digit( $line ) ) {
     576                $ids[] = absint( $line );
     577            }
     578        }
     579
     580        return array_unique( $ids );
    536581    }
    537582
     
    674719        $languages = array();
    675720
    676         // Polylang support.
    677721        if ( function_exists( 'pll_get_post_language' ) ) {
    678722            $current_lang = pll_get_post_language( $post_id, 'locale' );
     
    694738        }
    695739
    696         // WPML support.
    697740        if ( function_exists( 'apply_filters' ) && empty( $languages ) ) {
    698             // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Using WPML third-party hook.
     741            // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
    699742            $details = apply_filters( 'wpml_post_language_details', null, $post_id );
    700743            if ( is_array( $details ) && ! empty( $details['locale'] ) ) {
     
    703746        }
    704747
    705         // Fallback to site language.
    706748        if ( empty( $languages ) ) {
    707749            $site_lang = get_bloginfo( 'language' );
     
    721763     */
    722764    public static function get_canonical_url( $post_id ) {
    723         // Yoast SEO.
    724765        if ( class_exists( 'WPSEO_Meta' ) ) {
    725766            $canonical = WPSEO_Meta::get_value( 'canonical', $post_id );
     
    729770        }
    730771
    731         // Rank Math.
    732772        if ( class_exists( 'RankMath' ) ) {
    733773            $canonical = get_post_meta( $post_id, 'rank_math_canonical_url', true );
     
    741781
    742782    /**
    743      * Gets related URLs for a post (parent/children/pages/products/etc).
     783     * Gets related URLs for a post.
    744784     *
    745785     * @param int    $post_id   Post ID.
     
    750790        $related = array();
    751791
    752         // Pages: parent and children.
    753792        if ( 'page' === $post_type ) {
    754793            $parent_id = wp_get_post_parent_id( $post_id );
     
    770809        }
    771810
    772         // Posts: related by category.
    773811        if ( 'post' === $post_type ) {
    774812            $categories = wp_get_post_categories( $post_id, array( 'fields' => 'ids' ) );
     
    795833        }
    796834
    797         // WooCommerce products.
    798835        if ( 'product' === $post_type && function_exists( 'wc_get_related_products' ) ) {
    799836            $related_ids = wc_get_related_products( $post_id, 3 );
     
    815852     */
    816853    public static function detect_schema_type( $post_id, $post_type ) {
    817         // WooCommerce products.
    818854        if ( 'product' === $post_type || ( class_exists( 'WooCommerce' ) && 'product' === get_post_type( $post_id ) ) ) {
    819855            return 'Product';
    820856        }
    821857
    822         // Attachments/media.
    823858        if ( 'attachment' === $post_type ) {
    824859            $mime = get_post_mime_type( $post_id );
     
    832867        }
    833868
    834         // Pages.
    835869        if ( 'page' === $post_type ) {
    836870            $title_lower = strtolower( get_the_title( $post_id ) );
     
    851885        }
    852886
    853         // Posts.
    854887        if ( 'post' === $post_type ) {
    855888            return 'BlogPosting';
    856889        }
    857890
    858         // Events.
    859891        if ( 'event' === $post_type || 'tribe_events' === $post_type ) {
    860892            return 'Event';
  • coding-bunny-llms-generator/trunk/readme.txt

    r3467642 r3470017  
    55Tested up to: 6.9
    66Requires PHP: 8.0
    7 Stable tag: 1.2.0
     7Stable tag: 1.2.1
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    7373== Changelog ==
    7474
     75= 1.2.1: 2026-02-26 =
     76* Improved: Added the ability to exclude pages, posts, and products by ID
     77* Fix: Fixed a bug in Priority URLs ordering
     78
    7579= 1.2.0: 2026-02-23 =
    7680* New: Added "Additional content" field
Note: See TracChangeset for help on using the changeset viewer.