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/Breadcrumbs.tar

Frontend.php000066600000021055151135524620007047 0ustar00<?php
namespace AIOSEO\Plugin\Common\Breadcrumbs;

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

use AIOSEO\Plugin\Common\Integrations\BuddyPress as BuddyPressIntegration;

/**
 * Class Frontend.
 *
 * @since 4.1.1
 */
class Frontend {
	/**
	 * A local 'cached' crumb array.
	 *
	 * @since 4.1.1
	 *
	 * @var array
	 */
	public $breadcrumbs = [];

	/**
	 * Gets the current page's breadcrumbs.
	 *
	 * @since 4.1.1
	 *
	 * @return array
	 */
	public function getBreadcrumbs() {
		if ( ! empty( $this->breadcrumbs ) ) {
			return apply_filters( 'aioseo_breadcrumbs_trail', $this->breadcrumbs );
		}

		$reference = get_queried_object();
		$type      = '';
		if ( BuddyPressIntegration::isComponentPage() ) {
			$type = 'buddypress';
		}

		if ( ! $type ) {
			// These types need the queried object for reference.
			if ( is_object( $reference ) ) {
				if ( is_single() ) {
					$type = 'single';
				}

				if ( is_singular( 'post' ) ) {
					$type = 'post';
				}

				if ( is_page() && ! is_front_page() ) {
					$type = 'page';
				}

				if ( is_category() || is_tag() ) {
					$type = 'category';
				}

				if ( is_tax() ) {
					$type = 'taxonomy';
				}

				if ( is_post_type_archive() ) {
					$type = 'postTypeArchive';
				}

				if ( is_author() ) {
					$type = 'author';
				}

				if ( is_home() ) {
					$type = 'blog';
				}

				// Support WC shop page.
				if ( aioseo()->helpers->isWooCommerceShopPage() ) {
					$type = 'wcShop';
				}

				// Support WC products.
				if ( aioseo()->helpers->isWooCommerceProductPage() ) {
					$type = 'wcProduct';
				}
			}

			if ( is_date() ) {
				$type      = 'date';
				$reference = [
					'year'  => get_query_var( 'year' ),
					'month' => get_query_var( 'monthnum' ),
					'day'   => get_query_var( 'day' )
				];
			}

			if ( is_search() ) {
				$type      = 'search';
				$reference = htmlspecialchars( sanitize_text_field( get_search_query() ) );
			}

			if ( is_404() ) {
				$type = 'notFound';
			}
		}

		$paged = false;
		if ( is_paged() || ( is_singular() && 1 < get_query_var( 'page' ) ) ) {
			global $wp;
			$paged = [
				'paged' => get_query_var( 'paged' ) ? get_query_var( 'paged' ) : get_query_var( 'page' ),
				'link'  => home_url( $wp->request )
			];
		}

		return apply_filters( 'aioseo_breadcrumbs_trail', aioseo()->breadcrumbs->buildBreadcrumbs( $type, $reference, $paged ) );
	}

	/**
	 * Helper function to display breadcrumbs for a specific page.
	 *
	 * @since 4.1.1
	 *
	 * @param  bool        $echo      Print out the breadcrumb.
	 * @param  string      $type      The type for the breadcrumb.
	 * @param  string      $reference A reference to be used for rendering the breadcrumb.
	 * @return string|void            A html breadcrumb.
	 */
	public function sideDisplay( $echo = true, $type = '', $reference = '' ) {
		// Save previously built breadcrumbs.
		$previousCrumbs = $this->breadcrumbs;

		// Build and run the sideDisplay.
		$this->breadcrumbs = aioseo()->breadcrumbs->buildBreadcrumbs( $type, $reference );
		$sideDisplay       = $this->display( $echo );

		// Restore previously built breadcrumbs.
		$this->breadcrumbs = $previousCrumbs;

		return $sideDisplay;
	}

	/**
	 * Display a generic breadcrumb preview.
	 *
	 * @since 4.1.5
	 *
	 * @param  bool        $echo  Print out the breadcrumb.
	 * @param  string      $label The preview crumb label.
	 * @return string|void        A html breadcrumb.
	 */
	public function preview( $echo = true, $label = '' ) {
		// Translators: "Crumb" refers to a part of the breadcrumb trail.
		$label = empty( $label ) ? __( 'Sample Crumb', 'all-in-one-seo-pack' ) : $label;

		return $this->sideDisplay( $echo, 'preview', $label );
	}

	/**
	 * Display the breadcrumb in the frontend.
	 *
	 * @since 4.1.1
	 *
	 * @param  bool        $echo Print out the breadcrumb.
	 * @return string|void       A html breadcrumb.
	 */
	public function display( $echo = true ) {
		if (
			in_array( 'breadcrumbsEnable', aioseo()->internalOptions->deprecatedOptions, true ) &&
			! aioseo()->options->deprecated->breadcrumbs->enable
		) {
			return;
		}

		if ( ! apply_filters( 'aioseo_breadcrumbs_output', true ) ) {
			return;
		}

		// We can only run after this action because we need all post types loaded.
		if ( ! did_action( 'init' ) ) {
			return;
		}

		$breadcrumbs = $this->getBreadcrumbs();
		if ( empty( $breadcrumbs ) ) {
			return;
		}

		$breadcrumbsCount = count( $breadcrumbs );

		$display = '<div class="aioseo-breadcrumbs">';
		foreach ( $breadcrumbs as $breadcrumb ) {
			--$breadcrumbsCount;

			$breadcrumbDisplay = $this->breadcrumbToDisplay( $breadcrumb );

			// Strip link from Last crumb.
			if (
				0 === $breadcrumbsCount &&
				aioseo()->breadcrumbs->showCurrentItem() &&
				! $this->linkCurrentItem() &&
				'default' === $breadcrumbDisplay['templateType']
			) {
				$breadcrumbDisplay['template'] = $this->stripLink( $breadcrumbDisplay['template'] );
			}

			$display .= $breadcrumbDisplay['template'];

			if ( 0 < $breadcrumbsCount ) {
				$display .= $this->getSeparator();
			}
		}
		$display .= '</div>';

		// Final security cleaning.
		$display = wp_kses_post( $display );

		if ( $echo ) {
			echo $display; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}

		return $display;
	}

	/**
	 * Turns a crumb array into a rendered html crumb.
	 *
	 * @since 4.1.1
	 *
	 * @param  array       $item The crumb array.
	 * @return string|void       The crumb html.
	 */
	protected function breadcrumbToDisplay( $item ) {
		$templateItem = $this->getCrumbTemplate( $item );
		if ( empty( $templateItem['template'] ) ) {
			return;
		}

		// Do tags.
		$templateItem['template'] = aioseo()->breadcrumbs->tags->replaceTags( $templateItem['template'], $item );
		$templateItem['template'] = preg_replace_callback(
			'/>(?![^<]*>)(?![^>]*")([^<]*?)>/',
			function ( $matches ) {
				return '>' . $matches[1] . '>';
			},
			htmlentities( $templateItem['template'] )
		);

		// Restore html.
		$templateItem['template'] = aioseo()->helpers->decodeHtmlEntities( $templateItem['template'] );

		// Remove html link if it comes back from the template but we passed no links to it.
		if ( empty( $item['link'] ) ) {
			$templateItem['template'] = $this->stripLink( $templateItem['template'] );
		}

		// Allow shortcodes to run in the final html.
		$templateItem['template'] = do_shortcode( $templateItem['template'] );

		return $templateItem;
	}

	/**
	 * Helper function to get a crumb's template.
	 *
	 * @since 4.1.1
	 *
	 * @param  array $crumb The crumb array.
	 * @return string       The html template.
	 */
	protected function getTemplate( $crumb ) {
		return $this->getDefaultTemplate( $crumb );
	}

	/**
	 * Helper function to get a crumb's template.
	 *
	 * @since 4.1.1
	 *
	 * @param  array $crumb The crumb array.
	 * @return array        The template type and html.
	 */
	protected function getCrumbTemplate( $crumb ) {
		return [
			'templateType' => 'default',
			'template'     => $this->getTemplate( $crumb )
		];
	}

	/**
	 * Default html template.
	 *
	 * @since 4.1.1
	 *
	 * @param  string $type      The crumb's type.
	 * @param  mixed  $reference The crumb's reference.
	 * @return string            The default crumb template.
	 */
	public function getDefaultTemplate( $type = '', $reference = '' ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
		return <<<TEMPLATE
<span class="aioseo-breadcrumb">
	<a href="#breadcrumb_link" title="#breadcrumb_label">#breadcrumb_label</a>
</span>
TEMPLATE;
	}

	/**
	 * Helper function to strip a html link from the crumb.
	 *
	 * @since 4.1.1
	 *
	 * @param  string $html The crumb's html.
	 * @return string       A crumb html without links.
	 */
	public function stripLink( $html ) {
		return preg_replace( '/<a\s.*?>|<\/a>/is', '', (string) $html );
	}

	/**
	 * Get the breadcrumb configured separator.
	 *
	 * @since 4.1.1
	 *
	 * @return string The separator html.
	 */
	public function getSeparator() {
		$separator = aioseo()->options->breadcrumbs->separator;

		$separatorToOverride = aioseo()->breadcrumbs->getOverride( 'separator' );
		if ( ! empty( $separatorToOverride ) ) {
			$separator = $separatorToOverride;
		}

		$separator = apply_filters( 'aioseo_breadcrumbs_separator_symbol', $separator );

		return apply_filters( 'aioseo_breadcrumbs_separator', '<span class="aioseo-breadcrumb-separator">' . esc_html( $separator ) . '</span>' );
	}

	/**
	 * Function to filter the linkCurrentItem option.
	 *
	 * @since 4.1.3
	 *
	 * @return bool Link current item.
	 */
	public function linkCurrentItem() {
		return apply_filters( 'aioseo_breadcrumbs_link_current_item', aioseo()->options->breadcrumbs->linkCurrentItem );
	}
}Shortcode.php000066600000001037151135524620007220 0ustar00<?php
namespace AIOSEO\Plugin\Common\Breadcrumbs;

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

/**
 * Class Shortcode.
 *
 * @since 4.1.1
 */
class Shortcode {
	/**
	 * Shortcode constructor.
	 *
	 * @since 4.1.1
	 */
	public function __construct() {
		add_shortcode( 'aioseo_breadcrumbs', [ $this, 'display' ] );
	}

	/**
	 * Shortcode callback.
	 *
	 * @since 4.1.1
	 *
	 * @return string|void The breadcrumb html.
	 */
	public function display() {
		return aioseo()->breadcrumbs->frontend->display( false );
	}
}Block.php000066600000012762151135524620006327 0ustar00<?php
namespace AIOSEO\Plugin\Common\Breadcrumbs;

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

/**
 * Breadcrumb Block.
 *
 * @since 4.1.1
 */
class Block {
	/**
	 * The primary term list.
	 *
	 * @since 4.3.6
	 *
	 * @var array
	 */
	private $primaryTerm = [];

	/**
	 * The breadcrumb settings.
	 *
	 * @since 4.8.3
	 *
	 * @var array
	 */
	private $breadcrumbSettings = [
		'default'            => true,
		'separator'          => '›',
		'showHomeCrumb'      => true,
		'showTaxonomyCrumbs' => true,
		'showParentCrumbs'   => true,
		'parentTemplate'     => 'default',
		'template'           => 'default',
		'taxonomy'           => ''
	];

	/**
	 * Class constructor.
	 *
	 * @since 4.1.1
	 */
	public function __construct() {
		$this->register();
	}

	/**
	 * Registers the block.
	 *
	 * @since 4.1.1
	 *
	 * @return void
	 */
	public function register() {
		aioseo()->blocks->registerBlock(
			'aioseo/breadcrumbs', [
				'attributes'      => [
					'primaryTerm'        => [
						'type'    => 'string',
						'default' => null
					],
					'breadcrumbSettings' => [
						'type'    => 'object',
						'default' => $this->breadcrumbSettings
					]
				],
				'render_callback' => [ $this, 'render' ]
			]
		);
	}

	/**
	 * Renders the block.
	 *
	 * @since 4.1.1
	 *
	 * @param  array  $blockAttributes The block attributes.
	 * @return string                  The output from the output buffering.
	 */
	public function render( $blockAttributes ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
		// phpcs:disable HM.Security.ValidatedSanitizedInput.InputNotSanitized, HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended
		$postId = ! empty( $_GET['post_id'] ) ? (int) sanitize_text_field( wp_unslash( $_GET['post_id'] ) ) : false;
		// phpcs:enable

		if ( ! empty( $blockAttributes['primaryTerm'] ) ) {
			$this->primaryTerm = json_decode( $blockAttributes['primaryTerm'], true );
		}

		if ( ! empty( $blockAttributes['breadcrumbSettings'] ) ) {
			$this->breadcrumbSettings = $blockAttributes['breadcrumbSettings'];
		}

		aioseo()->breadcrumbs->setOverride( $this->getBlockOverrides() );

		if ( aioseo()->blocks->isRenderingBlockInEditor() && ! empty( $postId ) ) {
			add_filter( 'get_object_terms', [ $this, 'temporarilyAddTerm' ], 10, 3 );
			$breadcrumbs = aioseo()->breadcrumbs->frontend->sideDisplay( false, 'post' === get_post_type( $postId ) ? 'post' : 'single', get_post( $postId ) );
			remove_filter( 'get_object_terms', [ $this, 'temporarilyAddTerm' ], 10 );

			if (
				in_array( 'breadcrumbsEnable', aioseo()->internalOptions->deprecatedOptions, true ) &&
				! aioseo()->options->deprecated->breadcrumbs->enable
			) {
				return '<p>' .
						sprintf(
							// Translators: 1 - The plugin short name ("AIOSEO"), 2 - Opening HTML link tag, 3 - Closing HTML link tag.
							__( 'Breadcrumbs are currently disabled, so this block will be rendered empty. You can enable %1$s\'s breadcrumb functionality under %2$sGeneral Settings > Breadcrumbs%3$s.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
							AIOSEO_PLUGIN_SHORT_NAME,
							'<a href="' . esc_url( admin_url( 'admin.php?page=aioseo-settings#/breadcrumbs' ) ) . '" target="_blank">',
							'</a>'
						) .
						'</p>';
			}

			return $breadcrumbs;
		}

		return aioseo()->breadcrumbs->frontend->display( false );
	}

	/**
	 * Temporarily adds the primary term to the list of terms.
	 *
	 * @since 4.3.6
	 *
	 * @param  array  $terms      The list of terms.
	 * @param  array  $objectIds  The object IDs.
	 * @param  array  $taxonomies The taxonomies.
	 * @return array              The list of terms.
	 */
	public function temporarilyAddTerm( $terms, $objectIds, $taxonomies ) {
		$taxonomy = $taxonomies[0];
		if ( empty( $this->primaryTerm ) || empty( $this->primaryTerm[ $taxonomy ] ) ) {
			return $terms;
		}

		$term = aioseo()->helpers->getTerm( $this->primaryTerm[ $taxonomy ] );
		if ( is_a( $term, 'WP_Term' ) ) {
			$terms[] = $term;
		}

		return $terms;
	}

	/**
	 * Get the block overrides.
	 *
	 * @since 4.8.3
	 *
	 * @return array
	 */
	private function getBlockOverrides() {
		$default = filter_var( $this->breadcrumbSettings['default'], FILTER_VALIDATE_BOOLEAN );
		if ( true === $default || ! aioseo()->pro ) {
			return [];
		}

		return [
			'default'            => false,
			'taxonomy'           => $this->breadcrumbSettings['taxonomy'] ?? '',
			'separator'          => $this->breadcrumbSettings['separator'] ?? '›',
			'showHomeCrumb'      => filter_var( $this->breadcrumbSettings['showHomeCrumb'], FILTER_VALIDATE_BOOLEAN ),
			'showTaxonomyCrumbs' => filter_var( $this->breadcrumbSettings['showTaxonomyCrumbs'], FILTER_VALIDATE_BOOLEAN ),
			'showParentCrumbs'   => filter_var( $this->breadcrumbSettings['showParentCrumbs'], FILTER_VALIDATE_BOOLEAN ),
			'template'           => empty( $this->breadcrumbSettings['template'] ) ? '' : [
				'templateType' => 'custom',
				'template'     => aioseo()->helpers->decodeHtmlEntities( aioseo()->helpers->encodeOutputHtml( $this->breadcrumbSettings['template'] ) )
			],
			'parentTemplate'     => empty( $this->breadcrumbSettings['parentTemplate'] ) ? '' : [
				'templateType' => 'custom',
				'template'     => aioseo()->helpers->decodeHtmlEntities( aioseo()->helpers->encodeOutputHtml( $this->breadcrumbSettings['parentTemplate'] ) )
			],
			'primaryTerm'        => ! empty( $this->primaryTerm[ $this->breadcrumbSettings['taxonomy'] ] ) ? $this->primaryTerm[ $this->breadcrumbSettings['taxonomy'] ] : null
		];
	}
}Breadcrumbs.php000066600000053445151135524620007531 0ustar00<?php
namespace AIOSEO\Plugin\Common\Breadcrumbs {
	// Exit if accessed directly.
	if ( ! defined( 'ABSPATH' ) ) {
		exit;
	}

	/**
	 * Class Breadcrumbs.
	 *
	 * @since 4.1.1
	 */
	class Breadcrumbs {
		/** Instance of the frontend class.
		 *
		 * @since 4.1.1
		 *
		 * @var \AIOSEO\Plugin\Common\Breadcrumbs\Frontend|\AIOSEO\Plugin\Pro\Breadcrumbs\Frontend
		 */
		public $frontend;

		/**
		 * Instance of the shortcode class.
		 *
		 * @since 4.1.1
		 *
		 * @var Shortcode
		 */
		public $shortcode;

		/**
		 * Instance of the block class.
		 *
		 * @since 4.1.1
		 *
		 * @var Block
		 */
		public $block;

		/**
		 * Instance of the tags class.
		 *
		 * @since 4.1.1
		 *
		 * @var Tags
		 */
		public $tags;

		/**
		 * Array of crumbs.
		 *
		 * @since 4.1.1
		 *
		 * @var array An array of crumbs.
		 */
		public $breadcrumbs;

		/**
		 * Array of options to override.
		 *
		 * @since 4.8.3
		 *
		 * @var array An array of options to override.
		 */
		protected $override = [];

		/**
		 * Breadcrumbs constructor.
		 *
		 * @since 4.1.1
		 */
		public function __construct() {
			$this->frontend  = new Frontend();
			$this->shortcode = new Shortcode();
			$this->block     = new Block();

			add_action( 'widgets_init', [ $this, 'registerWidget' ] );

			// Init Tags class later as we need post types registered.
			add_action( 'init', [ $this, 'init' ], 50 );
		}

		public function init() {
			$this->tags = new Tags();
		}

		/**
		 * Helper to add crumbs on the breadcrumb array.
		 *
		 * @since 4.1.1
		 *
		 * @param  array $crumbs A single crumb or an array of crumbs.
		 * @return void
		 */
		public function addCrumbs( $crumbs ) {
			if ( empty( $crumbs ) || ! is_array( $crumbs ) ) {
				return;
			}

			// If it's a single crumb put it inside an array to merge.
			if ( isset( $crumbs['label'] ) ) {
				$crumbs = [ $crumbs ];
			}

			$this->breadcrumbs = array_merge( $this->breadcrumbs, $crumbs );
		}

		/**
		 * Builds a crumb array based on a type and a reference.
		 *
		 * @since 4.1.1
		 *
		 * @param  string $type      The type of breadcrumb ( post, single, page, category, tag, taxonomy, postTypeArchive, date,
		 *                           author, search, notFound, blog ).
		 * @param  mixed  $reference The reference can be an object ( WP_Post | WP_Term | WP_Post_Type | WP_User ), an array, an int or a string.
		 * @param  array  $paged     A reference for a paged crumb.
		 * @return array             An array of breadcrumbs with their label, link, type and reference.
		 */
		public function buildBreadcrumbs( $type, $reference, $paged = [] ) {
			// Clear the breadcrumb array and build a new one.
			$this->breadcrumbs = [];

			// Add breadcrumb prefix.
			$this->addCrumbs( $this->getPrefixCrumb( $type, $reference ) );

			// Set a home page in the beginning of the breadcrumb.
			$this->addCrumbs( $this->maybeGetHomePageCrumb( $type, $reference ) );

			// Woocommerce shop page support.
			$this->addCrumbs( $this->maybeGetWooCommerceShopCrumb() );

			// Blog home.
			if (
				aioseo()->options->breadcrumbs->showBlogHome &&
				in_array( $type, [ 'category', 'tag', 'post', 'author', 'date' ], true )
			) {
				$this->addCrumbs( $this->getBlogCrumb() );
			}

			switch ( $type ) {
				case 'post':
				case 'single':
					$this->addCrumbs( $this->getPostArchiveCrumb( $reference ) );
					$this->addCrumbs( $this->getPostTaxonomyCrumbs( $reference ) );
					$this->addCrumbs( $this->getPostParentCrumbs( $reference ) );
					$this->addCrumbs( $this->getPostCrumb( $reference ) );
					break;
				case 'page':
					$this->addCrumbs( $this->getPostParentCrumbs( $reference, 'page' ) );
					$this->addCrumbs( $this->getPostCrumb( $reference, 'page' ) );
					break;
				case 'category':
				case 'tag':
				case 'taxonomy':
					$this->addCrumbs( $this->getTermTaxonomyParentCrumbs( $reference ) );
					$this->addCrumbs( $this->getTermTaxonomyCrumb( $reference ) );
					break;
				case 'postTypeArchive':
					$this->addCrumbs( $this->getPostTypeArchiveCrumb( $reference ) );
					break;
				case 'date':
					$this->addCrumbs( $this->getDateCrumb( $reference ) );
					break;
				case 'author':
					$this->addCrumbs( $this->getAuthorCrumb( $reference ) );
					break;
				case 'blog':
					$this->addCrumbs( $this->getBlogCrumb() );
					break;
				case 'search':
					$this->addCrumbs( $this->getSearchCrumb( $reference ) );
					break;
				case 'notFound':
					$this->addCrumbs( $this->getNotFoundCrumb() );
					break;
				case 'preview':
					$this->addCrumbs( $this->getPreviewCrumb( $reference ) );
					break;
				case 'wcProduct':
					$this->addCrumbs( $this->getPostTaxonomyCrumbs( $reference ) );
					$this->addCrumbs( $this->getPostParentCrumbs( $reference ) );
					$this->addCrumbs( $this->getPostCrumb( $reference ) );
					break;
				case 'buddypress':
					$this->addCrumbs( aioseo()->standalone->buddyPress->component->getCrumbs() );
					break;
			}

			// Paged crumb.
			if ( ! empty( $paged['paged'] ) ) {
				$this->addCrumbs( $this->getPagedCrumb( $paged ) );
			}

			// Maybe remove the last crumb.
			if ( ! $this->showCurrentItem( $type, $reference ) ) {
				array_pop( $this->breadcrumbs );
			}

			// Remove empty crumbs.
			$this->breadcrumbs = array_filter( $this->breadcrumbs );

			return $this->breadcrumbs;
		}

		/**
		 * Gets the prefix crumb.
		 *
		 * @since 4.1.1
		 *
		 * @param  string $type      The type of breadcrumb.
		 * @param  mixed  $reference The breadcrumb reference.
		 * @return array             A crumb.
		 */
		public function getPrefixCrumb( $type, $reference ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
			if ( 0 === strlen( aioseo()->options->breadcrumbs->breadcrumbPrefix ) ) {
				return [];
			}

			return $this->makeCrumb( aioseo()->options->breadcrumbs->breadcrumbPrefix, '', 'prefix' );
		}

		/**
		 * Gets the 404 crumb.
		 *
		 * @since 4.1.1
		 *
		 * @return array A crumb.
		 */
		public function getNotFoundCrumb() {
			return $this->makeCrumb( aioseo()->options->breadcrumbs->errorFormat404, '', 'notFound' );
		}

		/**
		 * Gets the search crumb.
		 *
		 * @since 4.1.1
		 *
		 * @param  string $searchQuery The search query for reference.
		 * @return array               A crumb.
		 */
		public function getSearchCrumb( $searchQuery ) {
			return $this->makeCrumb( aioseo()->options->breadcrumbs->searchResultFormat, get_search_link( $searchQuery ), 'search', $searchQuery );
		}

		/**
		 * Gets the preview crumb.
		 *
		 * @since 4.1.5
		 *
		 * @param  string $label The preview label.
		 * @return array         A crumb.
		 */
		public function getPreviewCrumb( $label ) {
			return $this->makeCrumb( $label, '', 'preview' );
		}

		/**
		 * Gets the post type archive crumb.
		 *
		 * @since 4.1.1
		 *
		 * @param  \WP_Post_Type $postType The post type object for reference.
		 * @return array                   A crumb.
		 */
		public function getPostTypeArchiveCrumb( $postType ) {
			return $this->makeCrumb( aioseo()->options->breadcrumbs->archiveFormat, get_post_type_archive_link( $postType->name ), 'postTypeArchive', $postType );
		}

		/**
		 * Gets a post crumb.
		 *
		 * @since 4.1.1
		 *
		 * @param  \WP_Post $post    A post object for reference.
		 * @param  string   $type    The breadcrumb type.
		 * @param  string   $subType The breadcrumb subType.
		 * @return array             A crumb.
		 */
		public function getPostCrumb( $post, $type = 'single', $subType = '' ) {
			return $this->makeCrumb( get_the_title( $post ), get_permalink( $post ), $type, $post, $subType );
		}

		/**
		 * Gets the term crumb.
		 *
		 * @since 4.1.1
		 *
		 * @param  \WP_Term $term    The term object for reference.
		 * @param  string   $subType The breadcrumb subType.
		 * @return array             A crumb.
		 */
		public function getTermTaxonomyCrumb( $term, $subType = '' ) {
			return $this->makeCrumb( $term->name, get_term_link( $term ), 'taxonomy', $term, $subType );
		}

		/**
		 * Gets the paged crumb.
		 *
		 * @since 4.1.1
		 *
		 * @param  array $reference The paged array for reference.
		 * @return array             A crumb.
		 */
		public function getPagedCrumb( $reference ) {
			return $this->makeCrumb( sprintf( '%1$s %2$s', __( 'Page', 'all-in-one-seo-pack' ), $reference['paged'] ), $reference['link'], 'paged', $reference );
		}

		/**
		 * Gets the author crumb.
		 *
		 * @since 4.1.1
		 *
		 * @param  \WP_User $wpUser A WP_User object.
		 * @return array            A crumb.
		 */
		public function getAuthorCrumb( $wpUser ) {
			return $this->makeCrumb( $wpUser->display_name, get_author_posts_url( $wpUser->ID ), 'author', $wpUser );
		}

		/**
		 * Gets the date crumb.
		 *
		 * @since 4.1.1
		 *
		 * @param  array $reference An array of year, month and day values.
		 * @return array            A crumb.
		 */
		public function getDateCrumb( $reference ) {
			$dateCrumb = [];
			$addMonth  = false;
			$addYear   = false;
			if ( ! empty( $reference['day'] ) ) {
				$addMonth    = true;
				$addYear     = true;
				$dateCrumb[] = $this->makeCrumb(
					zeroise( (int) $reference['day'], 2 ),
					get_day_link( $reference['year'], $reference['month'], $reference['day'] ),
					'day',
					$reference['day']
				);
			}
			if ( ! empty( $reference['month'] ) || $addMonth ) {
				$addYear     = true;
				$dateCrumb[] = $this->makeCrumb(
					zeroise( (int) $reference['month'], 2 ),
					get_month_link( $reference['year'], $reference['month'] ),
					'month',
					$reference['month']
				);

			}
			if ( ! empty( $reference['year'] ) || $addYear ) {
				$dateCrumb[] = $this->makeCrumb(
					$reference['year'],
					get_year_link( $reference['year'] ),
					'year',
					$reference['year']
				);
			}

			return array_reverse( $dateCrumb );
		}

		/**
		 * Gets an array of crumbs parents for the term.
		 *
		 * @since 4.1.1
		 *
		 * @param  \WP_Term $term A WP_Term object.
		 * @return array          An array of parent crumbs.
		 */
		public function getTermTaxonomyParentCrumbs( $term ) {
			$crumbs = [];

			$termHierarchy = $this->getTermHierarchy( $term->term_id, $term->taxonomy );
			if ( ! empty( $termHierarchy ) ) {
				foreach ( $termHierarchy as $parentTermId ) {
					$parentTerm = aioseo()->helpers->getTerm( $parentTermId, $term->taxonomy );
					$crumbs[]   = $this->getTermTaxonomyCrumb( $parentTerm, 'parent' );
				}
			}

			return $crumbs;
		}

		/**
		 * Helper function to create a standard crumb array.
		 *
		 * @since 4.1.1
		 *
		 * @param  string $label     The crumb label.
		 * @param  string $link      The crumb url.
		 * @param  null   $type      The crumb type.
		 * @param  null   $reference The crumb reference.
		 * @param  null   $subType   The crumb subType ( single/parent ).
		 * @return array             A crumb array.
		 */
		public function makeCrumb( $label, $link = '', $type = null, $reference = null, $subType = null ) {
			return [
				'label'     => $label,
				'link'      => $link,
				'type'      => $type,
				'subType'   => $subType,
				'reference' => $reference
			];
		}

		/**
		 * Gets a post archive crumb if it's post type has archives.
		 *
		 * @since 4.1.1
		 *
		 * @param  int|\WP_Post $post An ID or a WP_Post object.
		 * @return array              A crumb.
		 */
		public function getPostArchiveCrumb( $post ) {
			$postType = get_post_type_object( get_post_type( $post ) );
			if ( ! $postType || ! $postType->has_archive ) {
				return [];
			}

			return $this->makeCrumb( $postType->labels->name, get_post_type_archive_link( $postType->name ), 'postTypeArchive', $postType );
		}

		/**
		 * Gets a post's taxonomy crumbs.
		 *
		 * @since 4.1.1
		 *
		 * @param  int|\WP_Post $post     An ID or a WP_Post object.
		 * @param  null         $taxonomy A taxonomy to use. If none is provided the first one with terms selected will be used.
		 * @return array                  An array of term crumbs.
		 */
		public function getPostTaxonomyCrumbs( $post, $taxonomy = null ) {
			$crumbs = [];

			$overrideTaxonomy = $this->getOverride( 'taxonomy' );
			if ( ! empty( $overrideTaxonomy ) ) {
				$taxonomy = $overrideTaxonomy;
			}

			if ( $taxonomy && ! is_array( $taxonomy ) ) {
				$taxonomy = [ $taxonomy ];
			}

			$termHierarchy = $this->getPostTaxTermHierarchy( $post, $taxonomy );
			if ( ! empty( $termHierarchy['terms'] ) ) {
				foreach ( $termHierarchy['terms'] as $termId ) {
					$term     = aioseo()->helpers->getTerm( $termId, $termHierarchy['taxonomy'] );
					$crumbs[] = $this->makeCrumb( $term->name, get_term_link( $term, $termHierarchy['taxonomy'] ), 'taxonomy', $term, 'parent' );
				}
			}

			return $crumbs;
		}

		/**
		 * Gets the post's parent crumbs.
		 *
		 * @since 4.1.1
		 *
		 * @param  int|\WP_Post $post An ID or a WP_Post object.
		 * @param  string       $type The crumb type.
		 * @return array              An array of the post parent crumbs.
		 */
		public function getPostParentCrumbs( $post, $type = 'single' ) {
			$crumbs = [];
			if ( ! is_post_type_hierarchical( get_post_type( $post ) ) ) {
				return $crumbs;
			}

			$postHierarchy = $this->getPostHierarchy( $post );
			if ( ! empty( $postHierarchy ) ) {
				foreach ( $postHierarchy as $parentID ) {
					// Do not include the Home Page.
					if ( aioseo()->helpers->getHomePageId() === $parentID ) {
						continue;
					}

					$crumbs[] = $this->getPostCrumb( get_post( $parentID ), $type, 'parent' );
				}
			}

			return $crumbs;
		}

		/**
		 * Function to extend on pro for extra functionality.
		 *
		 * @since 4.1.1
		 *
		 * @param  string $type      The type of breadcrumb.
		 * @param  mixed  $reference The breadcrumb reference.
		 * @return bool              Show current item.
		 */
		public function showCurrentItem( $type = null, $reference = null ) {
			return apply_filters( 'aioseo_breadcrumbs_show_current_item', aioseo()->options->breadcrumbs->showCurrentItem, $type, $reference );
		}

		/**
		 * Gets a home page crumb.
		 *
		 * @since 4.1.1
		 *
		 * @param  string     $type      The type of breadcrumb.
		 * @param  mixed      $reference The breadcrumb reference.
		 * @return array|void            The home crumb.
		 */
		public function maybeGetHomePageCrumb( $type = null, $reference = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
			if ( aioseo()->options->breadcrumbs->homepageLink ) {
				return $this->getHomePageCrumb();
			}
		}

		/**
		 * Gets a home page crumb.
		 *
		 * @since 4.1.1
		 *
		 * @return array The home crumb.
		 */
		public function getHomePageCrumb() {
			$homePageId = aioseo()->helpers->getHomePageId();

			$label = '';
			if ( $homePageId ) {
				$label = get_the_title( $homePageId );
			}

			if ( 0 < strlen( aioseo()->options->breadcrumbs->homepageLabel ) ) {
				$label = aioseo()->options->breadcrumbs->homepageLabel;
			}

			// Label fallback.
			if ( empty( $label ) ) {
				$label = __( 'Home', 'all-in-one-seo-pack' );
			}

			return $this->makeCrumb( $label, get_home_url(), 'homePage', aioseo()->helpers->getHomePage() );
		}

		/**
		 * Gets the blog crumb.
		 *
		 * @since 4.1.1
		 *
		 * @return array The blog crumb.
		 */
		public function getBlogCrumb() {
			$crumb = [];

			$blogPage = aioseo()->helpers->getBlogPage();
			if ( null !== $blogPage ) {
				$crumb = $this->makeCrumb( $blogPage->post_title, get_permalink( $blogPage ), 'blog', $blogPage );
			}

			return $crumb;
		}

		/**
		 * Maybe add the shop crumb to products and product categories.
		 *
		 * @since 4.5.5
		 *
		 * @return array The shop crumb.
		 */
		public function maybeGetWooCommerceShopCrumb() {
			$crumb = [];
			if (
				aioseo()->helpers->isWooCommerceShopPage() ||
				aioseo()->helpers->isWooCommerceProductPage() ||
				aioseo()->helpers->isWooCommerceTaxonomyPage()
			) {
				$crumb = $this->getWooCommerceShopCrumb();
			}

			return $crumb;
		}

		/**
		 * Gets the shop crumb.
		 * @see WC_Breadcrumb::prepend_shop_page()
		 *
		 * @since 4.5.5
		 *
		 * @return array The shop crumb.
		 */
		public function getWooCommerceShopCrumb() {
			$crumb = [];

			if (
				! function_exists( 'wc_get_page_id' ) ||
				apply_filters( 'aioseo_woocommerce_breadcrumb_hide_shop', false )
			) {
				return $crumb;
			}

			$shopPageId = wc_get_page_id( 'shop' );
			$shopPage   = get_post( $shopPageId );

			// WC checks if the permalink contains the shop page in the URI, but we prefer to
			// always show the shop page as the first crumb if it exists and it's not the home page.
			if (
				$shopPageId &&
				$shopPage &&
				aioseo()->helpers->getHomePageId() !== $shopPageId
			) {
				$crumb = $this->makeCrumb( get_the_title( $shopPage ), get_permalink( $shopPage ), 'wcShop' );
			}

			return $crumb;
		}

		/**
		 * Gets a post's term hierarchy for a list of taxonomies selecting the one that has a lengthier hierarchy.
		 *
		 * @since 4.1.1
		 *
		 * @param  int|\WP_Post $post                An ID or a WP_Post object.
		 * @param  array        $taxonomies          An array of taxonomy names.
		 * @param  false        $skipUnselectedTerms Allow unselected terms to be filtered out from the crumbs.
		 * @return array                             An array of the taxonomy name + a term hierarchy.
		 */
		public function getPostTaxTermHierarchy( $post, $taxonomies = [], $skipUnselectedTerms = false ) {
			// Get all taxonomies attached to the post.
			if ( empty( $taxonomies ) ) {
				$taxonomies = get_object_taxonomies( get_post_type( $post ), 'objects' );
				$taxonomies = wp_filter_object_list( $taxonomies, [ 'public' => true ], 'and', 'name' );
			}

			foreach ( $taxonomies as $taxonomy ) {
				$primaryTerm         = aioseo()->standalone->primaryTerm->getPrimaryTerm( $post->ID, $taxonomy );
				$overridePrimaryTerm = $this->getOverride( 'primaryTerm' );
				if ( ! empty( $overridePrimaryTerm ) ) {
					$primaryTerm = ! is_a( $overridePrimaryTerm, 'WP_Term' ) ? get_term( $overridePrimaryTerm, $taxonomy ) : $overridePrimaryTerm;
				}

				$terms = wp_get_object_terms( $post->ID, $taxonomy, [
					'orderby' => 'term_id',
					'order'   => 'ASC',
				] );
				// Use the first taxonomy with terms.
				if ( empty( $terms ) || is_wp_error( $terms ) ) {
					continue;
				}

				// Determines the lengthier term hierarchy.
				$termHierarchy = [];
				foreach ( $terms as $term ) {
					// Gets our filtered ancestors.
					$ancestors = $this->getFilteredTermHierarchy( $term->term_id, $term->taxonomy, $skipUnselectedTerms ? $terms : [] );

					// Merge the current term to be used in the breadcrumbs.
					$ancestors = array_merge( $ancestors, [ $term->term_id ] );

					// If the current term is the primary term, use it.
					if ( is_a( $primaryTerm, 'WP_Term' ) && $primaryTerm->term_id === $term->term_id ) {
						$termHierarchy = $ancestors;
						break;
					}

					$termHierarchy = ( count( $termHierarchy ) < count( $ancestors ) ) ? $ancestors : $termHierarchy;
				}

				// Return a top to bottom hierarchy.
				return [
					'taxonomy' => $taxonomy,
					'terms'    => $termHierarchy
				];
			}

			return [];
		}

		/**
		 * Filters a term's parent hierarchy against other terms.
		 *
		 * @since 4.1.1
		 *
		 * @param  int    $termId               A term id.
		 * @param  string $taxonomy             The taxonomy name.
		 * @param  array  $termsToFilterAgainst Terms to filter out of the hierarchy.
		 * @return array                        The term's parent hierarchy.
		 */
		public function getFilteredTermHierarchy( $termId, $taxonomy, $termsToFilterAgainst = [] ) {
			$ancestors = $this->getTermHierarchy( $termId, $taxonomy );

			// Keep only selected terms in the hierarchy.
			if ( ! empty( $termsToFilterAgainst ) ) {
				// If it's a WP_Term array make it a term_id array.
				if ( is_a( current( $termsToFilterAgainst ), 'WP_Term' ) ) {
					$termsToFilterAgainst = wp_list_pluck( $termsToFilterAgainst, 'term_id' );
				}

				$ancestors = array_intersect( $ancestors, $termsToFilterAgainst );
			}

			return $ancestors;
		}

		/**
		 * Gets a term's parent hierarchy.
		 *
		 * @since 4.1.1
		 *
		 * @param  int    $termId   A term id.
		 * @param  string $taxonomy A taxonomy name.
		 * @return array            The term parent hierarchy.
		 */
		public function getTermHierarchy( $termId, $taxonomy ) {
			// Return a top to bottom hierarchy.
			return array_reverse( get_ancestors( $termId, $taxonomy, 'taxonomy' ) );
		}

		/**
		 * Gets a post's parent hierarchy.
		 *
		 * @since 4.1.1
		 *
		 * @param  int|\WP_Post $post An ID or a WP_Post object.
		 * @return array              The post parent hierarchy.
		 */
		public function getPostHierarchy( $post ) {
			$postId = ! empty( $post->ID ) ? $post->ID : $post;

			// Return a top to bottom hierarchy.
			return array_reverse( get_ancestors( $postId, '', 'post_type' ) );
		}

		/**
		 * Register our breadcrumb widget.
		 *
		 * @since 4.1.1
		 *
		 * @return void
		 */
		public function registerWidget() {
			if ( aioseo()->helpers->canRegisterLegacyWidget( 'aioseo-breadcrumb-widget' ) ) {
				register_widget( 'AIOSEO\Plugin\Common\Breadcrumbs\Widget' );
			}
		}

		/**
		 * Setter for the override property.
		 *
		 * @since 4.8.3
		 *
		 * @param  array $toOverride Array containing data to override.
		 * @return void
		 */
		public function setOverride( $toOverride = [] ) {
			$this->override = $toOverride;
		}

		/**
		 * Getter for the override property.
		 *
		 * @since 4.8.3
		 *
		 * @param  string $optionName Optional. The specific option name to retrieve.
		 * @return array              Array containing data to override.
		 */
		public function getOverride( $optionName = null ) {
			if ( empty( $this->override ) ) {
				return $optionName ? null : [];
			}

			$value = $this->override[ $optionName ] ?? null;

			return $optionName ? $value : $this->override;
		}
	}
}

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

	if ( ! function_exists( 'aioseo_breadcrumbs' ) ) {
		/**
		 * Global function for breadcrumbs output.
		 *
		 * @since 4.1.1
		 *
		 * @param  boolean     $echo Echo or return the output.
		 * @return string|void       The output.
		 */
		function aioseo_breadcrumbs( $echo = true ) {
			return aioseo()->breadcrumbs->frontend->display( $echo );
		}
	}
}Widget.php000066600000006410151135524620006511 0ustar00<?php
namespace AIOSEO\Plugin\Common\Breadcrumbs;

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

/**
 * Class Widget.
 *
 * @since 4.1.1
 */
class Widget extends \WP_Widget {
	/**
	 * The default attributes.
	 *
	 * @since 4.2.7
	 *
	 * @var array
	 */
	private $defaults = [];

	/**
	 * Class constructor.
	 *
	 * @since 4.1.1
	 */
	public function __construct() {
		// Widget defaults.
		$this->defaults = [
			'title' => ''
		];

		// Widget Slug.
		$widgetSlug = 'aioseo-breadcrumb-widget';

		// Widget basics.
		$widgetOps = [
			'classname'   => $widgetSlug,
			'description' => esc_html__( 'Display the current page breadcrumb.', 'all-in-one-seo-pack' ),
		];

		// Widget controls.
		$controlOps = [
			'id_base' => $widgetSlug,
		];

		// Translators: 1 - The plugin short name ("AIOSEO").
		$name = sprintf( esc_html__( '%1$s - Breadcrumbs', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME );
		$name .= ' ' . esc_html__( '(legacy)', 'all-in-one-seo-pack' );
		parent::__construct( $widgetSlug, $name, $widgetOps, $controlOps );
	}

	/**
	 * Widget callback.
	 *
	 * @since 4.1.1
	 *
	 * @param  array $args     Widget args.
	 * @param  array $instance The widget instance options.
	 * @return void
	 */
	public function widget( $args, $instance ) {
		// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
		// Merge with defaults.
		$instance = wp_parse_args( (array) $instance, $this->defaults );

		echo $args['before_widget'];

		// Title.
		if ( ! empty( $instance['title'] ) ) {
			echo $args['before_title'];
			echo apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
			echo $args['after_title'];
		}

		// If not being previewed in the Customizer maybe show the dummy preview.
		if (
			! is_customize_preview() &&
			(
				false !== strpos( wp_get_referer(), admin_url( 'widgets.php' ) ) ||
				false !== strpos( wp_get_referer(), admin_url( 'customize.php' ) )
			)
		) {
			aioseo()->breadcrumbs->frontend->preview();
		} else {
			aioseo()->breadcrumbs->frontend->display();
		}

		echo $args['after_widget'];
		// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Widget option update.
	 *
	 * @since 4.1.1
	 *
	 * @param array $newInstance New instance options.
	 * @param array $oldInstance Old instance options.
	 * @return array              Processed new instance options.
	 */
	public function update( $newInstance, $oldInstance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
		$newInstance['title'] = wp_strip_all_tags( $newInstance['title'] );

		return $newInstance;
	}

	/**
	 * Widget options form.
	 *
	 * @since 4.1.1
	 *
	 * @param array $instance The widget instance options.
	 * @return void
	 */
	public function form( $instance ) {
		// Merge with defaults.
		$instance = wp_parse_args( (array) $instance, $this->defaults );
		?>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
				<?php echo esc_html( __( 'Title:', 'all-in-one-seo-pack' ) ); ?>
			</label>
			<input
					type="text"
					id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
					name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
					value="<?php echo esc_attr( $instance['title'] ); ?>"
					class="widefat"
			/>
		</p>
		<?php
	}
}Tags.php000066600000026211151135524620006165 0ustar00<?php
namespace AIOSEO\Plugin\Common\Breadcrumbs;

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

/**
 * Class to replace tag values with their data counterparts.
 *
 * @since 4.1.1
 */
class Tags {
	/**
	 * Tags constructor.
	 *
	 * @since 4.1.1
	 */
	public function __construct() {
		aioseo()->tags->addContext( $this->getContexts() );
		aioseo()->tags->addTags( $this->getTags() );
	}

	/**
	 * Replace the tags in the string provided.
	 *
	 * @since 4.1.1
	 *
	 * @param  string  $string           The string with tags.
	 * @param  array   $item             The breadcrumb item.
	 * @param  boolean $stripPunctuation Whether we should strip punctuation after the tags have been converted.
	 * @return string                    The string with tags replaced.
	 */
	public function replaceTags( $string, $item, $stripPunctuation = false ) {
		if ( ! $string || ! preg_match( '/#/', (string) $string ) ) {
			return $string;
		}

		// Replace separator tag so we don't strip it as punctuation.
		$separatorTag = aioseo()->tags->denotationChar . 'separator_sa';
		$string       = preg_replace( "/$separatorTag(?![a-zA-Z0-9_])/im", '>thisisjustarandomplaceholder<', (string) $string );

		// Replace custom breadcrumb tags.
		foreach ( $this->getTags() as $tag ) {
			$tagId   = aioseo()->tags->denotationChar . $tag['id'];
			$pattern = "/$tagId(?![a-zA-Z0-9_])/im";
			if ( preg_match( $pattern, (string) $string ) ) {
				$tagValue = str_replace( '$', '\$', (string) $this->getTagValue( $tag, $item ) );
				$string   = preg_replace( $pattern, $tagValue, (string) $string );
			}
		}

		if ( $stripPunctuation ) {
			$string = aioseo()->helpers->stripPunctuation( $string );
		}

		// Remove any remaining tags from the title attribute.
		$string = preg_replace_callback( '/title="([^"]*)"/i', function ( $matches ) {
			$sanitizedTitle = wp_strip_all_tags( html_entity_decode( $matches[1] ) );

			return 'title="' . esc_attr( $sanitizedTitle ) . '"';
		}, html_entity_decode( $string ) );

		return preg_replace(
			'/>thisisjustarandomplaceholder<(?![a-zA-Z0-9_])/im',
			aioseo()->helpers->decodeHtmlEntities( aioseo()->options->searchAppearance->global->separator ),
			(string) $string
		);
	}

	/**
	 * Get the value of the tag to replace.
	 *
	 * @since 4.1.1
	 *
	 * @param  string $tag  The tag to look for.
	 * @param  int    $item The crumb array.
	 * @return string       The value of the tag.
	 */
	public function getTagValue( $tag, $item ) {
		$product = false;
		if ( 0 === stripos( $tag['id'], 'breadcrumb_wc_product_' ) ) {
			$product = wc_get_product( $item['reference'] );
			if ( ! $product ) {
				return;
			}
		}

		switch ( $tag['id'] ) {
			case 'breadcrumb_link':
				return $item['link'];
			case 'breadcrumb_separator':
				return aioseo()->breadcrumbs->frontend->getSeparator();
			case 'breadcrumb_wc_product_price':
				return $product ? wc_price( $product->get_price() ) : '';
			case 'breadcrumb_wc_product_sku':
				return $product ? $product->get_sku() : '';
			case 'breadcrumb_wc_product_brand':
				return $product ? aioseo()->helpers->getWooCommerceBrand( $product->get_id() ) : '';
			case 'breadcrumb_author_first_name':
				return $item['reference']->first_name;
			case 'breadcrumb_author_last_name':
				return $item['reference']->last_name;
			case 'breadcrumb_archive_post_type_name':
				return $item['reference']->label;
			case 'breadcrumb_search_string':
				return $item['reference'];
			case 'breadcrumb_format_page_number':
				return $item['reference']['paged'];
			default:
				return $item['label'];
		}
	}

	/**
	 * Gets our breadcrumb custom tags.
	 *
	 * @since 4.1.1
	 *
	 * @return array An array of tags.
	 */
	public function getTags() {
		$tags = [
			[
				'id'          => 'breadcrumb_link',
				'name'        => __( 'Permalink', 'all-in-one-seo-pack' ),
				'description' => __( 'The permalink.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_label',
				'name'        => __( 'Label', 'all-in-one-seo-pack' ),
				'description' => __( 'The label.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_post_title',
				// Translators: 1 - The type of page (Post, Page, Category, Tag, etc.).
				'name'        => sprintf( __( '%1$s Title', 'all-in-one-seo-pack' ), 'Post' ),
				'description' => __( 'The original title of the current post.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_taxonomy_title',
				// Translators: 1 - The type of page (Post, Page, Category, Tag, etc.).
				'name'        => sprintf( __( '%1$s Title', 'all-in-one-seo-pack' ), 'Category' ),
				// Translators: 1 - The name of a taxonomy.
				'description' => sprintf( __( 'The %1$s title.', 'all-in-one-seo-pack' ), 'Category' )
			],
			[
				'id'          => 'breadcrumb_separator',
				'name'        => __( 'Separator', 'all-in-one-seo-pack' ),
				'description' => __( 'The crumb separator.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_blog_page_title',
				'name'        => __( 'Blog Page Title', 'all-in-one-seo-pack' ),
				'description' => __( 'The blog page title.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_author_display_name',
				'name'        => __( 'Author Display Name', 'all-in-one-seo-pack' ),
				'description' => __( 'The author\'s display name.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_author_first_name',
				'name'        => __( 'Author First Name', 'all-in-one-seo-pack' ),
				'description' => __( 'The author\'s first name.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_author_last_name',
				'name'        => __( 'Author Last Name', 'all-in-one-seo-pack' ),
				'description' => __( 'The author\'s last name.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_search_result_format',
				'name'        => __( 'Search result format', 'all-in-one-seo-pack' ),
				'description' => __( 'The search result format.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_404_error_format',
				'name'        => __( '404 Error Format', 'all-in-one-seo-pack' ),
				'description' => __( 'The 404 error format.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_date_archive_year',
				'name'        => __( 'Year', 'all-in-one-seo-pack' ),
				'description' => __( 'The year.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_date_archive_month',
				'name'        => __( 'Month', 'all-in-one-seo-pack' ),
				'description' => __( 'The month.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_date_archive_day',
				'name'        => __( 'Day', 'all-in-one-seo-pack' ),
				'description' => __( 'The day.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_search_string',
				'name'        => __( 'Search String', 'all-in-one-seo-pack' ),
				'description' => __( 'The search string.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_format_page_number',
				'name'        => __( 'Page Number', 'all-in-one-seo-pack' ),
				'description' => __( 'The page number.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_archive_post_type_format',
				'name'        => __( 'Archive format', 'all-in-one-seo-pack' ),
				'description' => __( 'The archive format.', 'all-in-one-seo-pack' )
			],
			[
				'id'          => 'breadcrumb_archive_post_type_name',
				'name'        => __( 'Post Type Name', 'all-in-one-seo-pack' ),
				'description' => __( 'The archive post type name.', 'all-in-one-seo-pack' )
			]
		];

		$postTypes = aioseo()->helpers->getPublicPostTypes();
		foreach ( $postTypes as $postType ) {
			if ( 'product' === $postType['name'] && aioseo()->helpers->isWoocommerceActive() ) {
				$tags[] = [
					'id'          => 'breadcrumb_wc_product_price',
					// Translators: 1 - The name of a post type.
					'name'        => sprintf( __( '%1$s Price', 'all-in-one-seo-pack' ), $postType['singular'] ),
					// Translators: 1 - The name of a post type.
					'description' => sprintf( __( 'The %1$s price.', 'all-in-one-seo-pack' ), $postType['singular'] )
				];
				$tags[] = [
					'id'          => 'breadcrumb_wc_product_sku',
					// Translators: 1 - The name of a post type.
					'name'        => sprintf( __( '%1$s SKU', 'all-in-one-seo-pack' ), $postType['singular'] ),
					// Translators: 1 - The name of a post type.
					'description' => sprintf( __( 'The %1$s SKU.', 'all-in-one-seo-pack' ), $postType['singular'] )
				];
				$tags[] = [
					'id'          => 'breadcrumb_wc_product_brand',
					// Translators: 1 - The name of a post type.
					'name'        => sprintf( __( '%1$s Brand', 'all-in-one-seo-pack' ), $postType['singular'] ),
					// Translators: 1 - The name of a post type.
					'description' => sprintf( __( 'The %1$s brand.', 'all-in-one-seo-pack' ), $postType['singular'] )
				];
			}
		}

		return $tags;
	}

	/**
	 * Gets our breadcrumb contexts.
	 *
	 * @since 4.1.1
	 *
	 * @return array An array of contexts.
	 */
	public function getContexts() {
		$contexts = [];

		$baseTags = [ 'breadcrumb_link', 'breadcrumb_separator' ];

		$postTypes = aioseo()->helpers->getPublicPostTypes();
		foreach ( $postTypes as $postType ) {
			$contexts[ 'breadcrumbs-post-type-' . $postType['name'] ] = array_merge( $baseTags, [ 'breadcrumb_post_title' ] );

			if ( 'product' === $postType['name'] && aioseo()->helpers->isWoocommerceActive() ) {
				$contexts[ 'breadcrumbs-post-type-' . $postType['name'] ] = array_merge( $contexts[ 'breadcrumbs-post-type-' . $postType['name'] ], [
					'breadcrumb_wc_product_price',
					'breadcrumb_wc_product_sku',
					'breadcrumb_wc_product_brand'
				] );
			}
		}

		$taxonomies = aioseo()->helpers->getPublicTaxonomies();
		foreach ( $taxonomies as $taxonomy ) {
			$contexts[ 'breadcrumbs-taxonomy-' . $taxonomy['name'] ] = array_merge( $baseTags, [ 'breadcrumb_taxonomy_title' ] );
		}

		$archives = aioseo()->helpers->getPublicPostTypes( false, true, true );
		foreach ( $archives as $archive ) {
			$contexts[ 'breadcrumbs-post-type-archive-' . $archive['name'] ] = array_merge( $baseTags, [
				'breadcrumb_archive_post_type_format',
				'breadcrumb_archive_post_type_name'
			] );
		}

		$contexts['breadcrumbs-blog-archive'] = array_merge( $baseTags, [ 'breadcrumb_blog_page_title' ] );

		$contexts['breadcrumbs-author'] = array_merge( $baseTags, [
			'breadcrumb_author_display_name',
			'breadcrumb_author_first_name',
			'breadcrumb_author_last_name'
		] );

		$contexts['breadcrumbs-search']             = array_merge( $baseTags, [ 'breadcrumb_search_result_format', 'breadcrumb_search_string' ] );
		$contexts['breadcrumbs-notFound']           = array_merge( $baseTags, [ 'breadcrumb_404_error_format' ] );
		$contexts['breadcrumbs-date-archive-year']  = array_merge( $baseTags, [ 'breadcrumb_date_archive_year' ] );
		$contexts['breadcrumbs-date-archive-month'] = array_merge( $baseTags, [ 'breadcrumb_date_archive_month' ] );
		$contexts['breadcrumbs-date-archive-day']   = array_merge( $baseTags, [ 'breadcrumb_date_archive_day' ] );

		$contexts['breadcrumbs-format-archive'] = [ 'breadcrumb_archive_post_type_name' ];
		$contexts['breadcrumbs-format-search']  = [ 'breadcrumb_search_string' ];
		$contexts['breadcrumbs-format-paged']   = [ 'breadcrumb_format_page_number' ];

		return $contexts;
	}
}