cluded taxonomies. */ public function includedTaxonomies() { $taxonomies = []; if ( aioseo()->options->sitemap->{aioseo()->sitemap->type}->taxonomies->all ) { $taxonomies = get_taxonomies(); } else { $taxonomies = aioseo()->options->sitemap->{aioseo()->sitemap->type}->taxonomies->included; } if ( ! $taxonomies ) { return []; } $options = aioseo()->options->noConflict(); $dynamicOptions = aioseo()->dynamicOptions->noConflict(); $publicTaxonomies = aioseo()->helpers->getPublicTaxonomies( true ); foreach ( $taxonomies as $taxonomy ) { // Check if taxonomy is no longer registered. if ( ! in_array( $taxonomy, $publicTaxonomies, true ) || ! $dynamicOptions->searchAppearance->taxonomies->has( $taxonomy ) ) { $taxonomies = aioseo()->helpers->unsetValue( $taxonomies, $taxonomy ); continue; } // Check if taxonomy isn't noindexed. if ( aioseo()->helpers->isTaxonomyNoindexed( $taxonomy ) ) { $taxonomies = aioseo()->helpers->unsetValue( $taxonomies, $taxonomy ); continue; } if ( $dynamicOptions->searchAppearance->taxonomies->$taxonomy->advanced->robotsMeta->default && ! $options->searchAppearance->advanced->globalRobotsMeta->default && $options->searchAppearance->advanced->globalRobotsMeta->noindex ) { $taxonomies = aioseo()->helpers->unsetValue( $taxonomies, $taxonomy ); continue; } } return $taxonomies; } /** * Splits sitemap entries into chuncks based on the max. amount of URLs per index. * * @since 4.0.0 * * @param array $entries The sitemap entries. * @return array The chunked sitemap entries. */ public function chunkEntries( $entries ) { return array_chunk( $entries, aioseo()->sitemap->linksPerIndex, true ); } /** * Formats the last Modified date of a user-submitted additional page as an ISO 8601 date. * * @since 4.0.0 * * @param object $page The additional page object. * @return string The formatted datetime. */ public function lastModifiedAdditionalPage( $page ) { return gmdate( 'c', strtotime( $page->lastModified ) ); } /** * Returns a list of excluded post IDs. * * @since 4.0.0 * * @return string The excluded IDs. */ public function excludedPosts() { return $this->excludedObjectIds( 'excludePosts' ); } /** * Returns a list of excluded term IDs. * * @since 4.0.0 * * @return string The excluded IDs. */ public function excludedTerms() { return $this->excludedObjectIds( 'excludeTerms' ); } /** * Returns a list of excluded IDs for a given option as a comma separated string. * * Helper method for excludedPosts() and excludedTerms(). * * @since 4.0.0 * @version 4.4.7 Improved method name. * * @param string $option The option name. * @return string The excluded IDs. */ private function excludedObjectIds( $option ) { $type = aioseo()->sitemap->type; // The RSS Sitemap needs to exclude whatever is excluded in the general sitemap. if ( 'rss' === $type ) { $type = 'general'; } // Allow WPML to filter out hidden language posts/terms. $hiddenObjectIds = []; if ( aioseo()->helpers->isWpmlActive() ) { $hiddenLanguages = apply_filters( 'wpml_setting', [], 'hidden_languages' ); foreach ( $hiddenLanguages as $language ) { $objectTypes = []; if ( 'excludePosts' === $option ) { $objectTypes = aioseo()->sitemap->helpers->includedPostTypes(); $objectTypes = array_map( function( $postType ) { return "post_{$postType}"; }, $objectTypes ); } if ( 'excludeTerms' === $option ) { $objectTypes = aioseo()->sitemap->helpers->includedTaxonomies(); $objectTypes = array_map( function( $taxonomy ) { return "tax_{$taxonomy}"; }, $objectTypes ); } $dbNoConflict = aioseo()->core->db->noConflict(); $rows = $dbNoConflict->start( 'icl_translations' ) ->select( 'element_id' ) ->whereIn( 'element_type', $objectTypes ) ->where( 'language_code', $language ) ->run() ->result(); $ids = array_map( function( $row ) { return (int) $row->element_id; }, $rows ); $hiddenObjectIds = array_merge( $hiddenObjectIds, $ids ); } } $hasFilter = has_filter( 'aioseo_sitemap_' . aioseo()->helpers->toSnakeCase( $option ) ); $advanced = aioseo()->options->sitemap->$type->advancedSettings->enable; $excluded = array_merge( $hiddenObjectIds, aioseo()->options->sitemap->{$type}->advancedSettings->{$option} ); if ( ! $advanced && empty( $excluded ) && ! $hasFilter ) { return ''; } $ids = []; foreach ( $excluded as $object ) { if ( is_numeric( $object ) ) { $ids[] = (int) $object; continue; } $object = json_decode( $object ); if ( is_int( $object->value ) ) { $ids[] = $object->value; } } if ( 'excludePosts' === $option ) { $ids = apply_filters( 'aioseo_sitemap_exclude_posts', $ids, $type ); } if ( 'excludeTerms' === $option ) { $ids = apply_filters( 'aioseo_sitemap_exclude_terms', $ids, $type ); } return count( $ids ) ? esc_sql( implode( ', ', $ids ) ) : ''; } /** * Returns the URLs of all active sitemaps. * * @since 4.0.0 * @version 4.6.2 Removed the prefix from the list of URLs. * * @return array $urls The sitemap URLs. */ public function getSitemapUrls() { static $urls = []; if ( $urls ) { return $urls; } $addonsUrls = array_filter( aioseo()->addons->doAddonFunction( 'helpers', 'getSitemapUrls' ) ); foreach ( $addonsUrls as $addonUrls ) { $urls = array_merge( $urls, $addonUrls ); } if ( aioseo()->options->sitemap->general->enable ) { $urls[] = $this->getUrl( 'general' ); } if ( aioseo()->options->sitemap->rss->enable ) { $urls[] = $this->getUrl( 'rss' ); } return $urls; } /** * Returns the URLs of all active sitemaps with the 'Sitemap: ' prefix. * * @since 4.6.2 * * @return array $urls The sitemap URLs. */ public function getSitemapUrlsPrefixed() { $urls = $this->getSitemapUrls(); foreach ( $urls as &$url ) { $url = 'Sitemap: ' . $url; } return $urls; } /** * Extracts existing sitemap URLs from the robots.txt file. * We need this in case users have existing sitemap directives added to their robots.txt file. * * @since 4.0.10 * @version 4.4.9 * * @return array The sitemap URLs. */ public function extractSitemapUrlsFromRobotsTxt() { // First, we need to remove our filter, so that it doesn't run unintentionally. remove_filter( 'robots_txt', [ aioseo()->robotsTxt, 'buildRules' ], 10000 ); $robotsTxt = apply_filters( 'robots_txt', '', true ); add_filter( 'robots_txt', [ aioseo()->robotsTxt, 'buildRules' ], 10000 ); if ( ! $robotsTxt ) { return []; } $lines = explode( "\n", $robotsTxt ); if ( ! is_array( $lines ) || ! count( $lines ) ) { return []; } return aioseo()->robotsTxt->extractSitemapUrls( $robotsTxt ); } /** * Returns the URL of the given sitemap type. * * @since 4.1.5 * * @param string $type The sitemap type. * @return string The sitemap URL. */ public function getUrl( $type ) { $url = home_url( 'sitemap.xml' ); if ( 'rss' === $type ) { $url = home_url( 'sitemap.rss' ); } if ( 'general' === $type ) { // Check if user has a custom filename from the V3 migration. $filename = $this->filename( 'general' ) ?: 'sitemap'; $url = home_url( $filename . '.xml' ); } $addon = aioseo()->addons->getLoadedAddon( $type ); if ( ! empty( $addon->helpers ) && method_exists( $addon->helpers, 'getUrl' ) ) { $url = $addon->helpers->getUrl(); } return $url; } /** * Returns if images should be excluded from the sitemap. * * @since 4.2.2 * * @return bool */ public function excludeImages() { $shouldExclude = aioseo()->options->sitemap->general->advancedSettings->enable && aioseo()->options->sitemap->general->advancedSettings->excludeImages; return apply_filters( 'aioseo_sitemap_exclude_images', $shouldExclude ); } /** * Returns the post types to check against for the author sitemap. * * @since 4.4.4 * * @return array The post types. */ public function getAuthorPostTypes() { // By default, WP only considers posts for author archives, but users can include additional post types. $postTypes = [ 'post' ]; return apply_filters( 'aioseo_sitemap_author_post_types', $postTypes ); } }