• File: indexable-author-builder.php
  • Full Path: /home/matthif/www/wp-content/plugins/wordpress-seo/src/builders/indexable-author-builder.php
  • Date Modified: 02/24/2024 11:29 PM
  • File size: 7.58 KB
  • MIME-type: text/x-php
  • Charset: utf-8
<?php

namespace Yoast\WP\SEO\Builders;

use 
Yoast\WP\SEO\Exceptions\Indexable\Author_Not_Built_Exception;
use 
Yoast\WP\SEO\Helpers\Author_Archive_Helper;
use 
Yoast\WP\SEO\Helpers\Options_Helper;
use 
Yoast\WP\SEO\Helpers\Post_Helper;
use 
Yoast\WP\SEO\Models\Indexable;
use 
Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions;

/**
 * Author Builder for the indexables.
 *
 * Formats the author meta to indexable format.
 */
class Indexable_Author_Builder {

    use 
Indexable_Social_Image_Trait;

    
/**
     * The author archive helper.
     *
     * @var Author_Archive_Helper
     */
    
private $author_archive;

    
/**
     * The latest version of the Indexable_Author_Builder.
     *
     * @var int
     */
    
protected $version;

    
/**
     * Holds the options helper instance.
     *
     * @var Options_Helper
     */
    
protected $options_helper;

    
/**
     * Holds the taxonomy helper instance.
     *
     * @var Post_Helper
     */
    
protected $post_helper;

    
/**
     * Indexable_Author_Builder constructor.
     *
     * @param Author_Archive_Helper      $author_archive The author archive helper.
     * @param Indexable_Builder_Versions $versions       The Indexable version manager.
     * @param Options_Helper             $options_helper The options helper.
     * @param Post_Helper                $post_helper    The post helper.
     */
    
public function __construct(
        
Author_Archive_Helper $author_archive,
        
Indexable_Builder_Versions $versions,
        
Options_Helper $options_helper,
        
Post_Helper $post_helper
    
) {
        
$this->author_archive $author_archive;
        
$this->version        $versions->get_latest_version_for_type'user' );
        
$this->options_helper $options_helper;
        
$this->post_helper    $post_helper;
    }

    
/**
     * Formats the data.
     *
     * @param int       $user_id   The user to retrieve the indexable for.
     * @param Indexable $indexable The indexable to format.
     *
     * @return Indexable The extended indexable.
     *
     * @throws Author_Not_Built_Exception When author is not built.
     */
    
public function build$user_idIndexable $indexable ) {
        
$exception $this->check_if_user_should_be_indexed$user_id );
        if ( 
$exception ) {
            throw 
$exception;
        }

        
$meta_data $this->get_meta_data$user_id );

        
$indexable->object_id              $user_id;
        
$indexable->object_type            'user';
        
$indexable->permalink              \get_author_posts_url$user_id );
        
$indexable->title                  $meta_data['wpseo_title'];
        
$indexable->description            $meta_data['wpseo_metadesc'];
        
$indexable->is_cornerstone         false;
        
$indexable->is_robots_noindex      = ( $meta_data['wpseo_noindex_author'] === 'on' );
        
$indexable->is_robots_nofollow     null;
        
$indexable->is_robots_noarchive    null;
        
$indexable->is_robots_noimageindex null;
        
$indexable->is_robots_nosnippet    null;
        
$indexable->is_public              = ( $indexable->is_robots_noindex ) ? false null;
        
$indexable->has_public_posts       $this->author_archive->author_has_public_posts$user_id );
        
$indexable->blog_id                \get_current_blog_id();

        
$this->reset_social_images$indexable );
        
$this->handle_social_images$indexable );

        
$timestamps                      $this->get_object_timestamps$user_id );
        
$indexable->object_published_at  $timestamps->published_at;
        
$indexable->object_last_modified $timestamps->last_modified;

        
$indexable->version $this->version;

        return 
$indexable;
    }

    
/**
     * Retrieves the meta data for this indexable.
     *
     * @param int $user_id The user to retrieve the meta data for.
     *
     * @return array List of meta entries.
     */
    
protected function get_meta_data$user_id ) {
        
$keys = [
            
'wpseo_title',
            
'wpseo_metadesc',
            
'wpseo_noindex_author',
        ];

        
$output = [];
        foreach ( 
$keys as $key ) {
            
$output$key ] = $this->get_author_meta$user_id$key );
        }

        return 
$output;
    }

    
/**
     * Retrieves the author meta.
     *
     * @param int    $user_id The user to retrieve the indexable for.
     * @param string $key     The meta entry to retrieve.
     *
     * @return string|null The value of the meta field.
     */
    
protected function get_author_meta$user_id$key ) {
        
$value \get_the_author_meta$key$user_id );
        if ( 
\is_string$value ) && $value === '' ) {
            return 
null;
        }

        return 
$value;
    }

    
/**
     * Finds an alternative image for the social image.
     *
     * @param Indexable $indexable The indexable.
     *
     * @return array|bool False when not found, array with data when found.
     */
    
protected function find_alternative_imageIndexable $indexable ) {
        
$gravatar_image \get_avatar_url(
            
$indexable->object_id,
            [
                
'size'   => 500,
                
'scheme' => 'https',
            ]
        );
        if ( 
$gravatar_image ) {
            return [
                
'image'  => $gravatar_image,
                
'source' => 'gravatar-image',
            ];
        }

        return 
false;
    }

    
/**
     * Returns the timestamps for a given author.
     *
     * @param int $author_id The author ID.
     *
     * @return object An object with last_modified and published_at timestamps.
     */
    
protected function get_object_timestamps$author_id ) {
        global 
$wpdb;
        
$post_statuses $this->post_helper->get_public_post_statuses();

        
$replacements   = [];
        
$replacements[] = 'post_modified_gmt';
        
$replacements[] = 'post_date_gmt';
        
$replacements[] = $wpdb->posts;
        
$replacements[] = 'post_status';
        
$replacements   \array_merge$replacements$post_statuses );
        
$replacements[] = 'post_password';
        
$replacements[] = 'post_author';
        
$replacements[] = $author_id;

        
//phpcs:disable WordPress.DB.PreparedSQLPlaceholders -- %i placeholder is still not recognized.
        //phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery -- Reason: Most performant way.
        //phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: No relevant caches.
        
return $wpdb->get_row(
            
$wpdb->prepare(
                
'
                SELECT MAX(p.%i) AS last_modified, MIN(p.%i) AS published_at
                FROM %i AS p
                WHERE p.%i IN (' 
\implode', '\array_fill0\count$post_statuses ), '%s' ) ) . ")
                    AND p.%i = ''
                    AND p.%i = %d
                "
,
                
$replacements
            
)
        );
        
//phpcs:enable
    
}

    
/**
     * Checks if the user should be indexed.
     * Returns an exception with an appropriate message if not.
     *
     * @param string $user_id The user id.
     *
     * @return Author_Not_Built_Exception|null The exception if it should not be indexed, or `null` if it should.
     */
    
protected function check_if_user_should_be_indexed$user_id ) {
        
$exception null;

        if ( 
$this->author_archive->are_disabled() ) {
            
$exception Author_Not_Built_Exception::author_archives_are_disabled$user_id );
        }

        
// We will check if the author has public posts the WP way, instead of the indexable way, to make sure we get proper results even if SEO optimization is not run.
        // In case the user has no public posts, we check if the user should be indexed anyway.
        
if ( $this->options_helper->get'noindex-author-noposts-wpseo'false ) === true && $this->author_archive->author_has_public_posts_wp$user_id ) === false ) {
            
$exception Author_Not_Built_Exception::author_archives_are_not_indexed_for_users_without_posts$user_id );
        }

        
/**
         * Filter: Include or exclude a user from being build and saved as an indexable.
         * Return an `Author_Not_Built_Exception` when the indexable should not be build, with an appropriate message telling why it should not be built.
         * Return `null` if the indexable should be build.
         *
         * @param Author_Not_Built_Exception|null $exception An exception if the indexable is not being built, `null` if the indexable should be built.
         * @param string                          $user_id   The ID of the user that should or should not be excluded.
         */
        
return \apply_filters'wpseo_should_build_and_save_user_indexable'$exception$user_id );
    }
}