diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php index 89e4a05e5a..7414bca7cc 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php @@ -152,9 +152,124 @@ protected static function plugin_json_jd_schema( $plugin ) { $schema[] = $software_application; + // FAQPage schema. + $content = Plugin_Directory::instance()->split_post_content_into_pages( get_the_content( $plugin ) ); + $faq_content = isset( $content['faq'] ) ? $content['faq'] : ''; + $faq_schema = self::build_plugin_faq_schema( $faq_content, get_permalink( $plugin ) ); + + if ( $faq_schema ) { + $schema[] = $faq_schema; + } + return $schema; } + /** + * Builds an FAQPage schema object from plugin content structured with
. + * + * @param string $faq_content Raw plugin content containing FAQ markup. + * @param string $plugin_url The URL of the plugin page. + * @return array|null FAQPage schema or null if none found. + */ + protected static function build_plugin_faq_schema( $faq_content, $plugin_url ) { + if ( empty( $faq_content ) || false === strpos( $faq_content, 'loadHTML( $faq_content ); + + libxml_use_internal_errors( $document_internal_errors ); + + $faq_entities = []; + $dts = $document->getElementsByTagName( 'dt' ); + + if ( 0 === $dts->length ) { + return null; + } + + foreach ( $dts as $dt ) { + $question = sanitize_text_field( $dt->textContent ); + + // Find the next
sibling. + $dd = $dt->nextSibling; + while ( $dd && 'dd' !== $dd->nodeName ) { + $dd = $dd->nextSibling; + } + if ( ! $dd ) { + continue; + } + + // Collect and sanitize answer HTML. + $answer_html = ''; + foreach ( $dd->childNodes as $child ) { + $answer_html .= $dd->ownerDocument->saveHTML( $child ); + } + + $faq_entities[] = [ + "@type" => "Question", + "name" => $question, + "acceptedAnswer" => [ + "@type" => "Answer", + "text" => self::sanitize_faq_answer_html( $answer_html ), + ], + ]; + } + + if ( ! $faq_entities ) { + return null; + } + + return [ + "@context" => "https://schema.org", + "@type" => "FAQPage", + "@id" => $plugin_url, + "url" => $plugin_url, + "mainEntity" => $faq_entities, + ]; + } + + /** + * Sanitizes FAQ answer HTML for use in FAQPage schema. + * + * Allows only tags supported by Google rich results: + *

-

,

,

,