Plugins
This commit is contained in:
@@ -1,206 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\Open_Graph_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Class Abstract_OpenGraph_Integration.
|
||||
*/
|
||||
abstract class Abstract_OpenGraph_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* The name or prefix for the social title option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_TITLE = '';
|
||||
|
||||
/**
|
||||
* The name or prefix for the social description option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_DESCRIPTION = '';
|
||||
|
||||
/**
|
||||
* The name or prefix for the social image ID option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_IMAGE_ID = '';
|
||||
|
||||
/**
|
||||
* The name or prefix for the social image URL option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_IMAGE = '';
|
||||
|
||||
/**
|
||||
* The options helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Integration constructor.
|
||||
*
|
||||
* @param Options_Helper $options The options helper.
|
||||
*/
|
||||
public function __construct( Options_Helper $options ) {
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the conditionals based in which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Open_Graph_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the relevant social title from the options.
|
||||
*
|
||||
* @param string $title The default title.
|
||||
*
|
||||
* @return mixed|string The filtered value.
|
||||
*/
|
||||
public function filter_title( $title ) {
|
||||
$social_title = $this->options->get( $this::OPTION_TITLES_KEY_TITLE );
|
||||
|
||||
if ( ! empty( $social_title ) ) {
|
||||
$title = $social_title;
|
||||
}
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the relevant social description from the options.
|
||||
*
|
||||
* @param string $description The default description.
|
||||
*
|
||||
* @return mixed|string The filtered value.
|
||||
*/
|
||||
public function filter_description( $description ) {
|
||||
$social_description = $this->options->get( $this::OPTION_TITLES_KEY_DESCRIPTION );
|
||||
|
||||
if ( ! empty( $social_description ) ) {
|
||||
$description = $social_description;
|
||||
}
|
||||
|
||||
return $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the relevant social image ID from the options.
|
||||
*
|
||||
* @param int $id The default image ID.
|
||||
*
|
||||
* @return mixed|int The filtered value.
|
||||
*/
|
||||
public function filter_image_id( $id ) {
|
||||
$social_id = $this->options->get( $this::OPTION_TITLES_KEY_IMAGE_ID );
|
||||
|
||||
if ( ! empty( $social_id ) ) {
|
||||
$id = $social_id;
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the relevant social image URL from the options.
|
||||
*
|
||||
* @param string $url The default image URL.
|
||||
*
|
||||
* @return mixed|int The filtered value.
|
||||
*/
|
||||
public function filter_image( $url ) {
|
||||
$social_url = $this->options->get( $this::OPTION_TITLES_KEY_IMAGE );
|
||||
|
||||
if ( ! empty( $social_url ) ) {
|
||||
$url = $social_url;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the relevant social title for the subtype from the options.
|
||||
*
|
||||
* @param string $title The default title.
|
||||
* @param string $object_subtype The subtype of the current indexable.
|
||||
*
|
||||
* @return mixed|string The filtered value.
|
||||
*/
|
||||
public function filter_title_for_subtype( $title, $object_subtype ) {
|
||||
$social_title = $this->options->get( $this::OPTION_TITLES_KEY_TITLE . $object_subtype );
|
||||
|
||||
if ( ! empty( $social_title ) ) {
|
||||
$title = $social_title;
|
||||
}
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the relevant social description for the subtype from the options.
|
||||
*
|
||||
* @param string $description The default description.
|
||||
* @param string $object_subtype The subtype of the current indexable.
|
||||
*
|
||||
* @return mixed|string The filtered value.
|
||||
*/
|
||||
public function filter_description_for_subtype( $description, $object_subtype ) {
|
||||
$social_description = $this->options->get( $this::OPTION_TITLES_KEY_DESCRIPTION . $object_subtype );
|
||||
|
||||
if ( ! empty( $social_description ) ) {
|
||||
$description = $social_description;
|
||||
}
|
||||
|
||||
return $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the relevant social image ID for the subtype from the options.
|
||||
*
|
||||
* @param int $id The default image ID.
|
||||
* @param string $object_subtype The subtype of the current indexable.
|
||||
*
|
||||
* @return mixed|string The filtered value.
|
||||
*/
|
||||
public function filter_image_id_for_subtype( $id, $object_subtype ) {
|
||||
$social_id = $this->options->get( $this::OPTION_TITLES_KEY_IMAGE_ID . $object_subtype );
|
||||
|
||||
if ( ! empty( $social_id ) ) {
|
||||
$id = $social_id;
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the relevant social image URL for the subtype from the options.
|
||||
*
|
||||
* @param string $url The default image URL.
|
||||
* @param string $object_subtype The subtype of the current indexable.
|
||||
*
|
||||
* @return mixed|string The filtered value.
|
||||
*/
|
||||
public function filter_image_for_subtype( $url, $object_subtype ) {
|
||||
$social_url = $this->options->get( $this::OPTION_TITLES_KEY_IMAGE . $object_subtype );
|
||||
|
||||
if ( ! empty( $social_url ) ) {
|
||||
$url = $social_url;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WPSEO_Addon_Manager;
|
||||
use WPSEO_Admin_Asset_Manager;
|
||||
use Yoast\WP\SEO\Conditionals\User_Profile_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Helpers\User_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Introductions\Infrastructure\Wistia_Embed_Permission_Repository;
|
||||
|
||||
/**
|
||||
* Ai_Consent_Integration class.
|
||||
*/
|
||||
class Ai_Consent_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Represents the admin asset manager.
|
||||
*
|
||||
* @var WPSEO_Admin_Asset_Manager
|
||||
*/
|
||||
private $asset_manager;
|
||||
|
||||
/**
|
||||
* Represents the add-on manager.
|
||||
*
|
||||
* @var WPSEO_Addon_Manager
|
||||
*/
|
||||
private $addon_manager;
|
||||
|
||||
/**
|
||||
* Represents the options manager.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
private $options_helper;
|
||||
|
||||
/**
|
||||
* Represents the user helper.
|
||||
*
|
||||
* @var User_Helper
|
||||
*/
|
||||
private $user_helper;
|
||||
|
||||
/**
|
||||
* Represents the wistia embed permission repository.
|
||||
*
|
||||
* @var Wistia_Embed_Permission_Repository
|
||||
*/
|
||||
private $wistia_embed_permission_repository;
|
||||
|
||||
/**
|
||||
* Returns the conditionals based in which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ User_Profile_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the class.
|
||||
*
|
||||
* @param WPSEO_Admin_Asset_Manager $asset_manager The admin asset manager.
|
||||
* @param WPSEO_Addon_Manager $addon_manager The addon manager.
|
||||
* @param Options_Helper $options_helper The options helper.
|
||||
* @param User_Helper $user_helper The user helper.
|
||||
* @param Wistia_Embed_Permission_Repository $wistia_embed_permission_repository The wistia embed permission
|
||||
* repository.
|
||||
*/
|
||||
public function __construct(
|
||||
WPSEO_Admin_Asset_Manager $asset_manager,
|
||||
WPSEO_Addon_Manager $addon_manager,
|
||||
Options_Helper $options_helper,
|
||||
User_Helper $user_helper,
|
||||
Wistia_Embed_Permission_Repository $wistia_embed_permission_repository
|
||||
) {
|
||||
$this->asset_manager = $asset_manager;
|
||||
$this->addon_manager = $addon_manager;
|
||||
$this->options_helper = $options_helper;
|
||||
$this->user_helper = $user_helper;
|
||||
$this->wistia_embed_permission_repository = $wistia_embed_permission_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
// Hide AI feature option in user profile if the user is not allowed to use it.
|
||||
if ( current_user_can( 'edit_posts' ) ) {
|
||||
\add_action( 'wpseo_user_profile_additions', [ $this, 'render_user_profile' ], 12 );
|
||||
}
|
||||
\add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ], 11 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the required assets.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
$this->asset_manager->enqueue_style( 'premium-ai-generator' );
|
||||
|
||||
\wp_enqueue_script( 'wp-seo-premium-manage-ai-consent-button' );
|
||||
$user_id = $this->user_helper->get_current_user_id();
|
||||
\wp_localize_script(
|
||||
'wp-seo-premium-manage-ai-consent-button',
|
||||
'wpseoPremiumManageAiConsentButton',
|
||||
[
|
||||
'hasConsent' => $this->user_helper->get_meta( $user_id, '_yoast_wpseo_ai_consent', true ),
|
||||
// Note: this is passing the Free plugin URL! As the image is located in there.
|
||||
'pluginUrl' => \plugins_url( '', \WPSEO_FILE ),
|
||||
'wistiaEmbedPermission' => $this->wistia_embed_permission_repository->get_value_for_user( $user_id ),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the AI consent button for the user profile.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function render_user_profile() {
|
||||
echo '<label for="ai-generator-consent-button">',
|
||||
esc_html__( 'AI features', 'wordpress-seo-premium' ),
|
||||
'</label>',
|
||||
'<div id="ai-generator-consent" style="display:inline-block; margin-top: 28px; padding-left:5px;"></div>';
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WPSEO_Addon_Manager;
|
||||
use WPSEO_Admin_Asset_Manager;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Helpers\User_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Conditionals\Ai_Editor_Conditional;
|
||||
|
||||
/**
|
||||
* Ai_Generator_Integration class.
|
||||
*/
|
||||
class Ai_Generator_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Represents the admin asset manager.
|
||||
*
|
||||
* @var WPSEO_Admin_Asset_Manager
|
||||
*/
|
||||
private $asset_manager;
|
||||
|
||||
/**
|
||||
* Represents the add-on manager.
|
||||
*
|
||||
* @var WPSEO_Addon_Manager
|
||||
*/
|
||||
private $addon_manager;
|
||||
|
||||
/**
|
||||
* Represents the options manager.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
private $options_helper;
|
||||
|
||||
/**
|
||||
* Represents the user helper.
|
||||
*
|
||||
* @var User_Helper
|
||||
*/
|
||||
private $user_helper;
|
||||
|
||||
/**
|
||||
* Returns the conditionals based in which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Ai_Editor_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the class.
|
||||
*
|
||||
* @param WPSEO_Admin_Asset_Manager $asset_manager The admin asset manager.
|
||||
* @param WPSEO_Addon_Manager $addon_manager The addon manager.
|
||||
* @param Options_Helper $options_helper The options helper.
|
||||
* @param User_Helper $user_helper The user helper.
|
||||
*/
|
||||
public function __construct(
|
||||
WPSEO_Admin_Asset_Manager $asset_manager,
|
||||
WPSEO_Addon_Manager $addon_manager,
|
||||
Options_Helper $options_helper,
|
||||
User_Helper $user_helper
|
||||
) {
|
||||
$this->asset_manager = $asset_manager;
|
||||
$this->addon_manager = $addon_manager;
|
||||
$this->options_helper = $options_helper;
|
||||
$this->user_helper = $user_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
if ( ! $this->options_helper->get( 'enable_ai_generator', false ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
\add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
// Enqueue after Elementor_Premium integration, which re-registers the assets.
|
||||
\add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'enqueue_assets' ], 11 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the required assets.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
\wp_enqueue_script( 'wp-seo-premium-ai-generator' );
|
||||
\wp_localize_script(
|
||||
'wp-seo-premium-ai-generator',
|
||||
'wpseoPremiumAiGenerator',
|
||||
[
|
||||
'adminUrl' => \admin_url( 'admin.php' ),
|
||||
'hasConsent' => $this->user_helper->get_meta( $this->user_helper->get_current_user_id(), '_yoast_wpseo_ai_consent', true ),
|
||||
'hasValidSubscription' => $this->addon_manager->has_valid_subscription( WPSEO_Addon_Manager::PREMIUM_SLUG ),
|
||||
'pluginUrl' => \plugins_url( '', \WPSEO_PREMIUM_FILE ),
|
||||
'postType' => \get_post_type(),
|
||||
]
|
||||
);
|
||||
$this->asset_manager->enqueue_style( 'premium-ai-generator' );
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WP_Query;
|
||||
use wpdb;
|
||||
use WPSEO_Admin_Asset_Manager;
|
||||
use Yoast\WP\Lib\Model;
|
||||
use Yoast\WP\SEO\Conditionals\Admin\Posts_Overview_Or_Ajax_Conditional;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Admin\Admin_Columns_Cache_Integration;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Conditionals\Cornerstone_Enabled_Conditional;
|
||||
use Yoast\WP\SEO\Premium\Presenters\Icons\Checkmark_Icon_Presenter;
|
||||
use Yoast\WP\SEO\Premium\Presenters\Icons\Cross_Icon_Presenter;
|
||||
|
||||
/**
|
||||
* Cornerstone_Column_Integration class.
|
||||
*/
|
||||
class Cornerstone_Column_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Name of the column.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CORNERSTONE_COLUMN_NAME = 'wpseo-cornerstone';
|
||||
|
||||
/**
|
||||
* The post type helper.
|
||||
*
|
||||
* @var Post_Type_Helper
|
||||
*/
|
||||
protected $post_type_helper;
|
||||
|
||||
/**
|
||||
* The database object.
|
||||
*
|
||||
* @var wpdb
|
||||
*/
|
||||
protected $wpdb;
|
||||
|
||||
/**
|
||||
* The admin columns cache.
|
||||
*
|
||||
* @var Admin_Columns_Cache_Integration
|
||||
*/
|
||||
protected $admin_columns_cache;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [
|
||||
Admin_Conditional::class,
|
||||
Posts_Overview_Or_Ajax_Conditional::class,
|
||||
Cornerstone_Enabled_Conditional::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cornerstone_Column_Integration constructor
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Post_Type_Helper $post_type_helper The post type helper.
|
||||
* @param wpdb $wpdb The wpdb object.
|
||||
* @param Admin_Columns_Cache_Integration $admin_columns_cache The admin columns cache.
|
||||
*/
|
||||
public function __construct(
|
||||
Post_Type_Helper $post_type_helper,
|
||||
wpdb $wpdb,
|
||||
Admin_Columns_Cache_Integration $admin_columns_cache
|
||||
) {
|
||||
$this->post_type_helper = $post_type_helper;
|
||||
$this->wpdb = $wpdb;
|
||||
$this->admin_columns_cache = $admin_columns_cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'posts_clauses', [ $this, 'order_by_cornerstone' ], 1, 2 );
|
||||
\add_action( 'admin_init', [ $this, 'register_init_hooks' ] );
|
||||
|
||||
// Adds a filter to exclude the attachments from the cornerstone column.
|
||||
\add_filter( 'wpseo_cornerstone_column_post_types', [ 'WPSEO_Post_Type', 'filter_attachment_post_type' ] );
|
||||
|
||||
\add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register hooks that need to be registered after `init` due to all post types not yet being registered.
|
||||
*/
|
||||
public function register_init_hooks() {
|
||||
$public_post_types = \apply_filters( 'wpseo_cornerstone_column_post_types', $this->post_type_helper->get_accessible_post_types() );
|
||||
|
||||
if ( ! \is_array( $public_post_types ) || empty( $public_post_types ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $public_post_types as $post_type ) {
|
||||
\add_filter( 'manage_' . $post_type . '_posts_columns', [ $this, 'add_cornerstone_column' ] );
|
||||
\add_action( 'manage_' . $post_type . '_posts_custom_column', [ $this, 'column_content' ], 10, 2 );
|
||||
\add_filter( 'manage_edit-' . $post_type . '_sortable_columns', [ $this, 'column_sort' ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the assets needed for the integration to work.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
\wp_enqueue_style( WPSEO_Admin_Asset_Manager::PREFIX . 'premium-post-overview' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the columns for the post overview.
|
||||
*
|
||||
* @param array $columns Array with columns.
|
||||
*
|
||||
* @return array The extended array with columns.
|
||||
*/
|
||||
public function add_cornerstone_column( $columns ) {
|
||||
if ( ! \is_array( $columns ) ) {
|
||||
return $columns;
|
||||
}
|
||||
|
||||
$columns[ self::CORNERSTONE_COLUMN_NAME ] = \sprintf(
|
||||
'<span class="yoast-column-cornerstone yoast-column-header-has-tooltip" data-tooltip-text="%1$s"><span class="screen-reader-text">%2$s</span></span>',
|
||||
\esc_attr__( 'Is this cornerstone content?', 'wordpress-seo-premium' ),
|
||||
/* translators: Hidden accessibility text. */
|
||||
\esc_html__( 'Cornerstone content', 'wordpress-seo-premium' )
|
||||
);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the query pieces to allow ordering column by cornerstone.
|
||||
*
|
||||
* @param array $pieces Array of Query pieces.
|
||||
* @param WP_Query $query The Query on which to apply.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function order_by_cornerstone( $pieces, $query ) {
|
||||
if ( $query->get( 'orderby' ) !== self::CORNERSTONE_COLUMN_NAME ) {
|
||||
return $pieces;
|
||||
}
|
||||
|
||||
return $this->build_sort_query_pieces( $pieces, $query );
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the pieces for a sorting query.
|
||||
*
|
||||
* @param array $pieces Array of Query pieces.
|
||||
* @param WP_Query $query The Query on which to apply.
|
||||
*
|
||||
* @return array Modified Query pieces.
|
||||
*/
|
||||
protected function build_sort_query_pieces( $pieces, $query ) {
|
||||
// We only want our code to run in the main WP query.
|
||||
if ( ! $query->is_main_query() ) {
|
||||
return $pieces;
|
||||
}
|
||||
|
||||
// Get the order query variable - ASC or DESC.
|
||||
$order = \strtoupper( $query->get( 'order' ) );
|
||||
|
||||
// Make sure the order setting qualifies. If not, set default as ASC.
|
||||
if ( ! \in_array( $order, [ 'ASC', 'DESC' ], true ) ) {
|
||||
$order = 'ASC';
|
||||
}
|
||||
|
||||
$table = Model::get_table_name( 'Indexable' );
|
||||
|
||||
$pieces['join'] .= " LEFT JOIN $table AS yoast_indexable ON yoast_indexable.object_id = {$this->wpdb->posts}.ID AND yoast_indexable.object_type = 'post' ";
|
||||
$pieces['orderby'] = "yoast_indexable.is_cornerstone $order, FIELD( {$this->wpdb->posts}.post_status, 'publish' ) $order, {$pieces['orderby']}";
|
||||
|
||||
return $pieces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the column content for the given column.
|
||||
*
|
||||
* @param string $column_name Column to display the content for.
|
||||
* @param int $post_id Post to display the column content for.
|
||||
*/
|
||||
public function column_content( $column_name, $post_id ) {
|
||||
$indexable = $this->admin_columns_cache->get_indexable( $post_id );
|
||||
// Nothing to output if we don't have the value.
|
||||
if ( empty( $indexable ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Security.EscapeOutput -- Reason: The Icons contains safe svg.
|
||||
if ( $column_name === self::CORNERSTONE_COLUMN_NAME ) {
|
||||
if ( $indexable->is_cornerstone === true ) {
|
||||
echo new Checkmark_Icon_Presenter( 20 );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
echo new Cross_Icon_Presenter( 20 );
|
||||
}
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sortable columns.
|
||||
*
|
||||
* @param array $columns Array with sortable columns.
|
||||
*
|
||||
* @return array The extended array with sortable columns.
|
||||
*/
|
||||
public function column_sort( $columns ) {
|
||||
$columns[ self::CORNERSTONE_COLUMN_NAME ] = self::CORNERSTONE_COLUMN_NAME;
|
||||
|
||||
return $columns;
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WPSEO_Admin_Asset_Manager;
|
||||
use WPSEO_Taxonomy_Meta;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Conditionals\Cornerstone_Enabled_Conditional;
|
||||
use Yoast\WP\SEO\Premium\Presenters\Icons\Checkmark_Icon_Presenter;
|
||||
use Yoast\WP\SEO\Premium\Presenters\Icons\Cross_Icon_Presenter;
|
||||
use Yoast\WP\SEO\Premium\Conditionals\Term_Overview_Or_Ajax_Conditional;
|
||||
use Yoast\WP\SEO\Premium\Helpers\Current_Page_Helper;
|
||||
|
||||
/**
|
||||
* Cornerstone_Taxonomy_Column_Integration class.
|
||||
*
|
||||
* phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Cornerstone_Taxonomy_Column_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Name of the column.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CORNERSTONE_COLUMN_NAME = 'wpseo-cornerstone';
|
||||
|
||||
/**
|
||||
* Holds the Current_Page_Helper instance.
|
||||
*
|
||||
* @var Current_Page_Helper
|
||||
*/
|
||||
private $current_page_helper;
|
||||
|
||||
/**
|
||||
* Returns the posted/get taxonomy value if it is set.
|
||||
*
|
||||
* @param Current_Page_Helper $current_page_helper The Current_Page_Helper.
|
||||
*/
|
||||
public function __construct( Current_Page_Helper $current_page_helper ) {
|
||||
$this->current_page_helper = $current_page_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [
|
||||
Admin_Conditional::class,
|
||||
Term_Overview_Or_Ajax_Conditional::class,
|
||||
Cornerstone_Enabled_Conditional::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'admin_init', [ $this, 'register_init_hooks' ] );
|
||||
\add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register hooks that need to be registered after `init` due to all post types not yet being registered.
|
||||
*/
|
||||
public function register_init_hooks() {
|
||||
$taxonomy = $this->current_page_helper->get_current_taxonomy();
|
||||
$is_product = $this->current_page_helper->get_current_post_type() === 'product';
|
||||
$is_product_cat = $taxonomy === 'product_cat';
|
||||
$is_product_tag = $taxonomy === 'product_tag';
|
||||
|
||||
if ( ( $is_product && ( $is_product_cat || $is_product_tag ) ) || ( ! $is_product && $taxonomy ) ) {
|
||||
\add_filter( 'manage_edit-' . $taxonomy . '_columns', [ $this, 'add_cornerstone_column' ] );
|
||||
\add_filter( 'manage_' . $taxonomy . '_custom_column', [ $this, 'column_content' ], 10, 3 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the assets needed for the integration to work.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
\wp_enqueue_style( WPSEO_Admin_Asset_Manager::PREFIX . 'premium-post-overview' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the cornerstone column for the term overview.
|
||||
*
|
||||
* @param array $columns Array with columns.
|
||||
*
|
||||
* @return array The extended array with columns.
|
||||
*/
|
||||
public function add_cornerstone_column( $columns ) {
|
||||
if ( ! \is_array( $columns ) ) {
|
||||
return $columns;
|
||||
}
|
||||
|
||||
$columns[ self::CORNERSTONE_COLUMN_NAME ] = \sprintf(
|
||||
'<span class="yoast-tooltip yoast-tooltip-n yoast-tooltip-alt" data-label="%1$s">
|
||||
<span class="yoast-column-cornerstone yoast-column-header-has-tooltip">
|
||||
<span class="screen-reader-text">%2$s</span>
|
||||
</span>
|
||||
</span>',
|
||||
\esc_attr__( 'Is this cornerstone content?', 'wordpress-seo-premium' ),
|
||||
/* translators: Hidden accessibility text. */
|
||||
\esc_html__( 'Cornerstone content', 'wordpress-seo-premium' )
|
||||
);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the column content for the given column.
|
||||
*
|
||||
* @param string $content The current content of the column.
|
||||
* @param string $column_name The name of the column.
|
||||
* @param int $term_id ID of requested taxonomy.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function column_content( $content, $column_name, $term_id ) {
|
||||
$is_cornerstone = (int) WPSEO_Taxonomy_Meta::get_term_meta( $term_id, $this->current_page_helper->get_current_taxonomy(), 'is_cornerstone' );
|
||||
|
||||
if ( $column_name === self::CORNERSTONE_COLUMN_NAME ) {
|
||||
if ( $is_cornerstone ) {
|
||||
// phpcs:disable WordPress.Security.EscapeOutput -- Reason: The Icons contains safe svg.
|
||||
echo new Checkmark_Icon_Presenter( 20 );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
echo new Cross_Icon_Presenter( 20 );
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WP_Query;
|
||||
use wpdb;
|
||||
use WPSEO_Admin_Asset_Manager;
|
||||
use Yoast\WP\Lib\Model;
|
||||
use Yoast\WP\SEO\Conditionals\Admin\Posts_Overview_Or_Ajax_Conditional;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Score_Icon_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Admin\Admin_Columns_Cache_Integration;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Conditionals\Inclusive_Language_Enabled_Conditional;
|
||||
|
||||
/**
|
||||
* Inclusive_Language_Column_Integration class.
|
||||
*
|
||||
* phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Inclusive_Language_Column_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Name of the column.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const INCLUSIVE_LANGUAGE_COLUMN_NAME = 'wpseo-inclusive-language';
|
||||
|
||||
/**
|
||||
* The post type helper.
|
||||
*
|
||||
* @var Post_Type_Helper
|
||||
*/
|
||||
protected $post_type_helper;
|
||||
|
||||
/**
|
||||
* The score icon helper.
|
||||
*
|
||||
* @var Score_Icon_Helper
|
||||
*/
|
||||
protected $score_icon_helper;
|
||||
|
||||
/**
|
||||
* The database object.
|
||||
*
|
||||
* @var wpdb
|
||||
*/
|
||||
protected $wpdb;
|
||||
|
||||
/**
|
||||
* The admin columns cache.
|
||||
*
|
||||
* @var Admin_Columns_Cache_Integration
|
||||
*/
|
||||
protected $admin_columns_cache;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [
|
||||
Admin_Conditional::class,
|
||||
Posts_Overview_Or_Ajax_Conditional::class,
|
||||
Inclusive_Language_Enabled_Conditional::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Inclusive_Language_Column_Integration constructor
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Post_Type_Helper $post_type_helper The post type helper.
|
||||
* @param Score_Icon_Helper $score_icon_helper The score icon helper.
|
||||
* @param wpdb $wpdb The wpdb object.
|
||||
* @param Admin_Columns_Cache_Integration $admin_columns_cache The admin columns cache.
|
||||
*/
|
||||
public function __construct(
|
||||
Post_Type_Helper $post_type_helper,
|
||||
Score_Icon_Helper $score_icon_helper,
|
||||
wpdb $wpdb,
|
||||
Admin_Columns_Cache_Integration $admin_columns_cache
|
||||
) {
|
||||
$this->post_type_helper = $post_type_helper;
|
||||
$this->score_icon_helper = $score_icon_helper;
|
||||
$this->wpdb = $wpdb;
|
||||
$this->admin_columns_cache = $admin_columns_cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'posts_clauses', [ $this, 'order_by_inclusive_language_score' ], 1, 2 );
|
||||
\add_action( 'admin_init', [ $this, 'register_init_hooks' ] );
|
||||
|
||||
// Adds a filter to exclude the attachments from the inclusive language column.
|
||||
\add_filter( 'wpseo_inclusive_language_column_post_types', [ 'WPSEO_Post_Type', 'filter_attachment_post_type' ] );
|
||||
|
||||
\add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register hooks that need to be registered after `init` due to all post types not yet being registered.
|
||||
*/
|
||||
public function register_init_hooks() {
|
||||
$public_post_types = \apply_filters( 'wpseo_inclusive_language_column_post_types', $this->post_type_helper->get_accessible_post_types() );
|
||||
|
||||
if ( ! \is_array( $public_post_types ) || empty( $public_post_types ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $public_post_types as $post_type ) {
|
||||
\add_filter( 'manage_' . $post_type . '_posts_columns', [ $this, 'add_inclusive_language_column' ] );
|
||||
\add_action( 'manage_' . $post_type . '_posts_custom_column', [ $this, 'column_content' ], 10, 2 );
|
||||
\add_filter( 'manage_edit-' . $post_type . '_sortable_columns', [ $this, 'column_sort' ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the assets needed for the integration to work.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
\wp_enqueue_style( WPSEO_Admin_Asset_Manager::PREFIX . 'premium-post-overview' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the inclusive language column for the post overview.
|
||||
*
|
||||
* @param array $columns Array with columns.
|
||||
*
|
||||
* @return array The extended array with columns.
|
||||
*/
|
||||
public function add_inclusive_language_column( $columns ) {
|
||||
if ( ! \is_array( $columns ) ) {
|
||||
return $columns;
|
||||
}
|
||||
|
||||
$columns[ self::INCLUSIVE_LANGUAGE_COLUMN_NAME ] = \sprintf(
|
||||
'<span class="yoast-column-inclusive-language yoast-column-header-has-tooltip" data-tooltip-text="%1$s"><span class="screen-reader-text">%2$s</span></span>',
|
||||
\esc_attr__( 'Inclusive language score', 'wordpress-seo-premium' ),
|
||||
\esc_html__( 'Inclusive language score', 'wordpress-seo-premium' )
|
||||
);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the query pieces to allow ordering column by inclusive language score.
|
||||
*
|
||||
* @param array $pieces Array of Query pieces.
|
||||
* @param WP_Query $query The Query on which to apply.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function order_by_inclusive_language_score( $pieces, $query ) {
|
||||
if ( $query->get( 'orderby' ) !== self::INCLUSIVE_LANGUAGE_COLUMN_NAME ) {
|
||||
return $pieces;
|
||||
}
|
||||
|
||||
return $this->build_sort_query_pieces( $pieces, $query );
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the pieces for a sorting query.
|
||||
*
|
||||
* @param array $pieces Array of Query pieces.
|
||||
* @param WP_Query $query The Query on which to apply.
|
||||
*
|
||||
* @return array Modified Query pieces.
|
||||
*/
|
||||
protected function build_sort_query_pieces( $pieces, $query ) {
|
||||
// We only want our code to run in the main WP query.
|
||||
if ( ! $query->is_main_query() ) {
|
||||
return $pieces;
|
||||
}
|
||||
|
||||
// Get the order query variable - ASC or DESC.
|
||||
$order = \strtoupper( $query->get( 'order' ) );
|
||||
|
||||
// Make sure the order setting qualifies. If not, set default as ASC.
|
||||
if ( ! \in_array( $order, [ 'ASC', 'DESC' ], true ) ) {
|
||||
$order = 'ASC';
|
||||
}
|
||||
|
||||
$table = Model::get_table_name( 'Indexable' );
|
||||
|
||||
$pieces['join'] .= " LEFT JOIN $table AS yoast_indexable ON yoast_indexable.object_id = {$this->wpdb->posts}.ID AND yoast_indexable.object_type = 'post' ";
|
||||
$pieces['orderby'] = "yoast_indexable.inclusive_language_score $order, {$pieces['orderby']}";
|
||||
|
||||
return $pieces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the column content for the given column.
|
||||
*
|
||||
* @param string $column_name Column to display the content for.
|
||||
* @param int $post_id Post to display the column content for.
|
||||
*/
|
||||
public function column_content( $column_name, $post_id ) {
|
||||
$indexable = $this->admin_columns_cache->get_indexable( $post_id );
|
||||
// Nothing to output if we don't have the value.
|
||||
if ( empty( $indexable ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $column_name === self::INCLUSIVE_LANGUAGE_COLUMN_NAME ) {
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Correctly escaped through the Score_Icon_Helper.
|
||||
echo $this->score_icon_helper->for_inclusive_language( $indexable->inclusive_language_score );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sortable columns.
|
||||
*
|
||||
* @param array $columns Array with sortable columns.
|
||||
*
|
||||
* @return array The extended array with sortable columns.
|
||||
*/
|
||||
public function column_sort( $columns ) {
|
||||
$columns[ self::INCLUSIVE_LANGUAGE_COLUMN_NAME ] = self::INCLUSIVE_LANGUAGE_COLUMN_NAME;
|
||||
|
||||
return $columns;
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WPSEO_Meta;
|
||||
use WPSEO_Rank;
|
||||
use Yoast\WP\SEO\Conditionals\Admin\Posts_Overview_Or_Ajax_Conditional;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Conditionals\Inclusive_Language_Enabled_Conditional;
|
||||
|
||||
/**
|
||||
* Inclusive_Language_Filter_Integration class.
|
||||
*
|
||||
* phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Inclusive_Language_Filter_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [
|
||||
Admin_Conditional::class,
|
||||
Posts_Overview_Or_Ajax_Conditional::class,
|
||||
Inclusive_Language_Enabled_Conditional::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register_hooks() {
|
||||
// Creates the inclusive language score filter dropdown -- with priority 20 to fire after the SEO/readability filter.
|
||||
\add_action( 'restrict_manage_posts', [ $this, 'posts_filter_dropdown_inclusive_language' ], 20 );
|
||||
// Adds the inclusive language score filter to the list of active filters -- if selected for filtering.
|
||||
\add_filter( 'wpseo_change_applicable_filters', [ $this, 'add_inclusive_language_filter' ] );
|
||||
// Adds the inclusive language score meta column to the order by part of the query -- if selected for ordering.
|
||||
\add_filter( 'wpseo_change_order_by', [ $this, 'add_inclusive_language_order_by' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dropdown that allows filtering on inclusive language score.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function posts_filter_dropdown_inclusive_language() {
|
||||
$ranks = WPSEO_Rank::get_all_inclusive_language_ranks();
|
||||
|
||||
echo '<label class="screen-reader-text" for="wpseo-inclusive-language-filter">'
|
||||
/* translators: Hidden accessibility text. */
|
||||
. \esc_html__( 'Filter by Inclusive Language Score', 'wordpress-seo-premium' )
|
||||
. '</label>';
|
||||
echo '<select name="inclusive_language_filter" id="wpseo-inclusive-language-filter">';
|
||||
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput -- Output is correctly escaped in the generate_option() method.
|
||||
echo $this->generate_option( '', __( 'All Inclusive Language Scores', 'wordpress-seo-premium' ) );
|
||||
|
||||
foreach ( $ranks as $rank ) {
|
||||
$selected = \selected( $this->get_current_inclusive_language_filter(), $rank->get_rank(), false );
|
||||
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput -- Output is correctly escaped in the generate_option() method.
|
||||
echo $this->generate_option( $rank->get_rank(), $rank->get_drop_down_inclusive_language_labels(), $selected );
|
||||
}
|
||||
|
||||
echo '</select>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an <option> element.
|
||||
*
|
||||
* @param string $value The option's value.
|
||||
* @param string $label The option's label.
|
||||
* @param string $selected HTML selected attribute for an option.
|
||||
*
|
||||
* @return string The generated <option> element.
|
||||
*/
|
||||
protected function generate_option( $value, $label, $selected = '' ) {
|
||||
return '<option ' . $selected . ' value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current inclusive language score filter value from the $_GET variable.
|
||||
*
|
||||
* @return string|null The sanitized inclusive language score filter value or null when the variable is not set in $_GET.
|
||||
*/
|
||||
public function get_current_inclusive_language_filter() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
|
||||
if ( isset( $_GET['inclusive_language_filter'] ) && \is_string( $_GET['inclusive_language_filter'] ) ) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
|
||||
return \sanitize_text_field( \wp_unslash( $_GET['inclusive_language_filter'] ) );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the inclusive language score filter to the meta query, based on the passed inclusive language filter.
|
||||
*
|
||||
* @param string $inclusive_language_filter The inclusive language filter to use to determine what further filter to apply.
|
||||
*
|
||||
* @return array The inclusive language score filter.
|
||||
*/
|
||||
public function determine_inclusive_language_filters( $inclusive_language_filter ) {
|
||||
$rank = new WPSEO_Rank( $inclusive_language_filter );
|
||||
|
||||
return $this->create_inclusive_language_score_filter( $rank->get_starting_score(), $rank->get_end_score() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an inclusive language score filter.
|
||||
*
|
||||
* @param number $low The lower boundary of the score.
|
||||
* @param number $high The higher boundary of the score.
|
||||
*
|
||||
* @return array The inclusive language score filter.
|
||||
*/
|
||||
protected function create_inclusive_language_score_filter( $low, $high ) {
|
||||
return [
|
||||
[
|
||||
'key' => WPSEO_Meta::$meta_prefix . 'inclusive_language_score',
|
||||
'value' => [ $low, $high ],
|
||||
'type' => 'numeric',
|
||||
'compare' => 'BETWEEN',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the inclusive language filter to the list of active filters -- if it has been used for filtering.
|
||||
*
|
||||
* @param array $active_filters The currently active filters.
|
||||
* @return array The active filters, including the inclusive language filter -- if it has been used for filtering.
|
||||
*/
|
||||
public function add_inclusive_language_filter( $active_filters ) {
|
||||
$inclusive_language_filter = $this->get_current_inclusive_language_filter();
|
||||
|
||||
if ( \is_string( $inclusive_language_filter ) && $inclusive_language_filter !== '' ) {
|
||||
$active_filters = \array_merge(
|
||||
$active_filters,
|
||||
$this->determine_inclusive_language_filters( $inclusive_language_filter )
|
||||
);
|
||||
}
|
||||
|
||||
return $active_filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the inclusive language score field to the order by part of the query -- if it has been selected during filtering.
|
||||
*
|
||||
* @param array $order_by The current order by statement.
|
||||
* @param string $order_by_column The column to use for ordering.
|
||||
* @return array The order by.
|
||||
*/
|
||||
public function add_inclusive_language_order_by( $order_by, $order_by_column = '' ) {
|
||||
if ( $order_by === [] && $order_by_column === Inclusive_Language_Column_Integration::INCLUSIVE_LANGUAGE_COLUMN_NAME ) {
|
||||
return [
|
||||
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting.
|
||||
'meta_key' => WPSEO_Meta::$meta_prefix . 'inclusive_language_score',
|
||||
'orderby' => 'meta_value_num',
|
||||
];
|
||||
}
|
||||
|
||||
return $order_by;
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WPSEO_Admin_Asset_Manager;
|
||||
use WPSEO_Taxonomy_Meta;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Score_Icon_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Conditionals\Inclusive_Language_Enabled_Conditional;
|
||||
use Yoast\WP\SEO\Premium\Conditionals\Term_Overview_Or_Ajax_Conditional;
|
||||
use Yoast\WP\SEO\Premium\Helpers\Current_Page_Helper;
|
||||
|
||||
/**
|
||||
* Inclusive_Language_Column_Integration class.
|
||||
*
|
||||
* phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Inclusive_Language_Taxonomy_Column_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Name of the column.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const INCLUSIVE_LANGUAGE_COLUMN_NAME = 'wpseo-inclusive-language';
|
||||
|
||||
/**
|
||||
* The score icon helper.
|
||||
*
|
||||
* @var Score_Icon_Helper
|
||||
*/
|
||||
protected $score_icon_helper;
|
||||
|
||||
/**
|
||||
* Holds the Current_Page_Helper instance.
|
||||
*
|
||||
* @var Current_Page_Helper
|
||||
*/
|
||||
private $current_page_helper;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [
|
||||
Admin_Conditional::class,
|
||||
Term_Overview_Or_Ajax_Conditional::class,
|
||||
Inclusive_Language_Enabled_Conditional::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Inclusive_Language_Column_Integration constructor
|
||||
*
|
||||
* @param Score_Icon_Helper $score_icon_helper The score icon helper.
|
||||
* @param Current_Page_Helper $current_page_helper The Current_Page_Helper.
|
||||
*/
|
||||
public function __construct(
|
||||
Score_Icon_Helper $score_icon_helper,
|
||||
Current_Page_Helper $current_page_helper
|
||||
) {
|
||||
$this->score_icon_helper = $score_icon_helper;
|
||||
$this->current_page_helper = $current_page_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'admin_init', [ $this, 'register_init_hooks' ] );
|
||||
\add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register hooks that need to be registered after `init` due to all post types not yet being registered.
|
||||
*/
|
||||
public function register_init_hooks() {
|
||||
$taxonomy = $this->current_page_helper->get_current_taxonomy();
|
||||
$is_product = $this->current_page_helper->get_current_post_type() === 'product';
|
||||
$is_product_cat = $taxonomy === 'product_cat';
|
||||
$is_product_tag = $taxonomy === 'product_tag';
|
||||
|
||||
if ( ( $is_product && ( $is_product_cat || $is_product_tag ) ) || ( ! $is_product && $taxonomy ) ) {
|
||||
\add_filter( 'manage_edit-' . $taxonomy . '_columns', [ $this, 'add_inclusive_language_column' ] );
|
||||
\add_filter( 'manage_' . $taxonomy . '_custom_column', [ $this, 'column_content' ], 10, 3 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the assets needed for the integration to work.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
\wp_enqueue_style( WPSEO_Admin_Asset_Manager::PREFIX . 'premium-post-overview' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the inclusive language column for the term overview.
|
||||
*
|
||||
* @param array $columns Array with columns.
|
||||
*
|
||||
* @return array The extended array with columns.
|
||||
*/
|
||||
public function add_inclusive_language_column( $columns ) {
|
||||
if ( ! \is_array( $columns ) ) {
|
||||
return $columns;
|
||||
}
|
||||
|
||||
$columns[ self::INCLUSIVE_LANGUAGE_COLUMN_NAME ] = \sprintf(
|
||||
'<span class="yoast-tooltip yoast-tooltip-n yoast-tooltip-alt" data-label="%1$s">
|
||||
<span class="yoast-column-inclusive-language yoast-column-header-has-tooltip">
|
||||
<span class="screen-reader-text">%2$s</span>
|
||||
</span>
|
||||
</span>',
|
||||
\esc_attr__( 'Inclusive language score', 'wordpress-seo-premium' ),
|
||||
\esc_html__( 'Inclusive language score', 'wordpress-seo-premium' )
|
||||
);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the column content for the given column.
|
||||
*
|
||||
* @param string $content The current content of the column.
|
||||
* @param string $column_name The name of the column.
|
||||
* @param int $term_id ID of requested taxonomy.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function column_content( $content, $column_name, $term_id ) {
|
||||
$score = (int) WPSEO_Taxonomy_Meta::get_term_meta( $term_id, $this->current_page_helper->get_current_taxonomy(), 'inclusive_language_score' );
|
||||
|
||||
if ( $column_name === self::INCLUSIVE_LANGUAGE_COLUMN_NAME ) {
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Correctly escaped through the Score_Icon_Helper.
|
||||
return $this->score_icon_helper->for_inclusive_language( $score );
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WP_Query;
|
||||
use WPSEO_Meta;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Keyword_Integration class
|
||||
*/
|
||||
class Keyword_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Admin_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'wpseo_posts_for_focus_keyword', [ $this, 'add_posts_for_focus_keyword' ], 10, 3 );
|
||||
\add_filter( 'wpseo_posts_for_related_keywords', [ $this, 'add_posts_for_related_keywords' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhances the array of posts that share their focus keywords with the post's related keywords by adding posts' ids with the same related keywords.
|
||||
*
|
||||
* @param array $usage The array of posts' ids that share their focus keywords with the post.
|
||||
* @param int $post_id The id of the post we're finding the usage of related keywords for.
|
||||
*
|
||||
* @return array The filtered array of posts' ids.
|
||||
*/
|
||||
public function add_posts_for_related_keywords( $usage, $post_id ) {
|
||||
$additional_keywords = \json_decode( WPSEO_Meta::get_value( 'focuskeywords', $post_id ), true );
|
||||
|
||||
if ( empty( $additional_keywords ) ) {
|
||||
return $usage;
|
||||
}
|
||||
|
||||
foreach ( $additional_keywords as $additional_keyword ) {
|
||||
if ( isset( $additional_keyword['keyword'] ) ) {
|
||||
$keyword = $additional_keyword['keyword'];
|
||||
|
||||
$usage[ $keyword ] = WPSEO_Meta::keyword_usage( $keyword, $post_id );
|
||||
}
|
||||
}
|
||||
|
||||
return $usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhances the array of posts that share their focus keywords with the post's focus keywords by adding posts' ids with the same related keywords.
|
||||
*
|
||||
* @param array $post_ids The array of posts' ids that share their related keywords with the post.
|
||||
* @param string $keyword The keyword to search for.
|
||||
* @param int $post_id The id of the post the keyword is associated to.
|
||||
*
|
||||
* @return array The filtered array of posts' ids.
|
||||
*/
|
||||
public function add_posts_for_focus_keyword( $post_ids, $keyword, $post_id ) {
|
||||
$query = [
|
||||
'meta_query' => [
|
||||
[
|
||||
'key' => '_yoast_wpseo_focuskeywords',
|
||||
'value' => \sprintf( '"keyword":"%s"', $keyword ),
|
||||
'compare' => 'LIKE',
|
||||
],
|
||||
],
|
||||
'post__not_in' => [ $post_id ],
|
||||
'fields' => 'ids',
|
||||
'post_type' => 'any',
|
||||
|
||||
/*
|
||||
* We only need to return zero, one or two results:
|
||||
* - Zero: keyword hasn't been used before
|
||||
* - One: Keyword has been used once before
|
||||
* - Two or more: Keyword has been used twice or more before
|
||||
*/
|
||||
'posts_per_page' => 2,
|
||||
];
|
||||
$get_posts = new WP_Query( $query );
|
||||
return \array_merge( $post_ids, $get_posts->posts );
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WPSEO_Options;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Metabox_Formatter_Integration class
|
||||
*/
|
||||
class Metabox_Formatter_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Admin_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'wpseo_social_template_post_type', [ $this, 'get_template_for_post_type' ], 10, 3 );
|
||||
\add_filter( 'wpseo_social_template_taxonomy', [ $this, 'get_template_for_taxonomy' ], 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a template for a post type.
|
||||
*
|
||||
* @param string $template The default template.
|
||||
* @param string $template_option_name The subname of the option in which the template you want to get is saved.
|
||||
* @param string $post_type The name of the post type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_for_post_type( $template, $template_option_name, $post_type ) {
|
||||
$needed_option = \sprintf( 'social-%s-%s', $template_option_name, $post_type );
|
||||
|
||||
if ( WPSEO_Options::get( $needed_option, '' ) !== '' ) {
|
||||
return WPSEO_Options::get( $needed_option );
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a template for a taxonomy.
|
||||
*
|
||||
* @param string $template The default template.
|
||||
* @param string $template_option_name The subname of the option in which the template you want to get is saved.
|
||||
* @param string $taxonomy The name of the taxonomy.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_for_taxonomy( $template, $template_option_name, $taxonomy ) {
|
||||
$needed_option = \sprintf( 'social-%s-tax-%s', $template_option_name, $taxonomy );
|
||||
return WPSEO_Options::get( $needed_option, $template );
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WPSEO_Addon_Manager;
|
||||
use WPSEO_Shortlinker;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Plugin_Links_Integration class
|
||||
*/
|
||||
class Plugin_Links_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Admin_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'plugin_action_links_' . \WPSEO_BASENAME, [ $this, 'remove_yoast_seo_action_link' ], 10 );
|
||||
\add_filter( 'network_admin_plugin_action_links_' . \WPSEO_BASENAME, [ $this, 'remove_yoast_seo_action_link' ], 10 );
|
||||
|
||||
\add_filter( 'plugin_action_links_' . \WPSEO_PREMIUM_BASENAME, [ $this, 'add_yoast_seo_premium_action_link' ], 10 );
|
||||
\add_filter( 'network_admin_plugin_action_links_' . \WPSEO_PREMIUM_BASENAME, [ $this, 'add_yoast_seo_premium_action_link' ], 10 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the upgrade link from Yoast SEO free.
|
||||
*
|
||||
* @param string[] $links The action links.
|
||||
*
|
||||
* @return string[] The action link with the upgrade link removed.
|
||||
*/
|
||||
public function remove_yoast_seo_action_link( $links ) {
|
||||
$link_to_remove = $this->get_upgrade_link();
|
||||
return \array_filter(
|
||||
$links,
|
||||
static function ( $link ) use ( $link_to_remove ) {
|
||||
return $link !== $link_to_remove;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the upgrade link to the premium actions.
|
||||
*
|
||||
* @param string[] $links The action links.
|
||||
*
|
||||
* @return string[] The action link with the upgrade link added.
|
||||
*/
|
||||
public function add_yoast_seo_premium_action_link( $links ) {
|
||||
$addon_manager = new WPSEO_Addon_Manager();
|
||||
|
||||
if ( ! $addon_manager->has_valid_subscription( WPSEO_Addon_Manager::PREMIUM_SLUG ) ) {
|
||||
\array_unshift( $links, $this->get_upgrade_link() );
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the upgrade link.
|
||||
*
|
||||
* @return string The upgrade link.
|
||||
*/
|
||||
protected function get_upgrade_link() {
|
||||
// phpcs:ignore WordPress.WP.I18n.TextDomainMismatch -- Reason: text is originally from Yoast SEO.
|
||||
return '<a style="font-weight: bold;" href="' . \esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/activate-my-yoast' ) ) . '" target="_blank">' . \__( 'Activate your subscription', 'wordpress-seo' ) . '</a>';
|
||||
}
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin\Prominent_Words;
|
||||
|
||||
use WPSEO_Language_Utils;
|
||||
use Yoast\WP\SEO\Actions\Indexing\Indexable_General_Indexation_Action;
|
||||
use Yoast\WP\SEO\Actions\Indexing\Indexable_Post_Indexation_Action;
|
||||
use Yoast\WP\SEO\Actions\Indexing\Indexable_Post_Type_Archive_Indexation_Action;
|
||||
use Yoast\WP\SEO\Actions\Indexing\Indexable_Term_Indexation_Action;
|
||||
use Yoast\WP\SEO\Actions\Indexing\Indexation_Action_Interface;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Conditionals\Migrations_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Language_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Url_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Actions\Prominent_Words\Content_Action;
|
||||
use Yoast\WP\SEO\Premium\Helpers\Prominent_Words_Helper;
|
||||
use Yoast\WP\SEO\Premium\Routes\Prominent_Words_Route;
|
||||
|
||||
/**
|
||||
* Class Indexing_Integration.
|
||||
*/
|
||||
class Indexing_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Number of prominent words to index per indexable
|
||||
* when a language has function word support.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const PER_INDEXABLE_LIMIT = 20;
|
||||
|
||||
/**
|
||||
* Number of prominent words to index per indexable
|
||||
* when a language does not have function word support.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const PER_INDEXABLE_LIMIT_NO_FUNCTION_WORD_SUPPORT = 30;
|
||||
|
||||
/**
|
||||
* All indexing actions.
|
||||
*
|
||||
* @var Indexation_Action_Interface[]
|
||||
*/
|
||||
protected $indexing_actions;
|
||||
|
||||
/**
|
||||
* Represents the language helper.
|
||||
*
|
||||
* @var Language_Helper
|
||||
*/
|
||||
protected $language_helper;
|
||||
|
||||
/**
|
||||
* Represents the url helper.
|
||||
*
|
||||
* @var Url_Helper
|
||||
*/
|
||||
protected $url_helper;
|
||||
|
||||
/**
|
||||
* Represents the prominent words helper.
|
||||
*
|
||||
* @var Prominent_Words_Helper
|
||||
*/
|
||||
protected $prominent_words_helper;
|
||||
|
||||
/**
|
||||
* Holds the total number of unindexed objects.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $total_unindexed;
|
||||
|
||||
/**
|
||||
* WPSEO_Premium_Prominent_Words_Recalculation constructor.
|
||||
*
|
||||
* @param Content_Action $content_indexation_action The content indexing action.
|
||||
* @param Indexable_Post_Indexation_Action $post_indexation_action The post indexing action.
|
||||
* @param Indexable_Term_Indexation_Action $term_indexation_action The term indexing action.
|
||||
* @param Indexable_General_Indexation_Action $general_indexation_action The general indexing action.
|
||||
* @param Indexable_Post_Type_Archive_Indexation_Action $post_type_archive_indexation_action The post type archive indexing action.
|
||||
* @param Language_Helper $language_helper The language helper.
|
||||
* @param Url_Helper $url_helper The url helper.
|
||||
* @param Prominent_Words_Helper $prominent_words_helper The prominent words helper.
|
||||
*/
|
||||
public function __construct(
|
||||
Content_Action $content_indexation_action,
|
||||
Indexable_Post_Indexation_Action $post_indexation_action,
|
||||
Indexable_Term_Indexation_Action $term_indexation_action,
|
||||
Indexable_General_Indexation_Action $general_indexation_action,
|
||||
Indexable_Post_Type_Archive_Indexation_Action $post_type_archive_indexation_action,
|
||||
Language_Helper $language_helper,
|
||||
Url_Helper $url_helper,
|
||||
Prominent_Words_Helper $prominent_words_helper
|
||||
) {
|
||||
$this->language_helper = $language_helper;
|
||||
$this->url_helper = $url_helper;
|
||||
$this->prominent_words_helper = $prominent_words_helper;
|
||||
|
||||
// Indexation actions are used to calculate the number of unindexed objects.
|
||||
$this->indexing_actions = [
|
||||
// Get the number of indexables that haven't had their prominent words indexed yet.
|
||||
$content_indexation_action,
|
||||
|
||||
// Take posts and terms into account that do not have indexables yet.
|
||||
// These need to be counted again here (in addition to being counted in Free) because them being unindexed
|
||||
// means that the above prominent words unindexed count couldn't detect these posts/terms for prominent words indexing.
|
||||
$post_indexation_action,
|
||||
$term_indexation_action,
|
||||
$general_indexation_action,
|
||||
$post_type_archive_indexation_action,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
|
||||
|
||||
\add_filter( 'wpseo_indexing_data', [ $this, 'adapt_indexing_data' ] );
|
||||
\add_filter( 'wpseo_indexing_get_unindexed_count', [ $this, 'get_unindexed_count' ] );
|
||||
\add_filter( 'wpseo_indexing_get_limited_unindexed_count', [ $this, 'get_limited_unindexed_count' ], 10, 2 );
|
||||
\add_filter( 'wpseo_indexing_endpoints', [ $this, 'add_endpoints' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the conditionals based in which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [
|
||||
Admin_Conditional::class,
|
||||
Migrations_Conditional::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the endpoints to call.
|
||||
*
|
||||
* @param array $endpoints The endpoints to extend.
|
||||
*
|
||||
* @return array The endpoints.
|
||||
*/
|
||||
public function add_endpoints( $endpoints ) {
|
||||
$endpoints['get_content'] = Prominent_Words_Route::FULL_GET_CONTENT_ROUTE;
|
||||
$endpoints['complete_words'] = Prominent_Words_Route::FULL_COMPLETE_ROUTE;
|
||||
|
||||
return $endpoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts the indexing data as sent to the JavaScript side of the
|
||||
* indexing process.
|
||||
*
|
||||
* Adds the appropriate prominent words endpoints and other settings.
|
||||
*
|
||||
* @param array $data The data to be adapted.
|
||||
*
|
||||
* @return array The adapted indexing data.
|
||||
*/
|
||||
public function adapt_indexing_data( $data ) {
|
||||
$site_locale = \get_locale();
|
||||
$language = WPSEO_Language_Utils::get_language( $site_locale );
|
||||
|
||||
$data['locale'] = $site_locale;
|
||||
$data['language'] = $language;
|
||||
|
||||
$data['morphologySupported'] = $this->language_helper->is_word_form_recognition_active( $language );
|
||||
|
||||
$per_indexable_limit = self::PER_INDEXABLE_LIMIT_NO_FUNCTION_WORD_SUPPORT;
|
||||
if ( $this->language_helper->has_function_word_support( $language ) ) {
|
||||
$per_indexable_limit = self::PER_INDEXABLE_LIMIT;
|
||||
}
|
||||
|
||||
$data['prominentWords'] = [
|
||||
'endpoint' => Prominent_Words_Route::FULL_SAVE_ROUTE,
|
||||
'perIndexableLimit' => $per_indexable_limit,
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the required scripts.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
if ( ! isset( $_GET['page'] )
|
||||
|| ( $_GET['page'] !== 'wpseo_tools' && $_GET['page'] !== 'wpseo_workouts' && $_GET['page'] !== 'wpseo_dashboard' )
|
||||
|| ( $_GET['page'] === 'wpseo_tools' && isset( $_GET['tool'] ) )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$is_completed = ( (int) $this->get_unindexed_count( 0 ) === 0 );
|
||||
$this->prominent_words_helper->set_indexing_completed( $is_completed );
|
||||
|
||||
\wp_enqueue_script( 'yoast-premium-prominent-words-indexation' );
|
||||
\wp_localize_script( 'yoast-premium-prominent-words-indexation', 'wpseoPremiumIndexationData', [ 'licensedURL' => $this->url_helper->network_safe_home_url() ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of unindexed objects.
|
||||
*
|
||||
* @param int $unindexed_count The unindexed count.
|
||||
*
|
||||
* @return int The total number of indexables to recalculate.
|
||||
*/
|
||||
public function get_unindexed_count( $unindexed_count ) {
|
||||
foreach ( $this->indexing_actions as $indexing_action ) {
|
||||
$unindexed_count += $indexing_action->get_total_unindexed();
|
||||
}
|
||||
return $unindexed_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a limited number of unindexed objects.
|
||||
*
|
||||
* @param int $unindexed_count The unindexed count.
|
||||
* @param int $limit Limit the number of unindexed objects that are counted.
|
||||
*
|
||||
* @return int The total number of unindexed objects.
|
||||
*/
|
||||
public function get_limited_unindexed_count( $unindexed_count, $limit ) {
|
||||
foreach ( $this->indexing_actions as $indexing_action ) {
|
||||
$unindexed_count += $indexing_action->get_limited_unindexed_count( $limit - $unindexed_count + 1 );
|
||||
if ( $unindexed_count > $limit ) {
|
||||
return $unindexed_count;
|
||||
}
|
||||
}
|
||||
|
||||
return $unindexed_count;
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin\Prominent_Words;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Actions\Prominent_Words\Save_Action;
|
||||
|
||||
/**
|
||||
* Adds a hidden field to the metabox for storing the calculated words and also
|
||||
* handles the value of it after posting.
|
||||
*/
|
||||
class Metabox_Integration implements Integration_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* Represents the prominent words save action.
|
||||
*
|
||||
* @var Save_Action
|
||||
*/
|
||||
protected $save_action;
|
||||
|
||||
/**
|
||||
* Prominent_Words_Metabox constructor.
|
||||
*
|
||||
* @param Save_Action $save_action The prominent words save action.
|
||||
*/
|
||||
public function __construct( Save_Action $save_action ) {
|
||||
$this->save_action = $save_action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the register_hooks function of the Integration interface.
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'wpseo_metabox_entries_general', [ $this, 'add_words_for_linking_hidden_field' ] );
|
||||
\add_filter( 'update_post_metadata', [ $this, 'save_prominent_words_for_post' ], 10, 4 );
|
||||
|
||||
\add_filter( 'wpseo_taxonomy_content_fields', [ $this, 'add_words_for_linking_hidden_field' ] );
|
||||
\add_action( 'edit_term', [ $this, 'save_prominent_words_for_term' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a hidden field for the prominent words to the metabox.
|
||||
*
|
||||
* @param array $field_defs The definitions for the input fields.
|
||||
*
|
||||
* @return array The definitions for the input fields.
|
||||
*/
|
||||
public function add_words_for_linking_hidden_field( $field_defs ) {
|
||||
if ( \is_array( $field_defs ) ) {
|
||||
$field_defs['words_for_linking'] = [
|
||||
'type' => 'hidden',
|
||||
'title' => 'words_for_linking',
|
||||
'label' => '',
|
||||
'options' => '',
|
||||
];
|
||||
}
|
||||
|
||||
return $field_defs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the value of the _yoast_wpseo_words_for_linking hidden field to the prominent_words table, not postmeta.
|
||||
* Added to the 'update_post_metadata' filter.
|
||||
*
|
||||
* @param false|null $check Whether to allow updating metadata for the given type.
|
||||
* @param int $object_id The post id.
|
||||
* @param string $meta_key The key of the metadata.
|
||||
* @param mixed $meta_value The value of the metadata.
|
||||
*
|
||||
* @return false|null Non-null value if meta data should not be updated.
|
||||
* Null if the metadata should be updated as normal.
|
||||
*/
|
||||
public function save_prominent_words_for_post( $check, $object_id, $meta_key, $meta_value ) {
|
||||
if ( $meta_key !== '_yoast_wpseo_words_for_linking' ) {
|
||||
return $check;
|
||||
}
|
||||
|
||||
// If the save was triggered with an empty meta value, don't update the prominent words.
|
||||
if ( empty( $meta_value ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1. Decode from stringified JSON.
|
||||
$words_for_linking = \json_decode( $meta_value, true );
|
||||
// 2. Save prominent words using the existing functionality.
|
||||
$this->save_action->link( 'post', $object_id, $words_for_linking );
|
||||
|
||||
// 3. Return non-null value so we don't save prominent words to the `post_meta` table.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the prominent words for a term.
|
||||
*
|
||||
* @param int $term_id The term id to save the words for.
|
||||
*/
|
||||
public function save_prominent_words_for_term( $term_id ) {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing -- The nonce is already validated.
|
||||
if ( ! isset( $_POST['wpseo_words_for_linking'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$words_for_linking = [];
|
||||
if ( ! empty( $_POST['wpseo_words_for_linking'] ) ) {
|
||||
$prominent_words = \sanitize_text_field( \wp_unslash( $_POST['wpseo_words_for_linking'] ) );
|
||||
// phpcs:enable
|
||||
$words_for_linking = \json_decode( $prominent_words, true );
|
||||
}
|
||||
|
||||
$this->save_action->link( 'term', $term_id, $words_for_linking );
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WPSEO_Meta;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Take related keyphrases into account when filtering posts on keyphrase.
|
||||
*
|
||||
* phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Related_Keyphrase_Filter_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* The conditionals that should be met for this integration to be active.
|
||||
*
|
||||
* @return string[] A list of fully qualified class names of the `Conditional`s that should be met.
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Admin_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the WordPress hooks needed for this integration to work.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'wpseo_change_keyphrase_filter_in_request', [ $this, 'add_related_keyphrase_filter' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts the keyphrase filter to also take related keyphrases into account.
|
||||
*
|
||||
* @param array $keyphrase_filter The current keyphrase filter.
|
||||
* @param string $keyphrase The keyphrase.
|
||||
*
|
||||
* @return array The new keyphrase filter,
|
||||
*/
|
||||
public function add_related_keyphrase_filter( $keyphrase_filter, $keyphrase ) {
|
||||
return [
|
||||
'relation' => 'OR',
|
||||
$keyphrase_filter,
|
||||
$this->get_related_keyphrase_filter( $keyphrase ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filter to use within the WP Meta Query to filter
|
||||
* on related keyphrase.
|
||||
*
|
||||
* @param string $focus_keyphrase The focus keyphrase to filter on.
|
||||
*
|
||||
* @return array The filter.
|
||||
*/
|
||||
private function get_related_keyphrase_filter( $focus_keyphrase ) {
|
||||
return [
|
||||
'post_type' => \get_query_var( 'post_type', 'post' ),
|
||||
'key' => WPSEO_Meta::$meta_prefix . 'focuskeywords',
|
||||
'value' => '"keyword":"' . \sanitize_text_field( $focus_keyphrase ) . '"',
|
||||
'compare' => 'LIKE',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WPSEO_Admin_Asset_Manager;
|
||||
use WPSEO_Metabox;
|
||||
use WPSEO_Options;
|
||||
use WPSEO_Post_Type;
|
||||
use WPSEO_Taxonomy;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Helpers\Current_Page_Helper;
|
||||
|
||||
/**
|
||||
* Replacement_Variables_Integration class
|
||||
*/
|
||||
class Replacement_Variables_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Admin_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the replacement variables styles and component.
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
|
||||
/**
|
||||
* Filter: 'wpseo_premium_load_emoji_picker' - Allow changing whether the emoji picker is loaded for the meta description and SEO title fields.
|
||||
*
|
||||
* Note: This is a Premium plugin-only hook.
|
||||
*
|
||||
* @since 19.0
|
||||
*
|
||||
* @param bool $load Whether to load the emoji picker.
|
||||
*/
|
||||
if ( ! \apply_filters( 'wpseo_premium_load_emoji_picker', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$is_elementor_action = false;
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
|
||||
if ( isset( $_GET['action'] ) && \is_string( $_GET['action'] ) ) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are not processing form information, We are only strictly comparing.
|
||||
$is_elementor_action = ( \wp_unslash( $_GET['action'] ) === 'elementor' );
|
||||
}
|
||||
|
||||
$is_settings_page = false;
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
|
||||
if ( isset( $_GET['page'] ) && \is_string( $_GET['page'] ) ) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are not processing form information, We are only strictly comparing.
|
||||
$is_settings_page = ( \wp_unslash( $_GET['page'] ) === 'wpseo_page_settings' );
|
||||
}
|
||||
|
||||
if ( ! $is_settings_page && ! $is_elementor_action && ! $this->load_metabox( $this->get_current_page() ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
\wp_enqueue_script( 'yoast-seo-premium-draft-js-plugins' );
|
||||
|
||||
\wp_enqueue_style( 'yoast-seo-premium-draft-js-plugins' );
|
||||
|
||||
$draft_js_external_script_location = 'https://yoast.com/shared-assets/scripts/wp-seo-premium-draft-js-plugins-source-2.0.0.min.js';
|
||||
|
||||
if ( \file_exists( \WPSEO_PREMIUM_PATH . 'assets/js/external/draft-js-emoji-picker.min.js' ) ) {
|
||||
$draft_js_external_script_location = \plugins_url( 'wordpress-seo-premium/assets/js/external/draft-js-emoji-picker.min.js' );
|
||||
}
|
||||
|
||||
\wp_enqueue_script(
|
||||
'yoast-seo-premium-draft-js-plugins-external',
|
||||
$draft_js_external_script_location,
|
||||
[
|
||||
'yoast-seo-premium-commons',
|
||||
WPSEO_Admin_Asset_Manager::PREFIX . 'search-metadata-previews',
|
||||
],
|
||||
\WPSEO_PREMIUM_VERSION,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not the metabox related scripts should be loaded.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param string $current_page The page we are on.
|
||||
*
|
||||
* @return bool True when it should be loaded.
|
||||
*/
|
||||
protected function load_metabox( $current_page ) {
|
||||
$page_helper = new Current_Page_Helper();
|
||||
// When the current page is a term related one.
|
||||
if ( WPSEO_Taxonomy::is_term_edit( $current_page ) || WPSEO_Taxonomy::is_term_overview( $current_page ) ) {
|
||||
return WPSEO_Options::get( 'display-metabox-tax-' . $page_helper->get_current_taxonomy() );
|
||||
}
|
||||
|
||||
// When the current page isn't a post related one.
|
||||
if ( WPSEO_Metabox::is_post_edit( $current_page ) || WPSEO_Metabox::is_post_overview( $current_page ) ) {
|
||||
return WPSEO_Post_Type::has_metabox_enabled( $page_helper->get_current_post_type() );
|
||||
}
|
||||
|
||||
// Make sure ajax integrations are loaded.
|
||||
return \wp_doing_ajax();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of the pagenow variable.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @return string The value of pagenow.
|
||||
*/
|
||||
private function get_current_page() {
|
||||
global $pagenow;
|
||||
|
||||
return $pagenow;
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WPSEO_Admin_Asset_Manager;
|
||||
use Yoast\WP\SEO\Conditionals\Settings_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Current_Page_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Class Settings_Integration.
|
||||
*/
|
||||
class Settings_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Holds the WPSEO_Admin_Asset_Manager.
|
||||
*
|
||||
* @var WPSEO_Admin_Asset_Manager
|
||||
*/
|
||||
protected $asset_manager;
|
||||
|
||||
/**
|
||||
* Holds the Current_Page_Helper.
|
||||
*
|
||||
* @var Current_Page_Helper
|
||||
*/
|
||||
protected $current_page_helper;
|
||||
|
||||
/**
|
||||
* Constructs Settings_Integration.
|
||||
*
|
||||
* @param WPSEO_Admin_Asset_Manager $asset_manager The WPSEO_Admin_Asset_Manager.
|
||||
* @param Current_Page_Helper $current_page_helper The Current_Page_Helper.
|
||||
*/
|
||||
public function __construct( WPSEO_Admin_Asset_Manager $asset_manager, Current_Page_Helper $current_page_helper ) {
|
||||
$this->asset_manager = $asset_manager;
|
||||
$this->current_page_helper = $current_page_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the conditionals based on which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Settings_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
// Are we on the settings page?
|
||||
if ( $this->current_page_helper->get_current_yoast_seo_page() === 'wpseo_page_settings' ) {
|
||||
\add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the assets.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
$this->asset_manager->enqueue_style( 'premium-settings' );
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin\Social_Templates;
|
||||
|
||||
use WP_Taxonomy;
|
||||
use WPSEO_Admin_Editor_Specific_Replace_Vars;
|
||||
use WPSEO_Admin_Recommended_Replace_Vars;
|
||||
use WPSEO_Replacevar_Editor;
|
||||
use Yoast\WP\SEO\Premium\Config\Badge_Group_Names;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Conditionals\Social_Templates_Conditional;
|
||||
use Yoast_Form;
|
||||
|
||||
/**
|
||||
* Class Social_Templates_Integration.
|
||||
*
|
||||
* Adds the social fields to the meta tabs for post types, taxonomies and archives.
|
||||
*/
|
||||
class Social_Templates_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Service that can be used to recommend a set of variables for a WPSEO_Replacevar_Editor.
|
||||
*
|
||||
* @var WPSEO_Admin_Recommended_Replace_Vars
|
||||
*/
|
||||
private $recommended_replace_vars;
|
||||
|
||||
/**
|
||||
* Service that can be used to recommend an editor specific set of variables for a WPSEO_Replacevar_Editor.
|
||||
*
|
||||
* @var WPSEO_Admin_Editor_Specific_Replace_Vars
|
||||
*/
|
||||
private $editor_specific_replace_vars;
|
||||
|
||||
/**
|
||||
* Group to which the 'New' badges belong to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $group;
|
||||
|
||||
/**
|
||||
* Returns the conditionals based on which this loadable should be active.
|
||||
*
|
||||
* @return array The conditionals that must be met to load this.
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [
|
||||
Social_Templates_Conditional::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Social_Templates_Integration constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->recommended_replace_vars = new WPSEO_Admin_Recommended_Replace_Vars();
|
||||
$this->editor_specific_replace_vars = new WPSEO_Admin_Editor_Specific_Replace_Vars();
|
||||
$this->group = 'global-templates';
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'Yoast\WP\SEO\admin_author_archives_meta', [ $this, 'social_author_archives' ] );
|
||||
\add_action( 'Yoast\WP\SEO\admin_date_archives_meta', [ $this, 'social_date_archives' ] );
|
||||
\add_action( 'Yoast\WP\SEO\admin_post_types_meta', [ $this, 'social_post_type' ], 8, 2 );
|
||||
\add_action( 'Yoast\WP\SEO\admin_post_types_archive', [ $this, 'social_post_types_archive' ], 10, 2 );
|
||||
\add_action( 'Yoast\WP\SEO\admin_taxonomies_meta', [ $this, 'social_taxonomies' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a set of social fields for the author archives in the Search Appearance section.
|
||||
*
|
||||
* @param Yoast_Form $yform The form builder.
|
||||
*/
|
||||
public function social_author_archives( $yform ) {
|
||||
$identifier = 'author-wpseo';
|
||||
$page_type_recommended = $this->recommended_replace_vars->determine_for_archive( 'author' );
|
||||
$page_type_specific = $this->editor_specific_replace_vars->determine_for_archive( 'author' );
|
||||
|
||||
$this->build_social_fields( $yform, $identifier, $page_type_recommended, $page_type_specific );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a set of social fields for the date archives in the Search Appearance section.
|
||||
*
|
||||
* @param Yoast_Form $yform The form builder.
|
||||
*/
|
||||
public function social_date_archives( $yform ) {
|
||||
$identifier = 'archive-wpseo';
|
||||
$page_type_recommended = $this->recommended_replace_vars->determine_for_archive( 'date' );
|
||||
$page_type_specific = $this->editor_specific_replace_vars->determine_for_archive( 'date' );
|
||||
|
||||
$this->build_social_fields( $yform, $identifier, $page_type_recommended, $page_type_specific );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a set of social fields for the post types in the Search Appearance section.
|
||||
*
|
||||
* @param Yoast_Form $yform The form builder.
|
||||
* @param string $post_type_name The name of the current post_type that gets the social fields added.
|
||||
*/
|
||||
public function social_post_type( $yform, $post_type_name ) {
|
||||
if ( $post_type_name === 'attachment' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$page_type_recommended = $this->recommended_replace_vars->determine_for_post_type( $post_type_name );
|
||||
$page_type_specific = $this->editor_specific_replace_vars->determine_for_post_type( $post_type_name );
|
||||
|
||||
$this->build_social_fields( $yform, $post_type_name, $page_type_recommended, $page_type_specific );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a set of social fields for the post types archives in the Search Appearance section.
|
||||
*
|
||||
* @param Yoast_Form $yform The form builder.
|
||||
* @param string $post_type_name The name of the current post_type that gets the social fields added.
|
||||
*/
|
||||
public function social_post_types_archive( $yform, $post_type_name ) {
|
||||
$identifier = 'ptarchive-' . $post_type_name;
|
||||
$page_type_recommended = $this->recommended_replace_vars->determine_for_archive( $post_type_name );
|
||||
$page_type_specific = $this->editor_specific_replace_vars->determine_for_archive( $post_type_name );
|
||||
|
||||
$this->build_social_fields( $yform, $identifier, $page_type_recommended, $page_type_specific );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a set of social fields for the taxonomies in the Search Appearance section.
|
||||
*
|
||||
* @param Yoast_Form $yform The form builder.
|
||||
* @param WP_Taxonomy $taxonomy The taxonomy that gets the social fields added.
|
||||
*/
|
||||
public function social_taxonomies( $yform, $taxonomy ) {
|
||||
$identifier = 'tax-' . $taxonomy->name;
|
||||
$page_type_recommended = $this->recommended_replace_vars->determine_for_term( $taxonomy->name );
|
||||
$page_type_specific = $this->editor_specific_replace_vars->determine_for_term( $taxonomy->name );
|
||||
|
||||
$this->build_social_fields( $yform, $identifier, $page_type_recommended, $page_type_specific );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a set of social fields for the Search Appearance section.
|
||||
*
|
||||
* @param Yoast_Form $yform The form builder.
|
||||
* @param string $identifier A page-wide unique identifier for data storage and unique DOM elements.
|
||||
* @param string $page_type_recommended Recommended type of page for a list of replaceable variables.
|
||||
* @param string $page_type_specific Editor specific type of page for a list of replaceable variables.
|
||||
*/
|
||||
protected function build_social_fields( Yoast_Form $yform, $identifier, $page_type_recommended, $page_type_specific ) {
|
||||
$image_url_field_id = 'social-image-url-' . $identifier;
|
||||
$image_id_field_id = 'social-image-id-' . $identifier;
|
||||
$badge_group_names = new Badge_Group_Names();
|
||||
|
||||
echo '<div class="yoast-settings-section">';
|
||||
|
||||
$yform->hidden( $image_url_field_id, $image_url_field_id );
|
||||
$yform->hidden( $image_id_field_id, $image_id_field_id );
|
||||
\printf(
|
||||
'<div
|
||||
id="%1$s"
|
||||
data-react-image-portal
|
||||
data-react-image-portal-target-image="%2$s"
|
||||
data-react-image-portal-target-image-id="%3$s"
|
||||
data-react-image-portal-has-new-badge="%4$s"
|
||||
></div>',
|
||||
\esc_attr( 'yoast-social-' . $identifier . '-image-select' ),
|
||||
\esc_attr( $image_url_field_id ),
|
||||
\esc_attr( $image_id_field_id ),
|
||||
\esc_attr( $badge_group_names->is_still_eligible_for_new_badge( $this->group ) )
|
||||
);
|
||||
|
||||
$editor = new WPSEO_Replacevar_Editor(
|
||||
$yform,
|
||||
[
|
||||
'title' => 'social-title-' . $identifier,
|
||||
'description' => 'social-description-' . $identifier,
|
||||
'page_type_recommended' => $page_type_recommended,
|
||||
'page_type_specific' => $page_type_specific,
|
||||
'paper_style' => false,
|
||||
'label_title' => \__( 'Social title', 'wordpress-seo-premium' ),
|
||||
'label_description' => \__( 'Social description', 'wordpress-seo-premium' ),
|
||||
'description_placeholder' => \__( 'Modify your social description by editing it right here.', 'wordpress-seo-premium' ),
|
||||
'has_new_badge' => $badge_group_names->is_still_eligible_for_new_badge( $this->group ),
|
||||
]
|
||||
);
|
||||
$editor->render();
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WPSEO_Admin_Asset_Manager;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Thank_You_Page_Integration class
|
||||
*
|
||||
* @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Thank_You_Page_Integration implements Integration_Interface {
|
||||
// phpcs:enable
|
||||
|
||||
/**
|
||||
* The options helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
protected $options_helper;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Admin_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Thank_You_Page_Integration constructor.
|
||||
*
|
||||
* @param Options_Helper $options_helper The options helper.
|
||||
*/
|
||||
public function __construct( Options_Helper $options_helper ) {
|
||||
$this->options_helper = $options_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'admin_menu', [ $this, 'add_submenu_page' ], 9 );
|
||||
\add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
\add_action( 'admin_init', [ $this, 'maybe_redirect' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects to the installation success page if an installation has just occured.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function maybe_redirect() {
|
||||
if ( ! $this->options_helper->get( 'should_redirect_after_install' ) ) {
|
||||
return;
|
||||
}
|
||||
$this->options_helper->set( 'should_redirect_after_install', false );
|
||||
|
||||
if ( ! empty( $this->options_helper->get( 'activation_redirect_timestamp' ) ) ) {
|
||||
return;
|
||||
}
|
||||
$this->options_helper->set( 'activation_redirect_timestamp', \time() );
|
||||
|
||||
\wp_safe_redirect( \admin_url( 'admin.php?page=wpseo_installation_successful' ), 302, 'Yoast SEO Premium' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the workouts submenu page.
|
||||
*
|
||||
* @param array $submenu_pages The Yoast SEO submenu pages.
|
||||
*
|
||||
* @return array the filtered submenu pages.
|
||||
*/
|
||||
public function add_submenu_page( $submenu_pages ) {
|
||||
\add_submenu_page(
|
||||
'',
|
||||
\__( 'Installation Successful', 'wordpress-seo-premium' ),
|
||||
'',
|
||||
'manage_options',
|
||||
'wpseo_installation_successful',
|
||||
[ $this, 'render_page' ]
|
||||
);
|
||||
|
||||
return $submenu_pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue assets on the Thank you page.
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Date is not processed or saved.
|
||||
if ( ! isset( $_GET['page'] ) || $_GET['page'] !== 'wpseo_installation_successful' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
||||
$asset_manager->enqueue_style( 'monorepo' );
|
||||
\wp_enqueue_style( 'yoast-seo-premium-thank-you' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the thank you page.
|
||||
*/
|
||||
public function render_page() {
|
||||
require \WPSEO_PREMIUM_PATH . 'classes/views/thank-you.php';
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WPSEO_Admin_Asset_Manager;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Capability_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Current_Page_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Helpers\Version_Helper;
|
||||
use Yoast\WP\SEO\Presenters\Admin\Notice_Presenter;
|
||||
|
||||
/**
|
||||
* Integration to display a notification urging to update Premium when a new version is available.
|
||||
*/
|
||||
class Update_Premium_Notification implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* The options' helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
private $options_helper;
|
||||
|
||||
/**
|
||||
* The version helper.
|
||||
*
|
||||
* @var Version_Helper
|
||||
*/
|
||||
private $version_helper;
|
||||
|
||||
/**
|
||||
* The capability helper.
|
||||
*
|
||||
* @var Capability_Helper
|
||||
*/
|
||||
private $capability_helper;
|
||||
|
||||
/**
|
||||
* The admin asset manager.
|
||||
*
|
||||
* @var WPSEO_Admin_Asset_Manager
|
||||
*/
|
||||
private $admin_asset_manager;
|
||||
|
||||
/**
|
||||
* The Current_Page_Helper.
|
||||
*
|
||||
* @var Current_Page_Helper
|
||||
*/
|
||||
private $current_page_helper;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Admin_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update_Premium_Notification constructor.
|
||||
*
|
||||
* @param Options_Helper $options_helper The options helper.
|
||||
* @param Version_Helper $version_helper The version helper.
|
||||
* @param Capability_Helper $capability_helper The capability helper.
|
||||
* @param WPSEO_Admin_Asset_Manager $admin_asset_manager The admin asset manager.
|
||||
* @param Current_Page_Helper $current_page_helper The Current_Page_Helper.
|
||||
*/
|
||||
public function __construct(
|
||||
Options_Helper $options_helper,
|
||||
Version_Helper $version_helper,
|
||||
Capability_Helper $capability_helper,
|
||||
WPSEO_Admin_Asset_Manager $admin_asset_manager,
|
||||
Current_Page_Helper $current_page_helper
|
||||
) {
|
||||
$this->options_helper = $options_helper;
|
||||
$this->version_helper = $version_helper;
|
||||
$this->capability_helper = $capability_helper;
|
||||
$this->admin_asset_manager = $admin_asset_manager;
|
||||
$this->current_page_helper = $current_page_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'admin_notices', [ $this, 'maybe_display_notification' ] );
|
||||
\add_action( 'wp_ajax_dismiss_update_premium_notification', [ $this, 'dismiss_update_premium_notification' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a notice if Free is newer than the minimum required version and Premium has an update available.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function maybe_display_notification() {
|
||||
if ( $this->current_page_helper->get_current_admin_page() === 'update.php' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->notice_was_dismissed_on_current_premium_version() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->capability_helper->current_user_can( 'wpseo_manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether Free is set to a version later than the minimum required and a Premium update is a available.
|
||||
if ( $this->version_helper->is_free_upgraded() && $this->version_helper->is_premium_update_available() ) {
|
||||
$this->admin_asset_manager->enqueue_style( 'monorepo' );
|
||||
|
||||
$is_plugins_page = $this->current_page_helper->get_current_admin_page() === 'plugins.php';
|
||||
$content = \sprintf(
|
||||
/* translators: 1: Yoast SEO Premium, 2 and 3: opening and closing anchor tag. */
|
||||
\esc_html__( 'Please %2$supdate %1$s to the latest version%3$s to ensure you can fully use all Premium settings and features.', 'wordpress-seo-premium' ),
|
||||
'Yoast SEO Premium',
|
||||
( $is_plugins_page ) ? '' : '<a href="' . \esc_url( \self_admin_url( 'plugins.php' ) ) . '">',
|
||||
( $is_plugins_page ) ? '' : '</a>'
|
||||
);
|
||||
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- Output of the title escaped in the Notice_Presenter.
|
||||
echo new Notice_Presenter(
|
||||
/* translators: 1: Yoast SEO Premium */
|
||||
\sprintf( \__( 'Update to the latest version of %1$s!', 'wordpress-seo-premium' ), 'Yoast SEO Premium' ),
|
||||
$content,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
'yoast-update-premium-notification'
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
// Enable permanently dismissing the notice.
|
||||
echo "<script>
|
||||
function dismiss_update_premium_notification(){
|
||||
var data = {
|
||||
'action': 'dismiss_update_premium_notification',
|
||||
};
|
||||
|
||||
jQuery.post( ajaxurl, data, function( response ) {
|
||||
jQuery( '#yoast-update-premium-notification' ).hide();
|
||||
});
|
||||
}
|
||||
|
||||
jQuery( document ).ready( function() {
|
||||
jQuery( 'body' ).on( 'click', '#yoast-update-premium-notification .notice-dismiss', function() {
|
||||
dismiss_update_premium_notification();
|
||||
} );
|
||||
} );
|
||||
</script>";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses the old premium notice.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function dismiss_update_premium_notification() {
|
||||
return $this->options_helper->set( 'dismiss_update_premium_notification', WPSEO_PREMIUM_VERSION );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the notification was dismissed in the current Premium version.
|
||||
*
|
||||
* @return bool Whether the notification was dismissed in the current Premium version.
|
||||
*/
|
||||
protected function notice_was_dismissed_on_current_premium_version() {
|
||||
$dismissed_notification_version = $this->options_helper->get( 'dismiss_update_premium_notification', '' );
|
||||
if ( ! empty( $dismissed_notification_version ) ) {
|
||||
return \version_compare( $dismissed_notification_version, WPSEO_PREMIUM_VERSION, '>=' );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WP_User;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Class User_Profile_Integration.
|
||||
*
|
||||
* This class deals with the interface for and the storing of user Schema fields. The output of these fields is done
|
||||
* by this class's sibling frontend integration. Note that the output is done "as is", so all the sanitization happens in
|
||||
* this class.
|
||||
*/
|
||||
class User_Profile_Integration implements Integration_Interface {
|
||||
|
||||
const NONCE_FIELD_ACTION = 'show_user_profile';
|
||||
const NONCE_FIELD_NAME = 'wpseo_premium_user_profile_schema_nonce';
|
||||
|
||||
/**
|
||||
* Holds the schema fields we're adding to the user profile.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $fields;
|
||||
|
||||
/**
|
||||
* User_Profile_Integration constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->set_fields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'show_user_profile', [ $this, 'user_profile' ], 5 );
|
||||
\add_action( 'edit_user_profile', [ $this, 'user_profile' ], 5 );
|
||||
|
||||
\add_action( 'personal_options_update', [ $this, 'process_user_option_update' ] );
|
||||
\add_action( 'edit_user_profile_update', [ $this, 'process_user_option_update' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the conditionals based in which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Admin_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fields and their labels and descriptions.
|
||||
*/
|
||||
private function set_fields() {
|
||||
$this->fields = [
|
||||
'basicInfo' => [
|
||||
'label' => \__( 'Basic information', 'wordpress-seo-premium' ),
|
||||
'type' => 'group',
|
||||
],
|
||||
'honorificPrefix' => [
|
||||
'label' => \__( 'Honorific prefix', 'wordpress-seo-premium' ),
|
||||
/* translators: %1$s is replaced by `<code>`, %2$s by `</code>`. */
|
||||
'description' => \sprintf( \esc_html__( 'E.g. %1$sDr%2$s, %1$sMs%2$s, %1$sMr%2$s', 'wordpress-seo-premium' ), '<code>', '</code>' ),
|
||||
'type' => 'string',
|
||||
],
|
||||
'honorificSuffix' => [
|
||||
'label' => \__( 'Honorific suffix', 'wordpress-seo-premium' ),
|
||||
/* translators: %1$s is replaced by `<code>`, %2$s by `</code>`. */
|
||||
'description' => \sprintf( \esc_html__( 'E.g. %1$sMD%2$s, %1$sPhD%2$s', 'wordpress-seo-premium' ), '<code>', '</code>' ),
|
||||
'type' => 'string',
|
||||
],
|
||||
'birthDate' => [
|
||||
'label' => \__( 'Birth date', 'wordpress-seo-premium' ),
|
||||
/* translators: %1$s is replaced by `<code>`, %2$s by `</code>`. */
|
||||
'description' => \sprintf( \esc_html__( 'Use format: %1$sYYYY-MM-DD%2$s', 'wordpress-seo-premium' ), '<code>', '</code>' ),
|
||||
'type' => 'date',
|
||||
],
|
||||
'gender' => [
|
||||
'label' => \__( 'Gender', 'wordpress-seo-premium' ),
|
||||
/* translators: %1$s is replaced by `<code>`, %2$s by `</code>`. */
|
||||
'description' => \sprintf( \esc_html__( 'E.g. %1$sfemale%2$s, %1$smale%2$s, %1$snon-binary%2$s', 'wordpress-seo-premium' ), '<code>', '</code>' ),
|
||||
'type' => 'string',
|
||||
],
|
||||
'extraInfo' => [
|
||||
'label' => \__( 'Extra information', 'wordpress-seo-premium' ),
|
||||
'type' => 'group',
|
||||
],
|
||||
'award' => [
|
||||
'label' => \__( 'Awards', 'wordpress-seo-premium' ),
|
||||
/* translators: %1$s is replaced by `<code>`, %2$s by `</code>`. */
|
||||
'description' => \sprintf( \esc_html__( 'Comma separated, e.g. %1$sMost likely to succeed - 1991, Smartest in class - 1990%2$s', 'wordpress-seo-premium' ), '<code>', '</code>' ),
|
||||
'type' => 'array',
|
||||
],
|
||||
'knowsAbout' => [
|
||||
'label' => \__( 'Expertise in', 'wordpress-seo-premium' ),
|
||||
/* translators: %1$s is replaced by `<code>`, %2$s by `</code>`. */
|
||||
'description' => \sprintf( \esc_html__( 'Comma separated, e.g. %1$sPHP, JavaScript, 90\'s rock music%2$s', 'wordpress-seo-premium' ), '<code>', '</code>' ),
|
||||
'type' => 'array',
|
||||
],
|
||||
'knowsLanguage' => [
|
||||
'label' => \__( 'Language(s) spoken', 'wordpress-seo-premium' ),
|
||||
/* translators: %1$s is replaced by `<code>`, %2$s by `</code>`. */
|
||||
'description' => \sprintf( \esc_html__( 'Comma separated, e.g. %1$sEnglish, French, Dutch%2$s', 'wordpress-seo-premium' ), '<code>', '</code>' ),
|
||||
'type' => 'array',
|
||||
],
|
||||
'jobInfo' => [
|
||||
'label' => \__( 'Employer information', 'wordpress-seo-premium' ),
|
||||
'type' => 'group',
|
||||
],
|
||||
'jobTitle' => [
|
||||
'label' => \__( 'Job title', 'wordpress-seo-premium' ),
|
||||
/* translators: %1$s is replaced by `<code>`, %2$s by `</code>`. */
|
||||
'description' => \sprintf( \esc_html__( 'E.g. %1$ssoftware engineer%2$s', 'wordpress-seo-premium' ), '<code>', '</code>' ),
|
||||
'type' => 'string',
|
||||
],
|
||||
'worksFor' => [
|
||||
'label' => \__( 'Employer name', 'wordpress-seo-premium' ),
|
||||
/* translators: %1$s is replaced by `<code>`, %2$s by `</code>`. */
|
||||
'description' => \sprintf( \esc_html__( 'E.g. %1$sAcme inc%2$s', 'wordpress-seo-premium' ), '<code>', '</code>' ),
|
||||
'type' => 'string',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a form to add Schema fields to a user.
|
||||
*
|
||||
* @param WP_User $user The current page's user.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function user_profile( $user ) {
|
||||
\wp_nonce_field( self::NONCE_FIELD_ACTION, self::NONCE_FIELD_NAME );
|
||||
|
||||
echo '<h2 id="yoast-seo-schema">', \esc_html__( 'Yoast SEO Schema enhancements', 'wordpress-seo-premium' ), '</h2>';
|
||||
echo '<p>', \esc_html__( 'The info you add below is added to the data Yoast SEO outputs in its schema.org output, for instance when you\'re the author of a page. Please only add the info you feel good sharing publicly.', 'wordpress-seo-premium' ), '</p>';
|
||||
|
||||
$user_schema = \get_user_meta( $user->ID, 'wpseo_user_schema', true );
|
||||
|
||||
echo '<div class="yoast yoast-settings">';
|
||||
foreach ( $this->fields as $key => $field ) {
|
||||
if ( $field['type'] === 'group' ) {
|
||||
echo '<h2>', \esc_html( $field['label'] ), '</h2>';
|
||||
continue;
|
||||
}
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- False positive, $key is set in the code above, not by a user.
|
||||
echo '<label for="wpseo_user_schema_', $key, '">', \esc_html( $field['label'] ), '</label>';
|
||||
$val = '';
|
||||
if ( isset( $user_schema[ $key ] ) ) {
|
||||
$val = $user_schema[ $key ];
|
||||
}
|
||||
if ( $field['type'] === 'array' && \is_array( $val ) ) {
|
||||
$val = \implode( ', ', $val );
|
||||
}
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- False positive, $key is set in the code above, not by a user.
|
||||
echo '<input class="yoast-settings__text regular-text" type="text" id="wpseo_user_schema_', $key, '" name="wpseo_user_schema[', $key, ']" value="', \esc_attr( $val ), '"/>';
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- False positive, $field['description'] is set in the code above, not by a user.
|
||||
echo '<p>', $field['description'], '</p>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the user metas that (might) have been set on the user profile page.
|
||||
*
|
||||
* @param int $user_id User ID of the updated user.
|
||||
*/
|
||||
public function process_user_option_update( $user_id ) {
|
||||
// I'm keeping this to conform to the original logic.
|
||||
if ( ! isset( $_POST[ self::NONCE_FIELD_NAME ] ) || ! \is_string( $_POST[ self::NONCE_FIELD_NAME ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
\check_admin_referer( self::NONCE_FIELD_ACTION, self::NONCE_FIELD_NAME );
|
||||
|
||||
\update_user_meta( $user_id, 'wpseo_user_schema', $this->get_posted_user_fields() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the posted user fields and sanitizes them.
|
||||
*
|
||||
* As we output these values straight from the database both on frontend and backend, this sanitization is quite important.
|
||||
*
|
||||
* @return array The posted user fields, restricted to allowed fields.
|
||||
*/
|
||||
private function get_posted_user_fields() {
|
||||
$user_schema = [];
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified in process_user_option_update.
|
||||
if ( isset( $_POST['wpseo_user_schema'] ) && \is_array( $_POST['wpseo_user_schema'] ) ) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified in process_user_option_update.
|
||||
$user_schema = \array_map( 'sanitize_text_field', \wp_unslash( $_POST['wpseo_user_schema'] ) );
|
||||
}
|
||||
|
||||
foreach ( $this->fields as $key => $object ) {
|
||||
switch ( $object['type'] ) {
|
||||
case 'array':
|
||||
$user_schema[ $key ] = \explode( ',', $user_schema[ $key ] );
|
||||
// Trim each item in the comma separated array.
|
||||
foreach ( $user_schema[ $key ] as $index => $item ) {
|
||||
$user_schema[ $key ][ $index ] = \trim( $item );
|
||||
}
|
||||
// Remove empty items.
|
||||
$user_schema[ $key ] = \array_filter( $user_schema[ $key ] );
|
||||
|
||||
if ( $user_schema[ $key ] === [] || $user_schema[ $key ][0] === '' ) {
|
||||
unset( $user_schema[ $key ] );
|
||||
}
|
||||
break;
|
||||
case 'date':
|
||||
$date = \explode( '-', $user_schema[ $key ] );
|
||||
if ( \count( $date ) !== 3 || ! \checkdate( (int) $date[1], (int) $date[2], (int) $date[0] ) ) {
|
||||
unset( $user_schema[ $key ] );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ( empty( $user_schema[ $key ] ) ) {
|
||||
unset( $user_schema[ $key ] );
|
||||
}
|
||||
// Nothing further to be done for strings.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $user_schema;
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Admin;
|
||||
|
||||
use WPSEO_Premium_Asset_JS_L10n;
|
||||
use WPSEO_Shortlinker;
|
||||
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Helpers\Prominent_Words_Helper;
|
||||
use Yoast\WP\SEO\Repositories\Indexable_Repository;
|
||||
|
||||
/**
|
||||
* WorkoutsIntegration class
|
||||
*/
|
||||
class Workouts_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* The indexable repository.
|
||||
*
|
||||
* @var Indexable_Repository The indexable repository.
|
||||
*/
|
||||
private $indexable_repository;
|
||||
|
||||
/**
|
||||
* The shortlinker.
|
||||
*
|
||||
* @var WPSEO_Shortlinker
|
||||
*/
|
||||
private $shortlinker;
|
||||
|
||||
/**
|
||||
* The options helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
private $options_helper;
|
||||
|
||||
/**
|
||||
* The prominent words helper.
|
||||
*
|
||||
* @var Prominent_Words_Helper
|
||||
*/
|
||||
private $prominent_words_helper;
|
||||
|
||||
/**
|
||||
* The post type helper.
|
||||
*
|
||||
* @var Post_Type_Helper
|
||||
*/
|
||||
private $post_type_helper;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Admin_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Workouts_Integration constructor.
|
||||
*
|
||||
* @param Indexable_Repository $indexable_repository The indexables repository.
|
||||
* @param WPSEO_Shortlinker $shortlinker The shortlinker.
|
||||
* @param Options_Helper $options_helper The options helper.
|
||||
* @param Prominent_Words_Helper $prominent_words_helper The prominent words helper.
|
||||
* @param Post_Type_Helper $post_type_helper The post type helper.
|
||||
*/
|
||||
public function __construct(
|
||||
Indexable_Repository $indexable_repository,
|
||||
WPSEO_Shortlinker $shortlinker,
|
||||
Options_Helper $options_helper,
|
||||
Prominent_Words_Helper $prominent_words_helper,
|
||||
Post_Type_Helper $post_type_helper
|
||||
) {
|
||||
$this->indexable_repository = $indexable_repository;
|
||||
$this->shortlinker = $shortlinker;
|
||||
$this->options_helper = $options_helper;
|
||||
$this->prominent_words_helper = $prominent_words_helper;
|
||||
$this->post_type_helper = $post_type_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the workouts app.
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Date is not processed or saved.
|
||||
if ( ! isset( $_GET['page'] ) || $_GET['page'] !== 'wpseo_workouts' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$workouts_option = $this->options_helper->get( 'workouts' );
|
||||
|
||||
$indexable_ids_in_workouts = [ 0 ];
|
||||
if ( isset( $workouts_option['orphaned']['indexablesByStep'] )
|
||||
&& \is_array( $workouts_option['orphaned']['indexablesByStep'] )
|
||||
&& isset( $workouts_option['cornerstone']['indexablesByStep'] )
|
||||
&& \is_array( $workouts_option['cornerstone']['indexablesByStep'] )
|
||||
) {
|
||||
foreach ( [ 'orphaned', 'cornerstone' ] as $workout ) {
|
||||
foreach ( $workouts_option[ $workout ]['indexablesByStep'] as $step => $indexables ) {
|
||||
if ( $step === 'removed' ) {
|
||||
continue;
|
||||
}
|
||||
foreach ( $indexables as $indexable_id ) {
|
||||
$indexable_ids_in_workouts[] = $indexable_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$orphaned = $this->get_orphaned( $indexable_ids_in_workouts );
|
||||
|
||||
$premium_localization = new WPSEO_Premium_Asset_JS_L10n();
|
||||
$premium_localization->localize_script( 'yoast-seo-premium-workouts' );
|
||||
\wp_enqueue_script( 'yoast-seo-premium-workouts' );
|
||||
\wp_localize_script(
|
||||
'yoast-seo-premium-workouts',
|
||||
'wpseoPremiumWorkoutsData',
|
||||
[
|
||||
'cornerstoneGuide' => $this->shortlinker->build_shortlink( 'https://yoa.st/4el' ),
|
||||
'orphanedGuide' => $this->shortlinker->build_shortlink( 'https://yoa.st/4fa' ),
|
||||
'orphanedUpdateContent' => $this->shortlinker->build_shortlink( 'https://yoa.st/4h9' ),
|
||||
'cornerstoneOn' => $this->options_helper->get( 'enable_cornerstone_content' ),
|
||||
'seoDataOptimizationNeeded' => ! $this->prominent_words_helper->is_indexing_completed(),
|
||||
'orphaned' => $orphaned,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the public indexable sub types.
|
||||
*
|
||||
* @return array The sub types.
|
||||
*/
|
||||
protected function get_public_sub_types() {
|
||||
$object_sub_types = \array_values(
|
||||
\array_merge(
|
||||
$this->post_type_helper->get_public_post_types(),
|
||||
\get_taxonomies( [ 'public' => true ] )
|
||||
)
|
||||
);
|
||||
|
||||
$excluded_post_types = \apply_filters( 'wpseo_indexable_excluded_post_types', [ 'attachment' ] );
|
||||
$object_sub_types = \array_diff( $object_sub_types, $excluded_post_types );
|
||||
return $object_sub_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the orphaned indexables.
|
||||
*
|
||||
* @param array $indexable_ids_in_orphaned_workout The orphaned indexable ids.
|
||||
* @param int $limit The limit.
|
||||
*
|
||||
* @return array The orphaned indexables.
|
||||
*/
|
||||
protected function get_orphaned( array $indexable_ids_in_orphaned_workout, $limit = 10 ) {
|
||||
$orphaned = $this->indexable_repository->query()
|
||||
->where_raw( '( incoming_link_count is NULL OR incoming_link_count < 3 )' )
|
||||
->where_raw( '( post_status = \'publish\' OR post_status IS NULL )' )
|
||||
->where_raw( '( is_robots_noindex = FALSE OR is_robots_noindex IS NULL )' )
|
||||
->where_raw( 'NOT ( object_sub_type = \'page\' AND permalink = %s )', [ \home_url( '/' ) ] )
|
||||
->where_in( 'object_sub_type', $this->get_public_sub_types() )
|
||||
->where_in( 'object_type', [ 'post' ] )
|
||||
->where_not_in( 'id', $indexable_ids_in_orphaned_workout )
|
||||
->order_by_asc( 'created_at' )
|
||||
->limit( $limit )
|
||||
->find_many();
|
||||
$orphaned = \array_map( [ $this->indexable_repository, 'ensure_permalink' ], $orphaned );
|
||||
return $orphaned;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Alerts;
|
||||
|
||||
use Yoast\WP\SEO\Integrations\Alerts\Abstract_Dismissable_Alert;
|
||||
|
||||
/**
|
||||
* Registers a dismissible alert.
|
||||
*
|
||||
* @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Ai_Generator_Tip_Notification extends Abstract_Dismissable_Alert {
|
||||
|
||||
/**
|
||||
* Holds the alert identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $alert_identifier = 'wpseo_premium_ai_generator';
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
<?php
|
||||
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.MaxExceeded
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.Invalid
|
||||
namespace Yoast\WP\SEO\Integrations\Blocks;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\Schema_Blocks_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Schema_Templates\Block_Patterns\Block_Pattern;
|
||||
use Yoast\WP\SEO\Schema_Templates\Block_Patterns\Block_Pattern_Categories;
|
||||
|
||||
/**
|
||||
* Registers the block patterns needed for the Premium Schema blocks.
|
||||
*/
|
||||
class Block_Patterns implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* The block patterns to register.
|
||||
*
|
||||
* @var Block_Pattern[]
|
||||
*/
|
||||
protected $block_patterns = [];
|
||||
|
||||
/**
|
||||
* Block_Patterns integration constructor.
|
||||
*
|
||||
* @param Block_Pattern ...$block_patterns The block patterns to register.
|
||||
*/
|
||||
public function __construct( Block_Pattern ...$block_patterns ) {
|
||||
$this->block_patterns = $block_patterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'admin_init', [ $this, 'register_block_pattern_category' ] );
|
||||
\add_action( 'admin_init', [ $this, 'register_block_patterns' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of conditionals.
|
||||
*
|
||||
* @return array The conditionals.
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Schema_Blocks_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the block patterns with WordPress.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_block_patterns() {
|
||||
foreach ( $this->block_patterns as $block_pattern ) {
|
||||
\register_block_pattern( $block_pattern->get_name(), $block_pattern->get_configuration() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the block pattern category with WordPress.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_block_pattern_category() {
|
||||
$category = [ 'label' => \__( 'Yoast Job Posting', 'wordpress-seo-premium' ) ];
|
||||
\register_block_pattern_category( Block_Pattern_Categories::YOAST_JOB_POSTING, $category );
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Blocks;
|
||||
|
||||
use Yoast\WP\SEO\Integrations\Blocks\Dynamic_Block;
|
||||
|
||||
/**
|
||||
* Estimated_Reading_Time_Block class.
|
||||
*
|
||||
* @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Estimated_Reading_Time_Block extends Dynamic_Block {
|
||||
|
||||
/**
|
||||
* The name of the block.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $block_name = 'estimated-reading-time';
|
||||
|
||||
/**
|
||||
* Holds the clock icon HTML.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $clock_icon = '<span class="yoast-reading-time__icon"><svg aria-hidden="true" focusable="false" data-icon="clock" width="20" height="20" fill="none" stroke="currentColor" style="display:inline-block;vertical-align:-0.1em" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg></span><span class="yoast-reading-time__spacer" style="display:inline-block;width:1em"></span>';
|
||||
|
||||
/**
|
||||
* The editor script for the block.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $script = 'wp-seo-premium-dynamic-blocks';
|
||||
|
||||
/**
|
||||
* Registers the block.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_block() {
|
||||
\register_block_type(
|
||||
'yoast-seo/' . $this->block_name,
|
||||
[
|
||||
'editor_script' => $this->script,
|
||||
'render_callback' => [ $this, 'present' ],
|
||||
'attributes' => [
|
||||
'className' => [
|
||||
'default' => '',
|
||||
'type' => 'string',
|
||||
],
|
||||
'estimatedReadingTime' => [
|
||||
'type' => 'number',
|
||||
'default' => 0,
|
||||
],
|
||||
'descriptiveText' => [
|
||||
'type' => 'string',
|
||||
'default' => \__( 'Estimated reading time:', 'wordpress-seo-premium' ) . ' ',
|
||||
],
|
||||
'showDescriptiveText' => [
|
||||
'type' => 'boolean',
|
||||
'default' => true,
|
||||
],
|
||||
'showIcon' => [
|
||||
'type' => 'boolean',
|
||||
'default' => true,
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Presents the block output.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $content The content.
|
||||
*
|
||||
* @return string The block output.
|
||||
*/
|
||||
public function present( $attributes, $content = '' ) {
|
||||
|
||||
$content = \preg_replace(
|
||||
'/<span class="yoast-reading-time__time-unit">.*<\/span>/',
|
||||
'<span class="yoast-reading-time__time-unit"> ' . \sprintf( \_n( 'minute', 'minutes', $attributes['estimatedReadingTime'], 'wordpress-seo-premium' ), $attributes['estimatedReadingTime'] ) . '</span>',
|
||||
$content,
|
||||
1
|
||||
);
|
||||
if ( $attributes['showIcon'] ) {
|
||||
// Replace 15.7 icon placeholder.
|
||||
$content = \preg_replace(
|
||||
'/ICON_PLACEHOLDER/',
|
||||
$this->clock_icon,
|
||||
$content,
|
||||
1
|
||||
);
|
||||
|
||||
// Replace the 15.8+ icon placeholder.
|
||||
return \preg_replace(
|
||||
'/<span class="yoast-reading-time__icon"><\/span>/',
|
||||
$this->clock_icon,
|
||||
$content,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.MaxExceeded
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.Invalid
|
||||
namespace Yoast\WP\SEO\Integrations\Blocks;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\Schema_Blocks_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Adds the block categories for the Jobs Posting block.
|
||||
*/
|
||||
class Job_Posting_Block implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Registers the hooks.
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'block_categories', [ $this, 'add_block_categories' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of conditionals.
|
||||
*
|
||||
* @return array The conditionals.
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [
|
||||
Schema_Blocks_Conditional::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Yoast block categories.
|
||||
*
|
||||
* @param array $categories The categories to filter.
|
||||
*
|
||||
* @return array The filtered categories.
|
||||
*/
|
||||
public function add_block_categories( $categories ) {
|
||||
$categories[] = [
|
||||
'slug' => 'yoast-required-job-blocks',
|
||||
'title' => \__( 'Required Job Posting Blocks', 'wordpress-seo-premium' ),
|
||||
];
|
||||
|
||||
$categories[] = [
|
||||
'slug' => 'yoast-recommended-job-blocks',
|
||||
'title' => \__( 'Recommended Job Posting Blocks', 'wordpress-seo-premium' ),
|
||||
];
|
||||
|
||||
return $categories;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Blocks;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Related content block class.
|
||||
*/
|
||||
class Related_Links_Block implements Integration_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\register_block_type( 'yoast-seo/related-links', [ 'editor_script' => 'wp-seo-premium-blocks' ] );
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Integrations\Blocks;
|
||||
|
||||
use WPSEO_Admin_Asset_Manager;
|
||||
use Yoast\WP\SEO\Conditionals\Schema_Blocks_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Loads the Premium schema block templates into Gutenberg.
|
||||
*/
|
||||
class Schema_Blocks implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Represents the asset manager.
|
||||
*
|
||||
* @var WPSEO_Admin_Asset_Manager
|
||||
*/
|
||||
protected $asset_manager;
|
||||
|
||||
/**
|
||||
* Schema_Blocks constructor.
|
||||
*
|
||||
* @param WPSEO_Admin_Asset_Manager $asset_manager The asset manager.
|
||||
*/
|
||||
public function __construct( WPSEO_Admin_Asset_Manager $asset_manager ) {
|
||||
$this->asset_manager = $asset_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of conditionals.
|
||||
*
|
||||
* @return array The conditionals.
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [
|
||||
Schema_Blocks_Conditional::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'wpseo_load_schema_templates', [ $this, 'add_premium_templates' ] );
|
||||
\add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the Premium structured data blocks templates.
|
||||
*
|
||||
* @param array $templates The templates from Yoast SEO.
|
||||
*
|
||||
* @return array All the templates that should be loaded.
|
||||
*/
|
||||
public function add_premium_templates( $templates ) {
|
||||
$premium_schema_templates_path = \WPSEO_PREMIUM_PATH . 'src/schema-templates/';
|
||||
|
||||
$premium_templates = \glob( $premium_schema_templates_path . '*.php' );
|
||||
|
||||
return \array_merge( $templates, $premium_templates );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the schema blocks css file.
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
\wp_enqueue_script( 'wp-seo-premium-schema-blocks' );
|
||||
$this->asset_manager->enqueue_style( 'premium-schema-blocks' );
|
||||
}
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations;
|
||||
|
||||
use wpdb;
|
||||
use Yoast\WP\Lib\Model;
|
||||
use Yoast\WP\SEO\Analytics\Domain\To_Be_Cleaned_Indexable_Bucket;
|
||||
use Yoast\WP\SEO\Analytics\Domain\To_Be_Cleaned_Indexable_Count;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Repositories\Indexable_Cleanup_Repository;
|
||||
|
||||
/**
|
||||
* Adds cleanup hooks.
|
||||
*/
|
||||
class Cleanup_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* The indexable cleanup repository.
|
||||
*
|
||||
* @var Indexable_Cleanup_Repository
|
||||
*/
|
||||
private $indexable_cleanup_repository;
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @param Indexable_Cleanup_Repository $indexable_cleanup_repository The indexable cleanup repository.
|
||||
*/
|
||||
public function __construct( Indexable_Cleanup_Repository $indexable_cleanup_repository ) {
|
||||
$this->indexable_cleanup_repository = $indexable_cleanup_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the conditionals based in which this loadable should be active.
|
||||
*
|
||||
* @return array The array of conditionals.
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'wpseo_cleanup_tasks', [ $this, 'add_cleanup_tasks' ] );
|
||||
\add_action( 'wpseo_add_cleanup_counts_to_indexable_bucket', [ $this, 'add_cleanup_counts' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds cleanup tasks for the cleanup integration.
|
||||
*
|
||||
* @param array $tasks Array of tasks to be added.
|
||||
*
|
||||
* @return array An associative array of tasks to be added to the cleanup integration.
|
||||
*/
|
||||
public function add_cleanup_tasks( $tasks ) {
|
||||
return \array_merge(
|
||||
$tasks,
|
||||
[
|
||||
'clean_orphaned_indexables_prominent_words' => function ( $limit ) {
|
||||
return $this->cleanup_orphaned_from_table( 'Prominent_Words', 'indexable_id', $limit );
|
||||
},
|
||||
'clean_old_prominent_word_entries' => function ( $limit ) {
|
||||
return $this->cleanup_old_prominent_words( $limit );
|
||||
},
|
||||
'clean_old_prominent_word_version_numbers' => function ( $limit ) {
|
||||
return $this->cleanup_old_prominent_word_version_numbers( $limit );
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds cleanup counts to the data bucket object.
|
||||
*
|
||||
* @param To_Be_Cleaned_Indexable_Bucket $to_be_cleaned_indexable_bucket The bucket with current indexable count data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_cleanup_counts( To_Be_Cleaned_Indexable_Bucket $to_be_cleaned_indexable_bucket ): void {
|
||||
$to_be_cleaned_indexable_bucket->add_to_be_cleaned_indexable_count( new To_Be_Cleaned_Indexable_Count( 'orphaned_indexables_prominent_words', $this->indexable_cleanup_repository->count_orphaned_from_table( 'Prominent_Words', 'indexable_id' ) ) );
|
||||
$to_be_cleaned_indexable_bucket->add_to_be_cleaned_indexable_count( new To_Be_Cleaned_Indexable_Count( 'orphaned_prominent_word_entries', $this->count_old_prominent_words() ) );
|
||||
$to_be_cleaned_indexable_bucket->add_to_be_cleaned_indexable_count( new To_Be_Cleaned_Indexable_Count( 'orphaned_prominent_word_version_numbers', $this->count_old_prominent_word_version_numbers() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans orphaned rows from a yoast table.
|
||||
*
|
||||
* @param string $table The table to cleanup.
|
||||
* @param string $column The table column the cleanup will rely on.
|
||||
* @param int $limit The limit we'll apply to the queries.
|
||||
*
|
||||
* @return int The number of deleted rows.
|
||||
*/
|
||||
public function cleanup_orphaned_from_table( $table, $column, $limit ) {
|
||||
global $wpdb;
|
||||
|
||||
$table = Model::get_table_name( $table );
|
||||
$indexable_table = Model::get_table_name( 'Indexable' );
|
||||
|
||||
// Warning: If this query is changed, make sure to update the query in cleanup_orphaned_from_table in Free as well.
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: There is no unescaped user input.
|
||||
$query = $wpdb->prepare(
|
||||
"
|
||||
SELECT table_to_clean.{$column}
|
||||
FROM {$table} table_to_clean
|
||||
LEFT JOIN {$indexable_table} AS indexable_table
|
||||
ON table_to_clean.{$column} = indexable_table.id
|
||||
WHERE indexable_table.id IS NULL
|
||||
AND table_to_clean.{$column} IS NOT NULL
|
||||
LIMIT %d",
|
||||
$limit
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: Already prepared.
|
||||
$orphans = $wpdb->get_col( $query );
|
||||
|
||||
if ( empty( $orphans ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: Already prepared.
|
||||
return \intval( $wpdb->query( "DELETE FROM $table WHERE {$column} IN( " . \implode( ',', $orphans ) . ' ) ' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up old style prominent words from the database.
|
||||
*
|
||||
* @param int $limit The maximum amount of old prominent words to clean up in one go. Defaults to 1000.
|
||||
*
|
||||
* @return int The number of deleted rows.
|
||||
*/
|
||||
public function cleanup_old_prominent_words( $limit = 1000 ) {
|
||||
global $wpdb;
|
||||
|
||||
$taxonomy_ids = $this->retrieve_prominent_word_taxonomies( $wpdb, $limit );
|
||||
|
||||
if ( \count( $taxonomy_ids ) === 0 ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$nr_of_deleted_rows = $this->delete_prominent_word_taxonomies_and_terms( $wpdb, $taxonomy_ids );
|
||||
|
||||
if ( $nr_of_deleted_rows === false ) {
|
||||
// Failed query.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $nr_of_deleted_rows;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||
|
||||
/**
|
||||
* Count up old style prominent words from the database.
|
||||
*
|
||||
* @return int The number of old prominent word rows.
|
||||
*/
|
||||
public function count_old_prominent_words() {
|
||||
global $wpdb;
|
||||
|
||||
$query = $wpdb->prepare(
|
||||
"SELECT count(term_taxonomy_id) FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s",
|
||||
[ 'yst_prominent_words' ]
|
||||
);
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: Already prepared.
|
||||
return $wpdb->get_col( $query )[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of prominent word taxonomy IDs.
|
||||
*
|
||||
* @param wpdb $wpdb The WordPress database object.
|
||||
* @param int $limit The maximum amount of prominent word taxonomies to retrieve.
|
||||
*
|
||||
* @return string[] A list of prominent word taxonomy IDs (of size 'limit').
|
||||
*/
|
||||
protected function retrieve_prominent_word_taxonomies( $wpdb, $limit ) {
|
||||
return $wpdb->get_col(
|
||||
$wpdb->prepare(
|
||||
"SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s LIMIT %d",
|
||||
[ 'yst_prominent_words', $limit ]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given list of taxonomies and their terms.
|
||||
*
|
||||
* @param wpdb $wpdb The WordPress database object.
|
||||
* @param string[] $taxonomy_ids The IDs of the taxonomies to remove and their corresponding terms.
|
||||
*
|
||||
* @return bool|int `false` if the query failed, the amount of rows deleted otherwise.
|
||||
*/
|
||||
protected function delete_prominent_word_taxonomies_and_terms( $wpdb, $taxonomy_ids ) {
|
||||
return $wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"DELETE t, tr, tt FROM {$wpdb->term_taxonomy} tt
|
||||
LEFT JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
|
||||
LEFT JOIN {$wpdb->term_relationships} tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
|
||||
WHERE tt.term_taxonomy_id IN ( " . \implode( ', ', \array_fill( 0, \count( $taxonomy_ids ), '%s' ) ) . ' )',
|
||||
$taxonomy_ids
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the old prominent word versions from the postmeta table in the database.
|
||||
*
|
||||
* @param int $limit The maximum number of prominent word version numbers to clean in one go.
|
||||
*
|
||||
* @return bool|int The number of cleaned up prominent word version numbers, or `false` if the query failed.
|
||||
*/
|
||||
protected function cleanup_old_prominent_word_version_numbers( $limit ) {
|
||||
global $wpdb;
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: There is no unescaped user input.
|
||||
$query = $wpdb->prepare(
|
||||
"DELETE FROM {$wpdb->postmeta} WHERE meta_key = %s LIMIT %d",
|
||||
[ '_yst_prominent_words_version', $limit ]
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: Already prepared.
|
||||
return $wpdb->query( $query );
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts up the old prominent word versions from the postmeta table in the database.
|
||||
*
|
||||
* @return bool|int The number of prominent word version numbers.
|
||||
*/
|
||||
protected function count_old_prominent_word_version_numbers() {
|
||||
global $wpdb;
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: There is no unescaped user input.
|
||||
$query = $wpdb->prepare(
|
||||
"SELECT count(*) FROM {$wpdb->postmeta} WHERE meta_key = %s",
|
||||
[ '_yst_prominent_words_version' ]
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: Already prepared.
|
||||
return $wpdb->get_col( $query )[0];
|
||||
}
|
||||
|
||||
// phpcs:enable
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Front_End;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\Robots_Txt_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Robots_Txt_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Handles adding the rules to `robots.txt`.
|
||||
*/
|
||||
class Robots_Txt_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Holds the options helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
protected $options_helper;
|
||||
|
||||
/**
|
||||
* Instantiates the `robots.txt` integration.
|
||||
*
|
||||
* @param Options_Helper $options_helper Options helper.
|
||||
*/
|
||||
public function __construct( Options_Helper $options_helper ) {
|
||||
$this->options_helper = $options_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the conditionals based in which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Robots_Txt_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
if ( \is_multisite() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->options_helper->get( 'deny_ccbot_crawling' ) ) {
|
||||
\add_action( 'Yoast\WP\SEO\register_robots_rules', [ $this, 'add_disallow_ccbot' ], 10, 1 );
|
||||
}
|
||||
if ( $this->options_helper->get( 'deny_google_extended_crawling' ) ) {
|
||||
\add_action( 'Yoast\WP\SEO\register_robots_rules', [ $this, 'add_disallow_google_extended_bot' ], 10, 1 );
|
||||
}
|
||||
if ( $this->options_helper->get( 'deny_gptbot_crawling' ) ) {
|
||||
\add_action( 'Yoast\WP\SEO\register_robots_rules', [ $this, 'add_disallow_gptbot' ], 10, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a disallow rule for Common Crawl CCBot agents to `robots.txt`.
|
||||
*
|
||||
* @param Robots_Txt_Helper $robots_txt_helper The Robots_Txt_Helper.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_disallow_ccbot( Robots_Txt_Helper $robots_txt_helper ) {
|
||||
$robots_txt_helper->add_disallow( 'CCBot', '/' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a disallow rule for Google-Extended agents to `robots.txt`.
|
||||
*
|
||||
* @param Robots_Txt_Helper $robots_txt_helper The Robots_Txt_Helper.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_disallow_google_extended_bot( Robots_Txt_Helper $robots_txt_helper ) {
|
||||
$robots_txt_helper->add_disallow( 'Google-Extended', '/' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a disallow rule for OpenAI GPTBot agents to `robots.txt`.
|
||||
*
|
||||
* @param Robots_Txt_Helper $robots_txt_helper The Robots_Txt_Helper.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_disallow_gptbot( Robots_Txt_Helper $robots_txt_helper ) {
|
||||
$robots_txt_helper->add_disallow( 'GPTBot', '/' );
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations;
|
||||
|
||||
use WP_Admin_Bar;
|
||||
use WPSEO_Metabox_Analysis_Readability;
|
||||
use WPSEO_Metabox_Analysis_SEO;
|
||||
use WPSEO_Options;
|
||||
use Yoast\WP\SEO\Conditionals\Front_End_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Robots_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Frontend_Inspector class
|
||||
*/
|
||||
class Frontend_Inspector implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* The identifier used for the frontend inspector submenu.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const FRONTEND_INSPECTOR_SUBMENU_IDENTIFIER = 'wpseo-frontend-inspector';
|
||||
|
||||
/**
|
||||
* Holds the Robots_Helper.
|
||||
*
|
||||
* @var Robots_Helper
|
||||
*/
|
||||
protected $robots_helper;
|
||||
|
||||
/**
|
||||
* Constructs a Frontend_Inspector.
|
||||
*
|
||||
* @param Robots_Helper $robots_helper The Robots_Helper.
|
||||
*/
|
||||
public function __construct( Robots_Helper $robots_helper ) {
|
||||
$this->robots_helper = $robots_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Front_End_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ], 11 );
|
||||
\add_action( 'wpseo_add_adminbar_submenu', [ $this, 'add_frontend_inspector_submenu' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the frontend inspector submenu.
|
||||
*
|
||||
* @param WP_Admin_Bar $wp_admin_bar The admin bar.
|
||||
* @param string $menu_identifier The menu identifier.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_frontend_inspector_submenu( WP_Admin_Bar $wp_admin_bar, $menu_identifier ) {
|
||||
if ( ! \is_admin() ) {
|
||||
$menu_args = [
|
||||
'parent' => $menu_identifier,
|
||||
'id' => self::FRONTEND_INSPECTOR_SUBMENU_IDENTIFIER,
|
||||
'title' => \sprintf(
|
||||
'%1$s <span class="yoast-badge yoast-beta-badge">%2$s</span>',
|
||||
\__( 'Front-end SEO inspector', 'wordpress-seo-premium' ),
|
||||
\__( 'Beta', 'wordpress-seo-premium' )
|
||||
),
|
||||
'href' => '#wpseo-frontend-inspector',
|
||||
'meta' => [
|
||||
'tabindex' => '0',
|
||||
],
|
||||
];
|
||||
$wp_admin_bar->add_menu( $menu_args );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the workouts app.
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
if ( ! \is_admin_bar_showing() || ! WPSEO_Options::get( 'enable_admin_bar_menu' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the current user can't write posts, this is all of no use, so let's not output an admin menu.
|
||||
if ( ! \current_user_can( 'edit_posts' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$analysis_seo = new WPSEO_Metabox_Analysis_SEO();
|
||||
$analysis_readability = new WPSEO_Metabox_Analysis_Readability();
|
||||
$current_page_meta = \YoastSEO()->meta->for_current_page();
|
||||
$indexable = $current_page_meta->indexable;
|
||||
$page_type = $current_page_meta->page_type;
|
||||
|
||||
$is_seo_analysis_active = $analysis_seo->is_enabled();
|
||||
$is_readability_analysis_active = $analysis_readability->is_enabled();
|
||||
$display_metabox = true;
|
||||
|
||||
switch ( $page_type ) {
|
||||
case 'Home_Page':
|
||||
case 'Post_Type_Archive':
|
||||
case 'Date_Archive':
|
||||
case 'Error_Page':
|
||||
case 'Fallback':
|
||||
case 'Search_Result_Page':
|
||||
break;
|
||||
case 'Static_Home_Page':
|
||||
case 'Static_Posts_Page':
|
||||
case 'Post_Type':
|
||||
$display_metabox = WPSEO_Options::get( 'display-metabox-pt-' . $indexable->object_sub_type );
|
||||
break;
|
||||
case 'Term_Archive':
|
||||
$display_metabox = WPSEO_Options::get( 'display-metabox-tax-' . $indexable->object_sub_type );
|
||||
break;
|
||||
case 'Author_Archive':
|
||||
$display_metabox = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ! $display_metabox ) {
|
||||
$is_seo_analysis_active = false;
|
||||
$is_readability_analysis_active = false;
|
||||
}
|
||||
|
||||
\wp_enqueue_script( 'yoast-seo-premium-frontend-inspector' );
|
||||
\wp_localize_script(
|
||||
'yoast-seo-premium-frontend-inspector',
|
||||
'wpseoScriptData',
|
||||
[
|
||||
'frontendInspector' => [
|
||||
'isIndexable' => $this->robots_helper->is_indexable( $indexable ),
|
||||
'indexable' => [
|
||||
'is_robots_noindex' => $indexable->is_robots_noindex,
|
||||
'primary_focus_keyword' => $indexable->primary_focus_keyword,
|
||||
'primary_focus_keyword_score' => $indexable->primary_focus_keyword_score,
|
||||
'readability_score' => $indexable->readability_score,
|
||||
],
|
||||
'contentAnalysisActive' => $is_readability_analysis_active,
|
||||
'keywordAnalysisActive' => $is_seo_analysis_active,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations;
|
||||
|
||||
use WP_Post;
|
||||
use WPSEO_Remote_Request;
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Request_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Index_Now_Ping class.
|
||||
*/
|
||||
class Index_Now_Ping implements Integration_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* The options helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
private $options_helper;
|
||||
|
||||
/**
|
||||
* The request helper.
|
||||
*
|
||||
* @var Request_Helper
|
||||
*/
|
||||
private $request_helper;
|
||||
|
||||
/**
|
||||
* The post type helper.
|
||||
*
|
||||
* @var Post_Type_Helper
|
||||
*/
|
||||
private $post_type_helper;
|
||||
|
||||
/**
|
||||
* The IndexNow endpoint URL we're using.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $endpoint;
|
||||
|
||||
/**
|
||||
* Index_Now_Ping integration constructor.
|
||||
*
|
||||
* @param Options_Helper $options_helper The option helper.
|
||||
* @param Request_Helper $request_helper The request helper.
|
||||
* @param Post_Type_Helper $post_type_helper The post type helper.
|
||||
*/
|
||||
public function __construct(
|
||||
Options_Helper $options_helper,
|
||||
Request_Helper $request_helper,
|
||||
Post_Type_Helper $post_type_helper
|
||||
) {
|
||||
$this->options_helper = $options_helper;
|
||||
$this->request_helper = $request_helper;
|
||||
$this->post_type_helper = $post_type_helper;
|
||||
|
||||
/**
|
||||
* Filter: 'Yoast\WP\SEO\indexnow_endpoint' - Allow changing the Indexnow endpoint.
|
||||
*
|
||||
* Note: This is a Premium plugin-only hook.
|
||||
*
|
||||
* @since 18.8
|
||||
*
|
||||
* @param string $endpoint The IndexNow endpoint URL.
|
||||
*/
|
||||
$this->endpoint = \apply_filters( 'Yoast\WP\SEO\indexnow_endpoint', 'https://api.indexnow.org/indexnow' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the hooks this integration acts on.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
if ( $this->options_helper->get( 'enable_index_now' ) === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( \wp_get_environment_type() !== 'production' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Please note that the name transition_post_status is misleading.
|
||||
* The hook does not only fire on a post status transition but also when a post is updated
|
||||
* while the status is not changed from one to another at all.
|
||||
*/
|
||||
\add_action( 'transition_post_status', [ $this, 'ping_index_now' ], 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Pings IndexNow for changes.
|
||||
*
|
||||
* @param string $new_status The new status for the post.
|
||||
* @param string $old_status The old status for the post.
|
||||
* @param WP_Post $post The post.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ping_index_now( $new_status, $old_status, $post ) {
|
||||
if ( $new_status !== 'publish' && $old_status !== 'publish' ) {
|
||||
// If we're not transitioning to or from a published status, do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
// The block editor saves published posts twice, we want to ping only on the first request.
|
||||
if ( $new_status === 'publish' && $this->request_helper->is_rest_request() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $post instanceof WP_Post ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! \in_array( $post->post_type, $this->post_type_helper->get_accessible_post_types(), true )
|
||||
|| ! $this->post_type_helper->is_indexable( $post->post_type ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bail out if last ping was less than two minutes ago.
|
||||
$indexnow_last_ping = \get_post_meta( $post->ID, '_yoast_indexnow_last_ping', true );
|
||||
if ( \is_numeric( $indexnow_last_ping ) && \abs( \time() - ( (int) $indexnow_last_ping ) ) < 120 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$key = $this->options_helper->get( 'index_now_key' );
|
||||
$permalink = $this->get_permalink( $post );
|
||||
$urls = [ $permalink ];
|
||||
|
||||
if ( $post->post_type === 'post' ) {
|
||||
$urls[] = \get_home_url();
|
||||
}
|
||||
|
||||
if ( ! empty( \get_option( 'permalink_structure' ) ) ) {
|
||||
$key_location = \trailingslashit( \get_home_url() ) . 'yoast-index-now-' . $key . '.txt';
|
||||
}
|
||||
else {
|
||||
$key_location = \add_query_arg( 'yoast_index_now_key', $key, \trailingslashit( \get_home_url() ) );
|
||||
}
|
||||
|
||||
$content = (object) [
|
||||
'host' => \wp_parse_url( \get_home_url(), \PHP_URL_HOST ),
|
||||
'key' => $key,
|
||||
'keyLocation' => $key_location,
|
||||
'urlList' => $urls,
|
||||
];
|
||||
|
||||
// Set a 'content-type' header of 'application/json' and an identifying source header.
|
||||
// The "false" on the end of the x-source-info header determines whether this is a manual submission or not.
|
||||
$request_args = [
|
||||
'headers' => [
|
||||
'content-type' => 'application/json; charset=utf-8',
|
||||
'x-source-info' => 'https://yoast.com/wordpress/plugins/seo-premium/' . \WPSEO_PREMIUM_VERSION . '/false',
|
||||
],
|
||||
];
|
||||
|
||||
$request = new WPSEO_Remote_Request( $this->endpoint, $request_args );
|
||||
// phpcs:ignore Yoast.Yoast.AlternativeFunctions.json_encode_wp_json_encode -- This is being sent to an API, not displayed.
|
||||
$request->set_body( \wp_json_encode( $content ) );
|
||||
$request->send();
|
||||
|
||||
\update_post_meta( $post->ID, '_yoast_indexnow_last_ping', \time() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the (former) permalink for a post.
|
||||
*
|
||||
* @param WP_Post $post Post object.
|
||||
*
|
||||
* @return string Permalink.
|
||||
*/
|
||||
private function get_permalink( WP_Post $post ) {
|
||||
if ( \in_array( $post->post_status, [ 'trash', 'draft', 'pending', 'future' ], true ) ) {
|
||||
if ( $post->post_status === 'trash' ) {
|
||||
// Fix the post_name.
|
||||
$post->post_name = \preg_replace( '/__trashed$/', '', $post->post_name );
|
||||
}
|
||||
// Force post_status to publish briefly, so we get the correct URL.
|
||||
$post->post_status = 'publish';
|
||||
}
|
||||
|
||||
return \get_permalink( $post );
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations;
|
||||
|
||||
use Yoast\WP\SEO\Actions\Indexing\Indexation_Action_Interface;
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Actions\Prominent_Words\Content_Action;
|
||||
|
||||
/**
|
||||
* Adds prominent words to the missing indexables bucket.
|
||||
*
|
||||
* @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Missing_Indexables_Count_Integration implements Integration_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* The content indexable action.
|
||||
*
|
||||
* @var Content_Action
|
||||
*/
|
||||
private $content_action;
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @param Content_Action $content_action The action.
|
||||
*/
|
||||
public function __construct( Content_Action $content_action ) {
|
||||
$this->content_action = $content_action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers hooks with WordPress.
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'wpseo_indexable_collector_add_indexation_actions', [ $this, 'add_index_action' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Content_Action to the indexable collector.
|
||||
*
|
||||
* @param array<Indexation_Action_Interface> $indexation_actions The current indexation actions.
|
||||
* @return array<Indexation_Action_Interface>
|
||||
*/
|
||||
public function add_index_action( $indexation_actions ) {
|
||||
$indexation_actions[] = $this->content_action;
|
||||
return $indexation_actions;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations;
|
||||
|
||||
/**
|
||||
* Class OpenGraph_Author_Archive.
|
||||
*/
|
||||
class OpenGraph_Author_Archive extends Abstract_OpenGraph_Integration {
|
||||
|
||||
/**
|
||||
* The name of the social title option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_TITLE = 'social-title-author-wpseo';
|
||||
|
||||
/**
|
||||
* The name of the social description option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_DESCRIPTION = 'social-description-author-wpseo';
|
||||
|
||||
/**
|
||||
* The name of the social image ID option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_IMAGE_ID = 'social-image-id-author-wpseo';
|
||||
|
||||
/**
|
||||
* The name of the social image URL option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_IMAGE = 'social-image-url-author-wpseo';
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_title_user', [ $this, 'filter_title' ] );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_description_user', [ $this, 'filter_description' ] );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_image_id_user', [ $this, 'filter_image_id' ] );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_image_user', [ $this, 'filter_image' ] );
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations;
|
||||
|
||||
/**
|
||||
* Class OpenGraph_Date_Archive.
|
||||
*/
|
||||
class OpenGraph_Date_Archive extends Abstract_OpenGraph_Integration {
|
||||
|
||||
/**
|
||||
* The name of the social title option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_TITLE = 'social-title-archive-wpseo';
|
||||
|
||||
/**
|
||||
* The name of the social description option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_DESCRIPTION = 'social-description-archive-wpseo';
|
||||
|
||||
/**
|
||||
* The name of the social image ID option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_IMAGE_ID = 'social-image-id-archive-wpseo';
|
||||
|
||||
/**
|
||||
* The name of the social image URL option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_IMAGE = 'social-image-url-archive-wpseo';
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_title_date-archive', [ $this, 'filter_title' ] );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_description_date-archive', [ $this, 'filter_description' ] );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_image_id_date-archive', [ $this, 'filter_image_id' ] );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_image_date-archive', [ $this, 'filter_image' ] );
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations;
|
||||
|
||||
/**
|
||||
* Class OpenGraph_Post_Type.
|
||||
*/
|
||||
class OpenGraph_Post_Type extends Abstract_OpenGraph_Integration {
|
||||
|
||||
/**
|
||||
* The prefix for the social title option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_TITLE = 'social-title-';
|
||||
|
||||
/**
|
||||
* The prefix for the social description option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_DESCRIPTION = 'social-description-';
|
||||
|
||||
/**
|
||||
* The prefix for the social image ID option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_IMAGE_ID = 'social-image-id-';
|
||||
|
||||
/**
|
||||
* The prefix for the social image URL option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_IMAGE = 'social-image-url-';
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_title_post', [ $this, 'filter_title_for_subtype' ], 10, 2 );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_description_post', [ $this, 'filter_description_for_subtype' ], 10, 2 );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_image_id_post', [ $this, 'filter_image_id_for_subtype' ], 10, 2 );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_image_post', [ $this, 'filter_image_for_subtype' ], 10, 2 );
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations;
|
||||
|
||||
/**
|
||||
* Class OpenGraph_PostType_Archive.
|
||||
*/
|
||||
class OpenGraph_PostType_Archive extends Abstract_OpenGraph_Integration {
|
||||
|
||||
/**
|
||||
* The prefix for the social title option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_TITLE = 'social-title-ptarchive-';
|
||||
|
||||
/**
|
||||
* The prefix for the social description option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_DESCRIPTION = 'social-description-ptarchive-';
|
||||
|
||||
/**
|
||||
* The prefix for the social image ID option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_IMAGE_ID = 'social-image-id-ptarchive-';
|
||||
|
||||
/**
|
||||
* The prefix for the social image URL option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_IMAGE = 'social-image-url-ptarchive-';
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_title_post-type-archive', [ $this, 'filter_title_for_subtype' ], 10, 2 );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_description_post-type-archive', [ $this, 'filter_description_for_subtype' ], 10, 2 );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_image_id_post-type-archive', [ $this, 'filter_image_id_for_subtype' ], 10, 2 );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_image_post-type-archive', [ $this, 'filter_image_for_subtype' ], 10, 2 );
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations;
|
||||
|
||||
/**
|
||||
* Class OpenGraph_Term_Archive.
|
||||
*/
|
||||
class OpenGraph_Term_Archive extends Abstract_OpenGraph_Integration {
|
||||
|
||||
/**
|
||||
* The prefix for the social title option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_TITLE = 'social-title-tax-';
|
||||
|
||||
/**
|
||||
* The prefix for the social description option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_DESCRIPTION = 'social-description-tax-';
|
||||
|
||||
/**
|
||||
* The prefix for the social image ID option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_IMAGE_ID = 'social-image-id-tax-';
|
||||
|
||||
/**
|
||||
* The prefix for the social image URL option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TITLES_KEY_IMAGE = 'social-image-url-tax-';
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_title_term', [ $this, 'filter_title_for_subtype' ], 10, 2 );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_description_term', [ $this, 'filter_description_for_subtype' ], 10, 2 );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_image_id_term', [ $this, 'filter_image_id_for_subtype' ], 10, 2 );
|
||||
\add_filter( 'Yoast\WP\SEO\open_graph_image_term', [ $this, 'filter_image_for_subtype' ], 10, 2 );
|
||||
}
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\Front_End_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Indexable_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Repositories\Indexable_Repository;
|
||||
use function get_post;
|
||||
|
||||
/**
|
||||
* Integration to add Publishing Principles to the Schema.
|
||||
*
|
||||
* @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Publishing_Principles_Schema_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Constant holding the mapping between database option and actual schema name.
|
||||
*/
|
||||
public const PRINCIPLES_MAPPING = [
|
||||
[ 'publishing_principles_id', 'publishingPrinciples' ],
|
||||
[ 'ownership_funding_info_id', 'ownershipFundingInfo' ],
|
||||
[ 'actionable_feedback_policy_id', 'actionableFeedbackPolicy' ],
|
||||
[ 'corrections_policy_id', 'correctionsPolicy' ],
|
||||
[ 'ethics_policy_id', 'ethicsPolicy' ],
|
||||
[ 'diversity_policy_id', 'diversityPolicy' ],
|
||||
[ 'diversity_staffing_report_id', 'diversityStaffingReport' ],
|
||||
];
|
||||
|
||||
/**
|
||||
* The indexable helper.
|
||||
*
|
||||
* @var Indexable_Helper $indexable_helper
|
||||
*/
|
||||
private $indexable_helper;
|
||||
|
||||
/**
|
||||
* The post type helper.
|
||||
*
|
||||
* @var Post_Type_Helper $post_type_helper
|
||||
*/
|
||||
private $post_type_helper;
|
||||
|
||||
/**
|
||||
* The options helper.
|
||||
*
|
||||
* @var Options_Helper $options_helper
|
||||
*/
|
||||
private $options_helper;
|
||||
|
||||
/**
|
||||
* The indexable repository.
|
||||
*
|
||||
* @var Indexable_Repository
|
||||
*/
|
||||
private $indexable_repository;
|
||||
|
||||
/**
|
||||
* Returns the conditionals based on which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Front_End_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishing_Principles_Schema_Integration constructor.
|
||||
*
|
||||
* @param Options_Helper $options_helper The options helper.
|
||||
* @param Indexable_Repository $indexable_repository The indexables repository.
|
||||
* @param Indexable_Helper $indexable_helper The indexables helper.
|
||||
* @param Post_Type_Helper $post_type_helper The post type helper.
|
||||
*/
|
||||
public function __construct(
|
||||
Options_Helper $options_helper,
|
||||
Indexable_Repository $indexable_repository,
|
||||
Indexable_Helper $indexable_helper,
|
||||
Post_Type_Helper $post_type_helper
|
||||
) {
|
||||
$this->options_helper = $options_helper;
|
||||
$this->indexable_repository = $indexable_repository;
|
||||
$this->indexable_helper = $indexable_helper;
|
||||
$this->post_type_helper = $post_type_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'wpseo_schema_organization', [ $this, 'filter_organization_schema' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the Organization policies are added to the schema output.
|
||||
*
|
||||
* @param array $data The organization schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filter_organization_schema( $data ) {
|
||||
$policy_indexables = $this->get_indexables_for_publishing_principle_pages(
|
||||
self::PRINCIPLES_MAPPING
|
||||
);
|
||||
|
||||
foreach ( $policy_indexables as $policy_data ) {
|
||||
$data = $this->add_schema_piece( $data, $policy_data );
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the data to the schema array.
|
||||
*
|
||||
* @param array $schema_graph The current schema graph.
|
||||
* @param array $policy_data The data present for a policy.
|
||||
*
|
||||
* @return array The new schema graph.
|
||||
*/
|
||||
private function add_schema_piece( $schema_graph, $policy_data ): array {
|
||||
if ( ! \is_null( $policy_data['permalink'] ) ) {
|
||||
$schema_graph[ $policy_data['schema'] ] = $policy_data['permalink'];
|
||||
}
|
||||
|
||||
return $schema_graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the indexables for all the given principles if they are set.
|
||||
*
|
||||
* @param array $principles_data The data for all the principles.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_indexables_for_publishing_principle_pages( $principles_data ): array {
|
||||
$principle_ids = [];
|
||||
$policies = [];
|
||||
$ids = [];
|
||||
foreach ( $principles_data as $principle ) {
|
||||
$option_value = $this->options_helper->get( $principle[0], false );
|
||||
if ( $option_value ) {
|
||||
$principle_ids[ $principle[0] ] = [
|
||||
'value' => $option_value,
|
||||
'schema' => $principle[1],
|
||||
];
|
||||
$ids[] = $option_value;
|
||||
}
|
||||
}
|
||||
|
||||
if ( \count( $ids ) === 0 ) {
|
||||
// Early return to not run an empty query.
|
||||
return [];
|
||||
}
|
||||
|
||||
if ( $this->indexable_helper->should_index_indexables() && $this->post_type_helper->is_of_indexable_post_type( 'page' ) ) {
|
||||
$indexables = $this->indexable_repository->find_by_multiple_ids_and_type( array_unique( $ids ), 'post' );
|
||||
|
||||
foreach ( $principle_ids as $key => $principle_id ) {
|
||||
foreach ( $indexables as $indexable ) {
|
||||
if ( $indexable && $principle_id['value'] === $indexable->object_id ) {
|
||||
if ( $indexable->post_status === 'publish' && $indexable->is_protected === false ) {
|
||||
$policies[ $key ] = [
|
||||
'permalink' => $indexable->permalink,
|
||||
'schema' => $principle_id['schema'],
|
||||
];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $policies;
|
||||
}
|
||||
|
||||
foreach ( $principle_ids as $key => $principle_id ) {
|
||||
foreach ( $ids as $post_id ) {
|
||||
$post = get_post( (int) $post_id );
|
||||
if ( is_object( $post ) ) {
|
||||
if ( (int) $principle_id['value'] === (int) $post_id && \get_post_status( $post_id ) === 'publish' && $post->post_password === '' ) {
|
||||
$policies[ $key ] = [
|
||||
'permalink' => get_permalink( $post_id ),
|
||||
'schema' => $principle_id['schema'],
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $policies;
|
||||
}
|
||||
}
|
||||
@@ -1,301 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Routes;
|
||||
|
||||
use RuntimeException;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Main;
|
||||
use Yoast\WP\SEO\Premium\Actions\AI_Generator_Action;
|
||||
use Yoast\WP\SEO\Premium\Exceptions\Remote_Request\Bad_Request_Exception;
|
||||
use Yoast\WP\SEO\Premium\Exceptions\Remote_Request\Forbidden_Exception;
|
||||
use Yoast\WP\SEO\Premium\Exceptions\Remote_Request\Internal_Server_Error_Exception;
|
||||
use Yoast\WP\SEO\Premium\Exceptions\Remote_Request\Not_Found_Exception;
|
||||
use Yoast\WP\SEO\Premium\Exceptions\Remote_Request\Payment_Required_Exception;
|
||||
use Yoast\WP\SEO\Premium\Exceptions\Remote_Request\Request_Timeout_Exception;
|
||||
use Yoast\WP\SEO\Premium\Exceptions\Remote_Request\Service_Unavailable_Exception;
|
||||
use Yoast\WP\SEO\Premium\Exceptions\Remote_Request\Too_Many_Requests_Exception;
|
||||
use Yoast\WP\SEO\Premium\Exceptions\Remote_Request\Unauthorized_Exception;
|
||||
use Yoast\WP\SEO\Premium\Helpers\AI_Generator_Helper;
|
||||
use Yoast\WP\SEO\Routes\Route_Interface;
|
||||
|
||||
/**
|
||||
* Registers the route for the AI_Generator integration.
|
||||
*/
|
||||
class AI_Generator_Route implements Route_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* The AI_Generator route prefix.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const ROUTE_PREFIX = 'ai_generator';
|
||||
|
||||
/**
|
||||
* The callback route constant (invoked by the API).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CALLBACK_ROUTE = self::ROUTE_PREFIX . '/callback';
|
||||
|
||||
/**
|
||||
* The refresh callback route constant (invoked by the API).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const REFRESH_CALLBACK_ROUTE = self::ROUTE_PREFIX . '/refresh_callback';
|
||||
|
||||
/**
|
||||
* The get_suggestions route constant.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const GET_SUGGESTIONS_ROUTE = self::ROUTE_PREFIX . '/get_suggestions';
|
||||
|
||||
/**
|
||||
* The get_suggestions route constant.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CONSENT_ROUTE = self::ROUTE_PREFIX . '/consent';
|
||||
|
||||
/**
|
||||
* The bust_subscription_cache route constant.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const BUST_SUBSCRIPTION_CACHE_ROUTE = self::ROUTE_PREFIX . '/bust_subscription_cache';
|
||||
|
||||
/**
|
||||
* Instance of the AI_Generator_Action.
|
||||
*
|
||||
* @var AI_Generator_Action
|
||||
*/
|
||||
protected $ai_generator_action;
|
||||
|
||||
/**
|
||||
* Instance of the AI_Generator_Helper.
|
||||
*
|
||||
* @var AI_Generator_Helper
|
||||
*/
|
||||
protected $ai_generator_helper;
|
||||
|
||||
/**
|
||||
* AI_Generator_Route constructor.
|
||||
*
|
||||
* @param AI_Generator_Action $ai_generator_action The action to handle the requests to the endpoint.
|
||||
* @param AI_Generator_Helper $ai_generator_helper The AI_Generator helper.
|
||||
*/
|
||||
public function __construct( AI_Generator_Action $ai_generator_action, AI_Generator_Helper $ai_generator_helper ) {
|
||||
$this->ai_generator_action = $ai_generator_action;
|
||||
$this->ai_generator_helper = $ai_generator_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers routes with WordPress.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_routes() {
|
||||
\register_rest_route(
|
||||
Main::API_V1_NAMESPACE,
|
||||
self::CONSENT_ROUTE,
|
||||
[
|
||||
'methods' => 'POST',
|
||||
'args' => [
|
||||
'consent' => [
|
||||
'required' => true,
|
||||
'type' => 'boolean',
|
||||
'description' => 'Whether the consent to use AI-based services has been given by the user.',
|
||||
],
|
||||
],
|
||||
'callback' => [ $this, 'consent' ],
|
||||
'permission_callback' => [ $this, 'check_permissions' ],
|
||||
]
|
||||
);
|
||||
|
||||
// Avoid registering the other routes if the feature is not enabled.
|
||||
if ( ! $this->ai_generator_helper->is_ai_generator_enabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$callback_route_args = [
|
||||
'methods' => 'POST',
|
||||
'args' => [
|
||||
'access_jwt' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'description' => 'The access JWT.',
|
||||
],
|
||||
'refresh_jwt' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'description' => 'The JWT to be used when the access JWT needs to be refreshed.',
|
||||
],
|
||||
'code_challenge' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'description' => 'The SHA266 of the verification code used to check the authenticity of a callback call.',
|
||||
],
|
||||
'user_id' => [
|
||||
'required' => true,
|
||||
'type' => 'integer',
|
||||
'description' => 'The id of the user associated to the code verifier.',
|
||||
],
|
||||
],
|
||||
'callback' => [ $this, 'callback' ],
|
||||
'permission_callback' => '__return_true',
|
||||
];
|
||||
\register_rest_route( Main::API_V1_NAMESPACE, self::CALLBACK_ROUTE, $callback_route_args );
|
||||
\register_rest_route( Main::API_V1_NAMESPACE, self::REFRESH_CALLBACK_ROUTE, $callback_route_args );
|
||||
|
||||
\register_rest_route(
|
||||
Main::API_V1_NAMESPACE,
|
||||
self::GET_SUGGESTIONS_ROUTE,
|
||||
[
|
||||
'methods' => 'POST',
|
||||
'args' => [
|
||||
'type' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'enum' => [
|
||||
'seo-title',
|
||||
'meta-description',
|
||||
],
|
||||
'description' => 'The type of suggestion requested.',
|
||||
],
|
||||
'prompt_content' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'description' => 'The content needed by the prompt to ask for suggestions.',
|
||||
],
|
||||
'focus_keyphrase' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'description' => 'The focus keyphrase associated to the post.',
|
||||
],
|
||||
'language' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'description' => 'The language the post is written in.',
|
||||
],
|
||||
'platform' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'enum' => [
|
||||
'Google',
|
||||
'Facebook',
|
||||
'Twitter',
|
||||
],
|
||||
'description' => 'The platform the post is intended for.',
|
||||
],
|
||||
],
|
||||
'callback' => [ $this, 'get_suggestions' ],
|
||||
'permission_callback' => [ $this, 'check_permissions' ],
|
||||
]
|
||||
);
|
||||
|
||||
\register_rest_route(
|
||||
Main::API_V1_NAMESPACE,
|
||||
self::BUST_SUBSCRIPTION_CACHE_ROUTE,
|
||||
[
|
||||
'methods' => 'POST',
|
||||
'args' => [],
|
||||
'callback' => [ $this, 'bust_subscription_cache' ],
|
||||
'permission_callback' => [ $this, 'check_permissions' ],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the callback to store connection credentials and the tokens locally.
|
||||
*
|
||||
* @param WP_REST_Request $request The request object.
|
||||
*
|
||||
* @return WP_REST_Response The response of the callback action.
|
||||
*/
|
||||
public function callback( WP_REST_Request $request ) {
|
||||
try {
|
||||
$code_verifier = $this->ai_generator_action->callback( $request['access_jwt'], $request['refresh_jwt'], $request['code_challenge'], $request['user_id'] );
|
||||
} catch ( Unauthorized_Exception $e ) {
|
||||
return new WP_REST_Response( 'Unauthorized.', 401 );
|
||||
}
|
||||
|
||||
return new WP_REST_Response(
|
||||
[
|
||||
'message' => 'Tokens successfully stored.',
|
||||
'code_verifier' => $code_verifier,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the callback to get ai-generated suggestions.
|
||||
*
|
||||
* @param WP_REST_Request $request The request object.
|
||||
*
|
||||
* @return WP_REST_Response The response of the get_suggestions action.
|
||||
*/
|
||||
public function get_suggestions( WP_REST_Request $request ) {
|
||||
try {
|
||||
$user = \wp_get_current_user();
|
||||
$data = $this->ai_generator_action->get_suggestions( $user, $request['type'], $request['prompt_content'], $request['focus_keyphrase'], $request['language'], $request['platform'] );
|
||||
} catch ( Bad_Request_Exception | Forbidden_Exception | Internal_Server_Error_Exception | Not_Found_Exception | Payment_Required_Exception | Request_Timeout_Exception | Service_Unavailable_Exception | Too_Many_Requests_Exception | Unauthorized_Exception $e ) {
|
||||
return new WP_REST_Response( $e->getMessage(), $e->getCode() );
|
||||
} catch ( RuntimeException $e ) {
|
||||
return new WP_REST_Response( 'Failed to get suggestions.', 500 );
|
||||
}
|
||||
|
||||
return new WP_REST_Response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the callback to store the consent given by the user to use AI-based services.
|
||||
*
|
||||
* @param WP_REST_Request $request The request object.
|
||||
*
|
||||
* @return WP_REST_Response The response of the callback action.
|
||||
*/
|
||||
public function consent( WP_REST_Request $request ) {
|
||||
$user_id = \get_current_user_id();
|
||||
$consent = \boolval( $request['consent'] );
|
||||
|
||||
try {
|
||||
$this->ai_generator_action->consent( $user_id, $consent );
|
||||
} catch ( Bad_Request_Exception | Forbidden_Exception | Internal_Server_Error_Exception | Not_Found_Exception | Payment_Required_Exception | Request_Timeout_Exception | Service_Unavailable_Exception | Too_Many_Requests_Exception | RuntimeException $e ) {
|
||||
return new WP_REST_Response( ( $consent ) ? 'Failed to store consent.' : 'Failed to revoke consent.', 500 );
|
||||
}
|
||||
|
||||
return new WP_REST_Response( ( $consent ) ? 'Consent successfully stored.' : 'Consent successfully revoked.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the callback that busts the subscription cache.
|
||||
*
|
||||
* @return WP_REST_Response The response of the callback action.
|
||||
*/
|
||||
public function bust_subscription_cache() {
|
||||
$this->ai_generator_action->bust_subscription_cache();
|
||||
|
||||
return new WP_REST_Response( 'Subscription cache successfully busted.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks:
|
||||
* - if the user is logged
|
||||
* - if the user can edit posts
|
||||
*
|
||||
* @return bool Whether the user is logged in, can edit posts and the feature is active.
|
||||
*/
|
||||
public function check_permissions() {
|
||||
$user = \wp_get_current_user();
|
||||
if ( $user === null || $user->ID < 1 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return \user_can( $user, 'edit_posts' );
|
||||
}
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Routes;
|
||||
|
||||
use WPSEO_Admin_Asset_Manager;
|
||||
use WPSEO_Shortlinker;
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Actions\Link_Suggestions_Action;
|
||||
use Yoast\WP\SEO\Premium\Helpers\Prominent_Words_Helper;
|
||||
use Yoast\WP\SEO\Repositories\Indexable_Repository;
|
||||
|
||||
/**
|
||||
* Workouts_Routes_Integration class
|
||||
*/
|
||||
class Workouts_Routes_Integration implements Integration_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* Allowed cornerstone steps.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
const ALLOWED_CORNERSTONE_STEPS = [
|
||||
'chooseCornerstones',
|
||||
'checkLinks',
|
||||
'addLinks',
|
||||
'improved',
|
||||
'skipped',
|
||||
];
|
||||
|
||||
/**
|
||||
* Allowed orphaned steps.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
const ALLOWED_ORPHANED_STEPS = [
|
||||
'improveRemove',
|
||||
'update',
|
||||
'addLinks',
|
||||
'removed',
|
||||
'noindexed',
|
||||
'improved',
|
||||
'skipped',
|
||||
];
|
||||
|
||||
/**
|
||||
* The indexable repository.
|
||||
*
|
||||
* @var Indexable_Repository The indexable repository.
|
||||
*/
|
||||
private $indexable_repository;
|
||||
|
||||
/**
|
||||
* The link suggestions action.
|
||||
*
|
||||
* @var Link_Suggestions_Action The action.
|
||||
*/
|
||||
private $link_suggestions_action;
|
||||
|
||||
/**
|
||||
* The admin asset manager.
|
||||
*
|
||||
* @var WPSEO_Admin_Asset_Manager
|
||||
*/
|
||||
private $admin_asset_manager;
|
||||
|
||||
/**
|
||||
* The shortlinker.
|
||||
*
|
||||
* @var WPSEO_Shortlinker
|
||||
*/
|
||||
private $shortlinker;
|
||||
|
||||
/**
|
||||
* The options helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
private $options_helper;
|
||||
|
||||
/**
|
||||
* The prominent words helper.
|
||||
*
|
||||
* @var Prominent_Words_Helper
|
||||
*/
|
||||
private $prominent_words_helper;
|
||||
|
||||
/**
|
||||
* The post type helper.
|
||||
*
|
||||
* @var Post_Type_Helper
|
||||
*/
|
||||
private $post_type_helper;
|
||||
|
||||
/**
|
||||
* Workouts_Integration constructor.
|
||||
*
|
||||
* @param Indexable_Repository $indexable_repository The indexables repository.
|
||||
* @param Link_Suggestions_Action $link_suggestions_action The link suggestions action.
|
||||
* @param WPSEO_Admin_Asset_Manager $admin_asset_manager The admin asset manager.
|
||||
* @param WPSEO_Shortlinker $shortlinker The shortlinker.
|
||||
* @param Options_Helper $options_helper The options helper.
|
||||
* @param Prominent_Words_Helper $prominent_words_helper The prominent words helper.
|
||||
* @param Post_Type_Helper $post_type_helper The post type helper.
|
||||
*/
|
||||
public function __construct(
|
||||
Indexable_Repository $indexable_repository,
|
||||
Link_Suggestions_Action $link_suggestions_action,
|
||||
WPSEO_Admin_Asset_Manager $admin_asset_manager,
|
||||
WPSEO_Shortlinker $shortlinker,
|
||||
Options_Helper $options_helper,
|
||||
Prominent_Words_Helper $prominent_words_helper,
|
||||
Post_Type_Helper $post_type_helper
|
||||
) {
|
||||
$this->indexable_repository = $indexable_repository;
|
||||
$this->link_suggestions_action = $link_suggestions_action;
|
||||
$this->admin_asset_manager = $admin_asset_manager;
|
||||
$this->shortlinker = $shortlinker;
|
||||
$this->options_helper = $options_helper;
|
||||
$this->prominent_words_helper = $prominent_words_helper;
|
||||
$this->post_type_helper = $post_type_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'Yoast\WP\SEO\workouts_route_args', [ $this, 'add_args_to_set_workouts_route' ] );
|
||||
\add_filter( 'Yoast\WP\SEO\workouts_route_save', [ $this, 'save_workouts_data' ], 10, 2 );
|
||||
\add_filter( 'Yoast\WP\SEO\workouts_options', [ $this, 'get_options' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds arguments to `set_workouts` route registration.
|
||||
*
|
||||
* @param array $args_array The existing array of arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function add_args_to_set_workouts_route( $args_array ) {
|
||||
$premium_args_array = [
|
||||
'cornerstone' => [
|
||||
'validate_callback' => [ $this, 'cornerstone_is_allowed' ],
|
||||
'required' => true,
|
||||
],
|
||||
'orphaned' => [
|
||||
'validate_callback' => [ $this, 'orphaned_is_allowed' ],
|
||||
'required' => true,
|
||||
],
|
||||
];
|
||||
|
||||
return \array_merge( $args_array, $premium_args_array );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the cornerstone attribute.
|
||||
*
|
||||
* @param array $workout The cornerstone workout.
|
||||
* @return bool If the payload is valid or not.
|
||||
*/
|
||||
public function cornerstone_is_allowed( $workout ) {
|
||||
return $this->is_allowed( $workout, self::ALLOWED_CORNERSTONE_STEPS );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the orphaned attribute.
|
||||
*
|
||||
* @param array $workout The orphaned workout.
|
||||
* @return bool If the payload is valid or not.
|
||||
*/
|
||||
public function orphaned_is_allowed( $workout ) {
|
||||
return $this->is_allowed( $workout, self::ALLOWED_ORPHANED_STEPS );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a workout.
|
||||
*
|
||||
* @param array $workout The workout.
|
||||
* @param array $allowed_steps The allowed steps for this workout.
|
||||
* @return bool If the payload is valid or not.
|
||||
*/
|
||||
public function is_allowed( $workout, $allowed_steps ) {
|
||||
// Only 3 properties are allowed, the below validated finishedSteps property.
|
||||
if ( \count( $workout ) !== 3 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( isset( $workout['finishedSteps'] ) && \is_array( $workout['finishedSteps'] ) ) {
|
||||
foreach ( $workout['finishedSteps'] as $step ) {
|
||||
if ( ! \in_array( $step, $allowed_steps, true ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the Premium workouts data to the database.
|
||||
*
|
||||
* @param mixed|null $result The result of the previous save operations.
|
||||
* @param array $workouts_data The complete workouts data.
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function save_workouts_data( $result, $workouts_data ) {
|
||||
$premium_workouts_data = [];
|
||||
$premium_workouts_data['cornerstone'] = $workouts_data['cornerstone'];
|
||||
$premium_workouts_data['orphaned'] = $workouts_data['orphaned'];
|
||||
|
||||
foreach ( $premium_workouts_data as $workout => $data ) {
|
||||
if ( isset( $data['indexablesByStep'] ) && \is_array( $data['indexablesByStep'] ) ) {
|
||||
foreach ( $data['indexablesByStep'] as $step => $indexables ) {
|
||||
if ( $step === 'removed' ) {
|
||||
continue;
|
||||
}
|
||||
$premium_workouts_data[ $workout ]['indexablesByStep'][ $step ] = \wp_list_pluck( $indexables, 'id' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->options_helper->set( 'workouts', $premium_workouts_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Premium workouts options from the database and adds it to the global array of workouts options.
|
||||
*
|
||||
* @param array $workouts_option The previous content of the workouts options.
|
||||
*
|
||||
* @return array The workouts options updated with the addition of the Premium workouts data.
|
||||
*/
|
||||
public function get_options( $workouts_option ) {
|
||||
$premium_option = $this->options_helper->get( 'workouts' );
|
||||
|
||||
if ( ! ( isset( $premium_option['orphaned']['indexablesByStep'] )
|
||||
&& \is_array( $premium_option['orphaned']['indexablesByStep'] )
|
||||
&& isset( $premium_option['cornerstone']['indexablesByStep'] )
|
||||
&& \is_array( $premium_option['cornerstone']['indexablesByStep'] ) )
|
||||
) {
|
||||
return \array_merge( $workouts_option, $premium_option );
|
||||
}
|
||||
|
||||
// Get all indexable ids from all workouts and all steps.
|
||||
$indexable_ids_in_workouts = [ 0 ];
|
||||
foreach ( [ 'orphaned', 'cornerstone' ] as $workout ) {
|
||||
foreach ( $premium_option[ $workout ]['indexablesByStep'] as $step => $indexables ) {
|
||||
if ( $step === 'removed' ) {
|
||||
continue;
|
||||
}
|
||||
foreach ( $indexables as $indexable_id ) {
|
||||
$indexable_ids_in_workouts[] = $indexable_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get all indexables corresponding to the indexable ids.
|
||||
$indexables_in_workouts = $this->indexable_repository->find_by_ids( $indexable_ids_in_workouts );
|
||||
|
||||
// Extend the workouts option with the indexables data.
|
||||
foreach ( [ 'orphaned', 'cornerstone' ] as $workout ) {
|
||||
// Don't add indexables for steps that are not allowed.
|
||||
$premium_option[ $workout ]['finishedSteps'] = \array_values(
|
||||
\array_intersect(
|
||||
$premium_option[ $workout ]['finishedSteps'],
|
||||
[
|
||||
'orphaned' => self::ALLOWED_ORPHANED_STEPS,
|
||||
'cornerstone' => self::ALLOWED_CORNERSTONE_STEPS,
|
||||
][ $workout ]
|
||||
)
|
||||
);
|
||||
|
||||
// Don't add indexables that are not published or are no-indexed.
|
||||
foreach ( $premium_option[ $workout ]['indexablesByStep'] as $step => $indexables ) {
|
||||
if ( $step === 'removed' ) {
|
||||
continue;
|
||||
}
|
||||
$premium_option[ $workout ]['indexablesByStep'][ $step ] = \array_values(
|
||||
\array_filter(
|
||||
\array_map(
|
||||
static function( $indexable_id ) use ( $indexables_in_workouts ) {
|
||||
foreach ( $indexables_in_workouts as $updated_indexable ) {
|
||||
if ( \is_array( $indexable_id ) ) {
|
||||
$indexable_id = $indexable_id['id'];
|
||||
}
|
||||
if ( (int) $indexable_id === $updated_indexable->id ) {
|
||||
if ( $updated_indexable->post_status !== 'publish' && $updated_indexable->post_status !== null ) {
|
||||
return false;
|
||||
}
|
||||
if ( $updated_indexable->is_robots_noindex ) {
|
||||
return false;
|
||||
}
|
||||
return $updated_indexable;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
$indexables
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return \array_merge( $workouts_option, $premium_option );
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Third_Party;
|
||||
|
||||
use WP_Post;
|
||||
use WP_Term;
|
||||
use WP_User;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Conditionals\Algolia_Enabled_Conditional;
|
||||
use Yoast\WP\SEO\Surfaces\Meta_Surface;
|
||||
use Yoast\WP\SEO\Surfaces\Values\Meta;
|
||||
|
||||
/**
|
||||
* BbPress integration.
|
||||
*/
|
||||
class Algolia implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* The options helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* The meta helper.
|
||||
*
|
||||
* @var Meta_Surface
|
||||
*/
|
||||
protected $meta;
|
||||
|
||||
/**
|
||||
* Algolia constructor.
|
||||
*
|
||||
* @codeCoverageIgnore It only sets dependencies.
|
||||
*
|
||||
* @param Options_Helper $options The options helper.
|
||||
* @param Meta_Surface $meta The meta surface.
|
||||
*/
|
||||
public function __construct( Options_Helper $options, Meta_Surface $meta ) {
|
||||
$this->options = $options;
|
||||
$this->meta = $meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the conditionals based in which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [
|
||||
Algolia_Enabled_Conditional::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'algolia_searchable_post_shared_attributes', [ $this, 'add_attributes_post' ], 10, 2 );
|
||||
\add_filter( 'algolia_term_record', [ $this, 'add_attributes_term' ] );
|
||||
\add_filter( 'algolia_user_record', [ $this, 'add_attributes_user' ] );
|
||||
\add_filter( 'algolia_should_index_searchable_post', [ $this, 'blacklist_no_index_posts' ], 10, 2 );
|
||||
\add_filter( 'algolia_should_index_term', [ $this, 'blacklist_no_index_terms' ], 10, 2 );
|
||||
\add_filter( 'algolia_should_index_user', [ $this, 'blacklist_no_index_users' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the search result priority and the number of internal links to an article to Algolia's index.
|
||||
*
|
||||
* @param array $attributes The attributes Algolia should index.
|
||||
* @param WP_Post $post The post object that is being indexed.
|
||||
*
|
||||
* @return array The attributes Algolia should index.
|
||||
*/
|
||||
public function add_attributes_post( $attributes, $post ) {
|
||||
$meta = $this->meta->for_post( $post->ID );
|
||||
|
||||
return $this->add_attributes( $attributes, $meta );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the attributes for a term.
|
||||
*
|
||||
* @param array $attributes The recorded attributes.
|
||||
*
|
||||
* @return array The recorded attributes.
|
||||
*/
|
||||
public function add_attributes_term( $attributes ) {
|
||||
$meta = $this->meta->for_term( $attributes['objectID'] );
|
||||
|
||||
return $this->add_attributes( $attributes, $meta );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the attributes for a term.
|
||||
*
|
||||
* @param array $attributes The recorded attributes.
|
||||
*
|
||||
* @return array The recorded attributes.
|
||||
*/
|
||||
public function add_attributes_user( $attributes ) {
|
||||
$meta = $this->meta->for_author( $attributes['objectID'] );
|
||||
|
||||
return $this->add_attributes( $attributes, $meta );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the attributes for a searchable object.
|
||||
*
|
||||
* @param array $attributes Attributes to update.
|
||||
* @param Meta $meta Meta value object for the current object.
|
||||
*
|
||||
* @return array Attributes for the searchable object.
|
||||
*/
|
||||
private function add_attributes( array $attributes, Meta $meta ) {
|
||||
$attributes['yoast_seo_links'] = (int) $meta->indexable->incoming_link_count;
|
||||
$attributes['yoast_seo_metadesc'] = $meta->meta_description;
|
||||
|
||||
return $this->add_social_image( $attributes, $meta->open_graph_images );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the social image to an attributes array if we have one.
|
||||
*
|
||||
* @param array $attributes The array of search attributes for a record.
|
||||
* @param array $og_images The social images for the current item.
|
||||
*
|
||||
* @return array The array of search attributes for a record.
|
||||
*/
|
||||
private function add_social_image( $attributes, $og_images ) {
|
||||
if ( \is_array( $og_images ) && \count( $og_images ) > 0 ) {
|
||||
$attributes['images']['social'] = \reset( $og_images );
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a post should be indexed, taking the Yoast SEO no-index state into account.
|
||||
*
|
||||
* @param bool $should_index Whether Algolia should index the post or not.
|
||||
* @param WP_Post $post The post object.
|
||||
*
|
||||
* @return bool Whether Algolia should index the post or not.
|
||||
*/
|
||||
public function blacklist_no_index_posts( $should_index, $post ) {
|
||||
if ( $this->meta->for_post( $post->ID )->robots['index'] === 'noindex' ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $should_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a term should be indexed, taking the Yoast SEO no-index state into account.
|
||||
*
|
||||
* @param bool $should_index Whether Algolia should index the term or not.
|
||||
* @param WP_Term $term The term object.
|
||||
*
|
||||
* @return bool Whether Algolia should index the term or not.
|
||||
*/
|
||||
public function blacklist_no_index_terms( $should_index, $term ) {
|
||||
if ( $this->meta->for_term( $term->term_id )->robots['index'] === 'noindex' ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $should_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a user should be indexed, taking the Yoast SEO no-index state into account.
|
||||
*
|
||||
* @param bool $should_index Whether Algolia should index the user or not.
|
||||
* @param WP_User $user The user object.
|
||||
*
|
||||
* @return bool Whether Algolia should index the user or not.
|
||||
*/
|
||||
public function blacklist_no_index_users( $should_index, $user ) {
|
||||
if ( $this->meta->for_author( $user->ID )->robots['index'] === 'noindex' ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $should_index;
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Third_Party;
|
||||
|
||||
use WPSEO_Schema_Context;
|
||||
use Yoast\WP\SEO\Conditionals\Front_End_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Conditionals\EDD_Conditional;
|
||||
use Yoast\WP\SEO\Surfaces\Meta_Surface;
|
||||
|
||||
/**
|
||||
* EDD integration.
|
||||
*/
|
||||
class EDD implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* The meta surface.
|
||||
*
|
||||
* @var Meta_Surface
|
||||
*/
|
||||
private $meta;
|
||||
|
||||
/**
|
||||
* Returns the conditionals based on which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Front_End_Conditional::class, EDD_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* EDD constructor.
|
||||
*
|
||||
* @codeCoverageIgnore It only sets dependencies.
|
||||
*
|
||||
* @param Meta_Surface $meta The meta surface.
|
||||
*/
|
||||
public function __construct( Meta_Surface $meta ) {
|
||||
$this->meta = $meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'edd_generate_download_structured_data', [ $this, 'filter_download_schema' ] );
|
||||
\add_filter( 'wpseo_schema_organization', [ $this, 'filter_organization_schema' ] );
|
||||
\add_filter( 'wpseo_schema_webpage', [ $this, 'filter_webpage_schema' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the Organization is classified as a Brand too.
|
||||
*
|
||||
* @param array $data The organization schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filter_organization_schema( $data ) {
|
||||
if ( \is_singular( 'download' ) ) {
|
||||
$data['@type'] = [ 'Organization', 'Brand' ];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the WebPage schema contains reference to the product.
|
||||
*
|
||||
* @param array $data The schema Webpage data.
|
||||
* @param WPSEO_Schema_Context $context Context object.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filter_webpage_schema( $data, $context ) {
|
||||
if ( \is_singular( [ 'download' ] ) ) {
|
||||
$data['about'] = [ '@id' => $context->canonical . '#/schema/edd-product/' . \get_the_ID() ];
|
||||
$data['mainEntity'] = [ '@id' => $context->canonical . '#/schema/edd-product/' . \get_the_ID() ];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the structured data output for a download to tie into Yoast SEO's output.
|
||||
*
|
||||
* @param array $data Structured data for a download.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filter_download_schema( $data ) {
|
||||
$data['@id'] = $this->meta->for_current_page()->canonical . '#/schema/edd-product/' . \get_the_ID();
|
||||
$data['sku'] = (string) $data['sku'];
|
||||
$data['brand'] = $this->return_organization_node();
|
||||
$data['offers'] = $this->clean_up_offer( $data['offers'] );
|
||||
|
||||
if ( ! isset( $data['description'] ) ) {
|
||||
$data['description'] = $this->meta->for_current_page()->open_graph_description;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up EDD generated Offers.
|
||||
*
|
||||
* @param array $offer The schema array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function clean_up_offer( $offer ) {
|
||||
if ( \array_key_exists( 'priceValidUntil', $offer ) && $offer['priceValidUntil'] === null ) {
|
||||
unset( $offer['priceValidUntil'] );
|
||||
}
|
||||
$offer['seller'] = $this->return_organization_node();
|
||||
|
||||
return $offer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Schema node for the current site's Organization.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function return_organization_node() {
|
||||
return [
|
||||
'@type' => [ 'Organization', 'Brand' ],
|
||||
'@id' => $this->meta->for_home_page()->canonical . '#organization',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,333 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Third_Party;
|
||||
|
||||
use WP_Post;
|
||||
use WPSEO_Admin_Asset_Manager;
|
||||
use WPSEO_Capability_Utils;
|
||||
use WPSEO_Custom_Fields_Plugin;
|
||||
use WPSEO_Language_Utils;
|
||||
use WPSEO_Metabox;
|
||||
use WPSEO_Metabox_Analysis_SEO;
|
||||
use WPSEO_Options;
|
||||
use WPSEO_Post_Type;
|
||||
use WPSEO_Post_Watcher;
|
||||
use WPSEO_Premium_Asset_JS_L10n;
|
||||
use WPSEO_Premium_Assets;
|
||||
use WPSEO_Premium_Prominent_Words_Support;
|
||||
use WPSEO_Social_Previews;
|
||||
use WPSEO_Utils;
|
||||
use Yoast\WP\SEO\Conditionals\Third_Party\Elementor_Edit_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Premium\Helpers\Current_Page_Helper;
|
||||
use Yoast\WP\SEO\Premium\Helpers\Prominent_Words_Helper;
|
||||
use Yoast\WP\SEO\Premium\Integrations\Admin\Prominent_Words\Indexing_Integration;
|
||||
use Yoast\WP\SEO\Premium\Integrations\Admin\Replacement_Variables_Integration;
|
||||
|
||||
/**
|
||||
* Elementor integration class for Yoast SEO Premium.
|
||||
*/
|
||||
class Elementor_Premium implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Holds the script handle.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SCRIPT_HANDLE = 'elementor-premium';
|
||||
|
||||
/**
|
||||
* Holds the Current_Page_Helper.
|
||||
*
|
||||
* @var Current_Page_Helper
|
||||
*/
|
||||
protected $current_page_helper;
|
||||
|
||||
/**
|
||||
* Represents the post.
|
||||
*
|
||||
* @var WP_Post|null
|
||||
*/
|
||||
protected $post;
|
||||
|
||||
/**
|
||||
* Represents the post watcher.
|
||||
*
|
||||
* @var WPSEO_Post_Watcher
|
||||
*/
|
||||
protected $post_watcher;
|
||||
|
||||
/**
|
||||
* The prominent words helper.
|
||||
*
|
||||
* @var Prominent_Words_Helper
|
||||
*/
|
||||
protected $prominent_words_helper;
|
||||
|
||||
/**
|
||||
* Returns the conditionals based in which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Elementor_Edit_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the class.
|
||||
*
|
||||
* @param Prominent_Words_Helper $prominent_words_helper The prominent words helper.
|
||||
* @param Current_Page_Helper $current_page_helper The Current_Page_Helper.
|
||||
*/
|
||||
public function __construct( Prominent_Words_Helper $prominent_words_helper, Current_Page_Helper $current_page_helper ) {
|
||||
$this->prominent_words_helper = $prominent_words_helper;
|
||||
$this->post_watcher = new WPSEO_Post_Watcher();
|
||||
$this->current_page_helper = $current_page_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'enqueue' ] );
|
||||
\add_action( 'post_updated', [ $this->post_watcher, 'detect_slug_change' ], 12, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues all the needed JS and CSS.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue() {
|
||||
// Check if we should load.
|
||||
if ( ! $this->load_metabox() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-register assets as Elementor unregister everything.
|
||||
$asset_manager = new WPSEO_Premium_Assets();
|
||||
$asset_manager->register_assets();
|
||||
|
||||
// Initialize Elementor (replaces premium-metabox).
|
||||
$this->enqueue_assets();
|
||||
|
||||
/*
|
||||
* Re-enqueue the integrations as `admin_enqueue_scripts` is undone.
|
||||
* Note the register_hooks were not even called (because it doesn't work anyway).
|
||||
*/
|
||||
$social_previews = new WPSEO_Social_Previews();
|
||||
$social_previews->enqueue_assets();
|
||||
$custom_fields = new WPSEO_Custom_Fields_Plugin();
|
||||
$custom_fields->enqueue();
|
||||
|
||||
$replacement_variables = new Replacement_Variables_Integration();
|
||||
$replacement_variables->enqueue_assets();
|
||||
}
|
||||
|
||||
// Below is mostly copied from `premium-metabox.php`.
|
||||
|
||||
/**
|
||||
* Enqueues assets when relevant.
|
||||
*
|
||||
* @codeCoverageIgnore Method uses dependencies.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
\wp_enqueue_script( static::SCRIPT_HANDLE );
|
||||
\wp_enqueue_style( static::SCRIPT_HANDLE );
|
||||
|
||||
$premium_localization = new WPSEO_Premium_Asset_JS_L10n();
|
||||
$premium_localization->localize_script( static::SCRIPT_HANDLE );
|
||||
|
||||
$this->send_data_to_assets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to assets by using wp_localize_script.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function send_data_to_assets() {
|
||||
$analysis_seo = new WPSEO_Metabox_Analysis_SEO();
|
||||
$assets_manager = new WPSEO_Admin_Asset_Manager();
|
||||
|
||||
$data = [
|
||||
'restApi' => $this->get_rest_api_config(),
|
||||
'seoAnalysisEnabled' => $analysis_seo->is_enabled(),
|
||||
'licensedURL' => WPSEO_Utils::get_home_url(),
|
||||
'settingsPageUrl' => \admin_url( 'admin.php?page=wpseo_page_settings#/site-features#card-wpseo-enable_link_suggestions' ),
|
||||
'integrationsTabURL' => \admin_url( 'admin.php?page=wpseo_integrations' ),
|
||||
'commonsScriptUrl' => \plugins_url(
|
||||
'assets/js/dist/commons-premium-' . $assets_manager->flatten_version( \WPSEO_PREMIUM_VERSION ) . \WPSEO_CSSJS_SUFFIX . '.js',
|
||||
\WPSEO_PREMIUM_FILE
|
||||
),
|
||||
'premiumAssessmentsScriptUrl' => \plugins_url(
|
||||
'assets/js/dist/register-premium-assessments-' . $assets_manager->flatten_version( \WPSEO_PREMIUM_VERSION ) . \WPSEO_CSSJS_SUFFIX . '.js',
|
||||
\WPSEO_PREMIUM_FILE
|
||||
),
|
||||
'pluginUrl' => \plugins_url( '', \WPSEO_PREMIUM_FILE ),
|
||||
];
|
||||
if ( \defined( 'YOAST_SEO_TEXT_FORMALITY' ) && \YOAST_SEO_TEXT_FORMALITY === true ) {
|
||||
$data['textFormalityScriptUrl'] = \plugins_url(
|
||||
'assets/js/dist/register-text-formality-' . $assets_manager->flatten_version( \WPSEO_PREMIUM_VERSION ) . \WPSEO_CSSJS_SUFFIX . '.js',
|
||||
\WPSEO_PREMIUM_FILE
|
||||
);
|
||||
}
|
||||
$data = \array_merge( $data, $this->get_post_metabox_config() );
|
||||
|
||||
if ( \current_user_can( 'edit_others_posts' ) ) {
|
||||
$data['workoutsUrl'] = \admin_url( 'admin.php?page=wpseo_workouts' );
|
||||
}
|
||||
|
||||
// Use an extra level in the array to preserve booleans. WordPress sanitizes scalar values in the first level of the array.
|
||||
\wp_localize_script( static::SCRIPT_HANDLE, 'wpseoPremiumMetaboxData', [ 'data' => $data ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the metabox config for a post.
|
||||
*
|
||||
* @return array The config.
|
||||
*/
|
||||
protected function get_post_metabox_config() {
|
||||
$link_suggestions_enabled = WPSEO_Options::get( 'enable_link_suggestions', false );
|
||||
|
||||
$prominent_words_support = new WPSEO_Premium_Prominent_Words_Support();
|
||||
$is_prominent_words_available = $prominent_words_support->is_post_type_supported( $this->get_metabox_post()->post_type );
|
||||
|
||||
$site_locale = \get_locale();
|
||||
$language = WPSEO_Language_Utils::get_language( $site_locale );
|
||||
|
||||
|
||||
return [
|
||||
'currentObjectId' => $this->get_metabox_post()->ID,
|
||||
'currentObjectType' => 'post',
|
||||
'linkSuggestionsEnabled' => ( $link_suggestions_enabled ) ? 'enabled' : 'disabled',
|
||||
'linkSuggestionsAvailable' => $is_prominent_words_available,
|
||||
'linkSuggestionsUnindexed' => ! $this->is_prominent_words_indexing_completed() && WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ),
|
||||
'perIndexableLimit' => $this->per_indexable_limit( $language ),
|
||||
'isProminentWordsAvailable' => $is_prominent_words_available,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the content endpoints are available.
|
||||
*
|
||||
* @return bool Returns true if the content endpoints are available
|
||||
*/
|
||||
public static function are_content_endpoints_available() {
|
||||
if ( \function_exists( 'rest_get_server' ) ) {
|
||||
$namespaces = \rest_get_server()->get_namespaces();
|
||||
return \in_array( 'wp/v2', $namespaces, true );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the REST API configuration.
|
||||
*
|
||||
* @return array The configuration.
|
||||
*/
|
||||
protected function get_rest_api_config() {
|
||||
return [
|
||||
'available' => WPSEO_Utils::is_api_available(),
|
||||
'contentEndpointsAvailable' => self::are_content_endpoints_available(),
|
||||
'root' => \esc_url_raw( \rest_url() ),
|
||||
'nonce' => \wp_create_nonce( 'wp_rest' ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the post for the current admin page.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @return WP_Post|null The post for the current admin page.
|
||||
*/
|
||||
protected function get_metabox_post() {
|
||||
if ( $this->post !== null ) {
|
||||
return $this->post;
|
||||
}
|
||||
|
||||
$post_id = $this->current_page_helper->get_current_post_id();
|
||||
|
||||
if ( $post_id ) {
|
||||
|
||||
$this->post = \get_post( $post_id );
|
||||
|
||||
return $this->post;
|
||||
}
|
||||
|
||||
if ( isset( $GLOBALS['post'] ) ) {
|
||||
$this->post = $GLOBALS['post'];
|
||||
|
||||
return $this->post;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not the metabox related scripts should be loaded.
|
||||
*
|
||||
* @return bool True when it should be loaded.
|
||||
*/
|
||||
protected function load_metabox() {
|
||||
// When the current page isn't a post related one.
|
||||
if ( WPSEO_Metabox::is_post_edit( $this->get_current_page() ) ) {
|
||||
return WPSEO_Post_Type::has_metabox_enabled( $this->current_page_helper->get_current_post_type() );
|
||||
}
|
||||
|
||||
// Make sure ajax integrations are loaded.
|
||||
return \wp_doing_ajax();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of the pagenow variable.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @return string The value of pagenow.
|
||||
*/
|
||||
protected function get_current_page() {
|
||||
global $pagenow;
|
||||
|
||||
return $pagenow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not we need to index more posts for correct link suggestion functionality.
|
||||
*
|
||||
* @return bool Whether or not we need to index more posts.
|
||||
*/
|
||||
protected function is_prominent_words_indexing_completed() {
|
||||
$is_indexing_completed = $this->prominent_words_helper->is_indexing_completed();
|
||||
if ( $is_indexing_completed === null ) {
|
||||
$indexation_integration = \YoastSEOPremium()->classes->get( Indexing_Integration::class );
|
||||
$is_indexing_completed = $indexation_integration->get_unindexed_count( 0 ) === 0;
|
||||
|
||||
$this->prominent_words_helper->set_indexing_completed( $is_indexing_completed );
|
||||
}
|
||||
|
||||
return $is_indexing_completed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of prominent words to store for content written in the given language.
|
||||
*
|
||||
* @param string $language The current language.
|
||||
*
|
||||
* @return int The number of words to store.
|
||||
*/
|
||||
protected function per_indexable_limit( $language ) {
|
||||
if ( \YoastSEO()->helpers->language->has_function_word_support( $language ) ) {
|
||||
return Indexing_Integration::PER_INDEXABLE_LIMIT;
|
||||
}
|
||||
|
||||
return Indexing_Integration::PER_INDEXABLE_LIMIT_NO_FUNCTION_WORD_SUPPORT;
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Third_Party;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Social_Profiles_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Mastodon integration.
|
||||
*/
|
||||
class Mastodon implements Integration_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* Holds the options helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
protected $options_helper;
|
||||
|
||||
/**
|
||||
* Holds the social profiles helper.
|
||||
*
|
||||
* @var Social_Profiles_Helper
|
||||
*/
|
||||
protected $social_profiles_helper;
|
||||
|
||||
/**
|
||||
* Sets the helpers.
|
||||
*
|
||||
* @param Options_Helper $options_helper Options helper.
|
||||
* @param Social_Profiles_Helper $social_profiles_helper Social Profiles helper.
|
||||
*/
|
||||
public function __construct( Options_Helper $options_helper, Social_Profiles_Helper $social_profiles_helper ) {
|
||||
$this->options_helper = $options_helper;
|
||||
$this->social_profiles_helper = $social_profiles_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'wpseo_frontend_presenter_classes', [ $this, 'add_social_link_tags' ], 10, 2 );
|
||||
\add_filter( 'wpseo_person_social_profile_fields', [ $this, 'add_mastodon_to_person_social_profile_fields' ], 11, 1 );
|
||||
\add_filter( 'wpseo_organization_social_profile_fields', [ $this, 'add_mastodon_to_organization_social_profile_fields' ], 11, 1 );
|
||||
\add_filter( 'wpseo_schema_person_social_profiles', [ $this, 'add_mastodon_to_person_schema' ], 10 );
|
||||
\add_filter( 'user_contactmethods', [ $this, 'add_mastodon_to_user_contactmethods' ], 10 );
|
||||
\add_filter( 'wpseo_mastodon_active', [ $this, 'check_mastodon_active' ], 10 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the social profiles presenter to the list of presenters to use.
|
||||
*
|
||||
* @param array $presenters The list of presenters.
|
||||
* @param string $page_type The page type for which the presenters have been collected.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function add_social_link_tags( $presenters, $page_type ) {
|
||||
// Bail out early if something's wrong with the presenters, let's not add any more confusion there.
|
||||
if ( ! \is_array( $presenters ) ) {
|
||||
return $presenters;
|
||||
}
|
||||
|
||||
if ( \in_array( $page_type, [ 'Static_Home_Page', 'Home_Page' ], true ) ) {
|
||||
$presenters = \array_merge( $presenters, [ 'Yoast\WP\SEO\Premium\Presenters\Mastodon_Link_Presenter' ] );
|
||||
}
|
||||
|
||||
return $presenters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Mastodon to the list of social profiles.
|
||||
*
|
||||
* @param array $social_profile_fields The social profiles array.
|
||||
*
|
||||
* @return array The updated social profiles array.
|
||||
*/
|
||||
public function add_mastodon_to_person_social_profile_fields( $social_profile_fields ) {
|
||||
// Bail out early if something's wrong with the social profiles, let's not add any more confusion there.
|
||||
if ( ! \is_array( $social_profile_fields ) ) {
|
||||
return $social_profile_fields;
|
||||
}
|
||||
$social_profile_fields['mastodon'] = 'get_non_valid_url';
|
||||
|
||||
return $social_profile_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Mastodon to the list of social profiles.
|
||||
*
|
||||
* @param array $social_profile_fields The social profiles array.
|
||||
*
|
||||
* @return array The updated social profiles array.
|
||||
*/
|
||||
public function add_mastodon_to_organization_social_profile_fields( $social_profile_fields ) {
|
||||
// Bail out early if something's wrong with the social profiles, let's not add any more confusion there.
|
||||
if ( ! \is_array( $social_profile_fields ) ) {
|
||||
return $social_profile_fields;
|
||||
}
|
||||
$social_profile_fields['mastodon_url'] = 'get_non_valid_url';
|
||||
|
||||
return $social_profile_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Mastodon to the list of social profiles to add to a Person's Schema.
|
||||
*
|
||||
* @param array $social_profiles The social profiles array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function add_mastodon_to_person_schema( $social_profiles ) {
|
||||
// Bail out early if something's wrong with the social profiles, let's not add any more confusion there.
|
||||
if ( ! \is_array( $social_profiles ) ) {
|
||||
return $social_profiles;
|
||||
}
|
||||
$social_profiles[] = 'mastodon';
|
||||
|
||||
return $social_profiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Mastodon to the list of contact methods for persons.
|
||||
*
|
||||
* @param array $contactmethods Currently set contactmethods.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function add_mastodon_to_user_contactmethods( $contactmethods ) {
|
||||
// Bail out early if something's wrong with the contact methods, let's not add any more confusion there.
|
||||
if ( ! \is_array( $contactmethods ) ) {
|
||||
return $contactmethods;
|
||||
}
|
||||
|
||||
$contactmethods['mastodon'] = \__( 'Mastodon profile URL', 'wordpress-seo-premium' );
|
||||
|
||||
return $contactmethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Mastodon field is filled in.
|
||||
*
|
||||
* @param bool $state The current state of the integration.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function check_mastodon_active( $state ) {
|
||||
switch ( $this->options_helper->get( 'company_or_person', false ) ) {
|
||||
case 'company':
|
||||
$social_profiles = $this->social_profiles_helper->get_organization_social_profiles();
|
||||
if ( ! empty( $social_profiles['mastodon_url'] ) ) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'person':
|
||||
$company_or_person_id = $this->options_helper->get( 'company_or_person_user_id', 0 );
|
||||
$social_profiles = $this->social_profiles_helper->get_person_social_profiles( $company_or_person_id );
|
||||
if ( ! empty( $social_profiles['mastodon'] ) ) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Integrations\Third_Party;
|
||||
|
||||
use DateTime;
|
||||
use stdClass;
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Helpers\Date_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Allows to download translations from TranslationsPress.
|
||||
*/
|
||||
class TranslationsPress implements Integration_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* The plugin slug to retrieve the translations for.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $slug = 'wordpress-seo-premium';
|
||||
|
||||
/**
|
||||
* The key of the custom transient where to store the translations info.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $transient_key;
|
||||
|
||||
/**
|
||||
* The URL for the TranslationsPress API service.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $api_url;
|
||||
|
||||
/**
|
||||
* The array to cache our addition to the `site_transient_update_plugins` filter.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $cached_translations;
|
||||
|
||||
/**
|
||||
* The Date helper object.
|
||||
*
|
||||
* @var Date_Helper
|
||||
*/
|
||||
protected $date_helper;
|
||||
|
||||
/**
|
||||
* Adds a new project to load translations for.
|
||||
*
|
||||
* @param Date_Helper $date_helper The Date Helper object.
|
||||
*/
|
||||
public function __construct( Date_Helper $date_helper ) {
|
||||
$this->transient_key = 'yoast_translations_' . $this->slug;
|
||||
$this->api_url = 'https://packages.translationspress.com/yoast/' . $this->slug . '/packages.json';
|
||||
$this->date_helper = $date_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'init', [ $this, 'register_clean_translations_cache' ], \PHP_INT_MAX );
|
||||
\add_filter( 'translations_api', [ $this, 'translations_api' ], 10, 3 );
|
||||
\add_filter( 'site_transient_update_plugins', [ $this, 'site_transient_update_plugins' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Short-circuits translations API requests for private projects.
|
||||
*
|
||||
* @param bool|array $result The result object. Default false.
|
||||
* @param string $requested_type The type of translations being requested.
|
||||
* @param object $args Translation API arguments.
|
||||
*
|
||||
* @return bool|array The translations array. False by default.
|
||||
*/
|
||||
public function translations_api( $result, $requested_type, $args ) {
|
||||
if ( $requested_type === 'plugins' && $args['slug'] === $this->slug ) {
|
||||
return $this->get_translations();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the translations transients to include the private plugin or theme.
|
||||
* Caches our own return value to prevent heavy overhead.
|
||||
*
|
||||
* @param bool|object $value The transient value.
|
||||
*
|
||||
* @return object The filtered transient value.
|
||||
*/
|
||||
public function site_transient_update_plugins( $value ) {
|
||||
if ( ! $value ) {
|
||||
$value = new stdClass();
|
||||
}
|
||||
|
||||
if ( ! isset( $value->translations ) ) {
|
||||
$value->translations = [];
|
||||
}
|
||||
|
||||
if ( \is_array( $this->cached_translations ) ) {
|
||||
$value->translations = \array_merge( $value->translations, $this->cached_translations );
|
||||
return $value;
|
||||
}
|
||||
|
||||
$this->cached_translations = [];
|
||||
|
||||
$translations = $this->get_translations();
|
||||
if ( empty( $translations[ $this->slug ]['translations'] ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// The following call is the reason we need to cache the results of this method.
|
||||
$installed_translations = \wp_get_installed_translations( 'plugins' );
|
||||
$available_languages = \get_available_languages();
|
||||
foreach ( $translations[ $this->slug ]['translations'] as $translation ) {
|
||||
if ( ! \in_array( $translation['language'], $available_languages, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $installed_translations[ $this->slug ][ $translation['language'] ] ) && $translation['updated'] ) {
|
||||
$local = new DateTime( $installed_translations[ $this->slug ][ $translation['language'] ]['PO-Revision-Date'] );
|
||||
$remote = new DateTime( $translation['updated'] );
|
||||
|
||||
if ( $local >= $remote ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$translation['type'] = 'plugin';
|
||||
$translation['slug'] = $this->slug;
|
||||
$translation['autoupdate'] = true;
|
||||
$value->translations[] = $translation;
|
||||
$this->cached_translations[] = $translation;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers actions for clearing translation caches.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_clean_translations_cache() {
|
||||
\add_action( 'set_site_transient_update_plugins', [ $this, 'clean_translations_cache' ] );
|
||||
\add_action( 'delete_site_transient_update_plugins', [ $this, 'clean_translations_cache' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears existing translation cache.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clean_translations_cache() {
|
||||
$translations = \get_site_transient( $this->transient_key );
|
||||
if ( ! \is_array( $translations ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$cache_lifespan = \DAY_IN_SECONDS;
|
||||
$time_not_changed = isset( $translations['_last_checked'] ) && ( $this->date_helper->current_time() - $translations['_last_checked'] ) > $cache_lifespan;
|
||||
|
||||
if ( ! $time_not_changed ) {
|
||||
return;
|
||||
}
|
||||
|
||||
\delete_site_transient( $this->transient_key );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translations for a given project.
|
||||
*
|
||||
* @return array The translation data.
|
||||
*/
|
||||
public function get_translations() {
|
||||
$translations = \get_site_transient( $this->transient_key );
|
||||
if ( $translations !== false && \is_array( $translations ) ) {
|
||||
return $translations;
|
||||
}
|
||||
|
||||
$translations = [];
|
||||
|
||||
$result = \json_decode( \wp_remote_retrieve_body( \wp_remote_get( $this->api_url ) ), true );
|
||||
|
||||
// Nothing found.
|
||||
if ( ! \is_array( $result ) ) {
|
||||
$result = [];
|
||||
}
|
||||
|
||||
$translations[ $this->slug ] = $result;
|
||||
$translations['_last_checked'] = $this->date_helper->current_time();
|
||||
|
||||
\set_site_transient( $this->transient_key, $translations );
|
||||
|
||||
return $translations;
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.Invalid
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.MaxExceeded
|
||||
|
||||
namespace Yoast\WP\SEO\Integrations\Third_Party;
|
||||
|
||||
use WPSEO_Meta;
|
||||
use Yoast\WP\SEO\Conditionals\Wincher_Enabled_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Enhances the Wincher keyphrases arrays.
|
||||
*/
|
||||
class Wincher_Keyphrases implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Returns the conditionals based in which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Wincher_Enabled_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'wpseo_wincher_keyphrases_from_post', [ $this, 'add_additional_keyphrases_from_post' ], 10, 2 );
|
||||
\add_filter( 'wpseo_wincher_all_keyphrases', [ $this, 'add_all_additional_keyphrases' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhances the keyphrases collected from a post with the additional ones.
|
||||
*
|
||||
* @param array $keyphrases The keyphrases array.
|
||||
* @param int $post_id The ID of the post.
|
||||
*
|
||||
* @return array The enhanced array.
|
||||
*/
|
||||
public function add_additional_keyphrases_from_post( $keyphrases, $post_id ) {
|
||||
$additional_keywords = \json_decode( WPSEO_Meta::get_value( 'focuskeywords', $post_id ), true );
|
||||
return \array_merge( $keyphrases, $additional_keywords );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhances the keyphrases collected from all the posts with the additional ones.
|
||||
*
|
||||
* @param array $keyphrases The keyphrases array.
|
||||
*
|
||||
* @return array The enhanced array.
|
||||
*/
|
||||
public function add_all_additional_keyphrases( $keyphrases ) {
|
||||
global $wpdb;
|
||||
$meta_key = WPSEO_Meta::$meta_prefix . 'focuskeywords';
|
||||
|
||||
$query = "
|
||||
SELECT meta_value
|
||||
FROM $wpdb->postmeta
|
||||
JOIN $wpdb->posts ON {$wpdb->posts}.id = {$wpdb->postmeta}.post_id
|
||||
WHERE meta_key = '$meta_key' AND post_status != 'trash'
|
||||
";
|
||||
|
||||
// phpcs:ignore -- ignoring since it's complaining about not using prepare when it's perfectly safe here.
|
||||
$results = $wpdb->get_results( $query );
|
||||
|
||||
if ( $results ) {
|
||||
foreach ( $results as $row ) {
|
||||
$additional_keywords = \json_decode( $row->meta_value, true );
|
||||
if ( $additional_keywords !== null ) {
|
||||
$additional_keywords = \array_column( $additional_keywords, 'keyword' );
|
||||
$keyphrases = \array_merge( $keyphrases, $additional_keywords );
|
||||
}
|
||||
}
|
||||
}
|
||||
return $keyphrases;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Integrations\Third_Party;
|
||||
|
||||
use WP_Post;
|
||||
use WPSEO_Admin_Utils;
|
||||
use Yoast\WP\SEO\Conditionals\Zapier_Enabled_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Zapier_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Class to manage the Zapier integration in the Classic editor.
|
||||
*/
|
||||
class Zapier_Classic_Editor implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* The Zapier helper.
|
||||
*
|
||||
* @var Zapier_Helper
|
||||
*/
|
||||
protected $zapier_helper;
|
||||
|
||||
/**
|
||||
* Zapier constructor.
|
||||
*
|
||||
* @param Zapier_Helper $zapier_helper The Zapier helper.
|
||||
*/
|
||||
public function __construct( Zapier_Helper $zapier_helper ) {
|
||||
$this->zapier_helper = $zapier_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the conditionals based in which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Zapier_Enabled_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'wpseo_publishbox_misc_actions', [ $this, 'add_publishbox_text' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Zapier text to the Classic Editor publish box.
|
||||
*
|
||||
* @param WP_Post $post The current post object.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_publishbox_text( WP_Post $post ) {
|
||||
if ( ! $this->zapier_helper->is_post_type_supported( $post->post_type )
|
||||
|| $this->zapier_helper->is_connected() ) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<div class="misc-pub-section yoast yoast-zapier-text">
|
||||
<svg class="yoast-zapier-text__icon" role="img" aria-hidden="true" focusable="false" width="2500" height="2500" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M159.999 128.056a76.55 76.55 0 0 1-4.915 27.024 76.745 76.745 0 0 1-27.032 4.923h-.108c-9.508-.012-18.618-1.75-27.024-4.919A76.557 76.557 0 0 1 96 128.056v-.112a76.598 76.598 0 0 1 4.91-27.02A76.492 76.492 0 0 1 127.945 96h.108a76.475 76.475 0 0 1 27.032 4.923 76.51 76.51 0 0 1 4.915 27.02v.112zm94.223-21.389h-74.716l52.829-52.833a128.518 128.518 0 0 0-13.828-16.349v-.004a129 129 0 0 0-16.345-13.816l-52.833 52.833V1.782A128.606 128.606 0 0 0 128.064 0h-.132c-7.248.004-14.347.62-21.265 1.782v74.716L53.834 23.665A127.82 127.82 0 0 0 37.497 37.49l-.028.02A128.803 128.803 0 0 0 23.66 53.834l52.837 52.833H1.782S0 120.7 0 127.956v.088c0 7.256.615 14.367 1.782 21.289h74.716l-52.837 52.833a128.91 128.91 0 0 0 30.173 30.173l52.833-52.837v74.72a129.3 129.3 0 0 0 21.24 1.778h.181a129.15 129.15 0 0 0 21.24-1.778v-74.72l52.838 52.837a128.994 128.994 0 0 0 16.341-13.82l.012-.012a129.245 129.245 0 0 0 13.816-16.341l-52.837-52.833h74.724c1.163-6.91 1.77-14 1.778-21.24v-.186c-.008-7.24-.615-14.33-1.778-21.24z" fill="#FF4A00"/></svg>
|
||||
<span>
|
||||
<?php
|
||||
\printf(
|
||||
/* translators: 1: Link start tag, 2: Yoast SEO, 3: Zapier, 4: Link closing tag. */
|
||||
\esc_html__( '%1$sConnect %2$s with %3$s%4$s to instantly share your published posts with 2000+ destinations such as Twitter, Facebook and more.', 'wordpress-seo-premium' ),
|
||||
'<a href="' . \esc_url( \admin_url( 'admin.php?page=wpseo_dashboard#top#integrations' ) ) . '" target="_blank">',
|
||||
'Yoast SEO',
|
||||
'Zapier',
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- The content is already escaped.
|
||||
WPSEO_Admin_Utils::get_new_tab_message() . '</a>'
|
||||
);
|
||||
?>
|
||||
</span>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Integrations\Third_Party;
|
||||
|
||||
use WP_Error;
|
||||
use Yoast\WP\SEO\Conditionals\Zapier_Enabled_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Meta_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Zapier_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Models\Indexable;
|
||||
|
||||
/**
|
||||
* Class to manage the triggering of the Zapier integration.
|
||||
*/
|
||||
class Zapier_Trigger implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* The meta helper.
|
||||
*
|
||||
* @var Meta_Helper
|
||||
*/
|
||||
protected $meta_helper;
|
||||
|
||||
/**
|
||||
* The Zapier helper.
|
||||
*
|
||||
* @var Zapier_Helper
|
||||
*/
|
||||
protected $zapier_helper;
|
||||
|
||||
/**
|
||||
* Zapier constructor.
|
||||
*
|
||||
* @param Meta_Helper $meta_helper The meta helper.
|
||||
* @param Zapier_Helper $zapier_helper The Zapier helper.
|
||||
*/
|
||||
public function __construct( Meta_Helper $meta_helper, Zapier_Helper $zapier_helper ) {
|
||||
$this->meta_helper = $meta_helper;
|
||||
$this->zapier_helper = $zapier_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the conditionals based in which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Zapier_Enabled_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'wpseo_save_indexable', [ $this, 'maybe_call_zapier' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides if Zapier should be triggered.
|
||||
*
|
||||
* Zapier should be triggered only if:
|
||||
* - we have a connection established
|
||||
* - the item is a post (in the Indexable sense, as opposed to taxonomies etc.)
|
||||
* - the item status is 'publish'
|
||||
* - we are not serving a REST request (to avoid triggering on the first request by the block editor)
|
||||
* - if the item hasn't been sent before
|
||||
* - if the post_date is recent (so we are not just updating a post published before enabling Zapier)
|
||||
*
|
||||
* @param Indexable $indexable The indexable.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function maybe_call_zapier( Indexable $indexable ) {
|
||||
if ( ! $this->zapier_helper->is_connected()
|
||||
|| $indexable->object_type !== 'post'
|
||||
|| $indexable->post_status !== 'publish'
|
||||
|| \defined( 'REST_REQUEST' ) && \REST_REQUEST
|
||||
|| $this->meta_helper->get_value( 'zapier_trigger_sent', $indexable->object_id ) === '1' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// All dates are GMT to prevent failing checks due to timezone differences.
|
||||
$post = \get_post( $indexable->object_id );
|
||||
$published_datetime_gmt = \strtotime( $post->post_date_gmt . ' +0000' );
|
||||
$half_an_hour_ago_datetime_gmt = ( \time() - ( \MINUTE_IN_SECONDS * 30 ) );
|
||||
if ( ! $this->zapier_helper->is_post_type_supported( $post->post_type )
|
||||
|| $published_datetime_gmt < $half_an_hour_ago_datetime_gmt ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->call_zapier( $indexable );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the Zapier trigger hook.
|
||||
*
|
||||
* @param Indexable $indexable The indexable.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function call_zapier( Indexable $indexable ) {
|
||||
$trigger_url = $this->zapier_helper->get_trigger_url();
|
||||
$zapier_data = $this->zapier_helper->get_data_for_zapier( $indexable );
|
||||
|
||||
$response = \wp_remote_post(
|
||||
$trigger_url,
|
||||
[
|
||||
'body' => $zapier_data,
|
||||
]
|
||||
);
|
||||
|
||||
if ( ! $response instanceof WP_Error ) {
|
||||
// Need to cast the new value to a string as booleans aren't supported.
|
||||
$this->meta_helper->set_value( 'zapier_trigger_sent', '1', $indexable->object_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Integrations\Third_Party;
|
||||
|
||||
use WPSEO_Admin_Asset_Manager;
|
||||
use WPSEO_Admin_Utils;
|
||||
use Yoast\WP\SEO\Conditionals\Yoast_Admin_And_Dashboard_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Zapier_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Presenters\Admin\Alert_Presenter;
|
||||
|
||||
/**
|
||||
* Zapier integration class for managing the toggle.
|
||||
*/
|
||||
class Zapier implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* The Zapier dashboard URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const ZAPIER_DASHBOARD_URL = 'https://zapier.com/app/zaps';
|
||||
|
||||
/**
|
||||
* Represents the admin asset manager.
|
||||
*
|
||||
* @var WPSEO_Admin_Asset_Manager
|
||||
*/
|
||||
protected $asset_manager;
|
||||
|
||||
/**
|
||||
* The Zapier helper.
|
||||
*
|
||||
* @var Zapier_Helper
|
||||
*/
|
||||
protected $zapier_helper;
|
||||
|
||||
/**
|
||||
* Returns the conditionals based in which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Yoast_Admin_And_Dashboard_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapier constructor.
|
||||
*
|
||||
* @param WPSEO_Admin_Asset_Manager $asset_manager The admin asset manager.
|
||||
* @param Zapier_Helper $zapier_helper The Zapier helper.
|
||||
*/
|
||||
public function __construct( WPSEO_Admin_Asset_Manager $asset_manager, Zapier_Helper $zapier_helper ) {
|
||||
$this->asset_manager = $asset_manager;
|
||||
$this->zapier_helper = $zapier_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
// Add the Zapier toggle to the Integrations tab in the admin.
|
||||
\add_filter( 'wpseo_integration_toggles', [ $this, 'add_integration_toggle' ] );
|
||||
\add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the required assets.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
$this->asset_manager->enqueue_style( 'monorepo' );
|
||||
\wp_enqueue_script( 'clipboard' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds integration toggles to the Integrations to be loaded.
|
||||
*
|
||||
* @param array $integration_toggles The feature toggles to extend.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function add_integration_toggle( array $integration_toggles ) {
|
||||
$integration_toggles[] = (object) [
|
||||
/* translators: %s: Zapier. */
|
||||
'name' => \sprintf( \esc_html__( '%s integration', 'wordpress-seo-premium' ), 'Zapier' ),
|
||||
'setting' => 'zapier_integration_active',
|
||||
'label' => \sprintf(
|
||||
/* translators: 1: Yoast SEO, 2: Zapier. */
|
||||
\__( 'Connecting %1$s to %2$s means you can instantly share your published posts with 2000+ destinations such as Twitter, Facebook and more.', 'wordpress-seo-premium' ),
|
||||
'Yoast SEO',
|
||||
'Zapier'
|
||||
),
|
||||
/* translators: %s: Zapier. */
|
||||
'read_more_label' => \sprintf( \__( 'Read more about %s.', 'wordpress-seo-premium' ), 'Zapier' ),
|
||||
'read_more_url' => 'https://yoa.st/46o',
|
||||
'after' => $this->toggle_after(),
|
||||
'order' => 20, // The SEMrush integration on Free has order => 10.
|
||||
];
|
||||
|
||||
return $integration_toggles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns additional content to be displayed after the Zapier toggle.
|
||||
*
|
||||
* @return string The additional content.
|
||||
*/
|
||||
private function toggle_after() {
|
||||
if ( $this->zapier_helper->is_connected() ) {
|
||||
return $this->get_connected_content();
|
||||
}
|
||||
|
||||
return $this->get_not_connected_content();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns additional content to be displayed when Zapier is connected.
|
||||
*
|
||||
* @return string The additional content.
|
||||
*/
|
||||
private function get_connected_content() {
|
||||
$alert = new Alert_Presenter(
|
||||
\sprintf(
|
||||
/* translators: 1: Yoast SEO, 2: Zapier. */
|
||||
\esc_html__( '%1$s is successfully connected to %2$s!', 'wordpress-seo-premium' ),
|
||||
'Yoast SEO',
|
||||
'Zapier'
|
||||
),
|
||||
'success'
|
||||
);
|
||||
|
||||
$output = '<div id="zapier-connection">';
|
||||
$output .= $alert->present();
|
||||
$output .= '<p><a href="' . self::ZAPIER_DASHBOARD_URL . '" class="yoast-button yoast-button--primary" type="button" target="_blank">' . \sprintf(
|
||||
/* translators: %s: Zapier. */
|
||||
\esc_html__( 'Go to your %s Dashboard', 'wordpress-seo-premium' ),
|
||||
'Zapier'
|
||||
) . WPSEO_Admin_Utils::get_new_tab_message() . '</a></p>';
|
||||
$output .= '<p>' . \sprintf(
|
||||
/* translators: 1: Zapier, 2: The Zapier API Key. */
|
||||
\esc_html__( '%1$s uses this API Key: %2$s', 'wordpress-seo-premium' ),
|
||||
'Zapier',
|
||||
'<strong>' . $this->zapier_helper->get_or_generate_zapier_api_key() . '</strong>'
|
||||
) . '</p>';
|
||||
$output .= '<p><button name="zapier_api_key_reset" value="1" type="submit" class="yoast-button yoast-button--secondary">' . \esc_html__( 'Reset API Key', 'wordpress-seo-premium' ) . '</button></p>';
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns additional content to be displayed when Zapier is not connected.
|
||||
*
|
||||
* @return string The additional content.
|
||||
*/
|
||||
private function get_not_connected_content() {
|
||||
$content = \sprintf(
|
||||
/* translators: 1: Yoast SEO, 2: Zapier, 3: Emphasis open tag, 4: Emphasis close tag. */
|
||||
\esc_html__( '%1$s is not connected to %2$s. To set up a connection, make sure you click %3$sSave changes%4$s first, then copy the given API key below and use it to %3$screate%4$s and %3$sturn on%4$s a Zap within your %2$s account.', 'wordpress-seo-premium' ),
|
||||
'Yoast SEO',
|
||||
'Zapier',
|
||||
'<em>',
|
||||
'</em>'
|
||||
);
|
||||
|
||||
$content .= '<br/><br/>';
|
||||
$content .= ' ' . \sprintf(
|
||||
/* translators: 1: Yoast SEO. */
|
||||
\esc_html__( 'Please note that you can only create 1 Zap with a trigger event from %1$s. Within this Zap you can choose one or more actions.', 'wordpress-seo-premium' ),
|
||||
'Yoast SEO'
|
||||
);
|
||||
|
||||
$alert = new Alert_Presenter(
|
||||
$content,
|
||||
'info'
|
||||
);
|
||||
|
||||
$output = '<div id="zapier-connection">';
|
||||
$output .= $alert->present();
|
||||
$output .= '<div class="yoast-field-group">';
|
||||
$output .= '<div class="yoast-field-group__title yoast-field-group__title--light">';
|
||||
$output .= '<label for="zapier-api-key">' . \sprintf(
|
||||
/* translators: %s: Zapier. */
|
||||
\esc_html__( '%s will ask for an API key. Use this one:', 'wordpress-seo-premium' ),
|
||||
'Zapier'
|
||||
) . '</label>';
|
||||
$output .= '</div>';
|
||||
$output .= '<div class="yoast-field-group__inline">';
|
||||
$output .= '<input class="yoast-field-group__inputfield" readonly type="text" id="zapier-api-key" name="wpseo[zapier_api_key]" value="' . $this->zapier_helper->get_or_generate_zapier_api_key() . '">';
|
||||
$output .= '<button type="button" class="yoast-button yoast-button--secondary" id="copy-zapier-api-key" data-clipboard-target="#zapier-api-key">' . \esc_html__( 'Copy to clipboard', 'wordpress-seo-premium' ) . '</button><br />';
|
||||
$output .= '</div>';
|
||||
$output .= '</div>';
|
||||
$output .= '<p><a href="' . self::ZAPIER_DASHBOARD_URL . '" class="yoast-button yoast-button--primary" type="button" target="_blank">' . \sprintf(
|
||||
/* translators: %s: Zapier. */
|
||||
\esc_html__( 'Create a Zap in %s', 'wordpress-seo-premium' ),
|
||||
'Zapier'
|
||||
) . WPSEO_Admin_Utils::get_new_tab_message() . '</a></p>';
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations;
|
||||
|
||||
use WPSEO_Upgrade_Manager;
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Class Upgrade_Integration.
|
||||
*/
|
||||
class Upgrade_Integration implements Integration_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'init', [ $this, 'run_upgrade' ], 11 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the upgrade for Yoast SEO Premium.
|
||||
*/
|
||||
public function run_upgrade() {
|
||||
$upgrade_manager = new WPSEO_Upgrade_Manager();
|
||||
$upgrade_manager->run_upgrade( \WPSEO_PREMIUM_VERSION );
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\Front_End_Conditional;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Class User_Profile_Integration.
|
||||
*/
|
||||
class User_Profile_Integration implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_filter( 'wpseo_schema_person_data', [ $this, 'filter_person_schema' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the Person schema to add our stored user schema fields.
|
||||
*
|
||||
* To see which fields we can store see this class's sibling admin integration.
|
||||
*
|
||||
* @param array $data The Person schema data.
|
||||
* @param int $user_id The user ID.
|
||||
*
|
||||
* @return array The Person schema data.
|
||||
*/
|
||||
public function filter_person_schema( $data, $user_id ) {
|
||||
$user_schema = \get_user_meta( $user_id, 'wpseo_user_schema', true );
|
||||
if ( \is_array( $user_schema ) ) {
|
||||
$data = \array_merge( $data, $user_schema );
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the conditionals based on which this loadable should be active.
|
||||
*
|
||||
* @return array The conditionals.
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Front_End_Conditional::class ];
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Integrations\Watchers;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Watcher for the wpseo option on Premium.
|
||||
*
|
||||
* Represents the option wpseo watcher for Premium.
|
||||
*/
|
||||
class Premium_Option_Wpseo_Watcher implements Integration_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* The options helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Watcher constructor.
|
||||
*
|
||||
* @param Options_Helper $options The options helper.
|
||||
*/
|
||||
public function __construct( Options_Helper $options ) {
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'update_option_wpseo', [ $this, 'check_zapier_option_disabled' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Zapier integration is disabled; if so, deletes the data.
|
||||
*
|
||||
* @param array $old_value The old value of the option.
|
||||
* @param array $new_value The new value of the option.
|
||||
*
|
||||
* @return bool Whether the Zapier data has been deleted or not.
|
||||
*/
|
||||
public function check_zapier_option_disabled( $old_value, $new_value ) {
|
||||
if ( \array_key_exists( 'zapier_integration_active', $new_value )
|
||||
&& $old_value['zapier_integration_active'] === true
|
||||
&& $new_value['zapier_integration_active'] === false ) {
|
||||
$this->options->set( 'zapier_subscription', [] );
|
||||
$this->options->set( 'zapier_api_key', '' );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Premium\Integrations\Watchers;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\No_Conditionals;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
use Yoast\WP\SEO\Models\Indexable;
|
||||
use Yoast\WP\SEO\Premium\Repositories\Prominent_Words_Repository;
|
||||
|
||||
/**
|
||||
* Watcher for changes that need to trigger actions related to prominent words.
|
||||
*/
|
||||
class Prominent_Words_Watcher implements Integration_Interface {
|
||||
|
||||
use No_Conditionals;
|
||||
|
||||
/**
|
||||
* The repository of the prominent words.
|
||||
*
|
||||
* @var Prominent_Words_Repository
|
||||
*/
|
||||
private $prominent_words_repository;
|
||||
|
||||
/**
|
||||
* The Prominent_Words_Watcher constructor
|
||||
*
|
||||
* @param Prominent_Words_Repository $prominent_words_repository The prominent words repository.
|
||||
*/
|
||||
public function __construct( Prominent_Words_Repository $prominent_words_repository ) {
|
||||
$this->prominent_words_repository = $prominent_words_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the action that triggers when an indexable is deleted.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'wpseo_indexable_deleted', [ $this, 'remove_prominent_words_for_indexable' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all prominent words for indexables if they are present.
|
||||
*
|
||||
* @param Indexable $indexable The indexable that got deleted.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function remove_prominent_words_for_indexable( $indexable ) {
|
||||
|
||||
$prominent_words = $this->prominent_words_repository->find_by_indexable_id( $indexable->id );
|
||||
|
||||
if ( \count( $prominent_words ) > 0 ) {
|
||||
$this->prominent_words_repository->delete_by_indexable_id( $indexable->id );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Integrations\Watchers;
|
||||
|
||||
use Yoast\WP\SEO\Conditionals\Zapier_Enabled_Conditional;
|
||||
use Yoast\WP\SEO\Helpers\Options_Helper;
|
||||
use Yoast\WP\SEO\Integrations\Integration_Interface;
|
||||
|
||||
/**
|
||||
* Watcher for resetting the Zapier API key.
|
||||
*
|
||||
* Represents the Zapier API key reset watcher for Premium.
|
||||
*/
|
||||
class Zapier_APIKey_Reset_Watcher implements Integration_Interface {
|
||||
|
||||
/**
|
||||
* The options helper.
|
||||
*
|
||||
* @var Options_Helper
|
||||
*/
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Watcher constructor.
|
||||
*
|
||||
* @param Options_Helper $options The options helper.
|
||||
*/
|
||||
public function __construct( Options_Helper $options ) {
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the conditionals based in which this loadable should be active.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conditionals() {
|
||||
return [ Zapier_Enabled_Conditional::class ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the integration.
|
||||
*
|
||||
* This is the place to register hooks and filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
\add_action( 'admin_init', [ $this, 'zapier_api_key_reset' ], 10 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Zapier API key must be reset; if so, deletes the data.
|
||||
*
|
||||
* @return bool Whether the Zapier data has been deleted or not.
|
||||
*/
|
||||
public function zapier_api_key_reset() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- The nonce is already validated.
|
||||
if ( isset( $_POST['zapier_api_key_reset'] ) && $_POST['zapier_api_key_reset'] === '1' ) {
|
||||
$this->options->set( 'zapier_api_key', '' );
|
||||
$this->options->set( 'zapier_subscription', [] );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user