Your IP : 216.73.216.162


Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/
Upload File :
Current File : /home/x/b/o/xbodynamge/namtation/wp-content/Image.tar

Image.php000066600000022536151144037730006320 0ustar00<?php
namespace AIOSEO\Plugin\Common\Sitemap\Image;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Determines which images are included in a post/term.
 *
 * @since 4.0.0
 */
class Image {
	/**
	 * The image scan action name.
	 *
	 * @since 4.0.13
	 *
	 * @var string
	 */
	private $imageScanAction = 'aioseo_image_sitemap_scan';

	/**
	 * The supported image extensions.
	 *
	 * @since 4.2.2
	 *
	 * @var array[string]
	 */
	public $supportedExtensions = [
		'gif',
		'heic',
		'jpeg',
		'jpg',
		'png',
		'svg',
		'webp',
		'ico'
	];

	/**
	 * The post object.
	 *
	 * @since 4.2.7
	 *
	 * @var \WP_Post
	 */
	private $post = null;

	/**
	 * Class constructor.
	 *
	 * @since 4.0.5
	 */
	public function __construct() {
		// Column may not have been created yet.
		if ( ! aioseo()->core->db->columnExists( 'aioseo_posts', 'image_scan_date' ) ) {
			return;
		}

		// NOTE: This needs to go above the is_admin check in order for it to run at all.
		add_action( $this->imageScanAction, [ $this, 'scanPosts' ] );

		// Don't schedule a scan if we are not in the admin.
		if ( ! is_admin() ) {
			return;
		}

		if ( wp_doing_ajax() || wp_doing_cron() ) {
			return;
		}

		// Don't schedule a scan if an importer or the V3 migration is running.
		// We'll do our scans there.
		if (
			aioseo()->importExport->isImportRunning() ||
			aioseo()->migration->isMigrationRunning()
		) {
			return;
		}
		// Action Scheduler hooks.
		add_action( 'init', [ $this, 'scheduleScan' ], 3001 );
	}

	/**
	 * Schedules the image sitemap scan.
	 *
	 * @since 4.0.5
	 *
	 * @return void
	 */
	public function scheduleScan() {
		if (
			! aioseo()->options->sitemap->general->enable ||
			aioseo()->sitemap->helpers->excludeImages()
		) {
			return;
		}

		aioseo()->actionScheduler->scheduleSingle( $this->imageScanAction, 10 );
	}

	/**
	 * Scans posts for images.
	 *
	 * @since 4.0.5
	 *
	 * @return void
	 */
	public function scanPosts() {
		if (
			! aioseo()->options->sitemap->general->enable ||
			aioseo()->sitemap->helpers->excludeImages()
		) {
			return;
		}

		$postsPerScan = apply_filters( 'aioseo_image_sitemap_posts_per_scan', 10 );
		$postTypes    = implode( "', '", aioseo()->helpers->getPublicPostTypes( true ) );

		$posts = aioseo()->core->db
			->start( aioseo()->core->db->db->posts . ' as p', true )
			->select( '`p`.`ID`, `p`.`post_type`, `p`.`post_content`, `p`.`post_excerpt`, `p`.`post_modified_gmt`' )
			->leftJoin( 'aioseo_posts as ap', '`ap`.`post_id` = `p`.`ID`' )
			->whereRaw( '( `ap`.`id` IS NULL OR `p`.`post_modified_gmt` > `ap`.`image_scan_date` OR `ap`.`image_scan_date` IS NULL )' )
			->whereRaw( "`p`.`post_status` IN ( 'publish', 'inherit' )" )
			->whereRaw( "`p`.`post_type` IN ( '$postTypes' )" )
			->limit( $postsPerScan )
			->run()
			->result();

		if ( ! $posts ) {
			aioseo()->actionScheduler->scheduleSingle( $this->imageScanAction, 15 * MINUTE_IN_SECONDS, [], true );

			return;
		}

		foreach ( $posts as $post ) {
			$this->scanPost( $post );
		}

		aioseo()->actionScheduler->scheduleSingle( $this->imageScanAction, 30, [], true );
	}

	/**
	 * Returns the image entries for a given post.
	 *
	 * @since 4.0.0
	 *
	 * @param  \WP_Post|int $post The post object or ID.
	 * @return void
	 */
	public function scanPost( $post ) {
		if ( is_numeric( $post ) ) {
			$post = get_post( $post );
		}

		$this->post = $post;

		if ( ! empty( $post->post_password ) ) {
			$this->updatePost( $post->ID );

			return;
		}

		if ( 'attachment' === $post->post_type ) {
			if ( ! wp_attachment_is( 'image', $post->ID ) ) {
				$this->updatePost( $post->ID );

				return;
			}

			$image = $this->buildEntries( [ $post->ID ] );
			$this->updatePost( $post->ID, $image );

			return;
		}

		$images = $this->extract();
		$images = $this->removeImageDimensions( $images );

		$images = apply_filters( 'aioseo_sitemap_images', $images, $post );

		// Limit to a 1,000 URLs, in accordance to Google's specifications.
		$images = array_slice( $images, 0, 1000 );
		$this->updatePost( $post->ID, $this->buildEntries( $images ) );
	}

	/**
	 * Returns the image entries for a given term.
	 *
	 * @since 4.0.0
	 *
	 * @param  \WP_Term $term The term object.
	 * @return array          The image entries.
	 */
	public function term( $term ) {
		if ( aioseo()->sitemap->helpers->excludeImages() ) {
			return [];
		}

		$id = get_term_meta( $term->term_id, 'thumbnail_id', true );
		if ( ! $id ) {
			return [];
		}

		return $this->buildEntries( [ $id ] );
	}

	/**
	 * Builds the image entries.
	 *
	 * @since 4.0.0
	 *
	 * @param  array $images The images, consisting of attachment IDs or external URLs.
	 * @return array         The image entries.
	 */
	private function buildEntries( $images ) {
		$entries = [];
		foreach ( $images as $image ) {
			$idOrUrl  = $this->getImageIdOrUrl( $image );
			$imageUrl = is_numeric( $idOrUrl ) ? wp_get_attachment_url( $idOrUrl ) : $idOrUrl;
			$imageUrl = aioseo()->sitemap->helpers->formatUrl( $imageUrl );
			if ( ! $imageUrl || ! preg_match( $this->getImageExtensionRegexPattern(), (string) $imageUrl ) ) {
				continue;
			}

			// If the image URL is not external, make it relative.
			// This is important for users who scan their sites in a local/staging environment and then
			// push the data to production.
			if ( ! aioseo()->helpers->isExternalUrl( $imageUrl ) ) {
				$imageUrl = aioseo()->helpers->makeUrlRelative( $imageUrl );
			}

			$entries[ $idOrUrl ] = [ 'image:loc' => $imageUrl ];
		}

		return array_values( $entries );
	}

	/**
	 * Returns the ID of the image if it's hosted on the site. Otherwise it returns the external URL.
	 *
	 * @since 4.1.3
	 *
	 * @param  int|string $image The attachment ID or URL.
	 * @return int|string        The attachment ID or URL.
	 */
	private function getImageIdOrUrl( $image ) {
		if ( is_numeric( $image ) ) {
			return $image;
		}

		$attachmentId = false;
		if ( aioseo()->helpers->isValidAttachment( $image ) ) {
			$attachmentId = aioseo()->helpers->attachmentUrlToPostId( $image );
		}

		return $attachmentId ? $attachmentId : $image;
	}

	/**
	 * Extracts all image URls and IDs from the post.
	 *
	 * @since 4.0.0
	 *
	 * @return array The image URLs and IDs.
	 */
	private function extract() {
		$images = [];

		if ( has_post_thumbnail( $this->post ) ) {
			$images[] = get_the_post_thumbnail_url( $this->post );
		}

		// Get the galleries here before doShortcodes() runs below to prevent buggy behaviour.
		// WordPress is supposed to only return the attached images but returns a different result if the shortcode has no valid attributes, so we need to grab them manually.
		$images = array_merge( $images, $this->getPostGalleryImages() );

		// Now, get the remaining images from image tags in the post content.
		$parsedPostContent = do_blocks( $this->post->post_content );
		$parsedPostContent = aioseo()->helpers->doShortcodes( $parsedPostContent, true, $this->post->ID );
		$parsedPostContent = preg_replace( '/\s\s+/u', ' ', (string) trim( $parsedPostContent ) ); // Trim both internal and external whitespace.

		// Get the images from any third-party plugins/themes that are active.
		$thirdParty = new ThirdParty( $this->post, $parsedPostContent );
		$images     = array_merge( $images, $thirdParty->extract() );

		preg_match_all( '#<(amp-)?img[^>]+src="([^">]+)"#', (string) $parsedPostContent, $matches );
		foreach ( $matches[2] as $url ) {
			$images[] = aioseo()->helpers->makeUrlAbsolute( $url );
		}

		return array_unique( $images );
	}

	/**
	 * Returns all images from WP Core post galleries.
	 *
	 * @since 4.2.2
	 *
	 * @return array[string] The image URLs.
	 */
	private function getPostGalleryImages() {
		$images    = [];
		$galleries = get_post_galleries( $this->post, false );
		foreach ( $galleries as $gallery ) {
			foreach ( $gallery['src'] as $imageUrl ) {
				$images[] = $imageUrl;
			}
		}

		// Now, get rid of them so that we don't process the shortcodes again.
		$regex                    = get_shortcode_regex( [ 'gallery' ] );
		$this->post->post_content = preg_replace( "/$regex/i", '', (string) $this->post->post_content );

		return $images;
	}

	/**
	 * Removes image dimensions from the slug.
	 *
	 * @since 4.0.0
	 *
	 * @param  array $urls         The image URLs.
	 * @return array $preparedUrls The formatted image URLs.
	 */
	private function removeImageDimensions( $urls ) {
		$preparedUrls = [];
		foreach ( $urls as $url ) {
			$preparedUrls[] = aioseo()->helpers->removeImageDimensions( $url );
		}

		return array_unique( array_filter( $preparedUrls ) );
	}

	/**
	 * Stores the image data for a given post in our DB table.
	 *
	 * @since 4.0.5
	 *
	 * @param  int   $postId The post ID.
	 * @param  array $images The images.
	 * @return void
	 */
	private function updatePost( $postId, $images = [] ) {
		$post                    = \AIOSEO\Plugin\Common\Models\Post::getPost( $postId );
		$meta                    = $post->exists() ? [] : aioseo()->migration->meta->getMigratedPostMeta( $postId );
		$meta['post_id']         = $postId;
		$meta['images']          = ! empty( $images ) ? $images : null;
		$meta['image_scan_date'] = gmdate( 'Y-m-d H:i:s' );

		$post->set( $meta );
		$post->save();
	}

	/**
	 * Returns the image extension regex pattern.
	 *
	 * @since 4.2.2
	 *
	 * @return string
	 */
	public function getImageExtensionRegexPattern() {
		static $pattern;
		if ( null !== $pattern ) {
			return $pattern;
		}

		$pattern = '/http.*\.(' . implode( '|', $this->supportedExtensions ) . ')$/i';

		return $pattern;
	}
}ThirdParty.php000066600000017641151144037730007371 0ustar00<?php
namespace AIOSEO\Plugin\Common\Sitemap\Image;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Holds all code to extract images from third-party content.
 *
 * @since 4.2.2
 */
class ThirdParty {
	/**
	 * The post object.
	 *
	 * @since 4.2.2
	 *
	 * @var \WP_Post
	 */
	private $post;

	/**
	 * The parsed post content.
	 * The post object holds the unparsed content as we need that for Divi.
	 *
	 * @since 4.2.5
	 *
	 * @var string
	 */
	private $parsedPostContent;

	/**
	 * The image URLs and IDs.
	 *
	 * @since 4.2.2
	 *
	 * @var array[mixed]
	 */
	private $images = [];

	/**
	 * Divi shortcodes.
	 *
	 * @since 4.2.3
	 *
	 * @var string[]
	 */
	private $shortcodes = [
		'et_pb_section',
		'et_pb_column',
		'et_pb_row',
		'et_pb_image',
		'et_pb_gallery',
		'et_pb_accordion',
		'et_pb_accordion_item',
		'et_pb_counters',
		'et_pb_blurb',
		'et_pb_cta',
		'et_pb_code',
		'et_pb_contact_form',
		'et_pb_divider',
		'et_pb_filterable_portfolio',
		'et_pb_map',
		'et_pb_number_counter',
		'et_pb_post_slider',
		'et_pb_pricing_tables',
		'et_pb_pricing_table',
		'et_pb_shop',
		'et_pb_slider',
		'et_pb_slide',
		'et_pb_tabs',
		'et_pb_tab',
		'et_pb_text',
		'et_pb_video',
		'et_pb_audio',
		'et_pb_blog',
		'et_pb_circle_counter',
		'et_pb_comments',
		'et_pb_countdown_timer',
		'et_pb_signup',
		'et_pb_login',
		'et_pb_menu',
		'et_pb_team_member',
		'et_pb_post_nav',
		'et_pb_post_title',
		'et_pb_search',
		'et_pb_sidebar',
		'et_pb_social_media_follow',
		'et_pb_social_media_follow_network',
		'et_pb_testimonial',
		'et_pb_toggle',
		'et_pb_video_slider',
		'et_pb_video_slider_item',
	];

	/**
	 * Class constructor.
	 *
	 * @since 4.2.2
	 *
	 * @param \WP_Post $post              The post object.
	 * @param string   $parsedPostContent The parsed post content.
	 */
	public function __construct( $post, $parsedPostContent ) {
		$this->post              = $post;
		$this->parsedPostContent = $parsedPostContent;
	}

	/**
	 * Extracts the images from third-party content.
	 *
	 * @since 4.2.2
	 *
	 * @return array[mixed] The image URLs and IDs.
	 */
	public function extract() {
		$integrations = [
			'acf',
			'divi',
			'nextGen',
			'wooCommerce',
			'kadenceBlocks'
		];

		foreach ( $integrations as $integration ) {
			$this->{$integration}();
		}

		return $this->images;
	}

	/**
	 * Extracts image URLs from ACF fields.
	 *
	 * @since 4.2.2
	 *
	 * @return void
	 */
	private function acf() {
		if ( ! class_exists( 'ACF' ) || ! function_exists( 'get_fields' ) ) {
			return;
		}

		$fields = get_fields( $this->post->ID );
		if ( ! $fields ) {
			return;
		}

		$images       = $this->acfHelper( $fields );
		$this->images = array_merge( $this->images, $images );
	}

	/**
	 * Helper function for acf().
	 *
	 * @since 4.2.2
	 *
	 * @param  array         $fields The ACF fields.
	 * @return array[string]         The image URLs or IDs.
	 */
	private function acfHelper( $fields ) {
		$images = [];
		foreach ( $fields as $value ) {
			if ( is_array( $value ) ) {
				// Recursively loop over grouped fields.
				// We continue on since arrays aren't necessarily groups and might also simply aLready contain the value we're looking for.
				$images = array_merge( $images, $this->acfHelper( $value ) );

				if ( isset( $value['type'] ) && 'image' !== strtolower( $value['type'] ) ) {
					$images[] = $value['url'];
				}

				continue;
			}

			// Capture the value if it's an image URL, but not the default thumbnail from ACF.
			if ( is_string( $value ) && preg_match( aioseo()->sitemap->image->getImageExtensionRegexPattern(), (string) $value ) && ! preg_match( '/media\/default\.png$/i', (string) $value ) ) {
				$images[] = $value;
				continue;
			}

			// Capture the value if it's a numeric image ID, but make sure it's not an array of random field object properties.
			if (
				is_numeric( $value ) &&
				! isset( $fields['ID'] ) &&
				! isset( $fields['thumbnail'] )
			) {
				$images[] = $value;
			}
		}

		return $images;
	}

	/**
	 * Extracts images from Divi shortcodes.
	 *
	 * @since 4.1.8
	 *
	 * @return void
	 */
	private function divi() {
		if ( ! defined( 'ET_BUILDER_VERSION' ) ) {
			return;
		}

		$urls  = [];
		$regex = implode( '|', array_map( 'preg_quote', $this->shortcodes ) );

		preg_match_all(
			"/\[($regex)(?![\w-])([^\]\/]*(?:\/(?!\])[^\]\/]*)*?)(?:(\/)\]|\](?:([^\[]*+(?:\[(?!\/\2\])[^\[]*+)*+)\[\/\2\])?)(\]?)/i",
			(string) $this->post->post_content,
			$matches,
			PREG_SET_ORDER
		);

		foreach ( $matches as $shortcode ) {
			$attributes = shortcode_parse_atts( $shortcode[0] );
			if ( ! empty( $attributes['src'] ) ) {
				$urls[] = $attributes['src'];
			}

			if ( ! empty( $attributes['image_src'] ) ) {
				$urls[] = $attributes['image_src'];
			}

			if ( ! empty( $attributes['image_url'] ) ) {
				$urls[] = $attributes['image_url'];
			}

			if ( ! empty( $attributes['portrait_url'] ) ) {
				$urls[] = $attributes['portrait_url'];
			}

			if ( ! empty( $attributes['image'] ) ) {
				$urls[] = $attributes['image'];
			}

			if ( ! empty( $attributes['background_image'] ) ) {
				$urls[] = $attributes['background_image'];
			}

			if ( ! empty( $attributes['logo'] ) ) {
				$urls[] = $attributes['logo'];
			}

			if ( ! empty( $attributes['gallery_ids'] ) ) {
				$attachmentIds = explode( ',', $attributes['gallery_ids'] );
				foreach ( $attachmentIds as $attachmentId ) {
					$urls[] = wp_get_attachment_url( $attachmentId );
				}
			}
		}

		$this->images = array_merge( $this->images, $urls );
	}

	/**
	 * Extracts the image IDs of more advanced NextGen Pro gallerlies like the Mosaic and Thumbnail Grid.
	 *
	 * @since 4.2.5
	 *
	 * @return void
	 */
	private function nextGen() {
		if ( ! defined( 'NGG_PLUGIN_BASENAME' ) && ! defined( 'NGG_PRO_PLUGIN_BASENAME' ) ) {
			return;
		}

		preg_match_all( '/data-image-id=\"([0-9]*)\"/i', (string) $this->parsedPostContent, $imageIds );
		if ( ! empty( $imageIds[1] ) ) {
			$this->images = array_merge( $this->images, $imageIds[1] );
		}

		// For this specific check, we only want to parse blocks and do not want to run shortcodes because some NextGen blocks (e.g. Mosaic) are parsed into shortcodes.
		// And after parsing the shortcodes, the attributes we're looking for are gone.
		$contentWithBlocksParsed = do_blocks( $this->post->post_content );

		$imageIds = [];
		preg_match_all( '/\[ngg.*src="galleries" ids="(.*?)".*\]/i', (string) $contentWithBlocksParsed, $shortcodes );
		if ( empty( $shortcodes[1] ) ) {
			return;
		}

		foreach ( $shortcodes[1] as $shortcode ) {
			$galleryIds = explode( ',', $shortcode[0] );
			foreach ( $galleryIds as $galleryId ) {
				global $nggdb;
				$galleryImageIds = $nggdb->get_ids_from_gallery( $galleryId );
				if ( empty( $galleryImageIds ) ) {
					continue;
				}

				foreach ( $galleryImageIds as $galleryImageId ) {
					$image = $nggdb->find_image( $galleryImageId );
					if ( ! empty( $image ) ) {
						$imageIds[] = $image->get_permalink();
					}
				}
			}
		}

		$this->images = array_merge( $this->images, $imageIds );
	}

	/**
	 * Extracts the image IDs of WooCommerce product galleries.
	 *
	 * @since 4.1.2
	 *
	 * @return void
	 */
	private function wooCommerce() {
		if ( ! aioseo()->helpers->isWooCommerceActive() || 'product' !== $this->post->post_type ) {
			return;
		}

		$productImageIds = get_post_meta( $this->post->ID, '_product_image_gallery', true );
		if ( ! $productImageIds ) {
			return;
		}

		$productImageIds = explode( ',', $productImageIds );
		$this->images    = array_merge( $this->images, $productImageIds );
	}

	/**
	 * Extracts the image IDs of Kadence Block galleries.
	 *
	 * @since 4.4.5
	 *
	 * @return void
	 */
	private function kadenceBlocks() {
		if ( ! defined( 'KADENCE_BLOCKS_VERSION' ) ) {
			return [];
		}

		$blocks = aioseo()->helpers->parseBlocks( $this->post );

		foreach ( $blocks as $block ) {
			if ( 'kadence/advancedgallery' === $block['blockName'] && ! empty( $block['attrs']['ids'] ) ) {
				$this->images = array_merge( $this->images, $block['attrs']['ids'] );
			}
		}
	}
}