| @@ -152,13 +152,20 @@ | ||
| 152 | 152 | global $wpdb; |
| 153 | 153 | |
| 154 | 154 | $charset_collate = $wpdb->get_charset_collate(); |
| 155 | 155 | |
| 156 | + // keyword_hash exists purely to make the lookup indexable: `keyword` is | |
| 157 | + // TEXT and is matched with BINARY (for collation safety), which no index | |
| 158 | + // can serve — so every front-end search used to full-scan this table. | |
| 159 | + // The hash narrows to a single row via the index; the BINARY comparison | |
| 160 | + // still decides the match, so behaviour is unchanged. | |
| 156 | 161 | $search_keyword_table = $wpdb->prefix . 'betterdocs_search_keyword'; |
| 157 | 162 | $search_keyword = "CREATE TABLE $search_keyword_table ( |
| 158 | 163 | id bigint NOT NULL AUTO_INCREMENT, |
| 159 | 164 | keyword text NOT NULL, |
| 160 | - PRIMARY KEY (id) | |
| 165 | + keyword_hash char(32) NOT NULL DEFAULT '', | |
| 166 | + PRIMARY KEY (id), | |
| 167 | + KEY keyword_hash (keyword_hash) | |
| 161 | 168 | ) {$charset_collate};"; |
| 162 | 169 | |
| 163 | 170 | $search_log_table = $wpdb->prefix . 'betterdocs_search_log'; |
| 164 | 171 | $search_log = "CREATE TABLE $search_log_table ( |
| @@ -168,9 +175,10 @@ | ||
| 168 | 175 | not_found_count mediumint(9) NULL, |
| 169 | 176 | created_at date DEFAULT '0000-00-00' NOT NULL, |
| 170 | 177 | PRIMARY KEY (id), |
| 171 | 178 | KEY keyword_id (keyword_id), |
| 172 | - KEY created_at (created_at) | |
| 179 | + KEY created_at (created_at), | |
| 180 | + KEY keyword_created (keyword_id, created_at) | |
| 173 | 181 | ) {$charset_collate};"; |
| 174 | 182 | |
| 175 | 183 | $_analytics_table = $wpdb->prefix . 'betterdocs_analytics'; |
| 176 | 184 | $analytics_table = "CREATE TABLE $_analytics_table ( |
| @@ -188,11 +196,15 @@ | ||
| 188 | 196 | KEY unique_visit (unique_visit), |
| 189 | 197 | KEY happy (happy), |
| 190 | 198 | KEY sad (sad), |
| 191 | 199 | KEY normal (normal), |
| 192 | - KEY created_at (created_at) | |
| 200 | + KEY created_at (created_at), | |
| 201 | + UNIQUE KEY post_created (post_id, created_at) | |
| 193 | 202 | ) {$charset_collate};"; |
| 194 | 203 | |
| 204 | + // The betterdocs_api_specs table moved to Pro (API Documentation is a | |
| 205 | + // Pro-only feature; Pro's Install creates it). | |
| 206 | + | |
| 195 | 207 | require_once ABSPATH . 'wp-admin/includes/upgrade.php'; |
| 196 | 208 | dbDelta( $search_keyword . $search_log . $analytics_table ); |
| 197 | 209 | |
| 198 | 210 | // Update DB Version |
| @@ -199,11 +211,46 @@ | ||
| 199 | 211 | update_option( 'betterdocs_db_version', $_db_code_version ); |
| 200 | 212 | } |
| 201 | 213 | |
| 202 | 214 | public function migrate_customizer() { |
| 203 | - $search_layout = get_theme_mod( 'betterdocs_search_layout_select' ); | |
| 204 | - if ( empty( $search_layout ) ) { | |
| 205 | - set_theme_mod( 'betterdocs_search_layout_select', 'layout-1' ); | |
| 215 | + /** | |
| 216 | + * Seed the layout Customizer settings for installs that never explicitly | |
| 217 | + * picked a layout. Some consumers read the raw theme_mod without a fallback | |
| 218 | + * (e.g. FrontEnd::article_reactions()), so an unseeded mod can render | |
| 219 | + * differently from the layout the front-end otherwise defaults to. | |
| 220 | + * | |
| 221 | + * Two rules keep this safe and correct: | |
| 222 | + * 1. Only seed a mod that has never been set ( get_theme_mod() === false ). | |
| 223 | + * An explicit choice — including an explicit 'layout-1' — is never | |
| 224 | + * overwritten, so existing sites cannot change layout on update. | |
| 225 | + * 2. Pull the value from Customizer\Defaults (the single source of truth | |
| 226 | + * the front-end already renders from) instead of a hardcoded string, so | |
| 227 | + * it can never drift from the real default again. Pro defaults are merged | |
| 228 | + * in automatically when BetterDocs Pro is active. | |
| 229 | + */ | |
| 230 | + $defaults = betterdocs()->customizer->defaults->defaults(); | |
| 231 | + $layout_mods = [ | |
| 232 | + 'betterdocs_search_layout_select', | |
| 233 | + 'betterdocs_docs_layout_select', | |
| 234 | + 'betterdocs_single_layout_select', | |
| 235 | + 'betterdocs_archive_layout_select', | |
| 236 | + 'betterdocs_select_faq_template', | |
| 237 | + 'betterdocs_multikb_layout_select', // BetterDocs Pro. | |
| 238 | + 'betterdocs_select_faq_template_mkb', // BetterDocs Pro. | |
| 239 | + ]; | |
| 240 | + | |
| 241 | + foreach ( $layout_mods as $mod ) { | |
| 242 | + // Respect an explicit user choice (including an explicit 'layout-1'). | |
| 243 | + if ( get_theme_mod( $mod, false ) !== false ) { | |
| 244 | + continue; | |
| 245 | + } | |
| 246 | + | |
| 247 | + // Only seed when a real default is registered (skips inactive add-ons). | |
| 248 | + if ( empty( $defaults[ $mod ] ) ) { | |
| 249 | + continue; | |
| 250 | + } | |
| 251 | + | |
| 252 | + set_theme_mod( $mod, $defaults[ $mod ] ); | |
| 206 | 253 | } |
| 207 | 254 | |
| 208 | 255 | $search_heading = get_theme_mod( 'betterdocs_live_search_heading_switch' ); |
| 209 | 256 | if ( empty( $search_heading ) ) { |
| @@ -209,45 +256,9 @@ | ||
| 209 | 256 | if ( empty( $search_heading ) ) { |
| 210 | 257 | set_theme_mod( 'betterdocs_live_search_heading_switch', false ); |
| 211 | 258 | } |
| 212 | 259 | |
| 213 | - $docs_layout = get_theme_mod( 'betterdocs_docs_layout_select' ); | |
| 214 | - if ( empty( $docs_layout ) ) { | |
| 215 | - set_theme_mod( 'betterdocs_docs_layout_select', 'layout-1' ); | |
| 216 | - } | |
| 217 | - | |
| 218 | - $single_layout = get_theme_mod( 'betterdocs_single_layout_select' ); | |
| 219 | - if ( empty( $single_layout ) ) { | |
| 220 | - set_theme_mod( 'betterdocs_single_layout_select', 'layout-1' ); | |
| 221 | - } | |
| 222 | - | |
| 223 | - $category_archive_layout = get_theme_mod( 'betterdocs_archive_layout_select' ); | |
| 224 | - if ( empty( $category_archive_layout ) ) { | |
| 225 | - set_theme_mod( 'betterdocs_archive_layout_select', 'layout-1' ); | |
| 226 | - } | |
| 227 | - | |
| 228 | - $search_layout_select = get_theme_mod( 'betterdocs_search_layout_select' ); | |
| 229 | - if ( empty( $search_layout_select ) ) { | |
| 230 | - set_theme_mod( 'betterdocs_search_layout_select', 'layout-1' ); | |
| 231 | - } | |
| 232 | - | |
| 233 | - $docs_faq = get_theme_mod( 'betterdocs_select_faq_template' ); | |
| 234 | - if ( empty( $docs_faq ) ) { | |
| 235 | - set_theme_mod( 'betterdocs_select_faq_template', 'layout-1' ); | |
| 236 | - } | |
| 237 | - | |
| 238 | 260 | if ( get_option( 'betterdocs_settings' ) && betterdocs()->settings->get( 'enable_estimated_reading_time' ) == false ) { |
| 239 | 261 | betterdocs()->settings->save( 'enable_estimated_reading_time', false ); |
| 240 | - } | |
| 241 | - | |
| 242 | - if ( function_exists( 'betterdocs_pro' ) ) { | |
| 243 | - $mkb_layout = get_theme_mod( 'betterdocs_multikb_layout_select' ); | |
| 244 | - if ( empty( $mkb_layout ) ) { | |
| 245 | - set_theme_mod( 'betterdocs_multikb_layout_select', 'layout-1' ); | |
| 246 | - } | |
| 247 | - $mkb_faq = get_theme_mod( 'betterdocs_select_faq_template_mkb' ); | |
| 248 | - if ( empty( $mkb_faq ) ) { | |
| 249 | - set_theme_mod( 'betterdocs_select_faq_template_mkb', 'layout-1' ); | |
| 250 | - } | |
| 251 | 262 | } |
| 252 | 263 | } |
| 253 | 264 | } |