<?php
/**
 * Genesis Framework.
 *
 * WARNING: This file is part of the core Genesis Framework. DO NOT edit this file under any circumstances.
 * Please do all modifications in the form of a child theme.
 *
 * @package StudioPress\Genesis
 * @author  StudioPress
 * @license GPL-2.0-or-later
 * @link    https://my.studiopress.com/themes/genesis/
 */

namespace StudioPress\Genesis\SEO;

/**
 * Genesis Open Graph support.
 *
 * @since 3.2.0
 */
class Open_Graph {

	/**
	 * Icon size for author and site icon square images.
	 *
	 * @since 3.2.0
	 *
	 * @var int
	 */
	protected $icon_size;

	/**
	 * Constructs the Open_Graph object.
	 *
	 * @since 3.2.0
	 *
	 * @param array $config Open Graph configuration.
	 */
	public function __construct( $config ) {
		$this->icon_size = $config['icon_size'];
	}

	/**
	 * Adds WordPress hooks.
	 *
	 * @since 3.2.0
	 */
	public function add_hooks() {
		add_action( 'wp_head', [ $this, 'output_open_graph_tags' ], 5 );
		add_action( 'customize_register', [ $this, 'add_default_image_control' ] );
	}

	/**
	 * Adds default Open Graph image control to the Customizer.
	 *
	 * Can be moved to `config/customizer-seo-settings.php` when it
	 * supports image controls. (Genesis issue #1885.)
	 *
	 * @since 3.2.0
	 *
	 * @param WP_Customize_Manager $wp_customize Customizer instance.
	 */
	public function add_default_image_control( $wp_customize ) {
		$wp_customize->add_setting(
			'genesis-seo-settings[open_graph_default_image]',
			[
				'default'           => '',
				'sanitize_callback' => 'esc_attr',
				'type'              => 'option',
			]
		);

		$wp_customize->add_control(
			new \WP_Customize_Media_Control(
				$wp_customize,
				'open_graph_default_image',
				[
					'label'       => __( 'Default Image', 'genesis' ),
					'description' => __( 'Select an image to use on social sites if no featured image is set.', 'genesis' ),
					'section'     => 'genesis_seo_open_graph',
					'settings'    => 'genesis-seo-settings[open_graph_default_image]',
					'mime_type'   => 'image',
				]
			)
		);
	}

	/**
	 * Outputs Open Graph meta elements if Open Graph support is enabled.
	 *
	 * @since 3.2.0
	 */
	public function output_open_graph_tags() {
		$open_graph_enabled = genesis_get_seo_option( 'open_graph' ) ?: false;

		if ( ! $open_graph_enabled ) {
			return;
		}

		echo "<!-- Genesis Open Graph -->\n"; // For support teams.
		$this->tag( 'title', wp_get_document_title() );
		$this->tag( 'type', $this->get_type() );
		$this->tag( 'description', genesis_get_seo_meta_description() );
		$this->tag( 'url', $this->get_url() );

		$image = $this->get_image();

		if ( isset( $image['url'] ) ) {
			$this->tag( 'image', $image['url'] );

			if ( isset( $image['width'] ) && isset( $image['height'] ) ) {
				$this->tag( 'image:width', $image['width'] );
				$this->tag( 'image:height', $image['height'] );
			}

			if ( isset( $image['alt'] ) ) {
				$this->tag( 'image:alt', $image['alt'] );
			}
		}
	}

	/**
	 * Gets the current Open Graph object type.
	 *
	 * Filterable via `genesis_open_graph_type`.
	 *
	 * @since 3.2.0
	 *
	 * @see https://ogp.me/#types Open Graph object types.
	 * @return string The Open Graph type.
	 */
	protected function get_type() {
		$type = 'website';

		if ( is_singular() && ! is_front_page() ) {
			$type = 'article';
		} elseif ( is_author() ) {
			$type = 'profile';
		}

		return $type;
	}

	/**
	 * Gets the current page URL.
	 *
	 * Filterable via `genesis_open_graph_url`.
	 *
	 * @since 3.2.0
	 *
	 * @return string The current page URL.
	 */
	protected function get_url() {
		global $wp;

		return trailingslashit( home_url( $wp->request ) );
	}

	/**
	 * Gets the image URL.
	 *
	 * Featured image if present, falling back to the first image attached to
	 * the post, then the default set in Customizer Open Graph SEO settings,
	 * then the site icon if set.
	 *
	 * Filterable via `genesis_open_graph_image`.
	 *
	 * @since 3.2.0
	 *
	 * @return array The featured or fallback image with 'url', 'height', and 'width' keys.
	 */
	protected function get_image() {
		$image = [];

		if ( is_author() ) {
			$image = $this->get_author_image();
		} else {
			$image = $this->get_genesis_image();
		}

		if ( ! $image || ( is_archive() && ! is_author() ) ) {
			$image = $this->get_open_graph_default_image();
		}

		if ( ! $image && function_exists( 'get_site_icon_url' ) && has_site_icon() ) {
			$image = $this->get_site_icon_image();
		}

		return $image;
	}

	/**
	 * Gets the author profile image information.
	 *
	 * @since 3.2.0
	 *
	 * @return array Image info with 'url', 'height', 'width', and 'alt' keys or empty array.
	 */
	protected function get_author_image() {
		$avatar_url = get_avatar_url(
			get_the_author_meta( 'ID' ),
			[ 'size' => $this->icon_size ]
		);

		return $this->get_image_info( $avatar_url, true );
	}

	/**
	 * Gets the default Genesis image featured image or first attachment.
	 *
	 * @since 3.2.0
	 *
	 * @return array Image info with 'url', 'height', 'width', and 'alt' keys or empty array.
	 */
	protected function get_genesis_image() {
		$genesis_image = genesis_get_image( [ 'format' => 'url' ] );

		return $this->get_image_info( attachment_url_to_postid( $genesis_image ) );
	}

	/**
	 * Gets the default Open Graph image if set in the Customizer.
	 *
	 * @since 3.2.0
	 *
	 * @return array Image info with 'url', 'height', 'width', and 'alt' keys or empty array.
	 */
	protected function get_open_graph_default_image() {
		$default_image_id = genesis_get_seo_option( 'open_graph_default_image' ) ?: false;

		return $this->get_image_info( $default_image_id );
	}

	/**
	 * Gets the site icon image info if set.
	 *
	 * @since 3.2.0
	 *
	 * @return array Image info with 'url', 'height', 'width', and 'alt' keys or empty array.
	 */
	protected function get_site_icon_image() {
		$site_icon = get_site_icon_url( $this->icon_size );

		return $this->get_image_info( $site_icon, true );
	}

	/**
	 * Gets image info given a URL or attachment ID.
	 *
	 * @since 3.2.0
	 *
	 * @param string|int $image URL or attachment ID. Icons assume use of URL.
	 * @param bool       $icon If true, gives square value from `icon_size` class property.
	 * @return array Image info with 'url', 'height', 'width', and 'alt' keys or empty array.
	 */
	protected function get_image_info( $image, $icon = false ) {
		if ( $icon && $image ) {
			return [
				'url'    => $image,
				'height' => $this->icon_size,
				'width'  => $this->icon_size,
			];
		}

		if ( is_numeric( $image ) ) {
			$url      = wp_get_attachment_url( $image );
			$info     = wp_get_attachment_metadata( $image );
			$alt_text = get_post_meta( $image, '_wp_attachment_image_alt', true );

			$image = [];

			if ( $url ) {
				$image['url'] = $url;

				if ( isset( $info['height'] ) && isset( $info['width'] ) ) {
					$image['height'] = $info['height'];
					$image['width']  = $info['width'];
				}

				if ( $alt_text ) {
					$image['alt'] = $alt_text;
				}
			}

			return $image;
		}

		return [];
	}

	/**
	 * Outputs a single Open Graph meta tag.
	 *
	 * @since 3.2.0
	 *
	 * @param string $property The Open Graph property without the `og:` prefix.
	 * @param string $content The value of the content attribute.
	 */
	protected function tag( $property, $content ) {
		/**
		 * Override the content attribute of any Genesis Open Graph property.
		 *
		 * ```php
		 * add_filter( 'genesis_open_graph_title', 'custom_open_graph_title' );
		 *
		 * function custom_open_graph_title( $content ) {
		 *    if ( is_front_page() ) {
		 *        $content = $content . ' — Some custom text';
		 *    }
		 *
		 *    return $content;
		 * }
		 * ```
		 *
		 * @since 3.2.0
		 *
		 * @param $content The content to return for the given property.
		 */
		$content = apply_filters( "genesis_open_graph_{$property}", $content );

		$content = trim( $content );

		if ( ! $content ) {
			return;
		}

		printf(
			'<meta property="og:%1$s" content="%2$s" />' . "\n",
			esc_attr( $property ),
			esc_attr( $content )
		);
	}

}
