importing/conflicting-plugins-service.php 0000644 00000006224 14751124776 0014723 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Importing;
use Yoast\WP\SEO\Config\Conflicting_Plugins;
/**
* Detects plugin conflicts.
*/
class Conflicting_Plugins_Service {
/**
* Detects the conflicting plugins.
*
* @return array A list of all active conflicting plugins.
*/
public function detect_conflicting_plugins() {
$all_active_plugins = $this->get_active_plugins();
// Search for active plugins.
return $this->get_active_conflicting_plugins( $all_active_plugins );
}
/**
* Deactivates the specified plugin(s) if any, or the entire list of known conflicting plugins.
*
* @param string|array|false $plugins Optional. The plugin filename, or array of plugin filenames, to deactivate.
*
* @return void
*/
public function deactivate_conflicting_plugins( $plugins = false ) {
// If no plugins are specified, deactivate any known conflicting plugins that are active.
if ( ! $plugins ) {
$plugins = $this->detect_conflicting_plugins();
}
// In case of a single plugin, wrap it in an array.
if ( \is_string( $plugins ) ) {
$plugins = [ $plugins ];
}
if ( ! \is_array( $plugins ) ) {
return;
}
// Deactivate all specified plugins across the network, while retaining their deactivation hook.
\deactivate_plugins( $plugins );
}
/**
* Loop through the list of known conflicting plugins to check if one of the plugins is active.
*
* @param array $all_active_plugins All plugins loaded by WordPress.
*
* @return array The array of activated conflicting plugins.
*/
protected function get_active_conflicting_plugins( $all_active_plugins ) {
$active_conflicting_plugins = [];
foreach ( Conflicting_Plugins::all_plugins() as $plugin ) {
if ( \in_array( $plugin, $all_active_plugins, true ) ) {
$active_conflicting_plugins[] = $plugin;
}
}
return $active_conflicting_plugins;
}
/**
* Get a list of all plugins active in the current WordPress instance.
*
* @return false|array The names of all active plugins.
*/
protected function get_active_plugins() {
// Request a list of active plugins from WordPress.
$all_active_plugins = \get_option( 'active_plugins' );
return $this->ignore_deactivating_plugin( $all_active_plugins );
}
/**
* While deactivating a plugin, we should ignore the plugin currently being deactivated.
*
* @param array $all_active_plugins All plugins currently loaded by WordPress.
*
* @return array The remaining active plugins.
*/
protected function ignore_deactivating_plugin( $all_active_plugins ) {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are strictly comparing only.
if ( isset( $_GET['action'] ) && isset( $_GET['plugin'] ) && \is_string( $_GET['action'] ) && \is_string( $_GET['plugin'] ) && \wp_unslash( $_GET['action'] ) === 'deactivate' ) {
$deactivated_plugin = \sanitize_text_field( \wp_unslash( $_GET['plugin'] ) );
\check_admin_referer( 'deactivate-plugin_' . $deactivated_plugin );
$key_to_remove = \array_search( $deactivated_plugin, $all_active_plugins, true );
if ( $key_to_remove !== false ) {
unset( $all_active_plugins[ $key_to_remove ] );
}
}
return $all_active_plugins;
}
}
importing/importable-detector-service.php 0000644 00000005031 14751124776 0014705 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Importing;
use Yoast\WP\SEO\Actions\Importing\Importing_Action_Interface;
/**
* Detects if any data from other SEO plugins is available for importing.
*/
class Importable_Detector_Service {
/**
* All known import actions
*
* @var array|Importing_Action_Interface[]
*/
protected $importers;
/**
* Importable_Detector_Service constructor.
*
* @param Importing_Action_Interface ...$importers All of the known importers.
*/
public function __construct( Importing_Action_Interface ...$importers ) {
$this->importers = $importers;
}
/**
* Returns the detected importers that have data to work with.
*
* @param string|null $plugin The plugin name of the importer.
* @param string|null $type The type of the importer.
*
* @return array The detected importers that have data to work with.
*/
public function detect_importers( $plugin = null, $type = null ) {
$detectors = $this->filter_actions( $this->importers, $plugin, $type );
$detected = [];
foreach ( $detectors as $detector ) {
if ( $detector->is_enabled() && $detector->get_type() !== 'cleanup' && ! $detector->get_completed() && $detector->get_limited_unindexed_count( 1 ) > 0 ) {
$detected[ $detector->get_plugin() ][] = $detector->get_type();
}
}
return $detected;
}
/**
* Returns the detected cleanups that have data to work with.
*
* @param string|null $plugin The plugin name of the cleanup.
*
* @return array The detected importers that have data to work with.
*/
public function detect_cleanups( $plugin = null ) {
$detectors = $this->filter_actions( $this->importers, $plugin, 'cleanup' );
$detected = [];
foreach ( $detectors as $detector ) {
if ( $detector->is_enabled() && ! $detector->get_completed() && $detector->get_limited_unindexed_count( 1 ) > 0 ) {
$detected[ $detector->get_plugin() ][] = $detector->get_type();
}
}
return $detected;
}
/**
* Filters all import actions from a list that do not match the given Plugin or Type.
*
* @param Importing_Action_Interface[] $all_actions The complete list of actions.
* @param string|null $plugin The Plugin name whose actions to keep.
* @param string|null $type The type of actions to keep.
*
* @return array
*/
public function filter_actions( $all_actions, $plugin = null, $type = null ) {
return \array_filter(
$all_actions,
static function ( $action ) use ( $plugin, $type ) {
return $action->is_compatible_with( $plugin, $type );
}
);
}
}
importing/aioseo/aioseo-robots-provider-service.php 0000644 00000003254 14751124776 0016641 0 ustar 00 <?php
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Services\Importing\Aioseo;
use Yoast\WP\SEO\Helpers\Aioseo_Helper;
/**
* Provides AISOEO search appearance robot settings.
*/
class Aioseo_Robots_Provider_Service {
/**
* The AIOSEO helper.
*
* @var Aioseo_Helper
*/
protected $aioseo_helper;
/**
* Class constructor.
*
* @param Aioseo_Helper $aioseo_helper The AIOSEO helper.
*/
public function __construct(
Aioseo_Helper $aioseo_helper
) {
$this->aioseo_helper = $aioseo_helper;
}
/**
* Retrieves the robot setting set globally in AIOSEO.
*
* @param string $setting_name The name of the robot setting, eg. noindex.
*
* @return bool Whether global robot settings enable or not the specific setting.
*/
public function get_global_robot_settings( $setting_name ) {
$aioseo_settings = $this->aioseo_helper->get_global_option();
if ( empty( $aioseo_settings ) ) {
return false;
}
$global_robot_settings = $aioseo_settings['searchAppearance']['advanced']['globalRobotsMeta'];
if ( $global_robot_settings['default'] === true ) {
return false;
}
return $global_robot_settings[ $setting_name ];
}
/**
* Gets the subtype's robot setting from the db.
*
* @param array $mapping The mapping of the setting we're working with.
*
* @return bool The robot setting.
*/
public function get_subtype_robot_setting( $mapping ) {
$aioseo_settings = \json_decode( \get_option( $mapping['option_name'], '' ), true );
return $aioseo_settings['searchAppearance'][ $mapping['type'] ][ $mapping['subtype'] ]['advanced']['robotsMeta'][ $mapping['robot_type'] ];
}
}
importing/aioseo/aioseo-robots-transformer-service.php 0000644 00000003326 14751124776 0017351 0 ustar 00 <?php
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Services\Importing\Aioseo;
/**
* Transforms AISOEO search appearance robot settings.
*/
class Aioseo_Robots_Transformer_Service {
/**
* The robots transfomer service.
*
* @var Aioseo_Robots_Provider_Service
*/
protected $robots_provider;
/**
* Class constructor.
*
* @param Aioseo_Robots_Provider_Service $robots_provider The robots provider service.
*/
public function __construct(
Aioseo_Robots_Provider_Service $robots_provider
) {
$this->robots_provider = $robots_provider;
}
/**
* Transforms the robot setting, taking into consideration whether they defer to global defaults.
*
* @param string $setting_name The name of the robot setting, eg. noindex.
* @param bool $setting_value The value of the robot setting.
* @param array $mapping The mapping of the setting we're working with.
*
* @return bool The transformed robot setting.
*/
public function transform_robot_setting( $setting_name, $setting_value, $mapping ) {
$aioseo_settings = \json_decode( \get_option( $mapping['option_name'], '' ), true );
// Let's check first if it defers to global robot settings.
if ( empty( $aioseo_settings ) || ! isset( $aioseo_settings['searchAppearance'][ $mapping['type'] ][ $mapping['subtype'] ]['advanced']['robotsMeta']['default'] ) ) {
return $setting_value;
}
$defers_to_defaults = $aioseo_settings['searchAppearance'][ $mapping['type'] ][ $mapping['subtype'] ]['advanced']['robotsMeta']['default'];
if ( $defers_to_defaults ) {
return $this->robots_provider->get_global_robot_settings( $setting_name );
}
return $setting_value;
}
}
importing/aioseo/aioseo-replacevar-service.php 0000644 00000006637 14751124776 0015635 0 ustar 00 <?php
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Services\Importing\Aioseo;
/**
* Replaces AISOEO replacevars with Yoast ones.
*/
class Aioseo_Replacevar_Service {
/**
* Mapping between the AiOSEO replace vars and the Yoast replace vars.
*
* @var array
*
* @see https://yoast.com/help/list-available-snippet-variables-yoast-seo/
*/
protected $replace_vars_map = [
// The key is the AiOSEO replace var, the value is the Yoast replace var (see class-wpseo-replace-vars).
'#archive_title' => '%%archive_title%%',
'#archive_date' => '%%date%%',
'#attachment_caption' => '%%caption%%',
'#author_bio' => '%%user_description%%',
'#author_first_name' => '%%author_first_name%%',
'#author_last_name' => '%%author_last_name%%',
'#author_name' => '%%name%%',
'#blog_title' => '%%sitename%%', // Same with #site_title.
'#categories' => '%%category%%',
'#current_date' => '%%currentdate%%',
'#current_day' => '%%currentday%%',
'#current_month' => '%%currentmonth%%',
'#current_year' => '%%currentyear%%',
'#parent_title' => '%%parent_title%%',
'#page_number' => '%%pagenumber%%',
'#permalink' => '%%permalink%%',
'#post_content' => '%%post_content%%',
'#post_date' => '%%date%%',
'#post_day' => '%%post_day%%',
'#post_month' => '%%post_month%%',
'#post_title' => '%%title%%',
'#post_year' => '%%post_year%%',
'#post_excerpt_only' => '%%excerpt_only%%',
'#post_excerpt' => '%%excerpt%%',
'#search_term' => '%%searchphrase%%',
'#separator_sa' => '%%sep%%',
'#site_title' => '%%sitename%%',
'#tagline' => '%%sitedesc%%',
'#taxonomy_title' => '%%category_title%%',
'#taxonomy_description' => '%%term_description%%',
];
/**
* Edits the replace_vars map of the class.
*
* @param string $aioseo_var The AIOSEO replacevar.
* @param string $yoast_var The Yoast replacevar.
*
* @return void
*/
public function compose_map( $aioseo_var, $yoast_var ) {
$map = $this->replace_vars_map;
$map[ $aioseo_var ] = $yoast_var;
$this->replace_vars_map = $map;
}
/**
* Transforms AIOSEO replacevars into Yoast replacevars.
*
* @param string $aioseo_replacevar The AIOSEO replacevar.
*
* @return string The Yoast replacevar.
*/
public function transform( $aioseo_replacevar ) {
$yoast_replacevar = \str_replace( \array_keys( $this->replace_vars_map ), \array_values( $this->replace_vars_map ), $aioseo_replacevar );
// Transform the '#custom_field-<custom_field>' tags into '%%cf_<custom_field>%%' ones.
$yoast_replacevar = \preg_replace_callback(
'/#custom_field-([a-zA-Z0-9_-]+)/',
static function ( $cf_matches ) {
return '%%cf_' . $cf_matches[1] . '%%';
},
$yoast_replacevar
);
// Transform the '#tax_name-<custom-tax-name>' tags into '%%ct_<custom-tax-name>%%' ones.
$yoast_replacevar = \preg_replace_callback(
'/#tax_name-([a-zA-Z0-9_-]+)/',
static function ( $ct_matches ) {
return '%%ct_' . $ct_matches[1] . '%%';
},
$yoast_replacevar
);
return $yoast_replacevar;
}
}
importing/aioseo/aioseo-social-images-provider-service.php 0000644 00000010653 14751124776 0020047 0 ustar 00 <?php
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Services\Importing\Aioseo;
use Yoast\WP\SEO\Helpers\Aioseo_Helper;
use Yoast\WP\SEO\Helpers\Image_Helper;
/**
* Provides AISOEO social images urls.
*/
class Aioseo_Social_Images_Provider_Service {
/**
* The AIOSEO helper.
*
* @var Aioseo_Helper
*/
protected $aioseo_helper;
/**
* The image helper.
*
* @var Image_Helper
*/
protected $image;
/**
* Class constructor.
*
* @param Aioseo_Helper $aioseo_helper The AIOSEO helper.
* @param Image_Helper $image The image helper.
*/
public function __construct(
Aioseo_Helper $aioseo_helper,
Image_Helper $image
) {
$this->aioseo_helper = $aioseo_helper;
$this->image = $image;
}
/**
* Retrieves the default source of social images.
*
* @param string $social_setting The social settings we're working with, eg. open-graph or twitter.
*
* @return string The default source of social images.
*/
public function get_default_social_image_source( $social_setting ) {
return $this->get_social_defaults( 'source', $social_setting );
}
/**
* Retrieves the default custom social image if there is any.
*
* @param string $social_setting The social settings we're working with, eg. open-graph or twitter.
*
* @return string The global default social image.
*/
public function get_default_custom_social_image( $social_setting ) {
return $this->get_social_defaults( 'custom_image', $social_setting );
}
/**
* Retrieves social defaults, be it Default Post Image Source or Default Post Image.
*
* @param string $setting The setting we want, eg. source or custom image.
* @param string $social_setting The social settings we're working with, eg. open-graph or twitter.
*
* @return string The social default.
*/
public function get_social_defaults( $setting, $social_setting ) {
switch ( $setting ) {
case 'source':
$setting_key = 'defaultImageSourcePosts';
break;
case 'custom_image':
$setting_key = 'defaultImagePosts';
break;
default:
return '';
}
$aioseo_settings = $this->aioseo_helper->get_global_option();
if ( $social_setting === 'og' ) {
$social_setting = 'facebook';
}
if ( ! isset( $aioseo_settings['social'][ $social_setting ]['general'][ $setting_key ] ) ) {
return '';
}
return $aioseo_settings['social'][ $social_setting ]['general'][ $setting_key ];
}
/**
* Retrieves the url of the first image in content.
*
* @param int $post_id The post id to extract the image from.
*
* @return string The url of the first image in content.
*/
public function get_first_image_in_content( $post_id ) {
$image = $this->image->get_gallery_image( $post_id );
if ( ! $image ) {
$image = $this->image->get_post_content_image( $post_id );
}
return $image;
}
/**
* Retrieves the url of the first attached image.
*
* @param int $post_id The post id to extract the image from.
*
* @return string The url of the first attached image.
*/
public function get_first_attached_image( $post_id ) {
if ( \get_post_type( $post_id ) === 'attachment' ) {
return $this->image->get_attachment_image_source( $post_id, 'fullsize' );
}
$attachments = \get_children(
[
'post_parent' => $post_id,
'post_status' => 'inherit',
'post_type' => 'attachment',
'post_mime_type' => 'image',
]
);
if ( $attachments && ! empty( $attachments ) ) {
return $this->image->get_attachment_image_source( \array_values( $attachments )[0]->ID, 'fullsize' );
}
return '';
}
/**
* Retrieves the url of the featured image.
*
* @param int $post_id The post id to extract the image from.
*
* @return string The url of the featured image.
*/
public function get_featured_image( $post_id ) {
$feature_image_id = \get_post_thumbnail_id( $post_id );
if ( $feature_image_id ) {
return $this->image->get_attachment_image_source( $feature_image_id, 'fullsize' );
}
return '';
}
/**
* Retrieves the url of the first available image. Tries each image source to get one image.
*
* @param int $post_id The post id to extract the image from.
*
* @return string The url of the featured image.
*/
public function get_auto_image( $post_id ) {
$image = $this->get_first_attached_image( $post_id );
if ( ! $image ) {
$image = $this->get_first_image_in_content( $post_id );
}
return $image;
}
}
indexables/indexable-version-manager.php 0000644 00000004440 14751124776 0014441 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Indexables;
use Yoast\WP\SEO\Models\Indexable;
use Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions;
/**
* Handles version control for Indexables.
*/
class Indexable_Version_Manager {
/**
* Stores the version of each Indexable type.
*
* @var Indexable_Builder_Versions The current versions of all indexable builders.
*/
protected $indexable_builder_versions;
/**
* Indexable_Version_Manager constructor.
*
* @param Indexable_Builder_Versions $indexable_builder_versions The current versions of all indexable builders.
*/
public function __construct( Indexable_Builder_Versions $indexable_builder_versions ) {
$this->indexable_builder_versions = $indexable_builder_versions;
}
/**
* Determines if an Indexable has a lower version than the builder for that Indexable's type.
*
* @param Indexable $indexable The Indexable to check.
*
* @return bool True if the given version is older than the current latest version.
*/
public function indexable_needs_upgrade( $indexable ) {
if ( ( ! $indexable )
|| ( ! \is_a( $indexable, Indexable::class ) )
) {
return false;
}
return $this->needs_upgrade( $indexable->object_type, $indexable->version );
}
/**
* Determines if an Indexable version for the type is lower than the current version for that Indexable type.
*
* @param string $object_type The Indexable's object type.
* @param int $indexable_version The Indexable's version.
*
* @return bool True if the given version is older than the current latest version.
*/
protected function needs_upgrade( $object_type, $indexable_version ) {
$current_indexable_builder_version = $this->indexable_builder_versions->get_latest_version_for_type( $object_type );
// If the Indexable's version is below the current version, that Indexable needs updating.
return $indexable_version < $current_indexable_builder_version;
}
/**
* Sets an Indexable's version to the latest version.
*
* @param Indexable $indexable The Indexable to update.
*
* @return Indexable
*/
public function set_latest( $indexable ) {
if ( ! $indexable ) {
return $indexable;
}
$indexable->version = $this->indexable_builder_versions->get_latest_version_for_type( $indexable->object_type );
return $indexable;
}
}
health-check/report-builder.php 0000644 00000011667 14751124776 0012565 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Provides an interface to build WordPress-friendly health check results.
*/
class Report_Builder {
/**
* Passed health check.
*/
public const STATUS_GOOD = 'good';
/**
* Changes are recommended but not necessary.
*/
public const STATUS_RECOMMENDED = 'recommended';
/**
* Significant issues that the user should consider fixing.
*/
public const STATUS_CRITICAL = 'critical';
/**
* The user-facing label.
*
* @var string
*/
private $label = '';
/**
* The identifier that WordPress uses for the health check.
*
* @var string
*/
private $test_identifier = '';
/**
* The test status (good, recommended, critical).
*
* @var string
*/
private $status = '';
/**
* The short description for the result.
*
* @var string
*/
private $description = '';
/**
* Actions that the user can take to solve the health check result.
*
* @var string
*/
private $actions = '';
/**
* Sets the label for the health check that the user can see.
*
* @param string $label The label that the user can see.
* @return Report_Builder This builder.
*/
public function set_label( $label ) {
$this->label = $label;
return $this;
}
/**
* Sets the name for the test that the plugin uses to identify the test.
*
* @param string $test_identifier The identifier for the health check.
* @return Report_Builder This builder.
*/
public function set_test_identifier( $test_identifier ) {
$this->test_identifier = $test_identifier;
return $this;
}
/**
* Sets the status of the test result to GOOD (green label).
*
* @return Report_Builder This builder.
*/
public function set_status_good() {
$this->status = self::STATUS_GOOD;
return $this;
}
/**
* Sets the status of the test result to RECOMMENDED (orange label).
*
* @return Report_Builder This builder.
*/
public function set_status_recommended() {
$this->status = self::STATUS_RECOMMENDED;
return $this;
}
/**
* Sets the status of the test result to CRITICAL (red label).
*
* @return Report_Builder This builder.
*/
public function set_status_critical() {
$this->status = self::STATUS_CRITICAL;
return $this;
}
/**
* Sets a description for the test result. This will be the heading for the result in the user interface.
*
* @param string $description The description for the test result.
* @return Report_Builder This builder.
*/
public function set_description( $description ) {
$this->description = $description;
return $this;
}
/**
* Sets a text that describes how the user can solve the failed health check.
*
* @param string $actions The descriptive text.
* @return Report_Builder This builder.
*/
public function set_actions( $actions ) {
$this->actions = $actions;
return $this;
}
/**
* Builds an array of strings in the format that WordPress uses to display health checks (https://developer.wordpress.org/reference/hooks/site_status_test_result/).
*
* @return array The report in WordPress' site status report format.
*/
public function build() {
return [
'label' => $this->label,
'status' => $this->status,
'badge' => $this->get_badge(),
'description' => $this->description,
'actions' => $this->get_actions_with_signature(),
'test' => $this->test_identifier,
];
}
/**
* Generates a badge that the user can see.
*
* @return string[] The badge.
*/
private function get_badge() {
return [
'label' => $this->get_badge_label(),
'color' => $this->get_badge_color(),
];
}
/**
* Generates the label for a badge.
*
* @return string The badge label.
*/
private function get_badge_label() {
return \__( 'SEO', 'wordpress-seo' );
}
/**
* Generates the color for the badge using the current status.
*
* @return string The color for the badge's outline.
*/
private function get_badge_color() {
if ( $this->status === self::STATUS_CRITICAL || $this->status === self::STATUS_RECOMMENDED ) {
return 'red';
}
return 'blue';
}
/**
* Concatenates the set actions with Yoast's signature.
*
* @return string A string containing the set actions and Yoast's signature.
*/
private function get_actions_with_signature() {
return $this->actions . $this->get_signature();
}
/**
* Generates Yoast's signature that's displayed at the bottom of the health check result.
*
* @return string Yoast's signature as an HTML string.
*/
private function get_signature() {
return \sprintf(
/* translators: 1: Start of a paragraph beginning with the Yoast icon, 2: Expands to 'Yoast SEO', 3: Paragraph closing tag. */
\esc_html__( '%1$sThis was reported by the %2$s plugin%3$s', 'wordpress-seo' ),
'<p class="yoast-site-health__signature"><img src="' . \esc_url( \plugin_dir_url( \WPSEO_FILE ) . 'packages/js/images/Yoast_SEO_Icon.svg' ) . '" alt="" height="20" width="20" class="yoast-site-health__signature-icon">',
'Yoast SEO',
'</p>'
);
}
}
health-check/report-builder-factory.php 0000644 00000000720 14751124776 0014216 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Creates Report_Builder instances.
*/
class Report_Builder_Factory {
/**
* Creates a new Report_Builder instance.
*
* @param string $test_identifier The test identifier as a string.
* @return Report_Builder The new Report_Builder instance.
*/
public function create( $test_identifier ) {
$instance = new Report_Builder();
return $instance->set_test_identifier( $test_identifier );
}
}
health-check/page-comments-reports.php 0000644 00000004611 14751124776 0014050 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Presents a set of different messages for the Page_Comments health check.
*/
class Page_Comments_Reports {
use Reports_Trait;
/**
* Constructor.
*
* @param Report_Builder_Factory $report_builder_factory The factory for result builder objects.
* This class uses the report builder to generate WordPress-friendly
* health check results.
*/
public function __construct( Report_Builder_Factory $report_builder_factory ) {
$this->report_builder_factory = $report_builder_factory;
}
/**
* Returns the report for when comments are set to be all on one page.
*
* @return string[] The message as a WordPress site status report.
*/
public function get_success_result() {
return $this->get_report_builder()
->set_label( \esc_html__( 'Comments are displayed on a single page', 'wordpress-seo' ) )
->set_status_good()
->set_description( \__( 'Comments on your posts are displayed on a single page. This is just like we\'d suggest it. You\'re doing well!', 'wordpress-seo' ) )
->build();
}
/**
* Returns the report for when comments are set to be broken up across multiple pages.
*
* @return string[] The message as a WordPress site status report.
*/
public function get_has_comments_on_multiple_pages_result() {
return $this->get_report_builder()
->set_label( \__( 'Comments break into multiple pages', 'wordpress-seo' ) )
->set_status_recommended()
->set_description( \__( 'Comments on your posts break into multiple pages. As this is not needed in 999 out of 1000 cases, we recommend you disable it. To fix this, uncheck "Break comments into pages..." on the Discussion Settings page.', 'wordpress-seo' ) )
->set_actions( $this->get_has_comments_on_multiple_pages_actions() )
->build();
}
/**
* Returns the actions for when the comments are set to be broken up across multiple pages.
*
* @return string The actions as a string.
*/
private function get_has_comments_on_multiple_pages_actions() {
return \sprintf(
/* translators: 1: Opening tag of the link to the discussion settings page, 2: Link closing tag. */
\esc_html__( '%1$sGo to the Discussion Settings page%2$s', 'wordpress-seo' ),
'<a href="' . \esc_url( \admin_url( 'options-discussion.php' ) ) . '">',
'</a>'
);
}
}
health-check/reports-trait.php 0000644 00000001637 14751124776 0012441 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Used by classes that use a health check Report_Builder.
*/
trait Reports_Trait {
/**
* The factory for the builder object that generates WordPress-friendly test results.
*
* @var Report_Builder_Factory
*/
private $report_builder_factory;
/**
* The test identifier that's set on the Report_Builder.
*
* @var string
*/
private $test_identifier = '';
/**
* Sets the name that WordPress uses to identify this health check.
*
* @param string $test_identifier The identifier.
* @return void
*/
public function set_test_identifier( $test_identifier ) {
$this->test_identifier = $test_identifier;
}
/**
* Returns a new Report_Builder instance using the set test identifier.
*
* @return Report_Builder
*/
private function get_report_builder() {
return $this->report_builder_factory->create( $this->test_identifier );
}
}
health-check/postname-permalink-check.php 0000644 00000002666 14751124776 0014506 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Paasses when permalinks are set to contain the post name.
*/
class Postname_Permalink_Check extends Health_Check {
/**
* Runs the health check.
*
* @var Postname_Permalink_Runner
*/
private $runner;
/**
* Generates WordPress-friendly health check results.
*
* @var Postname_Permalink_Reports
*/
private $reports;
/**
* Constructor.
*
* @param Postname_Permalink_Runner $runner The object that implements the actual health check.
* @param Postname_Permalink_Reports $reports The object that generates WordPress-friendly results.
*/
public function __construct(
Postname_Permalink_Runner $runner,
Postname_Permalink_Reports $reports
) {
$this->runner = $runner;
$this->reports = $reports;
$this->reports->set_test_identifier( $this->get_test_identifier() );
$this->set_runner( $this->runner );
}
/**
* Returns a human-readable label for this health check.
*
* @return string The human-readable label.
*/
public function get_test_label() {
return \__( 'Postname permalink', 'wordpress-seo' );
}
/**
* Returns the WordPress-friendly health check result.
*
* @return string[] The WordPress-friendly health check result.
*/
protected function get_result() {
if ( $this->runner->is_successful() ) {
return $this->reports->get_success_result();
}
return $this->reports->get_has_no_postname_in_permalink_result();
}
}
health-check/links-table-runner.php 0000644 00000004000 14751124776 0013321 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
use Yoast\WP\SEO\Config\Migration_Status;
use Yoast\WP\SEO\Helpers\Options_Helper;
/**
* Runs the Links_Table health check.
*/
class Links_Table_Runner implements Runner_Interface {
/**
* Is set to true when the links table is accessible.
*
* @var bool
*/
private $links_table_accessible = false;
/**
* The Migration_Status object used to determine whether the links table is accessible.
*
* @var Migration_Status
*/
private $migration_status;
/**
* The Options_Helper object used to determine whether the health check should run or not.
*
* @var Options_Helper
*/
private $options_helper;
/**
* Constructor.
*
* @param Migration_Status $migration_status Object used to determine whether the links table is accessible.
* @param Options_Helper $options_helper Object used to determine whether the health check should run.
*/
public function __construct(
Migration_Status $migration_status,
Options_Helper $options_helper
) {
$this->migration_status = $migration_status;
$this->options_helper = $options_helper;
}
/**
* Runs the health check. Checks if the tagline is set to WordPress' default tagline, or to its set translation.
*
* @return void
*/
public function run() {
if ( ! $this->should_run() ) {
return;
}
$this->links_table_accessible = $this->migration_status->is_version( 'free', \WPSEO_VERSION );
}
/**
* Determines whether the health check should run or not.
*
* @return bool True if the text link counter feature is enabled.
*/
public function should_run() {
$text_link_counter_enabled = $this->options_helper->get( 'enable_text_link_counter' );
if ( ! \is_bool( $text_link_counter_enabled ) ) {
return false;
}
return $text_link_counter_enabled;
}
/**
* Returns true if the links table is accessible
*
* @return bool The boolean indicating if the health check was succesful.
*/
public function is_successful() {
return $this->links_table_accessible;
}
}
health-check/myyoast-api-request-factory.php 0000644 00000000751 14751124776 0015225 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
use WPSEO_MyYoast_Api_Request;
/**
* Creates WPSEO_MyYoast_Api_Request objects.
*/
class MyYoast_Api_Request_Factory {
/**
* Creates a new WPSEO_MyYoast_API_Request.
*
* @param string $url The URL for the request.
* @param array $args Optional arguments for the request.
* @return WPSEO_MyYoast_Api_Request
*/
public function create( $url, $args = [] ) {
return new WPSEO_MyYoast_Api_Request( $url, $args );
}
}
health-check/page-comments-check.php 0000644 00000002614 14751124776 0013430 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Paasses when comments are set to be on a single page.
*/
class Page_Comments_Check extends Health_Check {
/**
* Runs the health check.
*
* @var Page_Comments_Runner
*/
private $runner;
/**
* Generates WordPress-friendly health check results.
*
* @var Page_Comments_Reports
*/
private $reports;
/**
* Constructor.
*
* @param Page_Comments_Runner $runner The object that implements the actual health check.
* @param Page_Comments_Reports $reports The object that generates WordPress-friendly results.
*/
public function __construct(
Page_Comments_Runner $runner,
Page_Comments_Reports $reports
) {
$this->runner = $runner;
$this->reports = $reports;
$this->reports->set_test_identifier( $this->get_test_identifier() );
$this->set_runner( $this->runner );
}
/**
* Returns a human-readable label for this health check.
*
* @return string The human-readable label.
*/
public function get_test_label() {
return \__( 'Page comments', 'wordpress-seo' );
}
/**
* Returns the WordPress-friendly health check result.
*
* @return string[] The WordPress-friendly health check result.
*/
protected function get_result() {
if ( $this->runner->is_successful() ) {
return $this->reports->get_success_result();
}
return $this->reports->get_has_comments_on_multiple_pages_result();
}
}
health-check/postname-permalink-runner.php 0000644 00000001614 14751124776 0014732 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Runs the Postname_Permalink health check.
*/
class Postname_Permalink_Runner implements Runner_Interface {
/**
* Is set to true when permalinks are set to contain the post name
*
* @var bool
*/
private $permalinks_contain_postname;
/**
* Constructor.
*/
public function __construct() {
$this->permalinks_contain_postname = false;
}
/**
* Runs the health check. Checks if permalinks are set to contain the post name.
*
* @return void
*/
public function run() {
$this->permalinks_contain_postname = ( \strpos( \get_option( 'permalink_structure' ), '%postname%' ) !== false );
}
/**
* Returns true if permalinks are set to contain the post name.
*
* @return bool True if permalinks are set to contain the post name.
*/
public function is_successful() {
return $this->permalinks_contain_postname;
}
}
health-check/page-comments-runner.php 0000644 00000001503 14751124776 0013660 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Runs the Page_Comments health check.
*/
class Page_Comments_Runner implements Runner_Interface {
/**
* Is set to true when comments are set to display on a single page.
*
* @var bool
*/
private $comments_on_single_page;
/**
* Constructor.
*/
public function __construct() {
$this->comments_on_single_page = false;
}
/**
* Runs the health check. Checks if comments are displayed on a single page.
*
* @return void
*/
public function run() {
$this->comments_on_single_page = \get_option( 'page_comments' ) !== '1';
}
/**
* Returns true if comments are displayed on a single page.
*
* @return bool True if comments are displayed on a single page.
*/
public function is_successful() {
return $this->comments_on_single_page;
}
}
health-check/runner-interface.php 0000644 00000000425 14751124776 0013063 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Interface for the health check runner. The abstract Health_Check uses this to run a health check.
*/
interface Runner_Interface {
/**
* Runs the health check.
*
* @return void
*/
public function run();
}
health-check/default-tagline-check.php 0000644 00000002661 14751124776 0013740 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Passes when the tagline is set to something other than the WordPress default tagline.
*/
class Default_Tagline_Check extends Health_Check {
/**
* Runs the health check.
*
* @var Default_Tagline_Runner
*/
private $runner;
/**
* Generates WordPress-friendly health check results.
*
* @var Default_Tagline_Reports
*/
private $reports;
/**
* Constructor.
*
* @param Default_Tagline_Runner $runner The object that implements the actual health check.
* @param Default_Tagline_Reports $reports The object that generates WordPress-friendly results.
*/
public function __construct(
Default_Tagline_Runner $runner,
Default_Tagline_Reports $reports
) {
$this->runner = $runner;
$this->reports = $reports;
$this->reports->set_test_identifier( $this->get_test_identifier() );
$this->set_runner( $this->runner );
}
/**
* Returns a human-readable label for this health check.
*
* @return string The human-readable label.
*/
public function get_test_label() {
return \__( 'Default tagline', 'wordpress-seo' );
}
/**
* Returns the WordPress-friendly health check result.
*
* @return string[] The WordPress-friendly health check result.
*/
protected function get_result() {
if ( $this->runner->is_successful() ) {
return $this->reports->get_success_result();
}
return $this->reports->get_has_default_tagline_result();
}
}
health-check/postname-permalink-reports.php 0000644 00000005247 14751124776 0015125 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Presents a set of different messages for the Postname_Permalink health check.
*/
class Postname_Permalink_Reports {
use Reports_Trait;
/**
* Constructor.
*
* @param Report_Builder_Factory $report_builder_factory The factory for result builder objects.
* This class uses the report builder to generate WordPress-friendly
* health check results.
*/
public function __construct( Report_Builder_Factory $report_builder_factory ) {
$this->report_builder_factory = $report_builder_factory;
}
/**
* Returns the report for when permalinks are set to contain the post name.
*
* @return string[] The message as a WordPress site status report.
*/
public function get_success_result() {
return $this->get_report_builder()
->set_label( \esc_html__( 'Your permalink structure includes the post name', 'wordpress-seo' ) )
->set_status_good()
->set_description( \__( 'You do have your postname in the URL of your posts and pages.', 'wordpress-seo' ) )
->build();
}
/**
* Returns the report for when permalinks are not set to contain the post name.
*
* @return string[] The message as a WordPress site status report.
*/
public function get_has_no_postname_in_permalink_result() {
return $this->get_report_builder()
->set_label( \__( 'You do not have your postname in the URL of your posts and pages', 'wordpress-seo' ) )
->set_status_recommended()
->set_description( $this->get_has_no_postname_in_permalink_description() )
->set_actions( $this->get_has_no_postname_in_permalink_actions() )
->build();
}
/**
* Returns the description for when permalinks are not set to contain the post name.
*
* @return string The description as a string.
*/
private function get_has_no_postname_in_permalink_description() {
return \sprintf(
/* translators: %s expands to '/%postname%/' */
\__( 'It\'s highly recommended to have your postname in the URL of your posts and pages. Consider setting your permalink structure to %s.', 'wordpress-seo' ),
'<strong>/%postname%/</strong>'
);
}
/**
* Returns the actions for when permalinks are not set to contain the post name.
*
* @return string The actions as a string.
*/
private function get_has_no_postname_in_permalink_actions() {
return \sprintf(
/* translators: %1$s is a link start tag to the permalink settings page, %2$s is the link closing tag. */
\__( 'You can fix this on the %1$sPermalink settings page%2$s.', 'wordpress-seo' ),
'<a href="' . \admin_url( 'options-permalink.php' ) . '">',
'</a>'
);
}
}
health-check/links-table-check.php 0000644 00000002647 14751124776 0013104 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Passes when the links table is accessible.
*/
class Links_Table_Check extends Health_Check {
/**
* Runs the health check.
*
* @var Links_Table_Runner
*/
private $runner;
/**
* Generates WordPress-friendly health check results.
*
* @var Links_Table_Reports
*/
private $reports;
/**
* Constructor.
*
* @param Links_Table_Runner $runner The object that implements the actual health check.
* @param Links_Table_Reports $reports The object that generates WordPress-friendly results.
*/
public function __construct(
Links_Table_Runner $runner,
Links_Table_Reports $reports
) {
$this->runner = $runner;
$this->reports = $reports;
$this->reports->set_test_identifier( $this->get_test_identifier() );
$this->set_runner( $this->runner );
}
/**
* Returns a human-readable label for this health check.
*
* @return string The human-readable label.
*/
public function get_test_label() {
return \__( 'Links table', 'wordpress-seo' );
}
/**
* Returns the WordPress-friendly health check result.
*
* @return string[] The WordPress-friendly health check result.
*/
protected function get_result() {
if ( ! $this->runner->should_run() ) {
return [];
}
if ( $this->runner->is_successful() ) {
return $this->reports->get_success_result();
}
return $this->reports->get_links_table_not_accessible_result();
}
}
health-check/default-tagline-runner.php 0000644 00000002165 14751124776 0014173 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Runs the Default_Tagline health check.
*/
class Default_Tagline_Runner implements Runner_Interface {
/**
* The default WordPress tagline.
*/
public const DEFAULT_BLOG_DESCRIPTION = 'Just another WordPress site';
/**
* Is set to true when the default tagline is set.
*
* @var bool
*/
private $has_default_tagline = true;
/**
* Runs the health check. Checks if the tagline is set to WordPress' default tagline, or to its set translation.
*
* @return void
*/
public function run() {
$blog_description = \get_option( 'blogdescription' );
// We are using the WordPress internal translation.
$translated_blog_description = \__( 'Just another WordPress site', 'default' );
$this->has_default_tagline = $translated_blog_description === $blog_description || $blog_description === self::DEFAULT_BLOG_DESCRIPTION;
}
/**
* Returns true if the tagline is set to a non-default tagline.
*
* @return bool The boolean indicating if the health check was succesful.
*/
public function is_successful() {
return ! $this->has_default_tagline;
}
}
health-check/health-check.php 0000644 00000004660 14751124776 0012141 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Abstract class for all health checks. Provides a uniform interface for the Health_Check_Integration.
*/
abstract class Health_Check {
/**
* The prefix to add to the test identifier. Used to differentiate between Yoast's health checks, and other health checks.
*/
public const TEST_IDENTIFIER_PREFIX = 'yoast-';
/**
* The object that runs the actual health check.
*
* @var Runner_Interface
*/
private $runner;
/**
* The health check implementation sets the runner so this class can start a health check.
*
* @param Runner_Interface $runner The health check runner.
* @return void
*/
protected function set_runner( $runner ) {
$this->runner = $runner;
}
/**
* Returns the identifier of health check implementation. WordPress needs this to manage the health check (https://developer.wordpress.org/reference/hooks/site_status_tests/).
*
* @return string The identifier that WordPress requires.
*/
public function get_test_identifier() {
$full_class_name = static::class;
$class_name_backslash_index = \strrpos( $full_class_name, '\\' );
$class_name = $full_class_name;
if ( $class_name_backslash_index ) {
$class_name_index = ( $class_name_backslash_index + 1 );
$class_name = \substr( $full_class_name, $class_name_index );
}
$lowercase = \strtolower( $class_name );
$whitespace_as_dashes = \str_replace( '_', '-', $lowercase );
$with_prefix = self::TEST_IDENTIFIER_PREFIX . $whitespace_as_dashes;
return $with_prefix;
}
/**
* Returns the name of health check implementation that the user can see. WordPress needs this to manage the health check (https://developer.wordpress.org/reference/hooks/site_status_tests/).
*
* @return string A human-readable label for the health check.
*/
abstract public function get_test_label();
/**
* Runs the health check, and returns its result in the format that WordPress requires to show the results to the user (https://developer.wordpress.org/reference/hooks/site_status_test_result/).
*
* @return string[] The array containing a WordPress site status report.
*/
public function run_and_get_result() {
$this->runner->run();
return $this->get_result();
}
/**
* Gets the result from the health check implementation.
*
* @return string[] The array containing a WordPress site status report.
*/
abstract protected function get_result();
}
health-check/default-tagline-reports.php 0000644 00000004410 14751124776 0014353 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
/**
* Presents a set of different messages for the Default_Tagline health check.
*/
class Default_Tagline_Reports {
use Reports_Trait;
/**
* Constructor
*
* @param Report_Builder_Factory $report_builder_factory The factory for result builder objects.
* This class uses the report builder to generate WordPress-friendly
* health check results.
*/
public function __construct( Report_Builder_Factory $report_builder_factory ) {
$this->report_builder_factory = $report_builder_factory;
}
/**
* Returns the message for a successful health check.
*
* @return string[] The message as a WordPress site status report.
*/
public function get_success_result() {
return $this->get_report_builder()
->set_label( \__( 'You changed the default WordPress tagline', 'wordpress-seo' ) )
->set_status_good()
->set_description( \__( 'You are using a custom tagline or an empty one.', 'wordpress-seo' ) )
->build();
}
/**
* Returns the message for a failed health check. In this case, when the user still has the default WordPress tagline set.
*
* @return string[] The message as a WordPress site status report.
*/
public function get_has_default_tagline_result() {
return $this->get_report_builder()
->set_label( \__( 'You should change the default WordPress tagline', 'wordpress-seo' ) )
->set_status_recommended()
->set_description( \__( 'You still have the default WordPress tagline. Even an empty one is probably better.', 'wordpress-seo' ) )
->set_actions( $this->get_actions() )
->build();
}
/**
* Returns the actions that the user should take when his tagline is still set to the WordPress default.
*
* @return string The actions as an HTML string.
*/
private function get_actions() {
$query_args = [
'autofocus[control]' => 'blogdescription',
];
$customize_url = \add_query_arg( $query_args, \wp_customize_url() );
return \sprintf(
/* translators: 1: link open tag; 2: link close tag. */
\esc_html__( '%1$sYou can change the tagline in the customizer%2$s.', 'wordpress-seo' ),
'<a href="' . \esc_url( $customize_url ) . '">',
'</a>'
);
}
}
health-check/links-table-reports.php 0000644 00000006650 14751124776 0013523 0 ustar 00 <?php
namespace Yoast\WP\SEO\Services\Health_Check;
use WPSEO_Admin_Utils;
use WPSEO_Shortlinker;
/**
* Presents a set of different messages for the Links_Table health check.
*/
class Links_Table_Reports {
use Reports_Trait;
/**
* Shortlinker object used to create short links for reports.
*
* @var WPSEO_Shortlinker
*/
private $shortlinker;
/**
* Constructor
*
* @param Report_Builder_Factory $report_builder_factory The factory for result builder objects.
* This class uses the report builder to generate WordPress-friendly
* health check results.
* @param WPSEO_Shortlinker $shortlinker Object used to add short links to the report description.
*/
public function __construct(
Report_Builder_Factory $report_builder_factory,
WPSEO_Shortlinker $shortlinker
) {
$this->report_builder_factory = $report_builder_factory;
$this->shortlinker = $shortlinker;
}
/**
* Returns the message for a successful health check.
*
* @return string[] The message as a WordPress site status report.
*/
public function get_success_result() {
return $this->get_report_builder()
->set_label( \__( 'The text link counter is working as expected', 'wordpress-seo' ) )
->set_status_good()
->set_description( $this->get_success_description() )
->build();
}
/**
* Returns the message for a failed health check.
*
* @return string[] The message as a WordPress site status report.
*/
public function get_links_table_not_accessible_result() {
return $this->get_report_builder()
->set_label( \__( 'The text link counter feature is not working as expected', 'wordpress-seo' ) )
->set_status_recommended()
->set_description( $this->get_links_table_not_accessible_description() )
->set_actions( $this->get_actions() )
->build();
}
/**
* Returns the description for when the health check was successful.
*
* @return string The description as a string.
*/
private function get_success_description() {
return \sprintf(
/* translators: 1: Link to the Yoast SEO blog, 2: Link closing tag. */
\esc_html__( 'The text link counter helps you improve your site structure. %1$sFind out how the text link counter can enhance your SEO%2$s.', 'wordpress-seo' ),
'<a href="' . $this->shortlinker->get( 'https://yoa.st/3zw' ) . '" target="_blank">',
WPSEO_Admin_Utils::get_new_tab_message() . '</a>'
);
}
/**
* Returns the description for when the health couldn't access the links table.
*
* @return string The description as a string.
*/
private function get_links_table_not_accessible_description() {
return \sprintf(
/* translators: 1: Yoast SEO. */
\__( 'For this feature to work, %1$s needs to create a table in your database. We were unable to create this table automatically.', 'wordpress-seo' ),
'Yoast SEO'
);
}
/**
* Returns the actions that the user should take when the links table is not accessible.
*
* @return string The actions as a string.
*/
private function get_actions() {
return \sprintf(
/* translators: 1: Link to the Yoast help center, 2: Link closing tag. */
\esc_html__( '%1$sFind out how to solve this problem on our help center%2$s.', 'wordpress-seo' ),
'<a href="' . $this->shortlinker->get( 'https://yoa.st/3zv' ) . '" target="_blank">',
WPSEO_Admin_Utils::get_new_tab_message() . '</a>'
);
}
}