Phase 5: Content and SEO - Yoast SEO, Schema.org markup, Open Graph, favicon support, XML sitemap
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config;
|
||||
|
||||
/**
|
||||
* Class Badge_Group_Names.
|
||||
*
|
||||
* This class defines groups for "new" badges, with the version in which those groups are no longer considered
|
||||
* to be "new".
|
||||
*/
|
||||
class Badge_Group_Names {
|
||||
|
||||
public const GROUP_GLOBAL_TEMPLATES = 'global-templates';
|
||||
|
||||
/**
|
||||
* Constant describing when certain groups of new badges will no longer be shown.
|
||||
*/
|
||||
public const GROUP_NAMES = [
|
||||
self::GROUP_GLOBAL_TEMPLATES => '16.7-beta0',
|
||||
];
|
||||
|
||||
/**
|
||||
* The current plugin version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
/**
|
||||
* Badge_Group_Names constructor.
|
||||
*
|
||||
* @param string|null $version Optional: the current plugin version.
|
||||
*/
|
||||
public function __construct( $version = null ) {
|
||||
if ( ! $version ) {
|
||||
$version = \WPSEO_VERSION;
|
||||
}
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a group of badges is still eligible for a "new" badge.
|
||||
*
|
||||
* @param string $group One of the GROUP_* constants.
|
||||
* @param string|null $current_version The current version of the plugin that's being checked.
|
||||
*
|
||||
* @return bool Whether a group of badges is still eligible for a "new" badge.
|
||||
*/
|
||||
public function is_still_eligible_for_new_badge( $group, $current_version = null ) {
|
||||
if ( ! \array_key_exists( $group, $this::GROUP_NAMES ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$group_version = $this::GROUP_NAMES[ $group ];
|
||||
|
||||
if ( $current_version === null ) {
|
||||
$current_version = $this->version;
|
||||
}
|
||||
|
||||
return (bool) \version_compare( $group_version, $current_version, '>' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config;
|
||||
|
||||
/**
|
||||
* Conflicting_Plugins class that holds all known conflicting plugins.
|
||||
*/
|
||||
class Conflicting_Plugins {
|
||||
|
||||
public const OPEN_GRAPH_PLUGINS = [
|
||||
'2-click-socialmedia-buttons/2-click-socialmedia-buttons.php',
|
||||
// 2 Click Social Media Buttons.
|
||||
'add-link-to-facebook/add-link-to-facebook.php', // Add Link to Facebook.
|
||||
'add-meta-tags/add-meta-tags.php', // Add Meta Tags.
|
||||
'easy-facebook-share-thumbnails/esft.php', // Easy Facebook Share Thumbnail.
|
||||
'facebook/facebook.php', // Facebook (official plugin).
|
||||
'facebook-awd/AWD_facebook.php', // Facebook AWD All in one.
|
||||
'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php',
|
||||
// Facebook Featured Image & OG Meta Tags.
|
||||
'facebook-meta-tags/facebook-metatags.php', // Facebook Meta Tags.
|
||||
'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php',
|
||||
// Facebook Open Graph Meta Tags for WordPress.
|
||||
'facebook-revised-open-graph-meta-tag/index.php', // Facebook Revised Open Graph Meta Tag.
|
||||
'facebook-thumb-fixer/_facebook-thumb-fixer.php', // Facebook Thumb Fixer.
|
||||
'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php',
|
||||
// Fedmich's Facebook Open Graph Meta.
|
||||
'network-publisher/networkpub.php', // Network Publisher.
|
||||
'nextgen-facebook/nextgen-facebook.php', // NextGEN Facebook OG.
|
||||
'opengraph/opengraph.php', // Open Graph.
|
||||
'open-graph-protocol-framework/open-graph-protocol-framework.php',
|
||||
// Open Graph Protocol Framework.
|
||||
'seo-facebook-comments/seofacebook.php', // SEO Facebook Comments.
|
||||
'sexybookmarks/sexy-bookmarks.php', // Shareaholic.
|
||||
'shareaholic/sexy-bookmarks.php', // Shareaholic.
|
||||
'sharepress/sharepress.php', // SharePress.
|
||||
'simple-facebook-connect/sfc.php', // Simple Facebook Connect.
|
||||
'social-discussions/social-discussions.php', // Social Discussions.
|
||||
'social-sharing-toolkit/social_sharing_toolkit.php', // Social Sharing Toolkit.
|
||||
'socialize/socialize.php', // Socialize.
|
||||
'only-tweet-like-share-and-google-1/tweet-like-plusone.php',
|
||||
// Tweet, Like, Google +1 and Share.
|
||||
'wordbooker/wordbooker.php', // Wordbooker.
|
||||
'wpsso/wpsso.php', // WordPress Social Sharing Optimization.
|
||||
'wp-caregiver/wp-caregiver.php', // WP Caregiver.
|
||||
'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php',
|
||||
// WP Facebook Like Send & Open Graph Meta.
|
||||
'wp-facebook-open-graph-protocol/wp-facebook-ogp.php', // WP Facebook Open Graph protocol.
|
||||
'wp-ogp/wp-ogp.php', // WP-OGP.
|
||||
'zoltonorg-social-plugin/zosp.php', // Zolton.org Social Plugin.
|
||||
];
|
||||
|
||||
public const XML_SITEMAPS_PLUGINS = [
|
||||
'google-sitemap-plugin/google-sitemap-plugin.php',
|
||||
// Google Sitemap (BestWebSoft).
|
||||
'xml-sitemaps/xml-sitemaps.php',
|
||||
// XML Sitemaps (Denis de Bernardy and Mike Koepke).
|
||||
'bwp-google-xml-sitemaps/bwp-simple-gxs.php',
|
||||
// Better WordPress Google XML Sitemaps (Khang Minh).
|
||||
'google-sitemap-generator/sitemap.php',
|
||||
// Google XML Sitemaps (Arne Brachhold).
|
||||
'xml-sitemap-feed/xml-sitemap.php',
|
||||
// XML Sitemap & Google News feeds (RavanH).
|
||||
'google-monthly-xml-sitemap/monthly-xml-sitemap.php',
|
||||
// Google Monthly XML Sitemap (Andrea Pernici).
|
||||
'simple-google-sitemap-xml/simple-google-sitemap-xml.php',
|
||||
// Simple Google Sitemap XML (iTx Technologies).
|
||||
'another-simple-xml-sitemap/another-simple-xml-sitemap.php',
|
||||
// Another Simple XML Sitemap.
|
||||
'xml-maps/google-sitemap.php',
|
||||
// Xml Sitemap (Jason Martens).
|
||||
'google-xml-sitemap-generator-by-anton-dachauer/adachauer-google-xml-sitemap.php',
|
||||
// Google XML Sitemap Generator by Anton Dachauer (Anton Dachauer).
|
||||
'wp-xml-sitemap/wp-xml-sitemap.php',
|
||||
// WP XML Sitemap (Team Vivacity).
|
||||
'sitemap-generator-for-webmasters/sitemap.php',
|
||||
// Sitemap Generator for Webmasters (iwebslogtech).
|
||||
'xml-sitemap-xml-sitemapcouk/xmls.php',
|
||||
// XML Sitemap - XML-Sitemap.co.uk (Simon Hancox).
|
||||
'sewn-in-xml-sitemap/sewn-xml-sitemap.php',
|
||||
// Sewn In XML Sitemap (jcow).
|
||||
'rps-sitemap-generator/rps-sitemap-generator.php',
|
||||
// RPS Sitemap Generator (redpixelstudios).
|
||||
];
|
||||
|
||||
public const CLOAKING_PLUGINS = [
|
||||
'rs-head-cleaner/rs-head-cleaner.php',
|
||||
// RS Head Cleaner Plus https://wordpress.org/plugins/rs-head-cleaner/.
|
||||
'rs-head-cleaner-lite/rs-head-cleaner-lite.php',
|
||||
// RS Head Cleaner Lite https://wordpress.org/plugins/rs-head-cleaner-lite/.
|
||||
];
|
||||
|
||||
public const SEO_PLUGINS = [
|
||||
'all-in-one-seo-pack/all_in_one_seo_pack.php', // All in One SEO Pack.
|
||||
'seo-ultimate/seo-ultimate.php', // SEO Ultimate.
|
||||
'seo-by-rank-math/rank-math.php', // Rank Math.
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns the list of all conflicting plugins.
|
||||
*
|
||||
* @return array The list of all conflicting plugins.
|
||||
*/
|
||||
public static function all_plugins() {
|
||||
return \array_merge(
|
||||
self::OPEN_GRAPH_PLUGINS,
|
||||
self::XML_SITEMAPS_PLUGINS,
|
||||
self::CLOAKING_PLUGINS,
|
||||
self::SEO_PLUGINS
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config;
|
||||
|
||||
/**
|
||||
* Class Indexing_Reasons. Contains constants that aren't context specific.
|
||||
*/
|
||||
class Indexing_Reasons {
|
||||
|
||||
/**
|
||||
* Represents the reason that the indexing process failed and should be tried again.
|
||||
*/
|
||||
public const REASON_INDEXING_FAILED = 'indexing_failed';
|
||||
|
||||
/**
|
||||
* Represents the reason that the permalink settings are changed.
|
||||
*/
|
||||
public const REASON_PERMALINK_SETTINGS = 'permalink_settings_changed';
|
||||
|
||||
/**
|
||||
* Represents the reason that the category base is changed.
|
||||
*/
|
||||
public const REASON_CATEGORY_BASE_PREFIX = 'category_base_changed';
|
||||
|
||||
/**
|
||||
* Represents the reason that the tag base is changed.
|
||||
*/
|
||||
public const REASON_TAG_BASE_PREFIX = 'tag_base_changed';
|
||||
|
||||
/**
|
||||
* Represents the reason that the home url option is changed.
|
||||
*/
|
||||
public const REASON_HOME_URL_OPTION = 'home_url_option_changed';
|
||||
|
||||
/**
|
||||
* Represents the reason that a post type has been made public.
|
||||
*/
|
||||
public const REASON_POST_TYPE_MADE_PUBLIC = 'post_type_made_public';
|
||||
|
||||
/**
|
||||
* Represents the reason that a post type has been made viewable.
|
||||
*/
|
||||
public const REASON_TAXONOMY_MADE_PUBLIC = 'taxonomy_made_public';
|
||||
|
||||
/**
|
||||
* Represents the reason that attachments have stopped being redirected.
|
||||
*/
|
||||
public const REASON_ATTACHMENTS_MADE_ENABLED = 'attachments_made_enabled';
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config;
|
||||
|
||||
/**
|
||||
* Migration_Status class.
|
||||
*
|
||||
* Used to validate whether or not migrations have been run and whether or not they should be run again.
|
||||
*/
|
||||
class Migration_Status {
|
||||
|
||||
/**
|
||||
* The migration option key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const MIGRATION_OPTION_KEY = 'yoast_migrations_';
|
||||
|
||||
/**
|
||||
* The migration options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $migration_options = [];
|
||||
|
||||
/**
|
||||
* Checks if a given migration should be run.
|
||||
*
|
||||
* @param string $name The name of the migration.
|
||||
* @param string $version The current version.
|
||||
*
|
||||
* @return bool Whether or not the migration should be run.
|
||||
*/
|
||||
public function should_run_migration( $name, $version = \WPSEO_VERSION ) {
|
||||
$migration_status = $this->get_migration_status( $name );
|
||||
|
||||
// Check if we've attempted to run this migration in the past 10 minutes. If so, it may still be running.
|
||||
if ( \array_key_exists( 'lock', $migration_status ) ) {
|
||||
$timestamp = \strtotime( '-10 minutes' );
|
||||
|
||||
return $timestamp > $migration_status['lock'];
|
||||
}
|
||||
|
||||
// Is the migration version less than the current version.
|
||||
return \version_compare( $migration_status['version'], $version, '<' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not the given migration is at least the given version, defaults to checking for the latest version.
|
||||
*
|
||||
* @param string $name The name of the migration.
|
||||
* @param string $version The version to check, defaults to the latest version.
|
||||
*
|
||||
* @return bool Whether or not the requested migration is at least the requested version.
|
||||
*/
|
||||
public function is_version( $name, $version = \WPSEO_VERSION ) {
|
||||
$migration_status = $this->get_migration_status( $name );
|
||||
|
||||
return \version_compare( $version, $migration_status['version'], '<=' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the error of a given migration if it exists.
|
||||
*
|
||||
* @param string $name The name of the migration.
|
||||
*
|
||||
* @return bool|array False if there is no error, otherwise the error.
|
||||
*/
|
||||
public function get_error( $name ) {
|
||||
$migration_status = $this->get_migration_status( $name );
|
||||
|
||||
if ( ! isset( $migration_status['error'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $migration_status['error'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an error for the migration.
|
||||
*
|
||||
* @param string $name The name of the migration.
|
||||
* @param string $message Message explaining the reason for the error.
|
||||
* @param string $version The current version.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_error( $name, $message, $version = \WPSEO_VERSION ) {
|
||||
$migration_status = $this->get_migration_status( $name );
|
||||
|
||||
$migration_status['error'] = [
|
||||
'time' => \strtotime( 'now' ),
|
||||
'version' => $version,
|
||||
'message' => $message,
|
||||
];
|
||||
|
||||
$this->set_migration_status( $name, $migration_status );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the migration version to the latest version.
|
||||
*
|
||||
* @param string $name The name of the migration.
|
||||
* @param string $version The current version.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_success( $name, $version = \WPSEO_VERSION ) {
|
||||
$migration_status = $this->get_migration_status( $name );
|
||||
unset( $migration_status['lock'] );
|
||||
unset( $migration_status['error'] );
|
||||
$migration_status['version'] = $version;
|
||||
$this->set_migration_status( $name, $migration_status );
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks the migration status.
|
||||
*
|
||||
* @param string $name The name of the migration.
|
||||
*
|
||||
* @return bool Whether or not the migration was succesfully locked.
|
||||
*/
|
||||
public function lock_migration( $name ) {
|
||||
$migration_status = $this->get_migration_status( $name );
|
||||
$migration_status['lock'] = \strtotime( 'now' );
|
||||
|
||||
return $this->set_migration_status( $name, $migration_status );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the migration option.
|
||||
*
|
||||
* @param string $name The name of the migration.
|
||||
*
|
||||
* @return bool|array The status of the migration, false if no status exists.
|
||||
*/
|
||||
protected function get_migration_status( $name ) {
|
||||
$current_blog_id = \get_current_blog_id();
|
||||
if ( ! isset( $this->migration_options[ $current_blog_id ][ $name ] ) ) {
|
||||
$migration_status = \get_option( self::MIGRATION_OPTION_KEY . $name );
|
||||
|
||||
if ( ! \is_array( $migration_status ) || ! isset( $migration_status['version'] ) ) {
|
||||
$migration_status = [ 'version' => '0.0' ];
|
||||
}
|
||||
|
||||
if ( ! isset( $this->migration_options[ $current_blog_id ] ) ) {
|
||||
$this->migration_options[ $current_blog_id ] = [];
|
||||
}
|
||||
$this->migration_options[ $current_blog_id ][ $name ] = $migration_status;
|
||||
}
|
||||
|
||||
return $this->migration_options[ $current_blog_id ][ $name ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the migration option.
|
||||
*
|
||||
* @param string $name The name of the migration.
|
||||
* @param array $migration_status The migration status.
|
||||
*
|
||||
* @return bool True if the status was succesfully updated, false otherwise.
|
||||
*/
|
||||
protected function set_migration_status( $name, $migration_status ) {
|
||||
if ( ! \is_array( $migration_status ) || ! isset( $migration_status['version'] ) ) {
|
||||
return false;
|
||||
}
|
||||
$current_blog_id = \get_current_blog_id();
|
||||
|
||||
if ( ! isset( $this->migration_options[ $current_blog_id ] ) ) {
|
||||
$this->migration_options[ $current_blog_id ] = [];
|
||||
}
|
||||
$this->migration_options[ $current_blog_id ][ $name ] = $migration_status;
|
||||
|
||||
return \update_option( self::MIGRATION_OPTION_KEY . $name, $migration_status );
|
||||
}
|
||||
}
|
||||
Executable
+364
@@ -0,0 +1,364 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* Indexable migration.
|
||||
*/
|
||||
class WpYoastIndexable extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$this->add_table();
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$this->drop_table( $this->get_table_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the indexable table.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function add_table() {
|
||||
$table_name = $this->get_table_name();
|
||||
|
||||
$indexable_table = $this->create_table( $table_name );
|
||||
|
||||
// Permalink.
|
||||
$indexable_table->column( 'permalink', 'mediumtext', [ 'null' => true ] );
|
||||
$indexable_table->column(
|
||||
'permalink_hash',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
]
|
||||
);
|
||||
|
||||
// Object information.
|
||||
$indexable_table->column(
|
||||
'object_id',
|
||||
'integer',
|
||||
[
|
||||
'unsigned' => true,
|
||||
'null' => true,
|
||||
'limit' => 11,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'object_type',
|
||||
'string',
|
||||
[
|
||||
'null' => false,
|
||||
'limit' => 32,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'object_sub_type',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 32,
|
||||
]
|
||||
);
|
||||
|
||||
// Ownership.
|
||||
$indexable_table->column(
|
||||
'author_id',
|
||||
'integer',
|
||||
[
|
||||
'unsigned' => true,
|
||||
'null' => true,
|
||||
'limit' => 11,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'post_parent',
|
||||
'integer',
|
||||
[
|
||||
'unsigned' => true,
|
||||
'null' => true,
|
||||
'limit' => 11,
|
||||
]
|
||||
);
|
||||
|
||||
// Title and description.
|
||||
$indexable_table->column(
|
||||
'title',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
]
|
||||
);
|
||||
$indexable_table->column( 'description', 'text', [ 'null' => true ] );
|
||||
$indexable_table->column(
|
||||
'breadcrumb_title',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
]
|
||||
);
|
||||
|
||||
// Post metadata: status, public, protected.
|
||||
$indexable_table->column(
|
||||
'post_status',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'is_public',
|
||||
'boolean',
|
||||
[
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
]
|
||||
);
|
||||
$indexable_table->column( 'is_protected', 'boolean', [ 'default' => false ] );
|
||||
$indexable_table->column(
|
||||
'has_public_posts',
|
||||
'boolean',
|
||||
[
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
]
|
||||
);
|
||||
|
||||
$indexable_table->column(
|
||||
'number_of_pages',
|
||||
'integer',
|
||||
[
|
||||
'unsigned' => true,
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
'limit' => 11,
|
||||
]
|
||||
);
|
||||
|
||||
$indexable_table->column( 'canonical', 'mediumtext', [ 'null' => true ] );
|
||||
|
||||
// SEO and readability analysis.
|
||||
$indexable_table->column(
|
||||
'primary_focus_keyword',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'primary_focus_keyword_score',
|
||||
'integer',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 3,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'readability_score',
|
||||
'integer',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 3,
|
||||
]
|
||||
);
|
||||
$indexable_table->column( 'is_cornerstone', 'boolean', [ 'default' => false ] );
|
||||
|
||||
// Robots.
|
||||
$indexable_table->column(
|
||||
'is_robots_noindex',
|
||||
'boolean',
|
||||
[
|
||||
'null' => true,
|
||||
'default' => false,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'is_robots_nofollow',
|
||||
'boolean',
|
||||
[
|
||||
'null' => true,
|
||||
'default' => false,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'is_robots_noarchive',
|
||||
'boolean',
|
||||
[
|
||||
'null' => true,
|
||||
'default' => false,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'is_robots_noimageindex',
|
||||
'boolean',
|
||||
[
|
||||
'null' => true,
|
||||
'default' => false,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'is_robots_nosnippet',
|
||||
'boolean',
|
||||
[
|
||||
'null' => true,
|
||||
'default' => false,
|
||||
]
|
||||
);
|
||||
|
||||
// Twitter.
|
||||
$indexable_table->column(
|
||||
'twitter_title',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
]
|
||||
);
|
||||
$indexable_table->column( 'twitter_image', 'mediumtext', [ 'null' => true ] );
|
||||
$indexable_table->column( 'twitter_description', 'mediumtext', [ 'null' => true ] );
|
||||
$indexable_table->column(
|
||||
'twitter_image_id',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'twitter_image_source',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
]
|
||||
);
|
||||
|
||||
// Open-Graph.
|
||||
$indexable_table->column(
|
||||
'open_graph_title',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
]
|
||||
);
|
||||
$indexable_table->column( 'open_graph_description', 'mediumtext', [ 'null' => true ] );
|
||||
$indexable_table->column( 'open_graph_image', 'mediumtext', [ 'null' => true ] );
|
||||
$indexable_table->column(
|
||||
'open_graph_image_id',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'open_graph_image_source',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
]
|
||||
);
|
||||
$indexable_table->column( 'open_graph_image_meta', 'text', [ 'null' => true ] );
|
||||
|
||||
// Link count.
|
||||
$indexable_table->column(
|
||||
'link_count',
|
||||
'integer',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 11,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'incoming_link_count',
|
||||
'integer',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 11,
|
||||
]
|
||||
);
|
||||
|
||||
// Prominent words.
|
||||
$indexable_table->column(
|
||||
'prominent_words_version',
|
||||
'integer',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 11,
|
||||
'unsigned' => true,
|
||||
'default' => null,
|
||||
]
|
||||
);
|
||||
|
||||
$indexable_table->finish();
|
||||
|
||||
$this->add_indexes( $table_name );
|
||||
|
||||
$this->add_timestamps( $table_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds indexes to the indexable table.
|
||||
*
|
||||
* @param string $indexable_table_name The name of the indexable table.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function add_indexes( $indexable_table_name ) {
|
||||
$this->add_index(
|
||||
$indexable_table_name,
|
||||
[
|
||||
'object_type',
|
||||
'object_sub_type',
|
||||
],
|
||||
[
|
||||
'name' => 'object_type_and_sub_type',
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_index(
|
||||
$indexable_table_name,
|
||||
'permalink_hash',
|
||||
[
|
||||
'name' => 'permalink_hash',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use for storing indexables.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
}
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* Migration for the Primary Term.
|
||||
*/
|
||||
class WpYoastPrimaryTerm extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$table_name = $this->get_table_name();
|
||||
|
||||
$indexable_table = $this->create_table( $table_name );
|
||||
|
||||
$indexable_table->column(
|
||||
'post_id',
|
||||
'integer',
|
||||
[
|
||||
'unsigned' => true,
|
||||
'null' => false,
|
||||
'limit' => 11,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'term_id',
|
||||
'integer',
|
||||
[
|
||||
'unsigned' => true,
|
||||
'null' => false,
|
||||
'limit' => 11,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'taxonomy',
|
||||
'string',
|
||||
[
|
||||
'null' => false,
|
||||
'limit' => 32,
|
||||
]
|
||||
);
|
||||
|
||||
// Executes the SQL to create the table.
|
||||
$indexable_table->finish();
|
||||
|
||||
$this->add_index(
|
||||
$table_name,
|
||||
[
|
||||
'post_id',
|
||||
'taxonomy',
|
||||
],
|
||||
[
|
||||
'name' => 'post_taxonomy',
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_index(
|
||||
$table_name,
|
||||
[
|
||||
'post_id',
|
||||
'term_id',
|
||||
],
|
||||
[
|
||||
'name' => 'post_term',
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_timestamps( $table_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$this->drop_table( $this->get_table_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use.
|
||||
*
|
||||
* @return string Table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Primary_Term' );
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* Class WpYoastDropIndexableMetaTableIfExists.
|
||||
*/
|
||||
class WpYoastDropIndexableMetaTableIfExists extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$table_name = $this->get_table_name();
|
||||
|
||||
// This can be done safely as it executes a DROP IF EXISTS.
|
||||
$this->drop_table( $table_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
// No down required. This specific table should never exist.
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable_Meta' );
|
||||
}
|
||||
}
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* Class WpYoastIndexableHierarchy.
|
||||
*/
|
||||
class WpYoastIndexableHierarchy extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$table_name = $this->get_table_name();
|
||||
|
||||
$indexable_table = $this->create_table( $table_name, [ 'id' => false ] );
|
||||
|
||||
$indexable_table->column(
|
||||
'indexable_id',
|
||||
'integer',
|
||||
[
|
||||
'primary_key' => true,
|
||||
'unsigned' => true,
|
||||
'null' => true,
|
||||
'limit' => 11,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'ancestor_id',
|
||||
'integer',
|
||||
[
|
||||
'primary_key' => true,
|
||||
'unsigned' => true,
|
||||
'null' => true,
|
||||
'limit' => 11,
|
||||
]
|
||||
);
|
||||
$indexable_table->column(
|
||||
'depth',
|
||||
'integer',
|
||||
[
|
||||
'unsigned' => true,
|
||||
'null' => true,
|
||||
'limit' => 11,
|
||||
]
|
||||
);
|
||||
$indexable_table->finish();
|
||||
|
||||
$this->add_index( $table_name, 'indexable_id', [ 'name' => 'indexable_id' ] );
|
||||
$this->add_index( $table_name, 'ancestor_id', [ 'name' => 'ancestor_id' ] );
|
||||
$this->add_index( $table_name, 'depth', [ 'name' => 'depth' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$this->drop_table( $this->get_table_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable_Hierarchy' );
|
||||
}
|
||||
}
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* Class AddCollationToTables.
|
||||
*/
|
||||
class AddCollationToTables extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
global $wpdb;
|
||||
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
if ( empty( $charset_collate ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tables = [
|
||||
Model::get_table_name( 'migrations' ),
|
||||
Model::get_table_name( 'Indexable' ),
|
||||
Model::get_table_name( 'Indexable_Hierarchy' ),
|
||||
Model::get_table_name( 'Primary_Term' ),
|
||||
];
|
||||
|
||||
foreach ( $tables as $table ) {
|
||||
$this->query( 'ALTER TABLE ' . $table . ' CONVERT TO ' . \str_replace( 'DEFAULT ', '', $charset_collate ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
// No down required.
|
||||
}
|
||||
}
|
||||
Executable
+96
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* Class AddColumnsToIndexables.
|
||||
*/
|
||||
class AddColumnsToIndexables extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$tables = $this->get_tables();
|
||||
$blog_id = \get_current_blog_id();
|
||||
foreach ( $tables as $table ) {
|
||||
$this->add_column(
|
||||
$table,
|
||||
'blog_id',
|
||||
'biginteger',
|
||||
[
|
||||
'null' => false,
|
||||
'limit' => 20,
|
||||
'default' => $blog_id,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$attr_limit_32 = [
|
||||
'null' => true,
|
||||
'limit' => 32,
|
||||
];
|
||||
$attr_limit_64 = [
|
||||
'null' => true,
|
||||
'limit' => 64,
|
||||
];
|
||||
|
||||
$indexable_table = $this->get_indexable_table();
|
||||
$this->add_column( $indexable_table, 'language', 'string', $attr_limit_32 );
|
||||
$this->add_column( $indexable_table, 'region', 'string', $attr_limit_32 );
|
||||
$this->add_column( $indexable_table, 'schema_page_type', 'string', $attr_limit_64 );
|
||||
$this->add_column( $indexable_table, 'schema_article_type', 'string', $attr_limit_64 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$tables = $this->get_tables();
|
||||
foreach ( $tables as $table ) {
|
||||
$this->remove_column( $table, 'blog_id' );
|
||||
}
|
||||
|
||||
$indexable_table = $this->get_indexable_table();
|
||||
$this->remove_column( $indexable_table, 'language' );
|
||||
$this->remove_column( $indexable_table, 'region' );
|
||||
$this->remove_column( $indexable_table, 'schema_page_type' );
|
||||
$this->remove_column( $indexable_table, 'schema_article_type' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Indexable table.
|
||||
*
|
||||
* @return string The Indexable table name.
|
||||
*/
|
||||
protected function get_indexable_table() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table names to use.
|
||||
*
|
||||
* @return string[] The table names to use.
|
||||
*/
|
||||
protected function get_tables() {
|
||||
return [
|
||||
$this->get_indexable_table(),
|
||||
Model::get_table_name( 'Indexable_Hierarchy' ),
|
||||
Model::get_table_name( 'Primary_Term' ),
|
||||
];
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* Class BreadcrumbTitleAndHierarchyReset.
|
||||
*/
|
||||
class BreadcrumbTitleAndHierarchyReset extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$this->change_column( $this->get_indexable_table_name(), 'breadcrumb_title', 'text', [ 'null' => true ] );
|
||||
$this->query( 'DELETE FROM ' . $this->get_indexable_hierarchy_table_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$this->change_column(
|
||||
$this->get_indexable_table_name(),
|
||||
'breadcrumb_title',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use for storing indexables.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_indexable_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_indexable_hierarchy_table_name() {
|
||||
return Model::get_table_name( 'Indexable_Hierarchy' );
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* Class ExpandIndexableColumnLengths.
|
||||
*/
|
||||
class ExpandIndexableColumnLengths extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$this->change_column( $this->get_table_name(), 'title', 'text', [ 'null' => true ] );
|
||||
$this->change_column( $this->get_table_name(), 'open_graph_title', 'text', [ 'null' => true ] );
|
||||
$this->change_column( $this->get_table_name(), 'twitter_title', 'text', [ 'null' => true ] );
|
||||
$this->change_column( $this->get_table_name(), 'open_graph_image_source', 'text', [ 'null' => true ] );
|
||||
$this->change_column( $this->get_table_name(), 'twitter_image_source', 'text', [ 'null' => true ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$attr_limit_191 = [
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
];
|
||||
|
||||
$this->change_column(
|
||||
$this->get_table_name(),
|
||||
'title',
|
||||
'string',
|
||||
$attr_limit_191
|
||||
);
|
||||
$this->change_column(
|
||||
$this->get_table_name(),
|
||||
'opengraph_title',
|
||||
'string',
|
||||
$attr_limit_191
|
||||
);
|
||||
$this->change_column(
|
||||
$this->get_table_name(),
|
||||
'twitter_title',
|
||||
'string',
|
||||
$attr_limit_191
|
||||
);
|
||||
$this->change_column(
|
||||
$this->get_table_name(),
|
||||
'open_graph_image_source',
|
||||
'string',
|
||||
$attr_limit_191
|
||||
);
|
||||
$this->change_column(
|
||||
$this->get_table_name(),
|
||||
'twitter_image_source',
|
||||
'string',
|
||||
$attr_limit_191
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use for storing indexables.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
}
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* Class TruncateIndexableTables.
|
||||
*/
|
||||
class TruncateIndexableTables extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$this->query( 'TRUNCATE TABLE ' . $this->get_indexable_table_name() );
|
||||
$this->query( 'TRUNCATE TABLE ' . $this->get_indexable_hierarchy_table_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use for storing indexables.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_indexable_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_indexable_hierarchy_table_name() {
|
||||
return Model::get_table_name( 'Indexable_Hierarchy' );
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* Class AddIndexableObjectIdAndTypeIndex.
|
||||
*/
|
||||
class AddIndexableObjectIdAndTypeIndex extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$this->add_index(
|
||||
$this->get_table_name(),
|
||||
[
|
||||
'object_id',
|
||||
'object_type',
|
||||
],
|
||||
[
|
||||
'name' => 'object_id_and_type',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$this->remove_index(
|
||||
$this->get_table_name(),
|
||||
[
|
||||
'object_id',
|
||||
'object_type',
|
||||
],
|
||||
[
|
||||
'name' => 'object_id_and_type',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use for storing indexables.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
}
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* Class ClearIndexableTables.
|
||||
*/
|
||||
class ClearIndexableTables extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$this->query( 'TRUNCATE TABLE ' . $this->get_indexable_table_name() );
|
||||
$this->query( 'TRUNCATE TABLE ' . $this->get_indexable_hierarchy_table_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use for storing indexables.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_indexable_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_indexable_hierarchy_table_name() {
|
||||
return Model::get_table_name( 'Indexable_Hierarchy' );
|
||||
}
|
||||
}
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* Class DeleteDuplicateIndexables.
|
||||
*/
|
||||
class DeleteDuplicateIndexables extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$table_name = $this->get_table_name();
|
||||
|
||||
/*
|
||||
* Deletes duplicate indexables that have the same object_id and object_type.
|
||||
* The rows with a higher ID are deleted as those should be unused and could be outdated.
|
||||
*/
|
||||
$this->query( 'DELETE wyi FROM ' . $table_name . ' wyi INNER JOIN ' . $table_name . ' wyi2 WHERE wyi2.object_id = wyi.object_id AND wyi2.object_type = wyi.object_type AND wyi2.id < wyi.id;' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* Class ResetIndexableHierarchyTable.
|
||||
*/
|
||||
class ResetIndexableHierarchyTable extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$this->query( 'TRUNCATE TABLE ' . $this->get_table_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable_Hierarchy' );
|
||||
}
|
||||
}
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
use Yoast\WP\SEO\WordPress\Wrapper;
|
||||
|
||||
/**
|
||||
* Class AddHasAncestorsColumn.
|
||||
*/
|
||||
class AddHasAncestorsColumn extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$this->add_column(
|
||||
Model::get_table_name( 'Indexable' ),
|
||||
'has_ancestors',
|
||||
'boolean',
|
||||
[
|
||||
'default' => false,
|
||||
]
|
||||
);
|
||||
|
||||
Wrapper::get_wpdb()->query(
|
||||
'
|
||||
UPDATE ' . Model::get_table_name( 'Indexable' ) . '
|
||||
SET has_ancestors = 1
|
||||
WHERE id IN ( SELECT indexable_id FROM ' . Model::get_table_name( 'Indexable_Hierarchy' ) . ' )
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$this->remove_column( Model::get_table_name( 'Indexable' ), 'has_ancestors' );
|
||||
}
|
||||
}
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* ReplacePermalinkHashIndex class.
|
||||
*/
|
||||
class ReplacePermalinkHashIndex extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$table_name = $this->get_table_name();
|
||||
$adapter = $this->get_adapter();
|
||||
|
||||
if ( ! $adapter->has_table( $table_name ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->change_column(
|
||||
$table_name,
|
||||
'permalink_hash',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 40,
|
||||
]
|
||||
);
|
||||
|
||||
if ( $adapter->has_index( $table_name, [ 'permalink_hash' ], [ 'name' => 'permalink_hash' ] ) ) {
|
||||
$this->remove_index(
|
||||
$table_name,
|
||||
[
|
||||
'permalink_hash',
|
||||
],
|
||||
[
|
||||
'name' => 'permalink_hash',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! $adapter->has_index( $table_name, [ 'permalink_hash', 'object_type' ], [ 'name' => 'permalink_hash_and_object_type' ] ) ) {
|
||||
$this->add_index(
|
||||
$table_name,
|
||||
[
|
||||
'permalink_hash',
|
||||
'object_type',
|
||||
],
|
||||
[
|
||||
'name' => 'permalink_hash_and_object_type',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$table_name = $this->get_table_name();
|
||||
$adapter = $this->get_adapter();
|
||||
|
||||
if ( ! $adapter->has_table( $table_name ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $adapter->has_index( $table_name, [ 'permalink_hash', 'object_type' ], [ 'name' => 'permalink_hash_and_object_type' ] ) ) {
|
||||
$this->remove_index(
|
||||
$table_name,
|
||||
[
|
||||
'permalink_hash',
|
||||
'object_type',
|
||||
],
|
||||
[
|
||||
'name' => 'permalink_hash_and_object_type',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$this->change_column(
|
||||
$table_name,
|
||||
'permalink_hash',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
]
|
||||
);
|
||||
|
||||
if ( ! $adapter->has_index( $table_name, [ 'permalink_hash' ], [ 'name' => 'permalink_hash' ] ) ) {
|
||||
$this->add_index(
|
||||
$table_name,
|
||||
[
|
||||
'permalink_hash',
|
||||
],
|
||||
[
|
||||
'name' => 'permalink_hash',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use for storing indexables.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
}
|
||||
Executable
+96
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* CreateSEOLinksTable class.
|
||||
*/
|
||||
class CreateSEOLinksTable extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$table_name = $this->get_table_name();
|
||||
$adapter = $this->get_adapter();
|
||||
|
||||
// The table may already have been created by legacy code.
|
||||
// If not, create it exactly as it was.
|
||||
if ( ! $adapter->table_exists( $table_name ) ) {
|
||||
$table = $this->create_table( $table_name, [ 'id' => false ] );
|
||||
$table->column(
|
||||
'id',
|
||||
'biginteger',
|
||||
[
|
||||
'primary_key' => true,
|
||||
'limit' => 20,
|
||||
'unsigned' => true,
|
||||
'auto_increment' => true,
|
||||
]
|
||||
);
|
||||
$table->column( 'url', 'string', [ 'limit' => 255 ] );
|
||||
$table->column(
|
||||
'post_id',
|
||||
'biginteger',
|
||||
[
|
||||
'limit' => 20,
|
||||
'unsigned' => true,
|
||||
]
|
||||
);
|
||||
$table->column(
|
||||
'target_post_id',
|
||||
'biginteger',
|
||||
[
|
||||
'limit' => 20,
|
||||
'unsigned' => true,
|
||||
]
|
||||
);
|
||||
$table->column( 'type', 'string', [ 'limit' => 8 ] );
|
||||
$table->finish();
|
||||
}
|
||||
if ( ! $adapter->has_index( $table_name, [ 'post_id', 'type' ], [ 'name' => 'link_direction' ] ) ) {
|
||||
$this->add_index( $table_name, [ 'post_id', 'type' ], [ 'name' => 'link_direction' ] );
|
||||
}
|
||||
|
||||
// Add these columns outside of the initial table creation as these did not exist on the legacy table.
|
||||
$this->add_column( $table_name, 'indexable_id', 'integer', [ 'unsigned' => true ] );
|
||||
$this->add_column( $table_name, 'target_indexable_id', 'integer', [ 'unsigned' => true ] );
|
||||
$this->add_column( $table_name, 'height', 'integer', [ 'unsigned' => true ] );
|
||||
$this->add_column( $table_name, 'width', 'integer', [ 'unsigned' => true ] );
|
||||
$this->add_column( $table_name, 'size', 'integer', [ 'unsigned' => true ] );
|
||||
$this->add_column( $table_name, 'language', 'string', [ 'limit' => 32 ] );
|
||||
$this->add_column( $table_name, 'region', 'string', [ 'limit' => 32 ] );
|
||||
|
||||
$this->add_index( $table_name, [ 'indexable_id', 'type' ], [ 'name' => 'indexable_link_direction' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$this->drop_table( $this->get_table_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SEO Links table name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_table_name() {
|
||||
return Model::get_table_name( 'SEO_Links' );
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* CreateIndexableSubpagesIndex class.
|
||||
*/
|
||||
class CreateIndexableSubpagesIndex extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$this->change_column(
|
||||
$this->get_table_name(),
|
||||
'post_status',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 20,
|
||||
]
|
||||
);
|
||||
$this->add_index(
|
||||
$this->get_table_name(),
|
||||
[ 'post_parent', 'object_type', 'post_status', 'object_id' ],
|
||||
[ 'name' => 'subpages' ]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$this->change_column(
|
||||
$this->get_table_name(),
|
||||
'post_status',
|
||||
'string',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
]
|
||||
);
|
||||
$this->remove_index(
|
||||
$this->get_table_name(),
|
||||
[ 'post_parent', 'object_type', 'post_status', 'object_id' ],
|
||||
[ 'name' => 'subpages' ]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use for storing indexables.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* AddIndexesForProminentWordsOnIndexables class.
|
||||
*/
|
||||
class AddIndexesForProminentWordsOnIndexables extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* The columns on which an index should be added.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $columns_with_index = [
|
||||
'prominent_words_version',
|
||||
'object_type',
|
||||
'object_sub_type',
|
||||
'post_status',
|
||||
];
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$table_name = $this->get_table_name();
|
||||
$adapter = $this->get_adapter();
|
||||
|
||||
if ( ! $adapter->has_index( $table_name, $this->columns_with_index, [ 'name' => 'prominent_words' ] ) ) {
|
||||
$this->add_index(
|
||||
$table_name,
|
||||
$this->columns_with_index,
|
||||
[
|
||||
'name' => 'prominent_words',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
}
|
||||
Executable
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* AddEstimatedReadingTime class.
|
||||
*/
|
||||
class AddEstimatedReadingTime extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$table_name = $this->get_table_name();
|
||||
|
||||
$this->add_column(
|
||||
$table_name,
|
||||
'estimated_reading_time_minutes',
|
||||
'integer',
|
||||
[
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$table_name = $this->get_table_name();
|
||||
|
||||
$this->remove_column( $table_name, 'estimated_reading_time_minutes' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* ExpandIndexableIDColumnLengths class.
|
||||
*/
|
||||
class ExpandIndexableIDColumnLengths extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* The columns to change the column type and length of.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $columns_to_change = [
|
||||
'object_id',
|
||||
'author_id',
|
||||
'post_parent',
|
||||
];
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
foreach ( self::$columns_to_change as $column ) {
|
||||
$this->change_column(
|
||||
$this->get_table_name(),
|
||||
$column,
|
||||
'biginteger',
|
||||
[ 'limit' => 20 ]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use for storing indexables.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* ExpandPrimaryTermIDColumnLengths class.
|
||||
*/
|
||||
class ExpandPrimaryTermIDColumnLengths extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* The columns to change the column type and length of.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $columns_to_change = [
|
||||
'post_id',
|
||||
'term_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
foreach ( self::$columns_to_change as $column ) {
|
||||
$this->change_column(
|
||||
$this->get_table_name(),
|
||||
$column,
|
||||
'biginteger',
|
||||
[ 'limit' => 20 ]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use for storing indexables.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Primary_Term' );
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* AddVersionColumnToIndexables class.
|
||||
*/
|
||||
class AddVersionColumnToIndexables extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$this->add_column(
|
||||
$this->get_table_name(),
|
||||
'version',
|
||||
'integer',
|
||||
[
|
||||
'default' => 1,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$this->remove_column(
|
||||
$this->get_table_name(),
|
||||
'version'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use for storing indexables.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
}
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* AddObjectTimestamps class.
|
||||
*/
|
||||
class AddObjectTimestamps extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$this->add_column(
|
||||
$this->get_table_name(),
|
||||
'object_last_modified',
|
||||
'datetime',
|
||||
[
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
]
|
||||
);
|
||||
$this->add_column(
|
||||
$this->get_table_name(),
|
||||
'object_published_at',
|
||||
'datetime',
|
||||
[
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
]
|
||||
);
|
||||
$this->add_index(
|
||||
$this->get_table_name(),
|
||||
[
|
||||
'object_published_at',
|
||||
'is_robots_noindex',
|
||||
'object_type',
|
||||
'object_sub_type',
|
||||
],
|
||||
[
|
||||
'name' => 'published_sitemap_index',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$this->remove_column( $this->get_table_name(), 'object_last_modified' );
|
||||
$this->remove_column( $this->get_table_name(), 'object_published_at' );
|
||||
$this->remove_index(
|
||||
$this->get_table_name(),
|
||||
[
|
||||
'object_published_at',
|
||||
'is_robots_noindex',
|
||||
'object_type',
|
||||
'object_sub_type',
|
||||
],
|
||||
[
|
||||
'name' => 'published_sitemap_index',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use for storing indexables.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
}
|
||||
Executable
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config\Migrations;
|
||||
|
||||
use Yoast\WP\Lib\Migrations\Migration;
|
||||
use Yoast\WP\Lib\Model;
|
||||
|
||||
/**
|
||||
* AddInclusiveLanguageScore class.
|
||||
*/
|
||||
class AddInclusiveLanguageScore extends Migration {
|
||||
|
||||
/**
|
||||
* The plugin this migration belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin = 'free';
|
||||
|
||||
/**
|
||||
* Migration up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up() {
|
||||
$table_name = $this->get_table_name();
|
||||
|
||||
$this->add_column(
|
||||
$table_name,
|
||||
'inclusive_language_score',
|
||||
'integer',
|
||||
[
|
||||
'null' => true,
|
||||
'limit' => 3,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration down.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down() {
|
||||
$table_name = $this->get_table_name();
|
||||
|
||||
$this->remove_column( $table_name, 'inclusive_language_score' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the table name to use.
|
||||
*
|
||||
* @return string The table name to use.
|
||||
*/
|
||||
protected function get_table_name() {
|
||||
return Model::get_table_name( 'Indexable' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config;
|
||||
|
||||
use Exception;
|
||||
use Yoast\WP\SEO\Exceptions\OAuth\Authentication_Failed_Exception;
|
||||
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Property_Exception;
|
||||
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Token_Exception;
|
||||
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Failed_Storage_Exception;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Values\OAuth\OAuth_Token;
|
||||
use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
use YoastSEO_Vendor\League\OAuth2\Client\Provider\GenericProvider;
|
||||
|
||||
/**
|
||||
* Class OAuth_Client
|
||||
*/
|
||||
abstract class OAuth_Client {
|
||||
|
||||
/**
|
||||
* The option's key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $token_option = null;
|
||||
|
||||
/**
|
||||
* The provider.
|
||||
*
|
||||
* @var Wincher_PKCE_Provider|GenericProvider
|
||||
*/
|
||||
protected $provider;
|
||||
|
||||
/**
|
||||
* The options helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
protected $options_helper;
|
||||
|
||||
/**
|
||||
* The token.
|
||||
*
|
||||
* @var OAuth_Token|null
|
||||
*/
|
||||
protected $token = null;
|
||||
|
||||
/**
|
||||
* OAuth_Client constructor.
|
||||
*
|
||||
* @param string $token_option The option's name to save the token as.
|
||||
* @param Wincher_PKCE_Provider|GenericProvider $provider The provider.
|
||||
* @param Options_Helper $options_helper The Options_Helper instance.
|
||||
*
|
||||
* @throws Empty_Property_Exception Exception thrown if a token property is empty.
|
||||
*/
|
||||
public function __construct(
|
||||
$token_option,
|
||||
$provider,
|
||||
Options_Helper $options_helper
|
||||
) {
|
||||
$this->provider = $provider;
|
||||
$this->token_option = $token_option;
|
||||
$this->options_helper = $options_helper;
|
||||
|
||||
$tokens = $this->options_helper->get( $this->token_option );
|
||||
|
||||
if ( ! empty( $tokens ) ) {
|
||||
$this->token = new OAuth_Token(
|
||||
$tokens['access_token'],
|
||||
$tokens['refresh_token'],
|
||||
$tokens['expires'],
|
||||
$tokens['has_expired'],
|
||||
$tokens['created_at'],
|
||||
( $tokens['error_count'] ?? 0 )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the access token and refresh token based on the passed code.
|
||||
*
|
||||
* @param string $code The code to send.
|
||||
*
|
||||
* @return OAuth_Token The requested tokens.
|
||||
*
|
||||
* @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
|
||||
*/
|
||||
public function request_tokens( $code ) {
|
||||
try {
|
||||
$response = $this->provider
|
||||
->getAccessToken(
|
||||
'authorization_code',
|
||||
[
|
||||
'code' => $code,
|
||||
]
|
||||
);
|
||||
|
||||
$token = OAuth_Token::from_response( $response );
|
||||
|
||||
return $this->store_token( $token );
|
||||
} catch ( Exception $exception ) {
|
||||
throw new Authentication_Failed_Exception( $exception );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an authenticated GET request to the desired URL.
|
||||
*
|
||||
* @param string $url The URL to send the request to.
|
||||
* @param array $options The options to pass along to the request.
|
||||
*
|
||||
* @return mixed The parsed API response.
|
||||
*
|
||||
* @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
|
||||
* @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
|
||||
* @throws Empty_Token_Exception Exception thrown if the token is empty.
|
||||
*/
|
||||
public function get( $url, $options = [] ) {
|
||||
return $this->do_request( 'GET', $url, $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an authenticated POST request to the desired URL.
|
||||
*
|
||||
* @param string $url The URL to send the request to.
|
||||
* @param mixed $body The data to send along in the request's body.
|
||||
* @param array $options The options to pass along to the request.
|
||||
*
|
||||
* @return mixed The parsed API response.
|
||||
*
|
||||
* @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
|
||||
* @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
|
||||
* @throws Empty_Token_Exception Exception thrown if the token is empty.
|
||||
*/
|
||||
public function post( $url, $body, $options = [] ) {
|
||||
$options['body'] = $body;
|
||||
|
||||
return $this->do_request( 'POST', $url, $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an authenticated DELETE request to the desired URL.
|
||||
*
|
||||
* @param string $url The URL to send the request to.
|
||||
* @param array $options The options to pass along to the request.
|
||||
*
|
||||
* @return mixed The parsed API response.
|
||||
*
|
||||
* @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
|
||||
* @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
|
||||
* @throws Empty_Token_Exception Exception thrown if the token is empty.
|
||||
*/
|
||||
public function delete( $url, $options = [] ) {
|
||||
return $this->do_request( 'DELETE', $url, $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether there are valid tokens available.
|
||||
*
|
||||
* @return bool Whether there are valid tokens.
|
||||
*/
|
||||
public function has_valid_tokens() {
|
||||
return ! empty( $this->token ) && $this->token->has_expired() === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stored tokens and refreshes them if they've expired.
|
||||
*
|
||||
* @return OAuth_Token The stored tokens.
|
||||
*
|
||||
* @throws Empty_Token_Exception Exception thrown if the token is empty.
|
||||
*/
|
||||
public function get_tokens() {
|
||||
if ( empty( $this->token ) ) {
|
||||
throw new Empty_Token_Exception();
|
||||
}
|
||||
|
||||
if ( $this->token->has_expired() ) {
|
||||
$this->token = $this->refresh_tokens( $this->token );
|
||||
}
|
||||
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the passed token.
|
||||
*
|
||||
* @param OAuth_Token $token The token to store.
|
||||
*
|
||||
* @return OAuth_Token The stored token.
|
||||
*
|
||||
* @throws Failed_Storage_Exception Exception thrown if storing of the token fails.
|
||||
*/
|
||||
public function store_token( OAuth_Token $token ) {
|
||||
$saved = $this->options_helper->set( $this->token_option, $token->to_array() );
|
||||
|
||||
if ( $saved === false ) {
|
||||
throw new Failed_Storage_Exception();
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the stored token from storage.
|
||||
*
|
||||
* @return bool The stored token.
|
||||
*
|
||||
* @throws Failed_Storage_Exception Exception thrown if clearing of the token fails.
|
||||
*/
|
||||
public function clear_token() {
|
||||
$saved = $this->options_helper->set( $this->token_option, [] );
|
||||
|
||||
if ( $saved === false ) {
|
||||
throw new Failed_Storage_Exception();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the specified request.
|
||||
*
|
||||
* @param string $method The HTTP method to use.
|
||||
* @param string $url The URL to send the request to.
|
||||
* @param array $options The options to pass along to the request.
|
||||
*
|
||||
* @return mixed The parsed API response.
|
||||
*
|
||||
* @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
|
||||
* @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
|
||||
* @throws Empty_Token_Exception Exception thrown if the token is empty.
|
||||
*/
|
||||
protected function do_request( $method, $url, array $options ) {
|
||||
$defaults = [
|
||||
'headers' => $this->provider->getHeaders( $this->get_tokens()->access_token ),
|
||||
];
|
||||
|
||||
$options = \array_merge_recursive( $defaults, $options );
|
||||
|
||||
if ( \array_key_exists( 'params', $options ) ) {
|
||||
$url .= '?' . \http_build_query( $options['params'] );
|
||||
unset( $options['params'] );
|
||||
}
|
||||
|
||||
$request = $this->provider
|
||||
->getAuthenticatedRequest( $method, $url, null, $options );
|
||||
|
||||
return $this->provider->getParsedResponse( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the outdated tokens.
|
||||
*
|
||||
* @param OAuth_Token $tokens The outdated tokens.
|
||||
*
|
||||
* @return OAuth_Token The refreshed tokens.
|
||||
*
|
||||
* @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
|
||||
*/
|
||||
protected function refresh_tokens( OAuth_Token $tokens ) {
|
||||
// We do this dance with transients since we need to make sure we don't
|
||||
// delete valid tokens because of a race condition when two calls are
|
||||
// made simultaneously to this function and refresh token rotation is
|
||||
// turned on in the OAuth server. This is not 100% safe, but should at
|
||||
// least be much better than not having any lock at all.
|
||||
$lock_name = \sprintf( 'lock:%s', $this->token_option );
|
||||
$can_lock = \get_transient( $lock_name ) === false;
|
||||
$has_lock = $can_lock && \set_transient( $lock_name, true, 30 );
|
||||
|
||||
try {
|
||||
$new_tokens = $this->provider->getAccessToken(
|
||||
'refresh_token',
|
||||
[
|
||||
'refresh_token' => $tokens->refresh_token,
|
||||
]
|
||||
);
|
||||
|
||||
$token_obj = OAuth_Token::from_response( $new_tokens );
|
||||
|
||||
return $this->store_token( $token_obj );
|
||||
} catch ( Exception $exception ) {
|
||||
// If we tried to refresh but the refresh token is invalid, delete
|
||||
// the tokens so that we don't try again. Only do this if we got the
|
||||
// lock at the beginning of the call.
|
||||
if ( $has_lock && $exception->getMessage() === 'invalid_grant' ) {
|
||||
try {
|
||||
// To protect from race conditions, only do this if we've
|
||||
// seen an error before with the same token.
|
||||
if ( $tokens->error_count >= 1 ) {
|
||||
$this->clear_token();
|
||||
}
|
||||
else {
|
||||
$tokens->error_count += 1;
|
||||
$this->store_token( $tokens );
|
||||
}
|
||||
} catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
|
||||
// Pass through.
|
||||
}
|
||||
}
|
||||
|
||||
throw new Authentication_Failed_Exception( $exception );
|
||||
} finally {
|
||||
\delete_transient( $lock_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config;
|
||||
|
||||
/**
|
||||
* Holds all languages supported with specific researches for our readability analysis.
|
||||
*/
|
||||
class Researcher_Languages {
|
||||
|
||||
public const SUPPORTED_LANGUAGES = [ 'ar', 'ca', 'de', 'en', 'es', 'fa', 'fr', 'he', 'hu', 'id', 'it', 'nb', 'nl', 'pl', 'pt', 'ru', 'sv', 'tr', 'cs', 'sk', 'el', 'ja' ];
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config;
|
||||
|
||||
/**
|
||||
* Class Schema_IDs.
|
||||
*/
|
||||
class Schema_IDs {
|
||||
|
||||
/**
|
||||
* Hash used for the Author `@id`.
|
||||
*/
|
||||
public const AUTHOR_HASH = '#author';
|
||||
|
||||
/**
|
||||
* Hash used for the Author Logo's `@id`.
|
||||
*/
|
||||
public const AUTHOR_LOGO_HASH = '#authorlogo';
|
||||
|
||||
/**
|
||||
* Hash used for the Breadcrumb's `@id`.
|
||||
*/
|
||||
public const BREADCRUMB_HASH = '#breadcrumb';
|
||||
|
||||
/**
|
||||
* Hash used for the Person `@id`.
|
||||
*/
|
||||
public const PERSON_HASH = '#/schema/person/';
|
||||
|
||||
/**
|
||||
* Hash used for the Article `@id`.
|
||||
*/
|
||||
public const ARTICLE_HASH = '#article';
|
||||
|
||||
/**
|
||||
* Hash used for the Organization `@id`.
|
||||
*/
|
||||
public const ORGANIZATION_HASH = '#organization';
|
||||
|
||||
/**
|
||||
* Hash used for the Organization `@id`.
|
||||
*/
|
||||
public const ORGANIZATION_LOGO_HASH = '#/schema/logo/image/';
|
||||
|
||||
/**
|
||||
* Hash used for the logo `@id`.
|
||||
*/
|
||||
public const PERSON_LOGO_HASH = '#/schema/person/image/';
|
||||
|
||||
/**
|
||||
* Hash used for an Article's primary image `@id`.
|
||||
*/
|
||||
public const PRIMARY_IMAGE_HASH = '#primaryimage';
|
||||
|
||||
/**
|
||||
* Hash used for the Website's `@id`.
|
||||
*/
|
||||
public const WEBSITE_HASH = '#website';
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config;
|
||||
|
||||
/**
|
||||
* Class Schema_Types.
|
||||
*/
|
||||
class Schema_Types {
|
||||
|
||||
/**
|
||||
* Holds the possible schema page types.
|
||||
*
|
||||
* Capitalized in this way so the value can be directly used in the schema output.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public const PAGE_TYPES = [
|
||||
'WebPage' => '',
|
||||
'ItemPage' => '',
|
||||
'AboutPage' => '',
|
||||
'FAQPage' => '',
|
||||
'QAPage' => '',
|
||||
'ProfilePage' => '',
|
||||
'ContactPage' => '',
|
||||
'MedicalWebPage' => '',
|
||||
'CollectionPage' => '',
|
||||
'CheckoutPage' => '',
|
||||
'RealEstateListing' => '',
|
||||
'SearchResultsPage' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* Holds the possible schema article types.
|
||||
*
|
||||
* Capitalized in this way so the value can be directly used in the schema output.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public const ARTICLE_TYPES = [
|
||||
'Article' => '',
|
||||
'BlogPosting' => '',
|
||||
'SocialMediaPosting' => '',
|
||||
'NewsArticle' => '',
|
||||
'AdvertiserContentArticle' => '',
|
||||
'SatiricalArticle' => '',
|
||||
'ScholarlyArticle' => '',
|
||||
'TechArticle' => '',
|
||||
'Report' => '',
|
||||
'None' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* Gets the page type options.
|
||||
*
|
||||
* @return array[] The schema page type options.
|
||||
*/
|
||||
public function get_page_type_options() {
|
||||
return [
|
||||
[
|
||||
'name' => \__( 'Web Page', 'wordpress-seo' ),
|
||||
'value' => 'WebPage',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Item Page', 'wordpress-seo' ),
|
||||
'value' => 'ItemPage',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'About Page', 'wordpress-seo' ),
|
||||
'value' => 'AboutPage',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'FAQ Page', 'wordpress-seo' ),
|
||||
'value' => 'FAQPage',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'QA Page', 'wordpress-seo' ),
|
||||
'value' => 'QAPage',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Profile Page', 'wordpress-seo' ),
|
||||
'value' => 'ProfilePage',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Contact Page', 'wordpress-seo' ),
|
||||
'value' => 'ContactPage',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Medical Web Page', 'wordpress-seo' ),
|
||||
'value' => 'MedicalWebPage',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Collection Page', 'wordpress-seo' ),
|
||||
'value' => 'CollectionPage',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Checkout Page', 'wordpress-seo' ),
|
||||
'value' => 'CheckoutPage',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Real Estate Listing', 'wordpress-seo' ),
|
||||
'value' => 'RealEstateListing',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Search Results Page', 'wordpress-seo' ),
|
||||
'value' => 'SearchResultsPage',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the article type options.
|
||||
*
|
||||
* @return array[] The schema article type options.
|
||||
*/
|
||||
public function get_article_type_options() {
|
||||
/**
|
||||
* Filter: 'wpseo_schema_article_types_labels' - Allow developers to filter the available article types and their labels.
|
||||
*
|
||||
* Make sure when you filter this to also filter `wpseo_schema_article_types`.
|
||||
*
|
||||
* @param array $schema_article_types_labels The available schema article types and their labels.
|
||||
*/
|
||||
return \apply_filters(
|
||||
'wpseo_schema_article_types_labels',
|
||||
[
|
||||
[
|
||||
'name' => \__( 'Article', 'wordpress-seo' ),
|
||||
'value' => 'Article',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Blog Post', 'wordpress-seo' ),
|
||||
'value' => 'BlogPosting',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Social Media Posting', 'wordpress-seo' ),
|
||||
'value' => 'SocialMediaPosting',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'News Article', 'wordpress-seo' ),
|
||||
'value' => 'NewsArticle',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Advertiser Content Article', 'wordpress-seo' ),
|
||||
'value' => 'AdvertiserContentArticle',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Satirical Article', 'wordpress-seo' ),
|
||||
'value' => 'SatiricalArticle',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Scholarly Article', 'wordpress-seo' ),
|
||||
'value' => 'ScholarlyArticle',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Tech Article', 'wordpress-seo' ),
|
||||
'value' => 'TechArticle',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'Report', 'wordpress-seo' ),
|
||||
'value' => 'Report',
|
||||
],
|
||||
[
|
||||
'name' => \__( 'None', 'wordpress-seo' ),
|
||||
'value' => 'None',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config;
|
||||
|
||||
use Yoast\WP\SEO\Exceptions\OAuth\Authentication_Failed_Exception;
|
||||
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Property_Exception;
|
||||
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Token_Exception;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Wrappers\WP_Remote_Handler;
|
||||
use YoastSEO_Vendor\GuzzleHttp\Client;
|
||||
use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
use YoastSEO_Vendor\League\OAuth2\Client\Provider\GenericProvider;
|
||||
|
||||
/**
|
||||
* Class SEMrush_Client
|
||||
*/
|
||||
class SEMrush_Client extends OAuth_Client {
|
||||
|
||||
/**
|
||||
* The option's key.
|
||||
*/
|
||||
public const TOKEN_OPTION = 'semrush_tokens';
|
||||
|
||||
/**
|
||||
* SEMrush_Client constructor.
|
||||
*
|
||||
* @param Options_Helper $options_helper The Options_Helper instance.
|
||||
* @param WP_Remote_Handler $wp_remote_handler The request handler.
|
||||
*
|
||||
* @throws Empty_Property_Exception Throws when one of the required properties is empty.
|
||||
*/
|
||||
public function __construct( Options_Helper $options_helper, WP_Remote_Handler $wp_remote_handler ) {
|
||||
$provider = new GenericProvider(
|
||||
[
|
||||
'clientId' => 'yoast',
|
||||
'clientSecret' => 'YdqNsWwnP4vE54WO1ugThKEjGMxMAHJt',
|
||||
'redirectUri' => 'https://oauth.semrush.com/oauth2/yoast/success',
|
||||
'urlAuthorize' => 'https://oauth.semrush.com/oauth2/authorize',
|
||||
'urlAccessToken' => 'https://oauth.semrush.com/oauth2/access_token',
|
||||
'urlResourceOwnerDetails' => 'https://oauth.semrush.com/oauth2/resource',
|
||||
],
|
||||
[
|
||||
'httpClient' => new Client( [ 'handler' => $wp_remote_handler ] ),
|
||||
]
|
||||
);
|
||||
|
||||
parent::__construct(
|
||||
self::TOKEN_OPTION,
|
||||
$provider,
|
||||
$options_helper
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the specified request.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param string $method The HTTP method to use.
|
||||
* @param string $url The URL to send the request to.
|
||||
* @param array $options The options to pass along to the request.
|
||||
*
|
||||
* @return mixed The parsed API response.
|
||||
*
|
||||
* @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
|
||||
* @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
|
||||
* @throws Empty_Token_Exception Exception thrown if the token is empty.
|
||||
*/
|
||||
public function do_request( $method, $url, array $options ) {
|
||||
// Add the access token to the GET parameters as well since this is what
|
||||
// the SEMRush API expects.
|
||||
$options = \array_merge_recursive(
|
||||
$options,
|
||||
[
|
||||
'params' => [
|
||||
'access_token' => $this->get_tokens()->access_token,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
return parent::do_request( $method, $url, $options );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config;
|
||||
|
||||
use WPSEO_Utils;
|
||||
use Yoast\WP\SEO\Exceptions\OAuth\Authentication_Failed_Exception;
|
||||
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Property_Exception;
|
||||
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Token_Exception;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Values\OAuth\OAuth_Token;
|
||||
use Yoast\WP\SEO\Wrappers\WP_Remote_Handler;
|
||||
use YoastSEO_Vendor\GuzzleHttp\Client;
|
||||
use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
|
||||
/**
|
||||
* Class Wincher_Client
|
||||
*/
|
||||
class Wincher_Client extends OAuth_Client {
|
||||
|
||||
/**
|
||||
* The option's key.
|
||||
*/
|
||||
public const TOKEN_OPTION = 'wincher_tokens';
|
||||
|
||||
/**
|
||||
* Name of the temporary PKCE cookie.
|
||||
*/
|
||||
public const PKCE_TRANSIENT_NAME = 'yoast_wincher_pkce';
|
||||
|
||||
/**
|
||||
* The WP_Remote_Handler instance.
|
||||
*
|
||||
* @var WP_Remote_Handler
|
||||
*/
|
||||
protected $wp_remote_handler;
|
||||
|
||||
/**
|
||||
* Wincher_Client constructor.
|
||||
*
|
||||
* @param Options_Helper $options_helper The Options_Helper instance.
|
||||
* @param WP_Remote_Handler $wp_remote_handler The request handler.
|
||||
*
|
||||
* @throws Empty_Property_Exception Exception thrown if a token property is empty.
|
||||
*/
|
||||
public function __construct( Options_Helper $options_helper, WP_Remote_Handler $wp_remote_handler ) {
|
||||
$provider = new Wincher_PKCE_Provider(
|
||||
[
|
||||
'clientId' => 'yoast',
|
||||
'redirectUri' => 'https://auth.wincher.com/yoast/setup',
|
||||
'urlAuthorize' => 'https://auth.wincher.com/connect/authorize',
|
||||
'urlAccessToken' => 'https://auth.wincher.com/connect/token',
|
||||
'urlResourceOwnerDetails' => 'https://api.wincher.com/beta/user',
|
||||
'scopes' => [ 'profile', 'account', 'websites:read', 'websites:write', 'offline_access' ],
|
||||
'scopeSeparator' => ' ',
|
||||
'pkceMethod' => 'S256',
|
||||
],
|
||||
[
|
||||
'httpClient' => new Client( [ 'handler' => $wp_remote_handler ] ),
|
||||
]
|
||||
);
|
||||
|
||||
parent::__construct(
|
||||
self::TOKEN_OPTION,
|
||||
$provider,
|
||||
$options_helper
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the authorization URL.
|
||||
*
|
||||
* @return string The authentication URL.
|
||||
*/
|
||||
public function get_authorization_url() {
|
||||
$parsed_site_url = \wp_parse_url( \get_site_url() );
|
||||
|
||||
$url = $this->provider->getAuthorizationUrl(
|
||||
[
|
||||
'state' => WPSEO_Utils::format_json_encode( [ 'domain' => $parsed_site_url['host'] ] ),
|
||||
]
|
||||
);
|
||||
|
||||
$pkce_code = $this->provider->getPkceCode();
|
||||
|
||||
// Store a transient value with the PKCE code that we need in order to
|
||||
// exchange the returned code for a token after authorization.
|
||||
\set_transient( self::PKCE_TRANSIENT_NAME, $pkce_code, \DAY_IN_SECONDS );
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the access token and refresh token based on the passed code.
|
||||
*
|
||||
* @param string $code The code to send.
|
||||
*
|
||||
* @return OAuth_Token The requested tokens.
|
||||
*
|
||||
* @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
|
||||
*/
|
||||
public function request_tokens( $code ) {
|
||||
$pkce_code = \get_transient( self::PKCE_TRANSIENT_NAME );
|
||||
if ( $pkce_code ) {
|
||||
$this->provider->setPkceCode( $pkce_code );
|
||||
}
|
||||
return parent::request_tokens( $code );
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the specified request.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param string $method The HTTP method to use.
|
||||
* @param string $url The URL to send the request to.
|
||||
* @param array $options The options to pass along to the request.
|
||||
*
|
||||
* @return mixed The parsed API response.
|
||||
*
|
||||
* @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
|
||||
* @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
|
||||
* @throws Empty_Token_Exception Exception thrown if the token is empty.
|
||||
*/
|
||||
protected function do_request( $method, $url, array $options ) {
|
||||
$options['headers'] = [ 'Content-Type' => 'application/json' ];
|
||||
return parent::do_request( $method, $url, $options );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Config;
|
||||
|
||||
use Exception;
|
||||
use UnexpectedValueException;
|
||||
use YoastSEO_Vendor\GuzzleHttp\Exception\BadResponseException;
|
||||
use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
use YoastSEO_Vendor\League\OAuth2\Client\Provider\GenericProvider;
|
||||
use YoastSEO_Vendor\League\OAuth2\Client\Token\AccessToken;
|
||||
use YoastSEO_Vendor\League\OAuth2\Client\Token\AccessTokenInterface;
|
||||
use YoastSEO_Vendor\League\OAuth2\Client\Tool\BearerAuthorizationTrait;
|
||||
use YoastSEO_Vendor\Psr\Http\Message\RequestInterface;
|
||||
use YoastSEO_Vendor\Psr\Log\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class Wincher_PKCE_Provider
|
||||
*
|
||||
* @codeCoverageIgnore Ignoring as this class is purely a temporary wrapper until https://github.com/thephpleague/oauth2-client/pull/901 is merged.
|
||||
*
|
||||
* @phpcs:disable WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase -- This class extends an external class.
|
||||
* @phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- This class extends an external class.
|
||||
*/
|
||||
class Wincher_PKCE_Provider extends GenericProvider {
|
||||
|
||||
use BearerAuthorizationTrait;
|
||||
|
||||
/**
|
||||
* The method to use.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $pkceMethod = null;
|
||||
|
||||
/**
|
||||
* The PKCE code.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pkceCode;
|
||||
|
||||
/**
|
||||
* Set the value of the pkceCode parameter.
|
||||
*
|
||||
* When using PKCE this should be set before requesting an access token.
|
||||
*
|
||||
* @param string $pkce_code The value for the pkceCode.
|
||||
* @return self
|
||||
*/
|
||||
public function setPkceCode( $pkce_code ) {
|
||||
$this->pkceCode = $pkce_code;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the pkceCode parameter.
|
||||
*
|
||||
* This can be accessed by the redirect handler during authorization.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPkceCode() {
|
||||
return $this->pkceCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new random string to use as PKCE code_verifier and
|
||||
* hashed as code_challenge parameters in an authorization flow.
|
||||
* Must be between 43 and 128 characters long.
|
||||
*
|
||||
* @param int $length Length of the random string to be generated.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws Exception Throws exception if an invalid value is passed to random_bytes.
|
||||
*/
|
||||
protected function getRandomPkceCode( $length = 64 ) {
|
||||
return \substr(
|
||||
\strtr(
|
||||
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
|
||||
\base64_encode( \random_bytes( $length ) ),
|
||||
'+/',
|
||||
'-_'
|
||||
),
|
||||
0,
|
||||
$length
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the pkceMethod parameter.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getPkceMethod() {
|
||||
return $this->pkceMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns authorization parameters based on provided options.
|
||||
*
|
||||
* @param array $options The options to use in the authorization parameters.
|
||||
*
|
||||
* @return array The authorization parameters
|
||||
*
|
||||
* @throws InvalidArgumentException Throws exception if an invalid PCKE method is passed in the options.
|
||||
* @throws Exception When something goes wrong with generating the PKCE code.
|
||||
*/
|
||||
protected function getAuthorizationParameters( array $options ) {
|
||||
if ( empty( $options['state'] ) ) {
|
||||
$options['state'] = $this->getRandomState();
|
||||
}
|
||||
|
||||
if ( empty( $options['scope'] ) ) {
|
||||
$options['scope'] = $this->getDefaultScopes();
|
||||
}
|
||||
|
||||
$options += [
|
||||
'response_type' => 'code',
|
||||
];
|
||||
|
||||
if ( \is_array( $options['scope'] ) ) {
|
||||
$separator = $this->getScopeSeparator();
|
||||
$options['scope'] = \implode( $separator, $options['scope'] );
|
||||
}
|
||||
|
||||
// Store the state as it may need to be accessed later on.
|
||||
$this->state = $options['state'];
|
||||
|
||||
$pkce_method = $this->getPkceMethod();
|
||||
if ( ! empty( $pkce_method ) ) {
|
||||
$this->pkceCode = $this->getRandomPkceCode();
|
||||
if ( $pkce_method === 'S256' ) {
|
||||
$options['code_challenge'] = \trim(
|
||||
\strtr(
|
||||
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
|
||||
\base64_encode( \hash( 'sha256', $this->pkceCode, true ) ),
|
||||
'+/',
|
||||
'-_'
|
||||
),
|
||||
'='
|
||||
);
|
||||
}
|
||||
elseif ( $pkce_method === 'plain' ) {
|
||||
$options['code_challenge'] = $this->pkceCode;
|
||||
}
|
||||
else {
|
||||
throw new InvalidArgumentException( 'Unknown PKCE method "' . $pkce_method . '".' );
|
||||
}
|
||||
$options['code_challenge_method'] = $pkce_method;
|
||||
}
|
||||
|
||||
// Business code layer might set a different redirect_uri parameter.
|
||||
// Depending on the context, leave it as-is.
|
||||
if ( ! isset( $options['redirect_uri'] ) ) {
|
||||
$options['redirect_uri'] = $this->redirectUri;
|
||||
}
|
||||
|
||||
$options['client_id'] = $this->clientId;
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests an access token using a specified grant and option set.
|
||||
*
|
||||
* @param mixed $grant The grant to request access for.
|
||||
* @param array $options The options to use with the current request.
|
||||
*
|
||||
* @return AccessToken|AccessTokenInterface The access token.
|
||||
*
|
||||
* @throws UnexpectedValueException Exception thrown if the provider response contains errors.
|
||||
*/
|
||||
public function getAccessToken( $grant, array $options = [] ) {
|
||||
$grant = $this->verifyGrant( $grant );
|
||||
|
||||
$params = [
|
||||
'client_id' => $this->clientId,
|
||||
'client_secret' => $this->clientSecret,
|
||||
'redirect_uri' => $this->redirectUri,
|
||||
];
|
||||
|
||||
if ( ! empty( $this->pkceCode ) ) {
|
||||
$params['code_verifier'] = $this->pkceCode;
|
||||
}
|
||||
|
||||
$params = $grant->prepareRequestParameters( $params, $options );
|
||||
$request = $this->getAccessTokenRequest( $params );
|
||||
$response = $this->getParsedResponse( $request );
|
||||
|
||||
if ( \is_array( $response ) === false ) {
|
||||
throw new UnexpectedValueException(
|
||||
'Invalid response received from Authorization Server. Expected JSON.'
|
||||
);
|
||||
}
|
||||
|
||||
$prepared = $this->prepareAccessTokenResponse( $response );
|
||||
$token = $this->createAccessToken( $prepared, $grant );
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all options that can be configured.
|
||||
*
|
||||
* @return array The configurable options.
|
||||
*/
|
||||
protected function getConfigurableOptions() {
|
||||
return \array_merge(
|
||||
$this->getRequiredOptions(),
|
||||
[
|
||||
'accessTokenMethod',
|
||||
'accessTokenResourceOwnerId',
|
||||
'scopeSeparator',
|
||||
'responseError',
|
||||
'responseCode',
|
||||
'responseResourceOwnerId',
|
||||
'scopes',
|
||||
'pkceMethod',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the request response.
|
||||
*
|
||||
* @param RequestInterface $request The request interface.
|
||||
*
|
||||
* @return array The parsed response.
|
||||
*
|
||||
* @throws IdentityProviderException Exception thrown if there is no proper identity provider.
|
||||
*/
|
||||
public function getParsedResponse( RequestInterface $request ) {
|
||||
try {
|
||||
$response = $this->getResponse( $request );
|
||||
} catch ( BadResponseException $e ) {
|
||||
$response = $e->getResponse();
|
||||
}
|
||||
|
||||
$parsed = $this->parseResponse( $response );
|
||||
|
||||
$this->checkResponse( $response, $parsed );
|
||||
|
||||
// We always expect an array from the API except for on DELETE requests.
|
||||
// We convert to an array here to prevent problems with array_key_exists on PHP8.
|
||||
if ( ! \is_array( $parsed ) ) {
|
||||
$parsed = [ 'data' => [] ];
|
||||
}
|
||||
|
||||
// Add the response code as this is omitted from Winchers API.
|
||||
if ( ! \array_key_exists( 'status', $parsed ) ) {
|
||||
$parsed['status'] = $response->getStatusCode();
|
||||
}
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user