Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,482 @@
<?php
/**
* Admin bar menu.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Core
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath;
use RankMath\Paper\Paper;
use RankMath\Traits\Ajax;
use RankMath\Traits\Meta;
use RankMath\Traits\Hooker;
use RankMath\Helpers\Arr;
use RankMath\Helpers\Url;
use RankMath\Helpers\Param;
defined( 'ABSPATH' ) || exit;
/**
* Admin_Bar_Menu class.
*/
class Admin_Bar_Menu {
use Hooker;
use Ajax;
use Meta;
/**
* The unique identifier used for the menu.
*
* @var string
*/
const MENU_IDENTIFIER = 'rank-math';
/**
* Hold menu items.
*
* @var array
*/
private $items = [];
/**
* Constructor method.
*/
public function __construct() {
$this->ajax( 'mark_page_as', 'mark_page_as' );
$this->action( 'admin_bar_menu', 'add_menu', 100 );
}
/**
* AJAX function to mark page as Pillar Content/Noindex/Nofollow.
*/
public function mark_page_as() {
check_ajax_referer( 'rank-math-ajax-nonce', 'security' );
$this->has_cap_ajax( 'onpage_general' );
$what = Param::post( 'what', '', FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_BACKTICK );
$object_id = Param::post( 'objectID', 0, FILTER_VALIDATE_INT );
$object_type = Param::post( 'objectType', '', FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_BACKTICK );
if ( ! $what || ! $object_id || ! $object_type ) {
return 0;
}
if ( 'pillar_content' === $what ) {
$current = $this->get_meta( $object_type, $object_id, 'rank_math_pillar_content' );
$updated = 'on' === $current ? 'off' : 'on';
$this->update_meta( $object_type, $object_id, 'rank_math_pillar_content', $updated );
die( '1' );
}
if ( 'noindex' === $what || 'nofollow' === $what ) {
$robots = (array) $this->get_meta( $object_type, $object_id, 'rank_math_robots' );
$robots = array_filter( $robots );
Arr::add_delete_value( $robots, $what );
$robots = $this->normalize_robots( $what, array_unique( $robots ) );
$this->update_meta( $object_type, $object_id, 'rank_math_robots', $robots );
if ( 'noindex' === $what ) {
$this->do_action( 'sitemap/invalidate_object_type', $object_type, $object_id );
}
die( '1' );
}
die();
}
/**
* Add SEO item to admin bar with context-specific submenu items.
*
* @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
*/
public function add_menu( $wp_admin_bar ) {
if ( ! $this->can_add_menu() ) {
return;
}
$this->add_root_menu();
if ( Helper::has_cap( 'titles' ) ) {
$this->add_page_menu();
}
if ( $this->is_front() ) {
$this->add_seo_tools();
}
if ( $this->can_add_mark_menu() ) {
$this->add_mark_page_menu();
}
/**
* Add item to rank math admin bar node.
*
* @param Admin_Bar_Menu $this Class instance.
*/
$this->do_action( 'admin_bar/items', $this );
$this->add_order();
uasort( $this->items, [ $this, 'sort_by_priority' ] );
array_walk( $this->items, [ $wp_admin_bar, 'add_node' ] );
}
/**
* Normalize robots.
*
* @param string $what Current admin menu process.
* @param array $robots Array to normalize.
*
* @return array
*/
private function normalize_robots( $what, $robots ) {
if ( 'noindex' !== $what ) {
return $robots;
}
if ( ! in_array( 'noindex', $robots, true ) ) {
$robots[] = ! in_array( 'index', $robots, true ) ? 'index' : '';
return $robots;
}
if ( false !== ( $key = array_search( 'index', $robots ) ) ) { // @codingStandardsIgnoreLine
unset( $robots[ $key ] );
}
return $robots;
}
/**
* Keep original order when uasort() deals with equal "priority" values.
*/
private function add_order() {
$order = 0;
foreach ( $this->items as &$item ) {
$item['order'] = $order++;
}
}
/**
* Add root menu.
*/
private function add_root_menu() {
$first_menu = get_transient( 'rank_math_first_submenu_id' );
$first_menu = $first_menu && 'rank-math' !== $first_menu ? str_replace( 'rank-math-', '', $first_menu ) : '';
$this->items['main'] = [
'id' => self::MENU_IDENTIFIER,
'title' => '<span class="rank-math-icon">' . $this->get_icon() . '</span><span class="rank-math-text">' . esc_html__( 'Rank Math SEO', 'rank-math' ) . '</span>',
'href' => Helper::get_admin_url( $first_menu ),
'meta' => [ 'title' => esc_html__( 'Rank Math Dashboard', 'rank-math' ) ],
'priority' => 10,
];
if ( current_user_can( 'manage_options' ) ) {
$this->add_sub_menu(
'dashboard',
[
'title' => esc_html__( 'Dashboard', 'rank-math' ),
'href' => $this->items['main']['href'],
'meta' => [ 'title' => esc_html__( 'Dashboard', 'rank-math' ) ],
'priority' => 20,
]
);
}
}
/**
* Add page menu.
*/
private function add_page_menu() {
$hash = [
'add_home_menu' => is_front_page(),
'add_post_type_menu' => is_singular( Helper::get_accessible_post_types() ) || is_home(),
'add_date_menu' => is_date(),
'add_taxonomy_menu' => is_archive() && ! is_post_type_archive() && ! is_author(),
'add_search_menu' => is_search(),
];
foreach ( $hash as $func => $can_run ) {
if ( true === $can_run ) {
$this->$func();
break;
}
}
}
/**
* Add homepage menu
*/
private function add_home_menu() {
$this->add_sub_menu(
'home',
[
'title' => esc_html__( 'Homepage SEO', 'rank-math' ),
'href' => Helper::get_settings_url( 'titles', 'homepage' ),
'meta' => [ 'title' => esc_html__( 'Edit Homepage SEO Settings', 'rank-math' ) ],
'priority' => 35,
]
);
}
/**
* Add post_type menu
*/
private function add_post_type_menu() {
$post_type = get_post_type();
if ( ! $post_type ) {
return;
}
$name = get_post_type_object( $post_type )->labels->name;
if ( is_home() ) {
$post_type = 'page';
$name = esc_html__( 'Pages', 'rank-math' );
}
$this->add_sub_menu(
'posttype',
[
/* translators: Post Type Singular Name */
'title' => sprintf( esc_html__( 'SEO Settings for %s', 'rank-math' ), $name ),
'href' => Helper::get_settings_url( 'titles', 'post-type-' . $post_type ),
'meta' => [ 'title' => esc_html__( 'Edit default SEO settings for this post type', 'rank-math' ) ],
'priority' => 35,
]
);
}
/**
* Add taxonomy menu
*/
private function add_taxonomy_menu() {
$term = get_queried_object();
if ( empty( $term ) || ! ( $term instanceof \WP_Term ) ) {
return;
}
$labels = get_taxonomy_labels( get_taxonomy( $term->taxonomy ) );
$this->add_sub_menu(
'tax',
[
/* translators: Taxonomy Singular Name */
'title' => sprintf( esc_html__( 'SEO Settings for %s', 'rank-math' ), $labels->name ),
'href' => Helper::get_settings_url( 'titles', 'taxonomy-' . $term->taxonomy ),
'meta' => [ 'title' => esc_html__( 'Edit SEO settings for this archive page', 'rank-math' ) ],
'priority' => 35,
]
);
}
/**
* Add date archive menu
*/
private function add_date_menu() {
$this->add_sub_menu(
'date',
[
'title' => esc_html__( 'SEO Settings for Date Archives', 'rank-math' ),
'href' => Helper::get_settings_url( 'titles', 'global' ),
'meta' => [ 'title' => esc_html__( 'Edit SEO settings for this archive page', 'rank-math' ) ],
'priority' => 35,
]
);
}
/**
* Add search result menu
*/
private function add_search_menu() {
$this->add_sub_menu(
'search',
[
'title' => esc_html__( 'SEO Settings for Search Page', 'rank-math' ),
'href' => Helper::get_settings_url( 'titles', 'global' ),
'meta' => [ 'title' => esc_html__( 'Edit SEO settings for the search results page', 'rank-math' ) ],
'priority' => 35,
]
);
}
/**
* Add mark page menu.
*/
private function add_mark_page_menu() {
$this->add_sub_menu(
'mark-me',
[
'title' => esc_html__( 'Mark this page', 'rank-math' ),
'href' => '#',
'priority' => 100,
]
);
$is_pillar_content = '';
$dashicon_format = '<span class="dashicons dashicons-%s rm-mark-page-icon"></span>';
if ( is_singular( Helper::get_accessible_post_types() ) ) {
if ( get_post_meta( get_the_ID(), 'rank_math_pillar_content', true ) === 'on' ) {
$is_pillar_content = sprintf( $dashicon_format, 'yes' );
}
$this->add_sub_menu(
'pillar-content',
[
'title' => $is_pillar_content . esc_html__( 'As Pillar Content', 'rank-math' ),
'href' => '#pillar_content',
'meta' => [ 'class' => 'mark-page-as' ],
],
'mark-me'
);
}
if ( Paper::get() ) {
$robots = Paper::get()->get_robots();
$noindex_check = in_array( 'noindex', $robots, true ) ? sprintf( $dashicon_format, 'yes' ) : '';
$this->add_sub_menu(
'no-index',
[
'title' => $noindex_check . esc_html__( 'As NoIndex', 'rank-math' ),
'href' => '#noindex',
'meta' => [ 'class' => 'mark-page-as' ],
],
'mark-me'
);
$nofollow_check = in_array( 'nofollow', $robots, true ) ? sprintf( $dashicon_format, 'yes' ) : '';
$this->add_sub_menu(
'no-follow',
[
'title' => $nofollow_check . esc_html__( 'As NoFollow', 'rank-math' ),
'href' => '#nofollow',
'meta' => [ 'class' => 'mark-page-as' ],
],
'mark-me'
);
}
}
/**
* Third party SEO Tools, like the Google Structured Data Testing Tool.
*/
private function add_seo_tools() {
$this->add_sub_menu(
'third-party',
[
'title' => esc_html__( 'External Tools', 'rank-math' ),
'href' => '#',
'priority' => 200,
]
);
$url = rawurlencode( Url::get_current_url() );
$items = [
'google-pagespeed' => [
'title' => esc_html__( 'Google PageSpeed', 'rank-math' ),
'href' => 'https://developers.google.com/speed/pagespeed/insights/?url=' . $url,
'meta' => [ 'title' => esc_html__( 'Google PageSpeed Insights', 'rank-math' ) ],
],
'google-richresults-mobile' => [
'title' => esc_html__( 'Google Rich Results (Mobile)', 'rank-math' ),
'href' => 'https://search.google.com/test/rich-results?url=' . $url . '&user_agent=1',
'meta' => [ 'title' => esc_html__( 'Google Rich Results Test - Googlebot Smartphone', 'rank-math' ) ],
],
'google-richresults-desktop' => [
'title' => esc_html__( 'Google Rich Results (Desktop)', 'rank-math' ),
'href' => 'https://search.google.com/test/rich-results?url=' . $url . '&user_agent=2',
'meta' => [ 'title' => esc_html__( 'Google Rich Results Test - Googlebot Desktop', 'rank-math' ) ],
],
'fb-debugger' => [
'title' => esc_html__( 'Facebook Debugger', 'rank-math' ),
'href' => 'https://developers.facebook.com/tools/debug/sharing/?q=' . $url,
'meta' => [ 'title' => esc_html__( 'Facebook Sharing Debugger', 'rank-math' ) ],
],
];
foreach ( $items as $id => $args ) {
$args['meta']['target'] = '_blank';
$this->add_sub_menu( $id, $args, 'third-party' );
}
}
/**
* Add sub menu item
*
* @param string $id Unique ID for the node.
* @param array $args Arguments for adding a node.
* @param string $parent_node Node parent.
*/
public function add_sub_menu( $id, $args, $parent_node = '' ) {
$args['priority'] = isset( $args['priority'] ) ? $args['priority'] : 999;
$args['id'] = 'rank-math-' . $id;
$args['parent'] = '' !== $parent_node ? 'rank-math-' . $parent_node : self::MENU_IDENTIFIER;
$this->items[ $id ] = $args;
}
/**
* Can current user has capability for admin menu.
*
* @return bool
*/
private function can_add_menu() {
return Helper::has_cap( 'admin_bar' );
}
/**
* Can add mark me menu.
*
* @return bool
*/
private function can_add_mark_menu() {
return $this->is_front() && Helper::has_cap( 'onpage_general' );
}
/**
* Is frontend.
*
* @return bool
*/
private function is_front() {
return ! is_admin() && ! is_preview();
}
/**
* Sort admin bar items callback.
*
* @param array $item1 Item A to compare.
* @param array $item2 Item B to compare.
*
* @return integer
*/
private function sort_by_priority( $item1, $item2 ) {
if ( $item1['priority'] === $item2['priority'] ) {
return $item1['order'] < $item2['order'] ? -1 : 1;
}
return $item1['priority'] < $item2['priority'] ? -1 : 1;
}
/**
* Get Rank Math icon.
*
* @param integer $width Width of the icon.
*
* @return string
*/
private function get_icon( $width = 20 ) {
return '<svg viewBox="0 0 462.03 462.03" xmlns="http://www.w3.org/2000/svg" width="' . $width . '"><g><path d="m462 234.84-76.17 3.43 13.43 21-127 81.18-126-52.93-146.26 60.97 10.14 24.34 136.1-56.71 128.57 54 138.69-88.61 13.43 21z"/><path d="m54.1 312.78 92.18-38.41 4.49 1.89v-54.58h-96.67zm210.9-223.57v235.05l7.26 3 89.43-57.05v-181zm-105.44 190.79 96.67 40.62v-165.19h-96.67z"/></g></svg>';
}
}

View File

@@ -0,0 +1,52 @@
<?php
/**
* Breadcrumbs for the Rank Math pages.
*
* @since 1.0.44
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Helper;
use RankMath\Helpers\Param;
use RankMath\Google\Console;
defined( 'ABSPATH' ) || exit;
/**
* Admin Header class.
*
* @codeCoverageIgnore
*/
class Admin_Breadcrumbs {
/**
* Display Header.
*/
public function display() {
?>
<div class="rank-math-breadcrumbs-wrap">
<div class="rank-math-breadcrumbs">
<span><?php echo esc_html__( 'Dashboard', 'rank-math' ); ?></span>
<span class="divider">/</span>
<span class="active"><?php echo esc_html( $this->get_page_title() ); ?></span>
</div>
</div>
<?php
}
/**
* Get Current Admin Page Title.
*/
private function get_page_title() {
$base = __( 'Modules', 'rank-math' );
if ( is_network_admin() ) {
$base = __( 'Help', 'rank-math' );
}
$default = 'rank-math' === Param::get( 'page' ) ? $base : get_admin_page_title();
return str_replace( '_', ' ', Param::get( 'view', $default, FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_BACKTICK ) );
}
}

View File

@@ -0,0 +1,135 @@
<?php
/**
* The nav tabs on the Dashboard page.
*
* @since 1.0.40
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Helper;
use RankMath\Helpers\Security;
use RankMath\Helpers\Param;
defined( 'ABSPATH' ) || exit;
/**
* Admin Dashboard Nav class.
*
* @codeCoverageIgnore
*/
class Admin_Dashboard_Nav {
/**
* Display dashboard tabs.
*/
public function display() {
$nav_links = $this->get_nav_links();
if ( empty( $nav_links ) ) {
return;
}
?>
<div class="rank-math-tab-nav" role="tablist" aria-orientation="horizontal">
<?php
foreach ( $nav_links as $id => $link ) {
$this->nav_link( $link );
}
?>
</div>
<?php
}
/**
* Get URL for dashboard nav links.
*
* @param array $link Link data.
* @return string Link URL.
*/
public function get_link_url( $link ) {
return is_network_admin() ?
Security::add_query_arg(
[
'page' => 'rank-math',
'view' => $link['id'],
],
network_admin_url( 'admin.php' )
) :
Helper::get_admin_url( $link['url'], $link['args'] );
}
/**
* Output dashboard nav link.
*
* @param array $link Link data.
* @return void
*/
public function nav_link( $link ) {
if ( isset( $link['cap'] ) && ! current_user_can( $link['cap'] ) ) {
return;
}
$default_tab = is_network_admin() ? 'help' : 'modules';
?>
<a
class="rank-math-tab<?php echo Param::get( 'view', $default_tab ) === sanitize_html_class( $link['id'] ) ? ' is-active' : ''; ?>"
href="<?php echo esc_url( $this->get_link_url( $link ) ); ?>"
title="<?php echo esc_attr( $link['title'] ); ?>">
<?php echo esc_html( $link['title'] ); ?>
</a>
<?php
}
/**
* Get dashbaord navigation links
*
* @return array
*/
private function get_nav_links() {
$links = [
'modules' => [
'id' => 'modules',
'url' => '',
'args' => 'view=modules',
'cap' => 'manage_options',
'title' => esc_html__( 'Modules', 'rank-math' ),
],
'help' => [
'id' => 'help',
'url' => '',
'args' => 'view=help',
'cap' => 'manage_options',
'title' => esc_html__( 'Help', 'rank-math' ),
],
'wizard' => [
'id' => 'wizard',
'url' => 'wizard',
'args' => '',
'cap' => 'manage_options',
'title' => esc_html__( 'Setup Wizard', 'rank-math' ),
],
];
if ( Helper::is_advanced_mode() ) {
$links['import-export'] = [
'id' => 'import-export',
'url' => 'status',
'args' => 'view=import_export',
'cap' => 'install_plugins',
'title' => esc_html__( 'Import &amp; Export', 'rank-math' ),
];
}
if ( Helper::is_plugin_active_for_network() ) {
unset( $links['help'] );
}
if ( is_network_admin() ) {
$links = [];
}
return apply_filters( 'rank_math/admin/dashboard_nav_links', $links );
}
}

View File

@@ -0,0 +1,157 @@
<?php
/**
* Header for the Rank Math pages
*
* @since 1.0.44
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Helper;
use RankMath\KB;
use RankMath\Helpers\Param;
defined( 'ABSPATH' ) || exit;
/**
* Admin Header class.
*
* @codeCoverageIgnore
*/
class Admin_Header {
/**
* Hold current screen ID.
*
* @var Current screen ID.
*/
private $screen_id = '';
/**
* Display Header.
*
* @param bool $show_breadcrumbs Determines whether to show breadcrumbs or not.
*/
public function display( $show_breadcrumbs ) {
$logo_url = '<a href="' . esc_url( Helper::get_admin_url() ) . '"><i class="rm-icon rm-icon-rank-math"></i></a>';
$this->screen_id = $this->get_current_screen();
?>
<div class="rank-math-header">
<div class="rank-math-logo">
<?php echo $logo_url; // phpcs:ignore ?>
</div>
<h1 class="rank-math-logo-text">
Rank Math SEO
<?php do_action( 'rank_math/pro_badge' ); ?>
</h1>
<?php $this->get_search_options(); ?>
<?php $this->get_mode_selector(); ?>
<?php do_action( 'rank_math/before_help_link' ); ?>
<a href="<?php echo esc_url( $this->get_help_link() ); ?>" title="<?php esc_attr_e( 'Rank Math Knowledge Base', 'rank-math' ); ?>" target="_blank" class="button rank-math-help"><i class="dashicons dashicons-editor-help"></i></a>
</div>
<?php
// Breadcrumbs.
if ( $show_breadcrumbs ) {
rank_math()->admin->display_admin_breadcrumbs();
}
}
/**
* Get Search Options.
*/
private function get_search_options() {
if (
! in_array(
$this->screen_id,
[
'rank-math_page_rank-math-options-general',
'rank-math_page_rank-math-options-titles',
'rank-math_page_rank-math-options-sitemap',
],
true
)
) {
return;
}
?>
<div class="rank-math-search-options">
<div class="components-input-control">
<div class="components-input-control__container">
<!-- <i class="rm-icon rm-icon-search"></i> -->
<input type="search" class="components-input-control__input" value="" placeholder="<?php esc_attr_e( 'Search Options', 'rank-math' ); ?>" style="width: 100%;">
<!-- <em class="clear-search dashicons dashicons-no-alt"></em> -->
</div>
</div>
</div>
<?php
}
/**
* Get Mode Selector.
*/
private function get_mode_selector() {
if (
! in_array(
$this->screen_id,
[
'toplevel_page_rank-math',
'rank-math_page_rank-math-status',
],
true
)
) {
return;
}
$is_advanced_mode = Helper::is_advanced_mode();
?>
<div class="rank-math-mode-selector">
<a href="#" class="<?php echo ! $is_advanced_mode ? 'active' : ''; ?>" data-mode="easy"><?php esc_attr_e( 'Easy Mode', 'rank-math' ); ?></a>
<a href="#" class="<?php echo $is_advanced_mode ? 'active' : ''; ?>" data-mode="advanced"><?php esc_attr_e( 'Advanced Mode', 'rank-math' ); ?></a>
</div>
<?php
}
/**
* Get Current Screen ID.
*/
private function get_help_link() {
$links = [
'import-export-settings' => 'import_export' === Param::get( 'view' ),
'version-control' => 'version_control' === Param::get( 'view' ) || 'rank-math-status' === Param::get( 'page' ),
'general-settings' => 'rank-math-options-general' === Param::get( 'page' ),
'titles-meta' => 'rank-math-options-titles' === Param::get( 'page' ),
'sitemap-general' => 'rank-math-options-sitemap' === Param::get( 'page' ),
'role-manager' => 'rank-math-role-manager' === Param::get( 'page' ),
'seo-analysis' => 'rank-math-seo-analysis' === Param::get( 'page' ),
'content-ai-restore-credits' => 'rank-math-content-ai-page' === Param::get( 'page' ),
];
$link = KB::get( 'knowledgebase', 'RM Header KB Icon' );
foreach ( $links as $key => $value ) {
if ( $value ) {
$link = KB::get( $key, 'Admin Bar ' . ucwords( str_replace( '-', ' ', $key ) ) );
break;
}
}
return $link;
}
/**
* Get Current Screen ID.
*/
private function get_current_screen() {
$screen = get_current_screen();
if ( empty( $screen ) ) {
return '';
}
return $screen->id;
}
}

View File

@@ -0,0 +1,479 @@
<?php
/**
* Admin helper Functions.
*
* This file contains functions needed on the admin screens.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\KB;
use RankMath\Helper;
use RankMath\Data_Encryption;
use RankMath\Helpers\Security;
use RankMath\Helpers\Param;
defined( 'ABSPATH' ) || exit;
/**
* Admin_Helper class.
*/
class Admin_Helper {
/**
* Get .htaccess related data.
*
* @return array
*/
public static function get_htaccess_data() {
if ( ! Helper::is_filesystem_direct() ) {
return [
'content' => '',
'writable' => false,
];
}
$wp_filesystem = Helper::get_filesystem();
if ( empty( $wp_filesystem ) ) {
return;
}
$htaccess_file = get_home_path() . '.htaccess';
return ! $wp_filesystem->exists( $htaccess_file ) ? false : [
'content' => $wp_filesystem->get_contents( $htaccess_file ),
'writable' => $wp_filesystem->is_writable( $htaccess_file ),
];
}
/**
* Get tooltip HTML.
*
* @param string $message Message to show in tooltip.
*
* @return string
*/
public static function get_tooltip( $message ) {
return '<span class="rank-math-tooltip"><em class="dashicons-before dashicons-editor-help"></em><span>' . $message . '</span></span>';
}
/**
* Get admin view file.
*
* @param string $view View filename.
*
* @return string Complete path to view
*/
public static function get_view( $view ) {
$view = sanitize_key( $view );
$view = rank_math()->admin_dir() . "views/{$view}.php";
if ( ! file_exists( $view ) ) {
Helper::redirect( Helper::get_admin_url() );
exit;
}
return $view;
}
/**
* Get taxonomies as choices.
*
* @param array $args (Optional) Arguments passed to filter list.
*
* @return array|bool
*/
public static function get_taxonomies_options( $args = [] ) {
global $wp_taxonomies;
$args = wp_parse_args( $args, [ 'public' => true ] );
$taxonomies = wp_filter_object_list( $wp_taxonomies, $args, 'and', 'label' );
return empty( $taxonomies ) ? false : [ 'off' => esc_html__( 'None', 'rank-math' ) ] + $taxonomies;
}
/**
* Registration data get/update.
*
* @param array|bool|null $data Array of data to save.
*
* @return array
*/
public static function get_registration_data( $data = null ) {
$row = 'rank_math_connect_data';
$keys = [
'username',
'email',
'api_key',
'plan',
];
// Setter.
if ( ! is_null( $data ) ) {
if ( false === $data ) {
update_option( 'rank_math_registration_skip', 1 );
return delete_option( $row );
}
foreach ( $keys as $key ) {
if ( isset( $data[ $key ] ) ) {
$data[ $key ] = Data_Encryption::encrypt( $data[ $key ] );
}
}
Helper::remove_notification( 'rank-math-site-url-mismatch' );
update_option( 'rank_math_registration_skip', 1 );
$connected = update_option( $row, $data );
do_action( 'rank_math/connect/account_connected', $data );
return $connected;
}
// Getter.
$options = Helper::is_plugin_active_for_network() ? get_blog_option( get_main_site_id(), $row, false ) : get_option( $row, false );
if ( empty( $options ) ) {
return false;
}
foreach ( $keys as $key ) {
if ( isset( $options[ $key ] ) ) {
$options[ $key ] = Data_Encryption::decrypt( $options[ $key ] );
}
}
if ( ! self::is_valid_registration( $options ) ) {
// Delete invalid registration data.
delete_option( $row );
// Ask the user to reconnect.
Helper::add_notification(
__( 'Unable to validate Rank Math SEO registration data.', 'rank-math' ) .
' <a href="' . esc_url( self::get_activate_url() ) . '">' . __( 'Please try reconnecting.', 'rank-math' ) . '</a> ' .
sprintf(
/* translators: KB Link */
__( 'If the issue persists, please try the solution described in our Knowledge Base article: %s', 'rank-math' ),
'<a href="' . KB::get( 'unable-to-encrypt', 'Registration Data' ) . '" target="_blank">' . __( '[3. Unable to Encrypt]', 'rank-math' ) . '</a>'
),
[ 'type' => 'error' ]
);
return false;
}
/**
* Filter whether we need to check for URL mismatch or not.
*/
$do_url_check = apply_filters( 'rank_math/registration/do_url_check', ! get_option( 'rank_math_siteurl_mismatch_notice_dismissed' ) );
if ( $do_url_check && isset( $options['site_url'] ) && Helper::get_home_url() !== $options['site_url'] ) {
$message = esc_html__( 'Seems like your site URL has changed since you connected to Rank Math.', 'rank-math' ) . ' <a href="' . self::get_activate_url() . '">' . esc_html__( 'Click here to reconnect.', 'rank-math' ) . '</a>';
Helper::add_notification(
$message,
[
'type' => 'warning',
'id' => 'rank-math-site-url-mismatch',
]
);
}
/**
* Ensure the site_url is returned if it is absent, as it is required for the Content AI.
*/
if ( empty( $options['site_url'] ) ) {
$options['site_url'] = Helper::get_home_url();
}
return $options;
}
/**
* Check if registration data is valid.
*
* @param array $data Registration data.
*
* @return bool
*/
public static function is_valid_registration( $data ) {
if ( empty( $data['username'] ) || empty( $data['email'] ) || empty( $data['api_key'] ) || empty( $data['plan'] ) ) {
return false;
}
if ( ! filter_var( $data['email'], FILTER_VALIDATE_EMAIL ) ) {
return false;
}
if ( strlen( $data['plan'] ) > 32 ) { // This can happen when the decryption fails for some reason.
return false;
}
return true;
}
/**
* Get user plan.
*/
public static function get_user_plan() {
$data = self::get_registration_data();
return $data['plan'];
}
/**
* Is user plan expired.
*
* @return boolean
*/
public static function is_plan_expired() {
$data = self::get_registration_data();
if ( ! isset( $data['plan'] ) ) {
return true;
}
return 'free' === $data['plan'];
}
/**
* Remove registration data and disconnect from RankMath.com.
*/
public static function deregister_user() {
$registered = self::get_registration_data();
if ( $registered && isset( $registered['username'] ) && isset( $registered['api_key'] ) ) {
Api::get()->deactivate_site( $registered['username'], $registered['api_key'] );
self::get_registration_data( false );
do_action( 'rank_math/deregister_site' );
}
}
/**
* Check if current page is media list page.
*
* @return bool
*/
public static function is_media_library() {
global $pagenow;
return 'upload.php' === $pagenow;
}
/**
* Check if current page is post list page.
*
* @return bool
*/
public static function is_post_list() {
global $pagenow;
return 'edit.php' === $pagenow;
}
/**
* Check if current page is post create/edit screen.
*
* @return bool
*/
public static function is_post_edit() {
global $pagenow;
return ! Helper::is_ux_builder() && ( 'post.php' === $pagenow || 'post-new.php' === $pagenow );
}
/**
* Check if current page is term create/edit screen.
*
* @return bool
*/
public static function is_term_edit() {
global $pagenow;
return 'term.php' === $pagenow || 'edit-tags.php' === $pagenow;
}
/**
* Check if current page is term create/term listing.
*
* @return bool
*/
public static function is_term_listing() {
global $pagenow;
return 'edit-tags.php' === $pagenow;
}
/**
* Check if current page is user create/edit screen.
*
* @return bool
*/
public static function is_user_edit() {
global $pagenow;
return 'profile.php' === $pagenow || 'user-edit.php' === $pagenow;
}
/**
* Check if current page is user or term create/edit screen.
*
* @return bool
*/
public static function is_term_profile_page() {
global $pagenow;
return self::is_term_edit() || self::is_user_edit();
}
/**
* Get Social Share buttons.
*
* @codeCoverageIgnore
*/
public static function get_social_share() {
if ( Helper::is_whitelabel() ) {
return;
}
$tw_link = KB::get( 'logo', 'Setup Wizard Tweet Button' );
$fb_link = rawurlencode( KB::get( 'logo', 'Facebook' ) );
/* translators: sitename */
$tw_message = rawurlencode( sprintf( esc_html__( 'I just installed @RankMathSEO #WordPress Plugin. It looks great! %s', 'rank-math' ), $tw_link ) );
/* translators: sitename */
$fb_message = rawurlencode( esc_html__( 'I just installed Rank Math SEO WordPress Plugin. It looks promising!', 'rank-math' ) );
$tweet_url = Security::add_query_arg(
[
'text' => $tw_message,
'hashtags' => 'SEO',
],
'https://twitter.com/intent/tweet'
);
$fb_share_url = Security::add_query_arg(
[
'u' => $fb_link,
'quote' => $fb_message,
'caption' => esc_html__( 'SEO by Rank Math', 'rank-math' ),
],
'https://www.facebook.com/sharer/sharer.php'
);
?>
<span class="wizard-share">
<a href="#" onclick="window.open('<?php echo esc_url( $tweet_url ); ?>', 'sharewindow', 'resizable,width=600,height=300'); return false;" class="share-twitter">
<span class="dashicons dashicons-twitter"></span> <?php esc_html_e( 'Tweet', 'rank-math' ); ?>
</a>
<a href="#" onclick="window.open('<?php echo esc_url( $fb_share_url ); ?>', 'sharewindow', 'resizable,width=600,height=300'); return false;" class="share-facebook">
<span class="dashicons dashicons-facebook-alt"></span> <?php esc_html_e( 'Share', 'rank-math' ); ?>
</a>
</span>
<?php
}
/**
* Get product activation URL.
*
* @param string $redirect_to Redirecto url.
*
* @return string Activate URL.
*/
public static function get_activate_url( $redirect_to = null ) {
if ( empty( $redirect_to ) ) {
$redirect_to = Security::add_query_arg_raw(
[
'page' => 'rank-math',
'view' => 'help',
'nonce' => wp_create_nonce( 'rank_math_register_product' ),
],
( is_multisite() && is_plugin_active_for_network( plugin_basename( RANK_MATH_FILE ) ) ) ? network_admin_url( 'admin.php' ) : admin_url( 'admin.php' )
);
} else {
$redirect_to = Security::add_query_arg_raw(
[
'nonce' => wp_create_nonce( 'rank_math_register_product' ),
],
$redirect_to
);
}
$args = [
'site' => rawurlencode( home_url() ),
'r' => rawurlencode( $redirect_to ),
];
return apply_filters(
'rank_math/license/activate_url',
Security::add_query_arg_raw( $args, RANK_MATH_SITE_URL . '/auth' ),
$args
);
}
/**
* Check if page is set as Homepage.
*
* @since 1.0.42
*
* @return boolean
*/
public static function is_home_page() {
$front_page = (int) get_option( 'page_on_front' );
if ( Helper::is_divi_frontend_editor() ) {
$p = get_post();
return ! empty( $p->ID ) && $p->ID === $front_page;
}
return $front_page && self::is_post_edit() && (int) Param::get( 'post' ) === $front_page;
}
/**
* Check if page is set as Posts Page.
*
* @since 1.0.43
*
* @return boolean
*/
public static function is_posts_page() {
$posts_page = (int) get_option( 'page_for_posts' );
return $posts_page && self::is_post_edit() && (int) Param::get( 'post' ) === $posts_page;
}
/**
* Get Trends icon <svg> element.
*
* @return string
*/
public static function get_trends_icon_svg() {
return '<svg viewBox="0 0 610 610"><path d="M18.85,446,174.32,290.48l58.08,58.08L76.93,504a14.54,14.54,0,0,1-20.55,0L18.83,466.48a14.54,14.54,0,0,1,0-20.55Z" style="fill:#4285f4"/><path d="M242.65,242.66,377.59,377.6l-47.75,47.75a14.54,14.54,0,0,1-20.55,0L174.37,290.43l47.75-47.75A14.52,14.52,0,0,1,242.65,242.66Z" style="fill:#ea4335"/><polygon points="319.53 319.53 479.26 159.8 537.34 217.88 377.61 377.62 319.53 319.53" style="fill:#fabb05"/><path d="M594.26,262.73V118.61h0a16.94,16.94,0,0,0-16.94-16.94H433.2a16.94,16.94,0,0,0-12,28.92L565.34,274.71h0a16.94,16.94,0,0,0,28.92-12Z" style="fill:#34a853"/><rect width="610" height="610" style="fill:none"/></svg>';
}
/**
* Check if siteurl & home options are both valid URLs.
*
* @return boolean
*/
public static function is_site_url_valid() {
return (bool) filter_var( get_option( 'siteurl' ), FILTER_VALIDATE_URL ) && (bool) filter_var( get_option( 'home' ), FILTER_VALIDATE_URL );
}
/**
* Maybe show notice about invalid siteurl.
*/
public static function maybe_show_invalid_siteurl_notice() {
if ( ! self::is_site_url_valid() ) {
?>
<p class="notice notice-warning notice-alt notice-connect-disabled">
<?php
printf(
// Translators: 1 is "WordPress Address (URL)", 2 is "Site Address (URL)", 3 is a link to the General Settings, with "WordPress General Settings" as anchor text.
esc_html__( 'Rank Math cannot be connected because your site URL doesn\'t appear to be a valid URL. If the domain name contains special characters, please make sure to use the encoded version in the %1$s &amp; %2$s fields on the %3$s page.', 'rank-math' ),
'<strong>' . esc_html__( 'WordPress Address (URL)', 'rank-math' ) . '</strong>',
'<strong>' . esc_html__( 'Site Address (URL)', 'rank-math' ) . '</strong>',
'<a href="' . esc_url( admin_url( 'options-general.php' ) ) . '">' . esc_html__( 'WordPress General Settings', 'rank-math' ) . '</a>'
);
?>
</p>
<?php
}
}
}

View File

@@ -0,0 +1,123 @@
<?php
/**
* The admin bootstrap of the plugin.
*
* @since 1.0.9
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Helper;
use RankMath\Updates;
use RankMath\Traits\Hooker;
defined( 'ABSPATH' ) || exit;
/**
* Admin_Init class.
*
* @codeCoverageIgnore
*/
class Admin_Init {
use Hooker;
/**
* The Constructor.
*/
public function __construct() {
rank_math()->admin = new Admin();
rank_math()->admin_assets = new Assets();
$this->load_review_reminders();
$this->load_pro_notice();
$this->load_setup_wizard();
$this->load_post_columns_and_filters();
$this->run(
[
rank_math()->admin,
rank_math()->admin_assets,
new Admin_Menu(),
new Option_Center(),
new Notices(),
new CMB2_Fields(),
new Metabox\Metabox(),
new Import_Export(),
new Updates(),
new Watcher(),
]
);
/**
* Fires when admin is loaded.
*/
$this->do_action( 'admin/loaded' );
}
/**
* Load out post list and edit screen class.
*/
private function load_post_columns_and_filters() {
$this->run( [ new Bulk_Actions() ] );
if ( Admin_Helper::is_post_list() || Admin_Helper::is_media_library() || Admin_Helper::is_term_listing() || wp_doing_ajax() ) {
$this->run(
[
new Post_Columns(),
new Post_Filters(),
]
);
}
}
/**
* Load review tab in metabox & footer notice.
*/
private function load_review_reminders() {
if ( get_option( 'rank_math_already_reviewed' ) ) {
return;
}
$this->run( [ new Ask_Review() ] );
}
/**
* Load Pro reminder notice.
*/
private function load_pro_notice() {
if ( ! is_main_site() ) {
return;
}
if ( defined( 'RANK_MATH_PRO_FILE' ) || get_option( 'rank_math_already_upgraded' ) ) {
return;
}
$this->run( [ new Pro_Notice() ] );
}
/**
* Run all the runners.
*
* @param array $runners Instances of runner classes.
*/
private function run( $runners ) {
foreach ( $runners as $runner ) {
$runner->hooks();
}
}
/**
* Load setup wizard.
*/
private function load_setup_wizard() {
if ( Helper::is_wizard() ) {
new Setup_Wizard();
}
}
}

View File

@@ -0,0 +1,309 @@
<?php
/**
* The admin pages of the plugin.
*
* @since 1.0.9
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\KB;
use RankMath\Helper;
use RankMath\Helpers\Param;
use RankMath\Runner;
use RankMath\Traits\Hooker;
use RankMath\Traits\Ajax;
use RankMath\Admin\Page;
defined( 'ABSPATH' ) || exit;
/**
* Admin_Menu class.
*
* @codeCoverageIgnore
*/
class Admin_Menu implements Runner {
use Hooker;
use Ajax;
/**
* Register hooks.
*/
public function hooks() {
$this->action( 'init', 'register_pages' );
$this->action( 'admin_menu', 'fix_admin_menu', 999 );
$this->action( 'admin_head', 'icon_css' );
$this->ajax( 'remove_offer_page', 'remove_offer_page' );
}
/**
* Register admin pages for plugin.
*/
public function register_pages() {
$this->maybe_deregister();
if ( Helper::is_invalid_registration() && ! is_network_admin() ) {
return;
}
$current_user = wp_get_current_user();
$capabilities = array_keys( Helper::get_roles_capabilities() );
if ( empty( array_intersect( $current_user->roles, $capabilities ) ) && ! current_user_can( 'setup_network' ) ) {
return;
}
$modules = rank_math()->manager->modules;
$data = [];
foreach ( $modules as $id => $module ) {
$data[ $id ] = array_merge(
[
'id' => $module->get_id(),
'isActive' => $module->is_active(),
'isHidden' => $module->is_hidden(),
'isPro' => $module->is_pro_module(),
],
$module->get_args()
);
}
// Dashboard / Welcome / About.
new Page(
'rank-math',
esc_html__( 'Rank Math', 'rank-math' ),
[
'position' => 50,
'menu_title' => 'Rank Math',
'capability' => 'manage_options',
'icon' => 'data:image/svg+xml;base64,' . \base64_encode( '<svg viewBox="0 0 462.03 462.03" xmlns="http://www.w3.org/2000/svg" width="20"><g fill="#fff"><path d="m462 234.84-76.17 3.43 13.43 21-127 81.18-126-52.93-146.26 60.97 10.14 24.34 136.1-56.71 128.57 54 138.69-88.61 13.43 21z"/><path d="m54.1 312.78 92.18-38.41 4.49 1.89v-54.58h-96.67zm210.9-223.57v235.05l7.26 3 89.43-57.05v-181zm-105.44 190.79 96.67 40.62v-165.19h-96.67z"/></g></svg>' ), // phpcs:ignore -- This should not cause any issue as we only pass a static svg code.
'render' => Admin_Helper::get_view( 'dashboard' ),
'classes' => [ 'rank-math-page' ],
'assets' => [
'styles' => [ 'rank-math-dashboard' => '' ],
'scripts' => [
'lodash' => '',
'rank-math-components' => '',
'rank-math-dashboard' => '',
'rank-math-modules' => rank_math()->plugin_url() . 'assets/admin/js/modules.js',
],
'json' => [
'isPro' => defined( 'RANK_MATH_PRO_FILE' ),
'isSiteConnected' => Helper::is_site_connected(),
'registerProductNonce' => wp_create_nonce( 'rank_math_register_product' ),
'activateUrl' => Admin_Helper::get_activate_url(),
'isSiteUrlValid' => Admin_Helper::is_site_url_valid(),
'isAdvancedMode' => Helper::is_advanced_mode(),
'contentAiPlan' => Helper::get_content_ai_plan(),
'data' => $data,
'isPluginActiveForNetwork' => Helper::is_plugin_active_for_network(),
'isNetworkAdmin' => is_network_admin(),
'canUser' => [
'manageOptions' => current_user_can( 'manage_options' ),
'setupNetwork' => current_user_can( 'setup_network' ),
'installPlugins' => current_user_can( 'install_plugins' ),
],
],
],
'is_network' => is_network_admin() && Helper::is_plugin_active_for_network(),
]
);
}
/**
* Fix menu names.
*/
public function fix_admin_menu() {
// Replace the main menu name "Rank Math" with "Rank Math SEO".
global $menu;
foreach ( $menu as $key => $item ) {
if ( 'Rank Math' === $item[0] ) {
$menu[ $key ][0] = esc_html__( 'Rank Math SEO', 'rank-math' ); // phpcs:ignore -- This is required to change the menu name without changing its slug `rank-math`
break;
}
}
// Replace the first submenu name "Rank Math" with "Dashboard".
global $submenu;
if ( ! isset( $submenu['rank-math'] ) ) {
return;
}
if ( 'Rank Math' === $submenu['rank-math'][0][0] ) {
if ( current_user_can( 'manage_options' ) ) {
$plan = Helper::get_content_ai_plan();
$notification = ( empty( $plan ) || 'free' === $plan ) && get_option( 'rank_math_view_modules' ) ? ' <span class="awaiting-mod count-1"><span class="pending-count" aria-hidden="true">1</span></span>' : '';
$submenu['rank-math'][0][0] = esc_html__( 'Dashboard', 'rank-math' ) . $notification; // phpcs:ignore -- This is required to change the menu name when the plugin is not configured.
} else {
unset( $submenu['rank-math'][0] );
}
}
if ( empty( $submenu['rank-math'] ) ) {
return;
}
$submenu['rank-math'][] = [ esc_html__( 'Help &amp; Support', 'rank-math' ) . '<i class="dashicons dashicons-external" style="font-size:12px;vertical-align:-2px;height:10px;"></i>', 'manage_options', KB::get( 'knowledgebase', 'Sidebar Help Link' ) ]; // phpcs:ignore -- A custom link to our KB article.
$this->add_offer_link( $submenu );
// Store ID of first_menu item so we can use it in the Admin menu item.
set_transient( 'rank_math_first_submenu_id', array_values( $submenu['rank-math'] )[0][2] );
}
/**
* Print icon CSS for admin menu bar.
*/
public function icon_css() {
?>
<script type="text/javascript">
// Open RM KB menu link in the new tab.
jQuery( document ).ready( function( $ ) {
$( "ul#adminmenu a[href$='<?php KB::the( 'knowledgebase', 'Sidebar Help Link' ); ?>']" ).attr( 'target', '_blank' );
$( "ul#adminmenu a[href$='<?php KB::the( 'offer', 'Offer Menu Item' ); ?>']" ).attr( 'target', '_blank' ).on( 'click', function() {
$( this ).remove()
$.ajax( {
url: window.ajaxurl,
type: 'POST',
data: {
action: 'rank_math_remove_offer_page',
security: rankMath.security,
},
} )
} );
} );
</script>
<style>
#wp-admin-bar-rank-math .rank-math-icon {
display: inline-block;
top: 6px;
position: relative;
padding-right: 10px;
max-width: 20px;
}
#wp-admin-bar-rank-math .rank-math-icon svg {
fill-rule: evenodd;
fill: #dedede;
}
#wp-admin-bar-rank-math:hover .rank-math-icon svg {
fill-rule: evenodd;
fill: #00b9eb;
}
#toplevel_page_rank-math:not(.wp-has-submenu) {
display: none;
}
.multisite.network-admin #toplevel_page_rank-math {
display: block;
}
#toplevel_page_rank-math a[href$='<?php KB::the( 'offer', 'Offer Menu Item' ); ?>'],
#toplevel_page_rank-math a[href$='<?php KB::the( 'offer', 'Offer Menu Item' ); ?>']:hover,
#toplevel_page_rank-math a[href$='<?php KB::the( 'offer', 'Offer Menu Item' ); ?>']:focus {
background-color: #10AC84;
color: #fff;
}
</style>
<?php
}
/**
* Check for deactivation.
*/
private function maybe_deregister() {
if ( ! Helper::has_cap( 'general' ) || 'deregister' !== Param::post( 'registration-action' ) ) {
return;
}
$nonce = Param::post( '_wpnonce' );
if ( ! $nonce || ! wp_verify_nonce( $nonce, 'rank_math_register_product' ) ) {
return;
}
Admin_Helper::deregister_user();
}
/**
* Function to add Offer link based on the date range.
*
* @param array $submenu Submenu items.
*/
private function add_offer_link( &$submenu ) {
$offer = $this->get_active_offer();
if ( ! $offer ) {
return;
}
$submenu['rank-math'][] = [ current( $offer ) . '&nbsp;', 'manage_options', KB::get( 'offer', 'Offer Menu Item' ) ];
}
/**
* Ajax handler callback to store active offer so it doesn't show up again on the site.
*/
public function remove_offer_page() {
check_ajax_referer( 'rank-math-ajax-nonce', 'security' );
$offer = $this->get_active_offer();
set_site_transient( 'rank_math_active_offer', key( $offer ) );
}
/**
* Function to get active offer
*/
private function get_active_offer() {
// Early Bail if PRO plugin is active.
if ( defined( 'RANK_MATH_PRO_FILE' ) ) {
return false;
}
$timezone = new \DateTimeZone( 'Asia/Kolkata' );
$current_date = new \DateTime( 'now', $timezone );
$dates = [
'halloween' => [
'start' => '2025-10-29',
'end' => '2025-11-05',
'text' => esc_html__( 'Halloween Sale', 'rank-math' ),
],
'anniversary' => [
'start' => '2025-11-05',
'end' => '2025-11-12',
'text' => esc_html__( 'Anniversary Sale', 'rank-math' ),
],
'black-friday' => [
'start' => '2025-11-12',
'end' => '2025-11-29',
'text' => esc_html__( 'Black Friday Sale', 'rank-math' ),
],
'cyber-monday' => [
'start' => '2025-11-29',
'end' => '2025-12-03',
'text' => esc_html__( 'Cyber Monday Sale', 'rank-math' ),
],
'christmas' => [
'start' => '2025-12-24',
'end' => '2025-12-31',
'text' => esc_html__( 'Christmas Sale', 'rank-math' ),
],
'new-year' => [
'start' => '2025-12-31',
'end' => '2026-01-07',
'text' => esc_html__( 'New Year Sale', 'rank-math' ),
],
];
$stored_offer = get_site_transient( 'rank_math_active_offer' );
$active_offer = '';
foreach ( $dates as $key => $date ) {
$start_date = new \DateTime( $date['start'] . ' 16:00:00', $timezone );
$end_date = new \DateTime( $date['end'] . ' 16:00:00', $timezone );
if ( $stored_offer !== $key && $current_date >= $start_date && $current_date <= $end_date ) {
$active_offer = [ $key => $date['text'] ];
break;
}
}
return $active_offer;
}
}

View File

@@ -0,0 +1,563 @@
<?php
/**
* The admin-specific functionality of the plugin.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Runner;
use RankMath\Helper;
use RankMath\Helpers\Str;
use RankMath\Helpers\DB as DB_Helper;
use RankMath\Helpers\Param;
use RankMath\Admin\Admin_Helper;
use RankMath\Traits\Ajax;
use RankMath\Traits\Hooker;
defined( 'ABSPATH' ) || exit;
/**
* Admin class.
*
* @codeCoverageIgnore
*/
class Admin implements Runner {
use Hooker;
use Ajax;
/**
* Register hooks.
*/
public function hooks() {
$this->action( 'init', 'flush', 999 );
$this->filter( 'user_contactmethods', 'update_user_contactmethods' );
$this->action( 'profile_update', 'profile_update', 10, 3 );
$this->action( 'admin_footer', 'convert_additional_profile_url_to_textarea' );
$this->action( 'save_post', 'canonical_check_notice' );
$this->action( 'cmb2_save_options-page_fields', 'update_is_configured_value', 10, 2 );
$this->filter( 'action_scheduler_pastdue_actions_check_pre', 'as_exclude_pastdue_actions' );
$this->filter( 'rank_math/pro_badge', 'offer_icon' );
$this->filter( 'load_script_translation_file', 'load_script_translation_file', 10, 3 );
// Use woocommerce textdomain for the Actiion scheduler strings.
$this->filter( 'gettext', 'remap_action_scheduler_translation', 10, 3 );
$this->filter( 'gettext_with_context', 'remap_action_scheduler_translation_with_context', 10, 4 );
// AJAX.
$this->ajax( 'search_pages', 'search_pages' );
$this->ajax( 'is_keyword_new', 'is_keyword_new' );
$this->ajax( 'save_checklist_layout', 'save_checklist_layout' );
$this->ajax( 'deactivate_plugins', 'deactivate_plugins' );
}
/**
* Update user profile.
*
* @param int $user_id The user ID.
* @param array $old_user_data Old user data.
* @param array $userdata User data.
*/
public function profile_update( $user_id, $old_user_data, $userdata ) {
if ( ! current_user_can( 'edit_user', $user_id ) ) {
return false;
}
$nonce = Param::post( '_wpnonce', '', FILTER_SANITIZE_SPECIAL_CHARS );
if ( ! wp_verify_nonce( $nonce, 'update-user_' . $user_id ) ) {
return false;
}
$twitter = Param::post( 'twitter', '', FILTER_SANITIZE_URL );
$facebook = Param::post( 'facebook', '', FILTER_SANITIZE_URL );
$additional_profile_urls = Param::post( 'additional_profile_urls', '' );
if ( $additional_profile_urls ) {
$additional_profile_urls = array_map( 'sanitize_url', explode( PHP_EOL, $additional_profile_urls ) );
$additional_profile_urls = implode( ' ', $additional_profile_urls );
}
update_user_meta( $user_id, 'twitter', $twitter );
update_user_meta( $user_id, 'facebook', $facebook );
update_user_meta( $user_id, 'additional_profile_urls', $additional_profile_urls );
}
/**
* Flush the rewrite rules once if the rank_math_flush_rewrite option is set.
*/
public function flush() {
if ( get_option( 'rank_math_flush_rewrite' ) ) {
flush_rewrite_rules();
delete_option( 'rank_math_flush_rewrite' );
}
if ( 'rank-math' === Param::get( 'page' ) && get_option( 'rank_math_view_modules' ) ) {
delete_option( 'rank_math_view_modules' );
}
}
/**
* Add Facebook and Twitter as user contact methods.
*
* @param array $contactmethods Current contact methods.
* @return array New contact methods with extra items.
*
* @copyright Copyright (C) 2008-2019, Yoast BV
* The following code is a derivative work of the code from the Yoast(https://github.com/Yoast/wordpress-seo/), which is licensed under GPL v3.
*/
public function update_user_contactmethods( $contactmethods ) {
$contactmethods['twitter'] = esc_html__( 'Twitter username (without @)', 'rank-math' );
$contactmethods['facebook'] = esc_html__( 'Facebook profile URL', 'rank-math' );
$contactmethods['additional_profile_urls'] = esc_html__( 'Additional profile URLs', 'rank-math' );
return $contactmethods;
}
/**
* Display admin header.
*
* @param bool $show_breadcrumbs Determines whether to show breadcrumbs or not.
*/
public function display_admin_header( $show_breadcrumbs = true ) {
$nav_tabs = new Admin_Header();
$nav_tabs->display( $show_breadcrumbs );
}
/**
* Display admin breadcrumbs.
*/
public function display_admin_breadcrumbs() {
$nav_tabs = new Admin_Breadcrumbs();
$nav_tabs->display();
}
/**
* Display dashboard tabs.
*/
public function display_dashboard_nav() {
$nav_tabs = new Admin_Dashboard_Nav();
$nav_tabs->display();
}
/**
* Show notice when canonical URL is not a valid URL.
*
* @param int $post_id The post ID.
*/
public function canonical_check_notice( $post_id ) {
$post_type = get_post_type( $post_id );
$is_allowed = in_array( $post_type, Helper::get_allowed_post_types(), true );
if ( ! $is_allowed || Helper::is_autosave() || Helper::is_ajax() || isset( $_REQUEST['bulk_edit'] ) ) { // phpcs:ignore
return $post_id;
}
if ( ! empty( $_POST['rank_math_canonical_url'] ) && false === Param::post( 'rank_math_canonical_url', false, FILTER_VALIDATE_URL ) ) { // phpcs:ignore
$message = esc_html__( 'The canonical URL you entered does not seem to be a valid URL. Please double check it in the SEO meta box &raquo; Advanced tab.', 'rank-math' );
Helper::add_notification( $message, [ 'type' => 'error' ] );
}
}
/**
* Save checklist layout.
*/
public function save_checklist_layout() {
check_ajax_referer( 'rank-math-ajax-nonce', 'security' );
$this->has_cap_ajax( 'onpage_general' );
if ( empty( $_POST['layout'] ) || ! is_array( $_POST['layout'] ) ) {
return;
}
$layout = Param::post( 'layout', [], FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
$allowed = [
'basic' => 1,
'advanced' => 1,
'title-readability' => 1,
'content-readability' => 1,
];
$layout = array_intersect_key( $layout, $allowed );
update_user_meta( get_current_user_id(), 'rank_math_metabox_checklist_layout', $layout );
exit;
}
/**
* Ajax handler to search pages based on the searched string. Used in the Local SEO Settings.
*/
public function search_pages() {
check_ajax_referer( 'rank-math-ajax-nonce', 'security' );
$this->has_cap_ajax( 'general' );
$term = Param::get( 'term' );
if ( empty( $term ) ) {
exit;
}
global $wpdb;
$pages = DB_Helper::get_results(
$wpdb->prepare(
"SELECT ID, post_title FROM {$wpdb->prefix}posts WHERE post_type = 'page' AND post_status = 'publish' AND post_title LIKE %s",
"%{$wpdb->esc_like( $term )}%"
),
ARRAY_A
);
$data = [];
foreach ( $pages as $page ) {
$data[] = [
'id' => $page['ID'],
'text' => $page['post_title'],
'url' => get_permalink( $page['ID'] ),
];
}
wp_send_json( [ 'results' => $data ] );
}
/**
* Check if the keyword has been used before for another post.
*/
public function is_keyword_new() {
global $wpdb;
check_ajax_referer( 'rank-math-ajax-nonce', 'security' );
$this->has_cap_ajax( 'onpage_general' );
$result = [ 'isNew' => true ];
if ( empty( $_GET['keyword'] ) ) {
$this->success( $result );
}
$keyword = Param::get( 'keyword', '', FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_BACKTICK );
$object_id = Param::get( 'objectID', 0, FILTER_VALIDATE_INT );
$object_type = Param::get( 'objectType', '', FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_BACKTICK );
$column_ids = [
'post' => 'ID',
'term' => 'term_id',
'user' => 'ID',
];
if ( ! in_array( $object_type, [ 'post', 'term', 'user' ], true ) ) {
$object_type = 'post';
}
$main = $wpdb->{$object_type . 's'};
$meta = $wpdb->{$object_type . 'meta'};
$query = sprintf( 'select %2$s.%1$s from %2$s inner join %3$s on %2$s.%1$s = %3$s.%4$s_id where ', $column_ids[ $object_type ], $main, $meta, $object_type );
if ( 'post' === $object_type ) {
$query .= sprintf( '%s.post_status = \'publish\' and ', $main );
}
$query .= sprintf( '%1$s.meta_key = \'rank_math_focus_keyword\' and ( %1$s.meta_value = %2$s OR %1$s.meta_value like %3$s ) and %1$s.%4$s_id != %5$d', $meta, '%s', '%s', $object_type, $object_id );
$data = DB_Helper::get_row( $wpdb->prepare( $query, $keyword, $wpdb->esc_like( $keyword ) . ',%' ) );
$result['isNew'] = empty( $data );
$this->success( $result );
}
/**
* Get link suggestions for the current post.
*
* @param int|WP_Post $post Current post.
* @return array
*/
public function get_link_suggestions( $post ) {
global $pagenow;
if ( 'post-new.php' === $pagenow ) {
return;
}
$output = [];
$post = get_post( $post );
$args = [
'post_type' => $post->post_type,
'post__not_in' => [ $post->ID ],
'posts_per_page' => 5,
'meta_key' => 'rank_math_pillar_content',
'meta_value' => 'on',
'tax_query' => [ 'relation' => 'OR' ],
];
$taxonomies = Helper::get_object_taxonomies( $post, 'names' );
$taxonomies = array_filter( $taxonomies, [ $this, 'is_taxonomy_allowed' ] );
foreach ( $taxonomies as $taxonomy ) {
$this->set_term_query( $args, $post->ID, $taxonomy );
}
$posts = get_posts( $args );
foreach ( $posts as $related_post ) {
$item = [
'title' => get_the_title( $related_post->ID ),
'url' => get_permalink( $related_post->ID ),
'post_id' => $related_post->ID,
'focus_keywords' => get_post_meta( $related_post->ID, 'rank_math_focus_keyword', true ),
];
$item['focus_keywords'] = empty( $item['focus_keywords'] ) ? [] : explode( ',', $item['focus_keywords'] );
$output[] = $item;
}
return $output;
}
/**
* Is taxonomy allowed
*
* @param string $taxonomy Taxonomy to check.
*
* @return bool
*/
public function is_taxonomy_allowed( $taxonomy ) {
$exclude_taxonomies = [ 'post_format', 'product_shipping_class' ];
if ( Str::starts_with( 'pa_', $taxonomy ) || in_array( $taxonomy, $exclude_taxonomies, true ) ) {
return false;
}
return true;
}
/**
* Remap Action Scheduler translation to use WooCommerce's text domain (no context).
*
* @param string $translated Translated text.
* @param string $text Original text.
* @param string $domain Text domain.
* @return string Modified translated text.
*/
public function remap_action_scheduler_translation( $translated, $text, $domain ) {
// phpcs:ignore -- Use WooCommerce text domain for Action Scheduler strings.
return $domain === 'action-scheduler' && Helper::is_woocommerce_active() ? __( $text, 'woocommerce' ) : $translated;
}
/**
* Remap Action Scheduler translation to use WooCommerce's text domain (with context).
*
* @param string $translated Translated text.
* @param string $text Original text.
* @param string $context Context information for translators.
* @param string $domain Text domain.
* @return string Modified translated text.
*/
public function remap_action_scheduler_translation_with_context( $translated, $text, $context, $domain ) {
// phpcs:ignore -- Use WooCommerce text domain for Action Scheduler strings.
return $domain === 'action-scheduler' && Helper::is_woocommerce_active() ? _x( $text, $context, 'woocommerce' ) : $translated;
}
/**
* Set term query.
*
* @param array $query Array of query.
* @param int $post_id Post ID to get terms from.
* @param string $taxonomy Taxonomy to get terms for.
*/
private function set_term_query( &$query, $post_id, $taxonomy ) {
$terms = wp_get_post_terms( $post_id, $taxonomy, [ 'fields' => 'ids' ] );
if ( empty( $terms ) || is_wp_error( $terms ) ) {
return;
}
$query['tax_query'][] = [
'taxonomy' => $taxonomy,
'field' => 'term_id',
'terms' => $terms,
];
}
/**
* Output link suggestions.
*
* @param array $suggestions Link items.
* @return string
*/
public function get_link_suggestions_html( $suggestions ) {
$output = '<div class="rank-math-link-suggestions-content" data-count="' . count( $suggestions ) . '">';
$is_use_fk = 'focus_keywords' === Helper::get_settings( 'titles.pt_' . get_post_type() . '_ls_use_fk' );
foreach ( $suggestions as $suggestion ) {
$label = $suggestion['title'];
if ( $is_use_fk && ! empty( $suggestion['focus_keywords'] ) ) {
$label = $suggestion['focus_keywords'][0];
}
$output .= sprintf(
'<div class="suggestion-item">
<div class="suggestion-actions">
<button class="dashicons dashicons-clipboard suggestion-copy" title="%5$s" data-clipboard-text="%2$s"></button>
<button class="dashicons dashicons-admin-links suggestion-insert" title="%6$s" data-url="%2$s" data-text="%7$s"></button>
</div>
<span class="suggestion-title" data-fk=\'%1$s\'><a target="_blank" href="%2$s" title="%3$s">%4$s</a></span>
</div>',
esc_attr( wp_json_encode( $suggestion['focus_keywords'] ) ),
$suggestion['url'],
$suggestion['title'],
$label,
esc_attr__( 'Copy Link URL to Clipboard', 'rank-math' ),
esc_attr__( 'Insert Link in Content', 'rank-math' ),
esc_attr( $label )
);
}
$output .= '</div>';
return $output;
}
/**
* Updates the is_configured value.
*
* @param int $object_id The ID of the current object.
* @param string $cmb_id The current box ID.
*/
public function update_is_configured_value( $object_id, $cmb_id ) {
if ( 0 !== strpos( $cmb_id, 'rank_math' ) && 0 !== strpos( $cmb_id, 'rank-math' ) ) {
return;
}
Helper::is_configured( true );
}
/**
* Deactivate plugin.
*/
public function deactivate_plugins() {
check_ajax_referer( 'rank-math-ajax-nonce', 'security' );
if ( ! current_user_can( 'activate_plugins' ) ) {
$this->error( esc_html__( 'You are not authorized to perform this action.', 'rank-math' ) );
}
$plugin = Param::post( 'plugin', '', FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_BACKTICK );
if ( 'all' !== $plugin ) {
deactivate_plugins( $plugin );
die( '1' );
}
Importers\Detector::deactivate_all();
die( '1' );
}
/**
* Action Scheduler: exclude our actions from the past-due checker.
* Since this is a *_pre hook, it replaces the original checker.
*
* We first do the same check as what ActionScheduler_AdminView->check_pastdue_actions() does,
* but then we also count how many of those past-due actions are ours.
*
* @param null $value Null value.
*/
public function as_exclude_pastdue_actions( $value ) {
$query_args = [
'date' => as_get_datetime_object( time() - DAY_IN_SECONDS ),
'status' => \ActionScheduler_Store::STATUS_PENDING,
'per_page' => 1,
];
$store = \ActionScheduler_Store::instance();
$num_pastdue_actions = (int) $store->query_actions( $query_args, 'count' );
if ( 0 !== $num_pastdue_actions ) {
$query_args['group'] = 'rank-math';
$num_pastdue_rm_actions = (int) $store->query_actions( $query_args, 'count' );
$num_pastdue_actions -= $num_pastdue_rm_actions;
}
$threshold_seconds = (int) apply_filters( 'action_scheduler_pastdue_actions_seconds', DAY_IN_SECONDS );
$threshhold_min = (int) apply_filters( 'action_scheduler_pastdue_actions_min', 1 );
$check = ( $num_pastdue_actions >= $threshhold_min );
return (bool) apply_filters( 'action_scheduler_pastdue_actions_check', $check, $num_pastdue_actions, $threshold_seconds, $threshhold_min );
}
/**
* Check and print the Anniversary icon in the header of Rank Math's setting pages.
*/
public static function offer_icon() {
if ( ! current_user_can( 'manage_options' ) || defined( 'RANK_MATH_PRO_FILE' ) ) {
return;
}
// Holiday Season related variables.
$time = time();
$current_year = 2025;
$anniversary_start_time = gmmktime( 17, 00, 00, 10, 29, $current_year ); // 29 Oct.
$anniversary_end_time = gmmktime( 17, 00, 00, 12, 03, $current_year ); // 30 Nov.
$holiday_start_time = gmmktime( 17, 00, 00, 12, 24, $current_year ); // 20 Dec.
$holiday_end_time = gmmktime( 17, 00, 00, 01, 07, 2026 ); // 07 Jan.
ob_start();
if (
( $time > $anniversary_start_time && $time < $anniversary_end_time ) ||
( $time > $holiday_start_time && $time < $holiday_end_time )
) { ?>
<a href="https://rankmath.com/pricing/?utm_source=Plugin&utm_medium=Header+Offer+Icon&utm_campaign=WP" target="_blank" class="rank-math-tooltip bottom" style="margin-left:5px;">
🎉
<span><?php esc_attr_e( 'Exclusive Offer!', 'rank-math' ); ?></span>
</a>
<?php
}
return ob_get_clean();
}
/**
* Code to convert Addiontal Profile URLs from input type text to textarea.
*/
public function convert_additional_profile_url_to_textarea() {
if ( ! Admin_Helper::is_user_edit() ) {
return;
}
$field_description = __( 'Additional Profiles to add in the <code>sameAs</code> Schema property.', 'rank-math' );
?>
<script type="text/javascript">
( function( $ ) {
$( function() {
const twitterWrapper = $( '.user-twitter-wrap' );
twitterWrapper.before( '<tr><th><h2 style="margin: 0;">Rank Math SEO</h2></th><td></td></tr>' );
const additionalProfileField = $( '#additional_profile_urls' );
if ( ! additionalProfileField.length ) {
return
}
var $txtarea = $( '<textarea />' );
$txtarea.attr( 'id', additionalProfileField[0].id );
$txtarea.attr( 'name', additionalProfileField[0].name );
$txtarea.attr( 'rows', 5 );
$txtarea.val( additionalProfileField[0].value.replaceAll( " ", "\n" ) );
additionalProfileField.replaceWith( $txtarea );
$( '<p class="description"><?php echo wp_kses_post( $field_description ); ?></p>' ).insertAfter( $txtarea );
} );
})(jQuery);
</script>
<?php
}
/**
* Function to replace domain with seo-by-rank-math in translation file.
*
* @param string|false $file Path to the translation file to load. False if there isn't one.
* @param string $handle Name of the script to register a translation domain to.
* @param string $domain The text domain.
*/
public function load_script_translation_file( $file, $handle, $domain ) {
if ( 'rank-math' !== $domain ) {
return $file;
}
$data = explode( '/', $file );
$data[ count( $data ) - 1 ] = preg_replace( '/rank-math/', 'seo-by-rank-math', $data[ count( $data ) - 1 ], 1 );
return implode( '/', $data );
}
}

View File

@@ -0,0 +1,278 @@
<?php
/**
* The RankMath API.
*
* @since 1.5.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use WP_Error;
/**
* Api class.
*/
class Api {
/**
* Rank Math SEO Checkup API.
*
* @var string
*/
protected $api_url = 'https://rankmath.com/wp-json/rankmath/v1/';
/**
* Was the last request successful.
*
* @var bool
*/
protected $is_success = false;
/**
* Last error.
*
* @var string
*/
protected $last_error = '';
/**
* Last response.
*
* @var array
*/
protected $last_response = [];
/**
* Last response header code.
*
* @var int
*/
protected $last_code = 0;
/**
* User agent.
*
* @var string
*/
protected $user_agent = '';
/**
* Is blocking.
*
* @var bool
*/
protected $is_blocking = true;
/**
* Main instance
*
* Ensure only one instance is loaded or can be loaded.
*
* @return Api
*/
public static function get() {
static $instance;
if ( is_null( $instance ) && ! ( $instance instanceof Api ) ) {
$instance = new Api();
$instance->is_blocking = true;
$instance->user_agent = 'RankMath/' . md5( esc_url( home_url( '/' ) ) );
}
return $instance;
}
/**
* Was the last request successful?
*
* @return bool True for success, false for failure
*/
public function is_success() {
return $this->is_success;
}
/**
* Get the last error returned by either the network transport, or by the API.
* If something didn't work, this should contain the string describing the problem.
*
* @return array|false describing the error
*/
public function get_error() {
return $this->last_error ? $this->last_error : false;
}
/**
* Get an array containing the HTTP headers and the body of the API response.
*
* @return array Assoc array with keys 'headers' and 'body'
*/
public function get_response() {
return $this->last_response;
}
/**
* Remove registration data and disconnect from RankMath.com.
*
* @param string $username Username.
* @param string $api_key Api key.
*/
public function deactivate_site( $username, $api_key ) {
$this->is_blocking = false;
$data = wp_json_encode(
[
'username' => $username,
'api_key' => $api_key,
'site_url' => esc_url( home_url() ),
]
);
$response = wp_remote_post(
RANK_MATH_SITE_URL . '/wp-json/rankmath/v1/deactivateSite',
[
'body' => $data,
'headers' => [
'Content-Type' => 'application/json',
],
],
);
}
/**
* Make an HTTP GET request - for retrieving data.
*
* @param string $url URL to do request.
* @param array $args Assoc array of arguments (usually your data).
* @param int $timeout Timeout limit for request in seconds.
*
* @return array|false Assoc array of API response, decoded from JSON.
*/
public function http_get( $url, $args = [], $timeout = 10 ) {
return $this->make_request( 'GET', $url, $args, $timeout );
}
/**
* Make an HTTP POST request - for creating and updating items.
*
* @param string $url URL to do request.
* @param array $args Assoc array of arguments (usually your data).
* @param int $timeout Timeout limit for request in seconds.
*
* @return array|false Assoc array of API response, decoded from JSON.
*/
public function http_post( $url, $args = [], $timeout = 10 ) {
return $this->make_request( 'POST', $url, $args, $timeout );
}
/**
* Make an HTTP PUT request - for creating new items.
*
* @param string $url URL to do request.
* @param array $args Assoc array of arguments (usually your data).
* @param int $timeout Timeout limit for request in seconds.
*
* @return array|false Assoc array of API response, decoded from JSON.
*/
public function http_put( $url, $args = [], $timeout = 10 ) {
return $this->make_request( 'PUT', $url, $args, $timeout );
}
/**
* Make an HTTP DELETE request - for deleting data.
*
* @param string $url URL to do request.
* @param array $args Assoc array of arguments (usually your data).
* @param int $timeout Timeout limit for request in seconds.
*
* @return array|false Assoc array of API response, decoded from JSON.
*/
public function http_delete( $url, $args = [], $timeout = 10 ) {
return $this->make_request( 'DELETE', $url, $args, $timeout );
}
/**
* Performs the underlying HTTP request. Not very exciting.
*
* @param string $http_verb The HTTP verb to use: get, post, put, patch, delete.
* @param string $url URL to do request.
* @param array $args Assoc array of parameters to be passed.
* @param int $timeout Timeout limit for request in seconds.
*
* @return array|false Assoc array of decoded result.
*/
protected function make_request( $http_verb, $url, $args = [], $timeout = 10 ) {
$params = [
'timeout' => $timeout,
'method' => $http_verb,
'user-agent' => $this->user_agent,
'blocking' => $this->is_blocking,
];
if ( ! empty( $args ) && is_array( $args ) ) {
$params['body'] = $args;
}
$this->reset();
$response = wp_remote_request( $this->api_url . $url, $params );
$this->determine_success( $response );
return $this->format_response( $response );
}
/**
* Decode the response and format any error messages for debugging
*
* @param array|WP_Error $response The response from the curl request.
*
* @return array|false The JSON decoded into an array
*/
protected function format_response( $response ) {
$this->last_response = $response;
if ( is_wp_error( $response ) ) {
return false;
}
if ( ! empty( $response['body'] ) ) {
return json_decode( $response['body'], true );
}
return false;
}
/**
* Check if the response was successful or a failure. If it failed, store the error.
*
* @param array|WP_Error $response The response from the curl request.
*/
protected function determine_success( $response ) {
if ( is_wp_error( $response ) ) {
$this->last_error = 'WP_Error: ' . $response->get_error_message();
return;
}
$this->last_code = wp_remote_retrieve_response_code( $response );
if ( in_array( $this->last_code, [ 200, 204 ], true ) ) {
$this->is_success = true;
return;
}
$this->last_error = 'Unknown error, call getLastResponse() to find out what happened.';
}
/**
* Reset request.
*/
protected function reset() {
$this->last_code = 0;
$this->last_error = '';
$this->is_success = false;
$this->is_blocking = true;
$this->last_response = [
'body' => null,
'headers' => null,
];
}
}

View File

@@ -0,0 +1,249 @@
<?php
/**
* Ask user to review Rank Math on wp.org, in the meta box after 2 weeks.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Helper;
use RankMath\Traits\Ajax;
use RankMath\Traits\Hooker;
defined( 'ABSPATH' ) || exit;
/**
* Ask_Review class.
*/
class Ask_Review {
use Hooker;
use Ajax;
/**
* Now.
*
* @var string
*/
public $current_time = '';
/**
* Date of release of version 1.0.57. Turned into a timestamp in the constructor.
*
* @var string
*/
public $record_date = '2021-02-03 13:00';
/**
* Rank Math plugin install date.
*
* @var string
*/
public $install_date = '';
/**
* Constructor method.
*/
public function __construct() {
$this->current_time = Helper::get_current_time();
$this->record_date = strtotime( $this->record_date );
$this->install_date = get_option( 'rank_math_install_date' );
if ( false === $this->install_date ) {
$this->install_date = $this->current_time;
}
}
/**
* Register hooks.
*/
public function hooks() {
$this->ajax( 'already_reviewed', 'already_reviewed' );
// Post editor tab.
if ( $this->current_time > $this->install_date + ( 10 * DAY_IN_SECONDS ) ) {
Helper::add_json( 'showReviewTab', true );
}
// Admin notice.
$review_notice_date = $this->get_review_notice_date();
if ( $this->current_time > $review_notice_date ) {
if ( get_option( 'rank_math_review_notice_added' ) === false && ! Helper::has_notification( 'rank_math_pro_notice' ) ) {
$this->add_notice();
}
// Make dismiss button work like the "Maybe later" link.
$this->action( 'wp_helpers_notification_dismissed', 'review_notice_after_dismiss' );
}
$this->action( 'admin_footer', 'review_notice_js', 15 );
}
/**
* Add inline JS related to the review notice.
*
* @return void
*/
public function review_notice_js() {
if ( ! Helper::has_notification( 'rank_math_review_plugin_notice' ) ) {
return;
}
?>
<script>
(function( $ ) {
$( function() {
$('.rank-math-dismiss-review-notice').on( 'click', function(e) {
var $this = $(this);
if ( ! $this.hasClass( 'rank-math-review-action' ) ) {
e.preventDefault();
}
if ( $this.hasClass( 'rank-math-maybe-later-action' ) ) {
$('#rank_math_review_plugin_notice').find( '.notice-dismiss' ).trigger('click');
return false;
}
jQuery.ajax( {
url: rankMath.ajaxurl,
data: { action: 'rank_math_already_reviewed', security: rankMath.security,
},
} );
$('#rank_math_review_plugin_notice').find( '.notice-dismiss' ).trigger('click');
});
});
})(jQuery);
</script>
<style>
#rank_math_review_plugin_notice .rank-math-notice.is-dismissible a,
#rank_math_pro_notice .rank-math-notice.is-dismissible a {
color: #4f52d4;
}
#rank_math_review_plugin_notice.is-dismissible,
#rank_math_pro_notice.is-dismissible {
border-width: 0 0 0 4px;
border-left-color: #6668BD;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
padding: 5px 10px 5px 65px;
}
#rank_math_review_plugin_notice.is-dismissible:before,
#rank_math_pro_notice.is-dismissible:before {
content: '';
width: 50px;
height: 100%;
background: rgba(102, 104, 189, 0.09);
position: absolute;
left: 0;
top: 0;
}
#rank_math_review_plugin_notice.is-dismissible:after,
#rank_math_pro_notice.is-dismissible:after {
content: url('data:image/svg+xml;charset=UTF-8, <svg viewBox="0 0 462.03 462.03" xmlns="http://www.w3.org/2000/svg" width="20"><g fill="white"><path d="m462 234.84-76.17 3.43 13.43 21-127 81.18-126-52.93-146.26 60.97 10.14 24.34 136.1-56.71 128.57 54 138.69-88.61 13.43 21z"></path><path d="m54.1 312.78 92.18-38.41 4.49 1.89v-54.58h-96.67zm210.9-223.57v235.05l7.26 3 89.43-57.05v-181zm-105.44 190.79 96.67 40.62v-165.19h-96.67z"></path></g></svg>' );
padding: 3px;
border-radius: 3px;
position: absolute;
left: 12px;
top: 18px;
background: linear-gradient(-135deg, #2488e1, #724bb7);
width: 23px;
height: 23px;
display: flex;
justify-content: center;
line-height: 1;
align-items: center;
}
</style>
<?php
}
/**
* Add admin notice.
*
* @return void
*/
public function add_notice() {
$review_links = [
'https://www.trustpilot.com/review/www.rankmath.com',
'https://www.trustpilot.com/evaluate/www.rankmath.com',
];
$selected_link = $review_links[ array_rand( $review_links ) ];
$message = '<p>';
// Translators: placeholder is the plugin name.
$message .= sprintf( esc_html__( 'Hey, we noticed you\'ve been using %s for more than a week now that\'s awesome!', 'rank-math' ), '<strong>' . _x( 'Rank Math SEO', 'plugin name inside the review notice', 'rank-math' ) . '</strong>' );
$message .= '<br>';
$message .= esc_html__( 'We would love to get your feedback! It\'s essential for our continued development. Please consider taking a moment to leave a review of your experience on Trustpilot.', 'rank-math' ) . '</p>
<p><strong>Bhanu Ahluwalia</strong><br>' . esc_html__( 'Co-founder of Rank Math', 'rank-math' ) . '</p>
<p>
<a href="' . esc_url( $selected_link ) . '" class="rank-math-dismiss-review-notice rank-math-review-action rank-math-review-out" target="_blank" rel="noopener noreferrer"><strong>' . esc_html__( 'Yes, you deserve it', 'rank-math' ) . '</strong></a><br>
<a href="#" class="rank-math-dismiss-review-notice rank-math-maybe-later-action">' . esc_html__( 'No, maybe later', 'rank-math' ) . '</a><br>
<a href="#" class="rank-math-dismiss-review-notice rank-math-already-reviewed-action">' . esc_html__( 'I already did', 'rank-math' ) . '</a>
</p>';
Helper::add_notification(
$message,
[
'type' => 'info',
'id' => 'rank_math_review_plugin_notice',
'capability' => 'install_plugins',
]
);
update_option( 'rank_math_review_notice_added', '1', false );
}
/**
* Set "already reviewed" flag after the user dismisses the notice.
*
* @param string $notification_id Dismissed notice ID.
* @return void
*/
public function review_notice_after_dismiss( $notification_id ) {
if ( 'rank_math_review_plugin_notice' !== $notification_id ) {
return;
}
delete_option( 'rank_math_review_notice_date' );
delete_option( 'rank_math_review_notice_added' );
update_option( 'rank_math_review_notice_delayed', true, false );
}
/**
* Get stored notice start date.
*
* @return int
*/
public function get_review_notice_date() {
$review_notice_date = get_option( 'rank_math_review_notice_date' );
if ( false !== $review_notice_date ) {
return $review_notice_date;
}
$delay_days = 14;
if ( $this->install_date < $this->record_date && ! get_option( 'rank_math_review_notice_delayed' ) ) {
$delay_days = wp_rand( 7, 30 );
}
$review_notice_date = $this->current_time + ( $delay_days * DAY_IN_SECONDS );
update_option( 'rank_math_review_notice_date', $review_notice_date, false );
return $review_notice_date;
}
/**
* Set "already reviewed" flag.
*/
public function already_reviewed() {
check_ajax_referer( 'rank-math-ajax-nonce', 'security' );
$this->has_cap_ajax( 'onpage_general' );
update_option( 'rank_math_already_reviewed', Helper::get_current_time() );
$this->success( 'success' );
}
}

View File

@@ -0,0 +1,295 @@
<?php
/**
* Register all the necessary CSS and JS.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\KB;
use RankMath\Helper;
use RankMath\Runner;
use RankMath\Traits\Hooker;
use RankMath\Google\Console;
use RankMath\Google\Analytics;
use RankMath\Analytics\Url_Inspection;
use RankMath\Admin\Admin_Helper;
use RankMath\Helpers\Str;
defined( 'ABSPATH' ) || exit;
/**
* Assets class.
*
* @codeCoverageIgnore
*/
class Assets implements Runner {
use Hooker;
/**
* Prefix for the enqueue handles.
*/
const PREFIX = 'rank-math-';
/**
* Register hooks.
*/
public function hooks() {
$this->action( 'admin_enqueue_scripts', 'register' );
$this->action( 'admin_enqueue_scripts', 'enqueue' );
$this->action( 'admin_enqueue_scripts', 'overwrite_wplink', 99 );
if ( 'elementor' === \RankMath\Helpers\Param::get( 'action' ) ) {
$this->action( 'elementor/editor/before_enqueue_scripts', 'register' );
$this->action( 'elementor/editor/before_enqueue_scripts', 'enqueue' );
$this->action( 'elementor/editor/before_enqueue_scripts', 'overwrite_wplink', 99 );
}
}
/**
* Register styles and scripts.
*/
public function register() {
$js = rank_math()->plugin_url() . 'assets/admin/js/';
$css = rank_math()->plugin_url() . 'assets/admin/css/';
$vendor = rank_math()->plugin_url() . 'assets/vendor/';
// Styles.
wp_register_style( self::PREFIX . 'common', $css . 'common.css', null, rank_math()->version );
wp_register_style( self::PREFIX . 'cmb2', $css . 'cmb2.css', null, rank_math()->version );
wp_register_style( self::PREFIX . 'dashboard', $css . 'dashboard.css', [ 'rank-math-common', 'wp-components' ], rank_math()->version );
wp_register_style( self::PREFIX . 'dashboard-widget', $css . 'dashboard-widget.css', null, rank_math()->version );
// Scripts.
wp_register_script( self::PREFIX . 'common', $js . 'common.js', [ 'jquery', 'wp-i18n', 'lodash' ], rank_math()->version, true );
wp_register_script( self::PREFIX . 'dashboard', $js . 'dashboard.js', [ 'jquery', 'clipboard', 'lodash', 'wp-components', 'wp-element', 'rank-math-components' ], rank_math()->version, true );
wp_register_script( self::PREFIX . 'components', $js . 'components.js', [ 'lodash', 'wp-components', 'wp-element', 'wp-api-fetch' ], rank_math()->version, true );
// Select2.
wp_register_style( 'select2-rm', $vendor . 'select2/select2.min.css', null, '4.0.6-rc.1' );
wp_register_script( 'select2-rm', $vendor . 'select2/select2.min.js', null, '4.0.6-rc.1', true );
// Inline script for core admin page Settings > Permalinks.
wp_register_script( self::PREFIX . 'core-permalink-settings', '' ); // phpcs:ignore
wp_add_inline_script( self::PREFIX . 'core-permalink-settings', $this->get_permalinks_inline_script() );
if ( ! wp_script_is( 'wp-hooks', 'registered' ) ) {
wp_register_script( 'wp-hooks', rank_math()->plugin_url() . 'assets/vendor/hooks.js', [], rank_math()->version, true );
}
if ( ! wp_script_is( 'wp-wordcount', 'registered' ) ) {
wp_register_script( 'wp-wordcount', rank_math()->plugin_url() . 'assets/vendor/wordcount.js', [], rank_math()->version, true );
}
if ( ! wp_script_is( 'wp-autop', 'registered' ) ) {
wp_register_script( 'wp-autop', rank_math()->plugin_url() . 'assets/vendor/autop.js', [], rank_math()->version, true );
}
if ( ! wp_script_is( 'wp-url', 'registered' ) ) {
wp_register_script( 'wp-url', rank_math()->plugin_url() . 'assets/vendor/url.js', [], rank_math()->version, true );
}
if ( ! wp_script_is( 'wp-i18n', 'registered' ) ) {
wp_register_script( 'wp-i18n', rank_math()->plugin_url() . 'assets/vendor/i18n.js', [], rank_math()->version, true );
}
if ( ! wp_script_is( 'clipboard', 'registered' ) ) {
wp_register_script( 'clipboard', rank_math()->plugin_url() . 'assets/vendor/clipboard.min.js', [], rank_math()->version, true );
}
if ( ! wp_script_is( 'lodash', 'registered' ) ) {
wp_register_script( 'lodash', rank_math()->plugin_url() . 'assets/vendor/lodash.js', [], rank_math()->version, [] );
wp_add_inline_script( 'lodash', 'window.lodash = _.noConflict();' );
}
Helper::add_json(
'api',
[
'root' => esc_url_raw( get_rest_url() ),
'nonce' => ( wp_installing() && ! is_multisite() ) ? '' : wp_create_nonce( 'wp_rest' ),
]
);
Helper::add_json(
'keywordsApi',
[
'url' => 'https://api.rankmath.com/ltkw/v1/',
]
);
Helper::add_json(
'links',
KB::get_links()
);
Helper::add_json(
'validationl10n',
[
'regexErrorDefault' => __( 'Please use the correct format.', 'rank-math' ),
'requiredErrorDefault' => __( 'This field is required.', 'rank-math' ),
'emailErrorDefault' => __( 'Please enter a valid email address.', 'rank-math' ),
'urlErrorDefault' => __( 'Please enter a valid URL.', 'rank-math' ),
]
);
Helper::add_json( 'capitalizeTitle', Helper::get_settings( 'titles.capitalize_titles' ) );
Helper::add_json( 'isConsoleConnected', Console::is_console_connected() );
Helper::add_json( 'isAnalyticsConnected', Analytics::is_analytics_connected() );
Helper::add_json( 'isUrlInspectionEnabled', Url_Inspection::is_enabled() );
/**
* Allow other plugins to register/deregister admin styles or scripts after plugin assets.
*/
$this->do_action( 'admin/register_scripts' );
}
/**
* Enqueue styles and scripts.
*/
public function enqueue() {
$screen = get_current_screen();
if ( 'dashboard' === $screen->id ) {
wp_enqueue_style( self::PREFIX . 'dashboard-widget' );
wp_enqueue_script( self::PREFIX . 'dashboard' );
}
if ( in_array( $screen->id, [ 'toplevel_page_rank-math', 'rank-math_page_rank-math-content-ai-page', 'rank-math_page_rank-math-analytics', 'rank-math_page_rank-math-role-manager', 'rank-math_page_rank-math-seo-analysis', 'rank-math_page_rank-math-status' ], true ) ||
Str::starts_with( 'rank-math_page_rank-math-options-', $screen->id )
) {
Helper::add_json(
'dashboardHeader',
[
'dashboardUrl' => esc_url( Helper::get_admin_url() ),
'proBadge' => $this->do_filter( 'pro_badge', '' ),
]
);
}
// Our screens only.
if ( ! in_array( $screen->taxonomy, Helper::get_allowed_taxonomies(), true ) && ! in_array( $screen->id, $this->get_admin_screen_ids(), true ) ) {
return;
}
// Add thank you.
$this->filter( 'admin_footer_text', 'admin_footer_text' );
/**
* Allow other plugins to enqueue/dequeue admin styles or scripts after plugin assets.
*/
$this->do_action( 'admin/enqueue_scripts' );
}
/**
* Add footer credit on admin pages.
*
* @param string $text Default text for admin footer.
* @return string
*/
public function admin_footer_text( $text ) {
/* translators: plugin url */
return Helper::is_whitelabel() ? $text : sprintf( wp_kses_post( __( 'Thank you for using <a href="%s" target="_blank">Rank Math</a>', 'rank-math' ) ), KB::get( 'seo-suite', 'Admin Footer Text' ) );
}
/**
* Overwrite wplink script file.
* Rank Math adds new options in the link popup when editing a post.
*/
public function overwrite_wplink() {
wp_deregister_script( 'wplink' );
wp_register_script( 'wplink', rank_math()->plugin_url() . 'assets/admin/js/wplink.js', [ 'jquery', 'wp-a11y' ], rank_math()->version, true );
wp_localize_script(
'wplink',
'wpLinkL10n',
[
'title' => esc_html__( 'Insert/edit link', 'rank-math' ),
'update' => esc_html__( 'Update', 'rank-math' ),
'save' => esc_html__( 'Add Link', 'rank-math' ),
'noTitle' => esc_html__( '(no title)', 'rank-math' ),
'noMatchesFound' => esc_html__( 'No matches found.', 'rank-math' ),
'linkSelected' => esc_html__( 'Link selected.', 'rank-math' ),
'linkInserted' => esc_html__( 'Link inserted.', 'rank-math' ),
'relCheckbox' => __( 'Add <code>rel="nofollow"</code>', 'rank-math' ),
'sponsoredCheckbox' => __( 'Add <code>rel="sponsored"</code>', 'rank-math' ),
'linkTitle' => esc_html__( 'Link Title', 'rank-math' ),
]
);
}
/**
* Enqueues styles.
*
* @param string $style Name of the style.
*/
public function enqueue_style( $style ) {
wp_enqueue_style( self::PREFIX . $style );
}
/**
* Enqueues scripts.
*
* @param string $script Name of the script.
*/
public function enqueue_script( $script ) {
wp_enqueue_script( self::PREFIX . $script );
}
/**
* Get admin screen ids.
*
* @return array
*/
private function get_admin_screen_ids() {
$pages = [
'toplevel_page_rank-math',
'rank-math_page_rank-math-role-manager',
'rank-math_page_rank-math-seo-analysis',
'rank-math_page_rank-math-404-monitor',
'rank-math_page_rank-math-redirections',
'rank-math_page_rank-math-link-builder',
'rank-math_page_rank-math-analytics',
'rank-math_page_rank-math-import-export',
'rank-math_page_rank-math-help',
'user-edit',
'profile',
'rank_math_schema',
];
return array_merge( $pages, Helper::get_allowed_post_types() );
}
/**
* Inline script to warn the user about the risks of changing the permalinks on a live site.
*
* @return string
*/
public function get_permalinks_inline_script() {
// Don't add the script if site is set to noindex.
if ( ! get_option( 'blog_public' ) ) {
return '';
}
return "jQuery( window ).load( function() {
var noticeShown = false;
var showNotice = function() {
if ( noticeShown ) {
return true;
}
jQuery( '.rank-math-notice-permalinks-warning' ).removeClass( 'hidden' ).insertBefore( 'p.submit' );
noticeShown = true;
return true;
}
jQuery( '.available-structure-tags button' ).on( 'click', showNotice );
jQuery( 'input[type=text], input[type=radio]' ).on( 'focus change', showNotice );
} );";
}
}

View File

@@ -0,0 +1,197 @@
<?php
/**
* The admin post columns functionality.
*
* @since 1.0.212
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Helper;
use RankMath\Helpers\Param;
use RankMath\Helpers\Url;
use RankMath\Runner;
use RankMath\Traits\Hooker;
use RankMath\Admin\Admin_Helper;
defined( 'ABSPATH' ) || exit;
/**
* Post_Columns class.
*/
class Bulk_Actions implements Runner {
use Hooker;
/**
* SEO data.
*
* @var array
*/
private $data = [];
/**
* Register hooks.
*/
public function hooks() {
$this->action( 'current_screen', 'init' );
}
/**
* Intialize.
*/
public function init() {
if ( ! Helper::has_cap( 'onpage_general' ) || ! $this->can_add() ) {
return;
}
$this->register_post_columns();
$this->action( 'admin_enqueue_scripts', 'enqueue' );
}
/**
* Register post column hooks.
*/
private function register_post_columns() {
$post_types = Helper::get_allowed_post_types();
foreach ( $post_types as $post_type ) {
$this->filter( "bulk_actions-edit-{$post_type}", 'post_bulk_actions' );
$this->filter( "handle_bulk_actions-edit-{$post_type}", 'handle_bulk_actions', 10, 3 );
}
$taxonomies = Helper::get_accessible_taxonomies();
unset( $taxonomies['post_format'] );
$taxonomies = wp_list_pluck( $taxonomies, 'label', 'name' );
foreach ( $taxonomies as $taxonomy => $label ) {
$this->filter( "bulk_actions-edit-{$taxonomy}", 'post_bulk_actions' );
}
}
/**
* Add bulk actions for applicable posts, pages, CPTs.
*
* @param array $actions Actions.
* @return array New actions.
*/
public function post_bulk_actions( $actions ) {
$new_actions = [ 'rank_math_options' => __( '&#8595; Rank Math', 'rank-math' ) ];
if ( Helper::has_cap( 'onpage_advanced' ) ) {
$new_actions['rank_math_bulk_robots_noindex'] = __( 'Set to noindex', 'rank-math' );
$new_actions['rank_math_bulk_robots_index'] = __( 'Set to index', 'rank-math' );
$new_actions['rank_math_bulk_robots_nofollow'] = __( 'Set to nofollow', 'rank-math' );
$new_actions['rank_math_bulk_robots_follow'] = __( 'Set to follow', 'rank-math' );
$new_actions['rank_math_bulk_remove_canonical'] = __( 'Remove custom canonical URL', 'rank-math' );
if ( Helper::is_module_active( 'redirections' ) && Helper::has_cap( 'redirections' ) ) {
$new_actions['rank_math_bulk_redirect'] = __( 'Redirect', 'rank-math' );
$new_actions['rank_math_bulk_stop_redirect'] = __( 'Remove redirection', 'rank-math' );
}
}
if ( Helper::is_module_active( 'rich-snippet' ) && Helper::has_cap( 'onpage_snippet' ) ) {
$new_actions['rank_math_bulk_schema_none'] = __( 'Set Schema: None', 'rank-math' );
$post_type = Param::get( 'post_type', get_post_type() );
$post_type_default = Helper::get_settings( 'titles.pt_' . $post_type . '_default_rich_snippet' );
if ( $post_type_default ) {
// Translators: placeholder is the default Schema type setting.
$new_actions['rank_math_bulk_schema_default'] = sprintf( __( 'Set Schema: Default (%s)', 'rank-math' ), $post_type_default );
}
}
if ( Helper::has_cap( 'onpage_general' ) && Helper::should_determine_search_intent() ) {
$new_actions['rank_math_bulk_determine_search_intent'] = __( 'Determine Search Intent', 'rank-math' );
}
if ( is_array( $actions ) && count( $new_actions ) > 1 ) {
return array_merge( $actions, $new_actions );
}
return $actions;
}
/**
* Handle bulk actions for applicable posts, pages, CPTs.
*
* @param string $redirect Redirect URL.
* @param string $doaction Performed action.
* @param array $object_ids Post IDs.
*
* @return string New redirect URL.
*/
public function handle_bulk_actions( $redirect, $doaction, $object_ids ) {
if (
$doaction === 'rank_math_bulk_determine_search_intent' &&
(
defined( 'RANK_MATH_PRO_VERSION' ) &&
version_compare( RANK_MATH_PRO_VERSION, '3.0.83-beta', '<' )
)
) {
Helper::add_notification(
esc_html__( 'Your current plugin version does not support this feature. Please update Rank Math PRO to version 3.0.83 or later to unlock full functionality.', 'rank-math' ),
[
'type' => 'error',
'id' => 'rank_math_search_intent_error',
'classes' => 'rank-math-notice',
]
);
}
return $redirect;
}
/**
* Enqueue styles and scripts.
*/
public function enqueue() {
$screen = get_current_screen();
wp_enqueue_style( 'rank-math-post-bulk-edit', rank_math()->plugin_url() . 'assets/admin/css/post-list.css', [ 'wp-components' ], rank_math()->version );
if ( Admin_Helper::is_term_listing() ) {
$allow_editing = Helper::get_settings( 'titles.tax_' . $screen->taxonomy . '_bulk_editing', false );
} else {
$allow_editing = Helper::get_settings( 'titles.pt_' . $screen->post_type . '_bulk_editing', true );
}
if ( ! $allow_editing || 'readonly' === $allow_editing ) {
return;
}
wp_enqueue_script( 'rank-math-post-bulk-edit', rank_math()->plugin_url() . 'assets/admin/js/post-list.js', [ 'lodash', 'wp-element', 'wp-components' ], rank_math()->version, true );
Helper::add_json(
'contentAI',
[
'isUserRegistered' => Helper::is_site_connected(),
'credits' => Helper::get_content_ai_credits(),
'plan' => Helper::get_content_ai_plan(),
]
);
Helper::add_json( 'isProActive', defined( 'RANK_MATH_PRO_FILE' ) );
Helper::add_json( 'connectSiteUrl', Admin_Helper::get_activate_url( Url::get_current_url() ) );
}
/**
* Whether to add Bulk actions on the page.
*/
private function can_add() {
global $pagenow;
if ( 'edit-tags.php' === $pagenow ) {
return Helper::get_settings( 'titles.tax_' . Param::get( 'taxonomy' ) . '_add_meta_box' );
}
if ( Admin_Helper::is_post_list() || Admin_Helper::is_media_library() ) {
$screen = get_current_screen();
$allowed_post_types = Helper::get_allowed_post_types();
$allowed_post_types[] = 'attachment';
return in_array( $screen->post_type, $allowed_post_types, true );
}
return false;
}
}

View File

@@ -0,0 +1,334 @@
<?php
/**
* The CMB2 fields for the plugin.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Runner;
use RankMath\Helpers\Str;
use RankMath\Traits\Hooker;
use RankMath\Admin\Admin_Helper;
defined( 'ABSPATH' ) || exit;
/**
* CMB2_Fields class.
*
* @codeCoverageIgnore
*/
class CMB2_Fields implements Runner {
use Hooker;
/**
* Register hooks.
*/
public function hooks() {
if ( ! has_action( 'cmb2_render_switch' ) ) {
$this->action( 'cmb2_render_switch', 'render_switch', 10, 5 );
}
if ( ! has_action( 'cmb2_render_notice' ) ) {
$this->action( 'cmb2_render_notice', 'render_notice' );
}
if ( ! has_action( 'cmb2_render_address' ) ) {
$this->action( 'cmb2_render_address', 'render_address', 10, 5 );
}
if ( ! has_action( 'cmb2_render_advanced_robots' ) ) {
$this->action( 'cmb2_render_advanced_robots', 'render_advanced_robots', 10, 5 );
}
if ( ! has_action( 'cmb2_render_toggle' ) ) {
$this->action( 'cmb2_render_toggle', 'render_toggle', 10, 5 );
}
$this->filter( 'cmb2_sanitize_toggle', 'sanitize_toggle', 10, 2 );
$this->filter( 'cmb2_field_arguments_raw', 'default_value', 10, 2 );
}
/**
* Set a default value in default_cb to prevent the callback function from executing on the site.
*
* @see https://github.com/CMB2/CMB2/issues/750
*
* @param array $args The field arguments.
* @param object $cmb2 The CMB2 object.
*/
public function default_value( $args, $cmb2 ) {
if (
! Str::starts_with( 'rank-math', trim( $cmb2->cmb_id ) ) ||
! isset( $args['default'] ) ||
! is_callable( $args['default'] )
) {
return $args;
}
$args['default_cb'] = function () use ( $args ) {
return $args['default'];
};
$args['default'] = null;
return $args;
}
/**
* Sanitize toggle field.
*
* @param string $override_value Sanitization override value to return.
* @param string $value The field value.
*/
public function sanitize_toggle( $override_value, $value ) {
return is_null( $value ) ? 'off' : $value;
}
/**
* Render toggle field.
*
* @param array $field The passed in `CMB2_Field` object.
* @param mixed $escaped_value The value of this field escaped
* It defaults to `sanitize_text_field`.
* If you need the unescaped value, you can access it
* via `$field->value()`.
* @param int $object_id The ID of the current object.
* @param string $object_type The type of object you are working with.
* Most commonly, `post` (this applies to all post-types),
* but could also be `comment`, `user` or `options-page`.
* @param object $field_type_object This `CMB2_Types` object.
*/
public function render_toggle( $field, $escaped_value, $object_id, $object_type, $field_type_object ) {
$field_name = $field->_name();
$active_value = ! empty( $field->args( 'active_value' ) ) ? $field->args( 'active_value' ) : 'on';
$escaped_value = ! empty( $field->args( 'force_enable' ) ) ? 'on' : $escaped_value;
$args = [
'type' => 'checkbox',
'id' => $field_name,
'name' => $field_name,
'desc' => '',
'value' => $active_value,
'disabled' => isset( $field->args['disabled'] ) ? $field->args['disabled'] : false,
];
if ( $escaped_value === $active_value ) {
$args['checked'] = 'checked';
}
echo '<label class="cmb2-toggle">';
echo $field_type_object->input( $args ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CMB2 handles escaping.
echo '<span class="cmb2-slider">';
echo '<svg width="3" height="8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 6" class="toggle_on" role="img" aria-hidden="true" focusable="false"><path d="M0 0h2v6H0z"></path></svg>';
echo '<svg width="8" height="8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 6" class="toggle_off" role="img" aria-hidden="true" focusable="false"><path d="M3 1.5c.8 0 1.5.7 1.5 1.5S3.8 4.5 3 4.5 1.5 3.8 1.5 3 2.2 1.5 3 1.5M3 0C1.3 0 0 1.3 0 3s1.3 3 3 3 3-1.3 3-3-1.3-3-3-3z"></path></svg>';
echo '</span>';
echo '</label>';
$field_type_object->_desc( true, true );
}
/**
* Render switch field.
*
* @param array $field The passed in `CMB2_Field` object.
* @param mixed $escaped_value The value of this field escaped
* It defaults to `sanitize_text_field`.
* If you need the unescaped value, you can access it
* via `$field->value()`.
* @param int $object_id The ID of the current object.
* @param string $object_type The type of object you are working with.
* Most commonly, `post` (this applies to all post-types),
* but could also be `comment`, `user` or `options-page`.
* @param object $field_type_object This `CMB2_Types` object.
*/
public function render_switch( $field, $escaped_value, $object_id, $object_type, $field_type_object ) {
if ( empty( $field->args['options'] ) ) {
$field->args['options'] = [
'off' => esc_html( $field->get_string( 'off', __( 'Off', 'rank-math' ) ) ),
'on' => esc_html( $field->get_string( 'on', __( 'On', 'rank-math' ) ) ),
];
}
$field->set_options();
echo $field_type_object->radio_inline(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CMB2 handles escaping.
}
/**
* Render notices
*
* @param object $field The passed in `CMB2_Field` object.
*/
public function render_notice( $field ) {
$hash = [
'error' => 'notice notice-alt notice-error error inline rank-math-notice',
'info' => 'notice notice-alt notice-info info inline rank-math-notice',
'warning' => 'notice notice-alt notice-warning warning inline rank-math-notice',
];
echo '<div class="' . esc_attr( $hash[ $field->args( 'what' ) ] ) . '"><p>' . $field->args( 'content' ) . '</p></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CMB2 handles escaping.
}
/**
* Render address field.
*
* @param object $field The passed in `CMB2_Field` object.
* @param mixed $escaped_value The value of this field escaped
* It defaults to `sanitize_text_field`.
* If you need the unescaped value, you can access it
* via `$field->value()`.
* @param int $object_id The ID of the current object.
* @param string $object_type The type of object you are working with.
* Most commonly, `post` (this applies to all post-types),
* but could also be `comment`, `user` or `options-page`.
* @param object $field_type_object This `CMB2_Types` object.
*/
public function render_address( $field, $escaped_value, $object_id, $object_type, $field_type_object ) {
// Make sure we assign each part of the value we need.
$value = wp_parse_args(
$escaped_value,
[
'streetAddress' => '',
'addressLocality' => '',
'addressRegion' => '',
'postalCode' => '',
'addressCountry' => '',
]
);
$strings = [
'streetAddress' => 'Street Address',
'addressLocality' => 'Locality',
'addressRegion' => 'Region',
'postalCode' => 'Postal Code',
'addressCountry' => '2-letter Country Code (ISO 3166-1)',
];
foreach ( array_keys( $value ) as $id ) :
$field_input = $field_type_object->input(
[
'name' => $field_type_object->_name( '[' . $id . ']' ),
'id' => $field_type_object->_id( '_' . $id ),
'value' => $value[ $id ],
'placeholder' => esc_html( $field->get_string( $id . '_text', $strings[ $id ] ) ),
]
);
echo '<div class="cmb-address-field">' . $field_input . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CMB2 handles escaping.
endforeach;
}
/**
* Render Advanced Robots fields.
*
* @param object $field The passed in `CMB2_Field` object.
* @param mixed $escaped_value The value of this field escaped
* It defaults to `sanitize_text_field`.
* If you need the unescaped value, you can access it
* via `$field->value()`.
* @param int $object_id The ID of the current object.
* @param string $object_type The type of object you are working with.
* Most commonly, `post` (this applies to all post-types),
* but could also be `comment`, `user` or `options-page`.
* @param object $field_type_object This `CMB2_Types` object.
*/
public function render_advanced_robots( $field, $escaped_value, $object_id, $object_type, $field_type_object ) {
// Make sure we assign each part of the value we need.
$values = wp_parse_args(
$escaped_value,
[
'max-snippet' => -1,
'max-video-preview' => -1,
'max-image-preview' => 'large',
]
);
$strings = [
'max-snippet' => __( 'Snippet', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Specify a maximum text-length, in characters, of a snippet for your page.', 'rank-math' ) ),
'max-video-preview' => __( 'Video Preview', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Specify a maximum duration in seconds of an animated video preview.', 'rank-math' ) ),
'max-image-preview' => __( 'Image Preview', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Specify a maximum size of image preview to be shown for images on this page.', 'rank-math' ) ),
];
echo '<ul class="cmb-advanced-robots-list no-select-all cmb2-list cmb-rank-math-advanced-robots-field">';
foreach ( $values as $id => $value ) :
$value = isset( $escaped_value[ $id ] ) ? $escaped_value[ $id ] : $value;
echo '<li>';
echo '<label for="' . esc_attr( $field_type_object->_id( '_' . $id . '_name' ) ) . '">';
echo $this->get_advanced_robots_field( 'checkbox', $field_type_object, $id, $value, $escaped_value ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CMB2 handles escaping.
echo wp_kses_post( $field->get_string( $id . '_text', $strings[ $id ] ) ) . '</label>';
if ( 'max-image-preview' === $id ) {
echo $this->get_advanced_robots_field( 'select', $field_type_object, $id, $value, $escaped_value ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CMB2 handles escaping.
}
if ( 'max-image-preview' !== $id ) {
echo $this->get_advanced_robots_field( 'input', $field_type_object, $id, $value, $escaped_value ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CMB2 handles escaping.
}
echo '</li>';
endforeach;
echo '</ul>';
}
/**
* Get the field markup for the advanced robots field.
*
* @param string $field_type The type of field.
* @param object $field_type_object The CMB2_Types object.
* @param string $id The field id.
* @param string $value The field value.
* @param string $escaped_value The escaped field value.
*
* @return string The field markup.
*/
private function get_advanced_robots_field( $field_type, $field_type_object, $id, $value, $escaped_value ) {
$props = [
'name' => $field_type_object->_name( "[{$id}][length]" ),
'id' => $field_type_object->_id( '_' . $id . '_name' ),
];
switch ( $field_type ) {
case 'checkbox':
$props['name'] = $field_type_object->_name( "[{$id}][enable]" );
$props['value'] = true;
$props['checked'] = ! empty( $escaped_value[ $id ] ) || empty( $escaped_value ) ? 'checked' : false;
break;
case 'select':
$props['options'] = $this->get_image_sizes( $value );
break;
case 'input':
$props['value'] = $value ? $value : -1;
$props['type'] = 'number';
$props['min'] = -1;
break;
}
return $field_type_object->$field_type( $props );
}
/**
* Get Image sizes.
*
* @param string $size The selected image size.
* @return string $options The image sizes.
*/
private function get_image_sizes( $size = 'large' ) {
$values = [
'large' => __( 'Large', 'rank-math' ),
'standard' => __( 'Standard', 'rank-math' ),
'none' => __( 'None', 'rank-math' ),
];
$options = '';
foreach ( $values as $data => $label ) {
$options .= '<option value="' . $data . '" ' . selected( $size, $data, false ) . ' >' . $label . '</option>';
}
return $options;
}
}

View File

@@ -0,0 +1,496 @@
<?php
/**
* The option page functionality of the plugin.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use WP_Http;
use RankMath\KB;
use RankMath\CMB2;
use RankMath\Helper;
use RankMath\Traits\Hooker;
use RankMath\Helpers\Str;
use RankMath\Helpers\Param;
defined( 'ABSPATH' ) || exit;
/**
* CMB2_Options class.
*/
class CMB2_Options {
use Hooker;
/**
* Page title.
*
* @var string
*/
public $title = 'Settings';
/**
* Menu title.
*
* @var string
*/
public $menu_title = 'Settings';
/**
* Hold tabs for page.
*
* @var array
*/
public $tabs = [];
/**
* Hold folder name for tab files.
*
* @var string
*/
public $folder = '';
/**
* Menu Position.
*
* @var int
*/
public $position = 10;
/**
* The capability required for this menu to be displayed to the user.
*
* @var string
*/
public $capability = 'manage_options';
/**
* CMB2 option page id.
*
* @var string
*/
private $cmb_id = null;
/**
* Options key.
*
* @var string
*/
public $key = '';
/**
* The Constructor
*
* @param array $config Array of configuration.
*/
public function __construct( $config ) {
$this->config( $config );
$this->cmb_id = $this->key . '_options';
$this->action( 'cmb2_admin_init', 'register_option_page', $this->position );
$this->action( 'admin_post_' . $this->key, 'reset_options', 2 );
if ( true === empty( get_option( $this->key ) ) ) {
$this->action( 'cmb2_init_hookup_' . $this->cmb_id, 'set_defaults', 11 );
}
if ( ! $this->is_current_page() ) {
return;
}
$this->action( 'admin_enqueue_scripts', 'enqueue' );
$this->action( 'admin_body_class', 'body_class' );
// Check for fields and act accordingly.
$this->action( 'cmb2_save_options-page_fields_rank-math-options-general_options', 'check_updated_fields', 25, 2 );
$this->action( 'cmb2_save_options-page_fields_rank-math-options-titles_options', 'check_updated_fields', 25, 2 );
}
/**
* Create cmb2 box.
*
* @return CMB2
*/
private function create_cmb2() {
return new_cmb2_box(
[
'id' => $this->cmb_id,
'title' => $this->title,
'menu_title' => $this->menu_title,
'capability' => $this->capability,
'object_types' => [ 'options-page' ],
'option_key' => $this->key,
'parent_slug' => 'rank-math',
'cmb_styles' => false,
'display_cb' => [ $this, 'display' ],
]
);
}
/**
* Create option object and add settings.
*/
public function register_option_page() {
$cmb = $this->create_cmb2();
$tabs = $this->get_tabs();
$cmb->add_field(
[
'id' => 'setting-panel-container-' . $this->cmb_id,
'type' => 'tab_container_open',
'classes' => 'before-header',
'tabs' => $tabs,
]
);
foreach ( $tabs as $id => $tab ) {
$located = $this->locate_file( $id, $tab );
if ( false === $located ) {
continue;
}
$cmb->add_field(
[
'name' => esc_html__( 'Panel', 'rank-math' ),
'id' => 'setting-panel-' . $id,
'type' => 'tab',
'open' => true,
'classes' => isset( $tab['classes'] ) ? $tab['classes'] : '',
]
);
$cmb->add_field(
[
'id' => $id . '_section_title',
'type' => 'title',
'name' => isset( $tab['page_title'] ) ? $tab['page_title'] : ( isset( $tab['title'] ) ? $tab['title'] : '' ),
'desc' => isset( $tab['desc'] ) ? $tab['desc'] : '',
'after' => isset( $tab['after'] ) ? $tab['after'] : '',
'classes' => 'tab-header',
'after_row' => isset( $tab['after_row'] ) ? $tab['after_row'] : '',
]
);
include $located;
$this->do_action( "admin/settings/{$id}", $cmb, $tab );
$cmb->add_field(
[
'id' => 'setting-panel-' . $id . '-close',
'type' => 'tab',
]
);
}
$cmb->add_field(
[
'id' => 'setting-panel-container-close-' . $this->cmb_id,
'type' => 'tab_container_close',
]
);
CMB2::pre_init( $cmb );
}
/**
* Set the default values if not set.
*
* @param CMB2 $cmb The CMB2 object to hookup.
*/
public function set_defaults( $cmb ) {
foreach ( $cmb->prop( 'fields' ) as $id => $field_args ) {
$field = $cmb->get_field( $id );
if ( isset( $field_args['default'] ) || isset( $field_args['default_cb'] ) ) {
$defaults[ $id ] = $field->get_default();
}
}
// Save Defaults if any.
if ( ! empty( $defaults ) ) {
add_option( $this->key, $defaults );
}
}
/**
* Reset options.
*/
public function reset_options() {
if ( ! check_admin_referer( 'rank-math-reset-options' ) || ! current_user_can( 'manage_options' ) ) {
return false;
}
$url = wp_get_referer();
if ( ! $url ) {
$url = admin_url();
}
if ( filter_has_var( INPUT_POST, 'reset-cmb' ) && Param::post( 'action' ) === $this->key ) {
delete_option( $this->key );
Helper::redirect( esc_url_raw( $url ), WP_Http::SEE_OTHER );
exit;
}
}
/**
* Enqueue styles and scripts.
*/
public function enqueue() {
$screen = get_current_screen();
if ( ! Str::contains( $this->key, $screen->id ) ) {
return;
}
\CMB2_Hookup::enqueue_cmb_css();
rank_math()->variables->setup_json();
wp_enqueue_style( 'rank-math-options', rank_math()->plugin_url() . 'assets/admin/css/option-panel.css', [ 'select2-rm', 'rank-math-common', 'rank-math-cmb2' ], rank_math()->version );
wp_enqueue_script( 'rank-math-options', rank_math()->plugin_url() . 'assets/admin/js/option-panel.js', [ 'underscore', 'select2-rm', 'lodash', 'rank-math-common', 'wp-api-fetch' ], rank_math()->version, true );
// Add thank you.
Helper::add_json( 'indexUrl', rank_math()->plugin_url() . 'assets/admin/js/search-index/' );
Helper::add_json( 'optionPage', str_replace( 'rank-math-options-', '', $this->key ) );
}
/**
* Add classes to <body> of WordPress admin.
*
* @param string $classes List of CSS classes.
* @return string
*/
public function body_class( $classes = '' ) {
$mode = Helper::is_advanced_mode() ? 'advanced' : 'basic';
return $classes . ' rank-math-page rank-math-mode-' . $mode;
}
/**
* Display Setting on a page.
*
* @param CMB2_Options $machine Current CMB2 box object.
*/
public function display( $machine ) {
$cmb = $machine->cmb;
// Header.
rank_math()->admin->display_admin_header();
?>
<?php if ( ! defined( 'RANK_MATH_PRO_FILE' ) ) : ?>
<div class="rank-math-unlock-pro-notice" id="rank-math-unlock-pro-notice">
<a href="<?php KB::the( 'pro', 'Unlock PRO Options Panel Notice' ); ?>" target="_blank" class="pro-link">
<p>
<?php esc_html_e( 'Take your SEO to the Next Level!', 'rank-math' ); ?>
<strong><?php esc_html_e( 'Get Rank Math PRO!', 'rank-math' ); ?></strong>
<span><?php esc_html_e( 'Click here to see all the exciting features.', 'rank-math' ); ?></span>
</p>
<div class="close-notice">
<span class="dashicons dashicons-dismiss"></span>
</div>
</a>
</div>
<?php endif; ?>
<div class="wrap rank-math-wrap rank-math-wrap-settings">
<span class="wp-header-end"></span>
<form class="cmb-form" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="POST" id="<?php echo esc_attr( $cmb->cmb_id ); ?>" enctype="multipart/form-data" encoding="multipart/form-data">
<input type="hidden" name="action" value="<?php echo esc_attr( $machine->option_key ); ?>">
<?php $machine->options_page_metabox(); ?>
<footer class="form-footer rank-math-ui settings-footer wp-clearfix">
<?php wp_nonce_field( 'rank-math-reset-options' ); ?>
<input type="submit" name="submit-cmb" id="submit-cmb" title="<?php echo esc_html__( 'Ctrl/Cmd + Enter', 'rank-math' ); ?>" class="button button-primary save-options" value="<?php esc_attr_e( 'Save Changes', 'rank-math' ); ?>">
<input type="submit" name="reset-cmb" id="rank-math-reset-cmb" value="<?php esc_attr_e( 'Reset Options', 'rank-math' ); ?>" class="button button-secondary reset-options alignleft">
</footer>
</form>
</div>
<?php
}
/**
* Check if we are on the correct page.
*
* @return bool
*/
public function is_current_page() {
return Param::request( 'page' ) === $this->key || Param::request( 'action' ) === $this->key;
}
/**
* Check if certain fields got updated.
*
* @param int $object_id The ID of the current object.
* @param array $updated Array of field ids that were updated.
* Will only include field ids that had values change.
*/
public function check_updated_fields( $object_id, $updated ) {
/**
* Filter: Allow developers to add option fields which will flush the rewrite rules when updated.
*
* @param array $flush_fields Array of field IDs for which we need to flush.
*/
$flush_fields = $this->do_filter(
'flush_fields',
[
'strip_category_base',
'disable_author_archives',
'url_author_base',
'attachment_redirect_urls',
'attachment_redirect_default',
'nofollow_external_links',
'nofollow_image_links',
'nofollow_domains',
'nofollow_exclude_domains',
'new_window_external_links',
'redirections_header_code',
'redirections_post_redirect',
'redirections_debug',
]
);
foreach ( $flush_fields as $field_id ) {
if ( in_array( $field_id, $updated, true ) ) {
Helper::schedule_flush_rewrite();
break;
}
}
$this->maybe_update_htaccess();
}
/**
* Update .htaccess.
*/
private function maybe_update_htaccess() {
if ( empty( Param::post( 'htaccess_accept_changes' ) ) ) {
return;
}
if ( ! is_super_admin() || ! Helper::has_cap( 'general' ) || ! Helper::has_cap( 'edit_htaccess' ) ) {
Helper::add_notification(
esc_html__( 'You do not have permission to edit the .htaccess file.', 'rank-math' ),
[ 'type' => 'error' ]
);
return;
}
if ( ! Helper::is_edit_allowed() ) {
Helper::add_notification(
esc_html__( 'You do not have permission to edit the .htaccess file.', 'rank-math' ),
[ 'type' => 'error' ]
);
return;
}
// phpcs:ignore= WordPress.Security.ValidatedSanitizedInput, WordPress.Security.NonceVerification -- Writing to .htaccess file and escaping for HTML will break functionality & CMB2 package handles the nonce verification
$content = isset( $_POST['htaccess_content'] ) ? wp_unslash( $_POST['htaccess_content'] ) : '';
if ( empty( $content ) ) {
return;
}
if ( ! $this->do_htaccess_backup() ) {
Helper::add_notification(
esc_html__( 'Failed to backup .htaccess file. Please check file permissions.', 'rank-math' ),
[ 'type' => 'error' ]
);
return;
}
if ( ! $this->do_htaccess_update( $content ) ) {
Helper::add_notification(
esc_html__( 'Failed to update .htaccess file. Please check file permissions.', 'rank-math' ),
[ 'type' => 'error' ]
);
return;
}
Helper::add_notification( esc_html__( '.htaccess file updated successfully.', 'rank-math' ) );
}
/**
* Create htaccess backup.
*
* @return bool
*/
private function do_htaccess_backup() {
if ( ! Helper::is_filesystem_direct() ) {
return false;
}
$wp_filesystem = Helper::get_filesystem();
$path = get_home_path();
$file = $path . '.htaccess';
if ( ! $wp_filesystem->is_writable( $path ) || ! $wp_filesystem->exists( $file ) ) {
return false;
}
$backup = $path . uniqid( '.htaccess_back_' );
return $wp_filesystem->copy( $file, $backup, true );
}
/**
* Update htaccess file.
*
* @param string $content Htaccess content.
* @return string|bool
*/
private function do_htaccess_update( $content ) {
if ( empty( $content ) || ! Helper::is_filesystem_direct() ) {
return false;
}
$wp_filesystem = Helper::get_filesystem();
$htaccess_file = get_home_path() . '.htaccess';
return ! $wp_filesystem->is_writable( $htaccess_file ) ? false : $wp_filesystem->put_contents( $htaccess_file, $content );
}
/**
* Get setting tabs.
*
* @return array
*/
private function get_tabs() {
$filter = str_replace( '-', '_', str_replace( 'rank-math-', '', $this->key ) );
/**
* Allow developers to add new tabs into option panel.
*
* The dynamic part of hook is, page name without 'rank-math-' prefix.
*
* @param array $tabs
*/
return $this->do_filter( "admin/options/{$filter}_tabs", $this->tabs );
}
/**
* Locate tab options file.
*
* @param string $id Tab id.
* @param array $tab Tab options.
* @return string|boolean
*/
private function locate_file( $id, $tab ) {
if ( isset( $tab['type'] ) && 'seprator' === $tab['type'] ) {
return false;
}
$file = isset( $tab['file'] ) && ! empty( $tab['file'] ) ? $tab['file'] : rank_math()->includes_dir() . "settings/{$this->folder}/{$id}.php";
return file_exists( $file ) ? $file : false;
}
}

View File

@@ -0,0 +1,224 @@
<?php
/**
* The Dashboard Widget of the plugin.
*
* @since 1.0.81
* @package RankMath
* @subpackage RankMath\Core
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath;
use RankMath\KB;
use RankMath\Helper;
use RankMath\Traits\Hooker;
defined( 'ABSPATH' ) || exit;
/**
* Dashboard_Widget class.
*
* @codeCoverageIgnore
*/
class Dashboard_Widget {
use Hooker;
/**
* Constructor.
*/
public function __construct() {
$this->action( 'wp_dashboard_setup', 'add_dashboard_widgets' );
$this->action( 'rank_math/dashboard/widget', 'dashboard_widget_feed', 98 );
$this->action( 'rank_math/dashboard/widget', 'dashboard_widget_footer', 99 );
}
/**
* Register dashboard widget.
*/
public function add_dashboard_widgets() {
// Early Bail if action is not registered for the dashboard widget hook.
if (
( ! Helper::is_module_active( '404-monitor' ) || ! Helper::has_cap( '404_monitor' ) ) &&
( ! Helper::is_module_active( 'redirections' ) || ! Helper::has_cap( 'redirections' ) ) &&
( ! Helper::is_module_active( 'analytics' ) || ! Helper::has_cap( 'analytics' ) )
) {
return;
}
$icon = '<span class="rank-math-icon"><svg viewBox="0 0 462.03 462.03" xmlns="http://www.w3.org/2000/svg" width="20"><g><path d="m462 234.84-76.17 3.43 13.43 21-127 81.18-126-52.93-146.26 60.97 10.14 24.34 136.1-56.71 128.57 54 138.69-88.61 13.43 21z"></path><path d="m54.1 312.78 92.18-38.41 4.49 1.89v-54.58h-96.67zm210.9-223.57v235.05l7.26 3 89.43-57.05v-181zm-105.44 190.79 96.67 40.62v-165.19h-96.67z"></path></g></svg></span>';
wp_add_dashboard_widget(
'rank_math_dashboard_widget',
$icon . esc_html__( 'Rank Math Overview', 'rank-math' ),
[ $this, 'render_dashboard_widget' ],
null,
null,
'normal',
'high'
);
}
/**
* Render dashboard widget.
*/
public function render_dashboard_widget() {
echo '<div id="rank-math-dashboard-widget" class="rank-math-loading"></div>';
}
/**
* Add Feed data in the admin dashboard widget.
*/
public function dashboard_widget_feed() {
$posts = $this->get_feed();
?>
<h3 class="rank-math-blog-title"><?php esc_html_e( 'Latest Blog Posts from Rank Math', 'rank-math' ); ?></h3>
<?php if ( empty( $posts ) ) : ?>
<p><?php esc_html_e( 'Error: the Rank Math blog feed could not be downloaded.', 'rank-math' ); ?></p>
<?php
return;
endif;
echo '<ul class="rank-math-blog-list">';
$posts = $this->filter_posts( $posts );
$label = $this->get_item_label( $posts );
foreach ( $posts as $index => $post ) :
$link = $this->add_utm_params( $post['link'], $index );
?>
<li class="rank-math-blog-post">
<h4>
<?php if ( $label ) : ?>
<span class="rank-math-new-badge"><?php echo esc_html( $label ); ?></span>
<?php endif; ?>
<a target="_blank" href="<?php echo esc_url( $link ); ?>">
<?php echo esc_html( $post['title']['rendered'] ); ?>
</a>
</h4>
</li>
<?php
$label = '';
endforeach;
echo '</ul>';
}
/**
* Get label for first post.
*
* @param array $posts Posts.
*/
private function get_item_label( $posts ) {
$label = '';
if ( ! empty( $posts[0]['custom_label'] ) ) {
$label = $posts[0]['custom_label'];
}
$is_new = time() - strtotime( $posts[0]['date'] ) < 15 * DAY_IN_SECONDS;
if ( $is_new && empty( $label ) ) {
$label = esc_html__( 'NEW', 'rank-math' );
}
return $label;
}
/**
* Filter posts by display condition.
*
* @param array $posts Posts.
*/
private function filter_posts( $posts ) {
$posts = array_filter(
$posts,
function ( $post ) {
if ( isset( $post['condition'] ) && 'is_free' === $post['condition'] && defined( 'RANK_MATH_PRO_FILE' ) ) {
return false;
}
return true;
}
);
return array_slice( $posts, 0, 3 ); // Max 3 posts.
}
/**
* Add UTM tags to links. Only add if UTM params are not already present.
*
* @param string $link Link.
* @param int $index Array index.
*/
private function add_utm_params( $link, $index ) {
// Skip if link has any UTM tags already set.
if ( preg_match( '/[?&]utm_[a-z_]+=/', $link ) ) {
return $link;
}
$utm_params = [
'utm_source' => 'Plugin',
'utm_medium' => 'Dashboard%20Widget%20Post%20' . ( $index + 1 ),
'utm_campaign' => 'WP',
];
return add_query_arg( $utm_params, $link );
}
/**
* Add footer in the admin dashboard widget.
*/
public function dashboard_widget_footer() {
?>
<div class="rank-math-widget-footer">
<a target="_blank" href="<?php KB::the( 'blog', 'Dashboard Widget Blog' ); ?>">
<?php esc_html_e( 'Blog', 'rank-math' ); ?>
<span class="screen-reader-text"><?php esc_html_e( '(opens in a new window)', 'rank-math' ); ?></span>
<span aria-hidden="true" class="dashicons dashicons-external"></span>
</a>
<a target="_blank" href="<?php KB::the( 'knowledgebase', 'Dashboard Widget Help' ); ?>">
<?php esc_html_e( 'Help', 'rank-math' ); ?>
<span class="screen-reader-text"><?php esc_html_e( '(opens in a new window)', 'rank-math' ); ?></span>
<span aria-hidden="true" class="dashicons dashicons-external"></span>
</a>
<?php if ( ! defined( 'RANK_MATH_PRO_FILE' ) ) { ?>
<a target="_blank" href="<?php KB::the( 'pro', 'Dashboard Widget PRO' ); ?>" class="rank-math-widget-go-pro">
<?php esc_html_e( 'Go Pro', 'rank-math' ); ?>
<span class="screen-reader-text"><?php esc_html_e( '(opens in a new window)', 'rank-math' ); ?></span>
<span aria-hidden="true" class="dashicons dashicons-external"></span>
</a>
<?php } ?>
</div>
<?php
}
/**
* Get posts.
*/
private function get_feed() {
$cache_key = 'rank_math_feed_posts_v2';
$cache = get_transient( $cache_key );
if ( false !== $cache ) {
return $cache;
}
$response = wp_remote_get( 'https://rankmath.com/wp-json/wp/v2/posts?dashboard_widget_feed=1' );
if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
set_transient( $cache_key, [], 2 * HOUR_IN_SECONDS );
return false;
}
$posts = json_decode( wp_remote_retrieve_body( $response ), true );
if ( empty( $posts ) || ! is_array( $posts ) ) {
set_transient( $cache_key, [], 2 * HOUR_IN_SECONDS );
return false;
}
set_transient( $cache_key, $posts, 12 * HOUR_IN_SECONDS );
return $posts;
}
}

View File

@@ -0,0 +1,102 @@
<?php
/**
* The Import Export Class
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use WP_REST_Response;
use RankMath\Helper;
use RankMath\Runner;
use RankMath\Traits\Ajax;
use RankMath\Traits\Hooker;
use RankMath\Helpers\Param;
use RankMath\Status\Backup;
use RankMath\Admin\Importers\Detector;
defined( 'ABSPATH' ) || exit;
/**
* Import_Export class.
*/
class Import_Export implements Runner {
use Hooker;
use Ajax;
/**
* Register hooks.
*/
public function hooks() {
$this->ajax( 'clean_plugin', 'clean_plugin' );
$this->ajax( 'import_plugin', 'import_plugin' );
}
/**
* Get localized JSON data to be used on the Import & Export tab of the Status & Tools page.
*/
public static function get_json_data() {
$detector = new Detector();
$importable_plugins = $detector->detect();
return [
'backups' => Backup::get_backups(),
'importablePlugins' => $importable_plugins,
];
}
/**
* Handles AJAX run plugin clean.
*/
public function clean_plugin() {
$this->verify_nonce( 'rank-math-ajax-nonce' );
$this->has_cap_ajax( 'general' );
$result = Detector::run_by_slug( Param::post( 'pluginSlug' ), 'cleanup' );
if ( $result['status'] ) {
/* translators: Plugin name */
$this->success( sprintf( esc_html__( 'Cleanup of %s data successfully done.', 'rank-math' ), $result['importer']->get_plugin_name() ) );
}
/* translators: Plugin name */
$this->error( sprintf( esc_html__( 'Cleanup of %s data failed.', 'rank-math' ), $result['importer']->get_plugin_name() ) );
}
/**
* Handles AJAX run plugin import.
*/
public function import_plugin() {
$this->verify_nonce( 'rank-math-ajax-nonce' );
$this->has_cap_ajax( 'general' );
$perform = Param::post( 'perform', '', FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_BACKTICK );
if ( ! $this->is_action_allowed( $perform ) ) {
$this->error( esc_html__( 'Action not allowed.', 'rank-math' ) );
}
try {
$result = Detector::run_by_slug( Param::post( 'pluginSlug' ), 'import', $perform );
$this->success( $result );
} catch ( \Exception $e ) {
$this->error( $e->getMessage() );
}
}
/**
* Check if given action is in the list of allowed actions.
*
* @param string $perform Action to check.
*
* @return bool
*/
private function is_action_allowed( $perform ) {
$allowed = [ 'settings', 'postmeta', 'termmeta', 'usermeta', 'redirections', 'blocks', 'deactivate', 'locations', 'news', 'video', 'recalculate' ];
return $perform && in_array( $perform, $allowed, true );
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* The List Table Base CLass.
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath\Admin
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin;
use WP_List_Table;
use RankMath\Helpers\Param;
/**
* List_Table class.
*/
class List_Table extends WP_List_Table {
/**
* Message to be displayed when there are no items.
*/
public function no_items() {
echo isset( $this->_args['no_items'] ) ? wp_kses_post( $this->_args['no_items'] ) : esc_html__( 'No items found.', 'rank-math' );
}
/**
* Get order setting.
*
* @return string
*/
protected function get_order() {
$order = Param::request( 'order', 'desc' );
return in_array( $order, [ 'desc', 'asc' ], true ) ? strtoupper( $order ) : 'DESC';
}
/**
* Get orderby setting.
*
* @param string $default_value (Optional) Extract order by from request.
*
* @return string
*/
protected function get_orderby( $default_value = 'create_date' ) {
return Param::get( 'orderby', $default_value, FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_BACKTICK );
}
/**
* Get search query variable.
*
* @return bool|string
*/
protected function get_search() {
return Param::request( 's', false, FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_BACKTICK );
}
/**
* Set column headers.
*
* @codeCoverageIgnore
*/
protected function set_column_headers() {
$this->_column_headers = [
$this->get_columns(),
[],
$this->get_sortable_columns(),
];
}
}

View File

@@ -0,0 +1,113 @@
<?php
/**
* The admin-specific functionality of the plugin.
*
* @since 1.0.223
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Helper;
use RankMath\Helpers\Editor;
use RankMath\Helpers\Param;
use RankMath\Traits\Hooker;
defined( 'ABSPATH' ) || exit;
/**
* Lock_Modified_Date class.
*
* @codeCoverageIgnore
*/
class Lock_Modified_Date {
use Hooker;
/**
* Register hooks.
*/
public function __construct() {
$this->action( 'rest_api_init', 'init_rest_api' );
$this->action( 'wp_insert_post_data', 'update_modified_date', 999, 3 );
}
/**
* Add REST filter to modify the post object.
*/
public function init_rest_api() {
$post_types = Helper::get_allowed_post_types();
foreach ( $post_types as $post_type ) {
$this->filter( "rest_pre_insert_{$post_type}", 'update_last_modified_parameter', 99, 2 );
}
}
/**
* Add last_modified parameter to a post when a post is updated from Block Editor.
*
* @param WP_POST $prepared_post Post object.
* @param WP_REST_Request $request Request object.
*/
public function update_last_modified_parameter( $prepared_post, $request ) {
$params = $request->get_params();
if ( isset( $params['meta']['rank_math_lock_modified_date'] ) ) {
$prepared_post->lock_modified_date = ! empty( $params['meta']['rank_math_lock_modified_date'] );
}
return $prepared_post;
}
/**
* Lock Modified date by overwriting the old value.
*
* @param array $data An array of slashed, sanitized, and processed post data.
* @param array $postarr An array of sanitized (and slashed) but otherwise unmodified post data.
*/
public function update_modified_date( $data, $postarr ) {
$post_id = ! empty( $postarr['ID'] ) ? $postarr['ID'] : 0;
if (
! $post_id ||
! isset( $postarr['post_modified'], $postarr['post_modified_gmt'] ) ||
! $this->lock_modified_date( $postarr, $post_id )
) {
return $data;
}
$data['post_modified'] = $postarr['post_modified'];
$data['post_modified_gmt'] = $postarr['post_modified_gmt'];
return $data;
}
/**
* Whether to lock modified date.
*
* @param array $data An array of sanitized (and slashed) but otherwise unmodified post data.
* @param int $post_id Post ID.
*/
private function lock_modified_date( $data, $post_id ) {
if ( ! Editor::can_add_lock_modified_date() ) {
return false;
}
if ( Param::request( 'action' ) === 'et_fb_ajax_save' ) {
if (
empty( $_REQUEST['et_fb_save_nonce'] ) ||
! wp_verify_nonce( Param::request( 'et_fb_save_nonce' ), 'et_fb_save_nonce' )
) {
return false;
}
$options = ! empty( $_REQUEST['options'] ) ? $_REQUEST['options'] : []; //phpcs:ignore
return ! empty( $options['conditional_tags'] ) && ! empty( $options['conditional_tags']['lock_modified_date'] );
}
if ( Param::request( 'action' ) === 'elementor_ajax' ) {
return wp_verify_nonce( Param::request( '_nonce' ), 'elementor_ajax' ) && ! empty( $_REQUEST['lock_modified_date'] );
}
return isset( $data['lock_modified_date'] ) ? $data['lock_modified_date'] : Helper::get_post_meta( 'lock_modified_date', $post_id );
}
}

View File

@@ -0,0 +1,306 @@
<?php
/**
* The admin notices.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Runner;
use RankMath\Helper;
use RankMath\Traits\Ajax;
use RankMath\Traits\Hooker;
use RankMath\Helpers\Sitepress;
use RankMath\Helpers\Param;
defined( 'ABSPATH' ) || exit;
/**
* Notices class.
*/
class Notices implements Runner {
use Hooker;
use Ajax;
/**
* Register hooks.
*/
public function hooks() {
$this->action( 'admin_init', 'notices' );
$this->action( 'wp_helpers_notification_dismissed', 'notice_dismissible' );
}
/**
* Run all notices routine.
*/
public function notices() {
$this->is_plugin_configured();
$this->new_post_type();
$this->convert_wpml_settings();
$this->permalink_changes_warning();
$this->react_settings_ui_notice();
}
/**
* Show a persistent admin notice when the React Settings UI is disabled.
*
* Adds a dismissible, persistent error when the temporary option to
* disable the React-based Settings UI is turned off. The notice is removed
* when the React Settings UI is enabled again.
*
* @since 1.0.255
* @return void
*/
private function react_settings_ui_notice() {
// Only relevant for admins in the dashboard context.
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$notice_id = 'rank_math_react_settings_ui_disabled';
if ( ! Helper::is_react_enabled() ) {
$message = sprintf(
// Translators: 1: opening anchor tag, 2: closing anchor tag.
__( 'The React Settings UI is currently disabled, and the classic settings interface is active. Note: The PHP-based settings interface will be removed in an upcoming release. %1$sEnable the React Settings UI%2$s to switch back.', 'rank-math' ),
'<a href="' . esc_url( Helper::get_dashboard_url() ) . '">',
'</a>'
);
Helper::add_notification(
$message,
[
'type' => 'error',
'id' => $notice_id,
]
);
return;
}
// React UI is enabled; ensure any prior notice is removed.
Helper::remove_notification( $notice_id );
}
/**
* Set known post type after notice dismissal.
*
* @param string $notification_id Notification id.
*/
public function notice_dismissible( $notification_id ) {
if ( 'new_post_type' === $notification_id ) {
$current = get_post_types( [ 'public' => true ] );
update_option( 'rank_math_known_post_types', $current );
if ( Helper::is_module_active( 'sitemap' ) ) {
\RankMath\Sitemap\Cache::invalidate_storage();
}
return;
}
if ( 'convert_wpml_settings' === $notification_id ) {
update_option( 'rank_math_wpml_notice_dismissed', true );
}
if ( 'rank-math-site-url-mismatch' === $notification_id ) {
update_option( 'rank_math_siteurl_mismatch_notice_dismissed', true );
}
}
/**
* If plugin configuration not done.
*/
private function is_plugin_configured() {
if ( 'mts-install-plugins' === Param::get( 'page' ) ) {
return;
}
if ( rank_math()->notification->get_notification_by_id( 'plugin_not_setup' ) && ! Helper::is_configured() ) {
$message = sprintf(
'<b>Warning!</b> You didn\'t set up your Rank Math SEO plugin yet, which means you\'re missing out on essential settings and tweaks! <a href="%s">Complete your setup by clicking here.</a>',
Helper::get_admin_url( 'wizard' )
);
Helper::add_notification(
$message,
[
'type' => 'warning',
'id' => 'plugin_not_setup',
]
);
}
}
/**
* Add notification if a new post type is detected.
*/
private function new_post_type() {
$known = get_option( 'rank_math_known_post_types', [] );
$current = Helper::get_accessible_post_types();
$new = array_diff( $current, $known );
if ( empty( $new ) ) {
return;
}
$list = '<code>' . implode( '</code>, <code>', $new ) . '</code>';
/* Translators: placeholder is the post type name. */
$message = __( 'Rank Math has detected a new post type: %1$s. You may want to check the settings of the <a href="%2$s">Titles &amp; Meta page</a>.', 'rank-math' );
$count = count( $new );
if ( $count > 1 ) {
/* Translators: placeholder is the post type names separated with commas. */
$message = __( 'Rank Math has detected new post types: %1$s. You may want to check the settings of the <a href="%2$s">Titles &amp; Meta page</a>.', 'rank-math' );
}
$message = $this->do_filter( 'admin/notice/new_post_type', $message, $count );
$message = sprintf( wp_kses_post( $message ), $list, Helper::get_settings_url( 'titles', 'post-type-' . key( $new ) ), Helper::get_settings_url( 'sitemap', 'post-type-' . key( $new ) ) );
Helper::add_notification(
$message,
[
'type' => 'info',
'id' => 'new_post_type',
]
);
}
/**
* Function to show Show String Translation plugin notice and convert the settings.
*/
private function convert_wpml_settings() {
if ( ! Sitepress::get()->is_active() || get_option( 'rank_math_wpml_data_converted' ) ) {
return;
}
if ( ! function_exists( 'icl_add_string_translation' ) ) {
if ( ! get_option( 'rank_math_wpml_notice_dismissed' ) ) {
Helper::add_notification(
__( 'Please activate the WPML String Translation plugin to convert Rank Math Setting values in different languages.', 'rank-math' ),
[
'type' => 'error',
'id' => 'convert_wpml_settings',
]
);
}
return;
}
$languages = icl_get_languages(); // @phpstan-ignore-line
foreach ( $languages as $lang_code => $language ) {
foreach ( [ 'general', 'titles' ] as $option ) {
$data = get_option( "rank-math-options-{$option}_$lang_code" );
if ( empty( $data ) ) {
continue;
}
$common_data = array_intersect( array_keys( $data ), $this->get_translatable_options() );
if ( empty( $common_data ) ) {
continue;
}
foreach ( $common_data as $option_key ) {
$string_id = icl_get_string_id( Helper::get_settings( "$option.$option_key" ), "admin_texts_rank-math-options-$option" ); // @phpstan-ignore-line
icl_add_string_translation( $string_id, $lang_code, $data[ $option_key ], 10 ); // @phpstan-ignore-line
}
}
}
update_option( 'rank_math_wpml_data_converted', true );
}
/**
* Get Translatable option keys.
*
* @return array
*/
private function get_translatable_options() {
$options = [
'img_alt_format',
'img_title_format',
'breadcrumbs_separator',
'breadcrumbs_prefix',
'breadcrumbs_home_link',
'breadcrumbs_home_label',
'breadcrumbs_archive_format',
'breadcrumbs_search_format',
'breadcrumbs_404_label',
'rss_before_content',
'rss_after_content',
'title_separator',
'homepage_title',
'homepage_description',
'homepage_facebook_title',
'homepage_facebook_description',
'author_archive_title',
'author_archive_description',
'date_archive_title',
'date_archive_description',
'search_title',
'404_title',
];
$post_types = Helper::get_accessible_post_types();
foreach ( $post_types as $post_type => $data ) {
$options = array_merge(
$options,
[
"pt_{$post_type}_title",
"pt_{$post_type}_description",
"pt_{$post_type}_archive_title",
"pt_{$post_type}_archive_description",
"pt_{$post_type}_default_snippet_name",
"pt_{$post_type}_default_snippet_desc",
]
);
}
$taxonomies = Helper::get_accessible_taxonomies();
foreach ( $taxonomies as $taxonomy => $data ) {
$options = array_merge(
$options,
[
"tax_{$taxonomy}_title",
"tax_{$taxonomy}_description",
]
);
}
return $options;
}
/**
* Maybe add notice on Permalinks page about the risks of changing the permalinks on a live site.
*
* @return void
*/
public function permalink_changes_warning() {
global $pagenow;
if ( 'options-permalink.php' !== $pagenow ) {
return;
}
$this->action( 'admin_enqueue_scripts', 'add_permalink_changes_warning', 12 );
}
/**
* Add the notice for the Permalinks page.
*
* @return void
*/
public function add_permalink_changes_warning() {
wp_enqueue_script( 'rank-math-core-permalink-settings' );
$message = __( '<b>Rank Math Warning:</b> Changing the permalinks on a live, indexed site may result in serious loss of traffic if done incorrectly. Consider adding a new redirection from the old URL format to the new one.', 'rank-math' );
Helper::add_notification(
$message,
[
'type' => 'warning',
'screen' => 'options-permalink',
'classes' => 'hidden rank-math-notice-permalinks-warning is-dismissible',
]
);
}
}

View File

@@ -0,0 +1,658 @@
<?php
/**
* The option center of the plugin.
*
* @since 1.0.9
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\KB;
use RankMath\CMB2;
use RankMath\Helper;
use RankMath\Runner;
use RankMath\Traits\Hooker;
use RankMath\Helpers\Arr;
use RankMath\Helpers\Param;
use RankMath\Wizard\Search_Console;
use RankMath\Admin\Sanitize_Settings;
defined( 'ABSPATH' ) || exit;
/**
* Option_Center class.
*/
class Option_Center implements Runner {
use Hooker;
/**
* Register hooks.
*/
public function hooks() {
$this->action( 'init', 'register_general_settings', 125 );
$this->action( 'init', 'register_title_settings', 125 );
$this->filter( 'rank_math/settings/title', 'title_post_type_settings', 1 );
$this->filter( 'rank_math/settings/title', 'title_taxonomy_settings', 1 );
$this->filter( 'rank_math/settings/general', 'remove_unwanted_general_tabs', 1 );
$this->action( 'admin_enqueue_scripts', 'enqueue_settings_translations', 11 );
}
/**
* General Settings.
*/
public function register_general_settings() {
$tabs = [
'links' => [
'icon' => 'rm-icon rm-icon-link',
'title' => esc_html__( 'Links', 'rank-math' ),
/* translators: Link to kb article */
'desc' => sprintf( esc_html__( 'Change how some of the links open and operate on your website. %s.', 'rank-math' ), '<a href="' . KB::get( 'link-settings', 'Options Panel Links Tab' ) . '" target="_blank">' . esc_html__( 'Learn More', 'rank-math' ) . '</a>' ),
],
'breadcrumbs' => [
'icon' => 'rm-icon rm-icon-direction',
'title' => esc_html__( 'Breadcrumbs', 'rank-math' ),
'classes' => 'rank-math-advanced-option',
/* translators: Link to kb article */
'desc' => sprintf( esc_html__( 'Here you can set up the breadcrumbs function. %s', 'rank-math' ), '<a href="' . KB::get( 'breadcrumbs', 'Options Panel Breadcrumbs Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>. <br/>' ),
'after_row' => current_theme_supports( 'rank-math-breadcrumbs' ) ? '' : '<div class="notice notice-alt notice-warning warning inline rank-math-notice"><p>' . esc_html__( 'Use the following code in your theme template files to display breadcrumbs.', 'rank-math' ) . ' <a href="' . KB::get( 'breadcrumbs-install', 'Options Panel Breadcrumbs Tab' ) . '" target="_blank">' . esc_html__( 'Learn More', 'rank-math' ) . '</a><br /><code>&lt;?php if (function_exists(\'rank_math_the_breadcrumbs\')) rank_math_the_breadcrumbs(); ?&gt;</code> OR <code>[rank_math_breadcrumb]</code></p></div>',
],
'webmaster' => [
'icon' => 'rm-icon rm-icon-toolbox',
'title' => esc_html__( 'Webmaster Tools', 'rank-math' ),
/* translators: Link to kb article */
'desc' => sprintf( esc_html__( 'Enter verification codes for third-party webmaster tools. %s', 'rank-math' ), '<a href="' . KB::get( 'webmaster-tools', 'Options Panel Webmaster Tools Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>.<br />' ),
],
'others' => [
'icon' => 'rm-icon rm-icon-misc',
'title' => esc_html__( 'Others', 'rank-math' ),
/* translators: Link to kb article */
'desc' => sprintf( esc_html__( 'Change some uncommon but essential settings here. %s.', 'rank-math' ), '<a href="' . KB::get( 'other-settings', 'Options Panel Others Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>' ),
'classes' => 'rank-math-advanced-option',
],
];
if ( is_super_admin() && 'rank-math-options-general' === Param::get( 'page' ) ) {
Arr::insert(
$tabs,
[
'htaccess' => [
'icon' => 'rm-icon rm-icon-htaccess',
'title' => esc_html__( 'Edit .htaccess', 'rank-math' ),
/* translators: Link to kb article */
'desc' => sprintf( esc_html__( 'Edit the contents of your .htaccess file easily. %s.', 'rank-math' ), '<a href="' . KB::get( 'edit-htaccess', 'Options Panel htaccess Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>' ),
'classes' => 'rank-math-advanced-option',
'json' => [
'htaccessData' => Admin_Helper::get_htaccess_data(),
],
],
],
5
);
}
/**
* Allow developers to add new sections in the General Settings.
*
* @param array $tabs
*/
$tabs = $this->do_filter( 'settings/general', $tabs );
new Register_Options_Page(
[
'key' => 'rank-math-options-general',
'title' => esc_html__( 'SEO Settings', 'rank-math' ),
'menu_title' => esc_html__( 'General Settings', 'rank-math' ),
'capability' => 'rank_math_general',
'folder' => 'general',
'tabs' => $tabs,
]
);
}
/**
* Remove unneeded tabs from the General Settings.
*
* @param array $tabs Hold tabs for optional panel.
* @return array
*/
public function remove_unwanted_general_tabs( $tabs ) {
if ( is_multisite() ) {
unset( $tabs['robots'] );
}
if ( ! Helper::has_cap( 'edit_htaccess' ) && is_multisite() ) {
unset( $tabs['htaccess'] );
}
return $tabs;
}
/**
* Register SEO Titles & Meta Settings.
*/
public function register_title_settings() {
$homepage_notice = '';
if ( 'page' === get_option( 'show_on_front' ) ) {
$home_page_id = get_option( 'page_on_front' );
if ( ! $home_page_id ) {
$home_page_id = get_option( 'page_for_posts' );
}
$homepage_notice = '<a href="' . admin_url( 'post.php?post=' . $home_page_id . '&action=edit' ) . '">' . esc_html__( 'Edit Page: ', 'rank-math' ) . get_the_title( $home_page_id ) . '</a>';
}
$tabs = [
'global' => [
'icon' => 'rm-icon rm-icon-settings',
'title' => esc_html__( 'Global Meta', 'rank-math' ),
/* translators: Link to KB article */
'desc' => sprintf( esc_html__( 'Change Global meta settings that take effect across your website. %s.', 'rank-math' ), '<a href="' . KB::get( 'titles-meta', 'Options Panel Meta Global Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>' ),
'json' => [
'overlayImages' => array_merge( [ '' => __( 'Off', 'rank-math' ) ], Helper::choices_overlay_images( 'names' ) ),
],
],
'local' => [
'icon' => 'rm-icon rm-icon-local-seo',
'title' => esc_html__( 'Local SEO', 'rank-math' ),
/* translators: Redirection page url */
'desc' => sprintf( wp_kses_post( __( 'Optimize for local searches and Knowledge Graph using these settings. %s.', 'rank-math' ) ), '<a href="' . KB::get( 'local-seo-settings', 'Options Panel Meta Local Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>' ),
'after_row' => '<div class="notice notice-alt notice-info info inline rank-math-notice"><p>' . __( 'Use the <code>[rank_math_contact_info]</code> shortcode to display contact information in a nicely formatted way. You should also claim your business on Google if you have not already.', 'rank-math' ) . '</p></div>',
],
'social' => [
'icon' => 'rm-icon rm-icon-social',
'title' => esc_html__( 'Social Meta', 'rank-math' ),
/* translators: Link to social setting KB article */
'desc' => sprintf( esc_html__( "Add social account information to your website's Schema and Open Graph. %s.", 'rank-math' ), '<a href="' . KB::get( 'social-meta-settings', 'Options Panel Meta Social Tab' ) . '" target="_blank">' . esc_html__( 'Learn More', 'rank-math' ) . '</a>' ),
],
'homepage' => [
'icon' => 'rm-icon rm-icon-home',
'title' => esc_html__( 'Homepage', 'rank-math' ),
'desc' => sprintf(
/* translators: Link to KB article */
esc_html__( 'Add SEO meta and OpenGraph details to your homepage. %s.', 'rank-math' ),
'<a href="' . KB::get( 'homepage-settings', 'Options Panel Meta Home Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>'
),
'json' => [
'staticHomePageNotice' => $homepage_notice,
],
],
'author' => [
'icon' => 'rm-icon rm-icon-users',
'title' => esc_html__( 'Authors', 'rank-math' ),
/* translators: Link to KB article */
'desc' => sprintf( esc_html__( 'Change SEO options related to the author archives. %s.', 'rank-math' ), '<a href="' . KB::get( 'author-settings', 'Options Panel Meta Author Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>' ),
'json' => [
'disableAutorArchive' => $this->do_filter( 'settings/titles/disable_author_archives', 'off' ),
],
],
'misc' => [
'icon' => 'rm-icon rm-icon-misc',
'title' => esc_html__( 'Misc Pages', 'rank-math' ),
/* translators: Link to KB article */
'desc' => sprintf( esc_html__( 'Customize SEO meta settings of pages like search results, 404s, etc. %s.', 'rank-math' ), '<a href="' . KB::get( 'misc-settings', 'Options Panel Meta Misc Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>' ),
],
];
/**
* Allow developers to add new section in the Title Settings.
*
* @param array $tabs
*/
$tabs = $this->do_filter( 'settings/title', $tabs );
new Register_Options_Page(
[
'key' => 'rank-math-options-titles',
'title' => esc_html__( 'SEO Titles &amp; Meta', 'rank-math' ),
'menu_title' => esc_html__( 'Titles &amp; Meta', 'rank-math' ),
'capability' => 'rank_math_titles',
'folder' => 'titles',
'tabs' => $tabs,
]
);
if ( is_admin() ) {
Helper::add_json( 'postTitle', 'Post Title' );
Helper::add_json( 'postUri', home_url( '/post-title' ) );
Helper::add_json( 'blogName', get_bloginfo( 'name' ) );
}
}
/**
* Add post type tabs in the Title Settings panel.
*
* @param array $tabs Holds the tabs of the options panel.
* @return array
*/
public function title_post_type_settings( $tabs ) {
$icons = Helper::choices_post_type_icons();
$links = [
'post' => '<a href="' . KB::get( 'post-settings', 'Options Panel Meta Posts Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>.',
'page' => '<a href="' . KB::get( 'page-settings', 'Options Panel Meta Pages Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>.',
'product' => '<a href="' . KB::get( 'product-settings', 'Options Panel Meta Products Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>.',
'attachment' => '<a href="' . KB::get( 'media-settings', 'Options Panel Meta Attachments Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>.',
];
$names = [
'post' => 'single %s',
'page' => 'single %s',
'product' => 'product pages',
'attachment' => 'media %s',
];
$tabs['p_types'] = [
'title' => esc_html__( 'Post Types:', 'rank-math' ),
'type' => 'seprator',
'name' => 'p_types_separator',
];
foreach ( Helper::get_accessible_post_types() as $post_type ) {
$obj = get_post_type_object( $post_type );
$link = isset( $links[ $obj->name ] ) ? $links[ $obj->name ] : '';
$obj_name = isset( $names[ $obj->name ] ) ? sprintf( $names[ $obj->name ], $obj->name ) : $obj->name;
$is_attachment = $post_type === 'attachment';
$richsnp_default = 'off';
if ( ( class_exists( 'WooCommerce' ) && 'product' === $post_type ) || ( class_exists( 'Easy_Digital_Downloads' ) && 'download' === $post_type ) ) {
$richsnp_default = 'product';
}
if ( $post_type === 'post' ) {
$richsnp_default = 'article';
}
$primary_taxonomy_hash = [
'post' => 'category',
'product' => 'product_cat',
];
$tabs[ 'post-type-' . $obj->name ] = [
'title' => $is_attachment ? esc_html__( 'Attachments', 'rank-math' ) : $obj->label,
'icon' => isset( $icons[ $obj->name ] ) ? $icons[ $obj->name ] : $icons['default'],
/* translators: 1. post type name 2. link */
'desc' => sprintf( esc_html__( 'Change Global SEO, Schema, and other settings for %1$s. %2$s', 'rank-math' ), $obj_name, $link ),
'post_type' => $obj->name,
'file' => rank_math()->includes_dir() . 'settings/titles/post-types.php',
'classes' => 'attachment' === $post_type ? 'rank-math-advanced-option' : '',
'json' => [
'isWooCommerceActive' => class_exists( 'WooCommerce' ),
'isEddActive' => class_exists( 'Easy_Digital_Downloads' ),
'isWebStoriesActive' => defined( 'WEBSTORIES_VERSION' ),
$post_type => [
'title' => $is_attachment ? esc_html__( 'Attachments', 'rank-math' ) : $obj->label,
'name' => $is_attachment ? esc_html__( 'Media', 'rank-math' ) : $obj->labels->singular_name,
'schemaTypes' => Helper::choices_rich_snippet_types( esc_html__( 'None (Click here to set one)', 'rank-math' ), $post_type ),
'taxonomies' => Helper::get_object_taxonomies( $post_type ),
'hasArchive' => $obj->has_archive,
'customRobots' => false,
'schemaDefault' => $this->do_filter( 'settings/snippet/type', $richsnp_default, $post_type ),
'articleType' => $this->do_filter( 'settings/snippet/article_type', 'post' === $post_type ? 'BlogPosting' : 'Article', $post_type ),
'enableLinkSuggestion' => $this->do_filter( 'settings/titles/link_suggestions', true, $post_type ),
'primaryTaxonomy' => isset( $primary_taxonomy_hash[ $post_type ] ) ? $primary_taxonomy_hash[ $post_type ] : 'off',
],
],
];
}
return $tabs;
}
/**
* Add taxonomy tabs in the Title Settings panel.
*
* @param array $tabs Holds the tabs of the options panel.
* @return array
*/
public function title_taxonomy_settings( $tabs ) {
$icons = Helper::choices_taxonomy_icons();
$hash_name = [
'category' => 'category archive pages',
'product_cat' => 'Product category pages',
'product_tag' => 'Product tag pages',
];
$hash_link = [
'category' => '<a href="' . KB::get( 'category-settings', 'Options Panel Meta Categories Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>.',
'post_tag' => '<a href="' . KB::get( 'tag-settings', 'Options Panel Meta Tags Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>.',
'product_cat' => '<a href="' . KB::get( 'product-categories-settings', 'Options Panel Meta Product Categories Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>.',
'product_tag' => '<a href="' . KB::get( 'product-tags-settings', 'Options Panel Meta Product Tags Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>.',
];
$taxonomies_data = [];
foreach ( Helper::get_accessible_taxonomies() as $taxonomy ) {
$attached = implode( ' + ', $taxonomy->object_type );
$taxonomies_data[ $attached ][ $taxonomy->name ] = $taxonomy;
}
foreach ( $taxonomies_data as $attached => $taxonomies ) {
// Seprator.
$tabs[ $attached ] = [
'title' => ucwords( $attached ) . ':',
'type' => 'seprator',
'name' => 'taxonomy_separator',
];
foreach ( $taxonomies as $taxonomy ) {
$link = isset( $hash_link[ $taxonomy->name ] ) ? $hash_link[ $taxonomy->name ] : '';
$taxonomy_name = isset( $hash_name[ $taxonomy->name ] ) ? $hash_name[ $taxonomy->name ] : $taxonomy->label;
$tabs[ 'taxonomy-' . $taxonomy->name ] = [
'icon' => isset( $icons[ $taxonomy->name ] ) ? $icons[ $taxonomy->name ] : $icons['default'],
'title' => $taxonomy->label,
/* translators: 1. taxonomy name 2. link */
'desc' => sprintf( esc_html__( 'Change Global SEO, Schema, and other settings for %1$s. %2$s', 'rank-math' ), $taxonomy_name, $link ),
'taxonomy' => $taxonomy->name,
'file' => rank_math()->includes_dir() . 'settings/titles/taxonomies.php',
];
}
}
if ( isset( $tabs['taxonomy-post_format'] ) ) {
$tab = $tabs['taxonomy-post_format'];
unset( $tabs['taxonomy-post_format'] );
$tab['title'] = esc_html__( 'Post Formats', 'rank-math' );
$tab['page_title'] = esc_html__( 'Post Formats Archive', 'rank-math' );
Arr::insert( $tabs, [ 'taxonomy-post_format' => $tab ], 5 );
}
return $tabs;
}
/**
* Save Settings data. Called from the `updateSettings` endpoint.
*
* @param string $type Settings type.
* @param array $settings Settings data.
* @param array $field_types Field ids with types use to sanitize the value.
* @param array $updated Array of field ids that were updated.
* @param bool $is_reset Whether the request is to reset the data.
* @return array
*/
public static function save_settings( $type, $settings, $field_types, $updated, $is_reset ) {
$notifications = [];
$update_htaccess = self::maybe_update_htaccess( $settings );
if ( ! empty( $update_htaccess ) ) {
$notifications[] = $update_htaccess;
}
$update_analytics = self::maybe_update_analytics( $settings, $updated );
if ( ! empty( $update_analytics ) ) {
$notifications[] = $update_analytics;
}
do_action( 'rank_math/settings/before_save', $type, $settings );
foreach (
[
'htaccess_allow_editing',
'htaccess_content',
'searchConsole',
'analyticsData',
'analytics',
'usage_tracking',
] as $key
) {
if ( isset( $settings[ $key ] ) ) {
unset( $settings[ $key ] );
}
}
$settings = Sanitize_Settings::sanitize( $settings, $field_types );
self::check_updated_fields( $updated, $is_reset );
// Get current settings to compare with new settings.
$current_settings = Helper::get_settings( $type );
// Only update fields that have actually changed.
$changed_settings = self::get_changed_settings( $current_settings, $settings );
// If no settings have changed, return early.
if ( empty( $changed_settings ) ) {
return [
'notifications' => $notifications,
'settings' => $current_settings,
];
}
$map = [
'general' => [ $changed_settings, null, null ],
'titles' => [ null, $changed_settings, null ],
'sitemap' => [ null, null, $changed_settings ],
];
Helper::update_all_settings( ...$map[ $type ] );
rank_math()->settings->reset();
do_action( 'rank_math/settings/after_save', $type, $changed_settings );
return [
'notifications' => $notifications,
'settings' => apply_filters( 'rank_math/settings/saved_data', Helper::get_settings( $type ), $type ),
];
}
/**
* Update Analytics data.
*
* @param array $settings Settings data.
* @param string $updated View that was updated.
*/
public static function maybe_update_analytics( $settings, $updated ) {
if ( empty( $updated ) || ( ! in_array( 'searchConsole', $updated, true ) && ! in_array( 'analyticsData', $updated, true ) ) ) {
return;
}
Search_Console::save( $settings );
$days = $settings['console_caching_control'] ?? 90;
$search_console = $settings['searchConsole'] ?? [];
if ( in_array( 'searchConsole', $updated, true ) && ! empty( $search_console ) ) {
$search_console['days'] = $days;
$response = \RankMath\Analytics\AJAX::get()->do_save_analytic_profile( $search_console );
if ( is_wp_error( $response ) ) {
return [
'error' => $response->get_error_message(),
];
}
}
$google_analytics = $settings['analyticsData'] ?? [];
if ( in_array( 'analyticsData', $updated, true ) && ! empty( $google_analytics ) ) {
$google_analytics['days'] = $days;
$response = \RankMath\Analytics\AJAX::get()->do_save_analytic_options( $google_analytics );
if ( is_wp_error( $response ) ) {
return [
'error' => $response->get_error_message(),
];
}
}
}
/**
* Enqueue settings translations when React UI is enabled as the settings pages are loaded in chunks.
*/
public function enqueue_settings_translations() {
if ( ! Helper::is_react_enabled() ) {
return;
}
$page = str_replace( 'rank-math-options-', '', Param::get( 'page', '' ) );
$hash = [
'general' => 'generalSettings',
'titles' => 'titleSettings',
'sitemap' => 'sitemapSettings',
'instant-indexing' => 'instantIndexingSettings',
];
if ( ! isset( $hash[ $page ] ) ) {
return;
}
$chunk = $hash[ $page ];
wp_enqueue_script( 'rank-math-settings-chunk', rank_math()->plugin_url() . "assets/admin/js/$chunk.js", [ 'rank-math-options' ], rank_math()->version, true );
wp_set_script_translations( 'rank-math-settings-chunk', 'rank-math', rank_math()->plugin_dir() . 'languages/' );
wp_set_script_translations( 'rank-math-options', 'rank-math', rank_math()->plugin_dir() . 'languages/' );
wp_set_script_translations( 'rank-math-components', 'rank-math', rank_math()->plugin_dir() . 'languages/' );
$this->do_action( 'admin/after_settings_scripts' );
}
/**
* Check if certain fields got updated.
*
* @param array $updated Updated fields id.
* @param bool $is_reset Whether to reset the settings.
*/
private static function check_updated_fields( $updated, $is_reset ) {
if ( $is_reset ) {
Helper::schedule_flush_rewrite();
return;
}
/**
* Filter: Allow developers to add option fields which will flush the rewrite rules when updated.
*
* @param array $flush_fields Array of field IDs for which we need to flush.
*/
$flush_fields = apply_filters(
'rank_math/flush_fields',
[
'strip_category_base',
'disable_author_archives',
'url_author_base',
'attachment_redirect_urls',
'attachment_redirect_default',
'nofollow_external_links',
'nofollow_image_links',
'nofollow_domains',
'nofollow_exclude_domains',
'new_window_external_links',
'redirections_header_code',
'redirections_post_redirect',
'redirections_debug',
]
);
foreach ( $flush_fields as $field_id ) {
if ( in_array( $field_id, $updated, true ) ) {
Helper::schedule_flush_rewrite();
break;
}
}
}
/**
* Get only the settings that have changed.
*
* @param array $current_settings Current settings from database.
* @param array $new_settings New settings to be saved.
* @return array
*/
private static function get_changed_settings( $current_settings, $new_settings ) {
// Filter out invalid keys from both arrays.
$new_settings = array_filter( $new_settings, [ __CLASS__, 'is_valid_key' ], ARRAY_FILTER_USE_KEY );
$current_settings = array_filter( $current_settings, [ __CLASS__, 'is_valid_key' ], ARRAY_FILTER_USE_KEY );
// Merge current settings with new settings, new settings take precedence.
return array_merge( $current_settings, $new_settings );
}
/**
* Check if a key is valid for settings.
*
* @param mixed $key The key to validate.
* @return bool
*/
private static function is_valid_key( $key ) {
return is_string( $key ) && ! empty( $key );
}
/**
* Update .htaccess.
*
* @param array { $settings } Settings data.
*/
private static function maybe_update_htaccess( $settings ) {
if ( empty( $settings['htaccess_allow_editing'] ) ) {
return;
}
if ( ! is_super_admin() || ! Helper::has_cap( 'general' ) || ! Helper::has_cap( 'edit_htaccess' ) ) {
return [
'error' => esc_html__( 'You do not have permission to edit the .htaccess file.', 'rank-math' ),
];
}
if ( ! Helper::is_edit_allowed() ) {
return [
'error' => esc_html__( 'You do not have permission to edit the .htaccess file.', 'rank-math' ),
];
}
// phpcs:ignore= WordPress.Security.ValidatedSanitizedInput -- Writing to .htaccess file and escaping for HTML will break functionality.
$content = isset( $settings['htaccess_content'] ) ? $settings['htaccess_content'] : '';
if ( empty( $content ) ) {
return;
}
if ( ! self::do_htaccess_backup() ) {
return [
'error' => esc_html__( 'Failed to backup .htaccess file. Please check file permissions.', 'rank-math' ),
];
}
if ( ! self::do_htaccess_update( $content ) ) {
return [
'error' => esc_html__( 'Failed to update .htaccess file. Please check file permissions.', 'rank-math' ),
];
}
return [
'success' => esc_html__( '.htaccess file updated successfully.', 'rank-math' ),
];
}
/**
* Create htaccess backup.
*
* @return bool
*/
private static function do_htaccess_backup() {
if ( ! Helper::is_filesystem_direct() ) {
return false;
}
$wp_filesystem = Helper::get_filesystem();
$path = get_home_path();
$file = $path . '.htaccess';
if ( ! $wp_filesystem->is_writable( $path ) || ! $wp_filesystem->exists( $file ) ) {
return false;
}
$backup = $path . uniqid( '.htaccess_back_' );
return $wp_filesystem->copy( $file, $backup, true );
}
/**
* Update htaccess file.
*
* @param string $content Htaccess content.
* @return string|bool
*/
private static function do_htaccess_update( $content ) {
if ( empty( $content ) || ! Helper::is_filesystem_direct() ) {
return false;
}
$wp_filesystem = Helper::get_filesystem();
$htaccess_file = get_home_path() . '.htaccess';
return ! $wp_filesystem->is_writable( $htaccess_file ) ? false : $wp_filesystem->put_contents( $htaccess_file, $content );
}
}

View File

@@ -0,0 +1,305 @@
<?php
/**
* The option page functionality of the plugin.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use WP_Http;
use RankMath\KB;
use RankMath\CMB2;
use RankMath\Helper;
use RankMath\Traits\Hooker;
use RankMath\Helpers\Str;
use RankMath\Helpers\Param;
use RankMath\Robots_Txt;
use RankMath\Sitemap\Router;
use RankMath\Sitemap\Sitemap;
use RankMath\Admin\Page;
defined( 'ABSPATH' ) || exit;
/**
* Options class.
*/
class Options {
use Hooker;
/**
* Page title.
*
* @var string
*/
public $title = 'Settings';
/**
* Menu title.
*
* @var string
*/
public $menu_title = 'Settings';
/**
* Hold tabs for page.
*
* @var array
*/
public $tabs = [];
/**
* Hold folder name for tab files.
*
* @var string
*/
public $folder = '';
/**
* Menu Position.
*
* @var int
*/
public $position = 10;
/**
* The capability required for this menu to be displayed to the user.
*
* @var string
*/
public $capability = 'manage_options';
/**
* CMB2 option page id.
*
* @var string
*/
private $cmb_id = null;
/**
* Options key.
*
* @var string
*/
public $key = '';
/**
* The Constructor
*
* @param array $config Array of configuration.
*/
public function __construct( $config ) {
$this->config( $config );
$this->cmb_id = $this->key . '_options';
$this->action( 'admin_post_' . $this->key, 'reset_options', 2 );
}
/**
* Create option object and add settings.
*/
public function register_option_page() {
$current_page = str_replace( 'rank-math-options-', '', $this->key );
new Page(
$this->key,
$this->title,
[
'position' => $this->position,
'priority' => 9999,
'parent' => 'rank-math',
'capability' => $this->capability,
'menu_title' => $this->menu_title,
'render' => [ $this, 'display' ],
'classes' => $this->get_body_class(),
'assets' => [
'styles' => [
'select2-rm' => '',
'rank-math-common' => '',
'rank-math-cmb2' => '',
'wp-components' => '',
'rank-math-options' => rank_math()->plugin_url() . 'assets/admin/css/option-panel.css',
],
'scripts' => [
'media-editor' => '',
'underscore' => '',
'select2-rm' => '',
'lodash' => '',
'rank-math-common' => '',
'wp-api-fetch' => '',
'wp-data' => '',
'rank-math-components' => '',
'rank-math-options' => rank_math()->plugin_url() . 'assets/admin/js/settings.js',
],
'json' => $this->get_json_data( $current_page ),
],
]
);
}
/**
* Set the default values if not set.
*
* @param CMB2 $cmb The CMB2 object to hookup.
*/
public function set_defaults( $cmb ) {
foreach ( $cmb->prop( 'fields' ) as $id => $field_args ) {
$field = $cmb->get_field( $id );
if ( isset( $field_args['default'] ) || isset( $field_args['default_cb'] ) ) {
$defaults[ $id ] = $field->get_default();
}
}
// Save Defaults if any.
if ( ! empty( $defaults ) ) {
add_option( $this->key, $defaults );
}
}
/**
* Reset options.
*/
public function reset_options() {
if ( ! check_admin_referer( 'rank-math-reset-options' ) || ! current_user_can( 'manage_options' ) ) {
return false;
}
$url = wp_get_referer();
if ( ! $url ) {
$url = admin_url();
}
if ( filter_has_var( INPUT_POST, 'reset-cmb' ) && Param::post( 'action' ) === $this->key ) {
delete_option( $this->key );
Helper::redirect( esc_url_raw( $url ), WP_Http::SEE_OTHER );
exit;
}
}
/**
* Add classes to <body> of WordPress admin.
*
* @return string
*/
public function get_body_class() {
$mode = Helper::is_advanced_mode() ? 'advanced' : 'basic';
return [
'rank-math-page ',
'rank-math-mode-' . $mode,
];
}
/**
* Display Setting on a page.
*/
public function display() {
?>
<div id="rank-math-options" class="<?php echo esc_attr( $this->cmb_id ); ?>"></div>
<?php
}
/**
* Get setting tabs.
*
* @return array
*/
private function get_tabs() {
$filter = str_replace( '-', '_', str_replace( 'rank-math-', '', $this->key ) );
/**
* Allow developers to add new tabs into option panel.
*
* The dynamic part of hook is, page name without 'rank-math-' prefix.
*
* @param array $tabs
*/
return $this->do_filter( "admin/options/{$filter}_tabs", $this->tabs );
}
/**
* Get localized data for the current settings page.
*
* @param string $current_page Current Settings page.
*
* @return array
*/
private function get_json_data( $current_page ) {
if ( Param::get( 'page' ) !== $this->key ) {
return [];
}
if ( is_admin() ) {
rank_math()->variables->setup();
rank_math()->variables->setup_json();
}
$tabs = $this->get_tabs();
$data = $this->do_filter(
"admin/options/{$current_page}_data",
[
'isPro' => defined( 'RANK_MATH_PRO_FILE' ),
'tabs' => array_keys( $tabs ),
'optionPage' => $current_page,
'homeUrl' => get_home_url(),
'data' => $current_page === 'instant-indexing' ? get_option( 'rank-math-options-instant-indexing' ) : Helper::get_settings( $current_page ),
'isSiteConnected' => Helper::is_site_connected(),
'choices' => [
'postTypes' => Helper::choices_post_types(),
'accessiblePostTypes' => Helper::get_accessible_post_types(),
'accessibleTaxonomies' => Helper::get_accessible_taxonomies(),
'choicesPostTypeIcons' => Helper::choices_post_type_icons(),
'choicesTaxonomyIcons' => Helper::choices_taxonomy_icons(),
],
]
);
foreach ( $tabs as $tab ) {
if ( empty( $tab['json'] ) ) {
continue;
}
$data = array_merge( $data, $tab['json'] );
}
$method = "get_{$current_page}_data";
if ( ! method_exists( $this, $method ) ) {
return $data;
}
return array_merge( $data, $this->$method() );
}
/**
* Get General Settings page data.
*
* @return array
*/
private function get_general_data() {
return [
'activateUrl' => Admin_Helper::get_activate_url( admin_url( 'admin.php??page=rank-math-options-general&tab=content-ai' ) ),
'hasBreadcrumbSupport' => current_theme_supports( 'rank-math-breadcrumbs' ),
'showBlogPage' => 'page' === get_option( 'show_on_front' ) && get_option( 'page_for_posts' ) > 0,
'isEditAllowed' => Helper::is_edit_allowed(),
'defaultLanguage' => Helper::content_ai_default_language(),
];
}
/**
* Get General Settings page data.
*
* @return array
*/
private function get_titles_data() {
$data = [
'choicesRobots' => Helper::choices_robots(),
'supportsTitleTag' => current_theme_supports( 'title-tag' ) || wp_is_block_theme(),
'schemaTypes' => Helper::choices_rich_snippet_types( esc_html__( 'None (Click here to set one)', 'rank-math' ) ),
'isRedirectAttachments' => Helper::get_settings( 'general.attachment_redirect_urls' ),
];
return $data;
}
}

View File

@@ -0,0 +1,380 @@
<?php
/**
* The admin-page functionality.
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath\Admin
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Helpers\Param;
use RankMath\Helper;
/**
* Page class.
*/
class Page {
/**
* Unique ID used for menu_slug.
*
* @var string
*/
public $id = null;
/**
* The text to be displayed in the title tags of the page.
*
* @var string
*/
public $title = null;
/**
* The slug name for the parent menu.
*
* @var string
*/
public $parent = null;
/**
* The The on-screen name text for the menu.
*
* @var string
*/
public $menu_title = null;
/**
* The capability required for this menu to be displayed to the user.
*
* @var string
*/
public $capability = 'manage_options';
/**
* The icon for this menu.
*
* @var string
*/
public $icon = 'dashicons-art';
/**
* The position in the menu order this menu should appear.
*
* @var int
*/
public $position = -1;
/**
* The init hook priority.
*
* @var int
*/
public $priority = 25;
/**
* The function/file that displays the page content for the menu page.
*
* @var string|callable
*/
public $render = null;
/**
* The function that run on page POST to save data.
*
* @var callable
*/
public $onsave = null;
/**
* Hold contextual help tabs.
*
* @var array
*/
public $help = null;
/**
* Hold scripts and styles.
*
* @var array
*/
public $assets = null;
/**
* Check if plugin is network active.
*
* @var array
*/
public $is_network = false;
/**
* Hold classes for body tag.
*
* @var array
*/
public $classes = null;
/**
* Hold localized data.
*
* @var array
*/
public $json = null;
/**
* The Constructor.
*
* @param string $id Admin page unique id.
* @param string $title Title of the admin page.
* @param array $config Optional. Override page settings.
*/
public function __construct( $id, $title, $config = [] ) {
// Early bail!
if ( ! $id ) {
wp_die( esc_html__( '$id variable required', 'rank-math' ), esc_html__( 'Variable Required', 'rank-math' ) );
}
if ( ! $title ) {
wp_die( esc_html__( '$title variable required', 'rank-math' ), esc_html__( 'Variable Required', 'rank-math' ) );
}
$this->id = $id;
$this->title = $title;
foreach ( $config as $key => $value ) {
$this->$key = $value;
}
if ( ! $this->menu_title ) {
$this->menu_title = $title;
}
add_action( 'init', [ $this, 'init' ], $this->priority ?? 25 );
}
/**
* Init admin page when WordPress Initialises.
*
* @codeCoverageIgnore
*/
public function init() {
$priority = $this->parent ? intval( $this->position ) : -1;
add_action( $this->is_network ? 'network_admin_menu' : 'admin_menu', [ $this, 'register_menu' ], $priority );
// If not the page is not this page stop here.
if ( ! $this->is_current_page() ) {
return;
}
$hooks = [
'admin_init' => [
'callback' => 'save',
'condition' => ! is_null( $this->onsave ) && is_callable( $this->onsave ),
],
'admin_enqueue_scripts' => [
'callback' => 'enqueue',
'condition' => ! empty( $this->assets ),
],
'admin_head' => [
'callback' => 'contextual_help',
'condition' => ! empty( $this->help ),
],
'admin_body_class' => [
'callback' => 'body_class',
'condition' => ! empty( $this->classes ),
],
];
foreach ( $hooks as $hook => $data ) {
if ( true === $data['condition'] ) {
add_action( $hook, [ $this, $data['callback'] ] );
}
}
}
/**
* Register Admin Menu.
*
* @codeCoverageIgnore
*/
public function register_menu() {
if ( ! $this->parent ) {
add_menu_page( $this->title, $this->menu_title, $this->capability, $this->id, [ $this, 'display' ], $this->icon, $this->position );
return;
}
add_submenu_page( $this->parent, $this->title, $this->menu_title, $this->capability, $this->id, [ $this, 'display' ] );
}
/**
* Enqueue styles and scripts.
*
* @codeCoverageIgnore
*/
public function enqueue() {
$this->enqueue_styles();
$this->enqueue_scripts();
$this->add_localized_data();
}
/**
* Add classes to <body> of WordPress admin.
*
* @codeCoverageIgnore
*
* @param string $classes Space-separated list of CSS classes.
*
* @return string
*/
public function body_class( $classes = '' ) {
return $classes . ' ' . join( ' ', $this->classes );
}
/**
* Save anything you want using onsave function.
*
* @codeCoverageIgnore
*/
public function save() {
call_user_func( $this->onsave, $this );
}
/**
* Contextual Help.
*
* @codeCoverageIgnore
*/
public function contextual_help() {
$screen = get_current_screen();
foreach ( $this->help as $tab_id => $tab ) {
$tab['id'] = $tab_id;
$tab['content'] = $this->get_help_content( $tab );
$screen->add_help_tab( $tab );
}
}
/**
* Render admin page content using render function you passed in config.
*
* @codeCoverageIgnore
*/
public function display() {
if ( is_null( $this->render ) ) {
return;
}
if ( 'settings' === $this->render ) {
return $this->display_settings();
}
if ( is_callable( $this->render ) ) {
call_user_func( $this->render, $this );
return;
}
if ( is_string( $this->render ) ) {
include_once $this->render;
}
}
/**
* Is the page is currrent page.
*
* @return bool
*/
public function is_current_page() {
return Param::get( 'page' ) === $this->id;
}
/**
* Enqueue styles
*
* @codeCoverageIgnore
*/
private function enqueue_styles() {
if ( ! isset( $this->assets['styles'] ) || empty( $this->assets['styles'] ) ) {
return;
}
foreach ( $this->assets['styles'] as $handle => $src ) {
wp_enqueue_style( $handle, $src, null, rank_math()->version );
}
}
/**
* Enqueue scripts.
*
* @codeCoverageIgnore
*/
private function enqueue_scripts() {
if ( ! isset( $this->assets['scripts'] ) || empty( $this->assets['scripts'] ) ) {
return;
}
foreach ( $this->assets['scripts'] as $handle => $src ) {
if ( $handle === 'media-editor' ) {
wp_enqueue_media();
}
wp_enqueue_script( $handle, $src, null, rank_math()->version, true );
}
do_action( 'rank-math/admin_enqueue_scripts' );
}
/**
* Get tab content
*
* @codeCoverageIgnore
*
* @param array $tab Tab to get content for.
*
* @return string
*/
private function get_help_content( $tab ) {
ob_start();
// If it is a function.
if ( isset( $tab['content'] ) && is_callable( $tab['content'] ) ) {
call_user_func( $tab['content'] );
}
// If it is a file.
if ( isset( $tab['view'] ) && $tab['view'] ) {
require $tab['view'];
}
return ob_get_clean();
}
/**
* Localized data.
*/
private function add_localized_data() {
if ( empty( $this->assets['json'] ) ) {
return;
}
foreach ( $this->assets['json'] as $key => $value ) {
Helper::add_json( $key, $value );
}
Helper::add_json(
'settings',
[
'general' => Helper::get_settings( 'general' ),
'titles' => Helper::get_settings( 'titles' ),
'sitemap' => Helper::get_settings( 'sitemap' ),
]
);
}
/**
* Display settings.
*/
private function display_settings() {
echo '<div id="rank-math-settings" class="' . esc_attr( $this->id ) . '"></div>';
}
}

View File

@@ -0,0 +1,571 @@
<?php
/**
* The admin post columns functionality.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Helper;
use RankMath\Helpers\Str;
use RankMath\Helpers\Param;
use RankMath\Runner;
use RankMath\Traits\Hooker;
use RankMath\Admin\Database\Database;
defined( 'ABSPATH' ) || exit;
/**
* Post_Columns class.
*/
class Post_Columns implements Runner {
use Hooker;
/**
* SEO data.
*
* @var array
*/
private $data = [];
/**
* Register hooks.
*/
public function hooks() {
$this->action( 'admin_init', 'init' );
}
/**
* Intialize.
*/
public function init() {
if ( ! Helper::has_cap( 'onpage_general' ) ) {
return;
}
$this->register_post_columns();
$this->register_media_columns();
$this->register_taxonomy_columns();
// Column Content.
$this->filter( 'rank_math_title', 'get_column_title', 5, 2 );
$this->filter( 'rank_math_description', 'get_column_description', 5, 2 );
$this->filter( 'rank_math_seo_details', 'get_column_seo_details', 5 );
}
/**
* Register post column hooks.
*/
private function register_post_columns() {
$post_types = Helper::get_allowed_post_types();
foreach ( $post_types as $post_type ) {
$this->filter( 'edd_download_columns', 'add_columns', 11 );
$this->filter( "manage_{$post_type}_posts_columns", 'add_columns', 11 );
$this->action( "manage_{$post_type}_posts_custom_column", 'columns_contents', 11, 2 );
$this->filter( "manage_edit-{$post_type}_sortable_columns", 'sortable_columns', 11 );
// Also make them hidden by default.
$user_id = get_current_user_id();
$columns_hidden = (array) get_user_meta( $user_id, "manageedit-{$post_type}columnshidden", true );
$maybe_hidden = get_user_meta( $user_id, "manageedit-{$post_type}columnshidden_default", true );
// Continue if default is already set.
if ( $maybe_hidden ) {
continue;
}
// Set it to hidden by default.
$columns_hidden = array_unique( array_merge( $columns_hidden, [ 'rank_math_title', 'rank_math_description' ] ) );
update_user_meta( $user_id, "manageedit-{$post_type}columnshidden", $columns_hidden );
update_user_meta( $user_id, "manageedit-{$post_type}columnshidden_default", '1' );
}
}
/**
* Register media column hooks.
*/
private function register_media_columns() {
if ( ! Helper::get_settings( 'titles.pt_attachment_bulk_editing' ) ) {
return;
}
$this->filter( 'manage_media_columns', 'add_media_columns', 11 );
$this->action( 'manage_media_custom_column', 'media_contents', 11, 2 );
}
/**
* Register taxonomy columns hooks.
*
* @return void
*/
private function register_taxonomy_columns() {
$taxonomies = Helper::get_allowed_taxonomies();
foreach ( $taxonomies as $taxonomy ) {
$this->filter( "manage_edit-{$taxonomy}_columns", 'add_taxonomy_columns', 9 );
$this->filter( "manage_{$taxonomy}_custom_column", 'taxonomy_columns_contents', 10, 3 );
}
}
/**
* Add new columns for SEO title, description and focus keywords.
*
* @param array $columns Array of column names.
*
* @return array
*/
public function add_columns( $columns ) {
global $post_type;
$current_pt = $post_type;
if ( ! $post_type && 'inline-save' === Param::post( 'action' ) ) {
$post_id = Param::post( 'post_ID', 0, FILTER_VALIDATE_INT );
$current_pt = get_post_type( $post_id );
}
$columns['rank_math_seo_details'] = esc_html__( 'SEO Details', 'rank-math' );
if ( Helper::get_settings( 'titles.pt_' . $current_pt . '_bulk_editing', true ) ) {
$columns['rank_math_title'] = esc_html__( 'SEO Title', 'rank-math' );
$columns['rank_math_description'] = esc_html__( 'SEO Desc', 'rank-math' );
}
return $columns;
}
/**
* Make the SEO Score column sortable.
*
* @param array $columns Array of column names.
*
* @return array
*/
public function sortable_columns( $columns ) {
$columns['rank_math_seo_details'] = 'rank_math_seo_score';
return $columns;
}
/**
* Add new columns for Media Alt & Title.
*
* @param array $columns Array of column names.
*
* @return array
*/
public function add_media_columns( $columns ) {
$columns['rank_math_image_title'] = esc_html__( 'Title', 'rank-math' );
$columns['rank_math_image_alt'] = esc_html__( 'Alternative Text', 'rank-math' );
return $columns;
}
/**
* Adds custom columns to taxonomy list view.
*
* @param array $columns Columns belonging to current taxonomy.
*
* @return array
*/
public function add_taxonomy_columns( $columns ) {
$screen = get_current_screen();
if ( Helper::get_settings( 'titles.tax_' . $screen->taxonomy . '_bulk_editing', false ) ) {
$columns['rank_math_title'] = esc_html__( 'SEO Title', 'rank-math' );
$columns['rank_math_description'] = esc_html__( 'SEO Desc', 'rank-math' );
}
return $columns;
}
/**
* Add content for custom column.
*
* @param string $column_name The name of the column to display.
* @param int $post_id The current post ID.
*/
public function columns_contents( $column_name, $post_id ) {
if ( Str::starts_with( 'rank_math', $column_name ) ) {
do_action( $column_name, $post_id );
}
}
/**
* Generate content of custom columns.
*
* @param string $content The content of the current column.
* @param string $column_name The column name.
* @param int $term_id The unique ID of the current term.
*
* @return string
*/
public function taxonomy_columns_contents( $content, $column_name, $term_id ) {
if ( Str::starts_with( 'rank_math_', $column_name ) ) {
do_action( $column_name, $term_id, 'term' );
}
return $content;
}
/**
* Add content for title column.
*
* @param int $object_id The current Object ID.
* @param string $object_type The current Object type.
*/
public function get_column_title( $object_id, $object_type = 'post' ) {
if ( empty( $this->data ) ) {
$method = "get_{$object_type}_seo_data";
$this->$method( $object_type );
}
$title = ! empty( $this->data[ $object_id ]['rank_math_title'] ) ? $this->data[ $object_id ]['rank_math_title'] : '';
if ( ! $title ) {
$title = $this->get_default_title( $object_id, $object_type );
}
?>
<span class="rank-math-column-display"><?php echo esc_html( $title ); ?></span>
<textarea class="rank-math-column-value" data-field="title" tabindex="11"><?php echo esc_attr( $title ); ?></textarea>
<div class="rank-math-column-edit">
<a href="#" class="rank-math-column-save"><?php esc_html_e( 'Save', 'rank-math' ); ?></a>
<a href="#" class="button-link-delete rank-math-column-cancel"><?php esc_html_e( 'Cancel', 'rank-math' ); ?></a>
</div>
<?php
}
/**
* Add content for description column.
*
* @param int $object_id The current Object ID.
* @param string $object_type The current Object type.
*/
public function get_column_description( $object_id, $object_type = 'post' ) {
$description = ! empty( $this->data[ $object_id ]['rank_math_description'] ) ? $this->data[ $object_id ]['rank_math_description'] : '';
if ( ! $description ) {
$description = $this->get_default_description( $object_id, $object_type );
}
?>
<span class="rank-math-column-display"><?php echo esc_html( $description ); ?></span>
<textarea class="rank-math-column-value" data-field="description" tabindex="11"><?php echo esc_attr( $description ); ?></textarea>
<div class="rank-math-column-edit">
<a href="#" class="rank-math-column-save"><?php esc_html_e( 'Save', 'rank-math' ); ?></a>
<a href="#" class="button-link-delete rank-math-column-cancel"><?php esc_html_e( 'Cancel', 'rank-math' ); ?></a>
</div>
<?php
}
/**
* Add content for title column.
*
* @param int $post_id The current post ID.
*/
public function get_column_seo_details( $post_id ) {
if ( empty( $this->data ) ) {
$this->get_post_seo_data();
}
$data = isset( $this->data[ $post_id ] ) ? $this->data[ $post_id ] : [];
if ( ! self::is_post_indexable( $post_id ) ) {
echo '<span class="rank-math-column-display seo-score no-score "><strong>N/A</strong></span>';
echo '<strong>' . esc_html__( 'No Index', 'rank-math' ) . '</strong>';
$this->do_action( 'post/column/seo_details', $post_id, $data, $this->data );
return;
}
$keyword = ! empty( $data['rank_math_focus_keyword'] ) ? $data['rank_math_focus_keyword'] : '';
$keyword = explode( ',', $keyword )[0];
$is_pillar = ! empty( $data['rank_math_pillar_content'] ) && 'on' === $data['rank_math_pillar_content'] ? true : false;
$score = empty( $keyword ) ? false : $this->get_seo_score( $data );
$class = ! $score ? 'no-score' : $this->get_seo_score_class( $score );
$score = $score ? $score . ' / 100' : 'N/A';
?>
<span class="rank-math-column-display seo-score <?php echo esc_attr( $class ); ?> <?php echo ! $score ? 'disabled' : ''; ?>">
<strong><?php echo esc_html( $score ); ?></strong>
<?php if ( $is_pillar ) { ?>
<img class="is-pillar" src="<?php echo esc_url( rank_math()->plugin_url() . 'assets/admin/img/pillar.svg' ); ?>" alt="<?php esc_html_e( 'Is Pillar', 'rank-math' ); ?>" title="<?php esc_html_e( 'Is Pillar', 'rank-math' ); ?>" width="25" />
<?php } ?>
</span>
<label><?php esc_html_e( 'Focus Keyword', 'rank-math' ); ?>:</label>
<span class="rank-math-column-display">
<strong title="Focus Keyword"><?php esc_html_e( 'Keyword', 'rank-math' ); ?>:</strong>
<span>
<?php
echo $keyword ? wp_kses_post( $this->do_filter( 'post/column/seo_details/focus_keyword', $keyword ) ) : esc_html__( 'Not Set', 'rank-math' );
?>
</span>
</span>
<input class="rank-math-column-value" data-field="focus_keyword" tabindex="11" value="<?php echo esc_attr( $keyword ); ?>" />
<?php $this->do_action( 'post/column/seo_details', $post_id, $data, $this->data ); ?>
<div class="rank-math-column-edit">
<a href="#" class="rank-math-column-save"><?php esc_html_e( 'Save', 'rank-math' ); ?></a>
<a href="#" class="button-link-delete rank-math-column-cancel"><?php esc_html_e( 'Cancel', 'rank-math' ); ?></a>
</div>
<?php
}
/**
* Add content for custom media column.
*
* @param string $column_name The name of the column to display.
* @param int $post_id The current post ID.
*/
public function media_contents( $column_name, $post_id ) {
if ( 'rank_math_image_title' === $column_name ) {
$title = get_the_title( $post_id );
?>
<span class="rank-math-column-display"><?php echo esc_html( $title ); ?></span>
<input class="rank-math-column-value" data-field="image_title" tabindex="11" value="<?php echo esc_attr( $title ); ?>" />
<div class="rank-math-column-edit">
<a href="#" class="rank-math-column-save"><?php esc_html_e( 'Save', 'rank-math' ); ?></a>
<a href="#" class="button-link-delete rank-math-column-cancel"><?php esc_html_e( 'Cancel', 'rank-math' ); ?></a>
</div>
<?php
return;
}
if ( 'rank_math_image_alt' === $column_name ) {
$alt = get_post_meta( $post_id, '_wp_attachment_image_alt', true );
?>
<span class="rank-math-column-display"><?php echo esc_html( $alt ); ?></span>
<input class="rank-math-column-value" data-field="image_alt" tabindex="11" value="<?php echo esc_attr( $alt ); ?>" />
<div class="rank-math-column-edit">
<a href="#" class="rank-math-column-save"><?php esc_html_e( 'Save', 'rank-math' ); ?></a>
<a href="#" class="button-link-delete rank-math-column-cancel"><?php esc_html_e( 'Cancel', 'rank-math' ); ?></a>
</div>
<?php
return;
}
}
/**
* Get Default title for the object type.
*
* @param int $object_id The current Object ID.
* @param string $object_type The current Object type.
*/
private function get_default_title( $object_id, $object_type ) {
if ( $object_type === 'term' ) {
$term = get_term( $object_id );
return Helper::get_settings( "titles.tax_{$term->taxonomy}_title" );
}
$post_type = get_post_type( $object_id );
return Helper::get_settings( "titles.pt_{$post_type}_title" );
}
/**
* Get Default description for the object type.
*
* @param int $object_id The current Object ID.
* @param string $object_type The current Object type.
*/
private function get_default_description( $object_id, $object_type ) {
if ( $object_type === 'term' ) {
$term = get_term( $object_id );
return Helper::get_settings( "titles.tax_{$term->taxonomy}_description" );
}
$post_type = get_post_type( $object_id );
$description = has_excerpt( $object_id ) ? '%excerpt%' : Helper::get_settings( "titles.pt_{$post_type}_description" );
}
/**
* Get Terms SEO data.
*/
private function get_term_seo_data() {
$wp_list_table = _get_list_table( 'WP_Terms_List_Table' );
$wp_list_table->prepare_items();
$items = $wp_list_table->items;
if ( empty( $items ) ) {
return false;
}
$term_ids = array_filter(
array_map(
function ( $item ) {
return isset( $item->term_id ) ? $item->term_id : '';
},
$items
)
);
$results = Database::table( 'termmeta' )->select( [ 'term_id', 'meta_key', 'meta_value' ] )->whereIn( 'term_id', $term_ids )->whereLike( 'meta_key', 'rank_math' )->get( ARRAY_A );
if ( empty( $results ) ) {
return false;
}
foreach ( $results as $result ) {
$this->data[ $result['term_id'] ][ $result['meta_key'] ] = $result['meta_value'];
}
}
/**
* Get Post SEO data.
*/
private function get_post_seo_data() {
$post_ids = [];
$post_ids = array_filter( $this->get_post_ids() );
$post_id = (int) Param::post( 'post_ID' );
if ( $post_id ) {
$post_ids[] = $post_id;
}
if ( empty( $post_ids ) ) {
return false;
}
$results = Database::table( 'postmeta' )->select( [ 'post_id', 'meta_key', 'meta_value' ] )->whereIn( 'post_id', $post_ids )->whereLike( 'meta_key', 'rank_math' )->get( ARRAY_A );
if ( empty( $results ) ) {
return false;
}
foreach ( $results as $result ) {
$this->data[ $result['post_id'] ][ $result['meta_key'] ] = $result['meta_value'];
}
}
/**
* Get Post IDs dispalyed on the Post lists page.
*/
private function get_post_ids() {
global $wp_query, $per_page;
if ( empty( $wp_query->posts ) ) {
return [];
}
$pages = $wp_query->posts;
if (
! is_post_type_hierarchical( Param::get( 'post_type' ) ) ||
'menu_order title' !== $wp_query->query['orderby']
) {
return array_map(
function ( $post ) {
return isset( $post->ID ) ? $post->ID : '';
},
$pages
);
}
$children_pages = [];
if ( empty( Param::request( 's' ) ) ) {
$top_level_pages = [];
foreach ( $pages as $page ) {
if ( $page->post_parent > 0 ) {
$children_pages[ $page->post_parent ][] = $page;
} else {
$top_level_pages[] = $page;
}
}
$pages = &$top_level_pages;
}
$pagenum = max( 1, Param::request( 'paged', 0 ) );
$count = 0;
$start = ( $pagenum - 1 ) * $per_page;
$end = $start + $per_page;
$ids = [];
foreach ( $pages as $page ) {
if ( $count >= $end ) {
break;
}
if ( $count >= $start ) {
$ids[] = $page->ID;
}
++$count;
$this->add_child_page_ids( $children_pages, $page->ID, $ids, $count );
}
return $ids;
}
/**
* Add the child page IDs to the list of IDs to be processed.
*
* @param array $children_pages Child Pages.
* @param int $id Current page ID.
* @param array $ids IDs to be processed.
* @param int $count Counter.
*/
private function add_child_page_ids( $children_pages, $id, &$ids, &$count ) {
if ( empty( $children_pages ) || empty( $children_pages[ $id ] ) ) {
return;
}
foreach ( $children_pages[ $id ] as $child_page ) {
$id = $child_page->ID;
$ids[] = $child_page->ID;
++$count;
$this->add_child_page_ids( $children_pages, $id, $ids, $count );
}
}
/**
* Get SEO score.
*
* @param array $data SEO data of current post.
*
* @return string
*/
private function get_seo_score( $data ) {
if ( ! isset( $data['rank_math_seo_score'] ) ) {
return false;
}
if ( ! Helper::is_score_enabled() ) {
return false;
}
return $data['rank_math_seo_score'] ? $data['rank_math_seo_score'] : 0;
}
/**
* Get SEO score rating string: great/good/bad.
*
* @param int $score Score.
*
* @return string
*/
private function get_seo_score_class( $score ) {
if ( $score > 80 ) {
return 'great';
}
if ( $score > 50 && $score < 81 ) {
return 'good';
}
return 'bad';
}
/**
* Check post indexable status.
*
* @param int $post_id Post ID.
*/
public static function is_post_indexable( $post_id ) {
$robots = Param::post( 'rank_math_robots', false, FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
$robots = apply_filters( 'rank_math/admin/robots', $robots, $post_id );
if ( ! empty( $robots ) ) {
return in_array( 'index', $robots, true ) ? true : false;
}
return Helper::is_post_indexable( $post_id );
}
}

View File

@@ -0,0 +1,389 @@
<?php
/**
* The admin post filters functionality.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use WP_Meta_Query;
use RankMath\Helper;
use RankMath\Runner;
use RankMath\Traits\Hooker;
use RankMath\Helpers\Security;
use RankMath\Helpers\Param;
use RankMath\Helpers\DB as DB_Helper;
defined( 'ABSPATH' ) || exit;
/**
* Post_Filters class.
*/
class Post_Filters implements Runner {
use Hooker;
/**
* Register hooks.
*/
public function hooks() {
$this->action( 'admin_init', 'init' );
}
/**
* Intialize.
*/
public function init() {
if ( ! Helper::has_cap( 'general' ) ) {
return;
}
$this->filter( 'pre_get_posts', 'posts_by_seo_filters' );
$this->filter( 'parse_query', 'posts_by_focus_keywords' );
$this->filter( 'restrict_manage_posts', 'add_seo_filters', 11 );
foreach ( Helper::get_allowed_post_types() as $post_type ) {
$this->filter( "views_edit-$post_type", 'add_pillar_content_filter_link' );
}
}
/**
* Filter posts in admin by Rank Math's Filter value.
*
* @param \WP_Query $query The wp_query instance.
*/
public function posts_by_seo_filters( $query ) {
if ( ! $this->can_seo_filters() ) {
return;
}
if ( 'rank_math_seo_score' === $query->get( 'orderby' ) ) {
$query->set( 'orderby', 'meta_value' );
$query->set( 'meta_key', 'rank_math_seo_score' );
$query->set( 'meta_type', 'numeric' );
}
if ( empty( $_GET['pillar_content'] ) && empty( $_GET['seo-filter'] ) ) {
return;
}
$meta_query = [];
// Check for Pillar Content filter.
if ( ! empty( $_GET['pillar_content'] ) ) {
$meta_query[] = [
'key' => 'rank_math_pillar_content',
'value' => 'on',
];
}
$this->set_seo_filters( $meta_query );
$query->set( 'meta_query', $meta_query );
}
/**
* Filter post in admin by Pillar Content.
*
* @param \WP_Query $query The wp_query instance.
*/
public function posts_by_focus_keywords( $query ) {
if ( ! $this->can_fk_filter() ) {
return;
}
if ( $ids = $this->posts_had_reviews() ) { // phpcs:ignore
$query->set( 'post_type', 'any' );
$query->set( 'post__in', $ids );
return;
}
$query->set( 'post_status', 'publish' );
if ( $ids = $this->has_fk_in_title() ) { // phpcs:ignore
$query->set( 'post__in', $ids );
return;
}
$focus_keyword = Param::get( 'focus_keyword', '', FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_BACKTICK );
if ( 1 === absint( $focus_keyword ) ) {
$query->set(
'meta_query',
[
'relation' => 'AND',
[
'key' => 'rank_math_focus_keyword',
'compare' => 'NOT EXISTS',
],
[
'relation' => 'OR',
[
'key' => 'rank_math_robots',
'value' => 'noindex',
'compare' => 'NOT LIKE',
],
[
'key' => 'rank_math_robots',
'compare' => 'NOT EXISTS',
],
],
]
);
return;
}
$query->set( 'post_type', 'any' );
$query->set(
'meta_query',
[
[
'relation' => 'OR',
[
'key' => 'rank_math_focus_keyword',
'value' => $focus_keyword . ',',
'compare' => 'LIKE',
],
[
'key' => 'rank_math_focus_keyword',
'value' => $focus_keyword,
'compare' => 'LIKE',
],
],
]
);
}
/**
* Add SEO filters.
*/
public function add_seo_filters() {
global $post_type;
if ( 'attachment' === $post_type || ! in_array( $post_type, Helper::get_allowed_post_types(), true ) ) {
return;
}
$options = [
'' => esc_html__( 'Rank Math', 'rank-math' ),
'great-seo' => esc_html__( 'SEO Score: Good', 'rank-math' ),
'good-seo' => esc_html__( 'SEO Score: Ok', 'rank-math' ),
'bad-seo' => esc_html__( 'SEO Score: Bad', 'rank-math' ),
'empty-fk' => esc_html__( 'Focus Keyword Not Set', 'rank-math' ),
'noindexed' => esc_html__( 'Articles noindexed', 'rank-math' ),
];
$options = $this->do_filter( 'manage_posts/seo_filter_options', $options, $post_type );
if ( empty( $options ) ) {
return;
}
$selected = Param::get( 'seo-filter' );
?>
<select name="seo-filter" id="rank-math-seo-filter">
<?php foreach ( $options as $val => $option ) : ?>
<option value="<?php echo esc_attr( $val ); ?>" <?php selected( $selected, $val, true ); ?>><?php echo esc_html( $option ); ?></option>
<?php endforeach; ?>
</select>
<?php
}
/**
* Add view to filter list for Pillar Content.
*
* @param array $views An array of available list table views.
*/
public function add_pillar_content_filter_link( $views ) {
global $typenow;
$current = empty( $_GET['pillar_content'] ) ? '' : ' class="current" aria-current="page"';
$pillars = get_posts(
[
'post_type' => $typenow,
'fields' => 'ids',
'posts_per_page' => -1,
'meta_key' => 'rank_math_pillar_content',
'meta_value' => 'on',
]
);
$views['pillar_content'] = sprintf(
'<a href="%1$s"%2$s>%3$s <span class="count">(%4$s)</span></a>',
Security::add_query_arg(
[
'post_type' => $typenow,
'pillar_content' => 1,
]
),
$current,
esc_html__( 'Pillar Content', 'rank-math' ),
number_format_i18n( count( $pillars ) )
);
return $views;
}
/**
* Can apply SEO filters.
*
* @return bool
*/
private function can_seo_filters() {
$screen = get_current_screen();
if ( is_null( $screen ) || ! in_array( $screen->post_type, Helper::get_allowed_post_types(), true ) ) {
return false;
}
return true;
}
/**
* Set SEO filters meta query.
*
* @param array $query Meta query.
*/
private function set_seo_filters( &$query ) {
$filter = Param::get( 'seo-filter' );
if ( false === $filter ) {
return;
}
$hash = [
'empty-fk' => [
'key' => 'rank_math_focus_keyword',
'compare' => 'NOT EXISTS',
],
'bad-seo' => [
'key' => 'rank_math_seo_score',
'value' => 50,
'compare' => '<=',
'type' => 'numeric',
],
'good-seo' => [
'key' => 'rank_math_seo_score',
'value' => [ 51, 80 ],
'compare' => 'BETWEEN',
'type' => 'numeric',
],
'great-seo' => [
'key' => 'rank_math_seo_score',
'value' => 80,
'compare' => '>',
'type' => 'numeric',
],
'noindexed' => [
'key' => 'rank_math_robots',
'value' => 'noindex',
'compare' => 'LIKE',
],
];
// Extra conditions for "SEO Score" filters.
$seo_score_filters = [ 'bad-seo', 'good-seo', 'great-seo' ];
if ( in_array( $filter, $seo_score_filters, true ) ) {
$query['relation'] = 'AND';
$query[] = [
'relation' => 'OR',
[
'key' => 'rank_math_robots',
'value' => 'noindex',
'compare' => 'NOT LIKE',
],
[
'key' => 'rank_math_robots',
'compare' => 'NOT EXISTS',
],
];
$query[] = [
'key' => 'rank_math_focus_keyword',
'compare' => 'EXISTS',
];
$query[] = [
'key' => 'rank_math_focus_keyword',
'value' => '',
'compare' => '!=',
];
}
if ( isset( $hash[ $filter ] ) ) {
$query[] = $hash[ $filter ];
}
}
/**
* Can apply Focus Keyword filter.
*
* @return bool
*/
private function can_fk_filter() {
$screen = get_current_screen();
if (
is_null( $screen ) ||
'edit' !== $screen->base ||
(
! isset( $_GET['focus_keyword'] ) &&
! isset( $_GET['fk_in_title'] ) &&
! isset( $_GET['review_posts'] )
)
) {
return false;
}
return true;
}
/**
* Check if Focus Keyword appears in the title.
*
* @return bool|array
*/
private function has_fk_in_title() {
global $wpdb;
if ( ! Param::get( 'fk_in_title' ) ) {
return false;
}
$screen = get_current_screen();
$meta_query = new WP_Meta_Query(
[
[
'key' => 'rank_math_focus_keyword',
'compare' => 'EXISTS',
],
[
'relation' => 'OR',
[
'key' => 'rank_math_robots',
'value' => 'noindex',
'compare' => 'NOT LIKE',
],
[
'key' => 'rank_math_robots',
'compare' => 'NOT EXISTS',
],
],
]
);
$meta_query = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
return DB_Helper::get_col( "SELECT {$wpdb->posts}.ID FROM $wpdb->posts {$meta_query['join']} WHERE 1=1 {$meta_query['where']} AND {$wpdb->posts}.post_type = '$screen->post_type' AND ({$wpdb->posts}.post_status = 'publish') AND REPLACE({$wpdb->posts}.post_title, '&amp;', '&') NOT LIKE CONCAT( '%', SUBSTRING_INDEX( {$wpdb->postmeta}.meta_value, ',', 1 ), '%' )" );
}
/**
* Check if any posts had Review schema.
*
* @return bool|array
*/
private function posts_had_reviews() {
global $wpdb;
$review_posts = Param::get( 'review_posts' );
if ( ! $review_posts ) {
return false;
}
return ! get_option( 'rank_math_review_posts_converted', false );
}
}

View File

@@ -0,0 +1,310 @@
<?php
/**
* Inform the user about Rank Math PRO after 20 days of usage.
*
* @since 1.0.69
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\KB;
use RankMath\Helper;
use RankMath\Traits\Ajax;
use RankMath\Traits\Hooker;
defined( 'ABSPATH' ) || exit;
/**
* Pro_Notice class.
*/
class Pro_Notice {
use Hooker;
use Ajax;
/**
* Now.
*
* @var string
*/
public $current_time = '';
/**
* Rank Math plugin install date.
*
* @var string
*/
public $install_date = '';
/**
* Date of release of version 1.0.69. Turned into a timestamp in the constructor.
*
* @var string
*/
public $record_date = '2021-07-30 13:00';
/**
* Constructor method.
*/
public function __construct() {
$this->current_time = Helper::get_current_time();
$this->record_date = strtotime( $this->record_date );
$this->install_date = get_option( 'rank_math_install_date' );
if ( false === $this->install_date ) {
$this->install_date = $this->current_time;
}
}
/**
* Register hooks.
*/
public function hooks() {
$this->ajax( 'dismiss_pro_notice', 'dismiss' );
// Admin notice.
$notice_date = $this->get_notice_date();
if ( $this->current_time > $notice_date ) {
if ( get_option( 'rank_math_pro_notice_added' ) === false && ! Helper::has_notification( 'rank_math_review_plugin_notice' ) ) {
$this->add_notice( (int) get_option( 'rank_math_pro_notice_delayed' ) );
}
// Make dismiss button work like the "Maybe later" link.
$this->action( 'wp_helpers_notification_dismissed', 'pro_notice_after_dismiss' );
$this->action( 'admin_footer', 'pro_notice_js', 15 );
}
}
/**
* Add inline JS & CSS related to the Pro notice.
*
* @return void
*/
public function pro_notice_js() {
if ( ! Helper::has_notification( 'rank_math_pro_notice' ) ) {
return;
}
?>
<script>
(function( $ ) {
$( function() {
$('.rank-math-dismiss-pro-notice').on( 'click', function(e) {
var $this = $(this);
if ( ! $this.hasClass( 'rank-math-upgrade-action' ) ) {
e.preventDefault();
}
if ( $this.hasClass( 'rank-math-maybe-later-action' ) ) {
$('#rank_math_pro_notice').find( '.notice-dismiss' ).trigger('click');
return false;
}
jQuery.ajax( {
url: rankMath.ajaxurl,
data: { action: 'rank_math_already_upgraded', security: rankMath.security,
},
} );
$('#rank_math_pro_notice').find( '.notice-dismiss' ).trigger('click');
});
});
})(jQuery);
</script>
<style>
#rank_math_pro_notice.is-dismissible {
background: #253142;
color: #e4e5e7;
border-width: 3px;
border-style: solid;
border-color: #161e28;
padding: 0.25rem 1rem 1rem;
border-radius: 5px;
}
#rank_math_pro_notice.is-dismissible p {
font-size: 1.25rem;
color: #f7d070;
margin-bottom: 0;
}
#rank_math_pro_notice.is-dismissible ul {
line-height: 1;
margin-bottom: 0;
text-align: left;
opacity: 0.8;
font-size: 15px;
max-width: 530px;
}
#rank_math_pro_notice.is-dismissible li {
display: inline-block;
width: 49%;
margin-bottom: 0.5rem;
}
#rank_math_pro_notice ul li:before {
font-family: dashicons;
font-size: 20px;
width: 20px;
height: 20px;
margin-right: 5px;
content: '\f147';
text-align: center;
vertical-align: middle;
color: #161e28;
border-radius: 10px;
background: #9ce2b6;
}
#rank_math_pro_notice .button {
border-color: #f7d070;
background: #f7d070;
color: #5a4000;
font-size: 15px;
margin-right: 12px;
}
div#rank_math_pro_notice .rank-math-maybe-later-action,
div#rank_math_pro_notice .rank-math-already-upgraded-action {
color: #f7d070;
opacity: 0.7;
margin: 0 12px;
font-size: 13px;
}
.toplevel_page_rank-math #rank_math_pro_notice,
body[class*="rank-math_page_rank-math-options-"] div#rank_math_pro_notice {
display: none;
}
</style>
<?php
}
/**
* Add admin notice.
*
* @param int $variant Notice variant.
* @return void
*/
public function add_notice( $variant = 0 ) {
$message = $this->get_notice_text( $variant );
Helper::add_notification(
$message,
[
'type' => 'info',
'id' => 'rank_math_pro_notice',
'capability' => 'install_plugins',
]
);
update_option( 'rank_math_pro_notice_added', '1', false );
}
/**
* Get notice texts.
*
* @param integer $variant Message variant.
* @return string
*/
public function get_notice_text( $variant = 0 ) {
$message = '';
switch ( (int) $variant ) {
case 1:
$message = '<p><strong>';
$message .= esc_html__( 'Rank Your Content With the Power of PRO & A.I.', 'rank-math' );
$message .= '</strong></p>';
$message .= '<ul>
<li>' . esc_html__( 'Unlimited Websites', 'rank-math' ) . '</li>
<li>' . esc_html__( 'Content A.I. (Artificial Intelligence)', 'rank-math' ) . '</li>
<li>' . esc_html__( 'Keyword Rank Tracker', 'rank-math' ) . '</li>
<li>' . esc_html__( 'Powerful Schema Generator', 'rank-math' ) . '</li>
<li>' . esc_html__( '24x7 Premium Support', 'rank-math' ) . '</li>
<li>' . esc_html__( 'SEO Email Reports', 'rank-math' ) . '</li>
<li>' . esc_html__( 'and Many More…', 'rank-math' ) . '</li>
</ul>';
$message .= '<p>
<a href="' . KB::get( 'pro', 'Upgrade Notice 2 New Yes' ) . '" class="button rank-math-dismiss-pro-notice rank-math-upgrade-action" target="_blank" rel="noopener noreferrer"><strong>' . esc_html__( 'Yes, I want to learn more', 'rank-math' ) . '</strong></a><a href="#" class="rank-math-dismiss-pro-notice rank-math-already-upgraded-action">' . esc_html__( 'No, I don\'t want it', 'rank-math' ) . '</a><a href="#" class="rank-math-dismiss-pro-notice rank-math-already-upgraded-action">' . esc_html__( 'I already upgraded', 'rank-math' ) . '</a>
</p>';
break;
default:
$message = '<p><strong>';
$message .= esc_html__( 'Rank Your Content With the Power of PRO & A.I.', 'rank-math' );
$message .= '</strong></p><p>';
$message .= '<ul>
<li>' . esc_html__( 'Unlimited Websites', 'rank-math' ) . '</li>
<li>' . esc_html__( 'Content A.I. (Artificial Intelligence)', 'rank-math' ) . '</li>
<li>' . esc_html__( 'Keyword Rank Tracker', 'rank-math' ) . '</li>
<li>' . esc_html__( 'Powerful Schema Generator', 'rank-math' ) . '</li>
<li>' . esc_html__( '24x7 Premium Support', 'rank-math' ) . '</li>
<li>' . esc_html__( 'SEO Email Reports', 'rank-math' ) . '</li>
<li>' . esc_html__( 'and Many More…', 'rank-math' ) . '</li>
</ul>';
$message .= '<p>
<a href="' . KB::get( 'pro', 'Upgrade Notice 1 New Yes' ) . '" class="button rank-math-dismiss-pro-notice rank-math-upgrade-action" target="_blank" rel="noopener noreferrer"><strong>' . esc_html__( 'Yes, I want better SEO', 'rank-math' ) . '</strong></a><a href="#" class="rank-math-dismiss-pro-notice rank-math-maybe-later-action">' . esc_html__( 'No, maybe later', 'rank-math' ) . '</a><a href="#" class="rank-math-dismiss-pro-notice rank-math-already-upgraded-action">' . esc_html__( 'I already purchased', 'rank-math' ) . '</a>
</p>';
break;
}
return $message;
}
/**
* Set "delayed" flag after the user dismisses the notice.
*
* @param string $notification_id Dismissed notice ID.
* @return void
*/
public function pro_notice_after_dismiss( $notification_id ) {
if ( 'rank_math_pro_notice' !== $notification_id ) {
return;
}
// If it has already been delayed once then dismiss it forever.
if ( get_option( 'rank_math_pro_notice_delayed' ) ) {
update_option( 'rank_math_already_upgraded', Helper::get_current_time() );
return;
}
delete_option( 'rank_math_pro_notice_date' );
delete_option( 'rank_math_pro_notice_added' );
update_option( 'rank_math_pro_notice_delayed', 1, false );
}
/**
* Get stored notice start date.
*
* @return int
*/
public function get_notice_date() {
$notice_date = get_option( 'rank_math_pro_notice_date' );
if ( false !== $notice_date ) {
return $notice_date;
}
$delay_days = 10;
if ( $this->install_date < $this->record_date && ! get_option( 'rank_math_pro_notice_delayed' ) ) {
$delay_days = wp_rand( 7, 30 );
}
$notice_date = $this->current_time + ( $delay_days * DAY_IN_SECONDS );
update_option( 'rank_math_pro_notice_date', $notice_date, false );
return $notice_date;
}
/**
* Set the "already upgraded" flag.
* This also sets the "already reviewed" flag, so the review notice will not show up anymore either.
*/
public function dismiss() {
check_ajax_referer( 'rank-math-ajax-nonce', 'security' );
$this->has_cap_ajax( 'onpage_general' );
update_option( 'rank_math_already_upgraded', Helper::get_current_time() );
update_option( 'rank_math_already_reviewed', Helper::get_current_time() );
$this->success( 'success' );
}
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* The option page functionality of the plugin.
*
* @since 1.0.250
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Helper;
defined( 'ABSPATH' ) || exit;
/**
* Register_Options_Page class.
*/
class Register_Options_Page {
/**
* The Constructor
*
* @param array $config Array of configuration.
*/
public function __construct( $config ) {
if ( ! Helper::is_react_enabled() ) {
new CMB2_Options( $config );
return;
}
$options_page = new Options( $config );
$options_page->register_option_page();
}
}

View File

@@ -0,0 +1,323 @@
<?php
/**
* The Setup Wizard - configure the SEO settings in just a few steps.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Helper;
use RankMath\Traits\Hooker;
use RankMath\Admin\Admin_Helper;
use RankMath\Helpers\Param;
use RankMath\Helpers\Security;
use RankMath\Google\Authentication;
defined( 'ABSPATH' ) || exit;
/**
* Registration class.
*/
class Registration {
use Hooker;
/**
* Page slug.
*
* @var string
*/
private $slug = 'rank-math-registration';
/**
* Hold current step.
*
* @var string
*/
protected $step = '';
/**
* Current step slug.
*
* @var string
*/
protected $step_slug = '';
/**
* Is registration invalid.
*
* @var bool
*/
public $invalid = false;
/**
* The Constructor.
*/
public function __construct() {
$this->step = 'register';
$this->step_slug = 'register';
$this->invalid = Helper::is_invalid_registration();
if ( $this->invalid ) {
$this->action( 'admin_menu', 'admin_menu' );
$this->action( 'admin_init', 'redirect_to_welcome' );
$this->action( 'admin_post_rank_math_save_registration', 'save_registration' );
$this->action( 'admin_post_rank_math_skip_wizard', 'skip_wizard' );
$this->action( 'admin_init', 'render_page', 30 );
}
$this->action( 'admin_init', 'handle_registration' );
$this->filter( 'allowed_redirect_hosts', 'allowed_redirect_hosts' );
}
/**
* Add allowed redirect hosts.
*
* @param array $hosts Allowed hosts.
* @return array
*/
public function allowed_redirect_hosts( $hosts ) {
$hosts[] = 'rankmath.com';
return $hosts;
}
/**
* Check for activation.
*/
public function handle_registration() {
// Bail if already connected.
if ( Helper::is_site_connected() ) {
return;
}
if ( ! Helper::has_cap( 'general' ) ) {
return;
}
$nonce = Param::get( 'nonce' );
if ( ! $nonce || ! wp_verify_nonce( $nonce, 'rank_math_register_product' ) ) {
return;
}
$status = Param::get( 'rankmath_connect' );
if ( $status && $redirect_to = $this->get_registration_url( $status ) ) { //phpcs:ignore
Helper::redirect( $redirect_to );
exit;
}
}
/**
* Handle activation.
*
* @param string $status Status parameter.
*/
private function get_registration_url( $status ) {
if ( 'cancel' === $status ) {
// User canceled activation.
Helper::add_notification( __( 'Rank Math plugin could not be connected.', 'rank-math' ), [ 'type' => 'error' ] );
return Security::remove_query_arg_raw( [ 'rankmath_connect', 'rankmath_auth' ] );
}
if ( 'banned' === $status ) {
// User or site banned.
Helper::add_notification( __( 'Unable to connect Rank Math.', 'rank-math' ), [ 'type' => 'error' ] );
return Security::remove_query_arg_raw( [ 'rankmath_connect', 'rankmath_auth' ] );
}
if ( 'ok' === $status && $auth_data = $this->get_registration_params() ) { // phpcs:ignore
Admin_Helper::get_registration_data(
[
'username' => $auth_data['username'],
'email' => $auth_data['email'],
'api_key' => $auth_data['api_key'],
'plan' => $auth_data['plan'],
'connected' => true,
'site_url' => Helper::get_home_url(),
]
);
if ( 1 === absint( Param::get( 'analytics' ) ) ) {
wp_redirect( Authentication::get_auth_url() ); //phpcs:ignore -- This is used to redirect to the external url.
exit;
}
// Redirect to the wizard is registration successful.
if ( Param::get( 'page' ) === 'rank-math-registration' ) {
return Helper::get_admin_url( 'wizard' );
}
return Security::remove_query_arg_raw( [ 'rankmath_connect', 'rankmath_auth', 'nonce' ] );
}
return false;
}
/**
* Check if 'rankmath_auth' contains all the data we need, in the
* correct format.
*
* @return bool|array Whether the input is valid.
*/
private function get_registration_params() {
$params = Param::get( 'rankmath_auth' );
if ( false === $params ) {
return false;
}
$params = json_decode( base64_decode( $params ), true ); // phpcs:ignore -- Verified as safe usage.
if (
! is_array( $params ) ||
! isset( $params['username'] ) ||
! isset( $params['email'] ) ||
! isset( $params['api_key'] )
) {
return false;
}
return $params;
}
/**
* Redirect to welcome page.
*
* Redirect the user to the welcome page after plugin activation.
*/
public function redirect_to_welcome() {
if ( ! $this->can_redirect() ) {
return;
}
$url = '';
if ( $this->invalid ) {
$url = 'registration';
} elseif ( ! get_option( 'rank_math_wizard_completed' ) ) {
$url = 'wizard';
}
Helper::redirect( Helper::get_admin_url( $url ) );
exit;
}
/**
* Add menu items.
*/
public function admin_menu() {
add_menu_page(
esc_html__( 'Rank Math', 'rank-math' ),
esc_html__( 'Rank Math SEO', 'rank-math' ),
'manage_options',
$this->slug,
[ $this, 'render_page' ]
);
}
/**
* Output the admin page.
*/
public function render_page() {
// Early bail if we're not on the right page.
if ( Param::get( 'page' ) !== $this->slug ) {
return;
}
if ( ob_get_length() ) {
ob_end_clean();
}
$assets = new Assets();
$assets->register();
wp_styles()->done = [];
wp_scripts()->done = [];
// Wizard.
wp_enqueue_media();
wp_enqueue_style( 'rank-math-wizard', rank_math()->plugin_url() . 'assets/admin/css/setup-wizard.css', [ 'wp-admin', 'buttons', 'wp-components', 'rank-math-common', 'rank-math-cmb2' ], rank_math()->version );
wp_enqueue_script( 'rank-math-registration', rank_math()->plugin_url() . 'assets/admin/js/registration.js', [ 'lodash', 'react', 'react-dom', 'wp-element', 'wp-data', 'rank-math-components' ], rank_math()->version, true );
Helper::add_json( 'logo', esc_url( rank_math()->plugin_url() . 'assets/admin/img/logo.svg' ) );
Helper::add_json( 'registerNonce', wp_create_nonce( 'rank-math-wizard' ) );
Helper::add_json( 'adminUrl', esc_url( admin_url( 'admin-post.php' ) ) );
Helper::add_json( 'isSiteUrlValid', Admin_Helper::is_site_url_valid() );
Helper::add_json( 'optionsPage', esc_url( admin_url( 'options-general.php' ) ) );
ob_start();
/**
* Start the actual page content.
*/
include_once rank_math()->admin_dir() . 'wizard/views/content.php';
exit;
}
/**
* Execute save handler for current step.
*/
public function save_registration() {
// If no form submission, bail.
$referer = Param::post( '_wp_http_referer', get_dashboard_url() );
if ( Param::post( 'step' ) !== 'register' ) {
return Helper::redirect( $referer );
}
check_admin_referer( 'rank-math-wizard', 'security' );
if ( ! Helper::has_cap( 'general' ) ) {
return Helper::redirect( $referer );
}
$this->redirect_to_connect( $_POST );
}
/**
* Skip wizard handler.
*/
public function skip_wizard() {
check_admin_referer( 'rank-math-wizard', 'security' );
if ( ! Helper::has_cap( 'general' ) ) {
exit;
}
add_option( 'rank_math_registration_skip', true );
Helper::redirect( Helper::get_admin_url( 'wizard' ) );
exit;
}
/**
* Authenticate registration.
*
* @param array $values Array of values for the step to process.
*/
private function redirect_to_connect( $values ) {
if ( ! isset( $values['rank_math_activate'] ) ) {
Admin_Helper::deregister_user();
return;
}
$url = Admin_Helper::get_activate_url( Helper::get_admin_url( 'registration' ) );
wp_safe_redirect( $url );
die();
}
/**
* Can redirect to setup/registration page after install.
*
* @return bool
*/
private function can_redirect() {
if ( ! get_transient( '_rank_math_activation_redirect' ) ) {
return false;
}
delete_transient( '_rank_math_activation_redirect' );
if ( ( ! empty( $_GET['page'] ) && in_array( $_GET['page'], [ 'rank-math-registration', 'rank-math-wizard' ], true ) ) || ! current_user_can( 'manage_options' ) ) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,362 @@
<?php
/**
* The option center of the plugin.
*
* @since 1.0.250
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Helper;
use RankMath\Helpers\Str;
/**
* Settings Sanitizer for React-based settings.
*/
class Sanitize_Settings {
/**
* Sanitize all settings data using field types.
*
* @param array $settings_data Array of setting data [field_id => value].
* @param array $field_types Array of field types [field_id => type].
*
* @return array Sanitized settings data.
*/
public static function sanitize( $settings_data, $field_types ) {
$sanitized = [];
foreach ( $settings_data as $field_id => $value ) {
$type = $field_types[ $field_id ] ?? 'text';
$sanitized[ $field_id ] = self::sanitize_field( $value, $type, $field_id );
}
return $sanitized;
}
/**
* Sanitize an individual field based on its type.
*
* @param mixed $value Field value.
* @param string $type Field type.
* @param string $field_id Field ID.
*
* @return mixed Sanitized value.
*/
public static function sanitize_field( $value, $type, $field_id ) {
// First: Check field ID-specific logic.
$field_specific = apply_filters( 'rank_math/settings/sanitize_fields', self::sanitize_by_field_id( $value, $field_id ), $value, $field_id );
if ( $field_specific !== null ) {
return $field_specific;
}
switch ( $type ) {
case 'text':
return is_array( $value ) ? array_map( [ __CLASS__, 'sanitize_textfield' ], $value ) : self::sanitize_textfield( $value );
case 'textarea':
return is_array( $value ) ? array_map( 'wp_kses_post', $value ) : wp_kses_post( $value );
case 'toggle':
return $value ? 'on' : 'off';
case 'checkbox':
case 'checkboxlist':
return is_array( $value ) ? array_map( 'sanitize_text_field', $value ) : [];
case 'select':
case 'selectSearch':
case 'selectVariable':
case 'searchPage':
case 'toggleGroup':
return is_array( $value ) ? array_map( [ __CLASS__, 'sanitize_textfield' ], $value ) : self::sanitize_textfield( $value );
case 'number':
return is_array( $value ) ? array_map( 'intval', $value ) : intval( $value );
case 'file':
return esc_url_raw( $value );
case 'group':
case 'repeatableGroup':
return self::sanitize_group_value( $value );
default:
// Fallback.
return map_deep( $value, [ __CLASS__, 'sanitize_default_value' ] );
}
}
/**
* Handles sanitization for default fields. Make sure to not change the boolean to blank text.
*
* @param string $value The unsanitized value from the form.
*
* @return string Sanitized value to be stored.
*/
public static function sanitize_default_value( $value ) {
return is_string( $value ) ? sanitize_text_field( $value ) : $value;
}
/**
* Sanitize an individual field based on its id.
*
* @param mixed $value Field value.
* @param string $field_id Field ID.
*
* @return mixed Sanitized value.
*/
private static function sanitize_by_field_id( $value, $field_id ) {
switch ( $field_id ) {
case 'robots_txt_content':
return self::sanitize_robots_text( $value );
case 'google_verify':
case 'bing_verify':
case 'baidu_verify':
case 'yandex_verify':
case 'pinterest_verify':
case 'norton_verify':
return self::sanitize_webmaster_tags( $value );
case 'custom_webmaster_tags':
return self::sanitize_custom_webmaster_tags( $value );
case 'console_caching_control':
return self::sanitize_cache_control( $value );
}
// Returning null means no special handling; fall back to type-based logic.
return null;
}
/**
* Handles sanitization for text fields.
*
* @param string $value The unsanitized value from the form.
*
* @return string Sanitized value to be stored.
*/
private static function sanitize_textfield( $value ) {
if ( is_object( $value ) || is_array( $value ) ) {
return '';
}
$value = (string) $value;
$filtered = wp_check_invalid_utf8( $value );
if ( strpos( $filtered, '<' ) !== false ) {
$filtered = wp_pre_kses_less_than( $filtered );
// Strip extra whitespace.
$filtered = wp_strip_all_tags( $filtered, false );
// Use html entities in a special case to make sure no later
// newline stripping stage could lead to a functional tag!
$filtered = str_replace( "<\n", "&lt;\n", $filtered );
}
$filtered = preg_replace( '/[\r\n\t ]+/', ' ', $filtered );
$filtered = trim( $filtered );
$found = false;
while ( preg_match( '/%[0-9]{2}/i', $filtered, $match ) ) {
$filtered = str_replace( $match[0], '', $filtered );
$found = true;
}
if ( $found ) {
// Strip out the whitespace that may now exist after removing the octets.
$filtered = trim( preg_replace( '/ +/', ' ', $filtered ) );
}
return apply_filters( 'sanitize_text_field', $filtered, $value );
}
/**
* Handles sanitization of Robots text.
*
* @since 1.0.45
*
* @param mixed $value The unsanitized Robots text.
*
* @return string Sanitized Robots text to be stored.
*/
private static function sanitize_robots_text( $value ) {
if ( empty( $value ) ) {
return '';
}
return wp_strip_all_tags( $value );
}
/**
* Handles sanitization for webmaster tag and remove <meta> tag.
*
* @param mixed $value The unsanitized value from the form.
*
* @return mixed Sanitized value to be stored.
*/
private static function sanitize_webmaster_tags( $value ) {
$value = trim( $value );
if ( ! empty( $value ) && Str::starts_with( '<meta', trim( $value ) ) ) {
preg_match( '/content="([^"]+)"/i', stripslashes( $value ), $matches );
$value = $matches[1];
}
return htmlentities( wp_strip_all_tags( $value ) );
}
/**
* Handles sanitization for custom webmaster tags.
* Only <meta> tags are allowed.
*
* @param mixed $value The unsanitized value from the form.
*/
private static function sanitize_custom_webmaster_tags( $value ) {
$sanitized = wp_kses(
$value,
[
'meta' => [
'name' => [],
'content' => [],
],
]
);
return $sanitized;
}
/**
* Handles sanitization for Analytics cache control option.
*
* @param mixed $value The unsanitized value from the form.
*/
private static function sanitize_cache_control( $value ) {
$max = apply_filters( 'rank_math/analytics/max_days_allowed', 90 );
$value = absint( $value );
if ( $value > $max ) {
$value = $max;
}
return $value;
}
/**
* Do not save if name or image is empty.
*
* @param array $value Field value to save.
* @return array
*/
private function sanitize_overlays( $value ) {
if ( ! is_array( $value ) ) {
return [];
}
foreach ( $value as $key => $overlay ) {
if ( empty( $overlay['image'] ) ) {
unset( $value[ $key ] );
} elseif ( empty( $overlay['name'] ) ) {
Helper::add_notification( esc_html__( 'A Custom Watermark item could not be saved because the name field is empty.', 'rank-math' ), [ 'type' => 'error' ] );
unset( $value[ $key ] );
}
}
return $value;
}
/**
* Handles sanitization of advanced robots data.
*
* @param array $robots The unsanitized value from the form.
*
* @return array Sanitized value to be stored.
*/
private static function sanitize_advanced_robots( $robots ) {
if ( empty( $robots ) ) {
return [];
}
$advanced_robots = [];
foreach ( $robots as $key => $robot ) {
$advanced_robots[ $key ] = ! empty( $robot['enable'] ) ? $robot['length'] : false;
}
return $advanced_robots;
}
/**
* Sanitize a group or repeatable group field.
*
* - For a single group, sanitizes keys and values.
* - For repeatable groups (array of group items), recursively sanitizes each item.
* - Preserves key casing.
*
* @param array $group_value The group or repeatable group value.
* @return array Sanitized group value.
*/
private static function sanitize_group_value( $group_value ) {
if ( ! is_array( $group_value ) ) {
return [];
}
// Check if this is a repeatable group (array of associative arrays).
$is_repeatable = array_keys( $group_value ) === range( 0, count( $group_value ) - 1 );
if ( $is_repeatable ) {
$sanitized = [];
foreach ( $group_value as $item ) {
if ( is_array( $item ) ) {
$sanitized[] = self::sanitize_array_recursive( $item );
}
}
return $sanitized;
}
// Single group.
return self::sanitize_array_recursive( $group_value );
}
/**
* Recursively sanitize an array's keys (preserving casing) and values.
*
* Uses sanitize_text_field() for scalar values. Nested arrays are handled recursively.
*
* @param array $data The array to recursively sanitize.
* @return array The sanitized array.
*/
private static function sanitize_array_recursive( $data ) {
$sanitized = [];
foreach ( $data as $key => $val ) {
$clean_key = self::sanitize_key_preserve_case( $key );
if ( is_array( $val ) ) {
$sanitized[ $clean_key ] = self::sanitize_array_recursive( $val );
} else {
$sanitized[ $clean_key ] = self::sanitize_textfield( $val );
}
}
return $sanitized;
}
/**
* Sanitize a key while preserving original casing.
*
* Removes unsafe characters like spaces, HTML, and control characters,
* but keeps casing and underscores intact.
*
* @param string $key The key to sanitize.
* @return string The sanitized key.
*/
private static function sanitize_key_preserve_case( $key ) {
$key = wp_strip_all_tags( $key );
$key = preg_replace( '/[^A-Za-z0-9_\-]/', '', $key );
return $key;
}
}

View File

@@ -0,0 +1,206 @@
<?php
/**
* The Setup Wizard - configure the SEO settings in just a few steps.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Helper;
use RankMath\Traits\Hooker;
use RankMath\Traits\Wizard;
use RankMath\Admin\Importers\Detector;
use RankMath\Helpers\Param;
defined( 'ABSPATH' ) || exit;
/**
* Setup_Wizard class.
*/
class Setup_Wizard {
use Hooker;
use Wizard;
/**
* Top level admin page.
*
* @var string
*/
protected $slug = 'rank-math-wizard';
/**
* Hook suffix.
*
* @var string
*/
public $hook_suffix = '';
/**
* The Constructor.
*/
public function __construct() {
$this->action( 'admin_menu', 'add_admin_menu' );
// If the page is not this page stop here.
if ( ! $this->is_current_page() ) {
return;
}
$this->action( 'admin_init', 'admin_page', 30 );
$this->filter( 'user_has_cap', 'filter_user_has_cap' );
}
/**
* Add the admin menu item, under Appearance.
*/
public function add_admin_menu() {
if ( Param::get( 'page' ) !== $this->slug ) {
return;
}
$this->hook_suffix = add_submenu_page(
'',
esc_html__( 'Setup Wizard', 'rank-math' ),
esc_html__( 'Setup Wizard', 'rank-math' ),
'manage_options',
$this->slug,
[ $this, 'admin_page' ]
);
}
/**
* Output the admin page.
*/
public function admin_page() {
// Do not proceed if we're not on the right page.
if ( Param::get( 'page' ) !== $this->slug ) {
return;
}
if ( ob_get_length() ) {
ob_end_clean();
}
// Enqueue styles.
rank_math()->admin_assets->register();
wp_enqueue_style( 'rank-math-wizard', rank_math()->plugin_url() . 'assets/admin/css/setup-wizard.css', [ 'wp-admin', 'buttons', 'select2-rm', 'rank-math-common', 'rank-math-cmb2', 'wp-components' ], rank_math()->version );
// Enqueue scripts for the SEO Score Updater tool.
\RankMath\Tools\Update_Score::get()->enqueue();
// Enqueue javascript.
wp_enqueue_media();
wp_enqueue_script( 'rank-math-wizard', rank_math()->plugin_url() . 'assets/admin/js/wizard.js', [ 'media-editor', 'select2-rm', 'lodash', 'rank-math-common', 'rank-math-components' ], rank_math()->version, true );
wp_set_script_translations( 'rank-math-wizard', 'rank-math' );
Helper::add_json( 'logo', esc_url( rank_math()->plugin_url() . 'assets/admin/img/logo.svg' ) );
ob_start();
/**
* Start the actual page content.
*/
include_once $this->get_view( 'content' );
exit;
}
/**
* Get view file to display.
*
* @param string $view View to display.
* @return string
*/
public function get_view( $view ) {
return rank_math()->admin_dir() . "wizard/views/{$view}.php";
}
/**
* Get Localized data for the given step.
*
* @param string $step Current Setup Wizard step.
*/
public static function get_localized_data( $step ) {
$steps = self::get_steps();
if ( ! isset( $steps[ $step ] ) ) {
return '';
}
$data = [
'isWhitelabel' => Helper::is_whitelabel(),
'isConfigured' => Helper::is_configured(),
'setup_mode' => Helper::get_settings( 'general.setup_mode', 'advanced' ),
'addImport' => ! self::maybe_remove_import(),
];
return apply_filters(
"rank_math/setup_wizard/$step/localized_data",
array_merge(
$data,
$steps[ $step ]::get_localized_data()
)
);
}
/**
* Get Localized data for the given step.
*
* @param string $step Current Setup Wizard step.
* @param array $values Values to update.
*/
public static function save_data( $step, $values ) {
$steps = self::get_steps();
if ( ! isset( $steps[ $step ] ) ) {
return '';
}
do_action( "rank_math/setup_wizard/$step/save_data", $values );
return $steps[ $step ]::save( $values );
}
/**
* Get Setup Wizard step class.
*/
private static function get_steps() {
return [
'compatibility' => '\\RankMath\\Wizard\\Compatibility',
'import' => '\\RankMath\\Wizard\\Import',
'yoursite' => '\\RankMath\\Wizard\\Your_Site',
'analytics' => '\\RankMath\\Wizard\\Search_Console',
'sitemaps' => '\\RankMath\\Wizard\\Sitemap',
'optimization' => '\\RankMath\\Wizard\\Optimization',
'ready' => '\\RankMath\\Wizard\\Ready',
'role' => '\\RankMath\\Wizard\\Role',
'redirection' => '\\RankMath\\Wizard\\Monitor_Redirection',
'schema-markup' => '\\RankMath\\Wizard\\Schema_Markup',
];
}
/**
* Maybe remove import step.
*
* @return bool
*/
private static function maybe_remove_import() {
$pre = apply_filters( 'rank_math/wizard/pre_remove_import_step', null );
if ( ! is_null( $pre ) ) {
return $pre;
}
if ( false === get_option( 'rank_math_is_configured' ) ) {
$detector = new Detector();
$plugins = $detector->detect();
if ( ! empty( $plugins ) ) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,129 @@
<?php
/**
* The clauses functions.
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath\Admin\Database
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin\Database;
/**
* Clauses class.
*/
trait Clauses {
/**
* List of SQL clauses.
*
* @var array
*/
private $sql_clauses = [];
/**
* SQL clause merge filters.
*
* @var array
*/
private $sql_filters = [
'where' => [
'where',
'where_time',
],
'join' => [
'right_join',
'join',
'left_join',
],
];
/**
* Check has SQL clause.
*
* @param string $type Clause type.
*
* @return boolean True if set and not empty.
*/
public function has_sql_clause( $type ) {
return isset( $this->sql_clauses[ $type ] ) && ! empty( $this->sql_clauses[ $type ] );
}
/**
* Add a SQL clause to be included when get_data is called.
*
* @param string $type Clause type.
* @param string $clause SQL clause.
*/
public function add_sql_clause( $type, $clause ) {
if ( isset( $this->sql_clauses[ $type ] ) && ! empty( $clause ) ) {
$this->sql_clauses[ $type ][] = $clause;
}
}
/**
* Clear SQL clauses by type.
*
* @param string|array $types Clause type.
*/
protected function clear_sql_clause( $types ) {
foreach ( (array) $types as $type ) {
if ( isset( $this->sql_clauses[ $type ] ) ) {
$this->sql_clauses[ $type ] = [];
}
}
}
/**
* Get SQL clause by type.
*
* @param string $type Clause type.
* @param string $filtered Whether to filter the return value. Default unfiltered.
*
* @return string SQL clause.
*/
protected function get_sql_clause( $type, $filtered = false ) {
if ( ! isset( $this->sql_clauses[ $type ] ) ) {
return '';
}
$separator = ' ';
if ( in_array( $type, [ 'select', 'order_by', 'group_by' ], true ) ) {
$separator = ', ';
}
/**
* Default to bypassing filters for clause retrieval internal to data stores.
* The filters are applied when the full SQL statement is retrieved.
*/
if ( false === $filtered ) {
return implode( $separator, $this->sql_clauses[ $type ] );
}
if ( isset( $this->sql_filters[ $type ] ) ) {
$clauses = [];
foreach ( $this->sql_filters[ $type ] as $subset ) {
$clauses = array_merge( $clauses, $this->sql_clauses[ $subset ] );
}
} else {
$clauses = $this->sql_clauses[ $type ];
}
/**
* Filter SQL clauses by type and context.
*
* @param array $clauses The original arguments for the request.
* @param string $context The data store context.
*/
$clauses = apply_filters( "rank_math_clauses_{$type}", $clauses, $this->context );
/**
* Filter SQL clauses by type and context.
*
* @param array $clauses The original arguments for the request.
*/
$clauses = apply_filters( "rank_math_clauses_{$type}_{$this->context}", $clauses );
return implode( $separator, $clauses );
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* The Database.
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath\Admin\Database
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin\Database;
/**
* Database class.
*/
class Database {
/**
* Array of all databases objects.
*
* @var array
*/
protected static $instances = [];
/**
* Retrieve a Database instance by table name.
*
* @param string $table_name A Database instance id.
*
* @return Database Database object instance.
*/
public static function table( $table_name ) {
global $wpdb;
if ( empty( self::$instances ) || empty( self::$instances[ $table_name ] ) ) {
self::$instances[ $table_name ] = new Query_Builder( $wpdb->prefix . $table_name );
}
return self::$instances[ $table_name ];
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* The escape functions.
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath\Admin\Database
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin\Database;
/**
* Escape class.
*/
trait Escape {
/**
* Escape array values for sql
*
* @param array $arr Array to escape.
*
* @return array
*/
public function esc_array( $arr ) {
return array_map( [ $this, 'esc_value' ], $arr );
}
/**
* Escape value for sql
*
* @param mixed $value Value to escape.
*
* @return mixed
*/
public function esc_value( $value ) {
global $wpdb;
if ( is_int( $value ) ) {
return $wpdb->prepare( '%d', $value );
}
if ( is_float( $value ) ) {
return $wpdb->prepare( '%f', $value );
}
return 'NULL' === $value ? $value : $wpdb->prepare( '%s', $value );
}
/**
* Escape value for like statement
*
* @codeCoverageIgnore
*
* @param string $value Value for like statement.
* @param string $start (Optional) The start of like query.
* @param string $end (Optional) The end of like query.
*
* @return string
*/
public function esc_like( $value, $start = '%', $end = '%' ) {
global $wpdb;
return $start . $wpdb->esc_like( $value ) . $end;
}
}

View File

@@ -0,0 +1,53 @@
<?php
/**
* The groupby functions.
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath\Admin\Database
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin\Database;
/**
* GroupBy class.
*/
trait GroupBy {
/**
* Add an group by statement to the current query
*
* ->groupBy('created_at')
*
* @param array|string $columns Columns.
*
* @return self The current query builder.
*/
public function groupBy( $columns ) { // @codingStandardsIgnoreLine
if ( is_string( $columns ) ) {
$columns = $this->argument_to_array( $columns );
}
foreach ( $columns as $column ) {
$this->add_sql_clause( 'group_by', $column );
}
return $this;
}
/**
* Generate Having clause
*
* @param string $column The SQL column.
* @param mixed $param1 Operator or value depending if $param2 isset.
* @param mixed $param2 The value if $param1 is an operator.
*
* @return self The current query builder.
*/
public function having( $column, $param1 = null, $param2 = null ) {
$this->add_sql_clause( 'having', $this->generateWhere( $column, $param1, $param2, 'HAVING' ) );
return $this;
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* The joins functions.
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath\Admin\Database
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin\Database;
/**
* Joins class.
*/
trait Joins {
/**
* Generate left join clause.
*
* @param string $table The SQL table.
* @param mixed $column1 The SQL Column.
* @param mixed $column2 The SQL Column.
* @param string $operator The Operator.
* @param string $alias The table alias.
*
* @return self The current query builder.
*/
public function leftJoin( $table, $column1, $column2, $operator = '=', $alias = '' ) { // @codingStandardsIgnoreLine
if ( empty( $table ) || empty( $column1 ) || empty( $column2 ) ) {
return $this;
}
if ( ! empty( $alias ) ) {
$table = "{$table} AS {$alias}";
}
$this->add_sql_clause( 'left_join', "LEFT JOIN {$table} ON {$column1} {$operator} {$column2}" );
return $this;
}
/**
* Generate right join clause.
*
* @param string $table The SQL table.
* @param mixed $column1 The SQL Column.
* @param mixed $column2 The SQL Column.
* @param string $operator The Operator.
* @param string $alias The table alias.
*
* @return self The current query builder.
*/
public function rightJoin( $table, $column1, $column2, $operator = '=', $alias = '' ) { // @codingStandardsIgnoreLine
if ( empty( $table ) || empty( $column1 ) || empty( $column2 ) ) {
return $this;
}
if ( ! empty( $alias ) ) {
$table = "{$table} AS {$alias}";
}
$this->add_sql_clause( 'right_join', "RIGHT JOIN {$table} ON {$column1} {$operator} {$column2}" );
return $this;
}
/**
* Generate left join clause.
*
* @param string $table The SQL table.
* @param mixed $column1 The SQL Column.
* @param mixed $column2 The SQL Column.
* @param string $operator The Operator.
* @param string $alias The table alias.
*
* @return self The current query builder.
*/
public function join( $table, $column1, $column2, $operator = '=', $alias = '' ) { // @codingStandardsIgnoreLine
if ( empty( $table ) || empty( $column1 ) || empty( $column2 ) ) {
return $this;
}
if ( ! empty( $alias ) ) {
$table = "{$table} AS {$alias}";
}
$this->add_sql_clause( 'join', "JOIN {$table} ON {$column1} {$operator} {$column2}" );
return $this;
}
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* The orderby functions.
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath\Admin\Database
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin\Database;
use RankMath\Helpers\Arr;
/**
* OrderBy class.
*/
trait OrderBy {
/**
* Add an order by statement to the current query
*
* ->orderBy('created_at')
* ->orderBy('modified_at', 'desc')
*
* // multiple order clauses
* ->orderBy(['firstname', 'lastname'], 'desc')
*
* // muliple order clauses with diffrent directions
* ->orderBy(['firstname' => 'asc', 'lastname' => 'desc'])
*
* @param array|string $columns Columns.
* @param string $direction Direction.
*
* @return self The current query builder.
*/
public function orderBy( $columns, $direction = 'ASC' ) { // @codingStandardsIgnoreLine
if ( is_string( $columns ) ) {
$columns = $this->argument_to_array( $columns );
}
$direction = $this->sanitize_direction( $direction );
foreach ( $columns as $key => $column ) {
if ( is_numeric( $key ) ) {
$this->add_sql_clause( 'order_by', "{$column}{$direction}" );
continue;
}
$column = $this->sanitize_direction( $column );
$this->add_sql_clause( 'order_by', "{$key}{$column}" );
}
return $this;
}
/**
* Sanitize direction
*
* @param string $direction Value to sanitize.
*
* @return string Sanitized value
*/
protected function sanitize_direction( $direction ) {
if ( empty( $direction ) || 'ASC' === $direction || 'asc' === $direction ) {
return '';
}
return ' ' . \strtoupper( $direction );
}
/**
* Returns an string argument as parsed array if possible
*
* @param string $argument Argument to validate.
*
* @return array
*/
protected function argument_to_array( $argument ) {
if ( false !== strpos( $argument, ',' ) ) {
return Arr::from_string( $argument );
}
return [ $argument ];
}
}

View File

@@ -0,0 +1,379 @@
<?php
/**
* The Query Builder.
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath\Admin\Database
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin\Database;
/**
* Query_Builder class.
*/
class Query_Builder {
use Escape;
use Select;
use Where;
use Joins;
use GroupBy;
use OrderBy;
use Clauses;
use Translate;
/**
* Table name.
*
* @var string
*/
public $table = '';
/**
* Save last query.
*
* @var string
*/
public $last_query = '';
/**
* Make a distinct selection
*
* @var bool
*/
protected $distinct = false;
/**
* Make SQL_CALC_FOUND_ROWS in selection
*
* @var bool
*/
protected $found_rows = false;
/**
* Data store context used to pass to filters.
*
* @var string
*/
protected $context;
/**
* Constructor
*
* @param string $table The table name.
* @param string $context Optional context passed to filters. Default empty string.
*/
public function __construct( $table = '', $context = '' ) {
$this->table = $table;
$this->context = $context;
$this->reset();
}
/**
* Translate the given query object and return the results
*
* @param string $output (Optional) Any of ARRAY_A | ARRAY_N | OBJECT | OBJECT_K constants.
*
* @return mixed
*/
public function get( $output = \OBJECT ) {
return $this->get_results( $this->translateSelect(), $output );
}
/**
* Translate the given query object and return the results
*
* @param string $output (Optional) Any of ARRAY_A | ARRAY_N | OBJECT | OBJECT_K constants.
*
* @return mixed
*/
public function one( $output = \OBJECT ) {
return $this->get_row( $this->translateSelect(), $output );
}
/**
* Translate the given query object and return one variable from the database
*
* @return mixed
*/
public function getVar() { // @codingStandardsIgnoreLine
$row = $this->one( \ARRAY_A );
return is_null( $row ) ? false : current( $row );
}
/**
* Insert a row into a table
*
* @codeCoverageIgnore
* @see wpdb::insert()
*
* @param array $data Data to insert (in column => value pairs). Both $data columns and $data values should be "raw" (neither should be SQL escaped).
* @param array $format (Optional) An array of formats to be mapped to each of the value in $data.
*
* @return mixed
*/
public function insert( $data, $format = null ) {
global $wpdb;
$wpdb->insert( $this->table, $data, $format );
return $wpdb->insert_id;
}
/**
* Update a row into a table
*
* @codeCoverageIgnore
*
* @return mixed
*/
public function update() {
$query = $this->translateUpdate();
$this->reset();
return $this->query( $query );
}
/**
* Delete data from table
*
* @codeCoverageIgnore
*
* @return mixed
*/
public function delete() {
$query = $this->translateDelete();
$this->reset();
return $this->query( $query );
}
/**
* Truncate table.
*
* @codeCoverageIgnore
*
* @return mixed
*/
public function truncate() {
return $this->query( "truncate table {$this->table};" );
}
/**
* Get found rows.
*
* @return int
*/
public function get_found_rows() {
return $this->get_var( 'SELECT FOUND_ROWS();' );
}
/**
* Get a single variable from the database.
*
* @param string $query The SQL query to run.
*/
public function get_var( $query ) {
return $this->run(
[
'type' => 'var',
'query' => $query,
]
);
}
/**
* Perform a MySQL database query, using current database connection.
*
* @codeCoverageIgnore
*
* @see wpdb::query
*
* @param string $query Database query.
*
* @return int|false Number of rows affected|selected or false on error.
*/
public function query( $query ) {
return $this->run(
[
'type' => 'query',
'query' => $query,
]
);
}
/**
* Set the limit clause.
*
* @param int $limit Limit size.
* @param int $offset Offeset.
*
* @return self The current query builder.
*/
public function limit( $limit, $offset = 0 ) {
global $wpdb;
$limit = \absint( $limit );
$offset = \absint( $offset );
$this->clear_sql_clause( 'limit' );
$this->add_sql_clause( 'limit', $wpdb->prepare( 'LIMIT %d, %d', $offset, $limit ) );
return $this;
}
/**
* Create an query limit based on a page and a page size
*
* @param int $page Page number.
* @param int $size Page size.
*
* @return self The current query builder.
*/
public function page( $page, $size = 25 ) {
$size = \absint( $size );
$offset = $size * \absint( $page );
$this->limit( $size, $offset );
return $this;
}
/**
* Set values for insert/update
*
* @param string|array $name Key of pair.
* @param string|array $value Value of pair.
*
* @return self The current query builder.
*/
public function set( $name, $value = null ) {
if ( is_array( $name ) ) {
$this->sql_clauses['values'] = $this->sql_clauses['values'] + $name;
return $this;
}
$this->sql_clauses['values'][ $name ] = $value;
return $this;
}
/**
* Reset all vaiables.
*
* @return self The current query builder.
*/
private function reset() {
$this->distinct = false;
$this->found_rows = false;
$this->sql_clauses = [
'select' => [],
'from' => [],
'left_join' => [],
'join' => [],
'right_join' => [],
'where' => [],
'where_time' => [],
'group_by' => [],
'having' => [],
'limit' => [],
'order_by' => [],
'values' => [],
];
return $this;
}
/**
* Get a single column from the database.
*
* @param string $query The SQL query to run.
* @param int $index The column index to retrieve.
*/
public function get_col( $query = '', $index = 0 ) {
return $this->run(
[
'type' => 'col',
'query' => $query,
'index' => $index,
]
);
}
/**
* Get a single row from the database.
*
* @param string $query The SQL query to run.
* @param string $output The output to retrieve.
* @param int $index The row index to retrieve.
*/
public function get_row( $query = '', $output = OBJECT, $index = 0 ) {
return $this->run(
[
'type' => 'row',
'query' => $query,
'index' => $index,
'output' => $output,
]
);
}
/**
* Get results from the database.
*
* @param string $query The SQL query to run.
* @param string $output The output format.
*/
public function get_results( $query = '', $output = OBJECT ) {
return $this->run(
[
'query' => $query,
'output' => $output,
]
);
}
/**
* Run the query and return the results.
*
* @param array $args The query arguments.
*/
public function run( $args = [] ) {
global $wpdb;
$output = isset( $args['output'] ) && $args['output'] ? $args['output'] : OBJECT;
$type = $args['type'] ?? '';
$query = $args['query'] ?? '';
$index = $args['index'] ?? '';
$start_time = microtime( true );
$this->last_query = $query;
$this->reset();
switch ( $type ) {
case 'row':
$results = $wpdb->get_row( $query, $output, $index );
break;
case 'col':
$results = $wpdb->get_col( $query, $index );
break;
case 'query':
$results = $wpdb->query( $query );
break;
case 'var':
$results = $wpdb->get_var( $query );
break;
default:
$results = $wpdb->get_results( $query, $output );
break;
}
return apply_filters( 'rank_math/database/query/results', $results, $args, $start_time );
}
}

View File

@@ -0,0 +1,131 @@
<?php
/**
* The select functions.
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath\Admin\Database
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin\Database;
/**
* Select class.
*/
trait Select {
/**
* Set the selected fields
*
* @param array $fields Fields to select.
*
* @return self The current query builder.
*/
public function select( $fields = '' ) {
if ( empty( $fields ) ) {
return $this;
}
if ( is_string( $fields ) ) {
$this->add_sql_clause( 'select', $fields );
return $this;
}
foreach ( $fields as $key => $field ) {
$this->add_sql_clause( 'select', is_string( $key ) ? "$key AS $field" : $field );
}
return $this;
}
/**
* Shortcut to add a count function
*
* ->selectCount('id')
* ->selectCount('id', 'count')
*
* @param string $field Column name.
* @param string $alias (Optional) Column alias.
*
* @return self The current query builder.
*/
public function selectCount( $field = '*', $alias = null ) { // @codingStandardsIgnoreLine
return $this->selectFunc( 'count', $field, $alias );
}
/**
* Shortcut to add a sum function
*
* ->selectSum('id')
* ->selectSum('id', 'total')
*
* @param string $field Column name.
* @param string $alias (Optional) Column alias.
*
* @return self The current query builder.
*/
public function selectSum( $field, $alias = null ) { // @codingStandardsIgnoreLine
return $this->selectFunc( 'sum', $field, $alias );
}
/**
* Shortcut to add a avg function
*
* ->selectAvg('id')
* ->selectAvg('id', 'average')
*
* @param string $field Column name.
* @param string $alias (Optional) Column alias.
*
* @return self The current query builder.
*/
public function selectAvg( $field, $alias = null ) { // @codingStandardsIgnoreLine
return $this->selectFunc( 'avg', $field, $alias );
}
/**
* Shortcut to add a function
*
* @param string $func Function name.
* @param string $field Column name.
* @param string $alias (Optional) Column alias.
*
* @return self The current query builder.
*/
public function selectFunc( $func, $field, $alias = null ) { // @codingStandardsIgnoreLine
$func = \strtoupper( $func );
$field = "$func({$field})";
if ( ! is_null( $alias ) ) {
$field .= " AS {$alias}";
}
$this->add_sql_clause( 'select', $field );
return $this;
}
/**
* Distinct select setter
*
* @param bool $distinct Is disticnt.
*
* @return self The current query builder.
*/
public function distinct( $distinct = true ) {
$this->distinct = $distinct;
return $this;
}
/**
* SQL_CALC_FOUND_ROWS select setter
*
* @param bool $found_rows Should get found rows.
*
* @return self The current query builder.
*/
public function found_rows( $found_rows = true ) {
$this->found_rows = $found_rows;
return $this;
}
}

View File

@@ -0,0 +1,136 @@
<?php
/**
* The translate functions.
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath\Admin\Database
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin\Database;
/**
* Translate class.
*/
trait Translate {
/**
* Translate the current query to an SQL select statement
*
* @return string
*/
private function translateSelect() { // @codingStandardsIgnoreLine
$query = [ 'SELECT' ];
if ( $this->found_rows ) {
$query[] = 'SQL_CALC_FOUND_ROWS';
}
if ( $this->distinct ) {
$query[] = 'DISTINCT';
}
$query[] = $this->has_sql_clause( 'select' ) ? $this->get_sql_clause( 'select', true ) : '*';
$query[] = $this->translateFrom();
$query[] = $this->get_sql_clause( 'join', true );
$query[] = $this->get_sql_clause( 'where', true );
$query[] = $this->translateGroupBy();
$query[] = $this->translateOrderBy();
$query[] = $this->translateLimit();
return join( ' ', array_filter( $query ) );
}
/**
* Translate the current query to an SQL update statement
*
* @return string
*/
private function translateUpdate() { // @codingStandardsIgnoreLine
$query = [ "UPDATE {$this->table} SET" ];
// Add the values.
$values = [];
foreach ( $this->sql_clauses['values'] as $key => $value ) {
$values[] = $key . ' = ' . $this->esc_value( $value );
}
if ( ! empty( $values ) ) {
$query[] = join( ', ', $values );
}
$query[] = $this->get_sql_clause( 'where', true );
$query[] = $this->translateLimit();
return join( ' ', array_filter( $query ) );
}
/**
* Translate the current query to an SQL delete statement
*
* @return string
*/
private function translateDelete() { // @codingStandardsIgnoreLine
$query = [ 'DELETE' ];
$query[] = $this->translateFrom();
$query[] = $this->get_sql_clause( 'where', true );
$query[] = $this->translateLimit();
return join( ' ', array_filter( $query ) );
}
/**
* Build the from statement.
*
* @return string
*/
private function translateFrom() { // @codingStandardsIgnoreLine
if ( ! $this->has_sql_clause( 'from' ) ) {
$this->add_sql_clause( 'from', $this->table );
}
return 'FROM ' . $this->get_sql_clause( 'from', true );
}
/**
* Build the order by statement
*
* @return string
*/
protected function translateOrderBy() { // @codingStandardsIgnoreLine
if ( ! $this->has_sql_clause( 'order_by' ) ) {
return '';
}
return 'ORDER BY ' . $this->get_sql_clause( 'order_by', true );
}
/**
* Build the group by clauses.
*
* @return string
*/
private function translateGroupBy() { // @codingStandardsIgnoreLine
if ( ! $this->has_sql_clause( 'group_by' ) ) {
return '';
}
$group_by = 'GROUP BY ' . $this->get_sql_clause( 'group_by', true );
if ( $this->has_sql_clause( 'having' ) ) {
$group_by .= ' ' . $this->get_sql_clause( 'having', true );
}
return $group_by;
}
/**
* Build offset and limit.
*
* @return string
*/
private function translateLimit() { // @codingStandardsIgnoreLine
return $this->get_sql_clause( 'limit', true );
}
}

View File

@@ -0,0 +1,366 @@
<?php
/**
* The where functions.
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath\Admin\Database
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin\Database;
/**
* Where class.
*/
trait Where {
/**
* Create a where statement
*
* ->where('name', 'ladina')
* ->where('age', '>', 18)
* ->where('name', 'in', ['charles', 'john', 'jeffry'])
*
* @throws \Exception If $type is not 'AND', 'OR', 'WHERE'.
*
* @param mixed $column The SQL column.
* @param mixed $param1 Operator or value depending if $param2 isset.
* @param mixed $param2 The value if $param1 is an operator.
* @param string $type the where type ( AND, OR ).
*
* @return self The current query builder.
*/
public function where( $column, $param1 = null, $param2 = null, $type = 'AND' ) {
$this->is_valid_type( $type );
$sub_type = is_null( $param1 ) ? $type : $param1;
if ( ! $this->has_sql_clause( 'where' ) ) {
$type = 'WHERE';
}
// When column is an array we assume to make a bulk and where.
if ( is_array( $column ) ) {
$this->bulk_where( $column, $type, $sub_type );
return $this;
}
$this->add_sql_clause( 'where', $this->generateWhere( $column, $param1, $param2, $type ) );
return $this;
}
/**
* Create an or where statement
*
* @param string $column The SQL column.
* @param mixed $param1 Operator or value depending if $param2 isset.
* @param mixed $param2 The value if $param1 is an operator.
*
* @return self The current query builder.
*/
public function orWhere( $column, $param1 = null, $param2 = null ) { // @codingStandardsIgnoreLine
return $this->where( $column, $param1, $param2, 'OR' );
}
/**
* Creates a where in statement
*
* ->whereIn('id', [42, 38, 12])
*
* @param string $column The SQL column.
* @param array $options Array of values for in statement.
*
* @return self The current query builder.
*/
public function whereIn( $column, $options ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'IN', $options );
}
/**
* Creates a where in statement
*
* ->orWhereIn('id', [42, 38, 12])
*
* @param string $column The SQL column.
* @param array $options Array of values for in statement.
*
* @return self The current query builder.
*/
public function orWhereIn( $column, $options ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'IN', $options, 'OR' );
}
/**
* Creates a where not in statement
*
* ->whereNotIn('id', [42, 38, 12])
*
* @param string $column The SQL column.
* @param array $options Array of values for in statement.
*
* @return self The current query builder.
*/
public function whereNotIn( $column, $options ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'NOT IN', $options );
}
/**
* Creates a where not in statement
*
* ->orWhereNotIn('id', [42, 38, 12])
*
* @param string $column The SQL column.
* @param array $options Array of values for in statement.
*
* @return self The current query builder.
*/
public function orWhereNotIn( $column, $options ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'NOT IN', $options, 'OR' );
}
/**
* Creates a where between statement
*
* ->whereBetween('id', [10, 100])
*
* @param string $column The SQL column.
* @param array $options Array of values for in statement.
*
* @return self The current query builder.
*/
public function whereBetween( $column, $options ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'BETWEEN', $options );
}
/**
* Creates a where between statement
*
* ->orWhereBetween('id', [10, 100])
*
* @param string $column The SQL column.
* @param array $options Array of values for in statement.
*
* @return self The current query builder.
*/
public function orWhereBetween( $column, $options ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'BETWEEN', $options, 'OR' );
}
/**
* Creates a where not between statement
*
* ->whereNotBetween('id', [10, 100])
*
* @param string $column The SQL column.
* @param array $options Array of values for in statement.
*
* @return self The current query builder.
*/
public function whereNotBetween( $column, $options ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'NOT BETWEEN', $options );
}
/**
* Creates a where not between statement
*
* ->orWhereNotBetween('id', [10, 100])
*
* @param string $column The SQL column.
* @param array $options Array of values for in statement.
*
* @return self The current query builder.
*/
public function orWhereNotBetween( $column, $options ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'NOT BETWEEN', $options, 'OR' );
}
/**
* Creates a where like statement
*
* ->whereLike('id', 'value')
*
* @codeCoverageIgnore
*
* @param string $column The SQL column.
* @param string $value Value for like statement.
* @param string $start (Optional) The start of like query.
* @param string $end (Optional) The end of like query.
*
* @return self The current query builder.
*/
public function whereLike( $column, $value, $start = '%', $end = '%' ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'LIKE', $this->esc_like( $value, $start, $end ) );
}
/**
* Creates a where like statement
*
* ->orWhereLike('id', 'value')
*
* @codeCoverageIgnore
*
* @param string $column The SQL column.
* @param string $value Value for like statement.
* @param string $start (Optional) The start of like query.
* @param string $end (Optional) The end of like query.
*
* @return self The current query builder.
*/
public function orWhereLike( $column, $value, $start = '%', $end = '%' ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'LIKE', $this->esc_like( $value, $start, $end ), 'OR' );
}
/**
* Creates a where not like statement
*
* ->whereNotLike('id', 'value' )
*
* @codeCoverageIgnore
*
* @param string $column The SQL column.
* @param mixed $value Value for like statement.
* @param string $start (Optional) The start of like query.
* @param string $end (Optional) The end of like query.
*
* @return self The current query builder.
*/
public function whereNotLike( $column, $value, $start = '%', $end = '%' ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'NOT LIKE', $this->esc_like( $value, $start, $end ) );
}
/**
* Creates a where not like statement
*
* ->orWhereNotLike('id', 'value' )
*
* @codeCoverageIgnore
*
* @param string $column The SQL column.
* @param mixed $value Value for like statement.
* @param string $start (Optional) The start of like query.
* @param string $end (Optional) The end of like query.
*
* @return self The current query builder.
*/
public function orWhereNotLike( $column, $value, $start = '%', $end = '%' ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'NOT LIKE', $this->esc_like( $value, $start, $end ), 'OR' );
}
/**
* Creates a where is null statement
*
* ->whereNull( 'name' )
*
* @param string $column The SQL column.
*
* @return self The current query builder.
*/
public function whereNull( $column ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'IS', 'NULL' );
}
/**
* Creates a where is null statement
*
* ->orWhereNull( 'name' )
*
* @param string $column The SQL column.
*
* @return self The current query builder.
*/
public function orWhereNull( $column ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'IS', 'NULL', 'OR' );
}
/**
* Creates a where is not null statement
*
* ->whereNotNull( 'name' )
*
* @param string $column The SQL column.
*
* @return self The current query builder.
*/
public function whereNotNull( $column ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'IS NOT', 'NULL' );
}
/**
* Creates a where is not null statement
*
* ->orWhereNotNull( 'name' )
*
* @param string $column The SQL column.
*
* @return self The current query builder.
*/
public function orWhereNotNull( $column ) { // @codingStandardsIgnoreLine
return $this->where( $column, 'IS NOT', 'NULL', 'OR' );
}
/**
* Generate Where clause
*
* @param string $column The SQL column.
* @param mixed $param1 Operator or value depending if $param2 isset.
* @param mixed $param2 The value if $param1 is an operator.
* @param string $type the where type ( AND, or ).
*
* @return string
*/
protected function generateWhere( $column, $param1 = null, $param2 = null, $type = 'AND' ) { // @codingStandardsIgnoreLine
// when param2 is null we replace param2 with param one as the
// value holder and make param1 to the = operator.
if ( is_null( $param2 ) ) {
$param2 = $param1;
$param1 = '=';
}
// When param2 is an array we probably
// have an "in" or "between" statement which has no need for duplicates.
if ( is_array( $param2 ) ) {
$param2 = $this->esc_array( array_unique( $param2 ) );
$param2 = in_array( $param1, [ 'BETWEEN', 'NOT BETWEEN' ], true ) ? join( ' AND ', $param2 ) : '(' . join( ', ', $param2 ) . ')';
} elseif ( is_scalar( $param2 ) ) {
$param2 = $this->esc_value( $param2 );
}
return join( ' ', [ $type, $column, $param1, $param2 ] );
}
/**
* Check if the where type is valid.
*
* @param string $type Value to check.
*
* @throws \Exception If not a valid type.
*/
private function is_valid_type( $type ) {
if ( ! in_array( $type, [ 'AND', 'OR', 'WHERE' ], true ) ) {
throw new \Exception( 'Invalid where type "' . esc_html( $type ) . '"' );
}
}
/**
* Create bulk where statement.
*
* @param array $where Array of statments.
* @param string $type Statement type.
* @param string $sub_type Statement sub-type.
*/
private function bulk_where( $where, $type, $sub_type ) {
$subquery = [];
foreach ( $where as $value ) {
if ( ! isset( $value[2] ) ) {
$value[2] = $value[1];
$value[1] = '=';
}
$subquery[] = $this->generateWhere( $value[0], $value[1], $value[2], empty( $subquery ) ? '' : $sub_type );
}
$this->add_sql_clause( 'where', $type . ' ( ' . trim( join( ' ', $subquery ) ) . ' )' );
}
}

View File

@@ -0,0 +1 @@
<?php // Silence is golden.

View File

@@ -0,0 +1,361 @@
<?php
/**
* The AIO Schema Rich Snippets Import Class
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin\Importers
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin\Importers;
use RankMath\Helper;
use RankMath\Admin\Admin_Helper;
use RankMath\Helpers\DB;
use RankMath\Helpers\Str;
defined( 'ABSPATH' ) || exit;
/**
* Import_AIO_Rich_Snippet class.
*/
class AIO_Rich_Snippet extends Plugin_Importer {
/**
* The plugin name.
*
* @var string
*/
protected $plugin_name = 'AIO Schema Rich Snippet';
/**
* Plugin options meta key.
*
* @var string
*/
protected $meta_key = '_bsf_post_type';
/**
* Option keys to import and clean.
*
* @var array
*/
protected $option_keys = [ 'bsf_', 'bsf_%' ];
/**
* Choices keys to import.
*
* @var array
*/
protected $choices = [ 'postmeta' ];
/**
* Import post meta of plugin.
*
* @return array
*/
protected function postmeta() {
$this->set_pagination( $this->get_post_ids( true ) );
// Set Converter.
foreach ( $this->get_post_ids() as $snippet_post ) {
$type = $this->is_allowed_type( $snippet_post->meta_value );
$meta_keys = $this->get_metakeys( $type );
if ( false === $type || false === $meta_keys ) {
continue;
}
$this->set_postmeta( $snippet_post->post_id, $type, $meta_keys );
}
return $this->get_pagination_arg();
}
/**
* Set snippet meta.
*
* @param int $post_id Post ID.
* @param string $type Type to get keys for.
* @param array $meta_keys Array of meta keys to save.
*/
private function set_postmeta( $post_id, $type, $meta_keys ) {
$data = [];
foreach ( $meta_keys as $snippet_key => $snippet_value ) {
$value = get_post_meta( $post_id, '_bsf_' . $snippet_key, true );
$this->validate_schema_data( $data, $value, $snippet_value );
}
if ( empty( $data ) ) {
return;
}
// Convert post now.
$data['@type'] = $this->validate_type( $type );
$data['metadata'] = [
'title' => Helper::sanitize_schema_title( $data['@type'] ),
'type' => 'template',
'isPrimary' => 1,
'shortcode' => uniqid( 's-' ),
];
update_post_meta( $post_id, 'rank_math_schema_' . $data['@type'], $data );
}
/**
* Validate schema type.
*
* @param string $type Schema Type.
*/
private function validate_type( $type ) {
if ( 'software' === $type ) {
return 'SoftwareApplication';
}
if ( 'video' === $type ) {
return 'VideoObject';
}
return ucfirst( $type );
}
/**
* Validate schema data.
*
* @param array $data Schema entity data.
* @param string $value Entity value.
* @param string $key Entity key.
*/
private function validate_schema_data( &$data, $value, $key ) {
if ( ! Str::contains( '.', $key ) ) {
$data[ $key ] = $value;
return;
}
$element = explode( '.', $key );
if ( 2 === count( $element ) ) {
$this->add_type( $data[ $element[0] ], $element[0] );
$data[ $element[0] ][ $element[1] ] = $value;
return;
}
if ( count( $element ) > 2 ) {
$this->add_type( $data[ $element[0] ], $element[0] );
$this->add_type( $data[ $element[0] ][ $element[1] ], $element[1] );
$data[ $element[0] ][ $element[1] ][ $element[2] ] = $value;
}
}
/**
* Add property type.
*
* @param array $data Schema entity data.
* @param string $key Entity key.
*/
private function add_type( &$data, $key ) {
if ( 'location' === $key ) {
$data['@type'] = 'Place';
}
if ( 'address' === $key ) {
$data['@type'] = 'PostalAddress';
}
if ( 'offers' === $key ) {
$data['@type'] = 'Offer';
}
if ( 'brand' === $key ) {
$data['@type'] = 'Brand';
}
if ( 'review' === $key ) {
$data['@type'] = 'Review';
}
if ( 'reviewRating' === $key ) {
$data['@type'] = 'Rating';
}
if ( 'nutrition' === $key ) {
$data['@type'] = 'NutritionInformation';
}
}
/**
* Get the actions which can be performed for the plugin.
*
* @return array
*/
public function get_choices() {
return [
'postmeta' => esc_html__( 'Import Schemas', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Import all Schema data for Posts, Pages, and custom post types.', 'rank-math' ) ),
];
}
/**
* Get all post IDs of all allowed post types only.
*
* @param bool $count If we need count only for pagination purposes.
* @return int|array
*/
protected function get_post_ids( $count = false ) {
$paged = $this->get_pagination_arg( 'page' );
$table = DB::query_builder( 'postmeta' )->where( 'meta_key', '_bsf_post_type' );
return $count ? absint( $table->selectCount( 'meta_id' )->getVar() ) :
$table->page( $paged - 1, $this->items_per_page )->get();
}
/**
* Get snippet types.
*
* @return array
*/
private function get_types() {
return [
'2' => 'event',
'5' => 'person',
'6' => 'product',
'7' => 'recipe',
'8' => 'software',
'9' => 'video',
'10' => 'article',
'11' => 'service',
];
}
/**
* Is snippet type allowed.
*
* @param string $type Type to check.
*
* @return bool
*/
private function is_allowed_type( $type ) {
$types = $this->get_types();
return isset( $types[ $type ] ) ? $types[ $type ] : false;
}
/**
* Get meta keys hash to import.
*
* @param string $type Type to get keys for.
*
* @return array
*/
private function get_metakeys( $type ) {
$hash = [
'event' => $this->get_event_fields(),
'product' => $this->get_product_fields(),
'recipe' => $this->get_recipe_fields(),
'software' => $this->get_software_fields(),
'video' => $this->get_video_fields(),
'article' => [
'article_name' => 'headline',
'article_desc' => 'description',
],
'person' => [
'people_fn' => 'name',
'people_nickname' => 'description',
'people_job_title' => 'jobTitle',
'people_street' => 'address.streetAddress',
'people_local' => 'address.addressLocality',
'people_region' => 'address.addressRegion',
'people_postal' => 'address.postalCode',
],
'service' => [
'service_type' => 'serviceType',
'service_desc' => 'description',
],
];
return isset( $hash[ $type ] ) ? $hash[ $type ] : false;
}
/**
* Get event fields.
*
* @return array
*/
private function get_event_fields() {
return [
'event_title' => 'name',
'event_desc' => 'description',
'event_organization' => 'location.name',
'event_street' => 'location.address.streetAddress',
'event_local' => 'location.address.addressLocality',
'event_region' => 'location.address.addressRegion',
'event_postal_code' => 'location.address.postalCode',
'event_start_date' => 'startDate',
'event_end_date' => 'endDate',
'event_price' => 'offers.price',
'event_cur' => 'offers.priceCurrency',
'event_ticket_url' => 'offers.url',
];
}
/**
* Get product fields.
*
* @return array
*/
private function get_product_fields() {
return [
'product_brand' => 'brand.name',
'product_name' => 'name',
'product_price' => 'offers.price',
'product_cur' => 'offers.priceCurrency',
'product_status' => 'offers.availability',
];
}
/**
* Get recipe fields.
*
* @return array
*/
private function get_recipe_fields() {
return [
'recipes_name' => 'name',
'recipes_desc' => 'description',
'recipes_preptime' => 'prepTime',
'recipes_cooktime' => 'cookTime',
'recipes_totaltime' => 'totalTime',
'recipes_ingredient' => 'recipeIngredient',
'recipes_nutrition' => 'nutrition.calories',
];
}
/**
* Get software fields.
*
* @return array
*/
private function get_software_fields() {
return [
'software_rating' => 'review.reviewRating.ratingValue',
'software_price' => 'offers.price',
'software_cur' => 'offers.priceCurrency',
'software_name' => 'name',
'software_os' => 'operatingSystem',
'software_cat' => 'applicationCategory',
];
}
/**
* Get video fields.
*
* @return array
*/
private function get_video_fields() {
return [
'video_title' => 'name',
'video_desc' => 'description',
'video_thumb' => 'thumbnailUrl',
'video_url' => 'contentUrl',
'video_emb_url' => 'embedUrl',
'video_duration' => 'duration',
];
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,201 @@
<?php
/**
* The functionality to detect whether we should import from another SEO plugin.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin\Importers
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin\Importers;
use RankMath\Traits\Hooker;
defined( 'ABSPATH' ) || exit;
/**
* Detector class.
*/
class Detector {
use Hooker;
/**
* Plugins we can import from.
*
* @var array
*/
public static $plugins = null;
/**
* Detects whether we can import anything or not.
*
* @return array List of plugins we can import from.
*/
public function detect() {
$this->requirements();
if ( ! is_null( self::$plugins ) ) {
return self::$plugins;
}
self::$plugins = [];
$plugins = $this->get();
foreach ( $plugins as $slug => $plugin ) {
if ( ! $this->is_detectable( $plugin, $plugins ) ) {
continue;
}
$this->can_import( $slug, $plugin );
}
return self::$plugins;
}
/**
* Run import class.
*
* @param Plugin_Importer $importer The importer that needs to perform this action.
* @param string $action The action to perform.
* @param string $perform The action to perform when running import action.
*/
public function run( $importer, $action = 'detect', $perform = '' ) {
if ( 'cleanup' === $action ) {
return $importer->run_cleanup();
} elseif ( 'import' === $action ) {
return $importer->run_import( $perform );
}
return $importer->run_detect();
}
/**
* Run action by slug.
*
* @param string $slug The importer slug that needs to perform this action.
* @param string $action The action to perform.
* @param string $perform The action to perform when running import action.
*/
public static function run_by_slug( $slug, $action, $perform = '' ) {
$detector = new self();
$importers = $detector->get();
if ( ! isset( $importers[ $slug ] ) ) {
return false;
}
$importer = $importers[ $slug ];
$importer = new $importer['class']( $importer['file'] );
$status = $detector->run( $importer, $action, $perform );
return \compact( 'importer', 'status' );
}
/**
* Deactivate all plugins.
*/
public static function deactivate_all() {
$detector = new Detector();
$plugins = $detector->get();
foreach ( $plugins as $plugin ) {
deactivate_plugins( $plugin['file'] );
}
}
/**
* Get the list of available importers.
*
* @return array Available importers.
*/
public function get() {
return $this->do_filter(
'importers/detect_plugins',
[
'yoast' => [
'class' => '\\RankMath\\Admin\\Importers\\Yoast',
'file' => 'wordpress-seo/wp-seo.php',
'premium' => 'yoast-premium',
],
'seopress' => [
'class' => '\\RankMath\\Admin\\Importers\\SEOPress',
'file' => 'wp-seopress/seopress.php',
],
'aioseo' => [
'class' => '\\RankMath\\Admin\\Importers\\AIOSEO',
'file' => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
'premium' => 'all-in-one-seo-pack-pro',
],
'all-in-one-seo-pack-pro' => [
'class' => '\\RankMath\\Admin\\Importers\\AIOSEO',
'file' => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
'parent' => 'aioseo',
],
'yoast-premium' => [
'class' => '\\RankMath\\Admin\\Importers\\Yoast',
'file' => 'wordpress-seo-premium/wp-seo-premium.php',
'parent' => 'yoast',
],
'aio-rich-snippet' => [
'class' => '\\RankMath\\Admin\\Importers\\AIO_Rich_Snippet',
'file' => 'all-in-one-schemaorg-rich-snippets/index.php',
],
'wp-schema-pro' => [
'class' => '\\RankMath\\Admin\\Importers\\WP_Schema_Pro',
'file' => 'wp-schema-pro/wp-schema-pro.php',
],
'redirections' => [
'class' => '\\RankMath\\Admin\\Importers\\Redirections',
'file' => 'redirection/redirection.php',
],
]
);
}
/**
* Check requirements.
*/
private function requirements() {
if ( ! function_exists( 'is_plugin_active' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php'; // @phpstan-ignore-line
}
}
/**
* Can import plugin data.
*
* @param string $slug Plugin slug.
* @param array $plugin Plugin data.
*/
private function can_import( $slug, $plugin ) {
$importer = new $plugin['class']( $plugin['file'] );
if ( $importer->run_detect() ) {
self::$plugins[ $slug ] = [
'name' => $importer->get_plugin_name(),
'file' => $importer->get_plugin_file(),
'choices' => $importer->get_choices(),
'isActive' => is_plugin_active( $importer->get_plugin_file() ),
];
}
}
/**
* Check if plugin is detectable.
*
* @param array $check Plugin to check.
* @param array $plugins Plugins data.
*
* @return bool
*/
private function is_detectable( $check, $plugins ) {
// Check if parent is set.
if ( isset( $check['parent'] ) && isset( self::$plugins[ $check['parent'] ] ) ) {
return false;
}
// Check if plugin has premium and it is active.
if ( isset( $check['premium'] ) && is_plugin_active( $plugins[ $check['premium'] ]['file'] ) ) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,586 @@
<?php
/**
* The abstract class for plugins import to inherit from
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin\Importers
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin\Importers;
use Exception;
use RankMath\Helper;
use RankMath\Traits\Ajax;
use RankMath\Traits\Meta;
use RankMath\Traits\Hooker;
use RankMath\Admin\Admin_Helper;
use RankMath\Helpers\DB as DB_Helper;
use RankMath\Helpers\Param;
use RankMath\Helpers\Attachment;
defined( 'ABSPATH' ) || exit;
/**
* Plugin_Importer class.
*/
abstract class Plugin_Importer {
use Hooker;
use Ajax;
use Meta;
/**
* The plugin name.
*
* @var string
*/
protected $plugin_name;
/**
* The plugin file.
*
* @var string
*/
protected $plugin_file;
/**
* Plugin options meta key.
*
* @var string
*/
protected $meta_key;
/**
* Option keys to import and clean.
*
* @var array
*/
protected $option_keys;
/**
* Table names to drop while cleaning.
*
* @var array
*/
protected $table_names;
/**
* Choices keys to import.
*
* @var array
*/
protected $choices;
/**
* Number of items to parse per page.
*
* @var int
*/
protected $items_per_page = 100;
/**
* Pagination arguments.
*
* @var array
*/
protected $pagination_args = [];
/**
* General settings.
*
* @var array
*/
protected $settings;
/**
* Titles settings.
*
* @var array
*/
protected $titles;
/**
* Sitemap settings.
*
* @var array
*/
protected $sitemap;
/**
* Class constructor.
*
* @param string $plugin_file Plugins file.
*/
public function __construct( $plugin_file ) {
$this->plugin_file = $plugin_file;
}
/**
* Get the name of the plugin we're importing from.
*
* @return string Plugin name.
*/
public function get_plugin_name() {
return $this->plugin_name;
}
/**
* Get the plugin file of the plugin we're importing from.
*
* @return string Plugin file
*/
public function get_plugin_file() {
return $this->plugin_file;
}
/**
* Get the actions which can be performed for the plugin.
*
* @return array
*/
public function get_choices() {
if ( empty( $this->choices ) ) {
return [];
}
return array_intersect_key(
[
'settings' => esc_html__( 'Import Settings', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Import plugin settings, global meta, sitemap settings, etc.', 'rank-math' ) ),
'postmeta' => esc_html__( 'Import Post Meta', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Import meta information of your posts/pages like the focus keyword, titles, descriptions, robots meta, OpenGraph info, etc.', 'rank-math' ) ),
'termmeta' => esc_html__( 'Import Term Meta', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Import data like category, tag, and CPT meta data from SEO.', 'rank-math' ) ),
'usermeta' => esc_html__( 'Import Author Meta', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Import meta information like titles, descriptions, focus keyword, robots meta, etc., of your author archive pages.', 'rank-math' ) ),
'redirections' => esc_html__( 'Import Redirections', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Import all the redirections you have already set up in Yoast Premium.', 'rank-math' ) ),
'blocks' => esc_html__( 'Import Blocks', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Import and convert all compatible blocks in post contents.', 'rank-math' ) ),
'locations' => esc_html__( 'Import Locations', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Import Locations Settings from Yoast plugin.', 'rank-math' ) ),
'news' => esc_html__( 'Import News Settings', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Import News Settings from Yoast News Add-on.', 'rank-math' ) ),
'video' => esc_html__( 'Import Video Sitemap Settings', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Import Video Sitemap Settings from Yoast Video Add-on.', 'rank-math' ) ),
],
array_combine(
$this->choices,
$this->choices
)
);
}
/**
* Check if import is needed from this plugin.
*
* @return bool Whether there is something to import.
*/
public function run_detect() {
return true === $this->has_options() ? true : $this->has_postmeta();
}
/**
* Delete all plugin data from the database.
*
* @return bool
*/
public function run_cleanup() {
if ( ! $this->run_detect() ) {
return false;
}
$result = $this->drop_custom_tables();
$result = $this->clean_meta_table();
$result = $this->clean_options();
return $result;
}
/**
* Run importer.
*
* @throws Exception Throws error if no perform function founds.
*
* @param string $perform The action to perform when running import action.
*/
public function run_import( $perform ) {
if ( ! method_exists( $this, $perform ) ) {
throw new Exception( esc_html__( 'Unable to perform action this time.', 'rank-math' ) );
}
/**
* Number of items to import per run.
*
* @param int $items_per_page Default 100.
*/
$this->items_per_page = absint( $this->do_filter( 'importers/items_per_page', 100 ) );
$status = new Status();
$result = $this->$perform();
$is_success = is_array( $result ) || true === $result;
$status->set_action( $perform );
$status->set_status( $is_success );
$message = $this->format_message( $result, $perform, $status->get_message() );
if ( is_scalar( $result ) ) {
$result = [];
}
if ( $is_success ) {
$result['message'] = $message;
$this->success( $result );
}
$result['error'] = $message;
$this->error( $result );
}
/**
* Get success message.
*
* @param array $result Array of result.
* @param string $action Action performed.
* @param string $message Message to format.
*
* @return mixed
*/
private function format_message( $result, $action, $message ) {
if ( 'blocks' === $action || 'recalculate' === $action ) {
return is_array( $result ) ? sprintf( $message, $result['start'], $result['end'], $result['total_items'] ) : $result;
}
if ( 'postmeta' === $action || 'usermeta' === $action ) {
return sprintf( $message, $result['start'], $result['end'], $result['total_items'] );
}
if ( 'termmeta' === $action || 'redirections' === $action ) {
return sprintf( $message, $result['count'] );
}
return $message;
}
/**
* Deactivate plugin action.
*/
protected function deactivate() {
if ( is_plugin_active( $this->get_plugin_file() ) ) {
deactivate_plugins( $this->get_plugin_file() );
}
return true;
}
/**
* Replace settings based on key/value hash.
*
* @param array $hash Array of hash for search and replace.
* @param array $source Array for source where to search.
* @param array $destination Array for destination where to save.
* @param bool $convert (Optional) Conversion type. Default: false.
*/
protected function replace( $hash, $source, &$destination, $convert = false ) {
foreach ( $hash as $search => $replace ) {
if ( ! isset( $source[ $search ] ) ) {
continue;
}
$destination[ $replace ] = false === $convert ? $source[ $search ] : $this->$convert( $source[ $search ] );
}
}
/**
* Replace meta based on key/value hash.
*
* @param array $hash Array of hash for search and replace.
* @param array $source Array for source where to search.
* @param int $object_id Object id for destination where to save.
* @param string $object_type Object type for destination where to save.
* @param bool $convert (Optional) Conversion type. Default: false.
*/
protected function replace_meta( $hash, $source, $object_id, $object_type, $convert = false ) {
foreach ( $hash as $search => $replace ) {
$value = ! empty( $source[ $search ] ) ? $source[ $search ] : $this->get_meta( $object_type, $object_id, $search );
if ( empty( $value ) ) {
continue;
}
$this->update_meta(
$object_type,
$object_id,
$replace,
false !== $convert ? $this->$convert( $value ) : $value
);
}
}
/**
* Replace an image to its URL and ID.
*
* @param string $source Source image url.
* @param array|callable $destination Destination array.
* @param string $image Image field key to save url.
* @param string $image_id Image id field key to save id.
* @param int $object_id Object ID either post ID, term ID or user ID.
*/
protected function replace_image( $source, $destination, $image, $image_id, $object_id = null ) {
if ( empty( $source ) ) {
return;
}
$attachment_id = Attachment::get_by_url( $source );
if ( 1 > $attachment_id ) {
return;
}
if ( is_null( $object_id ) ) {
$destination[ $image ] = $source;
$destination[ $image_id ] = $attachment_id;
return;
}
$this->update_meta( $destination, $object_id, $image, $source );
$this->update_meta( $destination, $object_id, $image_id, $attachment_id );
}
/**
* Convert bool value to switch.
*
* @param mixed $value Value to convert.
* @return string
*/
protected function convert_bool( $value ) {
if ( true === boolval( $value ) ) {
return 'on';
}
if ( false === boolval( $value ) ) {
return 'off';
}
return $value;
}
/**
* Set variable that twitter is using facebook data or not.
*
* @param string $object_type Object type for destination where to save.
* @param int $object_id Object id for destination where to save.
*/
protected function is_twitter_using_facebook( $object_type, $object_id ) {
$keys = [
'rank_math_twitter_title',
'rank_math_twitter_description',
'rank_math_twitter_image',
];
foreach ( $keys as $key ) {
if ( ! empty( $this->get_meta( $object_type, $object_id, $key, true ) ) ) {
$this->update_meta( $object_type, $object_id, 'rank_math_twitter_use_facebook', 'off' );
break;
}
}
}
/**
* Convert Yoast / AIO SEO variables if needed.
*
* @param string $value Value to convert.
* @return string
*/
protected function convert_variables( $value ) {
return str_replace( '%%', '%', $value );
}
/**
* Set pagination arguments.
*
* @param int $total_items Number of total items to set pagination.
*/
protected function set_pagination( $total_items = 0 ) {
$args = [
'total_pages' => 0,
'total_items' => $total_items,
'per_page' => $this->items_per_page,
];
// Total Pages.
if ( ! $args['total_pages'] && $args['per_page'] > 0 ) {
$args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
}
// Current Page.
$pagenum = Param::request( 'paged', 0, FILTER_VALIDATE_INT );
if ( isset( $args['total_pages'] ) && $pagenum > $args['total_pages'] ) {
$pagenum = $args['total_pages'];
}
$args['page'] = max( 1, $pagenum );
// Start n End.
$args['start'] = ( ( $args['page'] - 1 ) * $this->items_per_page ) + 1;
$args['end'] = min( $args['page'] * $this->items_per_page, $total_items );
$this->pagination_args = $args;
}
/**
* Get pagination arguments.
*
* @param bool $key If any specific data is required from arguments.
* @return mixed
*/
protected function get_pagination_arg( $key = false ) {
if ( false === $key ) {
return $this->pagination_args;
}
return isset( $this->pagination_args[ $key ] ) ? $this->pagination_args[ $key ] : false;
}
/**
* Get all post IDs of all allowed post types only.
*
* @param bool $count If we need count only for pagination purposes.
* @return int|array
*/
protected function get_post_ids( $count = false ) {
$paged = $this->get_pagination_arg( 'page' );
$table = DB_Helper::query_builder( 'posts' )->whereIn( 'post_type', Helper::get_accessible_post_types() );
return $count ? absint( $table->selectCount( 'ID', 'total' )->getVar() ) :
$table->select( 'ID' )->page( $paged - 1, $this->items_per_page )->get();
}
/**
* Get all user IDs.
*
* @param bool $count If we need count only for pagination purposes.
* @return int|array
*/
protected function get_user_ids( $count = false ) {
$paged = $this->get_pagination_arg( 'page' );
$table = DB_Helper::query_builder( 'users' );
return $count ? absint( $table->selectCount( 'ID', 'total' )->getVar() ) :
$table->select( 'ID' )->page( $paged - 1, $this->items_per_page )->get();
}
/**
* Get system settings.
*/
protected function get_settings() {
$all_opts = rank_math()->settings->all_raw();
$this->settings = $all_opts['general'];
$this->titles = $all_opts['titles'];
$this->sitemap = $all_opts['sitemap'];
}
/**
* Update system settings.
*/
protected function update_settings() {
Helper::update_all_settings(
$this->settings,
$this->titles,
$this->sitemap
);
}
/**
* Clean meta table for post, term and user.
*
* @return bool
*/
private function clean_meta_table() {
if ( empty( $this->meta_key ) ) {
return false;
}
$result = false;
$result = DB_Helper::query_builder( 'usermeta' )->whereLike( 'meta_key', $this->meta_key )->delete();
$result = DB_Helper::query_builder( 'termmeta' )->whereLike( 'meta_key', $this->meta_key )->delete();
$result = DB_Helper::query_builder( 'postmeta' )->whereLike( 'meta_key', $this->meta_key )->delete();
return $result;
}
/**
* Clean options table.
*
* @return bool
*/
private function clean_options() {
if ( empty( $this->option_keys ) ) {
return false;
}
$table = DB_Helper::query_builder( 'options' );
foreach ( $this->option_keys as $option_key ) {
$table->orWhereLike( 'option_name', $option_key );
}
return $table->delete();
}
/**
* Drop custom tables for plugins.
*
* @return bool
*/
private function drop_custom_tables() {
global $wpdb;
if ( empty( $this->table_names ) ) {
return false;
}
foreach ( $this->table_names as $table ) {
DB_Helper::query( "DROP TABLE IF EXISTS {$wpdb->prefix}{$table}" );
}
return true;
}
/**
* Check if plugin has options.
*
* @return bool
*/
private function has_options() {
if ( empty( $this->option_keys ) ) {
return false;
}
$table = DB_Helper::query_builder( 'options' )->select( 'option_id' );
foreach ( $this->option_keys as $option_key ) {
if ( '%' === substr( $option_key, -1 ) ) {
$table->orWhereLike( 'option_name', substr( $option_key, 0, -1 ), '' );
} else {
$table->orWhere( 'option_name', $option_key );
}
}
return absint( $table->getVar() ) > 0 ? true : false;
}
/**
* Check if plugin has postmeta.
*
* @return bool
*/
private function has_postmeta() {
if ( empty( $this->meta_key ) ) {
return false;
}
$result = DB_Helper::query_builder( 'postmeta' )->select( 'meta_id' )->whereLike( 'meta_key', $this->meta_key, '' )->getVar();
return absint( $result ) > 0 ? true : false;
}
/**
* Recalculate SEO scores.
*/
private function recalculate() {
$this->set_pagination( \RankMath\Tools\Update_Score::get()->find( false ) );
$return = $this->get_pagination_arg();
$data = \RankMath\Tools\Update_Score::get()->update_seo_score();
$return['data'] = $data;
return $return;
}
}

View File

@@ -0,0 +1,105 @@
<?php
/**
* The Redirections Import Class
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin\Importers
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin\Importers;
use RankMath\Helper;
use RankMath\Helpers\DB as DB_Helper;
use RankMath\Admin\Admin_Helper;
use RankMath\Redirections\Redirection;
defined( 'ABSPATH' ) || exit;
/**
* Redirections class.
*/
class Redirections extends Plugin_Importer {
/**
* The plugin name.
*
* @var string
*/
protected $plugin_name = 'Redirections';
/**
* Option keys to import and clean.
*
* @var array
*/
protected $option_keys = [ 'redirection_options' ];
/**
* Choices keys to import.
*
* @var array
*/
protected $choices = [ 'redirections' ];
/**
* Import redirections of plugin.
*
* @return bool
*/
protected function redirections() {
global $wpdb;
$count = 0;
$rows = DB_Helper::get_results( "SELECT * FROM {$wpdb->prefix}redirection_items" );
if ( empty( $rows ) ) {
return false;
}
foreach ( (array) $rows as $row ) {
$item = Redirection::from(
[
'sources' => [
[
'pattern' => $row->url,
'comparison' => empty( $row->regex ) ? 'exact' : 'regex',
],
],
'url_to' => $this->get_url_to( $row ),
'header_code' => $row->action_code,
'status' => 'disabled' === $row->status ? 'inactive' : 'active',
]
);
$id = $item->save();
if ( false !== $id ) {
do_action( 'rank_math/redirection/after_import', $id, $row );
++$count;
}
}
Helper::update_modules( [ 'redirections' => 'on' ] );
return compact( 'count' );
}
/**
* Get validated url to value
*
* @param object $row Current row we are processing.
* @return string
*/
private function get_url_to( $row ) {
$data = maybe_unserialize( $row->action_data );
if ( is_array( $data ) && ( isset( $data['url'] ) || isset( $data['url_from'] ) ) ) {
return isset( $data['url'] ) ? $data['url'] : $data['url_from'];
}
if ( is_string( $row->action_data ) ) {
return $row->action_data;
}
return '/';
}
}

View File

@@ -0,0 +1,844 @@
<?php
/**
* The SEOPress Import Class
*
* @since 1.0.24
* @package RankMath
* @subpackage RankMath\Admin\Importers
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin\Importers;
use RankMath\Helper;
use RankMath\Admin\Admin_Helper;
use RankMath\Redirections\Redirection;
use RankMath\Schema\JsonLD;
use RankMath\Schema\Singular;
use RankMath\Helpers\DB;
defined( 'ABSPATH' ) || exit;
/**
* SEOPress class.
*/
class SEOPress extends Plugin_Importer {
/**
* The plugin name.
*
* @var string
*/
protected $plugin_name = 'SEOPress';
/**
* Plugin options meta key.
*
* @var string
*/
protected $meta_key = 'seopress';
/**
* Option keys to import and clean.
*
* @var array
*/
protected $option_keys = [ 'seopress', 'seopress_%' ];
/**
* Choices keys to import.
*
* @var array
*/
protected $choices = [ 'settings', 'postmeta', 'termmeta', 'redirections' ];
/**
* JsonLD.
*
* @var JsonLD
*/
private $json_ld;
/**
* Singular.
*
* @var Singular
*/
private $single;
/**
* Convert SEOPress variables if needed.
*
* @param string $variable Value to convert.
*
* @return string
*/
public function convert_variables( $variable ) {
$variable = str_replace( '%%sitetitle%%', '%sitename%', $variable );
$variable = str_replace( '%%tagline%%', '%sitedesc%', $variable );
$variable = str_replace( '%%post_title%%', '%title%', $variable );
$variable = str_replace( '%%post_excerpt%%', '%excerpt%', $variable );
$variable = str_replace( '%%post_date%%', '%date%', $variable );
$variable = str_replace( '%%post_modified_date%%', '%modified%', $variable );
$variable = str_replace( '%post_author%%', '%name%', $variable );
$variable = str_replace( '%%post_category%%', '%category%', $variable );
$variable = str_replace( '%%post_tag%%', '%tag%', $variable );
$variable = str_replace( '%%_category_title%%', '%term%', $variable );
$variable = str_replace( '%%_category_description%%', '%term_description%', $variable );
$variable = str_replace( '%%tag_title%%', '%term%', $variable );
$variable = str_replace( '%%tag_description%%', '%term_description%', $variable );
$variable = str_replace( '%%term_title%%', '%term%', $variable );
$variable = str_replace( '%%term_description%%', '%term_description%', $variable );
$variable = str_replace( '%%search_keywords%%', '%search_query%', $variable );
$variable = str_replace( '%%current_pagination%%', '%page%', $variable );
$variable = str_replace( '%%cpt_plural%%', '%pt_plural%', $variable );
$variable = str_replace( '%%archive_title%%', '%title%', $variable );
$variable = str_replace( '%%archive_date%%', '%currentdate%', $variable );
$variable = str_replace( '%%archive_date_day%%', '%currentday%', $variable );
$variable = str_replace( '%%archive_date_month%%', '%currentmonth%', $variable );
$variable = str_replace( '%%archive_date_year%%', '%year%', $variable );
$variable = str_replace( '%%currentdate%%', '%currentdate%', $variable );
$variable = str_replace( '%%currentday%%', '%currentday%', $variable );
$variable = str_replace( '%%currentmonth%%', '%currentmonth%', $variable );
$variable = str_replace( '%%currentyear%%', '%currentyear%', $variable );
$variable = str_replace( '%%currenttime%%', '%time%', $variable );
$variable = str_replace( '%%author_bio%%', '%user_description%', $variable );
$variable = str_replace( '%%wc_single_cat%%', '%term%', $variable );
$variable = str_replace( '%%wc_single_tag%%', '%term%', $variable );
$variable = str_replace( '%%wc_single_short_desc%%', '%wc_shortdesc%', $variable );
$variable = str_replace( '%%wc_single_price%%', '%wc_price%', $variable );
return str_replace( '%%', '%', $variable );
}
/**
* Deactivate plugin action.
*/
protected function deactivate() {
if ( is_plugin_active( $this->get_plugin_file() ) ) {
deactivate_plugins( $this->get_plugin_file() );
deactivate_plugins( 'wp-seopress-pro/seopress-pro.php' );
}
return true;
}
/**
* Import settings of plugin.
*
* @return bool
*/
protected function settings() {
$this->get_settings();
$seopress_titles = get_option( 'seopress_titles_option_name' );
$seopress_sitemap = get_option( 'seopress_xml_sitemap_option_name' );
$seopress_local = get_option( 'seopress_pro_option_name' );
// Titles & Descriptions.
$hash = [
'seopress_titles_archives_author_disable' => 'disable_author_archives',
'seopress_titles_archives_date_disable' => 'disable_date_archives',
'seopress_titles_home_site_title' => 'homepage_title',
'seopress_titles_home_site_desc' => 'homepage_description',
'seopress_titles_archives_author_title' => 'author_archive_title',
'seopress_titles_archives_author_desc' => 'author_archive_description',
'seopress_titles_archives_date_title' => 'date_archive_title',
'seopress_titles_archives_date_desc' => 'date_archive_description',
'seopress_titles_archives_search_title' => 'search_title',
'seopress_titles_archives_404_title' => '404_title',
];
$this->replace( $hash, $seopress_titles, $this->titles, 'convert_variables' );
$this->replace( $hash, $seopress_titles, $this->titles, 'convert_bool' );
$this->titles['title_separator'] = \RankMath\CMB2::sanitize_htmlentities( $seopress_titles['seopress_titles_sep'] );
$this->titles['date_archive_robots'] = ! empty( $seopress_titles['seopress_titles_archives_date_noindex'] ) ? [ 'noindex' ] : [];
$this->set_robots( 'author', ! empty( $seopress_titles['seopress_titles_archives_author_noindex'] ), '' );
$this->update_modules( $seopress_local, $seopress_sitemap );
$this->social_settings();
$this->advanced_settings();
$this->post_type_settings( $seopress_titles, $seopress_sitemap );
$this->taxonomies_settings( $seopress_titles, $seopress_sitemap );
$this->local_seo_settings( $seopress_local );
$this->update_settings();
return true;
}
/**
* Import post meta of plugin.
*
* @return array
*/
protected function postmeta() {
$this->set_pagination( $this->get_post_ids( true ) );
$post_ids = $this->get_post_ids();
$this->set_primary_term( $post_ids );
$hash = [
'_seopress_titles_title' => 'rank_math_title',
'_seopress_titles_desc' => 'rank_math_description',
'_seopress_analysis_target_kw' => 'rank_math_focus_keyword',
'_seopress_robots_canonical' => 'rank_math_canonical_url',
'_seopress_social_fb_title' => 'rank_math_facebook_title',
'_seopress_social_fb_desc' => 'rank_math_facebook_description',
'_seopress_social_fb_img' => 'rank_math_facebook_image',
'_seopress_social_twitter_title' => 'rank_math_twitter_title',
'_seopress_social_twitter_desc' => 'rank_math_twitter_description',
'_seopress_social_twitter_img' => 'rank_math_twitter_image',
'_seopress_robots_breadcrumbs' => 'rank_math_breadcrumb_title',
];
// Set Converter.
$this->json_ld = new JsonLD();
$this->single = new Singular();
foreach ( $post_ids as $post ) {
$post_id = $post->ID;
$this->replace_meta( $hash, null, $post_id, 'post', 'convert_variables' );
delete_post_meta( $post_id, 'rank_math_permalink' );
$this->replace_image( get_post_meta( $post_id, '_seopress_social_fb_img', true ), 'post', 'rank_math_facebook_image', 'rank_math_facebook_image_id', $post_id );
$this->replace_image( get_post_meta( $post_id, '_seopress_social_twitter_img', true ), 'post', 'rank_math_twitter_image', 'rank_math_twitter_image_id', $post_id );
$this->is_twitter_using_facebook( 'post', $post_id );
$this->set_object_robots( $post_id, 'post' );
$this->set_schema_data( $post_id );
$this->set_object_redirection( $post_id, 'post' );
}
return $this->get_pagination_arg();
}
/**
* Import term meta of plugin.
*
* @return array
*/
protected function termmeta() {
$count = 0;
$terms = new \WP_Term_Query(
[
'meta_key' => '_seopress_titles_title',
'fields' => 'ids',
'hide_empty' => false,
'get' => 'all',
]
);
if ( empty( $terms ) ) {
return false;
}
$hash = [
'_seopress_titles_title' => 'rank_math_title',
'_seopress_titles_desc' => 'rank_math_description',
'_seopress_robots_canonical' => 'rank_math_canonical_url',
'_seopress_social_fb_title' => 'rank_math_facebook_title',
'_seopress_social_fb_desc' => 'rank_math_facebook_description',
'_seopress_social_fb_img' => 'rank_math_facebook_image',
'_seopress_social_twitter_title' => 'rank_math_twitter_title',
'_seopress_social_twitter_desc' => 'rank_math_twitter_description',
'_seopress_social_twitter_img' => 'rank_math_twitter_image',
];
foreach ( $terms->get_terms() as $term_id ) {
++$count;
$this->replace_meta( $hash, [], $term_id, 'term', 'convert_variables' );
delete_term_meta( $term_id, 'rank_math_permalink' );
$this->is_twitter_using_facebook( 'term', $term_id );
$this->set_object_robots( $term_id, 'term' );
$this->set_object_redirection( $term_id, 'term' );
}
return compact( 'count' );
}
/**
* Imports redirections data.
*
* @return array
*/
protected function redirections() {
$redirections = get_posts(
[
'posts_per_page' => -1,
'post_type' => 'seopress_404',
]
);
if ( empty( $redirections ) ) {
return false;
}
$count = 0;
foreach ( $redirections as $redirection ) {
$data = [
'source' => $redirection->post_title,
'destination' => get_post_meta( $redirection->ID, '_seopress_redirections_value', true ),
'code' => get_post_meta( $redirection->ID, '_seopress_redirections_type', true ),
'status' => 'publish' === $redirection->post_status ? true : false,
];
if ( false !== $this->save_redirection( $data ) ) {
++$count;
}
}
return compact( 'count' );
}
/**
* Update Modules.
*
* @param array $seopress_local Local SEO Settings.
* @param array $seopress_sitemap Sitemap Settings.
*/
private function update_modules( $seopress_local, $seopress_sitemap ) {
$seopress_toggle = get_option( 'seopress_toggle' );
// Enable/Disable Modules.
$modules = [
'local-seo' => ! empty( $seopress_toggle['toggle-local-business'] ) ? 'on' : 'off',
'sitemap' => ! empty( $seopress_toggle['toggle-xml-sitemap'] ) && ! empty( $seopress_sitemap['seopress_xml_sitemap_general_enable'] ) ? 'on' : 'off',
'rich-snippet' => ! empty( $seopress_toggle['toggle-rich-snippets'] ) ? 'on' : 'off',
'404-monitor' => ! empty( $seopress_toggle['toggle-404'] ) && ! empty( $seopress_local['seopress_404_enable'] ) ? 'on' : 'off',
];
foreach ( $modules as $key => $value ) {
Helper::update_modules( [ $key => $value ] );
}
}
/**
* Save redirection.
*
* @param WP_Post $redirection Redirection object.
*
* @return mixed
*/
private function save_redirection( $redirection ) {
if ( empty( $redirection['source'] ) || empty( $redirection['destination'] ) ) {
return false;
}
$item = Redirection::from(
[
'sources' => [
[
'pattern' => $redirection['source'],
'comparison' => 'exact',
],
],
'url_to' => $redirection['destination'],
'header_code' => $redirection['code'],
'status' => $redirection['status'] ? 'active' : 'inactive',
]
);
return $item->save();
}
/**
* Social settings.
*/
private function social_settings() {
$social = get_option( 'seopress_social_option_name' );
$hash = [
'seopress_social_accounts_facebook' => 'social_url_facebook',
'seopress_social_facebook_link_ownership_id' => 'facebook_author_urls',
'seopress_social_facebook_img' => 'open_graph_image',
'seopress_social_facebook_admin_id' => 'facebook_admin_id',
'seopress_social_facebook_app_id' => 'facebook_app_id',
'seopress_social_accounts_twitter' => 'twitter_author_names',
'seopress_social_knowledge_name' => 'knowledgegraph_name',
'seopress_social_knowledge_img' => 'knowledgegraph_logo',
];
$this->replace( $hash, $social, $this->titles );
$additional_urls = [];
foreach ( [ 'pinterest', 'instagram', 'youtube', 'linkedin' ] as $service ) {
if ( ! empty( $social[ "seopress_social_accounts_{$service}" ] ) ) {
$additional_urls[] = $social[ "seopress_social_accounts_{$service}" ];
}
}
if ( ! empty( $additional_urls ) ) {
$this->titles['social_additional_profiles'] = implode( PHP_EOL, $additional_urls );
}
// OpenGraph.
if ( isset( $social['og_default_image'] ) ) {
$this->replace_image( $social['og_default_image'], $this->titles, 'open_graph_image', 'open_graph_image_id' );
}
if ( isset( $social['og_frontpage_image'] ) ) {
$this->replace_image( $social['og_frontpage_image'], $this->titles, 'homepage_facebook_image', 'homepage_facebook_image_id' );
}
// Phone Numbers.
if ( ! empty( $social['seopress_social_knowledge_phone'] ) ) {
$this->titles['phone_numbers'] = [
[
'type' => $social['seopress_social_knowledge_contact_type'],
'number' => $social['seopress_social_knowledge_phone'],
],
];
}
$this->titles['knowledgegraph_type'] = isset( $social['seopress_social_knowledge_type'] ) && 'organization' === strtolower( $social['seopress_social_knowledge_type'] ) ? 'company' : 'person';
}
/**
* Post type settings.
*
* @param array $seopress_titles Titles & Meta Settings.
* @param array $seopress_sitemap Sitemap Settings.
*/
private function post_type_settings( $seopress_titles, $seopress_sitemap ) {
foreach ( Helper::get_accessible_post_types() as $post_type ) {
$this->titles[ "pt_{$post_type}_title" ] = isset( $seopress_titles['seopress_titles_single_titles'][ $post_type ] ) ? $this->convert_variables( $seopress_titles['seopress_titles_single_titles'][ $post_type ]['title'] ) : '';
$this->titles[ "pt_{$post_type}_description" ] = isset( $seopress_titles['seopress_titles_single_titles'][ $post_type ] ) ? $this->convert_variables( $seopress_titles['seopress_titles_single_titles'][ $post_type ]['description'] ) : '';
$this->set_robots(
"pt_{$post_type}",
! empty( $seopress_titles['seopress_titles_single_titles'][ $post_type ]['noindex'] ),
! empty( $seopress_titles['seopress_titles_single_titles'][ $post_type ]['nofollow'] )
);
$enable_sitemap = $this->enable_sitemap( 'post_types', $post_type, $seopress_sitemap );
$this->sitemap[ "pt_{$post_type}_sitemap" ] = $enable_sitemap ? 'on' : 'off';
if ( 'attachment' === $post_type && $enable_sitemap ) {
$this->settings['attachment_redirect_urls'] = 'off';
}
}
}
/**
* Taxonomies settings.
*
* @param array $seopress_titles Titles & Meta Settings.
* @param array $seopress_sitemap Sitemap Settings.
*/
private function taxonomies_settings( $seopress_titles, $seopress_sitemap ) {
foreach ( Helper::get_accessible_taxonomies() as $taxonomy => $object ) {
$this->titles[ "tax_{$taxonomy}_title" ] = ! empty( $seopress_titles['seopress_titles_tax_titles'][ $taxonomy ]['title'] ) ? $this->convert_variables( $seopress_titles['seopress_titles_tax_titles'][ $taxonomy ]['title'] ) : '';
$this->titles[ "tax_{$taxonomy}_description" ] = ! empty( $seopress_titles['seopress_titles_tax_titles'][ $taxonomy ]['description'] ) ? $this->convert_variables( $seopress_titles['seopress_titles_tax_titles'][ $taxonomy ]['description'] ) : '';
$this->set_robots(
"tax_{$taxonomy}",
! empty( $seopress_titles['seopress_titles_tax_titles'][ $taxonomy ]['noindex'] ),
! empty( $seopress_titles['seopress_titles_tax_titles'][ $taxonomy ]['nofollow'] )
);
$this->sitemap[ "tax_{$taxonomy}_sitemap" ] = $this->enable_sitemap( 'taxonomies', $taxonomy, $seopress_sitemap ) ? 'on' : 'off';
}
}
/**
* Whether to enable sitemap.
*
* @param string $object_prefix post_types/taxonomies.
* @param string $object_type Current object type.
* @param string $seopress_sitemap Sitemap settings.
*
* @return bool
*/
private function enable_sitemap( $object_prefix, $object_type, $seopress_sitemap ) {
return ! empty( $seopress_sitemap[ "seopress_xml_sitemap_{$object_prefix}_list" ][ $object_type ]['include'] );
}
/**
* Set robots settings.
*
* @param string $prefix Setting key prefix.
* @param bool $noindex Is noindex.
* @param bool $nofollow Is nofollow.
*/
private function set_robots( $prefix, $noindex, $nofollow ) {
if ( $noindex || $nofollow ) {
$robots = "{$prefix}_robots";
$custom = "{$prefix}_custom_robots";
// Settings.
$this->titles[ $custom ] = 'on';
$this->titles[ $robots ][] = $noindex ? 'noindex' : '';
$this->titles[ $robots ][] = $nofollow ? 'nofollow' : '';
$this->titles[ $robots ] = array_unique( $this->titles[ $robots ] );
}
}
/**
* Set Advanced settings.
*/
private function advanced_settings() {
$seopress_advanced = get_option( 'seopress_advanced_option_name' );
$hash = [
'seopress_advanced_advanced_google' => 'google_verify',
'seopress_advanced_advanced_bing' => 'bing_verify',
'seopress_advanced_advanced_yandex' => 'yandex_verify',
'seopress_advanced_advanced_pinterest' => 'pinterest_verify',
];
$this->replace( $hash, $seopress_advanced, $this->settings );
$this->replace( $hash, $seopress_advanced, $this->settings, 'convert_bool' );
$this->settings['attachment_redirect_urls'] = ! empty( $seopress_advanced['seopress_advanced_advanced_attachments'] ) ? 'on' : 'off';
$this->settings['strip_category_base'] = ! empty( $seopress_advanced['seopress_advanced_advanced_category_url'] ) ? 'on' : 'off';
$set_alt = ! empty( $seopress_advanced['seopress_advanced_advanced_image_auto_alt_editor'] ) ? 'on' : 'off';
$set_title = ! empty( $seopress_advanced['seopress_advanced_advanced_image_auto_title_editor'] ) ? 'on' : 'off';
$this->settings['add_img_alt'] = $set_alt;
$this->settings['add_img_title'] = $set_title;
$this->settings['img_alt_format'] = 'on' === $set_alt ? ' %filename%' : '';
$this->settings['img_title_format'] = 'on' === $set_title ? ' %filename%' : '';
}
/**
* Local SEO settings.
*
* @param array $seopress_local Local SEOPress data.
*/
private function local_seo_settings( $seopress_local ) {
if ( empty( $seopress_local ) ) {
return;
}
// Breadcrumbs.
$hash = [
'seopress_breadcrumbs_i18n_home' => 'breadcrumbs_home_label',
'seopress_breadcrumbs_i18n_search' => 'breadcrumbs_search_format',
'seopress_breadcrumbs_i18n_404' => 'breadcrumbs_404_label',
'seopress_breadcrumbs_enable' => 'breadcrumbs',
];
$this->replace( $hash, $seopress_local, $this->settings );
$this->replace( $hash, $seopress_local, $this->settings, 'convert_bool' );
$this->settings['breadcrumbs_separator'] = \RankMath\CMB2::sanitize_htmlentities( $seopress_local['seopress_breadcrumbs_separator'] );
$hash = [
'seopress_local_business_type' => 'local_business_type',
'seopress_local_business_price_range' => 'price_range',
'seopress_local_business_url' => 'url',
];
$this->replace( $hash, $seopress_local, $this->titles, 'convert_variables' );
// Street Address.
$address = [];
$hash = [
'seopress_local_business_street_address' => 'streetAddress',
'seopress_local_business_address_locality' => 'addressLocality',
'seopress_local_business_address_region' => 'addressRegion',
'seopress_local_business_postal_code' => 'postalCode',
'seopress_local_business_address_country' => 'addressCountry',
];
$this->replace( $hash, $seopress_local, $address );
$this->titles['local_address'] = $address;
// Coordinates.
if ( ! empty( $seopress_local['seopress_local_business_lat'] ) && ! empty( $seopress_local['seopress_local_business_lon'] ) ) {
$this->titles['geo'] = $seopress_local['seopress_local_business_lat'] . ', ' . $seopress_local['seopress_local_business_lon'];
}
$this->seopress_pro_settings( $seopress_local );
$this->seopress_set_opening_hours( $seopress_local );
}
/**
* 404 settings.
*
* @param array $seopress_local SEOPress Pro Settings.
*/
private function seopress_pro_settings( $seopress_local ) {
Helper::update_modules( [ 'redirections' => 'on' ] );
$hash = [
'seopress_rss_before_html' => 'rss_before_content',
'seopress_rss_after_html' => 'rss_after_content',
'seopress_404_redirect_custom_url' => 'redirections_custom_url',
'seopress_404_redirect_status_code' => 'redirections_header_code',
];
$this->replace( $hash, $seopress_local, $this->settings );
$this->settings['redirections_fallback'] = 'none' === $seopress_local['seopress_404_redirect_home'] ? 'default' : ( 'home' === $seopress_local['seopress_404_redirect_home'] ? 'homepage' : 'custom' );
}
/**
* Set Opening Hours.
*
* @param array $seopress_local SEOPress Pro Settings.
*/
private function seopress_set_opening_hours( $seopress_local ) {
$hash = [ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' ];
$data = [];
foreach ( $seopress_local['seopress_local_business_opening_hours'] as $key => $opening_hour ) {
if ( isset( $opening_hour['open'] ) ) {
continue;
}
$data[] = [
'day' => $hash[ $key ],
'time' => $opening_hour['start']['hours'] . ':' . $opening_hour['start']['mins'] . '-' . $opening_hour['end']['hours'] . ':' . $opening_hour['end']['mins'],
];
}
$this->titles['opening_hours'] = $data;
}
/**
* Set schema data.
*
* @param int $post_id Post ID.
*/
private function set_schema_data( $post_id ) {
if ( ! $type = get_post_meta( $post_id, '_seopress_pro_rich_snippets_type', true ) ) { // phpcs:ignore
return;
}
if ( $meta_keys = $this->get_schema_metakeys( $type ) ) { // phpcs:ignore
$schema_type = 's' === substr( $type, -1 ) ? substr( $type, 0, -1 ) : $type;
update_post_meta( $post_id, 'rank_math_rich_snippet', $schema_type );
foreach ( $meta_keys as $meta_key => $data ) {
$value = $this->get_snippet_value( $post_id, $meta_key );
if ( $value && 'events_location_address' === $meta_key ) {
$value = [ 'streetAddress' => $value ];
}
update_post_meta( $post_id, "rank_math_snippet_{$data}", $value );
}
// Convert post now.
$data = $this->json_ld->get_old_schema( $post_id, $this->single );
if ( isset( $data['richSnippet'] ) ) {
$data = $data['richSnippet'];
$type = $data['@type'];
$data['metadata'] = [
'title' => $type,
'type' => 'template',
];
update_post_meta( $post_id, 'rank_math_schema_' . $type, $data );
}
}
}
/**
* Set object redirection.
*
* @param int $object_id Object id for destination.
* @param string $object_type Object type for destination.
*/
private function set_object_redirection( $object_id, $object_type ) {
$source_url = 'term' === $object_type ? get_term_link( $object_id ) : get_permalink( $object_id );
if ( is_wp_error( $source_url ) ) { // phpcs:ignore
return;
}
$hash = [
'_seopress_redirections_type' => 'redirection_header_code',
'_seopress_redirections_value' => 'redirection_url_to',
];
$this->replace_meta( $hash, null, $object_id, $object_type, 'convert_variables' );
$redirection = [
'source' => trim( wp_parse_url( $source_url, PHP_URL_PATH ), '/' ),
'destination' => $this->get_meta( $object_type, $object_id, '_seopress_redirections_value' ),
'code' => $this->get_meta( $object_type, $object_id, '_seopress_redirections_type' ),
'status' => $this->get_meta( $object_type, $object_id, '_seopress_redirections_enabled' ),
];
$this->save_redirection( $redirection );
}
/**
* Get snippet value.
*
* @param int $post_id Post ID.
* @param int $meta_key Meta key.
*
* @return string $value Snippet value
*/
private function get_snippet_value( $post_id, $meta_key ) {
$prefix = in_array( $meta_key, [ 'events_offers_valid_from_date', 'events_offers_valid_from_time' ], true ) ? '_seopress_rich_snippets_' : '_seopress_pro_rich_snippets_';
$value = get_post_meta( $post_id, $prefix . $meta_key, true );
if ( in_array( $meta_key, [ 'recipes_prep_time', 'recipes_cook_time', 'videos_duration' ], true ) ) {
$value .= 'M';
}
$hash = [
'events_start_date' => 'events_start_time',
'events_end_date' => 'events_end_time',
'events_offers_valid_from_date' => 'events_offers_valid_from_time',
];
if ( isset( $hash[ $meta_key ] ) ) {
$time = get_post_meta( $post_id, $prefix . $hash[ $meta_key ], true );
$value = strtotime( $value . ' ' . $time );
}
return $value;
}
/**
* Get schema meta keys.
*
* @param string $type Type of snippet.
*
* @return array
*/
private function get_schema_metakeys( $type ) {
$hash = [
'articles' => [
'article_type' => 'article_type',
'article_title' => 'name',
],
'recipes' => [
'recipes_name' => 'name',
'recipes_desc' => 'desc',
'recipes_cat' => 'recipe_type',
'recipes_prep_time' => 'recipe_preptime',
'recipes_cook_time' => 'recipe_cooktime',
'recipes_calories' => 'recipe_calories',
'recipes_yield' => 'recipe_yield',
'recipes_ingredient' => 'recipe_ingredients',
],
'courses' => [
'courses_title' => 'name',
'courses_desc' => 'desc',
'courses_school' => 'course_provider',
'courses_website' => 'course_provider_url',
],
'videos' => [
'videos_name' => 'name',
'videos_description' => 'desc',
'videos_img' => 'rank_math_twitter_title',
'videos_url' => 'video_url',
'videos_duration' => 'video_duration',
],
'events' => [
'events_type' => 'event_type',
'events_name' => 'name',
'events_desc' => 'desc',
'events_location_address' => 'event_address',
'events_location_name' => 'event_venue',
'events_location_url' => 'event_venue_url',
'events_start_date' => 'event_startdate',
'events_end_date' => 'event_enddate',
'events_offers_price' => 'event_price',
'events_offers_price_currency' => 'event_currency',
'events_offers_url' => 'event_ticketurl',
'events_offers_availability' => 'event_availability',
'events_offers_valid_from_date' => 'event_availability_starts',
'events_performer' => 'event_performer',
],
'products' => [
'product_description' => 'desc',
'product_name' => 'name',
'product_price_currency' => 'product_currency',
'product_price' => 'product_price',
],
'review' => [
'review_item' => 'name',
'item_name' => 'desc',
'review_rating' => 'review_rating_value',
],
];
return isset( $hash[ $type ] ) ? $hash[ $type ] : false;
}
/**
* Set primary term for post
*
* @param int[] $post_ids Post IDs.
*/
private function set_primary_term( $post_ids ) {
$post_ids = wp_list_pluck( $post_ids, 'ID' );
$table = DB::query_builder( 'postmeta' );
$results = $table->whereLike( 'meta_key', '_seopress_robots_primary_cat' )->whereIn( 'post_id', $post_ids )->get();
foreach ( $results as $result ) {
if ( 'none' !== $result->meta_value ) {
update_post_meta( $result->post_id, 'rank_math_primary_category', $result->meta_value );
}
}
}
/**
* Set post/term robots.
*
* @param int $object_id Object id.
* @param string $object_type Object type.
*/
private function set_object_robots( $object_id, $object_type ) {
// Early bail if robots data is set in Rank Math plugin.
if ( ! empty( $this->get_meta( $object_type, $object_id, 'rank_math_robots' ) ) ) {
return;
}
$current = $this->get_robots_by_hash( $object_id, $object_type );
$is_noindex = in_array( 'noindex', $current, true );
$is_nofollow = in_array( 'nofollow', $current, true );
if ( ! $is_noindex || ! $is_nofollow ) {
$robots = $this->get_default_robots( $object_id, $object_type );
$current[] = ! $is_nofollow && ! empty( $robots['nofollow'] ) ? 'nofollow' : '';
// Keep global no index status.
if ( ! empty( $robots['noindex'] ) ) {
unset( $current['index'] );
$current[] = 'noindex';
}
}
$this->update_meta( $object_type, $object_id, 'rank_math_robots', array_unique( $current ) );
}
/**
* Get by meta hash.
*
* @param int $object_id Object id.
* @param string $object_type Object type.
*
* @return array Array of robots data.
*/
private function get_robots_by_hash( $object_id, $object_type ) {
$current = [];
$hash = [
'_seopress_robots_index' => 'noindex',
'_seopress_robots_follow' => 'nofollow',
'_seopress_robots_imageindex' => 'noimageindex',
'_seopress_robots_archive' => 'noarchive',
'_seopress_robots_snippet' => 'nosnippet',
];
foreach ( $hash as $source => $value ) {
if ( ! empty( $this->get_meta( $object_type, $object_id, $source ) ) ) {
$current[] = $value;
}
}
return $current;
}
/**
* Get default robots data from settings.
*
* @param int $object_id Object id.
* @param string $object_type Object type.
*
* @return array Array of robots data.
*/
private function get_default_robots( $object_id, $object_type ) {
$seopress_titles = get_option( 'seopress_titles_option_name' );
if ( 'post' === $object_type ) {
$post_type = get_post_type( $object_id );
return isset( $seopress_titles['seopress_titles_single_titles'][ $post_type ] ) ? $seopress_titles['seopress_titles_single_titles'][ $post_type ] : [];
}
$term = get_term( $object_id );
return isset( $seopress_titles['seopress_titles_tax_titles'][ $term->taxonomy ] ) ? $seopress_titles['seopress_titles_tax_titles'][ $term->taxonomy ] : [];
}
}

View File

@@ -0,0 +1,137 @@
<?php
/**
* The Status.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin\Importers
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin\Importers;
defined( 'ABSPATH' ) || exit;
/**
* Status class.
*/
class Status {
/**
* The status.
*
* @var bool
*/
private $status = false;
/**
* The message.
*
* @var string
*/
private $message = '';
/**
* The action being performed.
*
* @var string
*/
private $action;
/**
* Get the status.
*
* @return bool Status.
*/
public function is_success() {
return $this->status;
}
/**
* Get the message.
*
* @return string Status message.
*/
public function get_message() {
if ( '' === $this->message ) {
return $this->get_default_message();
}
return $this->message;
}
/**
* Get the action.
*
* @return string Action type.
*/
public function get_action() {
return $this->action;
}
/**
* Set the status.
*
* @param string $status Status.
*/
public function set_status( $status ) {
$this->status = $status;
}
/**
* Set the message.
*
* @param string $message Status message.
*/
public function set_message( $message ) {
$this->message = $message;
}
/**
* Set the action.
*
* @param string $action Action performing.
*/
public function set_action( $action ) {
$this->action = $action;
}
/**
* Get default message.
*
* @return string
*/
private function get_default_message() {
$hash = [
'settings' => esc_html__( 'Settings imported successfully.', 'rank-math' ),
'news' => esc_html__( 'News Settings imported successfully.', 'rank-math' ),
'video' => esc_html__( 'Video Settings imported successfully.', 'rank-math' ),
'deactivate' => esc_html__( 'Plugin deactivated successfully.', 'rank-math' ),
/* translators: start, end, total */
'postmeta' => esc_html__( 'Imported post meta for posts %1$s - %2$s out of %3$s ', 'rank-math' ),
/* translators: total */
'termmeta' => esc_html__( 'Imported term meta for %s terms.', 'rank-math' ),
/* translators: start, end, total */
'usermeta' => esc_html__( 'Imported user meta for users %1$s - %2$s out of %3$s ', 'rank-math' ),
/* translators: total */
'redirections' => esc_html__( 'Imported %s redirections.', 'rank-math' ),
/* translators: start, end, total */
'blocks' => esc_html__( 'Imported blocks from posts %1$s - %2$s out of %3$s ', 'rank-math' ),
/* translators: start, end, total */
'recalculate' => esc_html__( 'Recalculating scores for posts %1$s - %2$s... ', 'rank-math' ),
];
if ( false === $this->is_success() ) {
$hash = [
'settings' => esc_html__( 'Settings import failed.', 'rank-math' ),
'postmeta' => esc_html__( 'Posts meta import failed.', 'rank-math' ),
'termmeta' => esc_html__( 'Term meta import failed.', 'rank-math' ),
'usermeta' => esc_html__( 'User meta import failed.', 'rank-math' ),
'redirections' => esc_html__( 'There are no redirection to import.', 'rank-math' ),
'blocks' => esc_html__( 'Blocks import failed.', 'rank-math' ),
];
}
return isset( $hash[ $this->get_action() ] ) ? $hash[ $this->get_action() ] : '';
}
}

View File

@@ -0,0 +1,797 @@
<?php
/**
* The WP Schema Pro Import Class
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin\Importers
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin\Importers;
use RankMath\Helper;
use RankMath\Helpers\DB as DB_Helper;
use RankMath\Admin\Admin_Helper;
use RankMath\Helpers\Str;
defined( 'ABSPATH' ) || exit;
/**
* WP_Schema_Pro class.
*/
class WP_Schema_Pro extends Plugin_Importer {
/**
* The plugin name.
*
* @var string
*/
protected $plugin_name = 'WP Schema Pro';
/**
* Plugin options meta key.
*
* @var string
*/
protected $meta_key = 'bsf-aiosrs';
/**
* Option keys to import and clean.
*
* @var array
*/
protected $option_keys = [ 'wp-schema-pro-general-settings', 'wp-schema-pro-social-profiles', 'wp-schema-pro-global-schemas' ];
/**
* Choices keys to import.
*
* @var array
*/
protected $choices = [ 'settings', 'postmeta' ];
/**
* Convert Schema Pro variables if needed.
*
* @param string $value Value to convert.
*
* @return string
*/
public function convert_variables( $value ) {
$value = str_replace( 'blogname', '%sitename%', $value );
$value = str_replace( 'blogdescription', '%sitedesc%', $value );
$value = str_replace( 'site_url', get_bloginfo( 'url' ), $value );
$value = str_replace( 'site_logo', get_theme_mod( 'custom_logo' ), $value );
$value = str_replace( 'featured_image', '', $value );
$value = str_replace( 'featured_img', '', $value );
$value = str_replace( 'post_title', '%seo_title%', $value );
$value = str_replace( 'post_excerpt', '%seo_description%', $value );
$value = str_replace( 'post_content', '%seo_description%', $value );
$value = str_replace( 'post_date', '%date%', $value );
$value = str_replace( 'post_modified', '%modified%', $value );
$value = str_replace( 'post_permalink', '', $value );
$value = str_replace( 'author_name', '%name%', $value );
$value = str_replace( 'author_first_name', '%name%', $value );
$value = str_replace( 'author_last_name', '%name%', $value );
$value = str_replace( 'author_image', '', $value );
return $value;
}
/**
* Import settings of plugin.
*
* @return bool
*/
protected function settings() {
$this->get_settings();
$schema_general = get_option( 'wp-schema-pro-general-settings' );
$schema_social = get_option( 'wp-schema-pro-social-profiles' );
$schema_global = get_option( 'wp-schema-pro-global-schemas' );
// Knowledge Graph Logo.
if ( isset( $schema_general['site-logo-custom'] ) ) {
$this->replace_image( $schema_general['site-logo-custom'], $this->titles, 'knowledgegraph_logo', 'knowledgegraph_logo_id' );
}
// General.
$hash = [ 'site-represent' => 'knowledgegraph_type' ];
$has_key = 'person' === $schema_general['site-represent'] ? 'person-name' : 'site-name';
$hash[ $has_key ] = 'knowledgegraph_name';
$this->replace( $hash, $schema_general, $this->titles );
$this->titles['local_seo'] = isset( $schema_general['site-represent'] ) && ! empty( $yoast_titles['site-represent'] ) ? 'on' : 'off';
// Social.
$hash = [
'facebook' => 'social_url_facebook',
'twitter' => 'twitter_author_names',
];
$this->replace( $hash, $schema_social, $this->titles );
// About & Contact Page.
$hash = [
'about-page' => 'local_seo_about_page',
'contact-page' => 'local_seo_contact_page',
];
$this->replace( $hash, $schema_global, $this->titles );
$this->update_settings();
return true;
}
/**
* Import post meta of plugin.
*
* @return array
*/
protected function postmeta() {
$this->set_pagination( $this->get_post_ids( true ) );
foreach ( $this->get_post_ids() as $snippet_post ) {
$post_id = $snippet_post->ID;
$snippet = $this->get_snippet_details( $post_id );
if ( ! $snippet ) {
continue;
}
$this->update_postmeta( $post_id, $snippet );
}
return $this->get_pagination_arg();
}
/**
* Update post meta.
*
* @param int $post_id Post ID.
* @param array $snippet Snippet data.
*/
private function update_postmeta( $post_id, $snippet ) {
$type = $snippet['type'];
$hash = $this->get_schema_types();
if ( ! isset( $hash[ $type ] ) ) {
return;
}
$details = $snippet['details'];
$methods = [
'work-example' => 'get_book_editions',
'steps' => 'get_howto_steps',
'tool' => 'get_howto_tools',
'supply' => 'get_howto_supplies',
'rating' => 'get_rating',
];
$data = [];
foreach ( $hash[ $type ] as $snippet_key => $snippet_value ) {
$method = isset( $methods[ $snippet_key ] ) ? $methods[ $snippet_key ] : 'get_schema_meta';
$value = $this->$method( $details, $snippet_key, $post_id, $snippet, $snippet_value );
$this->validate_schema_data( $data, $value, $snippet_value, $snippet_key );
}
if ( ! empty( $data ) ) {
if ( isset( $data['schema-type'] ) ) {
$type = $data['schema-type'];
unset( $data['schema-type'] );
}
$type = $this->sanitize_schema_type( $type );
$data['@type'] = $type;
$data['metadata'] = [
'title' => Helper::sanitize_schema_title( $type ),
'type' => 'template',
'isPrimary' => 1,
'shortcode' => uniqid( 's-' ),
];
$type = in_array( $type, [ 'BlogPosting', 'NewsArticle' ], true ) ? 'Article' : $type;
update_post_meta( $post_id, 'rank_math_schema_' . $type, $data );
}
}
/**
* Validate schema data.
*
* @param array $data Schema entity data.
* @param string $value Entity value.
* @param string $key Entity key.
* @param string $snippet_key Snippet key.
*/
private function validate_schema_data( &$data, $value, $key, $snippet_key ) {
if ( 'question-answer' === $snippet_key && ! empty( $value ) ) {
foreach ( $value as $question ) {
$data[ $key ][] = [
'@type' => 'Question',
'name' => $question['question'],
'acceptedAnswer' => [
'@type' => 'Answer',
'text' => $question['answer'],
],
];
}
return;
}
if ( ! Str::contains( '.', $key ) ) {
$data[ $key ] = $value;
return;
}
$element = explode( '.', $key );
if ( 2 === count( $element ) ) {
$this->add_type( $data[ $element[0] ], $element[0] );
$data[ $element[0] ][ $element[1] ] = $value;
return;
}
if ( count( $element ) > 2 ) {
$this->add_type( $data[ $element[0] ], $element[0] );
$this->add_type( $data[ $element[0] ][ $element[1] ], $element[1] );
$data[ $element[0] ][ $element[1] ][ $element[2] ] = $value;
}
}
/**
* Add property type.
*
* @param array $data Schema entity data.
* @param string $key Entity key.
*/
private function add_type( &$data, $key ) {
if ( 'location' === $key || 'jobLocation' === $key ) {
$data['@type'] = 'Place';
}
if ( 'address' === $key ) {
$data['@type'] = 'PostalAddress';
}
if ( 'offers' === $key ) {
$data['@type'] = 'Offer';
}
if ( 'brand' === $key ) {
$data['@type'] = 'Brand';
}
if ( 'review' === $key ) {
$data['@type'] = 'Review';
}
if ( 'reviewRating' === $key ) {
$data['@type'] = 'Rating';
}
if ( 'nutrition' === $key ) {
$data['@type'] = 'NutritionInformation';
}
if ( 'baseSalary' === $key ) {
$data['@type'] = 'MonetaryAmount';
}
if ( 'value' === $key ) {
$data['@type'] = 'QuantitativeValue';
}
if ( 'performer' === $key ) {
$data['@type'] = 'Person';
}
if ( 'provider' === $key || 'hiringOrganization' === $key ) {
$data['@type'] = 'Organization';
}
}
/**
* Get ratings value.
*
* @param array $details Array of details.
* @param string $snippet_key Snippet key.
* @param string $post_id Post ID.
* @param array $snippet Snippet data.
* @param string $snippet_value Snippet value.
* @return string
*/
private function get_rating( $details, $snippet_key, $post_id, $snippet, $snippet_value ) {
return get_post_meta( $post_id, 'bsf-schema-pro-rating-' . $snippet['id'], true );
}
/**
* Get post meta for schema plugin
*
* @param array $details Array of details.
* @param string $snippet_key Snippet key.
* @param string $post_id Post ID.
* @param array $snippet Snippet data.
* @param string $snippet_value Snippet value.
* @return string
*/
private function get_schema_meta( $details, $snippet_key, $post_id, $snippet, $snippet_value ) {
$value = isset( $details[ $snippet_key ] ) ? $details[ $snippet_key ] : '';
if ( 'custom-text' === $value ) {
$value = isset( $details[ $snippet_key . '-custom-text' ] ) ? $details[ $snippet_key . '-custom-text' ] : '';
}
if ( 'create-field' === $value ) {
$value = get_post_meta( $post_id, $snippet['type'] . '-' . $snippet['id'] . '-' . $snippet_key, true );
}
if ( 'specific-field' === $value ) {
$key = isset( $details[ $snippet_key . '-specific-field' ] ) ? $details[ $snippet_key . '-specific-field' ] : '';
$value = get_post_meta( $post_id, $key, true );
}
return $this->convert_variables( $value );
}
/**
* Get Book Editions.
*
* @param array $details Array of details.
* @param string $snippet_key Snippet key.
* @param string $post_id Post ID.
* @param array $snippet Snippet data.
* @param string $snippet_value Snippet value.
* @return string
*/
private function get_howto_steps( $details, $snippet_key, $post_id, $snippet, $snippet_value ) {
$steps = get_post_meta( $post_id, "how-to-{$snippet['id']}-steps", true );
if ( empty( $steps ) ) {
return [];
}
$data = [];
foreach ( $steps as $step ) {
$entity = [
'@type' => 'HowToStep',
'name' => $step['name'],
'url' => $step['url'],
];
if ( ! empty( $step['description'] ) ) {
$entity['itemListElement'] = [
'@type' => 'HowToDirection',
'text' => $step['description'],
];
}
if ( ! empty( $step['image'] ) ) {
$entity['image'] = [
'@type' => 'ImageObject',
'text' => wp_get_attachment_url( $step['image'] ),
];
}
$data[] = $entity;
}
return $data;
}
/**
* Get Book Editions.
*
* @param array $details Array of details.
* @param string $snippet_key Snippet key.
* @param string $post_id Post ID.
* @param array $snippet Snippet data.
* @param string $snippet_value Snippet value.
* @return string
*/
private function get_howto_tools( $details, $snippet_key, $post_id, $snippet, $snippet_value ) {
$tools = get_post_meta( $post_id, "how-to-{$snippet['id']}-tool", true );
if ( empty( $tools ) ) {
return [];
}
$data = [];
foreach ( $tools as $tool ) {
$data[] = [
'@type' => 'HowToTool',
'name' => $tool['name'],
];
}
return $data;
}
/**
* Get Book Editions.
*
* @param array $details Array of details.
* @param string $snippet_key Snippet key.
* @param string $post_id Post ID.
* @param array $snippet Snippet data.
* @param string $snippet_value Snippet value.
* @return string
*/
private function get_howto_supplies( $details, $snippet_key, $post_id, $snippet, $snippet_value ) {
$supplies = get_post_meta( $post_id, "how-to-{$snippet['id']}-supply", true );
if ( empty( $supplies ) ) {
return [];
}
$data = [];
foreach ( $supplies as $supply ) {
$data[] = [
'@type' => 'HowToSupply',
'name' => $supply['name'],
];
}
return $data;
}
/**
* Get Book Editions.
*
* @param array $details Array of details.
* @param string $snippet_key Snippet key.
* @param string $post_id Post ID.
* @param array $snippet Snippet data.
* @param string $snippet_value Snippet value.
* @return string
*/
private function get_book_editions( $details, $snippet_key, $post_id, $snippet, $snippet_value ) {
if ( empty( $details[ $snippet_key ] ) ) {
return '';
}
$editions = [];
$data = [
'details' => $details,
'snippet_key' => $snippet_key,
'post_id' => $post_id,
'snippet' => $snippet,
'snippet_value' => $snippet_value,
];
foreach ( $details[ $snippet_key ] as $key => $edition ) {
$editions[] = [
'book_edition' => $this->normalize_edition( $key . '-book-edition', $edition['book-edition'], $data ),
'isbn' => $this->normalize_edition( $key . '-serial-number', $edition['serial-number'], $data ),
'url' => $this->normalize_edition( $key . '-url-template', $edition['url-template'], $data ),
'book_format' => $this->normalize_edition( $key . '-book-format', $edition['book-format'], $data ),
];
}
return $editions;
}
/**
* Normalize Book Edition.
*
* @param string $key Custom field key.
* @param string $value Custom field value.
* @param array $data Snippet data.
* @return string
*/
private function normalize_edition( $key, $value, $data ) {
if ( ! $value ) {
return '';
}
$hash = [
'custom-text' => 'get_custom_text',
'create-field' => 'get_created_field',
'specific-field' => 'get_specific_field',
];
if ( isset( $hash[ $value ] ) ) {
$method = $hash[ $value ];
$value = $this->$method( $key, $value, $data );
}
return $this->convert_variables( $value );
}
/**
* Get Custom Text added in the Settings.
*
* @param string $key Custom field key.
* @param string $value Custom field value.
* @param array $data Snippet data.
* @return string
*/
private function get_custom_text( $key, $value, $data ) {
$key = $data['snippet_key'] . '-custom-text';
return isset( $data['details'][ $key ] ) ? $data['details'][ $key ] : '';
}
/**
* Get Created field value added in the post metabox.
*
* @param string $key Custom field key.
* @param string $value Custom field value.
* @param array $data Snippet data.
* @return string
*/
private function get_created_field( $key, $value, $data ) {
$meta_key = $data['snippet']['type'] . '-' . $data['snippet']['id'] . '-' . $data['snippet_key'] . '-' . $key;
return get_post_meta( $data['post_id'], $meta_key, true );
}
/**
* Get Specific Custom field value.
*
* @param string $key Custom field key.
* @param string $value Custom field value.
* @param array $data Snippet data.
* @return string
*/
private function get_specific_field( $key, $value, $data ) {
$key = isset( $data['details'][ $data[ $key . '-specific-field' ] ] ) ? $data['details'][ $data[ $key . '-specific-field' ] ] : '';
return get_post_meta( $data['post_id'], $key, true );
}
/**
* Sanitize schema type before saving
*
* @param string $type Schema type to sanitize.
* @return string
*/
private function sanitize_schema_type( $type ) {
$hash = [
'job-posting' => 'JobPosting',
'video-object' => 'VideoObject',
'software-application' => 'SoftwareApplication',
'faq' => 'FAQPage',
'how-to' => 'HowTo',
];
$type = in_array( $type, [ 'AdvertiserContentArticle', 'Report', 'SatiricalArticle', 'ScholarlyArticle', 'TechArticle' ], true )
? 'Article'
: $type;
return isset( $hash[ $type ] ) ? $hash[ $type ] : ucfirst( $type );
}
/**
* Get Snippet Details stored in aiosrs-schema posts
*
* @param int $post_id Post ID.
* @return array
*/
private function get_snippet_details( $post_id ) {
global $wpdb;
$post_type = addcslashes( get_post_type( $post_id ), '_' );
$query = "SELECT p.ID, pm.meta_value FROM {$wpdb->postmeta} as pm
INNER JOIN {$wpdb->posts} as p ON pm.post_id = p.ID
WHERE pm.meta_key = 'bsf-aiosrs-schema-location'
AND p.post_type = 'aiosrs-schema'
AND p.post_status = 'publish'";
$orderby = ' ORDER BY p.post_date DESC LIMIT 1';
$meta_args = "pm.meta_value LIKE '%\"basic-global\"%'";
$meta_args .= " OR pm.meta_value LIKE '%\"basic-singulars\"%'";
$meta_args .= " OR pm.meta_value LIKE '%\"{$post_type}|all\"%'";
$meta_args .= " OR pm.meta_value LIKE '%\"post-{$post_id}\"%'";
$local_posts = DB_Helper::get_col( $query . ' AND (' . $meta_args . ')' . $orderby );
if ( empty( $local_posts ) ) {
return false;
}
$current_page_data = [];
foreach ( $local_posts as $local_post ) {
$snippet_type = get_post_meta( $local_post, 'bsf-aiosrs-schema-type', true );
return [
'id' => $local_post,
'type' => $snippet_type,
'details' => get_post_meta( $local_post, 'bsf-aiosrs-' . $snippet_type, true ),
];
}
return false;
}
/**
* Get the actions which can be performed for the plugin.
*
* @return array
*/
public function get_choices() {
return [
'settings' => esc_html__( 'Import Settings', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Plugin settings and site-wide meta data.', 'rank-math' ) ),
'postmeta' => esc_html__( 'Import Schemas', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Import all Schema data for Posts, Pages, and custom post types.', 'rank-math' ) ),
];
}
/**
* Get schema types
*
* @return array
*/
private function get_schema_types() {
return [
'event' => $this->get_event_fields(),
'job-posting' => $this->get_job_posting_fields(),
'product' => $this->get_product_fields(),
'recipe' => $this->get_recipe_fields(),
'software-application' => $this->get_software_fields(),
'video-object' => $this->get_video_fields(),
'article' => [
'name' => 'headline',
'description' => 'description',
'schema-type' => 'schema-type',
],
'book' => [
'name' => 'name',
'url' => 'url',
'author' => 'author.name',
'work-example' => 'book_editions',
'rating' => 'review.reviewRating.ratingValue',
],
'course' => [
'name' => 'name',
'description' => 'description',
'orgnization-name' => 'provider.name',
'same-as' => 'provider.sameAs',
'rating' => 'review.reviewRating.ratingValue',
],
'person' => [
'name' => 'name',
'email' => 'email',
'gender' => 'gender',
'job-title' => 'jobTitle',
'street' => 'address.streetAddress',
'locality' => 'address.addressLocality',
'region' => 'address.addressRegion',
'postal' => 'address.postalCode',
'country' => 'address.addressCountry',
],
'service' => [
'name' => 'name',
'description' => 'description',
'type' => 'serviceType',
'price-range' => 'offers.price',
],
'faq' => [
'question-answer' => 'mainEntity',
],
'how-to' => [
'name' => 'name',
'description' => 'description',
'total-time' => 'totalTime',
'steps' => 'step',
'supply' => 'supply',
'tool' => 'tool',
],
];
}
/**
* Get event fields.
*
* @return array
*/
private function get_event_fields() {
return [
'name' => 'name',
'description' => 'description',
'schema-type' => 'schema-type',
'event-status' => 'eventStatus',
'event-attendance-mode' => 'eventAttendanceMode',
'start-date' => 'startDate',
'end-date' => 'endDate',
'location' => 'location.name',
'location-street' => 'location.address.streetAddress',
'location-locality' => 'location.address.addressLocality',
'location-region' => 'location.address.addressRegion',
'location-postal' => 'location.address.postalCode',
'location-country' => 'location.address.addressCountry',
'ticket-buy-url' => 'offers.url',
'price' => 'offers.price',
'currency' => 'offers.priceCurrency',
'avail' => 'offers.availability',
'performer' => 'performer.name',
'rating' => 'review.reviewRating.ratingValue',
];
}
/**
* Get job_posting fields.
*
* @return array
*/
private function get_job_posting_fields() {
return [
'title' => 'title',
'description' => 'description',
'job-type' => 'employmentType',
'start-date' => 'datePosted',
'expiry-date' => 'validThrough',
'orgnization-name' => 'hiringOrganization.name',
'same-as' => 'hiringOrganization.sameAs',
'organization-logo' => 'hiringOrganization.logo',
'location-street' => 'jobLocation.address.streetAddress',
'location-locality' => 'jobLocation.address.addressLocality',
'location-region' => 'jobLocation.address.addressRegion',
'location-postal' => 'jobLocation.address.postalCode',
'location-country' => 'jobLocation.address.addressCountry',
'salary' => 'baseSalary.value.value',
'salary-currency' => 'baseSalary.currency',
'salary-unit' => 'baseSalary.value.unitText',
];
}
/**
* Get product fields.
*
* @return array
*/
private function get_product_fields() {
return [
'name' => 'name',
'description' => 'description',
'sku' => 'sku',
'brand-name' => 'brand.name',
'price' => 'offers.price',
'currency' => 'offers.priceCurrency',
'avail' => 'offers.availability',
'price-valid-until' => 'offers.priceValidUntil',
'rating' => 'review.reviewRating.ratingValue',
];
}
/**
* Get recipe fields.
*
* @return array
*/
private function get_recipe_fields() {
return [
'name' => 'name',
'description' => 'description',
'recipe-category' => 'recipeCategory',
'recipe-cuisine' => 'recipeCuisine',
'recipe-yield' => 'recipeYield',
'recipe-keywords' => 'keywords',
'nutrition' => 'nutrition.calories',
'preperation-time' => 'prepTime',
'cook-time' => 'cookTime',
'ingredients' => 'recipeIngredient',
'rating' => 'review.reviewRating.ratingValue',
];
}
/**
* Get software fields.
*
* @return array
*/
private function get_software_fields() {
return [
'name' => 'name',
'rating' => 'review.reviewRating.ratingValue',
'price' => 'offers.price',
'currency' => 'offers.priceCurrency',
'operating-system' => 'operatingSystem',
'category' => 'applicationCategory',
];
}
/**
* Get video fields.
*
* @return array
*/
private function get_video_fields() {
return [
'name' => 'name',
'description' => 'description',
'content-url' => 'contentUrl',
'embed-url' => 'embedUrl',
'duration' => 'duration',
'rating' => 'review.reviewRating.ratingValue',
];
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
<?php // Silence is golden.

View File

@@ -0,0 +1 @@
<?php // Silence is golden.

View File

@@ -0,0 +1,380 @@
<?php
/**
* The metabox functionality of the plugin.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin\Metabox
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin\Metabox;
use RankMath\CMB2;
use RankMath\Runner;
use RankMath\Traits\Hooker;
use RankMath\Helper;
use RankMath\Helpers\Param;
use RankMath\Admin\Admin_Helper;
defined( 'ABSPATH' ) || exit;
/**
* Metabox class.
*/
class Metabox implements Runner {
use Hooker;
/**
* Metabox id.
*
* @var string
*/
private $metabox_id = 'rank_math_metabox';
/**
* Screen object.
*
* @var Screen
*/
private $screen;
/**
* Register hooks.
*/
public function hooks() {
if ( $this->dont_load() ) {
return;
}
$this->screen = new Screen();
if ( $this->screen->is_loaded() ) {
$this->action( 'cmb2_admin_init', 'add_main_metabox', 30 );
$this->action( 'rank_math/admin/enqueue_scripts', 'enqueue' );
if ( Helper::is_site_editor() ) {
$this->action( 'enqueue_block_editor_assets', 'enqueue' );
}
if ( Helper::has_cap( 'link_builder' ) ) {
$this->action( 'cmb2_admin_init', 'add_link_suggestion_metabox', 30 );
}
}
$this->action( 'cmb2_' . CMB2::current_object_type() . '_process_fields_' . $this->metabox_id, 'save_meta' );
$this->action( 'cmb2_save_field', 'invalidate_facebook_object_cache', 10, 4 );
}
/**
* Enqueue styles and scripts for the metabox.
*/
public function enqueue() {
/**
* Allow other plugins to enqueue/dequeue admin styles or scripts before plugin assets.
*/
$this->do_action( 'admin/before_editor_scripts' );
$screen = get_current_screen();
$js = rank_math()->plugin_url() . 'assets/admin/js/';
$this->enqueue_commons();
$this->screen->enqueue();
$this->screen->localize();
$this->enqueue_translation();
rank_math()->variables->setup();
rank_math()->variables->setup_json();
$is_gutenberg = Helper::is_block_editor() && \rank_math_is_gutenberg();
$is_elementor = 'elementor' === Param::get( 'action' );
Helper::add_json( 'knowledgegraphType', Helper::get_settings( 'titles.knowledgegraph_type' ) );
if (
! $is_gutenberg &&
! $is_elementor &&
'rank_math_schema' !== $screen->post_type &&
'edit-tags' !== $screen->base
) {
\CMB2_Hookup::enqueue_cmb_css();
wp_enqueue_style(
'rank-math-metabox',
rank_math()->plugin_url() . 'assets/admin/css/metabox.css',
[
'rank-math-common',
'rank-math-cmb2',
'rank-math-editor',
'wp-components',
],
rank_math()->version
);
wp_enqueue_media();
wp_enqueue_script(
'rank-math-editor',
rank_math()->plugin_url() . 'assets/admin/js/classic.js',
[
'clipboard',
'wp-hooks',
'moment',
'wp-date',
'wp-data',
'wp-api-fetch',
'wp-components',
'wp-element',
'wp-i18n',
'wp-url',
'wp-media-utils',
'rank-math-common',
'rank-math-analyzer',
'wp-block-editor',
'rank-math-app',
],
rank_math()->version,
true
);
}
$this->do_action( 'enqueue_scripts/assessor' );
/**
* Allow other plugins to enqueue/dequeue admin styles or scripts after plugin assets.
*/
$this->do_action( 'admin/editor_scripts', $this->screen );
}
/**
* Enqueque scripts common for all builders.
*/
private function enqueue_commons() {
wp_register_style( 'rank-math-editor', rank_math()->plugin_url() . 'assets/admin/css/gutenberg.css', [ 'rank-math-common' ], rank_math()->version );
wp_register_script( 'rank-math-analyzer', rank_math()->plugin_url() . 'assets/admin/js/analyzer.js', [ 'lodash', 'wp-autop', 'wp-wordcount' ], rank_math()->version, true );
}
/**
* Enqueue translation.
*/
private function enqueue_translation() {
if ( function_exists( 'wp_set_script_translations' ) ) {
wp_set_script_translations( 'rank-math-analyzer', 'rank-math', rank_math()->plugin_dir() . 'languages/' );
wp_set_script_translations( 'rank-math-app', 'rank-math', rank_math()->plugin_dir() . 'languages/' );
}
}
/**
* Add main metabox.
*/
public function add_main_metabox() {
if ( $this->can_add_metabox() ) {
return;
}
$cmb = $this->create_metabox();
$cmb->add_field(
[
'id' => 'setting-panel-container-' . $this->metabox_id,
'type' => 'meta_tab_container_open',
'tabs' => [],
]
);
$cmb->add_field(
[
'id' => 'rank_math_metabox_wrapper',
'type' => 'raw',
'content' => '<div id="rank-math-metabox-wrapper"></div>',
'save_field' => false,
]
);
/**
* Allow disabling the primary term feature.
*
* @param bool $return True to disable.
*/
if ( false === apply_filters_deprecated( 'rank_math/primary_term', [ false ], '1.0.43', 'rank_math/admin/disable_primary_term' )
&& false === $this->do_filter( 'admin/disable_primary_term', false ) ) {
$taxonomies = Helper::get_object_taxonomies( Helper::get_post_type(), 'objects' );
$taxonomies = wp_filter_object_list( $taxonomies, [ 'hierarchical' => true ], 'and', 'name' );
foreach ( $taxonomies as $taxonomy ) {
$cmb->add_field(
[
'id' => 'rank_math_primary_' . $taxonomy,
'type' => 'hidden',
'default' => 0,
'attributes' => [
'data-primary-term' => $taxonomy,
],
]
);
}
}
$cmb->add_field(
[
'id' => 'setting-panel-container-close-' . $this->metabox_id,
'type' => 'tab_container_close',
]
);
CMB2::pre_init( $cmb );
}
/**
* Add link suggestion metabox.
*/
public function add_link_suggestion_metabox() {
$allowed_post_types = [];
foreach ( Helper::get_accessible_post_types() as $post_type ) {
if ( false === Helper::get_settings( 'titles.pt_' . $post_type . '_link_suggestions' ) ) {
continue;
}
$allowed_post_types[] = $post_type;
}
// Early bail.
if ( empty( $allowed_post_types ) ) {
return;
}
$cmb = new_cmb2_box(
[
'id' => $this->metabox_id . '_link_suggestions',
'title' => esc_html__( 'Link Suggestions', 'rank-math' ),
'object_types' => $allowed_post_types,
'context' => 'side',
'priority' => 'default',
]
);
$cmb->add_field(
[
'id' => $this->metabox_id . '_link_suggestions_tooltip',
'type' => 'raw',
'content' => '<div id="rank-math-link-suggestions-tooltip" class="hidden">' . Admin_Helper::get_tooltip( esc_html__( 'Click on the button to copy URL or insert link in content. You can also drag and drop links in the post content.', 'rank-math' ) ) . '</div>',
]
);
$cmb->add_field(
[
'id' => 'rank_math_social_tabs',
'type' => 'raw',
'file' => rank_math()->includes_dir() . 'metaboxes/link-suggestions.php',
'not_found' => '<em><small>' . esc_html__( 'We can\'t show any link suggestions for this post. Try selecting categories and tags for this post, and mark other posts as Pillar Content to make them show up here.', 'rank-math' ) . '</small></em>',
]
);
CMB2::pre_init( $cmb );
}
/**
* Save post meta handler.
*
* @param CMB2 $cmb CMB2 metabox object.
*/
public function save_meta( $cmb ) {
/**
* Hook into save handler for main metabox.
*
* @param CMB2 $cmb CMB2 object.
*/
$this->do_action( 'metabox/process_fields', $cmb );
}
/**
* Invalidate facebook object cache for the post.
*
* @param string $field_id The current field id paramater.
* @param bool $updated Whether the metadata update action occurred.
* @param string $action Action performed. Could be "repeatable", "updated", or "removed".
* @param CMB2_Field $field This field object.
*/
public function invalidate_facebook_object_cache( $field_id, $updated, $action, $field ) {
// Early Bail!
if ( ! in_array( $field_id, [ 'rank_math_facebook_title', 'rank_math_facebook_image', 'rank_math_facebook_description' ], true ) || ! $updated ) {
return;
}
$app_id = Helper::get_settings( 'titles.facebook_app_id' );
$secret = Helper::get_settings( 'titles.facebook_secret' );
// Early bail!
if ( ! $app_id || ! $secret ) {
return;
}
wp_remote_post(
'https://graph.facebook.com/',
[
'body' => [
'id' => get_permalink( $field->object_id() ),
'scrape' => true,
'access_token' => $app_id . '|' . $secret,
],
]
);
}
/**
* Create metabox
*
* @return CMB2
*/
private function create_metabox() {
return new_cmb2_box(
[
'id' => $this->metabox_id,
'title' => esc_html__( 'Rank Math SEO', 'rank-math' ),
'object_types' => $this->screen->get_object_types(),
'taxonomies' => Helper::get_allowed_taxonomies(),
'new_term_section' => false,
'new_user_section' => 'add-existing-user',
'context' => 'normal',
'priority' => $this->get_priority(),
'cmb_styles' => false,
'classes' => 'rank-math-metabox-wrap' . ( Admin_Helper::is_term_profile_page() ? ' rank-math-metabox-frame postbox' : '' ),
'mb_callback_args' => [ '__back_compat_meta_box' => \rank_math_is_gutenberg() ],
]
);
}
/**
* Get metabox priority
*
* @return string
*/
private function get_priority() {
$post_type = Param::get( 'post_type' );
if ( ! $post_type ) {
$post_type = get_post_type( Param::get( 'post', 0, FILTER_VALIDATE_INT ) );
}
$priority = 'product' === $post_type ? 'default' : 'high';
/**
* Filter: Change metabox priority.
*/
return $this->do_filter( 'metabox/priority', $priority );
}
/**
* Can add metabox
*
* @return bool
*/
private function can_add_metabox() {
return ! Helper::has_cap( 'onpage_general' ) &&
! Helper::has_cap( 'onpage_advanced' ) &&
! Helper::has_cap( 'onpage_snippet' ) &&
! Helper::has_cap( 'onpage_social' );
}
/**
* Can load metabox.
*
* @return bool
*/
private function dont_load() {
return Helper::is_heartbeat() || Helper::is_ajax() ||
( class_exists( 'Vc_Manager' ) && Param::get( 'vc_action' ) ) ||
is_network_admin();
}
}

View File

@@ -0,0 +1,454 @@
<?php
/**
* The post metabox screen.
*
* @since 1.0.25
* @package RankMath
* @subpackage RankMath\Admin\Metabox
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin\Metabox;
use RankMath\KB;
use RankMath\Helper;
use RankMath\Traits\Hooker;
use RankMath\Helpers\Editor;
use RankMath\Frontend_SEO_Score;
use RankMath\Admin\Admin_Helper;
use RankMath\Helpers\Str;
use RankMath\Helpers\Url;
use RankMath\Helpers\Param;
defined( 'ABSPATH' ) || exit;
/**
* Post metabox class.
*/
class Post_Screen implements IScreen {
use Hooker;
/**
* Hold primary taxonomy
*
* @var object
*/
private $primary_taxonomy = null;
/**
* Class construct
*/
public function __construct() {
$this->filter( 'rank_math/researches/tests', 'remove_tests', 10, 2 );
}
/**
* Get object id
*
* @return int
*/
public function get_object_id() {
global $post;
return ! empty( $post->ID ) ? $post->ID : '';
}
/**
* Get object type
*
* @return string
*/
public function get_object_type() {
return 'post';
}
/**
* Get object types to register metabox to
*
* @return array
*/
public function get_object_types() {
return Helper::get_allowed_post_types();
}
/**
* Enqueue Styles and Scripts required for screen.
*/
public function enqueue() {
$is_elementor = Helper::is_elementor_editor();
$is_block_editor = Helper::is_block_editor() && \rank_math_is_gutenberg();
Helper::add_json( 'postType', get_post_type() );
if ( ! $is_elementor ) {
$this->enqueue_custom_fields();
}
wp_register_script(
'rank-math-formats',
rank_math()->plugin_url() . 'assets/admin/js/gutenberg-formats.js',
[],
rank_math()->version,
true
);
if ( $is_block_editor || $is_elementor ) {
$this->enqueue_commons();
}
if ( $is_block_editor && ! $is_elementor && Editor::can_add_editor() ) {
$this->enqueue_for_gutenberg();
return;
}
if ( $is_elementor ) {
return;
}
// Classic.
if ( Helper::is_block_editor() ) {
wp_enqueue_script( 'rank-math-formats' );
wp_enqueue_script( 'rank-math-primary-term', rank_math()->plugin_url() . 'assets/admin/js/gutenberg-primary-term.js', [], rank_math()->version, true );
}
}
/**
* Get values for localize.
*
* @return array
*/
public function get_values() {
$post_type = $this->get_current_post_type();
return [
'parentDomain' => Url::get_domain( home_url() ),
'noFollowDomains' => Str::to_arr_no_empty( Helper::get_settings( 'general.nofollow_domains' ) ),
'noFollowExcludeDomains' => Str::to_arr_no_empty( Helper::get_settings( 'general.nofollow_exclude_domains' ) ),
'noFollowExternalLinks' => Helper::get_settings( 'general.nofollow_external_links' ),
'featuredImageNotice' => esc_html__( 'The featured image should be at least 200 by 200 pixels to be picked up by Facebook and other social media sites.', 'rank-math' ),
'pluginReviewed' => $this->plugin_reviewed(),
'postSettings' => [
'linkSuggestions' => Helper::get_settings( 'titles.pt_' . $post_type . '_link_suggestions' ),
'useFocusKeyword' => 'focus_keywords' === Helper::get_settings( 'titles.pt_' . $post_type . '_ls_use_fk' ),
],
'frontEndScore' => Frontend_SEO_Score::show_on(),
'postName' => get_post_field( 'post_name', get_post() ),
'permalinkFormat' => $this->get_permalink_format(),
'showLockModifiedDate' => Editor::can_add_lock_modified_date(),
'assessor' => [
'focusKeywordLink' => admin_url( 'edit.php?focus_keyword=%focus_keyword%&post_type=%post_type%' ),
'hasTOCPlugin' => $this->has_toc_plugin(),
'primaryTaxonomy' => $this->get_primary_taxonomy(),
],
];
}
/**
* Get object values for localize
*
* @return array
*/
public function get_object_values() {
global $post;
if ( empty( $post ) ) {
return [];
}
return [
'primaryTerm' => $this->get_primary_term_id(),
'authorName' => get_the_author_meta( 'display_name', $post->post_author ),
'titleTemplate' => Helper::get_settings( "titles.pt_{$post->post_type}_title", '%title% %sep% %sitename%' ),
'descriptionTemplate' => Helper::get_settings( "titles.pt_{$post->post_type}_description", '' ),
'showScoreFrontend' => ! Helper::get_post_meta( 'dont_show_seo_score', $this->get_object_id() ),
'lockModifiedDate' => ! empty( Helper::get_post_meta( 'lock_modified_date', $this->get_object_id() ) ),
];
}
/**
* Get analysis to run.
*
* @return array
*/
public function get_analysis() {
$tests = [
'contentHasTOC' => true,
'contentHasShortParagraphs' => true,
'contentHasAssets' => true,
'keywordInTitle' => true,
'keywordInMetaDescription' => true,
'keywordInPermalink' => true,
'keywordIn10Percent' => true,
'keywordInContent' => true,
'keywordInSubheadings' => true,
'keywordInImageAlt' => true,
'keywordDensity' => true,
'keywordNotUsed' => true,
'lengthContent' => true,
'lengthPermalink' => true,
'linksHasInternal' => true,
'linksHasExternals' => true,
'linksNotAllExternals' => true,
'titleStartWithKeyword' => true,
'titleSentiment' => true,
'titleHasPowerWords' => true,
'titleHasNumber' => true,
'hasContentAI' => true,
];
return $tests;
}
/**
* Remove few tests on static Homepage.
*
* @since 1.0.42
*
* @param array $tests Array of tests with score.
* @param string $type Object type. Can be post, user or term.
*/
public function remove_tests( $tests, $type ) {
if ( ! Admin_Helper::is_home_page() && ! Admin_Helper::is_posts_page() ) {
return $tests;
}
return array_diff_assoc( $tests, $this->exclude_tests() );
}
/**
* Function to get the permalink format.
*
* @since 1.0.69.2
*/
private function get_permalink_format() {
$post_id = $this->get_object_id();
$post = get_post( $post_id );
if ( empty( $post ) ) {
return;
}
if ( 'attachment' === $post->post_type ) {
return str_replace( $post->post_name, '%postname%', get_permalink( $post ) );
}
if ( ( 'auto-draft' !== $post->post_status || 'post' !== $post->post_type ) && function_exists( 'get_sample_permalink' ) ) {
$sample_permalink = \get_sample_permalink( $post_id, null, null );
return isset( $sample_permalink[0] ) ? $sample_permalink[0] : home_url();
}
$post_temp = $post;
$post_temp->post_status = 'publish';
return get_permalink( $post_temp, true );
}
/**
* Tests to exclude on Homepage and Blog page.
*
* @since 1.0.43
*
* @return array Array of excluded tests.
*/
private function exclude_tests() {
if ( Admin_Helper::is_home_page() ) {
return [
'contentHasTOC' => true,
'keywordInPermalink' => true,
'lengthPermalink' => true,
'linksHasExternals' => true,
'linksNotAllExternals' => true,
'titleSentiment' => true,
'titleHasPowerWords' => true,
'titleHasNumber' => true,
];
}
return [
'contentHasTOC' => true,
'contentHasShortParagraphs' => true,
'keywordIn10Percent' => true,
'keywordInContent' => true,
'keywordInSubheadings' => true,
'keywordDensity' => true,
'lengthContent' => true,
'linksHasInternal' => true,
'linksHasExternals' => true,
'linksNotAllExternals' => true,
];
}
/**
* Enqueque scripts common for all builders.
*/
private function enqueue_commons() {
wp_register_style( 'rank-math-editor', rank_math()->plugin_url() . 'assets/admin/css/gutenberg.css', [], rank_math()->version );
}
/**
* Enqueue script to analyze custom fields data.
*/
private function enqueue_custom_fields() {
global $post;
if ( empty( $post ) ) {
return;
}
$custom_fields = Str::to_arr_no_empty( Helper::get_settings( 'titles.pt_' . $post->post_type . '_analyze_fields' ) );
if ( empty( $custom_fields ) ) {
return;
}
$file = Helper::is_block_editor() ? 'glue-custom-fields.js' : 'custom-fields.js';
wp_enqueue_script( 'rank-math-custom-fields', rank_math()->plugin_url() . 'assets/admin/js/' . $file, [ 'wp-hooks', 'rank-math-analyzer' ], rank_math()->version, true );
Helper::add_json( 'analyzeFields', $custom_fields );
}
/**
* Enqueue scripts for gutenberg screen.
*/
private function enqueue_for_gutenberg() {
wp_enqueue_style( 'rank-math-editor' );
wp_enqueue_script( 'rank-math-formats' );
wp_enqueue_script(
'rank-math-editor',
rank_math()->plugin_url() . 'assets/admin/js/gutenberg.js',
[
'clipboard',
'wp-autop',
'wp-blocks',
'wp-components',
'wp-editor',
'wp-edit-post',
'wp-element',
'wp-i18n',
'wp-plugins',
'wp-wordcount',
'rank-math-analyzer',
'rank-math-app',
],
rank_math()->version,
true
);
}
/**
* Get current post type.
*
* @return string
*/
private function get_current_post_type() {
$post_type = get_post_type();
if ( function_exists( 'get_current_screen' ) ) {
$screen = get_current_screen();
$post_type = isset( $screen->post_type ) ? $screen->post_type : $post_type;
}
return $post_type;
}
/**
* Check if any TOC plugin detected
*
* @return bool
*/
private function has_toc_plugin() {
if ( \defined( 'ELEMENTOR_PRO_VERSION' ) ) {
return true;
}
$plugins_found = [];
$active_plugins = get_option( 'active_plugins' );
$active_plugins = is_multisite() ? array_merge( $active_plugins, array_keys( get_site_option( 'active_sitewide_plugins', [] ) ) ) : $active_plugins;
/**
* Allow developers to add plugins to the TOC list.
*
* @param array TOC plugins.
*/
$toc_plugins = $this->do_filter(
'researches/toc_plugins',
[
'wp-shortcode/wp-shortcode.php' => 'WP Shortcode by RankMath',
'wp-shortcode-pro/wp-shortcode-pro.php' => 'WP Shortcode Pro by RankMath',
]
);
foreach ( $toc_plugins as $plugin_slug => $plugin_name ) {
if ( in_array( $plugin_slug, $active_plugins, true ) !== false ) {
$plugins_found[ $plugin_slug ] = $plugin_name;
}
}
return empty( $plugins_found ) ? false : $plugins_found;
}
/**
* Plugin already reviewed.
*
* @return bool
*/
private function plugin_reviewed() {
return get_option( 'rank_math_already_reviewed' ) || Helper::get_current_time() < get_option( 'rank_math_install_date' ) + ( 2 * WEEK_IN_SECONDS );
}
/**
* Get primary taxonomy.
*
* @return bool|array
*/
private function get_primary_taxonomy() {
if ( ! is_null( $this->primary_taxonomy ) ) {
return $this->primary_taxonomy;
}
$taxonomy = false;
$post_type = $this->get_current_post_type();
/**
* Filter: Allow disabling the primary term feature.
* 'rank_math/primary_term' is deprecated,
* use 'rank_math/admin/disable_primary_term' instead.
*
* @param bool $return True to disable.
*/
if ( false === apply_filters_deprecated( 'rank_math/primary_term', [ false ], '1.0.43', 'rank_math/admin/disable_primary_term' )
&& false === $this->do_filter( 'admin/disable_primary_term', false ) ) {
$taxonomy = Helper::get_settings( 'titles.pt_' . $post_type . '_primary_taxonomy', false );
}
if ( ! $taxonomy ) {
return false;
}
$taxonomy = get_taxonomy( $taxonomy );
if ( empty( $taxonomy ) ) {
return false;
}
$this->primary_taxonomy = [
'title' => $taxonomy->labels->singular_name,
'name' => $taxonomy->name,
'singularLabel' => $taxonomy->labels->singular_name,
'restBase' => ( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name,
];
return $this->primary_taxonomy;
}
/**
* Get primary term ID.
*
* @return int
*/
private function get_primary_term_id() {
$taxonomy = $this->get_primary_taxonomy();
if ( ! $taxonomy ) {
return 0;
}
$id = Helper::get_post_meta( 'primary_' . $taxonomy['name'], $this->get_object_id() );
return $id ? absint( $id ) : 0;
}
}

View File

@@ -0,0 +1,385 @@
<?php
/**
* Metabox localization methods.
*
* @since 1.0.33
* @package RankMath
* @subpackage RankMath\Admin\Metabox
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin\Metabox;
use RankMath\KB;
use RankMath\Helper;
use RankMath\Traits\Meta;
use RankMath\Traits\Hooker;
use RankMath\Helpers\Locale;
use RankMath\Admin\Admin_Helper;
use RankMath\Helpers\Url;
use RankMath\Helpers\Param;
defined( 'ABSPATH' ) || exit;
/**
* Screen.
*/
class Screen implements IScreen {
use Meta;
use Hooker;
/**
* Current screen object.
*
* @var IScreen
*/
private $screen = null;
/**
* Class construct
*/
public function __construct() {
$this->load_screen();
}
/**
* Is creen loaded.
*
* @return bool
*/
public function is_loaded() {
return ! is_null( $this->screen );
}
/**
* Get object id
*
* @return int
*/
public function get_object_id() {
return $this->screen->get_object_id();
}
/**
* Get object type
*
* @return string
*/
public function get_object_type() {
return $this->screen->get_object_type();
}
/**
* Get object types to register metabox to
*
* @return array
*/
public function get_object_types() {
return $this->screen->get_object_types();
}
/**
* Enqueue Styles and Scripts required for screen.
*/
public function enqueue() {
$this->screen->enqueue();
}
/**
* Get analysis to run.
*
* @return array
*/
public function get_analysis() {
$analyses = $this->do_filter(
'researches/tests',
$this->screen->get_analysis(),
$this->screen->get_object_type()
);
return array_keys( $analyses );
}
/**
* Get values for localize.
*/
public function localize() {
$values = $this->get_values();
if ( empty( $values ) ) {
return;
}
foreach ( $values as $key => $value ) {
Helper::add_json( $key, $value );
}
}
/**
* Get common values.
*
* @return array
*/
public function get_values() {
$editor = Helper::get_current_editor();
$trends_link = KB::get( 'pro', 'CE General Tab Trends' );
if ( 'gutenberg' === $editor ) {
$trends_link = KB::get( 'pro', 'Gutenberg General Tab Trends' );
} elseif ( 'elementor' === $editor ) {
$trends_link = KB::get( 'pro', 'Elementor General Tab Trends' );
}
$values = array_merge_recursive(
$this->screen->get_values(),
[
'homeUrl' => home_url(),
'objectID' => $this->get_object_id(),
'objectType' => $this->get_object_type(),
'locale' => Locale::get_site_language(),
'localeFull' => get_locale(),
'overlayImages' => Helper::choices_overlay_images(),
'defautOgImage' => Helper::get_settings( 'titles.open_graph_image', rank_math()->plugin_url() . 'assets/admin/img/social-placeholder.jpg' ),
'customPermalinks' => (bool) get_option( 'permalink_structure', false ),
'isUserRegistered' => Helper::is_site_connected(),
'autoSuggestKeywords' => Helper::is_site_connected(),
'connectSiteUrl' => Admin_Helper::get_activate_url( Url::get_current_url() ),
'maxTags' => $this->do_filter( 'focus_keyword/maxtags', 5 ),
'trendsIcon' => Admin_Helper::get_trends_icon_svg(),
'showScore' => Helper::is_score_enabled(),
'siteFavIcon' => $this->get_site_icon(),
'canUser' => [
'general' => Helper::has_cap( 'onpage_general' ),
'advanced' => Helper::has_cap( 'onpage_advanced' ) && Helper::is_advanced_mode(),
'snippet' => Helper::has_cap( 'onpage_snippet' ),
'social' => Helper::has_cap( 'onpage_social' ),
'analysis' => Helper::has_cap( 'onpage_analysis' ),
'analytics' => Helper::has_cap( 'analytics' ),
'content_ai' => Helper::has_cap( 'content_ai' ),
],
'showKeywordIntent' => Helper::should_determine_search_intent(),
'assessor' => [
'serpData' => $this->get_object_values(),
'powerWords' => $this->power_words(),
'diacritics' => $this->diacritics(),
'researchesTests' => $this->get_analysis(),
'hasRedirection' => Helper::is_module_active( 'redirections' ),
'hasBreadcrumb' => Helper::is_breadcrumbs_enabled(),
],
'isPro' => defined( 'RANK_MATH_PRO_FILE' ),
'is_front_page' => Admin_Helper::is_home_page(),
'trendsUpgradeLink' => esc_url_raw( $trends_link ),
'trendsUpgradeLabel' => esc_html__( 'Upgrade', 'rank-math' ),
'trendsPreviewImage' => esc_url( rank_math()->plugin_url() . 'assets/admin/img/trends-preview.jpg' ),
'currentEditor' => $editor,
'homepageData' => [
'assessor' => [
'powerWords' => $this->power_words(),
'diacritics' => $this->diacritics(),
'researchesTests' => $this->get_analysis(),
'hasBreadcrumb' => Helper::is_breadcrumbs_enabled(),
'serpData' => [
'title' => Helper::get_settings( 'titles.homepage_title' ),
'description' => Helper::get_settings( 'titles.homepage_description', '' ),
'titleTemplate' => '%sitename% %page% %sep% %sitedesc%',
'descriptionTemplate' => '',
'focusKeywords' => '',
'breadcrumbTitle' => Helper::get_settings( 'general.breadcrumbs_home_label' ),
'robots' => $this->normalize_robots( Helper::get_settings( 'titles.homepage_robots' ) ),
'advancedRobots' => $this->normalize_advanced_robots( Helper::get_settings( 'titles.homepage_advanced_robots' ) ),
// Facebook.
'facebookTitle' => Helper::get_settings( 'titles.homepage_facebook_title', '' ),
'facebookDescription' => Helper::get_settings( 'titles.homepage_facebook_description', '' ),
'facebookImage' => Helper::get_settings( 'titles.homepage_facebook_image', '' ),
'facebookImageID' => Helper::get_settings( 'titles.homepage_facebook_image_id', '' ),
],
],
],
]
);
$values = $this->do_filter( 'metabox/values', $values, $this );
return $this->do_filter( 'metabox/' . $this->get_object_type() . '/values', $values, $this );
}
/**
* Get object values for localize
*
* @return array
*/
public function get_object_values() {
$keys = $this->do_filter(
'metabox/' . $this->get_object_type() . '/meta_keys',
[
'title' => 'title',
'description' => 'description',
'focusKeywords' => 'focus_keyword',
'pillarContent' => 'pillar_content',
'canonicalUrl' => 'canonical_url',
'breadcrumbTitle' => 'breadcrumb_title',
'advancedRobots' => 'advanced_robots',
// Facebook.
'facebookTitle' => 'facebook_title',
'facebookDescription' => 'facebook_description',
'facebookImage' => 'facebook_image',
'facebookImageID' => 'facebook_image_id',
'facebookHasOverlay' => 'facebook_enable_image_overlay',
'facebookImageOverlay' => 'facebook_image_overlay',
'facebookAuthor' => 'facebook_author',
// Twitter.
'twitterCardType' => 'twitter_card_type',
'twitterUseFacebook' => 'twitter_use_facebook',
'twitterTitle' => 'twitter_title',
'twitterDescription' => 'twitter_description',
'twitterImage' => 'twitter_image',
'twitterImageID' => 'twitter_image_id',
'twitterHasOverlay' => 'twitter_enable_image_overlay',
'twitterImageOverlay' => 'twitter_image_overlay',
// Player.
'twitterPlayerUrl' => 'twitter_player_url',
'twitterPlayerSize' => 'twitter_player_size',
'twitterPlayerStream' => 'twitter_player_stream',
'twitterPlayerStreamCtype' => 'twitter_player_stream_ctype',
// App.
'twitterAppDescription' => 'twitter_app_description',
'twitterAppIphoneName' => 'twitter_app_iphone_name',
'twitterAppIphoneID' => 'twitter_app_iphone_id',
'twitterAppIphoneUrl' => 'twitter_app_iphone_url',
'twitterAppIpadName' => 'twitter_app_ipad_name',
'twitterAppIpadID' => 'twitter_app_ipad_id',
'twitterAppIpadUrl' => 'twitter_app_ipad_url',
'twitterAppGoogleplayName' => 'twitter_app_googleplay_name',
'twitterAppGoogleplayID' => 'twitter_app_googleplay_id',
'twitterAppGoogleplayUrl' => 'twitter_app_googleplay_url',
'twitterAppCountry' => 'twitter_app_country',
]
);
// Generate data.
$data = [];
$object_id = $this->get_object_id();
$object_type = $this->get_object_type();
foreach ( $keys as $id => $key ) {
$data[ $id ] = $this->get_meta( $object_type, $object_id, 'rank_math_' . $key );
}
// Robots.
$data['robots'] = $this->normalize_robots( $this->get_meta( $object_type, $object_id, 'rank_math_robots' ) );
// Advanced Robots.
$data['advancedRobots'] = $this->normalize_advanced_robots( $this->get_meta( $object_type, $object_id, 'rank_math_advanced_robots' ) );
$data['pillarContent'] = 'on' === $data['pillarContent'];
// Username, avatar & Name.
$twitter_username = Helper::get_settings( 'titles.twitter_author_names' );
$data['twitterAuthor'] = $twitter_username ? $twitter_username : esc_html__( 'username', 'rank-math' );
$data['twitterUseFacebook'] = 'off' === $data['twitterUseFacebook'] ? false : true;
$data['facebookHasOverlay'] = empty( $data['facebookHasOverlay'] ) || 'off' === $data['facebookHasOverlay'] ? false : true;
$data['twitterHasOverlay'] = empty( $data['twitterHasOverlay'] ) || 'off' === $data['twitterHasOverlay'] ? false : true;
return wp_parse_args( $this->screen->get_object_values(), $data );
}
/**
* Get site fav icon.
*
* @return string
*/
private function get_site_icon() {
$favicon = get_site_icon_url( 16 );
return ! empty( $favicon ) ? $favicon : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABs0lEQVR4AWL4//8/RRjO8Iucx+noO0MWUDo16FYABMGP6ZfUcRnWtm27jVPbtm3bttuH2t3eFPcY9pLz7NxiLjCyVd87pKnHyqXyxtCs8APd0rnyxiu4qSeA3QEDrAwBDrT1s1Rc/OrjLZwqVmOSu6+Lamcpp2KKMA9PH1BYXMe1mUP5qotvXTywsOEEYHXxrY+3cqk6TMkYpNr2FeoY3KIr0RPtn9wQ2unlA+GMkRw6+9TFw4YTwDUzx/JVvARj9KaedXRO8P5B1Du2S32smzqUrcKGEyA+uAgQjKX7zf0boWHGfn71jIKj2689gxp7OAGShNcBUmLMPVjZuiKcA2vuWHHDCQxMCz629kXAIU4ApY15QwggAFbfOP9DhgBJ+nWVJ1AZAfICAj1pAlY6hCADZnveQf7bQIwzVONGJonhLIlS9gr5mFg44Xd+4S3XHoGNPdJl1INIwKyEgHckEhgTe1bGiFY9GSFBYUwLh1IkiJUbY407E7syBSFxKTszEoiE/YdrgCEayDmtaJwCI9uu8TKMuZSVfSa4BpGgzvomBR/INhLGzrqDotp01ZR8pn/1L0JN9d9XNyx0AAAAAElFTkSuQmCC';
}
/**
* Normalize robots.
*
* @param array $robots Array to normalize.
*
* @return array
*/
private function normalize_robots( $robots ) {
if ( ! is_array( $robots ) || empty( $robots ) ) {
$robots = Helper::get_robots_defaults();
}
return array_fill_keys( $robots, true );
}
/**
* Normalize advanced robots.
*
* @param array $advanced_robots Array to normalize.
*
* @return array
*/
private function normalize_advanced_robots( $advanced_robots ) {
if ( ! empty( $advanced_robots ) ) {
return $advanced_robots;
}
return Helper::get_advanced_robots_defaults();
}
/**
* Return power words.
*
* @return array
*/
private function power_words() {
static $words;
$locale = Locale::get_site_language();
$file = rank_math()->plugin_dir() . 'assets/vendor/powerwords/' . $locale . '.php';
if ( ! file_exists( $file ) ) {
return $this->do_filter( 'metabox/power_words', [], $locale );
}
$words = $words ? $words : include $file;
return $this->do_filter( 'metabox/power_words', array_map( 'strtolower', $words ), $locale );
}
/**
* Get diacritics (accents).
*
* @return array
*/
private function diacritics() {
$locale = Locale::get_site_language();
$locale = in_array( $locale, [ 'en', 'de', 'ru' ], true ) ? $locale : 'en';
$file = rank_math()->plugin_dir() . 'assets/vendor/diacritics/' . $locale . '.php';
if ( ! file_exists( $file ) ) {
return false;
}
$diacritics = include_once $file;
return $this->do_filter( 'metabox/diacritics', $diacritics, $locale );
}
/**
* Load required screen.
*
* @param string $manual To load any screen manually.
*/
public function load_screen( $manual = '' ) {
if ( Admin_Helper::is_post_edit() || 'post' === $manual || Helper::is_site_editor() ) {
$this->screen = new Post_Screen();
return;
}
$doing_quick_edit = Param::request( 'action' ) === 'inline-save-tax';
if ( Admin_Helper::is_term_edit() || 'term' === $manual || $doing_quick_edit ) {
$this->screen = new Taxonomy_Screen();
return;
}
if ( User_Screen::is_enable() || 'user' === $manual ) {
$this->screen = new User_Screen();
return;
}
}
}

View File

@@ -0,0 +1,193 @@
<?php
/**
* The metabox functionality of the plugin.
*
* @since 1.0.25
* @package RankMath
* @subpackage RankMath\Admin\Metabox
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin\Metabox;
use RankMath\Helper;
use RankMath\Traits\Hooker;
use RankMath\Helpers\Param;
defined( 'ABSPATH' ) || exit;
/**
* Taxonomy metabox class.
*/
class Taxonomy_Screen implements IScreen {
use Hooker;
/**
* Class construct
*/
public function __construct() {
add_action( 'init', [ $this, 'allow_tags' ], 15 );
}
/**
* Allow tags in term description.
*/
public function allow_tags() {
$taxonomies = Helper::get_allowed_taxonomies();
if ( is_array( $taxonomies ) && ! empty( $taxonomies ) ) {
remove_filter( 'pre_term_description', 'wp_filter_kses' );
remove_filter( 'term_description', 'wp_kses_data' );
add_filter( 'pre_term_description', 'wp_kses_post' );
add_filter( 'term_description', 'wp_kses_post' );
}
}
/**
* Get object ID.
*
* @return int
*/
public function get_object_id() {
return Param::request( 'tag_ID', 0, FILTER_VALIDATE_INT );
}
/**
* Get object type
*
* @return string
*/
public function get_object_type() {
return 'term';
}
/**
* Get object types to register metabox to
*
* @return array
*/
public function get_object_types() {
$object_types = [];
$taxonomies = Helper::get_allowed_taxonomies();
if ( is_array( $taxonomies ) && ! empty( $taxonomies ) ) {
$object_types[] = 'term';
$this->description_field_editor();
}
return $object_types;
}
/**
* Enqueue Styles and Scripts required for screen.
*/
public function enqueue() {}
/**
* Get analysis to run.
*
* @return array
*/
public function get_analysis() {
return [
'keywordInTitle' => true,
'keywordInMetaDescription' => true,
'keywordInPermalink' => true,
'keywordNotUsed' => true,
'titleStartWithKeyword' => true,
];
}
/**
* Get values for localize.
*
* @return array
*/
public function get_values() {
$url = '';
if ( $this->get_object_id() ) {
$url = get_term_link( $this->get_object_id() );
$data = array_filter( explode( '/', $url ) );
$url = ! empty( $data ) ? str_replace( array_pop( $data ), '%term%', $url ) : '';
}
return [
'permalinkFormat' => $url ? $url : home_url(),
];
}
/**
* Get object values for localize
*
* @return array
*/
public function get_object_values() {
$taxonomy = $this->get_taxonomy();
return [
'titleTemplate' => Helper::get_settings( "titles.tax_{$taxonomy}_title", '%term% %sep% %sitename%' ),
'descriptionTemplate' => Helper::get_settings( "titles.tax_{$taxonomy}_description", '%term_description%' ),
];
}
/**
* Output the WordPress editor.
*
* @param object $term Current taxonomy term object.
*/
public function category_description_editor( $term ) {
?>
<tr class="form-field term-description-wrap rank-math-term-description-wrap">
<th scope="row"><label for="description"><?php esc_html_e( 'Description', 'rank-math' ); ?></label></th>
<td>
<?php
wp_editor(
html_entity_decode( $term->description, ENT_QUOTES, 'UTF-8' ),
'rank_math_description_editor',
[
'textarea_name' => 'description',
'textarea_rows' => 5,
]
);
?>
</td>
<script>
// Remove the non-html field
jQuery('textarea#description').closest('.form-field').remove();
</script>
</tr>
<?php
}
/**
* Add the description field to the edit taxonomy screen if the metabox is
* enabled for the current taxonomy.
*
* @return void
*/
private function description_field_editor() {
$taxonomy = $this->get_taxonomy();
if (
! Helper::get_settings( 'titles.tax_' . $taxonomy . '_add_meta_box' ) ||
$this->do_filter( 'admin/disable_rich_editor', false, $taxonomy )
) {
return;
}
$this->action( "{$taxonomy}_edit_form_fields", 'category_description_editor', 1 );
}
/**
* Get current taxonomy.
*
* @return {string} Taxonomy slug.
*/
private function get_taxonomy() {
$taxonomy = filter_input( INPUT_GET, 'taxonomy', FILTER_DEFAULT, [ 'options' => [ 'default' => '' ] ] );
$taxonomy_object = get_taxonomy( $taxonomy );
if ( empty( $taxonomy_object ) || empty( $taxonomy_object->public ) ) {
return;
}
return $taxonomy;
}
}

View File

@@ -0,0 +1,116 @@
<?php
/**
* The metabox functionality of the plugin.
*
* @since 1.0.25
* @package RankMath
* @subpackage RankMath\Admin\Metabox
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin\Metabox;
use RankMath\Helper;
use RankMath\Traits\Hooker;
use RankMath\Admin\Admin_Helper;
defined( 'ABSPATH' ) || exit;
/**
* User metabox class.
*/
class User_Screen implements IScreen {
use Hooker;
/**
* Class construct
*/
public function __construct() {}
/**
* Get object ID.
*
* @return int
*/
public function get_object_id() {
global $user_id;
return $user_id;
}
/**
* Get object type
*
* @return string
*/
public function get_object_type() {
return 'user';
}
/**
* Get object types to register metabox to
*
* @return array
*/
public function get_object_types() {
return [ 'user' ];
}
/**
* Enqueue Styles and Scripts required for screen.
*/
public function enqueue() {
wp_enqueue_media();
}
/**
* Get analysis to run.
*
* @return array
*/
public function get_analysis() {
return [
'keywordInTitle' => true,
'keywordInMetaDescription' => true,
'keywordInPermalink' => true,
'keywordNotUsed' => true,
'titleStartWithKeyword' => true,
];
}
/**
* Get values for localize.
*
* @return array
*/
public function get_values() {
global $wp_rewrite;
return [
'permalinkFormat' => ! empty( $wp_rewrite->author_structure ) ? home_url( $wp_rewrite->author_structure ) : home_url(),
];
}
/**
* Get object values for localize
*
* @return array
*/
public function get_object_values() {
return [
'titleTemplate' => Helper::get_settings( 'titles.author_archive_title', '%name% %sep% %sitename% %page%' ),
'descriptionTemplate' => Helper::get_settings( 'titles.author_archive_description', '%user_description%' ),
];
}
/**
* Is user metabox enabled.
*
* @return bool
*/
public static function is_enable() {
return false === Helper::get_settings( 'titles.disable_author_archives' ) &&
Helper::get_settings( 'titles.author_add_meta_box' ) &&
Admin_Helper::is_user_edit();
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* An interface for getting values for screen.
*
* @since 1.0.33
* @package RankMath
* @subpackage RankMath\Admin\Metabox
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin\Metabox;
defined( 'ABSPATH' ) || exit;
/**
* Screen.
*/
interface IScreen {
/**
* Get object ID.
*
* @return int
*/
public function get_object_id();
/**
* Get object type
*
* @return string
*/
public function get_object_type();
/**
* Get object types to register metabox to
*
* @return array
*/
public function get_object_types();
/**
* Enqueue Styles and Scripts required for screen
*/
public function enqueue();
/**
* Get values for localize
*
* @return array
*/
public function get_values();
/**
* Get object values for localize
*
* @return array
*/
public function get_object_values();
/**
* Get analysis to run.
*
* @return array
*/
public function get_analysis();
}

View File

@@ -0,0 +1,358 @@
<?php
/**
* The Notification center handles notifications storage and display.
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin\Notifications;
/**
* Notification_Center class.
*/
class Notification_Center {
/**
* Option name to store notifications in.
*
* @var string
*/
private $storage_key = '';
/**
* Notifications.
*
* @var array
*/
private $notifications = [];
/**
* Stores whether we need to clear storage or not.
*
* @var array
*/
private $should_clear_storage = true;
/**
* Stores already displayed notice texts to avoid duplication.
*
* @var array
*/
private $displayed_notifications = [];
/**
* Internal flag for whether notifications have been retrieved from storage.
*
* @var bool
*/
private $retrieved = false;
/**
* Construct
*
* @param string $storage_key Option name to store notification in.
*/
public function __construct( $storage_key = 'mythemeshop_notifications' ) {
$this->storage_key = $storage_key;
add_action( 'plugins_loaded', [ $this, 'get_from_storage' ], 5 );
add_action( 'all_admin_notices', [ $this, 'display' ] );
add_action( 'shutdown', [ $this, 'update_storage' ] );
add_action( 'admin_footer', [ $this, 'print_javascript' ] );
add_action( 'wp_ajax_wp_helpers_notice_dismissible', [ $this, 'notice_dismissible' ] );
}
/**
* Retrieve the notifications from storage
*
* @codeCoverageIgnore
*
* @return void
*/
public function get_from_storage() {
if ( $this->retrieved ) {
return;
}
$this->retrieved = true;
$notifications = get_option( $this->storage_key );
// Check if notifications are stored.
if ( empty( $notifications ) ) {
$this->should_clear_storage = false;
return;
}
if ( is_array( $notifications ) ) {
foreach ( $notifications as $notification ) {
$this->notifications[] = new Notification(
$notification['message'],
$notification['options']
);
}
}
}
/**
* Display the notifications.
*
* @codeCoverageIgnore
*/
public function display() {
// Never display notifications for network admin.
if ( $this->is_network_admin() ) {
return;
}
$notifications = $this->get_sorted_notifications();
if ( empty( $notifications ) ) {
return;
}
foreach ( $notifications as $notification ) {
if ( $notification->can_display() && ! in_array( (string) $notification, $this->displayed_notifications, true ) ) {
echo wp_kses_post( $notification );
$this->displayed_notifications[] = (string) $notification;
}
}
}
/**
* Print JS for dismissile.
*
* @codeCoverageIgnore
*/
public function print_javascript() {
?>
<script>
;(function($) {
$( '.wp-helpers-notice.is-dismissible' ).on( 'click', '.notice-dismiss', function() {
var notice = $( this ).parent()
$.ajax({
url: ajaxurl,
type: 'POST',
dataType: 'json',
data: {
action: 'wp_helpers_notice_dismissible',
security: notice.data( 'security' ),
notificationId: notice.attr( 'id' )
}
});
});
})(jQuery);
</script>
<?php
}
/**
* Save persistent or transactional notifications to storage.
*
* We need to be able to retrieve these so they can be dismissed at any time during the execution.
*
* @codeCoverageIgnore
*/
public function update_storage() {
$notifications = $this->get_notifications();
$notifications = array_filter( $notifications, [ $this, 'remove_notification' ] );
/**
* Filter: 'wp_helpers_notifications_before_storage' - Allows developer to filter notifications before saving them.
*
* @param Notification[] $notifications
*/
$notifications = apply_filters( 'wp_helpers_notifications_before_storage', $notifications );
// No notifications to store, clear storage.
if ( empty( $notifications ) && $this->should_clear_storage ) {
delete_option( $this->storage_key );
return;
}
$notifications = array_map( [ $this, 'notification_to_array' ], $notifications );
// Save the notifications to the storage.
update_option( $this->storage_key, $notifications );
}
/**
* Dismiss persistent notice.
*
* @codeCoverageIgnore
*/
public function notice_dismissible() {
$notification_id = filter_input( INPUT_POST, 'notificationId' );
check_ajax_referer( $notification_id, 'security' );
$notification = $this->remove_by_id( $notification_id );
/**
* Filter: 'wp_helpers_notification_dismissed' - Allows developer to perform action after dismissed.
*
* @param string $notification_id
* @param Notification $notifications
*/
do_action( 'wp_helpers_notification_dismissed', $notification_id, $notification );
}
/**
* Add notification
*
* @param string $message Message string.
* @param array $options Set of options.
*/
public function add( $message, $options = [] ) {
if ( isset( $options['id'] ) && ! is_null( $this->get_notification_by_id( $options['id'] ) ) ) {
return;
}
$this->notifications[] = new Notification(
$message,
$options
);
}
/**
* Provide a way to verify present notifications
*
* @return array|Notification[] Registered notifications.
*/
public function get_notifications() {
return $this->notifications;
}
/**
* Get the notification by ID
*
* @param string $notification_id The ID of the notification to search for.
* @return null|Notification
*/
public function get_notification_by_id( $notification_id ) {
foreach ( $this->notifications as $notification ) {
if ( $notification_id === $notification->args( 'id' ) ) {
return $notification;
}
}
return null;
}
/**
* Remove the notification by ID
*
* @codeCoverageIgnore
*
* @param string $notification_id The ID of the notification to search for.
* @return Notification Instance of delete notification.
*/
public function remove_by_id( $notification_id ) {
$notification = $this->get_notification_by_id( $notification_id );
if ( ! is_null( $notification ) ) {
$notification->dismiss();
}
return $notification;
}
/**
* Remove notification after it has been displayed.
*
* @codeCoverageIgnore
*
* @param Notification $notification Notification to remove.
*/
public function remove_notification( Notification $notification ) {
if ( ! $notification->is_displayed() ) {
return true;
}
if ( $notification->is_persistent() ) {
return true;
}
return false;
}
/**
* Return the notifications sorted on type and priority
*
* @codeCoverageIgnore
*
* @return array|Notification[] Sorted Notifications
*/
private function get_sorted_notifications() {
$notifications = $this->get_notifications();
if ( empty( $notifications ) ) {
return [];
}
// Sort by severity, error first.
usort( $notifications, [ $this, 'sort_notifications' ] );
return $notifications;
}
/**
* Sort on type then priority
*
* @codeCoverageIgnore
*
* @param Notification $first Compare with B.
* @param Notification $second Compare with A.
* @return int 1, 0 or -1 for sorting offset.
*/
private function sort_notifications( Notification $first, Notification $second ) {
if ( 'error' === $first->args( 'type' ) ) {
return -1;
}
if ( 'error' === $second->args( 'type' ) ) {
return 1;
}
return 0;
}
/**
* Convert Notification to array representation
*
* @codeCoverageIgnore
*
* @param Notification $notification Notification to convert.
* @return array
*/
private function notification_to_array( Notification $notification ) {
return $notification->to_array();
}
/**
* Check if is network admin.
*
* @codeCoverageIgnore
*
* @return bool
*/
private function is_network_admin() {
return function_exists( 'is_network_admin' ) && is_network_admin();
}
/**
* Check if a notification with the given ID exists.
*
* @param string $id Notification ID.
* @return boolean
*/
public function has_notification( $id ) {
$notifications = $this->get_notifications();
foreach ( $notifications as $notification ) {
if ( isset( $notification->options['id'] ) && $notification->options['id'] === $id ) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,244 @@
<?php
/**
* The Notification
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin\Notifications;
use RankMath\Helpers\Str;
use RankMath\Helpers\HTML;
/**
* Notification class.
*/
class Notification {
/**
* Notification type.
*
* @var string
*/
const ERROR = 'error';
/**
* Notification type.
*
* @var string
*/
const SUCCESS = 'success';
/**
* Notification type.
*
* @var string
*/
const INFO = 'info';
/**
* Notification type.
*
* @var string
*/
const WARNING = 'warning';
/**
* Screen check.
*
* @var string
*/
const SCREEN_ANY = 'any';
/**
* User capability check.
*
* @var string
*/
const CAPABILITY_ANY = '';
/**
* The notification message.
*
* @var string
*/
public $message = '';
/**
* Contains optional arguments:
*
* - type: The notification type, i.e. 'updated' or 'error'
* - id: The ID of the notification
* - dismissal_key: Option name to save dismissal information in, ID will be used if not supplied.
* - screen: Only display on plugin page or on every page.
*
* @var array Options of this Notification.
*/
public $options = [];
/**
* Internal flag for whether notifications has been displayed.
*
* @var bool
*/
private $displayed = false;
/**
* Notification class constructor.
*
* @param string $message Message string.
* @param array $options Set of options.
*/
public function __construct( $message, $options = [] ) {
$this->message = $message;
$this->options = wp_parse_args(
$options,
[
'id' => '',
'classes' => '',
'type' => self::SUCCESS,
'screen' => self::SCREEN_ANY,
'capability' => self::CAPABILITY_ANY,
]
);
}
/**
* Adds string (view) behaviour to the Notification.
*
* @codeCoverageIgnore
*
* @return string
*/
public function __toString() {
return $this->render();
}
/**
* Return data from options.
*
* @param string $id Data to get.
* @return mixed
*/
public function args( $id ) {
return $this->options[ $id ];
}
/**
* Is this Notification persistent.
*
* @codeCoverageIgnore
*
* @return bool True if persistent, False if fire and forget.
*/
public function is_persistent() {
return ! empty( $this->args( 'id' ) );
}
/**
* Is this notification displayed.
*
* @codeCoverageIgnore
*
* @return bool
*/
public function is_displayed() {
return $this->displayed;
}
/**
* Can display on current screen.
*
* @codeCoverageIgnore
*
* @return bool
*/
public function can_display() {
// Removed.
if ( $this->displayed ) {
return false;
}
$screen = get_current_screen();
if ( self::SCREEN_ANY === $this->args( 'screen' ) || Str::contains( $this->args( 'screen' ), $screen->id ) ) {
$this->displayed = true;
}
if ( self::CAPABILITY_ANY !== $this->args( 'capability' ) && ! current_user_can( $this->args( 'capability' ) ) ) {
$this->displayed = false;
}
return $this->displayed;
}
/**
* Dismiss persisten notification.
*
* @codeCoverageIgnore
*/
public function dismiss() {
$this->displayed = true;
$this->options['id'] = '';
}
/**
* Return the object properties as an array.
*
* @codeCoverageIgnore
*
* @return array
*/
public function to_array() {
return [
'message' => $this->message,
'options' => $this->options,
];
}
/**
* Renders the notification as a string.
*
* @codeCoverageIgnore
*
* @return string The rendered notification.
*/
public function render() {
$attributes = [];
// Default notification classes.
$classes = [
'notice',
'notice-' . $this->args( 'type' ),
$this->args( 'classes' ),
];
// Maintain WordPress visualisation of alerts when they are not persistent.
if ( $this->is_persistent() ) {
$classes[] = 'is-dismissible';
$classes[] = 'wp-helpers-notice';
$attributes['id'] = $this->args( 'id' );
$attributes['data-security'] = wp_create_nonce( $this->args( 'id' ) );
}
if ( ! empty( $classes ) ) {
$attributes['class'] = implode( ' ', array_filter( $classes ) );
}
// Build the output DIV.
$output = '<div' . HTML::attributes_to_string( $attributes ) . '>' . wpautop( $this->message ) . '</div>' . PHP_EOL;
/**
* Filter: 'wp_helpers_notifications_render' - Allows developer to filter notifications before the output is finalized.
*
* @param string $output HTML output.
* @param string $message Notice message.
* @param array $options Notice args.
*/
$output = apply_filters( 'wp_helpers_notifications_render', $output, $this->message, $this->options );
return $output;
}
}

View File

@@ -0,0 +1,129 @@
<?php
/**
* Dashboard help tab template.
*
* @package RankMath
* @subpackage RankMath\Admin
*/
use RankMath\KB;
use RankMath\Helper;
defined( 'ABSPATH' ) || exit;
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
if ( Helper::has_cap( 'general' ) ) {
include_once 'plugin-activation.php';
}
require_once 'plugin-activation.php'; ?>
<div class="two-col rank-math-box-help">
<div class="col rank-math-box">
<header>
<h3><?php esc_html_e( 'Next steps&hellip;', 'rank-math' ); ?></h3>
</header>
<div class="rank-math-box-content">
<ul class="rank-math-list-icon">
<li>
<?php if ( ! defined( 'RANK_MATH_PRO_FILE' ) ) { ?>
<a href="<?php KB::the( 'pro', 'Help Tab PRO Link' ); ?>" target="_blank">
<i class="rm-icon rm-icon-star-filled"></i>
<div>
<strong><?php esc_html_e( 'Upgrade to PRO', 'rank-math' ); ?></strong>
<p><?php esc_html_e( 'Advanced Schema, Analytics and much more...', 'rank-math' ); ?></p>
</div>
</a>
<?php } else { ?>
<a href="<?php KB::the( 'how-to-setup', 'Help Tab Setup KB' ); ?>" target="_blank">
<i class="rm-icon rm-icon-settings"></i>
<div>
<strong><?php esc_html_e( 'Setup Rank Math', 'rank-math' ); ?></strong>
<p><?php esc_html_e( 'How to Properly Setup Rank Math', 'rank-math' ); ?></p>
</div>
</a>
<?php } ?>
</li>
<li>
<a href="<?php KB::the( 'seo-import', 'Help Tab Import Data' ); ?>" target="_blank">
<i class="rm-icon rm-icon-import"></i>
<div>
<strong><?php esc_html_e( 'Import Data', 'rank-math' ); ?></strong>
<p><?php esc_html_e( 'How to Import Data from Your Previous SEO Plugin', 'rank-math' ); ?></p>
</div>
</a>
</li>
<li>
<a href="<?php KB::the( 'score-100', 'Help Tab Score KB' ); ?>" target="_blank">
<i class="rm-icon rm-icon-post"></i>
<div>
<strong><?php esc_html_e( 'Improve SEO Score', 'rank-math' ); ?></strong>
<p><?php esc_html_e( 'How to Make Your Posts Pass All the Tests', 'rank-math' ); ?></p>
</div>
</a>
</li>
</ul>
</div>
</div>
<div class="col rank-math-box">
<header>
<h3><?php esc_html_e( 'Product Support', 'rank-math' ); ?></h3>
</header>
<div class="rank-math-box-content">
<ul class="rank-math-list-icon">
<li>
<a href="<?php KB::the( 'kb-seo-suite', 'Help Tab KB Link' ); ?>" target="_blank">
<i class="rm-icon rm-icon-help"></i>
<div>
<strong><?php esc_html_e( 'Online Documentation', 'rank-math' ); ?></strong>
<p><?php esc_html_e( 'Understand all the capabilities of Rank Math', 'rank-math' ); ?></p>
</div>
</a>
</li>
<li>
<a href="<?php KB::the( 'support', 'Help Tab Ticket' ); ?>" target="_blank">
<i class="rm-icon rm-icon-support"></i>
<div>
<strong><?php esc_html_e( 'Ticket Support', 'rank-math' ); ?></strong>
<p><?php esc_html_e( 'Direct help from our qualified support team', 'rank-math' ); ?></p>
</div>
</a>
</li>
<li>
<a href="<?php KB::the( 'help-affiliate', 'Help Tab Aff Link' ); ?>" target="_blank">
<i class="rm-icon rm-icon-sitemap"></i>
<div>
<strong><?php esc_html_e( 'Affiliate Program', 'rank-math' ); ?></strong>
<p><?php esc_html_e( 'Earn flat 30% on every sale!', 'rank-math' ); ?></p>
</div>
</a>
</li>
</ul>
</div>
</div>
</div><!--.two-col-->
</div><!--.dashboard-wrapper-->

View File

@@ -0,0 +1,11 @@
<?php
/**
* Dashboard page template.
*
* @package RankMath
* @subpackage RankMath\Admin
*/
defined( 'ABSPATH' ) || exit;
?>
<div id="rank-math-dashboard-page"></div>

View File

@@ -0,0 +1 @@
<?php // Silence is golden.

View File

@@ -0,0 +1,54 @@
<?php
/**
* Plugin activation template.
*
* @package RankMath
* @subpackage RankMath\Admin
*/
use RankMath\Helper;
use RankMath\Admin\Admin_Helper;
defined( 'ABSPATH' ) || exit;
$is_registered = Helper::is_site_connected();
$class = $is_registered ? 'status-green' : 'status-red';
$activate_url = Admin_Helper::get_activate_url();
$site_url_valid = Admin_Helper::is_site_url_valid();
$button_class = 'button button-primary button-connect' . ( $site_url_valid ? ' button-animated' : ' disabled' );
?>
<div class="rank-math-ui dashboard-wrapper container help">
<div class="rank-math-box <?php echo esc_attr( $class ); ?>">
<header>
<h3><?php esc_html_e( 'Account', 'rank-math' ); ?></h3>
<span class="button button-large <?php echo esc_attr( $class ); ?>"><?php echo $is_registered ? '<i class="rm-icon rm-icon-tick"></i>' . esc_html__( 'Connected', 'rank-math' ) : '<i class="rm-icon rm-icon-cross"></i>' . esc_html__( 'Not Connected', 'rank-math' ); ?></span>
</header>
<div class="rank-math-box-content rank-math-ui">
<form method="post" action="">
<input type="hidden" name="registration-action" value="<?php echo $is_registered ? 'deregister' : 'register'; ?>">
<?php wp_nonce_field( 'rank_math_register_product' ); ?>
<?php if ( ! $is_registered ) : ?>
<?php // translators: variables used to wrap the text in the strong tag. ?>
<p><?php printf( wp_kses_post( __( 'The plugin is currently not connected with your Rank Math account. Click on the button below to login or register for FREE using your %1$sGoogle account, Facebook account%2$s or %1$syour email account%2$s.', 'rank-math' ) ), '<strong>', '</strong>' ); ?></p>
<?php Admin_Helper::maybe_show_invalid_siteurl_notice(); ?>
<a href="<?php echo esc_url( $activate_url ); ?>" class="<?php echo esc_attr( $button_class ); ?>" ><?php esc_html_e( 'Connect Now', 'rank-math' ); ?></a>
<?php else : ?>
<?php // translators: variables used to wrap the text in the strong tag. ?>
<p><?php printf( wp_kses_post( __( 'You have successfully activated Rank Math. If you find the plugin useful, %1$s feel free to recommend it to your friends or colleagues %2$s.', 'rank-math' ) ), '<strong>', '</strong>' ); ?><?php Admin_Helper::get_social_share(); ?></p>
<div class="frm-submit">
<button type="submit" class="button button-link-delete button-xlarge" name="button"><?php echo esc_html__( 'Disconnect Account', 'rank-math' ); ?></button>
</div>
<?php endif; ?>
</form>
</div>
</div>

View File

@@ -0,0 +1,286 @@
<?php
/**
* The conflicting plugin watcher.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin;
use RankMath\Runner;
use RankMath\Traits\Hooker;
use RankMath\Helpers\Security;
use RankMath\Helpers\Param;
use RankMath\Helper as GlobalHelper;
defined( 'ABSPATH' ) || exit;
/**
* Watcher class.
*/
class Watcher implements Runner {
use Hooker;
/**
* Register hooks.
*/
public function hooks() {
$this->action( 'init', 'maybe_deactivate_plugins' );
$this->action( 'activated_plugin', 'check_activated_plugin' );
$this->action( 'deactivated_plugin', 'check_deactivated_plugin' );
$this->filter( 'wp_helpers_notifications_render', 'deactivate_plugins_link', 10, 3 );
$this->action( 'update_option_blog_public', 'check_search_engine_visibility' );
}
/**
* Set/Deactivate conflicting SEO or Sitemap plugins.
*/
public function maybe_deactivate_plugins() {
if ( ! Param::get( 'rank_math_deactivate_plugins' ) ) {
return;
}
if ( ! current_user_can( 'deactivate_plugins' ) ) {
wp_die( esc_html__( 'Sorry, you are not allowed to deactivate plugins for this site.', 'rank-math' ) );
}
check_admin_referer( 'rank_math_deactivate_plugins' );
$type = Param::get( 'plugin_type', 'seo', FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_BACKTICK );
$allowed = [ 'seo', 'sitemap' ];
if ( ! in_array( $type, $allowed, true ) ) {
return;
}
$this->deactivate_conflicting_plugins( $type );
}
/**
* Function to run when new plugin is activated.
*/
public static function check_activated_plugin() {
$set = [];
$plugins = get_option( 'active_plugins', [] );
foreach ( self::get_conflicting_plugins() as $plugin => $type ) {
if ( ! isset( $set[ $type ] ) && in_array( $plugin, $plugins, true ) ) {
$set[ $type ] = true;
self::set_notification( $type );
}
}
if ( in_array( 'wpml-string-translation/plugin.php', $plugins, true ) ) {
GlobalHelper::remove_notification( 'convert_wpml_settings' );
}
}
/**
* Function to run when plugin is deactivated.
*
* @param string $plugin Deactivated plugin path.
*/
public function check_deactivated_plugin( $plugin ) {
$plugins = self::get_conflicting_plugins();
if ( ! isset( $plugins[ $plugin ] ) ) {
return;
}
$this->remove_notification( $plugins[ $plugin ], $plugin );
}
/**
* Function to run when Module is enabled/disabled.
*
* @param string $module Module.
* @param string $state Module state.
*/
public static function module_changed( $module, $state ) {
if ( ! in_array( $module, [ 'sitemap', 'redirections', 'rich-snippet' ], true ) ) {
return;
}
if ( 'off' === $state ) {
$type = 'sitemap' === $module ? 'sitemap' : 'seo';
GlobalHelper::remove_notification( "conflicting_{$type}_plugins" );
}
self::check_activated_plugin();
}
/**
* Deactivate conflicting plugins.
*
* @param string $type Plugin type.
*/
private function deactivate_conflicting_plugins( $type ) {
foreach ( self::get_conflicting_plugins() as $plugin => $plugin_type ) {
if ( $type === $plugin_type && is_plugin_active( $plugin ) ) {
if ( ! current_user_can( 'deactivate_plugin', $plugin ) ) {
$message = sprintf(
/* translators: plugin name */
esc_html__( 'You are not allowed to deactivate this plugin: %s.', 'rank-math' ),
esc_html( $plugin )
);
GlobalHelper::add_notification(
$message,
[
'type' => 'error',
'classes' => 'is-dismissible',
]
);
continue;
}
deactivate_plugins( $plugin );
}
}
GlobalHelper::redirect( Security::remove_query_arg_raw( [ 'rank_math_deactivate_plugins', 'plugin_type', '_wpnonce' ] ) );
}
/**
* Function to set conflict plugin notification.
*
* @param string $type Plugin type.
*/
private static function set_notification( $type ) {
$message = sprintf(
/* translators: deactivation link */
esc_html__( 'Please keep only one SEO plugin active, otherwise, you might lose your rankings and traffic. %s.', 'rank-math' ),
'<a href="###DEACTIVATE_SEO_PLUGINS###">' . __( 'Click here to Deactivate', 'rank-math' ) . '</a>'
);
if ( 'sitemap' === $type ) {
$message = sprintf(
/* translators: deactivation link */
esc_html__( 'Please keep only one Sitemap plugin active, otherwise, you might lose your rankings and traffic. %s.', 'rank-math' ),
'<a href="###DEACTIVATE_SITEMAP_PLUGINS###">' . __( 'Click here to Deactivate', 'rank-math' ) . '</a>'
);
}
GlobalHelper::add_notification(
$message,
[
'id' => "conflicting_{$type}_plugins",
'type' => 'error',
'classes' => 'is-dismissible',
]
);
}
/**
* Function to remove conflict plugin notification.
*
* @param string $type Plugin type.
* @param string $plugin Plugin name.
*/
private function remove_notification( $type, $plugin ) {
foreach ( self::get_conflicting_plugins() as $file => $plugin_type ) {
if ( $plugin !== $file && $type === $plugin_type && is_plugin_active( $file ) ) {
return;
}
}
GlobalHelper::remove_notification( "conflicting_{$type}_plugins" );
}
/**
* Function to get all conflicting plugins.
*
* @return array
*/
private static function get_conflicting_plugins() {
$plugins = [
'wordpress-seo/wp-seo.php' => 'seo',
'wordpress-seo-premium/wp-seo-premium.php' => 'seo',
'wpseo-local/local-seo.php' => 'seo',
'wpseo-news/wpseo-news.php' => 'seo',
'wpseo-video/video-seo.php' => 'seo',
'all-in-one-seo-pack/all_in_one_seo_pack.php' => 'seo',
'all-in-one-seo-pack-pro/all_in_one_seo_pack.php' => 'seo',
'wp-seopress/seopress.php' => 'seo',
'wp-seopress-pro/seopress-pro.php' => 'seo',
];
if ( GlobalHelper::is_module_active( 'redirections' ) ) {
$plugins['redirection/redirection.php'] = 'seo';
}
if ( GlobalHelper::is_module_active( 'sitemap' ) ) {
$plugins['google-sitemap-generator/sitemap.php'] = 'sitemap';
$plugins['xml-sitemap-feed/xml-sitemap.php'] = 'sitemap';
}
if ( GlobalHelper::is_module_active( 'rich-snippet' ) ) {
$plugins['wp-schema-pro/wp-schema-pro.php'] = 'seo';
$plugins['all-in-one-schemaorg-rich-snippets/index.php'] = 'seo';
}
return $plugins;
}
/**
* Replace link inside notice dynamically to avoid issues with the nonce.
*
* @param string $output Notice output.
* @param string $message Notice message.
* @param array $options Notice options.
*
* @return string
*/
public function deactivate_plugins_link( $output, $message, $options ) {
if ( ! isset( $options['id'] ) || ! preg_match( '/conflicting_.*_plugins/', $options['id'] ) ) {
return $output;
}
$deactivate_url = Security::add_query_arg(
[
'rank_math_deactivate_plugins' => '1',
'plugin_type' => 'seo',
'_wpnonce' => wp_create_nonce( 'rank_math_deactivate_plugins' ),
],
admin_url( 'plugins.php' )
);
$output = str_replace( '###DEACTIVATE_SEO_PLUGINS###', $deactivate_url, $output );
$deactivate_sitemap_plugins_url = Security::add_query_arg(
[
'rank_math_deactivate_plugins' => '1',
'plugin_type' => 'sitemap',
'_wpnonce' => wp_create_nonce( 'rank_math_deactivate_plugins' ),
],
admin_url( 'plugins.php' )
);
$output = str_replace( '###DEACTIVATE_SITEMAP_PLUGINS###', $deactivate_sitemap_plugins_url, $output );
return $output;
}
/**
* Check search visibility
*
* @param integer $value Setting value.
*/
public function check_search_engine_visibility( $value ) {
if ( ! $value ) {
GlobalHelper::remove_notification( 'search_engine_visibility' );
return;
}
GlobalHelper::add_notification(
sprintf(
// translators: %1$s: general reading settings URL.
__( '<strong>SEO Notice</strong>: Your site is set to No Index and will not appear in search engines. You can change the Search engine visibility <a href="%1$s">from here</a>.', 'rank-math' ),
admin_url( 'options-reading.php' )
),
[
'type' => 'warning',
'id' => 'search_engine_visibility',
'classes' => 'is-dismissible',
]
);
}
}

View File

@@ -0,0 +1,183 @@
<?php
/**
* The Compatibility wizard step.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Wizard
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Wizard;
use RankMath\Helper;
defined( 'ABSPATH' ) || exit;
/**
* Step class.
*/
class Compatibility implements Wizard_Step {
/**
* Get Localized data to be used in the Compatibility step.
*
* @return array
*/
public static function get_localized_data() {
$php_version = phpversion();
return [
'conflictingPlugins' => self::get_conflicting_plugins(),
'phpVersion' => phpversion(),
'phpVersionOk' => version_compare( $php_version, rank_math()->php_version, '>' ),
'phpVersionRecommended' => version_compare( $php_version, '7.4', '<' ),
'extensions' => [
'dom' => extension_loaded( 'dom' ),
'simpleXml' => extension_loaded( 'SimpleXML' ),
'image' => extension_loaded( 'gd' ) || extension_loaded( 'imagick' ),
'mbString' => extension_loaded( 'mbstring' ),
'openSsl' => extension_loaded( 'openssl' ),
'base64Func' => function_exists( 'base64_encode' ) && function_exists( 'base64_decode' ) && (bool) base64_decode( base64_encode( '1' ) ), // phpcs:ignore -- Verified as safe usage.
],
];
}
/**
* Save handler for step.
*
* @param array $values Values to save.
*
* @return bool
*/
public static function save( $values ) {
$settings = wp_parse_args(
rank_math()->settings->all_raw(),
[ 'general' => '' ]
);
$settings['general']['setup_mode'] = ! empty( $values['setup_mode'] ) ? sanitize_text_field( $values['setup_mode'] ) : 'easy';
if ( 'custom' === $settings['general']['setup_mode'] ) {
// Don't change, use custom imported value.
return true;
}
Helper::update_all_settings( $settings['general'], null, null );
return true;
}
/**
* Get conflicting plugins.
*
* @return array
*/
private static function get_conflicting_plugins() {
$plugins_found = [];
$active_plugins = get_option( 'active_plugins' );
$conflicting_plugins = self::get_conflicting_plugins_list();
foreach ( $conflicting_plugins as $plugin_slug => $plugin_name ) {
if ( in_array( $plugin_slug, $active_plugins, true ) !== false ) {
$plugins_found[ $plugin_slug ] = $plugin_name;
}
}
return $plugins_found;
}
/**
* Return list of conflicting plugins.
*
* @return array List of plugins in path => name format.
*/
private static function get_conflicting_plugins_list() {
$plugins = [
'2-click-socialmedia-buttons/2-click-socialmedia-buttons.php' => '2 Click Social Media Buttons.',
'add-link-to-facebook/add-link-to-facebook.php' => 'Add Link to Facebook.',
'extended-wp-reset/extended-wp-reset.php' => 'Extended WP Reset.',
'add-meta-tags/add-meta-tags.php' => 'Add Meta Tags.',
'all-in-one-seo-pack/all_in_one_seo_pack.php' => 'All In One SEO Pack',
'easy-facebook-share-thumbnails/esft.php' => 'Easy Facebook Share Thumbnail.',
'facebook/facebook.php' => 'Facebook (official plugin).',
'facebook-awd/AWD_facebook.php' => 'Facebook AWD All in one.',
'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php' => 'Facebook Featured Image & OG Meta Tags.',
'facebook-meta-tags/facebook-metatags.php' => 'Facebook Meta Tags.',
'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php' => 'Facebook Open Graph Meta Tags for WordPress.',
'facebook-revised-open-graph-meta-tag/index.php' => 'Facebook Revised Open Graph Meta Tag.',
'facebook-thumb-fixer/_facebook-thumb-fixer.php' => 'Facebook Thumb Fixer.',
'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php' => 'Fedmich\'s Facebook Open Graph Meta.',
'network-publisher/networkpub.php' => 'Network Publisher.',
'nextgen-facebook/nextgen-facebook.php' => 'NextGEN Facebook OG.',
'opengraph/opengraph.php' => 'Open Graph.',
'open-graph-protocol-framework/open-graph-protocol-framework.php' => 'Open Graph Protocol Framework.',
'seo-facebook-comments/seofacebook.php' => 'SEO Facebook Comments.',
'seo-ultimate/seo-ultimate.php' => 'SEO Ultimate.',
'sexybookmarks/sexy-bookmarks.php' => 'Shareaholic.',
'shareaholic/sexy-bookmarks.php' => 'Shareaholic.',
'sharepress/sharepress.php' => 'SharePress.',
'simple-facebook-connect/sfc.php' => 'Simple Facebook Connect.',
'social-discussions/social-discussions.php' => 'Social Discussions.',
'social-sharing-toolkit/social_sharing_toolkit.php' => 'Social Sharing Toolkit.',
'socialize/socialize.php' => 'Socialize.',
'only-tweet-like-share-and-google-1/tweet-like-plusone.php' => 'Tweet, Like, Google +1 and Share.',
'wordbooker/wordbooker.php' => 'Wordbooker.',
'wordpress-seo/wp-seo.php' => 'Yoast SEO',
'wordpress-seo-premium/wp-seo-premium.php' => 'Yoast SEO Premium',
'wp-seopress/seopress.php' => 'SEOPress',
'wp-seopress-pro/seopress-pro.php' => 'SEOPress Pro',
'wpsso/wpsso.php' => 'WordPress Social Sharing Optimization.',
'wp-caregiver/wp-caregiver.php' => 'WP Caregiver.',
'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php' => 'WP Facebook Like Send & Open Graph Meta.',
'wp-facebook-open-graph-protocol/wp-facebook-ogp.php' => 'WP Facebook Open Graph protocol.',
'wp-ogp/wp-ogp.php' => 'WP-OGP.',
'zoltonorg-social-plugin/zosp.php' => 'Zolton.org Social Plugin.',
'all-in-one-schemaorg-rich-snippets/index.php' => 'All In One Schema Rich Snippets.',
'wp-schema-pro/wp-schema-pro.php' => 'Schema Pro',
'no-category-base-wpml/no-category-base-wpml.php' => 'No Category Base (WPML)',
'all-404-redirect-to-homepage/all-404-redirect-to-homepage.php' => 'All 404 Redirect to Homepage',
'remove-category-url/remove-category-url.php' => 'Remove Category URL',
];
$plugins = Helper::is_module_active( 'redirections' ) ? array_merge( $plugins, self::get_redirection_conflicting_plugins() ) : $plugins;
$plugins = Helper::is_module_active( 'sitemap' ) ? array_merge( $plugins, self::get_sitemap_conflicting_plugins() ) : $plugins;
return $plugins;
}
/**
* Redirection: conflicting plugins.
*
* @return array
*/
private static function get_redirection_conflicting_plugins() {
return [
'redirection/redirection.php' => 'Redirection',
];
}
/**
* Sitemap: conflicting plugins.
*
* @return array
*/
private static function get_sitemap_conflicting_plugins() {
return [
'google-sitemap-plugin/google-sitemap-plugin.php' => 'Google Sitemap (BestWebSoft).',
'xml-sitemaps/xml-sitemaps.php' => 'XML Sitemaps (Denis de Bernardy and Mike Koepke).',
'bwp-google-xml-sitemaps/bwp-simple-gxs.php' => 'Better WordPress Google XML Sitemaps (Khang Minh).',
'google-sitemap-generator/sitemap.php' => 'Google XML Sitemaps (Arne Brachhold).',
'xml-sitemap-feed/xml-sitemap.php' => 'XML Sitemap & Google News feeds (RavanH).',
'google-monthly-xml-sitemap/monthly-xml-sitemap.php' => 'Google Monthly XML Sitemap (Andrea Pernici).',
'simple-google-sitemap-xml/simple-google-sitemap-xml.php' => 'Simple Google Sitemap XML (iTx Technologies).',
'another-simple-xml-sitemap/another-simple-xml-sitemap.php' => 'Another Simple XML Sitemap.',
'xml-maps/google-sitemap.php' => 'Xml Sitemap (Jason Martens).',
'google-xml-sitemap-generator-by-anton-dachauer/adachauer-google-xml-sitemap.php' => 'Google XML Sitemap Generator by Anton Dachauer (Anton Dachauer).',
'wp-xml-sitemap/wp-xml-sitemap.php' => 'WP XML Sitemap (Team Vivacity).',
'sitemap-generator-for-webmasters/sitemap.php' => 'Sitemap Generator for Webmasters (iwebslogtech).',
'xml-sitemap-xml-sitemapcouk/xmls.php' => 'XML Sitemap - XML-Sitemap.co.uk (Simon Hancox).',
'sewn-in-xml-sitemap/sewn-xml-sitemap.php' => 'Sewn In XML Sitemap (jcow).',
'rps-sitemap-generator/rps-sitemap-generator.php' => 'RPS Sitemap Generator (redpixelstudios).',
];
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* The Import wizard step
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Wizard
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Wizard;
use RankMath\KB;
use RankMath\Admin\Importers\Detector;
defined( 'ABSPATH' ) || exit;
/**
* Step class.
*/
class Import implements Wizard_Step {
/**
* Get Localized data to be used in the Analytics step.
*
* @return array
*/
public static function get_localized_data() {
$detector = new Detector();
$plugins = $detector->detect();
$plugins = self::set_priority( $plugins );
return [
'importablePlugins' => $plugins,
];
}
/**
* Save handler for step.
*
* @param array $values Values to save.
*
* @return bool
*/
public static function save( $values ) {
delete_option( 'rank_math_yoast_block_posts' );
return true;
}
/**
* Set plugins priority.
*
* @param array $plugins Array of detected plugins.
*
* @return array
*/
private static function set_priority( $plugins ) {
$checked = false;
$priority = array_intersect( [ 'seopress', 'yoast', 'yoast-premium', 'aioseo' ], array_keys( $plugins ) );
foreach ( $priority as $slug ) {
if ( ! $checked ) {
$checked = true;
$plugins[ $slug ]['checked'] = true;
continue;
}
$plugins[ $slug ]['checked'] = false;
}
return $plugins;
}
/**
* Get description for choice field.
*
* @param string $slug Plugin slug.
* @param array $plugin Plugin info array.
* @param boolean $is_active Is plugin active.
*
* @return string
*/
private function get_choice_description( $slug, $plugin, $is_active ) {
/* translators: 1 is plugin name */
$desc = 'aio-rich-snippet' === $slug ? esc_html__( 'Import meta data from the %1$s plugin.', 'rank-math' ) : esc_html__( 'Import settings and meta data from the %1$s plugin.', 'rank-math' );
/* translators: 2 is link to Knowledge Base article */
$desc .= ' ' . __( 'The process may take a few minutes if you have a large number of posts or pages <a href="%2$s" target="_blank">Learn more about the import process here.</a>', 'rank-math' );
if ( $is_active ) {
/* translators: 1 is plugin name */
$desc .= '<br>' . __( ' %1$s plugin will be disabled automatically moving forward to avoid conflicts. <strong>It is thus recommended to import the data you need now.</strong>', 'rank-math' );
}
return sprintf( wp_kses_post( $desc ), $plugin['name'], KB::get( 'seo-import', 'SW Import Step' ) );
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* The Monitor Redirection wizard step
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Wizard
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Wizard;
use RankMath\Helper;
defined( 'ABSPATH' ) || exit;
/**
* Step class.
*/
class Monitor_Redirection implements Wizard_Step {
/**
* Get Localized data to be used in the Compatibility step.
*
* @return array
*/
public static function get_localized_data() {
return [
'404-monitor' => Helper::is_module_active( '404-monitor' ),
'redirections' => Helper::is_module_active( 'redirections' ),
];
}
/**
* Save handler for step.
*
* @param array $values Values to save.
*
* @return bool
*/
public static function save( $values ) {
Helper::update_modules(
[
'404-monitor' => $values['404-monitor'] ? 'on' : 'off',
'redirections' => $values['redirections'] ? 'on' : 'off',
]
);
return true;
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* The Optimization wizard step
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Wizard
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Wizard;
use RankMath\Helper;
defined( 'ABSPATH' ) || exit;
/**
* Step class.
*/
class Optimization implements Wizard_Step {
/**
* Get Localized data to be used in the Compatibility step.
*
* @return array
*/
public static function get_localized_data() {
return [
'noindex_empty_taxonomies' => Helper::get_settings( 'titles.noindex_empty_taxonomies' ),
'nofollow_external_links' => Helper::get_settings( 'general.nofollow_external_links' ),
'new_window_external_links' => Helper::get_settings( 'general.new_window_external_links' ),
];
}
/**
* Save handler for step.
*
* @param array $values Values to save.
*
* @return bool
*/
public static function save( $values ) {
$settings = rank_math()->settings->all_raw();
$settings['titles']['noindex_empty_taxonomies'] = $values['noindex_empty_taxonomies'] ? 'on' : 'off';
if ( isset( $values['attachment_redirect_urls'] ) && 'on' === $values['attachment_redirect_urls'] ) {
$settings['general']['attachment_redirect_urls'] = 'on';
$settings['general']['attachment_redirect_default'] = sanitize_url( $values['attachment_redirect_default'] );
}
$settings['general']['nofollow_external_links'] = ! empty( $values['nofollow_external_links'] ) ? 'on' : 'off';
$settings['general']['new_window_external_links'] = $values['new_window_external_links'] ? 'on' : 'off';
Helper::update_all_settings( $settings['general'], $settings['titles'], null );
Helper::schedule_flush_rewrite();
return true;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* The Ready wizard step
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Wizard
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Wizard;
use RankMath\Helper;
defined( 'ABSPATH' ) || exit;
/**
* Step class.
*/
class Ready implements Wizard_Step {
/**
* Get Localized data to be used in the Compatibility step.
*
* @return array
*/
public static function get_localized_data() {
Helper::is_configured( true );
return [
'scoreImg' => esc_url( rank_math()->plugin_url() . 'assets/admin/img/score-100.png' ),
'dashboardUrl' => Helper::get_dashboard_url(),
'enable_auto_update' => boolval( Helper::get_auto_update_setting() ),
];
}
/**
* Save handler for step.
*
* @param array $values Values to save.
*
* @return bool
*/
public static function save( $values ) {
$value = ! empty( $values['enable_auto_update'] ) ? 'on' : 'off';
Helper::toggle_auto_update_setting( $value );
return true;
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* The Role wizard step
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Wizard
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Wizard;
use RankMath\Helper;
use RankMath\Role_Manager\Capability_Manager;
defined( 'ABSPATH' ) || exit;
/**
* Step class.
*/
class Role implements Wizard_Step {
/**
* Get Localized data to be used in the Compatibility step.
*
* @return array
*/
public static function get_localized_data() {
Helper::is_configured( true );
return array_merge(
Helper::get_roles_capabilities(),
[
'role_manager' => Helper::is_module_active( 'role-manager' ),
'roles' => Helper::get_roles(),
'capabilities' => Capability_Manager::get()->get_capabilities(),
]
);
}
/**
* Save handler for step.
*
* @param array $values Values to save.
*
* @return bool
*/
public static function save( $values ) {
if ( empty( $values ) ) {
return false;
}
Helper::update_modules( [ 'role-manager' => $values['role_manager'] ? 'on' : 'off' ] );
Helper::set_capabilities( $values );
return true;
}
}

View File

@@ -0,0 +1,111 @@
<?php
/**
* The Schema_Markup wizard step
*
* @since 1.0.32
* @package RankMath
* @subpackage RankMath\Wizard
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Wizard;
use RankMath\Helper;
defined( 'ABSPATH' ) || exit;
/**
* Step class.
*/
class Schema_Markup implements Wizard_Step {
/**
* Get Localized data to be used in the Compatibility step.
*
* @return array
*/
public static function get_localized_data() {
return array_merge(
self::get_default_values(),
[
'rich_snippet' => Helper::is_module_active( 'rich-snippet' ),
'accessiblePostTypes' => Helper::get_accessible_post_types(),
'knowledgegraph_type' => Helper::get_settings( 'titles.knowledgegraph_type' ),
'schemaTypes' => Helper::choices_rich_snippet_types( esc_html__( 'None (Click here to set one)', 'rank-math' ) ),
'reviewPosts' => Helper::get_review_posts(),
]
);
}
/**
* Save handler for step.
*
* @param array $values Values to save.
*
* @return bool
*/
public static function save( $values ) {
$settings = rank_math()->settings->all_raw();
Helper::update_modules( [ 'rich-snippet' => $values['rich_snippet'] ? 'on' : 'off' ] );
// Schema.
if ( $values['rich_snippet'] ) {
self::save_rich_snippet( $settings, $values );
}
Helper::update_all_settings( $settings['general'], $settings['titles'], null );
return Helper::get_admin_url();
}
/**
* Save rich snippet values for post type.
*
* @param array $settings Array of setting.
* @param array $values Values to save.
*/
private static function save_rich_snippet( &$settings, $values ) {
foreach ( Helper::get_accessible_post_types() as $post_type ) {
if ( 'attachment' === $post_type ) {
continue;
}
$id = 'pt_' . $post_type . '_default_rich_snippet';
$article_type = 'pt_' . $post_type . '_default_article_type';
$settings['titles'][ $id ] = sanitize_text_field( $values[ $id ] );
$settings['titles'][ $article_type ] = sanitize_text_field( $values[ $article_type ] );
}
}
/**
* Get Default values for the schemas used for Post types.
*/
private static function get_default_values() {
$richsnp_default = [
'post' => 'article',
'product' => 'product',
];
$data = [];
foreach ( Helper::get_accessible_post_types() as $post_type ) {
if ( 'attachment' === $post_type ) {
continue;
}
$field_id = 'pt_' . $post_type . '_default_rich_snippet';
$default = $post_type === 'product' ? 'product' : 'off';
$value = Helper::get_settings( 'titles.pt_' . $post_type . '_default_rich_snippet', ( isset( $richsnp_default[ $post_type ] ) ? $richsnp_default[ $post_type ] : $default ) );
$data[ $field_id ] = $value ? $value : 'off';
if ( $post_type === 'product' ) {
continue;
}
$data[ 'pt_' . $post_type . '_default_article_type' ] = Helper::get_settings( 'titles.pt_' . $post_type . '_default_article_type', 'post' === $post_type ? 'BlogPosting' : 'Article' );
}
return $data;
}
}

View File

@@ -0,0 +1,153 @@
<?php
/**
* The Search Console wizard step
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Wizard
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Wizard;
use RankMath\Helper;
use RankMath\Helpers\Param;
use RankMath\Admin\Admin_Helper;
use RankMath\Google\Authentication;
use RankMath\Google\Permissions;
use RankMath\Analytics\Email_Reports;
use RankMath\Analytics\Workflow\Objects;
use RankMath\Analytics\Workflow\Console;
use RankMath\Analytics\Workflow\Inspections;
defined( 'ABSPATH' ) || exit;
/**
* Step class.
*/
class Search_Console implements Wizard_Step {
/**
* Get Localized data to be used in the Analytics step.
*
* @return array
*/
public static function get_localized_data() {
$all_services = get_option(
'rank_math_analytics_all_services',
[
'isVerified' => '',
'inSearchConsole' => '',
'hasSitemap' => '',
'hasAnalytics' => '',
'hasAnalyticsProperty' => '',
'homeUrl' => '',
'sites' => '',
'accounts' => [],
'adsenseAccounts' => [],
]
);
$analytics = wp_parse_args(
get_option( 'rank_math_google_analytic_options' ),
[
'adsense_id' => '',
'account_id' => '',
'property_id' => '',
'view_id' => '',
'measurement_id' => '',
'stream_name' => '',
'country' => 'all',
'install_code' => false,
'anonymize_ip' => false,
'local_ga_js' => false,
'exclude_loggedin' => false,
]
);
$page = Param::get( 'page', '', FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_BACKTICK );
$page = in_array( $page, [ 'rank-math-options-general', 'rank-math-analytics' ], true ) ? 'rank-math-options-general' : 'rank-math-wizard&step=analytics';
$activate_url = Admin_Helper::get_activate_url( admin_url( 'admin.php?analytics=1&page=' . $page ) );
$profile = wp_parse_args(
get_option( 'rank_math_google_analytic_profile' ),
[
'profile' => '',
'country' => 'all',
'enable_index_status' => true,
'sites' => $all_services['sites'],
]
);
return [
'isSiteConnected' => Helper::is_site_connected(),
'isAuthorized' => Authentication::is_authorized(),
'isSiteUrlValid' => Admin_Helper::is_site_url_valid(),
'hasConsolePermission' => Permissions::has_console(),
'hasAnalyticsPermission' => Permissions::has_analytics(),
'hasAdsensePermission' => Permissions::has_adsense(),
'activateUrl' => $activate_url,
'authUrl' => Authentication::get_auth_url(),
'reconnectGoogleUrl' => wp_nonce_url( admin_url( 'admin.php?reconnect=google' ), 'rank_math_reconnect_google' ),
'showEmailReports' => ! Email_Reports::are_fields_hidden(),
'searchConsole' => $profile,
'console_email_reports' => Helper::get_settings( 'general.console_email_reports' ),
'analyticsData' => $analytics,
'allServices' => $all_services,
];
}
/**
* Save handler for step.
*
* @param array $values Values to save.
*
* @return bool
*/
public static function save( $values ) {
if ( isset( $values['console_email_reports'] ) ) {
$settings = rank_math()->settings->all_raw();
$settings['general']['console_email_reports'] = $values['console_email_reports'] ? 'on' : 'off';
Helper::update_all_settings( $settings['general'], null, null );
}
// For Search console.
if ( isset( $values['searchConsole'] ) && ! empty( $values['searchConsole'] ) ) {
$search_console_data = $values['searchConsole'];
$value = [
'country' => sanitize_text_field( $search_console_data['country'] ),
'profile' => sanitize_text_field( $search_console_data['profile'] ?? '' ),
'enable_index_status' => sanitize_text_field( $search_console_data['enable_index_status'] ),
];
update_option( 'rank_math_google_analytic_profile', $value );
}
// For Analytics.
if ( isset( $values['analyticsData'] ) && ! empty( $values['analyticsData'] ) ) {
$analytics_data = $values['analyticsData'];
$analytic_value = [
'adsense_id' => sanitize_text_field( $analytics_data['adsense_id'] ),
'account_id' => sanitize_text_field( $analytics_data['account_id'] ),
'property_id' => sanitize_text_field( $analytics_data['property_id'] ),
'view_id' => sanitize_text_field( $analytics_data['view_id'] ),
'measurement_id' => sanitize_text_field( $analytics_data['measurement_id'] ),
'stream_name' => sanitize_text_field( $analytics_data['stream_name'] ),
'country' => sanitize_text_field( $analytics_data['country'] ),
'install_code' => sanitize_text_field( $analytics_data['install_code'] ),
'anonymize_ip' => sanitize_text_field( $analytics_data['anonymize_ip'] ),
'local_ga_js' => sanitize_text_field( $analytics_data['local_ga_js'] ),
'exclude_loggedin' => sanitize_text_field( $analytics_data['exclude_loggedin'] ),
];
update_option( 'rank_math_google_analytic_options', $analytic_value );
}
$page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
if ( 'rank-math-wizard' === $page ) {
new Objects();
new Console();
new Inspections();
}
return true;
}
}

View File

@@ -0,0 +1,151 @@
<?php
/**
* The Sitemap wizard step
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Wizard
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Wizard;
use RankMath\Helper;
defined( 'ABSPATH' ) || exit;
/**
* Step class.
*/
class Sitemap implements Wizard_Step {
/**
* Get Localized data to be used in the Compatibility step.
*
* @return array
*/
public static function get_localized_data() {
$post_types = self::get_post_types();
$taxonomies = self::get_taxonomies();
return [
'sitemap' => Helper::is_module_active( 'sitemap' ),
'include_images' => Helper::get_settings( 'sitemap.include_images' ),
'postTypes' => $post_types['post_types'],
'sitemap_post_types' => $post_types['defaults'],
'taxonomies' => $taxonomies['taxonomies'],
'sitemap_taxonomies' => $taxonomies['defaults'],
];
}
/**
* Save handler for step.
*
* @param array $values Values to save.
*
* @return bool
*/
public static function save( $values ) {
$settings = rank_math()->settings->all_raw();
Helper::update_modules( [ 'sitemap' => $values['sitemap'] ? 'on' : 'off' ] );
if ( $values['sitemap'] ) {
$settings['sitemap']['include_images'] = $values['include_images'] ? 'on' : 'off';
$settings = self::save_post_types( $settings, $values );
$settings = self::save_taxonomies( $settings, $values );
Helper::update_all_settings( null, null, $settings['sitemap'] );
}
Helper::schedule_flush_rewrite();
return true;
}
/**
* Get post type data.
*
* @return array
*/
private static function get_post_types() {
$p_defaults = [];
$post_types = Helper::choices_post_types();
if ( Helper::get_settings( 'general.attachment_redirect_urls', true ) ) {
unset( $post_types['attachment'] );
}
foreach ( $post_types as $post_type => $object ) {
if ( true === Helper::get_settings( "sitemap.pt_{$post_type}_sitemap" ) ) {
$p_defaults[] = $post_type;
}
}
return [
'defaults' => $p_defaults,
'post_types' => $post_types,
];
}
/**
* Get taxonomies data.
*
* @return array
*/
private static function get_taxonomies() {
$t_defaults = [];
$taxonomies = Helper::get_accessible_taxonomies();
unset( $taxonomies['post_tag'], $taxonomies['post_format'], $taxonomies['product_tag'] );
$taxonomies = wp_list_pluck( $taxonomies, 'label', 'name' );
foreach ( $taxonomies as $taxonomy => $label ) {
if ( true === Helper::get_settings( "sitemap.tax_{$taxonomy}_sitemap" ) ) {
$t_defaults[] = $taxonomy;
}
}
return [
'defaults' => $t_defaults,
'taxonomies' => $taxonomies,
];
}
/**
* Save Post Types
*
* @param array $settings Array of all settings.
* @param array $values Array of posted values.
*
* @return array
*/
private static function save_post_types( $settings, $values ) {
$post_types = Helper::choices_post_types();
if ( ! isset( $values['sitemap_post_types'] ) ) {
$values['sitemap_post_types'] = [];
}
foreach ( $post_types as $post_type => $object ) {
$settings['sitemap'][ "pt_{$post_type}_sitemap" ] = in_array( $post_type, $values['sitemap_post_types'], true ) ? 'on' : 'off';
}
return $settings;
}
/**
* Save Taxonomies
*
* @param array $settings Array of all settings.
* @param array $values Array of posted values.
*
* @return array
*/
private static function save_taxonomies( $settings, $values ) {
$taxonomies = Helper::get_accessible_taxonomies();
$taxonomies = wp_list_pluck( $taxonomies, 'label', 'name' );
if ( ! isset( $values['sitemap_taxonomies'] ) ) {
$values['sitemap_taxonomies'] = [];
}
foreach ( $taxonomies as $taxonomy => $label ) {
$settings['sitemap'][ "tax_{$taxonomy}_sitemap" ] = in_array( $taxonomy, $values['sitemap_taxonomies'], true ) ? 'on' : 'off';
}
return $settings;
}
}

View File

@@ -0,0 +1,279 @@
<?php
/**
* The Your Site wizard step
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Wizard
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Wizard;
use RankMath\Helper;
use RankMath\Admin\Admin_Helper;
defined( 'ABSPATH' ) || exit;
/**
* Step class.
*/
class Your_Site implements Wizard_Step {
/**
* Get Localized data to be used in the Compatibility step.
*
* @return array
*/
public static function get_localized_data() {
$displayname = self::get_site_display_name();
$data = [
'site_type' => self::get_default_site_type(),
'businessTypesChoices' => Helper::choices_business_types(),
'business_type' => Helper::get_settings( 'titles.local_business_type' ),
'website_name' => Helper::get_settings( 'titles.website_name', $displayname ),
'website_alternate_name' => Helper::get_settings( 'titles.website_alternate_name', '' ),
'company_name' => Helper::get_settings( 'titles.knowledgegraph_name', $displayname ),
'open_graph_image' => Helper::get_settings( 'titles.open_graph_image' ),
];
$company_logo = self::get_default_logo();
if ( $company_logo ) {
$data['company_logo'] = $company_logo;
$data['company_logo_id'] = attachment_url_to_postid( $company_logo );
}
$open_graph_image = Helper::get_settings( 'titles.open_graph_image' );
if ( $open_graph_image ) {
$data['open_graph_image'] = $open_graph_image;
$data['open_graph_image_id'] = attachment_url_to_postid( $open_graph_image );
}
return $data;
}
/**
* Save handler for step.
*
* @param array $values Values to save.
*
* @return bool
*/
public static function save( $values ) {
$settings = wp_parse_args(
rank_math()->settings->all_raw(),
[
'titles' => '',
'sitemap' => '',
]
);
$values = wp_parse_args(
$values,
[
'company_name' => '',
'company_logo' => '',
'company_logo_id' => '',
'open_graph_image' => '',
'open_graph_image_id' => '',
]
);
// Save these settings.
$functions = [ 'save_local_seo', 'save_open_graph', 'save_post_types', 'save_taxonomies' ];
foreach ( $functions as $function ) {
$settings = self::$function( $settings, $values );
}
$business_type = [ 'news', 'business', 'webshop', 'otherbusiness' ];
$modules = [ 'local-seo' => in_array( $values['site_type'], $business_type, true ) ? 'on' : 'off' ];
$users = get_users( [ 'role__in' => [ 'administrator', 'editor', 'author', 'contributor' ] ] );
if ( count( $users ) > 1 && ! is_plugin_active( 'members/members.php' ) ) {
$modules['role-manager'] = 'on';
}
set_transient( '_rank_math_site_type', sanitize_text_field( $values['site_type'] ) );
Helper::update_modules( $modules );
Helper::update_all_settings( null, $settings['titles'], null );
return true;
}
/**
* Save Local Seo
*
* @param array $settings Array of all settings.
* @param array $values Array of posted values.
*
* @return array
*/
private static function save_local_seo( $settings, $values ) {
switch ( $values['site_type'] ) {
case 'blog':
case 'portfolio':
$settings['titles']['knowledgegraph_type'] = 'person';
$settings['titles']['knowledgegraph_name'] = sanitize_text_field( $values['company_name'] );
$settings['titles']['knowledgegraph_logo'] = sanitize_url( $values['company_logo'] );
$settings['titles']['knowledgegraph_logo_id'] = absint( $values['company_logo_id'] );
break;
case 'news':
case 'webshop':
case 'business':
case 'otherbusiness':
$settings['titles']['knowledgegraph_type'] = 'company';
$settings['titles']['knowledgegraph_name'] = sanitize_text_field( $values['company_name'] );
$settings['titles']['knowledgegraph_logo'] = sanitize_url( $values['company_logo'] );
$settings['titles']['local_business_type'] = sanitize_text_field( $values['business_type'] );
$settings['titles']['knowledgegraph_logo_id'] = absint( $values['company_logo_id'] );
break;
case 'otherpersonal':
$settings['titles']['knowledgegraph_type'] = 'person';
$settings['titles']['knowledgegraph_name'] = sanitize_text_field( $values['company_name'] );
break;
}
foreach ( [ 'website_name', 'website_alternate_name' ] as $key ) {
if ( empty( $values[ $key ] ) ) {
continue;
}
$settings['titles'][ $key ] = sanitize_text_field( $values[ $key ] );
}
return $settings;
}
/**
* Save Open Graph
*
* @param array $settings Array of all settings.
* @param array $values Array of posted values.
*
* @return array
*/
private static function save_open_graph( $settings, $values ) {
if ( ! empty( $values['open_graph_image_id'] ) ) {
$settings['titles']['open_graph_image'] = sanitize_url( $values['open_graph_image'] );
$settings['titles']['open_graph_image_id'] = absint( $values['open_graph_image_id'] );
}
if ( empty( $values['company_logo_id'] ) ) {
unset( $settings['titles']['knowledgegraph_logo'] );
unset( $settings['titles']['knowledgegraph_logo_id'] );
}
return $settings;
}
/**
* Save Post Types
*
* @param array $settings Array of all settings.
* @param array $values Array of posted values.
*
* @return array
*/
private static function save_post_types( $settings, $values ) {
foreach ( Helper::get_accessible_post_types() as $post_type => $label ) {
if ( 'attachment' === $post_type ) {
continue;
}
$settings['titles'][ "pt_{$post_type}_add_meta_box" ] = 'on';
}
return $settings;
}
/**
* Save Taxonomies
*
* @param array $settings Array of all settings.
* @param array $values Array of posted values.
*
* @return array
*/
private static function save_taxonomies( $settings, $values ) {
$taxonomies = Admin_Helper::get_taxonomies_options();
array_shift( $taxonomies );
foreach ( $taxonomies as $taxonomy => $label ) {
$settings['titles'][ "tax_{$taxonomy}_add_meta_box" ] = 'on';
}
return $settings;
}
/**
* Get site display name.
*
* @return string
*/
protected static function get_site_display_name() {
$siteurl = get_bloginfo( 'url' );
$sitename = get_bloginfo( 'title' );
return $sitename ? $sitename : $siteurl;
}
/**
* Get default logo.
*
* @return string
*/
private static function get_default_logo() {
if ( defined( 'MTS_THEME_NAME' ) && MTS_THEME_NAME ) {
$theme_options = get_option( MTS_THEME_NAME );
if ( isset( $theme_options['mts_logo'] ) ) {
return wp_get_attachment_url( $theme_options['mts_logo'] );
}
}
if ( current_theme_supports( 'custom-logo' ) && ! empty( get_theme_mod( 'custom_logo' ) ) ) {
return wp_get_attachment_url( get_theme_mod( 'custom_logo' ) );
}
return Helper::get_settings( 'titles.knowledgegraph_logo' );
}
/**
* Get default site type.
*
* @return string
*/
private static function get_default_site_type() {
$default_type = get_transient( '_rank_math_site_type' );
return $default_type ? $default_type : ( class_exists( 'Easy_Digital_Downloads' ) || class_exists( 'WooCommerce' ) ? 'webshop' : 'blog' );
}
/**
* Get type dependecy.
*
* @return array
*/
private function get_type_dependency() {
return [
[ 'site_type', 'news' ],
[ 'site_type', 'business' ],
[ 'site_type', 'webshop' ],
[ 'site_type', 'otherbusiness' ],
];
}
/**
* Get type choices.
*
* @return array
*/
private function get_type_choices() {
return [
'blog' => esc_html__( 'Personal Blog', 'rank-math' ),
'news' => esc_html__( 'Community Blog/News Site', 'rank-math' ),
'portfolio' => esc_html__( 'Personal Portfolio', 'rank-math' ),
'business' => esc_html__( 'Small Business Site', 'rank-math' ),
'webshop' => esc_html__( 'Webshop', 'rank-math' ),
'otherpersonal' => esc_html__( 'Other Personal Website', 'rank-math' ),
'otherbusiness' => esc_html__( 'Other Business Website', 'rank-math' ),
];
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* The wizard step contract.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Wizard
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Wizard;
defined( 'ABSPATH' ) || exit;
/**
* Wizard step contract.
*/
interface Wizard_Step {
/**
* Localized data to be used in the step.
*
* @return array
*/
public static function get_localized_data();
/**
* Save handler for step.
*
* @param array $values Values to save.
*
* @return bool
*/
public static function save( $values );
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Setup wizard content template.
*
* @package RankMath
* @subpackage RankMath\Admin\Wizard
*/
use RankMath\KB;
defined( 'ABSPATH' ) || exit;
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta name="viewport" content="width=device-width"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title><?php esc_html_e( 'Setup Wizard - Rank Math', 'rank-math' ); ?></title>
<?php wp_print_head_scripts(); ?>
<?php wp_print_styles( 'rank-math-wizard' ); ?>
</head>
<body class="rank-math-wizard rank-math-page <?php echo is_rtl() ? ' rtl' : ''; ?>">
<div id="rank-math-wizard-wrapper"></div>
</body>
<?php
rank_math()->json->output();
if ( function_exists( 'wp_print_media_templates' ) ) {
wp_print_media_templates();
}
wp_print_footer_scripts();
?>
</html>

View File

@@ -0,0 +1,40 @@
<?php
/**
* Search console ui.
*
* @package RankMath
* @subpackage RankMath\Admin\Wizard
*/
use RankMath\KB;
use RankMath\Google\Authentication;
defined( 'ABSPATH' ) || exit;
// phpcs:disable
$is_authorized = Authentication::is_authorized();
$authorize = ! $is_authorized ? ( '<div class="connect-wrap" style="margin-top: 30px;"><a href="' . esc_url( Authentication::get_auth_url() ) . '" class="button button-primary button-animated rank-math-authorize-account">' . esc_html__( 'Connect Google Services', 'rank-math' ) . '</a></div>' ) : '';
$deauthorize = $is_authorized ? '<button class="button button-primary rank-math-deauthorize-account">' . esc_html__( 'Disconnect Account', 'rank-math' ) . '</button>' : '';
echo $authorize . $deauthorize;
?>
<div id="rank-math-pro-cta" class="analytics">
<div class="rank-math-cta-box width-100 no-shadow no-padding no-border">
<h3><?php echo esc_attr__( 'Benefits of Connecting Google Account', 'rank-math' ); ?></h3>
<ul>
<li><?php echo esc_attr__( 'Verify site ownership on Google Search Console in a single click', 'rank-math' ); ?></li>
<li><?php echo esc_attr__( 'Track page and keyword rankings with the Advanced Analytics module', 'rank-math' ); ?></li>
<li><?php echo esc_attr__( 'Easily set up Google Analytics without using another 3rd party plugin', 'rank-math' ); ?></li>
<li><?php echo esc_attr__( 'Automatically submit sitemaps to the Google Search Console', 'rank-math' ); ?></li>
<li><a href="<?php echo KB::get( 'help-analytics', 'SW Analytics Step Benefits' ); ?>" target="_blank"><?php echo esc_html__( 'Learn more about the benefits of connecting your account here.', 'rank-math' ); ?></a></li>
</ul>
</div>
</div>
<div id="rank-math-pro-cta" class="rank-math-privacy-box">
<div class="rank-math-cta-table">
<div class="rank-math-cta-body less-padding">
<i class="dashicons dashicons-lock"></i>
<p><?php printf( esc_html__( 'We do not store any of the data from your Google account on our servers, everything is processed & stored on your server. We take your privacy extremely seriously and ensure it is never misused. %s', 'rank-math' ), '<a href="' . KB::get( 'usage-policy', 'Analytics Privacy Notice' ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Learn more.', 'rank-math' ) . '</a>' ); ?></p>
</div>
</div>
</div>

View File

@@ -0,0 +1 @@
<?php // Silence is golden.

View File

@@ -0,0 +1,53 @@
<?php
/**
* Search console ui.
*
* @package RankMath
* @subpackage RankMath\Admin\Wizard
*/
use RankMath\KB;
use RankMath\Helpers\Param;
use RankMath\Admin\Admin_Helper;
defined( 'ABSPATH' ) || exit;
$sw_page = Param::get( 'page', '', FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_BACKTICK );
$sw_page = in_array( $sw_page, [ 'rank-math-options-general', 'rank-math-analytics' ], true ) ? 'rank-math-options-general' : 'rank-math-wizard&step=analytics';
$url = Admin_Helper::get_activate_url( admin_url( 'admin.php?analytics=1&page=' . $sw_page ) );
$site_url_valid = Admin_Helper::is_site_url_valid();
$button_class = 'button button-primary button-connect' . ( $site_url_valid ? ' button-animated' : ' disabled' );
?>
<?php Admin_Helper::maybe_show_invalid_siteurl_notice(); ?>
<div class="wp-core-ui rank-math-ui connect-wrap" style="margin-top: 30px;">
<a href="<?php echo esc_url( $url ); ?>" class="<?php echo esc_attr( $button_class ); ?>" name="rank_math_activate"><?php echo esc_attr__( 'Connect Your Rank Math Account', 'rank-math' ); ?></a>
</div>
<div id="rank-math-pro-cta" class="analytics">
<div class="rank-math-cta-box width-100 no-shadow no-padding no-border">
<h3><?php echo esc_attr__( 'Benefits of Connecting Rank Math Account', 'rank-math' ); ?></h3>
<ul>
<li><?php echo esc_attr__( 'Verify site ownership on Google Search Console in a single click', 'rank-math' ); ?></li>
<li><?php echo esc_attr__( 'Track page and keyword rankings with the Advanced Analytics module', 'rank-math' ); ?></li>
<li><?php echo esc_attr__( 'Easily set up Google Analytics without using another 3rd party plugin', 'rank-math' ); ?></li>
<li><?php echo esc_attr__( 'Automatically submit sitemaps to the Google Search Console', 'rank-math' ); ?></li>
<li><?php echo esc_attr__( 'Free keyword suggestions when entering a focus keyword', 'rank-math' ); ?></li>
<li><?php echo esc_attr__( 'Use our revolutionary SEO Analyzer to scan your website for SEO errors', 'rank-math' ); ?></li>
<li><a href="<?php echo esc_url( KB::get( 'free-account-benefits', 'SW Analytics Step' ) ); ?>" target="_blank"><?php echo esc_html__( 'Learn more about the benefits of connecting your account here.', 'rank-math' ); ?></a></li>
</ul>
</div>
</div>
<div id="rank-math-pro-cta" class="rank-math-privacy-box">
<div class="rank-math-cta-table">
<div class="rank-math-cta-body less-padding">
<i class="dashicons dashicons-lock"></i>
<p>
<?php
// Translators: placeholder is the KB link.
printf( esc_html__( 'We do not store any of the data from your Google account on our servers, everything is processed & stored on your server. We take your privacy extremely seriously and ensure it is never misused. %s', 'rank-math' ), '<a href="' . esc_url( KB::get( 'usage-policy', 'Analytics Privacy Notice' ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Learn more.', 'rank-math' ) . '</a>' );
?>
</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,404 @@
<?php
/**
* Search console UI.
*
* @package RankMath
* @subpackage RankMath\Admin\Wizard
*/
use RankMath\KB;
use RankMath\Helper;
use RankMath\Google\Authentication;
use RankMath\Google\Permissions;
use RankMath\Analytics\Url_Inspection;
use RankMath\Helpers\Str;
use RankMath\Google\Analytics;
use RankMath\Google\Console;
defined( 'ABSPATH' ) || exit;
if ( ! Helper::is_site_connected() ) {
require_once 'rank-math-connect.php';
return;
}
$is_authorized = Authentication::is_authorized();
if ( ! $is_authorized ) {
require_once 'google-connect.php';
return;
}
$profile = wp_parse_args(
get_option( 'rank_math_google_analytic_profile' ),
[
'profile' => '',
'country' => 'all',
]
);
$analytics = wp_parse_args(
get_option( 'rank_math_google_analytic_options' ),
[
'adsense_id' => '',
'account_id' => '',
'property_id' => '',
'view_id' => '',
'measurement_id' => '',
'stream_name' => '',
'country' => 'all',
'install_code' => false,
'anonymize_ip' => false,
'local_ga_js' => false,
'exclude_loggedin' => false,
]
);
$is_profile_connected = Console::is_console_connected();
$is_adsense_connected = ! empty( $analytics['adsense_id'] );
$is_analytics_connected = Analytics::is_analytics_connected();
$is_index_status_enabled = Url_Inspection::is_enabled() || ! $is_profile_connected;
$all_services = get_option(
'rank_math_analytics_all_services',
[
'isVerified' => '',
'inSearchConsole' => '',
'hasSitemap' => '',
'hasAnalytics' => '',
'hasAnalyticsProperty' => '',
'homeUrl' => '',
'sites' => '',
'accounts' => [],
'adsenseAccounts' => [],
]
);
$is_pro_active = defined( 'RANK_MATH_PRO_FILE' );
?>
<input type="hidden" class="cmb2-id-check-all-services" value="<?php echo $is_profile_connected && $is_analytics_connected ? '1' : '0'; ?>" />
<?php
$connections = [
'reconnect' => [
'link' => wp_nonce_url( admin_url( 'admin.php?reconnect=google' ), 'rank_math_reconnect_google' ),
'class' => 'rank-math-reconnect-google',
'text' => esc_html__( 'Reconnect', 'rank-math' ),
],
'disconnect' => [
'link' => '#',
'class' => 'rank-math-disconnect-google',
'text' => esc_html__( 'Disconnect', 'rank-math' ),
],
];
if ( Helper::is_advanced_mode() && ( $is_profile_connected || $is_adsense_connected || $is_analytics_connected ) ) {
$connections['test-connections'] = [
'link' => '#',
'class' => 'rank-math-test-connection-google',
'text' => esc_html__( 'Test Connections', 'rank-math' ),
];
}
$connections = apply_filters( 'rank_math/analytics/connect_actions', $connections );
?>
<div class="connect-actions">
<?php foreach ( $connections as $connection ) { ?>
<a href="<?php echo esc_attr( $connection['link'] ); ?>" class="button button-link <?php echo esc_attr( $connection['class'] ); ?>"><?php echo esc_html( $connection['text'] ); ?></a>
<?php } ?>
</div>
<?php
$console_classes = Helper::classnames(
'rank-math-box no-padding rank-math-accordion rank-math-connect-search-console',
[
'connected' => $is_profile_connected,
'disconnected' => ! $is_profile_connected,
'disabled' => ! Permissions::has_console(),
]
);
$console_status_classes = Helper::classnames(
'rank-math-connection-status',
[
'rank-math-connection-status-success' => Console::is_valid_connection(),
'rank-math-connection-status-error' => ! Console::is_valid_connection(),
]
);
$console_status = $is_profile_connected ? 'Connected' : 'Not Connected';
?>
<div class="<?php echo esc_attr( $console_classes ); ?>" tabindex="0">
<header>
<h3><span class="rank-math-connection-status-wrap"><span class="<?php echo esc_attr( $console_status_classes ); ?>" title="<?php echo esc_attr( $console_status ); ?>"></span></span> <?php esc_html_e( 'Search Console', 'rank-math' ); ?></h3>
</header>
<div class="rank-math-accordion-content">
<?php
if ( ! Permissions::has_console() ) {
Permissions::print_warning();
}
?>
<div class="cmb-row cmb-type-select">
<div class="cmb-row-col">
<label for="site-console-profile"><?php esc_html_e( 'Site', 'rank-math' ); ?></label>
<select class="cmb2_select site-console-profile notrack" name="site-console-profile" id="site-console-profile" data-selected="<?php echo esc_attr( $profile['profile'] ); ?>" disabled="disabled">
<?php if ( $is_profile_connected ) : ?>
<option value="<?php echo esc_attr( $profile['profile'] ); ?>"><?php echo esc_attr( $profile['profile'] ); ?></option>
<?php endif; ?>
</select>
</div>
<?php do_action( 'rank_math/analytics/options/console' ); ?>
</div>
<div class="cmb-row cmb-type-toggle">
<div class="cmb-td">
<label class="cmb2-toggle">
<input type="checkbox" class="regular-text notrack" name="enable-index-status" id="enable-index-status" value="on" <?php checked( $is_index_status_enabled ); ?> <?php disabled( ! $is_profile_connected ); ?>>
<span class="cmb2-slider">
<svg width="3" height="8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 6" class="toggle_on" role="img" aria-hidden="true" focusable="false"><path d="M0 0h2v6H0z"></path></svg>
<svg width="8" height="8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 6" class="toggle_off" role="img" aria-hidden="true" focusable="false"><path d="M3 1.5c.8 0 1.5.7 1.5 1.5S3.8 4.5 3 4.5 1.5 3.8 1.5 3 2.2 1.5 3 1.5M3 0C1.3 0 0 1.3 0 3s1.3 3 3 3 3-1.3 3-3-1.3-3-3-3z"></path></svg>
</span>
</label>
<label for="enable-index-status"><?php esc_html_e( 'Enable the Index Status tab', 'rank-math' ); ?></label>
<div class="cmb2-metabox-description"><?php esc_html_e( 'Enable this option to show the Index Status tab in the Analytics module.', 'rank-math' ); ?> <a href="<?php echo KB::get( 'url-inspection-api', 'SW Analytics Index Status Option' ); // phpcs:ignore ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Learn more.', 'rank-math' ); ?></a></div>
</div>
</div>
<?php if ( ! Console::is_valid_connection() ) : ?>
<div class="rank-math-notice rank-math-notice--error">
<p><?php esc_html_e( 'Data import will not work for this service as sufficient permissions are not given.', 'rank-math' ); ?></p>
</div>
<?php endif; ?>
</div>
</div>
<?php
$analytic_classes = Helper::classnames(
'rank-math-box no-padding rank-math-accordion rank-math-connect-analytics',
[
'connected' => $is_analytics_connected,
'disconnected' => ! $is_analytics_connected,
'disabled' => ! Permissions::has_analytics(),
]
);
$analytic_status_classes = Helper::classnames(
'rank-math-connection-status',
[
'rank-math-connection-status-success' => Analytics::is_valid_connection(),
'rank-math-connection-status-error' => ! Analytics::is_valid_connection(),
]
);
$analytic_status = $is_analytics_connected ? 'Connected' : 'Not Connected';
?>
<div class="<?php echo esc_attr( $analytic_classes ); ?>" tabindex="0">
<header>
<h3><span class="rank-math-connection-status-wrap"><span class="<?php echo esc_attr( $analytic_status_classes ); ?>" title="<?php echo esc_attr( $analytic_status ); ?>"></span></span><?php esc_html_e( 'Analytics', 'rank-math' ); ?></h3>
</header>
<div class="rank-math-accordion-content rank-math-analytics-content">
<?php
if ( ! Permissions::has_analytics() ) {
Permissions::print_warning();
}
?>
<p class="warning yellow">
<strong class="note"><?php echo esc_html__( 'Note', 'rank-math' ); ?></strong>
<?php
printf(
/* translators: %s: Link to KB article */
esc_html__( 'Ready to switch to Google Analytics 4? %s', 'rank-math' ),
'<a href="' . KB::get( 'using-ga4', 'Analytics GA4 KB' ) . '" target="_blank">' . esc_html__( 'Click here to know how', 'rank-math' ) . '</a>.' // phpcs:ignore
);
?>
</p>
<div class="cmb-row cmb-type-select">
<div class="cmb-row-col">
<label for="site-analytics-account"><?php esc_html_e( 'Account', 'rank-math' ); ?></label>
<select class="cmb2_select site-analytics-account notrack" name="site-analytics-account" id="site-analytics-account" data-selected="<?php echo esc_attr( $analytics['account_id'] ); ?>" disabled="disabled">
<?php
if ( $is_analytics_connected ) :
$analytic_account = $all_services['accounts'][ $analytics['account_id'] ]['name'] ?? '';
?>
<option value="<?php echo esc_attr( $analytics['account_id'] ); ?>"><?php echo esc_attr( $analytic_account ); ?></option>
<?php endif; ?>
</select>
</div>
<div class="cmb-row-col">
<label for="site-analytics-property"><?php esc_html_e( 'Property', 'rank-math' ); ?></label>
<select class="cmb2_select site-analytics-property notrack" name="site-analytics-property" id="site-analytics-property" data-selected="<?php echo esc_attr( $analytics['property_id'] ); ?>" disabled="disabled">
<?php
if ( $is_analytics_connected ) :
$analytic_property = $all_services['accounts'][ $analytics['account_id'] ]['properties'][ $analytics['property_id'] ]['name'] ?? '';
?>
<option value="<?php echo esc_attr( $analytics['property_id'] ); ?>"><?php echo esc_html( $analytic_property ); ?></option>
<?php endif; ?>
</select>
</div>
<div class="cmb-row-col">
<label for="site-analytics-view">
<?php echo esc_html__( 'Data Stream', 'rank-math' ); ?>
</label>
<select class="cmb2_select site-analytics-view notrack" name="site-analytics-view" id="site-analytics-view" data-selected="<?php echo esc_attr( $analytics['view_id'] ); ?>" disabled="disabled">
<?php
if ( $is_analytics_connected ) :
$analytic_view = $analytics['stream_name'] ? $analytics['stream_name'] : 'Website';
?>
<option value="<?php echo esc_attr( $analytics['view_id'] ); ?>"><?php echo esc_attr( $analytic_view ); ?></option>
<?php
endif;
?>
</select>
</div>
<input type="hidden" id="rank-math-analytics-measurement-id" name="measurementID" value="<?php echo esc_attr( $analytics['measurement_id'] ); ?>" />
<input type="hidden" id="rank-math-analytics-stream-name" name="streamName" value="<?php echo esc_attr( $analytics['stream_name'] ); ?>" />
<?php do_action( 'rank_math/analytics/options/analytics' ); ?>
</div>
<div class="cmb-row cmb-type-toggle">
<div class="cmb-td">
<label class="cmb2-toggle">
<input type="checkbox" class="regular-text notrack" name="install-code" id="install-code" value="on"<?php checked( $analytics['install_code'] ); ?>>
<span class="cmb2-slider">
<svg width="3" height="8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 6" class="toggle_on" role="img" aria-hidden="true" focusable="false"><path d="M0 0h2v6H0z"></path></svg>
<svg width="8" height="8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 6" class="toggle_off" role="img" aria-hidden="true" focusable="false"><path d="M3 1.5c.8 0 1.5.7 1.5 1.5S3.8 4.5 3 4.5 1.5 3.8 1.5 3 2.2 1.5 3 1.5M3 0C1.3 0 0 1.3 0 3s1.3 3 3 3 3-1.3 3-3-1.3-3-3-3z"></path></svg>
</span>
</label>
<label for="install-code"><?php esc_html_e( 'Install analytics code', 'rank-math' ); ?></label>
<div class="cmb2-metabox-description"><?php esc_html_e( 'Enable this option only if you are not using any other plugin/theme to install Google Analytics code.', 'rank-math' ); ?></div>
</div>
</div>
<div class="cmb-row cmb-type-toggle <?php echo ! $is_pro_active ? 'cmb-redirector-element' : ''; ?>" <?php echo ! $is_pro_active ? 'data-url="' . KB::the( 'free-vs-pro', 'Anonymize IP' ) . '"' : ''; // phpcs:ignore ?>>
<div class="cmb-td">
<label class="cmb2-toggle">
<input type="checkbox" class="regular-text notrack" name="anonymize-ip" id="anonymize-ip" value="on"<?php checked( $analytics['anonymize_ip'] ); ?><?php disabled( ! $is_pro_active ); ?>>
<span class="cmb2-slider<?php echo ! $is_pro_active ? ' disabled' : ''; ?> ">
<svg width="3" height="8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 6" class="toggle_on" role="img" aria-hidden="true" focusable="false"><path d="M0 0h2v6H0z"></path></svg>
<svg width="8" height="8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 6" class="toggle_off" role="img" aria-hidden="true" focusable="false"><path d="M3 1.5c.8 0 1.5.7 1.5 1.5S3.8 4.5 3 4.5 1.5 3.8 1.5 3 2.2 1.5 3 1.5M3 0C1.3 0 0 1.3 0 3s1.3 3 3 3 3-1.3 3-3-1.3-3-3-3z"></path></svg>
</span>
</label>
<label for="anonymize-ip">
<?php esc_html_e( 'Anonymize IP addresses', 'rank-math' ); ?>
<?php if ( ! $is_pro_active ) : ?>
<span class="rank-math-pro-badge">
<a href="<?php KB::the( 'pro', 'Anonymize IP' ); ?>" target="_blank" rel="noopener noreferrer">
<?php esc_html_e( 'PRO', 'rank-math' ); ?>
</a>
</span>
<?php endif; ?>
</label>
<div class="rank-math-cmb-dependency hidden" data-relation="or">
<span class="hidden" data-field="install-code" data-comparison="=" data-value="on"></span>
</div>
</div>
</div>
<div class="cmb-row cmb-type-toggle <?php echo ! $is_pro_active ? 'cmb-redirector-element' : ''; ?>" <?php echo ! $is_pro_active ? 'data-url="' . KB::the( 'pro', 'Localjs IP' ) . '"' : ''; // phpcs:ignore ?>>
<div class="cmb-td">
<label class="cmb2-toggle">
<input type="checkbox" class="regular-text notrack" name="local-ga-js" id="local-ga-js" value="on"<?php checked( $analytics['local_ga_js'] ); ?><?php disabled( ! $is_pro_active ); ?>>
<span class="cmb2-slider<?php echo ! $is_pro_active ? ' disabled' : ''; ?> ">
<svg width="3" height="8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 6" class="toggle_on" role="img" aria-hidden="true" focusable="false"><path d="M0 0h2v6H0z"></path></svg>
<svg width="8" height="8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 6" class="toggle_off" role="img" aria-hidden="true" focusable="false"><path d="M3 1.5c.8 0 1.5.7 1.5 1.5S3.8 4.5 3 4.5 1.5 3.8 1.5 3 2.2 1.5 3 1.5M3 0C1.3 0 0 1.3 0 3s1.3 3 3 3 3-1.3 3-3-1.3-3-3-3z"></path></svg>
</span>
</label>
<label for="local-ga-js">
<?php esc_html_e( 'Self-Hosted Analytics JS File', 'rank-math' ); ?>
<?php if ( ! $is_pro_active ) : ?>
<span class="rank-math-pro-badge">
<a href="<?php KB::the( 'pro', 'Localjs IP' ); ?>" target="_blank" rel="noopener noreferrer">
<?php esc_html_e( 'PRO', 'rank-math' ); ?>
</a>
</span>
<?php endif; ?>
</label>
<div class="rank-math-cmb-dependency hidden" data-relation="or">
<span class="hidden" data-field="install-code" data-comparison="=" data-value="on"></span>
</div>
</div>
</div>
<div class="cmb-row cmb-type-toggle">
<div class="cmb-td">
<label class="cmb2-toggle">
<input type="checkbox" class="regular-text notrack" name="exclude-loggedin" id="exclude-loggedin" value="on"<?php checked( $analytics['exclude_loggedin'] ); ?>>
<span class="cmb2-slider">
<svg width="3" height="8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 6" class="toggle_on" role="img" aria-hidden="true" focusable="false"><path d="M0 0h2v6H0z"></path></svg>
<svg width="8" height="8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 6" class="toggle_off" role="img" aria-hidden="true" focusable="false"><path d="M3 1.5c.8 0 1.5.7 1.5 1.5S3.8 4.5 3 4.5 1.5 3.8 1.5 3 2.2 1.5 3 1.5M3 0C1.3 0 0 1.3 0 3s1.3 3 3 3 3-1.3 3-3-1.3-3-3-3z"></path></svg>
</span>
</label>
<label for="exclude-loggedin"><?php esc_html_e( 'Exclude Logged-in users', 'rank-math' ); ?></label>
<div class="rank-math-cmb-dependency hidden" data-relation="or">
<span class="hidden" data-field="install-code" data-comparison="=" data-value="on"></span>
</div>
</div>
</div>
<?php if ( ! Analytics::is_valid_connection() ) : ?>
<div class="rank-math-notice rank-math-notice--error">
<p><?php esc_html_e( 'Data import will not work for this service as sufficient permissions are not given.', 'rank-math' ); ?></p>
</div>
<?php endif; ?>
</div>
</div>
<?php ob_start(); ?>
<div class="rank-math-box no-padding rank-math-accordion rank-math-connect-adsense disconnected" tabindex="0">
<header>
<h3>
<span class="rank-math-connection-status-wrap">
<span class="rank-math-connection-status rank-math-connection-status-error" title="Not Connected"></span>
</span><?php esc_html_e( 'AdSense', 'rank-math' ); ?>
</h3>
</header>
<div class="rank-math-accordion-content">
<div class="cmb-row cmb-type-select">
<div class="cmb-row-col">
<label for="site-adsense-account"><?php esc_html_e( 'Account', 'rank-math' ); ?></label>
<select class="cmb2_select site-adsense-account notrack" name="site-adsense-account" id="site-adsense-account" data-selected="" disabled="disabled">
<option value=""><?php esc_html_e( 'Select Account', 'rank-math' ); ?></option>
</select>
</div>
</div>
<div id="rank-math-pro-cta" class="no-margin">
<div class="rank-math-cta-text">
<span class="rank-math-pro-badge">
<a href="<?php KB::the( 'pro', 'AdSense Toggle' ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'PRO', 'rank-math' ); ?></a></span> <?php esc_html_e( "Google AdSense support is only available in Rank Math Pro's Advanced Analytics module.", 'rank-math' ); ?>
</div>
</div>
</div>
</div>
<?php echo apply_filters( 'rank_math/analytics/adsense', ob_get_clean(), $analytics, $all_services ); // phpcs:ignore ?>
<div id="rank-math-pro-cta" class="rank-math-privacy-box width-100">
<div class="rank-math-cta-table">
<div class="rank-math-cta-body less-padding">
<i class="dashicons dashicons-lock"></i>
<p>
<?php
/* translators: %s: Link to KB article */
printf( esc_html__( 'We do not store any of the data from your Google account on our servers, everything is processed & stored on your server. We take your privacy extremely seriously and ensure it is never misused. %s', 'rank-math' ), '<a href="' . KB::get( 'usage-policy', 'Analytics Privacy Notice' ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Learn more.', 'rank-math' ) . '</a>' ); // phpcs:ignore
?>
</p>
</div>
</div>
</div>
<?php
// phpcs:enable
if ( Helper::is_wizard() && ! RankMath\Analytics\Email_Reports::are_fields_hidden() ) {
?>
<div class="cmb-row email-reports-header text-center" style="border-top:0;">
<h1><?php esc_html_e( 'Email Reports', 'rank-math' ); ?></h1>
<div class="email-reports-desc text-center"><?php esc_html_e( 'Receive Analytics reports periodically in email.', 'rank-math' ); ?> <a href="#" target="_blank"><?php esc_html_e( 'Learn more about Email Reports.', 'rank-math' ); ?></a></div>
</div>
<div class="cmb-row cmb-type-toggle cmb2-id-console-email-reports" data-fieldtype="toggle">
<div class="cmb-th">
<label for="console_email_reports"><?php esc_html_e( 'Email Reports', 'rank-math' ); ?></label>
</div>
<div class="cmb-td">
<label class="cmb2-toggle"><input type="checkbox" class="regular-text" name="console_email_reports" id="console_email_reports" value="on" <?php checked( Helper::get_settings( 'general.console_email_reports' ) ); ?> data-hash="7e0rimtbvig0"><span class="cmb2-slider"><svg width="3" height="8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 6" class="toggle_on" role="img" aria-hidden="true" focusable="false"><path d="M0 0h2v6H0z"></path></svg><svg width="8" height="8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 6" class="toggle_off" role="img" aria-hidden="true" focusable="false"><path d="M3 1.5c.8 0 1.5.7 1.5 1.5S3.8 4.5 3 4.5 1.5 3.8 1.5 3 2.2 1.5 3 1.5M3 0C1.3 0 0 1.3 0 3s1.3 3 3 3 3-1.3 3-3-1.3-3-3-3z"></path></svg></span></label>
</div>
</div>
<?php
do_action( 'rank_math/analytics/options/wizard_after_email_report' );
}