/home/fdhrevqn/www/wp-content/plugins.disabled/co-authors-plus/php/integrations/yoast.php
<?php
/**
 * The main file for the Yoast integration
 */

namespace CoAuthors\Integrations;

use CoAuthors\Integrations\Yoast\CoAuthor;
use Yoast\WP\SEO\Config\Schema_Types;
use Yoast\WP\SEO\Context\Meta_Tags_Context;
use Yoast\WP\SEO\Generators\Schema\Abstract_Schema_Piece;
use Yoast\WP\SEO\Presentations\Indexable_Author_Archive_Presentation;
use WP_User;

/**
 * The main Yoast integration class
 */
class Yoast {

	/**
	 * This integration relies on the wpseo_schema_graph added in Yoast 18.7
	 */
	const YOAST_MIN_VERSION = '18.7';

	/**
	 * Public method to be used to initialize this integration
	 *
	 * It will register a callback to the init hook where the actual initializatino happens
	 *
	 * @return void
	 */
	public static function init(): void {
		add_action( 'plugins_loaded', [ __CLASS__, 'do_initialization' ] );
	}

	/**
	 * Do the actual initialization. We don't want to do call before plugins_loaded to make sure we have everything in place to verify our conditions
	 *
	 * @return void
	 */
	public static function do_initialization(): void {
		if ( self::should_initialize() ) {
			require_once __DIR__ . '/yoast/class-coauthor.php';
			self::register_hooks();
		}
	}

	/**
	 * Checks if we should initialize this integration
	 *
	 * @return boolean
	 */
	protected static function should_initialize(): bool {
		return self::is_yoast_active() && ! self::is_yoast_legacy_integration_enabled();
	}

	/**
	 * Checks if the Yoast plugin is active and running the required version
	 *
	 * @return boolean
	 */
	protected static function is_yoast_active(): bool {
		return defined( 'WPSEO_VERSION' ) && version_compare( WPSEO_VERSION, self::YOAST_MIN_VERSION, '>=' );
	}

	/**
	 * This integration was originally built in Yoast and left behind a feature flag
	 *
	 * Now that we are moving it to this plugin, lets make sure to not load it if the Yoast version is enabled to avoid conflicts
	 *
	 * @return boolean
	 */
	protected static function is_yoast_legacy_integration_enabled(): bool {
		return defined( 'YOAST_SEO_COAUTHORS_PLUS' ) && YOAST_SEO_COAUTHORS_PLUS;
	}

	/**
	 * Register the hooks
	 *
	 * @return void
	 */
	public static function register_hooks(): void {
		add_filter( 'wpseo_schema_graph', [ __CLASS__, 'filter_graph' ], 11, 2 );
		add_filter( 'wpseo_schema_author', [ __CLASS__, 'filter_author_graph' ], 11, 4 );
		add_filter( 'wpseo_schema_profilepage', [ __CLASS__, 'filter_schema_profilepage' ], 11, 4 );
		add_filter( 'wpseo_meta_author', [ __CLASS__, 'filter_author_meta' ], 11, 2 );
		add_filter( 'wpseo_enhanced_slack_data', [__CLASS__, 'filter_slack_data'], 10, 2 );
		add_filter( 'wpseo_robots_array', [ __CLASS__, 'allow_indexing_guest_author_archive' ], 10, 2 );
		add_filter( 'wpseo_opengraph_url', [ __CLASS__, 'fix_guest_author_archive_url_presenter' ], 10, 2 );
		add_filter( 'wpseo_replacements', [ __CLASS__, 'filter_author_name_variable' ], 10, 2 );
	}

	/**
	 * Filters the graph output of authors archive for guest authors.
	 *
	 * @param array                   $data                   The schema graph.
	 * @param Meta_Tags_Context       $context                The context object.
	 * @param Abstract_Schema_Piece   $graph_piece_generator  The graph piece generator.
	 * @param Abstract_Schema_Piece[] $graph_piece_generators The graph piece generators.
	 *
	 * @return array The (potentially altered) schema graph.
	 */
	public static function filter_schema_profilepage( $data, $context, $graph_piece_generator, $graph_piece_generators ): array {

		if ( ! is_author() ) {
			return $data;
		}

		$user = get_queried_object();

		if ( empty( $user->type ) || $user->type !== 'guest-author' ) {
			return $data;
		}

		// Fix author URL.
		$author_url                                     = get_author_posts_url( $user->ID, $user->user_nicename );
		$graph_piece_generator->context->canonical      = $author_url;
		$graph_piece_generator->context->main_schema_id = $author_url;

		return $graph_piece_generator->generate();
	}

	/**
	 * Filters the graph output to add authors.
	 *
	 * @param array                   $data                   The schema graph.
	 * @param Meta_Tags_Context       $context                The context object.
	 * @param Abstract_Schema_Piece   $graph_piece_generator  The graph piece generator.
	 * @param Abstract_Schema_Piece[] $graph_piece_generators The graph piece generators.
	 *
	 * @return array The (potentially altered) schema graph.
	 */
	public static function filter_author_graph( $data, $context, $graph_piece_generator, $graph_piece_generators ): array {
		if ( ! isset( $data['image']['url'] ) ) {
			return $data;
		}

		if ( isset( $data['image']['@id'] ) ) {
			$data['image']['@id'] .= md5( $data['image']['url'] );
		}

		if ( isset( $data['logo']['@id'] ) ) {
			$data['logo']['@id'] .= md5( $data['image']['url'] );
		}

		return $data;
	}

	/**
	 * Filters the graph output to add authors.
	 *
	 * @param array             $data    The schema graph.
	 * @param Meta_Tags_Context $context Context object.
	 *
	 * @return array The (potentially altered) schema graph.
	 */
	public static function filter_graph( $data, $context ): array {
		if ( ! is_singular() ) {
			return $data;
		}

		if ( ! function_exists( 'get_coauthors' ) ) {
			return $data;
		}

		/**
		 * Contains the authors from the Co-Authors Plus plugin.
		 *
		 * @var WP_User[] $author_objects
		 */
		$author_objects = get_coauthors( $context->post->ID );

		$ids     = [];
		$authors = [];

		// Add the authors to the schema.
		foreach ( $author_objects as $author ) {
			$author_generator          = new CoAuthor();
			$author_generator->context = $context;
			$author_generator->helpers = YoastSEO()->helpers;

			if ( $author instanceof WP_User ) {
				$author_data = $author_generator->generate_from_user_id( $author->ID );
			} elseif ( ! empty( $author->type ) && $author->type === 'guest-author' ) {
				$author_data = $author_generator->generate_from_guest_author( $author );
			}

			if ( ! empty( $author_data ) ) {
				$ids[]     = [ '@id' => $author_data['@id'] ];
				$authors[] = $author_data;
			}
		}
		$schema_types  = new Schema_Types();
		$article_types = array_column( $schema_types->get_article_type_options(), 'value' );

		// Change the author reference to reference our multiple authors.
		$add_to_graph = false;
		foreach ( $data as $key => $piece ) {
			if ( in_array( $piece['@type'], $article_types, true ) ) {
				$data[ $key ]['author'] = $ids;
				$add_to_graph           = true;
				break;
			}
		}

		if ( $add_to_graph ) {
			// Clean all Persons from the schema, as the user stored as post owner might be incorrectly added if the post has only guest authors as authors.
			$data = array_filter(
				$data,
				function( $piece ) {
					return empty( $piece['@type'] ) || $piece['@type'] !== 'Person';
				}
			);

			if ( ! empty( $author_data ) ) {
				if ( $context->site_represents !== 'person' || $author->ID !== $context->site_user_id ) {
					$data = array_merge( $data, $authors );
				}
			}
		}

		return $data;
	}

	/**
	 * Filters the author meta tag
	 *
	 * @param string                 $author_name  The article author's display name. Return empty to disable the tag.
	 * @param Indexable_Presentation $presentation The presentation of an indexable.
	 * @return string
	 */
	public static function filter_author_meta( $author_name, $presentation ): string {
		$author_objects = get_coauthors( $presentation->context->post->id );

		// Fallback in case of error.
		if ( empty( $author_objects ) ) {
			return $author_name;
		}

		return self::get_authors_display_names_output( $author_objects );
	}

	/**
	 * Filter the enhanced data for sharing on Slack.
	 *
	 * @param array                  $data         The enhanced Slack sharing data.
	 * @param Indexable_Presentation $presentation The presentation of an indexable.
	 * @return array The potentially amended enhanced Slack sharing data.
	 */
	public static function filter_slack_data( $data, $presentation ): array {
		$author_objects = get_coauthors( $presentation->context->post->id );

		// Fallback in case of error.
		if ( empty( $author_objects ) ) {
			return $data;
		}

		$output = self::get_authors_display_names_output( $author_objects );
		$data[ \__( 'Written by', 'co-authors-plus' ) ] = $output;
		return $data;
	}

	/**
	 * Returns the list of authors display names separated by commas.
	 *
	 * @param WP_User[] $author_objects The list of authors.
	 * @return string Author display names separated by commas.
	 */
	private static function get_authors_display_names_output( $author_objects ): string {
		$output = '';
		foreach ( $author_objects as $i => $author ) {
			$output .= $author->display_name;
			if ( $i <= ( count( $author_objects ) - 2 ) ) {
				$output .= ', ';
			}
		}
		return $output;
	}

	/**
	 * Co-Authors Plus and Yoast are incompatible where the author archives for guest authors are output as noindex.
	 * This filter will determine if we're on an author archive and reset the robots.txt string properly.
	 *
	 * See https://github.com/Yoast/wordpress-seo/issues/9147.
	 *
	 * @param string                 $robots       The meta robots directives to be echoed.
	 * @param Indexable_Presentation $presentation The presentation of an indexable.
	 */
	public static function allow_indexing_guest_author_archive( $robots, $presentation ) {
		if ( ! is_author() ) {
			return $robots;
		}

		if ( ! is_a( $presentation, Indexable_Author_Archive_Presentation::class ) ) {
			return $robots;
		}

		$post_type = get_post_type( get_queried_object_id() );
		if ( 'guest-author' !== $post_type ) {
			return $robots;
		}

		/*
		 * If this is a guest author archive and hasn't manually been set to noindex,
		 * make sure the robots.txt string is set properly.
		 */
		if ( empty( $presentation->model->is_robots_noindex ) || 0 === (int) $presentation->model->is_robots_noindex ) {
			if ( ! is_array( $robots ) ) {
				$robots = [];
			}
			$robots['index']  = 'index';
			$robots['follow'] = 'follow';
		}

		return $robots;
	}

	public static function fix_guest_author_archive_url_presenter( $url, $presenter ) {
		if ( ! is_author() ) {
			return $url;
		}

		$user = get_queried_object();

		if ( empty( $user->type ) || $user->type !== 'guest-author' ) {
			return $url;
		}

		return get_author_posts_url( $user->ID, $user->user_nicename );
	}

	/**
	 * Uses guest authors in the '%%name%%' Yoast variable when needed.
	 *
	 * See https://yoast.com/features/meta-tag-variables/.
	 *
	 * @param array    $replacements Key/val pair of variables and their transformed value.
	 * @param stdClass $args         Info about current queried object.
	 * @return array   Modified $replacements.
	 */
	public static function filter_author_name_variable( $replacements, $args ): array {
		if ( isset( $replacements['%%name%%'], $args->ID ) && is_single() ) {
			$author_objects = get_coauthors( $args->ID );

			// Fallback in case of error.
			if ( empty( $author_objects ) ) {
				return $replacements;
			}

			$replacements['%%name%%'] = self::get_authors_display_names_output( $author_objects );
		}

		return $replacements;
	}
}

Yoast::init();