fictioneer/includes/functions/_module-seo.php
Tetrakern 4b41dc825c Add helper to get chapter story ID
This would be useful if I ever decide to use the parent ID field instead if meta.
2024-10-26 02:03:23 +02:00

804 lines
27 KiB
PHP

<?php
// =============================================================================
// ADD SEO METABOX
// =============================================================================
/**
* Adds a SEO metabox to selected post types
*
* @since 4.0.0
*
* @param string $post_type Post type.
* @param WP_Post $post Post object.
*/
function fictioneer_add_seo_metabox( $post_type, $post ) {
// Setup
$for_these_types = ['post', 'page', 'fcn_story', 'fcn_chapter', 'fcn_collection', 'fcn_recommendation'];
$not_these_slugs = ['singular-bookshelf.php', 'singular-bookmarks.php', 'user-profile.php'];
// Add meta box
if ( in_array( $post_type, $for_these_types ) && ! in_array( get_page_template_slug( $post ), $not_these_slugs ) ) {
add_meta_box(
'fictioneer-search-engine-appearance',
__( 'SEO & Meta Tags', 'fictioneer' ),
'fictioneer_seo_fields',
null,
'normal',
'high'
);
}
}
if (
get_option( 'fictioneer_enable_seo' ) &&
! fictioneer_seo_plugin_active() &&
current_user_can( 'fcn_seo_meta' )
) {
add_action( 'add_meta_boxes', 'fictioneer_add_seo_metabox', 10, 2 );
}
// =============================================================================
// SEO METABOX HTML
// =============================================================================
if ( ! function_exists( 'fictioneer_seo_fields' ) ) {
/**
* Output HTML for SEO metabox
*
* @since 4.0.0
* @since 5.9.4 - Refactored meta fields.
*
* @param object $post The post object.
*/
function fictioneer_seo_fields( $post ) {
// Data
$seo_fields = get_post_meta( $post->ID, 'fictioneer_seo_fields', true );
$seo_fields = is_array( $seo_fields ) ? $seo_fields : array( 'title' => '', 'description' => '', 'og_image_id' => 0 );
// Title
$seo_title = $seo_fields['title'] ?? '';
$seo_title_placeholder = fictioneer_get_safe_title( $post->ID, 'seo-title-placeholder' );
// Description (truncated if necessary)
$seo_description = $seo_fields['description'] ?? '';
$seo_description_placeholder = wp_strip_all_tags( get_the_excerpt( $post ), true );
$seo_description_placeholder = fictioneer_truncate( $seo_description_placeholder, 155 );
if ( get_option( 'page_on_front' ) == $post->ID ) {
$seo_description_placeholder = get_bloginfo( 'description' );
}
// Open Graph image...
$seo_og_image = $seo_fields['og_image_id'] ?? 0;
$seo_og_image_display = wp_get_attachment_url( $seo_og_image );
$image_source = '';
// ... if not set, look for thumbnail...
if ( ! $seo_og_image_display && has_post_thumbnail( $post->ID ) ) {
$seo_og_image_display = get_the_post_thumbnail_url( $post->ID );
$image_source = 'thumbnail';
}
// ... if not found, look for parent thumbnail...
if ( ! $seo_og_image_display && $post->post_type === 'fcn_chapter' ) {
$story_id = fictioneer_get_chapter_story_id( $post->ID );
if ( $story_id && has_post_thumbnail( $story_id ) ) {
$seo_og_image_display = get_the_post_thumbnail_url( $story_id );
$image_source = 'parent-thumbnail';
}
}
// ... if not found either, try the default OG image...
if ( ! $seo_og_image_display && get_theme_mod( 'og_image' ) ) {
$seo_og_image_display = wp_get_attachment_url( get_theme_mod( 'og_image' ) );
$image_source = 'default';
}
// ... okay, use a placeholder
if ( ! $seo_og_image_display ) {
$seo_og_image_display = get_template_directory_uri() . '/img/no_image_placeholder.svg';
$image_source = 'placeholder';
}
// Start HTML ---> ?>
<div class="fictioneer-metabox">
<input type="hidden" name="fictioneer_metabox_seo_nonce" value="<?php echo wp_create_nonce( 'fictioneer-metabox-seo-save' ); ?>">
<p class="description"><?php _ex( 'Metadata for search engine results, schema graphs, and social media embeds. If left blank, defaults will be derived from the content. You can use <code>{{title}}</code>, <code>{{site}}</code>, and <code>{{excerpt}}</code> as placeholders. Whether these services actually display the offered data is entirely up to them.', 'Do not translate {{title}}, {{site}}, and {{excerpt}}.', 'fictioneer' ); ?></p>
<div class="fictioneer-metabox__left">
<div class="fictioneer-metabox__row">
<label for="fictioneer-seo-title">
<span><?php _e( 'Title', 'fictioneer' ); ?></span>
<span id="fictioneer-seo-title-chars" class="counter"></span>
</label>
<input id="fictioneer-seo-title" type="text" name="fictioneer_seo_title" value="<?php echo esc_attr( $seo_title ); ?>" placeholder="<?php echo esc_attr( $seo_title_placeholder ); ?>">
</div>
<div class="fictioneer-metabox__row">
<label for="fictioneer-seo-description">
<span><?php _e( 'Description', 'fictioneer' ); ?></span>
</label>
<textarea id="fictioneer-seo-description" name="fictioneer_seo_description" rows="3" placeholder="<?php echo esc_attr( $seo_description_placeholder ); ?>"><?php echo $seo_description; ?></textarea>
</div>
</div>
<div class="fictioneer-metabox__right">
<input type="hidden" id="fictioneer-seo-og-image" name="fictioneer_seo_og_image" value="<?php echo $seo_og_image; ?>">
<a href="#" id="fictioneer-button-seo-og-image-remove" class="og-remove <?php echo $seo_og_image ? '' : 'hidden'; ?>"><?php _e( 'Remove', 'fictioneer' ); ?></a>
<div class="og-source <?php echo $image_source; ?>">
<div class="thumbnail"><?php _e( 'Source: Thumbnail', 'fictioneer' ); ?></div>
<div class="parent-thumbnail"><?php _e( 'Source: Parent', 'fictioneer' ); ?></div>
<div class="default"><?php _e( 'Source: Site Default', 'fictioneer' ); ?></div>
</div>
<a href="#" id="fictioneer-button-og-upload">
<img id="fictioneer-seo-og-display" src="<?php echo esc_url( $seo_og_image_display ); ?>" data-placeholder="<?php echo esc_url( get_template_directory_uri() . '/img/no_image_placeholder.svg' ); ?>">
<div class="og-upload-label"><?php _e( 'Open Graph Image', 'fictioneer' ); ?></div>
</a>
</div>
</div>
<?php // <--- End HTML
}
}
// =============================================================================
// SAVE SEO METABOX
// =============================================================================
/**
* Save SEO metabox data
*
* @since 4.0.0
* @since 5.9.4 - Refactored meta fields.
*
* @param int $post_id The post ID.
*/
function fictioneer_save_seo_metabox( $post_id ) {
// Always purge meta cache
delete_post_meta( $post_id, 'fictioneer_seo_cache' );
// Validations
if (
! current_user_can( 'fcn_seo_meta' ) ||
! isset( $_POST['fictioneer_metabox_seo_nonce'] ) ||
! wp_verify_nonce( $_POST['fictioneer_metabox_seo_nonce'], 'fictioneer-metabox-seo-save' ) ||
! current_user_can( 'edit_post', $post_id ) ||
wp_is_post_autosave( $post_id ) ||
wp_is_post_revision( $post_id ) ||
in_array( get_post_status( $post_id ), ['auto-draft'] )
) {
return;
}
// Prepare data
$seo_data = array( 'title' => '', 'description' => '', 'og_image' => 0 );
// Get image
if ( isset( $_POST['fictioneer_seo_og_image'] ) ) {
$seo_data['og_image'] = absint( $_POST['fictioneer_seo_og_image'] );
}
// Get title
if ( isset( $_POST['fictioneer_seo_title'] ) ) {
$seo_data['title'] = sanitize_text_field( $_POST['fictioneer_seo_title'] );
}
// Get description
if ( isset( $_POST['fictioneer_seo_description'] ) ) {
$seo_data['description'] = sanitize_text_field( $_POST['fictioneer_seo_description'] );
}
// Let empty fields get deleted
if ( empty( $seo_data['og_image'] ) && empty( $seo_data['title'] ) && empty( $seo_data['description'] ) ) {
$seo_data = null;
}
// Save fields
fictioneer_update_post_meta( $post_id, 'fictioneer_seo_fields', $seo_data );
// Purge meta cache
delete_post_meta( $post_id, 'fictioneer_seo_cache' );
}
if ( get_option( 'fictioneer_enable_seo' ) && ! fictioneer_seo_plugin_active() ) {
add_action( 'save_post', 'fictioneer_save_seo_metabox' );
}
// =============================================================================
// GET SEO TITLE
// =============================================================================
if ( ! function_exists( 'fictioneer_get_seo_title' ) ) {
/**
* Get SEO title
*
* @since 4.0.0
* @since 5.9.4 - Refactored meta caching.
*
* @param int $post_id Optional. The post ID.
* @param array $args Optional. Array of arguments.
*
* @return string The SEO title.
*/
function fictioneer_get_seo_title( $post_id = null, $args = [] ) {
// Setup
$post_id = $post_id ? $post_id : get_queried_object_id();
$skip_cache = $args['skip_cache'] ?? false;
$default = ! empty( trim( $args['default'] ?? '' ) ) ? $args['default'] : false;
// Search?
if ( is_search() ) {
return esc_html(
sprintf(
_x( 'Search Results', 'SEO search results title.', 'fictioneer' ),
get_bloginfo( 'name' )
)
);
}
// Author?
if ( is_author() ) {
$author_id = get_queried_object_id();
$author = get_userdata( $author_id );
if ( $author ) {
return esc_html(
sprintf(
_x( 'Author: %s', 'SEO author page title.', 'fictioneer' ),
$author->display_name
)
);
} else {
return esc_html( _x( 'Author', 'SEO fallback title for author pages.', 'fictioneer' ) );
}
}
// Archive?
if ( is_archive() ) {
// Category?
if ( is_category() ) {
return esc_html(
sprintf(
_x( 'Category: %s', 'SEO post category title.', 'fictioneer' ),
single_cat_title( '', false )
)
);
}
// Tag?
if ( is_tag() ) {
return esc_html(
sprintf(
_x( 'Tag: %s', 'SEO post tag title.', 'fictioneer' ),
single_tag_title( '', false )
)
);
}
// Character?
if ( is_tax( 'fcn_character' ) ) {
return esc_html(
sprintf(
_x( 'Character: %s', 'SEO character taxonomy title.', 'fictioneer' ),
single_tag_title( '', false )
)
);
}
// Fandom?
if ( is_tax( 'fcn_fandom' ) ) {
return esc_html(
sprintf(
_x( 'Fandom: %s', 'SEO fandom taxonomy title.', 'fictioneer' ),
single_tag_title( '', false )
)
);
}
// Genre?
if ( is_tax( 'fcn_genre' ) ) {
return esc_html(
sprintf(
_x( 'Genre: %s', 'SEO genre taxonomy title.', 'fictioneer' ),
single_tag_title( '', false )
)
);
}
// Generic archive?
return esc_html( _x( 'Archive', 'SEO fallback archive title.', 'fictioneer' ) );
}
// Meta cache for title (purged on update)?
if ( ! $skip_cache ) {
$meta_cache = get_post_meta( $post_id, 'fictioneer_seo_cache', true );
if ( $meta_cache && is_array( $meta_cache ) && ! empty( $meta_cache['title'] ?? '' ) ) {
return $meta_cache['title'];
}
}
// Start building...
$seo_fields = get_post_meta( $post_id, 'fictioneer_seo_fields', true );
$seo_fields = is_array( $seo_fields ) ? $seo_fields : array( 'title' => '', 'description' => '', 'og_image_id' => 0 );
$seo_title = $seo_fields['title'] ?? '';
$title = fictioneer_get_safe_title( $post_id, 'seo-title' );
$default = empty( $default ) ? $title : $default;
// Special Case: Frontpage
if ( is_front_page() ) {
$default = empty( $default ) ? get_bloginfo( 'name' ) : $default;
}
// Placeholder: {{title}}
if ( str_contains( $seo_title, '{{title}}' ) ) {
$seo_title = str_replace( '{{title}}', $title, $seo_title );
}
// Placeholder: {{site}}
if ( str_contains( $seo_title, '{{site}}' ) ) {
$seo_title = str_replace( '{{site}}', get_bloginfo( 'name' ), $seo_title );
}
// Defaults...
if ( ! empty( $default ) && empty( $seo_title ) ) {
$seo_title = $default;
}
// Catch empty
if ( empty( $seo_title ) ) {
$seo_title = get_bloginfo( 'name' );
}
// Finalize
$seo_title = esc_html( trim( wp_strip_all_tags( $seo_title ) ) );
// Update meta cache
if ( ! $skip_cache ) {
// Ensure data structure
if ( ! is_array( $meta_cache ) ) {
$meta_cache = array( 'title' => '', 'description' => '', 'og_image' => [] );
}
$meta_cache['title'] = $seo_title;
update_post_meta( $post_id, 'fictioneer_seo_cache', $meta_cache );
}
// Done
return $seo_title;
}
}
// =============================================================================
// GET SEO DESCRIPTION
// =============================================================================
if ( ! function_exists( 'fictioneer_get_seo_description' ) ) {
/**
* Get SEO description
*
* @since 4.0.0
* @since 5.9.4 - Refactored meta caching.
*
* @param int $post_id Optional. The post ID.
* @param array $args Optional. Array of arguments.
*
* @return string The SEO description.
*/
function fictioneer_get_seo_description( $post_id = null, $args = [] ) {
// Setup
$post_id = $post_id ? $post_id : get_queried_object_id();
$skip_cache = $args['skip_cache'] ?? false;
$default = trim( $args['default'] ?? '' );
// Special Case: Frontpage
if ( ( is_front_page() || ! $post_id ) && empty( $default ) ) {
$default = get_bloginfo( 'description' );
}
// Search?
if ( is_search() ) {
return esc_html(
sprintf(
_x( 'Search results on %s.', 'Search page SEO description.', 'fictioneer' ),
get_bloginfo( 'name' )
)
);
}
// Author?
if ( is_author() ) {
$author_id = get_queried_object_id();
$author = get_userdata( $author_id );
if ( $author && ! empty( $author->user_description ) ) {
return esc_html( $author->user_description );
} else {
return esc_html(
sprintf(
_x( 'Author on %s.', 'Fallback SEO description for author pages.', 'fictioneer' ),
get_bloginfo( 'name' )
)
);
}
}
// Archive?
if ( is_archive() ) {
// Category?
if ( is_category() ) {
return esc_html(
sprintf(
__( 'Archive of all posts in the %s category.', 'fictioneer' ),
single_cat_title( '', false )
)
);
}
// Tag?
if ( is_tag() ) {
return esc_html(
sprintf(
__( 'Archive of all posts with the %s tag.', 'fictioneer' ),
single_tag_title( '', false )
)
);
}
// Character?
if ( is_tax( 'fcn_character' ) ) {
return esc_html(
sprintf(
__( 'Archive of all posts with the character %s.', 'fictioneer' ),
single_tag_title( '', false )
)
);
}
// Fandom?
if ( is_tax( 'fcn_fandom' ) ) {
return esc_html(
sprintf(
__( 'Archive of all posts in the %s fandom.', 'fictioneer' ),
single_tag_title( '', false )
)
);
}
// Genre?
if ( is_tax( 'fcn_genre' ) ) {
return esc_html(
sprintf(
__( 'Archive of all posts with the %s genre.', 'fictioneer' ),
single_tag_title( '', false )
)
);
}
// Generic archive?
return esc_html( sprintf( __( 'Archived posts on %s.', 'fictioneer' ), get_bloginfo( 'name' ) ) );
}
// Meta cache for description (purged on update)?
if ( ! $skip_cache ) {
$meta_cache = get_post_meta( $post_id, 'fictioneer_seo_cache', true );
if ( $meta_cache && is_array( $meta_cache ) && ! empty( $meta_cache['description'] ?? '' ) ) {
return $meta_cache['description'];
}
}
// Start building...
$seo_fields = get_post_meta( $post_id, 'fictioneer_seo_fields', true );
$seo_fields = is_array( $seo_fields ) ? $seo_fields : array( 'title' => '', 'description' => '', 'og_image_id' => 0 );
$seo_description = $seo_fields['description'] ?? '';
$title = fictioneer_get_safe_title( $post_id, 'seo-title-in_description' );
$excerpt = wp_strip_all_tags( get_the_excerpt( $post_id ), true );
$excerpt = fictioneer_truncate( $excerpt, 155 );
$default = empty( $default ) ? $excerpt : $default;
// Special Case: Recommendations
if ( get_post_type( $post_id ) == 'fcn_recommendation' ) {
$one_sentence = get_post_meta( $post_id, 'fictioneer_recommendation_one_sentence', true );
$one_sentence = trim( $one_sentence );
$default = empty( $one_sentence ) ? $default : $one_sentence;
}
// Placeholder: {{excerpt}}
if ( str_contains( $seo_description, '{{excerpt}}' ) ) {
$seo_description = str_replace( '{{excerpt}}', $excerpt ?? '', $seo_description );
}
// Placeholder: {{title}}
if ( str_contains( $seo_description, '{{title}}' ) ) {
$seo_description = str_replace( '{{title}}', $title, $seo_description );
}
// Placeholder: {{site}}
if ( str_contains( $seo_description, '{{site}}' ) ) {
$seo_description = str_replace( '{{site}}', get_bloginfo( 'name' ), $seo_description );
}
// Defaults...
if ( ! empty( $default ) && empty( $seo_description ) ) {
$seo_description = $default;
}
// Catch empty
if ( empty( $seo_description ) ) {
$seo_description = get_bloginfo( 'description' );
}
// Finalize
$seo_description = esc_html( trim( wp_strip_all_tags( $seo_description ) ) );
// Update meta cache
if ( ! $skip_cache ) {
// Ensure data structure
if ( ! is_array( $meta_cache ) ) {
$meta_cache = array( 'title' => '', 'description' => '', 'og_image' => [] );
}
$meta_cache['description'] = $seo_description;
update_post_meta( $post_id, 'fictioneer_seo_cache', $meta_cache );
}
// Done
return $seo_description;
}
}
// =============================================================================
// GET SEO IMAGE
// =============================================================================
if ( ! function_exists( 'fictioneer_get_seo_image' ) ) {
/**
* Get SEO image data array
*
* @since 4.0.0
* @since 5.9.4 - Refactored meta caching.
*
* @param int $post_id Optional. The post ID.
*
* @return array|null Data of the image or null if none has been found.
*/
function fictioneer_get_seo_image( $post_id = null ) {
// Setup
$post_id = $post_id ? $post_id : get_queried_object_id();
$use_default = is_archive() || is_search() || is_author();
$default_id = get_theme_mod( 'og_image' );
$image_id = false;
$image = null;
$meta_cache = null;
// Page with site default OG image?
if ( $use_default ) {
// Get default ID from settings
$image_id = $default_id;
}
// Meta cache for image (purged on update)? Except for site default, which can globally change!
if ( ! $use_default ) {
$meta_cache = get_post_meta( $post_id, 'fictioneer_seo_cache', true );
if (
$meta_cache &&
is_array( $meta_cache ) &&
is_array( $meta_cache['og_image'] ?? 0 ) &&
! empty( $meta_cache['og_image'] ?? [] )
) {
return $meta_cache['og_image'];
}
}
// Get image ID if not yet set
if ( ! $image_id ) {
$seo_fields = get_post_meta( $post_id, 'fictioneer_seo_fields', true );
$seo_fields = is_array( $seo_fields ) ? $seo_fields : array( 'title' => '', 'description' => '', 'og_image_id' => 0 );
$image_id = $seo_fields['og_image_id'] ?? '';
$image_id = wp_attachment_is_image( $image_id ) ? $image_id : false;
}
// No image found...
if ( ! $image_id ) {
// Try thumbnail...
if ( has_post_thumbnail( $post_id ) ) {
$image_id = get_post_thumbnail_id( $post_id );
}
// Try story thumbnail if chapter...
if ( ! $image_id && get_post_type( $post_id ) == 'fcn_chapter' ) {
$story_id = fictioneer_get_chapter_story_id( $post_id );
$image_id = has_post_thumbnail( $story_id ) ? get_post_thumbnail_id( $story_id ) : $image_id;
}
// Try default if still nothing...
if ( ! $image_id && get_theme_mod( 'og_image' ) ) {
// Get default ID from settings
$image_id = $default_id;
}
}
// If an image has been found...
if ( $image_id ) {
$meta = wp_get_attachment_metadata( $image_id );
if ( $meta ) {
$image = array(
'url' => wp_get_attachment_url( $image_id ),
'height' => $meta['height'],
'width' => $meta['width'],
'type' => get_post_mime_type( $image_id ),
'id' => $image_id
);
}
}
// Update meta cache (if image has been found)
if ( $image ) {
// Ensure data structure
if ( ! is_array( $meta_cache ) ) {
$meta_cache = array( 'title' => '', 'description' => '', 'og_image' => [] );
}
$meta_cache['og_image'] = $image;
update_post_meta( $post_id, 'fictioneer_seo_cache', $meta_cache );
}
// Done
return $image;
}
}
// =============================================================================
// OUTPUT HEAD SEO
// =============================================================================
/**
* Output HTML <head> SEO meta
*
* @since 5.0.0
*/
function fictioneer_output_head_seo() {
global $wp;
global $post;
// Setup
$post_id = get_queried_object_id(); // In archives and search, this is the first post
$post_type = get_post_type(); // In archives and search, this is the first post
$post_author = $post->post_author ?? 0;
$chapter_story_id = fictioneer_get_chapter_story_id( $post_id );
$canonical_url = wp_get_canonical_url(); // Only on singular pages
// Flags
$is_page = is_page() || is_front_page() || is_home();
$is_aggregated = is_archive() || is_search();
$is_article = ! $is_page && in_array( $post_type, ['fcn_story', 'fcn_chapter', 'fcn_recommendation', 'post'] );
$show_author = $is_article && is_single() && ! is_front_page() && ! is_home() && ! $is_aggregated;
// Derived
$og_type = $is_article ? 'article' : 'website';
$og_image = fictioneer_get_seo_image( $post_id );
$og_title = fictioneer_get_seo_title( $post_id );
$og_description = fictioneer_get_seo_description( $post_id );
$article_author = $post_author && $show_author ? get_the_author_meta( 'display_name', $post_author ) : false;
$article_twitter = $post_author && $show_author ? get_the_author_meta( 'twitter', $post_author ) : false;
// Special Case: Recommendation author
if ( $post_author && $show_author && $post_type == 'fcn_recommendation' ) {
$article_author = get_post_meta( $post_id, 'fictioneer_recommendation_author', true ) ?? $article_author;
}
// Special Case: Archives
if ( is_archive() ) {
$canonical_url = home_url( $wp->request );
}
// Special Case: Search
if ( is_search() ) {
$canonical_url = add_query_arg( 's', '', home_url( $wp->request ) );
}
// Start HTML ---> ?>
<link rel="canonical" href="<?php echo $canonical_url; ?>">
<meta name="description" content="<?php echo $og_description; ?>">
<meta property="og:locale" content="<?php echo get_locale(); ?>">
<meta property="og:type" content="<?php echo $og_type; ?>">
<meta property="og:title" content="<?php echo $og_title; ?>">
<meta property="og:description" content="<?php echo $og_description; ?>">
<meta property="og:url" content="<?php echo $canonical_url; ?>">
<meta property="og:site_name" content="<?php echo get_bloginfo( 'name' ); ?>">
<?php if ( ! $is_aggregated && $is_article ) : ?>
<meta property="article:published_time" content="<?php echo get_the_date( 'c' ); ?>">
<meta property="article:modified_time" content="<?php echo get_the_modified_date( 'c' ); ?>">
<?php if ( $show_author && $article_author ) : ?>
<?php
// Get URL for primary author (if any)
$article_author_url = get_the_author_meta( 'url', $post_author );
$article_author_url = empty( $article_author_url ) ? get_author_posts_url( $post_author ) : $article_author_url;
// Prepare author array with either URL (required) or name (better than nothing)
$all_authors = empty( $article_author_url ) ? [ $article_author ] : [ $article_author_url ];
// Get co-authors (if any)
$co_authors = $post_type == 'fcn_story' ?
get_post_meta( $post_id, 'fictioneer_story_co_authors', true ) :
get_post_meta( $post_id, 'fictioneer_chapter_co_authors', true );
// Add co-authors URL or name
if ( is_array( $co_authors ) && ! empty( $co_authors ) ) {
foreach ( $co_authors as $co_author_id ) {
$co_author_name = get_the_author_meta( 'display_name', $co_author_id );
$co_author_url = get_the_author_meta( 'url', $co_author_id );
$co_author_url = empty( $co_author_url ) ? get_author_posts_url( $co_author_id ) : $co_author_url;
$author_item = empty( $co_author_url ) ? $co_author_name : $co_author_url;
if ( ! empty( $author_item ) && ! in_array( $author_item, $all_authors ) ) {
$all_authors[] = $author_item;
}
}
}
// Output og array
foreach ( $all_authors as $author_name ) {
echo "<meta property='article:author' content='$author_name'>";
}
?>
<?php endif; ?>
<?php if ( $post_type == 'fcn_chapter' && ! empty( $chapter_story_id ) ) : ?>
<meta property="article:section" content="<?php echo get_the_title( $chapter_story_id ); ?>">
<?php endif; ?>
<?php endif; ?>
<?php if ( is_array( $og_image ) && count( $og_image ) >= 4 ) : ?>
<meta property="og:image" content="<?php echo $og_image['url']; ?>">
<meta property="og:image:width" content="<?php echo $og_image['width']; ?>">
<meta property="og:image:height" content="<?php echo $og_image['height']; ?>">
<meta property="og:image:type" content="<?php echo $og_image['type']; ?>">
<meta name="twitter:image" content="<?php echo $og_image['url']; ?>">
<?php endif; ?>
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="<?php echo $og_title; ?>">
<meta name="twitter:description" content="<?php echo $og_description; ?>">
<?php if ( $show_author && $article_twitter ) : ?>
<meta name="twitter:creator" content="<?php echo '@' . $article_twitter; ?>">
<meta name="twitter:site" content="<?php echo '@' . $article_twitter; ?>">
<?php endif; ?>
<?php // <--- End HTML
}
if ( get_option( 'fictioneer_enable_seo' ) && ! fictioneer_seo_plugin_active() ) {
remove_action( 'wp_head', 'rel_canonical' ); // Re-added in the new action
add_action( 'wp_head', 'fictioneer_output_head_seo', 5 );
}