Phase 5: Content and SEO - Yoast SEO, Schema.org markup, Open Graph, favicon support, XML sitemap
This commit is contained in:
+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