Phase 5: Content and SEO - Yoast SEO, Schema.org markup, Open Graph, favicon support, XML sitemap
This commit is contained in:
Executable
+202
@@ -0,0 +1,202 @@
|
||||
<?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>'
|
||||
);
|
||||
}
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\Alerts\Application\Ping_Other_Admins;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Product_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Short_Link_Helper;
|
||||
use Yoast\WP\SEO\Helpers\User_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast_Notification;
|
||||
use Yoast_Notification_Center;
|
||||
|
||||
/**
|
||||
* Ping_Other_Admins_Alert class.
|
||||
*/
|
||||
class Ping_Other_Admins_Alert implements Integration_Interface {
|
||||
|
||||
public const NOTIFICATION_ID = 'wpseo-ping-other-admins';
|
||||
|
||||
/**
|
||||
* The notifications center.
|
||||
*
|
||||
* @var Yoast_Notification_Center
|
||||
*/
|
||||
private $notification_center;
|
||||
|
||||
/**
|
||||
* The short link helper.
|
||||
*
|
||||
* @var Short_Link_Helper
|
||||
*/
|
||||
private $short_link_helper;
|
||||
|
||||
/**
|
||||
* The product helper.
|
||||
*
|
||||
* @var Product_Helper
|
||||
*/
|
||||
private $product_helper;
|
||||
|
||||
/**
|
||||
* The options helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
private $options_helper;
|
||||
|
||||
/**
|
||||
* The user helper.
|
||||
*
|
||||
* @var User_Helper
|
||||
*/
|
||||
private $user_helper;
|
||||
|
||||
/**
|
||||
* Ping_Other_Admins_Alert constructor.
|
||||
*
|
||||
* @param Yoast_Notification_Center $notification_center The notification center.
|
||||
* @param Short_Link_Helper $short_link_helper The short link helper.
|
||||
* @param Product_Helper $product_helper The product helper.
|
||||
* @param Options_Helper $options_helper The options helper.
|
||||
* @param User_Helper $user_helper The user helper.
|
||||
*/
|
||||
public function __construct(
|
||||
Yoast_Notification_Center $notification_center,
|
||||
Short_Link_Helper $short_link_helper,
|
||||
Product_Helper $product_helper,
|
||||
Options_Helper $options_helper,
|
||||
User_Helper $user_helper
|
||||
) {
|
||||
$this->notification_center = $notification_center;
|
||||
$this->short_link_helper = $short_link_helper;
|
||||
$this->product_helper = $product_helper;
|
||||
$this->options_helper = $options_helper;
|
||||
$this->user_helper = $user_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() {
|
||||
// @phpcs:ignore Squiz.PHP.CommentedOutCode.Found, Squiz.Commenting.InlineComment.InvalidEndChar -- we're gonna postpone this notification until we're actually ready for it.
|
||||
// \add_action( 'admin_init', [ $this, 'add_notifications' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds notification when user has not installed Yoast SEO themselves and has not resolved the notification yet.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_notifications() {
|
||||
if ( $this->has_user_installed_yoast() ) {
|
||||
$this->notification_center->remove_notification_by_id( self::NOTIFICATION_ID );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->has_notification_been_resolved() ) {
|
||||
$this->notification_center->remove_notification_by_id( self::NOTIFICATION_ID );
|
||||
return;
|
||||
}
|
||||
|
||||
$notification = $this->get_ping_other_admins_notification();
|
||||
|
||||
$this->notification_center->add_notification( $notification );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether user has installed Yoast SEO themselves.
|
||||
*
|
||||
* @return bool Whether the user has installed Yoast SEO themselves.
|
||||
*/
|
||||
private function has_user_installed_yoast(): bool {
|
||||
$first_activated_by = $this->options_helper->get( 'first_activated_by', 0 );
|
||||
|
||||
if ( $first_activated_by === 0 ) {
|
||||
return true; // We cannot be sure, so we assume they did.
|
||||
}
|
||||
|
||||
if ( \get_current_user_id() === $first_activated_by ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the alert has been resolved before.
|
||||
*
|
||||
* @return bool Whether the alert has been resolved before.
|
||||
*/
|
||||
private function has_notification_been_resolved(): bool {
|
||||
return $this->user_helper->get_meta( \get_current_user_id(), self::NOTIFICATION_ID . '_resolved', true ) === '1';
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the ping-other-admins notification.
|
||||
*
|
||||
* @return Yoast_Notification The ping-other-admins notification.
|
||||
*/
|
||||
private function get_ping_other_admins_notification(): Yoast_Notification {
|
||||
$message = $this->get_message();
|
||||
|
||||
return new Yoast_Notification(
|
||||
$message,
|
||||
[
|
||||
'id' => self::NOTIFICATION_ID,
|
||||
'type' => Yoast_Notification::WARNING,
|
||||
'capabilities' => [ 'wpseo_manage_options' ],
|
||||
'priority' => 20,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the notification as an HTML string.
|
||||
*
|
||||
* @return string The HTML string representation of the notification.
|
||||
*/
|
||||
private function get_message() {
|
||||
$shortlink = $this->short_link_helper->get( 'https://yoa.st/new-admin-newsletter-sign-up/' );
|
||||
|
||||
$message = \sprintf(
|
||||
/* translators: %1$s and %3$s expands to "Yoast SEO" , %2$s expands to an opening link tag, %4$s expands to a closing link tag. */
|
||||
\esc_html__( 'Looks like you’re new here. %1$s makes it easy to optimize your website for search engines. Want to keep your site healthy and easier to find? %2$sSign up for the %3$s newsletter for short, practical weekly tips%4$s.', 'wordpress-seo' ),
|
||||
'Yoast SEO',
|
||||
'<a href="' . \esc_url( $shortlink ) . '" target="_blank">',
|
||||
'Yoast SEO',
|
||||
'</a>'
|
||||
);
|
||||
|
||||
$notification_text = '<p>' . $message . '</p>';
|
||||
$notification_text .= '<a class="button wpseo-resolve-alert" href="#" data-alert-id="' . \esc_attr( self::NOTIFICATION_ID ) . '" data-nonce="' . \esc_attr( \wp_create_nonce( 'wpseo-resolve-alert-nonce' ) ) . '">';
|
||||
$notification_text .= \esc_html__( 'Dismiss', 'wordpress-seo' );
|
||||
$notification_text .= '</a>';
|
||||
|
||||
return $notification_text;
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong
|
||||
namespace Yoast\WP\SEO\Alerts\Infrastructure\Default_SEO_Data;
|
||||
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
|
||||
/**
|
||||
* Class that collects default SEO data.
|
||||
*/
|
||||
class Default_SEO_Data_Collector {
|
||||
|
||||
/**
|
||||
* Holds the Options_Helper instance.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
private $options_helper;
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @param Options_Helper $options_helper The options helper.
|
||||
*/
|
||||
public function __construct( Options_Helper $options_helper ) {
|
||||
$this->options_helper = $options_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the posts with default SEO title in their most recent.
|
||||
*
|
||||
* @return string[] The posts with default SEO title in their most recent.
|
||||
*/
|
||||
public function get_posts_with_default_seo_title(): array {
|
||||
return $this->options_helper->get( 'default_seo_title', [] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the posts with default SEO description in their most recent.
|
||||
*
|
||||
* @return string[] The posts with default SEO description in their most recent.
|
||||
*/
|
||||
public function get_posts_with_default_seo_description(): array {
|
||||
return $this->options_helper->get( 'default_seo_meta_desc', [] );
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\Alerts\User_Interface\Default_SEO_Data;
|
||||
|
||||
use Yoast\WP\SEO\Alerts\User_Interface\Default_Seo_Data\Default_SEO_Data_Cron_Scheduler;
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Repositories\Indexable_Repository;
|
||||
|
||||
/**
|
||||
* Cron Callback integration. This handles the actual process of detecting default SEO data in recent posts and updating the relevant options.
|
||||
*
|
||||
* @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Default_SEO_Data_Cron_Callback_Integration implements Integration_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* The options helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
private $options_helper;
|
||||
|
||||
/**
|
||||
* The scheduler.
|
||||
*
|
||||
* @var Default_SEO_Data_Cron_Scheduler
|
||||
*/
|
||||
private $scheduler;
|
||||
|
||||
/**
|
||||
* The indexable repository.
|
||||
*
|
||||
* @var Indexable_Repository
|
||||
*/
|
||||
private $indexable_repository;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Options_Helper $options_helper The options helper.
|
||||
* @param Default_SEO_Data_Cron_Scheduler $scheduler The scheduler.
|
||||
* @param Indexable_Repository $indexable_repository The indexable repository.
|
||||
*/
|
||||
public function __construct(
|
||||
Options_Helper $options_helper,
|
||||
Default_SEO_Data_Cron_Scheduler $scheduler,
|
||||
Indexable_Repository $indexable_repository
|
||||
) {
|
||||
$this->options_helper = $options_helper;
|
||||
$this->scheduler = $scheduler;
|
||||
$this->indexable_repository = $indexable_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the hooks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action(
|
||||
Default_SEO_Data_Cron_Scheduler::CRON_HOOK,
|
||||
[
|
||||
$this,
|
||||
'detect_default_seo_data_in_recent',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects default SEO data in recent posts and updates the relevant options.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function detect_default_seo_data_in_recent(): void {
|
||||
if ( ! \wp_doing_cron() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$recent_posts = $this->indexable_repository->get_recently_modified_posts( 'post', 5, false );
|
||||
|
||||
$recent_default_seo_title = [];
|
||||
$recent_default_seo_meta_desc = [];
|
||||
foreach ( $recent_posts as $post ) {
|
||||
if ( $post->title === null ) {
|
||||
$recent_default_seo_title[] = $post->object_id;
|
||||
}
|
||||
|
||||
if ( $post->description === null ) {
|
||||
$recent_default_seo_meta_desc[] = $post->object_id;
|
||||
}
|
||||
}
|
||||
|
||||
$this->options_helper->set( 'default_seo_title', $recent_default_seo_title );
|
||||
$this->options_helper->set( 'default_seo_meta_desc', $recent_default_seo_meta_desc );
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong
|
||||
namespace Yoast\WP\SEO\Alerts\User_Interface\Default_Seo_Data;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Responsible for scheduling and unscheduling the cron.
|
||||
*/
|
||||
class Default_SEO_Data_Cron_Scheduler implements Integration_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* The name of the cron job.
|
||||
*/
|
||||
public const CRON_HOOK = 'wpseo_detect_default_seo_data';
|
||||
|
||||
/**
|
||||
* Register hooks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'admin_init', [ $this, 'schedule_default_seo_data_detection' ] );
|
||||
\add_action( 'wpseo_deactivate', [ $this, 'unschedule_default_seo_data_detection' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules the default SEO data detection cron.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function schedule_default_seo_data_detection(): void {
|
||||
if ( ! \wp_next_scheduled( self::CRON_HOOK ) ) {
|
||||
\wp_schedule_event( ( \time() + \DAY_IN_SECONDS ), 'daily', self::CRON_HOOK );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unschedules the default SEO data detection cron.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unschedule_default_seo_data_detection() {
|
||||
$scheduled = \wp_next_scheduled( self::CRON_HOOK );
|
||||
if ( $scheduled ) {
|
||||
\wp_unschedule_event( $scheduled, self::CRON_HOOK );
|
||||
}
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\Alerts\User_Interface\Default_SEO_Data;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Models\Indexable;
|
||||
use Yoast\WP\SEO\Repositories\Indexable_Repository;
|
||||
/**
|
||||
* This handles the process of checking for non-default SEO in the latest content and updating the relevant options right away.
|
||||
*/
|
||||
class Default_SEO_Data_Watcher implements Integration_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* The indexable repository.
|
||||
*
|
||||
* @var Indexable_Repository
|
||||
*/
|
||||
private $indexable_repository;
|
||||
|
||||
/**
|
||||
* The options helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
private $options_helper;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Indexable_Repository $indexable_repository The indexable repository.
|
||||
* @param Options_Helper $options_helper The options helper.
|
||||
*/
|
||||
public function __construct(
|
||||
Indexable_Repository $indexable_repository,
|
||||
Options_Helper $options_helper
|
||||
) {
|
||||
$this->indexable_repository = $indexable_repository;
|
||||
$this->options_helper = $options_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the hooks with WordPress.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'wpseo_saved_indexable', [ $this, 'check_for_default_seo_data' ], 10, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for default SEO data in the saved indexable and the most recently modified posts.
|
||||
*
|
||||
* @param Indexable $saved_indexable The saved indexable.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function check_for_default_seo_data( $saved_indexable ): void {
|
||||
// We have activated this feature only for posts for now.
|
||||
if ( $saved_indexable->object_type !== 'post' || $saved_indexable->object_sub_type !== 'post' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the title or description is null, it means it's not default SEO data so let's reset the options.
|
||||
if ( $saved_indexable->title !== null ) {
|
||||
$this->options_helper->set( 'default_seo_title', [] );
|
||||
}
|
||||
|
||||
if ( $saved_indexable->description !== null ) {
|
||||
$this->options_helper->set( 'default_seo_meta_desc', [] );
|
||||
}
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\Alerts\User_Interface;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Helpers\Capability_Helper;
|
||||
use Yoast\WP\SEO\Helpers\User_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Registers a route to resolve an alert
|
||||
*
|
||||
* @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Resolve_Alert_Route implements Integration_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* The user helper.
|
||||
*
|
||||
* @var User_Helper
|
||||
*/
|
||||
private $user_helper;
|
||||
|
||||
/**
|
||||
* The capability helper.
|
||||
*
|
||||
* @var Capability_Helper
|
||||
*/
|
||||
private $capability_helper;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param User_Helper $user_helper The user helper.
|
||||
* @param Capability_Helper $capability_helper The capability helper.
|
||||
*/
|
||||
public function __construct(
|
||||
User_Helper $user_helper,
|
||||
Capability_Helper $capability_helper
|
||||
) {
|
||||
$this->user_helper = $user_helper;
|
||||
$this->capability_helper = $capability_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all hooks to WordPress.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'wp_ajax_wpseo_resolve_alert', [ $this, 'resolve_alert' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the callback to resolve an alert for the current user.
|
||||
*
|
||||
* @return void.
|
||||
*/
|
||||
public function resolve_alert() {
|
||||
if ( ! \check_ajax_referer( 'wpseo-resolve-alert-nonce', 'nonce', false ) || ! $this->capability_helper->current_user_can( 'wpseo_manage_options' ) ) {
|
||||
\wp_send_json_error(
|
||||
[
|
||||
'message' => 'Security check failed.',
|
||||
]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $_POST['alertId'] ) ) {
|
||||
\wp_send_json_error(
|
||||
[
|
||||
'message' => 'Alert ID is missing.',
|
||||
]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$alert_id = \sanitize_text_field( \wp_unslash( $_POST['alertId'] ) );
|
||||
$user_id = \get_current_user_id();
|
||||
|
||||
$this->user_helper->update_meta( $user_id, $alert_id . '_resolved', true );
|
||||
|
||||
\wp_send_json_success(
|
||||
[
|
||||
'message' => 'Alert resolved successfully.',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user