post_author ); if ( is_string( $author ) && '' !== $author ) { $lines[] = 'author: ' . self::scalar( $author ); } } if ( self::on( 'fm_tags' ) ) { $tags = self::post_tags( $post ); if ( ! empty( $tags ) ) { $lines[] = 'tags: ' . self::flow_list( $tags ); } } if ( self::on( 'fm_canonical' ) ) { $canonical = self::canonical_url( $post ); if ( '' !== $canonical ) { $lines[] = 'canonical: ' . esc_url_raw( $canonical ); } } // Custom fields / ACF. foreach ( self::custom_field_lines( $post ) as $line ) { $lines[] = $line; } return self::wrap( $lines ); } /** * Build the YAML front matter for a taxonomy term archive. * * @param WP_Term $term Term. * @return string */ public static function build_for_term( WP_Term $term ) { $lines = array(); if ( self::on( 'fm_title' ) ) { $lines[] = 'title: ' . self::scalar( $term->name ); } if ( self::on( 'fm_url' ) ) { $link = get_term_link( $term ); if ( ! is_wp_error( $link ) ) { $lines[] = 'url: ' . esc_url_raw( $link ); } } $lines[] = 'type: ' . self::scalar( $term->taxonomy ); if ( '' !== (string) $term->description ) { $desc = self::one_line( wp_strip_all_tags( $term->description ) ); $lines[] = 'description: ' . self::scalar( $desc ); } return self::wrap( $lines ); } /* --------------------------------------------------------------------- */ /** * Whether a front-matter field toggle is on. * * @param string $key Setting key. * @return bool */ private static function on( $key ) { return (bool) ADG_AIM_Plugin::get_setting( $key, true ); } /** * Wrap the collected lines into a YAML block. * * @param array $lines Lines. * @return string */ private static function wrap( array $lines ) { if ( empty( $lines ) ) { return ''; } return "---\n" . implode( "\n", $lines ) . "\n---\n\n"; } /** * Post tag names. * * @param WP_Post $post Post. * @return string[] */ private static function post_tags( WP_Post $post ) { if ( ! is_object_in_taxonomy( $post->post_type, 'post_tag' ) ) { return array(); } $terms = get_the_terms( $post, 'post_tag' ); if ( ! is_array( $terms ) ) { return array(); } $out = array(); foreach ( $terms as $t ) { $out[] = $t->name; } return $out; } /** * Canonical URL: SEO plugin value if present, otherwise the permalink. * * @param WP_Post $post Post. * @return string */ private static function canonical_url( WP_Post $post ) { $yoast = get_post_meta( $post->ID, '_yoast_wpseo_canonical', true ); if ( ! empty( $yoast ) ) { return (string) $yoast; } $rank = get_post_meta( $post->ID, 'rank_math_canonical_url', true ); if ( ! empty( $rank ) ) { return (string) $rank; } return (string) get_permalink( $post ); } /** * Build YAML lines for the user-selected custom fields / ACF keys. * * @param WP_Post $post Post. * @return string[] */ private static function custom_field_lines( WP_Post $post ) { $keys = self::parse_keys( ADG_AIM_Plugin::get_setting( 'meta_keys', '' ) ); if ( empty( $keys ) ) { return array(); } $lines = array(); foreach ( $keys as $key ) { $value = null; // Prefer ACF's formatted value when ACF is active. if ( function_exists( 'get_field' ) ) { $value = get_field( $key, $post->ID ); } if ( null === $value || false === $value || '' === $value || array() === $value ) { $value = get_post_meta( $post->ID, $key, true ); } $value = self::stringify( $value ); if ( '' === $value ) { continue; } $yaml_key = self::yaml_key( $key ); if ( '' === $yaml_key ) { continue; } $lines[] = $yaml_key . ': ' . self::scalar( $value ); } return $lines; } /** * Turn a mixed field value into a single scalar string (or '' to skip). * * @param mixed $value Value. * @return string */ private static function stringify( $value ) { if ( is_scalar( $value ) ) { $str = (string) $value; } elseif ( is_array( $value ) ) { $flat = array(); foreach ( $value as $item ) { if ( is_scalar( $item ) ) { $flat[] = (string) $item; } } $str = implode( ', ', $flat ); } else { return ''; } $str = self::one_line( $str ); if ( function_exists( 'mb_strlen' ) && mb_strlen( $str ) > self::MAX_FIELD_LEN ) { $str = mb_substr( $str, 0, self::MAX_FIELD_LEN ) . '…'; } return $str; } /** * Parse a comma/newline separated list of field keys. * * @param string $raw Raw setting. * @return string[] */ private static function parse_keys( $raw ) { $parts = preg_split( '/[\r\n,]+/', (string) $raw ); $out = array(); foreach ( (array) $parts as $p ) { $p = trim( $p ); if ( '' === $p || 0 === strpos( $p, '_adg' ) ) { continue; } $out[ $p ] = $p; } return array_values( $out ); } /** * Sanitize a field key into a safe YAML key. * * @param string $key Key. * @return string */ private static function yaml_key( $key ) { $key = preg_replace( '/[^A-Za-z0-9_]+/', '_', (string) $key ); return trim( strtolower( $key ), '_' ); } /** * Quote a string as a double-quoted YAML scalar. * * @param string $value Value. * @return string */ private static function scalar( $value ) { $value = self::one_line( (string) $value ); $value = str_replace( array( '\\', '"' ), array( '\\\\', '\\"' ), $value ); return '"' . $value . '"'; } /** * Render a YAML flow sequence: [ "a", "b" ]. * * @param string[] $items Items. * @return string */ private static function flow_list( array $items ) { $parts = array(); foreach ( $items as $item ) { $parts[] = self::scalar( $item ); } return '[' . implode( ', ', $parts ) . ']'; } /** * Collapse whitespace to a single line. * * @param string $text Text. * @return string */ private static function one_line( $text ) { return trim( preg_replace( '/\s+/u', ' ', (string) $text ) ); } }