Changeset 3693501
- Timestamp:
- 09/13/2026 09:56:37 AM (6 weeks ago)
- Location:
- ailinkmaster-internal-auto-manager
- Files:
-
- 13 added
- 1 deleted
- 12 edited
-
assets/banner-1544x500.png (modified) ( previous)
-
assets/banner-772x250.png (modified) ( previous)
-
trunk/ailinkmaster-internal-auto-manager.php (modified) (2 diffs)
-
trunk/includes/class-ailm-link-repository.php (modified) (2 diffs)
-
trunk/includes/class-ailm-reconciler.php (modified) (1 diff)
-
trunk/includes/utils/class-ailm-text-processor.php (modified) (1 diff)
-
trunk/modules/404-scout/class-ailm-scout-background-processor.php (modified) (2 diffs)
-
trunk/modules/404-scout/class-ailm-scout-scanner.php (modified) (1 diff)
-
trunk/modules/gutenberg-sidebar/tests (deleted)
-
trunk/modules/llms-txt/assets (added)
-
trunk/modules/llms-txt/assets/admin-llmstxt.js (added)
-
trunk/modules/llms-txt/class-ailm-llmstxt-admin.php (modified) (5 diffs)
-
trunk/modules/llms-txt/class-ailm-llmstxt-ajax.php (added)
-
trunk/modules/llms-txt/class-ailm-llmstxt-data.php (added)
-
trunk/modules/llms-txt/class-ailm-llmstxt-file-writer.php (added)
-
trunk/modules/llms-txt/class-ailm-llmstxt-full-generator.php (added)
-
trunk/modules/llms-txt/class-ailm-llmstxt-generator.php (added)
-
trunk/modules/llms-txt/class-ailm-llmstxt-init.php (added)
-
trunk/modules/llms-txt/class-ailm-llmstxt-markdown.php (added)
-
trunk/modules/llms-txt/class-ailm-llmstxt-rebuilder.php (added)
-
trunk/modules/llms-txt/class-ailm-llmstxt-scanner.php (added)
-
trunk/modules/llms-txt/class-ailm-llmstxt-server.php (added)
-
trunk/modules/llms-txt/class-ailm-llmstxt-settings.php (added)
-
trunk/modules/orphan-rescue/class-ailm-orphan-admin.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (3 diffs)
-
trunk/uninstall.php (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
ailinkmaster-internal-auto-manager/trunk/ailinkmaster-internal-auto-manager.php
r3651068 r3693501 3 3 Plugin Name: LinkMesh: Semantic AI Internal Links for SEO 4 4 Description: Automatically create smart internal links with AI-generated anchor text. Features orphan page rescue, keyword intelligence, 404 link monitoring, and click analytics for maximum SEO impact. 5 Version: 34.1. 75 Version: 34.1.8 6 6 Requires at least: 6.2 7 7 Requires PHP: 7.4 … … 27 27 } 28 28 29 30 // Load the llms.txt module (self-contained: hooks, cron, serving). 31 require_once AILM_REFACTORED_PATH . 'modules/llms-txt/class-ailm-llmstxt-init.php'; 32 AILM_LlmsTxt_Init::init(); 29 33 30 34 // Load the core -
ailinkmaster-internal-auto-manager/trunk/includes/class-ailm-link-repository.php
r3624641 r3693501 229 229 array( 230 230 'numberposts' => -1, 231 'post_type' => 'post',231 'post_type' => get_option('ailm_allowed_post_types', array('post', 'page')), 232 232 'post_status' => 'publish', 233 233 'fields' => 'ids', … … 271 271 $edges = array(); 272 272 273 // Collect all nodes (all published posts + pages)273 // Collect all nodes (all published allowed post types) 274 274 $all_ids = get_posts( 275 275 array( 276 276 'numberposts' => -1, 277 'post_type' => array('post', 'page'),277 'post_type' => get_option('ailm_allowed_post_types', array('post', 'page')), 278 278 'post_status' => 'publish', 279 279 'fields' => 'ids', -
ailinkmaster-internal-auto-manager/trunk/includes/class-ailm-reconciler.php
r3624641 r3693501 90 90 if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) { 91 91 return; 92 } 93 94 // Skip non-content post types (e.g. WooCommerce shop_order/shop_coupon 95 // saves) — reconciliation is only meaningful for allowed content types. 96 if (null === $post) { 97 $post = get_post($post_id); 98 } 99 if ($post) { 100 $allowed_types = get_option('ailm_allowed_post_types', array('post', 'page')); 101 if (!in_array($post->post_type, (array) $allowed_types, true)) { 102 return; 103 } 92 104 } 93 105 -
ailinkmaster-internal-auto-manager/trunk/includes/utils/class-ailm-text-processor.php
r3624641 r3693501 17 17 $html = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $html ); 18 18 // Удаляем HTML-комментарии (разметка Gutenberg) 19 $html = preg_replace( ' //is', '', $html );19 $html = preg_replace( '@<!--.*?-->@s', '', $html ); 20 20 21 21 return $html; -
ailinkmaster-internal-auto-manager/trunk/modules/404-scout/class-ailm-scout-background-processor.php
r3624641 r3693501 45 45 46 46 global $wpdb; 47 // User-configurable list: sanitize + whitelist before it is turned 48 // into a placeholder list, so nothing user-supplied reaches the SQL. 49 $allowed_types = get_option('ailm_allowed_post_types', array('post', 'page')); 50 $allowed_types = array_values(array_unique(array_filter(array_map('sanitize_key', (array) $allowed_types)))); 51 if (empty($allowed_types)) { 52 $allowed_types = array('post'); 53 } 54 $type_placeholders = implode(',', array_fill(0, count($allowed_types), '%s')); 55 $posts_table = $wpdb->posts; // Core table name, never user input. 56 // The interpolations below are trusted: $posts_table is the core posts 57 // table and $type_placeholders is a fixed run of "%s" built from the 58 // sanitized slugs above — no user input reaches the query string. 47 59 // Quickly get total post count for progress bar 48 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 49 $total_posts = (int) $wpdb->get_var("SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_type IN ('post', 'page') AND post_status = 'publish'"); 60 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber -- see note above: core table + an equivalent number of %s placeholders 61 $total_posts = (int) $wpdb->get_var($wpdb->prepare( 62 "SELECT COUNT(ID) FROM {$posts_table} WHERE post_type IN ($type_placeholders) AND post_status = 'publish'", 63 $allowed_types 64 )); 65 // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber 50 66 51 67 if ($total_posts === 0) { … … 117 133 $batch_size = 100; 118 134 119 // Lightweight query: get 25 posts with ID greater than the last checked one 120 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 135 // Sanitized + whitelisted exactly as in start_scan(). 136 $allowed_types = get_option('ailm_allowed_post_types', array('post', 'page')); 137 $allowed_types = array_values(array_unique(array_filter(array_map('sanitize_key', (array) $allowed_types)))); 138 if (empty($allowed_types)) { 139 $allowed_types = array('post'); 140 } 141 $type_placeholders = implode(',', array_fill(0, count($allowed_types), '%s')); 142 $posts_table = $wpdb->posts; // Core table name, never user input. 143 // Lightweight query: get the next batch of posts after $last_id. The 144 // interpolations are trusted exactly as in start_scan(): core table + 145 // a fixed run of "%s" placeholders. Note the disable/enable form — the 146 // sniff reports the line of the $wpdb->prepare() call, not the query, 147 // so a per-line ignore on the string would leave a warning behind. 148 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber -- see note above: core table + an equivalent number of placeholders 121 149 $posts = $wpdb->get_results($wpdb->prepare( 122 "SELECT ID FROM {$ wpdb->posts} WHERE post_type IN ('post', 'page') AND post_status = 'publish' AND ID > %d ORDER BY ID ASC LIMIT %d",123 $last_id,124 $batch_size125 ));150 "SELECT ID FROM {$posts_table} WHERE post_type IN ($type_placeholders) AND post_status = 'publish' AND ID > %d ORDER BY ID ASC LIMIT %d", 151 array_merge($allowed_types, array($last_id, $batch_size)) 152 )); 153 // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber 126 154 127 155 if (empty($posts)) { -
ailinkmaster-internal-auto-manager/trunk/modules/404-scout/class-ailm-scout-scanner.php
r3624641 r3693501 31 31 $post = get_post($post_id); 32 32 if (!$post) { 33 return; 34 } 35 36 // Only scan content post types configured for the plugin. This keeps 37 // WooCommerce order/coupon saves (shop_order, shop_coupon, ...) from 38 // triggering a full link scan on every storefront transaction. 39 $allowed_types = get_option('ailm_allowed_post_types', array('post', 'page')); 40 if (!in_array($post->post_type, (array) $allowed_types, true)) { 33 41 return; 34 42 } -
ailinkmaster-internal-auto-manager/trunk/modules/llms-txt/class-ailm-llmstxt-admin.php
r3626135 r3693501 2 2 /** 3 3 * Admin UI for the llms.txt module. 4 * 5 * Preview-first workflow: Generate preview → review/edit the draft → 6 * Publish. Manual curation of posts via select2. 4 7 * 5 8 * @since 34.1.2 … … 15 18 16 19 /** 17 * Render the module's admin page (placeholder). 20 * Register asset enqueue on the proper hook. 21 * 22 * render_tab() runs during page body output, far too late for 23 * wp_enqueue_style (which prints in <head>). The assets must be 24 * hooked to admin_enqueue_scripts instead. 25 * 26 * @return void 27 */ 28 public static function init() { 29 // Priority 20: the main admin controller enqueues the plugin-wide 30 // select2 at priority 10, so this handler must run afterwards to be 31 // able to detect (and reuse) that handle instead of adding a second 32 // copy of select2 on the same page. 33 add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ), 20 ); 34 } 35 36 /** 37 * Render the module's admin page. 18 38 * 19 39 * @return void … … 23 43 return; 24 44 } 45 46 $settings = AILM_LlmsTxt_Settings::get_all(); 47 $public_types = get_post_types( array( 'public' => true ), 'objects' ); 48 // Attachment is technically public but never a content page. 49 unset( $public_types['attachment'] ); 50 51 $draft = AILM_LlmsTxt_Rebuilder::get_draft(); 25 52 ?> 26 <div class="ailm-wrap" style="margin-top: 24px;">53 <div class="ailm-wrap" id="ailm-llmstxt-wrap" style="margin-top: 24px;"> 27 54 28 55 <div class="ailm-header-bar"> … … 36 63 </div> 37 64 38 <div class="ailm-card" style="text-align:center; padding:60px 24px;"> 39 <span class="dashicons dashicons-hammer" style="font-size:48px; width:48px; height:48px; color:#86868b;"></span> 40 <h2 style="margin:16px 0 8px; font-size:22px; color:#1d1d1f;"> 41 <?php esc_html_e( 'Coming soon', 'ailinkmaster-internal-auto-manager' ); ?> 42 </h2> 43 <p class="ailm-description" style="font-size:14px; color:#86868b; margin:0;"> 44 <?php esc_html_e( 'llms.txt module', 'ailinkmaster-internal-auto-manager' ); ?> 65 <?php if ( empty( $settings['enabled'] ) ) : ?> 66 <div class="notice notice-warning" style="margin: 12px 0;"> 67 <p> 68 <?php esc_html_e( 'The module is currently disabled. Click "Generate preview" to enable it and build a draft, or tick "Generate and serve llms.txt" in the settings below and save.', 'ailinkmaster-internal-auto-manager' ); ?> 69 </p> 70 </div> 71 <?php endif; ?> 72 73 <div id="ailm-llmstxt-notice" class="notice is-dismissible" style="display:none; margin: 12px 0;"></div> 74 75 <div class="ailm-card" style="padding: 20px; margin-bottom: 16px;"> 76 <h3 style="margin-top:0;"><?php esc_html_e( 'Status', 'ailinkmaster-internal-auto-manager' ); ?></h3> 77 <table class="widefat striped" style="max-width: 720px;"> 78 <tbody> 79 <tr> 80 <td><strong><?php esc_html_e( 'Module enabled', 'ailinkmaster-internal-auto-manager' ); ?></strong></td> 81 <td id="ailm-llmstxt-status-enabled">—</td> 82 </tr> 83 <tr> 84 <td><strong><?php esc_html_e( 'llms.txt file (live)', 'ailinkmaster-internal-auto-manager' ); ?></strong></td> 85 <td><span id="ailm-llmstxt-status-file">—</span> <a href="<?php echo esc_url( home_url( '/llms.txt' ) ); ?>" target="_blank" rel="noopener noreferrer"><?php echo esc_html( home_url( '/llms.txt' ) ); ?></a></td> 86 </tr> 87 <tr> 88 <td><strong><?php esc_html_e( 'llms-full.txt file', 'ailinkmaster-internal-auto-manager' ); ?></strong></td> 89 <td><span id="ailm-llmstxt-status-full">—</span> <a href="<?php echo esc_url( home_url( '/llms-full.txt' ) ); ?>" target="_blank" rel="noopener noreferrer"><?php echo esc_html( home_url( '/llms-full.txt' ) ); ?></a></td> 90 </tr> 91 <tr> 92 <td><strong><?php esc_html_e( 'Site root writable', 'ailinkmaster-internal-auto-manager' ); ?></strong></td> 93 <td id="ailm-llmstxt-status-writable">—</td> 94 </tr> 95 <tr> 96 <td><strong><?php esc_html_e( 'Cached AI summaries', 'ailinkmaster-internal-auto-manager' ); ?></strong></td> 97 <td id="ailm-llmstxt-status-summaries">—</td> 98 </tr> 99 <tr> 100 <td><strong><?php esc_html_e( 'Last rebuild', 'ailinkmaster-internal-auto-manager' ); ?></strong></td> 101 <td id="ailm-llmstxt-status-last-run">—</td> 102 </tr> 103 <tr> 104 <td><strong><?php esc_html_e( 'Last summaries sync', 'ailinkmaster-internal-auto-manager' ); ?></strong></td> 105 <td id="ailm-llmstxt-status-last-sync">—</td> 106 </tr> 107 </tbody> 108 </table> 109 110 <p style="margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap;"> 111 <button type="button" class="button button-primary" id="ailm-llmstxt-rebuild"><?php esc_html_e( 'Generate preview', 'ailinkmaster-internal-auto-manager' ); ?></button> 112 <button type="button" class="button" id="ailm-llmstxt-validate"><?php esc_html_e( 'Validate live file', 'ailinkmaster-internal-auto-manager' ); ?></button> 113 <button type="button" class="button" id="ailm-llmstxt-sync"><?php esc_html_e( 'Sync AI descriptions', 'ailinkmaster-internal-auto-manager' ); ?></button> 45 114 </p> 115 </div> 116 117 <div class="ailm-card" style="padding: 20px; margin-bottom: 16px;"> 118 <h3 style="margin-top:0;"><?php esc_html_e( 'Draft preview', 'ailinkmaster-internal-auto-manager' ); ?></h3> 119 <p class="description"> 120 <?php esc_html_e( 'Review the generated draft below. You can edit it freely — the live file is only updated when you press "Publish".', 'ailinkmaster-internal-auto-manager' ); ?> 121 </p> 122 <textarea id="ailm-llmstxt-draft" class="large-text" rows="18" style="font-family: monospace;" spellcheck="false"><?php echo esc_textarea( $draft ); ?></textarea> 123 <p style="display:flex; gap:8px; flex-wrap:wrap;"> 124 <button type="button" class="button button-primary" id="ailm-llmstxt-publish"><?php esc_html_e( 'Publish', 'ailinkmaster-internal-auto-manager' ); ?></button> 125 <button type="button" class="button" id="ailm-llmstxt-regenerate"><?php esc_html_e( 'Regenerate (discard edits)', 'ailinkmaster-internal-auto-manager' ); ?></button> 126 </p> 127 </div> 128 129 <div class="ailm-card" style="padding: 20px;"> 130 <h3 style="margin-top:0;"><?php esc_html_e( 'Settings', 'ailinkmaster-internal-auto-manager' ); ?></h3> 131 132 <form id="ailm-llmstxt-settings-form"> 133 <table class="form-table" role="presentation"> 134 <tbody> 135 <tr> 136 <th scope="row"><?php esc_html_e( 'Enable module', 'ailinkmaster-internal-auto-manager' ); ?></th> 137 <td> 138 <label> 139 <input type="checkbox" id="ailm-llmstxt-enabled" value="1" <?php checked( ! empty( $settings['enabled'] ) ); ?> /> 140 <?php esc_html_e( 'Generate and serve llms.txt', 'ailinkmaster-internal-auto-manager' ); ?> 141 </label> 142 </td> 143 </tr> 144 <tr> 145 <th scope="row"><?php esc_html_e( 'Content mode', 'ailinkmaster-internal-auto-manager' ); ?></th> 146 <td> 147 <label style="display:block; margin-bottom:6px;"> 148 <input type="radio" name="ailm_llmstxt_curation_mode" value="auto" <?php checked( 'auto', $settings['curation_mode'] ); ?> /> 149 <?php esc_html_e( 'Automatic — top posts by AILM Rank (per post type)', 'ailinkmaster-internal-auto-manager' ); ?> 150 </label> 151 <label style="display:block;"> 152 <input type="radio" name="ailm_llmstxt_curation_mode" value="manual" <?php checked( 'manual', $settings['curation_mode'] ); ?> /> 153 <?php esc_html_e( 'Manual — I select the pages myself', 'ailinkmaster-internal-auto-manager' ); ?> 154 </label> 155 </td> 156 </tr> 157 <tr id="ailm-llmstxt-manual-row" style="<?php echo 'manual' === $settings['curation_mode'] ? '' : 'display:none;'; ?>"> 158 <th scope="row"><?php esc_html_e( 'Selected pages', 'ailinkmaster-internal-auto-manager' ); ?></th> 159 <td> 160 <select id="ailm-llmstxt-selected-posts" multiple="multiple" style="width: 100%; min-width: 320px;"> 161 <?php foreach ( self::get_picker_items( (array) $settings['selected_posts'] ) as $item ) : ?> 162 <option value="<?php echo esc_attr( $item['id'] ); ?>" selected="selected"><?php echo esc_html( $item['text'] ); ?></option> 163 <?php endforeach; ?> 164 </select> 165 <p style="margin-top:8px;"> 166 <button type="button" class="button" id="ailm-llmstxt-prefill"><?php esc_html_e( 'Prefill top by AILM Rank', 'ailinkmaster-internal-auto-manager' ); ?></button> 167 </p> 168 <p class="description"><?php esc_html_e( 'Search and pick pages; drag to reorder. The file lists them in this exact order.', 'ailinkmaster-internal-auto-manager' ); ?></p> 169 </td> 170 </tr> 171 <tr> 172 <th scope="row"><?php esc_html_e( 'Pinned pages (listed first)', 'ailinkmaster-internal-auto-manager' ); ?></th> 173 <td> 174 <select id="ailm-llmstxt-pinned-pages" multiple="multiple" style="width: 100%; min-width: 320px;"> 175 <?php foreach ( self::get_picker_items( (array) $settings['pinned_pages'] ) as $item ) : ?> 176 <option value="<?php echo esc_attr( $item['id'] ); ?>" selected="selected"><?php echo esc_html( $item['text'] ); ?></option> 177 <?php endforeach; ?> 178 </select> 179 <p class="description"><?php esc_html_e( 'Pages such as About or Contacts, listed first so LLMs read them before everything else. Works in both modes; up to 20 pages.', 'ailinkmaster-internal-auto-manager' ); ?></p> 180 <p style="margin-top:8px;"> 181 <label for="ailm-llmstxt-pinned-title"><?php esc_html_e( 'Section heading', 'ailinkmaster-internal-auto-manager' ); ?></label><br /> 182 <input type="text" class="regular-text" id="ailm-llmstxt-pinned-title" value="<?php echo esc_attr( $settings['pinned_title'] ); ?>" placeholder="<?php echo esc_attr__( 'Key Pages', 'ailinkmaster-internal-auto-manager' ); ?>" /> 183 </p> 184 <p class="description"><?php esc_html_e( 'These pages are not repeated in the sections below.', 'ailinkmaster-internal-auto-manager' ); ?></p> 185 </td> 186 </tr> 187 <tr> 188 <th scope="row"><?php esc_html_e( 'Post types', 'ailinkmaster-internal-auto-manager' ); ?></th> 189 <td> 190 <?php foreach ( $public_types as $type_obj ) : ?> 191 <label style="display:inline-block; margin-right:16px;"> 192 <input type="checkbox" name="ailm_llmstxt_post_types[]" value="<?php echo esc_attr( $type_obj->name ); ?>" <?php checked( in_array( $type_obj->name, (array) $settings['post_types'], true ) ); ?> /> 193 <?php echo esc_html( $type_obj->labels->name ); ?> 194 </label> 195 <?php endforeach; ?> 196 <p class="description"><?php esc_html_e( 'Used in Automatic mode and as the search scope in Manual mode.', 'ailinkmaster-internal-auto-manager' ); ?></p> 197 </td> 198 </tr> 199 <tr> 200 <th scope="row"><label for="ailm-llmstxt-description"><?php esc_html_e( 'Site description', 'ailinkmaster-internal-auto-manager' ); ?></label></th> 201 <td> 202 <input type="text" class="regular-text" id="ailm-llmstxt-description" value="<?php echo esc_attr( $settings['site_description'] ); ?>" /> 203 <p class="description"><?php esc_html_e( 'Used for the "> description" line. Falls back to the site tagline when empty.', 'ailinkmaster-internal-auto-manager' ); ?></p> 204 </td> 205 </tr> 206 <tr> 207 <th scope="row"><label for="ailm-llmstxt-exclude-ids"><?php esc_html_e( 'Exclude post IDs', 'ailinkmaster-internal-auto-manager' ); ?></label></th> 208 <td> 209 <input type="text" class="regular-text" id="ailm-llmstxt-exclude-ids" value="<?php echo esc_attr( implode( ', ', (array) $settings['exclude_ids'] ) ); ?>" /> 210 <p class="description"><?php esc_html_e( 'Comma-separated post IDs to exclude from both files (Automatic mode).', 'ailinkmaster-internal-auto-manager' ); ?></p> 211 </td> 212 </tr> 213 <tr> 214 <th scope="row"><label for="ailm-llmstxt-max-posts"><?php esc_html_e( 'Max posts', 'ailinkmaster-internal-auto-manager' ); ?></label></th> 215 <td> 216 <input type="number" min="1" max="500" step="1" class="small-text" id="ailm-llmstxt-max-posts" value="<?php echo esc_attr( (int) $settings['max_posts'] ); ?>" /> 217 <p class="description"><?php esc_html_e( 'Hard cap of 500 posts in v1 (Automatic mode).', 'ailinkmaster-internal-auto-manager' ); ?></p> 218 </td> 219 </tr> 220 <tr> 221 <th scope="row"><?php esc_html_e( 'llms-full.txt', 'ailinkmaster-internal-auto-manager' ); ?></th> 222 <td> 223 <label> 224 <input type="checkbox" id="ailm-llmstxt-generate-full" value="1" <?php checked( ! empty( $settings['generate_full'] ) ); ?> /> 225 <?php esc_html_e( 'Also generate llms-full.txt (full content in Markdown)', 'ailinkmaster-internal-auto-manager' ); ?> 226 </label> 227 <p class="description"><?php esc_html_e( 'Not recommended for sites with more than 500 posts.', 'ailinkmaster-internal-auto-manager' ); ?></p> 228 </td> 229 </tr> 230 <tr> 231 <th scope="row"><?php esc_html_e( 'Automatic rebuild', 'ailinkmaster-internal-auto-manager' ); ?></th> 232 <td> 233 <label> 234 <input type="checkbox" id="ailm-llmstxt-auto-rebuild" value="1" <?php checked( ! empty( $settings['auto_rebuild'] ) ); ?> /> 235 <?php esc_html_e( 'Refresh the draft automatically when posts change', 'ailinkmaster-internal-auto-manager' ); ?> 236 </label> 237 </td> 238 </tr> 239 <tr> 240 <th scope="row"><?php esc_html_e( 'Autopublish', 'ailinkmaster-internal-auto-manager' ); ?></th> 241 <td> 242 <label> 243 <input type="checkbox" id="ailm-llmstxt-autopublish" value="1" <?php checked( ! empty( $settings['autopublish'] ) ); ?> /> 244 <?php esc_html_e( 'Publish the live file automatically without manual review', 'ailinkmaster-internal-auto-manager' ); ?> 245 </label> 246 <p class="description"><?php esc_html_e( 'Off (recommended): automatic rebuilds only update the draft; you publish manually. On: every rebuild writes the live file directly.', 'ailinkmaster-internal-auto-manager' ); ?></p> 247 </td> 248 </tr> 249 <tr> 250 <th scope="row"><?php esc_html_e( 'AI descriptions', 'ailinkmaster-internal-auto-manager' ); ?></th> 251 <td> 252 <label> 253 <input type="checkbox" id="ailm-llmstxt-use-summaries" value="1" <?php checked( ! empty( $settings['use_summaries'] ) ); ?> /> 254 <?php esc_html_e( 'Use cached AI summaries (from the LinkMesh index) as descriptions', 'ailinkmaster-internal-auto-manager' ); ?> 255 </label> 256 <p class="description"><?php esc_html_e( 'Summaries are synced from the server once per day or via the button above. Falls back to post excerpts.', 'ailinkmaster-internal-auto-manager' ); ?></p> 257 </td> 258 </tr> 259 <tr> 260 <th scope="row"><label for="ailm-llmstxt-manual-sections"><?php esc_html_e( 'Manual sections', 'ailinkmaster-internal-auto-manager' ); ?></label></th> 261 <td> 262 <textarea class="large-text" rows="6" id="ailm-llmstxt-manual-sections"><?php echo esc_textarea( $settings['manual_sections'] ); ?></textarea> 263 <p class="description"><?php esc_html_e( 'Optional Markdown appended after the site description (e.g. custom H2 sections with links).', 'ailinkmaster-internal-auto-manager' ); ?></p> 264 </td> 265 </tr> 266 </tbody> 267 </table> 268 269 <p> 270 <button type="submit" class="button button-primary" id="ailm-llmstxt-save"><?php esc_html_e( 'Save settings', 'ailinkmaster-internal-auto-manager' ); ?></button> 271 </p> 272 </form> 46 273 </div> 47 274 … … 49 276 <?php 50 277 } 278 279 /** 280 * Enqueue the module's admin script + select2 (admin_enqueue_scripts). 281 * 282 * @param string $hook_suffix Current admin page hook suffix. 283 * @return void 284 */ 285 public static function enqueue_assets( $hook_suffix = '' ) { 286 // Only load on the module's own page (hook contains the plugin 287 // slug + '-llms-txt'). 288 if ( false === strpos( (string) $hook_suffix, 'llms-txt' ) ) { 289 return; 290 } 291 292 if ( ! current_user_can( 'manage_options' ) ) { 293 return; 294 } 295 296 $plugin_url = plugins_url( '', dirname( dirname( dirname( __FILE__ ) ) ) . '/ailinkmaster-internal-auto-manager.php' ); 297 298 // Reuse the plugin-wide select2 when it is already queued. Loading a 299 // second select2 build on the same page replaces $.fn.select2 and 300 // leaves the pickers without a working widget, so the bundled copy 301 // is only a fallback for pages where the shared handle is missing. 302 $select2_handle = 'select2'; 303 if ( ! wp_script_is( 'select2', 'enqueued' ) && ! wp_script_is( 'select2', 'registered' ) ) { 304 $select2_handle = 'ailm-select2'; 305 wp_enqueue_style( 'ailm-select2', $plugin_url . '/assets/select2.min.css', array(), '4.0.13' ); 306 wp_enqueue_script( 'ailm-select2', $plugin_url . '/assets/select2.min.js', array( 'jquery' ), '4.0.13', true ); 307 } 308 309 // Cache-bust on file mtime so an updated picker script is never 310 // served from the browser cache after a plugin update. 311 $script_path = dirname( __FILE__ ) . '/assets/admin-llmstxt.js'; 312 $script_ver = file_exists( $script_path ) ? (string) filemtime( $script_path ) : '34.1.8'; 313 314 wp_enqueue_script( 315 'ailm-llmstxt-admin', 316 plugins_url( 'assets/admin-llmstxt.js', __FILE__ ), 317 array( 'jquery', $select2_handle ), 318 $script_ver, 319 true 320 ); 321 322 // Pre-select the currently chosen posts in the select2 boxes. 323 $selected = self::get_picker_items( (array) AILM_LlmsTxt_Settings::get( 'selected_posts', array() ) ); 324 $pinned = self::get_picker_items( (array) AILM_LlmsTxt_Settings::get( 'pinned_pages', array() ) ); 325 326 wp_localize_script( 327 'ailm-llmstxt-admin', 328 'ailm_llmstxt', 329 array( 330 'nonce' => wp_create_nonce( AILM_LlmsTxt_Ajax::NONCE_ACTION ), 331 'ajaxurl' => admin_url( 'admin-ajax.php' ), 332 'selected' => $selected, 333 'pinned' => $pinned, 334 ) 335 ); 336 } 337 338 /** 339 * Map stored post IDs to select2 items ({id, text}). 340 * 341 * @param array $ids Post IDs in the stored order. 342 * @return array List of picker items. 343 */ 344 private static function get_picker_items( array $ids ) { 345 $items = array(); 346 347 foreach ( $ids as $pid ) { 348 $pid = absint( $pid ); 349 $post = $pid ? get_post( $pid ) : null; 350 351 if ( ! $post ) { 352 continue; 353 } 354 355 // select2 renders the text as plain text, and the label is 356 // stored as-is for display only (no HTML is emitted). 357 $items[] = array( 358 'id' => (int) $pid, 359 'text' => $post->post_title . ' (' . $post->post_type . ')', 360 ); 361 } 362 363 return $items; 364 } 51 365 } -
ailinkmaster-internal-auto-manager/trunk/modules/orphan-rescue/class-ailm-orphan-admin.php
r3624641 r3693501 40 40 $all_post_ids = get_posts(array( 41 41 'numberposts' => -1, 42 'post_type' => 'post',42 'post_type' => get_option('ailm_allowed_post_types', array('post', 'page')), 43 43 'post_status' => 'publish', 44 44 'fields' => 'ids', … … 331 331 global $wpdb; 332 332 333 // Only valid post type slugs may enter the SQL fragment below. The 334 // option is sanitized and whitelisted here, so the placeholder list 335 // can never carry user input into the query string. 336 $allowed_types = get_option('ailm_allowed_post_types', array('post', 'page')); 337 $allowed_types = array_values(array_unique(array_filter(array_map('sanitize_key', (array) $allowed_types)))); 338 if (empty($allowed_types)) { 339 $allowed_types = array('post'); 340 } 341 $type_placeholders = implode(',', array_fill(0, count($allowed_types), '%s')); 342 $posts_table = $wpdb->posts; // Core table name, never user input. 343 344 // The two interpolations below are trusted: $posts_table is the core 345 // posts table and $type_placeholders is a fixed run of "%s" built from 346 // the sanitized slugs above — no user input reaches the query string. 347 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber -- see note above: core table + an equivalent number of %s placeholders 333 348 $total_posts = 0; 334 349 if ($offset === 0) { 335 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 336 $total_posts = (int) $wpdb->get_var("SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_type = 'post'"); 337 } 338 339 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 350 $total_posts = (int) $wpdb->get_var($wpdb->prepare( 351 "SELECT COUNT(ID) FROM {$posts_table} WHERE post_status = 'publish' AND post_type IN ($type_placeholders)", 352 $allowed_types 353 )); 354 } 355 340 356 $posts = $wpdb->get_results($wpdb->prepare( 341 "SELECT ID, post_title, post_content FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_type = 'post' ORDER BY ID DESC LIMIT %d OFFSET %d", 342 $limit, 343 $offset 357 "SELECT ID, post_title, post_content FROM {$posts_table} WHERE post_status = 'publish' AND post_type IN ($type_placeholders) ORDER BY ID DESC LIMIT %d OFFSET %d", 358 array_merge($allowed_types, array($limit, $offset)) 344 359 )); 360 // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber 345 361 346 362 if (empty($posts)) { -
ailinkmaster-internal-auto-manager/trunk/readme.txt
r3651068 r3693501 4 4 Requires at least: 6.2 5 5 Requires PHP: 7.4 6 Tested up to: 7. 07 Stable tag: 34.1. 76 Tested up to: 7.1 7 Stable tag: 34.1.8 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 68 68 Find and fix pages with zero inbound links – invisible to both users and search engines. 69 69 70 ### LLMS.txt (AI Control) 71 Take charge of how ChatGPT, Claude, and other AI agents read and index your site's content. 72 70 73 ### 404 Scout 71 74 A background scanner that finds broken links before your visitors (or Google) do. … … 119 122 120 123 ### Q3 2026 (In Progress) 121 - llms.txt & Markdown Endpoint124 - DONE! llms.txt & Markdown Endpoint 122 125 - DONE! (Bulk URL Update on Site Restructure) 123 126 -
ailinkmaster-internal-auto-manager/trunk/uninstall.php
r3645483 r3693501 152 152 'ailm_url_rewrite_last_heartbeat', 153 153 'ailm_url_rewrite_batch_lock', 154 155 // --- llms.txt module (modules/llms-txt/...) --- 156 'ailm_llmstxt_settings', 157 'ailm_llmstxt_summaries', 158 'ailm_llmstxt_summaries_synced_at', 159 'ailm_llmstxt_status', 160 'ailm_llmstxt_file_llms', 161 'ailm_llmstxt_file_llms_full', 162 'ailm_llmstxt_draft', 154 163 ]; 155 164 … … 178 187 '_ailm_pagerank', 179 188 '_ailm_needs_reindex', 189 190 // --- llms.txt module (modules/llms-txt/...) --- 191 '_ailm_llmstxt_dirty', 192 '_ailm_llmstxt_md_hash', 193 '_ailm_llmstxt_md_body', 180 194 ]; 181 195 … … 228 242 // Auto-reindex cron (includes/class-ailm-core.php). 229 243 'ailm_auto_reindex_cron', 244 // llms.txt module crons (modules/llms-txt/class-ailm-llmstxt-rebuilder.php). 245 'ailm_llmstxt_rebuild_cron', 246 'ailm_llmstxt_daily_sync', 230 247 ]; 231 248 … … 233 250 wp_clear_scheduled_hook( $ailm_cron_hook ); 234 251 } 252 253 /* 254 * =========================================================================== 255 * 6. PHYSICAL FILES (llms.txt module) 256 * =========================================================================== 257 * 258 * Static files written to the site root by the llms.txt module. Removed on 259 * uninstall so no stale llms.txt / llms-full.txt is left behind. 260 */ 261 $ailm_llmstxt_home = function_exists( 'get_home_path' ) ? get_home_path() : ABSPATH; 262 foreach ( array( 'llms.txt', 'llms-full.txt' ) as $ailm_llmstxt_file ) { 263 $ailm_llmstxt_path = wp_normalize_path( $ailm_llmstxt_home . $ailm_llmstxt_file ); 264 if ( is_file( $ailm_llmstxt_path ) ) { 265 wp_delete_file( $ailm_llmstxt_path ); 266 } 267 } 268 269 foreach ( $ailm_cron_hooks as $ailm_cron_hook ) { 270 wp_clear_scheduled_hook( $ailm_cron_hook ); 271 }
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)