Term data object. * @param int $depth Depth of category, used for padding. * @param array $args An array of arguments. * @param int $current_object_id ID of the current category. */ $atts = apply_filters( 'category_list_link_attributes', $atts, $category, $depth, $args, $current_object_id ); $attributes = ''; foreach ( $atts as $attr => $value ) { if ( is_scalar( $value ) && '' !== $value && false !== $value ) { $value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value ); $attributes .= ' ' . $attr . '="' . $value . '"'; } } $link = sprintf( '%s', $attributes, $cat_name ); if ( ! empty( $args['feed_image'] ) || ! empty( $args['feed'] ) ) { $link .= ' '; if ( empty( $args['feed_image'] ) ) { $link .= '('; } $link .= ''; } $link .= ''; if ( empty( $args['feed_image'] ) ) { $link .= ')'; } } if ( ! empty( $args['show_count'] ) ) { $link .= ' (' . number_format_i18n( $category->count ) . ')'; } if ( 'list' === $args['style'] ) { $output .= "\tterm_id, ); if ( ! empty( $args['current_category'] ) ) { // 'current_category' can be an array, so we use `get_terms()`. $_current_terms = get_terms( array( 'taxonomy' => $category->taxonomy, 'include' => $args['current_category'], 'hide_empty' => false, ) ); foreach ( $_current_terms as $_current_term ) { if ( $category->term_id === $_current_term->term_id ) { $css_classes[] = 'current-cat'; $link = str_replace( 'term_id === $_current_term->parent ) { $css_classes[] = 'current-cat-parent'; } while ( $_current_term->parent ) { if ( $category->term_id === $_current_term->parent ) { $css_classes[] = 'current-cat-ancestor'; break; } $_current_term = get_term( $_current_term->parent, $category->taxonomy ); } } } /** * Filters the list of CSS classes to include with each category in the list. * * @since 4.2.0 * * @see wp_list_categories() * * @param string[] $css_classes An array of CSS classes to be applied to each list item. * @param WP_Term $category Category data object. * @param int $depth Depth of page, used for padding. * @param array $args An array of wp_list_categories() arguments. */ $css_classes = implode( ' ', apply_filters( 'category_css_class', $css_classes, $category, $depth, $args ) ); $css_classes = $css_classes ? ' class="' . esc_attr( $css_classes ) . '"' : ''; $output .= $css_classes; $output .= ">$link\n"; } elseif ( isset( $args['separator'] ) ) { $output .= "\t$link" . $args['separator'] . "\n"; } else { $output .= "\t$link
\n"; } } /** * Ends the element output, if needed. * * @since 2.1.0 * @since 5.9.0 Renamed `$page` to `$data_object` to match parent class for PHP 8 named parameter support. * * @see Walker::end_el() * * @param string $output Used to append additional content (passed by reference). * @param object $data_object Category data object. Not used. * @param int $depth Optional. Depth of category. Not used. * @param array $args Optional. An array of arguments. Only uses 'list' for whether should * append to output. See wp_list_categories(). Default empty array. */ public function end_el( &$output, $data_object, $depth = 0, $args = array() ) { if ( 'list' !== $args['style'] ) { return; } $output .= "\n"; } } /** * 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; } $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; } }