<?php
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
namespace Yoast\WP\SEO\Alerts\Application\Default_SEO_Data;
use Yoast\WP\SEO\Alerts\Infrastructure\Default_SEO_Data\Default_SEO_Data_Collector;
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
use Yoast\WP\SEO\Helpers\Indexable_Helper;
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
use Yoast\WP\SEO\Helpers\Product_Helper;
use Yoast\WP\SEO\Helpers\Short_Link_Helper;
use Yoast\WP\SEO\Integrations\Integration_Interface;
use Yoast_Notification;
use Yoast_Notification_Center;
/**
* Default_SEO_Data_Alert class.
*/
class Default_SEO_Data_Alert implements Integration_Interface {
public const NOTIFICATION_ID = 'wpseo-default-seo-data';
/**
* The notifications center.
*
* @var Yoast_Notification_Center
*/
private $notification_center;
/**
* The default SEO data collector.
*
* @var Default_SEO_Data_Collector
*/
private $default_seo_data_collector;
/**
* The short link helper.
*
* @var Short_Link_Helper
*/
private $short_link_helper;
/**
* The product helper.
*
* @var Product_Helper
*/
private $product_helper;
/**
* The indexable helper.
*
* @var Indexable_Helper
*/
private $indexable_helper;
/**
* The post type helper.
*
* @var Post_Type_Helper
*/
private $post_type_helper;
/**
* Default_SEO_Data_Alert constructor.
*
* @param Yoast_Notification_Center $notification_center The notification center.
* @param Default_SEO_Data_Collector $default_seo_data_collector The default SEO data collector.
* @param Short_Link_Helper $short_link_helper The short link helper.
* @param Product_Helper $product_helper The product helper.
* @param Indexable_Helper $indexable_helper The indexable helper.
* @param Post_Type_Helper $post_type_helper The post type helper.
*/
public function __construct(
Yoast_Notification_Center $notification_center,
Default_SEO_Data_Collector $default_seo_data_collector,
Short_Link_Helper $short_link_helper,
Product_Helper $product_helper,
Indexable_Helper $indexable_helper,
Post_Type_Helper $post_type_helper
) {
$this->notification_center = $notification_center;
$this->default_seo_data_collector = $default_seo_data_collector;
$this->short_link_helper = $short_link_helper;
$this->product_helper = $product_helper;
$this->indexable_helper = $indexable_helper;
$this->post_type_helper = $post_type_helper;
}
/**
* Returns the conditionals based on which this loadable should be active.
*
* @return array<string>
*/
public static function get_conditionals() {
return [ Admin_Conditional::class ];
}
/**
* Initializes the integration.
*
* @return void
*/
public function register_hooks() {
\add_action( 'admin_init', [ $this, 'add_notifications' ] );
}
/**
* Adds notifications (when necessary).
*
* We want to show this notification only when there are enough posts that have the default SEO title or meta description, or both.
* If this is not the case we will not show the notification at all since it does not serve a purpose yet.
*
* @return void
*/
public function add_notifications() {
if ( ! $this->indexable_helper->should_index_indexables() ) {
// Do not show the notification when indexables are disabled.
$this->notification_center->remove_notification_by_id( self::NOTIFICATION_ID );
return;
}
if ( ! $this->post_type_helper->is_indexable( 'post' ) || ! $this->post_type_helper->has_metabox( 'post' ) ) {
// Do not show the notification when posts are not indexable or have no metabox.
$this->notification_center->remove_notification_by_id( self::NOTIFICATION_ID );
return;
}
$posts_with_default_seo_title = $this->default_seo_data_collector->get_posts_with_default_seo_title();
$posts_with_default_seo_description = $this->default_seo_data_collector->get_posts_with_default_seo_description();
$has_enough_posts_with_default_title = \count( $posts_with_default_seo_title ) > 4;
$has_enough_posts_with_default_desc = \count( $posts_with_default_seo_description ) > 4;
if ( ! $has_enough_posts_with_default_title && ! $has_enough_posts_with_default_desc ) {
$this->notification_center->remove_notification_by_id( self::NOTIFICATION_ID );
return;
}
$notification = $this->get_default_seo_data_notification( $has_enough_posts_with_default_title, $has_enough_posts_with_default_desc );
$this->notification_center->add_notification( $notification );
}
/**
* Build the default SEO data notification.
*
* @param bool $has_enough_posts_with_default_title Whether there are content types with default SEO title in their most recent posts.
* @param bool $has_enough_posts_with_default_desc Whether there are content types with default SEO description in their most recent posts.
*
* @return Yoast_Notification The notification containing the suggested plugin.
*/
private function get_default_seo_data_notification( $has_enough_posts_with_default_title, $has_enough_posts_with_default_desc ): Yoast_Notification {
$message = $this->get_default_seo_data_message( $has_enough_posts_with_default_title, $has_enough_posts_with_default_desc );
return new Yoast_Notification(
$message,
[
'id' => self::NOTIFICATION_ID,
'type' => Yoast_Notification::WARNING,
'capabilities' => [ 'wpseo_manage_options' ],
],
);
}
/**
* Creates a message to inform users that they are using only default SEO data lately.
*
* @param bool $has_enough_posts_with_default_title Whether there are content types with default SEO title in their most recent posts.
* @param bool $has_enough_posts_with_default_desc Whether there are content types with default SEO description in their most recent posts.
*
* @return string The default SEO data message.
*/
private function get_default_seo_data_message( $has_enough_posts_with_default_title, $has_enough_posts_with_default_desc ): string {
$shortlink = ( $this->product_helper->is_premium() ) ? $this->short_link_helper->get( 'https://yoa.st/ai-generate-alert-premium/' ) : $this->short_link_helper->get( 'https://yoa.st/ai-generate-alert-free/' );
if ( $has_enough_posts_with_default_title && $has_enough_posts_with_default_desc ) {
$default_seo_data = \esc_html__( 'SEO titles and meta descriptions', 'wordpress-seo' );
}
elseif ( $has_enough_posts_with_default_title ) {
$default_seo_data = \esc_html__( 'SEO titles', 'wordpress-seo' );
}
elseif ( $has_enough_posts_with_default_desc ) {
$default_seo_data = \esc_html__( 'meta descriptions', 'wordpress-seo' );
}
else {
$default_seo_data = \esc_html__( 'SEO data', 'wordpress-seo' );
}
/* translators: %1$s expands to "SEO title" or "meta description", %2$s expands to an opening link tag, %3$s expands to an opening strong tag, %4$s expands to a closing strong tag, %5$s expands to a closing link tag. */
$message = ( $this->product_helper->is_premium() ) ? \esc_html__( 'Your recent posts are using default %1$s, which can make them easy to overlook in search results. Update them manually or %2$sfind out how %3$sYoast AI Generate%4$s can improve them for you.%5$s', 'wordpress-seo' ) : \esc_html__( 'Your recent posts are using default %1$s, which can make them easy to overlook in search results. Update them for better visibility or %2$stry %3$sYoast AI Generate%4$s for free to do it faster.%5$s', 'wordpress-seo' );
return \sprintf(
$message,
$default_seo_data,
'<a href="' . \esc_url( $shortlink ) . '" target="_blank">',
'<strong>',
'</strong>',
'</a>',
);
}
}