← All changes
|
includes/frontend/class-global-seo-schema-output.php
+67
-9
2.3.0
→
2.9.0
View file →
| @@ -104,8 +104,22 @@ | ||
| 104 | 104 | } |
| 105 | 105 | |
| 106 | 106 | $settings = $this->get_global_seo_settings($post_type); |
| 107 | 107 | if (($settings['schema_type'] ?? '') === 'Product') { |
| 108 | + // ...but only if this class is actually going to emit it. The | |
| 109 | + // per-content-type Schema switch (#660) makes | |
| 110 | + // output_global_seo_schema() return before it builds anything, so | |
| 111 | + // claiming the entity here as well left the page with NO product | |
| 112 | + // structured data at all — strictly worse than the duplicate this | |
| 113 | + // method exists to prevent, and the opposite of what the docblock | |
| 114 | + // above promises for "ThinkRank's product schema disabled". | |
| 115 | + if (!\ThinkRank\SEO\Content_Type_Settings::is_enabled_for_current( | |
| 116 | + \ThinkRank\SEO\Content_Type_Settings::FEATURE_SCHEMA, | |
| 117 | + true | |
| 118 | + )) { | |
| 119 | + return $markup; | |
| 120 | + } | |
| 121 | + | |
| 108 | 122 | return []; |
| 109 | 123 | } |
| 110 | 124 | |
| 111 | 125 | // A per-post DEPLOYED Product schema duplicates WooCommerce's markup |
| @@ -153,8 +167,17 @@ | ||
| 153 | 167 | * @since 1.0.0 |
| 154 | 168 | * @return void |
| 155 | 169 | */ |
| 156 | 170 | public function output_global_seo_schema(): void { |
| 171 | + // Per-content-type schema switch. 'inherit' (the default) keeps schema | |
| 172 | + // on, exactly as before the matrix existed (#660). | |
| 173 | + if (!\ThinkRank\SEO\Content_Type_Settings::is_enabled_for_current( | |
| 174 | + \ThinkRank\SEO\Content_Type_Settings::FEATURE_SCHEMA, | |
| 175 | + true | |
| 176 | + )) { | |
| 177 | + return; | |
| 178 | + } | |
| 179 | + | |
| 157 | 180 | // Archives get a CollectionPage schema instead of the per-post-type one |
| 158 | 181 | if (!is_singular()) { |
| 159 | 182 | $this->output_archive_schema(); |
| 160 | 183 | return; |
| @@ -327,8 +350,16 @@ | ||
| 327 | 350 | * @param string $post_type Post type slug. |
| 328 | 351 | * @return bool True when structured data would be output for this post type. |
| 329 | 352 | */ |
| 330 | 353 | public function would_output_schema(string $post_type): bool { |
| 354 | + if (!\ThinkRank\SEO\Content_Type_Settings::is_enabled( | |
| 355 | + \ThinkRank\SEO\Content_Type_Settings::FEATURE_SCHEMA, | |
| 356 | + $post_type, | |
| 357 | + true | |
| 358 | + )) { | |
| 359 | + return false; | |
| 360 | + } | |
| 361 | + | |
| 331 | 362 | $settings = $this->get_global_seo_settings($post_type); |
| 332 | 363 | |
| 333 | 364 | return !empty($settings['schema_type']); |
| 334 | 365 | } |
| @@ -492,10 +523,11 @@ | ||
| 492 | 523 | 'datePublished' => get_the_date('c', $post), |
| 493 | 524 | 'dateModified' => get_the_modified_date('c', $post), |
| 494 | 525 | ]; |
| 495 | 526 | |
| 496 | - // Add description | |
| 497 | - $excerpt = get_the_excerpt($post); | |
| 527 | + // Add description. A Bricks page's stored `post_content` is not on the | |
| 528 | + // page, so core's derived excerpt must not describe it (#651). | |
| 529 | + $excerpt = $this->post_excerpt_text($post); | |
| 498 | 530 | if (!empty($excerpt)) { |
| 499 | 531 | $schema['description'] = wp_strip_all_tags($excerpt); |
| 500 | 532 | } |
| 501 | 533 | |
| @@ -566,9 +598,9 @@ | ||
| 566 | 598 | } |
| 567 | 599 | require_once $builder_file; |
| 568 | 600 | } |
| 569 | 601 | |
| 570 | - $excerpt = get_the_excerpt($post); | |
| 602 | + $excerpt = $this->post_excerpt_text($post); | |
| 571 | 603 | |
| 572 | 604 | $builder = new \ThinkRank\SEO\Schema_Builder(); |
| 573 | 605 | $schema = $builder->build_schema( |
| 574 | 606 | 'FAQPage', |
| @@ -573,9 +605,9 @@ | ||
| 573 | 605 | $schema = $builder->build_schema( |
| 574 | 606 | 'FAQPage', |
| 575 | 607 | [ |
| 576 | 608 | 'title' => get_the_title($post), |
| 577 | - 'content' => $post->post_content, | |
| 609 | + 'content' => \ThinkRank\SEO\Builder_Content::visible_content($post), | |
| 578 | 610 | 'excerpt' => $excerpt ? wp_strip_all_tags($excerpt) : '', |
| 579 | 611 | 'url' => get_permalink($post), |
| 580 | 612 | ], |
| 581 | 613 | get_post_type($post) === 'page' ? 'page' : 'post' |
| @@ -646,9 +678,9 @@ | ||
| 646 | 678 | 'dateModified' => get_the_modified_date('c', $post), |
| 647 | 679 | ]; |
| 648 | 680 | |
| 649 | 681 | // Add description |
| 650 | - $excerpt = get_the_excerpt($post); | |
| 682 | + $excerpt = $this->post_excerpt_text($post); | |
| 651 | 683 | if (!empty($excerpt)) { |
| 652 | 684 | $schema['description'] = wp_strip_all_tags($excerpt); |
| 653 | 685 | } |
| 654 | 686 | |
| @@ -716,9 +748,9 @@ | ||
| 716 | 748 | 'url' => get_permalink($post), |
| 717 | 749 | ]; |
| 718 | 750 | |
| 719 | 751 | // Add description |
| 720 | - $description = get_the_excerpt($post); | |
| 752 | + $description = $this->post_excerpt_text($post); | |
| 721 | 753 | if (empty($description)) { |
| 722 | 754 | $caption = wp_get_attachment_caption($post->ID); |
| 723 | 755 | if (!empty($caption)) { |
| 724 | 756 | $description = $caption; |
| @@ -882,15 +914,20 @@ | ||
| 882 | 914 | private function get_product_description(\WP_Post $post): string { |
| 883 | 915 | // Try custom meta field first |
| 884 | 916 | $description = get_post_meta($post->ID, '_thinkrank_product_description', true); |
| 885 | 917 | |
| 886 | - // Fallback to excerpt or content | |
| 918 | + // Fallback to excerpt or content. On a Bricks page the excerpt core | |
| 919 | + // derives comes from discarded `post_content`, so the visible body is | |
| 920 | + // used instead (#651). | |
| 887 | 921 | if (empty($description)) { |
| 888 | - $description = get_the_excerpt($post); | |
| 922 | + $description = $this->post_excerpt_text($post); | |
| 889 | 923 | } |
| 890 | 924 | |
| 891 | 925 | if (empty($description)) { |
| 892 | - $description = \ThinkRank\SEO\Pattern_Resolver::derive_excerpt((string) $post->post_content, 30); | |
| 926 | + $description = \ThinkRank\SEO\Pattern_Resolver::derive_excerpt( | |
| 927 | + \ThinkRank\SEO\Builder_Content::visible_content($post), | |
| 928 | + 30 | |
| 929 | + ); | |
| 893 | 930 | } |
| 894 | 931 | |
| 895 | 932 | return wp_strip_all_tags($description); |
| 896 | 933 | } |
| @@ -1154,8 +1191,29 @@ | ||
| 1154 | 1191 | } |
| 1155 | 1192 | } |
| 1156 | 1193 | |
| 1157 | 1194 | return $reviews; |
| 1195 | + } | |
| 1196 | + | |
| 1197 | + /** | |
| 1198 | + * The post's excerpt, taken from content the page actually renders. | |
| 1199 | + * | |
| 1200 | + * `get_the_excerpt()` falls back to trimming `post_content`, which a Bricks | |
| 1201 | + * page discards — so on one of those it describes text no visitor sees. A | |
| 1202 | + * hand-written excerpt is the author's own summary and still wins, because | |
| 1203 | + * `superseding_excerpt_source()` yields nothing for a post that has one | |
| 1204 | + * (#651). | |
| 1205 | + * | |
| 1206 | + * @since 2.3.1 | |
| 1207 | + * @param \WP_Post $post Post being described. | |
| 1208 | + * @return string | |
| 1209 | + */ | |
| 1210 | + private function post_excerpt_text(\WP_Post $post): string { | |
| 1211 | + $superseding = \ThinkRank\SEO\Builder_Content::superseding_excerpt_source($post); | |
| 1212 | + | |
| 1213 | + return '' !== $superseding | |
| 1214 | + ? \ThinkRank\SEO\Pattern_Resolver::derive_excerpt($superseding, 30) | |
| 1215 | + : (string) get_the_excerpt($post); | |
| 1158 | 1216 | } |
| 1159 | 1217 | |
| 1160 | 1218 | /** |
| 1161 | 1219 | * Register generated schema with the request's schema graph. |