Initial commit: Atomaste website
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: All in One SEO
|
||||
* Plugin URI: https://aioseo.com/
|
||||
* Description: SEO for WordPress. Features like XML Sitemaps, SEO for custom post types, SEO for blogs, business sites, ecommerce sites, and much more. More than 100 million downloads since 2007.
|
||||
* Author: All in One SEO Team
|
||||
* Author URI: https://aioseo.com/
|
||||
* Version: 4.8.1
|
||||
* Text Domain: all-in-one-seo-pack
|
||||
* Domain Path: /languages
|
||||
* License: GPL-3.0+
|
||||
*
|
||||
* All in One SEO is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* All in One SEO is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AIOSEO. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @since 4.0.0
|
||||
* @author All in One SEO Team
|
||||
* @package AIOSEO\Plugin
|
||||
* @license GPL-3.0+
|
||||
* @copyright Copyright © 2024, All in One SEO
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once dirname( __FILE__ ) . '/app/init/init.php';
|
||||
|
||||
// Check if this plugin should be disabled.
|
||||
if ( aioseoMaybePluginIsDisabled( __FILE__ ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! defined( 'AIOSEO_PHP_VERSION_DIR' ) ) {
|
||||
define( 'AIOSEO_PHP_VERSION_DIR', basename( dirname( __FILE__ ) ) );
|
||||
}
|
||||
|
||||
require_once dirname( __FILE__ ) . '/app/init/notices.php';
|
||||
require_once dirname( __FILE__ ) . '/app/init/activation.php';
|
||||
|
||||
// We require PHP 7.0 or higher for the whole plugin to work.
|
||||
if ( version_compare( PHP_VERSION, '7.0', '<' ) ) {
|
||||
add_action( 'admin_notices', 'aioseo_php_notice' );
|
||||
|
||||
// Do not process the plugin code further.
|
||||
return;
|
||||
}
|
||||
|
||||
// We require WordPress 5.3+ for the whole plugin to work.
|
||||
// Support for 5.3 is scheduled to be dropped in April 2025. 5.4, 5.5 and 5.6 will be dropped at the end of 2025.
|
||||
global $wp_version; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
if ( version_compare( $wp_version, '5.3', '<' ) ) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
add_action( 'admin_notices', 'aioseo_wordpress_notice' );
|
||||
|
||||
// Do not process the plugin code further.
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! defined( 'AIOSEO_DIR' ) ) {
|
||||
define( 'AIOSEO_DIR', __DIR__ );
|
||||
}
|
||||
if ( ! defined( 'AIOSEO_FILE' ) ) {
|
||||
define( 'AIOSEO_FILE', __FILE__ );
|
||||
}
|
||||
|
||||
// Don't allow multiple versions to be active.
|
||||
if ( function_exists( 'aioseo' ) ) {
|
||||
add_action( 'activate_all-in-one-seo-pack/all_in_one_seo_pack.php', 'aioseo_lite_just_activated' );
|
||||
add_action( 'deactivate_all-in-one-seo-pack/all_in_one_seo_pack.php', 'aioseo_lite_just_deactivated' );
|
||||
add_action( 'activate_all-in-one-seo-pack-pro/all_in_one_seo_pack.php', 'aioseo_pro_just_activated' );
|
||||
add_action( 'admin_notices', 'aioseo_lite_notice' );
|
||||
|
||||
// Do not process the plugin code further.
|
||||
return;
|
||||
}
|
||||
|
||||
// We will be deprecating these versions of PHP in the future, so let's let the user know.
|
||||
if ( version_compare( PHP_VERSION, '7.4', '<' ) ) {
|
||||
add_action( 'admin_notices', 'aioseo_php_notice_deprecated' );
|
||||
}
|
||||
|
||||
// Define the class and the function.
|
||||
// The AIOSEOAbstract class is required here because it can't be autoloaded.
|
||||
require_once dirname( __FILE__ ) . '/app/AIOSEOAbstract.php';
|
||||
require_once dirname( __FILE__ ) . '/app/AIOSEO.php';
|
||||
|
||||
aioseo();
|
||||
@@ -0,0 +1,402 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin {
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main AIOSEO class.
|
||||
* We extend the abstract class as that one holds all the class properties.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
final class AIOSEO extends \AIOSEOAbstract {
|
||||
|
||||
/**
|
||||
* Holds the instance of the plugin currently in use.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var AIOSEO
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Plugin version for enqueueing, etc.
|
||||
* The value is retrieved from the AIOSEO_VERSION constant.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $version = '';
|
||||
|
||||
/**
|
||||
* Paid returns true, free (Lite) returns false.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $pro = false;
|
||||
|
||||
/**
|
||||
* Returns 'Pro' or 'Lite'.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $versionPath = 'Lite';
|
||||
|
||||
/**
|
||||
* Whether we're in a dev environment.
|
||||
*
|
||||
* @since 4.1.9
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $isDev = false;
|
||||
|
||||
/**
|
||||
* Uninstall class instance.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*
|
||||
* @var Common\Main\Uninstall
|
||||
*/
|
||||
public $uninstall = null;
|
||||
|
||||
/**
|
||||
* Main AIOSEO Instance.
|
||||
*
|
||||
* Insures that only one instance of AIOSEO exists in memory at any one
|
||||
* time. Also prevents needing to define globals all over the place.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return AIOSEO The aioseo instance.
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( null === self::$instance || ! self::$instance instanceof self ) {
|
||||
self::$instance = new self();
|
||||
|
||||
self::$instance->init();
|
||||
|
||||
// Load our addons on the action right after plugins_loaded.
|
||||
add_action( 'sanitize_comment_cookies', [ self::$instance, 'loadAddons' ] );
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize All in One SEO!
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function init() {
|
||||
$this->constants();
|
||||
$this->includes();
|
||||
$this->preLoad();
|
||||
if ( ! $this->core->isUninstalling() ) {
|
||||
$this->load();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup plugin constants.
|
||||
* All the path/URL related constants are defined in main plugin file.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function constants() {
|
||||
$defaultHeaders = [
|
||||
'name' => 'Plugin Name',
|
||||
'version' => 'Version',
|
||||
];
|
||||
|
||||
$pluginData = get_file_data( AIOSEO_FILE, $defaultHeaders );
|
||||
|
||||
$constants = [
|
||||
'AIOSEO_PLUGIN_BASENAME' => plugin_basename( AIOSEO_FILE ),
|
||||
'AIOSEO_PLUGIN_NAME' => $pluginData['name'],
|
||||
'AIOSEO_PLUGIN_SHORT_NAME' => 'AIOSEO',
|
||||
'AIOSEO_PLUGIN_URL' => plugin_dir_url( AIOSEO_FILE ),
|
||||
'AIOSEO_VERSION' => $pluginData['version'],
|
||||
'AIOSEO_MARKETING_URL' => 'https://aioseo.com/',
|
||||
'AIOSEO_MARKETING_DOMAIN' => 'aioseo.com'
|
||||
];
|
||||
|
||||
foreach ( $constants as $constant => $value ) {
|
||||
if ( ! defined( $constant ) ) {
|
||||
define( $constant, $value );
|
||||
}
|
||||
}
|
||||
|
||||
$this->version = AIOSEO_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Including the new files with PHP 5.3 style.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function includes() {
|
||||
$dependencies = [
|
||||
'/vendor/autoload.php' => true,
|
||||
'/vendor/woocommerce/action-scheduler/action-scheduler.php' => true,
|
||||
'/vendor/jwhennessey/phpinsight/autoload.php' => false,
|
||||
'/vendor_prefixed/monolog/monolog/src/Monolog/Logger.php' => false
|
||||
];
|
||||
|
||||
foreach ( $dependencies as $path => $shouldRequire ) {
|
||||
if ( ! file_exists( AIOSEO_DIR . $path ) ) {
|
||||
// Something is not right.
|
||||
status_header( 500 );
|
||||
wp_die( esc_html__( 'Plugin is missing required dependencies. Please contact support for more information.', 'all-in-one-seo-pack' ) );
|
||||
}
|
||||
|
||||
if ( $shouldRequire ) {
|
||||
require_once AIOSEO_DIR . $path;
|
||||
}
|
||||
}
|
||||
|
||||
$this->loadVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the version of the plugin we are currently using.
|
||||
*
|
||||
* @since 4.1.9
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function loadVersion() {
|
||||
$proDir = is_dir( plugin_dir_path( AIOSEO_FILE ) . 'app/Pro' );
|
||||
|
||||
if (
|
||||
! class_exists( '\Dotenv\Dotenv' ) ||
|
||||
! file_exists( AIOSEO_DIR . '/build/.env' )
|
||||
) {
|
||||
$this->pro = $proDir;
|
||||
$this->versionPath = $proDir ? 'Pro' : 'Lite';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$dotenv = \Dotenv\Dotenv::createUnsafeImmutable( AIOSEO_DIR, '/build/.env' );
|
||||
$dotenv->load();
|
||||
|
||||
$version = defined( 'AIOSEO_DEV_VERSION' )
|
||||
? strtolower( AIOSEO_DEV_VERSION )
|
||||
: strtolower( getenv( 'VITE_VERSION' ) );
|
||||
if ( ! empty( $version ) ) {
|
||||
$this->isDev = true;
|
||||
|
||||
if ( file_exists( AIOSEO_DIR . '/build/filters.php' ) ) {
|
||||
require_once AIOSEO_DIR . '/build/filters.php';
|
||||
}
|
||||
}
|
||||
|
||||
if ( $proDir && 'pro' === $version ) {
|
||||
$this->pro = true;
|
||||
$this->versionPath = 'Pro';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs before we load the plugin.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function preLoad() {
|
||||
$this->core = new Common\Core\Core();
|
||||
|
||||
$this->backwardsCompatibility();
|
||||
|
||||
// Internal Options.
|
||||
$this->helpers = $this->pro ? new Pro\Utils\Helpers() : new Lite\Utils\Helpers();
|
||||
$this->internalNetworkOptions = ( $this->pro && $this->helpers->isPluginNetworkActivated() ) ? new Pro\Options\InternalNetworkOptions() : new Common\Options\InternalNetworkOptions();
|
||||
$this->internalOptions = $this->pro ? new Pro\Options\InternalOptions() : new Lite\Options\InternalOptions();
|
||||
$this->uninstall = new Common\Main\Uninstall();
|
||||
|
||||
// Run pre-updates.
|
||||
$this->preUpdates = $this->pro ? new Pro\Main\PreUpdates() : new Common\Main\PreUpdates();
|
||||
}
|
||||
|
||||
/**
|
||||
* To prevent errors and bugs from popping up,
|
||||
* we will run this backwards compatibility method.
|
||||
*
|
||||
* @since 4.1.9
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function backwardsCompatibility() {
|
||||
$this->db = $this->core->db;
|
||||
$this->cache = $this->core->cache;
|
||||
$this->transients = $this->cache;
|
||||
$this->cachePrune = $this->core->cachePrune;
|
||||
$this->optionsCache = $this->core->optionsCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* To prevent errors and bugs from popping up,
|
||||
* we will run this backwards compatibility method.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function backwardsCompatibilityLoad() {
|
||||
$this->postSettings->integrations = $this->standalone->pageBuilderIntegrations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load our classes.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function load() {
|
||||
// Load external translations if this is a Pro install.
|
||||
if ( $this->pro ) {
|
||||
$translations = new Pro\Main\Translations(
|
||||
'plugin',
|
||||
'all-in-one-seo-pack',
|
||||
'https://aioseo.com/aioseo-plugin/all-in-one-seo-pack/packages.json'
|
||||
);
|
||||
$translations->init();
|
||||
|
||||
$translations = new Pro\Main\Translations(
|
||||
'plugin',
|
||||
'aioseo-pro',
|
||||
'https://aioseo.com/aioseo-plugin/aioseo-pro/packages.json'
|
||||
);
|
||||
$translations->init();
|
||||
}
|
||||
|
||||
$this->addons = $this->pro ? new Pro\Utils\Addons() : new Common\Utils\Addons();
|
||||
$this->features = $this->pro ? new Pro\Utils\Features() : new Common\Utils\Features();
|
||||
$this->tags = $this->pro ? new Pro\Utils\Tags() : new Common\Utils\Tags();
|
||||
$this->blocks = new Common\Utils\Blocks();
|
||||
$this->badBotBlocker = new Common\Tools\BadBotBlocker();
|
||||
$this->breadcrumbs = $this->pro ? new Pro\Breadcrumbs\Breadcrumbs() : new Common\Breadcrumbs\Breadcrumbs();
|
||||
$this->dynamicBackup = $this->pro ? new Pro\Options\DynamicBackup() : new Common\Options\DynamicBackup();
|
||||
$this->options = $this->pro ? new Pro\Options\Options() : new Lite\Options\Options();
|
||||
$this->networkOptions = ( $this->pro && $this->helpers->isPluginNetworkActivated() ) ? new Pro\Options\NetworkOptions() : new Common\Options\NetworkOptions();
|
||||
$this->dynamicOptions = $this->pro ? new Pro\Options\DynamicOptions() : new Common\Options\DynamicOptions();
|
||||
$this->backup = new Common\Utils\Backup();
|
||||
$this->access = $this->pro ? new Pro\Utils\Access() : new Common\Utils\Access();
|
||||
$this->usage = $this->pro ? new Pro\Admin\Usage() : new Lite\Admin\Usage();
|
||||
$this->siteHealth = $this->pro ? new Pro\Admin\SiteHealth() : new Common\Admin\SiteHealth();
|
||||
$this->networkLicense = $this->pro && $this->helpers->isPluginNetworkActivated() ? new Pro\Admin\NetworkLicense() : null;
|
||||
$this->license = $this->pro ? new Pro\Admin\License() : null;
|
||||
$this->autoUpdates = $this->pro ? new Pro\Admin\AutoUpdates() : null;
|
||||
$this->updates = $this->pro ? new Pro\Main\Updates() : new Common\Main\Updates();
|
||||
$this->meta = $this->pro ? new Pro\Meta\Meta() : new Common\Meta\Meta();
|
||||
$this->social = $this->pro ? new Pro\Social\Social() : new Common\Social\Social();
|
||||
$this->robotsTxt = new Common\Tools\RobotsTxt();
|
||||
$this->htaccess = new Common\Tools\Htaccess();
|
||||
$this->term = $this->pro ? new Pro\Admin\Term() : null;
|
||||
$this->notices = $this->pro ? new Pro\Admin\Notices\Notices() : new Lite\Admin\Notices\Notices();
|
||||
$this->wpNotices = new Common\Admin\Notices\WpNotices();
|
||||
$this->admin = $this->pro ? new Pro\Admin\Admin() : new Lite\Admin\Admin();
|
||||
$this->networkAdmin = $this->helpers->isPluginNetworkActivated() ? ( $this->pro ? new Pro\Admin\NetworkAdmin() : new Common\Admin\NetworkAdmin() ) : null;
|
||||
$this->activate = $this->pro ? new Pro\Main\Activate() : new Common\Main\Activate();
|
||||
$this->conflictingPlugins = $this->pro ? new Pro\Admin\ConflictingPlugins() : new Common\Admin\ConflictingPlugins();
|
||||
$this->migration = $this->pro ? new Pro\Migration\Migration() : new Common\Migration\Migration();
|
||||
$this->importExport = $this->pro ? new Pro\ImportExport\ImportExport() : new Common\ImportExport\ImportExport();
|
||||
$this->sitemap = $this->pro ? new Pro\Sitemap\Sitemap() : new Common\Sitemap\Sitemap();
|
||||
$this->htmlSitemap = new Common\Sitemap\Html\Sitemap();
|
||||
$this->templates = $this->pro ? new Pro\Utils\Templates() : new Common\Utils\Templates();
|
||||
$this->categoryBase = new Common\Main\CategoryBase();
|
||||
$this->postSettings = $this->pro ? new Pro\Admin\PostSettings() : new Lite\Admin\PostSettings();
|
||||
$this->standalone = new Common\Standalone\Standalone();
|
||||
$this->searchStatistics = $this->pro ? new Pro\SearchStatistics\SearchStatistics() : new Common\SearchStatistics\SearchStatistics();
|
||||
$this->slugMonitor = new Common\Admin\SlugMonitor();
|
||||
$this->schema = $this->pro ? new Pro\Schema\Schema() : new Common\Schema\Schema();
|
||||
$this->actionScheduler = new Common\Utils\ActionScheduler();
|
||||
$this->seoRevisions = $this->pro ? new Pro\SeoRevisions\SeoRevisions() : new Common\SeoRevisions\SeoRevisions();
|
||||
$this->ai = $this->pro ? new Pro\Ai\Ai() : null;
|
||||
$this->filters = $this->pro ? new Pro\Main\Filters() : new Lite\Main\Filters();
|
||||
$this->crawlCleanup = new Common\QueryArgs\CrawlCleanup();
|
||||
$this->searchCleanup = new Common\SearchCleanup\SearchCleanup();
|
||||
$this->emailReports = new Common\EmailReports\EmailReports();
|
||||
$this->thirdParty = new Common\ThirdParty\ThirdParty();
|
||||
$this->writingAssistant = new Common\WritingAssistant\WritingAssistant();
|
||||
|
||||
if ( ! wp_doing_ajax() && ! wp_doing_cron() ) {
|
||||
$this->rss = new Common\Rss();
|
||||
$this->main = $this->pro ? new Pro\Main\Main() : new Common\Main\Main();
|
||||
$this->head = $this->pro ? new Pro\Main\Head() : new Common\Main\Head();
|
||||
$this->dashboard = $this->pro ? new Pro\Admin\Dashboard() : new Common\Admin\Dashboard();
|
||||
$this->api = $this->pro ? new Pro\Api\Api() : new Lite\Api\Api();
|
||||
$this->help = new Common\Help\Help();
|
||||
}
|
||||
|
||||
$this->backwardsCompatibilityLoad();
|
||||
|
||||
add_action( 'init', [ $this, 'loadInit' ], 999 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Things that need to load after init.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loadInit() {
|
||||
$this->settings = new Common\Utils\VueSettings( '_aioseo_settings' );
|
||||
$this->sitemap->init();
|
||||
|
||||
$this->badBotBlocker->init();
|
||||
|
||||
// We call this again to reset any post types/taxonomies that have not yet been set up.
|
||||
$this->dynamicOptions->refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads our addons.
|
||||
*
|
||||
* Runs right after the plugins_loaded hook.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loadAddons() {
|
||||
do_action( 'aioseo_loaded' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function which returns the one AIOSEO instance.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return AIOSEO\Plugin\AIOSEO The instance.
|
||||
*/
|
||||
function aioseo() {
|
||||
return AIOSEO\Plugin\AIOSEO::instance();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,610 @@
|
||||
<?php
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class holding the class properties of our main AIOSEO class.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*/
|
||||
abstract class AIOSEOAbstract {
|
||||
/**
|
||||
* Core class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Core\Core
|
||||
*/
|
||||
public $core = null;
|
||||
|
||||
/**
|
||||
* Helpers class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Lite\Utils\Helpers|\AIOSEO\Plugin\Pro\Utils\Helpers
|
||||
*/
|
||||
public $helpers = null;
|
||||
|
||||
/**
|
||||
* InternalNetworkOptions class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Options\InternalNetworkOptions|\AIOSEO\Plugin\Pro\Options\InternalNetworkOptions
|
||||
*/
|
||||
public $internalNetworkOptions = null;
|
||||
|
||||
/**
|
||||
* InternalOptions class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Lite\Options\InternalOptions|\AIOSEO\Plugin\Pro\Options\InternalOptions
|
||||
*/
|
||||
public $internalOptions = null;
|
||||
|
||||
/**
|
||||
* PreUpdates class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Main\PreUpdates|\AIOSEO\Plugin\Pro\Main\PreUpdates
|
||||
*/
|
||||
public $preUpdates = null;
|
||||
|
||||
/**
|
||||
* Db class instance.
|
||||
* This prop is set for backwards compatibility.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Utils\Database
|
||||
*/
|
||||
public $db = null;
|
||||
|
||||
/**
|
||||
* Transients class instance.
|
||||
* This prop is set for backwards compatibility.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Utils\Cache
|
||||
*/
|
||||
public $transients = null;
|
||||
|
||||
/**
|
||||
* OptionsCache class instance.
|
||||
* This prop is set for backwards compatibility.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Options\Cache
|
||||
*/
|
||||
public $optionsCache = null;
|
||||
|
||||
/**
|
||||
* PostSettings class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Lite\Admin\PostSettings|\AIOSEO\Plugin\Pro\Admin\PostSettings
|
||||
*/
|
||||
public $postSettings = null;
|
||||
|
||||
/**
|
||||
* Standalone class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Standalone\Standalone
|
||||
*/
|
||||
public $standalone = null;
|
||||
|
||||
/**
|
||||
* Search Statistics class instance.
|
||||
*
|
||||
* @since 4.3.0
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Pro\SearchStatistics\SearchStatistics
|
||||
*/
|
||||
public $searchStatistics = null;
|
||||
|
||||
/**
|
||||
* Tags class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Pro\Utils\Tags
|
||||
*/
|
||||
public $tags = null;
|
||||
|
||||
/**
|
||||
* Addons class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Utils\Blocks
|
||||
*/
|
||||
public $blocks = null;
|
||||
|
||||
/**
|
||||
* BadBotBlocker class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Tools\BadBotBlocker
|
||||
*/
|
||||
public $badBotBlocker = null;
|
||||
|
||||
/**
|
||||
* Breadcrumbs class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Breadcrumbs\Breadcrumbs|\AIOSEO\Plugin\Pro\Breadcrumbs\Breadcrumbs
|
||||
*/
|
||||
public $breadcrumbs = null;
|
||||
|
||||
/**
|
||||
* DynamicBackup class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Options\DynamicBackup|\AIOSEO\Plugin\Pro\Options\DynamicBackup
|
||||
*/
|
||||
public $dynamicBackup = null;
|
||||
|
||||
/**
|
||||
* NetworkOptions class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Options\NetworkOptions|\AIOSEO\Plugin\Pro\Options\NetworkOptions
|
||||
*/
|
||||
public $networkOptions = null;
|
||||
|
||||
/**
|
||||
* Backup class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Utils\Backup
|
||||
*/
|
||||
public $backup = null;
|
||||
|
||||
/**
|
||||
* Access class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Utils\Access|\AIOSEO\Plugin\Pro\Utils\Access
|
||||
*/
|
||||
public $access = null;
|
||||
|
||||
/**
|
||||
* NetworkLicense class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var null|\AIOSEO\Plugin\Pro\Admin\NetworkLicense
|
||||
*/
|
||||
public $networkLicense = null;
|
||||
|
||||
/**
|
||||
* License class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var null|\AIOSEO\Plugin\Pro\Admin\License
|
||||
*/
|
||||
public $license = null;
|
||||
|
||||
/**
|
||||
* Updates class isntance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Main\Updates|\AIOSEO\Plugin\Pro\Main\Updates
|
||||
*/
|
||||
public $updates = null;
|
||||
|
||||
/**
|
||||
* Meta class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Pro\Meta\Meta
|
||||
*/
|
||||
public $meta = null;
|
||||
|
||||
/**
|
||||
* Social class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Social\Social|\AIOSEO\Plugin\Pro\Social\Social
|
||||
*/
|
||||
public $social = null;
|
||||
|
||||
/**
|
||||
* RobotsTxt class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Tools\RobotsTxt
|
||||
*/
|
||||
public $robotsTxt = null;
|
||||
|
||||
/**
|
||||
* Htaccess class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Tools\Htaccess
|
||||
*/
|
||||
public $htaccess = null;
|
||||
|
||||
/**
|
||||
* Term class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var null|\AIOSEO\Plugin\Pro\Admin\Term
|
||||
*/
|
||||
public $term = null;
|
||||
|
||||
/**
|
||||
* Notices class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Lite\Admin\Notices\Notices|\AIOSEO\Plugin\Pro\Admin\Notices\Notices
|
||||
*/
|
||||
public $notices = null;
|
||||
|
||||
/**
|
||||
* WpNotices class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Admin\Notices\WpNotices
|
||||
*/
|
||||
public $wpNotices = null;
|
||||
|
||||
/**
|
||||
* Admin class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Lite\Admin\Admin|\AIOSEO\Plugin\Pro\Admin\Admin
|
||||
*/
|
||||
public $admin = null;
|
||||
|
||||
/**
|
||||
* NetworkAdmin class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Admin\NetworkAdmin|\AIOSEO\Plugin\Pro\Admin\NetworkAdmin
|
||||
*/
|
||||
public $networkAdmin = null;
|
||||
|
||||
/**
|
||||
* Activate class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Main\Activate|\AIOSEO\Plugin\Pro\Main\Activate
|
||||
*/
|
||||
public $activate = null;
|
||||
|
||||
/**
|
||||
* ConflictingPlugins class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Admin\ConflictingPlugins|\AIOSEO\Plugin\Pro\Admin\ConflictingPlugins
|
||||
*/
|
||||
public $conflictingPlugins = null;
|
||||
|
||||
/**
|
||||
* Migration class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Pro\Migration\Migration
|
||||
*/
|
||||
public $migration = null;
|
||||
|
||||
/**
|
||||
* ImportExport class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\ImportExport\ImportExport
|
||||
*/
|
||||
public $importExport = null;
|
||||
|
||||
/**
|
||||
* Sitemap class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Sitemap\Sitemap|\AIOSEO\Plugin\Pro\Sitemap\Sitemap
|
||||
*/
|
||||
public $sitemap = null;
|
||||
|
||||
/**
|
||||
* HtmlSitemap class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Sitemap\Html\Sitemap
|
||||
*/
|
||||
public $htmlSitemap = null;
|
||||
|
||||
/**
|
||||
* CategoryBase class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
* @version 4.7.1 Moved from Pro to Common.
|
||||
*
|
||||
* @var null|\AIOSEO\Plugin\Common\Main\CategoryBase
|
||||
*/
|
||||
public $categoryBase = null;
|
||||
|
||||
/**
|
||||
* SlugMonitor class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Admin\SlugMonitor
|
||||
*/
|
||||
public $slugMonitor = null;
|
||||
|
||||
/**
|
||||
* Schema class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Pro\Schema\Schema
|
||||
*/
|
||||
public $schema = null;
|
||||
|
||||
/**
|
||||
* Rss class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Rss
|
||||
*/
|
||||
public $rss = null;
|
||||
|
||||
/**
|
||||
* Main class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Main\Main|\AIOSEO\Plugin\Pro\Main\Main
|
||||
*/
|
||||
public $main = null;
|
||||
|
||||
/**
|
||||
* Head class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Main\Head|\AIOSEO\Plugin\Pro\Main\Head
|
||||
*/
|
||||
public $head = null;
|
||||
|
||||
/**
|
||||
* Dashboard class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Admin\Dashboard|\AIOSEO\Plugin\Pro\Admin\Dashboard
|
||||
*/
|
||||
public $dashboard = null;
|
||||
|
||||
/**
|
||||
* API class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Lite\Api\Api|\AIOSEO\Plugin\Pro\Api\Api
|
||||
*/
|
||||
public $api = null;
|
||||
|
||||
/**
|
||||
* Help class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Help\Help
|
||||
*/
|
||||
public $help = null;
|
||||
|
||||
/**
|
||||
* Settings class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Utils\VueSettings
|
||||
*/
|
||||
public $settings = null;
|
||||
|
||||
/**
|
||||
* Cache class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Utils\Cache
|
||||
*/
|
||||
public $cache = null;
|
||||
|
||||
/**
|
||||
* CachePrune class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Utils\CachePrune
|
||||
*/
|
||||
public $cachePrune = null;
|
||||
|
||||
/**
|
||||
* Addons class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Pro\Utils\Addons|\AIOSEO\Plugin\Common\Utils\Addons
|
||||
*/
|
||||
public $addons = null;
|
||||
|
||||
/**
|
||||
* Addons class instance.
|
||||
*
|
||||
* @since 4.3.0
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Utils\Features|\AIOSEO\Plugin\Pro\Utils\Features
|
||||
*/
|
||||
public $features = null;
|
||||
|
||||
/**
|
||||
* Options class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Options\Options|\AIOSEO\Plugin\Pro\Options\Options
|
||||
*/
|
||||
public $options = null;
|
||||
|
||||
/**
|
||||
* DynamicOptions class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Options\DynamicOptions|\AIOSEO\Plugin\Pro\Options\DynamicOptions
|
||||
*/
|
||||
public $dynamicOptions = null;
|
||||
|
||||
/**
|
||||
* Usage class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Lite\Admin\Usage|\AIOSEO\Plugin\Pro\Admin\Usage
|
||||
*/
|
||||
public $usage = null;
|
||||
|
||||
/**
|
||||
* SiteHealth class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Admin\SiteHealth|\AIOSEO\Plugin\Pro\Admin\SiteHealth
|
||||
*/
|
||||
public $siteHealth = null;
|
||||
|
||||
/**
|
||||
* AutoUpdates class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Pro\Admin\AutoUpdates
|
||||
*/
|
||||
public $autoUpdates = null;
|
||||
|
||||
/**
|
||||
* Templates class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Utils\Templates|\AIOSEO\Plugin\Pro\Utils\Templates
|
||||
*/
|
||||
public $templates = null;
|
||||
|
||||
/**
|
||||
* Filters class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Lite\Main\Filters|\AIOSEO\Plugin\Pro\Main\Filters
|
||||
*/
|
||||
public $filters = null;
|
||||
|
||||
/**
|
||||
* ActionScheduler class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Utils\ActionScheduler
|
||||
*/
|
||||
public $actionScheduler = null;
|
||||
|
||||
/**
|
||||
* AI class instance.
|
||||
*
|
||||
* @since 4.3.3
|
||||
*
|
||||
* @var null|\AIOSEO\Plugin\Pro\Ai\Ai
|
||||
*/
|
||||
public $ai = null;
|
||||
|
||||
/**
|
||||
* SeoRevisions class instance.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @var null|\AIOSEO\Plugin\Pro\SeoRevisions\SeoRevisions
|
||||
*/
|
||||
public $seoRevisions = null;
|
||||
|
||||
/**
|
||||
* Crawl Cleanup class instance.
|
||||
*
|
||||
* @since 4.5.8
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\QueryArgs\CrawlCleanup
|
||||
*/
|
||||
public $crawlCleanup = null;
|
||||
|
||||
/**
|
||||
* Search Cleanup class instance.
|
||||
*
|
||||
* @since 4.8.0
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\SearchCleanup\SearchCleanup
|
||||
*/
|
||||
public $searchCleanup = null;
|
||||
|
||||
/**
|
||||
* EmailReports class instance.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @var null|\AIOSEO\Plugin\Common\EmailReports\EmailReports
|
||||
*/
|
||||
public $emailReports = null;
|
||||
|
||||
/**
|
||||
* ThirdParty class instance.
|
||||
*
|
||||
* @since 4.7.6
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\ThirdParty\ThirdParty
|
||||
*/
|
||||
public $thirdParty = null;
|
||||
|
||||
/**
|
||||
* WritingAssistant class instance.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\WritingAssistant\WritingAssistant
|
||||
*/
|
||||
public $writingAssistant = null;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* Checks for conflicting plugins.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class ConflictingPlugins {
|
||||
/**
|
||||
* A list of conflicting plugin slugs.
|
||||
*
|
||||
* @since 4.5.1
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $conflictingPluginSlugs = [
|
||||
// Note: We should NOT add Jetpack here since they automatically disable their SEO module when ours is active.
|
||||
'wordpress-seo',
|
||||
'seo-by-rank-math',
|
||||
'wp-seopress',
|
||||
'autodescription',
|
||||
'slim-seo',
|
||||
'squirrly-seo',
|
||||
'google-sitemap-generator',
|
||||
'xml-sitemap-feed',
|
||||
'www-xml-sitemap-generator-org',
|
||||
'google-sitemap-plugin',
|
||||
];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
// We don't want to trigger our notices when not in the admin.
|
||||
if ( ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'init', [ $this, 'init' ], 20 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the conflicting plugins check.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
// Only do this for users who can install/deactivate plugins.
|
||||
if ( ! current_user_can( 'install_plugins' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$conflictingPlugins = $this->getAllConflictingPlugins();
|
||||
|
||||
$notification = Models\Notification::getNotificationByName( 'conflicting-plugins' );
|
||||
if ( empty( $conflictingPlugins ) ) {
|
||||
if ( ! $notification->exists() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Models\Notification::deleteNotificationByName( 'conflicting-plugins' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
aioseo()->notices->conflictingPlugins( $conflictingPlugins );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all conflicting plugins.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return array An array of conflicting plugins.
|
||||
*/
|
||||
public function getAllConflictingPlugins() {
|
||||
$conflictingSeoPlugins = $this->getConflictingPlugins( 'seo' );
|
||||
$conflictingSitemapPlugins = [];
|
||||
|
||||
if (
|
||||
aioseo()->options->sitemap->general->enable ||
|
||||
aioseo()->options->sitemap->rss->enable
|
||||
) {
|
||||
$conflictingSitemapPlugins = $this->getConflictingPlugins( 'sitemap' );
|
||||
}
|
||||
|
||||
return array_merge( $conflictingSeoPlugins, $conflictingSitemapPlugins );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of conflicting plugins for AIOSEO.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param string $type A type to look for.
|
||||
* @return array An array of conflicting plugins.
|
||||
*/
|
||||
public function getConflictingPlugins( $type ) {
|
||||
$activePlugins = wp_get_active_and_valid_plugins();
|
||||
if ( is_multisite() ) {
|
||||
$activePlugins = array_merge( $activePlugins, wp_get_active_network_plugins() );
|
||||
}
|
||||
|
||||
$conflictingPlugins = [];
|
||||
switch ( $type ) {
|
||||
// Note: We should NOT add Jetpack here since they automatically disable their SEO module when ours is active.
|
||||
case 'seo':
|
||||
$conflictingPlugins = [
|
||||
'Rank Math SEO' => 'seo-by-rank-math/rank-math.php',
|
||||
'Rank Math SEO Pro' => 'seo-by-rank-math-pro/rank-math-pro.php',
|
||||
'SEOPress' => 'wp-seopress/seopress.php',
|
||||
'The SEO Framework' => 'autodescription/autodescription.php',
|
||||
'Yoast SEO' => 'wordpress-seo/wp-seo.php',
|
||||
'Yoast SEO Premium' => 'wordpress-seo-premium/wp-seo-premium.php'
|
||||
];
|
||||
break;
|
||||
case 'sitemap':
|
||||
$conflictingPlugins = [
|
||||
'Google XML Sitemaps' => 'google-sitemap-generator/sitemap.php',
|
||||
'Google XML Sitemap Generator' => 'www-xml-sitemap-generator-org/www-xml-sitemap-generator-org.php',
|
||||
'Sitemap by BestWebSoft' => 'google-sitemap-plugin/google-sitemap-plugin.php',
|
||||
'XML Sitemap & Google News' => 'xml-sitemap-feed/xml-sitemap.php'
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
$activeConflictingPlugins = [];
|
||||
foreach ( $activePlugins as $pluginFilePath ) {
|
||||
foreach ( $conflictingPlugins as $index => $pluginPath ) {
|
||||
if ( false !== strpos( $pluginFilePath, $pluginPath ) ) {
|
||||
$activeConflictingPlugins[ $index ] = $pluginPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $activeConflictingPlugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate conflicting plugins.
|
||||
*
|
||||
* @since 4.5.1
|
||||
*
|
||||
* @param array $types An array of types to look for.
|
||||
* @return void
|
||||
*/
|
||||
public function deactivateConflictingPlugins( $types ) {
|
||||
$seo = in_array( 'seo', $types, true ) ? $this->getConflictingPlugins( 'seo' ) : [];
|
||||
$sitemap = in_array( 'sitemap', $types, true ) ? $this->getConflictingPlugins( 'sitemap' ) : [];
|
||||
$plugins = array_merge(
|
||||
$seo,
|
||||
$sitemap
|
||||
);
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
|
||||
foreach ( $plugins as $pluginPath ) {
|
||||
if ( is_plugin_active( $pluginPath ) ) {
|
||||
deactivate_plugins( $pluginPath );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of conflicting plugin slugs.
|
||||
*
|
||||
* @since 4.5.1
|
||||
*
|
||||
* @return array An array of conflicting plugin slugs.
|
||||
*/
|
||||
public function getConflictingPluginSlugs() {
|
||||
return $this->conflictingPluginSlugs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that holds our dashboard widget.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Dashboard {
|
||||
/**
|
||||
* Class Constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'wp_dashboard_setup', [ $this, 'addDashboardWidgets' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers our dashboard widgets.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addDashboardWidgets() {
|
||||
// Add the SEO Setup widget.
|
||||
if (
|
||||
$this->canShowWidget( 'seoSetup' ) &&
|
||||
apply_filters( 'aioseo_show_seo_setup', true ) &&
|
||||
( aioseo()->access->isAdmin() || aioseo()->access->hasCapability( 'aioseo_setup_wizard' ) ) &&
|
||||
! aioseo()->standalone->setupWizard->isCompleted()
|
||||
) {
|
||||
wp_add_dashboard_widget(
|
||||
'aioseo-seo-setup',
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
sprintf( esc_html__( '%s Setup', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME ),
|
||||
[
|
||||
$this,
|
||||
'outputSeoSetup',
|
||||
],
|
||||
null,
|
||||
null,
|
||||
'normal',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
|
||||
// Add the Overview widget.
|
||||
if (
|
||||
$this->canShowWidget( 'seoOverview' ) &&
|
||||
apply_filters( 'aioseo_show_seo_overview', true ) &&
|
||||
( aioseo()->access->isAdmin() || aioseo()->access->hasCapability( 'aioseo_page_analysis' ) ) &&
|
||||
aioseo()->options->advanced->truSeo
|
||||
) {
|
||||
wp_add_dashboard_widget(
|
||||
'aioseo-overview',
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
sprintf( esc_html__( '%s Overview', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME ),
|
||||
[
|
||||
$this,
|
||||
'outputSeoOverview',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Add the News widget.
|
||||
if (
|
||||
$this->canShowWidget( 'seoNews' ) &&
|
||||
apply_filters( 'aioseo_show_seo_news', true ) &&
|
||||
aioseo()->access->isAdmin()
|
||||
) {
|
||||
wp_add_dashboard_widget(
|
||||
'aioseo-rss-feed',
|
||||
esc_html__( 'SEO News', 'all-in-one-seo-pack' ),
|
||||
[
|
||||
$this,
|
||||
'displayRssDashboardWidget',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not to show the widget.
|
||||
*
|
||||
* @since 4.0.0
|
||||
* @version 4.2.8
|
||||
*
|
||||
* @param string $widget The widget to check if can show.
|
||||
* @return boolean True if yes, false otherwise.
|
||||
*/
|
||||
protected function canShowWidget( $widget ) { // phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the SEO Setup widget.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function outputSeoSetup() {
|
||||
$this->output( 'aioseo-seo-setup-app' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the SEO Overview widget.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function outputSeoOverview() {
|
||||
$this->output( 'aioseo-overview-app' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the widget wrapper for the Vue App.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @param string $appId The App ID to print out.
|
||||
* @return void
|
||||
*/
|
||||
private function output( $appId ) {
|
||||
// Enqueue the scripts for the widget.
|
||||
$this->enqueue();
|
||||
|
||||
// Opening tag.
|
||||
echo '<div id="' . esc_attr( $appId ) . '">';
|
||||
|
||||
// Loader element.
|
||||
require AIOSEO_DIR . '/app/Common/Views/parts/loader.php';
|
||||
|
||||
// Closing tag.
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the scripts and styles.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function enqueue() {
|
||||
aioseo()->core->assets->load( 'src/vue/standalone/dashboard-widgets/main.js', [], aioseo()->helpers->getVueData( 'dashboard' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Display RSS Dashboard Widget
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function displayRssDashboardWidget() {
|
||||
// Check if the user has chosen not to display this widget through screen options.
|
||||
$currentScreen = aioseo()->helpers->getCurrentScreen();
|
||||
if ( empty( $currentScreen->id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hiddenWidgets = get_user_meta( get_current_user_id(), 'metaboxhidden_' . $currentScreen->id );
|
||||
if ( $hiddenWidgets && count( $hiddenWidgets ) > 0 && is_array( $hiddenWidgets[0] ) && in_array( 'aioseo-rss-feed', $hiddenWidgets[0], true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rssItems = aioseo()->helpers->fetchAioseoArticles();
|
||||
if ( ! $rssItems ) {
|
||||
esc_html_e( 'Temporarily unable to load feed.', 'all-in-one-seo-pack' );
|
||||
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<ul>
|
||||
<?php
|
||||
foreach ( $rssItems as $item ) {
|
||||
?>
|
||||
<li>
|
||||
<a target="_blank" href="<?php echo esc_url( $item['url'] ); ?>" rel="noopener noreferrer">
|
||||
<?php echo esc_html( $item['title'] ); ?>
|
||||
</a>
|
||||
<span><?php echo esc_html( $item['date'] ); ?></span>
|
||||
<div>
|
||||
<?php echo esc_html( wp_strip_all_tags( $item['content'] ) ) . '...'; ?>
|
||||
</div>
|
||||
</li>
|
||||
<?php
|
||||
}
|
||||
|
||||
?>
|
||||
</ul>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivation survey.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*/
|
||||
class DeactivationSurvey {
|
||||
/**
|
||||
* The API URL we are calling.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $apiUrl = 'https://plugin.aioseo.com/wp-json/am-deactivate-survey/v1/deactivation-data';
|
||||
|
||||
/**
|
||||
* Name for this plugin.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Unique slug for this plugin.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $plugin;
|
||||
|
||||
/**
|
||||
* Primary class constructor.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @param string $name Plugin name.
|
||||
* @param string $plugin Plugin slug.
|
||||
*/
|
||||
public function __construct( $name = '', $plugin = '' ) {
|
||||
$this->name = $name;
|
||||
$this->plugin = $plugin;
|
||||
|
||||
// Don't run deactivation survey on dev sites.
|
||||
if ( aioseo()->helpers->isDev() ) {
|
||||
// return;
|
||||
}
|
||||
|
||||
add_action( 'admin_print_scripts', [ $this, 'js' ], 20 );
|
||||
add_action( 'admin_print_scripts', [ $this, 'css' ] );
|
||||
add_action( 'admin_footer', [ $this, 'modal' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL of the remote endpoint.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @return string The URL.
|
||||
*/
|
||||
public function getApiUrl() {
|
||||
if ( defined( 'AIOSEO_DEACTIVATION_SURVEY_URL' ) ) {
|
||||
return AIOSEO_DEACTIVATION_SURVEY_URL;
|
||||
}
|
||||
|
||||
return $this->apiUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current admin screen is the plugins page.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @return bool True if it is, false if not.
|
||||
*/
|
||||
public function isPluginPage() {
|
||||
$screen = aioseo()->helpers->getCurrentScreen();
|
||||
if ( empty( $screen->id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array( $screen->id, [ 'plugins', 'plugins-network' ], true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Survey javascript.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function js() {
|
||||
if ( ! $this->isPluginPage() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
window.addEventListener("load", function() {
|
||||
var deactivateLink = document.querySelector('#the-list [data-slug="<?php echo esc_html( $this->plugin ); ?>"] span.deactivate a') ||
|
||||
document.querySelector('#deactivate-<?php echo esc_html( $this->plugin ); ?>'),
|
||||
overlay = document.querySelector('#am-deactivate-survey-<?php echo esc_html( $this->plugin ); ?>'),
|
||||
form = overlay.querySelector('form'),
|
||||
formOpen = false;
|
||||
|
||||
deactivateLink.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
overlay.style.display = 'table';
|
||||
formOpen = true;
|
||||
form.querySelector('.am-deactivate-survey-option:first-of-type input[type=radio]').focus();
|
||||
});
|
||||
|
||||
form.addEventListener('change', function(event) {
|
||||
if (event.target.matches('input[type=radio]')) {
|
||||
event.preventDefault();
|
||||
Array.from(form.querySelectorAll('input[type=text], .error')).forEach(function(el) { el.style.display = 'none'; });
|
||||
Array.from(form.querySelectorAll('.am-deactivate-survey-option')).forEach(function(el) { el.classList.remove('selected'); });
|
||||
var option = event.target.closest('.am-deactivate-survey-option');
|
||||
option.classList.add('selected');
|
||||
|
||||
var otherField = option.querySelector('input[type=text]');
|
||||
if (otherField) {
|
||||
otherField.style.display = 'block';
|
||||
otherField.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
form.addEventListener('click', function(event) {
|
||||
if (event.target.matches('.am-deactivate-survey-deactivate')) {
|
||||
event.preventDefault();
|
||||
window.location.href = deactivateLink.getAttribute('href');
|
||||
}
|
||||
});
|
||||
|
||||
form.addEventListener('submit', function(event) {
|
||||
event.preventDefault();
|
||||
if (!form.querySelector('input[type=radio]:checked')) {
|
||||
if(!form.querySelector('span[class="error"]')) {
|
||||
form.querySelector('.am-deactivate-survey-footer')
|
||||
.insertAdjacentHTML('afterbegin', '<span class="error"><?php echo esc_js( __( 'Please select an option', 'all-in-one-seo-pack' ) ); ?></span>');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var selected = form.querySelector('.selected');
|
||||
var otherField = selected.querySelector('input[type=text]');
|
||||
var data = {
|
||||
code: selected.querySelector('input[type=radio]').value,
|
||||
reason: selected.querySelector('.am-deactivate-survey-option-reason').textContent,
|
||||
details: otherField ? otherField.value : '',
|
||||
site: '<?php echo esc_url( home_url() ); ?>',
|
||||
plugin: '<?php echo esc_html( $this->plugin ); ?>'
|
||||
}
|
||||
|
||||
var submitSurvey = fetch('<?php echo esc_url( $this->getApiUrl() ); ?>', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
|
||||
submitSurvey.finally(function() {
|
||||
window.location.href = deactivateLink.getAttribute('href');
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('keyup', function(event) {
|
||||
if (27 === event.keyCode && formOpen) {
|
||||
overlay.style.display = 'none';
|
||||
formOpen = false;
|
||||
deactivateLink.focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Survey CSS.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function css() {
|
||||
if ( ! $this->isPluginPage() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<style type="text/css">
|
||||
.am-deactivate-survey-modal {
|
||||
display: none;
|
||||
table-layout: fixed;
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: rgba(0,0,0,0.8);
|
||||
}
|
||||
.am-deactivate-survey-wrap {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.am-deactivate-survey {
|
||||
background-color: #fff;
|
||||
max-width: 550px;
|
||||
margin: 0 auto;
|
||||
padding: 30px;
|
||||
text-align: left;
|
||||
}
|
||||
.am-deactivate-survey .error {
|
||||
display: block;
|
||||
color: red;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
.am-deactivate-survey-title {
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 0 0 18px 0;
|
||||
margin: 0 0 18px 0;
|
||||
}
|
||||
.am-deactivate-survey-title span {
|
||||
color: #999;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.am-deactivate-survey-desc {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
margin: 0 0 18px 0;
|
||||
}
|
||||
.am-deactivate-survey-option {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
.am-deactivate-survey-option-input {
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
.am-deactivate-survey-option-details {
|
||||
display: none;
|
||||
width: 90%;
|
||||
margin: 10px 0 0 30px;
|
||||
}
|
||||
.am-deactivate-survey-footer {
|
||||
margin-top: 18px;
|
||||
}
|
||||
.am-deactivate-survey-deactivate {
|
||||
float: right;
|
||||
font-size: 13px;
|
||||
color: #ccc;
|
||||
text-decoration: none;
|
||||
padding-top: 7px;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Survey modal.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function modal() {
|
||||
if ( ! $this->isPluginPage() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$options = [
|
||||
1 => [
|
||||
'title' => esc_html__( 'I no longer need the plugin', 'all-in-one-seo-pack' ),
|
||||
],
|
||||
2 => [
|
||||
'title' => esc_html__( 'I\'m switching to a different plugin', 'all-in-one-seo-pack' ),
|
||||
'details' => esc_html__( 'Please share which plugin', 'all-in-one-seo-pack' ),
|
||||
],
|
||||
3 => [
|
||||
'title' => esc_html__( 'I couldn\'t get the plugin to work', 'all-in-one-seo-pack' ),
|
||||
],
|
||||
4 => [
|
||||
'title' => esc_html__( 'It\'s a temporary deactivation', 'all-in-one-seo-pack' ),
|
||||
],
|
||||
5 => [
|
||||
'title' => esc_html__( 'Other', 'all-in-one-seo-pack' ),
|
||||
'details' => esc_html__( 'Please share the reason', 'all-in-one-seo-pack' ),
|
||||
],
|
||||
];
|
||||
?>
|
||||
|
||||
<div class="am-deactivate-survey-modal" id="am-deactivate-survey-<?php echo esc_html( $this->plugin ); ?>">
|
||||
<div class="am-deactivate-survey-wrap">
|
||||
<form class="am-deactivate-survey" method="post">
|
||||
<span class="am-deactivate-survey-title"><span class="dashicons dashicons-testimonial"></span><?php echo ' ' . esc_html__( 'Quick Feedback', 'all-in-one-seo-pack' ); ?></span>
|
||||
<span class="am-deactivate-survey-desc">
|
||||
<?php
|
||||
echo esc_html(
|
||||
sprintf(
|
||||
// Translators: 1 - The plugin name.
|
||||
__( 'If you have a moment, please share why you are deactivating %1$s:', 'all-in-one-seo-pack' ),
|
||||
$this->name
|
||||
)
|
||||
);
|
||||
?>
|
||||
</span>
|
||||
<div class="am-deactivate-survey-options">
|
||||
<?php foreach ( $options as $id => $option ) : ?>
|
||||
<div class="am-deactivate-survey-option">
|
||||
<label for="am-deactivate-survey-option-<?php echo esc_html( $this->plugin ); ?>-<?php echo intval( $id ); ?>" class="am-deactivate-survey-option-label">
|
||||
<input
|
||||
id="am-deactivate-survey-option-<?php echo esc_html( $this->plugin ); ?>-<?php echo intval( $id ); ?>"
|
||||
class="am-deactivate-survey-option-input"
|
||||
type="radio"
|
||||
name="code"
|
||||
value="<?php echo intval( $id ); ?>"
|
||||
/>
|
||||
<span class="am-deactivate-survey-option-reason"><?php echo esc_html( $option['title'] ); ?></span>
|
||||
</label>
|
||||
<?php if ( ! empty( $option['details'] ) ) : ?>
|
||||
<input class="am-deactivate-survey-option-details" type="text" placeholder="<?php echo esc_html( $option['details'] ); ?>" />
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="am-deactivate-survey-footer">
|
||||
<button type="submit" class="am-deactivate-survey-submit button button-primary button-large">
|
||||
<?php
|
||||
echo sprintf(
|
||||
// Translators: 1 - & symbol.
|
||||
esc_html__( 'Submit %1$s Deactivate', 'all-in-one-seo-pack' ),
|
||||
'&'
|
||||
);
|
||||
?>
|
||||
</button>
|
||||
<a href="#" class="am-deactivate-survey-deactivate">
|
||||
<?php
|
||||
echo sprintf(
|
||||
// Translators: 1 - & symbol.
|
||||
esc_html__( 'Skip %1$s Deactivate', 'all-in-one-seo-pack' ),
|
||||
'&'
|
||||
);
|
||||
?>
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class that Pro and Lite both extend.
|
||||
*
|
||||
* @since 4.2.5
|
||||
*/
|
||||
class NetworkAdmin extends Admin {
|
||||
/**
|
||||
* Construct method.
|
||||
*
|
||||
* @since 4.2.5
|
||||
*/
|
||||
public function __construct() {
|
||||
include_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
if (
|
||||
is_network_admin() &&
|
||||
! is_plugin_active_for_network( plugin_basename( AIOSEO_FILE ) )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( wp_doing_ajax() || wp_doing_cron() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'sanitize_comment_cookies', [ $this, 'init' ], 21 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the admin.
|
||||
*
|
||||
* @since 4.2.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'network_admin_menu', [ $this, 'addNetworkMenu' ] );
|
||||
|
||||
add_action( 'init', [ $this, 'setPages' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the network menu inside of WordPress.
|
||||
*
|
||||
* @since 4.2.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addNetworkMenu() {
|
||||
$this->addMainMenu( 'aioseo' );
|
||||
|
||||
foreach ( $this->pages as $slug => $page ) {
|
||||
if (
|
||||
'aioseo-settings' !== $slug &&
|
||||
'aioseo-tools' !== $slug &&
|
||||
'aioseo-about' !== $slug &&
|
||||
'aioseo-feature-manager' !== $slug
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$hook = add_submenu_page(
|
||||
$this->pageSlug,
|
||||
! empty( $page['page_title'] ) ? $page['page_title'] : $page['menu_title'],
|
||||
$page['menu_title'],
|
||||
$this->getPageRequiredCapability( $slug ),
|
||||
$slug,
|
||||
[ $this, 'page' ]
|
||||
);
|
||||
add_action( "load-{$hook}", [ $this, 'hooks' ] );
|
||||
}
|
||||
|
||||
// Remove the "dashboard" submenu page that is not needed in the network admin.
|
||||
remove_submenu_page( $this->pageSlug, $this->pageSlug );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin\Notices;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the Conflicting Plugins notice..
|
||||
*
|
||||
* @since 4.5.1
|
||||
*/
|
||||
class ConflictingPlugins {
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.5.1
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'wp_ajax_aioseo-dismiss-conflicting-plugins-notice', [ $this, 'dismissNotice' ] );
|
||||
add_action( 'wp_ajax_aioseo-deactivate-conflicting-plugins-notice', [ $this, 'deactivateConflictingPlugins' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through all the checks to see if we should show the notice.
|
||||
*
|
||||
* @since 4.5.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function maybeShowNotice() {
|
||||
$dismissed = get_option( '_aioseo_conflicting_plugins_dismissed', true );
|
||||
if ( '1' === $dismissed ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'activate_plugins' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only show if there are conflicting plugins.
|
||||
$conflictingPlugins = aioseo()->conflictingPlugins->getAllConflictingPlugins();
|
||||
if ( empty( $conflictingPlugins ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->showNotice();
|
||||
|
||||
// Print the script to the footer.
|
||||
add_action( 'admin_footer', [ $this, 'printScript' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the notice.
|
||||
*
|
||||
* @since 4.5.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function showNotice() {
|
||||
$type = ! empty( aioseo()->conflictingPlugins->getConflictingPlugins( 'seo' ) ) ? 'SEO' : 'sitemap';
|
||||
?>
|
||||
<div class="notice notice-error aioseo-conflicting-plugin-notice is-dismissible">
|
||||
<p>
|
||||
<?php
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
// phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
// Translators: 1 - Type of conflicting plugin (i.e. SEO or Sitemap), 2 - Opening HTML link tag, 3 - Closing HTML link tag.
|
||||
__( 'Please keep only one %1$s plugin active, otherwise, you might lose your rankings and traffic. %2$sClick here to Deactivate.%3$s', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
$type,
|
||||
'<a href="#" rel="noopener noreferrer" class="deactivate-conflicting-plugins">',
|
||||
'</a>'
|
||||
),
|
||||
[
|
||||
'a' => [
|
||||
'href' => [],
|
||||
'rel' => [],
|
||||
'class' => []
|
||||
],
|
||||
'strong' => [],
|
||||
]
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#conflicting_seo_plugins.rank-math-notice {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the script for dismissing the notice.
|
||||
*
|
||||
* @since 4.5.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function printScript() {
|
||||
// Create a nonce.
|
||||
$nonce1 = wp_create_nonce( 'aioseo-dismiss-conflicting-plugins' );
|
||||
$nonce2 = wp_create_nonce( 'aioseo-deactivate-conflicting-plugins' );
|
||||
?>
|
||||
<script>
|
||||
window.addEventListener('load', function () {
|
||||
var dismissBtn,
|
||||
deactivateBtn
|
||||
|
||||
// Add an event listener to the dismiss button.
|
||||
dismissBtn = document.querySelector('.aioseo-conflicting-plugin-notice .notice-dismiss')
|
||||
dismissBtn.addEventListener('click', function (event) {
|
||||
var httpRequest = new XMLHttpRequest(),
|
||||
postData = ''
|
||||
|
||||
// Build the data to send in our request.
|
||||
postData += '&action=aioseo-dismiss-conflicting-plugins-notice'
|
||||
postData += '&nonce=<?php echo esc_html( $nonce1 ); ?>'
|
||||
|
||||
httpRequest.open('POST', '<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>')
|
||||
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
|
||||
httpRequest.send(postData)
|
||||
})
|
||||
|
||||
deactivateBtn = document.querySelector('.aioseo-conflicting-plugin-notice .deactivate-conflicting-plugins')
|
||||
deactivateBtn.addEventListener('click', function (event) {
|
||||
event.preventDefault()
|
||||
|
||||
var httpRequest = new XMLHttpRequest(),
|
||||
postData = ''
|
||||
|
||||
// Build the data to send in our request.
|
||||
postData += '&action=aioseo-deactivate-conflicting-plugins-notice'
|
||||
postData += '&nonce=<?php echo esc_html( $nonce2 ); ?>'
|
||||
|
||||
httpRequest.open('POST', '<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>')
|
||||
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
|
||||
httpRequest.onerror = function () {
|
||||
window.location.reload()
|
||||
}
|
||||
httpRequest.onload = function () {
|
||||
window.location.reload()
|
||||
}
|
||||
httpRequest.send(postData)
|
||||
})
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss the notice.
|
||||
*
|
||||
* @since 4.5.1
|
||||
*
|
||||
* @return string The successful response.
|
||||
*/
|
||||
public function dismissNotice() {
|
||||
// Early exit if we're not on a aioseo-dismiss-conflicting-plugins-notice action.
|
||||
if ( ! isset( $_POST['action'] ) || 'aioseo-dismiss-conflicting-plugins-notice' !== $_POST['action'] ) {
|
||||
return wp_send_json_error( 'invalid-action' );
|
||||
}
|
||||
|
||||
check_ajax_referer( 'aioseo-dismiss-conflicting-plugins', 'nonce' );
|
||||
|
||||
update_option( '_aioseo_conflicting_plugins_dismissed', true );
|
||||
|
||||
return wp_send_json_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates the conflicting plugins.
|
||||
*
|
||||
* @since 4.5.1
|
||||
*
|
||||
* @return string The successful response.
|
||||
*/
|
||||
public function deactivateConflictingPlugins() {
|
||||
// Early exit if we're not on a aioseo-dismiss-conflicting-plugins-notice action.
|
||||
if ( ! isset( $_POST['action'] ) || 'aioseo-deactivate-conflicting-plugins-notice' !== $_POST['action'] ) {
|
||||
return wp_send_json_error( 'invalid-action' );
|
||||
}
|
||||
|
||||
check_ajax_referer( 'aioseo-deactivate-conflicting-plugins', 'nonce' );
|
||||
|
||||
aioseo()->conflictingPlugins->deactivateConflictingPlugins( [ 'seo', 'sitemap' ] );
|
||||
|
||||
return wp_send_json_success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin\Notices;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WordPress Deprecated Notice.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*/
|
||||
class DeprecatedWordPress {
|
||||
/**
|
||||
* Class Constructor.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'wp_ajax_aioseo-dismiss-deprecated-wordpress-notice', [ $this, 'dismissNotice' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through all the checks to see if we should show the notice.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function maybeShowNotice() {
|
||||
global $wp_version; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
|
||||
$dismissed = get_option( '_aioseo_deprecated_wordpress_dismissed', true );
|
||||
if ( '1' === $dismissed ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only show to users that interact with our pluign.
|
||||
if ( ! current_user_can( 'publish_posts' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only show if WordPress version is deprecated.
|
||||
if ( version_compare( $wp_version, '5.3', '>=' ) ) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
return;
|
||||
}
|
||||
|
||||
$this->showNotice();
|
||||
|
||||
// Print the script to the footer.
|
||||
add_action( 'admin_footer', [ $this, 'printScript' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually show the review plugin.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function showNotice() {
|
||||
$medium = false !== strpos( AIOSEO_PHP_VERSION_DIR, 'pro' ) ? 'proplugin' : 'liteplugin';
|
||||
?>
|
||||
<div class="notice notice-warning aioseo-deprecated-wordpress-notice is-dismissible">
|
||||
<p>
|
||||
<?php
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
// Translators: 1 - Opening HTML bold tag, 2 - Closing HTML bold tag.
|
||||
__( 'Your site is running an %1$soutdated version%2$s of WordPress. We recommend using the latest version of WordPress in order to keep your site secure.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
),
|
||||
[
|
||||
'strong' => [],
|
||||
]
|
||||
);
|
||||
?>
|
||||
<br><br>
|
||||
<?php
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
// phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
// Translators: 1 - Opening HTML bold tag, 2 - Closing HTML bold tag, 3 - The short plugin name ("AIOSEO"), 4 - The current year, 5 - Opening HTML link tag, 6 - Closing HTML link tag.
|
||||
__( '%1$sNote:%2$s %3$s will be discontinuing support for WordPress versions older than version 5.3 by the end of %4$s. %5$sRead more for additional information.%6$s', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
'<strong>',
|
||||
'</strong>',
|
||||
'AIOSEO',
|
||||
gmdate( 'Y' ),
|
||||
'<a href="https://aioseo.com/docs/update-wordpress/?utm_source=WordPress&utm_medium=' . $medium . '&utm_campaign=outdated-wordpress-notice" target="_blank" rel="noopener noreferrer">', // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
'</a>'
|
||||
),
|
||||
[
|
||||
'a' => [
|
||||
'href' => [],
|
||||
'target' => [],
|
||||
'rel' => [],
|
||||
],
|
||||
'strong' => [],
|
||||
]
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
// In case this is on plugin activation.
|
||||
if ( isset( $_GET['activate'] ) ) { // phpcs:ignore HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended
|
||||
unset( $_GET['activate'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the script for dismissing the notice.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function printScript() {
|
||||
// Create a nonce.
|
||||
$nonce = wp_create_nonce( 'aioseo-dismiss-deprecated-wordpress' );
|
||||
?>
|
||||
<script>
|
||||
window.addEventListener('load', function () {
|
||||
var dismissBtn
|
||||
|
||||
// Add an event listener to the dismiss button.
|
||||
dismissBtn = document.querySelector('.aioseo-deprecated-wordpress-notice .notice-dismiss')
|
||||
dismissBtn.addEventListener('click', function (event) {
|
||||
var httpRequest = new XMLHttpRequest(),
|
||||
postData = ''
|
||||
|
||||
// Build the data to send in our request.
|
||||
postData += '&action=aioseo-dismiss-deprecated-wordpress-notice'
|
||||
postData += '&nonce=<?php echo esc_html( $nonce ); ?>'
|
||||
|
||||
httpRequest.open('POST', '<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>')
|
||||
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
|
||||
httpRequest.send(postData)
|
||||
})
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss the deprecated WordPress notice.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*
|
||||
* @return string The successful response.
|
||||
*/
|
||||
public function dismissNotice() {
|
||||
// Early exit if we're not on a aioseo-dismiss-deprecated-wordpress-notice action.
|
||||
if ( ! isset( $_POST['action'] ) || 'aioseo-dismiss-deprecated-wordpress-notice' !== $_POST['action'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
check_ajax_referer( 'aioseo-dismiss-deprecated-wordpress', 'nonce' );
|
||||
|
||||
update_option( '_aioseo_deprecated_wordpress_dismissed', true );
|
||||
|
||||
return wp_send_json_success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin\Notices;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin import notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Import {
|
||||
/**
|
||||
* Go through all the checks to see if we should show the notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function maybeShowNotice() {
|
||||
if ( ! aioseo()->importExport->isImportRunning() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->showNotice();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the notice so that it appears.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function showNotice() {
|
||||
$string1 = __( 'SEO Meta Import In Progress', 'all-in-one-seo-pack' );
|
||||
// Translators: 1 - The plugin name ("All in One SEO").
|
||||
$string2 = sprintf( __( '%1$s is importing your existing SEO data in the background.', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_NAME );
|
||||
$string3 = __( 'This notice will automatically disappear as soon as the import has completed. Meanwhile, everything should continue to work as expected.', 'all-in-one-seo-pack' );
|
||||
?>
|
||||
<div class="notice notice-info aioseo-migration">
|
||||
<p><strong><?php echo esc_html( $string1 ); ?></strong></p>
|
||||
<p><?php echo esc_html( $string2 ); ?></p>
|
||||
<p><?php echo esc_html( $string3 ); ?></p>
|
||||
</div>
|
||||
<style>
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin\Notices;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 to V4 migration notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Migration {
|
||||
/**
|
||||
* Go through all the checks to see if we should show the notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function maybeShowNotice() {
|
||||
$transientPosts = aioseo()->core->cache->get( 'v3_migration_in_progress_posts' );
|
||||
$transientTerms = aioseo()->core->cache->get( 'v3_migration_in_progress_terms' );
|
||||
if ( ! $transientPosts && ! $transientTerms ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->showNotice();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the notice so that it appears.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function showNotice() {
|
||||
// Translators: 1 - The plugin name ("AIOSEO).
|
||||
$string1 = sprintf( __( '%1$s V3->V4 Migration In Progress', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME );
|
||||
// Translators: 1 - The plugin name ("All in One SEO").
|
||||
$string2 = sprintf( __( '%1$s is currently upgrading your database and migrating your SEO data in the background.', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_NAME );
|
||||
$string3 = __( 'This notice will automatically disappear as soon as the migration has completed. Meanwhile, everything should continue to work as expected.', 'all-in-one-seo-pack' );
|
||||
?>
|
||||
<div class="notice notice-info aioseo-migration">
|
||||
<p><strong><?php echo esc_html( $string1 ); ?></strong></p>
|
||||
<p><?php echo esc_html( $string2 ); ?></p>
|
||||
<p><?php echo esc_html( $string3 ); ?></p>
|
||||
</div>
|
||||
<style>
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,607 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin\Notices;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* Abstract class that Pro and Lite both extend.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Notices {
|
||||
/**
|
||||
* Source of notifications content.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $url = 'https://plugin-cdn.aioseo.com/wp-content/notifications.json';
|
||||
|
||||
/**
|
||||
* Review class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Review
|
||||
*/
|
||||
private $review = null;
|
||||
|
||||
/**
|
||||
* Migration class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Migration
|
||||
*/
|
||||
private $migration = null;
|
||||
|
||||
/**
|
||||
* Import class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Import
|
||||
*/
|
||||
private $import = null;
|
||||
|
||||
/**
|
||||
* DeprecatedWordPress class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var DeprecatedWordPress
|
||||
*/
|
||||
private $deprecatedWordPress = null;
|
||||
|
||||
/**
|
||||
* ConflictingPlugins class instance.
|
||||
*
|
||||
* @since 4.5.1
|
||||
*
|
||||
* @var ConflictingPlugins
|
||||
*/
|
||||
private $conflictingPlugins = null;
|
||||
|
||||
/**
|
||||
* Class Constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'aioseo_admin_notifications_update', [ $this, 'update' ] );
|
||||
|
||||
if ( ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'updated_option', [ $this, 'maybeResetBlogVisibility' ], 10, 3 );
|
||||
add_action( 'init', [ $this, 'init' ], 2 );
|
||||
|
||||
$this->review = new Review();
|
||||
$this->migration = new Migration();
|
||||
$this->import = new Import();
|
||||
$this->deprecatedWordPress = new DeprecatedWordPress();
|
||||
$this->conflictingPlugins = new ConflictingPlugins();
|
||||
|
||||
add_action( 'admin_notices', [ $this, 'notices' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize notifications.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
// If our tables do not exist, create them now.
|
||||
if ( ! aioseo()->core->db->tableExists( 'aioseo_notifications' ) ) {
|
||||
aioseo()->updates->addInitialCustomTablesForV4();
|
||||
}
|
||||
|
||||
$this->maybeUpdate();
|
||||
$this->initInternalNotices();
|
||||
$this->deleteInternalNotices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we should update our notifications.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function maybeUpdate() {
|
||||
$nextRun = aioseo()->core->networkCache->get( 'admin_notifications_update' );
|
||||
if ( null !== $nextRun && time() < $nextRun ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Schedule the action.
|
||||
aioseo()->actionScheduler->scheduleAsync( 'aioseo_admin_notifications_update' );
|
||||
|
||||
// Update the cache.
|
||||
aioseo()->core->networkCache->update( 'admin_notifications_update', time() + DAY_IN_SECONDS );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Notifications from the server.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function update() {
|
||||
$notifications = $this->fetch();
|
||||
foreach ( $notifications as $notification ) {
|
||||
// First, let's check to see if this notification already exists. If so, we want to override it.
|
||||
$n = aioseo()->core->db
|
||||
->start( 'aioseo_notifications' )
|
||||
->where( 'notification_id', $notification->id )
|
||||
->run()
|
||||
->model( 'AIOSEO\\Plugin\\Common\\Models\\Notification' );
|
||||
|
||||
$buttons = [
|
||||
'button1' => [
|
||||
'label' => ! empty( $notification->btns->main->text ) ? $notification->btns->main->text : null,
|
||||
'url' => ! empty( $notification->btns->main->url ) ? $notification->btns->main->url : null
|
||||
],
|
||||
'button2' => [
|
||||
'label' => ! empty( $notification->btns->alt->text ) ? $notification->btns->alt->text : null,
|
||||
'url' => ! empty( $notification->btns->alt->url ) ? $notification->btns->alt->url : null
|
||||
]
|
||||
];
|
||||
|
||||
if ( $n->exists() ) {
|
||||
$n->title = $notification->title;
|
||||
$n->content = $notification->content;
|
||||
$n->type = ! empty( $notification->notification_type ) ? $notification->notification_type : 'info';
|
||||
$n->level = $notification->type;
|
||||
$n->notification_id = $notification->id;
|
||||
$n->start = ! empty( $notification->start ) ? $notification->start : null;
|
||||
$n->end = ! empty( $notification->end ) ? $notification->end : null;
|
||||
$n->button1_label = $buttons['button1']['label'];
|
||||
$n->button1_action = $buttons['button1']['url'];
|
||||
$n->button2_label = $buttons['button2']['label'];
|
||||
$n->button2_action = $buttons['button2']['url'];
|
||||
$n->save();
|
||||
continue;
|
||||
}
|
||||
|
||||
$n = new Models\Notification();
|
||||
$n->slug = uniqid();
|
||||
$n->title = $notification->title;
|
||||
$n->content = $notification->content;
|
||||
$n->type = ! empty( $notification->notification_type ) ? $notification->notification_type : 'info';
|
||||
$n->level = $notification->type;
|
||||
$n->notification_id = $notification->id;
|
||||
$n->start = ! empty( $notification->start ) ? $notification->start : null;
|
||||
$n->end = ! empty( $notification->end ) ? $notification->end : null;
|
||||
$n->button1_label = $buttons['button1']['label'];
|
||||
$n->button1_action = $buttons['button1']['url'];
|
||||
$n->button2_label = $buttons['button2']['label'];
|
||||
$n->button2_action = $buttons['button2']['url'];
|
||||
$n->dismissed = 0;
|
||||
$n->save();
|
||||
|
||||
// Since we've added a new remote notification, let's show the notification drawer.
|
||||
aioseo()->core->cache->update( 'show_notifications_drawer', true );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the feed of notifications.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return array An array of notifications.
|
||||
*/
|
||||
private function fetch() {
|
||||
$response = aioseo()->helpers->wpRemoteGet( $this->getUrl() );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
|
||||
if ( empty( $body ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->verify( json_decode( $body ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify notification data before it is saved.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param array $notifications Array of notifications items to verify.
|
||||
* @return array An array of verified notifications.
|
||||
*/
|
||||
private function verify( $notifications ) {
|
||||
$data = [];
|
||||
if ( ! is_array( $notifications ) || empty( $notifications ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
foreach ( $notifications as $notification ) {
|
||||
// The message and license should never be empty, if they are, ignore.
|
||||
if ( empty( $notification->content ) || empty( $notification->type ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! is_array( $notification->type ) ) {
|
||||
$notification->type = [ $notification->type ];
|
||||
}
|
||||
foreach ( $notification->type as $type ) {
|
||||
// Ignore if type does not match.
|
||||
if ( ! $this->validateType( $type ) ) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore if expired.
|
||||
if ( ! empty( $notification->end ) && time() > strtotime( $notification->end ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore if notification existed before installing AIOSEO.
|
||||
// Prevents bombarding the user with notifications after activation.
|
||||
$activated = aioseo()->internalOptions->internal->firstActivated( time() );
|
||||
if (
|
||||
! empty( $notification->start ) &&
|
||||
$activated > strtotime( $notification->start )
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[] = $notification;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the notification type.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param string $type The notification type we are targeting.
|
||||
* @return boolean True if yes, false if no.
|
||||
*/
|
||||
public function validateType( $type ) {
|
||||
$validated = false;
|
||||
|
||||
if ( 'all' === $type ) {
|
||||
$validated = true;
|
||||
}
|
||||
|
||||
// Store notice if version matches.
|
||||
if ( $this->versionMatch( aioseo()->version, $type ) ) {
|
||||
$validated = true;
|
||||
}
|
||||
|
||||
return $validated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Version Compare.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param string $currentVersion The current version being used.
|
||||
* @param string|array $compareVersion The version to compare with.
|
||||
* @return bool True if we match, false if not.
|
||||
*/
|
||||
public function versionMatch( $currentVersion, $compareVersion ) {
|
||||
if ( is_array( $compareVersion ) ) {
|
||||
foreach ( $compareVersion as $compare_single ) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
$recursiveResult = $this->versionMatch( $currentVersion, $compare_single ); // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
if ( $recursiveResult ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentParse = explode( '.', $currentVersion );
|
||||
if ( strpos( $compareVersion, '-' ) ) {
|
||||
$compareParse = explode( '-', $compareVersion );
|
||||
} elseif ( strpos( $compareVersion, '.' ) ) {
|
||||
$compareParse = explode( '.', $compareVersion );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentCount = count( $currentParse );
|
||||
$compareCount = count( $compareParse );
|
||||
for ( $i = 0; $i < $currentCount || $i < $compareCount; $i++ ) {
|
||||
if ( isset( $compareParse[ $i ] ) && 'x' === strtolower( $compareParse[ $i ] ) ) {
|
||||
unset( $compareParse[ $i ] );
|
||||
}
|
||||
|
||||
if ( ! isset( $currentParse[ $i ] ) ) {
|
||||
unset( $compareParse[ $i ] );
|
||||
} elseif ( ! isset( $compareParse[ $i ] ) ) {
|
||||
unset( $currentParse[ $i ] );
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $compareParse as $index => $subNumber ) {
|
||||
if ( $currentParse[ $index ] !== $subNumber ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the URL for the notifications api.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return string The URL to use for the api requests.
|
||||
*/
|
||||
private function getUrl() {
|
||||
if ( defined( 'AIOSEO_NOTIFICATIONS_URL' ) ) {
|
||||
return AIOSEO_NOTIFICATIONS_URL;
|
||||
}
|
||||
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add notices.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function notices() {
|
||||
// Double check we're actually in the admin before outputting anything.
|
||||
if ( ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->review->maybeShowNotice();
|
||||
$this->migration->maybeShowNotice();
|
||||
$this->import->maybeShowNotice();
|
||||
$this->deprecatedWordPress->maybeShowNotice();
|
||||
$this->conflictingPlugins->maybeShowNotice();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the internal notices.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function initInternalNotices() {
|
||||
$this->blogVisibility();
|
||||
$this->descriptionFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes internal notices we no longer need.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function deleteInternalNotices() {
|
||||
$pluginData = aioseo()->helpers->getPluginData();
|
||||
if ( $pluginData['miPro']['installed'] || $pluginData['miLite']['installed'] ) {
|
||||
$notification = Models\Notification::getNotificationByName( 'install-mi' );
|
||||
if ( ! $notification->exists() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Models\Notification::deleteNotificationByName( 'install-mi' );
|
||||
}
|
||||
|
||||
if ( $pluginData['optinMonster']['installed'] ) {
|
||||
$notification = Models\Notification::getNotificationByName( 'install-om' );
|
||||
if ( ! $notification->exists() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Models\Notification::deleteNotificationByName( 'install-om' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends a notice by a (default) 1 week start date.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param string $notice The notice to extend.
|
||||
* @param string $start How long to extend.
|
||||
* @return void
|
||||
*/
|
||||
public function remindMeLater( $notice, $start = '+1 week' ) {
|
||||
$notification = Models\Notification::getNotificationByName( $notice );
|
||||
if ( ! $notification->exists() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$notification->start = gmdate( 'Y-m-d H:i:s', strtotime( $start ) );
|
||||
$notification->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a notice if the blog is set to hidden.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function blogVisibility() {
|
||||
$notification = Models\Notification::getNotificationByName( 'blog-visibility' );
|
||||
if ( get_option( 'blog_public' ) ) {
|
||||
if ( $notification->exists() ) {
|
||||
Models\Notification::deleteNotificationByName( 'blog-visibility' );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $notification->exists() || ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Models\Notification::addNotification( [
|
||||
'slug' => uniqid(),
|
||||
'notification_name' => 'blog-visibility',
|
||||
'title' => __( 'Search Engines Blocked', 'all-in-one-seo-pack' ),
|
||||
'content' => sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
__( 'Warning: %1$s has detected that you are blocking access to search engines. You can change this in Settings > Reading if this was unintended.', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_SHORT_NAME
|
||||
),
|
||||
'type' => 'error',
|
||||
'level' => [ 'all' ],
|
||||
'button1_label' => __( 'Fix Now', 'all-in-one-seo-pack' ),
|
||||
'button1_action' => admin_url( 'options-reading.php' ),
|
||||
'button2_label' => __( 'Remind Me Later', 'all-in-one-seo-pack' ),
|
||||
'button2_action' => 'http://action#notification/blog-visibility-reminder',
|
||||
'start' => gmdate( 'Y-m-d H:i:s' )
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a notice if the description format is missing the Description tag.
|
||||
*
|
||||
* @since 4.0.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function descriptionFormat() {
|
||||
$notification = Models\Notification::getNotificationByName( 'description-format' );
|
||||
if ( ! in_array( 'descriptionFormat', aioseo()->internalOptions->deprecatedOptions, true ) ) {
|
||||
if ( $notification->exists() ) {
|
||||
Models\Notification::deleteNotificationByName( 'description-format' );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$descriptionFormat = aioseo()->options->deprecated->searchAppearance->global->descriptionFormat;
|
||||
if ( false !== strpos( $descriptionFormat, '#description' ) ) {
|
||||
if ( $notification->exists() ) {
|
||||
Models\Notification::deleteNotificationByName( 'description-format' );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $notification->exists() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Models\Notification::addNotification( [
|
||||
'slug' => uniqid(),
|
||||
'notification_name' => 'description-format',
|
||||
'title' => __( 'Invalid Description Format', 'all-in-one-seo-pack' ),
|
||||
'content' => sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
__( 'Warning: %1$s has detected that you may have an invalid description format. This could lead to descriptions not being properly applied to your content.', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_SHORT_NAME
|
||||
) . ' ' . __( 'A Description tag is required in order to properly display your meta descriptions on your site.', 'all-in-one-seo-pack' ),
|
||||
'type' => 'error',
|
||||
'level' => [ 'all' ],
|
||||
'button1_label' => __( 'Fix Now', 'all-in-one-seo-pack' ),
|
||||
'button1_action' => 'http://route#aioseo-search-appearance&aioseo-scroll=description-format&aioseo-highlight=description-format:advanced',
|
||||
'button2_label' => __( 'Remind Me Later', 'all-in-one-seo-pack' ),
|
||||
'button2_action' => 'http://action#notification/description-format-reminder',
|
||||
'start' => gmdate( 'Y-m-d H:i:s' )
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if blog visibility is changing and add/delete the appropriate notification.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param string $optionName The name of the option we are checking.
|
||||
* @param mixed $oldValue The old value.
|
||||
* @param mixed $newValue The new value.
|
||||
* @return void
|
||||
*/
|
||||
public function maybeResetBlogVisibility( $optionName, $oldValue = '', $newValue = '' ) {
|
||||
if ( 'blog_public' === $optionName ) {
|
||||
if ( 1 === intval( $newValue ) ) {
|
||||
$notification = Models\Notification::getNotificationByName( 'blog-visibility' );
|
||||
if ( ! $notification->exists() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Models\Notification::deleteNotificationByName( 'blog-visibility' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->blogVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a notice if the blog is set to hidden.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function conflictingPlugins( $plugins = [] ) {
|
||||
if ( empty( $plugins ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$content = sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
__( 'Warning: %1$s has detected other active SEO or sitemap plugins. We recommend that you deactivate the following plugins to prevent any conflicts:', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_SHORT_NAME
|
||||
) . '<ul>';
|
||||
|
||||
foreach ( $plugins as $pluginName => $pluginPath ) {
|
||||
$content .= '<li><strong>' . $pluginName . '</strong></li>';
|
||||
}
|
||||
|
||||
$content .= '</ul>';
|
||||
|
||||
// Update an existing notice.
|
||||
$notification = Models\Notification::getNotificationByName( 'conflicting-plugins' );
|
||||
if ( $notification->exists() ) {
|
||||
$notification->content = $content;
|
||||
$notification->save();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new one if it doesn't exist.
|
||||
Models\Notification::addNotification( [
|
||||
'slug' => uniqid(),
|
||||
'notification_name' => 'conflicting-plugins',
|
||||
'title' => __( 'Conflicting Plugins Detected', 'all-in-one-seo-pack' ),
|
||||
'content' => $content,
|
||||
'type' => 'error',
|
||||
'level' => [ 'all' ],
|
||||
'button1_label' => __( 'Fix Now', 'all-in-one-seo-pack' ),
|
||||
'button1_action' => 'http://action#sitemap/deactivate-conflicting-plugins?refresh',
|
||||
'button2_label' => __( 'Remind Me Later', 'all-in-one-seo-pack' ),
|
||||
'button2_action' => 'http://action#notification/conflicting-plugins-reminder',
|
||||
'start' => gmdate( 'Y-m-d H:i:s' )
|
||||
] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin\Notices;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Review Plugin Notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Review {
|
||||
/**
|
||||
* Class Constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'wp_ajax_aioseo-dismiss-review-plugin-cta', [ $this, 'dismissNotice' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through all the checks to see if we should show the notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function maybeShowNotice() {
|
||||
$dismissed = get_user_meta( get_current_user_id(), '_aioseo_plugin_review_dismissed', true );
|
||||
if ( '3' === $dismissed || '4' === $dismissed ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! empty( $dismissed ) && $dismissed > time() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only show to users that interact with our pluign.
|
||||
if ( ! current_user_can( 'publish_posts' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only show if plugin has been active for over 10 days.
|
||||
if ( ! aioseo()->internalOptions->internal->firstActivated ) {
|
||||
aioseo()->internalOptions->internal->firstActivated = time();
|
||||
}
|
||||
|
||||
$activated = aioseo()->internalOptions->internal->firstActivated( time() );
|
||||
if ( $activated > strtotime( '-10 days' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( get_option( 'aioseop_options' ) || get_option( 'aioseo_options_v3' ) ) {
|
||||
$this->showNotice();
|
||||
} else {
|
||||
$this->showNotice2();
|
||||
}
|
||||
|
||||
// Print the script to the footer.
|
||||
add_action( 'admin_footer', [ $this, 'printScript' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually show the review plugin.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function showNotice() {
|
||||
$feedbackUrl = add_query_arg(
|
||||
[
|
||||
'wpf7528_24' => untrailingslashit( home_url() ),
|
||||
'wpf7528_26' => aioseo()->options->has( 'general' ) && aioseo()->options->general->has( 'licenseKey' )
|
||||
? aioseo()->options->general->licenseKey
|
||||
: '',
|
||||
'wpf7528_27' => aioseo()->pro ? 'pro' : 'lite',
|
||||
'wpf7528_28' => AIOSEO_VERSION,
|
||||
'utm_source' => aioseo()->pro ? 'proplugin' : 'liteplugin',
|
||||
'utm_medium' => 'review-notice',
|
||||
'utm_campaign' => 'feedback',
|
||||
'utm_content' => AIOSEO_VERSION,
|
||||
],
|
||||
'https://aioseo.com/plugin-feedback/'
|
||||
);
|
||||
|
||||
$string1 = sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
__( 'Are you enjoying %1$s?', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_NAME
|
||||
);
|
||||
$string2 = __( 'Yes I love it', 'all-in-one-seo-pack' );
|
||||
$string3 = __( 'Not Really...', 'all-in-one-seo-pack' );
|
||||
$string4 = sprintf(
|
||||
// Translators: 1 - The plugin name ("All in One SEO").
|
||||
__( 'We\'re sorry to hear you aren\'t enjoying %1$s. We would love a chance to improve. Could you take a minute and let us know what we can do better?', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_NAME
|
||||
); // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
$string5 = __( 'Give feedback', 'all-in-one-seo-pack' );
|
||||
$string6 = __( 'No thanks', 'all-in-one-seo-pack' );
|
||||
$string7 = __( 'That\'s awesome! Could you please do us a BIG favor and give it a 5-star rating on WordPress to help us spread the word and boost our motivation?', 'all-in-one-seo-pack' );
|
||||
// Translators: 1 - The plugin name ("All in One SEO").
|
||||
$string9 = __( 'Ok, you deserve it', 'all-in-one-seo-pack' );
|
||||
$string10 = __( 'Nope, maybe later', 'all-in-one-seo-pack' );
|
||||
$string11 = __( 'I already did', 'all-in-one-seo-pack' );
|
||||
|
||||
?>
|
||||
<div class="notice notice-info aioseo-review-plugin-cta is-dismissible">
|
||||
<div class="step-1">
|
||||
<p><?php echo esc_html( $string1 ); ?></p>
|
||||
<p>
|
||||
<a href="#" class="aioseo-review-switch-step-3" data-step="3"><?php echo esc_html( $string2 ); ?></a> 🙂 |
|
||||
<a href="#" class="aioseo-review-switch-step-2" data-step="2"><?php echo esc_html( $string3 ); ?></a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="step-2" style="display:none;">
|
||||
<p><?php echo esc_html( $string4 ); ?></p>
|
||||
<p>
|
||||
<a href="<?php echo esc_url( $feedbackUrl ); ?>" class="aioseo-dismiss-review-notice" target="_blank" rel="noopener noreferrer"><?php echo esc_html( $string5 ); ?></a>
|
||||
<a href="#" class="aioseo-dismiss-review-notice" target="_blank" rel="noopener noreferrer"><?php echo esc_html( $string6 ); ?></a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="step-3" style="display:none;">
|
||||
<p><?php echo esc_html( $string7 ); ?></p>
|
||||
<p>
|
||||
<a href="https://wordpress.org/support/plugin/all-in-one-seo-pack/reviews/?filter=5#new-post" class="aioseo-dismiss-review-notice" target="_blank" rel="noopener noreferrer">
|
||||
<?php echo esc_html( $string9 ); ?>
|
||||
</a> •
|
||||
<a href="#" class="aioseo-dismiss-review-notice-delay" target="_blank" rel="noopener noreferrer">
|
||||
<?php echo esc_html( $string10 ); ?>
|
||||
</a> •
|
||||
<a href="#" class="aioseo-dismiss-review-notice" target="_blank" rel="noopener noreferrer">
|
||||
<?php echo esc_html( $string11 ); ?>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually show the review plugin 2.0.
|
||||
*
|
||||
* @since 4.2.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function showNotice2() {
|
||||
$string1 = sprintf(
|
||||
// Translators: 1 - The plugin name ("All in One SEO").
|
||||
__( 'Hey, we noticed you have been using %1$s for some time - that’s awesome! Could you please do us a BIG favor and give it a 5-star rating on WordPress to help us spread the word and boost our motivation?', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
'<strong>' . esc_html( AIOSEO_PLUGIN_NAME ) . '</strong>'
|
||||
);
|
||||
|
||||
// Translators: 1 - The plugin name ("All in One SEO").
|
||||
$string9 = __( 'Ok, you deserve it', 'all-in-one-seo-pack' );
|
||||
$string10 = __( 'Nope, maybe later', 'all-in-one-seo-pack' );
|
||||
$string11 = __( 'I already did', 'all-in-one-seo-pack' );
|
||||
|
||||
?>
|
||||
<div class="notice notice-info aioseo-review-plugin-cta is-dismissible">
|
||||
<div class="step-3">
|
||||
<p><?php echo $string1; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></p>
|
||||
<p>
|
||||
<a href="https://wordpress.org/support/plugin/all-in-one-seo-pack/reviews/?filter=5#new-post" class="aioseo-dismiss-review-notice" target="_blank" rel="noopener noreferrer">
|
||||
<?php echo esc_html( $string9 ); ?>
|
||||
</a> •
|
||||
<a href="#" class="aioseo-dismiss-review-notice-delay" target="_blank" rel="noopener noreferrer">
|
||||
<?php echo esc_html( $string10 ); ?>
|
||||
</a> •
|
||||
<a href="#" class="aioseo-dismiss-review-notice" target="_blank" rel="noopener noreferrer">
|
||||
<?php echo esc_html( $string11 ); ?>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the script for dismissing the notice.
|
||||
*
|
||||
* @since 4.0.13
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function printScript() {
|
||||
// Create a nonce.
|
||||
$nonce = wp_create_nonce( 'aioseo-dismiss-review' );
|
||||
?>
|
||||
<style>
|
||||
.aioseop-notice-review_plugin_cta .aioseo-action-buttons {
|
||||
display: none;
|
||||
}
|
||||
@keyframes dismissBtnVisible {
|
||||
from { opacity: 0.99; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
.aioseo-review-plugin-cta button.notice-dismiss {
|
||||
animation-duration: 0.001s;
|
||||
animation-name: dismissBtnVisible;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
window.addEventListener('load', function () {
|
||||
var aioseoSetupButton,
|
||||
dismissBtn
|
||||
|
||||
aioseoSetupButton = function (dismissBtn) {
|
||||
var notice = document.querySelector('.notice.aioseo-review-plugin-cta'),
|
||||
delay = false,
|
||||
relay = true,
|
||||
stepOne = notice.querySelector('.step-1'),
|
||||
stepTwo = notice.querySelector('.step-2'),
|
||||
stepThree = notice.querySelector('.step-3')
|
||||
|
||||
// Add an event listener to the dismiss button.
|
||||
dismissBtn.addEventListener('click', function (event) {
|
||||
var httpRequest = new XMLHttpRequest(),
|
||||
postData = ''
|
||||
|
||||
// Build the data to send in our request.
|
||||
postData += '&delay=' + delay
|
||||
postData += '&relay=' + relay
|
||||
postData += '&action=aioseo-dismiss-review-plugin-cta'
|
||||
postData += '&nonce=<?php echo esc_html( $nonce ); ?>'
|
||||
|
||||
httpRequest.open('POST', '<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>')
|
||||
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
|
||||
httpRequest.send(postData)
|
||||
})
|
||||
|
||||
notice.addEventListener('click', function (event) {
|
||||
if (event.target.matches('.aioseo-review-switch-step-3')) {
|
||||
event.preventDefault()
|
||||
stepOne.style.display = 'none'
|
||||
stepTwo.style.display = 'none'
|
||||
stepThree.style.display = 'block'
|
||||
}
|
||||
if (event.target.matches('.aioseo-review-switch-step-2')) {
|
||||
event.preventDefault()
|
||||
stepOne.style.display = 'none'
|
||||
stepThree.style.display = 'none'
|
||||
stepTwo.style.display = 'block'
|
||||
}
|
||||
if (event.target.matches('.aioseo-dismiss-review-notice-delay')) {
|
||||
event.preventDefault()
|
||||
delay = true
|
||||
relay = false
|
||||
dismissBtn.click()
|
||||
}
|
||||
if (event.target.matches('.aioseo-dismiss-review-notice')) {
|
||||
if ('#' === event.target.getAttribute('href')) {
|
||||
event.preventDefault()
|
||||
}
|
||||
relay = false
|
||||
dismissBtn.click()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
dismissBtn = document.querySelector('.aioseo-review-plugin-cta .notice-dismiss')
|
||||
if (!dismissBtn) {
|
||||
document.addEventListener('animationstart', function (event) {
|
||||
if (event.animationName == 'dismissBtnVisible') {
|
||||
dismissBtn = document.querySelector('.aioseo-review-plugin-cta .notice-dismiss')
|
||||
if (dismissBtn) {
|
||||
aioseoSetupButton(dismissBtn)
|
||||
}
|
||||
}
|
||||
}, false)
|
||||
|
||||
} else {
|
||||
aioseoSetupButton(dismissBtn)
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss the review plugin CTA.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return string The successful response.
|
||||
*/
|
||||
public function dismissNotice() {
|
||||
// Early exit if we're not on a aioseo-dismiss-review-plugin-cta action.
|
||||
if ( ! isset( $_POST['action'] ) || 'aioseo-dismiss-review-plugin-cta' !== $_POST['action'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
check_ajax_referer( 'aioseo-dismiss-review', 'nonce' );
|
||||
$delay = isset( $_POST['delay'] ) ? 'true' === sanitize_text_field( wp_unslash( $_POST['delay'] ) ) : false;
|
||||
$relay = isset( $_POST['relay'] ) ? 'true' === sanitize_text_field( wp_unslash( $_POST['relay'] ) ) : false;
|
||||
|
||||
if ( ! $delay ) {
|
||||
update_user_meta( get_current_user_id(), '_aioseo_plugin_review_dismissed', $relay ? '4' : '3' );
|
||||
|
||||
return wp_send_json_success();
|
||||
}
|
||||
|
||||
update_user_meta( get_current_user_id(), '_aioseo_plugin_review_dismissed', strtotime( '+1 week' ) );
|
||||
|
||||
return wp_send_json_success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin\Notices;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WpNotices class.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*/
|
||||
class WpNotices {
|
||||
/**
|
||||
* Notices array
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $notices = [];
|
||||
|
||||
/**
|
||||
* The cache key.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $cacheKey = 'wp_notices';
|
||||
|
||||
/**
|
||||
* Class Constructor.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'rest_api_init', [ $this, 'registerApiField' ] );
|
||||
add_action( 'enqueue_block_editor_assets', [ $this, 'enqueueScripts' ] );
|
||||
add_action( 'admin_notices', [ $this, 'adminNotices' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue notices scripts.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueueScripts() {
|
||||
aioseo()->core->assets->load( 'src/vue/standalone/wp-notices/main.js' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an API field with notices.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerApiField() {
|
||||
foreach ( aioseo()->helpers->getPublicPostTypes( true ) as $postType ) {
|
||||
register_rest_field( $postType, 'aioseo_notices', [
|
||||
'get_callback' => [ $this, 'apiGetNotices' ]
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* API field callback.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @return array Notices array
|
||||
*/
|
||||
public function apiGetNotices() {
|
||||
$notices = $this->getNoticesInContext();
|
||||
|
||||
// Notices show only one time.
|
||||
$this->removeNotices( $notices );
|
||||
|
||||
return $notices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all notices.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @return array Notices array
|
||||
*/
|
||||
public function getNotices() {
|
||||
if ( empty( $this->notices ) ) {
|
||||
$this->notices = (array) aioseo()->core->cache->get( $this->cacheKey );
|
||||
}
|
||||
|
||||
return ! empty( $this->notices ) ? $this->notices : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all notices in the current context.
|
||||
*
|
||||
* @since 4.2.6
|
||||
*
|
||||
* @return array Notices array
|
||||
*/
|
||||
public function getNoticesInContext() {
|
||||
$contextNotices = $this->getNotices();
|
||||
foreach ( $contextNotices as $key => $notice ) {
|
||||
if ( empty( $notice['allowedContexts'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$allowed = false;
|
||||
foreach ( $notice['allowedContexts'] as $allowedContext ) {
|
||||
if ( $this->isAllowedContext( $allowedContext ) ) {
|
||||
$allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $allowed ) {
|
||||
unset( $contextNotices[ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $contextNotices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if we are in the current context.
|
||||
*
|
||||
* @since 4.2.6
|
||||
*
|
||||
* @param string $context The context to test. (posts)
|
||||
* @return bool Is the required context.
|
||||
*/
|
||||
private function isAllowedContext( $context ) {
|
||||
switch ( $context ) {
|
||||
case 'posts':
|
||||
return aioseo()->helpers->isScreenPostList() ||
|
||||
aioseo()->helpers->isScreenPostEdit() ||
|
||||
aioseo()->helpers->isAjaxCronRestRequest();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a notice by message.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @param string $message The message string.
|
||||
* @param string $type The message type.
|
||||
* @return void|array The found notice.
|
||||
*/
|
||||
public function getNotice( $message, $type = '' ) {
|
||||
$notices = $this->getNotices();
|
||||
foreach ( $notices as $notice ) {
|
||||
if ( $notice['options']['id'] === $this->getNoticeId( $message, $type ) ) {
|
||||
return $notice;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a notice id.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @param string $message The message string.
|
||||
* @param string $type The message type.
|
||||
* @return string The notice id.
|
||||
*/
|
||||
public function getNoticeId( $message, $type = '' ) {
|
||||
return md5( $message . $type );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear notices.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearNotices() {
|
||||
$this->notices = [];
|
||||
$this->updateCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove certain notices.
|
||||
*
|
||||
* @since 4.2.6
|
||||
*
|
||||
* @param array $notices A list of notices to remove.
|
||||
* @return void
|
||||
*/
|
||||
public function removeNotices( $notices ) {
|
||||
foreach ( array_keys( $notices ) as $noticeKey ) {
|
||||
unset( $this->notices[ $noticeKey ] );
|
||||
}
|
||||
$this->updateCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a notice.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @param string $message The message.
|
||||
* @param string $status The message status [success, info, warning, error]
|
||||
* @param array $options Options for the message. https://developer.wordpress.org/block-editor/reference-guides/data/data-core-notices/#createnotice
|
||||
* @param array $allowedContexts The contexts where this notice will show.
|
||||
* @return void
|
||||
*/
|
||||
public function addNotice( $message, $status = 'warning', $options = [], $allowedContexts = [] ) {
|
||||
$type = ! empty( $options['type'] ) ? $options['type'] : '';
|
||||
$foundNotice = $this->getNotice( $message, $type );
|
||||
if ( empty( $message ) || ! empty( $foundNotice ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$notice = [
|
||||
'message' => $message,
|
||||
'status' => $status,
|
||||
'options' => wp_parse_args( $options, [
|
||||
'id' => $this->getNoticeId( $message, $type ),
|
||||
'isDismissible' => true
|
||||
] ),
|
||||
'allowedContexts' => $allowedContexts
|
||||
];
|
||||
|
||||
$this->notices[] = $notice;
|
||||
$this->updateCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show notices on classic editor.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function adminNotices() {
|
||||
// Double check we're actually in the admin before outputting anything.
|
||||
if ( ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$notices = $this->getNoticesInContext();
|
||||
foreach ( $notices as $notice ) {
|
||||
// Hide snackbar notices on classic editor.
|
||||
if ( ! empty( $notice['options']['type'] ) && 'snackbar' === $notice['options']['type'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$status = ! empty( $notice['status'] ) ? $notice['status'] : 'warning';
|
||||
$class = ! empty( $notice['options']['class'] ) ? $notice['options']['class'] : '';
|
||||
?>
|
||||
<div
|
||||
class="notice notice-<?php echo esc_attr( $status ) ?> <?php echo esc_attr( $class ) ?>">
|
||||
<?php echo '<p>' . $notice['message'] . '</p>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
<?php
|
||||
if ( ! empty( $notice['options']['actions'] ) ) {
|
||||
foreach ( $notice['options']['actions'] as $action ) {
|
||||
echo '<p>';
|
||||
if ( ! empty( $action['url'] ) ) {
|
||||
$class = ! empty( $action['class'] ) ? $action['class'] : '';
|
||||
$target = ! empty( $action['target'] ) ? $action['target'] : '';
|
||||
echo '<a
|
||||
href="' . esc_attr( $action['url'] ) . '"
|
||||
class="' . esc_attr( $class ) . '"
|
||||
target="' . esc_attr( $target ) . '"
|
||||
>';
|
||||
}
|
||||
echo $action['label']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
if ( ! empty( $action['url'] ) ) {
|
||||
echo '</a>';
|
||||
}
|
||||
echo '</p>';
|
||||
}
|
||||
?>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
// Notices show only one time.
|
||||
$this->removeNotices( $notices );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to update the cache with the current notices array.
|
||||
*
|
||||
* @since 4.2.6
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function updateCache() {
|
||||
aioseo()->core->cache->update( $this->cacheKey, $this->notices );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* Abstract class that Pro and Lite both extend.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class PostSettings {
|
||||
/**
|
||||
* The integrations instance.
|
||||
*
|
||||
* @since 4.4.3
|
||||
*
|
||||
* @var array[object]
|
||||
*/
|
||||
public $integrations;
|
||||
|
||||
/**
|
||||
* Initialize the admin.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the Post Type Overview cache.
|
||||
add_action( 'save_post', [ $this, 'clearPostTypeOverviewCache' ], 100 );
|
||||
add_action( 'delete_post', [ $this, 'clearPostTypeOverviewCache' ], 100 );
|
||||
add_action( 'wp_trash_post', [ $this, 'clearPostTypeOverviewCache' ], 100 );
|
||||
|
||||
if ( wp_doing_ajax() || wp_doing_cron() || ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load Vue APP.
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueuePostSettingsAssets' ] );
|
||||
|
||||
// Add metabox.
|
||||
add_action( 'add_meta_boxes', [ $this, 'addPostSettingsMetabox' ] );
|
||||
|
||||
// Add metabox (upsell) to terms on init hook.
|
||||
add_action( 'init', [ $this, 'init' ], 1000 );
|
||||
|
||||
// Save metabox.
|
||||
add_action( 'save_post', [ $this, 'saveSettingsMetabox' ] );
|
||||
add_action( 'edit_attachment', [ $this, 'saveSettingsMetabox' ] );
|
||||
add_action( 'add_attachment', [ $this, 'saveSettingsMetabox' ] );
|
||||
|
||||
// Filter the sql clauses to show posts filtered by our params.
|
||||
add_filter( 'posts_clauses', [ $this, 'changeClausesToFilterPosts' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the JS/CSS for the on page/posts settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueuePostSettingsAssets() {
|
||||
if (
|
||||
aioseo()->helpers->isScreenBase( 'event-espresso' ) ||
|
||||
aioseo()->helpers->isScreenBase( 'post' ) ||
|
||||
aioseo()->helpers->isScreenBase( 'term' ) ||
|
||||
aioseo()->helpers->isScreenBase( 'edit-tags' ) ||
|
||||
aioseo()->helpers->isScreenBase( 'site-editor' )
|
||||
) {
|
||||
$page = null;
|
||||
if (
|
||||
aioseo()->helpers->isScreenBase( 'event-espresso' ) ||
|
||||
aioseo()->helpers->isScreenBase( 'post' )
|
||||
) {
|
||||
$page = 'post';
|
||||
}
|
||||
|
||||
aioseo()->core->assets->load( 'src/vue/standalone/post-settings/main.js', [], aioseo()->helpers->getVueData( $page ) );
|
||||
aioseo()->core->assets->load( 'src/vue/standalone/link-format/main.js', [], aioseo()->helpers->getVueData( $page ) );
|
||||
}
|
||||
|
||||
$screen = aioseo()->helpers->getCurrentScreen();
|
||||
if ( empty( $screen->id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'attachment' === $screen->id ) {
|
||||
wp_enqueue_media();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether or not we can add the metabox.
|
||||
*
|
||||
* @since 4.1.7
|
||||
*
|
||||
* @param string $postType The post type to check.
|
||||
* @return boolean Whether or not can add the Metabox.
|
||||
*/
|
||||
public function canAddPostSettingsMetabox( $postType ) {
|
||||
$dynamicOptions = aioseo()->dynamicOptions->noConflict();
|
||||
|
||||
$pageAnalysisSettingsCapability = aioseo()->access->hasCapability( 'aioseo_page_analysis' );
|
||||
$generalSettingsCapability = aioseo()->access->hasCapability( 'aioseo_page_general_settings' );
|
||||
$socialSettingsCapability = aioseo()->access->hasCapability( 'aioseo_page_social_settings' );
|
||||
$schemaSettingsCapability = aioseo()->access->hasCapability( 'aioseo_page_schema_settings' );
|
||||
$linkAssistantCapability = aioseo()->access->hasCapability( 'aioseo_page_link_assistant_settings' );
|
||||
$redirectsCapability = aioseo()->access->hasCapability( 'aioseo_page_redirects_manage' );
|
||||
$advancedSettingsCapability = aioseo()->access->hasCapability( 'aioseo_page_advanced_settings' );
|
||||
$seoRevisionsSettingsCapability = aioseo()->access->hasCapability( 'aioseo_page_seo_revisions_settings' );
|
||||
|
||||
if (
|
||||
$dynamicOptions->searchAppearance->postTypes->has( $postType ) &&
|
||||
$dynamicOptions->searchAppearance->postTypes->$postType->advanced->showMetaBox &&
|
||||
! (
|
||||
empty( $pageAnalysisSettingsCapability ) &&
|
||||
empty( $generalSettingsCapability ) &&
|
||||
empty( $socialSettingsCapability ) &&
|
||||
empty( $schemaSettingsCapability ) &&
|
||||
empty( $linkAssistantCapability ) &&
|
||||
empty( $redirectsCapability ) &&
|
||||
empty( $advancedSettingsCapability ) &&
|
||||
empty( $seoRevisionsSettingsCapability )
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a meta box to page/posts screens.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPostSettingsMetabox() {
|
||||
$screen = aioseo()->helpers->getCurrentScreen();
|
||||
if ( empty( $screen->post_type ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$postType = $screen->post_type;
|
||||
if ( $this->canAddPostSettingsMetabox( $postType ) ) {
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
$aioseoMetaboxTitle = sprintf( esc_html__( '%1$s Settings', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME );
|
||||
|
||||
add_meta_box(
|
||||
'aioseo-settings',
|
||||
$aioseoMetaboxTitle,
|
||||
[ $this, 'postSettingsMetabox' ],
|
||||
[ $postType ],
|
||||
'normal',
|
||||
apply_filters( 'aioseo_post_metabox_priority', 'high' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the on page/posts settings metabox with Vue App wrapper.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function postSettingsMetabox() {
|
||||
$this->postSettingsHiddenField();
|
||||
?>
|
||||
<div id="aioseo-post-settings-metabox">
|
||||
<?php aioseo()->templates->getTemplate( 'parts/loader.php' ); ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the hidden field where all the metabox data goes.
|
||||
*
|
||||
* @since 4.0.17
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function postSettingsHiddenField() {
|
||||
static $fieldExists = false;
|
||||
if ( $fieldExists ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fieldExists = true;
|
||||
|
||||
?>
|
||||
<div id="aioseo-post-settings-field">
|
||||
<input type="hidden" name="aioseo-post-settings" id="aioseo-post-settings" value=""/>
|
||||
<?php wp_nonce_field( 'aioseoPostSettingsNonce', 'PostSettingsNonce' ); ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles metabox saving.
|
||||
*
|
||||
* @since 4.0.3
|
||||
*
|
||||
* @param int $postId Post ID.
|
||||
* @return void
|
||||
*/
|
||||
public function saveSettingsMetabox( $postId ) {
|
||||
if ( ! aioseo()->helpers->isValidPost( $postId, [ 'all' ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Security check.
|
||||
if ( ! isset( $_POST['PostSettingsNonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['PostSettingsNonce'] ) ), 'aioseoPostSettingsNonce' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we don't have our post settings input, we can safely skip.
|
||||
if ( ! isset( $_POST['aioseo-post-settings'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check user permissions.
|
||||
if ( ! current_user_can( 'edit_post', $postId ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$currentPost = json_decode( sanitize_text_field( wp_unslash( ( $_POST['aioseo-post-settings'] ) ) ), true );
|
||||
|
||||
// If there is no data, there likely was an error, e.g. if the hidden field wasn't populated on load and the user saved the post without making changes in the metabox.
|
||||
// In that case we should return to prevent a complete reset of the data.
|
||||
// https://github.com/awesomemotive/aioseo/issues/2254
|
||||
if ( empty( $currentPost ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Models\Post::savePost( $postId, $currentPost );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the Post Type Overview cache from our cache table.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @param int $postId The Post ID being updated/deleted.
|
||||
* @return void
|
||||
*/
|
||||
public function clearPostTypeOverviewCache( $postId ) {
|
||||
$postType = get_post_type( $postId );
|
||||
if ( empty( $postType ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
aioseo()->core->cache->delete( $postType . '_overview_data' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of post types with an overview showing how many posts are good, okay and so on.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @return array The list of post types with the overview.
|
||||
*/
|
||||
public function getPostTypesOverview() {
|
||||
$overviewData = [];
|
||||
$eligiblePostTypes = aioseo()->helpers->getTruSeoEligiblePostTypes();
|
||||
foreach ( aioseo()->helpers->getPublicPostTypes( true ) as $postType ) {
|
||||
if ( ! in_array( $postType, $eligiblePostTypes, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$overviewData[ $postType ] = $this->getPostTypeOverview( $postType );
|
||||
}
|
||||
|
||||
return $overviewData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get how many posts are good, okay, needs improvement or are missing the focus keyphrase for the given post type.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @param string $postType The post type name.
|
||||
* @return array The overview data for the given post type.
|
||||
*/
|
||||
public function getPostTypeOverview( $postType ) {
|
||||
$overviewData = aioseo()->core->cache->get( $postType . '_overview_data' );
|
||||
if ( null !== $overviewData ) {
|
||||
return $overviewData;
|
||||
}
|
||||
|
||||
$eligiblePostTypes = aioseo()->helpers->getTruSeoEligiblePostTypes();
|
||||
if ( ! in_array( $postType, $eligiblePostTypes, true ) ) {
|
||||
return [
|
||||
'total' => 0,
|
||||
'withoutFocusKeyphrase' => 0,
|
||||
'needsImprovement' => 0,
|
||||
'okay' => 0,
|
||||
'good' => 0
|
||||
];
|
||||
}
|
||||
|
||||
$specialPageIds = aioseo()->helpers->getSpecialPageIds();
|
||||
$implodedPageIdPlaceholders = array_fill( 0, count( $specialPageIds ), '%d' );
|
||||
$implodedPageIdPlaceholders = implode( ', ', $implodedPageIdPlaceholders );
|
||||
|
||||
global $wpdb;
|
||||
$overviewData = $wpdb->get_row(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
|
||||
$wpdb->prepare(
|
||||
"SELECT
|
||||
COUNT(*) as total,
|
||||
COALESCE( SUM(CASE WHEN ap.keyphrases = '' OR ap.keyphrases IS NULL OR ap.keyphrases LIKE %s THEN 1 ELSE 0 END), 0) as withoutFocusKeyphrase,
|
||||
COALESCE( SUM(CASE WHEN ap.seo_score < 50 AND NOT (ap.keyphrases = '' OR ap.keyphrases IS NULL OR ap.keyphrases LIKE %s) THEN 1 ELSE 0 END), 0) as needsImprovement,
|
||||
COALESCE( SUM(CASE WHEN ap.seo_score BETWEEN 50 AND 79 AND NOT (ap.keyphrases = '' OR ap.keyphrases IS NULL OR ap.keyphrases LIKE %s) THEN 1 ELSE 0 END), 0) as okay,
|
||||
COALESCE( SUM(CASE WHEN ap.seo_score >= 80 AND NOT (ap.keyphrases = '' OR ap.keyphrases IS NULL OR ap.keyphrases LIKE %s) THEN 1 ELSE 0 END), 0) as good
|
||||
FROM {$wpdb->posts} as p
|
||||
LEFT JOIN {$wpdb->prefix}aioseo_posts as ap ON ap.post_id = p.ID
|
||||
WHERE p.post_status = 'publish'
|
||||
AND p.post_type = %s
|
||||
AND p.ID NOT IN ( $implodedPageIdPlaceholders )",
|
||||
// phpcs:enable
|
||||
'{"focus":{"keyphrase":""%',
|
||||
'{"focus":{"keyphrase":""%',
|
||||
'{"focus":{"keyphrase":""%',
|
||||
'{"focus":{"keyphrase":""%',
|
||||
$postType,
|
||||
...array_values( $specialPageIds )
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
// Ensure sure all the values are integers.
|
||||
foreach ( $overviewData as $key => $value ) {
|
||||
$overviewData[ $key ] = (int) $value;
|
||||
}
|
||||
|
||||
// Give me the raw SQL of the query.
|
||||
aioseo()->core->cache->update( $postType . '_overview_data', $overviewData, HOUR_IN_SECONDS );
|
||||
|
||||
return $overviewData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the JOIN and WHERE clause to filter just the posts we need to show depending on the query string.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @param array $clauses Associative array of the clauses for the query.
|
||||
* @param \WP_Query $query The WP_Query instance (passed by reference).
|
||||
* @return array The clauses array updated.
|
||||
*/
|
||||
public function changeClausesToFilterPosts( $clauses, $query = null ) {
|
||||
if ( ! is_admin() || ! $query->is_main_query() ) {
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
$filter = filter_input( INPUT_GET, 'aioseo-filter' );
|
||||
if ( empty( $filter ) ) {
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
$whereClause = '';
|
||||
$noKeyphrasesClause = "(aioseo_p.keyphrases = '' OR aioseo_p.keyphrases IS NULL OR aioseo_p.keyphrases LIKE '{\"focus\":{\"keyphrase\":\"\"%')";
|
||||
switch ( $filter ) {
|
||||
case 'withoutFocusKeyphrase':
|
||||
$whereClause = " AND $noKeyphrasesClause ";
|
||||
break;
|
||||
case 'needsImprovement':
|
||||
$whereClause = " AND aioseo_p.seo_score < 50 AND NOT $noKeyphrasesClause ";
|
||||
break;
|
||||
case 'okay':
|
||||
$whereClause = " AND aioseo_p.seo_score BETWEEN 50 AND 80 AND NOT $noKeyphrasesClause ";
|
||||
break;
|
||||
case 'good':
|
||||
$whereClause = " AND aioseo_p.seo_score > 80 AND NOT $noKeyphrasesClause ";
|
||||
break;
|
||||
}
|
||||
|
||||
$prefix = aioseo()->core->db->prefix;
|
||||
$postsTable = aioseo()->core->db->db->posts;
|
||||
$clauses['join'] .= " LEFT JOIN {$prefix}aioseo_posts AS aioseo_p ON ({$postsTable}.ID = aioseo_p.post_id) ";
|
||||
$clauses['where'] .= $whereClause;
|
||||
|
||||
add_action( 'wp', [ $this, 'filterPostsAfterChangingClauses' ] );
|
||||
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the posts array to remove the ones that are not eligible for page analysis.
|
||||
* Hooked into `wp` action hook.
|
||||
*
|
||||
* @since 4.7.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function filterPostsAfterChangingClauses() {
|
||||
remove_action( 'wp', [ $this, 'filterPostsAfterChangingClauses' ] );
|
||||
// phpcs:disable Squiz.NamingConventions.ValidVariableName
|
||||
global $wp_query;
|
||||
if ( ! empty( $wp_query->posts ) && is_array( $wp_query->posts ) ) {
|
||||
$wp_query->posts = array_filter( $wp_query->posts, function ( $post ) {
|
||||
return aioseo()->helpers->isTruSeoEligible( $post->ID );
|
||||
} );
|
||||
|
||||
// Update `post_count` for pagination.
|
||||
if ( isset( $wp_query->post_count ) ) {
|
||||
$wp_query->post_count = count( $wp_query->posts );
|
||||
}
|
||||
}
|
||||
// phpcs:enable Squiz.NamingConventions.ValidVariableName
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all admin code for the SEO Analysis menu.
|
||||
*
|
||||
* @since 4.2.6
|
||||
*/
|
||||
class SeoAnalysis {
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.2.6
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'save_post', [ $this, 'bustStaticHomepageResults' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Busts the SEO Analysis for the static homepage when it is updated.
|
||||
*
|
||||
* @since 4.2.6
|
||||
*
|
||||
* @param int $postId The post ID.
|
||||
* @return void
|
||||
*/
|
||||
public function bustStaticHomepageResults( $postId ) {
|
||||
if ( ! aioseo()->helpers->isStaticHomePage( $postId ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
aioseo()->internalOptions->internal->siteAnalysis->score = 0;
|
||||
aioseo()->internalOptions->internal->siteAnalysis->results = null;
|
||||
|
||||
aioseo()->core->cache->delete( 'analyze_site_code' );
|
||||
aioseo()->core->cache->delete( 'analyze_site_body' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,543 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP Site Health class.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class SiteHealth {
|
||||
/**
|
||||
* Class Constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'site_status_tests', [ $this, 'registerTests' ], 0 );
|
||||
add_filter( 'debug_information', [ $this, 'addDebugInfo' ], 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add AIOSEO WP Site Health tests.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param array $tests The current filters array.
|
||||
* @return array
|
||||
*/
|
||||
public function registerTests( $tests ) {
|
||||
$tests['direct']['aioseo_site_public'] = [
|
||||
'label' => 'AIOSEO Site Public',
|
||||
'test' => [ $this, 'testCheckSitePublic' ],
|
||||
];
|
||||
$tests['direct']['aioseo_site_info'] = [
|
||||
'label' => 'AIOSEO Site Info',
|
||||
'test' => [ $this, 'testCheckSiteInfo' ],
|
||||
];
|
||||
$tests['direct']['aioseo_google_search_console'] = [
|
||||
'label' => 'AIOSEO Google Search Console',
|
||||
'test' => [ $this, 'testCheckGoogleSearchConsole' ],
|
||||
];
|
||||
$tests['direct']['aioseo_plugin_update'] = [
|
||||
'label' => 'AIOSEO Plugin Update',
|
||||
'test' => [ $this, 'testCheckPluginUpdate' ],
|
||||
];
|
||||
|
||||
$tests['direct']['aioseo_schema_markup'] = [
|
||||
'label' => 'AIOSEO Schema Markup',
|
||||
'test' => [ $this, 'testCheckSchemaMarkup' ],
|
||||
];
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds our site health debug info.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param array $debugInfo The debug info.
|
||||
* @return array $debugInfo The debug info.
|
||||
*/
|
||||
public function addDebugInfo( $debugInfo ) {
|
||||
$fields = [];
|
||||
|
||||
$noindexed = $this->noindexed();
|
||||
if ( $noindexed ) {
|
||||
$fields['noindexed'] = $this->field(
|
||||
__( 'Noindexed content', 'all-in-one-seo-pack' ),
|
||||
implode( ', ', $noindexed )
|
||||
);
|
||||
}
|
||||
|
||||
$nofollowed = $this->nofollowed();
|
||||
if ( $nofollowed ) {
|
||||
$fields['nofollowed'] = $this->field(
|
||||
__( 'Nofollowed content', 'all-in-one-seo-pack' ),
|
||||
implode( ', ', $nofollowed )
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! count( $fields ) ) {
|
||||
return $debugInfo;
|
||||
}
|
||||
|
||||
$debugInfo['aioseo'] = [
|
||||
'label' => __( 'SEO', 'all-in-one-seo-pack' ),
|
||||
'description' => sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
__( 'The fields below contain important SEO information from %1$s that may effect your site.', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_SHORT_NAME
|
||||
),
|
||||
'private' => false,
|
||||
'show_count' => true,
|
||||
'fields' => $fields,
|
||||
];
|
||||
|
||||
return $debugInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the site is public.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return array The test result.
|
||||
*/
|
||||
public function testCheckSitePublic() {
|
||||
$test = 'aioseo_site_public';
|
||||
|
||||
if ( ! get_option( 'blog_public' ) ) {
|
||||
return $this->result(
|
||||
$test,
|
||||
'critical',
|
||||
__( 'Your site does not appear in search results', 'all-in-one-seo-pack' ),
|
||||
__( 'Your site is set to private. This means WordPress asks search engines to exclude your website from search results.', 'all-in-one-seo-pack' ),
|
||||
$this->actionLink( admin_url( 'options-reading.php' ), __( 'Go to Settings > Reading', 'all-in-one-seo-pack' ) )
|
||||
);
|
||||
}
|
||||
|
||||
return $this->result(
|
||||
$test,
|
||||
'good',
|
||||
__( 'Your site appears in search results', 'all-in-one-seo-pack' ),
|
||||
__( 'Your site is set to public. Search engines will index your website and it will appear in search results.', 'all-in-one-seo-pack' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the site title and tagline are set.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return array The test result.
|
||||
*/
|
||||
public function testCheckSiteInfo() {
|
||||
$siteTitle = get_bloginfo( 'name' );
|
||||
$siteTagline = get_bloginfo( 'description' );
|
||||
|
||||
if ( ! $siteTitle || ! $siteTagline ) {
|
||||
return $this->result(
|
||||
'aioseo_site_info',
|
||||
'recommended',
|
||||
__( 'Your Site Title and/or Tagline are blank', 'all-in-one-seo-pack' ),
|
||||
sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
__(
|
||||
'Your Site Title and/or Tagline are blank. We recommend setting both of these values as %1$s requires these for various features, including our schema markup',
|
||||
'all-in-one-seo-pack'
|
||||
),
|
||||
AIOSEO_PLUGIN_SHORT_NAME
|
||||
),
|
||||
$this->actionLink( admin_url( 'options-general.php' ), __( 'Go to Settings > General', 'all-in-one-seo-pack' ) )
|
||||
);
|
||||
}
|
||||
|
||||
return $this->result(
|
||||
'aioseo_site_info',
|
||||
'good',
|
||||
__( 'Your Site Title and Tagline are set', 'all-in-one-seo-pack' ),
|
||||
sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
__( 'Great! These are required for %1$s\'s schema markup and are often used as fallback values for various other features.', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_SHORT_NAME
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether Google Search Console is connected.
|
||||
*
|
||||
* @since 4.6.2
|
||||
*
|
||||
* @return array The test result.
|
||||
*/
|
||||
public function testCheckGoogleSearchConsole() {
|
||||
$googleSearchConsole = aioseo()->searchStatistics->api->auth->isConnected();
|
||||
|
||||
if ( ! $googleSearchConsole ) {
|
||||
return $this->result(
|
||||
'aioseo_google_search_console',
|
||||
'recommended',
|
||||
__( 'Connect Your Site with Google Search Console', 'all-in-one-seo-pack' ),
|
||||
__( 'Sync your site with Google Search Console and get valuable insights right inside your WordPress dashboard. Track keyword rankings and search performance for individual posts with actionable insights to help you rank higher in search results!', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
$this->actionLink( admin_url( 'admin.php?page=aioseo-settings&aioseo-scroll=google-search-console-settings&aioseo-highlight=google-search-console-settings#/webmaster-tools?activetool=googleSearchConsole' ), __( 'Connect to Google Search Console', 'all-in-one-seo-pack' ) ) // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
);
|
||||
}
|
||||
|
||||
return $this->result(
|
||||
'aioseo_google_search_console',
|
||||
'good',
|
||||
__( 'Google Search Console is Connected', 'all-in-one-seo-pack' ),
|
||||
__( 'Awesome! Google Search Console is connected to your site. This will help you monitor and maintain your site\'s presence in Google Search results.', 'all-in-one-seo-pack' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the required settings for our schema markup are set.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return array The test result.
|
||||
*/
|
||||
public function testCheckSchemaMarkup() {
|
||||
$menuPath = admin_url( 'admin.php?page=aioseo-search-appearance' );
|
||||
|
||||
if ( 'organization' === aioseo()->options->searchAppearance->global->schema->siteRepresents ) {
|
||||
if (
|
||||
! aioseo()->options->searchAppearance->global->schema->organizationName ||
|
||||
(
|
||||
! aioseo()->options->searchAppearance->global->schema->organizationLogo &&
|
||||
! aioseo()->helpers->getSiteLogoUrl()
|
||||
)
|
||||
) {
|
||||
return $this->result(
|
||||
'aioseo_schema_markup',
|
||||
'recommended',
|
||||
__( 'Your Organization Name and/or Logo are blank', 'all-in-one-seo-pack' ),
|
||||
sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
__( 'Your Organization Name and/or Logo are blank. These values are required for %1$s\'s Organization schema markup.', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_SHORT_NAME
|
||||
),
|
||||
$this->actionLink( $menuPath, __( 'Go to Schema Settings', 'all-in-one-seo-pack' ) )
|
||||
);
|
||||
}
|
||||
|
||||
return $this->result(
|
||||
'aioseo_schema_markup',
|
||||
'good',
|
||||
__( 'Your Organization Name and Logo are set', 'all-in-one-seo-pack' ),
|
||||
sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
__( 'Awesome! These are required for %1$s\'s Organization schema markup.', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_SHORT_NAME
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
! aioseo()->options->searchAppearance->global->schema->person ||
|
||||
(
|
||||
'manual' === aioseo()->options->searchAppearance->global->schema->person &&
|
||||
(
|
||||
! aioseo()->options->searchAppearance->global->schema->personName ||
|
||||
! aioseo()->options->searchAppearance->global->schema->personLogo
|
||||
)
|
||||
)
|
||||
) {
|
||||
return $this->result(
|
||||
'aioseo_schema_markup',
|
||||
'recommended',
|
||||
__( 'Your Person Name and/or Image are blank', 'all-in-one-seo-pack' ),
|
||||
sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
__( 'Your Person Name and/or Image are blank. These values are required for %1$s\'s Person schema markup.', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_SHORT_NAME
|
||||
),
|
||||
$this->actionLink( $menuPath, __( 'Go to Schema Settings', 'all-in-one-seo-pack' ) )
|
||||
);
|
||||
}
|
||||
|
||||
return $this->result(
|
||||
'aioseo_schema_markup',
|
||||
'good',
|
||||
__( 'Your Person Name and Image are set', 'all-in-one-seo-pack' ),
|
||||
sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
__( 'Awesome! These are required for %1$s\'s Person schema markup.', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_SHORT_NAME
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the plugin should be updated.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @return bool Whether the plugin should be updated.
|
||||
*/
|
||||
public function shouldUpdate() {
|
||||
$response = wp_remote_get( 'https://api.wordpress.org/plugins/info/1.0/all-in-one-seo-pack.json' );
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
if ( ! $body ) {
|
||||
// Something went wrong.
|
||||
return false;
|
||||
}
|
||||
|
||||
$pluginData = json_decode( $body );
|
||||
|
||||
return version_compare( AIOSEO_VERSION, $pluginData->version, '<' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the required settings for our schema markup are set.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return array The test result.
|
||||
*/
|
||||
public function testCheckPluginUpdate() {
|
||||
if ( $this->shouldUpdate() ) {
|
||||
return $this->result(
|
||||
'aioseo_plugin_update',
|
||||
'critical',
|
||||
sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
__( '%1$s needs to be updated', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_SHORT_NAME
|
||||
),
|
||||
sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
__( 'An update is available for %1$s. Upgrade to the latest version to receive all the latest features, bug fixes and security improvements.', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_SHORT_NAME
|
||||
),
|
||||
$this->actionLink( admin_url( 'plugins.php' ), __( 'Go to Plugins', 'all-in-one-seo-pack' ) )
|
||||
);
|
||||
}
|
||||
|
||||
return $this->result(
|
||||
'aioseo_plugin_update',
|
||||
'good',
|
||||
sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
__( '%1$s is updated to the latest version', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_SHORT_NAME
|
||||
),
|
||||
__( 'Fantastic! By updating to the latest version, you have access to all the latest features, bug fixes and security improvements.', 'all-in-one-seo-pack' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of noindexed content.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return array $noindexed A list of noindexed content.
|
||||
*/
|
||||
protected function noindexed() {
|
||||
$globalDefault = aioseo()->options->searchAppearance->advanced->globalRobotsMeta->default;
|
||||
if (
|
||||
! $globalDefault &&
|
||||
aioseo()->options->searchAppearance->advanced->globalRobotsMeta->noindex
|
||||
) {
|
||||
return [
|
||||
__( 'Your entire site is set to globally noindex content.', 'all-in-one-seo-pack' )
|
||||
];
|
||||
}
|
||||
|
||||
$noindexed = [];
|
||||
|
||||
if (
|
||||
! $globalDefault &&
|
||||
aioseo()->options->searchAppearance->advanced->globalRobotsMeta->noindexPaginated
|
||||
) {
|
||||
$noindexed[] = __( 'Paginated Content', 'all-in-one-seo-pack' );
|
||||
}
|
||||
|
||||
$archives = [
|
||||
'author' => __( 'Author Archives', 'all-in-one-seo-pack' ),
|
||||
'date' => __( 'Date Archives', 'all-in-one-seo-pack' ),
|
||||
'search' => __( 'Search Page', 'all-in-one-seo-pack' )
|
||||
];
|
||||
|
||||
// Archives.
|
||||
foreach ( $archives as $name => $type ) {
|
||||
if (
|
||||
! aioseo()->options->searchAppearance->archives->{ $name }->advanced->robotsMeta->default &&
|
||||
aioseo()->options->searchAppearance->archives->{ $name }->advanced->robotsMeta->noindex
|
||||
) {
|
||||
$noindexed[] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( aioseo()->helpers->getPublicPostTypes() as $postType ) {
|
||||
if (
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->has( $postType['name'] ) &&
|
||||
! aioseo()->dynamicOptions->searchAppearance->postTypes->{ $postType['name'] }->advanced->robotsMeta->default &&
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->{ $postType['name'] }->advanced->robotsMeta->noindex
|
||||
) {
|
||||
$noindexed[] = $postType['label'] . ' (' . $postType['name'] . ')';
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( aioseo()->helpers->getPublicTaxonomies() as $taxonomy ) {
|
||||
if (
|
||||
aioseo()->dynamicOptions->searchAppearance->taxonomies->has( $taxonomy['name'] ) &&
|
||||
! aioseo()->dynamicOptions->searchAppearance->taxonomies->{ $taxonomy['name'] }->advanced->robotsMeta->default &&
|
||||
aioseo()->dynamicOptions->searchAppearance->taxonomies->{ $taxonomy['name'] }->advanced->robotsMeta->noindex
|
||||
) {
|
||||
$noindexed[] = $taxonomy['label'] . ' (' . $taxonomy['name'] . ')';
|
||||
}
|
||||
}
|
||||
|
||||
return $noindexed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of nofollowed content.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return array $nofollowed A list of nofollowed content.
|
||||
*/
|
||||
protected function nofollowed() {
|
||||
$globalDefault = aioseo()->options->searchAppearance->advanced->globalRobotsMeta->default;
|
||||
if (
|
||||
! $globalDefault &&
|
||||
aioseo()->options->searchAppearance->advanced->globalRobotsMeta->nofollow
|
||||
) {
|
||||
return [
|
||||
__( 'Your entire site is set to globally nofollow content.', 'all-in-one-seo-pack' )
|
||||
];
|
||||
}
|
||||
|
||||
$nofollowed = [];
|
||||
|
||||
if (
|
||||
! $globalDefault &&
|
||||
aioseo()->options->searchAppearance->advanced->globalRobotsMeta->nofollowPaginated
|
||||
) {
|
||||
$nofollowed[] = __( 'Paginated Content', 'all-in-one-seo-pack' );
|
||||
}
|
||||
|
||||
$archives = [
|
||||
'author' => __( 'Author Archives', 'all-in-one-seo-pack' ),
|
||||
'date' => __( 'Date Archives', 'all-in-one-seo-pack' ),
|
||||
'search' => __( 'Search Page', 'all-in-one-seo-pack' )
|
||||
];
|
||||
|
||||
// Archives.
|
||||
foreach ( $archives as $name => $type ) {
|
||||
if (
|
||||
! aioseo()->options->searchAppearance->archives->{ $name }->advanced->robotsMeta->default &&
|
||||
aioseo()->options->searchAppearance->archives->{ $name }->advanced->robotsMeta->nofollow
|
||||
) {
|
||||
$nofollowed[] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( aioseo()->helpers->getPublicPostTypes() as $postType ) {
|
||||
if (
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->has( $postType['name'] ) &&
|
||||
! aioseo()->dynamicOptions->searchAppearance->postTypes->{ $postType['name'] }->advanced->robotsMeta->default &&
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->{ $postType['name'] }->advanced->robotsMeta->nofollow
|
||||
) {
|
||||
$nofollowed[] = $postType['label'] . ' (' . $postType['name'] . ')';
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( aioseo()->helpers->getPublicTaxonomies() as $taxonomy ) {
|
||||
if (
|
||||
aioseo()->dynamicOptions->searchAppearance->taxonomies->has( $taxonomy['name'] ) &&
|
||||
! aioseo()->dynamicOptions->searchAppearance->taxonomies->{ $taxonomy['name'] }->advanced->robotsMeta->default &&
|
||||
aioseo()->dynamicOptions->searchAppearance->taxonomies->{ $taxonomy['name'] }->advanced->robotsMeta->nofollow
|
||||
) {
|
||||
$nofollowed[] = $taxonomy['label'] . ' (' . $taxonomy['name'] . ')';
|
||||
}
|
||||
}
|
||||
|
||||
return $nofollowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a debug info data field.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param string $label The field label.
|
||||
* @param string $value The field value.
|
||||
* @param boolean $private Whether the field shouldn't be included if the debug info is copied.
|
||||
* @return array The debug info data field.
|
||||
*/
|
||||
private function field( $label, $value, $private = false ) {
|
||||
return [
|
||||
'label' => $label,
|
||||
'value' => $value,
|
||||
'private' => $private,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the test result.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param string $name The test name.
|
||||
* @param string $status The result status.
|
||||
* @param string $header The test header.
|
||||
* @param string $description The result description.
|
||||
* @param string $actions The result actions.
|
||||
* @return array The test result.
|
||||
*/
|
||||
protected function result( $name, $status, $header, $description, $actions = '' ) {
|
||||
$color = 'blue';
|
||||
switch ( $status ) {
|
||||
case 'good':
|
||||
break;
|
||||
case 'recommended':
|
||||
$color = 'orange';
|
||||
break;
|
||||
case 'critical':
|
||||
$color = 'red';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return [
|
||||
'test' => $name,
|
||||
'status' => $status,
|
||||
'label' => $header,
|
||||
'description' => $description,
|
||||
'actions' => $actions,
|
||||
'badge' => [
|
||||
'label' => AIOSEO_PLUGIN_SHORT_NAME,
|
||||
'color' => $color,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an action link.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param string $path The path.
|
||||
* @param string $anchor The anchor text.
|
||||
* @return string The action link.
|
||||
*/
|
||||
protected function actionLink( $path, $anchor ) {
|
||||
return sprintf(
|
||||
'<p><a href="%1$s">%2$s</a></p>',
|
||||
$path,
|
||||
$anchor
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitors changes to post slugs.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*/
|
||||
class SlugMonitor {
|
||||
/**
|
||||
* Holds posts that have been updated.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $updatedPosts = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*/
|
||||
public function __construct() {
|
||||
// We can't monitor changes without permalinks enabled.
|
||||
if ( ! get_option( 'permalink_structure' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'pre_post_update', [ $this, 'prePostUpdate' ] );
|
||||
|
||||
// WP 5.6+.
|
||||
if ( function_exists( 'wp_after_insert_post' ) ) {
|
||||
add_action( 'wp_after_insert_post', [ $this, 'afterInsertPost' ], 11, 4 );
|
||||
} else {
|
||||
add_action( 'post_updated', [ $this, 'postUpdated' ], 11, 3 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remember the previous post permalink.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @param integer $postId The post ID.
|
||||
* @return void
|
||||
*/
|
||||
public function prePostUpdate( $postId ) {
|
||||
$this->updatedPosts[ $postId ] = get_permalink( $postId );
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a post has been completely inserted ( with categories and meta ).
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @param integer $postId The post ID.
|
||||
* @param \WP_Post $post The post object.
|
||||
* @param bool $update Whether this is an existing post being updated.
|
||||
* @param null|\WP_Post $postBefore The post object before changes were made.
|
||||
* @return void
|
||||
*/
|
||||
public function afterInsertPost( $postId, $post = null, $update = false, $postBefore = null ) {
|
||||
if ( ! $update ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->postUpdated( $postId, $post, $postBefore );
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a post has been updated - check if the slug has changed.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @param integer $postId The post ID.
|
||||
* @param \WP_Post $post The post object.
|
||||
* @param \WP_Post $postBefore The post object before changes were made.
|
||||
* @return void
|
||||
*/
|
||||
public function postUpdated( $postId, $post = null, $postBefore = null ) {
|
||||
if ( ! isset( $this->updatedPosts[ $postId ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$before = aioseo()->helpers->getPermalinkPath( $this->updatedPosts[ $postId ] );
|
||||
$after = aioseo()->helpers->getPermalinkPath( get_permalink( $postId ) );
|
||||
if ( ! aioseo()->helpers->hasPermalinkChanged( $before, $after ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Can we monitor this slug?
|
||||
if ( ! $this->canMonitorPost( $post, $postBefore ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ask aioseo-redirects if automatic redirects is monitoring it.
|
||||
if ( $this->automaticRedirect( $post->post_type, $before, $after ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter to allow users to disable the slug monitor messages.
|
||||
if ( apply_filters( 'aioseo_redirects_disable_slug_monitor', false ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$redirectUrl = $this->manualRedirectUrl( [
|
||||
'url' => $before,
|
||||
'target' => $after,
|
||||
'type' => 301
|
||||
] );
|
||||
|
||||
$message = __( 'The permalink for this post just changed! This could result in 404 errors for your site visitors.', 'all-in-one-seo-pack' );
|
||||
|
||||
// Default notice redirecting to the Redirects screen.
|
||||
$action = [
|
||||
'url' => $redirectUrl,
|
||||
'label' => __( 'Add Redirect to improve SEO', 'all-in-one-seo-pack' ),
|
||||
'target' => '_blank',
|
||||
'class' => 'aioseo-redirects-slug-changed'
|
||||
];
|
||||
|
||||
// If redirects is active we'll show add-redirect in a modal.
|
||||
if ( aioseo()->addons->getLoadedAddon( 'redirects' ) ) {
|
||||
// We need to remove the target here so the action keeps the url used by the add-redirect modal.
|
||||
unset( $action['target'] );
|
||||
}
|
||||
|
||||
aioseo()->wpNotices->addNotice( $message, 'warning', [ 'actions' => [ $action ] ], [ 'posts' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this is a post we can monitor.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @param \WP_Post $post The post object.
|
||||
* @param \WP_Post $postBefore The post object before changes were made.
|
||||
* @return boolean True if we can monitor this post.
|
||||
*/
|
||||
private function canMonitorPost( $post, $postBefore ) {
|
||||
// Check that this is for the expected post.
|
||||
if ( ! isset( $post->ID ) || ! isset( $this->updatedPosts[ $post->ID ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't do anything if we're not published.
|
||||
if ( 'publish' !== $post->post_status || 'publish' !== $postBefore->post_status ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't do anything is the post type is not public.
|
||||
if ( ! is_post_type_viewable( $post->post_type ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to add a automatic redirect.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @param string $postType The post type.
|
||||
* @param string $before The url before.
|
||||
* @param string $after The url after.
|
||||
* @return bool True if an automatic redirect was added.
|
||||
*/
|
||||
private function automaticRedirect( $postType, $before, $after ) {
|
||||
if ( ! aioseo()->addons->getLoadedAddon( 'redirects' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return aioseoRedirects()->monitor->automaticRedirect( $postType, $before, $after );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a URL for adding manual redirects.
|
||||
*
|
||||
* @since 4.2.3
|
||||
*
|
||||
* @param array $urls An array of [url, target, type, slash, case, regex].
|
||||
* @return string The redirect link.
|
||||
*/
|
||||
public function manualRedirectUrl( $urls ) {
|
||||
if ( ! aioseo()->addons->getLoadedAddon( 'redirects' ) ) {
|
||||
return admin_url( 'admin.php?page=aioseo-redirects' );
|
||||
}
|
||||
|
||||
return aioseoRedirects()->helpers->manualRedirectUrl( $urls );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage tracking class.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
abstract class Usage {
|
||||
/**
|
||||
* Returns the current plugin version type ("lite" or "pro").
|
||||
*
|
||||
* @since 4.1.3
|
||||
*
|
||||
* @return string The version type.
|
||||
*/
|
||||
abstract public function getType();
|
||||
|
||||
/**
|
||||
* Source of notifications content.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $url = 'https://aiousage.com/v1/track';
|
||||
|
||||
/**
|
||||
* Whether or not usage tracking is enabled.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $enabled = false;
|
||||
|
||||
/**
|
||||
* Class Constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'init', [ $this, 'init' ], 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs on the init action.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
try {
|
||||
$action = 'aioseo_send_usage_data';
|
||||
if ( ! $this->enabled ) {
|
||||
aioseo()->actionScheduler->unschedule( $action );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Register the action handler.
|
||||
add_action( $action, [ $this, 'process' ] );
|
||||
|
||||
if ( ! as_next_scheduled_action( $action ) ) {
|
||||
as_schedule_recurring_action( $this->generateStartDate(), WEEK_IN_SECONDS, $action, [], 'aioseo' );
|
||||
|
||||
// Run the task immediately using an async action.
|
||||
as_enqueue_async_action( $action, [], 'aioseo' );
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the usage tracking.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
if ( ! $this->enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_remote_post(
|
||||
$this->getUrl(),
|
||||
[
|
||||
'timeout' => 10,
|
||||
'headers' => array_merge( [
|
||||
'Content-Type' => 'application/json; charset=utf-8'
|
||||
], aioseo()->helpers->getApiHeaders() ),
|
||||
'user-agent' => aioseo()->helpers->getApiUserAgent(),
|
||||
'body' => wp_json_encode( $this->getData() )
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL for the notifications api.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return string The URL to use for the api requests.
|
||||
*/
|
||||
private function getUrl() {
|
||||
if ( defined( 'AIOSEO_USAGE_TRACKING_URL' ) ) {
|
||||
return AIOSEO_USAGE_TRACKING_URL;
|
||||
}
|
||||
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the data to send in the usage tracking.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return array An array of data to send.
|
||||
*/
|
||||
protected function getData() {
|
||||
$themeData = wp_get_theme();
|
||||
$type = $this->getType();
|
||||
|
||||
return [
|
||||
// Generic data (environment).
|
||||
'url' => home_url(),
|
||||
'php_version' => PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION,
|
||||
'wp_version' => get_bloginfo( 'version' ),
|
||||
'mysql_version' => aioseo()->core->db->db->db_version(),
|
||||
'server_version' => isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '',
|
||||
'is_ssl' => is_ssl(),
|
||||
'is_multisite' => is_multisite(),
|
||||
'sites_count' => function_exists( 'get_blog_count' ) ? (int) get_blog_count() : 1,
|
||||
'active_plugins' => $this->getActivePlugins(),
|
||||
'theme_name' => $themeData->name,
|
||||
'theme_version' => $themeData->version,
|
||||
'user_count' => function_exists( 'get_user_count' ) ? get_user_count() : null,
|
||||
'locale' => get_locale(),
|
||||
'timezone_offset' => wp_timezone_string(),
|
||||
'email' => get_bloginfo( 'admin_email' ),
|
||||
// AIOSEO specific data.
|
||||
'aioseo_version' => AIOSEO_VERSION,
|
||||
'aioseo_license_key' => null,
|
||||
'aioseo_license_type' => null,
|
||||
'aioseo_is_pro' => false,
|
||||
"aioseo_{$type}_installed_date" => aioseo()->internalOptions->internal->installed,
|
||||
'aioseo_settings' => $this->getSettings()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the settings and escape the quotes so it can be JSON encoded.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return array An array of settings data.
|
||||
*/
|
||||
private function getSettings() {
|
||||
$settings = aioseo()->options->all();
|
||||
array_walk_recursive( $settings, function( &$v ) {
|
||||
if ( is_string( $v ) && strpos( $v, '"' ) !== false ) {
|
||||
$v = str_replace( '"', '\"', $v );
|
||||
}
|
||||
});
|
||||
|
||||
$settings = $this->filterPrivateSettings( $settings );
|
||||
|
||||
$internal = aioseo()->internalOptions->all();
|
||||
array_walk_recursive( $internal, function( &$v ) {
|
||||
if ( is_string( $v ) && strpos( $v, '"' ) !== false ) {
|
||||
$v = str_replace( '"', '\"', $v );
|
||||
}
|
||||
});
|
||||
|
||||
return [
|
||||
'options' => $settings,
|
||||
'internal' => $internal
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of active plugins.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return array An array of active plugin data.
|
||||
*/
|
||||
private function getActivePlugins() {
|
||||
if ( ! function_exists( 'get_plugins' ) ) {
|
||||
include ABSPATH . '/wp-admin/includes/plugin.php';
|
||||
}
|
||||
$active = get_option( 'active_plugins', [] );
|
||||
$plugins = array_intersect_key( get_plugins(), array_flip( $active ) );
|
||||
|
||||
return array_map(
|
||||
static function ( $plugin ) {
|
||||
if ( isset( $plugin['Version'] ) ) {
|
||||
return $plugin['Version'];
|
||||
}
|
||||
|
||||
return 'Not Set';
|
||||
},
|
||||
$plugins
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random start date for usage tracking.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return integer The randomized start date.
|
||||
*/
|
||||
private function generateStartDate() {
|
||||
$tracking = [
|
||||
'days' => wp_rand( 0, 6 ) * DAY_IN_SECONDS,
|
||||
'hours' => wp_rand( 0, 23 ) * HOUR_IN_SECONDS,
|
||||
'minutes' => wp_rand( 0, 23 ) * HOUR_IN_SECONDS,
|
||||
'seconds' => wp_rand( 0, 59 )
|
||||
];
|
||||
|
||||
return strtotime( 'next sunday' ) + array_sum( $tracking );
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonimizes or obfuscates the value of certain settings.
|
||||
*
|
||||
* @since 4.3.2
|
||||
*
|
||||
* @param array $settings The settings.
|
||||
* @return array The altered settings.
|
||||
*/
|
||||
private function filterPrivateSettings( $settings ) {
|
||||
if ( ! empty( $settings['advanced']['openAiKey'] ) ) {
|
||||
$settings['advanced']['openAiKey'] = true;
|
||||
}
|
||||
|
||||
if ( ! empty( $settings['localBusiness']['maps']['apiKey'] ) ) {
|
||||
$settings['localBusiness']['maps']['apiKey'] = true;
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Admin;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* The Admin class.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*/
|
||||
class WritingAssistant {
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'add_meta_boxes', [ $this, 'addMetabox' ] );
|
||||
add_action( 'delete_post', [ $this, 'deletePost' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the writing assistant post.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*
|
||||
* @param int $postId The post id.
|
||||
* @return void
|
||||
*/
|
||||
public function deletePost( $postId ) {
|
||||
Models\WritingAssistantPost::getPost( $postId )->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a meta box to the page/posts screens.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addMetabox() {
|
||||
if ( ! aioseo()->access->hasCapability( 'aioseo_page_writing_assistant_settings' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$postType = get_post_type();
|
||||
if (
|
||||
(
|
||||
! aioseo()->options->writingAssistant->postTypes->all &&
|
||||
! in_array( $postType, aioseo()->options->writingAssistant->postTypes->included, true )
|
||||
) ||
|
||||
! in_array( $postType, aioseo()->helpers->getPublicPostTypes( true ), true )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip post types that do not support an editor.
|
||||
if ( ! post_type_supports( $postType, 'editor' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore certain plugins.
|
||||
if (
|
||||
aioseo()->thirdParty->webStories->isPluginActive() &&
|
||||
'web-story' === $postType
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueueAssets' ] );
|
||||
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
$aioseoMetaboxTitle = sprintf( esc_html__( '%1$s Writing Assistant', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME );
|
||||
|
||||
add_meta_box(
|
||||
'aioseo-writing-assistant-metabox',
|
||||
$aioseoMetaboxTitle,
|
||||
[ $this, 'renderMetabox' ],
|
||||
null,
|
||||
'normal',
|
||||
'low'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the on-page settings metabox with the Vue App wrapper.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function renderMetabox() {
|
||||
?>
|
||||
<div id="aioseo-writing-assistant-metabox-app">
|
||||
<?php aioseo()->templates->getTemplate( 'parts/loader.php' ); ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the JS/CSS for the standalone.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueueAssets() {
|
||||
if ( ! aioseo()->helpers->isScreenBase( 'post' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
aioseo()->core->assets->load(
|
||||
'src/vue/standalone/writing-assistant/main.js',
|
||||
[],
|
||||
aioseo()->writingAssistant->helpers->getStandaloneVueData(),
|
||||
'aioseoWritingAssistant'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Analyze {
|
||||
/**
|
||||
* Analyzes the site for SEO.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function analyzeSite( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$analyzeUrl = ! empty( $body['url'] ) ? esc_url_raw( urldecode( $body['url'] ) ) : null;
|
||||
$refreshResults = ! empty( $body['refresh'] ) ? (bool) $body['refresh'] : false;
|
||||
$analyzeOrHomeUrl = ! empty( $analyzeUrl ) ? $analyzeUrl : home_url();
|
||||
$responseCode = null === aioseo()->core->cache->get( 'analyze_site_code' ) ? [] : aioseo()->core->cache->get( 'analyze_site_code' );
|
||||
$responseBody = null === aioseo()->core->cache->get( 'analyze_site_body' ) ? [] : aioseo()->core->cache->get( 'analyze_site_body' );
|
||||
if (
|
||||
empty( $responseCode ) ||
|
||||
empty( $responseCode[ $analyzeOrHomeUrl ] ) ||
|
||||
empty( $responseBody ) ||
|
||||
empty( $responseBody[ $analyzeOrHomeUrl ] ) ||
|
||||
$refreshResults
|
||||
) {
|
||||
$token = aioseo()->internalOptions->internal->siteAnalysis->connectToken;
|
||||
$url = defined( 'AIOSEO_ANALYZE_URL' ) ? AIOSEO_ANALYZE_URL : 'https://analyze.aioseo.com';
|
||||
$response = aioseo()->helpers->wpRemotePost( $url . '/v3/analyze/', [
|
||||
'timeout' => 60,
|
||||
'headers' => [
|
||||
'X-AIOSEO-Key' => $token,
|
||||
'Content-Type' => 'application/json'
|
||||
],
|
||||
'body' => wp_json_encode( [
|
||||
'url' => $analyzeOrHomeUrl
|
||||
] ),
|
||||
] );
|
||||
|
||||
$responseCode[ $analyzeOrHomeUrl ] = wp_remote_retrieve_response_code( $response );
|
||||
$responseBody[ $analyzeOrHomeUrl ] = json_decode( wp_remote_retrieve_body( $response ), true );
|
||||
|
||||
aioseo()->core->cache->update( 'analyze_site_code', $responseCode, 10 * MINUTE_IN_SECONDS );
|
||||
aioseo()->core->cache->update( 'analyze_site_body', $responseBody, 10 * MINUTE_IN_SECONDS );
|
||||
}
|
||||
|
||||
if ( 200 !== $responseCode[ $analyzeOrHomeUrl ] || empty( $responseBody[ $analyzeOrHomeUrl ]['success'] ) || ! empty( $responseBody[ $analyzeOrHomeUrl ]['error'] ) ) {
|
||||
if ( ! empty( $responseBody[ $analyzeOrHomeUrl ]['error'] ) && 'invalid-token' === $responseBody[ $analyzeOrHomeUrl ]['error'] ) {
|
||||
aioseo()->internalOptions->internal->siteAnalysis->reset();
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'response' => $responseBody[ $analyzeOrHomeUrl ]
|
||||
], 400 );
|
||||
}
|
||||
|
||||
if ( $analyzeUrl ) {
|
||||
$competitors = aioseo()->internalOptions->internal->siteAnalysis->competitors;
|
||||
$competitors = array_reverse( $competitors, true );
|
||||
|
||||
$competitors[ $analyzeUrl ] = wp_json_encode( $responseBody[ $analyzeOrHomeUrl ] );
|
||||
|
||||
$competitors = array_reverse( $competitors, true );
|
||||
|
||||
// Reset the competitors.
|
||||
aioseo()->internalOptions->internal->siteAnalysis->competitors = $competitors;
|
||||
|
||||
return new \WP_REST_Response( $competitors, 200 );
|
||||
}
|
||||
|
||||
$results = $responseBody[ $analyzeOrHomeUrl ]['results'];
|
||||
|
||||
aioseo()->internalOptions->internal->siteAnalysis->results = wp_json_encode( $results );
|
||||
aioseo()->internalOptions->internal->siteAnalysis->score = $responseBody[ $analyzeOrHomeUrl ]['score'];
|
||||
|
||||
return new \WP_REST_Response( $responseBody[ $analyzeOrHomeUrl ], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the analyzed site for SEO.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function deleteSite( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$analyzeUrl = ! empty( $body['url'] ) ? esc_url_raw( urldecode( $body['url'] ) ) : null;
|
||||
|
||||
$competitors = aioseo()->internalOptions->internal->siteAnalysis->competitors;
|
||||
|
||||
unset( $competitors[ $analyzeUrl ] );
|
||||
|
||||
// Reset the competitors.
|
||||
aioseo()->internalOptions->internal->siteAnalysis->competitors = $competitors;
|
||||
|
||||
return new \WP_REST_Response( $competitors, 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes the title for SEO.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request.
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function analyzeHeadline( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$headline = ! empty( $body['headline'] ) ? sanitize_text_field( $body['headline'] ) : '';
|
||||
$shouldStoreHeadline = ! empty( $body['shouldStoreHeadline'] ) ? rest_sanitize_boolean( $body['shouldStoreHeadline'] ) : false;
|
||||
|
||||
if ( empty( $headline ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => __( 'Please enter a valid headline.', 'all-in-one-seo-pack' )
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$result = aioseo()->standalone->headlineAnalyzer->getResult( $headline );
|
||||
|
||||
if ( ! $result['analysed'] ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $result['result']->msg
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$headlines = aioseo()->internalOptions->internal->headlineAnalysis->headlines;
|
||||
$headlines = array_reverse( $headlines, true );
|
||||
|
||||
// Remove a headline from the list if it already exists.
|
||||
// This will ensure the new analysis is the first and open/highlighted.
|
||||
if ( array_key_exists( $headline, $headlines ) ) {
|
||||
unset( $headlines[ $headline ] );
|
||||
}
|
||||
|
||||
$headlines[ $headline ] = wp_json_encode( $result );
|
||||
|
||||
$headlines = array_reverse( $headlines, true );
|
||||
|
||||
// Store the headlines with the latest one.
|
||||
if ( $shouldStoreHeadline ) {
|
||||
aioseo()->internalOptions->internal->headlineAnalysis->headlines = $headlines;
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( $headlines, 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the analyzed Headline for SEO.
|
||||
*
|
||||
* @since 4.1.6
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request.
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function deleteHeadline( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$headline = sanitize_text_field( $body['headline'] );
|
||||
|
||||
$headlines = aioseo()->internalOptions->internal->headlineAnalysis->headlines;
|
||||
|
||||
unset( $headlines[ $headline ] );
|
||||
|
||||
// Reset the headlines.
|
||||
aioseo()->internalOptions->internal->headlineAnalysis->headlines = $headlines;
|
||||
|
||||
return new \WP_REST_Response( $headlines, 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Api class for the admin.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Api {
|
||||
/**
|
||||
* The REST API Namespace
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $namespace = 'aioseo/v1';
|
||||
|
||||
/**
|
||||
* The routes we use in the rest API.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $routes = [
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
'GET' => [
|
||||
'options' => [ 'callback' => [ 'Settings', 'getOptions' ], 'access' => 'everyone' ],
|
||||
'ping' => [ 'callback' => [ 'Ping', 'ping' ], 'access' => 'everyone' ],
|
||||
'post' => [ 'callback' => [ 'PostsTerms', 'getPostData' ], 'access' => 'everyone' ],
|
||||
'post/(?P<postId>[\d]+)/first-attached-image' => [ 'callback' => [ 'PostsTerms', 'getFirstAttachedImage' ], 'access' => 'aioseo_page_social_settings' ],
|
||||
'user/(?P<userId>[\d]+)/image' => [ 'callback' => [ 'User', 'getUserImage' ], 'access' => 'aioseo_page_social_settings' ],
|
||||
'tags' => [ 'callback' => [ 'Tags', 'getTags' ], 'access' => 'everyone' ],
|
||||
'search-statistics/url/auth' => [ 'callback' => [ 'SearchStatistics', 'getAuthUrl' ], 'access' => [ 'aioseo_search_statistics_settings', 'aioseo_general_settings', 'aioseo_setup_wizard' ] ], // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
'search-statistics/url/reauth' => [ 'callback' => [ 'SearchStatistics', 'getReauthUrl' ], 'access' => [ 'aioseo_search_statistics_settings', 'aioseo_general_settings' ] ],
|
||||
'writing-assistant/keyword/(?P<postId>[\d]+)' => [ 'callback' => [ 'WritingAssistant', 'getPostKeyword' ], 'access' => 'aioseo_page_writing_assistant_settings' ],
|
||||
'writing-assistant/user-info' => [ 'callback' => [ 'WritingAssistant', 'getUserInfo' ], 'access' => 'aioseo_page_writing_assistant_settings' ],
|
||||
'writing-assistant/user-options' => [ 'callback' => [ 'WritingAssistant', 'getUserOptions' ], 'access' => 'aioseo_page_writing_assistant_settings' ],
|
||||
'writing-assistant/report-history' => [ 'callback' => [ 'WritingAssistant', 'getReportHistory' ], 'access' => 'aioseo_page_writing_assistant_settings' ]
|
||||
],
|
||||
'POST' => [
|
||||
'htaccess' => [ 'callback' => [ 'Tools', 'saveHtaccess' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'post' => [
|
||||
'callback' => [ 'PostsTerms', 'updatePosts' ],
|
||||
'access' => [
|
||||
'aioseo_page_analysis',
|
||||
'aioseo_page_general_settings',
|
||||
'aioseo_page_advanced_settings',
|
||||
'aioseo_page_schema_settings',
|
||||
'aioseo_page_social_settings'
|
||||
]
|
||||
],
|
||||
'post/(?P<postId>[\d]+)/disable-primary-term-education' => [ 'callback' => [ 'PostsTerms', 'disablePrimaryTermEducation' ], 'access' => 'aioseo_page_general_settings' ],
|
||||
'post/(?P<postId>[\d]+)/disable-link-format-education' => [ 'callback' => [ 'PostsTerms', 'disableLinkFormatEducation' ], 'access' => 'aioseo_page_general_settings' ],
|
||||
'post/(?P<postId>[\d]+)/update-internal-link-count' => [ 'callback' => [ 'PostsTerms', 'updateInternalLinkCount' ], 'access' => 'aioseo_page_general_settings' ],
|
||||
'post/(?P<postId>[\d]+)/process-content' => [ 'callback' => [ 'PostsTerms', 'processContent' ], 'access' => 'aioseo_page_general_settings' ],
|
||||
'posts-list/load-details-column' => [ 'callback' => [ 'PostsTerms', 'loadPostDetailsColumn' ], 'access' => 'aioseo_page_general_settings' ],
|
||||
'posts-list/update-details-column' => [ 'callback' => [ 'PostsTerms', 'updatePostDetailsColumn' ], 'access' => 'aioseo_page_general_settings' ],
|
||||
'terms-list/load-details-column' => [ 'callback' => [ 'PostsTerms', 'loadTermDetailsColumn' ], 'access' => 'aioseo_page_general_settings' ],
|
||||
'terms-list/update-details-column' => [ 'callback' => [ 'PostsTerms', 'updateTermDetailsColumn' ], 'access' => 'aioseo_page_general_settings' ],
|
||||
'keyphrases' => [ 'callback' => [ 'PostsTerms', 'updatePostKeyphrases' ], 'access' => 'aioseo_page_analysis' ],
|
||||
'analyze' => [ 'callback' => [ 'Analyze', 'analyzeSite' ], 'access' => 'aioseo_seo_analysis_settings' ],
|
||||
'analyze-headline' => [ 'callback' => [ 'Analyze', 'analyzeHeadline' ], 'access' => 'everyone' ],
|
||||
'analyze-headline/delete' => [ 'callback' => [ 'Analyze', 'deleteHeadline' ], 'access' => 'aioseo_seo_analysis_settings' ],
|
||||
'analyze/delete-site' => [ 'callback' => [ 'Analyze', 'deleteSite' ], 'access' => 'aioseo_seo_analysis_settings' ],
|
||||
'clear-log' => [ 'callback' => [ 'Tools', 'clearLog' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'connect' => [ 'callback' => [ 'Connect', 'saveConnectToken' ], 'access' => [ 'aioseo_general_settings', 'aioseo_setup_wizard' ] ],
|
||||
'connect-pro' => [ 'callback' => [ 'Connect', 'processConnect' ], 'access' => [ 'aioseo_general_settings', 'aioseo_setup_wizard' ] ],
|
||||
'connect-url' => [ 'callback' => [ 'Connect', 'getConnectUrl' ], 'access' => [ 'aioseo_general_settings', 'aioseo_setup_wizard' ] ],
|
||||
'backup' => [ 'callback' => [ 'Tools', 'createBackup' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'backup/restore' => [ 'callback' => [ 'Tools', 'restoreBackup' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'email-debug-info' => [ 'callback' => [ 'Tools', 'emailDebugInfo' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'migration/fix-blank-formats' => [ 'callback' => [ 'Migration', 'fixBlankFormats' ], 'access' => 'any' ],
|
||||
'notification/blog-visibility-reminder' => [ 'callback' => [ 'Notifications', 'blogVisibilityReminder' ], 'access' => 'any' ],
|
||||
'notification/conflicting-plugins-reminder' => [ 'callback' => [ 'Notifications', 'conflictingPluginsReminder' ], 'access' => 'any' ],
|
||||
'notification/description-format-reminder' => [ 'callback' => [ 'Notifications', 'descriptionFormatReminder' ], 'access' => 'any' ],
|
||||
'notification/email-reports-enable' => [ 'callback' => [ 'EmailSummary', 'enableEmailReports' ], 'access' => 'any' ],
|
||||
'notification/install-addons-reminder' => [ 'callback' => [ 'Notifications', 'installAddonsReminder' ], 'access' => 'any' ],
|
||||
'notification/install-aioseo-image-seo-reminder' => [ 'callback' => [ 'Notifications', 'installImageSeoReminder' ], 'access' => 'any' ],
|
||||
'notification/install-aioseo-local-business-reminder' => [ 'callback' => [ 'Notifications', 'installLocalBusinessReminder' ], 'access' => 'any' ],
|
||||
'notification/install-aioseo-news-sitemap-reminder' => [ 'callback' => [ 'Notifications', 'installNewsSitemapReminder' ], 'access' => 'any' ],
|
||||
'notification/install-aioseo-video-sitemap-reminder' => [ 'callback' => [ 'Notifications', 'installVideoSitemapReminder' ], 'access' => 'any' ],
|
||||
'notification/install-mi-reminder' => [ 'callback' => [ 'Notifications', 'installMiReminder' ], 'access' => 'any' ],
|
||||
'notification/install-om-reminder' => [ 'callback' => [ 'Notifications', 'installOmReminder' ], 'access' => 'any' ],
|
||||
'notification/v3-migration-custom-field-reminder' => [ 'callback' => [ 'Notifications', 'migrationCustomFieldReminder' ], 'access' => 'any' ],
|
||||
'notification/v3-migration-schema-number-reminder' => [ 'callback' => [ 'Notifications', 'migrationSchemaNumberReminder' ], 'access' => 'any' ],
|
||||
'notifications/dismiss' => [ 'callback' => [ 'Notifications', 'dismissNotifications' ], 'access' => 'any' ],
|
||||
'objects' => [ 'callback' => [ 'PostsTerms', 'searchForObjects' ], 'access' => [ 'aioseo_search_appearance_settings', 'aioseo_sitemap_settings' ] ], // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
'options' => [ 'callback' => [ 'Settings', 'saveChanges' ], 'access' => 'any' ],
|
||||
'plugins/deactivate' => [ 'callback' => [ 'Plugins', 'deactivatePlugins' ], 'access' => 'aioseo_feature_manager_settings' ],
|
||||
'plugins/install' => [ 'callback' => [ 'Plugins', 'installPlugins' ], 'access' => [ 'install_plugins', 'aioseo_feature_manager_settings' ] ],
|
||||
'plugins/upgrade' => [ 'callback' => [ 'Plugins', 'upgradePlugins' ], 'access' => [ 'update_plugins', 'aioseo_feature_manager_settings' ] ],
|
||||
'reset-settings' => [ 'callback' => [ 'Settings', 'resetSettings' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'search-statistics/sitemap/delete' => [ 'callback' => [ 'SearchStatistics', 'deleteSitemap' ], 'access' => [ 'aioseo_search_statistics_settings', 'aioseo_general_settings' ] ], // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
'search-statistics/sitemap/ignore' => [ 'callback' => [ 'SearchStatistics', 'ignoreSitemap' ], 'access' => [ 'aioseo_search_statistics_settings', 'aioseo_general_settings' ] ], // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
'settings/export' => [ 'callback' => [ 'Settings', 'exportSettings' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'settings/export-content' => [ 'callback' => [ 'Settings', 'exportContent' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'settings/hide-setup-wizard' => [ 'callback' => [ 'Settings', 'hideSetupWizard' ], 'access' => 'any' ],
|
||||
'settings/hide-upgrade-bar' => [ 'callback' => [ 'Settings', 'hideUpgradeBar' ], 'access' => 'any' ],
|
||||
'settings/import' => [ 'callback' => [ 'Settings', 'importSettings' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'settings/import/(?P<siteId>[\d]+)' => [ 'callback' => [ 'Settings', 'importSettings' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'settings/import-plugins' => [ 'callback' => [ 'Settings', 'importPlugins' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'settings/toggle-card' => [ 'callback' => [ 'Settings', 'toggleCard' ], 'access' => 'any' ],
|
||||
'settings/toggle-radio' => [ 'callback' => [ 'Settings', 'toggleRadio' ], 'access' => 'any' ],
|
||||
'settings/dismiss-alert' => [ 'callback' => [ 'Settings', 'dismissAlert' ], 'access' => 'any' ],
|
||||
'settings/items-per-page' => [ 'callback' => [ 'Settings', 'changeItemsPerPage' ], 'access' => 'any' ],
|
||||
'settings/semrush-country' => [ 'callback' => [ 'Settings', 'changeSemrushCountry' ], 'access' => 'any' ],
|
||||
'settings/do-task' => [ 'callback' => [ 'Settings', 'doTask' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'sitemap/deactivate-conflicting-plugins' => [ 'callback' => [ 'Sitemaps', 'deactivateConflictingPlugins' ], 'access' => 'any' ],
|
||||
'sitemap/delete-static-files' => [ 'callback' => [ 'Sitemaps', 'deleteStaticFiles' ], 'access' => 'aioseo_sitemap_settings' ],
|
||||
'sitemap/validate-html-sitemap-slug' => [ 'callback' => [ 'Sitemaps', 'validateHtmlSitemapSlug' ], 'access' => 'aioseo_sitemap_settings' ],
|
||||
'tools/delete-robots-txt' => [ 'callback' => [ 'Tools', 'deleteRobotsTxt' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'tools/import-robots-txt' => [ 'callback' => [ 'Tools', 'importRobotsTxt' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'wizard' => [ 'callback' => [ 'Wizard', 'saveWizard' ], 'access' => 'aioseo_setup_wizard' ],
|
||||
'integration/semrush/authenticate' => [
|
||||
'callback' => [ 'Semrush', 'semrushAuthenticate', 'AIOSEO\\Plugin\\Common\\Api\\Integrations' ],
|
||||
'access' => 'aioseo_page_analysis'
|
||||
],
|
||||
'integration/semrush/refresh' => [
|
||||
'callback' => [ 'Semrush', 'semrushRefresh', 'AIOSEO\\Plugin\\Common\\Api\\Integrations' ],
|
||||
'access' => 'aioseo_page_analysis'
|
||||
],
|
||||
'integration/semrush/keyphrases' => [
|
||||
'callback' => [ 'Semrush', 'semrushGetKeyphrases', 'AIOSEO\\Plugin\\Common\\Api\\Integrations' ],
|
||||
'access' => 'aioseo_page_analysis'
|
||||
],
|
||||
'integration/wpcode/snippets' => [
|
||||
'callback' => [ 'WpCode', 'getSnippets', 'AIOSEO\\Plugin\\Common\\Api\\Integrations' ],
|
||||
'access' => 'aioseo_tools_settings'
|
||||
],
|
||||
'crawl-cleanup' => [
|
||||
'callback' => [ 'CrawlCleanup', 'fetchLogs', 'AIOSEO\\Plugin\\Common\\QueryArgs' ],
|
||||
'access' => 'aioseo_search_appearance_settings'
|
||||
],
|
||||
'crawl-cleanup/block' => [
|
||||
'callback' => [ 'CrawlCleanup', 'blockArg', 'AIOSEO\\Plugin\\Common\\QueryArgs' ],
|
||||
'access' => 'aioseo_search_appearance_settings'
|
||||
],
|
||||
'crawl-cleanup/delete-blocked' => [
|
||||
'callback' => [ 'CrawlCleanup', 'deleteBlocked', 'AIOSEO\\Plugin\\Common\\QueryArgs' ],
|
||||
'access' => 'aioseo_search_appearance_settings'
|
||||
],
|
||||
'crawl-cleanup/delete-unblocked' => [
|
||||
'callback' => [ 'CrawlCleanup', 'deleteLog', 'AIOSEO\\Plugin\\Common\\QueryArgs' ],
|
||||
'access' => 'aioseo_search_appearance_settings'
|
||||
],
|
||||
'email-summary/send' => [
|
||||
'callback' => [ 'EmailSummary', 'send' ],
|
||||
'access' => 'aioseo_page_advanced_settings'
|
||||
],
|
||||
'writing-assistant/process' => [
|
||||
'callback' => [ 'WritingAssistant', 'processKeyword' ],
|
||||
'access' => 'aioseo_page_writing_assistant_settings'
|
||||
],
|
||||
'writing-assistant/content-analysis' => [
|
||||
'callback' => [ 'WritingAssistant', 'getContentAnalysis' ],
|
||||
'access' => 'aioseo_page_writing_assistant_settings'
|
||||
],
|
||||
'writing-assistant/disconnect' => [
|
||||
'callback' => [ 'WritingAssistant', 'disconnect' ],
|
||||
'access' => 'aioseo_page_writing_assistant_settings'
|
||||
],
|
||||
'writing-assistant/user-options' => [
|
||||
'callback' => [ 'WritingAssistant', 'saveUserOptions' ],
|
||||
'access' => 'aioseo_page_writing_assistant_settings'
|
||||
],
|
||||
'writing-assistant/set-report-progress' => [
|
||||
'callback' => [ 'WritingAssistant', 'setReportProgress' ],
|
||||
'access' => 'aioseo_page_writing_assistant_settings'
|
||||
]
|
||||
],
|
||||
'DELETE' => [
|
||||
'backup' => [ 'callback' => [ 'Tools', 'deleteBackup' ], 'access' => 'aioseo_tools_settings' ],
|
||||
'search-statistics/auth' => [ 'callback' => [ 'SearchStatistics', 'deleteAuth' ], 'access' => [ 'aioseo_search_statistics_settings', 'aioseo_general_settings' ] ]
|
||||
]
|
||||
// phpcs:enable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'rest_allowed_cors_headers', [ $this, 'allowedHeaders' ] );
|
||||
add_action( 'rest_api_init', [ $this, 'registerRoutes' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the routes to register.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return array An array of routes.
|
||||
*/
|
||||
protected function getRoutes() {
|
||||
return $this->routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the API routes.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerRoutes() {
|
||||
$class = new \ReflectionClass( get_called_class() );
|
||||
foreach ( $this->getRoutes() as $method => $data ) {
|
||||
foreach ( $data as $route => $options ) {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
$route,
|
||||
[
|
||||
'methods' => $method,
|
||||
'permission_callback' => empty( $options['permissions'] ) ? [ $this, 'validRequest' ] : [ $this, $options['permissions'] ],
|
||||
'callback' => is_array( $options['callback'] )
|
||||
? [
|
||||
(
|
||||
! empty( $options['callback'][2] )
|
||||
? $options['callback'][2] . '\\' . $options['callback'][0]
|
||||
: (
|
||||
class_exists( $class->getNamespaceName() . '\\' . $options['callback'][0] )
|
||||
? $class->getNamespaceName() . '\\' . $options['callback'][0]
|
||||
: __NAMESPACE__ . '\\' . $options['callback'][0]
|
||||
)
|
||||
),
|
||||
$options['callback'][1]
|
||||
]
|
||||
: [ $this, $options['callback'] ]
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets headers that are allowed for our API routes.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function allowHeaders() {
|
||||
// TODO: Remove this entire function after a while. It's only here to ensure compatibility with people that are still using Image SEO 1.0.3 or lower.
|
||||
header( 'Access-Control-Allow-Headers: X-WP-Nonce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets headers that are allowed for our API routes.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param array $allowHeaders The allowed request headers.
|
||||
* @return array $allowHeaders The allowed request headers.
|
||||
*/
|
||||
public function allowedHeaders( $allowHeaders ) {
|
||||
if ( ! array_search( 'X-WP-Nonce', $allowHeaders, true ) ) {
|
||||
$allowHeaders[] = 'X-WP-Nonce';
|
||||
}
|
||||
|
||||
return $allowHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if logged in or has the proper permissions.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request.
|
||||
* @return bool True if validated, false if not.
|
||||
*/
|
||||
public function validRequest( $request ) {
|
||||
return is_user_logged_in() && $this->validateAccess( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates access from the routes array.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request.
|
||||
* @return bool True if validated, false if not.
|
||||
*/
|
||||
public function validateAccess( $request ) {
|
||||
$routeData = $this->getRouteData( $request );
|
||||
if ( empty( $routeData ) || empty( $routeData['access'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Admins always have access.
|
||||
if ( aioseo()->access->isAdmin() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch ( $routeData['access'] ) {
|
||||
case 'everyone':
|
||||
// Any user is able to access the route.
|
||||
return true;
|
||||
default:
|
||||
return aioseo()->access->hasCapability( $routeData['access'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data for the route that is being accessed.
|
||||
*
|
||||
* @since 4.1.6
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request.
|
||||
* @return array The route data.
|
||||
*/
|
||||
protected function getRouteData( $request ) {
|
||||
// NOTE: Since WordPress uses case-insensitive patterns to match routes,
|
||||
// we are forcing everything to lowercase to ensure we have the proper route.
|
||||
// This prevents users with lower privileges from accessing routes they shouldn't.
|
||||
$route = aioseo()->helpers->toLowercase( $request->get_route() );
|
||||
$route = untrailingslashit( str_replace( '/' . $this->namespace . '/', '', $route ) );
|
||||
$routeData = isset( $this->getRoutes()[ $request->get_method() ][ $route ] ) ? $this->getRoutes()[ $request->get_method() ][ $route ] : [];
|
||||
|
||||
// No direct route name, let's try the regexes.
|
||||
if ( empty( $routeData ) ) {
|
||||
foreach ( $this->getRoutes()[ $request->get_method() ] as $routeRegex => $routeInfo ) {
|
||||
$routeRegex = str_replace( '@', '\@', $routeRegex );
|
||||
if ( preg_match( "@{$routeRegex}@", (string) $route ) ) {
|
||||
$routeData = $routeInfo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $routeData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Connect {
|
||||
/**
|
||||
* Get the connect URL.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function getConnectUrl( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$key = ! empty( $body['licenseKey'] ) ? sanitize_text_field( $body['licenseKey'] ) : null;
|
||||
$wizard = ! empty( $body['wizard'] ) ? (bool) $body['wizard'] : false;
|
||||
$success = true;
|
||||
$urlData = aioseo()->admin->connect->generateConnectUrl( $key, $wizard ? admin_url( 'index.php?page=aioseo-setup-wizard#/success' ) : null );
|
||||
$url = '';
|
||||
$message = '';
|
||||
|
||||
if ( ! empty( $urlData['error'] ) ) {
|
||||
$success = false;
|
||||
$message = $urlData['error'];
|
||||
}
|
||||
|
||||
$url = $urlData['url'];
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => $success,
|
||||
'url' => $url,
|
||||
'message' => $message,
|
||||
'popup' => ! isset( $urlData['popup'] ) ? true : $urlData['popup']
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the connection.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function processConnect( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$wizard = ! empty( $body['wizard'] ) ? sanitize_text_field( $body['wizard'] ) : null;
|
||||
$success = true;
|
||||
|
||||
if ( $wizard ) {
|
||||
aioseo()->internalOptions->internal->wizard = $wizard;
|
||||
}
|
||||
|
||||
$response = aioseo()->admin->connect->process();
|
||||
if ( ! empty( $response['error'] ) ) {
|
||||
$message = $response['error'];
|
||||
} else {
|
||||
$message = $response['success'];
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => $success,
|
||||
'message' => $message
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the connect token.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function saveConnectToken( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$token = ! empty( $body['token'] ) ? sanitize_text_field( $body['token'] ) : null;
|
||||
$success = true;
|
||||
$message = 'token-saved';
|
||||
|
||||
aioseo()->internalOptions->internal->siteAnalysis->connectToken = $token;
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => $success,
|
||||
'message' => $message
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* Email Summary related REST API endpoint callbacks.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*/
|
||||
class EmailSummary {
|
||||
/**
|
||||
* Sends a summary.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function send( $request ) {
|
||||
try {
|
||||
$body = $request->get_json_params();
|
||||
|
||||
$to = $body['to'] ?? '';
|
||||
$frequency = $body['frequency'] ?? '';
|
||||
if ( $to && $frequency ) {
|
||||
aioseo()->emailReports->summary->run( [
|
||||
'recipient' => $to,
|
||||
'frequency' => $frequency,
|
||||
] );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
], 200 );
|
||||
} catch ( \Exception $e ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable email reports from notification.
|
||||
*
|
||||
* @since 4.7.7
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function enableEmailReports() {
|
||||
// Update option.
|
||||
aioseo()->options->advanced->emailSummary->enable = true;
|
||||
|
||||
// Remove notification.
|
||||
$notification = Models\Notification::getNotificationByName( 'email-reports-enable-reminder' );
|
||||
if ( $notification->exists() ) {
|
||||
$notification->delete();
|
||||
}
|
||||
|
||||
// Send a success response.
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'notifications' => Models\Notification::getNotifications()
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api\Integrations;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Integrations\Semrush as SemrushIntegration;
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*/
|
||||
class Semrush {
|
||||
/**
|
||||
* Fetches the additional keyphrases.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function semrushGetKeyphrases( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$keyphrases = SemrushIntegration::getKeyphrases( $body['keyphrase'], $body['database'] );
|
||||
if ( false === $keyphrases ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'You may have sent too many requests to Semrush. Please wait a few minutes and try again.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'keyphrases' => $keyphrases
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates with Semrush.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function semrushAuthenticate( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
|
||||
if ( empty( $body['code'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'Missing authorization code.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$success = SemrushIntegration::authenticate( $body['code'] );
|
||||
if ( ! $success ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'Authentication failed.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'semrush' => aioseo()->internalOptions->integrations->semrush->all()
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the API tokens.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function semrushRefresh() {
|
||||
$success = SemrushIntegration::refreshTokens();
|
||||
if ( ! $success ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'API tokens could not be refreshed.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'semrush' => aioseo()->internalOptions->integrations->semrush->all()
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api\Integrations;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Integrations\WpCode as WpCodeIntegration;
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.3.8
|
||||
*/
|
||||
class WpCode {
|
||||
/**
|
||||
* Load the WPCode Snippets from the library, if available.
|
||||
*
|
||||
* @since 4.3.8
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function getSnippets( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'snippets' => WpCodeIntegration::loadWpCodeSnippets(),
|
||||
'pluginInstalled' => WpCodeIntegration::isPluginInstalled(),
|
||||
'pluginActive' => WpCodeIntegration::isPluginActive(),
|
||||
'pluginNeedsUpdate' => WpCodeIntegration::pluginNeedsUpdate()
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Migration as CommonMigration;
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.0.6
|
||||
*/
|
||||
class Migration {
|
||||
/**
|
||||
* Resets blank title formats and retriggers the post/term meta migration.
|
||||
*
|
||||
* @since 4.0.6
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function fixBlankFormats() {
|
||||
$oldOptions = ( new CommonMigration\OldOptions() )->oldOptions;
|
||||
if ( ! $oldOptions ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'message' => 'Could not load v3 options.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$postTypes = aioseo()->helpers->getPublicPostTypes( true );
|
||||
$taxonomies = aioseo()->helpers->getPublicTaxonomies( true );
|
||||
foreach ( $oldOptions as $k => $v ) {
|
||||
if ( ! preg_match( '/^aiosp_([a-zA-Z]*)_title_format$/', (string) $k, $match ) || ! empty( $v ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$objectName = $match[1];
|
||||
if ( in_array( $objectName, $postTypes, true ) && aioseo()->dynamicOptions->searchAppearance->postTypes->has( $objectName ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$objectName->title = '#post_title #separator_sa #site_title';
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( in_array( $objectName, $taxonomies, true ) && aioseo()->dynamicOptions->searchAppearance->taxonomies->has( $objectName ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->taxonomies->$objectName->title = '#taxonomy_title #separator_sa #site_title';
|
||||
}
|
||||
}
|
||||
|
||||
aioseo()->migration->redoMetaMigration();
|
||||
|
||||
Models\Notification::deleteNotificationByName( 'v3-migration-title-formats-blank' );
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'message' => 'Title formats have been reset; post/term migration has been scheduled.',
|
||||
'notifications' => Models\Notification::getNotifications()
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.2.5
|
||||
*/
|
||||
class Network {
|
||||
/**
|
||||
* Save network robots rules.
|
||||
*
|
||||
* @since 4.2.5
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function saveNetworkRobots( $request ) {
|
||||
$isNetwork = 'network' === $request->get_param( 'siteId' );
|
||||
$siteId = $isNetwork ? aioseo()->helpers->getNetworkId() : (int) $request->get_param( 'siteId' );
|
||||
$body = $request->get_json_params();
|
||||
$rules = ! empty( $body['rules'] ) ? array_map( 'sanitize_text_field', $body['rules'] ) : [];
|
||||
$enabled = isset( $body['enabled'] ) ? boolval( $body['enabled'] ) : null;
|
||||
$searchAppearance = ! empty( $body['searchAppearance'] ) ? $body['searchAppearance'] : [];
|
||||
|
||||
aioseo()->helpers->switchToBlog( $siteId );
|
||||
|
||||
$options = $isNetwork ? aioseo()->networkOptions : aioseo()->options;
|
||||
$enabled = null === $enabled ? $options->tools->robots->enable : $enabled;
|
||||
|
||||
$options->sanitizeAndSave( [
|
||||
'tools' => [
|
||||
'robots' => [
|
||||
'enable' => $enabled,
|
||||
'rules' => $rules
|
||||
]
|
||||
],
|
||||
'searchAppearance' => $searchAppearance
|
||||
] );
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Notifications {
|
||||
/**
|
||||
* Extend the start date of a notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function blogVisibilityReminder() {
|
||||
return self::reminder( 'blog-visibility' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the start date of a notice.
|
||||
*
|
||||
* @since 4.0.5
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function descriptionFormatReminder() {
|
||||
return self::reminder( 'description-format' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the start date of a notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function installMiReminder() {
|
||||
return self::reminder( 'install-mi' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the start date of a notice.
|
||||
*
|
||||
* @since 4.2.1
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function installOmReminder() {
|
||||
return self::reminder( 'install-om' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the start date of a notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function installAddonsReminder() {
|
||||
return self::reminder( 'install-addons' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the start date of a notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function installImageSeoReminder() {
|
||||
return self::reminder( 'install-aioseo-image-seo' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the start date of a notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function installLocalBusinessReminder() {
|
||||
return self::reminder( 'install-aioseo-local-business' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the start date of a notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function installNewsSitemapReminder() {
|
||||
return self::reminder( 'install-aioseo-news-sitemap' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the start date of a notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function installVideoSitemapReminder() {
|
||||
return self::reminder( 'install-aioseo-video-sitemap' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the start date of a notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function conflictingPluginsReminder() {
|
||||
return self::reminder( 'conflicting-plugins' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the start date of a notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function migrationCustomFieldReminder() {
|
||||
return self::reminder( 'v3-migration-custom-field' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the start date of a notice.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function migrationSchemaNumberReminder() {
|
||||
return self::reminder( 'v3-migration-schema-number' );
|
||||
}
|
||||
|
||||
/**
|
||||
* This allows us to not repeat code over and over.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param string $slug The slug of the reminder.
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
protected static function reminder( $slug ) {
|
||||
aioseo()->notices->remindMeLater( $slug );
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'notifications' => Models\Notification::getNotifications()
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss notifications.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function dismissNotifications( $request ) {
|
||||
$slugs = $request->get_json_params();
|
||||
|
||||
$notifications = aioseo()->core->db
|
||||
->start( 'aioseo_notifications' )
|
||||
->whereIn( 'slug', $slugs )
|
||||
->run()
|
||||
->models( 'AIOSEO\\Plugin\\Common\\Models\\Notification' );
|
||||
|
||||
foreach ( $notifications as $notification ) {
|
||||
$notification->dismissed = 1;
|
||||
$notification->save();
|
||||
}
|
||||
|
||||
// Dismiss static notifications.
|
||||
if ( in_array( 'notification-review', $slugs, true ) ) {
|
||||
update_user_meta( get_current_user_id(), '_aioseo_notification_plugin_review_dismissed', '3' );
|
||||
}
|
||||
|
||||
if ( in_array( 'notification-review-delay', $slugs, true ) ) {
|
||||
update_user_meta( get_current_user_id(), '_aioseo_notification_plugin_review_dismissed', strtotime( '+1 week' ) );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'notifications' => Models\Notification::getNotifications()
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Ping {
|
||||
/**
|
||||
* Returns a success if the API is alive.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function ping() {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Plugins {
|
||||
/**
|
||||
* Installs plugins from vue.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function installPlugins( $request ) {
|
||||
$error = esc_html__( 'Installation failed. Please check permissions and try again.', 'all-in-one-seo-pack' );
|
||||
$body = $request->get_json_params();
|
||||
$plugins = ! empty( $body['plugins'] ) ? $body['plugins'] : [];
|
||||
$network = ! empty( $body['network'] ) ? $body['network'] : false;
|
||||
|
||||
if ( ! is_array( $plugins ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $error
|
||||
], 400 );
|
||||
}
|
||||
|
||||
if ( ! aioseo()->addons->canInstall() ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $error
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$failed = [];
|
||||
$completed = [];
|
||||
foreach ( $plugins as $plugin ) {
|
||||
if ( empty( $plugin['plugin'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $error
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$result = aioseo()->addons->installAddon( $plugin['plugin'], $network );
|
||||
if ( ! $result ) {
|
||||
$failed[] = $plugin['plugin'];
|
||||
} else {
|
||||
$completed[ $plugin['plugin'] ] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'completed' => $completed,
|
||||
'failed' => $failed
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade plugins from vue.
|
||||
*
|
||||
* @since 4.1.6
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function upgradePlugins( $request ) {
|
||||
$error = esc_html__( 'Plugin update failed. Please check permissions and try again.', 'all-in-one-seo-pack' );
|
||||
$body = $request->get_json_params();
|
||||
$plugins = ! empty( $body['plugins'] ) ? $body['plugins'] : [];
|
||||
$network = ! empty( $body['network'] ) ? $body['network'] : false;
|
||||
|
||||
if ( ! is_array( $plugins ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $error
|
||||
], 400 );
|
||||
}
|
||||
|
||||
if ( ! aioseo()->addons->canUpdate() ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $error
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$failed = [];
|
||||
$completed = [];
|
||||
foreach ( $plugins as $plugin ) {
|
||||
if ( empty( $plugin['plugin'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $error
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$result = aioseo()->addons->upgradeAddon( $plugin['plugin'], $network );
|
||||
if ( ! $result ) {
|
||||
$failed[] = $plugin['plugin'];
|
||||
} else {
|
||||
$completed[ $plugin['plugin'] ] = aioseo()->addons->getAddon( $plugin['plugin'], true );
|
||||
}
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'completed' => $completed,
|
||||
'failed' => $failed
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates plugins from vue.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function deactivatePlugins( $request ) {
|
||||
$error = esc_html__( 'Deactivation failed. Please check permissions and try again.', 'all-in-one-seo-pack' );
|
||||
$body = $request->get_json_params();
|
||||
$plugins = ! empty( $body['plugins'] ) ? $body['plugins'] : [];
|
||||
$network = ! empty( $body['network'] ) ? $body['network'] : false;
|
||||
|
||||
if ( ! is_array( $plugins ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $error
|
||||
], 400 );
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'install_plugins' ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $error
|
||||
], 400 );
|
||||
}
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
|
||||
$failed = [];
|
||||
$completed = [];
|
||||
foreach ( $plugins as $plugin ) {
|
||||
if ( empty( $plugin['plugin'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $error
|
||||
], 400 );
|
||||
}
|
||||
|
||||
deactivate_plugins( $plugin['plugin'], false, $network );
|
||||
|
||||
$stillActive = $network ? is_plugin_active_for_network( $plugin['plugin'] ) : is_plugin_active( $plugin['plugin'] );
|
||||
if ( $stillActive ) {
|
||||
$failed[] = $plugin['plugin'];
|
||||
}
|
||||
|
||||
$completed[] = $plugin['plugin'];
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'completed' => $completed,
|
||||
'failed' => $failed
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,503 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class PostsTerms {
|
||||
/**
|
||||
* Searches for posts or terms by ID/name.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function searchForObjects( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
|
||||
if ( empty( $body['query'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'No search term was provided.'
|
||||
], 400 );
|
||||
}
|
||||
if ( empty( $body['type'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'No type was provided.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$searchQuery = esc_sql( aioseo()->core->db->db->esc_like( $body['query'] ) );
|
||||
|
||||
$objects = [];
|
||||
$dynamicOptions = aioseo()->dynamicOptions->noConflict();
|
||||
if ( 'posts' === $body['type'] ) {
|
||||
|
||||
$postTypes = aioseo()->helpers->getPublicPostTypes( true );
|
||||
foreach ( $postTypes as $postType ) {
|
||||
// Check if post type isn't noindexed.
|
||||
if ( $dynamicOptions->searchAppearance->postTypes->has( $postType ) && ! $dynamicOptions->searchAppearance->postTypes->$postType->show ) {
|
||||
$postTypes = aioseo()->helpers->unsetValue( $postTypes, $postType );
|
||||
}
|
||||
}
|
||||
|
||||
$objects = aioseo()->core->db
|
||||
->start( 'posts' )
|
||||
->select( 'ID, post_type, post_title, post_name' )
|
||||
->whereRaw( "( post_title LIKE '%{$searchQuery}%' OR post_name LIKE '%{$searchQuery}%' OR ID = '{$searchQuery}' )" )
|
||||
->whereIn( 'post_type', $postTypes )
|
||||
->whereIn( 'post_status', [ 'publish', 'draft', 'future', 'pending' ] )
|
||||
->orderBy( 'post_title' )
|
||||
->limit( 10 )
|
||||
->run()
|
||||
->result();
|
||||
|
||||
} elseif ( 'terms' === $body['type'] ) {
|
||||
|
||||
$taxonomies = aioseo()->helpers->getPublicTaxonomies( true );
|
||||
foreach ( $taxonomies as $taxonomy ) {
|
||||
// Check if taxonomy isn't noindexed.
|
||||
if ( $dynamicOptions->searchAppearance->taxonomies->has( $taxonomy ) && ! $dynamicOptions->searchAppearance->taxonomies->$taxonomy->show ) {
|
||||
$taxonomies = aioseo()->helpers->unsetValue( $taxonomies, $taxonomy );
|
||||
}
|
||||
}
|
||||
|
||||
$objects = aioseo()->core->db
|
||||
->start( 'terms as t' )
|
||||
->select( 't.term_id as term_id, t.slug as slug, t.name as name, tt.taxonomy as taxonomy' )
|
||||
->join( 'term_taxonomy as tt', 't.term_id = tt.term_id', 'INNER' )
|
||||
->whereRaw( "( t.name LIKE '%{$searchQuery}%' OR t.slug LIKE '%{$searchQuery}%' OR t.term_id = '{$searchQuery}' )" )
|
||||
->whereIn( 'tt.taxonomy', $taxonomies )
|
||||
->orderBy( 't.name' )
|
||||
->limit( 10 )
|
||||
->run()
|
||||
->result();
|
||||
}
|
||||
|
||||
if ( empty( $objects ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'objects' => []
|
||||
], 200 );
|
||||
}
|
||||
|
||||
$parsed = [];
|
||||
foreach ( $objects as $object ) {
|
||||
if ( 'posts' === $body['type'] ) {
|
||||
$parsed[] = [
|
||||
'type' => $object->post_type,
|
||||
'value' => (int) $object->ID,
|
||||
'slug' => $object->post_name,
|
||||
'label' => $object->post_title,
|
||||
'link' => get_permalink( $object->ID )
|
||||
];
|
||||
} elseif ( 'terms' === $body['type'] ) {
|
||||
$parsed[] = [
|
||||
'type' => $object->taxonomy,
|
||||
'value' => (int) $object->term_id,
|
||||
'slug' => $object->slug,
|
||||
'label' => $object->name,
|
||||
'link' => get_term_link( $object->term_id )
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'objects' => $parsed
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post data for fetch requests
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function getPostData( $request ) {
|
||||
$args = $request->get_query_params();
|
||||
|
||||
if ( empty( $args['postId'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'No post ID was provided.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$thePost = Models\Post::getPost( $args['postId'] );
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'post' => $thePost,
|
||||
'postData' => [
|
||||
'parsedTitle' => aioseo()->tags->replaceTags( $thePost->title, $args['postId'] ),
|
||||
'parsedDescription' => aioseo()->tags->replaceTags( $thePost->description, $args['postId'] ),
|
||||
'content' => aioseo()->helpers->theContent( self::getAnalysisContent( $args['postId'] ) ),
|
||||
'slug' => get_post_field( 'post_name', $args['postId'] )
|
||||
]
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first attached image for a post.
|
||||
*
|
||||
* @since 4.1.8
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function getFirstAttachedImage( $request ) {
|
||||
$args = $request->get_params();
|
||||
|
||||
if ( empty( $args['postId'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'No post ID was provided.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
// Disable the cache.
|
||||
aioseo()->social->image->useCache = false;
|
||||
|
||||
$post = aioseo()->helpers->getPost( $args['postId'] );
|
||||
$url = aioseo()->social->image->getImage( 'facebook', 'attach', $post );
|
||||
|
||||
// Reset the cache property.
|
||||
aioseo()->social->image->useCache = true;
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'url' => is_array( $url ) ? $url[0] : $url,
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the posts custom fields.
|
||||
*
|
||||
* @since 4.0.6
|
||||
*
|
||||
* @param \WP_Post|int $post The post.
|
||||
* @return string The custom field content.
|
||||
*/
|
||||
private static function getAnalysisContent( $post = null ) {
|
||||
$analysisContent = apply_filters( 'aioseo_analysis_content', aioseo()->helpers->getPostContent( $post ) );
|
||||
|
||||
return sanitize_post_field( 'post_content', $analysisContent, $post->ID, 'display' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update post settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function updatePosts( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$postId = ! empty( $body['id'] ) ? intval( $body['id'] ) : null;
|
||||
|
||||
if ( ! $postId ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'Post ID is missing.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$body['id'] = $postId;
|
||||
$body['title'] = ! empty( $body['title'] ) ? sanitize_text_field( $body['title'] ) : null;
|
||||
$body['description'] = ! empty( $body['description'] ) ? sanitize_text_field( $body['description'] ) : null;
|
||||
$body['keywords'] = ! empty( $body['keywords'] ) ? aioseo()->helpers->sanitize( $body['keywords'] ) : null;
|
||||
$body['og_title'] = ! empty( $body['og_title'] ) ? sanitize_text_field( $body['og_title'] ) : null;
|
||||
$body['og_description'] = ! empty( $body['og_description'] ) ? sanitize_text_field( $body['og_description'] ) : null;
|
||||
$body['og_article_section'] = ! empty( $body['og_article_section'] ) ? sanitize_text_field( $body['og_article_section'] ) : null;
|
||||
$body['og_article_tags'] = ! empty( $body['og_article_tags'] ) ? aioseo()->helpers->sanitize( $body['og_article_tags'] ) : null;
|
||||
$body['twitter_title'] = ! empty( $body['twitter_title'] ) ? sanitize_text_field( $body['twitter_title'] ) : null;
|
||||
$body['twitter_description'] = ! empty( $body['twitter_description'] ) ? sanitize_text_field( $body['twitter_description'] ) : null;
|
||||
|
||||
$error = Models\Post::savePost( $postId, $body );
|
||||
|
||||
if ( ! empty( $error ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'Failed update query: ' . $error
|
||||
], 401 );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'posts' => $postId
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Load post settings from Post screen.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function loadPostDetailsColumn( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$ids = ! empty( $body['ids'] ) ? (array) $body['ids'] : [];
|
||||
|
||||
if ( ! $ids ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'Post IDs are missing.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$posts = [];
|
||||
foreach ( $ids as $postId ) {
|
||||
$postTitle = get_the_title( $postId );
|
||||
$headline = ! empty( $postTitle ) ? sanitize_text_field( $postTitle ) : ''; // We need this to achieve consistency for the score when using special characters in titles
|
||||
$headlineResult = aioseo()->standalone->headlineAnalyzer->getResult( $headline );
|
||||
|
||||
$posts[] = [
|
||||
'id' => $postId,
|
||||
'titleParsed' => aioseo()->meta->title->getPostTitle( $postId ),
|
||||
'descriptionParsed' => aioseo()->meta->description->getPostDescription( $postId ),
|
||||
'headlineScore' => ! empty( $headlineResult['score'] ) ? (int) $headlineResult['score'] : 0,
|
||||
];
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'posts' => $posts
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update post settings from Post screen.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function updatePostDetailsColumn( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$postId = ! empty( $body['postId'] ) ? intval( $body['postId'] ) : null;
|
||||
$isMedia = isset( $body['isMedia'] ) ? true : false;
|
||||
|
||||
if ( ! $postId ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'Post ID is missing.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$aioseoPost = Models\Post::getPost( $postId );
|
||||
$aioseoData = json_decode( wp_json_encode( $aioseoPost ), true );
|
||||
|
||||
if ( $isMedia ) {
|
||||
wp_update_post(
|
||||
[
|
||||
'ID' => $postId,
|
||||
'post_title' => sanitize_text_field( $body['imageTitle'] ),
|
||||
]
|
||||
);
|
||||
update_post_meta( $postId, '_wp_attachment_image_alt', sanitize_text_field( $body['imageAltTag'] ) );
|
||||
}
|
||||
|
||||
Models\Post::savePost( $postId, array_replace( $aioseoData, $body ) );
|
||||
|
||||
$lastError = aioseo()->core->db->lastError();
|
||||
if ( ! empty( $lastError ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'Failed update query: ' . $lastError
|
||||
], 401 );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'posts' => $postId,
|
||||
'title' => aioseo()->meta->title->getPostTitle( $postId ),
|
||||
'description' => aioseo()->meta->description->getPostDescription( $postId )
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update post keyphrases.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function updatePostKeyphrases( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$postId = ! empty( $body['postId'] ) ? intval( $body['postId'] ) : null;
|
||||
|
||||
if ( ! $postId ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'Post ID is missing.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$thePost = Models\Post::getPost( $postId );
|
||||
|
||||
$thePost->post_id = $postId;
|
||||
if ( ! empty( $body['keyphrases'] ) ) {
|
||||
$thePost->keyphrases = wp_json_encode( $body['keyphrases'] );
|
||||
}
|
||||
if ( ! empty( $body['page_analysis'] ) ) {
|
||||
$thePost->page_analysis = wp_json_encode( $body['page_analysis'] );
|
||||
}
|
||||
if ( ! empty( $body['seo_score'] ) ) {
|
||||
$thePost->seo_score = sanitize_text_field( $body['seo_score'] );
|
||||
}
|
||||
$thePost->updated = gmdate( 'Y-m-d H:i:s' );
|
||||
$thePost->save();
|
||||
|
||||
$lastError = aioseo()->core->db->lastError();
|
||||
if ( ! empty( $lastError ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'Failed update query: ' . $lastError
|
||||
], 401 );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'post' => $postId
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the Primary Term education.
|
||||
*
|
||||
* @since 4.3.6
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function disablePrimaryTermEducation( $request ) {
|
||||
$args = $request->get_params();
|
||||
|
||||
if ( empty( $args['postId'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'No post ID was provided.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$thePost = Models\Post::getPost( $args['postId'] );
|
||||
$thePost->options->primaryTerm->productEducationDismissed = true;
|
||||
$thePost->save();
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the link format education.
|
||||
*
|
||||
* @since 4.2.2
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function disableLinkFormatEducation( $request ) {
|
||||
$args = $request->get_params();
|
||||
|
||||
if ( empty( $args['postId'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'No post ID was provided.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$thePost = Models\Post::getPost( $args['postId'] );
|
||||
$thePost->options->linkFormat->linkAssistantDismissed = true;
|
||||
$thePost->save();
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the internal link count.
|
||||
*
|
||||
* @since 4.2.2
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function updateInternalLinkCount( $request ) {
|
||||
$args = $request->get_params();
|
||||
$body = $request->get_json_params();
|
||||
$count = ! empty( $body['count'] ) ? intval( $body['count'] ) : null;
|
||||
|
||||
if ( empty( $args['postId'] ) || null === $count ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'No post ID or count was provided.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$thePost = Models\Post::getPost( $args['postId'] );
|
||||
$thePost->options->linkFormat->internalLinkCount = $count;
|
||||
$thePost->save();
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the processed content by the given raw content.
|
||||
*
|
||||
* @since 4.5.2
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request.
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function processContent( $request ) {
|
||||
$args = $request->get_params();
|
||||
$body = $request->get_json_params();
|
||||
|
||||
if ( empty( $args['postId'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'No post ID was provided.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
// Check if we can process it using a page builder integration.
|
||||
$pageBuilder = aioseo()->helpers->getPostPageBuilderName( $args['postId'] );
|
||||
if ( ! empty( $pageBuilder ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'content' => aioseo()->standalone->pageBuilderIntegrations[ $pageBuilder ]->processContent( $args['postId'], $body['content'] ),
|
||||
], 200 );
|
||||
}
|
||||
|
||||
// Check if the content was passed, otherwise get it from the post.
|
||||
$content = $body['content'] ?? aioseo()->helpers->getPostContent( $args['postId'] );
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'content' => apply_filters( 'the_content', $content ),
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\SearchStatistics\Api;
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.3.0
|
||||
* @version 4.6.2 Moved from Pro to Common.
|
||||
*/
|
||||
class SearchStatistics {
|
||||
/**
|
||||
* Get the authorize URL.
|
||||
*
|
||||
* @since 4.3.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function getAuthUrl( $request ) {
|
||||
$body = $request->get_params();
|
||||
|
||||
if ( aioseo()->searchStatistics->api->auth->isConnected() ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'Cannot authenticate. Please re-authenticate.'
|
||||
], 200 );
|
||||
}
|
||||
|
||||
$returnTo = ! empty( $body['returnTo'] ) ? sanitize_key( $body['returnTo'] ) : '';
|
||||
$url = add_query_arg( [
|
||||
'tt' => aioseo()->searchStatistics->api->trustToken->get(),
|
||||
'sitei' => aioseo()->searchStatistics->api->getSiteIdentifier(),
|
||||
'version' => aioseo()->version,
|
||||
'ajaxurl' => admin_url( 'admin-ajax.php' ),
|
||||
'siteurl' => site_url(),
|
||||
'return' => urlencode( admin_url( 'admin.php?page=aioseo&return-to=' . $returnTo ) ),
|
||||
'testurl' => 'https://' . aioseo()->searchStatistics->api->getApiUrl() . '/v1/test/'
|
||||
], 'https://' . aioseo()->searchStatistics->api->getApiUrl() . '/v1/auth/new/' . aioseo()->searchStatistics->api->auth->type . '/' );
|
||||
|
||||
$url = apply_filters( 'aioseo_search_statistics_auth_url', $url );
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'url' => $url,
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reauthorize URL.
|
||||
*
|
||||
* @since 4.3.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function getReauthUrl( $request ) {
|
||||
$body = $request->get_params();
|
||||
|
||||
if ( ! aioseo()->searchStatistics->api->auth->isConnected() ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'Cannot re-authenticate. Please authenticate.',
|
||||
], 200 );
|
||||
}
|
||||
|
||||
$returnTo = ! empty( $body['returnTo'] ) ? sanitize_key( $body['returnTo'] ) : '';
|
||||
$url = add_query_arg( [
|
||||
'tt' => aioseo()->searchStatistics->api->trustToken->get(),
|
||||
'sitei' => aioseo()->searchStatistics->api->getSiteIdentifier(),
|
||||
'version' => aioseo()->version,
|
||||
'ajaxurl' => admin_url( 'admin-ajax.php' ),
|
||||
'siteurl' => site_url(),
|
||||
'key' => aioseo()->searchStatistics->api->auth->getKey(),
|
||||
'token' => aioseo()->searchStatistics->api->auth->getToken(),
|
||||
'return' => urlencode( admin_url( 'admin.php?page=aioseo&return-to=' . $returnTo ) ),
|
||||
'testurl' => 'https://' . aioseo()->searchStatistics->api->getApiUrl() . '/v1/test/'
|
||||
], 'https://' . aioseo()->searchStatistics->api->getApiUrl() . '/v1/auth/reauth/' . aioseo()->searchStatistics->api->auth->type . '/' );
|
||||
|
||||
$url = apply_filters( 'aioseo_search_statistics_reauth_url', $url );
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'url' => $url,
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the authorization.
|
||||
*
|
||||
* @since 4.3.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function deleteAuth() {
|
||||
if ( ! aioseo()->searchStatistics->api->auth->isConnected() ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'Cannot deauthenticate. You are not currently authenticated.'
|
||||
], 200 );
|
||||
}
|
||||
|
||||
aioseo()->searchStatistics->api->auth->delete();
|
||||
aioseo()->searchStatistics->cancelActions();
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'message' => 'Successfully deauthenticated.'
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a sitemap.
|
||||
*
|
||||
* @since 4.6.2
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function deleteSitemap( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$sitemap = ! empty( $body['sitemap'] ) ? $body['sitemap'] : '';
|
||||
|
||||
if ( empty( $sitemap ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'No sitemap provided.'
|
||||
], 200 );
|
||||
}
|
||||
|
||||
$args = [
|
||||
'sitemap' => $sitemap
|
||||
];
|
||||
|
||||
$api = new Api\Request( 'google-search-console/sitemap/delete/', $args, 'POST' );
|
||||
$response = $api->request();
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $response['message']
|
||||
], 200 );
|
||||
}
|
||||
|
||||
aioseo()->internalOptions->searchStatistics->sitemap->list = $response['data'];
|
||||
aioseo()->internalOptions->searchStatistics->sitemap->lastFetch = time();
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'internalOptions' => aioseo()->internalOptions->searchStatistics->sitemap->all(),
|
||||
'sitemapsWithErrors' => aioseo()->searchStatistics->sitemap->getSitemapsWithErrors()
|
||||
]
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignores a sitemap.
|
||||
*
|
||||
* @since 4.6.2
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function ignoreSitemap( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$sitemap = ! empty( $body['sitemap'] ) ? $body['sitemap'] : '';
|
||||
|
||||
if ( empty( $sitemap ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'No sitemap provided.'
|
||||
], 200 );
|
||||
}
|
||||
|
||||
$ignoredSitemaps = aioseo()->internalOptions->searchStatistics->sitemap->ignored;
|
||||
if ( is_array( $sitemap ) ) {
|
||||
$ignoredSitemaps = array_merge( $ignoredSitemaps, $sitemap );
|
||||
} else {
|
||||
$ignoredSitemaps[] = $sitemap;
|
||||
}
|
||||
|
||||
$ignoredSitemaps = array_unique( $ignoredSitemaps ); // Remove duplicates.
|
||||
$ignoredSitemaps = array_filter( $ignoredSitemaps ); // Remove empty values.
|
||||
|
||||
aioseo()->internalOptions->searchStatistics->sitemap->ignored = $ignoredSitemaps;
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'internalOptions' => aioseo()->internalOptions->searchStatistics->sitemap->all(),
|
||||
'sitemapsWithErrors' => aioseo()->searchStatistics->sitemap->getSitemapsWithErrors()
|
||||
]
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,852 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
use AIOSEO\Plugin\Common\Migration;
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Settings {
|
||||
/**
|
||||
* Contents to import.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $importFile = [];
|
||||
|
||||
/**
|
||||
* Update the settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function getOptions() {
|
||||
return new \WP_REST_Response( [
|
||||
'options' => aioseo()->options->all(),
|
||||
'settings' => aioseo()->settings->all()
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles a card in the settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function toggleCard( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$card = ! empty( $body['card'] ) ? sanitize_text_field( $body['card'] ) : null;
|
||||
$cards = aioseo()->settings->toggledCards;
|
||||
if ( array_key_exists( $card, $cards ) ) {
|
||||
$cards[ $card ] = ! $cards[ $card ];
|
||||
aioseo()->settings->toggledCards = $cards;
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles a radio in the settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function toggleRadio( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$radio = ! empty( $body['radio'] ) ? sanitize_text_field( $body['radio'] ) : null;
|
||||
$value = ! empty( $body['value'] ) ? sanitize_text_field( $body['value'] ) : null;
|
||||
$radios = aioseo()->settings->toggledRadio;
|
||||
if ( array_key_exists( $radio, $radios ) ) {
|
||||
$radios[ $radio ] = $value;
|
||||
aioseo()->settings->toggledRadio = $radios;
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses an alert.
|
||||
*
|
||||
* @since 4.3.6
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function dismissAlert( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$alert = ! empty( $body['alert'] ) ? sanitize_text_field( $body['alert'] ) : null;
|
||||
$alerts = aioseo()->settings->dismissedAlerts;
|
||||
if ( array_key_exists( $alert, $alerts ) ) {
|
||||
$alerts[ $alert ] = true;
|
||||
aioseo()->settings->dismissedAlerts = $alerts;
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles a table's items per page setting.
|
||||
*
|
||||
* @since 4.2.5
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function changeItemsPerPage( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$table = ! empty( $body['table'] ) ? sanitize_text_field( $body['table'] ) : null;
|
||||
$value = ! empty( $body['value'] ) ? intval( $body['value'] ) : null;
|
||||
$tables = aioseo()->settings->tablePagination;
|
||||
if ( array_key_exists( $table, $tables ) ) {
|
||||
$tables[ $table ] = $value;
|
||||
aioseo()->settings->tablePagination = $tables;
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses the upgrade bar.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function hideUpgradeBar() {
|
||||
aioseo()->settings->showUpgradeBar = false;
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the Setup Wizard CTA.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function hideSetupWizard() {
|
||||
aioseo()->settings->showSetupWizard = false;
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save options from the front end.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function saveChanges( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$options = ! empty( $body['options'] ) ? $body['options'] : [];
|
||||
$dynamicOptions = ! empty( $body['dynamicOptions'] ) ? $body['dynamicOptions'] : [];
|
||||
$network = ! empty( $body['network'] ) ? (bool) $body['network'] : false;
|
||||
$networkOptions = ! empty( $body['networkOptions'] ) ? $body['networkOptions'] : [];
|
||||
|
||||
// If this is the network admin, reset the options.
|
||||
if ( $network ) {
|
||||
aioseo()->networkOptions->sanitizeAndSave( $networkOptions );
|
||||
} else {
|
||||
aioseo()->options->sanitizeAndSave( $options );
|
||||
aioseo()->dynamicOptions->sanitizeAndSave( $dynamicOptions );
|
||||
}
|
||||
|
||||
// Re-initialize notices.
|
||||
aioseo()->notices->init();
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'notifications' => Models\Notification::getNotifications()
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function resetSettings( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$settings = ! empty( $body['settings'] ) ? $body['settings'] : [];
|
||||
|
||||
$notAllowedOptions = aioseo()->access->getNotAllowedOptions();
|
||||
|
||||
foreach ( $settings as $setting ) {
|
||||
$optionAccess = in_array( $setting, [ 'robots', 'blocker' ], true ) ? 'tools' : $setting;
|
||||
|
||||
if ( in_array( $optionAccess, $notAllowedOptions, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ( $setting ) {
|
||||
case 'robots':
|
||||
aioseo()->options->tools->robots->reset();
|
||||
aioseo()->options->searchAppearance->advanced->unwantedBots->reset();
|
||||
aioseo()->options->searchAppearance->advanced->searchCleanup->settings->preventCrawling = false;
|
||||
break;
|
||||
case 'blocker':
|
||||
aioseo()->options->deprecated->tools->blocker->reset();
|
||||
break;
|
||||
default:
|
||||
if ( 'searchAppearance' === $setting ) {
|
||||
aioseo()->robotsTxt->resetSearchAppearanceRules();
|
||||
}
|
||||
|
||||
if ( aioseo()->options->has( $setting ) ) {
|
||||
aioseo()->options->$setting->reset();
|
||||
}
|
||||
if ( aioseo()->dynamicOptions->has( $setting ) ) {
|
||||
aioseo()->dynamicOptions->$setting->reset();
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'access-control' === $setting ) {
|
||||
aioseo()->access->addCapabilities();
|
||||
}
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Import settings from external file.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request.
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function importSettings( $request ) {
|
||||
$file = $request->get_file_params()['file'];
|
||||
$isJSONFile = 'application/json' === $file['type'];
|
||||
$isCSVFile = 'text/csv' === $file['type'];
|
||||
$isOctetFile = 'application/octet-stream' === $file['type'];
|
||||
if (
|
||||
empty( $file['tmp_name'] ) ||
|
||||
empty( $file['type'] ) ||
|
||||
(
|
||||
! $isJSONFile &&
|
||||
! $isCSVFile &&
|
||||
! $isOctetFile
|
||||
)
|
||||
) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$contents = aioseo()->core->fs->getContents( $file['tmp_name'] );
|
||||
if ( empty( $contents ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false
|
||||
], 400 );
|
||||
}
|
||||
|
||||
if ( $isJSONFile ) {
|
||||
self::$importFile = json_decode( $contents, true );
|
||||
}
|
||||
|
||||
if ( $isCSVFile ) {
|
||||
// Transform the CSV content into the original JSON array.
|
||||
self::$importFile = self::prepareCsvImport( $contents );
|
||||
}
|
||||
|
||||
// If the file is invalid just return.
|
||||
if ( empty( self::$importFile ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false
|
||||
], 400 );
|
||||
}
|
||||
|
||||
// Import settings.
|
||||
if ( ! empty( self::$importFile['settings'] ) ) {
|
||||
self::importSettingsFromFile( self::$importFile['settings'] );
|
||||
}
|
||||
|
||||
// Import posts.
|
||||
if ( ! empty( self::$importFile['postOptions'] ) ) {
|
||||
self::importPostsFromFile( self::$importFile['postOptions'] );
|
||||
}
|
||||
|
||||
// Import INI.
|
||||
if ( $isOctetFile ) {
|
||||
$response = aioseo()->importExport->importIniData( self::$importFile );
|
||||
if ( ! $response ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false
|
||||
], 400 );
|
||||
}
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'options' => aioseo()->options->all()
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Import settings from a file.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param array $settings The data to import.
|
||||
*/
|
||||
private static function importSettingsFromFile( $settings ) {
|
||||
// Clean up the array removing options the user should not manage.
|
||||
$notAllowedOptions = aioseo()->access->getNotAllowedOptions();
|
||||
$settings = array_diff_key( $settings, $notAllowedOptions );
|
||||
if ( ! empty( $settings['deprecated'] ) ) {
|
||||
$settings['deprecated'] = array_diff_key( $settings['deprecated'], $notAllowedOptions );
|
||||
}
|
||||
|
||||
// Remove any dynamic options and save them separately since this has been refactored.
|
||||
$commonDynamic = [
|
||||
'sitemap',
|
||||
'searchAppearance',
|
||||
'breadcrumbs',
|
||||
'accessControl'
|
||||
];
|
||||
|
||||
foreach ( $commonDynamic as $cd ) {
|
||||
if ( ! empty( $settings[ $cd ]['dynamic'] ) ) {
|
||||
$settings['dynamic'][ $cd ] = $settings[ $cd ]['dynamic'];
|
||||
unset( $settings[ $cd ]['dynamic'] );
|
||||
}
|
||||
}
|
||||
|
||||
// These options have a very different structure so we'll do them separately.
|
||||
if ( ! empty( $settings['social']['facebook']['general']['dynamic'] ) ) {
|
||||
$settings['dynamic']['social']['facebook']['general'] = $settings['social']['facebook']['general']['dynamic'];
|
||||
unset( $settings['social']['facebook']['general']['dynamic'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $settings['dynamic'] ) ) {
|
||||
aioseo()->dynamicOptions->sanitizeAndSave( $settings['dynamic'] );
|
||||
unset( $settings['dynamic'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $settings['tools']['robots']['rules'] ) ) {
|
||||
$settings['tools']['robots']['rules'] = array_merge( aioseo()->robotsTxt->extractSearchAppearanceRules(), $settings['tools']['robots']['rules'] );
|
||||
}
|
||||
|
||||
aioseo()->options->sanitizeAndSave( $settings );
|
||||
}
|
||||
|
||||
/**
|
||||
* Import posts from a file.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param array $postOptions The data to import.
|
||||
*/
|
||||
private static function importPostsFromFile( $postOptions ) {
|
||||
$notAllowedFields = aioseo()->access->getNotAllowedPageFields();
|
||||
|
||||
foreach ( $postOptions as $postData ) {
|
||||
if ( ! empty( $postData['posts'] ) ) {
|
||||
foreach ( $postData['posts'] as $post ) {
|
||||
unset( $post['id'] );
|
||||
// Clean up the array removing fields the user should not manage.
|
||||
$post = array_diff_key( $post, $notAllowedFields );
|
||||
$thePost = Models\Post::getPost( $post['post_id'] );
|
||||
|
||||
// Remove primary term if the term is not attached to the post anymore.
|
||||
if ( ! empty( $post['primary_term'] ) && aioseo()->helpers->isJsonString( $post['primary_term'] ) ) {
|
||||
$primaryTerms = json_decode( $post['primary_term'], true );
|
||||
|
||||
foreach ( $primaryTerms as $tax => $termId ) {
|
||||
$terms = wp_get_post_terms( $post['post_id'], $tax, [
|
||||
'fields' => 'ids'
|
||||
] );
|
||||
|
||||
if ( is_array( $terms ) && ! in_array( $termId, $terms, true ) ) {
|
||||
unset( $primaryTerms[ $tax ] );
|
||||
}
|
||||
}
|
||||
|
||||
$post['primary_term'] = empty( $primaryTerms ) ? null : wp_json_encode( $primaryTerms );
|
||||
}
|
||||
|
||||
// Remove FAQ Block schema if the block is not present in the post anymore.
|
||||
if ( ! empty( $post['schema'] ) && aioseo()->helpers->isJsonString( $post['schema'] ) ) {
|
||||
$schemas = json_decode( $post['schema'], true );
|
||||
|
||||
foreach ( $schemas['blockGraphs'] as $index => $block ) {
|
||||
if ( 'aioseo/faq' !== $block['type'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$postBlocks = parse_blocks( get_the_content( null, false, $post['post_id'] ) );
|
||||
$postFaqBlock = array_filter( $postBlocks, function( $block ) {
|
||||
return 'aioseo/faq' === $block['blockName'];
|
||||
} );
|
||||
|
||||
if ( empty( $postFaqBlock ) ) {
|
||||
unset( $schemas['blockGraphs'][ $index ] );
|
||||
}
|
||||
}
|
||||
|
||||
$post['schema'] = wp_json_encode( $schemas );
|
||||
}
|
||||
|
||||
$thePost->set( $post );
|
||||
$thePost->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the content from CSV to the original JSON array to import.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param string $fileContent The Data to import.
|
||||
* @return array The content.
|
||||
*/
|
||||
public static function prepareCSVImport( $fileContent ) {
|
||||
$content = [];
|
||||
$newContent = [
|
||||
'postOptions' => null
|
||||
];
|
||||
|
||||
$rows = str_getcsv( $fileContent, "\n" );
|
||||
|
||||
// Get the first row to check if the file has post_id or term_id.
|
||||
$header = str_getcsv( $rows[0], ',' );
|
||||
$header = aioseo()->helpers->sanitizeOption( $header );
|
||||
|
||||
// Check if the file has post_id or term_id.
|
||||
$type = in_array( 'post_id', $header, true ) ? 'posts' : null;
|
||||
$type = in_array( 'term_id', $header, true ) ? 'terms' : $type;
|
||||
|
||||
if ( ! $type ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove header row.
|
||||
unset( $rows[0] );
|
||||
|
||||
$jsonFields = [
|
||||
'keywords',
|
||||
'keyphrases',
|
||||
'page_analysis',
|
||||
'primary_term',
|
||||
'og_article_tags',
|
||||
'schema',
|
||||
'options',
|
||||
'open_ai',
|
||||
'videos'
|
||||
];
|
||||
|
||||
foreach ( $rows as $row ) {
|
||||
$row = str_replace( '\\""', '\\"', $row );
|
||||
$row = str_getcsv( $row, ',' );
|
||||
|
||||
foreach ( $row as $key => $value ) {
|
||||
$key = aioseo()->helpers->sanitizeOption( $key );
|
||||
|
||||
if ( ! empty( $value ) && in_array( $header[ $key ], $jsonFields, true ) && ! aioseo()->helpers->isJsonString( $value ) ) {
|
||||
continue;
|
||||
} elseif ( '' === trim( $value ) ) {
|
||||
$value = null;
|
||||
}
|
||||
|
||||
$content[ $header [ $key ] ] = $value;
|
||||
}
|
||||
$newContent['postOptions']['content'][ $type ][] = $content;
|
||||
}
|
||||
|
||||
return $newContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function exportSettings( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$settings = ! empty( $body['settings'] ) ? $body['settings'] : [];
|
||||
$allSettings = [
|
||||
'settings' => []
|
||||
];
|
||||
|
||||
if ( empty( $settings ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$options = aioseo()->options->noConflict();
|
||||
$dynamicOptions = aioseo()->dynamicOptions->noConflict();
|
||||
$notAllowedOptions = aioseo()->access->getNotAllowedOptions();
|
||||
foreach ( $settings as $setting ) {
|
||||
$optionAccess = in_array( $setting, [ 'robots', 'blocker' ], true ) ? 'tools' : $setting;
|
||||
|
||||
if ( in_array( $optionAccess, $notAllowedOptions, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ( $setting ) {
|
||||
case 'robots':
|
||||
$allSettings['settings']['tools']['robots'] = $options->tools->robots->all();
|
||||
// Search Appearance settings that are also found in the robots settings.
|
||||
if ( empty( $allSettings['settings']['searchAppearance']['advanced'] ) ) {
|
||||
$allSettings['settings']['searchAppearance']['advanced'] = [
|
||||
'unwantedBots' => $options->searchAppearance->advanced->unwantedBots->all(),
|
||||
'searchCleanup' => [
|
||||
'settings' => [
|
||||
'preventCrawling' => $options->searchAppearance->advanced->searchCleanup->settings->preventCrawling
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ( $options->has( $setting ) ) {
|
||||
$allSettings['settings'][ $setting ] = $options->$setting->all();
|
||||
}
|
||||
|
||||
// If there are related dynamic settings, let's include them.
|
||||
if ( $dynamicOptions->has( $setting ) ) {
|
||||
$allSettings['settings']['dynamic'][ $setting ] = $dynamicOptions->$setting->all();
|
||||
}
|
||||
|
||||
// It there is a related deprecated $setting, include it.
|
||||
if ( $options->deprecated->has( $setting ) ) {
|
||||
$allSettings['settings']['deprecated'][ $setting ] = $options->deprecated->$setting->all();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'settings' => $allSettings
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Export post data.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request.
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function exportContent( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$postOptions = $body['postOptions'] ?? [];
|
||||
$typeFile = $body['typeFile'] ?? false;
|
||||
$siteId = (int) ( $body['siteId'] ?? get_current_blog_id() );
|
||||
$contentPostType = null;
|
||||
$return = true;
|
||||
|
||||
try {
|
||||
aioseo()->helpers->switchToBlog( $siteId );
|
||||
|
||||
// Get settings from post types selected.
|
||||
if ( ! empty( $postOptions ) ) {
|
||||
$fieldsToExclude = [
|
||||
'seo_score' => '',
|
||||
'schema_type' => '',
|
||||
'schema_type_options' => '',
|
||||
'images' => '',
|
||||
'image_scan_date' => '',
|
||||
'videos' => '',
|
||||
'video_thumbnail' => '',
|
||||
'video_scan_date' => '',
|
||||
'link_scan_date' => '',
|
||||
'link_suggestions_scan_date' => '',
|
||||
'local_seo' => '',
|
||||
'options' => '',
|
||||
'open_ai' => ''
|
||||
];
|
||||
|
||||
$notAllowed = array_merge( aioseo()->access->getNotAllowedPageFields(), $fieldsToExclude );
|
||||
$posts = self::getPostTypesData( $postOptions, $notAllowed );
|
||||
|
||||
// Generate content to CSV or JSON.
|
||||
if ( ! empty( $posts ) ) {
|
||||
// Change the order of keys so the post_title shows up at the beginning.
|
||||
$data = [];
|
||||
foreach ( $posts as $p ) {
|
||||
$item = [
|
||||
'id' => '',
|
||||
'post_id' => '',
|
||||
'post_title' => '',
|
||||
'title' => ''
|
||||
];
|
||||
|
||||
$p['title'] = aioseo()->helpers->decodeHtmlEntities( $p['title'] );
|
||||
$p['post_title'] = aioseo()->helpers->decodeHtmlEntities( $p['post_title'] );
|
||||
|
||||
$data[] = array_merge( $item, $p );
|
||||
}
|
||||
|
||||
if ( 'csv' === $typeFile ) {
|
||||
$contentPostType = self::dataToCsv( $data );
|
||||
}
|
||||
|
||||
if ( 'json' === $typeFile ) {
|
||||
$contentPostType['postOptions']['content']['posts'] = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch ( \Throwable $th ) {
|
||||
$return = false;
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => $return,
|
||||
'postTypeData' => $contentPostType
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the posts of specific post types.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param array $postOptions The post types to get data from.
|
||||
* @param array $notAllowedFields An array of fields not allowed to be returned.
|
||||
* @return array The posts.
|
||||
*/
|
||||
private static function getPostTypesData( $postOptions, $notAllowedFields = [] ) {
|
||||
$posts = aioseo()->core->db->start( 'aioseo_posts as ap' )
|
||||
->select( 'ap.*, p.post_title' )
|
||||
->join( 'posts as p', 'ap.post_id = p.ID' )
|
||||
->whereIn( 'p.post_type', $postOptions )
|
||||
->orderBy( 'ap.id' )
|
||||
->run()
|
||||
->result();
|
||||
|
||||
if ( ! empty( $notAllowedFields ) ) {
|
||||
foreach ( $posts as $key => &$p ) {
|
||||
$p = array_diff_key( (array) $p, $notAllowedFields );
|
||||
if ( count( $p ) <= 2 ) {
|
||||
unset( $posts[ $key ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CSV string.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param array $data An array of data to transform into a CSV.
|
||||
* @return string The CSV string.
|
||||
*/
|
||||
public static function dataToCsv( $data ) {
|
||||
// Get the header row.
|
||||
$csvString = implode( ',', array_keys( (array) $data[0] ) ) . "\r\n";
|
||||
|
||||
// Get the content rows.
|
||||
foreach ( $data as $row ) {
|
||||
$row = (array) $row;
|
||||
foreach ( $row as &$value ) {
|
||||
if ( aioseo()->helpers->isJsonString( $value ) ) {
|
||||
$value = '"' . str_replace( '"', '""', $value ) . '"';
|
||||
} elseif ( false !== strpos( (string) $value, ',' ) ) {
|
||||
$value = '"' . $value . '"';
|
||||
}
|
||||
}
|
||||
|
||||
$csvString .= implode( ',', $row ) . "\r\n";
|
||||
}
|
||||
|
||||
return $csvString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import other plugin settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function importPlugins( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$plugins = ! empty( $body['plugins'] ) ? $body['plugins'] : [];
|
||||
|
||||
foreach ( $plugins as $plugin ) {
|
||||
aioseo()->importExport->startImport( $plugin['plugin'], $plugin['settings'] );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a given administrative task.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function doTask( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$action = ! empty( $body['action'] ) ? $body['action'] : '';
|
||||
$data = ! empty( $body['data'] ) ? $body['data'] : [];
|
||||
$network = ! empty( $body['network'] ) ? boolval( $body['network'] ) : false;
|
||||
$siteId = ! empty( $body['siteId'] ) ? intval( $body['siteId'] ) : false;
|
||||
$siteOrNetwork = empty( $siteId ) ? aioseo()->helpers->getNetworkId() : $siteId; // If we don't have a siteId, we will use the networkId.
|
||||
|
||||
// When on network admin page and no siteId, it is supposed to perform on network level.
|
||||
if ( $network && 'clear-cache' === $action && empty( $siteId ) ) {
|
||||
aioseo()->core->networkCache->clear();
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
// Switch to the right blog before processing any task.
|
||||
aioseo()->helpers->switchToBlog( $siteOrNetwork );
|
||||
|
||||
switch ( $action ) {
|
||||
// General
|
||||
case 'clear-cache':
|
||||
aioseo()->core->cache->clear();
|
||||
break;
|
||||
case 'clear-plugin-updates-transient':
|
||||
delete_site_transient( 'update_plugins' );
|
||||
break;
|
||||
case 'readd-capabilities':
|
||||
aioseo()->access->addCapabilities();
|
||||
break;
|
||||
case 'reset-data':
|
||||
aioseo()->uninstall->dropData( true );
|
||||
aioseo()->internalOptions->database->installedTables = '';
|
||||
aioseo()->internalOptions->internal->lastActiveVersion = '4.0.0';
|
||||
aioseo()->internalOptions->save( true );
|
||||
aioseo()->updates->addInitialCustomTablesForV4();
|
||||
break;
|
||||
// Sitemap
|
||||
case 'clear-image-data':
|
||||
aioseo()->sitemap->query->resetImages();
|
||||
break;
|
||||
// Migrations
|
||||
case 'rerun-migrations':
|
||||
aioseo()->internalOptions->database->installedTables = '';
|
||||
aioseo()->internalOptions->internal->lastActiveVersion = '4.0.0';
|
||||
aioseo()->internalOptions->save( true );
|
||||
break;
|
||||
case 'rerun-addon-migrations':
|
||||
aioseo()->internalOptions->database->installedTables = '';
|
||||
|
||||
foreach ( $data as $sku ) {
|
||||
$convertedSku = aioseo()->helpers->dashesToCamelCase( $sku );
|
||||
if (
|
||||
function_exists( $convertedSku ) &&
|
||||
isset( $convertedSku()->internalOptions )
|
||||
) {
|
||||
$convertedSku()->internalOptions->internal->lastActiveVersion = '0.0';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'restart-v3-migration':
|
||||
Migration\Helpers::redoMigration();
|
||||
break;
|
||||
// Old Issues
|
||||
case 'remove-duplicates':
|
||||
aioseo()->updates->removeDuplicateRecords();
|
||||
break;
|
||||
case 'unescape-data':
|
||||
aioseo()->admin->scheduleUnescapeData();
|
||||
break;
|
||||
// Deprecated Options
|
||||
case 'deprecated-options':
|
||||
// Check if the user is forcefully wanting to add a deprecated option.
|
||||
$allDeprecatedOptions = aioseo()->internalOptions->getAllDeprecatedOptions() ?: [];
|
||||
$enableOptions = array_keys( array_filter( $data ) );
|
||||
$enabledDeprecated = array_intersect( $allDeprecatedOptions, $enableOptions );
|
||||
|
||||
aioseo()->internalOptions->internal->deprecatedOptions = array_values( $enabledDeprecated );
|
||||
aioseo()->internalOptions->save( true );
|
||||
break;
|
||||
case 'aioseo-reset-seoboost-logins':
|
||||
aioseo()->writingAssistant->seoBoost->resetLogins();
|
||||
break;
|
||||
default:
|
||||
aioseo()->helpers->restoreCurrentBlog();
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'error' => 'The given action isn\'t defined.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
// Revert back to the current blog after processing to avoid conflict with other actions.
|
||||
aioseo()->helpers->restoreCurrentBlog();
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Change Sem Rush Focus Keyphrase default country.
|
||||
*
|
||||
* @since 4.7.5
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function changeSemrushCountry( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$country = ! empty( $body['value'] ) ? sanitize_text_field( $body['value'] ) : 'US';
|
||||
|
||||
aioseo()->settings->semrushCountry = $country;
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Sitemaps {
|
||||
/**
|
||||
* Delete all static sitemap files.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function deleteStaticFiles() {
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
$files = list_files( get_home_path(), 1 );
|
||||
if ( ! count( $files ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$isGeneralSitemapStatic = aioseo()->options->sitemap->general->advancedSettings->enable &&
|
||||
in_array( 'staticSitemap', aioseo()->internalOptions->internal->deprecatedOptions, true ) &&
|
||||
! aioseo()->options->deprecated->sitemap->general->advancedSettings->dynamic;
|
||||
|
||||
$detectedFiles = [];
|
||||
if ( ! $isGeneralSitemapStatic ) {
|
||||
foreach ( $files as $filename ) {
|
||||
if ( preg_match( '#.*sitemap.*#', (string) $filename ) ) {
|
||||
// We don't want to delete the video sitemap here at all.
|
||||
$isVideoSitemap = preg_match( '#.*video.*#', (string) $filename ) ? true : false;
|
||||
if ( ! $isVideoSitemap ) {
|
||||
$detectedFiles[] = $filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! count( $detectedFiles ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'No sitemap files found.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$fs = aioseo()->core->fs;
|
||||
if ( ! $fs->isWpfsValid() ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'No access to filesystem.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
foreach ( $detectedFiles as $file ) {
|
||||
$fs->fs->delete( $file, false, 'f' );
|
||||
}
|
||||
|
||||
Models\Notification::deleteNotificationByName( 'sitemap-static-files' );
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'notifications' => Models\Notification::getNotifications()
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates conflicting plugins.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function deactivateConflictingPlugins() {
|
||||
$error = esc_html__( 'Deactivation failed. Please check permissions and try again.', 'all-in-one-seo-pack' );
|
||||
if ( ! current_user_can( 'install_plugins' ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $error
|
||||
], 400 );
|
||||
}
|
||||
|
||||
aioseo()->conflictingPlugins->deactivateConflictingPlugins( [ 'seo', 'sitemap' ] );
|
||||
|
||||
Models\Notification::deleteNotificationByName( 'conflicting-plugins' );
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'notifications' => Models\Notification::getNotifications()
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the slug for the HTML sitemap is not in use.
|
||||
*
|
||||
* @since 4.1.3
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function validateHtmlSitemapSlug( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
|
||||
$pageUrl = ! empty( $body['pageUrl'] ) ? sanitize_text_field( $body['pageUrl'] ) : '';
|
||||
if ( empty( $pageUrl ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'No path was provided.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$parsedPageUrl = wp_parse_url( $pageUrl );
|
||||
if ( empty( $parsedPageUrl['path'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'The given path is invalid.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$isUrl = aioseo()->helpers->isUrl( $pageUrl );
|
||||
$isInternalUrl = aioseo()->helpers->isInternalUrl( $pageUrl );
|
||||
if ( $isUrl && ! $isInternalUrl ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'The given URL is not a valid internal URL.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$pathExists = self::pathExists( $parsedPageUrl['path'], false );
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'exists' => $pathExists
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given path is unique or not.
|
||||
*
|
||||
* @since 4.1.4
|
||||
* @version 4.2.6
|
||||
*
|
||||
* @param string $path The path.
|
||||
* @param bool $path Whether the given path is a URL.
|
||||
* @return boolean Whether the path exists.
|
||||
*/
|
||||
private static function pathExists( $path, $isUrl ) {
|
||||
$path = trim( aioseo()->helpers->excludeHomePath( $path ), '/' );
|
||||
$url = $isUrl ? $path : trailingslashit( home_url() ) . $path;
|
||||
$url = user_trailingslashit( $url );
|
||||
|
||||
// Let's do another check here, just to be sure that the domain matches.
|
||||
if ( ! aioseo()->helpers->isInternalUrl( $url ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$response = wp_safe_remote_head( $url );
|
||||
$status = wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( ! $status ) {
|
||||
// If there is no status code, we might be in a local environment with CURL misconfigured.
|
||||
// In that case we can still check if a post exists for the path by quering the DB.
|
||||
$post = aioseo()->helpers->getPostbyPath(
|
||||
$path,
|
||||
OBJECT,
|
||||
aioseo()->helpers->getPublicPostTypes( true )
|
||||
);
|
||||
|
||||
return is_object( $post );
|
||||
}
|
||||
|
||||
return 200 === $status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Tags {
|
||||
/**
|
||||
* Get all Tags.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function getTags() {
|
||||
return new \WP_REST_Response( aioseo()->tags->all( true ), 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
use AIOSEO\Plugin\Common\Tools as CommonTools;
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Tools {
|
||||
/**
|
||||
* Import contents from a robots.txt url, static file or pasted text.
|
||||
*
|
||||
* @since 4.0.0
|
||||
* @version 4.4.2
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function importRobotsTxt( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$blogId = ! empty( $body['blogId'] ) ? $body['blogId'] : 0;
|
||||
$source = ! empty( $body['source'] ) ? $body['source'] : '';
|
||||
$text = ! empty( $body['text'] ) ? sanitize_textarea_field( $body['text'] ) : '';
|
||||
$url = ! empty( $body['url'] ) ? sanitize_url( $body['url'], [ 'http', 'https' ] ) : '';
|
||||
|
||||
try {
|
||||
if ( is_multisite() && 'network' !== $blogId ) {
|
||||
aioseo()->helpers->switchToBlog( $blogId );
|
||||
}
|
||||
|
||||
switch ( $source ) {
|
||||
case 'url':
|
||||
aioseo()->robotsTxt->importRobotsTxtFromUrl( $url, $blogId );
|
||||
|
||||
break;
|
||||
case 'text':
|
||||
aioseo()->robotsTxt->importRobotsTxtFromText( $text, $blogId );
|
||||
|
||||
break;
|
||||
case 'static':
|
||||
default:
|
||||
aioseo()->robotsTxt->importPhysicalRobotsTxt( $blogId );
|
||||
aioseo()->robotsTxt->deletePhysicalRobotsTxt();
|
||||
|
||||
$options = aioseo()->options;
|
||||
if ( 'network' === $blogId ) {
|
||||
$options = aioseo()->networkOptions;
|
||||
}
|
||||
|
||||
$options->tools->robots->enable = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'notifications' => Models\Notification::getNotifications()
|
||||
], 200 );
|
||||
} catch ( \Exception $e ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
], 400 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the static robots.txt file.
|
||||
*
|
||||
* @since 4.0.0
|
||||
* @version 4.4.5
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function deleteRobotsTxt() {
|
||||
try {
|
||||
aioseo()->robotsTxt->deletePhysicalRobotsTxt();
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'notifications' => Models\Notification::getNotifications()
|
||||
], 200 );
|
||||
} catch ( \Exception $e ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
], 400 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Email debug info.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function emailDebugInfo( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$email = ! empty( $body['email'] ) ? $body['email'] : null;
|
||||
|
||||
if ( ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'invalid-email-address'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/update.php';
|
||||
|
||||
// Translators: 1 - The plugin name ("All in One SEO"), 2 - The Site URL.
|
||||
$html = sprintf( __( '%1$s Debug Info from %2$s', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_NAME, aioseo()->helpers->getSiteDomain() ) . "\r\n------------------\r\n\r\n";
|
||||
$info = CommonTools\SystemStatus::getSystemStatusInfo();
|
||||
foreach ( $info as $group ) {
|
||||
if ( empty( $group['results'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$html .= "\r\n\r\n{$group['label']}\r\n";
|
||||
foreach ( $group['results'] as $data ) {
|
||||
$html .= "{$data['header']}: {$data['value']}\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! wp_mail(
|
||||
$email,
|
||||
// Translators: 1 - The plugin name ("All in One SEO).
|
||||
sprintf( __( '%1$s Debug Info', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_NAME ),
|
||||
$html
|
||||
) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'Unable to send debug email, please check your email send settings and try again.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a settings backup.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function createBackup( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
aioseo()->backup->create();
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'backups' => array_reverse( aioseo()->backup->all() )
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a settings backup.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function restoreBackup( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$backup = ! empty( $body['backup'] ) ? (int) $body['backup'] : null;
|
||||
if ( empty( $backup ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'backups' => array_reverse( aioseo()->backup->all() )
|
||||
], 400 );
|
||||
}
|
||||
|
||||
aioseo()->backup->restore( $backup );
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'backups' => array_reverse( aioseo()->backup->all() ),
|
||||
'options' => aioseo()->options->all(),
|
||||
'internalOptions' => aioseo()->internalOptions->all()
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a settings backup.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function deleteBackup( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$backup = ! empty( $body['backup'] ) ? (int) $body['backup'] : null;
|
||||
if ( empty( $backup ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'backups' => array_reverse( aioseo()->backup->all() )
|
||||
], 400 );
|
||||
}
|
||||
|
||||
aioseo()->backup->delete( $backup );
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'backups' => array_reverse( aioseo()->backup->all() )
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the .htaccess file.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function saveHtaccess( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$htaccess = ! empty( $body['htaccess'] ) ? sanitize_textarea_field( $body['htaccess'] ) : '';
|
||||
|
||||
if ( empty( $htaccess ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => __( '.htaccess file is empty.', 'all-in-one-seo-pack' )
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$htaccess = aioseo()->helpers->decodeHtmlEntities( $htaccess );
|
||||
$saveHtaccess = (object) aioseo()->htaccess->saveContents( $htaccess );
|
||||
if ( ! $saveHtaccess->success ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => $saveHtaccess->message ? $saveHtaccess->message : __( 'An error occurred while trying to write to the .htaccess file. Please try again later.', 'all-in-one-seo-pack' ),
|
||||
'reason' => $saveHtaccess->reason
|
||||
], 400 );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the passed in log.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function clearLog( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$log = ! empty( $body['log'] ) ? $body['log'] : null;
|
||||
|
||||
$logSize = 0;
|
||||
switch ( $log ) {
|
||||
case 'badBotBlockerLog':
|
||||
aioseo()->badBotBlocker->clearLog();
|
||||
$logSize = aioseo()->badBotBlocker->getLogSize();
|
||||
break;
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'logSize' => aioseo()->helpers->convertFileSize( $logSize )
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles user related API routes.
|
||||
*
|
||||
* @since 4.2.8
|
||||
*/
|
||||
class User {
|
||||
/**
|
||||
* Get the user image.
|
||||
*
|
||||
* @since 4.2.8
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function getUserImage( $request ) {
|
||||
$args = $request->get_params();
|
||||
|
||||
if ( empty( $args['userId'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => 'No user ID was provided.'
|
||||
], 400 );
|
||||
}
|
||||
|
||||
$url = get_avatar_url( $args['userId'] );
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'url' => is_array( $url ) ? $url[0] : $url,
|
||||
], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,483 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Wizard {
|
||||
/**
|
||||
* Save the wizard information.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function saveWizard( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$section = ! empty( $body['section'] ) ? sanitize_text_field( $body['section'] ) : null;
|
||||
$wizard = ! empty( $body['wizard'] ) ? $body['wizard'] : null;
|
||||
$network = ! empty( $body['network'] ) ? $body['network'] : false;
|
||||
$options = aioseo()->options->noConflict();
|
||||
$dynamicOptions = aioseo()->dynamicOptions->noConflict();
|
||||
|
||||
aioseo()->internalOptions->internal->wizard = wp_json_encode( $wizard );
|
||||
|
||||
// Process the importers.
|
||||
if ( 'importers' === $section && ! empty( $wizard['importers'] ) ) {
|
||||
$importers = $wizard['importers'];
|
||||
|
||||
try {
|
||||
foreach ( $importers as $plugin ) {
|
||||
aioseo()->importExport->startImport( $plugin, [
|
||||
'settings',
|
||||
'postMeta',
|
||||
'termMeta'
|
||||
] );
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
// Import failed. Let's create a notification but move on.
|
||||
$notification = Models\Notification::getNotificationByName( 'import-failed' );
|
||||
if ( ! $notification->exists() ) {
|
||||
Models\Notification::addNotification( [
|
||||
'slug' => uniqid(),
|
||||
'notification_name' => 'import-failed',
|
||||
'title' => __( 'SEO Plugin Import Failed', 'all-in-one-seo-pack' ),
|
||||
'content' => __( 'Unfortunately, there was an error importing your SEO plugin settings. This could be due to an incompatibility in the version installed. Make sure you are on the latest version of the plugin and try again.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
'type' => 'error',
|
||||
'level' => [ 'all' ],
|
||||
'button1_label' => __( 'Try Again', 'all-in-one-seo-pack' ),
|
||||
'button1_action' => 'http://route#aioseo-tools&aioseo-scroll=aioseo-import-others&aioseo-highlight=aioseo-import-others:import-export',
|
||||
'start' => gmdate( 'Y-m-d H:i:s' )
|
||||
] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save the category section.
|
||||
if (
|
||||
( 'category' === $section || 'searchAppearance' === $section ) && // We allow the user to update the site title/description in search appearance.
|
||||
! empty( $wizard['category'] )
|
||||
) {
|
||||
$category = $wizard['category'];
|
||||
if ( ! empty( $category['category'] ) ) {
|
||||
aioseo()->internalOptions->internal->category = $category['category'];
|
||||
}
|
||||
|
||||
if ( ! empty( $category['categoryOther'] ) ) {
|
||||
aioseo()->internalOptions->internal->categoryOther = $category['categoryOther'];
|
||||
}
|
||||
|
||||
// If the home page is a static page, let's find and set that,
|
||||
// otherwise set our home page settings.
|
||||
$staticHomePage = 'page' === get_option( 'show_on_front' ) ? get_post( get_option( 'page_on_front' ) ) : null;
|
||||
if ( ! empty( $staticHomePage ) ) {
|
||||
$update = false;
|
||||
$page = Models\Post::getPost( $staticHomePage->ID );
|
||||
if ( ! empty( $category['siteTitle'] ) ) {
|
||||
$update = true;
|
||||
$page->title = $category['siteTitle'];
|
||||
}
|
||||
|
||||
if ( ! empty( $category['metaDescription'] ) ) {
|
||||
$update = true;
|
||||
$page->description = $category['metaDescription'];
|
||||
}
|
||||
|
||||
if ( $update ) {
|
||||
$page->save();
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $staticHomePage ) ) {
|
||||
if ( ! empty( $category['siteTitle'] ) ) {
|
||||
$options->searchAppearance->global->siteTitle = $category['siteTitle'];
|
||||
}
|
||||
|
||||
if ( ! empty( $category['metaDescription'] ) ) {
|
||||
$options->searchAppearance->global->metaDescription = $category['metaDescription'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save the additional information section.
|
||||
if ( 'additionalInformation' === $section && ! empty( $wizard['additionalInformation'] ) ) {
|
||||
$additionalInformation = $wizard['additionalInformation'];
|
||||
if ( ! empty( $additionalInformation['siteRepresents'] ) ) {
|
||||
$options->searchAppearance->global->schema->siteRepresents = $additionalInformation['siteRepresents'];
|
||||
}
|
||||
|
||||
if ( ! empty( $additionalInformation['person'] ) ) {
|
||||
$options->searchAppearance->global->schema->person = $additionalInformation['person'];
|
||||
}
|
||||
|
||||
if ( ! empty( $additionalInformation['organizationName'] ) ) {
|
||||
$options->searchAppearance->global->schema->organizationName = $additionalInformation['organizationName'];
|
||||
}
|
||||
|
||||
if ( ! empty( $additionalInformation['organizationDescription'] ) ) {
|
||||
$options->searchAppearance->global->schema->organizationDescription = $additionalInformation['organizationDescription'];
|
||||
}
|
||||
|
||||
if ( ! empty( $additionalInformation['phone'] ) ) {
|
||||
$options->searchAppearance->global->schema->phone = $additionalInformation['phone'];
|
||||
}
|
||||
|
||||
if ( ! empty( $additionalInformation['organizationLogo'] ) ) {
|
||||
$options->searchAppearance->global->schema->organizationLogo = $additionalInformation['organizationLogo'];
|
||||
}
|
||||
|
||||
if ( ! empty( $additionalInformation['personName'] ) ) {
|
||||
$options->searchAppearance->global->schema->personName = $additionalInformation['personName'];
|
||||
}
|
||||
|
||||
if ( ! empty( $additionalInformation['personLogo'] ) ) {
|
||||
$options->searchAppearance->global->schema->personLogo = $additionalInformation['personLogo'];
|
||||
}
|
||||
|
||||
if ( ! empty( $additionalInformation['socialShareImage'] ) ) {
|
||||
$options->social->facebook->general->defaultImagePosts = $additionalInformation['socialShareImage'];
|
||||
$options->social->twitter->general->defaultImagePosts = $additionalInformation['socialShareImage'];
|
||||
}
|
||||
|
||||
if ( ! empty( $additionalInformation['social'] ) && ! empty( $additionalInformation['social']['profiles'] ) ) {
|
||||
$profiles = $additionalInformation['social']['profiles'];
|
||||
if ( ! empty( $profiles['sameUsername'] ) ) {
|
||||
$sameUsername = $profiles['sameUsername'];
|
||||
if ( isset( $sameUsername['enable'] ) ) {
|
||||
$options->social->profiles->sameUsername->enable = $sameUsername['enable'];
|
||||
}
|
||||
|
||||
if ( ! empty( $sameUsername['username'] ) ) {
|
||||
$options->social->profiles->sameUsername->username = $sameUsername['username'];
|
||||
}
|
||||
|
||||
if ( ! empty( $sameUsername['included'] ) ) {
|
||||
$options->social->profiles->sameUsername->included = $sameUsername['included'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $profiles['urls'] ) ) {
|
||||
$urls = $profiles['urls'];
|
||||
if ( ! empty( $urls['facebookPageUrl'] ) ) {
|
||||
$options->social->profiles->urls->facebookPageUrl = $urls['facebookPageUrl'];
|
||||
}
|
||||
|
||||
if ( ! empty( $urls['twitterUrl'] ) ) {
|
||||
$options->social->profiles->urls->twitterUrl = $urls['twitterUrl'];
|
||||
}
|
||||
|
||||
if ( ! empty( $urls['instagramUrl'] ) ) {
|
||||
$options->social->profiles->urls->instagramUrl = $urls['instagramUrl'];
|
||||
}
|
||||
|
||||
if ( ! empty( $urls['tiktokUrl'] ) ) {
|
||||
$options->social->profiles->urls->tiktokUrl = $urls['tiktokUrl'];
|
||||
}
|
||||
|
||||
if ( ! empty( $urls['pinterestUrl'] ) ) {
|
||||
$options->social->profiles->urls->pinterestUrl = $urls['pinterestUrl'];
|
||||
}
|
||||
|
||||
if ( ! empty( $urls['youtubeUrl'] ) ) {
|
||||
$options->social->profiles->urls->youtubeUrl = $urls['youtubeUrl'];
|
||||
}
|
||||
|
||||
if ( ! empty( $urls['linkedinUrl'] ) ) {
|
||||
$options->social->profiles->urls->linkedinUrl = $urls['linkedinUrl'];
|
||||
}
|
||||
|
||||
if ( ! empty( $urls['tumblrUrl'] ) ) {
|
||||
$options->social->profiles->urls->tumblrUrl = $urls['tumblrUrl'];
|
||||
}
|
||||
|
||||
if ( ! empty( $urls['yelpPageUrl'] ) ) {
|
||||
$options->social->profiles->urls->yelpPageUrl = $urls['yelpPageUrl'];
|
||||
}
|
||||
|
||||
if ( ! empty( $urls['soundCloudUrl'] ) ) {
|
||||
$options->social->profiles->urls->soundCloudUrl = $urls['soundCloudUrl'];
|
||||
}
|
||||
|
||||
if ( ! empty( $urls['wikipediaUrl'] ) ) {
|
||||
$options->social->profiles->urls->wikipediaUrl = $urls['wikipediaUrl'];
|
||||
}
|
||||
|
||||
if ( ! empty( $urls['myspaceUrl'] ) ) {
|
||||
$options->social->profiles->urls->myspaceUrl = $urls['myspaceUrl'];
|
||||
}
|
||||
|
||||
if ( ! empty( $urls['googlePlacesUrl'] ) ) {
|
||||
$options->social->profiles->urls->googlePlacesUrl = $urls['googlePlacesUrl'];
|
||||
}
|
||||
|
||||
if ( ! empty( $urls['wordPressUrl'] ) ) {
|
||||
$options->social->profiles->urls->wordPressUrl = $urls['wordPressUrl'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true
|
||||
], 200 );
|
||||
}
|
||||
|
||||
// Save the features section.
|
||||
if ( 'features' === $section && ! empty( $wizard['features'] ) ) {
|
||||
self::installPlugins( $wizard['features'], $network );
|
||||
|
||||
if ( in_array( 'email-reports', $wizard['features'], true ) ) {
|
||||
$options->advanced->emailSummary->enable = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the search appearance section.
|
||||
if ( 'searchAppearance' === $section && ! empty( $wizard['searchAppearance'] ) ) {
|
||||
$searchAppearance = $wizard['searchAppearance'];
|
||||
|
||||
if ( isset( $searchAppearance['underConstruction'] ) ) {
|
||||
update_option( 'blog_public', ! $searchAppearance['underConstruction'] );
|
||||
}
|
||||
|
||||
if (
|
||||
! empty( $searchAppearance['postTypes'] ) &&
|
||||
! empty( $searchAppearance['postTypes']['postTypes'] )
|
||||
) {
|
||||
// Robots.
|
||||
if ( ! empty( $searchAppearance['postTypes']['postTypes']['all'] ) ) {
|
||||
foreach ( aioseo()->helpers->getPublicPostTypes( true ) as $postType ) {
|
||||
if ( $dynamicOptions->searchAppearance->postTypes->has( $postType ) ) {
|
||||
$dynamicOptions->searchAppearance->postTypes->$postType->show = true;
|
||||
$dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->default = true;
|
||||
$dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->noindex = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ( aioseo()->helpers->getPublicPostTypes( true ) as $postType ) {
|
||||
if ( $dynamicOptions->searchAppearance->postTypes->has( $postType ) ) {
|
||||
if ( in_array( $postType, (array) $searchAppearance['postTypes']['postTypes']['included'], true ) ) {
|
||||
$dynamicOptions->searchAppearance->postTypes->$postType->show = true;
|
||||
$dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->default = true;
|
||||
$dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->noindex = false;
|
||||
} else {
|
||||
$dynamicOptions->searchAppearance->postTypes->$postType->show = false;
|
||||
$dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->default = false;
|
||||
$dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->noindex = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sitemaps.
|
||||
if ( isset( $searchAppearance['postTypes']['postTypes']['all'] ) ) {
|
||||
$options->sitemap->general->postTypes->all = $searchAppearance['postTypes']['postTypes']['all'];
|
||||
}
|
||||
|
||||
if ( isset( $searchAppearance['postTypes']['postTypes']['included'] ) ) {
|
||||
$options->sitemap->general->postTypes->included = $searchAppearance['postTypes']['postTypes']['included'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $searchAppearance['multipleAuthors'] ) ) {
|
||||
$options->searchAppearance->archives->author->show = $searchAppearance['multipleAuthors'];
|
||||
$options->searchAppearance->archives->author->advanced->robotsMeta->default = $searchAppearance['multipleAuthors'];
|
||||
$options->searchAppearance->archives->author->advanced->robotsMeta->noindex = ! $searchAppearance['multipleAuthors'];
|
||||
}
|
||||
|
||||
if ( isset( $searchAppearance['redirectAttachmentPages'] ) && $dynamicOptions->searchAppearance->postTypes->has( 'attachment' ) ) {
|
||||
$dynamicOptions->searchAppearance->postTypes->attachment->redirectAttachmentUrls = $searchAppearance['redirectAttachmentPages'] ? 'attachment' : 'disabled';
|
||||
}
|
||||
|
||||
if ( isset( $searchAppearance['emailReports'] ) ) {
|
||||
$options->advanced->emailSummary->enable = $searchAppearance['emailReports'];
|
||||
}
|
||||
}
|
||||
|
||||
// Save the smart recommendations section.
|
||||
if ( 'smartRecommendations' === $section && ! empty( $wizard['smartRecommendations'] ) ) {
|
||||
$smartRecommendations = $wizard['smartRecommendations'];
|
||||
if ( ! empty( $smartRecommendations['accountInfo'] ) && ! aioseo()->internalOptions->internal->siteAnalysis->connectToken ) {
|
||||
$url = defined( 'AIOSEO_CONNECT_DIRECT_URL' ) ? AIOSEO_CONNECT_DIRECT_URL : 'https://aioseo.com/wp-json/aioseo-lite-connect/v1/connect/';
|
||||
$response = wp_remote_post( $url, [
|
||||
'timeout' => 10,
|
||||
'headers' => array_merge( [
|
||||
'Content-Type' => 'application/json'
|
||||
], aioseo()->helpers->getApiHeaders() ),
|
||||
'user-agent' => aioseo()->helpers->getApiUserAgent(),
|
||||
'body' => wp_json_encode( [
|
||||
'accountInfo' => $smartRecommendations['accountInfo'],
|
||||
'homeurl' => home_url()
|
||||
] )
|
||||
] );
|
||||
|
||||
$token = json_decode( wp_remote_retrieve_body( $response ) );
|
||||
if ( ! empty( $token->token ) ) {
|
||||
aioseo()->internalOptions->internal->siteAnalysis->connectToken = $token->token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'options' => aioseo()->options->all()
|
||||
], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Install all plugins that were selected in the features page of the Setup Wizard.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @param array $features The features that were selected.
|
||||
* @param bool $network Whether to install the plugins on the network.
|
||||
* @return void
|
||||
*/
|
||||
private static function installPlugins( $features, $network ) {
|
||||
$pluginData = aioseo()->helpers->getPluginData();
|
||||
|
||||
if ( in_array( 'analytics', $features, true ) ) {
|
||||
self::installMonsterInsights( $network );
|
||||
}
|
||||
|
||||
if ( in_array( 'conversion-tools', $features, true ) && ! $pluginData['optinMonster']['activated'] ) {
|
||||
self::installOptinMonster( $network );
|
||||
}
|
||||
|
||||
if ( in_array( 'broken-link-checker', $features, true ) && ! $pluginData['brokenLinkChecker']['activated'] ) {
|
||||
self::installBlc( $network );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the MonsterInsights plugin.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @param bool $network Whether to install the plugin on the network.
|
||||
* @return void
|
||||
*/
|
||||
private static function installMonsterInsights( $network ) {
|
||||
$pluginData = aioseo()->helpers->getPluginData();
|
||||
|
||||
$args = [
|
||||
'id' => 'miLite',
|
||||
'pluginName' => 'MonsterInsights',
|
||||
'pluginLongName' => 'MonsterInsights Analytics',
|
||||
'notification-name' => 'install-mi'
|
||||
];
|
||||
|
||||
// If MI Pro is active, bail.
|
||||
if ( $pluginData['miPro']['activated'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If MI Pro is installed but not active, activate MI Pro.
|
||||
if ( $pluginData['miPro']['installed'] ) {
|
||||
$args['id'] = 'miPro';
|
||||
}
|
||||
|
||||
if ( self::installPlugin( $args, $network ) ) {
|
||||
delete_transient( '_monsterinsights_activation_redirect' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the OptinMonster plugin.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @param bool $network Whether to install the plugin on the network.
|
||||
* @return void
|
||||
*/
|
||||
private static function installOptinMonster( $network ) {
|
||||
$args = [
|
||||
'id' => 'optinMonster',
|
||||
'pluginName' => 'OptinMonster',
|
||||
'pluginLongName' => 'OptinMonster Conversion Tools',
|
||||
'notification-name' => 'install-om'
|
||||
];
|
||||
|
||||
if ( self::installPlugin( $args, $network ) ) {
|
||||
delete_transient( 'optin_monster_api_activation_redirect' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the Broken Link Checker plugin.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @param bool $network Whether to install the plugin on the network.
|
||||
* @return void
|
||||
*/
|
||||
private static function installBlc( $network ) {
|
||||
$args = [
|
||||
'id' => 'brokenLinkChecker',
|
||||
'pluginName' => 'Broken Link Checker',
|
||||
'notification-name' => 'install-blc'
|
||||
];
|
||||
|
||||
if ( self::installPlugin( $args, $network ) && function_exists( 'aioseoBrokenLinkChecker' ) ) {
|
||||
aioseoBrokenLinkChecker()->core->cache->delete( 'activation_redirect' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to install plugins through the Setup Wizard.
|
||||
* Creates a notification if the plugin can't be installed.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @param array $args The plugin arguments.
|
||||
* @param bool $network Whether to install the plugin on the network.
|
||||
* @return bool Whether the plugin was installed.
|
||||
*/
|
||||
private static function installPlugin( $args, $network = false ) {
|
||||
if ( aioseo()->addons->canInstall() ) {
|
||||
return aioseo()->addons->installAddon( $args['id'], $network );
|
||||
}
|
||||
|
||||
$pluginData = aioseo()->helpers->getPluginData();
|
||||
|
||||
$notification = Models\Notification::getNotificationByName( $args['notification-name'] );
|
||||
if ( ! $notification->exists() ) {
|
||||
Models\Notification::addNotification( [
|
||||
'slug' => uniqid(),
|
||||
'notification_name' => $args['notification-name'],
|
||||
'title' => sprintf(
|
||||
// Translators: 1 - A plugin name (e.g. "MonsterInsights", "Broken Link Checker", etc.).
|
||||
__( 'Install %1$s', 'all-in-one-seo-pack' ),
|
||||
$args['pluginName']
|
||||
),
|
||||
'content' => sprintf(
|
||||
// Translators: 1 - A plugin name (e.g. "MonsterInsights", "Broken Link Checker", etc.), 2 - The plugin short name ("AIOSEO").
|
||||
__( 'You selected to install the free %1$s plugin during the setup of %2$s, but there was an issue during installation. Click below to manually install.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
AIOSEO_PLUGIN_SHORT_NAME,
|
||||
! empty( $args['pluginLongName'] ) ? $args['pluginLongName'] : $args['pluginName']
|
||||
),
|
||||
'type' => 'info',
|
||||
'level' => [ 'all' ],
|
||||
'button1_label' => sprintf(
|
||||
// Translators: 1 - A plugin name (e.g. "MonsterInsights", "Broken Link Checker", etc.).
|
||||
__( 'Install %1$s', 'all-in-one-seo-pack' ),
|
||||
$args['pluginName']
|
||||
),
|
||||
'button1_action' => $pluginData[ $args['id'] ]['wpLink'],
|
||||
'button2_label' => __( 'Remind Me Later', 'all-in-one-seo-pack' ),
|
||||
'button2_action' => "http://action#notification/{$args['notification-name']}-reminder",
|
||||
'start' => gmdate( 'Y-m-d H:i:s' )
|
||||
] );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Api;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* WritingAssistant class for the API.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*/
|
||||
class WritingAssistant {
|
||||
/**
|
||||
* Process the keyword.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function processKeyword( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$postId = absint( $body['postId'] );
|
||||
$keywordText = sanitize_text_field( $body['keyword'] );
|
||||
$country = sanitize_text_field( $body['country'] );
|
||||
$language = sanitize_text_field( strtolower( $body['language'] ) );
|
||||
|
||||
if ( empty( $keywordText ) || empty( $country ) || empty( $language ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'error' => __( 'Missing data to generate a report', 'all-in-one-seo-pack' )
|
||||
] );
|
||||
}
|
||||
|
||||
$keyword = Models\WritingAssistantKeyword::getKeyword( $keywordText, $country, $language );
|
||||
$writingAssistantPost = Models\WritingAssistantPost::getPost( $postId );
|
||||
if ( $keyword->exists() ) {
|
||||
$writingAssistantPost->attachKeyword( $keyword->id );
|
||||
|
||||
// Returning early will let the UI code start polling the keyword.
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'progress' => $keyword->progress
|
||||
], 200 );
|
||||
}
|
||||
|
||||
// Start a new keyword process.
|
||||
$processResult = aioseo()->writingAssistant->seoBoost->service->processKeyword( $keywordText, $country, $language );
|
||||
if ( is_wp_error( $processResult ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'error' => $processResult->get_error_message()
|
||||
] );
|
||||
}
|
||||
|
||||
// Store the new keyword.
|
||||
$keyword->uuid = $processResult['slug'];
|
||||
$keyword->progress = 0;
|
||||
$keyword->save();
|
||||
|
||||
// Update the writing assistant post with the current keyword.
|
||||
$writingAssistantPost->attachKeyword( $keyword->id );
|
||||
|
||||
return new \WP_REST_Response( [ 'success' => true ], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current keyword for a Post.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function getPostKeyword( $request ) {
|
||||
$postId = $request->get_param( 'postId' );
|
||||
|
||||
if ( empty( $postId ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => __( 'Empty Post ID', 'all-in-one-seo-pack' )
|
||||
], 404 );
|
||||
}
|
||||
|
||||
$keyword = Models\WritingAssistantPost::getKeyword( $postId );
|
||||
if ( $keyword && 100 !== $keyword->progress ) {
|
||||
// Update progress.
|
||||
$newProgress = aioseo()->writingAssistant->seoBoost->service->getProgressAndResult( $keyword->uuid );
|
||||
if ( is_wp_error( $newProgress ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'error' => $newProgress->get_error_message()
|
||||
], 200 );
|
||||
}
|
||||
|
||||
if ( 'success' !== $newProgress['status'] ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'error' => $newProgress['msg']
|
||||
], 200 );
|
||||
}
|
||||
|
||||
$keyword->progress = ! empty( $newProgress['report']['progress'] ) ? $newProgress['report']['progress'] : $keyword->progress;
|
||||
|
||||
if ( ! empty( $newProgress['report']['keywords'] ) ) {
|
||||
$keyword->keywords = $newProgress['report']['keywords'];
|
||||
}
|
||||
|
||||
if ( ! empty( $newProgress['report']['competitors'] ) ) {
|
||||
$keyword->competitors = [
|
||||
'competitors' => $newProgress['report']['competitors'],
|
||||
'summary' => $newProgress['report']['competitors_summary']
|
||||
];
|
||||
}
|
||||
|
||||
$keyword->save();
|
||||
}
|
||||
|
||||
// Return a refreshed keyword here because we need some parsed data.
|
||||
$keyword = Models\WritingAssistantPost::getKeyword( $postId );
|
||||
|
||||
return new \WP_REST_Response( $keyword, 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content analysis for a post.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function getContentAnalysis( $request ) {
|
||||
$title = $request->get_param( 'title' );
|
||||
$description = $request->get_param( 'description' );
|
||||
$content = apply_filters( 'the_content', $request->get_param( 'content' ) );
|
||||
$postId = $request->get_param( 'postId' );
|
||||
if ( empty( $content ) || empty( $postId ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'message' => __( 'Empty Content or Post ID', 'all-in-one-seo-pack' )
|
||||
], 200 );
|
||||
}
|
||||
|
||||
$keyword = Models\WritingAssistantPost::getKeyword( $postId );
|
||||
if (
|
||||
! $keyword ||
|
||||
! $keyword->exists() ||
|
||||
100 !== $keyword->progress
|
||||
) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'error' => __( 'Keyword not found or not ready', 'all-in-one-seo-pack' )
|
||||
], 200 );
|
||||
}
|
||||
|
||||
$writingAssistantPost = Models\WritingAssistantPost::getPost( $postId );
|
||||
|
||||
// Make sure we're not analysing the same content again.
|
||||
$contentHash = sha1( $content );
|
||||
if (
|
||||
! empty( $writingAssistantPost->content_analysis ) &&
|
||||
$writingAssistantPost->content_analysis_hash === $contentHash
|
||||
) {
|
||||
return new \WP_REST_Response( $writingAssistantPost->content_analysis, 200 );
|
||||
}
|
||||
|
||||
// Call SEOBoost service to get the content analysis.
|
||||
$contentAnalysis = aioseo()->writingAssistant->seoBoost->service->getContentAnalysis( $title, $description, $content, $keyword->uuid );
|
||||
if ( is_wp_error( $contentAnalysis ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'error' => $contentAnalysis->get_error_message()
|
||||
], 200 );
|
||||
}
|
||||
|
||||
if ( empty( $contentAnalysis['result'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'error' => __( 'Empty response from service', 'all-in-one-seo-pack' )
|
||||
], 200 );
|
||||
}
|
||||
|
||||
// Update the post with the content analysis.
|
||||
$writingAssistantPost->content_analysis = $contentAnalysis['result'];
|
||||
$writingAssistantPost->content_analysis_hash = $contentHash;
|
||||
$writingAssistantPost->save();
|
||||
|
||||
return new \WP_REST_Response( $contentAnalysis['result'], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user info.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function getUserInfo() {
|
||||
$userInfo = aioseo()->writingAssistant->seoBoost->service->getUserInfo();
|
||||
if ( is_wp_error( $userInfo ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'error' => $userInfo->get_error_message()
|
||||
], 200 );
|
||||
}
|
||||
|
||||
if ( empty( $userInfo['status'] ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'error' => __( 'Empty response from service', 'all-in-one-seo-pack' )
|
||||
], 200 );
|
||||
}
|
||||
|
||||
if ( 'success' !== $userInfo['status'] ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'error' => $userInfo['msg']
|
||||
], 200 );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( $userInfo, 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user info.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function getUserOptions() {
|
||||
$userOptions = aioseo()->writingAssistant->seoBoost->getUserOptions();
|
||||
|
||||
return new \WP_REST_Response( $userOptions, 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the report history.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function getReportHistory() {
|
||||
$reportHistory = aioseo()->writingAssistant->seoBoost->getReportHistory();
|
||||
|
||||
if ( is_wp_error( $reportHistory ) ) {
|
||||
return new \WP_REST_Response( [
|
||||
'success' => false,
|
||||
'error' => $reportHistory->get_error_message()
|
||||
], 200 );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response( $reportHistory, 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect the user.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function disconnect() {
|
||||
aioseo()->writingAssistant->seoBoost->setAccessToken( '' );
|
||||
|
||||
return new \WP_REST_Response( [ 'success' => true ], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save user options.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function saveUserOptions( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
|
||||
$userOptions = [
|
||||
'country' => $body['country'],
|
||||
'language' => $body['language'],
|
||||
];
|
||||
|
||||
aioseo()->writingAssistant->seoBoost->setUserOptions( $userOptions );
|
||||
|
||||
return new \WP_REST_Response( [ 'success' => true ], 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the report progress.
|
||||
*
|
||||
* @since 4.7.4
|
||||
*
|
||||
* @param \WP_REST_Request $request The REST Request
|
||||
* @return \WP_REST_Response The response.
|
||||
*/
|
||||
public static function setReportProgress( $request ) {
|
||||
$body = $request->get_json_params();
|
||||
$keyword = Models\WritingAssistantPost::getKeyword( (int) $body['postId'] );
|
||||
$keyword->progress = (int) $body['progress'];
|
||||
$keyword->save();
|
||||
|
||||
return new \WP_REST_Response( [ 'success' => true ], 200 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Breadcrumbs;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Breadcrumb Block.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*/
|
||||
class Block {
|
||||
/**
|
||||
* The primary term list.
|
||||
*
|
||||
* @since 4.3.6
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $primaryTerm = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->register();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the block.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register() {
|
||||
aioseo()->blocks->registerBlock(
|
||||
'aioseo/breadcrumbs', [
|
||||
'attributes' => [
|
||||
'primaryTerm' => [
|
||||
'type' => 'string',
|
||||
'default' => null
|
||||
]
|
||||
],
|
||||
'render_callback' => [ $this, 'render' ]
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the block.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param array $blockAttributes The block attributes.
|
||||
* @return string The output from the output buffering.
|
||||
*/
|
||||
public function render( $blockAttributes ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
// phpcs:disable HM.Security.ValidatedSanitizedInput.InputNotSanitized, HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended
|
||||
$postId = ! empty( $_GET['post_id'] ) ? (int) sanitize_text_field( wp_unslash( $_GET['post_id'] ) ) : false;
|
||||
// phpcs:enable
|
||||
|
||||
if ( ! empty( $blockAttributes['primaryTerm'] ) ) {
|
||||
$this->primaryTerm = json_decode( $blockAttributes['primaryTerm'], true );
|
||||
}
|
||||
|
||||
if ( aioseo()->blocks->isRenderingBlockInEditor() && ! empty( $postId ) ) {
|
||||
add_filter( 'aioseo_post_primary_term', [ $this, 'changePrimaryTerm' ], 10, 2 );
|
||||
add_filter( 'get_object_terms', [ $this, 'temporarilyAddTerm' ], 10, 3 );
|
||||
$breadcrumbs = aioseo()->breadcrumbs->frontend->sideDisplay( false, 'post' === get_post_type( $postId ) ? 'post' : 'single', get_post( $postId ) );
|
||||
remove_filter( 'aioseo_post_primary_term', [ $this, 'changePrimaryTerm' ], 10 );
|
||||
remove_filter( 'get_object_terms', [ $this, 'temporarilyAddTerm' ], 10 );
|
||||
|
||||
if (
|
||||
in_array( 'breadcrumbsEnable', aioseo()->internalOptions->deprecatedOptions, true ) &&
|
||||
! aioseo()->options->deprecated->breadcrumbs->enable
|
||||
) {
|
||||
return '<p>' .
|
||||
sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO"), 2 - Opening HTML link tag, 3 - Closing HTML link tag.
|
||||
__( 'Breadcrumbs are currently disabled, so this block will be rendered empty. You can enable %1$s\'s breadcrumb functionality under %2$sGeneral Settings > Breadcrumbs%3$s.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
AIOSEO_PLUGIN_SHORT_NAME,
|
||||
'<a href="' . esc_url( admin_url( 'admin.php?page=aioseo-settings#/breadcrumbs' ) ) . '" target="_blank">',
|
||||
'</a>'
|
||||
) .
|
||||
'</p>';
|
||||
}
|
||||
|
||||
return $breadcrumbs;
|
||||
}
|
||||
|
||||
return aioseo()->breadcrumbs->frontend->display( false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily adds the primary term to the list of terms.
|
||||
*
|
||||
* @since 4.3.6
|
||||
*
|
||||
* @param array $terms The list of terms.
|
||||
* @param array $objectIds The object IDs.
|
||||
* @param array $taxonomies The taxonomies.
|
||||
* @return array The list of terms.
|
||||
*/
|
||||
public function temporarilyAddTerm( $terms, $objectIds, $taxonomies ) {
|
||||
$taxonomy = $taxonomies[0];
|
||||
if ( empty( $this->primaryTerm ) || empty( $this->primaryTerm[ $taxonomy ] ) ) {
|
||||
return $terms;
|
||||
}
|
||||
|
||||
$term = aioseo()->helpers->getTerm( $this->primaryTerm[ $taxonomy ] );
|
||||
if ( is_a( $term, 'WP_Term' ) ) {
|
||||
$terms[] = $term;
|
||||
}
|
||||
|
||||
return $terms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the primary term.
|
||||
*
|
||||
* @since 4.3.6
|
||||
*
|
||||
* @param \WP_Term $term The term object.
|
||||
* @param string $taxonomy The taxonomy name.
|
||||
* @return \WP_Term The term object.
|
||||
*/
|
||||
public function changePrimaryTerm( $term, $taxonomy ) {
|
||||
if ( empty( $this->primaryTerm ) || empty( $this->primaryTerm[ $taxonomy ] ) ) {
|
||||
return $term;
|
||||
}
|
||||
|
||||
return aioseo()->helpers->getTerm( $this->primaryTerm[ $taxonomy ], $taxonomy );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,744 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Breadcrumbs {
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class Breadcrumbs.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*/
|
||||
class Breadcrumbs {
|
||||
/** Instance of the frontend class.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @var \AIOSEO\Plugin\Common\Breadcrumbs\Frontend|\AIOSEO\Plugin\Pro\Breadcrumbs\Frontend
|
||||
*/
|
||||
public $frontend;
|
||||
|
||||
/**
|
||||
* Instance of the shortcode class.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @var Shortcode
|
||||
*/
|
||||
public $shortcode;
|
||||
|
||||
/**
|
||||
* Instance of the block class.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @var Block
|
||||
*/
|
||||
public $block;
|
||||
|
||||
/**
|
||||
* Instance of the tags class.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @var Tags
|
||||
*/
|
||||
public $tags;
|
||||
|
||||
/**
|
||||
* Array of crumbs.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @var array An array of crumbs.
|
||||
*/
|
||||
public $breadcrumbs;
|
||||
|
||||
/**
|
||||
* Breadcrumbs constructor.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->frontend = new Frontend();
|
||||
$this->shortcode = new Shortcode();
|
||||
$this->block = new Block();
|
||||
|
||||
add_action( 'widgets_init', [ $this, 'registerWidget' ] );
|
||||
|
||||
// Init Tags class later as we need post types registered.
|
||||
add_action( 'init', [ $this, 'init' ], 50 );
|
||||
}
|
||||
|
||||
public function init() {
|
||||
$this->tags = new Tags();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to add crumbs on the breadcrumb array.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param array $crumbs A single crumb or an array of crumbs.
|
||||
* @return void
|
||||
*/
|
||||
public function addCrumbs( $crumbs ) {
|
||||
if ( empty( $crumbs ) || ! is_array( $crumbs ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If it's a single crumb put it inside an array to merge.
|
||||
if ( isset( $crumbs['label'] ) ) {
|
||||
$crumbs = [ $crumbs ];
|
||||
}
|
||||
|
||||
$this->breadcrumbs = array_merge( $this->breadcrumbs, $crumbs );
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a crumb array based on a type and a reference.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param string $type The type of breadcrumb ( post, single, page, category, tag, taxonomy, postTypeArchive, date,
|
||||
* author, search, notFound, blog ).
|
||||
* @param mixed $reference The reference can be an object ( WP_Post | WP_Term | WP_Post_Type | WP_User ), an array, an int or a string.
|
||||
* @param array $paged A reference for a paged crumb.
|
||||
* @return array An array of breadcrumbs with their label, link, type and reference.
|
||||
*/
|
||||
public function buildBreadcrumbs( $type, $reference, $paged = [] ) {
|
||||
// Clear the breadcrumb array and build a new one.
|
||||
$this->breadcrumbs = [];
|
||||
|
||||
// Add breadcrumb prefix.
|
||||
$this->addCrumbs( $this->getPrefixCrumb( $type, $reference ) );
|
||||
|
||||
// Set a home page in the beginning of the breadcrumb.
|
||||
$this->addCrumbs( $this->maybeGetHomePageCrumb( $type, $reference ) );
|
||||
|
||||
// Woocommerce shop page support.
|
||||
$this->addCrumbs( $this->maybeGetWooCommerceShopCrumb() );
|
||||
|
||||
// Blog home.
|
||||
if (
|
||||
aioseo()->options->breadcrumbs->showBlogHome &&
|
||||
in_array( $type, [ 'category', 'tag', 'post', 'author', 'date' ], true )
|
||||
) {
|
||||
$this->addCrumbs( $this->getBlogCrumb() );
|
||||
}
|
||||
|
||||
switch ( $type ) {
|
||||
case 'post':
|
||||
case 'single':
|
||||
$this->addCrumbs( $this->getPostArchiveCrumb( $reference ) );
|
||||
$this->addCrumbs( $this->getPostTaxonomyCrumbs( $reference ) );
|
||||
$this->addCrumbs( $this->getPostParentCrumbs( $reference ) );
|
||||
$this->addCrumbs( $this->getPostCrumb( $reference ) );
|
||||
break;
|
||||
case 'page':
|
||||
$this->addCrumbs( $this->getPostParentCrumbs( $reference, 'page' ) );
|
||||
$this->addCrumbs( $this->getPostCrumb( $reference, 'page' ) );
|
||||
break;
|
||||
case 'category':
|
||||
case 'tag':
|
||||
case 'taxonomy':
|
||||
$this->addCrumbs( $this->getTermTaxonomyParentCrumbs( $reference ) );
|
||||
$this->addCrumbs( $this->getTermTaxonomyCrumb( $reference ) );
|
||||
break;
|
||||
case 'postTypeArchive':
|
||||
$this->addCrumbs( $this->getPostTypeArchiveCrumb( $reference ) );
|
||||
break;
|
||||
case 'date':
|
||||
$this->addCrumbs( $this->getDateCrumb( $reference ) );
|
||||
break;
|
||||
case 'author':
|
||||
$this->addCrumbs( $this->getAuthorCrumb( $reference ) );
|
||||
break;
|
||||
case 'blog':
|
||||
$this->addCrumbs( $this->getBlogCrumb() );
|
||||
break;
|
||||
case 'search':
|
||||
$this->addCrumbs( $this->getSearchCrumb( $reference ) );
|
||||
break;
|
||||
case 'notFound':
|
||||
$this->addCrumbs( $this->getNotFoundCrumb() );
|
||||
break;
|
||||
case 'preview':
|
||||
$this->addCrumbs( $this->getPreviewCrumb( $reference ) );
|
||||
break;
|
||||
case 'wcProduct':
|
||||
$this->addCrumbs( $this->getPostTaxonomyCrumbs( $reference ) );
|
||||
$this->addCrumbs( $this->getPostParentCrumbs( $reference ) );
|
||||
$this->addCrumbs( $this->getPostCrumb( $reference ) );
|
||||
break;
|
||||
case 'buddypress':
|
||||
$this->addCrumbs( aioseo()->standalone->buddyPress->component->getCrumbs() );
|
||||
break;
|
||||
}
|
||||
|
||||
// Paged crumb.
|
||||
if ( ! empty( $paged['paged'] ) ) {
|
||||
$this->addCrumbs( $this->getPagedCrumb( $paged ) );
|
||||
}
|
||||
|
||||
// Maybe remove the last crumb.
|
||||
if ( ! $this->showCurrentItem( $type, $reference ) ) {
|
||||
array_pop( $this->breadcrumbs );
|
||||
}
|
||||
|
||||
// Remove empty crumbs.
|
||||
$this->breadcrumbs = array_filter( $this->breadcrumbs );
|
||||
|
||||
return $this->breadcrumbs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the prefix crumb.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param string $type The type of breadcrumb.
|
||||
* @param mixed $reference The breadcrumb reference.
|
||||
* @return array A crumb.
|
||||
*/
|
||||
public function getPrefixCrumb( $type, $reference ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
if ( 0 === strlen( aioseo()->options->breadcrumbs->breadcrumbPrefix ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->makeCrumb( aioseo()->options->breadcrumbs->breadcrumbPrefix, '', 'prefix' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 404 crumb.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @return array A crumb.
|
||||
*/
|
||||
public function getNotFoundCrumb() {
|
||||
return $this->makeCrumb( aioseo()->options->breadcrumbs->errorFormat404, '', 'notFound' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the search crumb.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param string $searchQuery The search query for reference.
|
||||
* @return array A crumb.
|
||||
*/
|
||||
public function getSearchCrumb( $searchQuery ) {
|
||||
return $this->makeCrumb( aioseo()->options->breadcrumbs->searchResultFormat, get_search_link( $searchQuery ), 'search', $searchQuery );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the preview crumb.
|
||||
*
|
||||
* @since 4.1.5
|
||||
*
|
||||
* @param string $label The preview label.
|
||||
* @return array A crumb.
|
||||
*/
|
||||
public function getPreviewCrumb( $label ) {
|
||||
return $this->makeCrumb( $label, '', 'preview' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the post type archive crumb.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param \WP_Post_Type $postType The post type object for reference.
|
||||
* @return array A crumb.
|
||||
*/
|
||||
public function getPostTypeArchiveCrumb( $postType ) {
|
||||
return $this->makeCrumb( aioseo()->options->breadcrumbs->archiveFormat, get_post_type_archive_link( $postType->name ), 'postTypeArchive', $postType );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a post crumb.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param \WP_Post $post A post object for reference.
|
||||
* @param string $type The breadcrumb type.
|
||||
* @param string $subType The breadcrumb subType.
|
||||
* @return array A crumb.
|
||||
*/
|
||||
public function getPostCrumb( $post, $type = 'single', $subType = '' ) {
|
||||
return $this->makeCrumb( get_the_title( $post ), get_permalink( $post ), $type, $post, $subType );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the term crumb.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param \WP_Term $term The term object for reference.
|
||||
* @param string $subType The breadcrumb subType.
|
||||
* @return array A crumb.
|
||||
*/
|
||||
public function getTermTaxonomyCrumb( $term, $subType = '' ) {
|
||||
return $this->makeCrumb( $term->name, get_term_link( $term ), 'taxonomy', $term, $subType );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the paged crumb.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param array $reference The paged array for reference.
|
||||
* @return array A crumb.
|
||||
*/
|
||||
public function getPagedCrumb( $reference ) {
|
||||
return $this->makeCrumb( sprintf( '%1$s %2$s', __( 'Page', 'all-in-one-seo-pack' ), $reference['paged'] ), $reference['link'], 'paged', $reference );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the author crumb.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param \WP_User $wpUser A WP_User object.
|
||||
* @return array A crumb.
|
||||
*/
|
||||
public function getAuthorCrumb( $wpUser ) {
|
||||
return $this->makeCrumb( $wpUser->display_name, get_author_posts_url( $wpUser->ID ), 'author', $wpUser );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date crumb.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param array $reference An array of year, month and day values.
|
||||
* @return array A crumb.
|
||||
*/
|
||||
public function getDateCrumb( $reference ) {
|
||||
$dateCrumb = [];
|
||||
$addMonth = false;
|
||||
$addYear = false;
|
||||
if ( ! empty( $reference['day'] ) ) {
|
||||
$addMonth = true;
|
||||
$addYear = true;
|
||||
$dateCrumb[] = $this->makeCrumb(
|
||||
zeroise( (int) $reference['day'], 2 ),
|
||||
get_day_link( $reference['year'], $reference['month'], $reference['day'] ),
|
||||
'day',
|
||||
$reference['day']
|
||||
);
|
||||
}
|
||||
if ( ! empty( $reference['month'] ) || $addMonth ) {
|
||||
$addYear = true;
|
||||
$dateCrumb[] = $this->makeCrumb(
|
||||
zeroise( (int) $reference['month'], 2 ),
|
||||
get_month_link( $reference['year'], $reference['month'] ),
|
||||
'month',
|
||||
$reference['month']
|
||||
);
|
||||
|
||||
}
|
||||
if ( ! empty( $reference['year'] ) || $addYear ) {
|
||||
$dateCrumb[] = $this->makeCrumb(
|
||||
$reference['year'],
|
||||
get_year_link( $reference['year'] ),
|
||||
'year',
|
||||
$reference['year']
|
||||
);
|
||||
}
|
||||
|
||||
return array_reverse( $dateCrumb );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of crumbs parents for the term.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param \WP_Term $term A WP_Term object.
|
||||
* @return array An array of parent crumbs.
|
||||
*/
|
||||
public function getTermTaxonomyParentCrumbs( $term ) {
|
||||
$crumbs = [];
|
||||
|
||||
$termHierarchy = $this->getTermHierarchy( $term->term_id, $term->taxonomy );
|
||||
if ( ! empty( $termHierarchy ) ) {
|
||||
foreach ( $termHierarchy as $parentTermId ) {
|
||||
$parentTerm = aioseo()->helpers->getTerm( $parentTermId, $term->taxonomy );
|
||||
$crumbs[] = $this->getTermTaxonomyCrumb( $parentTerm, 'parent' );
|
||||
}
|
||||
}
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a standard crumb array.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param string $label The crumb label.
|
||||
* @param string $link The crumb url.
|
||||
* @param null $type The crumb type.
|
||||
* @param null $reference The crumb reference.
|
||||
* @param null $subType The crumb subType ( single/parent ).
|
||||
* @return array A crumb array.
|
||||
*/
|
||||
public function makeCrumb( $label, $link = '', $type = null, $reference = null, $subType = null ) {
|
||||
return [
|
||||
'label' => $label,
|
||||
'link' => $link,
|
||||
'type' => $type,
|
||||
'subType' => $subType,
|
||||
'reference' => $reference
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a post archive crumb if it's post type has archives.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param int|\WP_Post $post An ID or a WP_Post object.
|
||||
* @return array A crumb.
|
||||
*/
|
||||
public function getPostArchiveCrumb( $post ) {
|
||||
$postType = get_post_type_object( get_post_type( $post ) );
|
||||
if ( ! $postType || ! $postType->has_archive ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->makeCrumb( $postType->labels->name, get_post_type_archive_link( $postType->name ), 'postTypeArchive', $postType );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a post's taxonomy crumbs.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param int|\WP_Post $post An ID or a WP_Post object.
|
||||
* @param null $taxonomy A taxonomy to use. If none is provided the first one with terms selected will be used.
|
||||
* @return array An array of term crumbs.
|
||||
*/
|
||||
public function getPostTaxonomyCrumbs( $post, $taxonomy = null ) {
|
||||
$crumbs = [];
|
||||
|
||||
if ( $taxonomy && ! is_array( $taxonomy ) ) {
|
||||
$taxonomy = [ $taxonomy ];
|
||||
}
|
||||
|
||||
$termHierarchy = $this->getPostTaxTermHierarchy( $post, $taxonomy );
|
||||
if ( ! empty( $termHierarchy['terms'] ) ) {
|
||||
foreach ( $termHierarchy['terms'] as $termId ) {
|
||||
$term = aioseo()->helpers->getTerm( $termId, $termHierarchy['taxonomy'] );
|
||||
$crumbs[] = $this->makeCrumb( $term->name, get_term_link( $term, $termHierarchy['taxonomy'] ), 'taxonomy', $term, 'parent' );
|
||||
}
|
||||
}
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the post's parent crumbs.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param int|\WP_Post $post An ID or a WP_Post object.
|
||||
* @param string $type The crumb type.
|
||||
* @return array An array of the post parent crumbs.
|
||||
*/
|
||||
public function getPostParentCrumbs( $post, $type = 'single' ) {
|
||||
$crumbs = [];
|
||||
if ( ! is_post_type_hierarchical( get_post_type( $post ) ) ) {
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
$postHierarchy = $this->getPostHierarchy( $post );
|
||||
if ( ! empty( $postHierarchy ) ) {
|
||||
foreach ( $postHierarchy as $parentID ) {
|
||||
// Do not include the Home Page.
|
||||
if ( aioseo()->helpers->getHomePageId() === $parentID ) {
|
||||
continue;
|
||||
}
|
||||
$crumbs[] = $this->getPostCrumb( get_post( $parentID ), $type, 'parent' );
|
||||
}
|
||||
}
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to extend on pro for extra functionality.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param string $type The type of breadcrumb.
|
||||
* @param mixed $reference The breadcrumb reference.
|
||||
* @return bool Show current item.
|
||||
*/
|
||||
public function showCurrentItem( $type = null, $reference = null ) {
|
||||
return apply_filters( 'aioseo_breadcrumbs_show_current_item', aioseo()->options->breadcrumbs->showCurrentItem, $type, $reference );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a home page crumb.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param string $type The type of breadcrumb.
|
||||
* @param mixed $reference The breadcrumb reference.
|
||||
* @return array|void The home crumb.
|
||||
*/
|
||||
public function maybeGetHomePageCrumb( $type = null, $reference = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
if ( aioseo()->options->breadcrumbs->homepageLink ) {
|
||||
return $this->getHomePageCrumb();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a home page crumb.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @return array The home crumb.
|
||||
*/
|
||||
public function getHomePageCrumb() {
|
||||
$homePageId = aioseo()->helpers->getHomePageId();
|
||||
|
||||
$label = '';
|
||||
if ( $homePageId ) {
|
||||
$label = get_the_title( $homePageId );
|
||||
}
|
||||
|
||||
if ( 0 < strlen( aioseo()->options->breadcrumbs->homepageLabel ) ) {
|
||||
$label = aioseo()->options->breadcrumbs->homepageLabel;
|
||||
}
|
||||
|
||||
// Label fallback.
|
||||
if ( empty( $label ) ) {
|
||||
$label = __( 'Home', 'all-in-one-seo-pack' );
|
||||
}
|
||||
|
||||
return $this->makeCrumb( $label, get_home_url(), 'homePage', aioseo()->helpers->getHomePage() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the blog crumb.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @return array The blog crumb.
|
||||
*/
|
||||
public function getBlogCrumb() {
|
||||
$crumb = [];
|
||||
|
||||
$blogPage = aioseo()->helpers->getBlogPage();
|
||||
if ( null !== $blogPage ) {
|
||||
$crumb = $this->makeCrumb( $blogPage->post_title, get_permalink( $blogPage ), 'blog', $blogPage );
|
||||
}
|
||||
|
||||
return $crumb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe add the shop crumb to products and product categories.
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @return array The shop crumb.
|
||||
*/
|
||||
public function maybeGetWooCommerceShopCrumb() {
|
||||
$crumb = [];
|
||||
if (
|
||||
aioseo()->helpers->isWooCommerceShopPage() ||
|
||||
aioseo()->helpers->isWooCommerceProductPage() ||
|
||||
aioseo()->helpers->isWooCommerceTaxonomyPage()
|
||||
) {
|
||||
$crumb = $this->getWooCommerceShopCrumb();
|
||||
}
|
||||
|
||||
return $crumb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shop crumb.
|
||||
* @see WC_Breadcrumb::prepend_shop_page()
|
||||
*
|
||||
* @since 4.5.5
|
||||
*
|
||||
* @return array The shop crumb.
|
||||
*/
|
||||
public function getWooCommerceShopCrumb() {
|
||||
$crumb = [];
|
||||
|
||||
if (
|
||||
! function_exists( 'wc_get_page_id' ) ||
|
||||
apply_filters( 'aioseo_woocommerce_breadcrumb_hide_shop', false )
|
||||
) {
|
||||
return $crumb;
|
||||
}
|
||||
|
||||
$shopPageId = wc_get_page_id( 'shop' );
|
||||
$shopPage = get_post( $shopPageId );
|
||||
|
||||
// WC checks if the permalink contains the shop page in the URI, but we prefer to
|
||||
// always show the shop page as the first crumb if it exists and it's not the home page.
|
||||
if (
|
||||
$shopPageId &&
|
||||
$shopPage &&
|
||||
aioseo()->helpers->getHomePageId() !== $shopPageId
|
||||
) {
|
||||
$crumb = $this->makeCrumb( get_the_title( $shopPage ), get_permalink( $shopPage ), 'wcShop' );
|
||||
}
|
||||
|
||||
return $crumb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a post's term hierarchy for a list of taxonomies selecting the one that has a lengthier hierarchy.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param int|\WP_Post $post An ID or a WP_Post object.
|
||||
* @param array $taxonomies An array of taxonomy names.
|
||||
* @param false $skipUnselectedTerms Allow unselected terms to be filtered out from the crumbs.
|
||||
* @return array An array of the taxonomy name + a term hierarchy.
|
||||
*/
|
||||
public function getPostTaxTermHierarchy( $post, $taxonomies = [], $skipUnselectedTerms = false ) {
|
||||
// Get all taxonomies attached to the post.
|
||||
if ( empty( $taxonomies ) ) {
|
||||
$taxonomies = get_object_taxonomies( get_post_type( $post ), 'objects' );
|
||||
$taxonomies = wp_filter_object_list( $taxonomies, [ 'public' => true ], 'and', 'name' );
|
||||
}
|
||||
|
||||
foreach ( $taxonomies as $taxonomy ) {
|
||||
$primaryTerm = aioseo()->standalone->primaryTerm->getPrimaryTerm( $post->ID, $taxonomy );
|
||||
$terms = wp_get_object_terms( $post->ID, $taxonomy, [
|
||||
'orderby' => 'term_id',
|
||||
'order' => 'ASC',
|
||||
] );
|
||||
// Use the first taxonomy with terms.
|
||||
if ( empty( $terms ) || is_wp_error( $terms ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determines the lengthier term hierarchy.
|
||||
$termHierarchy = [];
|
||||
foreach ( $terms as $term ) {
|
||||
// Gets our filtered ancestors.
|
||||
$ancestors = $this->getFilteredTermHierarchy( $term->term_id, $term->taxonomy, $skipUnselectedTerms ? $terms : [] );
|
||||
|
||||
// Merge the current term to be used in the breadcrumbs.
|
||||
$ancestors = array_merge( $ancestors, [ $term->term_id ] );
|
||||
|
||||
// If the current term is the primary term, use it.
|
||||
if ( is_a( $primaryTerm, 'WP_Term' ) && $primaryTerm->term_id === $term->term_id ) {
|
||||
$termHierarchy = $ancestors;
|
||||
break;
|
||||
}
|
||||
|
||||
$termHierarchy = ( count( $termHierarchy ) < count( $ancestors ) ) ? $ancestors : $termHierarchy;
|
||||
}
|
||||
|
||||
// Return a top to bottom hierarchy.
|
||||
return [
|
||||
'taxonomy' => $taxonomy,
|
||||
'terms' => $termHierarchy
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a term's parent hierarchy against other terms.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param int $termId A term id.
|
||||
* @param string $taxonomy The taxonomy name.
|
||||
* @param array $termsToFilterAgainst Terms to filter out of the hierarchy.
|
||||
* @return array The term's parent hierarchy.
|
||||
*/
|
||||
public function getFilteredTermHierarchy( $termId, $taxonomy, $termsToFilterAgainst = [] ) {
|
||||
$ancestors = $this->getTermHierarchy( $termId, $taxonomy );
|
||||
|
||||
// Keep only selected terms in the hierarchy.
|
||||
if ( ! empty( $termsToFilterAgainst ) ) {
|
||||
// If it's a WP_Term array make it a term_id array.
|
||||
if ( is_a( current( $termsToFilterAgainst ), 'WP_Term' ) ) {
|
||||
$termsToFilterAgainst = wp_list_pluck( $termsToFilterAgainst, 'term_id' );
|
||||
}
|
||||
|
||||
$ancestors = array_intersect( $ancestors, $termsToFilterAgainst );
|
||||
}
|
||||
|
||||
return $ancestors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a term's parent hierarchy.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param int $termId A term id.
|
||||
* @param string $taxonomy A taxonomy name.
|
||||
* @return array The term parent hierarchy.
|
||||
*/
|
||||
public function getTermHierarchy( $termId, $taxonomy ) {
|
||||
// Return a top to bottom hierarchy.
|
||||
return array_reverse( get_ancestors( $termId, $taxonomy, 'taxonomy' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a post's parent hierarchy.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param int|\WP_Post $post An ID or a WP_Post object.
|
||||
* @return array The post parent hierarchy.
|
||||
*/
|
||||
public function getPostHierarchy( $post ) {
|
||||
$postId = ! empty( $post->ID ) ? $post->ID : $post;
|
||||
|
||||
// Return a top to bottom hierarchy.
|
||||
return array_reverse( get_ancestors( $postId, '', 'post_type' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register our breadcrumb widget.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerWidget() {
|
||||
if ( aioseo()->helpers->canRegisterLegacyWidget( 'aioseo-breadcrumb-widget' ) ) {
|
||||
register_widget( 'AIOSEO\Plugin\Common\Breadcrumbs\Widget' );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'aioseo_breadcrumbs' ) ) {
|
||||
/**
|
||||
* Global function for breadcrumbs output.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param boolean $echo Echo or return the output.
|
||||
* @return string|void The output.
|
||||
*/
|
||||
function aioseo_breadcrumbs( $echo = true ) {
|
||||
return aioseo()->breadcrumbs->frontend->display( $echo );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Breadcrumbs;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Integrations\BuddyPress as BuddyPressIntegration;
|
||||
|
||||
/**
|
||||
* Class Frontend.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*/
|
||||
class Frontend {
|
||||
/**
|
||||
* A local 'cached' crumb array.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $breadcrumbs = [];
|
||||
|
||||
/**
|
||||
* Gets the current page's breadcrumbs.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBreadcrumbs() {
|
||||
if ( ! empty( $this->breadcrumbs ) ) {
|
||||
return apply_filters( 'aioseo_breadcrumbs_trail', $this->breadcrumbs );
|
||||
}
|
||||
|
||||
$reference = get_queried_object();
|
||||
$type = '';
|
||||
if ( BuddyPressIntegration::isComponentPage() ) {
|
||||
$type = 'buddypress';
|
||||
}
|
||||
|
||||
if ( ! $type ) {
|
||||
// These types need the queried object for reference.
|
||||
if ( is_object( $reference ) ) {
|
||||
if ( is_single() ) {
|
||||
$type = 'single';
|
||||
}
|
||||
|
||||
if ( is_singular( 'post' ) ) {
|
||||
$type = 'post';
|
||||
}
|
||||
|
||||
if ( is_page() && ! is_front_page() ) {
|
||||
$type = 'page';
|
||||
}
|
||||
|
||||
if ( is_category() || is_tag() ) {
|
||||
$type = 'category';
|
||||
}
|
||||
|
||||
if ( is_tax() ) {
|
||||
$type = 'taxonomy';
|
||||
}
|
||||
|
||||
if ( is_post_type_archive() ) {
|
||||
$type = 'postTypeArchive';
|
||||
}
|
||||
|
||||
if ( is_author() ) {
|
||||
$type = 'author';
|
||||
}
|
||||
|
||||
if ( is_home() ) {
|
||||
$type = 'blog';
|
||||
}
|
||||
|
||||
// Support WC shop page.
|
||||
if ( aioseo()->helpers->isWooCommerceShopPage() ) {
|
||||
$type = 'wcShop';
|
||||
}
|
||||
|
||||
// Support WC products.
|
||||
if ( aioseo()->helpers->isWooCommerceProductPage() ) {
|
||||
$type = 'wcProduct';
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_date() ) {
|
||||
$type = 'date';
|
||||
$reference = [
|
||||
'year' => get_query_var( 'year' ),
|
||||
'month' => get_query_var( 'monthnum' ),
|
||||
'day' => get_query_var( 'day' )
|
||||
];
|
||||
}
|
||||
|
||||
if ( is_search() ) {
|
||||
$type = 'search';
|
||||
$reference = htmlspecialchars( sanitize_text_field( get_search_query() ) );
|
||||
}
|
||||
|
||||
if ( is_404() ) {
|
||||
$type = 'notFound';
|
||||
}
|
||||
}
|
||||
|
||||
$paged = false;
|
||||
if ( is_paged() || ( is_singular() && 1 < get_query_var( 'page' ) ) ) {
|
||||
global $wp;
|
||||
$paged = [
|
||||
'paged' => get_query_var( 'paged' ) ? get_query_var( 'paged' ) : get_query_var( 'page' ),
|
||||
'link' => home_url( $wp->request )
|
||||
];
|
||||
}
|
||||
|
||||
return apply_filters( 'aioseo_breadcrumbs_trail', aioseo()->breadcrumbs->buildBreadcrumbs( $type, $reference, $paged ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to display breadcrumbs for a specific page.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param bool $echo Print out the breadcrumb.
|
||||
* @param string $type The type for the breadcrumb.
|
||||
* @param string $reference A reference to be used for rendering the breadcrumb.
|
||||
* @return string|void A html breadcrumb.
|
||||
*/
|
||||
public function sideDisplay( $echo = true, $type = '', $reference = '' ) {
|
||||
// Save previously built breadcrumbs.
|
||||
$previousCrumbs = $this->breadcrumbs;
|
||||
|
||||
// Build and run the sideDisplay.
|
||||
$this->breadcrumbs = aioseo()->breadcrumbs->buildBreadcrumbs( $type, $reference );
|
||||
$sideDisplay = $this->display( $echo );
|
||||
|
||||
// Restore previously built breadcrumbs.
|
||||
$this->breadcrumbs = $previousCrumbs;
|
||||
|
||||
return $sideDisplay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a generic breadcrumb preview.
|
||||
*
|
||||
* @since 4.1.5
|
||||
*
|
||||
* @param bool $echo Print out the breadcrumb.
|
||||
* @param string $label The preview crumb label.
|
||||
* @return string|void A html breadcrumb.
|
||||
*/
|
||||
public function preview( $echo = true, $label = '' ) {
|
||||
// Translators: "Crumb" refers to a part of the breadcrumb trail.
|
||||
$label = empty( $label ) ? __( 'Sample Crumb', 'all-in-one-seo-pack' ) : $label;
|
||||
|
||||
return $this->sideDisplay( $echo, 'preview', $label );
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the breadcrumb in the frontend.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param bool $echo Print out the breadcrumb.
|
||||
* @return string|void A html breadcrumb.
|
||||
*/
|
||||
public function display( $echo = true ) {
|
||||
if (
|
||||
in_array( 'breadcrumbsEnable', aioseo()->internalOptions->deprecatedOptions, true ) &&
|
||||
! aioseo()->options->deprecated->breadcrumbs->enable
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! apply_filters( 'aioseo_breadcrumbs_output', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We can only run after this action because we need all post types loaded.
|
||||
if ( ! did_action( 'init' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$breadcrumbs = $this->getBreadcrumbs();
|
||||
if ( empty( $breadcrumbs ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$breadcrumbsCount = count( $breadcrumbs );
|
||||
|
||||
$display = '<div class="aioseo-breadcrumbs">';
|
||||
foreach ( $breadcrumbs as $breadcrumb ) {
|
||||
--$breadcrumbsCount;
|
||||
|
||||
$breadcrumbDisplay = $this->breadcrumbToDisplay( $breadcrumb );
|
||||
|
||||
// Strip link from Last crumb.
|
||||
if (
|
||||
0 === $breadcrumbsCount &&
|
||||
aioseo()->breadcrumbs->showCurrentItem() &&
|
||||
! $this->linkCurrentItem() &&
|
||||
'default' === $breadcrumbDisplay['templateType']
|
||||
) {
|
||||
$breadcrumbDisplay['template'] = $this->stripLink( $breadcrumbDisplay['template'] );
|
||||
}
|
||||
|
||||
$display .= $breadcrumbDisplay['template'];
|
||||
|
||||
if ( 0 < $breadcrumbsCount ) {
|
||||
$display .= $this->getSeparator();
|
||||
}
|
||||
}
|
||||
$display .= '</div>';
|
||||
|
||||
// Final security cleaning.
|
||||
$display = wp_kses_post( $display );
|
||||
|
||||
if ( $echo ) {
|
||||
echo $display; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
return $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a crumb array into a rendered html crumb.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param array $item The crumb array.
|
||||
* @return string|void The crumb html.
|
||||
*/
|
||||
protected function breadcrumbToDisplay( $item ) {
|
||||
$templateItem = $this->getCrumbTemplate( $item );
|
||||
if ( empty( $templateItem['template'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do tags.
|
||||
$templateItem['template'] = aioseo()->breadcrumbs->tags->replaceTags( $templateItem['template'], $item );
|
||||
$templateItem['template'] = preg_replace_callback(
|
||||
'/>(?![^<]*>)(?![^>]*")([^<]*?)>/',
|
||||
function ( $matches ) {
|
||||
return '>' . $matches[1] . '>';
|
||||
},
|
||||
htmlentities( $templateItem['template'] )
|
||||
);
|
||||
|
||||
// Restore html.
|
||||
$templateItem['template'] = aioseo()->helpers->decodeHtmlEntities( $templateItem['template'] );
|
||||
|
||||
// Remove html link if it comes back from the template but we passed no links to it.
|
||||
if ( empty( $item['link'] ) ) {
|
||||
$templateItem['template'] = $this->stripLink( $templateItem['template'] );
|
||||
}
|
||||
|
||||
// Allow shortcodes to run in the final html.
|
||||
$templateItem['template'] = do_shortcode( $templateItem['template'] );
|
||||
|
||||
return $templateItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get a crumb's template.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param array $crumb The crumb array.
|
||||
* @return string The html template.
|
||||
*/
|
||||
protected function getTemplate( $crumb ) {
|
||||
return $this->getDefaultTemplate( $crumb );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get a crumb's template.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param array $crumb The crumb array.
|
||||
* @return array The template type and html.
|
||||
*/
|
||||
protected function getCrumbTemplate( $crumb ) {
|
||||
return [
|
||||
'templateType' => 'default',
|
||||
'template' => $this->getTemplate( $crumb )
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Default html template.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param string $type The crumb's type.
|
||||
* @param mixed $reference The crumb's reference.
|
||||
* @return string The default crumb template.
|
||||
*/
|
||||
public function getDefaultTemplate( $type = '', $reference = '' ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
return <<<TEMPLATE
|
||||
<span class="aioseo-breadcrumb">
|
||||
<a href="#breadcrumb_link" title="#breadcrumb_label">#breadcrumb_label</a>
|
||||
</span>
|
||||
TEMPLATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to strip a html link from the crumb.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param string $html The crumb's html.
|
||||
* @return string A crumb html without links.
|
||||
*/
|
||||
public function stripLink( $html ) {
|
||||
return preg_replace( '/<a\s.*?>|<\/a>/is', '', (string) $html );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the breadcrumb configured separator.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @return string The separator html.
|
||||
*/
|
||||
public function getSeparator() {
|
||||
$separator = apply_filters( 'aioseo_breadcrumbs_separator_symbol', aioseo()->options->breadcrumbs->separator );
|
||||
|
||||
return apply_filters( 'aioseo_breadcrumbs_separator', '<span class="aioseo-breadcrumb-separator">' . esc_html( $separator ) . '</span>' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to filter the linkCurrentItem option.
|
||||
*
|
||||
* @since 4.1.3
|
||||
*
|
||||
* @return bool Link current item.
|
||||
*/
|
||||
public function linkCurrentItem() {
|
||||
return apply_filters( 'aioseo_breadcrumbs_link_current_item', aioseo()->options->breadcrumbs->linkCurrentItem );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Breadcrumbs;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class Shortcode.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*/
|
||||
class Shortcode {
|
||||
/**
|
||||
* Shortcode constructor.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*/
|
||||
public function __construct() {
|
||||
add_shortcode( 'aioseo_breadcrumbs', [ $this, 'display' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcode callback.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @return string|void The breadcrumb html.
|
||||
*/
|
||||
public function display() {
|
||||
return aioseo()->breadcrumbs->frontend->display( false );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Breadcrumbs;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to replace tag values with their data counterparts.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*/
|
||||
class Tags {
|
||||
/**
|
||||
* Tags constructor.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*/
|
||||
public function __construct() {
|
||||
aioseo()->tags->addContext( $this->getContexts() );
|
||||
aioseo()->tags->addTags( $this->getTags() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the tags in the string provided.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param string $string The string with tags.
|
||||
* @param array $item The breadcrumb item.
|
||||
* @param boolean $stripPunctuation Whether we should strip punctuation after the tags have been converted.
|
||||
* @return string The string with tags replaced.
|
||||
*/
|
||||
public function replaceTags( $string, $item, $stripPunctuation = false ) {
|
||||
if ( ! $string || ! preg_match( '/#/', (string) $string ) ) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
// Replace separator tag so we don't strip it as punctuation.
|
||||
$separatorTag = aioseo()->tags->denotationChar . 'separator_sa';
|
||||
$string = preg_replace( "/$separatorTag(?![a-zA-Z0-9_])/im", '>thisisjustarandomplaceholder<', (string) $string );
|
||||
|
||||
// Replace custom breadcrumb tags.
|
||||
foreach ( $this->getTags() as $tag ) {
|
||||
$tagId = aioseo()->tags->denotationChar . $tag['id'];
|
||||
$pattern = "/$tagId(?![a-zA-Z0-9_])/im";
|
||||
if ( preg_match( $pattern, (string) $string ) ) {
|
||||
$tagValue = str_replace( '$', '\$', $this->getTagValue( $tag, $item ) );
|
||||
$string = preg_replace( $pattern, $tagValue, (string) $string );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $stripPunctuation ) {
|
||||
$string = aioseo()->helpers->stripPunctuation( $string );
|
||||
}
|
||||
|
||||
// Remove any remaining tags from the title attribute.
|
||||
$string = preg_replace_callback( '/title="([^"]*)"/i', function ( $matches ) {
|
||||
$sanitizedTitle = wp_strip_all_tags( html_entity_decode( $matches[1] ) );
|
||||
|
||||
return 'title="' . esc_attr( $sanitizedTitle ) . '"';
|
||||
}, html_entity_decode( $string ) );
|
||||
|
||||
return preg_replace(
|
||||
'/>thisisjustarandomplaceholder<(?![a-zA-Z0-9_])/im',
|
||||
aioseo()->helpers->decodeHtmlEntities( aioseo()->options->searchAppearance->global->separator ),
|
||||
(string) $string
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the tag to replace.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param string $tag The tag to look for.
|
||||
* @param int $item The crumb array.
|
||||
* @return string The value of the tag.
|
||||
*/
|
||||
public function getTagValue( $tag, $item ) {
|
||||
$product = false;
|
||||
if ( 0 === stripos( $tag['id'], 'breadcrumb_wc_product_' ) ) {
|
||||
$product = wc_get_product( $item['reference'] );
|
||||
if ( ! $product ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch ( $tag['id'] ) {
|
||||
case 'breadcrumb_link':
|
||||
return $item['link'];
|
||||
case 'breadcrumb_separator':
|
||||
return aioseo()->breadcrumbs->frontend->getSeparator();
|
||||
case 'breadcrumb_wc_product_price':
|
||||
return $product ? wc_price( $product->get_price() ) : '';
|
||||
case 'breadcrumb_wc_product_sku':
|
||||
return $product ? $product->get_sku() : '';
|
||||
case 'breadcrumb_wc_product_brand':
|
||||
return $product ? aioseo()->helpers->getWooCommerceBrand( $product->get_id() ) : '';
|
||||
case 'breadcrumb_author_first_name':
|
||||
return $item['reference']->first_name;
|
||||
case 'breadcrumb_author_last_name':
|
||||
return $item['reference']->last_name;
|
||||
case 'breadcrumb_archive_post_type_name':
|
||||
return $item['reference']->label;
|
||||
case 'breadcrumb_search_string':
|
||||
return $item['reference'];
|
||||
case 'breadcrumb_format_page_number':
|
||||
return $item['reference']['paged'];
|
||||
default:
|
||||
return $item['label'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets our breadcrumb custom tags.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @return array An array of tags.
|
||||
*/
|
||||
public function getTags() {
|
||||
$tags = [
|
||||
[
|
||||
'id' => 'breadcrumb_link',
|
||||
'name' => __( 'Permalink', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The permalink.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_label',
|
||||
'name' => __( 'Label', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The label.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_post_title',
|
||||
// Translators: 1 - The type of page (Post, Page, Category, Tag, etc.).
|
||||
'name' => sprintf( __( '%1$s Title', 'all-in-one-seo-pack' ), 'Post' ),
|
||||
'description' => __( 'The original title of the current post.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_taxonomy_title',
|
||||
// Translators: 1 - The type of page (Post, Page, Category, Tag, etc.).
|
||||
'name' => sprintf( __( '%1$s Title', 'all-in-one-seo-pack' ), 'Category' ),
|
||||
// Translators: 1 - The name of a taxonomy.
|
||||
'description' => sprintf( __( 'The %1$s title.', 'all-in-one-seo-pack' ), 'Category' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_separator',
|
||||
'name' => __( 'Separator', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The crumb separator.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_blog_page_title',
|
||||
'name' => __( 'Blog Page Title', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The blog page title.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_author_display_name',
|
||||
'name' => __( 'Author Display Name', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The author\'s display name.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_author_first_name',
|
||||
'name' => __( 'Author First Name', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The author\'s first name.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_author_last_name',
|
||||
'name' => __( 'Author Last Name', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The author\'s last name.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_search_result_format',
|
||||
'name' => __( 'Search result format', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The search result format.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_404_error_format',
|
||||
'name' => __( '404 Error Format', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The 404 error format.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_date_archive_year',
|
||||
'name' => __( 'Year', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The year.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_date_archive_month',
|
||||
'name' => __( 'Month', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The month.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_date_archive_day',
|
||||
'name' => __( 'Day', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The day.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_search_string',
|
||||
'name' => __( 'Search String', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The search string.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_format_page_number',
|
||||
'name' => __( 'Page Number', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The page number.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_archive_post_type_format',
|
||||
'name' => __( 'Archive format', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The archive format.', 'all-in-one-seo-pack' )
|
||||
],
|
||||
[
|
||||
'id' => 'breadcrumb_archive_post_type_name',
|
||||
'name' => __( 'Post Type Name', 'all-in-one-seo-pack' ),
|
||||
'description' => __( 'The archive post type name.', 'all-in-one-seo-pack' )
|
||||
]
|
||||
];
|
||||
|
||||
$postTypes = aioseo()->helpers->getPublicPostTypes();
|
||||
foreach ( $postTypes as $postType ) {
|
||||
if ( 'product' === $postType['name'] && aioseo()->helpers->isWoocommerceActive() ) {
|
||||
$tags[] = [
|
||||
'id' => 'breadcrumb_wc_product_price',
|
||||
// Translators: 1 - The name of a post type.
|
||||
'name' => sprintf( __( '%1$s Price', 'all-in-one-seo-pack' ), $postType['singular'] ),
|
||||
// Translators: 1 - The name of a post type.
|
||||
'description' => sprintf( __( 'The %1$s price.', 'all-in-one-seo-pack' ), $postType['singular'] )
|
||||
];
|
||||
$tags[] = [
|
||||
'id' => 'breadcrumb_wc_product_sku',
|
||||
// Translators: 1 - The name of a post type.
|
||||
'name' => sprintf( __( '%1$s SKU', 'all-in-one-seo-pack' ), $postType['singular'] ),
|
||||
// Translators: 1 - The name of a post type.
|
||||
'description' => sprintf( __( 'The %1$s SKU.', 'all-in-one-seo-pack' ), $postType['singular'] )
|
||||
];
|
||||
$tags[] = [
|
||||
'id' => 'breadcrumb_wc_product_brand',
|
||||
// Translators: 1 - The name of a post type.
|
||||
'name' => sprintf( __( '%1$s Brand', 'all-in-one-seo-pack' ), $postType['singular'] ),
|
||||
// Translators: 1 - The name of a post type.
|
||||
'description' => sprintf( __( 'The %1$s brand.', 'all-in-one-seo-pack' ), $postType['singular'] )
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets our breadcrumb contexts.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @return array An array of contexts.
|
||||
*/
|
||||
public function getContexts() {
|
||||
$contexts = [];
|
||||
|
||||
$baseTags = [ 'breadcrumb_link', 'breadcrumb_separator' ];
|
||||
|
||||
$postTypes = aioseo()->helpers->getPublicPostTypes();
|
||||
foreach ( $postTypes as $postType ) {
|
||||
$contexts[ 'breadcrumbs-post-type-' . $postType['name'] ] = array_merge( $baseTags, [ 'breadcrumb_post_title' ] );
|
||||
|
||||
if ( 'product' === $postType['name'] && aioseo()->helpers->isWoocommerceActive() ) {
|
||||
$contexts[ 'breadcrumbs-post-type-' . $postType['name'] ] = array_merge( $contexts[ 'breadcrumbs-post-type-' . $postType['name'] ], [
|
||||
'breadcrumb_wc_product_price',
|
||||
'breadcrumb_wc_product_sku',
|
||||
'breadcrumb_wc_product_brand'
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
$taxonomies = aioseo()->helpers->getPublicTaxonomies();
|
||||
foreach ( $taxonomies as $taxonomy ) {
|
||||
$contexts[ 'breadcrumbs-taxonomy-' . $taxonomy['name'] ] = array_merge( $baseTags, [ 'breadcrumb_taxonomy_title' ] );
|
||||
}
|
||||
|
||||
$archives = aioseo()->helpers->getPublicPostTypes( false, true, true );
|
||||
foreach ( $archives as $archive ) {
|
||||
$contexts[ 'breadcrumbs-post-type-archive-' . $archive['name'] ] = array_merge( $baseTags, [
|
||||
'breadcrumb_archive_post_type_format',
|
||||
'breadcrumb_archive_post_type_name'
|
||||
] );
|
||||
}
|
||||
|
||||
$contexts['breadcrumbs-blog-archive'] = array_merge( $baseTags, [ 'breadcrumb_blog_page_title' ] );
|
||||
|
||||
$contexts['breadcrumbs-author'] = array_merge( $baseTags, [
|
||||
'breadcrumb_author_display_name',
|
||||
'breadcrumb_author_first_name',
|
||||
'breadcrumb_author_last_name'
|
||||
] );
|
||||
|
||||
$contexts['breadcrumbs-search'] = array_merge( $baseTags, [ 'breadcrumb_search_result_format', 'breadcrumb_search_string' ] );
|
||||
$contexts['breadcrumbs-notFound'] = array_merge( $baseTags, [ 'breadcrumb_404_error_format' ] );
|
||||
$contexts['breadcrumbs-date-archive-year'] = array_merge( $baseTags, [ 'breadcrumb_date_archive_year' ] );
|
||||
$contexts['breadcrumbs-date-archive-month'] = array_merge( $baseTags, [ 'breadcrumb_date_archive_month' ] );
|
||||
$contexts['breadcrumbs-date-archive-day'] = array_merge( $baseTags, [ 'breadcrumb_date_archive_day' ] );
|
||||
|
||||
$contexts['breadcrumbs-format-archive'] = [ 'breadcrumb_archive_post_type_name' ];
|
||||
$contexts['breadcrumbs-format-search'] = [ 'breadcrumb_search_string' ];
|
||||
$contexts['breadcrumbs-format-paged'] = [ 'breadcrumb_format_page_number' ];
|
||||
|
||||
return $contexts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Breadcrumbs;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class Widget.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*/
|
||||
class Widget extends \WP_Widget {
|
||||
/**
|
||||
* The default attributes.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $defaults = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*/
|
||||
public function __construct() {
|
||||
// Widget defaults.
|
||||
$this->defaults = [
|
||||
'title' => ''
|
||||
];
|
||||
|
||||
// Widget Slug.
|
||||
$widgetSlug = 'aioseo-breadcrumb-widget';
|
||||
|
||||
// Widget basics.
|
||||
$widgetOps = [
|
||||
'classname' => $widgetSlug,
|
||||
'description' => esc_html__( 'Display the current page breadcrumb.', 'all-in-one-seo-pack' ),
|
||||
];
|
||||
|
||||
// Widget controls.
|
||||
$controlOps = [
|
||||
'id_base' => $widgetSlug,
|
||||
];
|
||||
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
$name = sprintf( esc_html__( '%1$s - Breadcrumbs', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME );
|
||||
$name .= ' ' . esc_html__( '(legacy)', 'all-in-one-seo-pack' );
|
||||
parent::__construct( $widgetSlug, $name, $widgetOps, $controlOps );
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget callback.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param array $args Widget args.
|
||||
* @param array $instance The widget instance options.
|
||||
* @return void
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
// Merge with defaults.
|
||||
$instance = wp_parse_args( (array) $instance, $this->defaults );
|
||||
|
||||
echo $args['before_widget'];
|
||||
|
||||
// Title.
|
||||
if ( ! empty( $instance['title'] ) ) {
|
||||
echo $args['before_title'];
|
||||
echo apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
|
||||
echo $args['after_title'];
|
||||
}
|
||||
|
||||
// If not being previewed in the Customizer maybe show the dummy preview.
|
||||
if (
|
||||
! is_customize_preview() &&
|
||||
(
|
||||
false !== strpos( wp_get_referer(), admin_url( 'widgets.php' ) ) ||
|
||||
false !== strpos( wp_get_referer(), admin_url( 'customize.php' ) )
|
||||
)
|
||||
) {
|
||||
aioseo()->breadcrumbs->frontend->preview();
|
||||
} else {
|
||||
aioseo()->breadcrumbs->frontend->display();
|
||||
}
|
||||
|
||||
echo $args['after_widget'];
|
||||
// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget option update.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param array $newInstance New instance options.
|
||||
* @param array $oldInstance Old instance options.
|
||||
* @return array Processed new instance options.
|
||||
*/
|
||||
public function update( $newInstance, $oldInstance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$newInstance['title'] = wp_strip_all_tags( $newInstance['title'] );
|
||||
|
||||
return $newInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget options form.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param array $instance The widget instance options.
|
||||
* @return void
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
// Merge with defaults.
|
||||
$instance = wp_parse_args( (array) $instance, $this->defaults );
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
|
||||
<?php echo esc_html( __( 'Title:', 'all-in-one-seo-pack' ) ); ?>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
|
||||
name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
|
||||
value="<?php echo esc_attr( $instance['title'] ); ?>"
|
||||
class="widefat"
|
||||
/>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Core;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Options;
|
||||
use AIOSEO\Plugin\Common\Utils;
|
||||
|
||||
/**
|
||||
* Loads core classes.
|
||||
*
|
||||
* @since 4.1.9
|
||||
*/
|
||||
class Core {
|
||||
/**
|
||||
* List of AIOSEO tables.
|
||||
*
|
||||
* @since 4.2.5
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $aioseoTables = [
|
||||
'aioseo_cache',
|
||||
'aioseo_crawl_cleanup_blocked_args',
|
||||
'aioseo_crawl_cleanup_logs',
|
||||
'aioseo_links',
|
||||
'aioseo_links_suggestions',
|
||||
'aioseo_notifications',
|
||||
'aioseo_posts',
|
||||
'aioseo_redirects',
|
||||
'aioseo_redirects_404',
|
||||
'aioseo_redirects_404_logs',
|
||||
'aioseo_redirects_hits',
|
||||
'aioseo_redirects_logs',
|
||||
'aioseo_terms',
|
||||
'aioseo_search_statistics_objects',
|
||||
'aioseo_revisions'
|
||||
];
|
||||
|
||||
/**
|
||||
* Filesystem class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Utils\Filesystem
|
||||
*/
|
||||
public $fs = null;
|
||||
|
||||
/**
|
||||
* Assets class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Utils\Assets
|
||||
*/
|
||||
public $assets = null;
|
||||
|
||||
/**
|
||||
* DB class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Utils\Database
|
||||
*/
|
||||
public $db = null;
|
||||
|
||||
/**
|
||||
* Cache class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Utils\Cache
|
||||
*/
|
||||
public $cache = null;
|
||||
|
||||
/**
|
||||
* NetworkCache class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Utils\NetworkCache
|
||||
*/
|
||||
public $networkCache = null;
|
||||
|
||||
/**
|
||||
* CachePrune class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Utils\CachePrune
|
||||
*/
|
||||
public $cachePrune = null;
|
||||
|
||||
/**
|
||||
* Options Cache class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Options\Cache
|
||||
*/
|
||||
public $optionsCache = null;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.9
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->fs = new Utils\Filesystem( $this );
|
||||
$this->assets = new Utils\Assets( $this );
|
||||
$this->db = new Utils\Database();
|
||||
$this->cache = new Utils\Cache();
|
||||
$this->networkCache = new Utils\NetworkCache();
|
||||
$this->cachePrune = new Utils\CachePrune();
|
||||
$this->optionsCache = new Options\Cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the DB tables with prefix.
|
||||
*
|
||||
* @since 4.2.5
|
||||
*
|
||||
* @return array An array of tables.
|
||||
*/
|
||||
public function getDbTables() {
|
||||
global $wpdb;
|
||||
|
||||
$tables = [];
|
||||
foreach ( $this->aioseoTables as $tableName ) {
|
||||
$tables[] = $wpdb->prefix . $tableName;
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current request is uninstalling (deleting) AIOSEO.
|
||||
*
|
||||
* @since 4.3.7
|
||||
*
|
||||
* @return bool Whether AIOSEO is being uninstalled/deleted or not.
|
||||
*/
|
||||
public function isUninstalling() {
|
||||
if (
|
||||
defined( 'AIOSEO_FILE' ) &&
|
||||
defined( 'WP_UNINSTALL_PLUGIN' )
|
||||
) {
|
||||
// Make sure `plugin_basename()` exists.
|
||||
include_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
|
||||
return WP_UNINSTALL_PLUGIN === plugin_basename( AIOSEO_FILE );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace AIOSEO\Plugin\Common\EmailReports;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* EmailReports class.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*/
|
||||
class EmailReports {
|
||||
/**
|
||||
* Mail object.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @var Mail
|
||||
*/
|
||||
public $mail = null;
|
||||
|
||||
/**
|
||||
* Summary object.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @var Summary\Summary
|
||||
*/
|
||||
public $summary;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->mail = new Mail();
|
||||
$this->summary = new Summary\Summary();
|
||||
|
||||
add_action( 'aioseo_email_reports_enable_reminder', [ $this, 'enableReminder' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable reminder.
|
||||
*
|
||||
* @since 4.7.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enableReminder() {
|
||||
// User already enabled email reports.
|
||||
if ( aioseo()->options->advanced->emailSummary->enable ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if notification exists.
|
||||
$notification = Models\Notification::getNotificationByName( 'email-reports-enable-reminder' );
|
||||
if ( $notification->exists() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add notification.
|
||||
Models\Notification::addNotification( [
|
||||
'slug' => uniqid(),
|
||||
'notification_name' => 'email-reports-enable-reminder',
|
||||
'title' => __( 'Email Reports', 'all-in-one-seo-pack' ),
|
||||
'content' => __( 'Stay ahead in SEO with our new email digest! Get the latest tips, trends, and tools delivered right to your inbox, helping you optimize smarter and faster. Enable it today and never miss an update that can take your rankings to the next level.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
'type' => 'info',
|
||||
'level' => [ 'all' ],
|
||||
'button1_label' => __( 'Enable Email Reports', 'all-in-one-seo-pack' ),
|
||||
'button1_action' => 'https://route#aioseo-settings&aioseo-scroll=aioseo-email-summary-row&aioseo-highlight=aioseo-email-summary-row:advanced',
|
||||
'button2_label' => __( 'All Good, I\'m already getting it', 'all-in-one-seo-pack' ),
|
||||
'button2_action' => 'http://action#notification/email-reports-enable',
|
||||
'start' => gmdate( 'Y-m-d H:i:s' )
|
||||
] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace AIOSEO\Plugin\Common\EmailReports;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mail class.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*/
|
||||
class Mail {
|
||||
/**
|
||||
* Send the email.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param mixed $to Receiver.
|
||||
* @param mixed $subject Email subject.
|
||||
* @param mixed $message Message.
|
||||
* @param array $headers Email headers.
|
||||
* @return bool Whether the email was sent successfully.
|
||||
*/
|
||||
public function send( $to, $subject, $message, $headers = [ 'Content-Type: text/html; charset=UTF-8' ] ) {
|
||||
return wp_mail( $to, $subject, $message, $headers );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,693 @@
|
||||
<?php
|
||||
|
||||
namespace AIOSEO\Plugin\Common\EmailReports\Summary;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* Summary content class.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*/
|
||||
class Content {
|
||||
/**
|
||||
* The date range data.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $dateRange;
|
||||
|
||||
/**
|
||||
* The SEO Statistics data.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $seoStatistics = [];
|
||||
|
||||
/**
|
||||
* The Keywords data.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $keywords = [];
|
||||
|
||||
/**
|
||||
* The Search Statistics page URL.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $searchStatisticsUrl;
|
||||
|
||||
/**
|
||||
* The featured image placeholder URL.
|
||||
*
|
||||
* @since 4.7.3
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $featuredImagePlaceholder = 'https://static.aioseo.io/report/ste/featured-image-placeholder.png';
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param array $dateRange The date range data.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $dateRange ) {
|
||||
$this->dateRange = $dateRange;
|
||||
$this->searchStatisticsUrl = admin_url( 'admin.php?page=aioseo-search-statistics' );
|
||||
|
||||
$this->setSeoStatistics();
|
||||
$this->setKeywords();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the SEO Statistics data.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setSeoStatistics() {
|
||||
try {
|
||||
$seoStatistics = aioseo()->searchStatistics->getSeoStatisticsData( [
|
||||
'startDate' => gmdate( 'Y-m-d', $this->dateRange['startDateRaw'] ),
|
||||
'endDate' => gmdate( 'Y-m-d', $this->dateRange['endDateRaw'] ),
|
||||
'orderBy' => 'clicks',
|
||||
'orderDir' => 'desc',
|
||||
'limit' => '5',
|
||||
'offset' => '0',
|
||||
'filter' => 'all',
|
||||
] );
|
||||
|
||||
if ( empty( $seoStatistics['data'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->seoStatistics = $seoStatistics['data'];
|
||||
} catch ( \Exception $e ) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Keywords data.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setKeywords() {
|
||||
try {
|
||||
$keywords = aioseo()->searchStatistics->getKeywordsData( [
|
||||
'startDate' => gmdate( 'Y-m-d', $this->dateRange['startDateRaw'] ),
|
||||
'endDate' => gmdate( 'Y-m-d', $this->dateRange['endDateRaw'] ),
|
||||
'orderBy' => 'clicks',
|
||||
'orderDir' => 'desc',
|
||||
'limit' => '5',
|
||||
'offset' => '0',
|
||||
'filter' => 'all',
|
||||
] );
|
||||
|
||||
if ( empty( $keywords['data'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->keywords = $keywords['data'];
|
||||
} catch ( \Exception $e ) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the content performance data.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @return array The content performance data or an empty array.
|
||||
*/
|
||||
public function getPostsStatistics() {
|
||||
if ( ! $this->seoStatistics ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [
|
||||
'winning' => [
|
||||
'url' => add_query_arg( [
|
||||
'aioseo-scroll' => 'aioseo-search-statistics-post-table',
|
||||
'aioseo-tab' => 'seo-statistics',
|
||||
'table-filter' => 'TopWinningPages'
|
||||
], $this->searchStatisticsUrl ),
|
||||
'items' => []
|
||||
],
|
||||
'losing' => [
|
||||
'url' => add_query_arg( [
|
||||
'aioseo-scroll' => 'aioseo-search-statistics-post-table',
|
||||
'aioseo-tab' => 'seo-statistics',
|
||||
'table-filter' => 'TopLosingPages'
|
||||
], $this->searchStatisticsUrl ),
|
||||
'items' => []
|
||||
]
|
||||
];
|
||||
|
||||
foreach ( array_slice( $this->seoStatistics['pages']['topWinning']['rows'], 0, 3 ) as $row ) {
|
||||
$postId = $row['objectId'] ?? 0;
|
||||
$result['winning']['items'][] = [
|
||||
'title' => $row['objectTitle'],
|
||||
'url' => get_permalink( $postId ),
|
||||
'tru_seo' => aioseo()->helpers->isTruSeoEligible( $postId ) ? $this->parseSeoScore( $row['seoScore'] ?? 0 ) : [],
|
||||
'clicks' => $this->parseClicks( $row['clicks'] ),
|
||||
'difference' => [
|
||||
'clicks' => $this->parseDifference( $row['difference']['clicks'] ?? '' ),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
$result['winning']['show_tru_seo'] = ! empty( array_filter( array_column( $result['winning']['items'], 'tru_seo' ) ) );
|
||||
|
||||
foreach ( array_slice( $this->seoStatistics['pages']['topLosing']['rows'], 0, 3 ) as $row ) {
|
||||
$postId = $row['objectId'] ?? 0;
|
||||
$result['losing']['items'][] = [
|
||||
'title' => $row['objectTitle'],
|
||||
'url' => get_permalink( $postId ),
|
||||
'tru_seo' => aioseo()->helpers->isTruSeoEligible( $postId ) ? $this->parseSeoScore( $row['seoScore'] ?? 0 ) : [],
|
||||
'clicks' => $this->parseClicks( $row['clicks'] ),
|
||||
'difference' => [
|
||||
'clicks' => $this->parseDifference( $row['difference']['clicks'] ?? '' ),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
$result['losing']['show_tru_seo'] = ! empty( array_filter( array_column( $result['losing']['items'], 'tru_seo' ) ) );
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the milestones data.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @return array The milestones data or an empty array.
|
||||
*/
|
||||
public function getMilestones() { // phpcs:ignore Generic.Files.LineLength.MaxExceeded
|
||||
$milestones = [];
|
||||
if ( ! $this->seoStatistics ) {
|
||||
return $milestones;
|
||||
}
|
||||
|
||||
$currentData = [
|
||||
'impressions' => $this->seoStatistics['statistics']['impressions'] ?? null,
|
||||
'clicks' => $this->seoStatistics['statistics']['clicks'] ?? null,
|
||||
'ctr' => $this->seoStatistics['statistics']['ctr'] ?? null,
|
||||
'keywords' => $this->seoStatistics['statistics']['keywords'] ?? null,
|
||||
];
|
||||
$difference = [
|
||||
'impressions' => $this->seoStatistics['statistics']['difference']['impressions'] ?? null,
|
||||
'clicks' => $this->seoStatistics['statistics']['difference']['clicks'] ?? null,
|
||||
'ctr' => $this->seoStatistics['statistics']['difference']['ctr'] ?? null,
|
||||
'keywords' => $this->seoStatistics['statistics']['difference']['keywords'] ?? null,
|
||||
];
|
||||
|
||||
if ( is_numeric( $currentData['impressions'] ) && is_numeric( $difference['impressions'] ) ) {
|
||||
$intDifference = intval( $difference['impressions'] );
|
||||
$message = esc_html__( 'Your site has received the same number of impressions compared to the previous period.', 'all-in-one-seo-pack' );
|
||||
|
||||
if ( $intDifference > 0 ) {
|
||||
// Translators: 1 - The number of impressions, 2 - The percentage increase.
|
||||
$message = esc_html__( 'Your site has received %1$s more impressions compared to the previous period, which is a %2$s increase.', 'all-in-one-seo-pack' );
|
||||
}
|
||||
|
||||
if ( $intDifference < 0 ) {
|
||||
// Translators: 1 - The number of impressions, 2 - The percentage increase.
|
||||
$message = esc_html__( 'Your site has received %1$s fewer impressions compared to the previous period, which is a %2$s decrease.', 'all-in-one-seo-pack' );
|
||||
}
|
||||
|
||||
if ( false !== strpos( $message, '%1' ) ) {
|
||||
$percentageDiff = 0 === absint( $currentData['impressions'] )
|
||||
? 100
|
||||
: round( ( absint( $intDifference ) / absint( $currentData['impressions'] ) ) * 100, 2 );
|
||||
$percentageDiff = false !== strpos( $percentageDiff, '.' )
|
||||
? number_format_i18n( $percentageDiff, count( explode( '.', $percentageDiff ) ) )
|
||||
: $percentageDiff;
|
||||
$message = sprintf(
|
||||
$message,
|
||||
'<strong>' . aioseo()->helpers->compactNumber( absint( $intDifference ) ) . '</strong>',
|
||||
'<strong>' . $percentageDiff . '%</strong>'
|
||||
);
|
||||
}
|
||||
|
||||
$milestones[] = [
|
||||
'message' => $message,
|
||||
'background' => '#f0f6ff',
|
||||
'color' => '#004F9D',
|
||||
'icon' => 'icon-milestone-impressions'
|
||||
];
|
||||
}
|
||||
|
||||
if ( is_numeric( $currentData['clicks'] ) && is_numeric( $difference['clicks'] ) ) {
|
||||
$intDifference = intval( $difference['clicks'] );
|
||||
$message = esc_html__( 'Your site has received the same number of clicks compared to the previous period.', 'all-in-one-seo-pack' );
|
||||
|
||||
if ( $intDifference > 0 ) {
|
||||
// Translators: 1 - The number of clicks, 2 - The percentage increase.
|
||||
$message = esc_html__( 'Your site has received %1$s more clicks compared to the previous period, which is a %2$s increase.', 'all-in-one-seo-pack' );
|
||||
}
|
||||
|
||||
if ( $intDifference < 0 ) {
|
||||
// Translators: 1 - The number of clicks, 2 - The percentage increase.
|
||||
$message = esc_html__( 'Your site has received %1$s fewer clicks compared to the previous period, which is a %2$s decrease.', 'all-in-one-seo-pack' );
|
||||
}
|
||||
|
||||
if ( false !== strpos( $message, '%1' ) ) {
|
||||
$percentageDiff = 0 === absint( $currentData['clicks'] )
|
||||
? 100
|
||||
: round( ( absint( $intDifference ) / absint( $currentData['clicks'] ) ) * 100, 2 );
|
||||
$percentageDiff = false !== strpos( $percentageDiff, '.' )
|
||||
? number_format_i18n( $percentageDiff, count( explode( '.', $percentageDiff ) ) )
|
||||
: $percentageDiff;
|
||||
$message = sprintf(
|
||||
$message,
|
||||
'<strong>' . aioseo()->helpers->compactNumber( absint( $intDifference ) ) . '</strong>',
|
||||
'<strong>' . $percentageDiff . '%</strong>'
|
||||
);
|
||||
}
|
||||
|
||||
$milestones[] = [
|
||||
'message' => $message,
|
||||
'background' => '#ecfdf5',
|
||||
'color' => '#077647',
|
||||
'icon' => 'icon-milestone-clicks'
|
||||
];
|
||||
}
|
||||
|
||||
if ( is_numeric( $currentData['ctr'] ) && is_numeric( $difference['ctr'] ) ) {
|
||||
$intDifference = floatval( $difference['ctr'] );
|
||||
$message = esc_html__( 'Your site has the same CTR compared to the previous period.', 'all-in-one-seo-pack' );
|
||||
|
||||
if ( $intDifference > 0 ) {
|
||||
// Translators: 1 - The CTR.
|
||||
$message = esc_html__( 'Your site has a %1$s higher CTR compared to the previous period.', 'all-in-one-seo-pack' );
|
||||
}
|
||||
|
||||
if ( $intDifference < 0 ) {
|
||||
// Translators: 1 - The CTR.
|
||||
$message = esc_html__( 'Your site has a %1$s lower CTR compared to the previous period.', 'all-in-one-seo-pack' );
|
||||
}
|
||||
|
||||
if ( false !== strpos( $message, '%1' ) ) {
|
||||
$message = sprintf(
|
||||
$message,
|
||||
'<strong>' . number_format_i18n( abs( $intDifference ), count( explode( '.', $intDifference ) ) ) . '%</strong>'
|
||||
);
|
||||
}
|
||||
|
||||
$milestones[] = [
|
||||
'message' => $message,
|
||||
'background' => '#fffbeb',
|
||||
'color' => '#be6903',
|
||||
'icon' => 'icon-milestone-ctr'
|
||||
];
|
||||
}
|
||||
|
||||
if ( is_numeric( $currentData['keywords'] ) && is_numeric( $difference['keywords'] ) ) {
|
||||
$intDifference = intval( $difference['keywords'] );
|
||||
$message = esc_html__( 'Your site ranked for the same number of keywords compared to the previous period.', 'all-in-one-seo-pack' );
|
||||
|
||||
if ( $intDifference > 0 ) {
|
||||
// Translators: 1 - The number of keywords, 2 - The percentage increase.
|
||||
$message = esc_html__( 'Your site ranked for %1$s more keywords compared to the previous period, which is a %2$s increase.', 'all-in-one-seo-pack' );
|
||||
}
|
||||
|
||||
if ( $intDifference < 0 ) {
|
||||
// Translators: 1 - The number of keywords, 2 - The percentage increase.
|
||||
$message = esc_html__( 'Your site ranked for %1$s fewer keywords compared to the previous period, which is a %2$s decrease.', 'all-in-one-seo-pack' );
|
||||
}
|
||||
|
||||
if ( false !== strpos( $message, '%1' ) ) {
|
||||
$percentageDiff = 0 === absint( $currentData['keywords'] )
|
||||
? 100
|
||||
: round( ( absint( $intDifference ) / absint( $currentData['keywords'] ) ) * 100, 2 );
|
||||
$percentageDiff = false !== strpos( $percentageDiff, '.' )
|
||||
? number_format_i18n( $percentageDiff, count( explode( '.', $percentageDiff ) ) )
|
||||
: $percentageDiff;
|
||||
$message = sprintf(
|
||||
$message,
|
||||
'<strong>' . aioseo()->helpers->compactNumber( absint( $intDifference ) ) . '</strong>',
|
||||
'<strong>' . $percentageDiff . '%</strong>'
|
||||
);
|
||||
}
|
||||
|
||||
$milestones[] = [
|
||||
'message' => $message,
|
||||
'background' => '#fef2f2',
|
||||
'color' => '#ab2039',
|
||||
'icon' => 'icon-milestone-keywords'
|
||||
];
|
||||
}
|
||||
|
||||
return $milestones;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the keyword performance data.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @return array The keyword performance data or an empty array.
|
||||
*/
|
||||
public function getKeywords() {
|
||||
if ( ! $this->keywords ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [
|
||||
'winning' => [
|
||||
'url' => add_query_arg( [
|
||||
'aioseo-scroll' => 'aioseo-search-statistics-keywords-table',
|
||||
'aioseo-tab' => 'keyword-rank-tracker',
|
||||
'tab' => 'AllKeywords',
|
||||
'table-filter' => 'TopWinningKeywords'
|
||||
], $this->searchStatisticsUrl ),
|
||||
'items' => []
|
||||
],
|
||||
'losing' => [
|
||||
'url' => add_query_arg( [
|
||||
'aioseo-scroll' => 'aioseo-search-statistics-keywords-table',
|
||||
'aioseo-tab' => 'keyword-rank-tracker',
|
||||
'tab' => 'AllKeywords',
|
||||
'table-filter' => 'TopLosingKeywords'
|
||||
], $this->searchStatisticsUrl ),
|
||||
'items' => []
|
||||
]
|
||||
];
|
||||
|
||||
foreach ( array_slice( $this->keywords['topWinning'], 0, 3 ) as $row ) {
|
||||
$result['winning']['items'][] = [
|
||||
'title' => $row['keyword'],
|
||||
'clicks' => $this->parseClicks( $row['clicks'] ),
|
||||
'difference' => [
|
||||
'clicks' => $this->parseDifference( $row['difference']['clicks'] ?? '' ),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
foreach ( array_slice( $this->keywords['topLosing'], 0, 3 ) as $row ) {
|
||||
$result['losing']['items'][] = [
|
||||
'title' => $row['keyword'],
|
||||
'clicks' => $this->parseClicks( $row['clicks'] ),
|
||||
'difference' => [
|
||||
'clicks' => $this->parseDifference( $row['difference']['clicks'] ?? '' ),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the posts data.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @return array The posts' data.
|
||||
*/
|
||||
public function getAioPosts() {
|
||||
$result = [
|
||||
'publish' => [],
|
||||
'optimize' => [],
|
||||
'cta' => [
|
||||
'text' => esc_html__( 'Create New Post', 'all-in-one-seo-pack' ),
|
||||
'url' => admin_url( 'post-new.php' )
|
||||
],
|
||||
];
|
||||
|
||||
// 1. Retrieve the published posts.
|
||||
$publishPosts = aioseo()->core->db
|
||||
->start( 'posts as wp' )
|
||||
->select( 'wp.ID, wp.post_title, aio.seo_score' )
|
||||
->join( 'aioseo_posts as aio', 'aio.post_id = wp.ID', 'INNER' )
|
||||
->whereIn( 'wp.post_type', [ 'post' ] )
|
||||
->whereIn( 'wp.post_status', [ 'publish' ] )
|
||||
->orderBy( 'wp.post_date DESC' )
|
||||
->limit( 5 )
|
||||
->run()
|
||||
->result();
|
||||
|
||||
if ( $publishPosts ) {
|
||||
$items = $this->parsePosts( $publishPosts );
|
||||
$result['publish'] = [
|
||||
'url' => admin_url( 'edit.php?post_status=publish&post_type=post' ),
|
||||
'items' => $items,
|
||||
'show_stats' => ! empty( array_filter( array_column( $items, 'stats' ) ) ),
|
||||
'show_tru_seo' => ! empty( array_filter( array_column( $items, 'tru_seo' ) ) ),
|
||||
];
|
||||
}
|
||||
|
||||
// 2. Retrieve the posts to optimize.
|
||||
$optimizePosts = aioseo()->searchStatistics->getContentRankingsData( [
|
||||
'endDate' => gmdate( 'Y-m-d', $this->dateRange['endDateRaw'] ),
|
||||
'orderBy' => 'decayPercent',
|
||||
'orderDir' => 'asc',
|
||||
'limit' => '3',
|
||||
'offset' => '0',
|
||||
'filter' => 'all',
|
||||
] );
|
||||
|
||||
if ( is_array( $optimizePosts['data']['paginated']['rows'] ?? '' ) ) {
|
||||
$items = [];
|
||||
foreach ( array_slice( $optimizePosts['data']['paginated']['rows'], 0, 3 ) as $i => $row ) {
|
||||
$postId = $row['objectId'] ?? 0;
|
||||
$items[ $i ] = [
|
||||
'title' => $row['objectTitle'],
|
||||
'url' => get_permalink( $postId ),
|
||||
'image_url' => $this->getThumbnailUrl( $postId ),
|
||||
'tru_seo' => aioseo()->helpers->isTruSeoEligible( $postId ) ? $this->parseSeoScore( $row['seoScore'] ?? 0 ) : [],
|
||||
'decay_percent' => $this->parseDifference( $row['decayPercent'] ?? '', true ),
|
||||
'issues' => [
|
||||
'url' => add_query_arg( [
|
||||
'aioseo-tab' => 'post-detail',
|
||||
'post' => $postId
|
||||
], $this->searchStatisticsUrl ),
|
||||
'items' => []
|
||||
]
|
||||
];
|
||||
|
||||
$aioPost = Models\Post::getPost( $postId );
|
||||
if ( $aioPost ) {
|
||||
$items[ $i ]['issues']['items'] = aioseo()->searchStatistics->helpers->getSuggestedChanges( $aioPost );
|
||||
}
|
||||
}
|
||||
|
||||
$result['optimize'] = [
|
||||
'url' => add_query_arg( [
|
||||
'aioseo-tab' => 'content-rankings',
|
||||
], $this->searchStatisticsUrl ),
|
||||
'items' => $items,
|
||||
'show_tru_seo' => ! empty( array_filter( array_column( $items, 'tru_seo' ) ) ),
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the resources data.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @return array The resources' data.
|
||||
*/
|
||||
public function getResources() {
|
||||
$items = aioseo()->helpers->fetchAioseoArticles( true );
|
||||
|
||||
return array_slice( array_filter( $items ), 0, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if Search Statistics content is allowed.
|
||||
*
|
||||
* @since 4.7.3
|
||||
*
|
||||
* @return bool Whether Search Statistics content is allowed.
|
||||
*/
|
||||
public function allowSearchStatistics() {
|
||||
static $return = null;
|
||||
if ( isset( $return ) ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
$return = aioseo()->searchStatistics->api->auth->isConnected() &&
|
||||
aioseo()->license &&
|
||||
aioseo()->license->hasCoreFeature( 'search-statistics', 'seo-statistics' ) &&
|
||||
aioseo()->license->hasCoreFeature( 'search-statistics', 'keyword-rankings' );
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the SEO score.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param int|string $score The SEO score.
|
||||
* @return array The parsed SEO score.
|
||||
*/
|
||||
private function parseSeoScore( $score ) {
|
||||
$score = intval( $score );
|
||||
$parsed = [
|
||||
'value' => $score,
|
||||
'color' => '#a1a1a1',
|
||||
'text' => $score ? "$score/100" : esc_html__( 'N/A', 'all-in-one-seo-pack' ),
|
||||
];
|
||||
|
||||
if ( $parsed['value'] > 79 ) {
|
||||
$parsed['color'] = '#00aa63';
|
||||
} elseif ( $parsed['value'] > 49 ) {
|
||||
$parsed['color'] = '#ff8c00';
|
||||
} elseif ( $parsed['value'] > 0 ) {
|
||||
$parsed['color'] = '#df2a4a';
|
||||
}
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a difference.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param int|string $number The number to parse.
|
||||
* @param bool $percentage Whether to return the text result as a percentage.
|
||||
* @return array The parsed result.
|
||||
*/
|
||||
private function parseDifference( $number, $percentage = false ) {
|
||||
$parsed = [
|
||||
'color' => '#a1a1a1',
|
||||
'text' => esc_html__( 'N/A', 'all-in-one-seo-pack' ),
|
||||
];
|
||||
if ( ! is_numeric( $number ) ) {
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
$number = intval( $number );
|
||||
$parsed['text'] = aioseo()->helpers->compactNumber( absint( $number ) );
|
||||
|
||||
if ( $percentage ) {
|
||||
$parsed['text'] = $number . '%';
|
||||
}
|
||||
|
||||
if ( $number > 0 ) {
|
||||
$parsed['color'] = '#00aa63';
|
||||
} elseif ( $number < 0 ) {
|
||||
$parsed['color'] = '#df2a4a';
|
||||
}
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the clicks number.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param float|int|string $number The number of clicks.
|
||||
* @return string The parsed number of clicks.
|
||||
*/
|
||||
private function parseClicks( $number ) {
|
||||
return aioseo()->helpers->compactNumber( $number );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the posts data.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param array $posts The posts.
|
||||
* @return array The parsed posts' data.
|
||||
*/
|
||||
private function parsePosts( $posts ) {
|
||||
$parsed = [];
|
||||
foreach ( $posts as $k => $item ) {
|
||||
$parsed[ $k ] = [
|
||||
'title' => aioseo()->helpers->truncate( $item->post_title, 75 ),
|
||||
'url' => get_permalink( $item->ID ),
|
||||
'image_url' => $this->getThumbnailUrl( $item->ID ),
|
||||
'tru_seo' => aioseo()->helpers->isTruSeoEligible( $item->ID ) ? $this->parseSeoScore( $item->seo_score ?? 0 ) : [],
|
||||
'stats' => []
|
||||
];
|
||||
|
||||
try {
|
||||
$statistics = [];
|
||||
if (
|
||||
$this->allowSearchStatistics() &&
|
||||
method_exists( aioseo()->searchStatistics, 'getPostDetailSeoStatisticsData' )
|
||||
) {
|
||||
$statistics = aioseo()->searchStatistics->getPostDetailSeoStatisticsData( [
|
||||
'startDate' => gmdate( 'Y-m-d', $this->dateRange['startDateRaw'] ),
|
||||
'endDate' => gmdate( 'Y-m-d', $this->dateRange['endDateRaw'] ),
|
||||
'postId' => $item->ID,
|
||||
], false );
|
||||
}
|
||||
|
||||
if ( isset( $statistics['data']['statistics']['position'] ) ) {
|
||||
$parsed[ $k ]['stats'][] = [
|
||||
'icon' => 'position',
|
||||
'label' => esc_html__( 'Position', 'all-in-one-seo-pack' ),
|
||||
'value' => round( floatval( $statistics['data']['statistics']['position'] ) ),
|
||||
];
|
||||
}
|
||||
|
||||
if ( isset( $statistics['data']['statistics']['ctr'] ) ) {
|
||||
$value = round( floatval( $statistics['data']['statistics']['ctr'] ), 2 );
|
||||
$parsed[ $k ]['stats'][] = [
|
||||
'icon' => 'ctr',
|
||||
'label' => 'CTR',
|
||||
'value' => ( number_format_i18n( $value, count( explode( '.', $value ) ) ) ) . '%',
|
||||
];
|
||||
}
|
||||
|
||||
if ( isset( $statistics['data']['statistics']['impressions'] ) ) {
|
||||
$parsed[ $k ]['stats'][] = [
|
||||
'icon' => 'impressions',
|
||||
'label' => esc_html__( 'Impressions', 'all-in-one-seo-pack' ),
|
||||
'value' => aioseo()->helpers->compactNumber( $statistics['data']['statistics']['impressions'] ),
|
||||
];
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the thumbnail URL.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param int $postId The post ID.
|
||||
* @return string The post featured image URL (thumbnail size).
|
||||
*/
|
||||
private function getThumbnailUrl( $postId ) {
|
||||
$imageUrl = get_the_post_thumbnail_url( $postId );
|
||||
|
||||
return $imageUrl ?: $this->featuredImagePlaceholder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
<?php
|
||||
|
||||
namespace AIOSEO\Plugin\Common\EmailReports\Summary;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary class.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*/
|
||||
class Summary {
|
||||
/**
|
||||
* The action hook to execute when the event is run.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $actionHook = 'aioseo_report_summary';
|
||||
|
||||
/**
|
||||
* Recipient for the email. Multiple recipients can be separated by a comma.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $recipient;
|
||||
|
||||
/**
|
||||
* Email chosen frequency. Can be either 'weekly' or 'monthly'.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $frequency;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*/
|
||||
public function __construct() {
|
||||
// No need to run any of this during a WP AJAX request.
|
||||
if ( wp_doing_ajax() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// No need to keep trying scheduling unless on admin.
|
||||
add_action( 'admin_init', [ $this, 'maybeSchedule' ], 20 );
|
||||
|
||||
add_action( $this->actionHook, [ $this, 'cronTrigger' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* The summary cron callback.
|
||||
* Hooked into `{@see self::$actionHook}` action hook.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param string $frequency The frequency of the email.
|
||||
* @return void
|
||||
*/
|
||||
public function cronTrigger( $frequency ) {
|
||||
// Keep going only if the feature is enabled.
|
||||
if (
|
||||
! aioseo()->options->advanced->emailSummary->enable ||
|
||||
! apply_filters( 'aioseo_report_summary_enable', true, $frequency )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all recipients for the given frequency.
|
||||
$recipients = wp_list_filter( aioseo()->options->advanced->emailSummary->recipients, [ 'frequency' => $frequency ] );
|
||||
if ( ! $recipients ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Get only the email addresses.
|
||||
$recipients = array_column( $recipients, 'email' );
|
||||
|
||||
$this->run( [
|
||||
'recipient' => implode( ',', $recipients ),
|
||||
'frequency' => $frequency,
|
||||
] );
|
||||
} catch ( \Exception $e ) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger the sending of the summary.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @param array $data All the initial data needed for the summary to be sent.
|
||||
* @throws \Exception If the email could not be sent.
|
||||
* @return void
|
||||
*/
|
||||
public function run( $data ) {
|
||||
try {
|
||||
$this->recipient = $data['recipient'] ?? '';
|
||||
$this->frequency = $data['frequency'] ?? '';
|
||||
|
||||
aioseo()->emailReports->mail->send( $this->getRecipient(), $this->getSubject(), $this->getContentHtml(), $this->getHeaders() );
|
||||
} catch ( \Exception $e ) {
|
||||
throw new \Exception( esc_html( $e->getMessage() ), esc_html( $e->getCode() ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe (re)schedule the summary.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function maybeSchedule() {
|
||||
$allowedFrequencies = $this->getAllowedFrequencies();
|
||||
|
||||
// Add at least 6 hours after the day starts.
|
||||
$addToStart = HOUR_IN_SECONDS * 6;
|
||||
// Add the timezone offset.
|
||||
$addToStart -= aioseo()->helpers->getTimeZoneOffset();
|
||||
// Add a random time offset to avoid all emails being sent at the same time. 1440 * 3 = 3 days range.
|
||||
$addToStart += aioseo()->helpers->generateRandomTimeOffset( aioseo()->helpers->getSiteDomain( true ), 1440 * 3 ) * MINUTE_IN_SECONDS;
|
||||
|
||||
foreach ( $allowedFrequencies as $frequency => $data ) {
|
||||
aioseo()->actionScheduler->scheduleRecurrent( $this->actionHook, $data['start'] + $addToStart, $data['interval'], compact( 'frequency' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get one or more valid recipients.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @throws \Exception If no valid recipient was set for the email.
|
||||
* @return string The valid recipients.
|
||||
*/
|
||||
private function getRecipient() {
|
||||
$recipients = array_map( 'trim', explode( ',', $this->recipient ) );
|
||||
$recipients = array_filter( $recipients, 'is_email' );
|
||||
|
||||
if ( empty( $recipients ) ) {
|
||||
throw new \Exception( 'No valid recipient was set for the email.' ); // Not shown to the user.
|
||||
}
|
||||
|
||||
return implode( ',', $recipients );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email subject.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @return string The email subject.
|
||||
*/
|
||||
private function getSubject() {
|
||||
// Translators: 1 - Date range.
|
||||
$out = esc_html__( 'Your SEO Performance Report for %1$s', 'all-in-one-seo-pack' );
|
||||
|
||||
$dateRange = $this->getDateRange();
|
||||
$suffix = date_i18n( 'F', $dateRange['endDateRaw'] );
|
||||
if ( 'weekly' === $this->frequency ) {
|
||||
$suffix = $dateRange['range'];
|
||||
}
|
||||
|
||||
return sprintf( $out, $suffix );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content html.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @return string The email content.
|
||||
*/
|
||||
private function getContentHtml() { // phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$dateRange = $this->getDateRange();
|
||||
$content = new Content( $dateRange );
|
||||
$upsell = [
|
||||
'search-statistics' => []
|
||||
];
|
||||
$preHeader = sprintf(
|
||||
// Translators: 1 - The plugin short name ("AIOSEO").
|
||||
esc_html__( 'Dive into your top-performing pages with %1$s and uncover growth opportunities.', 'all-in-one-seo-pack' ),
|
||||
AIOSEO_PLUGIN_SHORT_NAME
|
||||
);
|
||||
$iconCalendar = 'weekly' === $this->frequency
|
||||
? 'icon-calendar-weekly'
|
||||
: 'icon-calendar-monthly';
|
||||
$heading = 'weekly' === $this->frequency
|
||||
? esc_html__( 'Your Weekly SEO Email Summary', 'all-in-one-seo-pack' )
|
||||
: esc_html__( 'Your Monthly SEO Email Summary', 'all-in-one-seo-pack' );
|
||||
$subheading = 'weekly' === $this->frequency
|
||||
? esc_html__( 'Let\'s take a look at your SEO updates and content progress this week.', 'all-in-one-seo-pack' )
|
||||
: esc_html__( 'Let\'s take a look at your SEO updates and content progress this month.', 'all-in-one-seo-pack' );
|
||||
$statisticsReport = [
|
||||
'posts' => [],
|
||||
'keywords' => [],
|
||||
'milestones' => [],
|
||||
'cta' => [
|
||||
'text' => esc_html__( 'See All SEO Statistics', 'all-in-one-seo-pack' ),
|
||||
'url' => $content->searchStatisticsUrl
|
||||
],
|
||||
];
|
||||
|
||||
if ( ! $content->allowSearchStatistics() ) {
|
||||
$upsell['search-statistics'] = [
|
||||
'cta' => [
|
||||
'text' => esc_html__( 'Unlock Search Statistics', 'all-in-one-seo-pack' ),
|
||||
'url' => $content->searchStatisticsUrl,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
if ( ! $upsell['search-statistics'] ) {
|
||||
$subheading = 'weekly' === $this->frequency
|
||||
? esc_html__( 'Let\'s take a look at how your site has performed in search results this week.', 'all-in-one-seo-pack' )
|
||||
: esc_html__( 'Let\'s take a look at how your site has performed in search results this month.', 'all-in-one-seo-pack' );
|
||||
|
||||
$statisticsReport['posts'] = $content->getPostsStatistics();
|
||||
$statisticsReport['keywords'] = $content->getKeywords();
|
||||
$statisticsReport['milestones'] = $content->getMilestones();
|
||||
}
|
||||
|
||||
$mktUrl = trailingslashit( AIOSEO_MARKETING_URL );
|
||||
$medium = 'email-report-summary';
|
||||
|
||||
$posts = $content->getAioPosts();
|
||||
$resources = [
|
||||
'posts' => array_map( function ( $item ) use ( $medium, $content ) {
|
||||
return array_merge( $item, [
|
||||
'url' => aioseo()->helpers->utmUrl( $item['url'], $medium ),
|
||||
'image' => [
|
||||
'url' => ! empty( $item['image']['sizes']['medium']['source_url'] )
|
||||
? $item['image']['sizes']['medium']['source_url']
|
||||
: $content->featuredImagePlaceholder
|
||||
]
|
||||
] );
|
||||
}, $content->getResources() ),
|
||||
'cta' => [
|
||||
'text' => esc_html__( 'See All Resources', 'all-in-one-seo-pack' ),
|
||||
'url' => aioseo()->helpers->utmUrl( 'https://aioseo.com/blog/', $medium ),
|
||||
],
|
||||
];
|
||||
$links = [
|
||||
'disable' => admin_url( 'admin.php?page=aioseo-settings&aioseo-scroll=aioseo-email-summary-row&aioseo-highlight=aioseo-email-summary-row&aioseo-tab=advanced' ),
|
||||
'update' => admin_url( 'update-core.php' ),
|
||||
'marketing-site' => aioseo()->helpers->utmUrl( $mktUrl, $medium ),
|
||||
'facebook' => aioseo()->helpers->utmUrl( $mktUrl . 'plugin/facebook', $medium ),
|
||||
'linkedin' => aioseo()->helpers->utmUrl( $mktUrl . 'plugin/linkedin', $medium ),
|
||||
'youtube' => aioseo()->helpers->utmUrl( $mktUrl . 'plugin/youtube', $medium ),
|
||||
'twitter' => aioseo()->helpers->utmUrl( $mktUrl . 'plugin/twitter', $medium ),
|
||||
];
|
||||
|
||||
ob_start();
|
||||
require AIOSEO_DIR . '/app/Common/Views/report/summary.php';
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email headers.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @return array The email headers.
|
||||
*/
|
||||
private function getHeaders() {
|
||||
return [ 'Content-Type: text/html; charset=UTF-8' ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all allowed frequencies.
|
||||
*
|
||||
* @since 4.7.2
|
||||
*
|
||||
* @return array The email allowed frequencies.
|
||||
*/
|
||||
private function getAllowedFrequencies() {
|
||||
$time = time();
|
||||
$secondsTillNow = $time - strtotime( 'today' );
|
||||
|
||||
return [
|
||||
'weekly' => [
|
||||
'interval' => WEEK_IN_SECONDS,
|
||||
'start' => strtotime( 'next Monday' ) - $time
|
||||
],
|
||||
'monthly' => [
|
||||
'interval' => MONTH_IN_SECONDS,
|
||||
'start' => ( strtotime( 'first day of next month' ) + ( DAY_IN_SECONDS * 2 ) - $secondsTillNow ) - $time
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the date range data based on the frequency.
|
||||
*
|
||||
* @since 4.7.3
|
||||
*
|
||||
* @return array The date range data.
|
||||
*/
|
||||
private function getDateRange() {
|
||||
$dateFormat = get_option( 'date_format' );
|
||||
|
||||
// If frequency is 'monthly'.
|
||||
$endDateRaw = strtotime( 'last day of last month' );
|
||||
$startDateRaw = strtotime( 'first day of last month' );
|
||||
|
||||
// If frequency is 'weekly'.
|
||||
if ( 'weekly' === $this->frequency ) {
|
||||
$endDateRaw = strtotime( 'last Saturday' );
|
||||
$startDateRaw = strtotime( 'last Sunday', $endDateRaw );
|
||||
}
|
||||
|
||||
$endDate = date_i18n( $dateFormat, $endDateRaw );
|
||||
$startDate = date_i18n( $dateFormat, $startDateRaw );
|
||||
|
||||
return [
|
||||
'endDate' => $endDate,
|
||||
'endDateRaw' => $endDateRaw,
|
||||
'startDate' => $startDate,
|
||||
'startDateRaw' => $startDateRaw,
|
||||
'range' => "$startDate - $endDate",
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Help;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class Help {
|
||||
/**
|
||||
* Source of the documentation content.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $url = 'https://cdn.aioseo.com/wp-content/docs.json';
|
||||
|
||||
/**
|
||||
* Settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $settings = [
|
||||
'docsUrl' => 'https://aioseo.com/docs/',
|
||||
'supportTicketUrl' => 'https://aioseo.com/account/support/',
|
||||
'upgradeUrl' => 'https://aioseo.com/pricing/'
|
||||
];
|
||||
|
||||
/**
|
||||
* Gets the URL for the notifications api.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return string The URL to use for the api requests.
|
||||
*/
|
||||
private function getUrl() {
|
||||
if ( defined( 'AIOSEO_DOCS_FEED_URL' ) ) {
|
||||
return AIOSEO_DOCS_FEED_URL;
|
||||
}
|
||||
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the help docs for our menus.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return array The help docs.
|
||||
*/
|
||||
public function getDocs() {
|
||||
$helpDocs = aioseo()->core->networkCache->get( 'admin_help_docs' );
|
||||
if ( null !== $helpDocs ) {
|
||||
if ( is_array( $helpDocs ) ) {
|
||||
return $helpDocs;
|
||||
}
|
||||
|
||||
return json_decode( $helpDocs, true );
|
||||
}
|
||||
|
||||
$request = aioseo()->helpers->wpRemoteGet( $this->getUrl() );
|
||||
if ( is_wp_error( $request ) ) {
|
||||
aioseo()->core->networkCache->update( 'admin_help_docs', [], DAY_IN_SECONDS );
|
||||
}
|
||||
|
||||
$helpDocs = wp_remote_retrieve_body( $request );
|
||||
|
||||
aioseo()->core->networkCache->update( 'admin_help_docs', $helpDocs, WEEK_IN_SECONDS );
|
||||
|
||||
return json_decode( $helpDocs, true );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains helper methods for the import from other plugins.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
abstract class Helpers {
|
||||
/**
|
||||
* Converts macros to smart tags.
|
||||
*
|
||||
* @since 4.1.3
|
||||
*
|
||||
* @param string $value The string with macros.
|
||||
* @return string The string with macros converted.
|
||||
*/
|
||||
abstract public function macrosToSmartTags( $value );
|
||||
|
||||
/**
|
||||
* Maps a list of old settings from V3 to their counterparts in V4.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param array $mappings The old settings, mapped to their new settings.
|
||||
* @param array $group The old settings group.
|
||||
* @param bool $convertMacros Whether to convert the old V3 macros to V4 smart tags.
|
||||
* @return void
|
||||
*/
|
||||
public function mapOldToNew( $mappings, $group, $convertMacros = false ) {
|
||||
if (
|
||||
! is_array( $mappings ) ||
|
||||
! is_array( $group ) ||
|
||||
! count( $mappings ) ||
|
||||
! count( $group )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$mainOptions = aioseo()->options->noConflict();
|
||||
$dynamicOptions = aioseo()->dynamicOptions->noConflict();
|
||||
foreach ( $mappings as $name => $values ) {
|
||||
if ( ! isset( $group[ $name ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$error = false;
|
||||
$options = ! empty( $values['dynamic'] ) ? $dynamicOptions : $mainOptions;
|
||||
$lastOption = '';
|
||||
for ( $i = 0; $i < count( $values['newOption'] ); $i++ ) {
|
||||
$lastOption = $values['newOption'][ $i ];
|
||||
if ( ! $options->has( $lastOption, false ) ) {
|
||||
$error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( count( $values['newOption'] ) - 1 !== $i ) {
|
||||
$options = $options->$lastOption;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $error ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ( $values['type'] ) {
|
||||
case 'boolean':
|
||||
if ( ! empty( $group[ $name ] ) ) {
|
||||
$options->$lastOption = true;
|
||||
break;
|
||||
}
|
||||
$options->$lastOption = false;
|
||||
break;
|
||||
case 'integer':
|
||||
case 'float':
|
||||
$value = aioseo()->helpers->sanitizeOption( $group[ $name ] );
|
||||
if ( $value ) {
|
||||
$options->$lastOption = $value;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$value = $group[ $name ];
|
||||
if ( $convertMacros ) {
|
||||
$value = $this->macrosToSmartTags( $value );
|
||||
}
|
||||
$options->$lastOption = aioseo()->helpers->sanitizeOption( $value );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,423 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* Handles the importing/exporting of settings and SEO data.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class ImportExport {
|
||||
/**
|
||||
* List of plugins for importing.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $plugins = [];
|
||||
|
||||
/**
|
||||
* YoastSeo class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var YoastSeo\YoastSeo
|
||||
*/
|
||||
public $yoastSeo = null;
|
||||
|
||||
/**
|
||||
* RankMath class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var RankMath\RankMath
|
||||
*/
|
||||
public $rankMath = null;
|
||||
|
||||
/**
|
||||
* SeoPress class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var SeoPress\SeoPress
|
||||
*/
|
||||
public $seoPress = null;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->yoastSeo = new YoastSeo\YoastSeo( $this );
|
||||
$this->rankMath = new RankMath\RankMath( $this );
|
||||
$this->seoPress = new SeoPress\SeoPress( $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the content of a given V3 .ini settings file to an array of settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param string $contents The .ini file contents.
|
||||
* @return array The settings.
|
||||
*/
|
||||
public function importIniData( $contents ) {
|
||||
$lines = array_filter( preg_split( '/\r\n|\r|\n/', (string) $contents ) );
|
||||
|
||||
$sections = [];
|
||||
$sectionLabel = '';
|
||||
$sectionCount = 0;
|
||||
|
||||
foreach ( $lines as $line ) {
|
||||
$line = trim( $line );
|
||||
// Ignore comments.
|
||||
if ( preg_match( '#^;.*#', (string) $line ) || preg_match( '#\<(\?php|script)#', (string) $line ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$matches = [];
|
||||
if ( preg_match( '#^\[(\S+)\]$#', (string) $line, $label ) ) {
|
||||
$sectionLabel = strval( $label[1] );
|
||||
if ( 'post_data' === $sectionLabel ) {
|
||||
$sectionCount++;
|
||||
}
|
||||
if ( ! isset( $sections[ $sectionLabel ] ) ) {
|
||||
$sections[ $sectionLabel ] = [];
|
||||
}
|
||||
} elseif ( preg_match( "#^(\S+)\s*=\s*'(.*)'$#", (string) $line, $matches ) ) {
|
||||
if ( 'post_data' === $sectionLabel ) {
|
||||
$sections[ $sectionLabel ][ $sectionCount ][ $matches[1] ] = $matches[2];
|
||||
} else {
|
||||
$sections[ $sectionLabel ][ $matches[1] ] = $matches[2];
|
||||
}
|
||||
} elseif ( preg_match( '#^(\S+)\s*=\s*NULL$#', (string) $line, $matches ) ) {
|
||||
if ( 'post_data' === $sectionLabel ) {
|
||||
$sections[ $sectionLabel ][ $sectionCount ][ $matches[1] ] = '';
|
||||
} else {
|
||||
$sections[ $sectionLabel ][ $matches[1] ] = '';
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$sanitizedSections = [];
|
||||
foreach ( $sections as $section => $options ) {
|
||||
$sanitizedSection = [];
|
||||
foreach ( $options as $option => $value ) {
|
||||
$sanitizedSection[ $option ] = $this->convertAndSanitize( $value );
|
||||
}
|
||||
$sanitizedSections[ $section ] = $sanitizedSection;
|
||||
}
|
||||
|
||||
$oldOptions = [];
|
||||
$postData = [];
|
||||
foreach ( $sanitizedSections as $label => $data ) {
|
||||
switch ( $label ) {
|
||||
case 'aioseop_options':
|
||||
$oldOptions = array_merge( $oldOptions, $data );
|
||||
break;
|
||||
case 'aiosp_feature_manager_options':
|
||||
case 'aiosp_opengraph_options':
|
||||
case 'aiosp_sitemap_options':
|
||||
case 'aiosp_video_sitemap_options':
|
||||
case 'aiosp_schema_local_business_options':
|
||||
case 'aiosp_image_seo_options':
|
||||
case 'aiosp_robots_options':
|
||||
case 'aiosp_bad_robots_options':
|
||||
$oldOptions['modules'][ $label ] = $data;
|
||||
break;
|
||||
case 'post_data':
|
||||
$postData = $data;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $oldOptions ) ) {
|
||||
aioseo()->migration->migrateSettings( $oldOptions );
|
||||
}
|
||||
|
||||
if ( ! empty( $postData ) ) {
|
||||
$this->importOldPostMeta( $postData );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the post meta from V3.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param array $postData The post data.
|
||||
* @return void
|
||||
*/
|
||||
private function importOldPostMeta( $postData ) {
|
||||
$mappedMeta = [
|
||||
'_aioseop_title' => 'title',
|
||||
'_aioseop_description' => 'description',
|
||||
'_aioseop_custom_link' => 'canonical_url',
|
||||
'_aioseop_sitemap_exclude' => '',
|
||||
'_aioseop_disable' => '',
|
||||
'_aioseop_noindex' => 'robots_noindex',
|
||||
'_aioseop_nofollow' => 'robots_nofollow',
|
||||
'_aioseop_sitemap_priority' => 'priority',
|
||||
'_aioseop_sitemap_frequency' => 'frequency',
|
||||
'_aioseop_keywords' => 'keywords',
|
||||
'_aioseop_opengraph_settings' => ''
|
||||
];
|
||||
|
||||
$excludedPosts = [];
|
||||
$sitemapExcludedPosts = [];
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/post.php';
|
||||
foreach ( $postData as $post => $values ) {
|
||||
$postId = \post_exists( $values['post_title'], '', $values['post_date'] );
|
||||
if ( ! $postId ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$meta = [
|
||||
'post_id' => $postId,
|
||||
];
|
||||
|
||||
foreach ( $values as $name => $value ) {
|
||||
if ( ! in_array( $name, array_keys( $mappedMeta ), true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ( $name ) {
|
||||
case '_aioseop_sitemap_exclude':
|
||||
if ( empty( $value ) ) {
|
||||
break;
|
||||
}
|
||||
$sitemapExcludedPosts[] = $postId;
|
||||
break;
|
||||
case '_aioseop_disable':
|
||||
if ( empty( $value ) ) {
|
||||
break;
|
||||
}
|
||||
$excludedPosts[] = $postId;
|
||||
break;
|
||||
case '_aioseop_noindex':
|
||||
case '_aioseop_nofollow':
|
||||
$meta[ $mappedMeta[ $name ] ] = ! empty( $value );
|
||||
if ( ! empty( $value ) ) {
|
||||
$meta['robots_default'] = false;
|
||||
}
|
||||
break;
|
||||
case '_aioseop_keywords':
|
||||
$meta[ $mappedMeta[ $name ] ] = aioseo()->migration->helpers->oldKeywordsToNewKeywords( $value );
|
||||
break;
|
||||
case '_aioseop_opengraph_settings':
|
||||
$class = new \AIOSEO\Plugin\Common\Migration\Meta();
|
||||
$meta += $class->convertOpenGraphMeta( $value );
|
||||
break;
|
||||
default:
|
||||
$meta[ $mappedMeta[ $name ] ] = esc_html( wp_strip_all_tags( strval( $value ) ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
$post = Models\Post::getPost( $postId );
|
||||
$post->set( $meta );
|
||||
$post->save();
|
||||
}
|
||||
|
||||
if ( count( $excludedPosts ) ) {
|
||||
$deprecatedOptions = aioseo()->internalOptions->internal->deprecatedOptions;
|
||||
if ( ! in_array( 'excludePosts', $deprecatedOptions, true ) ) {
|
||||
array_push( $deprecatedOptions, 'excludePosts' );
|
||||
aioseo()->internalOptions->internal->deprecatedOptions = $deprecatedOptions;
|
||||
}
|
||||
|
||||
$posts = aioseo()->options->deprecated->searchAppearance->advanced->excludePosts;
|
||||
|
||||
foreach ( $excludedPosts as $id ) {
|
||||
if ( ! intval( $id ) ) {
|
||||
continue;
|
||||
}
|
||||
$post = get_post( $id );
|
||||
if ( ! is_object( $post ) ) {
|
||||
continue;
|
||||
}
|
||||
$excludedPost = new \stdClass();
|
||||
$excludedPost->type = $post->post_type;
|
||||
$excludedPost->value = $post->ID;
|
||||
$excludedPost->label = $post->post_title;
|
||||
$excludedPost->link = get_permalink( $post );
|
||||
|
||||
$posts[] = wp_json_encode( $excludedPost );
|
||||
}
|
||||
aioseo()->options->deprecated->searchAppearance->advanced->excludePosts = $posts;
|
||||
}
|
||||
|
||||
if ( count( $sitemapExcludedPosts ) ) {
|
||||
aioseo()->options->sitemap->general->advancedSettings->enable = true;
|
||||
|
||||
$posts = aioseo()->options->sitemap->general->advancedSettings->excludePosts;
|
||||
foreach ( $sitemapExcludedPosts as $id ) {
|
||||
if ( ! intval( $id ) ) {
|
||||
continue;
|
||||
}
|
||||
$post = get_post( $id );
|
||||
if ( ! is_object( $post ) ) {
|
||||
continue;
|
||||
}
|
||||
$excludedPost = new \stdClass();
|
||||
$excludedPost->type = $post->post_type;
|
||||
$excludedPost->value = $post->ID;
|
||||
$excludedPost->label = $post->post_title;
|
||||
$excludedPost->link = get_permalink( $post );
|
||||
|
||||
$posts[] = wp_json_encode( $excludedPost );
|
||||
}
|
||||
aioseo()->options->sitemap->general->advancedSettings->excludePosts = $posts;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserializes an option value if needed and then sanitizes it.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param string $value The option value.
|
||||
* @return mixed The sanitized, converted option value.
|
||||
*/
|
||||
private function convertAndSanitize( $value ) {
|
||||
$value = aioseo()->helpers->maybeUnserialize( $value );
|
||||
|
||||
switch ( gettype( $value ) ) {
|
||||
case 'boolean':
|
||||
return (bool) $value;
|
||||
case 'string':
|
||||
return esc_html( wp_strip_all_tags( wp_check_invalid_utf8( trim( $value ) ) ) );
|
||||
case 'integer':
|
||||
return intval( $value );
|
||||
case 'double':
|
||||
return floatval( $value );
|
||||
case 'array':
|
||||
$sanitized = [];
|
||||
foreach ( (array) $value as $k => $v ) {
|
||||
$sanitized[ $k ] = $this->convertAndSanitize( $v );
|
||||
}
|
||||
|
||||
return $sanitized;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an import.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param string $plugin The slug of the plugin to import.
|
||||
* @param array $settings Which settings to import.
|
||||
* @return void
|
||||
*/
|
||||
public function startImport( $plugin, $settings ) {
|
||||
// First cancel any scans running that might interfere with our import.
|
||||
$this->cancelScans();
|
||||
|
||||
foreach ( $this->plugins as $pluginData ) {
|
||||
if ( $pluginData['slug'] === $plugin ) {
|
||||
$pluginData['class']->doImport( $settings );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel scans that are currently running and could conflict with our migration.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function cancelScans() {
|
||||
// Figure out how to check if these addons are enabled and then get the action names that way.
|
||||
aioseo()->actionScheduler->unschedule( 'aioseo_video_sitemap_scan' );
|
||||
aioseo()->actionScheduler->unschedule( 'aioseo_image_sitemap_scan' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an import is currently running.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return boolean True if an import is currently running.
|
||||
*/
|
||||
public function isImportRunning() {
|
||||
$importsRunning = aioseo()->core->cache->get( 'import_%_meta_%' );
|
||||
|
||||
return ! empty( $importsRunning );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds plugins to the import/export.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param array $plugins The plugins to add.
|
||||
* @return void
|
||||
*/
|
||||
public function addPlugins( $plugins ) {
|
||||
$this->plugins = array_merge( $this->plugins, $plugins );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugins we allow importing from.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function plugins() {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
$plugins = [];
|
||||
$installedPlugins = array_keys( get_plugins() );
|
||||
foreach ( $this->plugins as $importerPlugin ) {
|
||||
$data = [
|
||||
'slug' => $importerPlugin['slug'],
|
||||
'name' => $importerPlugin['name'],
|
||||
'version' => null,
|
||||
'canImport' => false,
|
||||
'basename' => $importerPlugin['basename'],
|
||||
'installed' => false
|
||||
];
|
||||
|
||||
if ( in_array( $importerPlugin['basename'], $installedPlugins, true ) ) {
|
||||
$pluginData = get_file_data( trailingslashit( WP_PLUGIN_DIR ) . $importerPlugin['basename'], [
|
||||
'name' => 'Plugin Name',
|
||||
'version' => 'Version',
|
||||
] );
|
||||
|
||||
$canImport = false;
|
||||
if ( version_compare( $importerPlugin['version'], $pluginData['version'], '<=' ) ) {
|
||||
$canImport = true;
|
||||
}
|
||||
|
||||
$data['name'] = $pluginData['name'];
|
||||
$data['version'] = $pluginData['version'];
|
||||
$data['canImport'] = $canImport;
|
||||
$data['installed'] = true;
|
||||
}
|
||||
|
||||
$plugins[] = $data;
|
||||
}
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the settings and meta data from other plugins.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
abstract class Importer {
|
||||
/**
|
||||
* Imports the settings.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function importSettings() {}
|
||||
|
||||
/**
|
||||
* Imports the post meta.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function importPostMeta() {}
|
||||
|
||||
/**
|
||||
* Imports the term meta.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function importTermMeta() {}
|
||||
|
||||
/**
|
||||
* PostMeta class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Object
|
||||
*/
|
||||
protected $postMeta = null;
|
||||
|
||||
/**
|
||||
* TermMeta class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Object
|
||||
*/
|
||||
protected $termMeta = null;
|
||||
|
||||
/**
|
||||
* Helpers class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Object
|
||||
*/
|
||||
public $helpers = null;
|
||||
|
||||
/**
|
||||
* Starts the import.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param array $options What the user wants to import.
|
||||
* @return void
|
||||
*/
|
||||
public function doImport( $options = [] ) {
|
||||
if ( empty( $options ) ) {
|
||||
$this->importSettings();
|
||||
$this->importPostMeta();
|
||||
$this->importTermMeta();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $options as $optionName ) {
|
||||
switch ( $optionName ) {
|
||||
case 'settings':
|
||||
$this->importSettings();
|
||||
break;
|
||||
case 'postMeta':
|
||||
$this->postMeta->scheduleImport();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\RankMath;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Migrates the General Settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class GeneralSettings {
|
||||
/**
|
||||
* List of options.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'rank-math-options-general' );
|
||||
if ( empty( $this->options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->isTruSeoDisabled();
|
||||
$this->migrateRedirectAttachments();
|
||||
$this->migrateStripCategoryBase();
|
||||
$this->migrateRssContentSettings();
|
||||
|
||||
$settings = [
|
||||
'google_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'google' ] ],
|
||||
'bing_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'bing' ] ],
|
||||
'yandex_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'yandex' ] ],
|
||||
'baidu_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'baidu' ] ],
|
||||
'pinterest_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'pinterest' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->rankMath->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether TruSEO should be disabled.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function isTruSeoDisabled() {
|
||||
if ( ! empty( $this->options['frontend_seo_score'] ) ) {
|
||||
aioseo()->options->advanced->truSeo = 'on' === $this->options['frontend_seo_score'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Redirect Attachments setting.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateRedirectAttachments() {
|
||||
if ( isset( $this->options['attachment_redirect_urls'] ) ) {
|
||||
if ( 'on' === $this->options['attachment_redirect_urls'] ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->attachment->redirectAttachmentUrls = 'attachment_parent';
|
||||
} else {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->attachment->redirectAttachmentUrls = 'disabled';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Strip Category Base setting.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateStripCategoryBase() {
|
||||
if ( isset( $this->options['strip_category_base'] ) ) {
|
||||
aioseo()->options->searchAppearance->advanced->removeCategoryBase = 'on' === $this->options['strip_category_base'] ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the RSS content settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateRssContentSettings() {
|
||||
if ( isset( $this->options['rss_before_content'] ) ) {
|
||||
aioseo()->options->rssContent->before = esc_html( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $this->options['rss_before_content'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $this->options['rss_after_content'] ) ) {
|
||||
aioseo()->options->rssContent->after = esc_html( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $this->options['rss_after_content'] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\RankMath;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\ImportExport;
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Contains helper methods for the import from Rank Math.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Helpers extends ImportExport\Helpers {
|
||||
/**
|
||||
* Converts the macros from Rank Math to our own smart tags.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param string $string The string with macros.
|
||||
* @param string $pageType The page type.
|
||||
* @return string $string The string with smart tags.
|
||||
*/
|
||||
public function macrosToSmartTags( $string, $pageType = null ) {
|
||||
$macros = $this->getMacros( $pageType );
|
||||
|
||||
if ( preg_match( '#%BLOGDESCLINK%#', (string) $string ) ) {
|
||||
$blogDescriptionLink = '<a href="' .
|
||||
aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'url' ) ) . '">' .
|
||||
aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'name' ) ) . ' - ' .
|
||||
aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'description' ) ) . '</a>';
|
||||
|
||||
$string = str_replace( '%BLOGDESCLINK%', $blogDescriptionLink, $string );
|
||||
}
|
||||
|
||||
if ( preg_match_all( '#%customfield\(([^%\s]*)\)%#', (string) $string, $matches ) && ! empty( $matches[1] ) ) {
|
||||
foreach ( $matches[1] as $name ) {
|
||||
$string = aioseo()->helpers->pregReplace( "#%customfield\($name\)%#", "#custom_field-$name", $string );
|
||||
}
|
||||
}
|
||||
|
||||
if ( preg_match_all( '#%customterm\(([^%\s]*)\)%#', (string) $string, $matches ) && ! empty( $matches[1] ) ) {
|
||||
foreach ( $matches[1] as $name ) {
|
||||
$string = aioseo()->helpers->pregReplace( "#%customterm\($name\)%#", "#tax_name-$name", $string );
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $macros as $macro => $tag ) {
|
||||
$string = aioseo()->helpers->pregReplace( "#$macro(?![a-zA-Z0-9_])#im", $tag, $string );
|
||||
}
|
||||
|
||||
// Strip out all remaining tags.
|
||||
$string = aioseo()->helpers->pregReplace( '/%[^\%\s]*\([^\%]*\)%/i', '', aioseo()->helpers->pregReplace( '/%[^\%\s]*%/i', '', $string ) );
|
||||
|
||||
return trim( $string );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the macro mappings.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param string $pageType The page type.
|
||||
* @return array $macros The macros.
|
||||
*/
|
||||
protected function getMacros( $pageType = null ) {
|
||||
$macros = [
|
||||
'%sitename%' => '#site_title',
|
||||
'%blog_title%' => '#site_title',
|
||||
'%blog_description%' => '#tagline',
|
||||
'%sitedesc%' => '#tagline',
|
||||
'%sep%' => '#separator_sa',
|
||||
'%post_title%' => '#post_title',
|
||||
'%page_title%' => '#post_title',
|
||||
'%postname%' => '#post_title',
|
||||
'%title%' => '#post_title',
|
||||
'%seo_title%' => '#post_title',
|
||||
'%excerpt%' => '#post_excerpt',
|
||||
'%wc_shortdesc%' => '#post_excerpt',
|
||||
'%category%' => '#taxonomy_title',
|
||||
'%term%' => '#taxonomy_title',
|
||||
'%term_description%' => '#taxonomy_description',
|
||||
'%currentdate%' => '#current_date',
|
||||
'%currentday%' => '#current_day',
|
||||
'%currentyear%' => '#current_year',
|
||||
'%currentmonth%' => '#current_month',
|
||||
'%name%' => '#author_first_name #author_last_name',
|
||||
'%author%' => '#author_first_name #author_last_name',
|
||||
'%date%' => '#post_date',
|
||||
'%year%' => '#current_year',
|
||||
'%search_query%' => '#search_term',
|
||||
// RSS Content tags.
|
||||
'%AUTHORLINK%' => '#author_link',
|
||||
'%POSTLINK%' => '#post_link',
|
||||
'%BLOGLINK%' => '#site_link',
|
||||
'%FEATUREDIMAGE%' => '#featured_image'
|
||||
];
|
||||
|
||||
switch ( $pageType ) {
|
||||
case 'archive':
|
||||
$macros['%title%'] = '#archive_title';
|
||||
break;
|
||||
case 'term':
|
||||
$macros['%title%'] = '#taxonomy_title';
|
||||
break;
|
||||
default:
|
||||
$macros['%title%'] = '#post_title';
|
||||
break;
|
||||
}
|
||||
|
||||
// Strip all other tags.
|
||||
$macros['%[^%]*%'] = '';
|
||||
|
||||
return $macros;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\RankMath;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Imports the post meta from Rank Math.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class PostMeta {
|
||||
/**
|
||||
* Schedules the post meta import.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function scheduleImport() {
|
||||
try {
|
||||
if ( as_next_scheduled_action( aioseo()->importExport->rankMath->postActionName ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! aioseo()->core->cache->get( 'import_post_meta_rank_math' ) ) {
|
||||
aioseo()->core->cache->update( 'import_post_meta_rank_math', time(), WEEK_IN_SECONDS );
|
||||
}
|
||||
|
||||
as_schedule_single_action( time(), aioseo()->importExport->rankMath->postActionName, [], 'aioseo' );
|
||||
} catch ( \Exception $e ) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the post meta.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function importPostMeta() {
|
||||
$postsPerAction = apply_filters( 'aioseo_import_rank_math_posts_per_action', 100 );
|
||||
$publicPostTypes = implode( "', '", aioseo()->helpers->getPublicPostTypes( true ) );
|
||||
$timeStarted = gmdate( 'Y-m-d H:i:s', aioseo()->core->cache->get( 'import_post_meta_rank_math' ) );
|
||||
|
||||
$posts = aioseo()->core->db
|
||||
->start( 'posts' . ' as p' )
|
||||
->select( 'p.ID, p.post_type' )
|
||||
->join( 'postmeta as pm', '`p`.`ID` = `pm`.`post_id`' )
|
||||
->leftJoin( 'aioseo_posts as ap', '`p`.`ID` = `ap`.`post_id`' )
|
||||
->whereRaw( "pm.meta_key LIKE 'rank_math_%'" )
|
||||
->whereRaw( "( p.post_type IN ( '$publicPostTypes' ) )" )
|
||||
->whereRaw( "( ap.post_id IS NULL OR ap.updated < '$timeStarted' )" )
|
||||
->orderBy( 'p.ID DESC' )
|
||||
->groupBy( 'p.ID' )
|
||||
->limit( $postsPerAction )
|
||||
->run()
|
||||
->result();
|
||||
|
||||
if ( ! $posts || ! count( $posts ) ) {
|
||||
aioseo()->core->cache->delete( 'import_post_meta_rank_math' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$mappedMeta = [
|
||||
'rank_math_title' => 'title',
|
||||
'rank_math_description' => 'description',
|
||||
'rank_math_canonical_url' => 'canonical_url',
|
||||
'rank_math_focus_keyword' => 'keyphrases',
|
||||
'rank_math_robots' => '',
|
||||
'rank_math_advanced_robots' => '',
|
||||
'rank_math_facebook_title' => 'og_title',
|
||||
'rank_math_facebook_description' => 'og_description',
|
||||
'rank_math_facebook_image' => 'og_image_custom_url',
|
||||
'rank_math_twitter_use_facebook' => 'twitter_use_og',
|
||||
'rank_math_twitter_title' => 'twitter_title',
|
||||
'rank_math_twitter_description' => 'twitter_description',
|
||||
'rank_math_twitter_image' => 'twitter_image_custom_url',
|
||||
'rank_math_twitter_card_type' => 'twitter_card',
|
||||
'rank_math_primary_category' => 'primary_term',
|
||||
'rank_math_pillar_content' => 'pillar_content',
|
||||
];
|
||||
|
||||
foreach ( $posts as $post ) {
|
||||
$postMeta = aioseo()->core->db
|
||||
->start( 'postmeta' . ' as pm' )
|
||||
->select( 'pm.meta_key, pm.meta_value' )
|
||||
->where( 'pm.post_id', $post->ID )
|
||||
->whereRaw( "`pm`.`meta_key` LIKE 'rank_math_%'" )
|
||||
->run()
|
||||
->result();
|
||||
|
||||
$meta = [
|
||||
'post_id' => $post->ID,
|
||||
'robots_default' => true,
|
||||
'robots_noarchive' => false,
|
||||
'canonical_url' => '',
|
||||
'robots_nofollow' => false,
|
||||
'robots_noimageindex' => false,
|
||||
'robots_noindex' => false,
|
||||
'robots_noodp' => false,
|
||||
'robots_nosnippet' => false,
|
||||
'keyphrases' => [
|
||||
'focus' => [ 'keyphrase' => '' ],
|
||||
'additional' => []
|
||||
],
|
||||
];
|
||||
|
||||
if ( ! $postMeta || ! count( $postMeta ) ) {
|
||||
$aioseoPost = Models\Post::getPost( (int) $post->ID );
|
||||
$aioseoPost->set( $meta );
|
||||
$aioseoPost->save();
|
||||
|
||||
aioseo()->migration->meta->migrateAdditionalPostMeta( $post->ID );
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $postMeta as $record ) {
|
||||
$name = $record->meta_key;
|
||||
$value = $record->meta_value;
|
||||
|
||||
if (
|
||||
! in_array( $post->post_type, [ 'page', 'attachment' ], true ) &&
|
||||
preg_match( '#^rank_math_schema_([^\s]*)$#', (string) $name, $match ) && ! empty( $match[1] )
|
||||
) {
|
||||
switch ( $match[1] ) {
|
||||
case 'Article':
|
||||
case 'NewsArticle':
|
||||
case 'BlogPosting':
|
||||
$meta['schema_type'] = 'Article';
|
||||
$meta['schema_type_options'] = wp_json_encode(
|
||||
[ 'article' => [ 'articleType' => $match[1] ] ]
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! in_array( $name, array_keys( $mappedMeta ), true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ( $name ) {
|
||||
case 'rank_math_focus_keyword':
|
||||
$keyphrases = array_map( 'trim', explode( ',', $value ) );
|
||||
$keyphraseArray = [
|
||||
'focus' => [ 'keyphrase' => aioseo()->helpers->sanitizeOption( $keyphrases[0] ) ],
|
||||
'additional' => []
|
||||
];
|
||||
unset( $keyphrases[0] );
|
||||
foreach ( $keyphrases as $keyphrase ) {
|
||||
$keyphraseArray['additional'][] = [ 'keyphrase' => aioseo()->helpers->sanitizeOption( $keyphrase ) ];
|
||||
}
|
||||
|
||||
$meta['keyphrases'] = $keyphraseArray;
|
||||
break;
|
||||
case 'rank_math_robots':
|
||||
$value = aioseo()->helpers->maybeUnserialize( $value );
|
||||
if ( ! empty( $value ) ) {
|
||||
$supportedValues = [ 'index', 'noindex', 'nofollow', 'noarchive', 'noimageindex', 'nosnippet' ];
|
||||
$meta['robots_default'] = false;
|
||||
|
||||
foreach ( $supportedValues as $val ) {
|
||||
$meta[ "robots_$val" ] = false;
|
||||
}
|
||||
|
||||
// This is a separated foreach as we can import any and all values.
|
||||
foreach ( $value as $robotsName ) {
|
||||
$meta[ "robots_$robotsName" ] = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'rank_math_advanced_robots':
|
||||
$value = aioseo()->helpers->maybeUnserialize( $value );
|
||||
if ( isset( $value['max-snippet'] ) && is_numeric( $value['max-snippet'] ) ) {
|
||||
$meta['robots_default'] = false;
|
||||
$meta['robots_max_snippet'] = intval( $value['max-snippet'] );
|
||||
}
|
||||
if ( isset( $value['max-video-preview'] ) && is_numeric( $value['max-video-preview'] ) ) {
|
||||
$meta['robots_default'] = false;
|
||||
$meta['robots_max_videopreview'] = intval( $value['max-video-preview'] );
|
||||
}
|
||||
if ( ! empty( $value['max-image-preview'] ) ) {
|
||||
$meta['robots_default'] = false;
|
||||
$meta['robots_max_imagepreview'] = aioseo()->helpers->sanitizeOption( lcfirst( $value['max-image-preview'] ) );
|
||||
}
|
||||
break;
|
||||
case 'rank_math_facebook_image':
|
||||
$meta['og_image_type'] = 'custom_image';
|
||||
$meta[ $mappedMeta[ $name ] ] = esc_url( $value );
|
||||
break;
|
||||
case 'rank_math_twitter_image':
|
||||
$meta['twitter_image_type'] = 'custom_image';
|
||||
$meta[ $mappedMeta[ $name ] ] = esc_url( $value );
|
||||
break;
|
||||
case 'rank_math_twitter_card_type':
|
||||
preg_match( '#large#', (string) $value, $match );
|
||||
$meta[ $mappedMeta[ $name ] ] = ! empty( $match ) ? 'summary_large_image' : 'summary';
|
||||
break;
|
||||
case 'rank_math_twitter_use_facebook':
|
||||
$meta[ $mappedMeta[ $name ] ] = 'on' === $value;
|
||||
break;
|
||||
case 'rank_math_primary_category':
|
||||
$taxonomy = 'category';
|
||||
$options = new \stdClass();
|
||||
$options->$taxonomy = (int) $value;
|
||||
$meta[ $mappedMeta[ $name ] ] = wp_json_encode( $options );
|
||||
break;
|
||||
case 'rank_math_title':
|
||||
case 'rank_math_description':
|
||||
if ( 'page' === $post->post_type ) {
|
||||
$value = aioseo()->helpers->pregReplace( '#%category%#', '', $value );
|
||||
$value = aioseo()->helpers->pregReplace( '#%excerpt%#', '', $value );
|
||||
}
|
||||
$value = aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $value );
|
||||
|
||||
$meta[ $mappedMeta[ $name ] ] = esc_html( wp_strip_all_tags( strval( $value ) ) );
|
||||
break;
|
||||
case 'rank_math_pillar_content':
|
||||
$meta['pillar_content'] = 'on' === $value ? 1 : 0;
|
||||
break;
|
||||
default:
|
||||
$meta[ $mappedMeta[ $name ] ] = esc_html( wp_strip_all_tags( strval( $value ) ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$aioseoPost = Models\Post::getPost( $post->ID );
|
||||
$aioseoPost->set( $meta );
|
||||
$aioseoPost->save();
|
||||
|
||||
aioseo()->migration->meta->migrateAdditionalPostMeta( $post->ID );
|
||||
|
||||
// Clear the Overview cache.
|
||||
aioseo()->postSettings->clearPostTypeOverviewCache( $post->ID );
|
||||
}
|
||||
|
||||
if ( count( $posts ) === $postsPerAction ) {
|
||||
try {
|
||||
as_schedule_single_action( time() + 5, aioseo()->importExport->rankMath->postActionName, [], 'aioseo' );
|
||||
} catch ( \Exception $e ) {
|
||||
// Do nothing.
|
||||
}
|
||||
} else {
|
||||
aioseo()->core->cache->delete( 'import_post_meta_rank_math' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\RankMath;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\ImportExport;
|
||||
|
||||
class RankMath extends ImportExport\Importer {
|
||||
/**
|
||||
* A list of plugins to look for to import.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $plugins = [
|
||||
[
|
||||
'name' => 'Rank Math SEO',
|
||||
'version' => '1.0',
|
||||
'basename' => 'seo-by-rank-math/rank-math.php',
|
||||
'slug' => 'rank-math-seo'
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* The post action name.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $postActionName = 'aioseo_import_post_meta_rank_math';
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param ImportExport\ImportExport $importer the ImportExport class.
|
||||
*/
|
||||
public function __construct( $importer ) {
|
||||
$this->helpers = new Helpers();
|
||||
$this->postMeta = new PostMeta();
|
||||
add_action( $this->postActionName, [ $this->postMeta, 'importPostMeta' ] );
|
||||
|
||||
$plugins = $this->plugins;
|
||||
foreach ( $plugins as $key => $plugin ) {
|
||||
$plugins[ $key ]['class'] = $this;
|
||||
}
|
||||
$importer->addPlugins( $plugins );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Imports the settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function importSettings() {
|
||||
new GeneralSettings();
|
||||
new TitleMeta();
|
||||
new Sitemap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\RankMath;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Migrates the sitemap settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Sitemap {
|
||||
/**
|
||||
* List of options.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'rank-math-options-sitemap' );
|
||||
if ( empty( $this->options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->migrateIncludedObjects();
|
||||
$this->migrateIncludeImages();
|
||||
$this->migrateExcludedPosts();
|
||||
$this->migrateExcludedTerms();
|
||||
|
||||
$settings = [
|
||||
'items_per_page' => [ 'type' => 'string', 'newOption' => [ 'sitemap', 'general', 'linksPerIndex' ] ],
|
||||
];
|
||||
|
||||
aioseo()->options->sitemap->general->indexes = true;
|
||||
aioseo()->importExport->rankMath->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the included post types and taxonomies.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateIncludedObjects() {
|
||||
$includedPostTypes = [];
|
||||
$includedTaxonomies = [];
|
||||
|
||||
$allowedPostTypes = array_values( array_diff( aioseo()->helpers->getPublicPostTypes( true ), aioseo()->helpers->getNoindexedPostTypes() ) );
|
||||
foreach ( $allowedPostTypes as $postType ) {
|
||||
foreach ( $this->options as $name => $value ) {
|
||||
if ( preg_match( "#pt_{$postType}_sitemap$#", (string) $name, $match ) && 'on' === $this->options[ $name ] ) {
|
||||
$includedPostTypes[] = $postType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$allowedTaxonomies = array_values( array_diff( aioseo()->helpers->getPublicTaxonomies( true ), aioseo()->helpers->getNoindexedTaxonomies() ) );
|
||||
foreach ( $allowedTaxonomies as $taxonomy ) {
|
||||
foreach ( $this->options as $name => $value ) {
|
||||
if ( preg_match( "#tax_{$taxonomy}_sitemap$#", (string) $name, $match ) && 'on' === $this->options[ $name ] ) {
|
||||
$includedTaxonomies[] = $taxonomy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aioseo()->options->sitemap->general->postTypes->included = $includedPostTypes;
|
||||
if ( count( $allowedPostTypes ) !== count( $includedPostTypes ) ) {
|
||||
aioseo()->options->sitemap->general->postTypes->all = false;
|
||||
}
|
||||
|
||||
aioseo()->options->sitemap->general->taxonomies->included = $includedTaxonomies;
|
||||
if ( count( $allowedTaxonomies ) !== count( $includedTaxonomies ) ) {
|
||||
aioseo()->options->sitemap->general->taxonomies->all = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Redirect Attachments setting.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateIncludeImages() {
|
||||
if ( ! empty( $this->options['include_images'] ) ) {
|
||||
if ( 'off' === $this->options['include_images'] ) {
|
||||
aioseo()->options->sitemap->general->advancedSettings->enable = true;
|
||||
aioseo()->options->sitemap->general->advancedSettings->excludeImages = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the posts that are excluded from the sitemap.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateExcludedPosts() {
|
||||
if ( empty( $this->options['exclude_posts'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rmExcludedPosts = array_filter( explode( ',', $this->options['exclude_posts'] ) );
|
||||
$excludedPosts = aioseo()->options->sitemap->general->advancedSettings->excludePosts;
|
||||
|
||||
if ( count( $rmExcludedPosts ) ) {
|
||||
foreach ( $rmExcludedPosts as $rmExcludedPost ) {
|
||||
$post = get_post( trim( $rmExcludedPost ) );
|
||||
if ( ! is_object( $post ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$excludedPost = new \stdClass();
|
||||
$excludedPost->value = $post->ID;
|
||||
$excludedPost->type = $post->post_type;
|
||||
$excludedPost->label = $post->post_title;
|
||||
$excludedPost->link = get_permalink( $post->ID );
|
||||
|
||||
array_push( $excludedPosts, wp_json_encode( $excludedPost ) );
|
||||
}
|
||||
aioseo()->options->sitemap->general->advancedSettings->enable = true;
|
||||
}
|
||||
aioseo()->options->sitemap->general->advancedSettings->excludePosts = $excludedPosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the terms that are excluded from the sitemap.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateExcludedTerms() {
|
||||
if ( empty( $this->options['exclude_terms'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rmExcludedTerms = array_filter( explode( ',', $this->options['exclude_terms'] ) );
|
||||
$excludedTerms = aioseo()->options->sitemap->general->advancedSettings->excludeTerms;
|
||||
|
||||
if ( count( $rmExcludedTerms ) ) {
|
||||
foreach ( $rmExcludedTerms as $rmExcludedTerm ) {
|
||||
$term = get_term( trim( $rmExcludedTerm ) );
|
||||
if ( ! is_object( $term ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$excludedTerm = new \stdClass();
|
||||
$excludedTerm->value = $term->term_id;
|
||||
$excludedTerm->type = $term->taxonomy;
|
||||
$excludedTerm->label = $term->name;
|
||||
$excludedTerm->link = get_term_link( $term );
|
||||
|
||||
array_push( $excludedTerms, wp_json_encode( $excludedTerm ) );
|
||||
}
|
||||
aioseo()->options->sitemap->general->advancedSettings->enable = true;
|
||||
}
|
||||
aioseo()->options->sitemap->general->advancedSettings->excludeTerms = $excludedTerms;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,504 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\RankMath;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\ImportExport;
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Migrates the Search Appearance settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class TitleMeta extends ImportExport\SearchAppearance {
|
||||
/**
|
||||
* Our robot meta settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
private $robotMetaSettings = [
|
||||
'noindex',
|
||||
'nofollow',
|
||||
'noarchive',
|
||||
'noimageindex',
|
||||
'nosnippet'
|
||||
];
|
||||
|
||||
/**
|
||||
* List of options.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'rank-math-options-titles' );
|
||||
if ( empty( $this->options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->migrateHomePageSettings();
|
||||
$this->migratePostTypeSettings();
|
||||
$this->migratePostTypeArchiveSettings();
|
||||
$this->migrateArchiveSettings();
|
||||
$this->migrateRobotMetaSettings();
|
||||
$this->migrateKnowledgeGraphSettings();
|
||||
$this->migrateSocialMetaSettings();
|
||||
|
||||
$settings = [
|
||||
'title_separator' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'separator' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->rankMath->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the homepage settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateHomePageSettings() {
|
||||
if ( isset( $this->options['homepage_title'] ) ) {
|
||||
aioseo()->options->searchAppearance->global->siteTitle =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $this->options['homepage_title'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $this->options['homepage_description'] ) ) {
|
||||
aioseo()->options->searchAppearance->global->metaDescription =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $this->options['homepage_description'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $this->options['homepage_facebook_title'] ) ) {
|
||||
aioseo()->options->social->facebook->homePage->title = aioseo()->helpers->sanitizeOption( $this->options['homepage_facebook_title'] );
|
||||
}
|
||||
|
||||
if ( isset( $this->options['homepage_facebook_description'] ) ) {
|
||||
aioseo()->options->social->facebook->homePage->description = aioseo()->helpers->sanitizeOption( $this->options['homepage_facebook_description'] );
|
||||
}
|
||||
|
||||
if ( isset( $this->options['homepage_facebook_image'] ) ) {
|
||||
aioseo()->options->social->facebook->homePage->image = esc_url( $this->options['homepage_facebook_image'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the archive settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateArchiveSettings() {
|
||||
$archives = [
|
||||
'author',
|
||||
'date'
|
||||
];
|
||||
|
||||
foreach ( $archives as $archive ) {
|
||||
// Reset existing values first.
|
||||
foreach ( $this->robotMetaSettings as $robotsMetaName ) {
|
||||
aioseo()->options->searchAppearance->archives->$archive->advanced->robotsMeta->$robotsMetaName = false;
|
||||
}
|
||||
|
||||
if ( isset( $this->options[ "disable_{$archive}_archives" ] ) ) {
|
||||
aioseo()->options->searchAppearance->archives->$archive->show = 'off' === $this->options[ "disable_{$archive}_archives" ];
|
||||
aioseo()->options->searchAppearance->archives->$archive->advanced->robotsMeta->default = 'on' === $this->options[ "disable_{$archive}_archives" ];
|
||||
aioseo()->options->searchAppearance->archives->$archive->advanced->robotsMeta->noindex = 'on' === $this->options[ "disable_{$archive}_archives" ];
|
||||
}
|
||||
|
||||
if ( isset( $this->options[ "{$archive}_archive_title" ] ) ) {
|
||||
$value = aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $this->options[ "{$archive}_archive_title" ], 'archive' ) );
|
||||
if ( 'date' !== $archive ) {
|
||||
// Archive Title tag needs to be stripped since we don't support it for author archives.
|
||||
$value = aioseo()->helpers->pregReplace( '/#archive_title/', '', $value );
|
||||
}
|
||||
aioseo()->options->searchAppearance->archives->$archive->title = $value;
|
||||
}
|
||||
|
||||
if ( isset( $this->options[ "{$archive}_archive_description" ] ) ) {
|
||||
aioseo()->options->searchAppearance->archives->$archive->metaDescription =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $this->options[ "{$archive}_archive_description" ], 'archive' ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options[ "{$archive}_custom_robots" ] ) ) {
|
||||
aioseo()->options->searchAppearance->archives->$archive->advanced->robotsMeta->default = 'off' === $this->options[ "{$archive}_custom_robots" ];
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options[ "{$archive}_robots" ] ) ) {
|
||||
foreach ( $this->options[ "{$archive}_robots" ] as $robotsName ) {
|
||||
if ( 'index' === $robotsName ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( 'noindex' === $robotsName ) {
|
||||
aioseo()->options->searchAppearance->archives->{$archive}->show = false;
|
||||
}
|
||||
|
||||
aioseo()->options->searchAppearance->archives->{$archive}->advanced->robotsMeta->{$robotsName} = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options[ "{$archive}_advanced_robots" ] ) ) {
|
||||
if ( isset( $this->options[ "{$archive}_advanced_robots" ]['max-snippet'] ) && is_numeric( $this->options[ "{$archive}_advanced_robots" ]['max-snippet'] ) ) {
|
||||
aioseo()->options->searchAppearance->archives->$archive->advanced->robotsMeta->maxSnippet = intval( $this->options[ "{$archive}_advanced_robots" ]['max-snippet'] );
|
||||
}
|
||||
if ( isset( $this->options[ "{$archive}_advanced_robots" ]['max-video-preview'] ) && is_numeric( isset( $this->options[ "{$archive}_advanced_robots" ]['max-video-preview'] ) ) ) {
|
||||
aioseo()->options->searchAppearance->archives->$archive->advanced->robotsMeta->maxVideoPreview = intval( $this->options[ "{$archive}_advanced_robots" ]['max-video-preview'] );
|
||||
}
|
||||
if ( ! empty( $this->options[ "{$archive}_advanced_robots" ]['max-image-preview'] ) ) {
|
||||
aioseo()->options->searchAppearance->archives->$archive->advanced->robotsMeta->maxImagePreview =
|
||||
aioseo()->helpers->sanitizeOption( lcfirst( $this->options[ "{$archive}_advanced_robots" ]['max-image-preview'] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $this->options['search_title'] ) ) {
|
||||
// Archive Title tag needs to be stripped since we don't support it for search archives.
|
||||
$value = aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $this->options['search_title'], 'archive' ) );
|
||||
aioseo()->options->searchAppearance->archives->search->title = aioseo()->helpers->pregReplace( '/#archive_title/', '', $value );
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['noindex_search'] ) ) {
|
||||
aioseo()->options->searchAppearance->archives->search->show = 'off' === $this->options['noindex_search'];
|
||||
aioseo()->options->searchAppearance->archives->search->advanced->robotsMeta->default = 'on' === $this->options['noindex_search'];
|
||||
aioseo()->options->searchAppearance->archives->search->advanced->robotsMeta->noindex = 'on' === $this->options['noindex_search'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the post type settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migratePostTypeSettings() {
|
||||
$supportedSettings = [
|
||||
'title',
|
||||
'description',
|
||||
'custom_robots',
|
||||
'robots',
|
||||
'advanced_robots',
|
||||
'default_rich_snippet',
|
||||
'default_article_type',
|
||||
'add_meta_box'
|
||||
];
|
||||
|
||||
foreach ( aioseo()->helpers->getPublicPostTypes( true ) as $postType ) {
|
||||
// Reset existing values first.
|
||||
foreach ( $this->robotMetaSettings as $robotsMetaName ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->$robotsMetaName = false;
|
||||
}
|
||||
|
||||
foreach ( $this->options as $name => $value ) {
|
||||
if ( ! preg_match( "#^pt_{$postType}_(.*)$#", (string) $name, $match ) || ! in_array( $match[1], $supportedSettings, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ( $match[1] ) {
|
||||
case 'title':
|
||||
if ( 'page' === $postType ) {
|
||||
$value = aioseo()->helpers->pregReplace( '#%category%#', '', $value );
|
||||
$value = aioseo()->helpers->pregReplace( '#%excerpt%#', '', $value );
|
||||
}
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->title =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $value ) );
|
||||
break;
|
||||
case 'description':
|
||||
if ( 'page' === $postType ) {
|
||||
$value = aioseo()->helpers->pregReplace( '#%category%#', '', $value );
|
||||
$value = aioseo()->helpers->pregReplace( '#%excerpt%#', '', $value );
|
||||
}
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->metaDescription =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $value ) );
|
||||
break;
|
||||
case 'custom_robots':
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->default = 'off' === $value;
|
||||
break;
|
||||
case 'robots':
|
||||
if ( ! empty( $value ) ) {
|
||||
foreach ( $value as $robotsName ) {
|
||||
if ( 'index' === $robotsName ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( 'noindex' === $robotsName ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->{$postType}->show = false;
|
||||
}
|
||||
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->$robotsName = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'advanced_robots':
|
||||
if ( isset( $value['max-snippet'] ) && is_numeric( $value['max-snippet'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->maxSnippet = intval( $value['max-snippet'] );
|
||||
}
|
||||
if ( isset( $value['max-video-preview'] ) && is_numeric( $value['max-video-preview'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->maxVideoPreview = intval( $value['max-video-preview'] );
|
||||
}
|
||||
if ( ! empty( $value['max-image-preview'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->maxImagePreview =
|
||||
aioseo()->helpers->sanitizeOption( $value['max-image-preview'] );
|
||||
}
|
||||
break;
|
||||
case 'add_meta_box':
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->showMetaBox = 'on' === $value;
|
||||
break;
|
||||
case 'default_rich_snippet':
|
||||
$value = aioseo()->helpers->pregReplace( '#\s#', '', $value );
|
||||
if ( 'off' === lcfirst( $value ) || in_array( $postType, [ 'page', 'attachment' ], true ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->schemaType = 'none';
|
||||
break;
|
||||
}
|
||||
if ( in_array( ucfirst( $value ), ImportExport\SearchAppearance::$supportedSchemaGraphs, true ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->schemaType = ucfirst( $value );
|
||||
}
|
||||
break;
|
||||
case 'default_article_type':
|
||||
if ( in_array( $postType, [ 'page', 'attachment' ], true ) ) {
|
||||
break;
|
||||
}
|
||||
$value = aioseo()->helpers->pregReplace( '#\s#', '', $value );
|
||||
if ( in_array( ucfirst( $value ), ImportExport\SearchAppearance::$supportedArticleGraphs, true ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->articleType = ucfirst( $value );
|
||||
} else {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->articleType = 'BlogPosting';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the post type archive settings.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migratePostTypeArchiveSettings() {
|
||||
$supportedSettings = [
|
||||
'title',
|
||||
'description'
|
||||
];
|
||||
|
||||
foreach ( aioseo()->helpers->getPublicPostTypes( true, true ) as $postType ) {
|
||||
foreach ( $this->options as $name => $value ) {
|
||||
if ( ! preg_match( "#^pt_{$postType}_archive_(.*)$#", (string) $name, $match ) || ! in_array( $match[1], $supportedSettings, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ( $match[1] ) {
|
||||
case 'title':
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$postType->title =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $value, 'archive' ) );
|
||||
break;
|
||||
case 'description':
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$postType->metaDescription =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $value, 'archive' ) );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Migrates the robots meta settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateRobotMetaSettings() {
|
||||
// Reset existing values first.
|
||||
foreach ( $this->robotMetaSettings as $robotsMetaName ) {
|
||||
aioseo()->options->searchAppearance->advanced->globalRobotsMeta->$robotsMetaName = false;
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['robots_global'] ) ) {
|
||||
foreach ( $this->options['robots_global'] as $robotsName ) {
|
||||
if ( 'index' === $robotsName ) {
|
||||
continue;
|
||||
}
|
||||
aioseo()->options->searchAppearance->advanced->globalRobotsMeta->default = false;
|
||||
aioseo()->options->searchAppearance->advanced->globalRobotsMeta->$robotsName = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['advanced_robots_global'] ) ) {
|
||||
aioseo()->options->searchAppearance->advanced->globalRobotsMeta->default = false;
|
||||
|
||||
if ( isset( $this->options['robots_global']['max-snippet'] ) && is_numeric( $this->options['robots_global']['max-snippet'] ) ) {
|
||||
aioseo()->options->searchAppearance->advanced->globalRobotsMeta->maxSnippet = intval( $this->options['robots_global']['max-snippet'] );
|
||||
}
|
||||
if ( isset( $this->options['robots_global']['max-video-preview'] ) && is_numeric( $this->options['robots_global']['max-video-preview'] ) ) {
|
||||
aioseo()->options->searchAppearance->advanced->globalRobotsMeta->maxVideoPreview = intval( $this->options['robots_global']['max-video-preview'] );
|
||||
}
|
||||
if ( ! empty( $this->options['robots_global']['max-image-preview'] ) ) {
|
||||
aioseo()->options->searchAppearance->advanced->globalRobotsMeta->maxImagePreview =
|
||||
aioseo()->helpers->sanitizeOption( $this->options['robots_global']['max-image-preview'] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['noindex_paginated_pages'] ) ) {
|
||||
aioseo()->options->searchAppearance->advanced->globalRobotsMeta->default = false;
|
||||
aioseo()->options->searchAppearance->advanced->globalRobotsMeta->noindexPaginated = 'on' === $this->options['noindex_paginated_pages'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Knowledge Graph settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateKnowledgeGraphSettings() {
|
||||
if ( empty( $this->options['knowledgegraph_type'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
aioseo()->options->searchAppearance->global->schema->siteRepresents =
|
||||
'company' === $this->options['knowledgegraph_type'] ? 'organization' : 'person';
|
||||
|
||||
if ( ! empty( $this->options['knowledgegraph_name'] ) && 'company' === $this->options['knowledgegraph_type'] ) {
|
||||
aioseo()->options->searchAppearance->global->schema->organizationName = aioseo()->helpers->sanitizeOption( $this->options['knowledgegraph_name'] );
|
||||
} elseif ( ! empty( $this->options['knowledgegraph_logo'] ) ) {
|
||||
aioseo()->options->searchAppearance->global->schema->person = 'manual';
|
||||
aioseo()->options->searchAppearance->global->schema->personName = aioseo()->helpers->sanitizeOption( $this->options['knowledgegraph_name'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['knowledgegraph_logo'] ) && 'company' === $this->options['knowledgegraph_type'] ) {
|
||||
aioseo()->options->searchAppearance->global->schema->organizationLogo = esc_url( $this->options['knowledgegraph_logo'] );
|
||||
} elseif ( ! empty( $this->options['knowledgegraph_logo'] ) ) {
|
||||
aioseo()->options->searchAppearance->global->schema->person = 'manual';
|
||||
aioseo()->options->searchAppearance->global->schema->personLogo = esc_url( $this->options['knowledgegraph_logo'] );
|
||||
}
|
||||
|
||||
$this->migrateKnowledgeGraphPhoneNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Knowledge Graph phone number.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateKnowledgeGraphPhoneNumber() {
|
||||
if ( empty( $this->options['phone'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phoneNumber = aioseo()->helpers->sanitizeOption( $this->options['phone'] );
|
||||
if ( ! preg_match( '#\+\d+#', (string) $phoneNumber ) ) {
|
||||
$notification = Models\Notification::getNotificationByName( 'v3-migration-schema-number' );
|
||||
if ( $notification->notification_name ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Models\Notification::addNotification( [
|
||||
'slug' => uniqid(),
|
||||
'notification_name' => 'v3-migration-schema-number',
|
||||
'title' => __( 'Invalid Phone Number for Knowledge Graph', 'all-in-one-seo-pack' ),
|
||||
'content' => sprintf(
|
||||
// Translators: 1 - The phone number.
|
||||
__( 'We were unable to import the phone number that you previously entered for your Knowledge Graph schema markup.
|
||||
As it needs to be internationally formatted, please enter it (%1$s) with the country code, e.g. +1 (555) 555-1234.', 'all-in-one-seo-pack' ),
|
||||
"<strong>$phoneNumber</strong>"
|
||||
),
|
||||
'type' => 'warning',
|
||||
'level' => [ 'all' ],
|
||||
'button1_label' => __( 'Fix Now', 'all-in-one-seo-pack' ),
|
||||
'button1_action' => 'http://route#aioseo-search-appearance&aioseo-scroll=schema-graph-phone&aioseo-highlight=schema-graph-phone:schema-markup',
|
||||
'button2_label' => __( 'Remind Me Later', 'all-in-one-seo-pack' ),
|
||||
'button2_action' => 'http://action#notification/v3-migration-schema-number-reminder',
|
||||
'start' => gmdate( 'Y-m-d H:i:s' )
|
||||
] );
|
||||
|
||||
return;
|
||||
}
|
||||
aioseo()->options->searchAppearance->global->schema->phone = $phoneNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Social Meta settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateSocialMetaSettings() {
|
||||
if ( ! empty( $this->options['open_graph_image'] ) ) {
|
||||
$defaultImage = esc_url( $this->options['open_graph_image'] );
|
||||
aioseo()->options->social->facebook->general->defaultImagePosts = $defaultImage;
|
||||
aioseo()->options->social->twitter->general->defaultImagePosts = $defaultImage;
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['social_url_facebook'] ) ) {
|
||||
aioseo()->options->social->profiles->urls->facebookPageUrl = esc_url( $this->options['social_url_facebook'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['facebook_author_urls'] ) ) {
|
||||
aioseo()->options->social->facebook->advanced->enable = true;
|
||||
aioseo()->options->social->facebook->advanced->authorUrl = esc_url( $this->options['facebook_author_urls'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['facebook_admin_id'] ) ) {
|
||||
aioseo()->options->social->facebook->advanced->enable = true;
|
||||
aioseo()->options->social->facebook->advanced->adminId = aioseo()->helpers->sanitizeOption( $this->options['facebook_admin_id'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['facebook_app_id'] ) ) {
|
||||
aioseo()->options->social->facebook->advanced->enable = true;
|
||||
aioseo()->options->social->facebook->advanced->appId = aioseo()->helpers->sanitizeOption( $this->options['facebook_app_id'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['twitter_author_names'] ) ) {
|
||||
aioseo()->options->social->profiles->urls->twitterUrl =
|
||||
'https://x.com/' . aioseo()->helpers->sanitizeOption( $this->options['twitter_author_names'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['twitter_card_type'] ) ) {
|
||||
preg_match( '#large#', $this->options['twitter_card_type'], $match );
|
||||
aioseo()->options->social->twitter->general->defaultCardType = ! empty( $match ) ? 'summary_large_image' : 'summary';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the default social image for posts.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateDefaultPostSocialImage() {
|
||||
if ( ! empty( $this->options['open_graph_image'] ) ) {
|
||||
$defaultImage = esc_url( $this->options['open_graph_image'] );
|
||||
aioseo()->options->social->facebook->general->defaultImagePosts = $defaultImage;
|
||||
aioseo()->options->social->twitter->general->defaultImagePosts = $defaultImage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Search Appearance settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
abstract class SearchAppearance {
|
||||
/**
|
||||
* The schema graphs we support.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $supportedSchemaGraphs = [
|
||||
'none',
|
||||
'WebPage',
|
||||
'Article'
|
||||
];
|
||||
|
||||
/**
|
||||
* The WebPage graphs we support.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $supportedWebPageGraphs = [
|
||||
'AboutPage',
|
||||
'CollectionPage',
|
||||
'ContactPage',
|
||||
'FAQPage',
|
||||
'ItemPage',
|
||||
'ProfilePage',
|
||||
'RealEstateListing',
|
||||
'SearchResultsPage',
|
||||
'WebPage'
|
||||
];
|
||||
|
||||
/**
|
||||
* The Article graphs we support.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $supportedArticleGraphs = [
|
||||
'Article',
|
||||
'BlogPosting',
|
||||
'NewsArticle'
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\SeoPress;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Migrates the Analytics Settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
class Analytics {
|
||||
/**
|
||||
* List of options.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'seopress_google_analytics_option_name' );
|
||||
if ( empty( $this->options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = [
|
||||
'seopress_google_analytics_other_tracking' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'miscellaneousVerification' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->seoPress->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\SeoPress;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Migrates the Breadcrumb settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
class Breadcrumbs {
|
||||
/**
|
||||
* List of options.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'seopress_pro_option_name' );
|
||||
if ( empty( $this->options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->migrate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Breadcrumbs settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrate() {
|
||||
if ( ! empty( $this->options['seopress_breadcrumbs_i18n_search'] ) ) {
|
||||
aioseo()->options->breadcrumbs->searchResultFormat = sprintf( '%1$s #breadcrumb_archive_post_type_name', $this->options['seopress_breadcrumbs_i18n_search'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['seopress_breadcrumbs_remove_blog_page'] ) ) {
|
||||
aioseo()->options->breadcrumbs->showBlogHome = false;
|
||||
}
|
||||
|
||||
$settings = [
|
||||
'seopress_breadcrumbs_enable' => [ 'type' => 'boolean', 'newOption' => [ 'breadcrumbs', 'enable' ] ],
|
||||
'seopress_breadcrumbs_separator' => [ 'type' => 'string', 'newOption' => [ 'breadcrumbs', 'separator' ] ],
|
||||
'seopress_breadcrumbs_i18n_home' => [ 'type' => 'string', 'newOption' => [ 'breadcrumbs', 'homepageLabel' ] ],
|
||||
'seopress_breadcrumbs_i18n_here' => [ 'type' => 'string', 'newOption' => [ 'breadcrumbs', 'breadcrumbPrefix' ] ],
|
||||
'seopress_breadcrumbs_i18n_404' => [ 'type' => 'string', 'newOption' => [ 'breadcrumbs', 'errorFormat404' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->seoPress->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\SeoPress;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Migrates the General Settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
class GeneralSettings {
|
||||
/**
|
||||
* List of options.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* List of our access control roles.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $roles = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'seopress_advanced_option_name' );
|
||||
if ( empty( $this->options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->roles = aioseo()->access->getRoles();
|
||||
|
||||
$this->migrateBlockMetaboxRoles();
|
||||
$this->migrateBlockContentAnalysisRoles();
|
||||
$this->migrateAttachmentRedirects();
|
||||
|
||||
$settings = [
|
||||
'seopress_advanced_advanced_google' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'google' ] ],
|
||||
'seopress_advanced_advanced_bing' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'bing' ] ],
|
||||
'seopress_advanced_advanced_pinterest' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'pinterest' ] ],
|
||||
'seopress_advanced_advanced_yandex' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'yandex' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->seoPress->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates Block AIOSEO metabox setting.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateBlockMetaboxRoles() {
|
||||
$seoPressRoles = ! empty( $this->options['seopress_advanced_security_metaboxe_role'] ) ? $this->options['seopress_advanced_security_metaboxe_role'] : '';
|
||||
if ( empty( $seoPressRoles ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$roleSettings = [ 'useDefault', 'pageAnalysis', 'pageGeneralSettings', 'pageSocialSettings', 'pageSchemaSettings', 'pageAdvancedSettings' ];
|
||||
|
||||
foreach ( $seoPressRoles as $wpRole => $value ) {
|
||||
$role = $this->roles[ $wpRole ];
|
||||
if ( empty( $role ) || aioseo()->access->isAdmin( $role ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( aioseo()->options->accessControl->has( $role ) ) {
|
||||
foreach ( $roleSettings as $setting ) {
|
||||
aioseo()->options->accessControl->$role->$setting = false;
|
||||
}
|
||||
} elseif ( aioseo()->dynamicOptions->accessControl->has( $role ) ) {
|
||||
foreach ( $roleSettings as $setting ) {
|
||||
aioseo()->dynamicOptions->accessControl->$role->$setting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates Block Content analysis metabox setting.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateBlockContentAnalysisRoles() {
|
||||
$seoPressRoles = ! empty( $this->options['seopress_advanced_security_metaboxe_ca_role'] ) ? $this->options['seopress_advanced_security_metaboxe_ca_role'] : '';
|
||||
if ( empty( $seoPressRoles ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$roleSettings = [ 'useDefault', 'pageAnalysis' ];
|
||||
|
||||
foreach ( $seoPressRoles as $wpRole => $value ) {
|
||||
$role = $this->roles[ $wpRole ];
|
||||
if ( empty( $role ) || aioseo()->access->isAdmin( $role ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( aioseo()->options->accessControl->has( $role ) ) {
|
||||
foreach ( $roleSettings as $setting ) {
|
||||
aioseo()->options->accessControl->$role->$setting = false;
|
||||
}
|
||||
} elseif ( aioseo()->dynamicOptions->accessControl->has( $role ) ) {
|
||||
foreach ( $roleSettings as $setting ) {
|
||||
aioseo()->dynamicOptions->accessControl->$role->$setting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates redirect attachment pages settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateAttachmentRedirects() {
|
||||
if ( ! empty( $this->options['seopress_advanced_advanced_attachments'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->attachment->redirectAttachmentUrls = 'attachment_parent';
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['seopress_advanced_advanced_attachments_file'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->attachment->redirectAttachmentUrls = 'attachment';
|
||||
}
|
||||
|
||||
if ( empty( $this->options['seopress_advanced_advanced_attachments'] ) && empty( $this->options['seopress_advanced_advanced_attachments_file'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->attachment->redirectAttachmentUrls = 'disabled';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\SeoPress;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\ImportExport;
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Contains helper methods for the import from SEOPress.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
class Helpers extends ImportExport\Helpers {
|
||||
/**
|
||||
* Converts the macros from SEOPress to our own smart tags.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @param string $string The string with macros.
|
||||
* @param string $postType The post type.
|
||||
* @return string The string with smart tags.
|
||||
*/
|
||||
public function macrosToSmartTags( $string, $postType = null ) {
|
||||
$macros = $this->getMacros( $postType );
|
||||
|
||||
foreach ( $macros as $macro => $tag ) {
|
||||
$string = aioseo()->helpers->pregReplace( "#$macro(?![a-zA-Z0-9_])#im", $tag, $string );
|
||||
}
|
||||
|
||||
return trim( $string );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the macro mappings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @param string $postType The post type.
|
||||
* @param string $pageType The page type.
|
||||
* @return array $macros The macros.
|
||||
*/
|
||||
protected function getMacros( $postType = null, $pageType = null ) {
|
||||
$macros = [
|
||||
'%%sep%%' => '#separator_sa',
|
||||
'%%sitetitle%%' => '#site_title',
|
||||
'%%sitename%%' => '#site_title',
|
||||
'%%tagline%%' => '#tagline',
|
||||
'%%sitedesc%%' => '#tagline',
|
||||
'%%title%%' => '#site_title',
|
||||
'%%post_title%%' => '#post_title',
|
||||
'%%post_excerpt%%' => '#post_excerpt',
|
||||
'%%excerpt%%' => '#post_excerpt',
|
||||
'%%post_content%%' => '#post_content',
|
||||
'%%post_url%%' => '#permalink',
|
||||
'%%post_date%%' => '#post_date',
|
||||
'%%post_permalink%%' => '#permalink',
|
||||
'%%date%%' => '#post_date',
|
||||
'%%post_author%%' => '#author_name',
|
||||
'%%post_category%%' => '#categories',
|
||||
'%%_category_title%%' => '#taxonomy_title',
|
||||
'%%_category_description%%' => '#taxonomy_description',
|
||||
'%%tag_title%%' => '#taxonomy_title',
|
||||
'%%tag_description%%' => '#taxonomy_description',
|
||||
'%%term_title%%' => '#taxonomy_title',
|
||||
'%%term_description%%' => '#taxonomy_description',
|
||||
'%%search_keywords%%' => '#search_term',
|
||||
'%%current_pagination%%' => '#page_number',
|
||||
'%%page%%' => '#page_number',
|
||||
'%%archive_title%%' => '#archive_title',
|
||||
'%%archive_date%%' => '#archive_date',
|
||||
'%%wc_single_price%%' => '#woocommerce_price',
|
||||
'%%wc_sku%%' => '#woocommerce_sku',
|
||||
'%%currentday%%' => '#current_day',
|
||||
'%%currentmonth%%' => '#current_month',
|
||||
'%%currentmonth_short%%' => '#current_month',
|
||||
'%%currentyear%%' => '#current_year',
|
||||
'%%currentdate%%' => '#current_date',
|
||||
'%%author_first_name%%' => '#author_first_name',
|
||||
'%%author_last_name%%' => '#author_last_name',
|
||||
'%%author_website%%' => '#author_link',
|
||||
'%%author_nickname%%' => '#author_first_name',
|
||||
'%%author_bio%%' => '#author_bio',
|
||||
'%%currentmonth_num%%' => '#current_month',
|
||||
];
|
||||
|
||||
if ( $postType ) {
|
||||
$postType = get_post_type_object( $postType );
|
||||
if ( ! empty( $postType ) ) {
|
||||
$macros += [
|
||||
'%%cpt_plural%%' => $postType->labels->name,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
switch ( $pageType ) {
|
||||
case 'archive':
|
||||
$macros['%%title%%'] = '#archive_title';
|
||||
break;
|
||||
case 'term':
|
||||
$macros['%%title%%'] = '#taxonomy_title';
|
||||
break;
|
||||
default:
|
||||
$macros['%%title%%'] = '#post_title';
|
||||
break;
|
||||
}
|
||||
|
||||
// Strip all other tags.
|
||||
$macros['%%[^%]*%%'] = '';
|
||||
|
||||
return $macros;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\SeoPress;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Imports the post meta from SEOPress.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
class PostMeta {
|
||||
/**
|
||||
* The mapped meta
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $mappedMeta = [
|
||||
'_seopress_analysis_target_kw' => '',
|
||||
'_seopress_robots_archive' => 'robots_noarchive',
|
||||
'_seopress_robots_canonical' => 'canonical_url',
|
||||
'_seopress_robots_follow' => 'robots_nofollow',
|
||||
'_seopress_robots_imageindex' => 'robots_noimageindex',
|
||||
'_seopress_robots_index' => 'robots_noindex',
|
||||
'_seopress_robots_odp' => 'robots_noodp',
|
||||
'_seopress_robots_snippet' => 'robots_nosnippet',
|
||||
'_seopress_social_twitter_desc' => 'twitter_description',
|
||||
'_seopress_social_twitter_img' => 'twitter_image_custom_url',
|
||||
'_seopress_social_twitter_title' => 'twitter_title',
|
||||
'_seopress_social_fb_desc' => 'og_description',
|
||||
'_seopress_social_fb_img' => 'og_image_custom_url',
|
||||
'_seopress_social_fb_title' => 'og_title',
|
||||
'_seopress_titles_desc' => 'description',
|
||||
'_seopress_titles_title' => 'title',
|
||||
'_seopress_robots_primary_cat' => 'primary_term'
|
||||
];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
public function scheduleImport() {
|
||||
if ( aioseo()->actionScheduler->scheduleSingle( aioseo()->importExport->seoPress->postActionName, 0 ) ) {
|
||||
if ( ! aioseo()->core->cache->get( 'import_post_meta_seopress' ) ) {
|
||||
aioseo()->core->cache->update( 'import_post_meta_seopress', time(), WEEK_IN_SECONDS );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the post meta.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function importPostMeta() {
|
||||
$postsPerAction = apply_filters( 'aioseo_import_seopress_posts_per_action', 100 );
|
||||
$publicPostTypes = implode( "', '", aioseo()->helpers->getPublicPostTypes( true ) );
|
||||
$timeStarted = gmdate( 'Y-m-d H:i:s', aioseo()->core->cache->get( 'import_post_meta_seopress' ) );
|
||||
|
||||
$posts = aioseo()->core->db
|
||||
->start( 'posts as p' )
|
||||
->select( 'p.ID, p.post_type' )
|
||||
->join( 'postmeta as pm', '`p`.`ID` = `pm`.`post_id`' )
|
||||
->leftJoin( 'aioseo_posts as ap', '`p`.`ID` = `ap`.`post_id`' )
|
||||
->whereRaw( "pm.meta_key LIKE '_seopress_%'" )
|
||||
->whereRaw( "( p.post_type IN ( '$publicPostTypes' ) )" )
|
||||
->whereRaw( "( ap.post_id IS NULL OR ap.updated < '$timeStarted' )" )
|
||||
->groupBy( 'p.ID' )
|
||||
->orderBy( 'p.ID DESC' )
|
||||
->limit( $postsPerAction )
|
||||
->run()
|
||||
->result();
|
||||
|
||||
if ( ! $posts || ! count( $posts ) ) {
|
||||
aioseo()->core->cache->delete( 'import_post_meta_seopress' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $posts as $post ) {
|
||||
$postMeta = aioseo()->core->db
|
||||
->start( 'postmeta' . ' as pm' )
|
||||
->select( 'pm.meta_key, pm.meta_value' )
|
||||
->where( 'pm.post_id', $post->ID )
|
||||
->whereRaw( "`pm`.`meta_key` LIKE '_seopress_%'" )
|
||||
->run()
|
||||
->result();
|
||||
|
||||
$meta = array_merge( [
|
||||
'post_id' => (int) $post->ID,
|
||||
], $this->getMetaData( $postMeta, $post->ID ) );
|
||||
|
||||
if ( ! $postMeta || ! count( $postMeta ) ) {
|
||||
$aioseoPost = Models\Post::getPost( (int) $post->ID );
|
||||
$aioseoPost->set( $meta );
|
||||
$aioseoPost->save();
|
||||
|
||||
aioseo()->migration->meta->migrateAdditionalPostMeta( $post->ID );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$aioseoPost = Models\Post::getPost( (int) $post->ID );
|
||||
$aioseoPost->set( $meta );
|
||||
$aioseoPost->save();
|
||||
|
||||
aioseo()->migration->meta->migrateAdditionalPostMeta( $post->ID );
|
||||
|
||||
// Clear the Overview cache.
|
||||
aioseo()->postSettings->clearPostTypeOverviewCache( $post->ID );
|
||||
}
|
||||
|
||||
if ( count( $posts ) === $postsPerAction ) {
|
||||
aioseo()->actionScheduler->scheduleSingle( aioseo()->importExport->seoPress->postActionName, 5, [], true );
|
||||
} else {
|
||||
aioseo()->core->cache->delete( 'import_post_meta_seopress' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the meta data by post meta.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @param object $postMeta The post meta from database.
|
||||
* @return array The meta data.
|
||||
*/
|
||||
public function getMetaData( $postMeta, $postId ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$meta = [
|
||||
'robots_default' => true,
|
||||
'robots_noarchive' => false,
|
||||
'canonical_url' => '',
|
||||
'robots_nofollow' => false,
|
||||
'robots_noimageindex' => false,
|
||||
'robots_noindex' => false,
|
||||
'robots_noodp' => false,
|
||||
'robots_nosnippet' => false,
|
||||
'twitter_use_og' => aioseo()->options->social->twitter->general->useOgData,
|
||||
'twitter_title' => '',
|
||||
'twitter_description' => ''
|
||||
];
|
||||
foreach ( $postMeta as $record ) {
|
||||
$name = $record->meta_key;
|
||||
$value = $record->meta_value;
|
||||
|
||||
if ( ! in_array( $name, array_keys( $this->mappedMeta ), true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ( $name ) {
|
||||
case '_seopress_analysis_target_kw':
|
||||
$keyphrases = array_map( 'trim', explode( ',', $value ) );
|
||||
$keyphraseArray = [
|
||||
'focus' => [ 'keyphrase' => aioseo()->helpers->sanitizeOption( $keyphrases[0] ) ],
|
||||
'additional' => []
|
||||
];
|
||||
unset( $keyphrases[0] );
|
||||
foreach ( $keyphrases as $keyphrase ) {
|
||||
$keyphraseArray['additional'][] = [ 'keyphrase' => aioseo()->helpers->sanitizeOption( $keyphrase ) ];
|
||||
}
|
||||
|
||||
$meta['keyphrases'] = $keyphraseArray;
|
||||
break;
|
||||
case '_seopress_robots_snippet':
|
||||
case '_seopress_robots_archive':
|
||||
case '_seopress_robots_imageindex':
|
||||
case '_seopress_robots_odp':
|
||||
case '_seopress_robots_follow':
|
||||
case '_seopress_robots_index':
|
||||
if ( 'yes' === $value ) {
|
||||
$meta['robots_default'] = false;
|
||||
$meta[ $this->mappedMeta[ $name ] ] = true;
|
||||
}
|
||||
break;
|
||||
case '_seopress_social_twitter_img':
|
||||
$meta['twitter_use_og'] = false;
|
||||
$meta['twitter_image_type'] = 'custom_image';
|
||||
$meta[ $this->mappedMeta[ $name ] ] = esc_url( $value );
|
||||
break;
|
||||
case '_seopress_social_twitter_desc':
|
||||
case '_seopress_social_twitter_title':
|
||||
$meta['twitter_use_og'] = false;
|
||||
$meta[ $this->mappedMeta[ $name ] ] = esc_html( wp_strip_all_tags( strval( $value ) ) );
|
||||
break;
|
||||
case '_seopress_social_fb_img':
|
||||
$meta['og_image_type'] = 'custom_image';
|
||||
$meta[ $this->mappedMeta[ $name ] ] = esc_url( $value );
|
||||
break;
|
||||
case '_seopress_robots_primary_cat':
|
||||
$taxonomy = 'category';
|
||||
$options = new \stdClass();
|
||||
$options->$taxonomy = (int) $value;
|
||||
$meta[ $this->mappedMeta[ $name ] ] = wp_json_encode( $options );
|
||||
break;
|
||||
case '_seopress_titles_title':
|
||||
case '_seopress_titles_desc':
|
||||
$value = aioseo()->importExport->seoPress->helpers->macrosToSmartTags( $value );
|
||||
default:
|
||||
$meta[ $this->mappedMeta[ $name ] ] = esc_html( wp_strip_all_tags( strval( $value ) ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $meta;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\SeoPress;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Migrates the robots.txt settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
class RobotsTxt {
|
||||
/**
|
||||
* List of options.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'seopress_pro_option_name', [] );
|
||||
if ( empty( $this->options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->migrateRobotsTxt();
|
||||
|
||||
$settings = [
|
||||
'seopress_robots_enable' => [ 'type' => 'boolean', 'newOption' => [ 'tools', 'robots', 'enable' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->seoPress->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the robots.txt.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function migrateRobotsTxt() {
|
||||
$lines = ! empty( $this->options['seopress_robots_file'] ) ? (string) $this->options['seopress_robots_file'] : '';
|
||||
|
||||
if ( $lines ) {
|
||||
$allRules = aioseo()->robotsTxt->extractRules( $lines );
|
||||
|
||||
aioseo()->options->tools->robots->rules = aioseo()->robotsTxt->prepareRobotsTxt( $allRules );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\SeoPress;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Migrates the RSS settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
class Rss {
|
||||
/**
|
||||
* List of options.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'seopress_pro_option_name' );
|
||||
if ( empty( $this->options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->migrateRss();
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the RSS settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function migrateRss() {
|
||||
if ( ! empty( $this->options['seopress_rss_before_html'] ) ) {
|
||||
aioseo()->options->rssContent->before = esc_html( aioseo()->importExport->seoPress->helpers->macrosToSmartTags( $this->options['seopress_rss_before_html'] ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['seopress_rss_after_html'] ) ) {
|
||||
aioseo()->options->rssContent->after = esc_html( aioseo()->importExport->seoPress->helpers->macrosToSmartTags( $this->options['seopress_rss_after_html'] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\SeoPress;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\ImportExport;
|
||||
|
||||
class SeoPress extends ImportExport\Importer {
|
||||
/**
|
||||
* A list of plugins to look for to import.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $plugins = [
|
||||
[
|
||||
'name' => 'SEOPress',
|
||||
'version' => '4.0',
|
||||
'basename' => 'wp-seopress/seopress.php',
|
||||
'slug' => 'seopress'
|
||||
],
|
||||
[
|
||||
'name' => 'SEOPress PRO',
|
||||
'version' => '4.0',
|
||||
'basename' => 'wp-seopress-pro/seopress-pro.php',
|
||||
'slug' => 'seopress-pro'
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The post action name.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $postActionName = 'aioseo_import_post_meta_seopress';
|
||||
|
||||
/**
|
||||
* The post action name.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @param ImportExport\ImportExport $importer The main importer class.
|
||||
*/
|
||||
public function __construct( $importer ) {
|
||||
$this->helpers = new Helpers();
|
||||
$this->postMeta = new PostMeta();
|
||||
add_action( $this->postActionName, [ $this->postMeta, 'importPostMeta' ] );
|
||||
|
||||
$plugins = $this->plugins;
|
||||
foreach ( $plugins as $key => $plugin ) {
|
||||
$plugins[ $key ]['class'] = $this;
|
||||
}
|
||||
$importer->addPlugins( $plugins );
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function importSettings() {
|
||||
new GeneralSettings();
|
||||
new Analytics();
|
||||
new SocialMeta();
|
||||
new Titles();
|
||||
new Sitemap();
|
||||
new RobotsTxt();
|
||||
new Rss();
|
||||
new Breadcrumbs();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\SeoPress;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Migrates the Sitemap Settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
class Sitemap {
|
||||
/**
|
||||
* List of options.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'seopress_xml_sitemap_option_name' );
|
||||
if ( empty( $this->options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->migratePostTypesInclude();
|
||||
$this->migrateTaxonomiesInclude();
|
||||
|
||||
$settings = [
|
||||
'seopress_xml_sitemap_general_enable' => [ 'type' => 'boolean', 'newOption' => [ 'sitemap', 'general', 'enable' ] ],
|
||||
'seopress_xml_sitemap_author_enable' => [ 'type' => 'boolean', 'newOption' => [ 'sitemap', 'general', 'author' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->seoPress->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the post types to include in sitemap settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function migratePostTypesInclude() {
|
||||
$postTypesMigrate = $this->options['seopress_xml_sitemap_post_types_list'];
|
||||
$postTypesInclude = [];
|
||||
|
||||
foreach ( $postTypesMigrate as $postType => $options ) {
|
||||
$postTypesInclude[] = $postType;
|
||||
}
|
||||
|
||||
aioseo()->options->sitemap->general->postTypes->included = $postTypesInclude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the taxonomies to include in sitemap settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function migrateTaxonomiesInclude() {
|
||||
$taxonomiesMigrate = $this->options['seopress_xml_sitemap_taxonomies_list'];
|
||||
$taxonomiesInclude = [];
|
||||
|
||||
foreach ( $taxonomiesMigrate as $taxonomy => $options ) {
|
||||
$taxonomiesInclude[] = $taxonomy;
|
||||
}
|
||||
|
||||
aioseo()->options->sitemap->general->taxonomies->included = $taxonomiesInclude;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\SeoPress;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Migrates the Social Meta Settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
class SocialMeta {
|
||||
/**
|
||||
* List of options.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'seopress_social_option_name' );
|
||||
if ( empty( $this->options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->migrateSocialUrls();
|
||||
$this->migrateKnowledge();
|
||||
$this->migrateFacebookSettings();
|
||||
$this->migrateTwitterSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates Basic Social Profiles URLs.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateSocialUrls() {
|
||||
$settings = [
|
||||
'seopress_social_accounts_facebook' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'facebookPageUrl' ] ],
|
||||
'seopress_social_accounts_twitter' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'twitterUrl' ] ],
|
||||
'seopress_social_accounts_pinterest' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'pinterestUrl' ] ],
|
||||
'seopress_social_accounts_instagram' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'instagramUrl' ] ],
|
||||
'seopress_social_accounts_youtube' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'youtubeUrl' ] ],
|
||||
'seopress_social_accounts_linkedin' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'linkedinUrl' ] ],
|
||||
'seopress_social_accounts_myspace' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'myspaceUrl' ] ],
|
||||
'seopress_social_accounts_soundcloud' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'soundCloudUrl' ] ],
|
||||
'seopress_social_accounts_tumblr' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'tumblrUrl' ] ],
|
||||
'seopress_social_accounts_wordpress' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'wordPressUrl' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->seoPress->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates Knowledge Graph data.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateKnowledge() {
|
||||
$type = 'organization';
|
||||
if ( ! empty( $this->options['seopress_social_knowledge_type'] ) ) {
|
||||
$type = strtolower( $this->options['seopress_social_knowledge_type'] );
|
||||
if ( 'person' === $type ) {
|
||||
aioseo()->options->searchAppearance->global->schema->person = 'manual';
|
||||
}
|
||||
}
|
||||
|
||||
aioseo()->options->searchAppearance->global->schema->siteRepresents = $type;
|
||||
|
||||
$settings = [
|
||||
'seopress_social_knowledge_img' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', $type . 'Logo' ] ],
|
||||
'seopress_social_knowledge_name' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', $type . 'Name' ] ],
|
||||
'seopress_social_knowledge_phone' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'phone' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->seoPress->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Facebook settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateFacebookSettings() {
|
||||
if ( ! empty( $this->options['seopress_social_facebook_admin_id'] ) || ! empty( $this->options['seopress_social_facebook_app_id'] ) ) {
|
||||
aioseo()->options->social->facebook->advanced->enable = true;
|
||||
}
|
||||
|
||||
$settings = [
|
||||
'seopress_social_facebook_og' => [ 'type' => 'boolean', 'newOption' => [ 'social', 'facebook', 'general', 'enable' ] ],
|
||||
'seopress_social_facebook_img' => [ 'type' => 'string', 'newOption' => [ 'social', 'facebook', 'homePage', 'image' ] ],
|
||||
'seopress_social_facebook_admin_id' => [ 'type' => 'string', 'newOption' => [ 'social', 'facebook', 'advanced', 'adminId' ] ],
|
||||
'seopress_social_facebook_app_id' => [ 'type' => 'string', 'newOption' => [ 'social', 'facebook', 'advanced', 'appId' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->seoPress->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Twitter settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateTwitterSettings() {
|
||||
if ( ! empty( $this->options['seopress_social_twitter_card_img_size'] ) ) {
|
||||
$twitterCard = ( 'large' === $this->options['seopress_social_twitter_card_img_size'] ) ? 'summary-card' : 'summary';
|
||||
aioseo()->options->social->twitter->general->defaultCardType = $twitterCard;
|
||||
}
|
||||
|
||||
$settings = [
|
||||
'seopress_social_twitter_card' => [ 'type' => 'boolean', 'newOption' => [ 'social', 'twitter', 'general', 'enable' ] ],
|
||||
'seopress_social_twitter_card_img' => [ 'type' => 'string', 'newOption' => [ 'social', 'twitter', 'homePage', 'image' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->seoPress->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\SeoPress;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Migrates the Titles Settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
class Titles {
|
||||
/**
|
||||
* List of options.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'seopress_titles_option_name' );
|
||||
if ( empty( $this->options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
! empty( $this->options['seopress_titles_archives_author_title'] ) ||
|
||||
! empty( $this->options['seopress_titles_archives_author_desc'] ) ||
|
||||
! empty( $this->options['seopress_titles_archives_author_noindex'] )
|
||||
) {
|
||||
aioseo()->options->searchAppearance->archives->author->show = true;
|
||||
}
|
||||
|
||||
if (
|
||||
! empty( $this->options['seopress_titles_archives_date_title'] ) ||
|
||||
! empty( $this->options['seopress_titles_archives_date_desc'] ) ||
|
||||
! empty( $this->options['seopress_titles_archives_date_noindex'] )
|
||||
) {
|
||||
aioseo()->options->searchAppearance->archives->date->show = true;
|
||||
}
|
||||
|
||||
if (
|
||||
! empty( $this->options['seopress_titles_archives_search_title'] ) ||
|
||||
! empty( $this->options['seopress_titles_archives_search_desc'] )
|
||||
) {
|
||||
aioseo()->options->searchAppearance->archives->search->show = true;
|
||||
}
|
||||
|
||||
$this->migrateTitleFormats();
|
||||
$this->migrateDescriptionFormats();
|
||||
$this->migrateNoIndexFormats();
|
||||
$this->migratePostTypeSettings();
|
||||
$this->migrateTaxonomiesSettings();
|
||||
$this->migrateArchiveSettings();
|
||||
$this->migrateAdvancedSettings();
|
||||
|
||||
$settings = [
|
||||
'seopress_titles_sep' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'separator' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->seoPress->helpers->mapOldToNew( $settings, $this->options, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the title formats.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateTitleFormats() {
|
||||
$settings = [
|
||||
'seopress_titles_home_site_title' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'siteTitle' ] ],
|
||||
'seopress_titles_archives_author_title' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'author', 'title' ] ],
|
||||
'seopress_titles_archives_date_title' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'date', 'title' ] ],
|
||||
'seopress_titles_archives_search_title' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'search', 'title' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->seoPress->helpers->mapOldToNew( $settings, $this->options, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the description formats.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateDescriptionFormats() {
|
||||
$settings = [
|
||||
'seopress_titles_home_site_desc' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'metaDescription' ] ],
|
||||
'seopress_titles_archives_author_desc' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'author', 'metaDescription' ] ],
|
||||
'seopress_titles_archives_date_desc' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'date', 'metaDescription' ] ],
|
||||
'seopress_titles_archives_search_desc' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'search', 'metaDescription' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->seoPress->helpers->mapOldToNew( $settings, $this->options, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the NoIndex formats.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateNoIndexFormats() {
|
||||
$settings = [
|
||||
'seopress_titles_archives_author_noindex' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'archives', 'author', 'show' ] ],
|
||||
'seopress_titles_archives_date_noindex' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'archives', 'date', 'show' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->seoPress->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the post type settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migratePostTypeSettings() {
|
||||
$titles = $this->options['seopress_titles_single_titles'];
|
||||
if ( empty( $titles ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $titles as $postType => $options ) {
|
||||
if ( ! aioseo()->dynamicOptions->searchAppearance->postTypes->has( $postType ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! empty( $options['title'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->title =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->seoPress->helpers->macrosToSmartTags( $options['title'] ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $options['description'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->metaDescription =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->seoPress->helpers->macrosToSmartTags( $options['description'] ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $options['enable'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->showMetaBox = false;
|
||||
}
|
||||
|
||||
if ( ! empty( $options['noindex'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->show = false;
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->default = false;
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->noindex = true;
|
||||
}
|
||||
|
||||
if ( ! empty( $options['nofollow'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->show = false;
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->default = false;
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->nofollow = true;
|
||||
}
|
||||
|
||||
if ( ! empty( $options['date'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->showDateInGooglePreview = false;
|
||||
}
|
||||
|
||||
if ( ! empty( $options['thumb_gcs'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->showPostThumbnailInSearch = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the taxonomies settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateTaxonomiesSettings() {
|
||||
$titles = ! empty( $this->options['seopress_titles_tax_titles'] ) ? $this->options['seopress_titles_tax_titles'] : '';
|
||||
if ( empty( $titles ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $titles as $taxonomy => $options ) {
|
||||
if ( ! aioseo()->dynamicOptions->searchAppearance->taxonomies->has( $taxonomy ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! empty( $options['title'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->taxonomies->$taxonomy->title =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->seoPress->helpers->macrosToSmartTags( $options['title'] ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $options['description'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->taxonomies->$taxonomy->metaDescription =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->seoPress->helpers->macrosToSmartTags( $options['description'] ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $options['enable'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->taxonomies->$taxonomy->advanced->showMetaBox = false;
|
||||
}
|
||||
|
||||
if ( ! empty( $options['noindex'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->taxonomies->$taxonomy->show = false;
|
||||
aioseo()->dynamicOptions->searchAppearance->taxonomies->$taxonomy->advanced->robotsMeta->default = false;
|
||||
aioseo()->dynamicOptions->searchAppearance->taxonomies->$taxonomy->advanced->robotsMeta->noindex = true;
|
||||
}
|
||||
|
||||
if ( ! empty( $options['nofollow'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->taxonomies->$taxonomy->show = false;
|
||||
aioseo()->dynamicOptions->searchAppearance->taxonomies->$taxonomy->advanced->robotsMeta->default = false;
|
||||
aioseo()->dynamicOptions->searchAppearance->taxonomies->$taxonomy->advanced->robotsMeta->nofollow = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the archives settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateArchiveSettings() {
|
||||
$titles = $this->options['seopress_titles_archive_titles'];
|
||||
if ( empty( $titles ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $titles as $archive => $options ) {
|
||||
if ( ! aioseo()->dynamicOptions->searchAppearance->archives->has( $archive ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! empty( $options['title'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$archive->title =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->seoPress->helpers->macrosToSmartTags( $options['title'] ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $options['description'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$archive->metaDescription =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->seoPress->helpers->macrosToSmartTags( $options['description'] ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $options['noindex'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$archive->show = false;
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$archive->advanced->robotsMeta->default = false;
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$archive->advanced->robotsMeta->noindex = true;
|
||||
}
|
||||
|
||||
if ( ! empty( $options['nofollow'] ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$archive->show = false;
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$archive->advanced->robotsMeta->default = false;
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$archive->advanced->robotsMeta->nofollow = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the advanced settings.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateAdvancedSettings() {
|
||||
if (
|
||||
! empty( $this->options['seopress_titles_noindex'] ) || ! empty( $this->options['seopress_titles_nofollow'] ) || ! empty( $this->options['seopress_titles_noodp'] ) ||
|
||||
! empty( $this->options['seopress_titles_noimageindex'] ) || ! empty( $this->options['seopress_titles_noarchive'] ) ||
|
||||
! empty( $this->options['seopress_titles_nosnippet'] ) || ! empty( $this->options['seopress_titles_paged_noindex'] )
|
||||
) {
|
||||
aioseo()->options->searchAppearance->advanced->globalRobotsMeta->default = false;
|
||||
}
|
||||
|
||||
$settings = [
|
||||
'seopress_titles_noindex' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'advanced', 'globalRobotsMeta', 'noindex' ] ],
|
||||
'seopress_titles_nofollow' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'advanced', 'globalRobotsMeta', 'nofollow' ] ],
|
||||
'seopress_titles_noodp' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'advanced', 'globalRobotsMeta', 'noodp' ] ],
|
||||
'seopress_titles_noimageindex' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'advanced', 'globalRobotsMeta', 'noimageindex' ] ],
|
||||
'seopress_titles_noarchive' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'advanced', 'globalRobotsMeta', 'noarchive' ] ],
|
||||
'seopress_titles_nosnippet' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'advanced', 'globalRobotsMeta', 'nosnippet' ] ],
|
||||
'seopress_titles_paged_noindex' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'advanced', 'globalRobotsMeta', 'noindexPaginated' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->seoPress->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\YoastSeo;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Migrates the General Settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class GeneralSettings {
|
||||
/**
|
||||
* List of options.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'wpseo' );
|
||||
if ( empty( $this->options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = [
|
||||
'googleverify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'google' ] ],
|
||||
'msverify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'bing' ] ],
|
||||
'yandexverify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'yandex' ] ],
|
||||
'baiduverify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'baidu' ] ],
|
||||
'enable_xml_sitemap' => [ 'type' => 'boolean', 'newOption' => [ 'sitemap', 'general', 'enable' ] ]
|
||||
];
|
||||
|
||||
aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\YoastSeo;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\ImportExport;
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Contains helper methods for the import from Rank Math.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Helpers extends ImportExport\Helpers {
|
||||
/**
|
||||
* Converts the macros from Yoast SEO to our own smart tags.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param string $string The string with macros.
|
||||
* @param string $postType The post type.
|
||||
* @param string $pageType The page type.
|
||||
* @return string $string The string with smart tags.
|
||||
*/
|
||||
public function macrosToSmartTags( $string, $postType = null, $pageType = null ) {
|
||||
$macros = $this->getMacros( $postType, $pageType );
|
||||
|
||||
if ( preg_match( '#%%BLOGDESCLINK%%#', (string) $string ) ) {
|
||||
$blogDescriptionLink = '<a href="' .
|
||||
aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'url' ) ) . '">' .
|
||||
aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'name' ) ) . ' - ' .
|
||||
aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'description' ) ) . '</a>';
|
||||
|
||||
$string = str_replace( '%%BLOGDESCLINK%%', $blogDescriptionLink, $string );
|
||||
}
|
||||
|
||||
if ( preg_match_all( '#%%cf_([^%]*)%%#', (string) $string, $matches ) && ! empty( $matches[1] ) ) {
|
||||
foreach ( $matches[1] as $name ) {
|
||||
if ( ! preg_match( '#\s#', (string) $name ) ) {
|
||||
$string = aioseo()->helpers->pregReplace( "#%%cf_$name%%#", "#custom_field-$name", $string );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( preg_match_all( '#%%tax_([^%]*)%%#', (string) $string, $matches ) && ! empty( $matches[1] ) ) {
|
||||
foreach ( $matches[1] as $name ) {
|
||||
if ( ! preg_match( '#\s#', (string) $name ) ) {
|
||||
$string = aioseo()->helpers->pregReplace( "#%%tax_$name%%#", "#tax_name-$name", $string );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $macros as $macro => $tag ) {
|
||||
$string = aioseo()->helpers->pregReplace( "#$macro(?![a-zA-Z0-9_])#im", $tag, $string );
|
||||
}
|
||||
|
||||
// Strip out all remaining tags.
|
||||
$string = aioseo()->helpers->pregReplace( '/%[^\%\s]*\([^\%]*\)%/i', '', aioseo()->helpers->pregReplace( '/%[^\%\s]*%/i', '', $string ) );
|
||||
|
||||
return trim( $string );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the macro mappings.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param string $postType The post type.
|
||||
* @param string $pageType The page type.
|
||||
* @return array $macros The macros.
|
||||
*/
|
||||
protected function getMacros( $postType = null, $pageType = null ) {
|
||||
$macros = [
|
||||
'%%sitename%%' => '#site_title',
|
||||
'%%sitedesc%%' => '#tagline',
|
||||
'%%sep%%' => '#separator_sa',
|
||||
'%%term_title%%' => '#taxonomy_title',
|
||||
'%%term_description%%' => '#taxonomy_description',
|
||||
'%%category_description%%' => '#taxonomy_description',
|
||||
'%%tag_description%%' => '#taxonomy_description',
|
||||
'%%primary_category%%' => '#taxonomy_title',
|
||||
'%%archive_title%%' => '#archive_title',
|
||||
'%%pagenumber%%' => '#page_number',
|
||||
'%%caption%%' => '#attachment_caption',
|
||||
'%%name%%' => '#author_first_name #author_last_name',
|
||||
'%%user_description%%' => '#author_bio',
|
||||
'%%date%%' => '#archive_date',
|
||||
'%%currentday%%' => '#current_day',
|
||||
'%%currentmonth%%' => '#current_month',
|
||||
'%%currentyear%%' => '#current_year',
|
||||
'%%searchphrase%%' => '#search_term',
|
||||
'%%AUTHORLINK%%' => '#author_link',
|
||||
'%%POSTLINK%%' => '#post_link',
|
||||
'%%BLOGLINK%%' => '#site_link',
|
||||
'%%category%%' => '#categories',
|
||||
'%%parent_title%%' => '#parent_title',
|
||||
'%%wc_sku%%' => '#woocommerce_sku',
|
||||
'%%wc_price%%' => '#woocommerce_price',
|
||||
'%%wc_brand%%' => '#woocommerce_brand',
|
||||
'%%excerpt%%' => '#post_excerpt',
|
||||
'%%excerpt_only%%' => '#post_excerpt_only'
|
||||
/* '%%tag%%' => '',
|
||||
'%%id%%' => '',
|
||||
'%%page%%' => '',
|
||||
'%%modified%%' => '',
|
||||
'%%pagetotal%%' => '',
|
||||
'%%focuskw%%' => '',
|
||||
'%%term404%%' => '',
|
||||
'%%ct_desc_[^%]*%%' => '' */
|
||||
];
|
||||
|
||||
if ( $postType ) {
|
||||
$postType = get_post_type_object( $postType );
|
||||
if ( ! empty( $postType ) ) {
|
||||
$macros += [
|
||||
'%%pt_single%%' => $postType->labels->singular_name,
|
||||
'%%pt_plural%%' => $postType->labels->name,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
switch ( $pageType ) {
|
||||
case 'archive':
|
||||
$macros['%%title%%'] = '#archive_title';
|
||||
break;
|
||||
case 'term':
|
||||
$macros['%%title%%'] = '#taxonomy_title';
|
||||
break;
|
||||
default:
|
||||
$macros['%%title%%'] = '#post_title';
|
||||
break;
|
||||
}
|
||||
|
||||
// Strip all other tags.
|
||||
$macros['%%[^%]*%%'] = '';
|
||||
|
||||
return $macros;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\YoastSeo;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\ImportExport;
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Imports the post meta from Yoast SEO.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class PostMeta {
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function scheduleImport() {
|
||||
try {
|
||||
if ( as_next_scheduled_action( aioseo()->importExport->yoastSeo->postActionName ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! aioseo()->core->cache->get( 'import_post_meta_yoast_seo' ) ) {
|
||||
aioseo()->core->cache->update( 'import_post_meta_yoast_seo', time(), WEEK_IN_SECONDS );
|
||||
}
|
||||
|
||||
as_schedule_single_action( time(), aioseo()->importExport->yoastSeo->postActionName, [], 'aioseo' );
|
||||
} catch ( \Exception $e ) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the post meta.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function importPostMeta() {
|
||||
$postsPerAction = apply_filters( 'aioseo_import_yoast_seo_posts_per_action', 100 );
|
||||
$publicPostTypes = implode( "', '", aioseo()->helpers->getPublicPostTypes( true ) );
|
||||
$timeStarted = gmdate( 'Y-m-d H:i:s', aioseo()->core->cache->get( 'import_post_meta_yoast_seo' ) );
|
||||
|
||||
$posts = aioseo()->core->db
|
||||
->start( 'posts' . ' as p' )
|
||||
->select( 'p.ID, p.post_type' )
|
||||
->leftJoin( 'aioseo_posts as ap', '`p`.`ID` = `ap`.`post_id`' )
|
||||
->whereRaw( "( p.post_type IN ( '$publicPostTypes' ) )" )
|
||||
->whereRaw( "( ap.post_id IS NULL OR ap.updated < '$timeStarted' )" )
|
||||
->orderBy( 'p.ID DESC' )
|
||||
->limit( $postsPerAction )
|
||||
->run()
|
||||
->result();
|
||||
|
||||
if ( ! $posts || ! count( $posts ) ) {
|
||||
aioseo()->core->cache->delete( 'import_post_meta_yoast_seo' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$mappedMeta = [
|
||||
'_yoast_wpseo_title' => 'title',
|
||||
'_yoast_wpseo_metadesc' => 'description',
|
||||
'_yoast_wpseo_canonical' => 'canonical_url',
|
||||
'_yoast_wpseo_meta-robots-noindex' => 'robots_noindex',
|
||||
'_yoast_wpseo_meta-robots-nofollow' => 'robots_nofollow',
|
||||
'_yoast_wpseo_meta-robots-adv' => '',
|
||||
'_yoast_wpseo_focuskw' => '',
|
||||
'_yoast_wpseo_focuskeywords' => '',
|
||||
'_yoast_wpseo_opengraph-title' => 'og_title',
|
||||
'_yoast_wpseo_opengraph-description' => 'og_description',
|
||||
'_yoast_wpseo_opengraph-image' => 'og_image_custom_url',
|
||||
'_yoast_wpseo_twitter-title' => 'twitter_title',
|
||||
'_yoast_wpseo_twitter-description' => 'twitter_description',
|
||||
'_yoast_wpseo_twitter-image' => 'twitter_image_custom_url',
|
||||
'_yoast_wpseo_schema_page_type' => '',
|
||||
'_yoast_wpseo_schema_article_type' => '',
|
||||
'_yoast_wpseo_is_cornerstone' => 'pillar_content'
|
||||
];
|
||||
|
||||
foreach ( $posts as $post ) {
|
||||
$postMeta = aioseo()->core->db
|
||||
->start( 'postmeta' . ' as pm' )
|
||||
->select( 'pm.meta_key, pm.meta_value' )
|
||||
->where( 'pm.post_id', $post->ID )
|
||||
->whereRaw( "`pm`.`meta_key` LIKE '_yoast_wpseo_%'" )
|
||||
->run()
|
||||
->result();
|
||||
|
||||
$featuredImage = get_the_post_thumbnail_url( $post->ID );
|
||||
$meta = [
|
||||
'post_id' => (int) $post->ID,
|
||||
'twitter_use_og' => true,
|
||||
'og_image_type' => $featuredImage ? 'featured' : 'content',
|
||||
'pillar_content' => 0,
|
||||
'canonical_url' => '',
|
||||
'robots_default' => true,
|
||||
'robots_noarchive' => false,
|
||||
'robots_nofollow' => false,
|
||||
'robots_noimageindex' => false,
|
||||
'robots_noindex' => false,
|
||||
'robots_noodp' => false,
|
||||
'robots_nosnippet' => false,
|
||||
'title' => '',
|
||||
'description' => '',
|
||||
'og_title' => '',
|
||||
'og_description' => '',
|
||||
'og_image_custom_url' => '',
|
||||
'twitter_title' => '',
|
||||
'twitter_description' => '',
|
||||
'twitter_image_custom_url' => '',
|
||||
'twitter_image_type' => 'default'
|
||||
];
|
||||
|
||||
if ( ! $postMeta || ! count( $postMeta ) ) {
|
||||
$aioseoPost = Models\Post::getPost( (int) $post->ID );
|
||||
$aioseoPost->set( $meta );
|
||||
$aioseoPost->save();
|
||||
|
||||
aioseo()->migration->meta->migrateAdditionalPostMeta( $post->ID );
|
||||
continue;
|
||||
}
|
||||
|
||||
$title = '';
|
||||
foreach ( $postMeta as $record ) {
|
||||
$name = $record->meta_key;
|
||||
$value = $record->meta_value;
|
||||
|
||||
// Handles primary taxonomy terms.
|
||||
// We need to handle it separately because it's stored in a different format.
|
||||
if ( false !== stripos( $name, '_yoast_wpseo_primary_' ) ) {
|
||||
sscanf( $name, '_yoast_wpseo_primary_%s', $taxonomy );
|
||||
if ( null === $taxonomy ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$options = new \stdClass();
|
||||
if ( isset( $meta['primary_term'] ) ) {
|
||||
$options = json_decode( $meta['primary_term'] );
|
||||
}
|
||||
|
||||
$options->$taxonomy = (int) $value;
|
||||
$meta['primary_term'] = wp_json_encode( $options );
|
||||
}
|
||||
|
||||
if ( ! in_array( $name, array_keys( $mappedMeta ), true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ( $name ) {
|
||||
case '_yoast_wpseo_meta-robots-noindex':
|
||||
case '_yoast_wpseo_meta-robots-nofollow':
|
||||
if ( (bool) $value ) {
|
||||
$meta[ $mappedMeta[ $name ] ] = (bool) $value;
|
||||
$meta['robots_default'] = false;
|
||||
}
|
||||
break;
|
||||
case '_yoast_wpseo_meta-robots-adv':
|
||||
$supportedValues = [ 'index', 'noarchive', 'noimageindex', 'nosnippet' ];
|
||||
foreach ( $supportedValues as $val ) {
|
||||
$meta[ "robots_$val" ] = false;
|
||||
}
|
||||
|
||||
// This is a separated foreach so we can import any and all values.
|
||||
$values = explode( ',', $value );
|
||||
if ( $values ) {
|
||||
$meta['robots_default'] = false;
|
||||
|
||||
foreach ( $values as $value ) {
|
||||
$meta[ "robots_$value" ] = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '_yoast_wpseo_canonical':
|
||||
$meta[ $mappedMeta[ $name ] ] = esc_url( $value );
|
||||
break;
|
||||
case '_yoast_wpseo_opengraph-image':
|
||||
$meta['og_image_type'] = 'custom_image';
|
||||
$meta[ $mappedMeta[ $name ] ] = esc_url( $value );
|
||||
break;
|
||||
case '_yoast_wpseo_twitter-image':
|
||||
$meta['twitter_use_og'] = false;
|
||||
$meta['twitter_image_type'] = 'custom_image';
|
||||
$meta[ $mappedMeta[ $name ] ] = esc_url( $value );
|
||||
break;
|
||||
case '_yoast_wpseo_schema_page_type':
|
||||
$value = aioseo()->helpers->pregReplace( '#\s#', '', $value );
|
||||
if ( in_array( $post->post_type, [ 'post', 'page', 'attachment' ], true ) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ! in_array( $value, ImportExport\SearchAppearance::$supportedWebPageGraphs, true ) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
$meta[ $mappedMeta[ $name ] ] = 'WebPage';
|
||||
$meta['schema_type_options'] = wp_json_encode( [
|
||||
'webPage' => [
|
||||
'webPageType' => $value
|
||||
]
|
||||
] );
|
||||
break;
|
||||
case '_yoast_wpseo_schema_article_type':
|
||||
$value = aioseo()->helpers->pregReplace( '#\s#', '', $value );
|
||||
if ( 'none' === lcfirst( $value ) ) {
|
||||
$meta[ $mappedMeta[ $name ] ] = 'None';
|
||||
break;
|
||||
}
|
||||
|
||||
if ( in_array( $post->post_type, [ 'page', 'attachment' ], true ) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
$options = new \stdClass();
|
||||
if ( isset( $meta['schema_type_options'] ) ) {
|
||||
$options = json_decode( $meta['schema_type_options'] );
|
||||
}
|
||||
|
||||
$options->article = [ 'articleType' => 'Article' ];
|
||||
if ( in_array( $value, ImportExport\SearchAppearance::$supportedArticleGraphs, true ) ) {
|
||||
$options->article = [ 'articleType' => $value ];
|
||||
} else {
|
||||
$options->article = [ 'articleType' => 'BlogPosting' ];
|
||||
}
|
||||
|
||||
$meta['schema_type'] = 'Article';
|
||||
$meta['schema_type_options'] = wp_json_encode( $options );
|
||||
break;
|
||||
case '_yoast_wpseo_focuskw':
|
||||
$focusKeyphrase = [
|
||||
'focus' => [ 'keyphrase' => aioseo()->helpers->sanitizeOption( $value ) ]
|
||||
];
|
||||
|
||||
// Merge with existing keyphrases if the array key already exists.
|
||||
if ( ! empty( $meta['keyphrases'] ) ) {
|
||||
$meta['keyphrases'] = array_merge( $meta['keyphrases'], $focusKeyphrase );
|
||||
} else {
|
||||
$meta['keyphrases'] = $focusKeyphrase;
|
||||
}
|
||||
break;
|
||||
case '_yoast_wpseo_focuskeywords':
|
||||
$keyphrases = [];
|
||||
if ( ! empty( $meta[ $mappedMeta[ $name ] ] ) ) {
|
||||
$keyphrases = (array) json_decode( $meta[ $mappedMeta[ $name ] ] );
|
||||
}
|
||||
|
||||
$yoastKeyphrases = json_decode( $value, true );
|
||||
if ( is_array( $yoastKeyphrases ) ) {
|
||||
foreach ( $yoastKeyphrases as $yoastKeyphrase ) {
|
||||
if ( ! empty( $yoastKeyphrase['keyword'] ) ) {
|
||||
$keyphrase = [ 'keyphrase' => aioseo()->helpers->sanitizeOption( $yoastKeyphrase['keyword'] ) ];
|
||||
|
||||
if ( ! isset( $keyphrases['additional'] ) ) {
|
||||
$keyphrases['additional'] = [];
|
||||
}
|
||||
|
||||
$keyphrases['additional'][] = $keyphrase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $keyphrases ) ) {
|
||||
// Merge with existing keyphrases if the array key already exists.
|
||||
if ( ! empty( $meta['keyphrases'] ) ) {
|
||||
$meta['keyphrases'] = array_merge( $meta['keyphrases'], $keyphrases );
|
||||
} else {
|
||||
$meta['keyphrases'] = $keyphrases;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '_yoast_wpseo_title':
|
||||
case '_yoast_wpseo_metadesc':
|
||||
case '_yoast_wpseo_opengraph-title':
|
||||
case '_yoast_wpseo_opengraph-description':
|
||||
case '_yoast_wpseo_twitter-title':
|
||||
case '_yoast_wpseo_twitter-description':
|
||||
if ( 'page' === $post->post_type ) {
|
||||
$value = aioseo()->helpers->pregReplace( '#%%primary_category%%#', '', $value );
|
||||
$value = aioseo()->helpers->pregReplace( '#%%excerpt%%#', '', $value );
|
||||
}
|
||||
|
||||
if ( '_yoast_wpseo_twitter-title' === $name || '_yoast_wpseo_twitter-description' === $name ) {
|
||||
$meta['twitter_use_og'] = false;
|
||||
}
|
||||
|
||||
$value = aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $value, 'post', $post->post_type );
|
||||
|
||||
if ( '_yoast_wpseo_title' === $name ) {
|
||||
$title = $value;
|
||||
}
|
||||
|
||||
$meta[ $mappedMeta[ $name ] ] = esc_html( wp_strip_all_tags( strval( $value ) ) );
|
||||
break;
|
||||
case '_yoast_wpseo_is_cornerstone':
|
||||
$meta['pillar_content'] = (bool) $value ? 1 : 0;
|
||||
break;
|
||||
default:
|
||||
$meta[ $mappedMeta[ $name ] ] = esc_html( wp_strip_all_tags( strval( $value ) ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Resetting the `twitter_use_og` option if the user has a custom title and no twitter title.
|
||||
if ( $meta['twitter_use_og'] && $title && empty( $meta['twitter_title'] ) ) {
|
||||
$meta['twitter_use_og'] = false;
|
||||
$meta['twitter_title'] = $title;
|
||||
}
|
||||
|
||||
$aioseoPost = Models\Post::getPost( (int) $post->ID );
|
||||
$aioseoPost->set( $meta );
|
||||
$aioseoPost->save();
|
||||
|
||||
aioseo()->migration->meta->migrateAdditionalPostMeta( $post->ID );
|
||||
|
||||
// Clear the Overview cache.
|
||||
aioseo()->postSettings->clearPostTypeOverviewCache( $post->ID );
|
||||
}
|
||||
|
||||
if ( count( $posts ) === $postsPerAction ) {
|
||||
try {
|
||||
as_schedule_single_action( time() + 5, aioseo()->importExport->yoastSeo->postActionName, [], 'aioseo' );
|
||||
} catch ( \Exception $e ) {
|
||||
// Do nothing.
|
||||
}
|
||||
} else {
|
||||
aioseo()->core->cache->delete( 'import_post_meta_yoast_seo' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\YoastSeo;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\ImportExport;
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Migrates the Search Appearance settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class SearchAppearance {
|
||||
/**
|
||||
* List of options.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Whether the homepage social settings have been imported here.
|
||||
*
|
||||
* @since 4.2.4
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $hasImportedHomepageSocialSettings = false;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'wpseo_titles' );
|
||||
if ( empty( $this->options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->migrateSeparator();
|
||||
$this->migrateTitleFormats();
|
||||
$this->migrateDescriptionFormats();
|
||||
$this->migrateNoindexSettings();
|
||||
$this->migratePostTypeSettings();
|
||||
$this->migratePostTypeArchiveSettings();
|
||||
$this->migrateRedirectAttachments();
|
||||
$this->migrateKnowledgeGraphSettings();
|
||||
$this->migrateRssContentSettings();
|
||||
$this->migrateStripCategoryBase();
|
||||
$this->migrateHomepageSocialSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the title/description separator.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateSeparator() {
|
||||
$separators = [
|
||||
'sc-dash' => '-',
|
||||
'sc-ndash' => '–',
|
||||
'sc-mdash' => '—',
|
||||
'sc-colon' => ':',
|
||||
'sc-middot' => '·',
|
||||
'sc-bull' => '•',
|
||||
'sc-star' => '*',
|
||||
'sc-smstar' => '⋆',
|
||||
'sc-pipe' => '|',
|
||||
'sc-tilde' => '~',
|
||||
'sc-laquo' => '«',
|
||||
'sc-raquo' => '»',
|
||||
'sc-lt' => '<',
|
||||
'sc-gt' => '>',
|
||||
];
|
||||
|
||||
if ( ! empty( $this->options['separator'] ) && in_array( $this->options['separator'], array_keys( $separators ), true ) ) {
|
||||
aioseo()->options->searchAppearance->global->separator = $separators[ $this->options['separator'] ];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the title formats.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateTitleFormats() {
|
||||
aioseo()->options->searchAppearance->global->siteTitle =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $this->options['title-home-wpseo'] ) );
|
||||
|
||||
aioseo()->options->searchAppearance->archives->date->title =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $this->options['title-archive-wpseo'], null, 'archive' ) );
|
||||
|
||||
// Archive Title tag needs to be stripped since we don't support it for these two archives.
|
||||
$value = aioseo()->helpers->sanitizeOption( aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $this->options['title-author-wpseo'], null, 'archive' ) );
|
||||
aioseo()->options->searchAppearance->archives->author->title = aioseo()->helpers->pregReplace( '/#archive_title/', '', $value );
|
||||
|
||||
$value = aioseo()->helpers->sanitizeOption( aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $this->options['title-search-wpseo'], null, 'archive' ) );
|
||||
aioseo()->options->searchAppearance->archives->search->title = aioseo()->helpers->pregReplace( '/#archive_title/', '', $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the description formats.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateDescriptionFormats() {
|
||||
$settings = [
|
||||
'metadesc-home-wpseo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'metaDescription' ] ],
|
||||
'metadesc-author-wpseo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'author', 'metaDescription' ] ],
|
||||
'metadesc-archive-wpseo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'date', 'metaDescription' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the noindex settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateNoindexSettings() {
|
||||
if ( ! empty( $this->options['noindex-author-wpseo'] ) ) {
|
||||
aioseo()->options->searchAppearance->archives->author->show = false;
|
||||
aioseo()->options->searchAppearance->archives->author->advanced->robotsMeta->default = false;
|
||||
aioseo()->options->searchAppearance->archives->author->advanced->robotsMeta->noindex = true;
|
||||
} else {
|
||||
aioseo()->options->searchAppearance->archives->author->show = true;
|
||||
}
|
||||
|
||||
if ( ! empty( $this->options['noindex-archive-wpseo'] ) ) {
|
||||
aioseo()->options->searchAppearance->archives->date->show = false;
|
||||
aioseo()->options->searchAppearance->archives->date->advanced->robotsMeta->default = false;
|
||||
aioseo()->options->searchAppearance->archives->date->advanced->robotsMeta->noindex = true;
|
||||
} else {
|
||||
aioseo()->options->searchAppearance->archives->date->show = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the post type settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migratePostTypeSettings() {
|
||||
$supportedSettings = [
|
||||
'title',
|
||||
'metadesc',
|
||||
'noindex',
|
||||
'display-metabox-pt',
|
||||
'schema-page-type',
|
||||
'schema-article-type'
|
||||
];
|
||||
|
||||
foreach ( aioseo()->helpers->getPublicPostTypes( true ) as $postType ) {
|
||||
foreach ( $this->options as $name => $value ) {
|
||||
if ( ! preg_match( "#(.*)-$postType$#", (string) $name, $match ) || ! in_array( $match[1], $supportedSettings, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ( $match[1] ) {
|
||||
case 'title':
|
||||
if ( 'page' === $postType ) {
|
||||
$value = aioseo()->helpers->pregReplace( '#%%primary_category%%#', '', $value );
|
||||
$value = aioseo()->helpers->pregReplace( '#%%excerpt%%#', '', $value );
|
||||
}
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->title =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $value, $postType ) );
|
||||
break;
|
||||
case 'metadesc':
|
||||
if ( 'page' === $postType ) {
|
||||
$value = aioseo()->helpers->pregReplace( '#%%primary_category%%#', '', $value );
|
||||
$value = aioseo()->helpers->pregReplace( '#%%excerpt%%#', '', $value );
|
||||
}
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->metaDescription =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $value, $postType ) );
|
||||
break;
|
||||
case 'noindex':
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->show = empty( $value ) ? true : false;
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->default = empty( $value ) ? true : false;
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->robotsMeta->noindex = empty( $value ) ? false : true;
|
||||
break;
|
||||
case 'display-metabox-pt':
|
||||
if ( empty( $value ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->advanced->showMetaBox = false;
|
||||
}
|
||||
break;
|
||||
case 'schema-page-type':
|
||||
$value = aioseo()->helpers->pregReplace( '#\s#', '', $value );
|
||||
if ( in_array( $postType, [ 'post', 'page', 'attachment' ], true ) ) {
|
||||
break;
|
||||
}
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->schemaType = 'WebPage';
|
||||
if ( in_array( $value, ImportExport\SearchAppearance::$supportedWebPageGraphs, true ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->webPageType = $value;
|
||||
}
|
||||
break;
|
||||
case 'schema-article-type':
|
||||
$value = aioseo()->helpers->pregReplace( '#\s#', '', $value );
|
||||
if ( 'none' === lcfirst( $value ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->articleType = 'none';
|
||||
break;
|
||||
}
|
||||
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->articleType = 'Article';
|
||||
if ( in_array( $value, ImportExport\SearchAppearance::$supportedArticleGraphs, true ) ) {
|
||||
if ( ! in_array( $postType, [ 'page', 'attachment' ], true ) ) {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->articleType = $value;
|
||||
}
|
||||
} else {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->$postType->articleType = 'BlogPosting';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the post type archive settings.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migratePostTypeArchiveSettings() {
|
||||
$supportedSettings = [
|
||||
'title',
|
||||
'metadesc',
|
||||
'noindex'
|
||||
];
|
||||
|
||||
foreach ( aioseo()->helpers->getPublicPostTypes( true, true ) as $postType ) {
|
||||
foreach ( $this->options as $name => $value ) {
|
||||
if ( ! preg_match( "#(.*)-ptarchive-$postType$#", (string) $name, $match ) || ! in_array( $match[1], $supportedSettings, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ( $match[1] ) {
|
||||
case 'title':
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$postType->title =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $value, $postType, 'archive' ) );
|
||||
break;
|
||||
case 'metadesc':
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$postType->metaDescription =
|
||||
aioseo()->helpers->sanitizeOption( aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $value, $postType, 'archive' ) );
|
||||
break;
|
||||
case 'noindex':
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$postType->show = empty( $value ) ? true : false;
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$postType->advanced->robotsMeta->default = empty( $value ) ? true : false;
|
||||
aioseo()->dynamicOptions->searchAppearance->archives->$postType->advanced->robotsMeta->noindex = empty( $value ) ? false : true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Knowledge Graph settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateKnowledgeGraphSettings() {
|
||||
if ( ! empty( $this->options['company_or_person'] ) ) {
|
||||
aioseo()->options->searchAppearance->global->schema->siteRepresents =
|
||||
'company' === $this->options['company_or_person'] ? 'organization' : 'person';
|
||||
}
|
||||
|
||||
$settings = [
|
||||
'company_or_person_user_id' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'person' ] ],
|
||||
'person_logo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'personLogo' ] ],
|
||||
'person_name' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'personName' ] ],
|
||||
'company_name' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'organizationName' ] ],
|
||||
'company_logo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'organizationLogo' ] ],
|
||||
'org-email' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'email' ] ],
|
||||
'org-phone' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'phone' ] ],
|
||||
'org-description' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'organizationDescription' ] ],
|
||||
'org-founding-date' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'foundingDate' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options );
|
||||
|
||||
// Additional Info
|
||||
// Reset data
|
||||
aioseo()->options->noConflict()->searchAppearance->global->schema->numberOfEmployees->reset();
|
||||
|
||||
$numberOfEmployees = $this->options['org-number-employees'];
|
||||
if ( ! empty( $numberOfEmployees ) ) {
|
||||
list( $num1, $num2 ) = explode( '-', $numberOfEmployees );
|
||||
|
||||
if ( $num2 ) {
|
||||
aioseo()->options->noConflict()->searchAppearance->global->schema->numberOfEmployees->isRange = true;
|
||||
aioseo()->options->noConflict()->searchAppearance->global->schema->numberOfEmployees->from = (int) $num1;
|
||||
aioseo()->options->noConflict()->searchAppearance->global->schema->numberOfEmployees->to = (int) $num2;
|
||||
} else {
|
||||
aioseo()->options->noConflict()->searchAppearance->global->schema->numberOfEmployees->number = (int) $num1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the RSS content settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateRssContentSettings() {
|
||||
if ( isset( $this->options['rssbefore'] ) ) {
|
||||
aioseo()->options->rssContent->before = esc_html( aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $this->options['rssbefore'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $this->options['rssafter'] ) ) {
|
||||
aioseo()->options->rssContent->after = esc_html( aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $this->options['rssafter'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Redirect Attachments setting.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateRedirectAttachments() {
|
||||
aioseo()->dynamicOptions->searchAppearance->postTypes->attachment->redirectAttachmentUrls = empty( $this->options['disable-attachment'] ) ? 'disabled' : 'attachment';
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the strip category base option.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateStripCategoryBase() {
|
||||
aioseo()->options->searchAppearance->advanced->removeCategoryBase = empty( $this->options['stripcategorybase'] ) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate the social settings for the homepage.
|
||||
*
|
||||
* @since 4.2.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateHomepageSocialSettings() {
|
||||
if (
|
||||
empty( $this->options['open_graph_frontpage_title'] ) &&
|
||||
empty( $this->options['open_graph_frontpage_desc'] ) &&
|
||||
empty( $this->options['open_graph_frontpage_image'] )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->hasImportedHomepageSocialSettings = true;
|
||||
|
||||
$settings = [
|
||||
// These settings can also be found in the SocialMeta class, but Yoast recently moved them here.
|
||||
// We'll still keep them in the other class for backwards compatibility.
|
||||
'open_graph_frontpage_title' => [ 'type' => 'string', 'newOption' => [ 'social', 'facebook', 'homePage', 'title' ] ],
|
||||
'open_graph_frontpage_desc' => [ 'type' => 'string', 'newOption' => [ 'social', 'facebook', 'homePage', 'description' ] ],
|
||||
'open_graph_frontpage_image' => [ 'type' => 'string', 'newOption' => [ 'social', 'facebook', 'homePage', 'image' ] ]
|
||||
];
|
||||
|
||||
aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options, true );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\YoastSeo;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Migrates the Social Meta.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class SocialMeta {
|
||||
/**
|
||||
* List of options.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'wpseo_social' );
|
||||
|
||||
if ( empty( $this->options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->migrateSocialUrls();
|
||||
$this->migrateFacebookSettings();
|
||||
$this->migrateTwitterSettings();
|
||||
$this->migrateFacebookAdminId();
|
||||
$this->migrateSiteName();
|
||||
$this->migrateArticleTags();
|
||||
$this->migrateAdditionalTwitterData();
|
||||
|
||||
$settings = [
|
||||
'pinterestverify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'pinterest' ] ]
|
||||
];
|
||||
|
||||
aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Social URLs.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateSocialUrls() {
|
||||
$settings = [
|
||||
'facebook_site' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'facebookPageUrl' ] ],
|
||||
'instagram_url' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'instagramUrl' ] ],
|
||||
'linkedin_url' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'linkedinUrl' ] ],
|
||||
'myspace_url' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'myspaceUrl' ] ],
|
||||
'pinterest_url' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'pinterestUrl' ] ],
|
||||
'youtube_url' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'youtubeUrl' ] ],
|
||||
'wikipedia_url' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'wikipediaUrl' ] ],
|
||||
'wordpress_url' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'wordPressUrl' ] ],
|
||||
];
|
||||
|
||||
if ( ! empty( $this->options['twitter_site'] ) ) {
|
||||
aioseo()->options->social->profiles->urls->twitterUrl =
|
||||
'https://x.com/' . aioseo()->helpers->sanitizeOption( $this->options['twitter_site'] );
|
||||
}
|
||||
|
||||
aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Facebook settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateFacebookSettings() {
|
||||
if ( ! empty( $this->options['og_default_image'] ) ) {
|
||||
$defaultImage = esc_url( $this->options['og_default_image'] );
|
||||
aioseo()->options->social->facebook->general->defaultImagePosts = $defaultImage;
|
||||
aioseo()->options->social->facebook->general->defaultImageSourcePosts = 'default';
|
||||
|
||||
aioseo()->options->social->twitter->general->defaultImagePosts = $defaultImage;
|
||||
aioseo()->options->social->twitter->general->defaultImageSourcePosts = 'default';
|
||||
}
|
||||
|
||||
$settings = [
|
||||
'opengraph' => [ 'type' => 'boolean', 'newOption' => [ 'social', 'facebook', 'general', 'enable' ] ],
|
||||
];
|
||||
|
||||
if ( ! aioseo()->importExport->yoastSeo->searchAppearance->hasImportedHomepageSocialSettings ) {
|
||||
// These settings were moved to the Search Appearance tab of Yoast, but we'll leave this here to support older versions.
|
||||
// However, we want to make sure we import them only if the other ones aren't set.
|
||||
$settings = array_merge( $settings, [
|
||||
'og_frontpage_title' => [ 'type' => 'string', 'newOption' => [ 'social', 'facebook', 'homePage', 'title' ] ],
|
||||
'og_frontpage_desc' => [ 'type' => 'string', 'newOption' => [ 'social', 'facebook', 'homePage', 'description' ] ],
|
||||
'og_frontpage_image' => [ 'type' => 'string', 'newOption' => [ 'social', 'facebook', 'homePage', 'image' ] ]
|
||||
] );
|
||||
}
|
||||
|
||||
aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options, true );
|
||||
|
||||
// Migrate home page object type.
|
||||
aioseo()->options->social->facebook->homePage->objectType = 'website';
|
||||
if ( 'page' === get_option( 'show_on_front' ) ) {
|
||||
$staticHomePageId = get_option( 'page_on_front' );
|
||||
|
||||
// We must check if the ID exists because one might select the static homepage option but not actually set one.
|
||||
if ( ! $staticHomePageId ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aioseoPost = Models\Post::getPost( (int) $staticHomePageId );
|
||||
$aioseoPost->set( [
|
||||
'og_object_type' => 'website'
|
||||
] );
|
||||
$aioseoPost->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Twitter settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateTwitterSettings() {
|
||||
$settings = [
|
||||
'twitter' => [ 'type' => 'boolean', 'newOption' => [ 'social', 'twitter', 'general', 'enable' ] ],
|
||||
'twitter_card_type' => [ 'type' => 'string', 'newOption' => [ 'social', 'twitter', 'general', 'defaultCardType' ] ],
|
||||
];
|
||||
|
||||
aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the Facebook admin ID.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateFacebookAdminId() {
|
||||
if ( ! empty( $this->options['fbadminapp'] ) ) {
|
||||
aioseo()->options->social->facebook->advanced->enable = true;
|
||||
aioseo()->options->social->facebook->advanced->adminId = aioseo()->helpers->sanitizeOption( $this->options['fbadminapp'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Yoast sets the og:site_name to '#site_title';
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateSiteName() {
|
||||
aioseo()->options->social->facebook->general->siteName = '#site_title';
|
||||
}
|
||||
|
||||
/**
|
||||
* Yoast uses post tags by default, so we need to enable this.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateArticleTags() {
|
||||
aioseo()->options->social->facebook->advanced->enable = true;
|
||||
aioseo()->options->social->facebook->advanced->generateArticleTags = true;
|
||||
aioseo()->options->social->facebook->advanced->usePostTagsInTags = true;
|
||||
aioseo()->options->social->facebook->advanced->useKeywordsInTags = false;
|
||||
aioseo()->options->social->facebook->advanced->useCategoriesInTags = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable additional Twitter Data.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateAdditionalTwitterData() {
|
||||
aioseo()->options->social->twitter->general->additionalData = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\YoastSeo;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\ImportExport;
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
|
||||
/**
|
||||
* Imports the user meta from Yoast SEO.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class UserMeta {
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function scheduleImport() {
|
||||
aioseo()->actionScheduler->scheduleSingle( aioseo()->importExport->yoastSeo->userActionName, 30 );
|
||||
|
||||
if ( ! aioseo()->core->cache->get( 'import_user_meta_yoast_seo' ) ) {
|
||||
aioseo()->core->cache->update( 'import_user_meta_yoast_seo', 0, WEEK_IN_SECONDS );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the post meta.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function importUserMeta() {
|
||||
$usersPerAction = 100;
|
||||
$offset = aioseo()->core->cache->get( 'import_user_meta_yoast_seo' );
|
||||
|
||||
$usersMeta = aioseo()->core->db
|
||||
->start( aioseo()->core->db->db->usermeta . ' as um', true )
|
||||
->whereRaw( "um.meta_key IN ('facebook', 'twitter', 'instagram', 'linkedin', 'myspace', 'pinterest', 'soundcloud', 'tumblr', 'wikipedia', 'youtube', 'mastodon')" )
|
||||
->whereRaw( "um.meta_value != ''" )
|
||||
->limit( $usersPerAction, $offset )
|
||||
->run()
|
||||
->result();
|
||||
|
||||
if ( ! $usersMeta || ! count( $usersMeta ) ) {
|
||||
aioseo()->core->cache->delete( 'import_user_meta_yoast_seo' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$mappedMeta = [
|
||||
'facebook' => 'aioseo_facebook_page_url',
|
||||
'twitter' => 'aioseo_twitter_url',
|
||||
'instagram' => 'aioseo_instagram_url',
|
||||
'linkedin' => 'aioseo_linkedin_url',
|
||||
'myspace' => 'aioseo_myspace_url',
|
||||
'pinterest' => 'aioseo_pinterest_url',
|
||||
'soundcloud' => 'aioseo_sound_cloud_url',
|
||||
'tumblr' => 'aioseo_tumblr_url',
|
||||
'wikipedia' => 'aioseo_wikipedia_url',
|
||||
'youtube' => 'aioseo_youtube_url',
|
||||
'mastodon' => 'aioseo_profiles_additional_urls',
|
||||
];
|
||||
|
||||
foreach ( $usersMeta as $meta ) {
|
||||
if ( isset( $mappedMeta[ $meta->meta_key ] ) ) {
|
||||
$value = 'twitter' === $meta->meta_key ? 'https://x.com/' . $meta->meta_value : $meta->meta_value;
|
||||
update_user_meta( $meta->user_id, $mappedMeta[ $meta->meta_key ], $value );
|
||||
}
|
||||
}
|
||||
|
||||
if ( count( $usersMeta ) === $usersPerAction ) {
|
||||
aioseo()->core->cache->update( 'import_user_meta_yoast_seo', 100 + $offset, WEEK_IN_SECONDS );
|
||||
aioseo()->actionScheduler->scheduleSingle( aioseo()->importExport->yoastSeo->userActionName, 5, [], true );
|
||||
} else {
|
||||
aioseo()->core->cache->delete( 'import_user_meta_yoast_seo' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\ImportExport\YoastSeo;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\ImportExport;
|
||||
|
||||
class YoastSeo extends ImportExport\Importer {
|
||||
/**
|
||||
* A list of plugins to look for to import.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $plugins = [
|
||||
[
|
||||
'name' => 'Yoast SEO',
|
||||
'version' => '14.0',
|
||||
'basename' => 'wordpress-seo/wp-seo.php',
|
||||
'slug' => 'yoast-seo'
|
||||
],
|
||||
[
|
||||
'name' => 'Yoast SEO Premium',
|
||||
'version' => '14.0',
|
||||
'basename' => 'wordpress-seo-premium/wp-seo-premium.php',
|
||||
'slug' => 'yoast-seo-premium'
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The post action name.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $postActionName = 'aioseo_import_post_meta_yoast_seo';
|
||||
|
||||
/**
|
||||
* The user action name.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $userActionName = 'aioseo_import_user_meta_yoast_seo';
|
||||
|
||||
/**
|
||||
* UserMeta class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var UserMeta
|
||||
*/
|
||||
private $userMeta = null;
|
||||
|
||||
/**
|
||||
* SearchAppearance class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var SearchAppearance
|
||||
*/
|
||||
public $searchAppearance = null;
|
||||
|
||||
/**
|
||||
* The post action name.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param ImportExport\ImportExport $importer The main importer class.
|
||||
*/
|
||||
public function __construct( $importer ) {
|
||||
$this->helpers = new Helpers();
|
||||
$this->postMeta = new PostMeta();
|
||||
$this->userMeta = new UserMeta();
|
||||
|
||||
add_action( $this->postActionName, [ $this->postMeta, 'importPostMeta' ] );
|
||||
add_action( $this->userActionName, [ $this->userMeta, 'importUserMeta' ] );
|
||||
|
||||
$plugins = $this->plugins;
|
||||
foreach ( $plugins as $key => $plugin ) {
|
||||
$plugins[ $key ]['class'] = $this;
|
||||
}
|
||||
$importer->addPlugins( $plugins );
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function importSettings() {
|
||||
new GeneralSettings();
|
||||
$this->searchAppearance = new SearchAppearance();
|
||||
// NOTE: The Social Meta settings need to be imported after the Search Appearance ones because some imports depend on what was imported there.
|
||||
new SocialMeta();
|
||||
$this->userMeta->scheduleImport();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Integrations;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to integrate with the bbPress plugin.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*/
|
||||
class BbPress {
|
||||
/**
|
||||
* Returns whether the current page is a bbPress component page.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*
|
||||
* @return bool Whether the current page is a bbPress component page.
|
||||
*/
|
||||
public static function isComponentPage() {
|
||||
return ! empty( aioseo()->standalone->bbPress->component->templateType );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Integrations;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to integrate with the BuddyPress plugin.
|
||||
*
|
||||
* @since 4.7.6
|
||||
*/
|
||||
class BuddyPress {
|
||||
/**
|
||||
* Call the callback given by the first parameter.
|
||||
*
|
||||
* @since 4.7.6
|
||||
*
|
||||
* @param callable $callback The function to be called.
|
||||
* @param mixed ...$args Zero or more parameters to be passed to the function
|
||||
* @return mixed|null The function result or null if the function is not callable.
|
||||
*/
|
||||
public static function callFunc( $callback, ...$args ) {
|
||||
if ( is_callable( $callback ) ) {
|
||||
return call_user_func( $callback, ...$args );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BuddyPress email custom post type slug.
|
||||
*
|
||||
* @since 4.7.6
|
||||
*
|
||||
* @return string The BuddyPress email custom post type slug if found or an empty string.
|
||||
*/
|
||||
public static function getEmailCptSlug() {
|
||||
$slug = '';
|
||||
if ( aioseo()->helpers->isPluginActive( 'buddypress' ) ) {
|
||||
$slug = self::callFunc( 'bp_get_email_post_type' );
|
||||
}
|
||||
|
||||
return is_scalar( $slug ) ? strval( $slug ) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the BuddyPress component archive page permalink.
|
||||
*
|
||||
* @since 4.7.6
|
||||
*
|
||||
* @param string $component The BuddyPress component.
|
||||
* @return string The component archive page permalink.
|
||||
*/
|
||||
public static function getComponentArchiveUrl( $component ) {
|
||||
switch ( $component ) {
|
||||
case 'activity':
|
||||
$output = self::callFunc( 'bp_get_activity_directory_permalink' );
|
||||
break;
|
||||
case 'member':
|
||||
$output = self::callFunc( 'bp_get_members_directory_permalink' );
|
||||
break;
|
||||
case 'group':
|
||||
$output = self::callFunc( 'bp_get_groups_directory_url' );
|
||||
break;
|
||||
default:
|
||||
$output = '';
|
||||
}
|
||||
|
||||
return is_scalar( $output ) ? strval( $output ) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BuddyPress component single page permalink.
|
||||
*
|
||||
* @since 4.7.6
|
||||
*
|
||||
* @param string $component The BuddyPress component.
|
||||
* @param mixed $id The component ID.
|
||||
* @return string The component single page permalink.
|
||||
*/
|
||||
public static function getComponentSingleUrl( $component, $id ) {
|
||||
switch ( $component ) {
|
||||
case 'activity':
|
||||
$output = self::callFunc( 'bp_activity_get_permalink', $id );
|
||||
break;
|
||||
case 'group':
|
||||
$output = self::callFunc( 'bp_get_group_url', $id );
|
||||
break;
|
||||
case 'member':
|
||||
$output = self::callFunc( 'bp_core_get_userlink', $id, false, true );
|
||||
break;
|
||||
default:
|
||||
$output = '';
|
||||
}
|
||||
|
||||
return is_scalar( $output ) ? strval( $output ) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BuddyPress component edit link.
|
||||
*
|
||||
* @since 4.7.6
|
||||
*
|
||||
* @param string $component The BuddyPress component.
|
||||
* @param mixed $id The component ID.
|
||||
* @return string The component edit link.
|
||||
*/
|
||||
public static function getComponentEditUrl( $component, $id ) {
|
||||
switch ( $component ) {
|
||||
case 'activity':
|
||||
$output = add_query_arg( [
|
||||
'page' => 'bp-activity',
|
||||
'aid' => $id,
|
||||
'action' => 'edit'
|
||||
], self::callFunc( 'bp_get_admin_url', 'admin.php' ) );
|
||||
break;
|
||||
case 'group':
|
||||
$output = add_query_arg( [
|
||||
'page' => 'bp-groups',
|
||||
'gid' => $id,
|
||||
'action' => 'edit'
|
||||
], self::callFunc( 'bp_get_admin_url', 'admin.php' ) );
|
||||
break;
|
||||
case 'member':
|
||||
$output = get_edit_user_link( $id );
|
||||
break;
|
||||
default:
|
||||
$output = '';
|
||||
}
|
||||
|
||||
return is_scalar( $output ) ? strval( $output ) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the BuddyPress component is active or not.
|
||||
*
|
||||
* @since 4.7.6
|
||||
*
|
||||
* @param string $component The BuddyPress component.
|
||||
* @return bool Whether the BuddyPress component is active.
|
||||
*/
|
||||
public static function isComponentActive( $component ) {
|
||||
static $active = [];
|
||||
if ( isset( $active[ $component ] ) ) {
|
||||
return $active[ $component ];
|
||||
}
|
||||
|
||||
switch ( $component ) {
|
||||
case 'activity':
|
||||
$active[ $component ] = self::callFunc( 'bp_is_active', 'activity' );
|
||||
break;
|
||||
case 'group':
|
||||
$active[ $component ] = self::callFunc( 'bp_is_active', 'groups' );
|
||||
break;
|
||||
case 'member':
|
||||
$active[ $component ] = self::callFunc( 'bp_is_active', 'members' );
|
||||
break;
|
||||
default:
|
||||
$active[ $component ] = false;
|
||||
}
|
||||
|
||||
return $active[ $component ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current page is a BuddyPress component page.
|
||||
*
|
||||
* @since 4.7.6
|
||||
*
|
||||
* @return bool Whether the current page is a BuddyPress component page.
|
||||
*/
|
||||
public static function isComponentPage() {
|
||||
return ! empty( aioseo()->standalone->buddyPress->component->templateType );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Integrations;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to integrate with the Semrush API.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*/
|
||||
class Semrush {
|
||||
/**
|
||||
* The Oauth2 URL.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $url = 'https://oauth.semrush.com/oauth2/access_token';
|
||||
|
||||
/**
|
||||
* The client ID for the Oauth2 integration.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $clientId = 'aioseo';
|
||||
|
||||
/**
|
||||
* The client secret for the Oauth2 integration.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $clientSecret = 'sdDUjYt6umO7sKM7mp4OrN8yeePTOQBy';
|
||||
|
||||
/**
|
||||
* Static method to authenticate the user.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*
|
||||
* @param string $authorizationCode The authorization code for the Oauth2 authentication.
|
||||
* @return bool Whether the user is succesfully authenticated.
|
||||
*/
|
||||
public static function authenticate( $authorizationCode ) {
|
||||
$time = time();
|
||||
$response = wp_remote_post( self::$url, [
|
||||
'headers' => [ 'Content-Type' => 'application/json' ],
|
||||
'body' => wp_json_encode( [
|
||||
'client_id' => self::$clientId,
|
||||
'client_secret' => self::$clientSecret,
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => $authorizationCode,
|
||||
'redirect_uri' => 'https://oauth.semrush.com/oauth2/aioseo/success'
|
||||
] )
|
||||
] );
|
||||
|
||||
$responseCode = wp_remote_retrieve_response_code( $response );
|
||||
if ( 200 === $responseCode ) {
|
||||
$tokens = json_decode( wp_remote_retrieve_body( $response ) );
|
||||
|
||||
return self::saveTokens( $tokens, $time );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to refresh the tokens once expired.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*
|
||||
* @return bool Whether the tokens were successfully renewed.
|
||||
*/
|
||||
public static function refreshTokens() {
|
||||
$refreshToken = aioseo()->internalOptions->integrations->semrush->refreshToken;
|
||||
if ( empty( $refreshToken ) ) {
|
||||
self::reset();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$time = time();
|
||||
$response = wp_remote_post( self::$url, [
|
||||
'headers' => [ 'Content-Type' => 'application/json' ],
|
||||
'body' => wp_json_encode( [
|
||||
'client_id' => self::$clientId,
|
||||
'client_secret' => self::$clientSecret,
|
||||
'grant_type' => 'refresh_token',
|
||||
'refresh_token' => $refreshToken
|
||||
] )
|
||||
] );
|
||||
|
||||
$responseCode = wp_remote_retrieve_response_code( $response );
|
||||
if ( 200 === $responseCode ) {
|
||||
$tokens = json_decode( wp_remote_retrieve_body( $response ) );
|
||||
|
||||
return self::saveTokens( $tokens, $time );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out the internal options to reset the tokens.
|
||||
*
|
||||
* @since 4.1.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function reset() {
|
||||
aioseo()->internalOptions->integrations->semrush->accessToken = '';
|
||||
aioseo()->internalOptions->integrations->semrush->tokenType = '';
|
||||
aioseo()->internalOptions->integrations->semrush->expires = '';
|
||||
aioseo()->internalOptions->integrations->semrush->refreshToken = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the token has expired
|
||||
*
|
||||
* @since 4.0.16
|
||||
*
|
||||
* @return boolean Whether or not the token has expired.
|
||||
*/
|
||||
public static function hasExpired() {
|
||||
$tokens = self::getTokens();
|
||||
|
||||
return time() >= $tokens['expires'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tokens.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*
|
||||
* @return array An array of token data.
|
||||
*/
|
||||
public static function getTokens() {
|
||||
return aioseo()->internalOptions->integrations->semrush->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the token options.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*
|
||||
* @param Object $tokens The tokens object.
|
||||
* @param string $time The time set before the request was made.
|
||||
* @return bool Whether the response was valid and successfully saved.
|
||||
*/
|
||||
public static function saveTokens( $tokens, $time ) {
|
||||
$expectedProps = [
|
||||
'access_token',
|
||||
'token_type',
|
||||
'expires_in',
|
||||
'refresh_token'
|
||||
];
|
||||
|
||||
// If the oAuth response does not include all expected properties, drop it.
|
||||
foreach ( $expectedProps as $prop ) {
|
||||
if ( empty( $tokens->$prop ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the options.
|
||||
aioseo()->internalOptions->integrations->semrush->accessToken = $tokens->access_token;
|
||||
aioseo()->internalOptions->integrations->semrush->tokenType = $tokens->token_type;
|
||||
aioseo()->internalOptions->integrations->semrush->expires = $time + $tokens->expires_in;
|
||||
aioseo()->internalOptions->integrations->semrush->refreshToken = $tokens->refresh_token;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* API call to get keyphrases from semrush.
|
||||
*
|
||||
* @since 4.0.16
|
||||
*
|
||||
* @param string $keyphrase A primary keyphrase.
|
||||
* @param string $database A country database.
|
||||
* @return object|bool The response object or false if the tokens could not be refreshed.
|
||||
*/
|
||||
public static function getKeyphrases( $keyphrase, $database ) {
|
||||
if ( self::hasExpired() ) {
|
||||
$success = self::refreshTokens();
|
||||
if ( ! $success ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$transientKey = 'semrush_keyphrases_' . $keyphrase . '_' . $database;
|
||||
$results = aioseo()->core->cache->get( $transientKey );
|
||||
|
||||
if ( null !== $results ) {
|
||||
return $results;
|
||||
}
|
||||
|
||||
$params = [
|
||||
'phrase' => $keyphrase,
|
||||
'export_columns' => 'Ph,Nq,Td',
|
||||
'database' => strtolower( $database ),
|
||||
'display_limit' => 10,
|
||||
'display_offset' => 0,
|
||||
'display_sort' => 'nq_desc',
|
||||
'display_filter' => '%2B|Nq|Lt|1000',
|
||||
'access_token' => aioseo()->internalOptions->integrations->semrush->accessToken
|
||||
];
|
||||
|
||||
$url = 'https://oauth.semrush.com/api/v1/keywords/phrase_fullsearch?' . http_build_query( $params );
|
||||
|
||||
$response = wp_remote_get( $url );
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ) );
|
||||
|
||||
aioseo()->core->cache->update( $transientKey, $body );
|
||||
|
||||
return $body;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Integrations;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Route class for the API.
|
||||
*
|
||||
* @since 4.3.8
|
||||
*/
|
||||
class WpCode {
|
||||
/**
|
||||
* Load the WPCode snippets for our desired username or return an empty array if not available.
|
||||
*
|
||||
* @since 4.3.8
|
||||
*
|
||||
* @return array The snippets.
|
||||
*/
|
||||
public static function loadWpCodeSnippets() {
|
||||
$snippets = self::getPlaceholderSnippets();
|
||||
if ( function_exists( 'wpcode_get_library_snippets_by_username' ) ) {
|
||||
$snippets = wpcode_get_library_snippets_by_username( 'aioseo' );
|
||||
}
|
||||
|
||||
return $snippets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the plugin is installed, either the lite or premium version.
|
||||
*
|
||||
* @since 4.3.8
|
||||
*
|
||||
* @return bool True if the plugin is installed.
|
||||
*/
|
||||
public static function isPluginInstalled() {
|
||||
return self::isProInstalled() || self::isLiteInstalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the pro plugin installed.
|
||||
*
|
||||
* @since 4.3.8
|
||||
*
|
||||
* @return bool True if the pro plugin is installed.
|
||||
*/
|
||||
public static function isProInstalled() {
|
||||
$installedPlugins = array_keys( get_plugins() );
|
||||
|
||||
return in_array( 'wpcode-premium/wpcode.php', $installedPlugins, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the lite plugin installed.
|
||||
*
|
||||
* @since 4.3.8
|
||||
*
|
||||
* @return bool True if the lite plugin is installed.
|
||||
*/
|
||||
public static function isLiteInstalled() {
|
||||
$installedPlugins = array_keys( get_plugins() );
|
||||
|
||||
return in_array( 'insert-headers-and-footers/ihaf.php', $installedPlugins, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic check if the plugin is active by looking for the main function.
|
||||
*
|
||||
* @since 4.3.8
|
||||
*
|
||||
* @return bool True if the plugin is active.
|
||||
*/
|
||||
public static function isPluginActive() {
|
||||
return function_exists( 'wpcode' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the plugin is active but needs to be updated by checking if the function to load the
|
||||
* library snippets by username exists.
|
||||
*
|
||||
* @since 4.3.8
|
||||
*
|
||||
* @return bool True if the plugin is active but needs to be updated.
|
||||
*/
|
||||
public static function pluginNeedsUpdate() {
|
||||
return self::isPluginActive() && ! function_exists( 'wpcode_get_library_snippets_by_username' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get placeholder snippets if the WPCode snippets are not available.
|
||||
*
|
||||
* @since 4.3.8
|
||||
*
|
||||
* @return array The placeholder snippets.
|
||||
*/
|
||||
private static function getPlaceholderSnippets() {
|
||||
$snippetTitles = [
|
||||
'Disable autogenerated shipping details schema for WooCommerce',
|
||||
'Disable SEO Preview feature',
|
||||
'Disable Shortcode Parsing in All in One SEO',
|
||||
'Enable WooCommerce Product Attributes in Search Appearance',
|
||||
'Fix LearnPress conflict that hides AIOSEO tabs on settings pages',
|
||||
'Limit Meta Description to 160 characters',
|
||||
'Limit SEO Title to 60 characters',
|
||||
'Noindex Product Search Pages',
|
||||
'Noindex Products under a Product Category',
|
||||
];
|
||||
|
||||
$placeholderSnippets = [];
|
||||
foreach ( $snippetTitles as $snippetTitle ) {
|
||||
// Add placeholder install link so we show a button.
|
||||
$placeholderSnippets[] = [
|
||||
'title' => $snippetTitle,
|
||||
'install' => 'https://library.wpcode.com/'
|
||||
];
|
||||
}
|
||||
|
||||
return $placeholderSnippets;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Main;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class that Pro and Lite both extend.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Activate {
|
||||
/**
|
||||
* Construct method.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
register_activation_hook( AIOSEO_FILE, [ $this, 'activate' ] );
|
||||
register_deactivation_hook( AIOSEO_FILE, [ $this, 'deactivate' ] );
|
||||
|
||||
// The following only needs to happen when in the admin.
|
||||
if ( ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This needs to run on at least 1000 because we load the roles in the Access class on 999.
|
||||
add_action( 'init', [ $this, 'init' ], 1000 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize activation.
|
||||
*
|
||||
* @since 4.1.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
// If Pro just deactivated the lite version, we need to manually run the activation hook, because it doesn't run here.
|
||||
$proDeactivatedLite = (bool) aioseo()->core->cache->get( 'pro_just_deactivated_lite' );
|
||||
if ( ! $proDeactivatedLite ) {
|
||||
// Also check for the old transient in the options table (because a user might switch from an older Lite version that lacks the Cache class).
|
||||
$proDeactivatedLite = (bool) get_option( '_aioseo_cache_pro_just_deactivated_lite' );
|
||||
}
|
||||
|
||||
if ( $proDeactivatedLite ) {
|
||||
aioseo()->core->cache->delete( 'pro_just_deactivated_lite' );
|
||||
$this->activate( false );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs on activation.
|
||||
*
|
||||
* @since 4.0.17
|
||||
*
|
||||
* @param bool $networkWide Whether or not this is a network wide activation.
|
||||
* @return void
|
||||
*/
|
||||
public function activate( $networkWide ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
aioseo()->access->addCapabilities();
|
||||
|
||||
// Make sure our tables exist.
|
||||
aioseo()->updates->addInitialCustomTablesForV4();
|
||||
|
||||
// Set the activation timestamps.
|
||||
$time = time();
|
||||
aioseo()->internalOptions->internal->activated = $time;
|
||||
|
||||
if ( ! aioseo()->internalOptions->internal->firstActivated ) {
|
||||
aioseo()->internalOptions->internal->firstActivated = $time;
|
||||
}
|
||||
|
||||
aioseo()->core->cache->clear();
|
||||
|
||||
$this->maybeRunSetupWizard();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs on deactivation.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deactivate() {
|
||||
aioseo()->access->removeCapabilities();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we should redirect on activation.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function maybeRunSetupWizard() {
|
||||
if ( '0.0' !== aioseo()->internalOptions->internal->lastActiveVersion ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$oldOptions = get_option( 'aioseop_options' );
|
||||
if ( ! empty( $oldOptions ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_network_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isset( $_GET['activate-multi'] ) ) { // phpcs:ignore HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended
|
||||
return;
|
||||
}
|
||||
|
||||
// Sets 30 second transient for welcome screen redirect on activation.
|
||||
aioseo()->core->cache->update( 'activation_redirect', true, 30 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds our capabilities to all roles on the next request and the installing user on the current request after upgrading to Pro.
|
||||
*
|
||||
* @link https://github.com/awesomemotive/aioseo/issues/2267
|
||||
* @link https://github.com/awesomemotive/aioseo/issues/2288
|
||||
*
|
||||
* @since 4.1.4.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addCapabilitiesOnUpgrade() {
|
||||
// In case the user is switching to Pro via the AIOSEO Connect feature,
|
||||
// we need to set this transient here as the regular activation hooks won't run and Pro otherwise won't clear the cache and add the required capabilities.
|
||||
aioseo()->core->cache->update( 'pro_just_deactivated_lite', true );
|
||||
|
||||
// Doing the above isn't sufficient because the current user will be lacking the capabilities on the first request. Therefore, we add them manually just for him.
|
||||
$userId = function_exists( 'get_current_user_id' ) && get_current_user_id()
|
||||
? get_current_user_id() // If there is a logged in user, the user is switching from Lite to Pro via the Plugins menu.
|
||||
: aioseo()->core->cache->get( 'connect_active_user' ); // If there is no logged in user, we're upgrading via AIOSEO Connect.
|
||||
|
||||
$user = get_userdata( $userId );
|
||||
if ( is_object( $user ) ) {
|
||||
$capabilities = aioseo()->access->getCapabilityList();
|
||||
foreach ( $capabilities as $capability ) {
|
||||
$user->add_cap( $capability );
|
||||
}
|
||||
}
|
||||
|
||||
aioseo()->core->cache->delete( 'connect_active_user' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Main;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main class with methods that are called.
|
||||
*
|
||||
* @since 4.2.0
|
||||
* @version 4.7.1 Moved from Pro to Common.
|
||||
*/
|
||||
class CategoryBase {
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( ! aioseo()->options->searchAppearance->advanced->removeCategoryBase ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( 'query_vars', [ $this, 'queryVars' ] );
|
||||
add_filter( 'request', [ $this, 'maybeRedirectCategoryUrl' ] );
|
||||
add_filter( 'category_rewrite_rules', [ $this, 'categoryRewriteRules' ] );
|
||||
add_filter( 'term_link', [ $this, 'modifyTermLink' ], 10, 3 );
|
||||
|
||||
// Flush rewrite rules on any of the following actions.
|
||||
add_action( 'created_category', [ $this, 'scheduleFlushRewrite' ] );
|
||||
add_action( 'delete_category', [ $this, 'scheduleFlushRewrite' ] );
|
||||
add_action( 'edited_category', [ $this, 'scheduleFlushRewrite' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the redirect var to the query vars if the "strip category bases" option is enabled.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @param array $queryVars Query vars to filter.
|
||||
* @return array The filtered query vars.
|
||||
*/
|
||||
public function queryVars( $queryVars ) {
|
||||
$queryVars[] = 'aioseo_category_redirect';
|
||||
|
||||
return $queryVars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect the category URL to the new one.
|
||||
*
|
||||
* @param array $queryVars Query vars to check for redirect var.
|
||||
* @return array The original query vars.
|
||||
*/
|
||||
public function maybeRedirectCategoryUrl( $queryVars ) {
|
||||
if ( isset( $queryVars['aioseo_category_redirect'] ) ) {
|
||||
$categoryUrl = trailingslashit( get_option( 'home' ) ) . user_trailingslashit( $queryVars['aioseo_category_redirect'], 'category' );
|
||||
wp_redirect( $categoryUrl, 301, 'AIOSEO' );
|
||||
die;
|
||||
}
|
||||
|
||||
return $queryVars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite the category base.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @return array The rewritten rules.
|
||||
*/
|
||||
public function categoryRewriteRules() {
|
||||
global $wp_rewrite; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
|
||||
$categoryRewrite = $this->getCategoryRewriteRules();
|
||||
|
||||
// Redirect from the old base.
|
||||
$categoryStructure = $wp_rewrite->get_category_permastruct(); // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
$categoryBase = trim( str_replace( '%category%', '(.+)', $categoryStructure ), '/' ) . '$';
|
||||
|
||||
// Add the rewrite rules.
|
||||
$categoryRewrite[ $categoryBase ] = 'index.php?aioseo_category_redirect=$matches[1]';
|
||||
|
||||
return $categoryRewrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rewrite rules for the category.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @return array An array of category rewrite rules.
|
||||
*/
|
||||
private function getCategoryRewriteRules() {
|
||||
global $wp_rewrite; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
|
||||
$categoryRewrite = [];
|
||||
$categories = get_categories( [ 'hide_empty' => false ] );
|
||||
|
||||
if ( empty( $categories ) ) {
|
||||
return $categoryRewrite;
|
||||
}
|
||||
|
||||
$blogPrefix = $this->getBlogPrefix();
|
||||
$paginationBase = $wp_rewrite->pagination_base; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
foreach ( $categories as $category ) {
|
||||
$nicename = $this->getCategoryParents( $category ) . $category->slug;
|
||||
$categoryRewrite = $this->addCategoryRewrites( $categoryRewrite, $nicename, $blogPrefix, $paginationBase );
|
||||
|
||||
// Also add the rules for uppercase.
|
||||
$filteredNicename = $this->convertEncodedToUppercase( $nicename );
|
||||
|
||||
if ( $nicename !== $filteredNicename ) {
|
||||
$categoryRewrite = $this->addCategoryRewrites( $categoryRewrite, $filteredNicename, $blogPrefix, $paginationBase );
|
||||
}
|
||||
}
|
||||
|
||||
return $categoryRewrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the blog prefix.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @return string The prefix for the blog.
|
||||
*/
|
||||
private function getBlogPrefix() {
|
||||
$permalinkStructure = get_option( 'permalink_structure' );
|
||||
if (
|
||||
is_multisite() &&
|
||||
! is_subdomain_install() &&
|
||||
is_main_site() &&
|
||||
0 === strpos( $permalinkStructure, '/blog/' )
|
||||
) {
|
||||
return 'blog/';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve category parents with separator.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @param \WP_Term $category the category instance.
|
||||
* @return string A list of category parents.
|
||||
*/
|
||||
private function getCategoryParents( $category ) {
|
||||
if (
|
||||
$category->parent === $category->term_id ||
|
||||
absint( $category->parent ) < 1
|
||||
) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$parents = get_category_parents( $category->parent, false, '/', true );
|
||||
|
||||
return is_wp_error( $parents ) ? '' : $parents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks through category nicename and convert encoded parts
|
||||
* into uppercase using $this->encode_to_upper().
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @param string $nicename The encoded category string.
|
||||
* @return string The converted category string.
|
||||
*/
|
||||
private function convertEncodedToUppercase( $nicename ) {
|
||||
// Checks if name has any encoding in it.
|
||||
if ( false === strpos( $nicename, '%' ) ) {
|
||||
return $nicename;
|
||||
}
|
||||
|
||||
$nicenames = explode( '/', $nicename );
|
||||
$nicenames = array_map( [ $this, 'convertToUppercase' ], $nicenames );
|
||||
|
||||
return implode( '/', $nicenames );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the encoded URI string to uppercase.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @param string $encoded The encoded category string.
|
||||
* @return string The converted category string.
|
||||
*/
|
||||
private function convertToUppercase( $encoded ) {
|
||||
if ( false === strpos( $encoded, '%' ) ) {
|
||||
return $encoded;
|
||||
}
|
||||
|
||||
return strtoupper( $encoded );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the required category rewrites rules.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @param array $categoryRewrite The current set of rules.
|
||||
* @param string $categoryNicename The category nicename.
|
||||
* @param string $blogPrefix Multisite blog prefix.
|
||||
* @param string $paginationBase WP_Query pagination base.
|
||||
* @return array The added set of rules.
|
||||
*/
|
||||
private function addCategoryRewrites( $categoryRewrite, $categoryNicename, $blogPrefix, $paginationBase ) {
|
||||
$categoryRewrite[ $blogPrefix . '(' . $categoryNicename . ')/(?:feed/)?(feed|rdf|rss|rss2|atom)/?$' ] = 'index.php?category_name=$matches[1]&feed=$matches[2]';
|
||||
$categoryRewrite[ $blogPrefix . '(' . $categoryNicename . ')/' . $paginationBase . '/?([0-9]{1,})/?$' ] = 'index.php?category_name=$matches[1]&paged=$matches[2]';
|
||||
$categoryRewrite[ $blogPrefix . '(' . $categoryNicename . ')/?$' ] = 'index.php?category_name=$matches[1]';
|
||||
|
||||
return $categoryRewrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the category base from the category link.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @param string $link Term link.
|
||||
* @param object $term The current Term Object.
|
||||
* @param string $taxonomy The current Taxonomy.
|
||||
* @return string The modified term link.
|
||||
*/
|
||||
public function modifyTermLink( $link, $term = null, $taxonomy = '' ) {
|
||||
if ( 'category' !== $taxonomy ) {
|
||||
return $link;
|
||||
}
|
||||
|
||||
$categoryBase = get_option( 'category_base' );
|
||||
if ( empty( $categoryBase ) ) {
|
||||
global $wp_rewrite; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
$categoryStructure = $wp_rewrite->get_category_permastruct(); // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
$categoryBase = trim( str_replace( '%category%', '', $categoryStructure ), '/' );
|
||||
}
|
||||
|
||||
// Remove initial slash, if there is one (we remove the trailing slash in the regex replacement and don't want to end up short a slash).
|
||||
if ( '/' === substr( $categoryBase, 0, 1 ) ) {
|
||||
$categoryBase = substr( $categoryBase, 1 );
|
||||
}
|
||||
|
||||
$categoryBase .= '/';
|
||||
|
||||
return preg_replace( '`' . preg_quote( (string) $categoryBase, '`' ) . '`u', '', (string) $link, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the rewrite rules.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function scheduleFlushRewrite() {
|
||||
aioseo()->options->flushRewriteRules();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,605 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Main;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
use AIOSEO\Plugin\Common\Integrations\BuddyPress as BuddyPressIntegration;
|
||||
|
||||
/**
|
||||
* Abstract class that Pro and Lite both extend.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
abstract class Filters {
|
||||
/**
|
||||
* The plugin we are checking.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $plugin;
|
||||
|
||||
/**
|
||||
* ID of the WooCommerce product that is being duplicated.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private static $originalProductId;
|
||||
|
||||
/**
|
||||
* Construct method.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'wp_optimize_get_tables', [ $this, 'wpOptimizeAioseoTables' ] );
|
||||
|
||||
// This action needs to run on AJAX/cron for scheduled rewritten posts in Yoast Duplicate Post.
|
||||
add_action( 'duplicate_post_after_rewriting', [ $this, 'updateRescheduledPostMeta' ], 10, 2 );
|
||||
|
||||
if ( wp_doing_ajax() || wp_doing_cron() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( 'plugin_row_meta', [ $this, 'pluginRowMeta' ], 10, 2 );
|
||||
add_filter( 'plugin_action_links_' . AIOSEO_PLUGIN_BASENAME, [ $this, 'pluginActionLinks' ], 10, 2 );
|
||||
|
||||
// Genesis theme compatibility.
|
||||
add_filter( 'genesis_detect_seo_plugins', [ $this, 'genesisTheme' ] );
|
||||
|
||||
// WeGlot compatibility.
|
||||
if ( isset( $_SERVER['REQUEST_URI'] ) && preg_match( '#(/default-sitemap\.xsl)$#i', (string) sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) ) {
|
||||
add_filter( 'weglot_active_translation_before_treat_page', '__return_false' );
|
||||
}
|
||||
|
||||
if ( isset( $_SERVER['REQUEST_URI'] ) && preg_match( '#(\.xml)$#i', (string) sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) ) {
|
||||
add_filter( 'jetpack_boost_should_defer_js', '__return_false' );
|
||||
}
|
||||
|
||||
// GoDaddy CDN compatibility.
|
||||
add_filter( 'wpaas_cdn_file_ext', [ $this, 'goDaddySitemapXml' ] );
|
||||
|
||||
// Duplicate Post integration.
|
||||
add_action( 'dp_duplicate_post', [ $this, 'duplicatePost' ], 10, 2 );
|
||||
add_action( 'dp_duplicate_page', [ $this, 'duplicatePost' ], 10, 2 );
|
||||
add_action( 'woocommerce_product_duplicate_before_save', [ $this, 'scheduleDuplicateProduct' ], 10, 2 );
|
||||
add_action( 'add_post_meta', [ $this, 'rewriteAndRepublish' ], 10, 3 );
|
||||
|
||||
// BBpress compatibility.
|
||||
add_action( 'init', [ $this, 'resetUserBBPress' ], -1 );
|
||||
add_filter( 'the_title', [ $this, 'maybeRemoveBBPressReplyFilter' ], 0, 2 );
|
||||
|
||||
// Bypass the JWT Auth plugin's unnecessary restrictions. https://wordpress.org/plugins/jwt-auth/
|
||||
add_filter( 'jwt_auth_default_whitelist', [ $this, 'allowRestRoutes' ] );
|
||||
|
||||
// Clear the site authors cache.
|
||||
add_action( 'profile_update', [ $this, 'clearAuthorsCache' ] );
|
||||
add_action( 'user_register', [ $this, 'clearAuthorsCache' ] );
|
||||
|
||||
add_filter( 'aioseo_public_post_types', [ $this, 'removeInvalidPublicPostTypes' ] );
|
||||
add_filter( 'aioseo_public_taxonomies', [ $this, 'removeInvalidPublicTaxonomies' ] );
|
||||
|
||||
add_action( 'admin_print_scripts', [ $this, 'removeEmojiDetectionScripts' ], 0 );
|
||||
|
||||
// Disable Jetpack sitemaps module.
|
||||
if ( aioseo()->options->sitemap->general->enable ) {
|
||||
add_filter( 'jetpack_get_available_modules', [ $this, 'disableJetpackSitemaps' ] );
|
||||
}
|
||||
|
||||
add_action( 'after_setup_theme', [ $this, 'removeHelloElementorDescriptionTag' ] );
|
||||
add_action( 'wp', [ $this, 'removeAvadaOgTags' ] );
|
||||
add_action( 'init', [ $this, 'declareAioseoFollowingConsentApi' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares AIOSEO and its addons as following the Consent API.
|
||||
*
|
||||
* @since 4.6.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function declareAioseoFollowingConsentApi() {
|
||||
add_filter( 'wp_consent_api_registered_all-in-one-seo-pack/all_in_one_seo_pack.php', '__return_true' );
|
||||
add_filter( 'wp_consent_api_registered_all-in-one-seo-pack-pro/all_in_one_seo_pack.php', '__return_true' );
|
||||
|
||||
foreach ( aioseo()->addons->getAddons() as $addon ) {
|
||||
if ( empty( $addon->installed ) || empty( $addon->basename ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( isset( $addon->basename ) ) {
|
||||
add_filter( 'wp_consent_api_registered_' . $addon->basename, '__return_true' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes emoji detection scripts on WP 6.2 which broke our Emojis.
|
||||
*
|
||||
* @since 4.3.4.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeEmojiDetectionScripts() {
|
||||
global $wp_version; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
if ( version_compare( $wp_version, '6.2', '>=' ) ) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the current user if bbPress is active.
|
||||
* We have to do this because our calls to wp_get_current_user() set the current user early and this breaks core functionality in bbPress.
|
||||
*
|
||||
* @link https://github.com/awesomemotive/aioseo/issues/22300
|
||||
*
|
||||
* @since 4.1.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function resetUserBBPress() {
|
||||
if ( function_exists( 'bbpress' ) ) {
|
||||
global $current_user; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
$current_user = null; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the bbPress title filter when adding a new reply with empty title to avoid fatal error.
|
||||
*
|
||||
* @link https://github.com/awesomemotive/aioseo/issues/4183
|
||||
*
|
||||
* @since 4.3.1
|
||||
*
|
||||
* @param string $title The post title.
|
||||
* @param int $id The post ID (optional - in order to fix an issue where other plugins/themes don't pass in the second arg).
|
||||
* @return string The post title.
|
||||
*/
|
||||
public function maybeRemoveBBPressReplyFilter( $title, $id = 0 ) {
|
||||
if (
|
||||
function_exists( 'bbp_get_reply_post_type' ) &&
|
||||
get_post_type( $id ) === bbp_get_reply_post_type() &&
|
||||
aioseo()->helpers->isScreenBase( 'post' )
|
||||
) {
|
||||
remove_filter( 'the_title', 'bbp_get_reply_title_fallback', 2 );
|
||||
}
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicates the model when duplicate post is triggered.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param integer $targetPostId The target post ID.
|
||||
* @param \WP_Post $sourcePost The source post object.
|
||||
* @return void
|
||||
*/
|
||||
public function duplicatePost( $targetPostId, $sourcePost = null ) {
|
||||
$sourcePostId = ! empty( $sourcePost->ID ) ? $sourcePost->ID : $sourcePost;
|
||||
$sourceAioseoPost = Models\Post::getPost( $sourcePostId );
|
||||
$targetPost = Models\Post::getPost( $targetPostId );
|
||||
|
||||
$columns = $sourceAioseoPost->getColumns();
|
||||
foreach ( $columns as $column => $value ) {
|
||||
// Skip the ID column.
|
||||
if ( 'id' === $column ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( 'post_id' === $column ) {
|
||||
$targetPost->$column = $targetPostId;
|
||||
continue;
|
||||
}
|
||||
|
||||
$targetPost->$column = $sourceAioseoPost->$column;
|
||||
}
|
||||
|
||||
$targetPost->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicates the model when rewrite and republish is triggered.
|
||||
*
|
||||
* @since 4.3.4
|
||||
*
|
||||
* @param integer $postId The post ID.
|
||||
* @param string $metaKey The meta key.
|
||||
* @param mixed $metaValue The meta value.
|
||||
* @return void
|
||||
*/
|
||||
public function rewriteAndRepublish( $postId, $metaKey = '', $metaValue = '' ) {
|
||||
if ( '_dp_has_rewrite_republish_copy' !== $metaKey ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$originalPost = aioseo()->helpers->getPost( $postId );
|
||||
if ( ! is_object( $originalPost ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->duplicatePost( (int) $metaValue, $originalPost );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the model when a post is republished.
|
||||
* Yoast Duplicate Post doesn't do this since we store our data in a custom table.
|
||||
*
|
||||
* @since 4.6.7
|
||||
*
|
||||
* @param int $scheduledPostId The ID of the scheduled post.
|
||||
* @param int $originalPostId The ID of the original post.
|
||||
* @return void
|
||||
*/
|
||||
public function updateRescheduledPostMeta( $scheduledPostId, $originalPostId ) {
|
||||
$this->duplicatePost( $originalPostId, $scheduledPostId );
|
||||
|
||||
// Delete the AIOSEO post record for the scheduled post.
|
||||
$scheduledAioseoPost = Models\Post::getPost( $scheduledPostId );
|
||||
$scheduledAioseoPost->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules an action to duplicate our meta after the duplicated WooCommerce product has been saved.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @param \WC_Product $newProduct The new, duplicated product.
|
||||
* @param \WC_Product $originalProduct The original product.
|
||||
* @return void
|
||||
*/
|
||||
public function scheduleDuplicateProduct( $newProduct, $originalProduct = null ) {
|
||||
self::$originalProductId = $originalProduct->get_id();
|
||||
add_action( 'wp_insert_post', [ $this, 'duplicateProduct' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicates our meta for the new WooCommerce product.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @param integer $postId The new post ID.
|
||||
* @param \WP_Post $post The new post object.
|
||||
* @return void
|
||||
*/
|
||||
public function duplicateProduct( $postId, $post = null ) {
|
||||
if ( ! self::$originalProductId || 'product' !== $post->post_type ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->duplicatePost( $postId, self::$originalProductId );
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable SEO inside the Genesis theme if it's running.
|
||||
*
|
||||
* @since 4.0.3
|
||||
*
|
||||
* @param array $array An array of checks.
|
||||
* @return array An array with our function added.
|
||||
*/
|
||||
public function genesisTheme( $array ) {
|
||||
if ( empty( $array ) || ! isset( $array['functions'] ) ) {
|
||||
return $array;
|
||||
}
|
||||
|
||||
$array['functions'][] = 'aioseo';
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove XML from the GoDaddy CDN so our urls remain intact.
|
||||
*
|
||||
* @since 4.0.5
|
||||
*
|
||||
* @param array $extensions The original extensions list.
|
||||
* @return array The extensions list without xml.
|
||||
*/
|
||||
public function goDaddySitemapXml( $extensions ) {
|
||||
$key = array_search( 'xml', $extensions, true );
|
||||
unset( $extensions[ $key ] );
|
||||
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers our row meta for the plugins page.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param array $actions List of existing actions.
|
||||
* @param string $pluginFile The plugin file.
|
||||
* @return array List of action links.
|
||||
*/
|
||||
abstract public function pluginRowMeta( $actions, $pluginFile = '' );
|
||||
|
||||
/**
|
||||
* Registers our action links for the plugins page.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param array $actions List of existing actions.
|
||||
* @param string $pluginFile The plugin file.
|
||||
* @return array List of action links.
|
||||
*/
|
||||
abstract public function pluginActionLinks( $actions, $pluginFile = '' );
|
||||
|
||||
/**
|
||||
* Parses the action links.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param array $actions The actions.
|
||||
* @param string $pluginFile The plugin file.
|
||||
* @param array $actionLinks The action links.
|
||||
* @param string $position The position.
|
||||
* @return array The parsed actions.
|
||||
*/
|
||||
protected function parseActionLinks( $actions, $pluginFile, $actionLinks = [], $position = 'after' ) {
|
||||
if ( empty( $this->plugin ) ) {
|
||||
$this->plugin = AIOSEO_PLUGIN_BASENAME;
|
||||
}
|
||||
|
||||
if ( $this->plugin === $pluginFile && ! empty( $actionLinks ) ) {
|
||||
foreach ( $actionLinks as $key => $value ) {
|
||||
$link = [
|
||||
$key => sprintf(
|
||||
'<a href="%1$s" %2$s target="_blank">%3$s</a>',
|
||||
esc_url( $value['url'] ),
|
||||
isset( $value['title'] ) ? 'title="' . esc_attr( $value['title'] ) . '"' : '',
|
||||
$value['label']
|
||||
)
|
||||
];
|
||||
|
||||
$actions = 'after' === $position ? array_merge( $actions, $link ) : array_merge( $link, $actions );
|
||||
}
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add our routes to this plugins allow list.
|
||||
*
|
||||
* @since 4.1.4
|
||||
*
|
||||
* @param array $allowList The original list.
|
||||
* @return array The modified list.
|
||||
*/
|
||||
public function allowRestRoutes( $allowList ) {
|
||||
return array_merge( $allowList, [
|
||||
'/aioseo/'
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the site authors cache when user is updated or registered.
|
||||
*
|
||||
* @since 4.1.8
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearAuthorsCache() {
|
||||
aioseo()->core->cache->delete( 'site_authors' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out post types that aren't really public when getPublicPostTypes() is called.
|
||||
*
|
||||
* @since 4.1.9
|
||||
*
|
||||
* @param array[object]|array[string] $postTypes The post types.
|
||||
* @return array[object]|array[string] The filtered post types.
|
||||
*/
|
||||
public function removeInvalidPublicPostTypes( $postTypes ) {
|
||||
$postTypesToRemove = [
|
||||
'fusion_element', // Avada
|
||||
'elementor_library',
|
||||
'redirect_rule', // Safe Redirect Manager
|
||||
'seedprod',
|
||||
'tcb_lightbox',
|
||||
|
||||
// Thrive Themes internal post types.
|
||||
'tva_module',
|
||||
'tvo_display',
|
||||
'tvo_capture',
|
||||
'tva_module',
|
||||
'tve_lead_1c_signup',
|
||||
'tve_form_type',
|
||||
'tvd_login_edit',
|
||||
'tve_global_cond_set',
|
||||
'tve_cond_display',
|
||||
'tve_lead_2s_lightbox',
|
||||
'tcb_symbol',
|
||||
'td_nm_notification',
|
||||
'tvd_content_set',
|
||||
'tve_saved_lp',
|
||||
'tve_notifications',
|
||||
'tve_user_template',
|
||||
'tve_video_data',
|
||||
'tva_course_type',
|
||||
'tva-acc-restriction',
|
||||
'tva_course_overview',
|
||||
'tve_ult_schedule',
|
||||
'tqb_optin',
|
||||
'tqb_splash',
|
||||
'tva_certificate',
|
||||
'tva_course_overview',
|
||||
|
||||
// BuddyPress post types.
|
||||
BuddyPressIntegration::getEmailCptSlug()
|
||||
];
|
||||
|
||||
foreach ( $postTypes as $index => $postType ) {
|
||||
if ( is_string( $postType ) && in_array( $postType, $postTypesToRemove, true ) ) {
|
||||
unset( $postTypes[ $index ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( is_array( $postType ) && in_array( $postType['name'], $postTypesToRemove, true ) ) {
|
||||
unset( $postTypes[ $index ] );
|
||||
}
|
||||
}
|
||||
|
||||
return array_values( $postTypes );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out taxonomies that aren't really public when getPublicTaxonomies() is called.
|
||||
*
|
||||
* @since 4.2.4
|
||||
*
|
||||
* @param array[object]|array[string] $taxonomies The taxonomies.
|
||||
* @return array[object]|array[string] The filtered taxonomies.
|
||||
*/
|
||||
public function removeInvalidPublicTaxonomies( $taxonomies ) {
|
||||
$taxonomiesToRemove = [
|
||||
'fusion_tb_category',
|
||||
'element_category',
|
||||
'template_category',
|
||||
|
||||
// Thrive Themes internal taxonomies.
|
||||
'tcb_symbols_tax'
|
||||
];
|
||||
|
||||
foreach ( $taxonomies as $index => $taxonomy ) {
|
||||
if ( is_string( $taxonomy ) && in_array( $taxonomy, $taxonomiesToRemove, true ) ) {
|
||||
unset( $taxonomies[ $index ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( is_array( $taxonomy ) && in_array( $taxonomy['name'], $taxonomiesToRemove, true ) ) {
|
||||
unset( $taxonomies[ $index ] );
|
||||
}
|
||||
}
|
||||
|
||||
return array_values( $taxonomies );
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable Jetpack sitemaps module.
|
||||
*
|
||||
* @since 4.2.2
|
||||
*/
|
||||
public function disableJetpackSitemaps( $active ) {
|
||||
unset( $active['sitemaps'] );
|
||||
|
||||
return $active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeues third-party scripts from the other plugins or themes that crashes our menu pages.
|
||||
*
|
||||
* @since 4.1.9
|
||||
* @version 4.3.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function dequeueThirdPartyAssets() {
|
||||
// TagDiv Opt-in Builder plugin.
|
||||
wp_dequeue_script( 'tds_js_vue_files_last' );
|
||||
|
||||
// MyListing theme.
|
||||
if ( function_exists( 'mylisting' ) ) {
|
||||
wp_dequeue_script( 'vuejs' );
|
||||
wp_dequeue_script( 'theme-script-vendor' );
|
||||
wp_dequeue_script( 'theme-script-main' );
|
||||
}
|
||||
|
||||
// Voxel theme.
|
||||
if ( class_exists( '\Voxel\Controllers\Assets_Controller' ) ) {
|
||||
wp_dequeue_script( 'vue' );
|
||||
wp_dequeue_script( 'vx:backend.js' );
|
||||
}
|
||||
|
||||
// Meta tags for seo plugin.
|
||||
if ( class_exists( '\Pagup\MetaTags\Settings' ) ) {
|
||||
wp_dequeue_script( 'pmt__vuejs' );
|
||||
wp_dequeue_script( 'pmt__script' );
|
||||
}
|
||||
|
||||
// Plugin: Wpbingo Core (By TungHV).
|
||||
if ( strpos( wp_styles()->query( 'bwp-lookbook-css' )->src ?? '', 'wpbingo' ) !== false ) {
|
||||
wp_dequeue_style( 'bwp-lookbook-css' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeues third-party scripts from the other plugins or themes that crashes our menu pages.
|
||||
*
|
||||
* @version 4.3.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function dequeueThirdPartyAssetsEarly() {
|
||||
// Disables scripts for plugins StmMotorsExtends and StmPostType.
|
||||
if ( class_exists( 'STM_Metaboxes' ) ) {
|
||||
remove_action( 'admin_enqueue_scripts', [ 'STM_Metaboxes', 'wpcfto_scripts' ] );
|
||||
}
|
||||
|
||||
// Disables scripts for LearnPress plugin.
|
||||
if ( function_exists( 'learn_press_admin_assets' ) ) {
|
||||
remove_action( 'admin_enqueue_scripts', [ learn_press_admin_assets(), 'load_scripts' ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the duplicate meta description tag from the Hello Elementor theme.
|
||||
*
|
||||
* @since 4.4.3
|
||||
*
|
||||
* @link https://developers.elementor.com/docs/hello-elementor-theme/hello_elementor_add_description_meta_tag/
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeHelloElementorDescriptionTag() {
|
||||
remove_action( 'wp_head', 'hello_elementor_add_description_meta_tag' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the Avada OG tags.
|
||||
*
|
||||
* @since 4.6.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeAvadaOgTags() {
|
||||
if ( function_exists( 'Avada' ) ) {
|
||||
$avada = Avada();
|
||||
if ( is_object( $avada->head ?? null ) ) {
|
||||
remove_action( 'wp_head', [ $avada->head, 'insert_og_meta' ], 5 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent WP-Optimize from deleting our tables.
|
||||
*
|
||||
* @since 4.4.5
|
||||
*
|
||||
* @param array $tables List of tables.
|
||||
* @return array Filtered tables.
|
||||
*/
|
||||
public function wpOptimizeAioseoTables( $tables ) {
|
||||
foreach ( $tables as &$table ) {
|
||||
if (
|
||||
is_object( $table ) &&
|
||||
property_exists( $table, 'Name' ) &&
|
||||
false !== stripos( $table->Name, 'aioseo_' )
|
||||
) {
|
||||
$table->is_using = true;
|
||||
$table->can_be_removed = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Main;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Meta;
|
||||
|
||||
/**
|
||||
* Outputs anything we need to the head of the site.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Head {
|
||||
/**
|
||||
* The page title.
|
||||
*
|
||||
* @since 4.0.5
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $pageTitle = null;
|
||||
|
||||
/**
|
||||
* Title class instance.
|
||||
*
|
||||
* @since 4.3.9
|
||||
*
|
||||
* @var Title
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* Links class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Meta\Links
|
||||
*/
|
||||
protected $links = null;
|
||||
|
||||
/**
|
||||
* Keywords class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Meta\Keywords
|
||||
*/
|
||||
protected $keywords = null;
|
||||
|
||||
/**
|
||||
* Verification class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Meta\SiteVerification
|
||||
*/
|
||||
protected $verification = null;
|
||||
|
||||
/**
|
||||
* The views to output.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $views = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'wp', [ $this, 'registerTitleHooks' ], 1000 );
|
||||
add_action( 'wp_head', [ $this, 'wpHead' ], 1 );
|
||||
|
||||
$this->title = new Title();
|
||||
$this->links = new Meta\Links();
|
||||
$this->keywords = new Meta\Keywords();
|
||||
$this->verification = new Meta\SiteVerification();
|
||||
$this->views = [
|
||||
'meta' => AIOSEO_DIR . '/app/Common/Views/main/meta.php',
|
||||
'social' => AIOSEO_DIR . '/app/Common/Views/main/social.php',
|
||||
'schema' => AIOSEO_DIR . '/app/Common/Views/main/schema.php',
|
||||
'clarity' => AIOSEO_DIR . '/app/Common/Views/main/clarity.php'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers our title hooks.
|
||||
*
|
||||
* @since 4.0.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerTitleHooks() {
|
||||
if ( apply_filters( 'aioseo_disable', false ) || apply_filters( 'aioseo_disable_title_rewrites', false ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( 'pre_get_document_title', [ $this, 'getTitle' ], 99999 );
|
||||
add_filter( 'wp_title', [ $this, 'getTitle' ], 99999 );
|
||||
if ( ! current_theme_supports( 'title-tag' ) ) {
|
||||
add_action( 'template_redirect', [ $this->title, 'startOutputBuffering' ], 99999 );
|
||||
add_action( 'wp_head', [ $this->title, 'endOutputBuffering' ], 99999 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the head.
|
||||
*
|
||||
* @since 4.0.5
|
||||
* @version 4.6.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function wpHead() {
|
||||
$included = new Meta\Included();
|
||||
if ( is_admin() || wp_doing_ajax() || wp_doing_cron() || ! $included->isIncluded() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->output();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page title.
|
||||
*
|
||||
* @since 4.0.5
|
||||
*
|
||||
* @param string $wpTitle The original page title from WordPress.
|
||||
* @return string $pageTitle The page title.
|
||||
*/
|
||||
public function getTitle( $wpTitle = '' ) {
|
||||
if ( null !== self::$pageTitle ) {
|
||||
return self::$pageTitle;
|
||||
}
|
||||
self::$pageTitle = aioseo()->meta->title->filterPageTitle( $wpTitle );
|
||||
|
||||
return self::$pageTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* The output function itself.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function output() {
|
||||
remove_action( 'wp_head', 'rel_canonical' );
|
||||
|
||||
$views = apply_filters( 'aioseo_meta_views', $this->views );
|
||||
if ( empty( $views ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo "\n\t\t<!-- " . sprintf(
|
||||
'%1$s %2$s',
|
||||
esc_html( AIOSEO_PLUGIN_NAME ),
|
||||
aioseo()->helpers->getAioseoVersion() // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
) . " - aioseo.com -->\n";
|
||||
|
||||
foreach ( $views as $view ) {
|
||||
require $view;
|
||||
}
|
||||
|
||||
echo "\t\t<!-- " . esc_html( AIOSEO_PLUGIN_NAME ) . " -->\n\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Main;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models;
|
||||
|
||||
/**
|
||||
* Abstract class that Pro and Lite both extend.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Main {
|
||||
/**
|
||||
* Construct method.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
new Media();
|
||||
new QueryArgs();
|
||||
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueueTranslations' ] );
|
||||
add_action( 'wp_enqueue_scripts', [ $this, 'enqueueFrontEndAssets' ] );
|
||||
add_action( 'admin_footer', [ $this, 'adminFooter' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the translations seperately so it can be called from anywhere.
|
||||
*
|
||||
* @since 4.1.9
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueueTranslations() {
|
||||
aioseo()->core->assets->load( 'src/vue/standalone/app/main.js', [], [
|
||||
'translations' => aioseo()->helpers->getJedLocaleData( 'all-in-one-seo-pack' )
|
||||
], 'aioseoTranslations' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue styles on the front-end.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueueFrontEndAssets() {
|
||||
$canManageSeo = apply_filters( 'aioseo_manage_seo', 'aioseo_manage_seo' );
|
||||
if (
|
||||
! aioseo()->helpers->isAdminBarEnabled() ||
|
||||
! ( current_user_can( $canManageSeo ) || aioseo()->access->canManage() )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
aioseo()->core->assets->enqueueCss( 'src/vue/assets/scss/app/admin-bar.scss' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the footer file to let vue attach.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function adminFooter() {
|
||||
echo '<div id="aioseo-admin"></div>';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Main;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Media class.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Media {
|
||||
/**
|
||||
* Construct method.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'template_redirect', [ $this, 'attachmentRedirect' ], 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the user wants to redirect attachment pages, this is where we do it.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function attachmentRedirect() {
|
||||
if ( ! is_attachment() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
! aioseo()->dynamicOptions->searchAppearance->postTypes->has( 'attachment' )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$redirect = aioseo()->dynamicOptions->searchAppearance->postTypes->attachment->redirectAttachmentUrls;
|
||||
if ( 'disabled' === $redirect ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'attachment' === $redirect ) {
|
||||
$url = wp_get_attachment_url( get_queried_object_id() );
|
||||
if ( empty( $url ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
return wp_safe_redirect( $url, 301, AIOSEO_PLUGIN_SHORT_NAME );
|
||||
}
|
||||
|
||||
global $post;
|
||||
if ( ! empty( $post->post_parent ) ) {
|
||||
wp_safe_redirect( urldecode( get_permalink( $post->post_parent ) ), 301 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Main;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class contains pre-updates necessary for the next updates class to run.
|
||||
*
|
||||
* @since 4.1.5
|
||||
*/
|
||||
class PreUpdates {
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public function __construct() {
|
||||
// We don't want an AJAX request check here since the plugin might be installed/activated for the first time via AJAX (e.g. EDD/BLC).
|
||||
// If that's the case, the cache table needs to be created before the activation hook runs.
|
||||
if ( wp_doing_cron() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$lastActiveVersion = aioseo()->internalOptions->internal->lastActiveVersion;
|
||||
if ( aioseo()->version !== $lastActiveVersion ) {
|
||||
// Bust the table/columns cache so that we can start the update migrations with a fresh slate.
|
||||
aioseo()->internalOptions->database->installedTables = '';
|
||||
}
|
||||
|
||||
if ( version_compare( $lastActiveVersion, '4.1.5', '<' ) ) {
|
||||
$this->createCacheTable();
|
||||
}
|
||||
|
||||
if ( version_compare( $lastActiveVersion, AIOSEO_VERSION, '<' ) ) {
|
||||
aioseo()->core->cache->clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new aioseo_cache table.
|
||||
*
|
||||
* @since 4.1.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function createCacheTable() {
|
||||
$db = aioseo()->core->db->db;
|
||||
$charsetCollate = '';
|
||||
|
||||
if ( ! empty( $db->charset ) ) {
|
||||
$charsetCollate .= "DEFAULT CHARACTER SET {$db->charset}";
|
||||
}
|
||||
if ( ! empty( $db->collate ) ) {
|
||||
$charsetCollate .= " COLLATE {$db->collate}";
|
||||
}
|
||||
|
||||
$tableName = aioseo()->core->cache->getTableName();
|
||||
if ( ! aioseo()->core->db->tableExists( $tableName ) ) {
|
||||
$tableName = $db->prefix . $tableName;
|
||||
|
||||
aioseo()->core->db->execute(
|
||||
"CREATE TABLE {$tableName} (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`key` varchar(80) NOT NULL,
|
||||
`value` longtext NOT NULL,
|
||||
`expiration` datetime NULL,
|
||||
`created` datetime NOT NULL,
|
||||
`updated` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY ndx_aioseo_cache_key (`key`),
|
||||
KEY ndx_aioseo_cache_expiration (`expiration`)
|
||||
) {$charsetCollate};"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Main;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Models\CrawlCleanupLog;
|
||||
use AIOSEO\Plugin\Common\Models\CrawlCleanupBlockedArg;
|
||||
|
||||
/**
|
||||
* Query arguments class.
|
||||
*
|
||||
* @since 4.2.1
|
||||
* @version 4.5.8
|
||||
*/
|
||||
class QueryArgs {
|
||||
/**
|
||||
* Construct method.
|
||||
*
|
||||
* @since 4.2.1
|
||||
*/
|
||||
public function __construct() {
|
||||
if (
|
||||
is_admin() ||
|
||||
aioseo()->helpers->isWpLoginPage() ||
|
||||
aioseo()->helpers->isAjaxCronRestRequest() ||
|
||||
aioseo()->helpers->isDoingWpCli()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'template_redirect', [ $this, 'maybeRemoveQueryArgs' ], 1 );
|
||||
|
||||
$this->removeReplyToCom();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we can remove query args.
|
||||
*
|
||||
* @since 4.5.8
|
||||
*
|
||||
* @return boolean True if the query args can be removed.
|
||||
*/
|
||||
private function canRemoveQueryArgs() {
|
||||
if (
|
||||
! aioseo()->options->searchAppearance->advanced->blockArgs->enable ||
|
||||
is_user_logged_in() ||
|
||||
is_admin() ||
|
||||
is_robots() ||
|
||||
get_query_var( 'aiosp_sitemap_path' ) ||
|
||||
empty( $_GET ) // phpcs:ignore HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( is_singular() ) {
|
||||
global $post;
|
||||
$thePost = aioseo()->helpers->getPost( $post->ID );
|
||||
|
||||
// Leave the preview query arguments intact.
|
||||
if (
|
||||
// phpcs:disable phpcs:ignore HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended
|
||||
isset( $_GET['preview'] ) &&
|
||||
isset( $_GET['preview_nonce'] ) &&
|
||||
// phpcs:enable
|
||||
wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['preview_nonce'] ) ), 'post_preview_' . $thePost->ID ) &&
|
||||
current_user_can( 'edit_post', $thePost->ID )
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe remove query args.
|
||||
*
|
||||
* @since 4.5.8
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function maybeRemoveQueryArgs() {
|
||||
if ( ! $this->canRemoveQueryArgs() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$currentRequest = aioseo()->helpers->getRequestUrl();
|
||||
|
||||
// Remove the home path from the url for subfolder installs.
|
||||
$currentRequest = aioseo()->helpers->excludeHomePath( $currentRequest );
|
||||
$currentRequestParsed = wp_parse_url( $currentRequest );
|
||||
|
||||
// No query args? Never mind!
|
||||
if ( empty( $currentRequestParsed['query'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
parse_str( $currentRequestParsed['query'], $currentRequestQueryArgs );
|
||||
$notAllowed = [];
|
||||
$recognizedQueryLogs = [];
|
||||
|
||||
foreach ( $currentRequestQueryArgs as $key => $value ) {
|
||||
if ( ! is_string( $value ) ) {
|
||||
continue;
|
||||
}
|
||||
$this->addQueryLog( $currentRequestParsed['path'], $key, $value );
|
||||
|
||||
$blocked = CrawlCleanupBlockedArg::getByKeyValue( $key, null );
|
||||
if ( ! $blocked->exists() ) {
|
||||
$blocked = CrawlCleanupBlockedArg::getByKeyValue( $key, $value );
|
||||
}
|
||||
|
||||
if ( ! $blocked->exists() ) {
|
||||
$blocked = CrawlCleanupBlockedArg::matchRegex( $key, $value );
|
||||
}
|
||||
|
||||
if ( $blocked->exists() ) {
|
||||
$queryArg = $key . ( $value ? '=' . $value : null );
|
||||
$notAllowed[] = $queryArg;
|
||||
$blocked->addHit();
|
||||
continue;
|
||||
}
|
||||
|
||||
$recognizedQueryLogs[ $key ] = empty( $value ) ? true : $value;
|
||||
}
|
||||
|
||||
if ( ! empty( $notAllowed ) ) {
|
||||
$newUrl = home_url( $currentRequestParsed['path'] );
|
||||
|
||||
header( 'Content-Type: redirect', true );
|
||||
header_remove( 'Content-Type' );
|
||||
header_remove( 'Last-Modified' );
|
||||
header_remove( 'X-Pingback' );
|
||||
|
||||
wp_safe_redirect( add_query_arg( $recognizedQueryLogs, $newUrl ), 301, AIOSEO_PLUGIN_SHORT_NAME . ' Crawl Cleanup' );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove ?replytocom.
|
||||
*
|
||||
* @since 4.5.8
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function removeReplyToCom() {
|
||||
if ( ! apply_filters( 'aioseo_remove_reply_to_com', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( 'comment_reply_link', [ $this, 'removeReplyToComLink' ] );
|
||||
add_action( 'template_redirect', [ $this, 'replyToComRedirect' ], 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove ?replytocom.
|
||||
*
|
||||
* @since 4.7.3
|
||||
*
|
||||
* @param string $link The comment link as a string.
|
||||
* @return string The modified link.
|
||||
*/
|
||||
public function removeReplyToComLink( $link ) {
|
||||
return preg_replace( '`href=(["\'])(?:.*(?:\?|&|&)replytocom=(\d+)#respond)`', 'href=$1#comment-$2', (string) $link );
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects out the ?replytocom variables.
|
||||
*
|
||||
* @since 4.7.3
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function replyToComRedirect() {
|
||||
$replyToCom = absint( wp_unslash( $_GET['replytocom'] ?? null ) ); // phpcs:ignore HM.Security.NonceVerification.Recommended
|
||||
if ( ! empty( $replyToCom ) && is_singular() ) {
|
||||
$url = get_permalink( $GLOBALS['post']->ID );
|
||||
if ( isset( $_SERVER['QUERY_STRING'] ) ) {
|
||||
$queryString = remove_query_arg( 'replytocom', sanitize_text_field( wp_unslash( $_SERVER['QUERY_STRING'] ) ) );
|
||||
if ( ! empty( $queryString ) ) {
|
||||
$url = add_query_arg( [], $url ) . '?' . $queryString;
|
||||
}
|
||||
}
|
||||
$url = add_query_arg( [], $url ) . '#comment-' . $replyToCom;
|
||||
|
||||
wp_safe_redirect( $url, 301, AIOSEO_PLUGIN_SHORT_NAME );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add query args log.
|
||||
*
|
||||
* @since 4.5.8
|
||||
*
|
||||
* @param string $path A String of the path to create a slug.
|
||||
* @param string $key A String of key from query arg.
|
||||
* @param string $value A String of value from query arg.
|
||||
* @return void
|
||||
*/
|
||||
private function addQueryLog( $path, $key, $value = null ) {
|
||||
$slug = $path . '?' . $key . ( 0 < strlen( $value ) ? '=' . $value : '' );
|
||||
$log = CrawlCleanupLog::getBySlug( $slug );
|
||||
|
||||
$data = [
|
||||
'slug' => $slug,
|
||||
'key' => $key,
|
||||
'value' => $value
|
||||
];
|
||||
|
||||
$log->set( $data );
|
||||
$log->create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Main;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Document Title class.
|
||||
*
|
||||
* @since 4.3.9
|
||||
*/
|
||||
class Title {
|
||||
/**
|
||||
* Keeps the buffer level.
|
||||
*
|
||||
* @since 4.3.9
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $bufferLevel = 0;
|
||||
|
||||
/**
|
||||
* Starts the output buffering.
|
||||
*
|
||||
* @since 4.3.2
|
||||
* @version 4.3.9
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startOutputBuffering() {
|
||||
ob_start();
|
||||
|
||||
$this->bufferLevel = ob_get_level();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the output buffering.
|
||||
*
|
||||
* @since 4.3.2
|
||||
* @version 4.3.9
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function endOutputBuffering() {
|
||||
// Bail if our code didn't start the output buffering at all.
|
||||
if ( 0 === $this->bufferLevel ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* In case the current buffer level is different from the one we kept earlier, then: either a plugin started a new buffer or ended our buffer earlier.
|
||||
* If that's the case, we can't properly rewrite the document title anymore since we don't know what buffer content we'd parse below.
|
||||
* In order to avoid conflicts/errors (blank/broken pages), we just bail.
|
||||
* If we bail, the page won't have the title set by AIOSEO, but this can be fixed if the active theme starts supporting the "title-tag" feature {@link https://codex.wordpress.org/Title_Tag}.
|
||||
*/
|
||||
if ( ob_get_level() !== $this->bufferLevel ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->rewriteTitle( (string) ob_get_clean() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the page document title.
|
||||
*
|
||||
* @since 4.0.5
|
||||
* @version 4.3.2
|
||||
* @version 4.3.9
|
||||
*
|
||||
* @param string $content The buffer content.
|
||||
* @return string The rewritten title.
|
||||
*/
|
||||
private function rewriteTitle( $content ) {
|
||||
if ( strpos( $content, '<!-- All in One SEO' ) === false ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
// Remove all existing title tags.
|
||||
$content = preg_replace( '#<title.*?/title>#s', '', (string) $content );
|
||||
$pageTitle = aioseo()->helpers->escapeRegexReplacement( aioseo()->head->getTitle() );
|
||||
|
||||
// Return new output with our new title tag included in our own comment block.
|
||||
return preg_replace( '/(<!--\sAll\sin\sOne\sSEO[a-z0-9\s.]+\s-\saioseo\.com\s-->)/i', "$1\r\n\t\t<title>$pageTitle</title>", (string) $content, 1 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Main;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Utils;
|
||||
|
||||
/**
|
||||
* Handles plugin deinstallation.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*/
|
||||
class Uninstall {
|
||||
/**
|
||||
* Removes all data.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*
|
||||
* @param bool $force Whether we should ignore the uninstall option or not. We ignore it when we reset all data via the Debug Panel.
|
||||
* @return void
|
||||
*/
|
||||
public function dropData( $force = false ) {
|
||||
// Don't call `aioseo()->options` as it's not loaded during uninstall.
|
||||
$aioseoOptions = get_option( 'aioseo_options', '' );
|
||||
$aioseoOptions = json_decode( $aioseoOptions, true );
|
||||
|
||||
// Confirm that user has decided to remove all data, otherwise stop.
|
||||
if (
|
||||
! $force &&
|
||||
empty( $aioseoOptions['advanced']['uninstall'] )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Drop our custom tables.
|
||||
$this->uninstallDb();
|
||||
|
||||
// Delete all our custom capabilities.
|
||||
$this->uninstallCapabilities();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all our tables and options.
|
||||
*
|
||||
* @since 4.2.3
|
||||
* @version 4.8.1 Moved from Core to Uninstall.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function uninstallDb() {
|
||||
// Delete all our custom tables.
|
||||
global $wpdb;
|
||||
|
||||
// phpcs:disable WordPress.DB.DirectDatabaseQuery
|
||||
foreach ( aioseo()->core->getDbTables() as $tableName ) {
|
||||
$wpdb->query( $wpdb->prepare( 'DROP TABLE IF EXISTS %i', $tableName ) );
|
||||
}
|
||||
|
||||
// Delete all AIOSEO Locations and Location Categories.
|
||||
$wpdb->delete( $wpdb->posts, [ 'post_type' => 'aioseo-location' ], [ '%s' ] );
|
||||
$wpdb->delete( $wpdb->term_taxonomy, [ 'taxonomy' => 'aioseo-location-category' ], [ '%s' ] );
|
||||
|
||||
// Delete all the plugin settings.
|
||||
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", 'aioseo\_%' ) );
|
||||
|
||||
// Remove any transients we've left behind.
|
||||
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", '\_aioseo\_%' ) );
|
||||
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", 'aioseo\_%' ) );
|
||||
|
||||
// Delete all entries from the action scheduler table.
|
||||
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}actionscheduler_actions WHERE hook LIKE %s", 'aioseo\_%' ) );
|
||||
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}actionscheduler_groups WHERE slug = %s", 'aioseo' ) );
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all our custom capabilities.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function uninstallCapabilities() {
|
||||
$access = new Utils\Access();
|
||||
$customCapabilities = $access->getCapabilityList() ?? [];
|
||||
$roles = aioseo()->helpers->getUserRoles();
|
||||
|
||||
// Loop through roles and remove custom capabilities.
|
||||
foreach ( $roles as $roleName => $roleInfo ) {
|
||||
$role = get_role( $roleName );
|
||||
|
||||
if ( $role ) {
|
||||
$role->remove_cap( 'aioseo_admin' );
|
||||
$role->remove_cap( 'aioseo_manage_seo' );
|
||||
|
||||
foreach ( $customCapabilities as $capability ) {
|
||||
$role->remove_cap( $capability );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remove_role( 'aioseo_manager' );
|
||||
remove_role( 'aioseo_editor' );
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Meta;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds support for Google AMP.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Amp {
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'init', [ $this, 'runAmp' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the AMP hooks.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function runAmp() {
|
||||
if ( is_admin() || wp_doing_ajax() || wp_doing_cron() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add social meta to AMP plugin.
|
||||
$enableAmp = apply_filters( 'aioseo_enable_amp_social_meta', true );
|
||||
|
||||
if ( $enableAmp ) {
|
||||
$useSchema = apply_filters( 'aioseo_amp_schema', true );
|
||||
|
||||
if ( $useSchema ) {
|
||||
add_action( 'amp_post_template_head', [ $this, 'removeHooksAmpSchema' ], 9 );
|
||||
}
|
||||
|
||||
add_action( 'amp_post_template_head', [ aioseo()->head, 'output' ], 11 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Hooks with AMP's Schema.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeHooksAmpSchema() {
|
||||
// Remove AMP Schema hook used for outputting data.
|
||||
remove_action( 'amp_post_template_head', 'amp_print_schemaorg_metadata' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Meta;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use AIOSEO\Plugin\Common\Integrations\BuddyPress as BuddyPressIntegration;
|
||||
|
||||
/**
|
||||
* Handles the (Open Graph) description.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Description {
|
||||
/**
|
||||
* Helpers class instance.
|
||||
*
|
||||
* @since 4.2.7
|
||||
*
|
||||
* @var Helpers
|
||||
*/
|
||||
public $helpers = null;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->helpers = new Helpers( 'description' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the homepage description.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return string The homepage description.
|
||||
*/
|
||||
public function getHomePageDescription() {
|
||||
if ( 'page' === get_option( 'show_on_front' ) ) {
|
||||
$description = $this->getPostDescription( (int) get_option( 'page_on_front' ) );
|
||||
|
||||
return $description ? $description : aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'description' ) );
|
||||
}
|
||||
|
||||
$description = aioseo()->options->searchAppearance->global->metaDescription;
|
||||
if ( aioseo()->helpers->isWpmlActive() ) {
|
||||
// Allow WPML to translate the title if the homepage is not static.
|
||||
$description = apply_filters( 'wpml_translate_single_string', $description, 'admin_texts_aioseo_options_localized', '[aioseo_options_localized]searchAppearance_global_metaDescription' );
|
||||
}
|
||||
|
||||
$description = $this->helpers->prepare( $description );
|
||||
|
||||
return $description ? $description : aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'description' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description for the current page.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_Post $post The post object (optional).
|
||||
* @param boolean $default Whether we want the default value, not the post one.
|
||||
* @return string The page description.
|
||||
*/
|
||||
public function getDescription( $post = null, $default = false ) {
|
||||
if ( BuddyPressIntegration::isComponentPage() ) {
|
||||
return aioseo()->standalone->buddyPress->component->getMeta( 'description' );
|
||||
}
|
||||
|
||||
if ( is_home() ) {
|
||||
return $this->getHomePageDescription();
|
||||
}
|
||||
|
||||
if ( $post || is_singular() || aioseo()->helpers->isStaticPage() ) {
|
||||
$description = $this->getPostDescription( $post, $default );
|
||||
if ( $description ) {
|
||||
return $description;
|
||||
}
|
||||
|
||||
if ( is_attachment() ) {
|
||||
$post = empty( $post ) ? aioseo()->helpers->getPost() : $post;
|
||||
$caption = wp_get_attachment_caption( $post->ID );
|
||||
|
||||
return $caption ? $this->helpers->prepare( $caption ) : $this->helpers->prepare( $post->post_content );
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_category() || is_tag() || is_tax() ) {
|
||||
$term = $post ? $post : aioseo()->helpers->getTerm();
|
||||
|
||||
return $this->getTermDescription( $term, $default );
|
||||
}
|
||||
|
||||
if ( is_author() ) {
|
||||
$description = $this->helpers->prepare( aioseo()->options->searchAppearance->archives->author->metaDescription );
|
||||
if ( $description ) {
|
||||
return $description;
|
||||
}
|
||||
|
||||
$author = get_queried_object();
|
||||
|
||||
return $author ? $this->helpers->prepare( get_the_author_meta( 'description', $author->ID ) ) : '';
|
||||
}
|
||||
|
||||
if ( is_date() ) {
|
||||
return $this->helpers->prepare( aioseo()->options->searchAppearance->archives->date->metaDescription );
|
||||
}
|
||||
|
||||
if ( is_search() ) {
|
||||
return $this->helpers->prepare( aioseo()->options->searchAppearance->archives->search->metaDescription );
|
||||
}
|
||||
|
||||
if ( is_post_type_archive() ) {
|
||||
$postType = get_queried_object();
|
||||
if ( is_a( $postType, 'WP_Post_Type' ) ) {
|
||||
return $this->helpers->prepare( $this->getArchiveDescription( $postType->name ) );
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description for a given post.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param \WP_Post|int $post The post object or ID.
|
||||
* @param boolean $default Whether we want the default value, not the post one.
|
||||
* @return string The post description.
|
||||
*/
|
||||
public function getPostDescription( $post, $default = false ) {
|
||||
$post = $post && is_object( $post ) ? $post : aioseo()->helpers->getPost( $post );
|
||||
if ( ! is_a( $post, 'WP_Post' ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
static $posts = [];
|
||||
if ( isset( $posts[ $post->ID ] ) ) {
|
||||
return $posts[ $post->ID ];
|
||||
}
|
||||
|
||||
$description = '';
|
||||
$metaData = aioseo()->meta->metaData->getMetaData( $post );
|
||||
if ( ! empty( $metaData->description ) && ! $default ) {
|
||||
$description = $this->helpers->prepare( $metaData->description, $post->ID, false );
|
||||
}
|
||||
|
||||
if (
|
||||
$description ||
|
||||
(
|
||||
in_array( 'autogenerateDescriptions', aioseo()->internalOptions->deprecatedOptions, true ) &&
|
||||
! aioseo()->options->deprecated->searchAppearance->advanced->autogenerateDescriptions
|
||||
)
|
||||
) {
|
||||
$posts[ $post->ID ] = $description;
|
||||
|
||||
return $description;
|
||||
}
|
||||
|
||||
$description = $this->helpers->sanitize( $this->getPostTypeDescription( $post->post_type ), $post->ID, $default );
|
||||
|
||||
$generateDescriptions = apply_filters( 'aioseo_generate_descriptions_from_content', true, [ $post ] );
|
||||
if ( ! $description && ! post_password_required( $post ) ) {
|
||||
$description = $post->post_excerpt;
|
||||
if (
|
||||
$generateDescriptions &&
|
||||
in_array( 'useContentForAutogeneratedDescriptions', aioseo()->internalOptions->deprecatedOptions, true ) &&
|
||||
aioseo()->options->deprecated->searchAppearance->advanced->useContentForAutogeneratedDescriptions
|
||||
) {
|
||||
$description = aioseo()->helpers->getDescriptionFromContent( $post );
|
||||
}
|
||||
|
||||
$description = $this->helpers->sanitize( $description, $post->ID, $default );
|
||||
if ( ! $description && $generateDescriptions && $post->post_content ) {
|
||||
$description = $this->helpers->sanitize( aioseo()->helpers->getDescriptionFromContent( $post ), $post->ID, $default );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! is_paged() ) {
|
||||
if ( in_array( 'descriptionFormat', aioseo()->internalOptions->deprecatedOptions, true ) ) {
|
||||
$descriptionFormat = aioseo()->options->deprecated->searchAppearance->global->descriptionFormat;
|
||||
if ( $descriptionFormat ) {
|
||||
$description = preg_replace( '/#description/', $description, (string) $descriptionFormat );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$posts[ $post->ID ] = $description ? $this->helpers->prepare( $description, $post->ID, $default ) : $this->helpers->prepare( term_description( '' ), $post->ID, $default );
|
||||
|
||||
return $posts[ $post->ID ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the default description for the archive template.
|
||||
*
|
||||
* @since 4.7.6
|
||||
*
|
||||
* @param string $postType The custom post type.
|
||||
* @return string The description.
|
||||
*/
|
||||
public function getArchiveDescription( $postType ) {
|
||||
static $archiveDescription = [];
|
||||
if ( isset( $archiveDescription[ $postType ] ) ) {
|
||||
return $archiveDescription[ $postType ];
|
||||
}
|
||||
|
||||
$archiveDescription[ $postType ] = '';
|
||||
|
||||
$dynamicOptions = aioseo()->dynamicOptions->noConflict();
|
||||
if ( $dynamicOptions->searchAppearance->archives->has( $postType ) ) {
|
||||
$archiveDescription[ $postType ] = aioseo()->dynamicOptions->searchAppearance->archives->{$postType}->metaDescription;
|
||||
}
|
||||
|
||||
return $archiveDescription[ $postType ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the default description for the post type.
|
||||
*
|
||||
* @since 4.0.6
|
||||
*
|
||||
* @param string $postType The post type.
|
||||
* @return string The description.
|
||||
*/
|
||||
public function getPostTypeDescription( $postType ) {
|
||||
static $postTypeDescription = [];
|
||||
if ( isset( $postTypeDescription[ $postType ] ) ) {
|
||||
return $postTypeDescription[ $postType ];
|
||||
}
|
||||
|
||||
if ( aioseo()->dynamicOptions->searchAppearance->postTypes->has( $postType ) ) {
|
||||
$description = aioseo()->dynamicOptions->searchAppearance->postTypes->{$postType}->metaDescription;
|
||||
}
|
||||
|
||||
$postTypeDescription[ $postType ] = empty( $description ) ? '' : $description;
|
||||
|
||||
return $postTypeDescription[ $postType ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the term description.
|
||||
*
|
||||
* @since 4.0.6
|
||||
*
|
||||
* @param \WP_Term $term The term object.
|
||||
* @param boolean $default Whether we want the default value, not the post one.
|
||||
* @return string The term description.
|
||||
*/
|
||||
public function getTermDescription( $term, $default = false ) {
|
||||
if ( ! is_a( $term, 'WP_Term' ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
static $terms = [];
|
||||
if ( isset( $terms[ $term->term_id ] ) ) {
|
||||
return $terms[ $term->term_id ];
|
||||
}
|
||||
|
||||
$description = '';
|
||||
if (
|
||||
in_array( 'autogenerateDescriptions', aioseo()->internalOptions->deprecatedOptions, true ) &&
|
||||
! aioseo()->options->deprecated->searchAppearance->advanced->autogenerateDescriptions
|
||||
) {
|
||||
$terms[ $term->term_id ] = $description;
|
||||
|
||||
return $description;
|
||||
}
|
||||
|
||||
$dynamicOptions = aioseo()->dynamicOptions->noConflict();
|
||||
if ( ! $description && $dynamicOptions->searchAppearance->taxonomies->has( $term->taxonomy ) ) {
|
||||
$description = $this->helpers->prepare( aioseo()->dynamicOptions->searchAppearance->taxonomies->{$term->taxonomy}->metaDescription, false, $default );
|
||||
}
|
||||
|
||||
$terms[ $term->term_id ] = $description ? $description : $this->helpers->prepare( term_description( $term->term_id ), false, $default );
|
||||
|
||||
return $terms[ $term->term_id ];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Meta;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains helper methods for the title/description classes.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*/
|
||||
class Helpers {
|
||||
use Traits\Helpers\BuddyPress;
|
||||
|
||||
/**
|
||||
* The name of the class where this instance is constructed.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*
|
||||
* @param string $name The name of the class. Either "title" or "description".
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* Supported filters we can run after preparing the value.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $supportedFilters = [
|
||||
'title' => 'aioseo_title',
|
||||
'description' => 'aioseo_description'
|
||||
];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*
|
||||
* @param string $name The name of the class where this instance is constructed.
|
||||
*/
|
||||
public function __construct( $name ) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the title/description.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*
|
||||
* @param string $value The value.
|
||||
* @param int|bool $objectId The post/term ID.
|
||||
* @param bool $replaceTags Whether the smart tags should be replaced.
|
||||
* @return string The sanitized value.
|
||||
*/
|
||||
public function sanitize( $value, $objectId = false, $replaceTags = false ) {
|
||||
$value = $replaceTags ? $value : aioseo()->tags->replaceTags( $value, $objectId );
|
||||
$value = aioseo()->helpers->doShortcodes( $value );
|
||||
|
||||
$value = aioseo()->helpers->decodeHtmlEntities( $value );
|
||||
$value = $this->encodeExceptions( $value );
|
||||
$value = wp_strip_all_tags( strip_shortcodes( $value ) );
|
||||
// Because we encoded the exceptions, we need to decode them again first to prevent double encoding later down the line.
|
||||
$value = aioseo()->helpers->decodeHtmlEntities( $value );
|
||||
|
||||
// Trim internal and external whitespace.
|
||||
$value = preg_replace( '/[\s]+/u', ' ', (string) trim( $value ) );
|
||||
|
||||
return aioseo()->helpers->internationalize( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the title/description before returning it.
|
||||
*
|
||||
* @since 4.1.2
|
||||
*
|
||||
* @param string $value The value.
|
||||
* @param int|bool $objectId The post/term ID.
|
||||
* @param bool $replaceTags Whether the smart tags should be replaced.
|
||||
* @return string The sanitized value.
|
||||
*/
|
||||
public function prepare( $value, $objectId = false, $replaceTags = false ) {
|
||||
if (
|
||||
! empty( $value ) &&
|
||||
! is_admin() &&
|
||||
1 < aioseo()->helpers->getPageNumber()
|
||||
) {
|
||||
$value .= ' ' . trim( aioseo()->options->searchAppearance->advanced->pagedFormat );
|
||||
}
|
||||
|
||||
$value = $replaceTags ? $value : aioseo()->tags->replaceTags( $value, $objectId );
|
||||
$value = apply_filters( $this->supportedFilters[ $this->name ], $value );
|
||||
|
||||
return $this->sanitize( $value, $objectId, $replaceTags );
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a number of exceptions before we strip tags.
|
||||
* We need this function to allow certain character (combinations) in the title/description.
|
||||
*
|
||||
* @since 4.1.1
|
||||
*
|
||||
* @param string $string The string.
|
||||
* @return string $string The string with exceptions encoded.
|
||||
*/
|
||||
public function encodeExceptions( $string ) {
|
||||
$exceptions = [ '<3' ];
|
||||
foreach ( $exceptions as $exception ) {
|
||||
$string = preg_replace( "/$exception/", aioseo()->helpers->encodeOutputHtml( $exception ), (string) $string );
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
namespace AIOSEO\Plugin\Common\Meta;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* To check whether SEO is enabled for the queried object.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Included {
|
||||
/**
|
||||
* Checks whether the queried object is included.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isIncluded() {
|
||||
if ( is_admin() || is_feed() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( apply_filters( 'aioseo_disable', false ) || $this->isExcludedGlobal() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $this->isQueriedObjectPublic() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the queried object is public.
|
||||
*
|
||||
* @since 4.2.2
|
||||
*
|
||||
* @return bool Whether the queried object is public.
|
||||
*/
|
||||
protected function isQueriedObjectPublic() {
|
||||
$queriedObject = get_queried_object(); // Don't use the getTerm helper here.
|
||||
|
||||
if ( is_a( $queriedObject, 'WP_Post' ) ) {
|
||||
return aioseo()->helpers->isPostTypePublic( $queriedObject->post_type );
|
||||
}
|
||||
|
||||
// Check if the current page is a post type archive page.
|
||||
if ( is_a( $queriedObject, 'WP_Post_Type' ) ) {
|
||||
return aioseo()->helpers->isPostTypePublic( $queriedObject->name );
|
||||
}
|
||||
|
||||
if ( is_a( $queriedObject, 'WP_Term' ) ) {
|
||||
if ( aioseo()->helpers->isWooCommerceProductAttribute( $queriedObject->taxonomy ) ) {
|
||||
// Check if the attribute has archives enabled.
|
||||
$taxonomy = get_taxonomy( $queriedObject->taxonomy );
|
||||
|
||||
return $taxonomy->public;
|
||||
}
|
||||
|
||||
return aioseo()->helpers->isTaxonomyPublic( $queriedObject->taxonomy );
|
||||
}
|
||||
|
||||
// Return true in all other cases (e.g. search page, date archive, etc.).
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the queried object has been excluded globally.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isExcludedGlobal() {
|
||||
if ( is_category() || is_tag() || is_tax() ) {
|
||||
return $this->isTaxExcludedGlobal();
|
||||
}
|
||||
|
||||
if ( ! in_array( 'excludePosts', aioseo()->internalOptions->deprecatedOptions, true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$excludedPosts = aioseo()->options->deprecated->searchAppearance->advanced->excludePosts;
|
||||
|
||||
if ( empty( $excludedPosts ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
foreach ( $excludedPosts as $object ) {
|
||||
$object = json_decode( $object );
|
||||
if ( is_int( $object->value ) ) {
|
||||
$ids[] = (int) $object->value;
|
||||
}
|
||||
}
|
||||
|
||||
$post = aioseo()->helpers->getPost();
|
||||
if ( empty( $post ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( in_array( (int) $post->ID, $ids, true ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the queried object has been excluded globally.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isTaxExcludedGlobal() {
|
||||
if ( ! in_array( 'excludeTerms', aioseo()->internalOptions->deprecatedOptions, true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$excludedTerms = aioseo()->options->deprecated->searchAppearance->advanced->excludeTerms;
|
||||
|
||||
if ( empty( $excludedTerms ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
foreach ( $excludedTerms as $object ) {
|
||||
$object = json_decode( $object );
|
||||
if ( is_int( $object->value ) ) {
|
||||
$ids[] = (int) $object->value;
|
||||
}
|
||||
}
|
||||
|
||||
$term = aioseo()->helpers->getTerm();
|
||||
if ( in_array( (int) $term->term_id, $ids, true ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user