Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to CoAuthor Plus' guest authors #1

Open
wants to merge 10 commits into
base: trunk
Choose a base branch
from
74 changes: 73 additions & 1 deletion src/generators/schema/third-party/coauthor.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace Yoast\WP\SEO\Generators\Schema\Third_Party;

use Yoast\WP\SEO\Config\Schema_IDs;
use Yoast\WP\SEO\Generators\Schema\Author;


/**
* Returns schema Author data for the CoAuthor Plus assigned user on a post.
*/
Expand All @@ -12,7 +14,7 @@ class CoAuthor extends Author {
/**
* The user ID of the author we're generating data for.
*
* @var int
* @var int $user_id
*/
private $user_id;

Expand Down Expand Up @@ -62,6 +64,29 @@ public function generate_from_user_id( $user_id ) {
return $this->generate();
}

/**
* Generate the Person data given a Guest Author object.
*
* @param object $guest_author The Guest Author object.
*
* @return array|bool
*/
public function generate_from_guest_author( $guest_author ) {
$this->user_id = $user_id;

$data = $this->build_person_data_for_guest_author( $guest_author, true );

$data['@type'] = 'Person';
unset( $data['logo'] );

// If this is a post and the author archives are enabled, set the author archive url as the author url.
if ( $this->helpers->options->get( 'disable-author' ) !== true ) {
$data['url'] = \get_author_posts_url( $guest_author->ID, $guest_author->user_nicename );
}

return $data;
}

/**
* Determines a User ID for the Person data.
*
Expand All @@ -70,4 +95,51 @@ public function generate_from_user_id( $user_id ) {
protected function determine_user_id() {
return $this->user_id;
}

/**
* Builds our array of Schema Person data for a given Guest Author.
*
* @param object $guest_author The Guest Author object.
* @param bool $add_hash Wether or not the person's image url hash should be added to the image id.
*
* @return array An array of Schema Person data.
*/
protected function build_person_data_for_guest_author( $guest_author, $add_hash = false ) {
$schema_id = $this->context->site_url . Schema_IDs::PERSON_LOGO_HASH;
$data = [
'@type' => $this->type,
'@id' => $schema_id . \wp_hash( $guest_author->user_login . $guest_author->ID . 'guest' ),
];

$data['name'] = $this->helpers->schema->html->smart_strip_tags( $guest_author->display_name );

$data = $this->set_image_from_avatar( $data, $guest_author, $schema_id, $add_hash );

// If local avatar is present, override.
$avatar_meta = \wp_get_attachment_image_src( \get_post_thumbnail_id( $guest_author->ID ) );
if ( $avatar_meta ) {
$avatar_meta = [
'url' => $avatar_meta[0],
'width' => $avatar_meta[1],
'height' => $avatar_meta[2],
];
$data['image'] = $this->helpers->schema->image->generate_from_attachment_meta( $schema_id, $avatar_meta, $data['name'], $add_hash );
}

if ( ! empty( $guest_author->description ) ) {
$data['description'] = $this->helpers->schema->html->smart_strip_tags( $guest_author->description );
}

$data = $this->add_same_as_urls( $data, $guest_author, $user_id );

/**
* Filter: 'wpseo_schema_person_data' - Allows filtering of schema data per user.
*
* @param array $data The schema data we have for this person.
* @param int $user_id The current user we're collecting schema data for.
*/
$data = \apply_filters( 'wpseo_schema_person_data', $data, $user_id );

return $data;
}
}
96 changes: 81 additions & 15 deletions src/integrations/third-party/coauthors-plus.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

namespace Yoast\WP\SEO\Integrations\Third_Party;

use WP_User;
use Yoast\WP\SEO\Config\Schema_Types;
use Yoast\WP\SEO\Conditionals\Third_Party\CoAuthors_Plus_Activated_Conditional;
use Yoast\WP\SEO\Conditionals\Third_Party\CoAuthors_Plus_Flag_Conditional;
use Yoast\WP\SEO\Config\Schema_Types;
use Yoast\WP\SEO\Context\Meta_Tags_Context;
use Yoast\WP\SEO\Generators\Schema\Abstract_Schema_Piece;
use Yoast\WP\SEO\Generators\Schema\Third_Party\CoAuthor;
Expand All @@ -32,6 +31,8 @@ class CoAuthors_Plus implements Integration_Interface {
public function register_hooks() {
\add_filter( 'wpseo_schema_graph', [ $this, 'filter_graph' ], 11, 2 );
\add_filter( 'wpseo_schema_author', [ $this, 'filter_author_graph' ], 11, 4 );
\add_filter( 'wpseo_schema_profilepage', [ $this, 'filter_schema_profilepage' ], 11, 4 );
\add_filter( 'wpseo_meta_author', [ $this, 'filter_author_meta' ], 11, 2 );
}

/**
Expand All @@ -57,6 +58,36 @@ public function __construct( Helpers_Surface $helpers ) {
$this->helpers = $helpers;
}

/**
* Filters the graph output of authors archive for guest authors.
*
* @param array $data The schema graph.
* @param Meta_Tags_Context $context The context object.
* @param Abstract_Schema_Piece $graph_piece_generator The graph piece generator.
* @param Abstract_Schema_Piece[] $graph_piece_generators The graph piece generators.
*
* @return array The (potentially altered) schema graph.
*/
public function filter_schema_profilepage( $data, $context, $graph_piece_generator, $graph_piece_generators ) {

if ( ! is_author() ) {
return $data;
}

$user = \get_queried_object();

if ( empty( $user->type ) || $user->type !== 'guest-author' ) {
return $data;
}

// Fix author URL.
$author_url = \get_author_posts_url( $user->ID, $user->user_nicename );
$graph_piece_generator->context->canonical = $author_url;
$graph_piece_generator->context->main_schema_id = $author_url;

return $graph_piece_generator->generate();
}

/**
* Filters the graph output to add authors.
*
Expand Down Expand Up @@ -103,50 +134,85 @@ public function filter_graph( $data, $context ) {
/**
* Contains the authors from the CoAuthors Plus plugin.
*
* @var WP_User[] $author_objects
* @var \WP_User[] $author_objects
*/
$author_objects = \get_coauthors( $context->post->ID );
if ( \count( $author_objects ) <= 1 ) {
return $data;
}

$ids = [];
$ids = [];
$authors = [];

// Add the authors to the schema.
foreach ( $author_objects as $author ) {
if ( $author->ID === (int) $context->post->post_author ) {
continue;
}
$author_generator = new CoAuthor();
$author_generator->context = $context;
$author_generator->helpers = $this->helpers;
$author_data = $author_generator->generate_from_user_id( $author->ID );

if ( $author instanceof \WP_User ) {
$author_data = $author_generator->generate_from_user_id( $author->ID );
}
elseif ( ! empty( $author->type ) && $author->type === 'guest-author' ) {
$author_data = $author_generator->generate_from_guest_author( $author );
}

if ( ! empty( $author_data ) ) {
$ids[] = [ '@id' => $author_data['@id'] ];
$ids[] = [ '@id' => $author_data['@id'] ];
$authors[] = $author_data;
}
}

$schema_types = new Schema_Types();
$article_types = $schema_types->get_article_type_options_values();

// Change the author reference to reference our multiple authors.
$add_to_graph = false;
foreach ( $data as $key => $piece ) {
if ( \in_array( $piece['@type'], $article_types, true ) ) {
$data[ $key ]['author'] = \array_merge( [ $piece['author'] ], $ids );
$data[ $key ]['author'] = $ids;
$add_to_graph = true;
break;
}
}

if ( $add_to_graph ) {
// Clean all Persons from the schema, as the user stored as post owner might be incorrectly added if the post post has only guest authors as authors.
$data = \array_filter(
$data,
function( $piece ) {
return empty( $piece['@type'] ) || $piece['@type'] !== 'Person';
}
);

if ( ! empty( $author_data ) ) {
if ( $context->site_represents !== 'person' || $author->ID !== $context->site_user_id ) {
$data[] = $author_data;
$data = \array_merge( $data, $authors );
}
}
}

return $data;
}

/**
* Filters the author meta tag
*
* @param string $author_name The article author's display name. Return empty to disable the tag.
* @param Indexable_Presentation $presentation The presentation of an indexable.
* @return string
*/
public function filter_author_meta( $author_name, $presentation ) {
$author_objects = \get_coauthors( $presentation->context->post->id );

// Fallback in case of error.
if ( empty( $author_objects ) ) {
return $author_name;
}

$output = '';
foreach ( $author_objects as $i => $author ) {
$output .= $author->display_name;
if ( $i <= ( count( $author_objects ) - 2 ) ) {
$output .= ', ';
}
}
return $output;
}
}