ID !== (int) $pages_settings['category'] && $post->ID !== (int) $pages_settings['tag'] ) { return $title; } $wpseo_titles = get_option( 'wpseo_titles' ); $sep_options = WPSEO_Option_Titles::get_instance()->get_separator_options(); if ( isset( $wpseo_titles['separator'] ) && isset( $sep_options[ $wpseo_titles['separator'] ] ) ) { $sep = $sep_options[ $wpseo_titles['separator'] ]; } else { $sep = '-'; // Setting default separator if Admin didn't set it from backed } $replacements = array( '%%sep%%' => $sep, '%%page%%' => '', '%%primary_category%%' => '', '%%sitename%%' => sanitize_text_field( get_bloginfo( 'name' ) ) ); $title_template = ''; // Category page if ( $post->ID === (int) $pages_settings['category'] ) { if ( $slug = get_query_var( 'aiopod_category' ) ) { // Get Archive SEO title if ( array_key_exists( 'title-tax-aiopod_categories', $wpseo_titles ) ) { $title_template = $wpseo_titles['title-tax-aiopod_categories']; } // Get Term SEO title if ( $term = get_term_by( 'slug', $slug, 'aiopod_categories' ) ) { $replacements['%%term_title%%'] = $term->name; $meta = get_option( 'wpseo_taxonomy_meta' ); if ( array_key_exists( 'aiopod_categories', $meta ) ) { if ( array_key_exists( $term->term_id, $meta['aiopod_categories'] ) ) { if ( array_key_exists( 'wpseo_title', $meta['aiopod_categories'][ $term->term_id ] ) ) { $title_template = $meta['aiopod_categories'][ $term->term_id ]['wpseo_title']; } } } } } } // Tag page if ( $post->ID === (int) $pages_settings['tag'] ) { if ( $slug = get_query_var( 'aiopod_tag' ) ) { // Get Archive SEO title if ( array_key_exists( 'title-tax-aiopod_tags', $wpseo_titles ) ) { $title_template = $wpseo_titles['title-tax-aiopod_tags']; } // Get Term SEO title if ( $term = get_term_by( 'slug', $slug, 'aiopod_tags' ) ) { $replacements['%%term_title%%'] = $term->name; $meta = get_option( 'wpseo_taxonomy_meta' ); if ( array_key_exists( 'aiopod_tags', $meta ) ) { if ( array_key_exists( $term->term_id, $meta['aiopod_tags'] ) ) { if ( array_key_exists( 'wpseo_title', $meta['aiopod_tags'][ $term->term_id ] ) ) { $title_template = $meta['aiopod_tags'][ $term->term_id ]['wpseo_title']; } } } } } } // Participant page if ( $post->ID === (int) $pages_settings['participant'] ) { if ( $slug = get_query_var( 'aiopod_participant' ) ) { // Get Archive SEO title if ( array_key_exists( 'title-tax-aiopod_participants', $wpseo_titles ) ) { $title_template = $wpseo_titles['title-tax-aiopod_participants']; } // Get Term SEO title if ( $term = get_term_by( 'slug', $slug, 'aiopod_participants' ) ) { $replacements['%%term_title%%'] = $term->name; $meta = get_option( 'wpseo_taxonomy_meta' ); if ( array_key_exists( 'aiopod_participants', $meta ) ) { if ( array_key_exists( $term->term_id, $meta['aiopod_participants'] ) ) { if ( array_key_exists( 'wpseo_title', $meta['aiopod_participants'][ $term->term_id ] ) ) { $title_template = $meta['aiopod_participants'][ $term->term_id ]['wpseo_title']; } } } } } } // Return if ( ! empty( $title_template ) ) { $title = strtr( $title_template, $replacements ); } return $title; } /** * Construct Yoast SEO description for our category and user podcasts pages. * * @since 1.0.0 * @param array $desc The Yoast description. * @return Filtered description. */ public function wpseo_metadesc( $desc ) { global $post; if ( ! isset( $post ) ) { return $desc; } $pages_settings = aiopod_get_option( 'aiopod_pages_settings' ); if ( $post->ID != $pages_settings['category'] && $post->ID != $pages_settings['tag'] ) { return $desc; } $wpseo_titles = get_option( 'wpseo_titles' ); $sep_options = WPSEO_Option_Titles::get_instance()->get_separator_options(); if ( isset( $wpseo_titles['separator'] ) && isset( $sep_options[ $wpseo_titles['separator'] ] ) ) { $sep = $sep_options[ $wpseo_titles['separator'] ]; } else { $sep = '-'; // Setting default separator if Admin didn't set it from backed } $replacements = array( '%%sep%%' => $sep, '%%page%%' => '', '%%title%%' => '', '%%primary_category%%' => '', '%%sitename%%' => sanitize_text_field( get_bloginfo( 'name' ) ) ); $desc_template = ''; // Category page if ( $post->ID === (int) $pages_settings['category'] ) { if ( $slug = get_query_var( 'aiopod_category' ) ) { // Get Archive SEO desc if ( array_key_exists( 'metadesc-tax-aiopod_categories', $wpseo_titles ) ) { $desc_template = $wpseo_titles['metadesc-tax-aiopod_categories']; } // Get Term SEO desc if ( $term = get_term_by( 'slug', $slug, 'aiopod_categories' ) ) { $replacements['%%term_title%%'] = $term->name; $meta = get_option( 'wpseo_taxonomy_meta' ); if ( array_key_exists( 'aiopod_categories', $meta ) ) { if ( array_key_exists( $term->term_id, $meta['aiopod_categories'] ) ) { if ( array_key_exists( 'wpseo_desc', $meta['aiopod_categories'][ $term->term_id ] ) ) { $desc_template = $meta['aiopod_categories'][ $term->term_id ]['wpseo_desc']; } } } } } } // Tag page if ( $post->ID === (int) $pages_settings['tag'] ) { if ( $slug = get_query_var( 'aiopod_tag' ) ) { // Get Archive SEO desc if ( array_key_exists( 'metadesc-tax-aiopod_tags', $wpseo_titles ) ) { $desc_template = $wpseo_titles['metadesc-tax-aiopod_tags']; } // Get Term SEO desc if ( $term = get_term_by( 'slug', $slug, 'aiopod_tags' ) ) { $replacements['%%term_title%%'] = $term->name; $meta = get_option( 'wpseo_taxonomy_meta' ); if ( array_key_exists( 'aiopod_tags', $meta ) ) { if ( array_key_exists( $term->term_id, $meta['aiopod_tags'] ) ) { if ( array_key_exists( 'wpseo_desc', $meta['aiopod_tags'][ $term->term_id ] ) ) { $desc_template = $meta['aiopod_tags'][ $term->term_id ]['wpseo_desc']; } } } } } } // Participant page if ( $post->ID === (int) $pages_settings['participant'] ) { if ( $slug = get_query_var( 'aiopod_participant' ) ) { // Get Archive SEO desc if ( array_key_exists( 'metadesc-tax-aiopod_participants', $wpseo_titles ) ) { $desc_template = $wpseo_titles['metadesc-tax-aiopod_participants']; } // Get Term SEO desc if ( $term = get_term_by( 'slug', $slug, 'aiopod_participants' ) ) { $replacements['%%term_title%%'] = $term->name; $meta = get_option( 'wpseo_taxonomy_meta' ); if ( array_key_exists( 'aiopod_participants', $meta ) ) { if ( array_key_exists( $term->term_id, $meta['aiopod_participants'] ) ) { if ( array_key_exists( 'wpseo_desc', $meta['aiopod_participants'][ $term->term_id ] ) ) { $desc_template = $meta['aiopod_participants'][ $term->term_id ]['wpseo_desc']; } } } } } } // Return if ( ! empty( $desc_template ) ) { $desc = strtr( $desc_template, $replacements ); } return $desc; } /** * Override the Yoast SEO canonical URL on our category and user podcasts pages. * * @since 1.0.0 * @param array $url The Yoast canonical URL. * @return Filtered canonical URL. */ public function wpseo_canonical( $url ) { global $post; if ( ! isset( $post ) ) { return $url; } $pages_settings = aiopod_get_option( 'aiopod_pages_settings' ); // Category page if ( $post->ID === (int) $pages_settings['category'] ) { if ( $slug = get_query_var( 'aiopod_category' ) ) { if ( $term = get_term_by( 'slug', $slug, 'aiopod_categories' ) ) { $url = aiopod_get_category_page_url( $term ); } } } // Tag page if ( $post->ID === (int) $pages_settings['tag'] ) { if ( $slug = get_query_var( 'aiopod_tag' ) ) { if ( $term = get_term_by( 'slug', $slug, 'aiopod_tags' ) ) { $url = aiopod_get_tag_page_url( $term ); } } } // Participant page if ( $post->ID === (int) $pages_settings['participant'] ) { if ( $slug = get_query_var( 'aiopod_participant' ) ) { if ( $term = get_term_by( 'slug', $slug, 'aiopod_participants' ) ) { $url = aiopod_get_participant_page_url( $term ); } } } // Return return $url; } /** * Override the Yoast SEO Open Graph image URLs on our plugin pages. * * @since 1.0.0 * @param array $url The Yoast image URL. * @return Filtered image URL. */ public function wpseo_opengraph_image( $url ) { global $post; if ( ! isset( $post ) ) { return $url; } if ( is_singular( array( 'aiopod_podcasts', 'aiopod_episodes' ) ) ) { $image = get_post_meta( $post->ID, '_yoast_wpseo_opengraph-image', true ); if ( empty( $image ) ) { $image_data = aiopod_get_image( array( 'post_id' => $post->ID, 'size' => 'large', 'alt' => '' ) ); $image = $image_data['src']; } if ( ! empty( $image ) ) { $url = $image; } } else { $pages_settings = aiopod_get_option( 'aiopod_pages_settings' ); // Category page if ( $post->ID === (int) $pages_settings['category'] ) { if ( $slug = get_query_var( 'aiopod_category' ) ) { if ( $term = get_term_by( 'slug', $slug, 'aiopod_categories' ) ) { $meta = get_option( 'wpseo_taxonomy_meta' ); $image = ''; if ( array_key_exists( 'aiopod_categories', $meta ) ) { // Get custom share image from Yoast if ( array_key_exists( $term->term_id, $meta['aiopod_categories'] ) ) { if ( array_key_exists( 'wpseo_opengraph-image', $meta['aiopod_categories'][ $term->term_id ] ) ) { $image = $meta['aiopod_categories'][ $term->term_id ]['wpseo_opengraph-image']; } } } if ( empty( $image ) ) { $image_data = aiopod_get_image( array( 'term_id' => $term->term_id, 'size' => 'large', 'alt' => '' ) ); $image = $image_data['src']; } if ( ! empty( $image ) ) { $url = $image; } } } } // Tag page if ( $post->ID === (int) $pages_settings['tag'] ) { if ( $slug = get_query_var( 'aiopod_tag' ) ) { if ( $term = get_term_by( 'slug', $slug, 'aiopod_tags' ) ) { $meta = get_option( 'wpseo_taxonomy_meta' ); $image = ''; if ( array_key_exists( 'aiopod_tags', $meta ) ) { // Get custom share image from Yoast if ( array_key_exists( $term->term_id, $meta['aiopod_tags'] ) ) { if ( array_key_exists( 'wpseo_opengraph-image', $meta['aiopod_tags'][ $term->term_id ] ) ) { $image = $meta['aiopod_tags'][ $term->term_id ]['wpseo_opengraph-image']; } } } if ( ! empty( $image ) ) { $url = $image; } } } } // Participant page if ( $post->ID === (int) $pages_settings['participant'] ) { if ( $slug = get_query_var( 'aiopod_participant' ) ) { if ( $term = get_term_by( 'slug', $slug, 'aiopod_participants' ) ) { $meta = get_option( 'wpseo_taxonomy_meta' ); $image = ''; if ( array_key_exists( 'aiopod_participants', $meta ) ) { // Get custom share image from Yoast if ( array_key_exists( $term->term_id, $meta['aiopod_participants'] ) ) { if ( array_key_exists( 'wpseo_opengraph-image', $meta['aiopod_participants'][ $term->term_id ] ) ) { $image = $meta['aiopod_participants'][ $term->term_id ]['wpseo_opengraph-image']; } } } if ( ! empty( $image ) ) { $url = $image; } } } } } // Return return $url; } /** * Add custom Twitter Social Share images for Yoast SEO. * * @since 1.0.0 * @param array $url The Yoast image URL. * @return Filtered image URL. */ public function wpseo_twitter_image( $url ) { global $post; if ( ! isset( $post ) ) { return $url; } if ( is_singular( array( 'aiopod_podcasts', 'aiopod_episodes' ) ) ) { $image = get_post_meta( $post->ID, '_yoast_wpseo_twitter-image', true ); if ( empty( $image ) ) { $image_data = aiopod_get_image( array( 'post_id' => $post->ID, 'size' => 'large', 'alt' => '' ) ); $image = $image_data['src']; } if ( ! empty( $image ) ) { $url = $image; } } else { $pages_settings = aiopod_get_option( 'aiopod_pages_settings' ); // Category page if ( $post->ID === (int) $pages_settings['category'] ) { if ( $slug = get_query_var( 'aiopod_category' ) ) { if ( $term = get_term_by( 'slug', $slug, 'aiopod_categories' ) ) { $meta = get_option( 'wpseo_taxonomy_meta' ); $image = ''; if ( array_key_exists( 'aiopod_categories', $meta ) ) { // Get custom share image from Yoast if ( array_key_exists( $term->term_id, $meta['aiopod_categories'] ) ) { if ( array_key_exists( 'wpseo_twitter-image', $meta['aiopod_categories'][ $term->term_id ] ) ) { $image = $meta['aiopod_categories'][ $term->term_id ]['wpseo_twitter-image']; } } } if ( empty( $image ) ) { $image_data = aiopod_get_image( array( 'term_id' => $term->term_id, 'size' => 'large', 'alt' => '' ) ); $image = $image_data['src']; } if ( ! empty( $image ) ) { $url = $image; } } } } // Tag page if ( $post->ID === (int) $pages_settings['tag'] ) { if ( $slug = get_query_var( 'aiopod_tag' ) ) { if ( $term = get_term_by( 'slug', $slug, 'aiopod_tags' ) ) { $meta = get_option( 'wpseo_taxonomy_meta' ); $image = ''; if ( array_key_exists( 'aiopod_tags', $meta ) ) { // Get custom share image from Yoast if ( array_key_exists( $term->term_id, $meta['aiopod_tags'] ) ) { if ( array_key_exists( 'wpseo_twitter-image', $meta['aiopod_tags'][ $term->term_id ] ) ) { $image = $meta['aiopod_tags'][ $term->term_id ]['wpseo_twitter-image']; } } } if ( ! empty( $image ) ) { $url = $image; } } } } // Participant page if ( $post->ID === (int) $pages_settings['participant'] ) { if ( $slug = get_query_var( 'aiopod_participant' ) ) { if ( $term = get_term_by( 'slug', $slug, 'aiopod_participants' ) ) { $meta = get_option( 'wpseo_taxonomy_meta' ); $image = ''; if ( array_key_exists( 'aiopod_participants', $meta ) ) { // Get custom share image from Yoast if ( array_key_exists( $term->term_id, $meta['aiopod_participants'] ) ) { if ( array_key_exists( 'wpseo_twitter-image', $meta['aiopod_participants'][ $term->term_id ] ) ) { $image = $meta['aiopod_participants'][ $term->term_id ]['wpseo_twitter-image']; } } } if ( ! empty( $image ) ) { $url = $image; } } } } } // Return return $url; } /** * Filter Yoast SEO breadcrumbs. * * @since 1.0.0 * @param array $crumbs Array of crumbs. * @return array $crumbs Filtered array of crumbs. */ public function wpseo_breadcrumb_links( $crumbs ) { global $post; if ( empty( $post ) ) { return $crumbs; } // Explicit CPT → option key mapping (future-proof) $archive_map = array( 'aiopod_podcasts' => 'podcast_archive_page', 'aiopod_episodes' => 'episode_archive_page' ); $allowed_post_types = array_keys( $archive_map ); if ( is_singular( $allowed_post_types ) ) { // The queried object, not the global $post, so a theme calling the breadcrumbs from // inside a secondary loop still gets the trail for the page being viewed $crumbs = $this->add_archive_page_crumbs( $crumbs, get_post_type( get_queried_object_id() ), $archive_map ); } else { $pages_settings = aiopod_get_option( 'aiopod_pages_settings' ); // On every page the plugin manages, fill in any ancestor Yoast has not supplied // itself, so a page nested under the podcast archive reads as the whole path to it $plugin_pages = array( 'category', 'tag', 'participant', 'podcast_archive_page', 'episode_archive_page' ); foreach ( $plugin_pages as $page_key ) { if ( ! empty( $pages_settings[ $page_key ] ) && $post->ID === (int) $pages_settings[ $page_key ] ) { $crumbs = $this->add_page_ancestor_crumbs( $crumbs, $post->ID ); break; } } if ( $post->ID === (int) $pages_settings['category'] ) { if ( $slug = get_query_var( 'aiopod_category' ) ) { if ( $term = get_term_by( 'slug', $slug, 'aiopod_categories' ) ) { // Include the parent categories if available if ( $ancestors = get_ancestors( $term->term_id, 'aiopod_categories' ) ) { $ancestors = array_reverse( $ancestors ); foreach ( $ancestors as $term_id ) { if ( $parent_term = get_term_by( 'term_id', $term_id, 'aiopod_categories' ) ) { $crumbs[] = array( 'text' => $parent_term->name, 'url' => aiopod_get_category_page_url( $parent_term ) ); } } } // Include the current category page. The URL matters: Yoast drops the whole // BreadcrumbList when a crumb has no `url` key at all $crumbs[] = array( 'text' => $term->name, 'url' => aiopod_get_category_page_url( $term ) ); } } } if ( $post->ID === (int) $pages_settings['tag'] ) { if ( $slug = get_query_var( 'aiopod_tag' ) ) { if ( $term = get_term_by( 'slug', $slug, 'aiopod_tags' ) ) { $crumbs[] = array( 'text' => $term->name, 'url' => aiopod_get_tag_page_url( $term ) ); } } } if ( $post->ID === (int) $pages_settings['participant'] ) { if ( $slug = get_query_var( 'aiopod_participant' ) ) { if ( $term = get_term_by( 'slug', $slug, 'aiopod_participants' ) ) { $crumbs[] = array( 'text' => $term->name, 'url' => aiopod_get_participant_page_url( $term ) ); } } } } return $crumbs; } /** * Build the trail of a single podcast or episode. * * An episode reads `Home > Podcasts > Show > Episode`: the show it belongs to is the useful step, * and the episode archive is not on the way to it, so that crumb is replaced rather than kept. * A podcast reads `Home > Podcasts > Show`. Both are the same shape whatever the archive pages * are set to, which is the point. * * @access private * @since 1.0.4 * @param array $crumbs Array of crumbs. * @param string $post_type Post type of the single being viewed. * @param array $archive_map CPT → archive page option key. * @return array $crumbs Filtered array of crumbs. */ private function add_archive_page_crumbs( $crumbs, $post_type, $archive_map ) { if ( empty( $archive_map[ $post_type ] ) ) { return $crumbs; } if ( 'aiopod_episodes' === $post_type ) { $episode_id = get_queried_object_id(); $podcast_id = aiopod_get_episode_podcast_id( $episode_id ); // An episode with no published show of its own keeps the episode archive trail if ( $podcast_id ) { $chain = $this->get_archive_crumbs( 'aiopod_podcasts', $archive_map ); if ( ! empty( $chain ) ) { $chain[] = $this->get_page_crumb( $podcast_id ); $crumbs = $this->remove_archive_crumb( $crumbs, $post_type ); return $this->insert_crumb_chain( $crumbs, $chain ); } } $crumbs = $this->remove_unpublished_podcast_crumb( $crumbs, $episode_id ); } $chain = $this->get_archive_crumbs( $post_type, $archive_map, true ); if ( empty( $chain ) ) { return $crumbs; } // Yoast adds its own archive crumb for a post type that still has an archive. Where it did, // its text is the owner's Search Appearance setting, so only the URL is redirected $page_url = $chain[ count( $chain ) - 1 ]['url']; foreach ( $crumbs as $index => $crumb ) { if ( ! empty( $crumb['ptarchive'] ) && $crumb['ptarchive'] === $post_type ) { $crumbs[ $index ]['url'] = $page_url; return $crumbs; } } return $this->insert_crumb_chain( $crumbs, $chain ); } /** * Build the crumbs that stand for a post type's archive. * * The assigned archive page where there is one — with its own ancestors, so a nested page reads * as the whole path to it — and the post type's own archive where there is not. * * @access private * @since 1.0.4 * @param string $post_type Post type whose archive is wanted. * @param array $archive_map CPT → archive page option key. * @param bool $page_only Return nothing rather than fall back to the post type archive. * @return array Crumbs, outermost first. Empty when there is no archive to point at. */ private function get_archive_crumbs( $post_type, $archive_map, $page_only = false ) { $chain = array(); $pages_settings = aiopod_get_option( 'aiopod_pages_settings' ); $option_key = isset( $archive_map[ $post_type ] ) ? $archive_map[ $post_type ] : ''; if ( $option_key && ! empty( $pages_settings[ $option_key ] ) ) { $page_id = (int) $pages_settings[ $option_key ]; $page = get_post( $page_id ); if ( $page && 'publish' === $page->post_status && get_permalink( $page_id ) ) { foreach ( array_reverse( get_post_ancestors( $page_id ) ) as $ancestor_id ) { $ancestor = get_post( $ancestor_id ); if ( $ancestor && 'publish' === $ancestor->post_status ) { $chain[] = $this->get_page_crumb( $ancestor_id ); } } $chain[] = $this->get_page_crumb( $page_id ); return $chain; } } // No usable page assigned. The caller decides whether the post type's own archive will do: // it will for an episode pointed at the podcast archive, and it will not where Yoast has // already drawn that same archive crumb itself if ( $page_only ) { return $chain; } $url = get_post_type_archive_link( $post_type ); $obj = get_post_type_object( $post_type ); if ( ! $url || ! $obj ) { return $chain; } // Yoast's own Search Appearance title for this archive, where the owner set one $text = WPSEO_Options::get( 'bctitle-ptarchive-' . $post_type ); $chain[] = array( 'url' => $url, 'text' => empty( $text ) ? $obj->labels->name : $text ); return $chain; } /** * Drop the post type archive crumb Yoast drew for a post type. * * @access private * @since 1.0.4 * @param array $crumbs Array of crumbs. * @param string $post_type Post type whose archive crumb is not wanted. * @return array $crumbs Filtered array of crumbs. */ private function remove_archive_crumb( $crumbs, $post_type ) { foreach ( $crumbs as $index => $crumb ) { if ( ! empty( $crumb['ptarchive'] ) && $crumb['ptarchive'] === $post_type ) { unset( $crumbs[ $index ] ); return array_values( $crumbs ); } } return $crumbs; } /** * Take an unpublished show out of an episode's trail. * * Both SEO plugins build a post's ancestors from `post_parent` without checking status, so a * podcast reverted to draft still appears in the breadcrumbs of its published episodes, named * and linked to an address that answers 404. Declining to add the crumb is not enough — it is * already there, and has to be taken out. * * @access private * @since 1.0.4 * @param array $crumbs Array of crumbs. * @param int $episode_id ID of the episode being viewed. * @return array $crumbs Filtered array of crumbs. */ private function remove_unpublished_podcast_crumb( $crumbs, $episode_id ) { $podcast_id = (int) get_post_field( 'post_parent', $episode_id ); // `get_post( 0 )` falls back to the global post, so the parent has to be checked first if ( $podcast_id <= 0 ) { return $crumbs; } $podcast = get_post( $podcast_id ); if ( ! $podcast || 'aiopod_podcasts' !== $podcast->post_type || 'publish' === $podcast->post_status ) { return $crumbs; } $index = $this->find_crumb( $crumbs, $this->get_page_crumb( $podcast_id ) ); if ( null === $index ) { return $crumbs; } unset( $crumbs[ $index ] ); return array_values( $crumbs ); } /** * Fill in the ancestors of one of the plugin's overview pages. * * Yoast builds a page's ancestors from its own indexable hierarchy, which is empty until its * indexation has run — and on the category, tag and participant pages the plugin takes the * trail over from there, so the gap shows. * * @access private * @since 1.0.4 * @param array $crumbs Array of crumbs. * @param int $page_id ID of the page being viewed. * @return array $crumbs Filtered array of crumbs. */ private function add_page_ancestor_crumbs( $crumbs, $page_id ) { $chain = array(); foreach ( array_reverse( get_post_ancestors( $page_id ) ) as $ancestor_id ) { $ancestor = get_post( $ancestor_id ); if ( $ancestor && 'publish' === $ancestor->post_status ) { $chain[] = $this->get_page_crumb( $ancestor_id ); } } return $this->insert_crumb_chain( $crumbs, $chain ); } /** * Insert a top-down chain of crumbs into the trail, skipping any already in it. * * The cursor is what keeps a partly-built trail in order: a crumb that is already present moves * the insertion point to just after itself, so anything still missing below it lands in the * right place rather than all of it being stacked behind the home crumb. * * @access private * @since 1.0.4 * @param array $crumbs Array of crumbs. * @param array $chain Crumbs to insert, outermost first. * @return array $crumbs Filtered array of crumbs. */ private function insert_crumb_chain( $crumbs, $chain ) { $position = $this->get_crumb_insert_position( $crumbs ); foreach ( $chain as $crumb ) { $existing = $this->find_crumb( $crumbs, $crumb ); if ( null !== $existing ) { $position = $existing + 1; continue; } array_splice( $crumbs, $position, 0, array( $crumb ) ); $position++; } return $crumbs; } /** * Build a Yoast breadcrumb for a post or page. * * @access private * @since 1.0.4 * @param int $post_id ID of the post or page. * @return array Breadcrumb for it. */ private function get_page_crumb( $post_id ) { // Yoast's own Breadcrumbs Title field, so an inserted crumb reads the way Yoast would // have titled it had it built the crumb itself $text = get_post_meta( $post_id, '_yoast_wpseo_bctitle', true ); if ( empty( $text ) ) { $text = get_the_title( $post_id ); } return array( 'url' => get_permalink( $post_id ), 'text' => $text, 'id' => $post_id ); } /** * Find a crumb already in the trail, by post ID or by URL. * * @access private * @since 1.0.4 * @param array $crumbs Array of crumbs. * @param array $needle Crumb to look for. * @return int|null Index of the crumb, or null when it is not in the trail. */ private function find_crumb( $crumbs, $needle ) { foreach ( $crumbs as $index => $crumb ) { // Yoast carries the post ID on every crumb it builds for a post or a page if ( ! empty( $needle['id'] ) && isset( $crumb['id'] ) && (int) $crumb['id'] === (int) $needle['id'] ) { return $index; } if ( ! empty( $needle['url'] ) && ! empty( $crumb['url'] ) && untrailingslashit( $crumb['url'] ) === untrailingslashit( $needle['url'] ) ) { return $index; } } return null; } /** * Find where an inserted crumb belongs — after the home crumb, where there is one. * * @access private * @since 1.0.4 * @param array $crumbs Array of crumbs. * @return int Index to insert at. */ private function get_crumb_insert_position( $crumbs ) { if ( empty( $crumbs ) ) { return 0; } // The home crumb is optional, and Yoast's own setting is a surer test than the first // crumb's URL — an owner can point a home crumb somewhere other than the site root if ( class_exists( 'WPSEO_Options' ) ) { return ( '' !== WPSEO_Options::get( 'breadcrumbs-home' ) && ! is_front_page() ) ? 1 : 0; } if ( ! empty( $crumbs[0]['url'] ) && untrailingslashit( $crumbs[0]['url'] ) === untrailingslashit( home_url() ) ) { return 1; } return 0; } }