' . "\n";
}
// Speakable schema on singular pages (only if explicitly enabled).
// Speakable is a beta Google feature for topical news queries on Google Assistant.
if ( is_singular() && ! empty( $settings['speakable_enabled'] ) ) {
$this->render_speakable_schema();
}
// Meta description fallback on singular pages.
if ( is_singular() ) {
$this->maybe_render_meta_description();
}
}
/**
* Output meta description when no SEO plugin handles it.
* Uses AI-generated description stored in _asgm_meta_description.
*/
private function maybe_render_meta_description() {
// Skip if Yoast or Rank Math already handles meta descriptions.
if ( class_exists( 'WPSEO_Meta' ) || class_exists( 'RankMath' ) || class_exists( 'JEsuspended\All_in_One_SEO_Pack' ) ) {
return;
}
$desc = get_post_meta( get_the_ID(), '_asgm_meta_description', true );
if ( ! empty( $desc ) ) {
echo '' . "\n";
}
}
/**
* Render speakable schema markup.
*/
private function render_speakable_schema() {
$settings = get_option( 'asgm_settings', array() );
$selectors = ! empty( $settings['speakable_selectors'] )
? $settings['speakable_selectors']
: get_option( 'asgm_speakable_selectors', array( '.entry-content h2', '.entry-content p:first-of-type' ) );
$schema = array(
'@context' => 'https://schema.org',
'@type' => 'WebPage',
'name' => get_the_title(),
'url' => get_permalink(),
'speakable' => array(
'@type' => 'SpeakableSpecification',
'cssSelector' => $selectors,
),
);
$json = wp_json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP );
if ( $json ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- JSON_HEX_TAG | JSON_HEX_AMP encode <, >, & as unicode sequences preventing script breakout.
echo '\n";
}
}
}