*/ /* ------------------------------------------------------------------ */ public function output_schema() { if ( ! is_singular() ) return; $post_id = get_the_ID(); if ( ! $post_id ) return; if ( ! SFAQ_Plugin::has_faqs( $post_id ) ) return; // Resolve schema enabled $per_post = get_post_meta( $post_id, '_sfaq_schema_enabled', true ); if ( $per_post === '0' ) return; if ( $per_post === '' && ! SFAQ_Plugin::get_setting( 'schema_enabled', '1' ) ) return; $items = SFAQ_Plugin::get_faq_items( $post_id ); if ( empty( $items ) ) return; $use_custom = SFAQ_Plugin::get_setting( 'use_custom_schema', '' ); $json = $use_custom ? $this->build_custom_schema( $items, $post_id ) : $this->build_standard_schema( $items, $post_id ); if ( $json ) { // JSON-LD requires raw output inside ' . "\n"; } } /* ------------------------------------------------------------------ */ /* Standard FAQPage schema */ /* ------------------------------------------------------------------ */ private function build_standard_schema( $items, $post_id ) { $entities = []; foreach ( $items as $item ) { $entities[] = [ '@type' => 'Question', 'name' => wp_strip_all_tags( $item['question'] ), 'acceptedAnswer' => [ '@type' => 'Answer', 'text' => wp_strip_all_tags( $item['answer'] ), ], ]; } $schema = [ '@context' => 'https://schema.org', '@type' => 'FAQPage', 'mainEntity' => $entities, ]; return wp_json_encode( $schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT ); } /* ------------------------------------------------------------------ */ /* Custom schema template with placeholders */ /* ------------------------------------------------------------------ */ private function build_custom_schema( $items, $post_id ) { $template = SFAQ_Plugin::get_setting( 'custom_schema', '' ); if ( ! $template ) { return $this->build_standard_schema( $items, $post_id ); } // Build {{faq_block}} — array of Question objects as JSON fragment $entities = []; foreach ( $items as $item ) { $entities[] = [ '@type' => 'Question', 'name' => wp_strip_all_tags( $item['question'] ), 'acceptedAnswer' => [ '@type' => 'Answer', 'text' => wp_strip_all_tags( $item['answer'] ), ], ]; } $faq_block = wp_json_encode( $entities, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); // For single-FAQ placeholders, use the first FAQ. // JSON-escape each string value so special characters can't break the JSON structure. $first_q = wp_strip_all_tags( $items[0]['question'] ?? '' ); $first_a = wp_strip_all_tags( $items[0]['answer'] ?? '' ); $json_esc = function( $val ) { // wp_json_encode wraps in quotes; strip them to get a safe bare string for insertion. $encoded = wp_json_encode( $val, JSON_UNESCAPED_UNICODE ); return substr( $encoded, 1, -1 ); }; $replacements = [ '{{question}}' => $json_esc( $first_q ), '{{answer}}' => $json_esc( $first_a ), '{{page_url}}' => esc_url( get_permalink( $post_id ) ), '{{page_title}}' => $json_esc( get_the_title( $post_id ) ), '{{faq_block}}' => $faq_block, ]; return str_replace( array_keys( $replacements ), array_values( $replacements ), $template ); } }