| Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/ |
| Current File : /home/x/b/o/xbodynamge/namtation/wp-content/Traits.tar |
Image.php 0000666 00000005462 15112711340 0006305 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Schema\Graphs\Traits;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Trait that handles images for the graphs.
*
* @since 4.2.5
*/
trait Image {
/**
* Builds the graph data for a given image with a given schema ID.
*
* @since 4.0.0
*
* @param int $imageId The image ID.
* @param string $graphId The graph ID (optional).
* @return array $data The image graph data.
*/
protected function image( $imageId, $graphId = '' ) {
$attachmentId = is_string( $imageId ) && ! is_numeric( $imageId ) ? aioseo()->helpers->attachmentUrlToPostId( $imageId ) : $imageId;
$imageUrl = wp_get_attachment_image_url( $attachmentId, 'full' );
$data = [
'@type' => 'ImageObject',
'url' => $imageUrl ? $imageUrl : $imageId,
];
if ( $graphId ) {
$baseUrl = aioseo()->schema->context['url'] ?? aioseo()->helpers->getUrl();
$data['@id'] = trailingslashit( $baseUrl ) . '#' . $graphId;
}
if ( ! $attachmentId ) {
return $data;
}
$metaData = wp_get_attachment_metadata( $attachmentId );
if ( $metaData && ! empty( $metaData['width'] ) && ! empty( $metaData['height'] ) ) {
$data['width'] = (int) $metaData['width'];
$data['height'] = (int) $metaData['height'];
}
$caption = $this->getImageCaption( $attachmentId );
if ( ! empty( $caption ) ) {
$data['caption'] = $caption;
}
return $data;
}
/**
* Get the image caption.
*
* @since 4.1.4
*
* @param int $attachmentId The attachment ID.
* @return string The caption.
*/
private function getImageCaption( $attachmentId ) {
$caption = wp_get_attachment_caption( $attachmentId );
if ( ! empty( $caption ) ) {
return $caption;
}
return get_post_meta( $attachmentId, '_wp_attachment_image_alt', true );
}
/**
* Returns the graph data for the avatar of a given user.
*
* @since 4.0.0
*
* @param int $userId The user ID.
* @param string $graphId The graph ID.
* @return array The graph data.
*/
protected function avatar( $userId, $graphId ) {
if ( ! get_option( 'show_avatars' ) ) {
return [];
}
$avatar = get_avatar_data( $userId );
if ( ! $avatar['found_avatar'] ) {
return [];
}
return array_filter( [
'@type' => 'ImageObject',
'@id' => aioseo()->schema->context['url'] . "#$graphId",
'url' => $avatar['url'],
'width' => $avatar['width'],
'height' => $avatar['height'],
'caption' => get_the_author_meta( 'display_name', $userId )
] );
}
/**
* Returns the graph data for the post's featured image.
*
* @since 4.2.5
*
* @return string The featured image URL.
*/
protected function getFeaturedImage() {
$post = aioseo()->helpers->getPost();
return has_post_thumbnail( $post ) ? $this->image( get_post_thumbnail_id() ) : '';
}
} Options.php 0000666 00000005230 15113402514 0006710 0 ustar 00 <?php
namespace AIOSEO\Plugin\Lite\Traits;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Options trait.
*
* @since 4.0.0
*/
trait Options {
/**
* Initialize the options.
*
* @since 4.1.4
*
* @return void
*/
public function init() {
parent::init();
$dbOptions = $this->getDbOptions( $this->optionsName . '_lite' );
// Refactor options.
$this->defaultsMerged = array_replace_recursive( $this->defaults, $this->liteDefaults );
$mergedDefaults = array_replace_recursive(
$this->liteDefaults,
$this->addValueToValuesArray( $this->liteDefaults, $dbOptions )
);
$cachedOptions = aioseo()->core->optionsCache->getOptions( $this->optionsName );
$dbOptions = array_replace_recursive(
$cachedOptions,
$mergedDefaults
);
aioseo()->core->optionsCache->setOptions( $this->optionsName, $dbOptions );
}
/**
* Merge defaults with liteDefaults.
*
* @since 4.1.4
*
* @return array An array of dafults.
*/
public function getDefaults() {
return array_replace_recursive( parent::getDefaults(), $this->liteDefaults );
}
/**
* Updates the options in the database.
*
* @since 4.1.4
*
* @param string $optionsName An optional option name to update.
* @param string $defaults The defaults to filter the options by.
* @param array|null $options An optional options array.
* @return void
*/
public function update( $optionsName = null, $defaults = null, $options = null ) {
$optionsName = empty( $optionsName ) ? $this->optionsName . '_lite' : $optionsName;
$defaults = empty( $defaults ) ? $this->liteDefaults : $defaults;
// We're creating a new array here because it was setting it by reference.
$cachedOptions = aioseo()->core->optionsCache->getOptions( $this->optionsName );
$optionsBefore = json_decode( wp_json_encode( $cachedOptions ), true );
parent::update( $this->optionsName, $options );
parent::update( $optionsName, $defaults, $optionsBefore );
}
/**
* Updates the options in the database.
*
* @since 4.1.4
*
* @param boolean $force Whether or not to force an immediate save.
* @param string $optionsName An optional option name to update.
* @param string $defaults The defaults to filter the options by.
* @return void
*/
public function save( $force = false, $optionsName = null, $defaults = null ) {
if ( ! $this->shouldSave && ! $force ) {
return;
}
$optionsName = empty( $optionsName ) ? $this->optionsName . '_lite' : $optionsName;
$defaults = empty( $defaults ) ? $this->liteDefaults : $defaults;
parent::save( $force, $this->optionsName );
parent::save( $force, $optionsName, $defaults );
}
} Helpers/BuddyPress.php 0000666 00000001370 15113552476 0010761 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Meta\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains BuddyPress specific helper methods.
*
* @since 4.7.6
*/
trait BuddyPress {
/**
* Sanitizes the title/description.
*
* @since 4.7.6
*
* @param string $value The value.
* @param int $objectId The object ID.
* @param bool $replaceTags Whether the smart tags should be replaced.
* @return string The sanitized value.
*/
public function bpSanitize( $value, $objectId = 0, $replaceTags = false ) {
$value = $replaceTags ? $value : aioseo()->standalone->buddyPress->tags->replaceTags( $value, $objectId );
return $this->sanitize( $value, $objectId, true );
}
} NetworkOptions.php 0000666 00000003700 15114625461 0010273 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Options trait.
*
* @since 4.2.5
*/
trait NetworkOptions {
/**
* Initializes the options.
*
* @since 4.2.5
*
* @return void
*/
protected function init() {
if ( ! is_multisite() ) {
return;
}
aioseo()->helpers->switchToBlog( $this->helpers->getNetworkId() );
$dbOptions = json_decode( get_option( $this->optionsName ), true );
if ( empty( $dbOptions ) ) {
$dbOptions = [];
}
$this->defaultsMerged = aioseo()->helpers->arrayReplaceRecursive( $this->defaults, $this->defaultsMerged );
$options = aioseo()->helpers->arrayReplaceRecursive(
$this->defaultsMerged,
$this->addValueToValuesArray( $this->defaultsMerged, $dbOptions )
);
aioseo()->core->optionsCache->setOptions( $this->optionsName, $options );
aioseo()->helpers->restoreCurrentBlog();
}
/**
* Sanitizes, then saves the options to the database.
*
* @since 4.2.5
*
* @param array $newOptions The new options to sanitize, then save.
* @return void
*/
public function sanitizeAndSave( $newOptions ) {
if ( ! is_multisite() ) {
return;
}
if ( ! is_array( $newOptions ) ) {
return;
}
$this->init();
aioseo()->helpers->switchToBlog( $this->helpers->getNetworkId() );
$cachedOptions = aioseo()->core->optionsCache->getOptions( $this->optionsName );
$dbOptions = aioseo()->helpers->arrayReplaceRecursive(
$cachedOptions,
$this->addValueToValuesArray( $cachedOptions, $newOptions, [], true )
);
// Tools.
if ( ! empty( $newOptions['tools'] ) ) {
if ( isset( $newOptions['tools']['robots']['rules'] ) ) {
$dbOptions['tools']['robots']['rules']['value'] = $this->sanitizeField( $newOptions['tools']['robots']['rules'], 'array' );
}
}
aioseo()->core->optionsCache->setOptions( $this->optionsName, $dbOptions );
$this->save( true );
aioseo()->helpers->restoreCurrentBlog();
}
} SocialProfiles.php 0000666 00000011740 15114625461 0010207 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Trait that handles the social profiles.
*
* @since 4.2.2
*/
trait SocialProfiles {
/**
* List of base URLs.
*
* @since 4.2.2
*
* @var array
*/
private $baseUrls = [
'facebookPageUrl' => 'https://facebook.com/',
'twitterUrl' => 'https://x.com/',
'instagramUrl' => 'https://instagram.com/',
'tiktokUrl' => 'https://tiktok.com/@',
'pinterestUrl' => 'https://pinterest.com/',
'youtubeUrl' => 'https://youtube.com/',
'linkedinUrl' => 'https://linkedin.com/in/',
'tumblrUrl' => 'https://tumblr.com/',
'yelpPageUrl' => 'https://yelp.com/biz/',
'soundCloudUrl' => 'https://soundcloud.com/',
'wikipediaUrl' => 'https://en.wikipedia.org/wiki/',
'myspaceUrl' => 'https://myspace.com/',
'wordPressUrl' => 'https://profiles.wordpress.org/',
'blueskyUrl' => 'https://bsky.app/profile/',
'threadsUrl' => 'https://threads.com/@',
];
/**
* Returns the profiles of the organization, set under Social Networks.
*
* @since 4.2.2
*
* @return array List of social profiles.
*/
protected function getOrganizationProfiles() {
$socialProfiles = [
'facebookPageUrl' => aioseo()->options->social->profiles->urls->facebookPageUrl,
'twitterUrl' => aioseo()->options->social->profiles->urls->twitterUrl,
'instagramUrl' => aioseo()->options->social->profiles->urls->instagramUrl,
'tiktokUrl' => aioseo()->options->social->profiles->urls->tiktokUrl,
'pinterestUrl' => aioseo()->options->social->profiles->urls->pinterestUrl,
'youtubeUrl' => aioseo()->options->social->profiles->urls->youtubeUrl,
'linkedinUrl' => aioseo()->options->social->profiles->urls->linkedinUrl,
'tumblrUrl' => aioseo()->options->social->profiles->urls->tumblrUrl,
'yelpPageUrl' => aioseo()->options->social->profiles->urls->yelpPageUrl,
'soundCloudUrl' => aioseo()->options->social->profiles->urls->soundCloudUrl,
'wikipediaUrl' => aioseo()->options->social->profiles->urls->wikipediaUrl,
'myspaceUrl' => aioseo()->options->social->profiles->urls->myspaceUrl,
'wordPressUrl' => aioseo()->options->social->profiles->urls->wordPressUrl,
'blueskyUrl' => aioseo()->options->social->profiles->urls->blueskyUrl,
'threadsUrl' => aioseo()->options->social->profiles->urls->threadsUrl,
];
if ( aioseo()->options->social->profiles->sameUsername->enable ) {
$username = aioseo()->options->social->profiles->sameUsername->username;
$includedPlatforms = aioseo()->options->social->profiles->sameUsername->included;
foreach ( $this->baseUrls as $platformKey => $baseUrl ) {
if ( ! in_array( $platformKey, $includedPlatforms, true ) ) {
continue;
}
$socialProfiles[ $platformKey ] = $baseUrl . $username;
}
}
if ( aioseo()->options->social->profiles->additionalUrls ) {
$additionalUrls = preg_split( '/\n|\r|\r\n/', (string) aioseo()->options->social->profiles->additionalUrls );
$socialProfiles = array_merge( $socialProfiles, $additionalUrls );
}
if ( ! aioseo()->options->social->facebook->general->showAuthor ) {
unset( $socialProfiles['facebookPageUrl'] );
}
if ( ! aioseo()->options->social->twitter->general->showAuthor ) {
unset( $socialProfiles['twitterUrl'] );
}
return array_filter( $socialProfiles );
}
/**
* Returns the profiles of the given user, set under the User Profile.
*
* @since 4.2.2
*
* @param int $userId The user ID.
* @return array List of social profiles.
*/
protected function getUserProfiles( $userId ) {
$socialProfiles = $this->baseUrls;
foreach ( $socialProfiles as $platformKey => $v ) {
$metaName = 'aioseo_' . aioseo()->helpers->toSnakeCase( $platformKey );
$socialProfiles[ $platformKey ] = get_user_meta( $userId, $metaName, true );
}
$sameUsernameData = get_user_meta( $userId, 'aioseo_profiles_same_username', true );
if ( is_array( $sameUsernameData ) && (bool) $sameUsernameData['enable'] ) {
foreach ( $this->baseUrls as $platform => $baseUrl ) {
if ( ! in_array( $platform, $sameUsernameData['included'], true ) ) {
continue;
}
$socialProfiles[ $platform ] = $baseUrl . $sameUsernameData['username'];
}
}
$additionalUrls = get_user_meta( $userId, 'aioseo_profiles_additional_urls', true );
if ( $additionalUrls ) {
$additionalUrls = preg_split( '/\n|\r|\r\n/', (string) $additionalUrls );
foreach ( $additionalUrls as $additionalUrl ) {
// We need to set a random key because otherwise we'll override the ones from the organization.
$socialProfiles[ uniqid() ] = $additionalUrl;
}
}
if ( ! aioseo()->options->social->facebook->general->showAuthor ) {
unset( $socialProfiles['facebookPageUrl'] );
}
if ( ! aioseo()->options->social->twitter->general->showAuthor ) {
unset( $socialProfiles['twitterUrl'] );
}
return array_filter( $socialProfiles );
}
} Assets.php 0000666 00000035647 15114625461 0006547 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Assets trait.
*
* @since 4.1.9
*/
trait Assets {
/**
* Whether we should load dev scripts.
*
* @since 4.1.9
*
* @var boolean|null
*/
private $shouldLoadDevScripts = null;
/**
* Holds the location of the manifest file.
*
* @since 4.1.9
*
* @var string
*/
private $manifestFile;
/**
* True if we are in a dev environment. This mirrors the global isDev.
*
* @since 4.1.9
*
* @var bool
*/
private $isDev = false;
/**
* Asset handles that should load as regular JS and not as modern JS module.
*
* @since 4.1.9
*
* @var array An array of handles.
*/
private $noModuleTag = [];
/**
* Core class instance.
*
* @since 4.2.7
*
* @var \AIOSEO\Plugin\Common\Core\Core
*/
protected $core = null;
/**
* The LocalBusiness addon version.
*
* @since 4.2.7
*
* @var string
*/
protected $version = '';
/**
* The development site domain.
*
* @since 4.2.7
*
* @var string
*/
protected $domain = '';
/**
* The development server port.
*
* @since 4.2.7
*
* @var int
*/
protected $port = 0;
/**
* The asset to load.
*
* @since 4.1.9
*
* @param string $asset The asset to load.
* @param array $dependencies An array of dependencies.
* @param mixed $data Any data to be localized.
* @param string $objectName The object name to use when localizing.
* @return void
*/
public function load( $asset, $dependencies = [], $data = null, $objectName = 'aioseo' ) {
$this->jsPreloadImports( $asset );
$this->loadCss( $asset );
$this->enqueueJs( $asset, $dependencies, $data, $objectName );
}
/**
* Filter the script loader tag if this is our script.
*
* @since 4.1.9
*
* @param string $tag The tag that is going to be output.
* @param string $handle The handle for the script.
* @return string The modified tag.
*/
public function scriptLoaderTag( $tag, $handle = '', $src = '' ) {
if ( $this->skipModuleTag( $handle ) ) {
return $tag;
}
$tag = str_replace( $src, $this->normalizeAssetsHost( $src ), $tag );
// Remove the type and re-add it as module.
$tag = preg_replace( '/type=[\'"].*?[\'"]/', '', (string) $tag );
$tag = preg_replace( '/<script/', '<script type="module"', (string) $tag );
return $tag;
}
/**
* Preload JS imports.
*
* @since 4.1.9
*
* @param string $asset The asset to load imports for.
* @return void
*/
private function jsPreloadImports( $asset ) {
static $urls = []; // Prevent script from being loaded multiple times.
$res = '';
foreach ( $this->importsUrls( $asset ) as $url ) {
if ( isset( $urls[ $url ] ) ) {
continue;
}
$urls[ $url ] = true;
$res .= '<link rel="modulepreload" href="' . esc_attr( $url ) . "\">\n";
}
$allowedHtml = [
'link' => [
'rel' => [],
'href' => []
]
];
if ( ! empty( $res ) ) {
if ( ! function_exists( 'wp_enqueue_script_module' ) ) {
add_action( 'admin_head', function () use ( &$res, $allowedHtml ) {
echo wp_kses( $res, $allowedHtml );
} );
add_action( 'wp_head', function () use ( &$res, $allowedHtml ) {
echo wp_kses( $res, $allowedHtml );
} );
} else {
add_action( 'admin_print_footer_scripts', function () use ( &$res, $allowedHtml ) {
echo wp_kses( $res, $allowedHtml );
}, 1000 );
}
}
}
/**
* Loads CSS for an asset from the manifest file.
*
* @since 4.1.9
*
* @param string $asset The script to load CSS for.
* @return void
*/
public function loadCss( $asset ) {
if ( $this->shouldLoadDev() ) {
return;
}
foreach ( $this->getCssUrls( $asset ) as $file => $url ) {
wp_enqueue_style( $this->cssHandle( $file ), $url, [], $this->version );
}
}
/**
* Register a CSS asset.
*
* @since 4.1.9
*
* @param string $asset The script to load CSS for.
* @param array $dependencies An array of dependencies.
* @return void
*/
public function registerCss( $asset, $dependencies = [] ) {
$handle = $this->cssHandle( $asset );
if ( wp_style_is( $handle, 'registered' ) ) {
return;
}
$url = $this->shouldLoadDev()
? $this->getDevUrl() . ltrim( $asset, '/' )
: $this->assetUrl( $asset );
if ( ! $url ) {
return;
}
wp_register_style( $handle, $url, $dependencies, $this->version );
}
/**
* Enqueue css.
*
* @since 4.1.9
*
* @param string $asset The css to load.
* @param array $dependencies An array of dependencies.
* @return void
*/
public function enqueueCss( $asset, $dependencies = [] ) {
$this->registerCss( $asset, $dependencies );
$handle = $this->cssHandle( $asset );
if ( wp_style_is( $handle, 'enqueued' ) ) {
return;
}
wp_enqueue_style( $handle );
}
/**
* Register the JS to enqueue.
*
* @since 4.1.9
*
* @param string $asset The script to load.
* @param array $dependencies An array of dependencies.
* @param mixed $data Any data to be localized.
* @param string $objectName The object name to use when localizing.
* @return void
*/
public function registerJs( $asset, $dependencies = [], $data = null, $objectName = 'aioseo' ) {
$handle = $this->jsHandle( $asset );
if ( wp_script_is( $handle, 'registered' ) ) {
// If it's already registered let's add the data.
if ( ! empty( $data ) ) {
wp_localize_script(
$handle,
$objectName,
$data
);
}
return;
}
$url = $this->shouldLoadDev()
? $this->getDevUrl() . ltrim( $asset, '/' )
: $this->jsUrl( $asset );
if ( ! $url ) {
return;
}
wp_register_script( $handle, $url, $dependencies, $this->version, true );
if ( empty( $data ) ) {
return;
}
wp_localize_script(
$handle,
$objectName,
$data
);
}
/**
* Register the JS to enqueue.
*
* @since 4.1.9
*
* @param string $asset The script to load.
* @param array $dependencies An array of dependencies.
* @param mixed $data Any data to be localized.
* @param string $objectName The object name to use when localizing.
* @return void
*/
public function enqueueJs( $asset, $dependencies = [], $data = null, $objectName = 'aioseo' ) {
$this->registerJs( $asset, $dependencies, $data, $objectName );
$handle = $this->jsHandle( $asset );
if ( wp_script_is( $handle, 'enqueued' ) ) {
return;
}
wp_enqueue_script( $handle );
}
/**
* Return the dev URL.
*
* @since 4.1.9
*
* @return string The dev URL.
*/
private function getDevUrl() {
$protocol = is_ssl() ? 'https://' : 'http://';
return $protocol . $this->domain . ':' . $this->port . '/';
}
/**
* Get the asset URL.
*
* @since 4.1.9
*
* @param string $asset The asset to find the URL for.
* @return string The URL for the asset.
*/
private function assetUrl( $asset ) {
$assetManifest = $this->getAssetManifestItem( $asset );
return ! empty( $assetManifest['file'] )
? $this->basePath() . $assetManifest['file']
: $this->basePath() . ltrim( $asset, '/' );
}
/**
* Get the JS URL.
*
* @since 4.1.9
*
* @param string $asset The asset to find the URL for.
* @return string The URL for the asset.
*/
public function jsUrl( $asset ) {
$manifestAsset = $this->getManifestItem( $asset );
return ! empty( $manifestAsset['file'] )
? $this->basePath() . $manifestAsset['file']
: $this->basePath() . ltrim( $asset, '/' );
}
/**
* Get an item from the manifest.
*
* @since 4.1.9
*
* @param string $asset The asset to find.
* @return string Manifest object.
*/
private function getManifestItem( $asset ) {
$manifest = $this->getManifest();
$asset = ltrim( $asset, '/' );
return isset( $manifest[ $asset ] ) ? $manifest[ $asset ] : null;
}
/**
* Get the CSS asset handle.
*
* @since 4.1.9
*
* @param string $asset The asset to find the handle for.
* @return string The asset handle.
*/
public function cssHandle( $asset ) {
return "{$this->scriptHandle}/css/$asset";
}
/**
* Get the JS asset handle.
*
* @since 4.1.9
*
* @param string $asset The asset to find the handle for.
* @return string The asset handle.
*/
public function jsHandle( $asset = '' ) {
return "{$this->scriptHandle}/js/$asset";
}
/**
* Get the manifest to load assets from.
*
* @since 4.1.9
*
* @return array An array of files.
*/
private function getManifest() {
static $file = null;
if ( $file ) {
return $file;
}
$manifestJson = ''; // This is set in the view.
if ( file_exists( $this->manifestFile ) ) {
require_once $this->manifestFile;
}
$file = json_decode( $manifestJson, true );
return $file;
}
/**
* Get an item from the asset manifest.
*
* @since 4.1.9
*
* @param string $item An item to retrieve.
* @return string|null The asset item.
*/
private function getAssetManifestItem( $item ) {
$assetManifest = $this->getManifest();
return ! empty( $assetManifest[ $item ] ) ? $assetManifest[ $item ] : null;
}
/**
* Get an asset's array of URLs to import.
*
* @since 4.1.9
*
* @param string $asset The asset to find imports for.
* @return array An array of imports.
*/
private function importsUrls( $asset ) {
$urls = [];
$manifestAsset = $this->getManifestItem( $asset );
if ( ! empty( $manifestAsset['imports'] ) ) {
foreach ( $manifestAsset['imports'] as $import ) {
$importAsset = $this->getManifestItem( $import );
if ( ! empty( $importAsset['file'] ) ) {
$urls[] = $this->getPublicUrlBase() . $importAsset['file'];
// Load the import's CSS if any.
$this->loadCss( $import );
}
}
}
return $urls;
}
/**
* Returns an asset's CSS urls.
*
* @since 4.1.9
*
* @param string $asset The asset to find CSS URLs for.
* @return array An array of CSS URLs to load.
*/
private function getCssUrls( $asset ) {
$urls = [];
$manifestAsset = $this->getManifestItem( $asset );
if ( ! empty( $manifestAsset['css'] ) ) {
foreach ( $manifestAsset['css'] as $file ) {
$urls[ $file ] = $this->getPublicUrlBase() . $file;
}
}
return $urls;
}
/**
* Check if we should load the dev watcher scripts.
*
* @since 4.1.9
*
* @return boolean True if we should load the dev watcher scripts.
*/
private function shouldLoadDev() {
if ( null !== $this->shouldLoadDevScripts ) {
return $this->shouldLoadDevScripts;
}
if (
! $this->isDev ||
(
defined( 'AIOSEO_LOAD_DEV_SCRIPTS' ) &&
false === AIOSEO_LOAD_DEV_SCRIPTS
)
) {
$this->shouldLoadDevScripts = false;
return $this->shouldLoadDevScripts;
}
if ( ! $this->domain && ! $this->port ) {
$this->shouldLoadDevScripts = false;
return $this->shouldLoadDevScripts;
}
set_error_handler( function() {} ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
$connection = fsockopen( $this->domain, $this->port ); // phpcs:ignore WordPress.WP.AlternativeFunctions
restore_error_handler();
if ( ! $connection ) {
$this->shouldLoadDevScripts = false;
return $this->shouldLoadDevScripts;
}
$this->shouldLoadDevScripts = true;
return $this->shouldLoadDevScripts;
}
/**
* Get the path for the assets.
*
* @since 4.1.9
*
* @param bool $maybeDev Whether to try and load dev scripts.
* @return string The path for the assets.
*/
public function getAssetsPath( $maybeDev = true ) {
return $maybeDev && $this->shouldLoadDev()
? $this->getDevUrl()
: $this->basePath();
}
/**
* Finds out if a handle should be loaded as regular JS and not as modern JS module.
*
* @since 4.1.9
*
* @param string $handle The script handle.
* @return bool Should the module tag be skipped.
*/
public function skipModuleTag( $handle ) {
if ( ! aioseo()->helpers->stringContains( $handle, $this->jsHandle( '' ) ) ) {
return true;
}
foreach ( $this->noModuleTag as $tag ) {
if ( aioseo()->helpers->stringContains( $handle, $tag ) ) {
return true;
}
}
return false;
}
/**
* Normalize the assets host. Some sites manually set the WP_PLUGINS_URL
* and if that domain has www. and the site_url does not, then it will fail to load
* our assets. This doesn't fix the issue 100% because it will still fail on
* sub-domains that don't have the proper CORS headers. Those sites will need
* manual fixes.
*
* @since 4.1.10
*
* @param string $path The path to normalize.
* @return string The normalized path.
*/
public function normalizeAssetsHost( $path ) {
static $paths = [];
if ( isset( $paths[ $path ] ) ) {
return apply_filters( 'aioseo_normalize_assets_host', $paths[ $path ] );
}
// We need to verify the domain on the $path attribute matches
// what's in site_url() for our assets or they won't load.
$siteUrl = site_url();
$siteUrlEscaped = aioseo()->helpers->escapeRegex( $siteUrl );
if ( preg_match( "/^$siteUrlEscaped/i", (string) $path ) ) {
$paths[ $path ] = $path;
return apply_filters( 'aioseo_normalize_assets_host', $paths[ $path ] );
}
// We now know that the path doesn't contain the site_url().
$newPath = $path;
$siteUrlParsed = wp_parse_url( $siteUrl );
$host = aioseo()->helpers->escapeRegex( str_replace( 'www.', '', $siteUrlParsed['host'] ) );
$scheme = aioseo()->helpers->escapeRegex( $siteUrlParsed['scheme'] );
$siteUrlHasWww = preg_match( "/^{$scheme}:\/\/www\.$host/", (string) $siteUrl );
$pathHasWww = preg_match( "/^{$scheme}:\/\/www\.$host/", (string) $path );
// Check if the path contains www.
if ( $pathHasWww && ! $siteUrlHasWww ) {
// If the path contains www., we want to strip it out.
$newPath = preg_replace( "/^({$scheme}:\/\/)(www\.)($host)/", '$1$3', (string) $path );
}
// Check if the site_url contains www.
if ( $siteUrlHasWww && ! $pathHasWww ) {
// If the site_url contains www., we want to add it in to the path.
$newPath = preg_replace( "/^({$scheme}:\/\/)($host)/", '$1www.$2', (string) $path );
}
$paths[ $path ] = $newPath;
return apply_filters( 'aioseo_normalize_assets_host', $paths[ $path ] );
}
/**
* Get all the CSS files which a JS asset depends on.
* This won't work properly unless you've run `npm run build` first.
*
* @since 4.3.1
*
* @param string $asset The asset to find the CSS dependencies for.
* @return array All the asset's CSS dependencies if any.
*/
public function getJsAssetCssQueue( $asset ) {
$queue = [];
foreach ( $this->getCssUrls( $asset ) as $file => $url ) {
$queue[] = [
'handle' => $this->cssHandle( $file ),
'url' => $url
];
}
$manifestAsset = $this->getManifestItem( $asset );
if ( ! empty( $manifestAsset['imports'] ) ) {
foreach ( $manifestAsset['imports'] as $subAsset ) {
foreach ( $this->getCssUrls( $subAsset ) as $file => $url ) {
$queue[] = [
'handle' => $this->cssHandle( $file ),
'url' => $url
];
}
}
}
return $queue;
}
} Helpers/ThirdParty.php 0000666 00000055042 15114625461 0010770 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains all third-party related helper methods.
*
* @since 4.1.4
*/
trait ThirdParty {
/**
* Checks whether WooCommerce is active.
*
* @since 4.0.0
*
* @return bool Whether WooCommerce is active.
*/
public function isWooCommerceActive() {
return class_exists( 'WooCommerce' );
}
/**
* Checks if the current page is a special WooCommerce page (Cart, Checkout, ...).
*
* @since 4.0.0
*
* @param int $postId The post ID.
* @return string The type of page or an empty string if it isn't a WooCommerce page.
*/
public function isWooCommercePage( $postId = 0 ) {
$postId = $postId ? (int) $postId : get_the_ID();
$specialWooCommercePages = $this->getWooCommercePages();
if ( in_array( $postId, $specialWooCommercePages, true ) ) {
return array_search( $postId, $specialWooCommercePages, true );
}
return '';
}
/**
* Returns the WooCommerce pages.
*
* @since 4.7.3
*
* @return array An associative list of special WooCommerce pages.
*/
public function getWooCommercePages() {
if ( ! $this->isWooCommerceActive() ) {
$wooCommercePages = [];
return $wooCommercePages;
}
$wooCommercePages = [
'cart' => (int) get_option( 'woocommerce_cart_page_id' ),
'checkout' => (int) get_option( 'woocommerce_checkout_page_id' ),
'myAccount' => (int) get_option( 'woocommerce_myaccount_page_id' ),
'terms' => (int) get_option( 'woocommerce_terms_page_id' ),
];
return $wooCommercePages;
}
/**
* Checks whether the current page is a special WooCommerce page we shouldn't show our schema settings for.
*
* @since 4.1.6
*
* @param int $postId The post ID.
* @return bool Whether the current page is a disallowed WooCommerce page.
*/
public function isWooCommercePageWithoutSchema( $postId = 0 ) {
$page = $this->isWooCommercePage( $postId );
if ( ! $page ) {
return false;
}
$disallowedPages = [ 'cart', 'checkout', 'myAccount' ];
return in_array( $page, $disallowedPages, true );
}
/**
* Checks whether the queried object is the WooCommerce shop page.
*
* @since 4.0.0
*
* @param int $id The post ID to check against (optional).
* @return bool Whether the current page is the WooCommerce shop page.
*/
public function isWooCommerceShopPage( $id = 0 ) {
if ( ! $this->isWooCommerceActive() ) {
return false;
}
if ( ! is_admin() && ! aioseo()->helpers->isAjaxCronRestRequest() && function_exists( 'is_shop' ) ) {
return is_shop();
}
// Prevent non-numeric id.
$id = is_numeric( $id ) ? (int) $id : 0;
// phpcs:disable HM.Security.ValidatedSanitizedInput, HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended
$id = ! $id && ! empty( $_GET['post'] )
? (int) sanitize_text_field( wp_unslash( $_GET['post'] ) )
: $id;
// phpcs:enable
return $id && wc_get_page_id( 'shop' ) === $id;
}
/**
* Checks whether the queried object is the WooCommerce cart page.
*
* @since 4.1.3
*
* @param int $id The post ID to check against (optional).
* @return bool Whether the current page is the WooCommerce cart page.
*/
public function isWooCommerceCartPage( $id = 0 ) {
if ( ! $this->isWooCommerceActive() ) {
return false;
}
if ( ! is_admin() && ! aioseo()->helpers->isAjaxCronRestRequest() && function_exists( 'is_cart' ) ) {
return is_cart();
}
// phpcs:disable HM.Security.ValidatedSanitizedInput, HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended
$id = ! $id && ! empty( $_GET['post'] )
? (int) sanitize_text_field( wp_unslash( $_GET['post'] ) )
: (int) $id;
// phpcs:enable
return $id && wc_get_page_id( 'cart' ) === $id;
}
/**
* Checks whether the queried object is the WooCommerce checkout page.
*
* @since 4.1.3
*
* @param int $id The post ID to check against (optional).
* @return bool Whether the current page is the WooCommerce checkout page.
*/
public function isWooCommerceCheckoutPage( $id = 0 ) {
if ( ! $this->isWooCommerceActive() ) {
return false;
}
if ( ! is_admin() && ! aioseo()->helpers->isAjaxCronRestRequest() && function_exists( 'is_checkout' ) ) {
return is_checkout();
}
// phpcs:disable HM.Security.ValidatedSanitizedInput, HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended
$id = ! $id && ! empty( $_GET['post'] )
? (int) sanitize_text_field( wp_unslash( $_GET['post'] ) )
: (int) $id;
// phpcs:enable
return $id && wc_get_page_id( 'checkout' ) === $id;
}
/**
* Checks whether the queried object is the WooCommerce account page.
*
* @since 4.1.3
*
* @param int $id The post ID to check against (optional).
* @return bool Whether the current page is the WooCommerce account page.
*/
public function isWooCommerceAccountPage( $id = 0 ) {
if ( ! $this->isWooCommerceActive() ) {
return false;
}
if ( ! is_admin() && ! aioseo()->helpers->isAjaxCronRestRequest() && function_exists( 'is_account_page' ) ) {
return is_account_page();
}
// phpcs:disable HM.Security.ValidatedSanitizedInput, HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended
$id = ! $id && ! empty( $_GET['post'] )
? (int) sanitize_text_field( wp_unslash( $_GET['post'] ) )
: (int) $id;
// phpcs:enable
return $id && wc_get_page_id( 'myaccount' ) === $id;
}
/**
* Checks whether the queried object is a WooCommerce product page.
*
* @since 4.5.5
*
* @return bool Whether the current page is a WooCommerce product page.
*/
public function isWooCommerceProductPage() {
if (
! $this->isWooCommerceActive() ||
! function_exists( 'is_product' )
) {
return false;
}
return is_product();
}
/**
* Checks whether the queried object is a WooCommerce taxonomy page.
*
* @since 4.5.5
*
* @return bool Whether the current page is a WooCommerce taxonomy page.
*/
public function isWooCommerceTaxonomyPage() {
if (
! $this->isWooCommerceActive() ||
! function_exists( 'is_product_taxonomy' )
) {
return false;
}
return is_product_taxonomy();
}
/**
* Internationalize.
*
* @since 4.0.0
*
* @param $in
* @return mixed|void
*/
public function internationalize( $in ) {
if ( function_exists( 'langswitch_filter_langs_with_message' ) ) {
$in = langswitch_filter_langs_with_message( $in );
}
if ( function_exists( 'polyglot_filter' ) ) {
$in = polyglot_filter( $in );
}
if ( function_exists( 'qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage' ) ) {
$in = qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage( $in );
} elseif ( function_exists( 'ppqtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage' ) ) {
$in = ppqtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage( $in );
} elseif ( function_exists( 'qtranxf_useCurrentLanguageIfNotFoundUseDefaultLanguage' ) ) {
$in = qtranxf_useCurrentLanguageIfNotFoundUseDefaultLanguage( $in );
}
return apply_filters( 'localization', $in );
}
/**
* Checks if WPML is active.
*
* @since 4.0.0
*
* @return bool True if it is, false if not.
*/
public function isWpmlActive() {
return class_exists( 'SitePress' );
}
/**
* Checks if TranslatePress is active.
*
* @since 4.7.3
*
* @return bool True if it is, false if not.
*/
public function isTranslatePressActive() {
return class_exists( 'TRP_Translate_Press' );
}
/**
* Localizes a given URL.
*
* This is required for compatibility with WPML.
*
* @since 4.0.0
*
* @param string $path The relative path of the URL.
* @return string $url The filtered URL.
*/
public function localizedUrl( $path ) {
$url = apply_filters( 'wpml_home_url', home_url( '/' ) );
// Remove URL parameters.
preg_match_all( '/\?[\s\S]+/', (string) $url, $matches );
// Get the base URL.
$url = preg_replace( '/\?[\s\S]+/', '', (string) $url );
$url = trailingslashit( $url );
$url .= preg_replace( '/\//', '', (string) $path, 1 );
// Readd URL parameters.
if ( $matches && $matches[0] ) {
$url .= $matches[0][0];
}
return $url;
}
/**
* Checks whether BuddyPress is active.
*
* @since 4.0.0
*
* @return boolean
*/
public function isBuddyPressActive() {
return class_exists( 'BuddyPress' );
}
/**
* Checks whether the queried object is a buddy press user page.
*
* @since 4.0.0
*
* @return boolean
*/
public function isBuddyPressUser() {
return $this->isBuddyPressActive() && function_exists( 'bp_is_user' ) && bp_is_user();
}
/**
* Returns if the page is a BuddyPress page (Activity, Members, Groups).
*
* @since 4.0.0
*
* @param int $postId The post ID.
* @return bool If the page is a BuddyPress page or not.
*/
public function isBuddyPressPage( $postId = 0 ) {
$bpPageIds = $this->getBuddyPressPageIds();
return in_array( $postId, $bpPageIds, true );
}
/**
* Returns the BuddyPress pages.
*
* @since 4.7.3
*
* @return array A list of BuddyPress page IDs.
*/
public function getBuddyPressPageIds() {
if ( ! $this->isBuddyPressActive() ) {
return [];
}
static $bpPageIds = null;
if ( null === $bpPageIds ) {
$bpPageIds = (array) get_option( 'bp-pages' );
$bpPageIds = array_map( 'intval', $bpPageIds );
}
return $bpPageIds;
}
/**
* Returns ACF fields as an array of meta keys and values.
*
* @since 4.0.6
*
* @param \WP_Post|int $post The post.
* @param array $types A whitelist of ACF field types.
* @return array An array of meta keys and values.
*/
public function getAcfContent( $post = null, $types = [] ) {
$post = ( $post && is_object( $post ) ) ? $post : $this->getPost( $post );
if ( ! class_exists( 'ACF' ) || ! function_exists( 'get_field_objects' ) ) {
return [];
}
if ( defined( 'ACF_VERSION' ) && version_compare( ACF_VERSION, '5.7.0', '<' ) ) {
return [];
}
// Set defaults.
$allowedTypes = [
'text',
'textarea',
'email',
'url',
'wysiwyg',
'image',
'gallery',
'link',
];
$types = wp_parse_args( $types, $allowedTypes );
$fieldObjects = get_field_objects( $post->ID );
if ( empty( $fieldObjects ) ) {
return [];
}
// Filter out any fields that are not in our allowed types.
$fields = array_filter( $fieldObjects, function( $object ) use ( $types ) {
return ! empty( $object['value'] ) && in_array( $object['type'], $types, true );
});
// Create an array with the field names and values with added HTML markup.
$acfFields = [];
foreach ( $fields as $field ) {
switch ( $field['type'] ) {
case 'url':
$value = make_clickable( $field['value'] ?? '' );
break;
case 'image':
// Image format options are array, URL (string), id (int).
$imageUrl = is_array( $field['value'] ) ? $field['value']['url'] : $field['value'];
$imageUrl = is_numeric( $imageUrl ) ? wp_get_attachment_image_url( $imageUrl ) : $imageUrl;
$value = "<img src='$imageUrl' />"; // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage
break;
case 'gallery':
$imageUrl = $field['value'];
// The value of a gallery field should always be an array.
if ( is_array( $imageUrl ) ) {
$imageUrl = current( $imageUrl );
}
// Image array format.
if ( is_array( $imageUrl ) && ! empty( $imageUrl['url'] ) ) {
$imageUrl = $imageUrl['url'];
}
// Image ID format.
$imageUrl = is_numeric( $imageUrl ) ? wp_get_attachment_image_url( $imageUrl ) : $imageUrl;
$value = ! empty( $imageUrl ) ? "<img src='{$imageUrl}' />" : ''; // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage
break;
case 'link':
$value = make_clickable( $field['value']['url'] ?? $field['value'] ?? '' );
break;
default:
$value = $field['value'];
break;
}
if ( $value ) {
$acfFields[ $field['name'] ] = $value;
}
}
return $acfFields;
}
/**
* Retrieves the ACF Flexible Content field value for a given post.
*
* @since 4.7.9
*
* @param string $name The name of the field.
* @param int|object $post The post ID or object.
* @return string The field value.
*/
public function getAcfFlexibleContentField( $name, $post ) {
$output = '';
if ( ! function_exists( 'acf_get_raw_field' ) || ! function_exists( 'acf_get_field' ) ) {
return $output;
}
$parentTrace = [];
$field = acf_get_raw_field( $name ) ?? [];
while ( ! empty( $field['parent'] ) && ! empty( $field['parent_layout'] ) ) {
$parentField = acf_get_field( $field['parent'] );
$parentTrace[] = $parentField['name'] ?? '';
$field = $parentField;
}
$parentTrace = array_filter( $parentTrace );
if ( empty( $parentTrace ) ) {
return $output;
}
$parentTrace = array_reverse( $parentTrace );
$parentName = array_shift( $parentTrace );
$highestParentField = get_field( $parentName, $post );
for ( $i = 0; $i <= count( $parentTrace ); $i++ ) {
$values = array_filter( array_column( $highestParentField, $name ), 'is_scalar' );
if ( $values ) {
return implode( ' ', $values );
}
$highestParentField = $highestParentField[0] ?? '';
if (
! is_array( $highestParentField ) ||
! isset( $parentTrace[ $i ] )
) {
break;
}
$highestParentField = $highestParentField[ $parentTrace[ $i ] ];
}
return $output;
}
/**
* Checks whether the Smash Balloon Custom Facebook Feed plugin is active.
*
* @since 4.2.0
*
* @return bool Whether the SB CFF plugin is active.
*/
public function isSbCustomFacebookFeedActive() {
static $isActive = null;
if ( null !== $isActive ) {
return $isActive;
}
$isActive = defined( 'CFFVER' ) || is_plugin_active( 'custom-facebook-feed/custom-facebook-feed.php' );
return $isActive;
}
/**
* Returns the access token for Facebook from Smash Balloon if there is one.
*
* @since 4.2.0
*
* @return string|false The access token or false if there is none.
*/
public function getSbAccessToken() {
static $accessToken = null;
if ( null !== $accessToken ) {
return $accessToken;
}
if ( ! $this->isSbCustomFacebookFeedActive() ) {
$accessToken = false;
return $accessToken;
}
$oembedTokenData = get_option( 'cff_oembed_token', [] );
if ( ! $oembedTokenData || empty( $oembedTokenData['access_token'] ) ) {
$accessToken = false;
return $accessToken;
}
$sbFacebookDataEncryptionInstance = new \CustomFacebookFeed\SB_Facebook_Data_Encryption();
$accessToken = $sbFacebookDataEncryptionInstance->maybe_decrypt( $oembedTokenData['access_token'] );
return $accessToken;
}
/**
* Returns the homepage URL for a language code.
*
* @since 4.2.1
*
* @param string|int $identifier The language code or the post id to return the url.
* @return string The home URL.
*/
public function wpmlHomeUrl( $identifier ) {
foreach ( $this->wpmlHomePages() as $langCode => $wpmlHomePage ) {
if (
( is_string( $identifier ) && $langCode === $identifier ) ||
( is_numeric( $identifier ) && $wpmlHomePage['id'] === $identifier )
) {
return $wpmlHomePage['url'];
}
}
return '';
}
/**
* Returns the homepage IDs.
*
* @since 4.2.1
*
* @return array An array of home page ids.
*/
public function wpmlHomePages() {
global $sitepress;
static $homePages = [];
if ( ! $this->isWpmlActive() || empty( $sitepress ) || ! method_exists( $sitepress, 'language_url' ) ) {
return $homePages;
}
if ( empty( $homePages ) ) {
$languages = apply_filters( 'wpml_active_languages', [] );
$homePageId = (int) get_option( 'page_on_front' );
foreach ( $languages as $language ) {
$homePages[ $language['code'] ] = [
'id' => apply_filters( 'wpml_object_id', $homePageId, 'page', false, $language['code'] ),
'url' => $sitepress->language_url( $language['code'] )
];
}
}
return $homePages;
}
/**
* Returns if the post id os a WPML home page.
*
* @since 4.2.1
*
* @param int $postId The post ID.
* @return bool Is the post id a home page.
*/
public function wpmlIsHomePage( $postId ) {
foreach ( $this->wpmlHomePages() as $wpmlHomePage ) {
if ( $wpmlHomePage['id'] === $postId ) {
return true;
}
}
return false;
}
/**
* Returns the WPML url format.
*
* @since 4.2.8
*
* @return string The format.
*/
public function getWpmlUrlFormat() {
global $sitepress;
if (
! $this->isWpmlActive() ||
empty( $sitepress ) ||
! method_exists( $sitepress, 'get_setting' )
) {
return '';
}
switch ( $sitepress->get_setting( 'language_negotiation_type' ) ) {
case WPML_LANGUAGE_NEGOTIATION_TYPE_DIRECTORY:
case 1:
return 'directory';
case WPML_LANGUAGE_NEGOTIATION_TYPE_DOMAIN:
case 2:
return 'domain';
case WPML_LANGUAGE_NEGOTIATION_TYPE_PARAMETER:
case 3:
return 'parameter';
default:
return '';
}
}
/**
* Returns the TranslatePress slugs code and slug.
*
* @since 4.7.3
*
* @return array The slugs.
*/
public function getTranslatePressUrlSlugs() {
if ( ! $this->isTranslatePressActive() ) {
return [];
}
$settings = maybe_unserialize( get_option( 'trp_settings', [] ) );
return isset( $settings['url-slugs'] ) ? $settings['url-slugs'] : [];
}
/**
* Checks whether the WooCommerce Follow Up Emails plugin is active.
*
* @since 4.2.2
*
* @return bool Whether the plugin is active.
*/
public function isWooCommerceFollowupEmailsActive() {
$isActive = defined( 'FUE_VERSION' ) || is_plugin_active( 'woocommerce-follow-up-emails/woocommerce-follow-up-emails.php' );
return $isActive;
}
/**
* Checks if the current page is an AMP page.
* This function is only effective if called after the `wp` action.
*
* @since 4.2.3
*
* @param string $pluginName The name of the AMP plugin to check for (optional).
* @return bool Whether the current page is an AMP page.
*/
public function isAmpPage( $pluginName = '' ) {
// Official AMP plugin.
if ( 'amp' === $pluginName ) {
// If we're checking for the AMP page plugin specifically, return early if it's not active.
// Otherwise, we'll return true if AMP for WP is enabled because the helper method doesn't distinguish between the two.
if ( ! defined( 'AMP__VERSION' ) ) {
return false;
}
$options = get_option( 'amp-options' );
if ( ! empty( $options['theme_support'] ) && 'standard' === strtolower( $options['theme_support'] ) ) {
return true;
}
}
return $this->isAmpPageHelper();
}
/**
* Helper function for {@see isAmpPage()}.
* Checks if the current page is an AMP page.
*
* @since 4.2.4
*
* @return bool Whether the current page is an AMP page.
*/
private function isAmpPageHelper() {
// First check for the existence of any AMP plugin functions. Bail early if none are found, and prevent false positives.
if (
! function_exists( 'amp_is_request' ) &&
! function_exists( 'is_amp_endpoint' ) &&
! function_exists( 'ampforwp_is_amp_endpoint' ) &&
! function_exists( 'is_amp_wp' )
) {
// If none of the AMP plugin functions are found, return false and allow compatibility with custom implementations.
return apply_filters( 'aioseo_is_amp_page', false );
}
// AMP plugin requires the `wp` action to be called to function properly, otherwise, it will throw warnings.
if ( did_action( 'wp' ) ) {
// Check for the "AMP" plugin.
if ( function_exists( 'amp_is_request' ) ) {
return (bool) amp_is_request();
}
// Check for the "AMP" plugin (`is_amp_endpoint()` is deprecated).
if ( function_exists( 'is_amp_endpoint' ) ) {
return (bool) is_amp_endpoint();
}
// Check for the "AMP for WP – Accelerated Mobile Pages" plugin.
if ( function_exists( 'ampforwp_is_amp_endpoint' ) ) {
return (bool) ampforwp_is_amp_endpoint();
}
// Check for the "AMP WP" plugin.
if ( function_exists( 'is_amp_wp' ) ) {
return (bool) is_amp_wp();
}
}
return false;
}
/**
* If we're in a LearnPress lesson page, return the lesson ID.
*
* @since 4.3.1
*
* @return int|false
*/
public function getLearnPressLesson() {
// phpcs:disable Squiz.NamingConventions.ValidVariableName
global $lp_course_item;
if ( $lp_course_item && method_exists( $lp_course_item, 'get_id' ) ) {
return $lp_course_item->get_id();
}
// phpcs:enable Squiz.NamingConventions.ValidVariableName
return false;
}
/**
* Set a flag to indicate Divi whether it is processing internal content or not.
*
* @since 4.4.3
*
* @param null|bool $flag The flag value.
* @return null|bool The previous flag value to reset it later.
*/
public function setDiviInternalRendering( $flag ) {
if ( ! defined( 'ET_BUILDER_VERSION' ) ) {
return null;
}
// phpcs:disable Squiz.NamingConventions.ValidVariableName
global $et_pb_rendering_column_content;
$originalValue = $et_pb_rendering_column_content;
$et_pb_rendering_column_content = $flag;
// phpcs:enable Squiz.NamingConventions.ValidVariableName
return $originalValue;
}
/**
* Checks whether the current request is being done by a crawler from Yandex.
*
* @since 4.4.0
*
* @return bool Whether the current request is being done by a crawler from Yandex.
*/
public function isYandexUserAgent() {
if ( ! isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
return false;
}
return preg_match( '#.*Yandex.*#', (string) sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) );
}
/**
* Checks whether the taxonomy is a WooCommerce product attribute.
*
* @since 4.7.8
*
* @param mixed $taxonomy The taxonomy.
* @return bool Whether the taxonomy is a WooCommerce product attribute.
*/
public function isWooCommerceProductAttribute( $taxonomy ) {
$name = is_object( $taxonomy )
? $taxonomy->name
: (
is_array( $taxonomy )
? $taxonomy['name']
: $taxonomy
);
return ! empty( $name ) && 'pa_' === substr( $name, 0, 3 );
}
/**
* Returns whether a plugin is active or not using abstraction.
*
* @since 4.8.1
*
* @param string $slug The plugin slug.
* @return bool Whether the plugin is active.
*/
public function isPluginActive( $slug ) {
$mapped = [
'buddypress' => 'buddypress/bp-loader.php',
'bbpress' => 'bbpress/bbpress.php',
'weglot' => 'weglot/weglot.php'
];
static $output = [];
if ( isset( $output[ $slug ] ) ) {
return $output[ $slug ];
}
$mapped[ $slug ] = $mapped[ $slug ] ?? $slug;
$output[ $slug ] = function_exists( 'is_plugin_active' ) && is_plugin_active( $mapped[ $slug ] );
return $output[ $slug ];
}
} Helpers/WpContext.php 0000666 00000070077 15114625461 0010636 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains all context related helper methods.
* This includes methods to check the context of the current request, but also get WP objects.
*
* @since 4.1.4
*/
trait WpContext {
/**
* The original main query.
*
* @since 4.3.0
*
* @var \WP_Query
*/
public $originalQuery;
/**
* The original main post variable.
*
* @since 4.3.0
*
* @var \WP_Post
*/
public $originalPost;
/**
* Get the home page object.
*
* @since 4.1.1
*
* @return \WP_Post|null The home page.
*/
public function getHomePage() {
$homePageId = $this->getHomePageId();
return $homePageId ? get_post( $homePageId ) : null;
}
/**
* Get the ID of the home page.
*
* @since 4.0.0
*
* @return int|false The home page ID.
*/
public function getHomePageId() {
static $homeId = null;
if ( null !== $homeId ) {
return $homeId;
}
$pageShowOnFront = ( 'page' === get_option( 'show_on_front' ) );
$pageOnFrontId = get_option( 'page_on_front' );
$homeId = $pageShowOnFront && $pageOnFrontId ? (int) $pageOnFrontId : false;
return $homeId;
}
/**
* Returns the blog page.
*
* @since 4.0.0
*
* @return \WP_Post|null The blog page.
*/
public function getBlogPage() {
$blogPageId = $this->getBlogPageId();
return $blogPageId ? get_post( $blogPageId ) : null;
}
/**
* Gets the current blog page id if it's configured.
*
* @since 4.1.1
*
* @return int|null
*/
public function getBlogPageId() {
$pageShowOnFront = ( 'page' === get_option( 'show_on_front' ) );
$blogPageId = (int) get_option( 'page_for_posts' );
return $pageShowOnFront && $blogPageId ? $blogPageId : null;
}
/**
* Checks whether the current page is a taxonomy term archive.
*
* @since 4.0.0
*
* @return bool Whether the current page is a taxonomy term archive.
*/
public function isTaxTerm() {
$object = get_queried_object();
return $object instanceof \WP_Term;
}
/**
* Checks whether the current page is a static one.
*
* @since 4.0.0
*
* @return bool Whether the current page is a static one.
*/
public function isStaticPage() {
return $this->isStaticHomePage() || $this->isStaticPostsPage() || $this->isWooCommerceShopPage();
}
/**
* Checks whether the current page is the static homepage.
*
* @since 4.0.0
*
* @param mixed $post Pass in an optional post to check if its the static home page.
* @return bool Whether the current page is the static homepage.
*/
public function isStaticHomePage( $post = null ) {
static $isHomePage = null;
if ( null !== $isHomePage ) {
return $isHomePage;
}
$post = aioseo()->helpers->getPost( $post );
$isHomePage = ( 'page' === get_option( 'show_on_front' ) && ! empty( $post->ID ) && (int) get_option( 'page_on_front' ) === $post->ID );
return $isHomePage;
}
/**
* Checks whether the current page is the dynamic homepage.
*
* @since 4.2.3
*
* @return bool Whether the current page is the dynamic homepage.
*/
public function isDynamicHomePage() {
return is_front_page() && is_home();
}
/**
* Checks whether the current page is the static posts page.
*
* @since 4.0.0
*
* @return bool Whether the current page is the static posts page.
*/
public function isStaticPostsPage( $post = null ) {
static $isStaticPostsPage = null;
if ( null !== $isStaticPostsPage ) {
return $isStaticPostsPage;
}
$post = aioseo()->helpers->getPost( $post );
$isStaticPostsPage = (
( is_home() && ( 0 !== (int) get_option( 'page_for_posts' ) ) ) ||
( ! empty( $post->ID ) && (int) get_option( 'page_for_posts' ) === $post->ID )
);
return $isStaticPostsPage;
}
/**
* Checks whether current page supports meta.
*
* @since 4.0.0
*
* @return bool Whether the current page supports meta.
*/
public function supportsMeta() {
return ! is_date() && ! is_author() && ! is_search() && ! is_404();
}
/**
* Returns the current post object.
*
* @since 4.0.0
*
* @param \WP_Post|int|bool $postId The post ID.
* @return \WP_Post|null The post object.
*/
public function getPost( $postId = false ) {
$postId = is_a( $postId, 'WP_Post' ) ? $postId->ID : $postId;
if ( aioseo()->helpers->isWooCommerceShopPage( $postId ) ) {
return get_post( wc_get_page_id( 'shop' ) );
}
if ( is_front_page() || is_home() ) {
$showOnFront = 'page' === get_option( 'show_on_front' );
if ( $showOnFront ) {
if ( is_front_page() ) {
$pageOnFront = (int) get_option( 'page_on_front' );
return get_post( $pageOnFront );
} elseif ( is_home() ) {
$pageForPosts = (int) get_option( 'page_for_posts' );
return get_post( $pageForPosts );
}
}
}
// Learnpress lessons load the course. So here we need to switch to the lesson.
$learnPressLesson = aioseo()->helpers->getLearnPressLesson();
if ( ! $postId && $learnPressLesson ) {
$postId = $learnPressLesson;
}
// Allow other plugins to filter the post ID e.g. for a special archive page.
$postId = apply_filters( 'aioseo_get_post_id', $postId );
// We need to check these conditions and cannot always return get_post() because we'll return the first post on archive pages (dynamic homepage, term pages, etc.).
if (
$this->isScreenBase( 'post' ) ||
$postId ||
is_singular()
) {
return get_post( $postId );
}
return null;
}
/**
* Returns the term object for the given ID or the one from the main query.
*
* @since 4.7.8
*
* @param int $termId The term ID.
* @param string $taxonomy The taxonomy.
* @return \WP_Term The term object.
*/
public function getTerm( $termId = 0, $taxonomy = '' ) {
$term = null;
if ( $termId ) {
$term = get_term( $termId, $taxonomy );
} else {
$term = get_queried_object();
}
// If the term is a Product Attribute, set its parent taxonomy to our fake
// "product_attributes" taxonomy so we can use the default settings.
if ( is_a( $term, 'WP_Term' ) && $this->isWooCommerceProductAttribute( $term->taxonomy ) ) {
$term = clone $term;
$term->taxonomy = 'product_attributes';
}
return $term;
}
/**
* Returns the current post ID.
*
* @since 4.3.1
*
* @return int|null The post ID.
*/
public function getPostId() {
$post = $this->getPost();
return is_object( $post ) && property_exists( $post, 'ID' ) ? $post->ID : null;
}
/**
* Returns the post content after parsing it.
*
* @since 4.1.5
*
* @param \WP_Post|int $post The post (optional).
* @return string The post content.
*/
public function getPostContent( $post = null ) {
$post = is_a( $post, 'WP_Post' ) ? $post : $this->getPost( $post );
static $content = [];
if ( isset( $content[ $post->ID ] ) ) {
return $content[ $post->ID ];
}
// We need to process the content for page builders.
$postContent = $post->post_content;
$pageBuilder = aioseo()->helpers->getPostPageBuilderName( $post->ID );
if ( ! empty( $pageBuilder ) ) {
$postContent = aioseo()->standalone->pageBuilderIntegrations[ $pageBuilder ]->processContent( $post->ID, $postContent );
}
$postContent = is_string( $postContent ) ? $postContent : '';
$content[ $post->ID ] = $this->theContent( $postContent );
if ( apply_filters( 'aioseo_description_include_custom_fields', true, $post ) ) {
$content[ $post->ID ] .= $this->theContent( $this->getPostCustomFieldsContent( $post ) );
}
return $content[ $post->ID ];
}
/**
* Gets the content from configured custom fields.
*
* @since 4.2.7
*
* @param \WP_Post|int $post A post object or ID.
* @return string The content.
*/
public function getPostCustomFieldsContent( $post = null ) {
$post = is_a( $post, 'WP_Post' ) ? $post : $this->getPost( $post );
if ( ! aioseo()->dynamicOptions->searchAppearance->postTypes->has( $post->post_type ) ) {
return '';
}
$customFieldKeys = aioseo()->dynamicOptions->searchAppearance->postTypes->{$post->post_type}->customFields;
if ( empty( $customFieldKeys ) ) {
return '';
}
$customFieldKeys = explode( ' ', sanitize_text_field( $customFieldKeys ) );
return aioseo()->helpers->getCustomFieldsContent( $post, $customFieldKeys );
}
/**
* Returns the post content after parsing shortcodes and blocks.
* We avoid using the "the_content" hook because it breaks stuff if we call it outside the loop or main query.
* See https://developer.wordpress.org/reference/hooks/the_content/
*
* @since 4.1.5.2
*
* @param string $postContent The post content.
* @return string The parsed post content.
*/
public function theContent( $postContent ) {
if ( ! aioseo()->options->searchAppearance->advanced->runShortcodes ) {
return $postContent;
}
// Because do_blocks() and do_shortcodes() can trigger conflicts, we need to clone these objects and restore them afterwards.
// We need to clone deep to sever pointers/references because these have nested object properties.
global $wp_query, $post; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
$this->originalQuery = $this->deepClone( $wp_query ); // phpcs:ignore Squiz.NamingConventions.ValidVariableName
$this->originalPost = is_a( $post, 'WP_Post' ) ? $this->deepClone( $post ) : null;
// The order of the function calls below is intentional and should NOT change.
$postContent = do_blocks( $postContent );
$postContent = wpautop( $postContent );
$postContent = $this->doShortcodes( $postContent );
$this->restoreWpQuery();
return $postContent;
}
/**
* Returns the description based on the post content.
*
* @since 4.0.0
*
* @param \WP_Post|int $post The post (optional).
* @return string The description.
*/
public function getDescriptionFromContent( $post = null ) {
$post = is_a( $post, 'WP_Post' ) ? $post : $this->getPost( $post );
static $content = [];
if ( isset( $content[ $post->ID ] ) ) {
return $content[ $post->ID ];
}
$content[ $post->ID ] = '';
if ( ! empty( $post->post_password ) ) {
return $content[ $post->ID ];
}
$postContent = $this->getPostContent( $post );
// Strip images, captions and WP oembed wrappers (e.g. YouTube URLs) from the post content.
$postContent = preg_replace( '/(<figure.*?\/figure>|<img.*?\/>|<div.*?class="wp-block-embed__wrapper".*?>.*?<\/div>)/s', '', (string) $postContent );
$postContent = str_replace( ']]>', ']]>', (string) $postContent );
$postContent = trim( wp_strip_all_tags( strip_shortcodes( (string) $postContent ) ) );
$content[ $post->ID ] = wp_trim_words( (string) $postContent, 55, '' );
return $content[ $post->ID ];
}
/**
* Returns custom fields as a string.
*
* @since 4.0.6
*
* @param \WP_Post|int $post The post.
* @param array $keys The post meta_keys to check for values.
* @return string The custom field content.
*/
public function getCustomFieldsContent( $post = null, $keys = [] ) {
$post = is_a( $post, 'WP_Post' ) ? $post : $this->getPost( $post );
$customFieldContent = '';
$acfFields = $this->getAcfContent( $post );
foreach ( $keys as $key ) {
// Try ACF.
if ( isset( $acfFields[ $key ] ) && is_scalar( $acfFields[ $key ] ) ) {
$customFieldContent .= "$acfFields[$key] ";
continue;
}
// Fallback to post meta.
$value = get_post_meta( $post->ID, $key, true );
if ( $value && is_scalar( $value ) ) {
$customFieldContent .= $value . ' ';
}
}
return $customFieldContent;
}
/**
* Returns if the page is a special type (WooCommerce pages, Privacy page).
*
* @since 4.0.0
*
* @param int $postId The post ID.
* @return bool If the page is special or not.
*/
public function isSpecialPage( $postId = 0 ) {
$specialPages = $this->getSpecialPageIds();
return in_array( (int) $postId, $specialPages, true );
}
/**
* Returns the ID of all special pages (e.g. homepage, blog page, WooCommerce, BuddyPress, etc.).
* This cannot be cached because the plugins need to be loaded first.
*
* @since 4.7.3
*
* @return array The IDs of all special pages.
*/
public function getSpecialPageIds() {
$pageForPostsId = (int) get_option( 'page_for_posts' );
$pageForPrivacyPolicyId = (int) get_option( 'wp_page_for_privacy_policy' );
$buddyPressPageIds = $this->getBuddyPressPageIds();
$wooCommercePageIds = array_values( $this->getWooCommercePages() );
$specialPageIds = array_merge(
[
$pageForPostsId,
$pageForPrivacyPolicyId,
],
$buddyPressPageIds,
$wooCommercePageIds
);
// Ensure all values are integers.
$specialPageIds = array_map( 'intval', $specialPageIds );
return $specialPageIds;
}
/**
* Returns whether a post is eligible for being analyzed by TruSEO.
*
* @since 4.6.1
* @version 4.7.3 Renamed from "isPageAnalysisEligible" to "isTruSeoEligible" to make it more clear.
*
* @param int $postId Post ID.
* @return bool Whether a post is eligible for being analyzed by TruSEO.
*/
public function isTruSeoEligible( $postId ) {
static $isTruSeoEnabled = null;
if ( null === $isTruSeoEnabled ) {
$isTruSeoEnabled = aioseo()->options->advanced->truSeo;
}
if ( ! $isTruSeoEnabled ) {
return false;
}
static $isPostEligible = [];
if ( isset( $isPostEligible[ $postId ] ) ) {
return $isPostEligible[ $postId ];
}
// Set the default to true.
$isPostEligible[ $postId ] = true;
$wpPost = $this->getPost( $postId );
if ( ! is_a( $wpPost, 'WP_Post' ) ) {
$isPostEligible[ $postId ] = false;
return false;
}
$eligiblePostTypes = $this->getTruSeoEligiblePostTypes();
if (
! in_array( $wpPost->post_type, $eligiblePostTypes, true ) ||
$this->isSpecialPage( $wpPost->ID )
) {
$isPostEligible[ $postId ] = false;
}
return $isPostEligible[ $postId ];
}
/**
* Returns the post types that are eligible for TruSEO analysis.
*
* @since 4.7.3
*
* @return array The post types that are eligible for TruSEO analysis.
*/
public function getTruSeoEligiblePostTypes() {
$allowedPostTypes = aioseo()->helpers->getPublicPostTypes( true );
$excludedPostTypes = [ 'attachment', 'aioseo-location', 'web-story' ];
if ( class_exists( 'bbPress' ) ) {
$excludedPostTypes = array_merge( $excludedPostTypes, [ 'forum', 'topic', 'reply' ] );
}
// Remove the excluded post types from the allowed ones.
$allowedPostTypes = array_diff( $allowedPostTypes, $excludedPostTypes );
// Now, check if the metabox is enabled and that the post type is public for each of these.
foreach ( $allowedPostTypes as $postType ) {
$postObjectType = get_post_type_object( $postType );
if ( is_a( $postObjectType, 'WP_Post_Type' ) && ! $postObjectType->public ) {
unset( $allowedPostTypes[ $postType ] );
}
$dynamicOptions = aioseo()->dynamicOptions->noConflict();
if ( ! $dynamicOptions->searchAppearance->postTypes->has( $postType, false ) || ! $dynamicOptions->{$postType}->advanced->showMetaBox ) {
// If not, unset it.
unset( $allowedPostTypes[ $postType ] );
}
}
// Considering post types get registered during various stages of the WP load process, we should not cache this.
return $allowedPostTypes;
}
/**
* Returns the page number of the current page.
*
* @since 4.0.0
*
* @return int The page number.
*/
public function getPageNumber() {
$page = get_query_var( 'page' );
if ( ! empty( $page ) ) {
return (int) $page;
}
$paged = get_query_var( 'paged' );
if ( ! empty( $paged ) ) {
return (int) $paged;
}
return 1;
}
/**
* Returns the page number for the comment page.
*
* @since 4.2.1
*
* @return int|false The page number or false if we're not on a comment page.
*/
public function getCommentPageNumber() {
$cpage = get_query_var( 'cpage', null );
if ( $this->isBlockTheme() ) {
global $wp_query; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
// For block themes we can't rely on `get_query_var()` because of {@see build_comment_query_vars_from_block()},
// so we need to check the query directly.
$cpage = $wp_query->query['cpage'] ?? null; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
}
return isset( $cpage ) ? (int) $cpage : false;
}
/**
* Check if the post passed in is a valid post, not a revision or autosave.
*
* @since 4.0.5
*
* @param \WP_Post $post The Post object to check.
* @param array $allowedPostStatuses Allowed post statuses.
* @return bool True if valid, false if not.
*/
public function isValidPost( $post, $allowedPostStatuses = [ 'publish' ] ) {
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return false;
}
if ( ! is_object( $post ) ) {
$post = get_post( $post );
}
// No post, no go.
if ( empty( $post ) ) {
return false;
}
// In order to prevent recursion, we are skipping scheduled-action posts and revisions.
if (
'scheduled-action' === $post->post_type ||
'revision' === $post->post_type
) {
return false;
}
// Ensure this post has the proper post status.
if (
! in_array( $post->post_status, $allowedPostStatuses, true ) &&
! in_array( 'all', $allowedPostStatuses, true )
) {
return false;
}
return true;
}
/**
* Checks whether the given URL is a valid attachment.
*
* @since 4.0.13
*
* @param string $url The URL.
* @return bool Whether the URL is a valid attachment.
*/
public function isValidAttachment( $url ) {
$uploadDirUrl = aioseo()->helpers->escapeRegex( $this->getWpContentUrl() );
return preg_match( "/$uploadDirUrl.*/", (string) $url );
}
/**
* Tries to convert an attachment URL into a post ID.
*
* This our own optimized version of attachment_url_to_postid().
*
* @since 4.0.13
*
* @param string $url The attachment URL.
* @return int|bool The attachment ID or false if no attachment could be found.
*/
public function attachmentUrlToPostId( $url ) {
$cacheName = 'attachment_url_to_post_id_' . sha1( "aioseo_attachment_url_to_post_id_$url" );
$cachedId = aioseo()->core->cache->get( $cacheName );
if ( $cachedId ) {
return 'none' !== $cachedId && is_numeric( $cachedId ) ? (int) $cachedId : false;
}
$path = $url;
$uploadDirInfo = wp_get_upload_dir();
$siteUrl = wp_parse_url( $uploadDirInfo['url'] );
$imagePath = wp_parse_url( $path );
// Force the protocols to match if needed.
if ( isset( $imagePath['scheme'] ) && ( $imagePath['scheme'] !== $siteUrl['scheme'] ) ) {
$path = str_replace( $imagePath['scheme'], $siteUrl['scheme'], $path );
}
if ( ! $this->isValidAttachment( $path ) ) {
aioseo()->core->cache->update( $cacheName, 'none' );
return false;
}
if ( 0 === strpos( $path, $uploadDirInfo['baseurl'] . '/' ) ) {
$path = substr( $path, strlen( $uploadDirInfo['baseurl'] . '/' ) );
}
$results = aioseo()->core->db->start( 'postmeta' )
->select( 'post_id' )
->where( 'meta_key', '_wp_attached_file' )
->where( 'meta_value', $path )
->limit( 1 )
->run()
->result();
if ( empty( $results[0]->post_id ) ) {
aioseo()->core->cache->update( $cacheName, 'none' );
return false;
}
aioseo()->core->cache->update( $cacheName, $results[0]->post_id );
return $results[0]->post_id;
}
/**
* Returns true if the request is a non-legacy REST API request.
* This function was copied from WooCommerce and improved.
*
* @since 4.1.2
*
* @return bool True if this is a REST API request.
*/
public function isRestApiRequest() {
if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
return true;
}
global $wp_rewrite; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
if ( empty( $wp_rewrite ) ) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName
return false;
}
if ( empty( $_SERVER['REQUEST_URI'] ) ) {
return false;
}
$restUrl = wp_parse_url( get_rest_url() );
$restUrl = $restUrl['path'] . ( ! empty( $restUrl['query'] ) ? '?' . $restUrl['query'] : '' );
$isRestApiRequest = ( 0 === strpos( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ), $restUrl ) );
return apply_filters( 'aioseo_is_rest_api_request', $isRestApiRequest );
}
/**
* Checks whether the current request is an AJAX, CRON or REST request.
*
* @since 4.1.3
*
* @return bool Whether the request is an AJAX, CRON or REST request.
*/
public function isAjaxCronRestRequest() {
return wp_doing_ajax() || wp_doing_cron() || $this->isRestApiRequest();
}
/**
* Check if we are in the middle of a WP-CLI call.
*
* @since 4.2.8
*
* @return bool True if we are in the WP_CLI context.
*/
public function isDoingWpCli() {
return defined( 'WP_CLI' ) && WP_CLI;
}
/**
* Checks whether we're on the given screen.
*
* @since 4.0.7
* @version 4.3.1
*
* @param string $screenName The screen name.
* @param string $comparison Check as a prefix.
* @return bool Whether we're on the given screen.
*/
public function isScreenBase( $screenName, $comparison = '' ) {
$screen = $this->getCurrentScreen();
if ( ! $screen || ! isset( $screen->base ) ) {
return false;
}
if ( 'prefix' === $comparison ) {
return 0 === stripos( $screen->base, $screenName );
}
return $screen->base === $screenName;
}
/**
* Returns if current screen is of a post type
*
* @since 4.0.17
*
* @param string $postType Post type slug
* @return bool True if the current screen is a post type screen.
*/
public function isScreenPostType( $postType ) {
$screen = $this->getCurrentScreen();
if ( ! $screen || ! isset( $screen->post_type ) ) {
return false;
}
return $screen->post_type === $postType;
}
/**
* Returns if current screen is a post list, optionaly of a post type.
*
* @since 4.2.4
*
* @param string $postType Post type slug.
* @return bool Is a post list.
*/
public function isScreenPostList( $postType = '' ) {
$screen = $this->getCurrentScreen();
if (
! $this->isScreenBase( 'edit' ) ||
empty( $screen->post_type )
) {
return false;
}
if ( ! empty( $postType ) && $screen->post_type !== $postType ) {
return false;
}
return true;
}
/**
* Returns if current screen is a post edit screen, optionaly of a post type.
*
* @since 4.2.4
*
* @param string $postType Post type slug.
* @return bool Is a post editing screen.
*/
public function isScreenPostEdit( $postType = '' ) {
$screen = $this->getCurrentScreen();
if (
! $this->isScreenBase( 'post' ) ||
empty( $screen->post_type )
) {
return false;
}
if ( ! empty( $postType ) && $screen->post_type !== $postType ) {
return false;
}
return true;
}
/**
* Gets current admin screen.
*
* @since 4.0.17
*
* @return false|\WP_Screen|null
*/
public function getCurrentScreen() {
if ( ! is_admin() || ! function_exists( 'get_current_screen' ) ) {
return false;
}
return get_current_screen();
}
/**
* Checks whether the current site is a multisite subdomain.
*
* @since 4.1.9
*
* @return bool Whether the current site is a subdomain.
*/
public function isSubdomain() {
if ( ! is_multisite() ) {
return false;
}
return apply_filters( 'aioseo_multisite_subdomain', is_subdomain_install() );
}
/**
* Returns if the current page is the login or register page.
*
* @since 4.2.1
*
* @return bool Login or register page.
*/
public function isWpLoginPage() {
// We can't sanitize the filename using sanitize_file_name() here because it will cause issues with custom login pages and certain plugins/themes where this function is not defined.
$self = ! empty( $_SERVER['PHP_SELF'] ) ? sanitize_text_field( wp_unslash( $_SERVER['PHP_SELF'] ) ) : ''; // phpcs:ignore HM.Security.ValidatedSanitizedInput.InputNotSanitized
if ( preg_match( '/wp-login\.php$|wp-register\.php$/', (string) $self ) ) {
return true;
}
return false;
}
/**
* Returns which type of WordPress page we're seeing.
* It will only work if {@see \WP_Query::$queried_object} has been set.
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/#filter-hierarchy
*
* @since 4.2.8
*
* @return string|null The template type or `null` if no match.
*/
public function getTemplateType() {
static $type = null;
if ( ! empty( $type ) ) {
return $type;
}
if ( is_attachment() ) {
$type = 'attachment';
} elseif ( is_single() ) {
$type = 'single';
} elseif (
is_page() ||
$this->isStaticPostsPage() ||
$this->isWooCommerceShopPage()
) {
$type = 'page';
} elseif ( is_author() ) { // An author page is an archive page, so it needs to be checked before `is_archive()`.
$type = 'author';
} elseif (
is_tax() ||
is_category() ||
is_tag()
) { // A taxonomy term page is an archive page, so it needs to be checked before `is_archive()`.
$type = 'taxonomy';
} elseif ( is_date() ) { // A date page is an archive page, so it needs to be checked before `is_archive()`.
$type = 'date';
} elseif ( is_archive() ) {
$type = 'archive';
} elseif ( is_home() && is_front_page() ) {
$type = 'dynamic_home';
} elseif ( is_search() ) {
$type = 'search';
}
return $type;
}
/**
* Sets the given post as the queried object of the main query.
*
* @since 4.3.0
*
* @param \WP_Post|int $wpPost The post object or ID.
* @return void
*/
public function setWpQueryPost( $wpPost ) {
$wpPost = is_a( $wpPost, 'WP_Post' ) ? $wpPost : get_post( $wpPost );
// phpcs:disable Squiz.NamingConventions.ValidVariableName
global $wp_query, $post;
$this->originalQuery = $this->deepClone( $wp_query );
$this->originalPost = is_a( $post, 'WP_Post' ) ? $this->deepClone( $post ) : null;
$wp_query->posts = [ $wpPost ];
$wp_query->post = $wpPost;
$wp_query->post_count = 1;
$wp_query->get_queried_object_id = (int) $wpPost->ID;
$wp_query->queried_object = $wpPost;
$wp_query->is_single = true;
$wp_query->is_singular = true;
if ( 'page' === $wpPost->post_type ) {
$wp_query->is_page = true;
}
// phpcs:enable Squiz.NamingConventions.ValidVariableName
$post = $wpPost;
}
/**
* Restores the main query back to the original query.
*
* @since 4.3.0
*
* @return void
*/
public function restoreWpQuery() {
global $wp_query, $post; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
if ( is_a( $this->originalQuery, 'WP_Query' ) ) {
// Loop over all properties and replace the ones that have changed.
// We want to avoid replacing the entire object because it can cause issues with other plugins.
foreach ( $this->originalQuery as $key => $value ) {
if ( $value !== $wp_query->{$key} ) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName
$wp_query->{$key} = $value; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
}
}
}
if ( is_a( $this->originalPost, 'WP_Post' ) ) {
foreach ( $this->originalPost as $key => $value ) {
if ( $value !== $post->{$key} ) {
$post->{$key} = $value;
}
}
}
$this->originalQuery = null;
$this->originalPost = null;
}
/**
* Gets the list of theme features.
*
* @since 4.4.9
*
* @return array List of theme features.
*/
public function getThemeFeatures() {
global $_wp_theme_features; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
return isset( $_wp_theme_features ) && is_array( $_wp_theme_features ) ? $_wp_theme_features : []; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
}
/**
* Returns whether the active theme is a block-based theme or not.
*
* @since 4.5.3
*
* @return bool Whether the active theme is a block-based theme or not.
*/
public function isBlockTheme() {
if ( function_exists( 'wp_is_block_theme' ) ) {
return wp_is_block_theme(); // phpcs:ignore AIOSEO.WpFunctionUse.NewFunctions.wp_is_block_themeFound
}
return false;
}
/**
* Retrieves the website name.
*
* @since 4.6.1
*
* @return string The website name.
*/
public function getWebsiteName() {
return aioseo()->options->searchAppearance->global->schema->websiteName
? aioseo()->tags->replaceTags( aioseo()->options->searchAppearance->global->schema->websiteName )
: aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'name' ) );
}
} Helpers/Deprecated.php 0000666 00000006757 15114625461 0010747 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains deprecated methods to be removed at a later date..
*
* @since 4.1.9
*/
trait Deprecated {
/**
* Helper method to enqueue scripts.
*
* @since 4.0.0
*
* @param string $script The script to enqueue.
* @param string $url The URL of the script.
* @param bool $vue Whether or not this is a vue script.
* @return void
*/
public function enqueueScript( $script, $url, $vue = true ) {
if ( ! wp_script_is( $script, 'enqueued' ) ) {
wp_enqueue_script(
$script,
$this->getScriptUrl( $url, $vue ),
[],
aioseo()->version,
true
);
}
}
/**
* Helper method to enqueue stylesheets.
*
* @since 4.0.0
*
* @param string $style The stylesheet to enqueue.
* @param string $url The URL of the stylesheet.
* @param bool $vue Whether or not this is a vue stylesheet.
* @return void
*/
public function enqueueStyle( $style, $url, $vue = true ) {
if ( ! wp_style_is( $style, 'enqueued' ) && $this->shouldEnqueue( $url ) ) {
wp_enqueue_style(
$style,
$this->getScriptUrl( $url, $vue ),
[],
aioseo()->version
);
}
}
/**
* Whether or not we should enqueue a file.
*
* @since 4.0.0
*
* @param string $url The url to check against.
* @return bool Whether or not we should enqueue.
*/
private function shouldEnqueue( $url ) {
$version = strtoupper( aioseo()->versionPath );
$host = defined( 'AIOSEO_DEV_' . $version ) ? constant( 'AIOSEO_DEV_' . $version ) : false;
if ( ! $host ) {
return true;
}
if ( false !== strpos( $url, 'chunk-common.css' ) ) {
// return false;
}
return true;
}
/**
* Retrieve the proper URL for this script or style.
*
* @since 4.0.0
*
* @param string $url The url.
* @param bool $vue Whether or not this is a vue script.
* @return string The modified url.
*/
public function getScriptUrl( $url, $vue = true ) {
$version = strtoupper( aioseo()->versionPath );
$host = $vue && defined( 'AIOSEO_DEV_' . $version ) ? constant( 'AIOSEO_DEV_' . $version ) : false;
$localUrl = $url;
$url = plugins_url( 'dist/' . aioseo()->versionPath . '/assets/' . $url, AIOSEO_FILE );
if ( ! $host ) {
return $url;
}
if ( $host && ! self::$connection ) {
$splitHost = explode( ':', str_replace( '/', '', str_replace( 'http://', '', str_replace( 'https://', '', $host ) ) ) );
self::$connection = @fsockopen( $splitHost[0], $splitHost[1] ); // phpcs:ignore WordPress
}
if ( ! self::$connection ) {
return $url;
}
return $host . $localUrl;
}
/**
* Returns the filesystem object if we have access to it.
*
* @since 4.0.0
*
* @param array $args The connection args.
* @return \WP_Filesystem_Base|bool The filesystem object.
*/
public function wpfs( $args = [] ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
WP_Filesystem( $args );
// phpcs:disable Squiz.NamingConventions.ValidVariableName
global $wp_filesystem;
if ( is_object( $wp_filesystem ) ) {
return $wp_filesystem;
}
// phpcs:enable Squiz.NamingConventions.ValidVariableName
return false;
}
/**
* Checks whether the current request is an AJAX, CRON or REST request.
*
* @since 4.1.9.1
*
* @return bool Whether the current request is an AJAX, CRON or REST request.
*/
public function isAjaxCronRest() {
return $this->isAjaxCronRestRequest();
}
} Helpers/Language.php 0000666 00000000744 15114625461 0010420 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains i18n and language (code) helper methods.
*
* @since 4.1.4
*/
trait Language {
/**
* Returns the language of the current response in BCP 47 format.
*
* @since 4.1.4
*
* @return string The language code in BCP 47 format.
*/
public function currentLanguageCodeBCP47() {
return str_replace( '_', '-', determine_locale() );
}
} Helpers/PostType.php 0000666 00000001410 15114625461 0010453 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains WordPress Post Type helpers.
*
* @since 4.2.4
*/
trait PostType {
/**
* Returns a post type feature.
*
* @since 4.2.4
*
* @param string|\WP_Post_Type $postType The post type.
* @param string $feature The feature to find.
* @return mixed|false The post type feature or false if not found.
*/
public function getPostTypeFeature( $postType, $feature ) {
if ( is_string( $postType ) ) {
$postType = get_post_type_object( $postType );
}
if ( ! is_a( $postType, 'WP_Post_Type' ) || ! isset( $postType->$feature ) ) {
return false;
}
return $postType->$feature;
}
} Helpers/Vue.php 0000666 00000065374 15114625461 0007446 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use AIOSEO\Plugin\Common\Integrations\WpCode as WpCodeIntegration;
use AIOSEO\Plugin\Common\Models;
use AIOSEO\Plugin\Common\Tools;
/**
* Contains all Vue related helper methods.
*
* @since 4.1.4
*/
trait Vue {
/**
* Holds the data for Vue.
*
* @since 4.4.9
*
* @var array
*/
private $data = [];
/**
* Optional arguments for setting the data.
*
* @since 4.4.9
*
* @var array
*/
private $args = [];
/**
* Holds the cached data.
*
* @since 4.5.1
*
* @var array
*/
private $cache = [];
/**
* Returns the data for Vue.
*
* @since 4.0.0
* @version 4.4.9
*
* @param string $page The current page.
* @param int $staticPostId Data for a specific post.
* @param string $integration Data for integration (builder).
* @return array The data.
*/
public function getVueData( $page = null, $staticPostId = null, $integration = null ) {
$this->args = compact( 'page', 'staticPostId', 'integration' );
$hash = md5( implode( '', array_map( 'strval', $this->args ) ) );
if ( isset( $this->cache[ $hash ] ) ) {
return $this->cache[ $hash ];
}
// Clear the data so we start fresh.
$this->data = [];
$this->setInitialData();
$this->setMultisiteData();
$this->setPostData();
$this->setDashboardData();
$this->setSearchStatisticsData();
$this->setSitemapsData();
$this->setSetupWizardData();
$this->setSearchAppearanceData();
$this->setSocialNetworksData();
$this->setSeoRevisionsData();
$this->setToolsOrSettingsData();
$this->setPageBuilderData();
$this->setWritingAssistantData();
$this->setBreadcrumbsData();
$this->setSeoAnalyzerData();
$this->cache[ $hash ] = $this->data;
return $this->cache[ $hash ];
}
/**
* Set Vue initial data.
*
* @since 4.4.9
*
* @return void
*/
private function setInitialData() {
$screen = aioseo()->helpers->getCurrentScreen();
$isStaticHomePage = 'page' === get_option( 'show_on_front' );
$staticHomePage = intval( get_option( 'page_on_front' ) );
$this->data = [
'page' => $this->args['page'],
'screen' => [
'base' => isset( $screen->base ) ? $screen->base : '',
'postType' => isset( $screen->post_type ) ? $screen->post_type : '',
'blockEditor' => isset( $screen->is_block_editor ) ? $screen->is_block_editor : false,
'new' => isset( $screen->action ) && 'add' === $screen->action
],
'internalOptions' => aioseo()->internalOptions->all(),
'options' => aioseo()->options->all(),
'dynamicOptions' => aioseo()->dynamicOptions->all(),
'deprecatedOptions' => aioseo()->internalOptions->getAllDeprecatedOptions( true ),
'settings' => aioseo()->settings->all(),
'additional_scripts' => apply_filters( 'aioseo_vue_additional_scripts_enabled', true ),
'tags' => aioseo()->tags->all( true ),
'nonce' => wp_create_nonce( 'wp_rest' ),
'urls' => [
'domain' => $this->getSiteDomain(),
'mainSiteUrl' => $this->getSiteUrl(),
'siteLogo' => aioseo()->helpers->getSiteLogoUrl(),
'home' => home_url(),
'restUrl' => aioseo()->helpers->getRestUrl(),
'editScreen' => admin_url( 'edit.php' ),
'publicPath' => aioseo()->core->assets->normalizeAssetsHost( plugin_dir_url( AIOSEO_FILE ) ),
'assetsPath' => aioseo()->core->assets->getAssetsPath(),
'generalSitemapUrl' => aioseo()->sitemap->helpers->getUrl( 'general' ),
'rssSitemapUrl' => aioseo()->sitemap->helpers->getUrl( 'rss' ),
'robotsTxtUrl' => $this->getSiteUrl() . '/robots.txt',
'upgradeUrl' => apply_filters( 'aioseo_upgrade_link', AIOSEO_MARKETING_URL ),
'staticHomePage' => 'page' === get_option( 'show_on_front' ) ? get_edit_post_link( get_option( 'page_on_front' ), 'url' ) : null,
'feeds' => [
'rdf' => get_bloginfo( 'rdf_url' ),
'rss' => get_bloginfo( 'rss_url' ),
'atom' => get_bloginfo( 'atom_url' ),
'global' => get_bloginfo( 'rss2_url' ),
'globalComments' => get_bloginfo( 'comments_rss2_url' ),
'staticBlogPage' => $this->getBlogPageId() ? trailingslashit( get_permalink( $this->getBlogPageId() ) ) . 'feed' : ''
],
'connect' => add_query_arg( [
'siteurl' => site_url(),
'homeurl' => home_url(),
'redirect' => rawurldecode( base64_encode( admin_url( 'index.php?page=aioseo-connect' ) ) )
], defined( 'AIOSEO_CONNECT_URL' ) ? AIOSEO_CONNECT_URL : 'https://connect.aioseo.com' ),
'aio' => [
'about' => is_network_admin() ? network_admin_url( 'admin.php?page=aioseo-about' ) : admin_url( 'admin.php?page=aioseo-about' ),
'dashboard' => admin_url( 'admin.php?page=aioseo' ),
'featureManager' => admin_url( 'admin.php?page=aioseo-feature-manager' ),
'linkAssistant' => admin_url( 'admin.php?page=aioseo-link-assistant' ),
'localSeo' => admin_url( 'admin.php?page=aioseo-local-seo' ),
'monsterinsights' => admin_url( 'admin.php?page=aioseo-monsterinsights' ),
'redirects' => admin_url( 'admin.php?page=aioseo-redirects' ),
'searchAppearance' => admin_url( 'admin.php?page=aioseo-search-appearance' ),
'searchStatistics' => admin_url( 'admin.php?page=aioseo-search-statistics' ),
'seoAnalysis' => admin_url( 'admin.php?page=aioseo-seo-analysis' ),
'settings' => admin_url( 'admin.php?page=aioseo-settings' ),
'sitemaps' => admin_url( 'admin.php?page=aioseo-sitemaps' ),
'socialNetworks' => admin_url( 'admin.php?page=aioseo-social-networks' ),
'tools' => admin_url( 'admin.php?page=aioseo-tools' ),
'wizard' => admin_url( 'index.php?page=aioseo-setup-wizard' ),
'networkSettings' => is_network_admin() ? network_admin_url( 'admin.php?page=aioseo-settings' ) : '',
'seoRevisions' => admin_url( 'admin.php?page=aioseo-seo-revisions' ),
],
'admin' => [
'widgets' => admin_url( 'widgets.php' ),
'optionsReading' => admin_url( 'options-reading.php' ),
'scheduledActions' => admin_url( '/tools.php?page=action-scheduler&status=pending&s=aioseo' ),
'generalSettings' => admin_url( 'options-general.php' )
],
'truSeoWorker' => aioseo()->core->assets->jsUrl( 'src/app/tru-seo/analyzer/main.js' )
],
'backups' => [],
'importers' => [],
'data' => [
'server' => aioseo()->helpers->getServerName(),
'robots' => [
'defaultRules' => [],
'hasPhysicalRobots' => null,
'rewriteExists' => null,
'sitemapUrls' => []
],
'status' => [],
'htaccess' => '',
'isMultisite' => is_multisite(),
'isNetworkAdmin' => is_network_admin(),
'currentBlogId' => get_current_blog_id(),
'mainSite' => is_main_site(),
'subdomain' => $this->isSubdomain(),
'isBBPressActive' => class_exists( 'bbPress' ),
'isClassicEditorActive' => $this->isClassicEditorActive(),
'isWooCommerceActive' => $this->isWooCommerceActive(),
'staticHomePage' => $isStaticHomePage ? $staticHomePage : false,
'staticBlogPage' => $this->getBlogPageId(),
'staticBlogPageTitle' => get_the_title( $this->getBlogPageId() ),
'isDev' => $this->isDev(),
'isLocal' => $this->isLocalUrl( site_url() ),
'isSsl' => is_ssl(),
'hasUrlTrailingSlash' => '/' === user_trailingslashit( '' ),
'permalinkStructure' => get_option( 'permalink_structure' ),
'usingPermalinks' => aioseo()->helpers->usingPermalinks(),
'dateFormat' => get_option( 'date_format' ),
'timeFormat' => get_option( 'time_format' ),
'siteName' => aioseo()->helpers->getWebsiteName(),
'adminEmail' => get_bloginfo( 'admin_email' ),
'blocks' => [
'toc' => [
'hashPrefix' => apply_filters( 'aioseo_toc_hash_prefix', 'aioseo-' )
]
]
],
'user' => [
'canManage' => aioseo()->access->canManage(),
'capabilities' => aioseo()->access->getAllCapabilities(),
'customRoles' => $this->getCustomRoles(),
'data' => wp_get_current_user(),
'locale' => function_exists( 'get_user_locale' ) ? get_user_locale() : get_locale(),
'roles' => $this->getUserRoles(),
'unfilteredHtml' => current_user_can( 'unfiltered_html' )
],
'plugins' => $this->getPluginData(),
'postData' => [
'postTypes' => array_values( $this->getPublicPostTypes( false, false, true ) ),
'taxonomies' => array_values( $this->getPublicTaxonomies( false, true ) ),
'archives' => array_values( $this->getPublicPostTypes( false, true, true ) ),
'postStatuses' => array_values( $this->getPublicPostStatuses() )
],
'notifications' => array_merge( Models\Notification::getNotifications( true ), [
'force' => $this->showNotificationsDrawer()
] ),
'addons' => aioseo()->addons->getAddons(),
'features' => aioseo()->features->getFeatures(),
'version' => AIOSEO_VERSION,
'wpVersion' => get_bloginfo( 'version' ),
'phpVersion' => PHP_VERSION,
'helpPanel' => aioseo()->help->getDocs(),
'scheduledActions' => [
'sitemaps' => []
],
'integration' => $this->args['integration'],
'theme' => [
'features' => aioseo()->helpers->getThemeFeatures()
]
];
}
/**
* Set Vue multisite data.
*
* @since 4.4.9
*
* @return void
*/
private function setMultisiteData() {
if ( ! is_multisite() ) {
return;
}
$this->data['internalNetworkOptions'] = aioseo()->internalNetworkOptions->all();
$this->data['networkOptions'] = aioseo()->networkOptions->all();
}
/**
* Set Vue post data.
*
* @since 4.4.9
*
* @return void
*/
private function setPostData() {
if ( 'post' !== $this->args['page'] ) {
return;
}
$postId = $this->args['staticPostId'] ?: get_the_ID();
$postTypeObj = get_post_type_object( get_post_type( $postId ) );
$post = Models\Post::getPost( $postId );
$wpPost = get_post( $postId );
$staticHomePage = intval( get_option( 'page_on_front' ) );
$this->data['currentPost'] = [
'context' => 'post',
'tags' => aioseo()->tags->getDefaultPostTags( $postId ),
'id' => $postId,
'priority' => isset( $post->priority ) && null !== $post->priority ? (float) $post->priority : 'default',
'frequency' => ! empty( $post->frequency ) ? $post->frequency : 'default',
'permalink' => get_permalink( $postId ),
'editlink' => aioseo()->helpers->getPostEditLink( $postId ),
'title' => ! empty( $post->title ) ? $post->title : aioseo()->meta->title->getPostTypeTitle( $postTypeObj->name ),
'description' => ! empty( $post->description ) ? $post->description : aioseo()->meta->description->getPostTypeDescription( $postTypeObj->name ),
'descriptionIncludeCustomFields' => apply_filters( 'aioseo_description_include_custom_fields', true, $post ),
'keywords' => ! empty( $post->keywords ) ? $post->keywords : [],
'keyphrases' => Models\Post::getKeyphrasesDefaults( $post->keyphrases ),
'page_analysis' => Models\Post::getPageAnalysisDefaults( $post->page_analysis ),
'loading' => [
'focus' => false,
'additional' => [],
],
'type' => $postTypeObj->labels->singular_name,
'postType' => 'type' === $postTypeObj->name ? '_aioseo_type' : $postTypeObj->name,
'postStatus' => get_post_status( $postId ),
'postAuthor' => (int) $wpPost->post_author,
'isSpecialPage' => $this->isSpecialPage( $postId ),
'isTruSeoEligible' => $this->isTruSeoEligible( $postId ),
'isStaticPostsPage' => aioseo()->helpers->isStaticPostsPage(),
'isHomePage' => $postId === $staticHomePage,
'isWooCommercePageWithoutSchema' => $this->isWooCommercePageWithoutSchema( $postId ),
'seo_score' => (int) $post->seo_score,
'pillar_content' => ( (int) $post->pillar_content ) === 0 ? false : true,
'canonicalUrl' => $post->canonical_url,
'default' => ( (int) $post->robots_default ) === 0 ? false : true,
'noindex' => ( (int) $post->robots_noindex ) === 0 ? false : true,
'noarchive' => ( (int) $post->robots_noarchive ) === 0 ? false : true,
'nosnippet' => ( (int) $post->robots_nosnippet ) === 0 ? false : true,
'nofollow' => ( (int) $post->robots_nofollow ) === 0 ? false : true,
'noimageindex' => ( (int) $post->robots_noimageindex ) === 0 ? false : true,
'noodp' => ( (int) $post->robots_noodp ) === 0 ? false : true,
'notranslate' => ( (int) $post->robots_notranslate ) === 0 ? false : true,
'maxSnippet' => null === $post->robots_max_snippet ? -1 : (int) $post->robots_max_snippet,
'maxVideoPreview' => null === $post->robots_max_videopreview ? -1 : (int) $post->robots_max_videopreview,
'maxImagePreview' => $post->robots_max_imagepreview,
'modalOpen' => false,
'generalMobilePrev' => false,
'og_object_type' => ! empty( $post->og_object_type ) ? $post->og_object_type : 'default',
'og_title' => $post->og_title,
'og_description' => $post->og_description,
'og_image_custom_url' => $post->og_image_custom_url,
'og_image_custom_fields' => $post->og_image_custom_fields,
'og_image_type' => ! empty( $post->og_image_type ) ? $post->og_image_type : 'default',
'og_video' => ! empty( $post->og_video ) ? $post->og_video : '',
'og_article_section' => ! empty( $post->og_article_section ) ? $post->og_article_section : '',
'og_article_tags' => ! empty( $post->og_article_tags ) ? $post->og_article_tags : [],
'twitter_use_og' => ( (int) $post->twitter_use_og ) === 0 ? false : true,
'twitter_card' => $post->twitter_card,
'twitter_image_custom_url' => $post->twitter_image_custom_url,
'twitter_image_custom_fields' => $post->twitter_image_custom_fields,
'twitter_image_type' => $post->twitter_image_type,
'twitter_title' => $post->twitter_title,
'twitter_description' => $post->twitter_description,
'schema' => Models\Post::getDefaultSchemaOptions( $post->schema, aioseo()->helpers->getPost( $postId ) ),
'metaDefaults' => [
'title' => aioseo()->meta->title->getPostTypeTitle( $postTypeObj->name ),
'description' => aioseo()->meta->description->getPostTypeDescription( $postTypeObj->name )
],
'linkAssistant' => [
'modalOpen' => false
],
'limit_modified_date' => ( (int) $post->limit_modified_date ) === 0 ? false : true,
'redirects' => [
'modalOpen' => false
],
'options' => $post->options,
'maxAdditionalKeyphrases' => 0,
];
if ( empty( $this->args['integration'] ) ) {
$this->data['integration'] = aioseo()->helpers->getPostPageBuilderName( $postId );
}
if ( ! $post->exists() ) {
$oldPostMeta = aioseo()->migration->meta->getMigratedPostMeta( $postId );
foreach ( $oldPostMeta as $k => $v ) {
if ( preg_match( '#robots_.*#', (string) $k ) ) {
$oldPostMeta[ preg_replace( '#robots_#', '', (string) $k ) ] = $v;
continue;
}
if ( 'canonical_url' === $k ) {
$oldPostMeta['canonicalUrl'] = $v;
}
}
$this->data['currentPost'] = array_merge( $this->data['currentPost'], $oldPostMeta );
}
}
/**
* Set Vue dashboard data.
*
* @since 4.4.9
*
* @return void
*/
private function setDashboardData() {
if ( 'dashboard' !== $this->args['page'] ) {
return;
}
$this->data['setupWizard']['isCompleted'] = aioseo()->standalone->setupWizard->isCompleted();
$this->data['seoOverview'] = aioseo()->postSettings->getPostTypesOverview();
$this->data['importers'] = aioseo()->importExport->plugins();
}
/**
* Set Vue search statistics data.
*
* @since 4.4.9
*
* @return void
*/
private function setSearchStatisticsData() {
$this->data['searchStatistics'] = [
'isConnected' => aioseo()->searchStatistics->api->auth->isConnected(),
'sitemapsWithErrors' => aioseo()->searchStatistics->sitemap->getSitemapsWithErrors(),
];
if ( 'post' === $this->args['page'] ) {
$this->data['keywordRankTracker'] = aioseo()->searchStatistics->keywordRankTracker->getVueDataEdit();
}
if ( 'search-statistics' === $this->args['page'] ) {
$this->data['seoOverview'] = aioseo()->postSettings->getPostTypesOverview();
$this->data['searchStatistics'] = array_merge( $this->data['searchStatistics'], aioseo()->searchStatistics->getVueData() );
$this->data['keywordRankTracker'] = aioseo()->searchStatistics->keywordRankTracker->getVueData();
$this->data['indexStatus'] = aioseo()->searchStatistics->indexStatus->getVueData();
}
}
/**
* Set Vue sitemaps data.
*
* @since 4.4.9
*
* @return void
*/
private function setSitemapsData() {
if ( 'sitemaps' !== $this->args['page'] ) {
return;
}
$this->data['data']['sitemapUrls'] = aioseo()->sitemap->helpers->getSitemapUrls();
try {
if ( as_next_scheduled_action( 'aioseo_static_sitemap_regeneration' ) ) {
$this->data['scheduledActions']['sitemap'][] = 'staticSitemapRegeneration';
}
} catch ( \Exception $e ) {
// Do nothing.
}
}
/**
* Set Vue setup wizard data.
*
* @since 4.4.9
*
* @return void
*/
private function setSetupWizardData() {
if ( 'setup-wizard' !== $this->args['page'] ) {
return;
}
$isStaticHomePage = 'page' === get_option( 'show_on_front' );
$staticHomePage = intval( get_option( 'page_on_front' ) );
$this->data['users'] = $this->getSiteUsers( [ 'administrator', 'editor', 'author' ] );
$this->data['importers'] = aioseo()->importExport->plugins();
$this->data['data'] += [
'staticHomePageTitle' => $isStaticHomePage ? aioseo()->meta->title->getTitle( $staticHomePage ) : '',
'staticHomePageDescription' => $isStaticHomePage ? aioseo()->meta->description->getDescription( $staticHomePage ) : '',
];
}
/**
* Set Vue search appearance data.
*
* @since 4.4.9
*
* @return void
*/
private function setSearchAppearanceData() {
if ( 'search-appearance' !== $this->args['page'] ) {
return;
}
$isStaticHomePage = 'page' === get_option( 'show_on_front' );
$staticHomePage = intval( get_option( 'page_on_front' ) );
$this->data['users'] = $this->getSiteUsers( [ 'administrator', 'editor', 'author' ] );
$this->data['data'] += [
'staticHomePageTitle' => $isStaticHomePage ? aioseo()->meta->title->getTitle( $staticHomePage ) : '',
'staticHomePageDescription' => $isStaticHomePage ? aioseo()->meta->description->getDescription( $staticHomePage ) : '',
];
}
/**
* Set Vue social networks data.
*
* @since 4.4.9
*
* @return void
*/
private function setSocialNetworksData() {
if ( 'social-networks' !== $this->args['page'] ) {
return;
}
$isStaticHomePage = 'page' === get_option( 'show_on_front' );
$staticHomePage = intval( get_option( 'page_on_front' ) );
$this->data['data'] += [
'staticHomePageOgTitle' => $isStaticHomePage ? aioseo()->social->facebook->getTitle( $staticHomePage ) : '',
'staticHomePageOgDescription' => $isStaticHomePage ? aioseo()->social->facebook->getDescription( $staticHomePage ) : '',
'staticHomePageTwitterTitle' => $isStaticHomePage ? aioseo()->social->twitter->getTitle( $staticHomePage ) : '',
'staticHomePageTwitterDescription' => $isStaticHomePage ? aioseo()->social->twitter->getDescription( $staticHomePage ) : '',
];
}
/**
* Set Vue seo revisions data.
*
* @since 4.4.9
*
* @return void
*/
private function setSeoRevisionsData() {
if ( 'post' === $this->args['page'] ) {
$this->data['seoRevisions'] = aioseo()->seoRevisions->getVueDataEdit( $this->args['staticPostId'] ?? null );
}
if ( 'seo-revisions' === $this->args['page'] ) {
$this->data['seoRevisions'] = aioseo()->seoRevisions->getVueDataCompare();
}
}
/**
* Set Vue tools or settings data.
*
* @since 4.4.9
*
* @return void
*/
private function setToolsOrSettingsData() {
if (
'tools' !== $this->args['page'] &&
'settings' !== $this->args['page']
) {
return;
}
if ( 'tools' === $this->args['page'] ) {
$this->data['backups'] = array_reverse( aioseo()->backup->all() );
$this->data['importers'] = aioseo()->importExport->plugins();
$this->data['data']['robots'] = [
'defaultRules' => $this->args['page'] ? aioseo()->robotsTxt->extractRules( aioseo()->robotsTxt->getDefaultRobotsTxtContent() ) : [],
'hasPhysicalRobots' => aioseo()->robotsTxt->hasPhysicalRobotsTxt(),
'rewriteExists' => aioseo()->robotsTxt->rewriteRulesExist(),
'sitemapUrls' => array_merge( aioseo()->sitemap->helpers->getSitemapUrlsPrefixed(), aioseo()->sitemap->helpers->extractSitemapUrlsFromRobotsTxt() )
];
$this->data['data']['status'] = Tools\SystemStatus::getSystemStatusInfo();
$this->data['data']['htaccess'] = aioseo()->htaccess->getContents();
$this->data['data']['v3Options'] = ! empty( get_option( 'aioseop_options' ) );
$this->data['integrations']['wpcode'] = [
'snippets' => WpCodeIntegration::loadWpCodeSnippets(),
'pluginInstalled' => WpCodeIntegration::isPluginInstalled(),
'pluginActive' => WpCodeIntegration::isPluginActive(),
'pluginNeedsUpdate' => WpCodeIntegration::pluginNeedsUpdate()
];
}
if ( 'settings' === $this->args['page'] ) {
$this->data['breadcrumbs']['defaultTemplate'] = aioseo()->helpers->encodeOutputHtml( aioseo()->breadcrumbs->frontend->getDefaultTemplate() );
}
if (
is_multisite() &&
is_network_admin()
) {
$this->data['data']['network'] = [
'sites' => aioseo()->helpers->getSites( aioseo()->settings->tablePagination['networkDomains'] ),
'backups' => []
];
}
}
/**
* Set Vue Page Builder data.
*
* @since 4.4.9
* @version 4.5.2 Renamed.
*
* @return void
*/
private function setPageBuilderData() {
if ( empty( $this->args['integration'] ) ) {
return;
}
if ( 'divi' === $this->args['integration'] ) {
// This needs to be dropped in order to prevent JavaScript errors in Divi's visual builder.
// Some of the data from the site analysis can contain HTML tags, e.g. the search preview, and somehow that causes JSON.parse to fail on our localized Vue data.
unset( $this->data['internalOptions']['internal']['siteAnalysis'] );
}
}
/**
* Returns Jed-formatted localization data. Added for backwards-compatibility.
*
* @since 4.0.0
*
* @param string $domain Translation domain.
* @return array The information of the locale.
*/
public function getJedLocaleData( $domain ) {
$translations = get_translations_for_domain( $domain );
$locale = [
'' => [
'domain' => $domain,
'lang' => is_admin() && function_exists( 'get_user_locale' ) ? get_user_locale() : get_locale()
],
];
if ( ! empty( $translations->headers['Plural-Forms'] ) ) {
$locale['']['plural_forms'] = $translations->headers['Plural-Forms'];
}
foreach ( $translations->entries as $entry ) {
if ( empty( $entry->translations ) || ! is_array( $entry->translations ) ) {
continue;
}
foreach ( $entry->translations as $translation ) {
// If any of the translated strings contains an HTML line break, we need to ignore it. Otherwise, logging into the admin breaks.
if ( preg_match( '/<br[\s\/\\\\]*>/', (string) $translation ) ) {
continue 2;
}
}
// Set the translation data using the singular string as the index. This is how Jed expects it, even for plural strings.
$locale[ $entry->singular ] = $entry->translations;
}
return $locale;
}
/**
* Set Vue writing assistant data.
*
* @since 4.7.4
*
* @return void
*/
private function setWritingAssistantData() {
// Settings page or not a post screen.
if (
'settings' !== $this->args['page'] &&
! aioseo()->helpers->isScreenBase( 'post' )
) {
return;
}
$this->data['writingAssistantSettings'] = aioseo()->writingAssistant->helpers->getSettingsVueData();
}
/**
* Whether the notifications drawer should be shown or not.
*
* @since 4.4.9
*
* @return bool True if it should be shown, false otherwise.
*/
private function showNotificationsDrawer() {
static $showNotificationsDrawer = null;
if ( null === $showNotificationsDrawer ) {
$showNotificationsDrawer = (bool) aioseo()->core->cache->get( 'show_notifications_drawer' );
// If this is set to true, let's disable it now, so it doesn't pop up again.
if ( $showNotificationsDrawer ) {
aioseo()->core->cache->delete( 'show_notifications_drawer' );
}
}
return $showNotificationsDrawer;
}
/**
* Set Vue breadcrumbs data.
*
* @since 4.8.3
*
* @return void
*/
private function setBreadcrumbsData() {
$isPostOrTermPage = aioseo()->helpers->isScreenBase( 'post' ) || aioseo()->helpers->isScreenBase( 'term' );
$isCurrentPageUsingPageBuilder = 'post' === $this->args['page'] && ! empty( $this->args['integration'] );
$isSettingsPage = ! empty( $this->args['page'] ) && 'settings' === $this->args['page'];
if ( ! $isSettingsPage && ! $isCurrentPageUsingPageBuilder && ! $isPostOrTermPage ) {
return;
}
$this->data['breadcrumbs']['defaultTemplate'] = aioseo()->helpers->encodeOutputHtml( aioseo()->breadcrumbs->frontend->getDefaultTemplate() );
}
/**
* Set Vue SEO Analyzer data.
*
* @since 4.8.3
*
* @return void
*/
private function setSeoAnalyzerData() {
if ( 'seo-analysis' !== $this->args['page'] ) {
return;
}
$this->data['analyzer']['homeResults'] = Models\SeoAnalyzerResult::getResults();
$this->data['analyzer']['competitors'] = Models\SeoAnalyzerResult::getCompetitorsResults();
}
} Helpers/Numbers.php 0000666 00000001570 15114625461 0010306 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Numbers trait.
*
* @since 4.7.2
*/
trait Numbers {
/**
* Formats a number to a compact format.
*
* @since 4.7.2
*
* @param float|int|string $number The number to format.
* @param int $decimals The number of decimal places to include.
* @return string Formatted number in string format.
*/
public function compactNumber( $number, $decimals = 1 ) {
$suffixes = [ '', 'K', 'M', 'B', 'T', 'q', 'Q' ];
$suffixIndex = 0;
while ( abs( $number ) >= 1000 && $suffixIndex < count( $suffixes ) - 1 ) {
$suffixIndex++;
$number /= 1000;
}
// Remove trailing zeros.
return preg_replace( '/\D0+$/', '', (string) number_format_i18n( $number, $decimals ) ) . $suffixes[ $suffixIndex ];
}
} Helpers/Wp.php 0000666 00000066265 15114625461 0007275 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use AIOSEO\Plugin\Common\Utils;
/**
* Contains all WP related helper methods.
*
* @since 4.1.4
*/
trait Wp {
/**
* Whether or not we have a local connection.
*
* @since 4.0.0
*
* @var bool
*/
private static $connection = false;
/**
* Returns user roles in the current WP install.
*
* @since 4.0.0
*
* @return array An array of user roles.
*/
public function getUserRoles() {
global $wp_roles; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
$wpRoles = $wp_roles; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
if ( ! is_object( $wpRoles ) ) {
// Don't assign this to the global because otherwise WordPress won't override it.
$wpRoles = new \WP_Roles();
}
$roleNames = $wpRoles->get_names();
asort( $roleNames );
return $roleNames;
}
/**
* Returns the custom roles in the current WP install.
*
* @since 4.1.3
*
* @return array An array of custom roles.
*/
public function getCustomRoles() {
$allRoles = $this->getUserRoles();
$toSkip = array_merge(
// Default WordPress roles.
[ 'superadmin', 'administrator', 'editor', 'author', 'contributor' ],
// Default AIOSEO roles.
[ 'aioseo_manager', 'aioseo_editor' ],
// Filterable roles.
apply_filters( 'aioseo_access_control_excluded_roles', array_merge( [
'subscriber'
], aioseo()->helpers->isWooCommerceActive() ? [ 'customer' ] : [] ) )
);
// Remove empty entries.
$toSkip = array_filter( $toSkip );
$customRoles = [];
foreach ( $allRoles as $roleName => $role ) {
// Skip specific roles.
if ( in_array( $roleName, $toSkip, true ) ) {
continue;
}
$customRoles[ $roleName ] = $role;
}
return $customRoles;
}
/**
* Returns an array of plugins with the active status.
*
* @since 4.0.0
*
* @return array An array of plugins with active status.
*/
public function getPluginData() {
$pluginUpgrader = new Utils\PluginUpgraderSilentAjax();
$installedPlugins = array_keys( get_plugins() );
$plugins = [];
foreach ( $pluginUpgrader->pluginSlugs as $key => $slug ) {
$adminUrl = admin_url( $pluginUpgrader->pluginAdminUrls[ $key ] );
$networkAdminUrl = null;
if (
is_multisite() &&
is_network_admin() &&
! empty( $pluginUpgrader->hasNetworkAdmin[ $key ] )
) {
$networkAdminUrl = network_admin_url( $pluginUpgrader->hasNetworkAdmin[ $key ] );
if ( aioseo()->helpers->isPluginNetworkActivated( $pluginUpgrader->pluginSlugs[ $key ] ) ) {
$adminUrl = $networkAdminUrl;
}
}
$plugins[ $key ] = [
'basename' => $slug,
'installed' => in_array( $slug, $installedPlugins, true ),
'activated' => is_plugin_active( $slug ),
'adminUrl' => $adminUrl,
'networkAdminUrl' => $networkAdminUrl,
'canInstall' => aioseo()->addons->canInstall(),
'canActivate' => aioseo()->addons->canActivate(),
'canUpdate' => aioseo()->addons->canUpdate(),
'wpLink' => ! empty( $pluginUpgrader->wpPluginLinks[ $key ] ) ? $pluginUpgrader->wpPluginLinks[ $key ] : null
];
}
return $plugins;
}
/**
* Returns all registered Post Statuses.
*
* @since 4.1.6
*
* @param boolean $statusesOnly Whether or not to only return statuses.
* @return array An array of post statuses.
*/
public function getPublicPostStatuses( $statusesOnly = false ) {
$allStatuses = get_post_stati( [ 'show_in_admin_all_list' => true ], 'objects' );
$postStatuses = [];
foreach ( $allStatuses as $status => $data ) {
if (
! $data->public &&
! $data->protected &&
! $data->private
) {
continue;
}
if ( $statusesOnly ) {
$postStatuses[] = $status;
continue;
}
$postStatuses[] = [
'label' => $data->label,
'status' => $status
];
}
return $postStatuses;
}
/**
* Returns a list of public post types objects or names.
*
* @since 4.0.0
*
* @param bool $namesOnly Whether only the names should be returned.
* @param bool $hasArchivesOnly Whether to only include post types which have archives.
* @param bool $rewriteType Whether to rewrite the type slugs.
* @param array $args Additional arguments.
* @return array List of public post types.
*/
public function getPublicPostTypes( $namesOnly = false, $hasArchivesOnly = false, $rewriteType = false, $args = [] ) {
$args = array_merge( [
'include' => [] // Post types to include.
], $args );
$postTypes = [];
$postTypeObjects = get_post_types( [], 'objects' );
foreach ( $postTypeObjects as $postTypeObject ) {
if ( ! is_post_type_viewable( $postTypeObject ) ) {
continue;
}
$postTypeArray = $this->getPostType( $postTypeObject, $namesOnly, $hasArchivesOnly, $rewriteType );
if ( ! empty( $postTypeArray ) ) {
$postTypes[] = $postTypeArray;
}
}
if ( isset( aioseo()->standalone->buddyPress ) ) {
aioseo()->standalone->buddyPress->maybeAddPostTypes( $postTypes, $namesOnly, $hasArchivesOnly, $args );
}
return apply_filters( 'aioseo_public_post_types', $postTypes, $namesOnly, $hasArchivesOnly, $args );
}
/**
* Returns the data for the given post type.
*
* @since 4.2.2
*
* @param \WP_Post_Type $postTypeObject The post type object.
* @param bool $namesOnly Whether only the names should be returned.
* @param bool $hasArchivesOnly Whether to only include post types which have archives.
* @param bool $rewriteType Whether to rewrite the type slugs.
* @return mixed Data for the post type.
*/
public function getPostType( $postTypeObject, $namesOnly = false, $hasArchivesOnly = false, $rewriteType = false ) {
if ( empty( $postTypeObject->label ) ) {
return $namesOnly ? null : [];
}
// We don't want to include archives for the WooCommerce shop page.
if (
$hasArchivesOnly &&
(
! $postTypeObject->has_archive ||
( 'product' === $postTypeObject->name && $this->isWooCommerceActive() )
)
) {
return $namesOnly ? null : [];
}
if ( $namesOnly ) {
return $postTypeObject->name;
}
if ( 'attachment' === $postTypeObject->name ) {
// We have to check if the 'init' action has been fired to avoid a PHP notice
// in WP 6.7+ due to loading translations too early.
if ( did_action( 'init' ) ) {
$postTypeObject->label = __( 'Attachments', 'all-in-one-seo-pack' );
}
}
if ( 'product' === $postTypeObject->name && $this->isWooCommerceActive() ) {
$postTypeObject->menu_icon = 'dashicons-products';
}
$name = $postTypeObject->name;
if ( 'type' === $postTypeObject->name && $rewriteType ) {
$name = '_aioseo_type';
}
return [
'name' => $name,
'label' => ucwords( $postTypeObject->label ),
'singular' => ucwords( $postTypeObject->labels->singular_name ),
'icon' => $postTypeObject->menu_icon,
'hasArchive' => $postTypeObject->has_archive,
'hierarchical' => $postTypeObject->hierarchical,
'taxonomies' => get_object_taxonomies( $name ),
'slug' => isset( $postTypeObject->rewrite['slug'] ) ? $postTypeObject->rewrite['slug'] : $name,
'supports' => get_all_post_type_supports( $name )
];
}
/**
* Returns a list of public taxonomies objects or names.
*
* @since 4.0.0
*
* @param bool $namesOnly Whether only the names should be returned.
* @param bool $rewriteType Whether to rewrite the type slugs.
* @return array List of public taxonomies.
*/
public function getPublicTaxonomies( $namesOnly = false, $rewriteType = false ) {
$taxonomies = [];
if ( count( $taxonomies ) ) {
return $taxonomies;
}
$taxObjects = get_taxonomies( [], 'objects' );
foreach ( $taxObjects as $taxObject ) {
if (
empty( $taxObject->label ) ||
! is_taxonomy_viewable( $taxObject ) ||
aioseo()->helpers->isWooCommerceProductAttribute( $taxObject->name )
) {
continue;
}
if ( in_array( $taxObject->name, [
'product_shipping_class',
'post_format'
], true ) ) {
continue;
}
if ( $namesOnly ) {
$taxonomies[] = $taxObject->name;
continue;
}
$name = $taxObject->name;
if ( 'type' === $taxObject->name && $rewriteType ) {
$name = '_aioseo_type';
}
global $wp_taxonomies; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
$taxonomyPostTypes = ! empty( $wp_taxonomies[ $name ] ) // phpcs:ignore Squiz.NamingConventions.ValidVariableName
? $wp_taxonomies[ $name ]->object_type // phpcs:ignore Squiz.NamingConventions.ValidVariableName
: [];
$taxonomies[] = [
'name' => $name,
'label' => ucwords( $taxObject->label ),
'singular' => ucwords( $taxObject->labels->singular_name ),
'icon' => strpos( $taxObject->label, 'categor' ) !== false ? 'dashicons-category' : 'dashicons-tag',
'hierarchical' => $taxObject->hierarchical,
'slug' => isset( $taxObject->rewrite['slug'] ) ? $taxObject->rewrite['slug'] : '',
'primaryTermSupport' => (bool) $taxObject->hierarchical,
'restBase' => ( $taxObject->rest_base ) ? $taxObject->rest_base : $taxObject->name,
'postTypes' => $taxonomyPostTypes
];
}
if ( $this->isWooCommerceActive() ) {
// We inject a fake one for WooCommerce product attributes so that we can show a single set of settings
// instead of having to duplicate them for each attribute.
if ( $namesOnly ) {
$taxonomies[] = 'product_attributes';
} else {
$taxonomies[] = [
'name' => 'product_attributes',
'label' => __( 'Product Attributes', 'all-in-one-seo-pack' ),
'singular' => __( 'Product Attribute', 'all-in-one-seo-pack' ),
'icon' => 'dashicons-products',
'hierarchical' => true,
'slug' => 'product_attributes',
'primaryTermSupport' => true,
'restBase' => 'product_attributes_class',
'postTypes' => [ 'product' ]
];
}
}
return apply_filters( 'aioseo_public_taxonomies', $taxonomies, $namesOnly );
}
/**
* Retrieve a list of users that match passed in roles.
*
* @since 4.0.0
*
* @return array An array of user data.
*/
public function getSiteUsers( $roles ) {
static $users = [];
if ( ! empty( $users ) ) {
return $users;
}
$rolesWhere = [];
foreach ( $roles as $role ) {
$rolesWhere[] = '(um.meta_key = \'' . aioseo()->core->db->db->prefix . 'capabilities\' AND um.meta_value LIKE \'%\"' . $role . '\"%\')';
}
// We get the table name from WPDB since multisites share the same table.
$usersTableName = aioseo()->core->db->db->users;
$usermetaTableName = aioseo()->core->db->db->usermeta;
$dbUsers = aioseo()->core->db->start( "$usersTableName as u", true )
->select( 'u.ID, u.display_name, u.user_nicename, u.user_email' )
->join( "$usermetaTableName as um", 'u.ID = um.user_id', '', true )
->whereRaw( '(' . implode( ' OR ', $rolesWhere ) . ')' )
->orderBy( 'u.user_nicename' )
->run()
->result();
foreach ( $dbUsers as $dbUser ) {
$users[] = [
'id' => (int) $dbUser->ID,
'displayName' => $dbUser->display_name,
'niceName' => $dbUser->user_nicename,
'email' => $dbUser->user_email,
'gravatar' => get_avatar_url( $dbUser->user_email )
];
}
return $users;
}
/**
* Returns the ID of the site logo if it exists.
*
* @since 4.0.0
*
* @return int
*/
public function getSiteLogoId() {
if ( ! get_theme_support( 'custom-logo' ) ) {
return false;
}
return get_theme_mod( 'custom_logo' );
}
/**
* Returns the URL of the site logo if it exists.
*
* @since 4.0.0
*
* @return string
*/
public function getSiteLogoUrl() {
$id = $this->getSiteLogoId();
if ( ! $id ) {
return false;
}
$image = wp_get_attachment_image_src( $id, 'full' );
if ( empty( $image ) ) {
return false;
}
return $image[0];
}
/**
* Returns noindexed post types.
*
* @since 4.0.0
*
* @return array A list of noindexed post types.
*/
public function getNoindexedPostTypes() {
return $this->getNoindexedObjects( 'postTypes' );
}
/**
* Checks whether a given post type is noindexed.
*
* @since 4.0.0
*
* @param string $postType The post type.
* @return bool Whether the post type is noindexed.
*/
public function isPostTypeNoindexed( $postType ) {
$noindexedPostTypes = $this->getNoindexedPostTypes();
return in_array( $postType, $noindexedPostTypes, true );
}
/**
* Checks whether a given post type is public.
*
* @since 4.2.2
*
* @param string $postType The post type.
* @return bool Whether the post type is public.
*/
public function isPostTypePublic( $postType ) {
$publicPostTypes = $this->getPublicPostTypes( true );
return in_array( $postType, $publicPostTypes, true );
}
/**
* Returns noindexed taxonomies.
*
* @since 4.0.0
*
* @return array A list of noindexed taxonomies.
*/
public function getNoindexedTaxonomies() {
return $this->getNoindexedObjects( 'taxonomies' );
}
/**
* Checks whether a given post type is noindexed.
*
* @since 4.0.0
*
* @param string $taxonomy The taxonomy.
* @return bool Whether the taxonomy is noindexed.
*/
public function isTaxonomyNoindexed( $taxonomy ) {
$noindexedTaxonomies = $this->getNoindexedTaxonomies();
return in_array( $taxonomy, $noindexedTaxonomies, true );
}
/**
* Checks whether a given taxonomy is public.
*
* @since 4.2.2
*
* @param string $taxonomy The taxonomy.
* @return bool Whether the taxonomy is public.
*/
public function isTaxonomyPublic( $taxonomy ) {
$publicTaxonomies = $this->getPublicTaxonomies( true );
return in_array( $taxonomy, $publicTaxonomies, true );
}
/**
* Returns noindexed object types of a given parent type.
*
* @since 4.0.0
*
* @param string $type The parent object type ("postTypes", "archives", "taxonomies").
* @return array A list of noindexed objects types.
*/
public function getNoindexedObjects( $type ) {
$noindexed = [];
foreach ( aioseo()->dynamicOptions->searchAppearance->$type->all() as $name => $object ) {
if (
! $object['show'] ||
( $object['advanced']['robotsMeta'] && ! $object['advanced']['robotsMeta']['default'] && $object['advanced']['robotsMeta']['noindex'] )
) {
$noindexed[] = $name;
}
}
return $noindexed;
}
/**
* Returns all categories for a post.
*
* @since 4.1.4
*
* @param int $postId The post ID.
* @return array The category names.
*/
public function getAllCategories( $postId = 0 ) {
$names = [];
$categories = get_the_category( $postId );
if ( $categories && count( $categories ) ) {
foreach ( $categories as $category ) {
$names[] = aioseo()->helpers->internationalize( $category->name );
}
}
return $names;
}
/**
* Returns all tags for a post.
*
* @since 4.1.4
*
* @param int $postId The post ID.
* @return array $names The tag names.
*/
public function getAllTags( $postId = 0 ) {
$names = [];
$tags = get_the_tags( $postId );
if ( ! empty( $tags ) && ! is_wp_error( $tags ) ) {
foreach ( $tags as $tag ) {
if ( ! empty( $tag->name ) ) {
$names[] = aioseo()->helpers->internationalize( $tag->name );
}
}
}
return $names;
}
/**
* Loads the translations for a given domain.
*
* @since 4.1.4
*
* @return void
*/
public function loadTextDomain( $domain ) {
if ( ! is_user_logged_in() ) {
return;
}
// Unload the domain in case WordPress has enqueued the translations for the site language instead of profile language.
// Reloading the text domain will otherwise not override the existing loaded translations.
unload_textdomain( $domain );
$mofile = $domain . '-' . get_user_locale() . '.mo';
load_textdomain( $domain, WP_LANG_DIR . '/plugins/' . $mofile );
}
/**
* Get the page builder the given Post ID was built with.
*
* @since 4.1.7
*
* @param int $postId The Post ID.
* @return bool|string The page builder or false if not built with page builders.
*/
public function getPostPageBuilderName( $postId ) {
foreach ( aioseo()->standalone->pageBuilderIntegrations as $integration => $pageBuilder ) {
if ( $pageBuilder->isBuiltWith( $postId ) ) {
return $integration;
}
}
return false;
}
/**
* Get the edit link for the given Post ID.
*
* @since 4.3.1
*
* @param int $postId The Post ID.
* @return bool|string The edit link or false if not built with page builders.
*/
public function getPostEditLink( $postId ) {
$pageBuilder = $this->getPostPageBuilderName( $postId );
if ( ! empty( $pageBuilder ) ) {
return aioseo()->standalone->pageBuilderIntegrations[ $pageBuilder ]->getEditUrl( $postId );
}
return get_edit_post_link( $postId );
}
/**
* Checks if the current user can edit posts of the given post type.
*
* @since 4.1.9
*
* @param string $postType The name of the post type.
* @return bool Whether the user can edit posts of the given post type.
*/
public function canEditPostType( $postType ) {
$capabilities = $this->getPostTypeCapabilities( $postType );
return current_user_can( $capabilities['edit_posts'] );
}
/**
* Returns a list of capabilities for the given post type.
*
* @since 4.1.9
*
* @param string $postType The name of the post type.
* @return array The capabilities.
*/
public function getPostTypeCapabilities( $postType ) {
static $capabilities = [];
if ( isset( $capabilities[ $postType ] ) ) {
return $capabilities[ $postType ];
}
$postTypeObject = get_post_type_object( $postType );
if ( ! is_a( $postTypeObject, 'WP_Post_Type' ) ) {
$capabilities[ $postType ] = [];
return $capabilities[ $postType ];
}
$capabilityType = $postTypeObject->capability_type;
if ( ! is_array( $capabilityType ) ) {
$capabilityType = [
$capabilityType,
$capabilityType . 's'
];
}
// Singular base for meta capabilities, plural base for primitive capabilities.
list( $singularBase, $pluralBase ) = $capabilityType;
$capabilities[ $postType ] = [
'edit_post' => 'edit_' . $singularBase,
'read_post' => 'read_' . $singularBase,
'delete_post' => 'delete_' . $singularBase,
'edit_posts' => 'edit_' . $pluralBase,
'edit_others_posts' => 'edit_others_' . $pluralBase,
'delete_posts' => 'delete_' . $pluralBase,
'publish_posts' => 'publish_' . $pluralBase,
'read_private_posts' => 'read_private_' . $pluralBase,
];
return $capabilities[ $postType ];
}
/**
* Checks if the current user can edit terms of the given taxonomy.
*
* @since 4.1.9
*
* @param string $taxonomy The name of the taxonomy.
* @return bool Whether the user can edit posts of the given taxonomy.
*/
public function canEditTaxonomy( $taxonomy ) {
$capabilities = $this->getTaxonomyCapabilities( $taxonomy );
return current_user_can( $capabilities['edit_terms'] );
}
/**
* Returns a list of capabilities for the given taxonomy.
*
* @since 4.1.9
*
* @param string $taxonomy The name of the taxonomy.
* @return array The capabilities.
*/
public function getTaxonomyCapabilities( $taxonomy ) {
static $capabilities = [];
if ( isset( $capabilities[ $taxonomy ] ) ) {
return $capabilities[ $taxonomy ];
}
$taxonomyObject = get_taxonomy( $taxonomy );
if ( ! is_a( $taxonomyObject, 'WP_Taxonomy' ) ) {
$capabilities[ $taxonomy ] = [];
return $capabilities[ $taxonomy ];
}
$capabilities[ $taxonomy ] = (array) $taxonomyObject->cap;
return $capabilities[ $taxonomy ];
}
/**
* Returns the charset for the site.
*
* @since 4.2.3
*
* @return string The name of the charset.
*/
public function getCharset() {
static $charset = null;
if ( null !== $charset ) {
return $charset;
}
$charset = get_option( 'blog_charset' );
$charset = $charset ? $charset : 'UTF-8';
return $charset;
}
/**
* Returns the given data as JSON.
* We temporarily change the floating point precision in order to prevent rounding errors.
* Otherwise e.g. 4.9 could be output as 4.90000004.
*
* @since 4.2.7
*
* @param mixed $data The data.
* @param int $flags The flags.
* @return string The JSON output.
*/
public function wpJsonEncode( $data, $flags = 0 ) {
$originalPrecision = false;
$originalSerializePrecision = false;
if ( version_compare( PHP_VERSION, '7.1', '>=' ) ) {
$originalPrecision = ini_get( 'precision' );
$originalSerializePrecision = ini_get( 'serialize_precision' );
ini_set( 'precision', 17 );
ini_set( 'serialize_precision', -1 );
}
$json = wp_json_encode( $data, $flags );
if ( version_compare( PHP_VERSION, '7.1', '>=' ) ) {
ini_set( 'precision', $originalPrecision );
ini_set( 'serialize_precision', $originalSerializePrecision );
}
return $json;
}
/**
* Returns the post title or a placeholder if there isn't one.
*
* @since 4.3.0
*
* @param int $postId The post ID.
* @return string The post title.
*/
public function getPostTitle( $postId ) {
static $titles = [];
if ( isset( $titles[ $postId ] ) ) {
return $titles[ $postId ];
}
$post = aioseo()->helpers->getPost( $postId );
if ( ! is_a( $post, 'WP_Post' ) ) {
$titles[ $postId ] = __( '(no title)', 'default' ); // phpcs:ignore AIOSEO.Wp.I18n.TextDomainMismatch, WordPress.WP.I18n.TextDomainMismatch
return $titles[ $postId ];
}
$title = $post->post_title;
$title = $title ? $title : __( '(no title)', 'default' ); // phpcs:ignore AIOSEO.Wp.I18n.TextDomainMismatch, WordPress.WP.I18n.TextDomainMismatch
$titles[ $postId ] = aioseo()->helpers->decodeHtmlEntities( $title );
return $titles[ $postId ];
}
/**
* Checks whether the post status should be considered viewable.
* This function is a copy of the WordPress core function is_post_status_viewable() which was introduced in WP 5.7.
*
* @since 4.5.0
*
* @param string|\stdClass $postStatus The post status name or object.
* @return bool Whether the post status is viewable.
*/
public function isPostStatusViewable( $postStatus ) {
if ( is_scalar( $postStatus ) ) {
$postStatus = get_post_status_object( $postStatus );
if ( ! $postStatus ) {
return false;
}
}
if (
! is_object( $postStatus ) ||
$postStatus->internal ||
$postStatus->protected
) {
return false;
}
return $postStatus->publicly_queryable || ( $postStatus->_builtin && $postStatus->public );
}
/**
* Checks whether the given post is publicly viewable.
* This function is a copy of the WordPress core function is_post_publicly_viewable() which was introduced in WP 5.7.
*
* @since 4.5.0
*
* @param int|\WP_Post $post Optional. Post ID or post object. Defaults to global $post.
* @return boolean Whether the post is publicly viewable or not.
*/
public function isPostPubliclyViewable( $post = null ) {
$post = get_post( $post );
if ( empty( $post ) ) {
return false;
}
$postType = get_post_type( $post );
$postStatus = get_post_status( $post );
return is_post_type_viewable( $postType ) && $this->isPostStatusViewable( $postStatus );
}
/**
* Only register a legacy widget if the WP version is lower than 5.8 or the widget is being used.
* The "Block-based Widgets Editor" was released in WP 5.8, so for WP versions below 5.8 it's okay to register them.
* The main purpose here is to avoid blocks and widgets with the same name to be displayed on the Customizer,
* like e.g. the "Breadcrumbs" Block and Widget.
*
* @since 4.3.9
*
* @param string $idBase The base ID of a widget created by extending WP_Widget.
* @return bool Whether the legacy widget can be registered.
*/
public function canRegisterLegacyWidget( $idBase ) {
global $wp_version; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
if (
version_compare( $wp_version, '5.8', '<' ) || // phpcs:ignore Squiz.NamingConventions.ValidVariableName
is_active_widget( false, false, $idBase ) ||
aioseo()->standalone->pageBuilderIntegrations['elementor']->isPluginActive()
) {
return true;
}
return false;
}
/**
* Parses blocks for a given post.
*
* @since 4.6.8
*
* @param \WP_Post|int $post The post or post ID.
* @param bool $flattenBlocks Whether to flatten the blocks.
* @return array The parsed blocks.
*/
public function parseBlocks( $post, $flattenBlocks = true ) {
if ( ! is_a( $post, 'WP_Post' ) ) {
$post = aioseo()->helpers->getPost( $post );
}
static $parsedBlocks = [];
if ( isset( $parsedBlocks[ $post->ID ] ) ) {
return $parsedBlocks[ $post->ID ];
}
$parsedBlocks = parse_blocks( $post->post_content );
if ( $flattenBlocks ) {
$parsedBlocks = $this->flattenBlocks( $parsedBlocks );
}
$parsedBlocks[ $post->ID ] = $parsedBlocks;
return $parsedBlocks[ $post->ID ];
}
/**
* Flattens the given blocks.
*
* @since 4.6.8
*
* @param array $blocks The blocks.
* @return array The flattened blocks.
*/
public function flattenBlocks( $blocks ) {
$flattenedBlocks = [];
foreach ( $blocks as $block ) {
if ( ! empty( $block['innerBlocks'] ) ) {
// Flatten inner blocks first.
$innerBlocks = $this->flattenBlocks( $block['innerBlocks'] );
unset( $block['innerBlocks'] );
// Add the current block to the result.
$flattenedBlocks[] = $block;
// Add the flattened inner blocks to the result.
$flattenedBlocks = array_merge( $flattenedBlocks, $innerBlocks );
} else {
// If no inner blocks, just add the block to the result.
$flattenedBlocks[] = $block;
}
}
return $flattenedBlocks;
}
/**
* Checks if the Classic eEditor is active and if the Block Editor is disabled in its settings.
*
* @since 4.7.3
*
* @return bool Whether the Classic Editor is active.
*/
public function isClassicEditorActive() {
include_once ABSPATH . 'wp-admin/includes/plugin.php';
if ( ! is_plugin_active( 'classic-editor/classic-editor.php' ) ) {
return false;
}
return 'classic' === get_option( 'classic-editor-replace' );
}
/**
* Redirects to a 404 Not Found page if the sitemap is disabled.
*
* @since 4.0.0
* @version 4.8.0 Moved from the Sitemap class.
*
* @return void
*/
public function notFoundPage() {
global $wp_query; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
$wp_query->set_404(); // phpcs:ignore Squiz.NamingConventions.ValidVariableName
status_header( 404 );
include_once get_404_template();
exit;
}
/**
* Retrieves the post type labels for the given post type.
*
* @since 4.8.2
*
* @param string $postType The name of a registered post type.
* @return object Object with all the labels as member variables.
*/
public function getPostTypeLabels( $postType ) {
static $postTypeLabels = [];
if ( ! isset( $postTypeLabels[ $postType ] ) ) {
$postTypeObject = get_post_type_object( $postType );
if ( ! is_a( $postTypeObject, 'WP_Post_Type' ) ) {
return null;
}
$postTypeLabels[ $postType ] = get_post_type_labels( $postTypeObject );
}
return $postTypeLabels[ $postType ];
}
} Helpers/Shortcodes.php 0000666 00000014440 15114625461 0011010 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains shortcode specific helper methods.
*
* @since 4.1.2
*/
trait Shortcodes {
/**
* Shortcodes known to conflict with AIOSEO.
* NOTE: This is deprecated and only there for users who already were using the aioseo_conflicting_shortcodes_hook before 4.2.0.
*
* @since 4.1.2
*
* @var array
*/
private $conflictingShortcodes = [
'WooCommerce Login' => 'woocommerce_my_account',
'WooCommerce Checkout' => 'woocommerce_checkout',
'WooCommerce Order Tracking' => 'woocommerce_order_tracking',
'WooCommerce Cart' => 'woocommerce_cart',
'WooCommerce Registration' => 'wwp_registration_form',
'WISDM Group Registration' => 'wdm_group_users',
'WISDM Quiz Reporting' => 'wdm_quiz_statistics_details',
'WISDM Course Review' => 'rrf_course_review',
'Simple Membership Login' => 'swpm_login_form',
'Simple Membership Mini Login' => 'swpm_mini_login',
'Simple Membership Payment Button' => 'swpm_payment_button',
'Simple Membership Thank You Page' => 'swpm_thank_you_page_registration',
'Simple Membership Registration' => 'swpm_registration_form',
'Simple Membership Profile' => 'swpm_profile_form',
'Simple Membership Reset' => 'swpm_reset_form',
'Simple Membership Update Level' => 'swpm_update_level_to',
'Simple Membership Member Info' => 'swpm_show_member_info',
'Revslider' => 'rev_slider'
];
/**
* Returns the content with shortcodes replaced.
*
* @since 4.0.5
*
* @param string $content The post content.
* @param bool $override Whether shortcodes should be parsed regardless of the context. Needed for ActionScheduler actions.
* @param int $postId The post ID (optional).
* @return string $content The post content with shortcodes replaced.
*/
public function doShortcodes( $content, $override = false, $postId = 0 ) {
// NOTE: This is_admin() check can never be removed because themes like Avada will otherwise load the wrong post.
if ( ! $override && is_admin() ) {
return $content;
}
if ( ! wp_doing_cron() && ! wp_doing_ajax() ) {
if ( ! $override && apply_filters( 'aioseo_disable_shortcode_parsing', false ) ) {
return $content;
}
if ( ! $override && ! aioseo()->options->searchAppearance->advanced->runShortcodes ) {
return $this->doAllowedShortcodes( $content, $postId );
}
}
$content = $this->doShortcodesHelper( $content, [], $postId );
return $content;
}
/**
* Returns the content with only the allowed shortcodes and wildcards replaced.
*
* @since 4.1.2
* @version 4.6.6 Added the $allowedTags parameter.
*
* @param string $content The content.
* @param int $postId The post ID (optional).
* @param array $allowedTags The shortcode tags to allow (optional).
* @return string The content with shortcodes replaced.
*/
public function doAllowedShortcodes( $content, $postId = null, $allowedTags = [] ) {
// Extract list of shortcodes from the post content.
$tags = $this->getShortcodeTags( $content );
if ( ! count( $tags ) ) {
return $content;
}
$allowedTags = apply_filters( 'aioseo_allowed_shortcode_tags', $allowedTags );
$tagsToRemove = array_diff( $tags, $allowedTags );
$content = $this->doShortcodesHelper( $content, $tagsToRemove, $postId );
return $content;
}
/**
* Returns the content with only the allowed shortcodes and wildcards replaced.
*
* @since 4.1.2
*
* @param string $content The content.
* @param array $tagsToRemove The shortcode tags to remove (optional).
* @param int $postId The post ID (optional).
* @return string The content with shortcodes replaced.
*/
private function doShortcodesHelper( $content, $tagsToRemove = [], $postId = 0 ) {
global $shortcode_tags; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
$conflictingShortcodes = array_merge( $tagsToRemove, $this->conflictingShortcodes );
$conflictingShortcodes = apply_filters( 'aioseo_conflicting_shortcodes', $conflictingShortcodes );
$tagsToRemove = [];
foreach ( $conflictingShortcodes as $shortcode ) {
$shortcodeTag = str_replace( [ '[', ']' ], '', $shortcode );
if ( array_key_exists( $shortcodeTag, $shortcode_tags ) ) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName
$tagsToRemove[ $shortcodeTag ] = $shortcode_tags[ $shortcodeTag ]; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
}
}
// Remove all conflicting shortcodes before parsing the content.
foreach ( $tagsToRemove as $shortcodeTag => $shortcodeCallback ) {
remove_shortcode( $shortcodeTag );
}
if ( $postId ) {
global $post;
$post = get_post( $postId );
if ( is_a( $post, 'WP_Post' ) ) {
// Add the current post to the loop so that shortcodes can use it if needed.
setup_postdata( $post );
}
}
// Set a flag to indicate Divi that it's processing internal content.
$default = aioseo()->helpers->setDiviInternalRendering( true );
$content = do_shortcode( $content );
// Reset the Divi flag to its default value.
aioseo()->helpers->setDiviInternalRendering( $default );
if ( $postId ) {
wp_reset_postdata();
}
// Add back shortcodes as remove_shortcode() disables them site-wide.
foreach ( $tagsToRemove as $shortcodeTag => $shortcodeCallback ) {
add_shortcode( $shortcodeTag, $shortcodeCallback );
}
return $content;
}
/**
* Extracts the shortcode tags from the content.
*
* @since 4.1.2
*
* @param string $content The content.
* @return array $tags The shortcode tags.
*/
private function getShortcodeTags( $content ) {
$tags = [];
$pattern = '\\[(\\[?)([^\s]*)(?![\\w-])([^\\]\\/]*(?:\\/(?!\\])[^\\]\\/]*)*?)(?:(\\/)\\]|\\](?:([^\\[]*+(?:\\[(?!\\/\\2\\])[^\\[]*+)*+)\\[\\/\\2\\])?)(\\]?)';
if ( preg_match_all( "#$pattern#s", (string) $content, $matches ) && array_key_exists( 2, $matches ) ) {
$tags = array_unique( $matches[2] );
}
if ( ! count( $tags ) ) {
return $tags;
}
// Extract nested shortcodes.
foreach ( $matches[5] as $innerContent ) {
$tags = array_merge( $tags, $this->getShortcodeTags( $innerContent ) );
}
return $tags;
}
} Helpers/Svg.php 0000666 00000002017 15114625461 0007427 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains SVG specific helper methods.
*
* @since 4.1.4
*/
trait Svg {
/**
* Sanitizes a SVG string.
*
* @since 4.1.4
*
* @param string $svgString The SVG to check.
* @return string The sanitized SVG.
*/
public function escSvg( $svgString ) {
if ( ! is_string( $svgString ) ) {
return false;
}
$ksesDefaults = wp_kses_allowed_html( 'post' );
$svgArgs = [
'svg' => [
'class' => true,
'aria-hidden' => true,
'aria-labelledby' => true,
'role' => true,
'xmlns' => true,
'width' => true,
'height' => true,
'viewbox' => true, // <= Must be lower case!
],
'g' => [ 'fill' => true ],
'title' => [ 'title' => true ],
'path' => [
'd' => true,
'fill' => true,
]
];
return wp_kses( $svgString, array_merge( $ksesDefaults, $svgArgs ) );
}
} Helpers/Api.php 0000666 00000004752 15114625461 0007411 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains Action Scheduler specific helper methods.
*
* @since 4.2.4
*/
trait Api {
/**
* Request the remote URL via wp_remote_post and return a json decoded response.
*
* @since 4.2.4
*
* @param array $body The content to retrieve from the remote URL.
* @param array $headers The headers to send to the remote URL.
* @return object|null JSON decoded response on success, false on failure.
*/
public function sendRequest( $url, $body = [], $headers = [] ) {
$body = wp_json_encode( $body );
// Build the headers of the request.
$headers = wp_parse_args(
$headers,
[
'Content-Type' => 'application/json'
]
);
// Setup variable for wp_remote_post.
$requestArgs = [
'headers' => $headers,
'body' => $body,
'timeout' => 20
];
// Perform the query and retrieve the response.
$response = $this->wpRemotePost( $url, $requestArgs );
$responseBody = wp_remote_retrieve_body( $response );
// Bail out early if there are any errors.
if ( ! $responseBody ) {
return null;
}
// Return the json decoded content.
return json_decode( $responseBody );
}
/**
* Default arguments for wp_remote_get and wp_remote_post.
*
* @since 4.2.4
*
* @return array An array of default arguments for the request.
*/
private function getWpApiRequestDefaults() {
return [
'timeout' => 10,
'headers' => aioseo()->helpers->getApiHeaders(),
'user-agent' => aioseo()->helpers->getApiUserAgent()
];
}
/**
* Sends a request using wp_remote_post.
*
* @since 4.2.4
*
* @param string $url The URL to send the request to.
* @param array $args The args to use in the request.
* @return array|\WP_Error The response as an array or WP_Error on failure.
*/
public function wpRemotePost( $url, $args = [] ) {
return wp_remote_post( $url, array_replace_recursive( $this->getWpApiRequestDefaults(), $args ) );
}
/**
* Sends a request using wp_remote_get.
*
* @since 4.2.4
*
* @param string $url The URL to send the request to.
* @param array $args The args to use in the request.
* @return array|\WP_Error The response as an array or WP_Error on failure.
*/
public function wpRemoteGet( $url, $args = [] ) {
return wp_remote_get( $url, array_replace_recursive( $this->getWpApiRequestDefaults(), $args ) );
}
} Helpers/Strings.php 0000666 00000046236 15114625461 0010334 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains string specific helper methods.
*
* @since 4.0.13
*/
trait Strings {
/**
* Convert to snake case.
*
* @since 4.0.0
*
* @param string $string The string to convert.
* @return string The converted string.
*/
public function toSnakeCase( $string ) {
$string[0] = strtolower( $string[0] );
return preg_replace_callback( '/([A-Z])/', function ( $value ) {
return '_' . strtolower( $value[1] );
}, $string );
}
/**
* Convert to camel case.
*
* @since 4.0.0
*
* @param string $string The string to convert.
* @param bool $capitalize Whether or not to capitalize the first letter.
* @return string The converted string.
*/
public function toCamelCase( $string, $capitalize = false ) {
$string[0] = strtolower( $string[0] );
if ( $capitalize ) {
$string[0] = strtoupper( $string[0] );
}
return preg_replace_callback( '/_([a-z0-9])/', function ( $value ) {
return strtoupper( $value[1] );
}, $string );
}
/**
* Converts kebab case to camel case.
*
* @since 4.0.0
*
* @param string $string The string to convert.
* @param bool $capitalizeFirstCharacter Whether to capitalize the first letter.
* @return string The converted string.
*/
public function dashesToCamelCase( $string, $capitalizeFirstCharacter = false ) {
$string = str_replace( ' ', '', ucwords( str_replace( '-', ' ', $string ) ) );
if ( ! $capitalizeFirstCharacter ) {
$string[0] = strtolower( $string[0] );
}
return $string;
}
/**
* Truncates a given string.
*
* @since 4.0.0
*
* @param string $string The string.
* @param int $maxCharacters The max. amount of characters.
* @param boolean $shouldHaveEllipsis Whether the string should have a trailing ellipsis (defaults to true).
* @return string The string.
*/
public function truncate( $string, $maxCharacters, $shouldHaveEllipsis = true ) {
$length = strlen( $string );
$excessLength = $length - $maxCharacters;
if ( 0 < $excessLength ) {
// If the string is longer than 65535 characters, we first need to shorten it due to the character limit of the regex pattern quantifier.
if ( 65535 < $length ) {
$string = substr( $string, 0, 65534 );
}
$string = preg_replace( "#[^\pZ\pP]*.{{$excessLength}}$#", '', (string) $string );
if ( $shouldHaveEllipsis ) {
$string = $string . ' ...';
}
}
return $string;
}
/**
* Escapes special regex characters.
*
* @since 4.0.5
*
* @param string $string The string.
* @param string $delimiter The delimiter character.
* @return string The escaped string.
*/
public function escapeRegex( $string, $delimiter = '/' ) {
static $escapeRegex = [];
if ( isset( $escapeRegex[ $string ] ) ) {
return $escapeRegex[ $string ];
}
$escapeRegex[ $string ] = preg_quote( (string) $string, $delimiter );
return $escapeRegex[ $string ];
}
/**
* Escapes special regex characters inside the replacement string.
*
* @since 4.0.7
*
* @param string $string The string.
* @return string The escaped string.
*/
public function escapeRegexReplacement( $string ) {
static $escapeRegexReplacement = [];
if ( isset( $escapeRegexReplacement[ $string ] ) ) {
return $escapeRegexReplacement[ $string ];
}
$escapeRegexReplacement[ $string ] = str_replace( '$', '\$', $string );
return $escapeRegexReplacement[ $string ];
}
/**
* preg_replace but with the replacement escaped.
*
* @since 4.0.10
*
* @param string $pattern The pattern to search for.
* @param string $replacement The replacement string.
* @param string $subject The subject to search in.
* @return string The subject with matches replaced.
*/
public function pregReplace( $pattern, $replacement, $subject ) {
if ( ! $subject ) {
return $subject;
}
$key = $pattern . $replacement . $subject;
static $pregReplace = [];
if ( isset( $pregReplace[ $key ] ) ) {
return $pregReplace[ $key ];
}
// TODO: In the future, we should consider escaping the search pattern as well.
// We can use the following pattern for this - (?<!\\)([\/.^$*+?|()[{}\]]{1})
// The pattern above will only escape special characters if they're not escaped yet, which makes it compatible with all our patterns that are already escaped.
// The caveat is that we'd need to first trim off slash delimiters and add them back later - otherwise they'd be escaped as well.
$replacement = $this->escapeRegexReplacement( $replacement );
$pregReplace[ $key ] = preg_replace( $pattern, $replacement, (string) $subject );
return $pregReplace[ $key ];
}
/**
* Returns string after converting it to lowercase.
*
* @since 4.0.13
*
* @param string $string The original string.
* @return string The string converted to lowercase.
*/
public function toLowerCase( $string ) {
static $lowerCased = [];
if ( isset( $lowerCased[ $string ] ) ) {
return $lowerCased[ $string ];
}
$lowerCased[ $string ] = function_exists( 'mb_strtolower' ) ? mb_strtolower( $string, $this->getCharset() ) : strtolower( $string );
return $lowerCased[ $string ];
}
/**
* Returns the index of a substring in a string.
*
* @since 4.1.6
*
* @param string $stack The stack.
* @param string $needle The needle.
* @param int $offset The offset.
* @return int|bool The index where the string starts or false if it does not exist.
*/
public function stringIndex( $stack, $needle, $offset = 0 ) {
$key = $stack . $needle . $offset;
static $stringIndex = [];
if ( isset( $stringIndex[ $key ] ) ) {
return $stringIndex[ $key ];
}
$stringIndex[ $key ] = function_exists( 'mb_strpos' ) ? mb_strpos( $stack, $needle, $offset, $this->getCharset() ) : strpos( $stack, $needle, $offset );
return $stringIndex[ $key ];
}
/**
* Checks if the given string contains the given substring.
*
* @since 4.1.0.2
*
* @param string $stack The stack.
* @param string $needle The needle.
* @param int $offset The offset.
* @return bool Whether the substring occurs in the main string.
*/
public function stringContains( $stack, $needle, $offset = 0 ) {
$key = $stack . $needle . $offset;
static $stringContains = [];
if ( isset( $stringContains[ $key ] ) ) {
return $stringContains[ $key ];
}
$stringContains[ $key ] = false !== $this->stringIndex( $stack, $needle, $offset );
return $stringContains[ $key ];
}
/**
* Check if a string is JSON encoded or not.
*
* @since 4.1.2
*
* @param mixed $string The string to check.
* @return bool True if it is JSON or false if not.
*/
public function isJsonString( $string ) {
if ( ! is_string( $string ) ) {
return false;
}
json_decode( $string );
// Return a boolean whether or not the last error matches.
return json_last_error() === JSON_ERROR_NONE;
}
/**
* Strips punctuation from a given string.
*
* @since 4.0.0
* @version 4.7.9 Added the $keepSpaces parameter.
*
* @param string $string The string.
* @param array $charactersToKeep The characters that can't be stripped (optional).
* @param bool $keepSpaces Whether to keep spaces.
* @return string The string without punctuation.
*/
public function stripPunctuation( $string, $charactersToKeep = [], $keepSpaces = false ) {
$characterRegexPattern = '';
if ( ! empty( $charactersToKeep ) ) {
$characterString = implode( '', $charactersToKeep );
$characterRegexPattern = "(?![$characterString])";
}
$string = aioseo()->helpers->decodeHtmlEntities( (string) $string );
$string = preg_replace( "/{$characterRegexPattern}[\p{P}\d+]/u", '', $string );
$string = aioseo()->helpers->encodeOutputHtml( $string );
// Trim both internal and external whitespace.
return $keepSpaces ? $string : preg_replace( '/\s\s+/u', ' ', trim( $string ) );
}
/**
* Returns the string after it is encoded with htmlspecialchars().
*
* @since 4.0.0
*
* @param string $string The string to encode.
* @return string The encoded string.
*/
public function encodeOutputHtml( $string ) {
if ( ! is_string( $string ) ) {
return '';
}
return htmlspecialchars( $string, ENT_COMPAT | ENT_HTML401, $this->getCharset(), false );
}
/**
* Returns the string after all HTML entities have been decoded.
*
* @since 4.0.0
*
* @param string $string The string to decode.
* @return string The decoded string.
*/
public function decodeHtmlEntities( $string ) {
static $decodeHtmlEntities = [];
if ( isset( $decodeHtmlEntities[ $string ] ) ) {
return $decodeHtmlEntities[ $string ];
}
// We must manually decode non-breaking spaces since html_entity_decode doesn't do this.
$string = $this->pregReplace( '/ /', ' ', $string );
$decodeHtmlEntities[ $string ] = html_entity_decode( (string) $string, ENT_QUOTES );
return $decodeHtmlEntities[ $string ];
}
/**
* Returns the string with script tags stripped.
*
* @since 4.0.0
*
* @param string $string The string.
* @return string The modified string.
*/
public function stripScriptTags( $string ) {
static $stripScriptTags = [];
if ( isset( $stripScriptTags[ $string ] ) ) {
return $stripScriptTags[ $string ];
}
$stripScriptTags[ $string ] = $this->pregReplace( '/<script(.*?)>(.*?)<\/script>/is', '', $string );
return $stripScriptTags[ $string ];
}
/**
* Returns the string with incomplete HTML tags stripped.
* Incomplete tags are not unopened/unclosed pairs but rather single tags that aren't properly formed.
* e.g. <a href='something'
* e.g. href='something' >
*
* @since 4.1.6
*
* @param string $string The string.
* @return string The modified string.
*/
public function stripIncompleteHtmlTags( $string ) {
static $stripIncompleteHtmlTags = [];
if ( isset( $stripIncompleteHtmlTags[ $string ] ) ) {
return $stripIncompleteHtmlTags[ $string ];
}
$stripIncompleteHtmlTags[ $string ] = $this->pregReplace( '/(^(?!<).*?(\/>)|<[^>]*?(?!\/>)$)/is', '', $string );
return $stripIncompleteHtmlTags[ $string ];
}
/**
* Returns the given JSON formatted data tags as a comma separated list with their values instead.
*
* @since 4.1.0
*
* @param string|array $tags The Array or JSON formatted data tags.
* @return string The comma separated values.
*/
public function jsonTagsToCommaSeparatedList( $tags ) {
$tags = is_string( $tags ) ? json_decode( $tags ) : $tags;
$values = [];
foreach ( $tags as $k => $tag ) {
$values[ $k ] = is_object( $tag ) ? $tag->value : $tag['value'];
}
return implode( ',', $values );
}
/**
* Returns the character length of the given string.
*
* @since 4.1.6
*
* @param string $string The string.
* @return int The string length.
*/
public function stringLength( $string ) {
static $stringLength = [];
if ( isset( $stringLength[ $string ] ) ) {
return $stringLength[ $string ];
}
$stringLength[ $string ] = function_exists( 'mb_strlen' ) ? mb_strlen( $string, $this->getCharset() ) : strlen( $string );
return $stringLength[ $string ];
}
/**
* Returns the word count of the given string.
*
* @since 4.1.6
*
* @param string $string The string.
* @return int The word count.
*/
public function stringWordCount( $string ) {
static $stringWordCount = [];
if ( isset( $stringWordCount[ $string ] ) ) {
return $stringWordCount[ $string ];
}
$stringWordCount[ $string ] = str_word_count( $string );
return $stringWordCount[ $string ];
}
/**
* Explodes the given string into an array.
*
* @since 4.1.6
*
* @param string $delimiter The delimiter.
* @param string $string The string.
* @return array The exploded words.
*/
public function explode( $delimiter, $string ) {
$key = $delimiter . $string;
static $exploded = [];
if ( isset( $exploded[ $key ] ) ) {
return $exploded[ $key ];
}
$exploded[ $key ] = explode( $delimiter, $string );
return $exploded[ $key ];
}
/**
* Implodes an array into a WHEREIN clause useable string.
*
* @since 4.1.6
*
* @param array $array The array.
* @param bool $outerQuotes Whether outer quotes should be added.
* @return string The imploded array.
*/
public function implodeWhereIn( $array, $outerQuotes = false ) {
// Reset the keys first in case there is no 0 index.
$array = array_values( $array );
if ( ! isset( $array[0] ) ) {
return '';
}
if ( is_numeric( $array[0] ) ) {
return implode( ', ', $array );
}
return $outerQuotes ? "'" . implode( "', '", $array ) . "'" : implode( "', '", $array );
}
/**
* Returns an imploded string of placeholders for usage in a WPDB prepare statement.
*
* @since 4.1.9
*
* @param array $array The array.
* @param string $placeholder The placeholder (e.g. "%s" or "%d").
* @return string The imploded string with placeholders.
*/
public function implodePlaceholders( $array, $placeholder = '%s' ) {
return implode( ', ', array_fill( 0, count( $array ), $placeholder ) );
}
/**
* Verifies that a string is indeed a valid regular expression.
*
* @since 4.2.1
*
* @return boolean True if the string is a valid regular expression.
*/
public function isValidRegex( $pattern ) {
// Set a custom error handler to prevent throwing errors on a bad Regular Expression.
set_error_handler( function() {}, E_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
$isValid = true;
if ( false === preg_match( $pattern, '' ) ) {
$isValid = false;
}
// Restore the error handler.
restore_error_handler();
return $isValid;
}
/**
* Removes the leading slash(es) from a string.
*
* @since 4.2.3
*
* @param string $string The string.
* @return string The modified string.
*/
public function unleadingSlashIt( $string ) {
return ltrim( $string, '/' );
}
/**
* Convert the case of the given string.
*
* @since 4.2.4
*
* @param string $string The string.
* @param string $type The casing ("lower", "title", "sentence").
* @return string The converted string.
*/
public function convertCase( $string, $type ) {
switch ( $type ) {
case 'lower':
return strtolower( $string );
case 'title':
return $this->toTitleCase( $string );
case 'sentence':
return $this->toSentenceCase( $string );
default:
return $string;
}
}
/**
* Converts the given string to title case.
*
* @since 4.2.4
*
* @param string $string The string.
* @return string The converted string.
*/
public function toTitleCase( $string ) {
// List of common English words that aren't typically modified.
$exceptions = apply_filters( 'aioseo_title_case_exceptions', [
'of',
'a',
'the',
'and',
'an',
'or',
'nor',
'but',
'is',
'if',
'then',
'else',
'when',
'at',
'from',
'by',
'on',
'off',
'for',
'in',
'out',
'over',
'to',
'into',
'with'
] );
$words = explode( ' ', strtolower( $string ) );
foreach ( $words as $k => $word ) {
if ( ! in_array( $word, $exceptions, true ) ) {
$words[ $k ] = ucfirst( $word );
}
}
$string = implode( ' ', $words );
return $string;
}
/**
* Converts the given string to sentence case.
*
* @since 4.2.4
*
* @param string $string The string.
* @return string The converted string.
*/
public function toSentenceCase( $string ) {
$phrases = preg_split( '/([.?!]+)/', (string) $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
$convertedString = '';
foreach ( $phrases as $index => $sentence ) {
$convertedString .= ( $index & 1 ) === 0 ? ucfirst( strtolower( trim( $sentence ) ) ) : $sentence . ' ';
}
return trim( $convertedString );
}
/**
* Returns the substring with a given start index and length.
*
* @since 4.2.5
*
* @param string $string The string.
* @param int $startIndex The start index.
* @param int $length The length.
* @return string The substring.
*/
public function substring( $string, $startIndex, $length ) {
return function_exists( 'mb_substr' ) ? mb_substr( $string, $startIndex, $length, $this->getCharset() ) : substr( $string, $startIndex, $length );
}
/**
* Strips emoji characters from a given string.
*
* @since 4.7.3
*
* @param string $string The string.
* @return string The string without emoji characters.
*/
public function stripEmoji( $string ) {
// First, decode HTML entities to convert them to actual Unicode characters.
$string = $this->decodeHtmlEntities( $string );
// Pattern to match emoji characters.
$emojiPattern = '/[\x{1F600}-\x{1F64F}' . // Emoticons
'\x{1F300}-\x{1F5FF}' . // Misc Symbols and Pictographs
'\x{1F680}-\x{1F6FF}' . // Transport and Map Symbols
'\x{1F1E0}-\x{1F1FF}' . // Flags (iOS)
'\x{2600}-\x{26FF}' . // Misc symbols
'\x{2700}-\x{27BF}' . // Dingbats
'\x{FE00}-\x{FE0F}' . // Variation Selectors
'\x{1F900}-\x{1F9FF}' . // Supplemental Symbols and Pictographs
']/u';
$filteredString = preg_replace( $emojiPattern, '', (string) $string );
// Re-encode special characters to HTML entities.
return $this->encodeOutputHtml( $filteredString );
}
/**
* Creates a sha1 hash from the given arguments.
*
* @since 4.7.8
*
* @param mixed ...$args The arguments to create a sha1 hash from.
* @return string The sha1 hash.
*/
public function createHash( ...$args ) {
return sha1( wp_json_encode( $args ) );
}
/**
* Extracts URLs from a given string.
*
* @since 4.8.1
*
* @param string $string The string.
* @return array The extracted URLs.
*/
public function extractUrls( $string ) {
$urls = wp_extract_urls( $string );
if ( empty( $urls ) ) {
return [];
}
$allUrls = [];
// Attempt to split multiple URLs. Elementor does not always separate them properly.
foreach ( $urls as $url ) {
$splitUrls = preg_split( '/(?=https?:\/\/)/', $url, - 1, PREG_SPLIT_NO_EMPTY );
$allUrls = array_merge( $allUrls, $splitUrls );
}
return $allUrls;
}
/**
* Determines if a text string contains an emoji or not.
*
* @since 4.8.0
*
* @param string $string The text string to detect emoji in.
* @return bool
*/
public function hasEmojis( $string ) {
$emojisRegexPattern = '/[\x{1F600}-\x{1F64F}' . // Emoticons
'\x{1F300}-\x{1F5FF}' . // Misc Symbols and Pictographs
'\x{1F680}-\x{1F6FF}' . // Transport and Map Symbols
'\x{1F1E0}-\x{1F1FF}' . // Flags (iOS)
'\x{2600}-\x{26FF}' . // Misc symbols
'\x{2700}-\x{27BF}' . // Dingbats
'\x{FE00}-\x{FE0F}' . // Variation Selectors
'\x{1F900}-\x{1F9FF}' . // Supplemental Symbols and Pictographs
'\x{1F018}-\x{1F270}' . // Various Asian characters
'\x{238C}-\x{2454}' . // Misc items
'\x{20D0}-\x{20FF}' . // Combining Diacritical Marks for Symbols
']/u';
return preg_match( $emojisRegexPattern, $string );
}
} Helpers/Constants.php 0000666 00000025544 15114625461 0010656 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains constant specific helper methods.
*
* @since 4.0.17
*/
trait Constants {
/**
* Returns the All in One SEO Logo
*
* @since 4.0.0
*
* @param string $width The width of the image.
* @param string $height The height of the image.
* @param string $colorCode The color of the image.
* @return string The logo as a string.
*/
public function logo( $width, $height, $colorCode ) {
return '<svg viewBox="0 0 20 20" width="' . $width . '" height="' . $height . '" fill="none" xmlns="http://www.w3.org/2000/svg" class="aioseo-gear"><path fill-rule="evenodd" clip-rule="evenodd" d="M9.98542 19.9708C15.5002 19.9708 19.9708 15.5002 19.9708 9.98542C19.9708 4.47063 15.5002 0 9.98542 0C4.47063 0 0 4.47063 0 9.98542C0 15.5002 4.47063 19.9708 9.98542 19.9708ZM8.39541 3.65464C8.26016 3.4485 8.0096 3.35211 7.77985 3.43327C7.51816 3.52572 7.26218 3.63445 7.01349 3.7588C6.79519 3.86796 6.68566 4.11731 6.73372 4.36049L6.90493 5.22694C6.949 5.44996 6.858 5.6763 6.68522 5.82009C6.41216 6.04734 6.16007 6.30426 5.93421 6.58864C5.79383 6.76539 5.57233 6.85907 5.35361 6.81489L4.50424 6.6433C4.26564 6.5951 4.02157 6.70788 3.91544 6.93121C3.85549 7.05738 3.79889 7.1862 3.74583 7.31758C3.69276 7.44896 3.64397 7.58105 3.59938 7.71369C3.52048 7.94847 3.61579 8.20398 3.81839 8.34133L4.53958 8.83027C4.72529 8.95617 4.81778 9.1819 4.79534 9.40826C4.75925 9.77244 4.76072 10.136 4.79756 10.4936C4.82087 10.7198 4.72915 10.9459 4.54388 11.0724L3.82408 11.5642C3.62205 11.7022 3.52759 11.9579 3.60713 12.1923C3.69774 12.4593 3.8043 12.7205 3.92615 12.9743C4.03313 13.1971 4.27749 13.3088 4.51581 13.2598L5.36495 13.0851C5.5835 13.0401 5.80533 13.133 5.94623 13.3093C6.16893 13.5879 6.42071 13.8451 6.6994 14.0756C6.87261 14.2188 6.96442 14.4448 6.92112 14.668L6.75296 15.5348C6.70572 15.7782 6.81625 16.0273 7.03511 16.1356C7.15876 16.1967 7.285 16.2545 7.41375 16.3086C7.54251 16.3628 7.67196 16.4126 7.80195 16.4581C8.18224 16.5912 8.71449 16.1147 9.108 15.7625C9.30205 15.5888 9.42174 15.343 9.42301 15.0798C9.42301 15.0784 9.42302 15.077 9.42302 15.0756L9.42301 13.6263C9.42301 13.6109 9.4236 13.5957 9.42476 13.5806C8.26248 13.2971 7.39838 12.2301 7.39838 10.9572V9.41823C7.39838 9.30125 7.49131 9.20642 7.60596 9.20642H8.32584V7.6922C8.32584 7.48312 8.49193 7.31364 8.69683 7.31364C8.90171 7.31364 9.06781 7.48312 9.06781 7.6922V9.20642H11.0155V7.6922C11.0155 7.48312 11.1816 7.31364 11.3865 7.31364C11.5914 7.31364 11.7575 7.48312 11.7575 7.6922V9.20642H12.4773C12.592 9.20642 12.6849 9.30125 12.6849 9.41823V10.9572C12.6849 12.2704 11.7653 13.3643 10.5474 13.6051C10.5477 13.6121 10.5478 13.6192 10.5478 13.6263L10.5478 15.0694C10.5478 15.3377 10.6711 15.5879 10.871 15.7622C11.2715 16.1115 11.8129 16.5837 12.191 16.4502C12.4527 16.3577 12.7086 16.249 12.9573 16.1246C13.1756 16.0155 13.2852 15.7661 13.2371 15.5229L13.0659 14.6565C13.0218 14.4334 13.1128 14.2071 13.2856 14.0633C13.5587 13.8361 13.8107 13.5792 14.0366 13.2948C14.177 13.118 14.3985 13.0244 14.6172 13.0685L15.4666 13.2401C15.7052 13.2883 15.9493 13.1756 16.0554 12.9522C16.1153 12.8261 16.1719 12.6972 16.225 12.5659C16.2781 12.4345 16.3269 12.3024 16.3714 12.1698C16.4503 11.935 16.355 11.6795 16.1524 11.5421L15.4312 11.0532C15.2455 10.9273 15.153 10.7015 15.1755 10.4752C15.2116 10.111 15.2101 9.74744 15.1733 9.38986C15.1499 9.16361 15.2417 8.93757 15.4269 8.811L16.1467 8.31927C16.3488 8.18126 16.4432 7.92558 16.3637 7.69115C16.2731 7.42411 16.1665 7.16292 16.0447 6.90915C15.9377 6.68638 15.6933 6.57462 15.455 6.62366L14.6059 6.79837C14.3873 6.84334 14.1655 6.75048 14.0246 6.57418C13.8019 6.29554 13.5501 6.03832 13.2714 5.80784C13.0982 5.6646 13.0064 5.43858 13.0497 5.2154L13.2179 4.34868C13.2651 4.10521 13.1546 3.85616 12.9357 3.74787C12.8121 3.68669 12.6858 3.62895 12.5571 3.5748C12.4283 3.52065 12.2989 3.47086 12.1689 3.42537C11.9388 3.34485 11.6884 3.44211 11.5538 3.64884L11.0746 4.38475C10.9513 4.57425 10.73 4.66862 10.5082 4.64573C10.1513 4.6089 9.79502 4.61039 9.44459 4.64799C9.22286 4.67177 9.00134 4.57818 8.87731 4.38913L8.39541 3.65464Z" fill="' . $colorCode . '" /></svg>'; // phpcs:ignore Generic.Files.LineLength.MaxExceeded
}
/**
* Returns the country name by code.
*
* @since 4.0.17
*
* @param string $countryCode The country code.
* @return string Country name.
*/
public function getCountryName( $countryCode ) {
return isset( $this->countryList()[ $countryCode ] ) ? $this->countryList()[ $countryCode ] : '';
}
/**
* Returns a list of countries.
*
* @since 4.0.17
*
* @return array A list of countries.
*/
public function countryList() {
return [
'AF' => 'Afghanistan',
'AL' => 'Albania',
'DZ' => 'Algeria',
'AS' => 'American Samoa',
'AD' => 'Andorra',
'AO' => 'Angola',
'AI' => 'Anguilla',
'AQ' => 'Antarctica',
'AG' => 'Antigua and Barbuda',
'AR' => 'Argentina',
'AM' => 'Armenia',
'AW' => 'Aruba',
'AU' => 'Australia',
'AT' => 'Austria',
'AZ' => 'Azerbaijan',
'BS' => 'Bahamas',
'BH' => 'Bahrain',
'BD' => 'Bangladesh',
'BB' => 'Barbados',
'BY' => 'Belarus',
'BE' => 'Belgium',
'BZ' => 'Belize',
'BJ' => 'Benin',
'BM' => 'Bermuda',
'BT' => 'Bhutan',
'BO' => 'Bolivia',
'BQ' => 'Bonaire',
'BA' => 'Bosnia and Herzegovina',
'BW' => 'Botswana',
'BV' => 'Bouvet Island',
'BR' => 'Brazil',
'IO' => 'British Indian Ocean Territory',
'BN' => 'Brunei Darussalam',
'BG' => 'Bulgaria',
'BF' => 'Burkina Faso',
'BI' => 'Burundi',
'CV' => 'Cabo Verde',
'KH' => 'Cambodia',
'CM' => 'Cameroon',
'CA' => 'Canada',
'KY' => 'Cayman Islands',
'CF' => 'Central African Republic',
'TD' => 'Chad',
'CL' => 'Chile',
'CN' => 'China',
'CX' => 'Christmas Island',
'CC' => 'Cocos (Keeling) Islands',
'CO' => 'Colombia',
'KM' => 'Comoros',
'CD' => 'Democratic Republic of the Congo',
'CG' => 'Congo',
'CK' => 'Cook Islands',
'CR' => 'Costa Rica',
'HR' => 'Croatia',
'CU' => 'Cuba',
'CW' => 'Curaçao',
'CY' => 'Cyprus',
'CZ' => 'Czechia',
'CI' => 'Côte d\'Ivoire',
'DK' => 'Denmark',
'DJ' => 'Djibouti',
'DM' => 'Dominica',
'DO' => 'Dominican Republic',
'EC' => 'Ecuador',
'EG' => 'Egypt',
'SV' => 'El Salvador',
'GQ' => 'Equatorial Guinea',
'ER' => 'Eritrea',
'EE' => 'Estonia',
'SZ' => 'Eswatini',
'ET' => 'Ethiopia',
'FK' => 'Falkland Islands',
'FO' => 'Faroe Islands',
'FJ' => 'Fiji',
'FI' => 'Finland',
'FR' => 'France',
'GF' => 'French Guiana',
'PF' => 'French Polynesia',
'TF' => 'French Southern Territories',
'GA' => 'Gabon',
'GM' => 'Gambia',
'GE' => 'Georgia',
'DE' => 'Germany',
'GH' => 'Ghana',
'GI' => 'Gibraltar',
'GR' => 'Greece',
'GL' => 'Greenland',
'GD' => 'Grenada',
'GP' => 'Guadeloupe',
'GU' => 'Guam',
'GT' => 'Guatemala',
'GG' => 'Guernsey',
'GN' => 'Guinea',
'GW' => 'Guinea-Bissau',
'GY' => 'Guyana',
'HT' => 'Haiti',
'HM' => 'Heard Island and McDonald Islands',
'VA' => 'Holy See',
'HN' => 'Honduras',
'HK' => 'Hong Kong',
'HU' => 'Hungary',
'IS' => 'Iceland',
'IN' => 'India',
'ID' => 'Indonesia',
'IR' => 'Iran',
'IQ' => 'Iraq',
'IE' => 'Ireland',
'IM' => 'Isle of Man',
'IL' => 'Israel',
'IT' => 'Italy',
'JM' => 'Jamaica',
'JP' => 'Japan',
'JE' => 'Jersey',
'JO' => 'Jordan',
'KZ' => 'Kazakhstan',
'KE' => 'Kenya',
'KI' => 'Kiribati',
'KR' => 'South Korea',
'KW' => 'Kuwait',
'KG' => 'Kyrgyzstan',
'LA' => 'Lao People\'s Democratic Republic',
'LV' => 'Latvia',
'LB' => 'Lebanon',
'LS' => 'Lesotho',
'LR' => 'Liberia',
'LY' => 'Libya',
'LI' => 'Liechtenstein',
'LT' => 'Lithuania',
'LU' => 'Luxembourg',
'MO' => 'Macao',
'MG' => 'Madagascar',
'MW' => 'Malawi',
'MY' => 'Malaysia',
'MV' => 'Maldives',
'ML' => 'Mali',
'MT' => 'Malta',
'MH' => 'Marshall Islands',
'MQ' => 'Martinique',
'MR' => 'Mauritania',
'MU' => 'Mauritius',
'YT' => 'Mayotte',
'MX' => 'Mexico',
'FM' => 'Micronesia',
'MD' => 'Moldova',
'MC' => 'Monaco',
'MN' => 'Mongolia',
'ME' => 'Montenegro',
'MS' => 'Montserrat',
'MA' => 'Morocco',
'MZ' => 'Mozambique',
'MM' => 'Myanmar',
'NA' => 'Namibia',
'NR' => 'Nauru',
'NP' => 'Nepal',
'NL' => 'Netherlands',
'NC' => 'New Caledonia',
'NZ' => 'New Zealand',
'NI' => 'Nicaragua',
'NE' => 'Niger',
'NG' => 'Nigeria',
'NU' => 'Niue',
'NF' => 'Norfolk Island',
'MP' => 'Northern Mariana Islands',
'NO' => 'Norway',
'OM' => 'Oman',
'PK' => 'Pakistan',
'PW' => 'Palau',
'PS' => 'Palestine, State of',
'PA' => 'Panama',
'PG' => 'Papua New Guinea',
'PY' => 'Paraguay',
'PE' => 'Peru',
'PH' => 'Philippines',
'PN' => 'Pitcairn',
'PL' => 'Poland',
'PT' => 'Portugal',
'PR' => 'Puerto Rico',
'QA' => 'Qatar',
'MK' => 'Republic of North Macedonia',
'RO' => 'Romania',
'RU' => 'Russian Federation',
'RW' => 'Rwanda',
'RE' => 'Réunion',
'BL' => 'Saint Barthélemy',
'SH' => 'Saint Helena, Ascension and Tristan da Cunha',
'KN' => 'Saint Kitts and Nevis',
'LC' => 'Saint Lucia',
'MF' => 'Saint Martin',
'PM' => 'Saint Pierre and Miquelon',
'VC' => 'Saint Vincent and the Grenadines',
'WS' => 'Samoa',
'SM' => 'San Marino',
'ST' => 'Sao Tome and Principe',
'SA' => 'Saudi Arabia',
'SN' => 'Senegal',
'RS' => 'Serbia',
'SC' => 'Seychelles',
'SL' => 'Sierra Leone',
'SG' => 'Singapore',
'SX' => 'Sint Maarten',
'SK' => 'Slovakia',
'SI' => 'Slovenia',
'SB' => 'Solomon Islands',
'SO' => 'Somalia',
'ZA' => 'South Africa',
'GS' => 'South Georgia and the South Sandwich Islands',
'SS' => 'South Sudan',
'ES' => 'Spain',
'LK' => 'Sri Lanka',
'SD' => 'Sudan',
'SR' => 'Suriname',
'SJ' => 'Svalbard and Jan Mayen',
'SE' => 'Sweden',
'CH' => 'Switzerland',
'SY' => 'Syrian Arab Republic',
'TW' => 'Taiwan',
'TJ' => 'Tajikistan',
'TZ' => 'Tanzania, United Republic of',
'TH' => 'Thailand',
'TL' => 'Timor-Leste',
'TG' => 'Togo',
'TK' => 'Tokelau',
'TO' => 'Tonga',
'TT' => 'Trinidad and Tobago',
'TN' => 'Tunisia',
'TR' => 'Turkey',
'TM' => 'Turkmenistan',
'TC' => 'Turks and Caicos Islands',
'TV' => 'Tuvalu',
'UG' => 'Uganda',
'UA' => 'Ukraine',
'AE' => 'United Arab Emirates',
'GB' => 'United Kingdom of Great Britain and Northern Ireland',
'UM' => 'United States Minor Outlying Islands',
'US' => 'United States of America',
'UY' => 'Uruguay',
'UZ' => 'Uzbekistan',
'VU' => 'Vanuatu',
'VE' => 'Venezuela',
'VN' => 'Vietnam',
'VG' => 'Virgin Islands (British)',
'VI' => 'Virgin Islands (U.S.)',
'WF' => 'Wallis and Futuna',
'EH' => 'Western Sahara',
'YE' => 'Yemen',
'ZM' => 'Zambia',
'ZW' => 'Zimbabwe',
'AX' => 'Åland Islands'
];
}
} Helpers/Arrays.php 0000666 00000020052 15114625461 0010130 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains array specific helper methods.
*
* @since 4.1.4
*/
trait Arrays {
/**
* Unsets a given value in a given array.
* This should only be used if the given value only appears once in the array.
*
* @since 4.0.0
*
* @param array $array The array.
* @param string $value The value that needs to be removed from the array.
* @return array $array The filtered array.
*/
public function unsetValue( $array, $value ) {
if ( in_array( $value, $array, true ) ) {
unset( $array[ array_search( $value, $array, true ) ] );
}
return $array;
}
/**
* Compares two multidimensional arrays to see if they're different.
*
* @since 4.0.0
*
* @param array $array1 The first array.
* @param array $array2 The second array.
* @return boolean Whether the arrays are different.
*/
public function arraysDifferent( $array1, $array2 ) {
foreach ( $array1 as $key => $value ) {
// Check for non-existing values.
if ( ! isset( $array2[ $key ] ) ) {
return true;
}
if ( is_array( $value ) ) {
if ( $this->arraysDifferent( $value, $array2[ $key ] ) ) {
return true;
}
} else {
if ( $value !== $array2[ $key ] ) {
return true;
}
}
}
return false;
}
/**
* Checks whether the given array is associative.
* Arrays that only have consecutive, sequential numeric keys are numeric.
* Otherwise they are associative.
*
* @since 4.1.4
*
* @param array $array The array.
* @return bool Whether the array is associative.
*/
public function isArrayAssociative( $array ) {
return 0 < count( array_filter( array_keys( $array ), 'is_string' ) );
}
/**
* Checks whether the given array is numeric.
*
* @since 4.1.4
*
* @param array $array The array.
* @return bool Whether the array is numeric.
*/
public function isArrayNumeric( $array ) {
return ! $this->isArrayAssociative( $array );
}
/**
* Recursively replaces the values from one array with the ones from another.
* This function should act identical to the built-in array_replace_recursive(), with the exception that it also replaces array values with empty arrays.
*
* @since 4.2.4
*
* @param array $targetArray The target array
* @param array $replacementArray The array with values to replace in the target array.
* @return array The modified array.
*/
public function arrayReplaceRecursive( $targetArray, $replacementArray ) {
// In some cases the target array isn't an array yet (due to e.g. race conditions in InternalOptions), so in that case we can just return the replacement array.
if ( ! is_array( $targetArray ) ) {
return $replacementArray;
}
foreach ( $replacementArray as $k => $v ) {
// If the key does not exist yet on the target array, add it.
if ( ! isset( $targetArray[ $k ] ) ) {
$targetArray[ $k ] = $replacementArray[ $k ];
continue;
}
// If the value is an array, only try to recursively replace it if the value isn't empty.
// Otherwise empty arrays will be ignored and won't override the existing value of the target array.
if ( is_array( $v ) && ! empty( $v ) ) {
$targetArray[ $k ] = $this->arrayReplaceRecursive( $targetArray[ $k ], $v );
continue;
}
// Replace with non-array value or empty array.
$targetArray[ $k ] = $v;
}
return $targetArray;
}
/**
* Recursively intersects the two given arrays.
* You can pass in an optional argument (allowedKey) to restrict the intersect to arrays with a specific key.
* This is needed when we are e.g. sanitizing array values before setting/saving them to an option.
* This helper method was mainly built to support our complex options architecture.
*
* @since 4.2.5
*
* @param array $array1 The first array.
* @param array $array2 The second array.
* @param string $allowedKey The only key the method should run for (optional).
* @param string $parentKey The parent key.
* @return array The intersected array.
*/
public function arrayIntersectRecursive( $array1, $array2, $allowedKey = '', $parentKey = '' ) {
if ( ! $allowedKey || $allowedKey === $parentKey ) {
$array1 = $this->arrayIntersectRecursiveHelper( $array1, $array2 );
}
if ( empty( $array1 ) ) {
return [];
}
foreach ( $array1 as $k => $v ) {
if ( is_array( $v ) && isset( $array2[ $k ] ) ) {
$array1[ $k ] = $this->arrayIntersectRecursive( $array1[ $k ], $array2[ $k ], $allowedKey, $k );
}
}
if ( $this->isArrayNumeric( $array1 ) ) {
$array1 = array_values( $array1 );
}
return $array1;
}
/**
* Recursively intersects the two given arrays. Supports arrays with a mix of nested arrays and primitive values.
* Helper function for arrayIntersectRecursive().
*
* @since 4.5.4
*
* @param array $array1 The first array.
* @param array $array2 The second array.
* @return array The intersected array.
*/
private function arrayIntersectRecursiveHelper( $array1, $array2 ) {
if ( null === $array2 ) {
$array2 = [];
}
if ( is_array( $array1 ) ) {
// First, check with keys are nested arrays and which are primitive values.
$arrays = [];
$primitives = [];
foreach ( $array1 as $k => $v ) {
if ( is_array( $v ) ) {
$arrays[ $k ] = $v;
} else {
$primitives[ $k ] = $v;
}
}
// Then, intersect the primitive values.
$intersectedPrimitives = array_intersect_assoc( $primitives, $array2 );
// Finally, recursively intersect the nested arrays.
$intersectedArrays = [];
foreach ( $arrays as $k => $v ) {
if ( isset( $array2[ $k ] ) ) {
$intersectedArrays[ $k ] = $this->arrayIntersectRecursiveHelper( $v, $array2[ $k ] );
} else {
// If the nested array doesn't exist in the second array, we can just unset it.
unset( $arrays[ $k ] );
}
}
// Merge the intersected arrays and primitive values.
return array_merge( $intersectedPrimitives, $intersectedArrays );
}
return array_intersect_assoc( $array1, $array2 );
}
/**
* Sorts the keys of an array alphabetically.
* The array is passed by reference, so it's not returned the same as in `ksort()`.
*
* @since 4.4.0.3
*
* @param array $array The array to sort, passed by reference.
*/
public function arrayRecursiveKsort( &$array ) {
foreach ( $array as &$value ) {
if ( is_array( $value ) ) {
$this->arrayRecursiveKsort( $value );
}
}
ksort( $array );
}
/**
* Creates a multidimensional array from a list of keys and a value.
*
* @since 4.5.3
*
* @param array $keys The keys to create the array from.
* @param mixed $value The value to assign to the last key.
* @param array $array The array when recursing.
* @return array The multidimensional array.
*/
public function createMultidimensionalArray( $keys, $value, $array = [] ) {
$key = array_shift( $keys );
if ( empty( $array[ $key ] ) ) {
$array[ $key ] = null;
}
if ( 0 < count( $keys ) ) {
$array[ $key ] = $this->createMultidimensionalArray( $keys, $value, $array[ $key ] );
} else {
$array[ $key ] = $value;
}
return $array;
}
/**
* Sorts an array of arrays by a specific key.
*
* @since 4.7.4
*
* @param array $arr The input array.
* @param string $key The key to sort by.
* @param string $order Designates ascending or descending order. Default 'asc'. Accepts 'asc', 'desc'.
* @return void
*/
public function usortByKey( &$arr, $key, $order = 'asc' ) {
if ( empty( $arr ) || ! is_array( $arr ) ) {
return;
}
usort( $arr, function ( $a, $b ) use ( $key, $order ) {
return 'asc' === $order ? $a[ $key ] <=> $b[ $key ] : $b[ $key ] <=> $a[ $key ];
} );
}
/**
* Flattens a multidimensional array.
*
* @since 4.7.6
*
* @param array $arr The input array.
* @return array The flattened array.
*/
public function flatten( $arr ) {
$result = [];
array_walk_recursive( $arr, function ( $value ) use ( &$result ) {
$result[] = $value;
} );
return $result;
}
} Helpers/WpMultisite.php 0000666 00000017264 15114625461 0011170 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains methods related to multisite.
*
* @since 4.2.5
*/
trait WpMultisite {
/**
* Returns the ID of the network's main site.
*
* @since 4.2.5
*
* @return int The ID of the network's main site.
*/
public function getNetworkId() {
if ( is_multisite() ) {
return get_network()->site_id;
}
return get_current_blog_id();
}
/**
* Get a site (with aliases) by it's blog ID.
*
* @since 4.2.5
*
* @param int $blogId The blog ID.
* @return \WP_Site|null The site.
*/
public function getSiteByBlogId( $blogId ) {
$sites = $this->getSites();
foreach ( $sites['sites'] as $site ) {
if ( $site->blog_id === $blogId ) {
return $site;
}
}
return null;
}
/**
* Get the current site.
*
* @since 4.2.5
*
* @return \WP_Site|object A WP_Site instance of the current site or an object representing the same.
*/
public function getSite() {
if ( is_multisite() ) {
return get_site();
}
return (object) [
'domain' => $this->getSiteDomain( true ),
'path' => $this->getHomePath( true )
];
}
/**
* Get all sites in the multisite network.
*
* @since 4.2.5
*
* @param int|string $limit The number of sites to get or 'all'.
* @param int $offset The offset to start at.
* @param null|string $searchTerm The search term to look for.
* @param null|string $filter A filter to look up sites by.
* @param null|string $orderBy The column to order results by. Defaults to null.
* @param string $orderDir The direction to order results by. Defaults to 'DESC'.
* @return array An array of sites.
*/
public function getSites( $limit = 'all', $offset = 0, $searchTerm = null, $filter = 'all', $orderBy = null, $orderDir = 'DESC' ) {
$countSites = $this->countSites();
$sites = get_sites( [
'network_id' => get_current_network_id(),
'number' => $countSites['public'],
'public' => 1
] );
$allSites = [];
foreach ( $sites as $site ) {
$clonedSite = clone $site;
$clonedSite->adminUrl = get_admin_url( $site->blog_id );
$clonedSite->homeUrl = get_home_url( $site->blog_id );
if ( $this->includeSite( $clonedSite, $filter ) ) {
$allSites[] = $clonedSite;
}
// We need to look up aliases for Mercator, this checks to see if it's even enabled.
if ( ! class_exists( '\Mercator\Mapping' ) ) {
continue;
}
$aliases = $this->getSiteAliases( $site );
foreach ( $aliases as $alias ) {
$aliasSite = clone $clonedSite;
$aliasSite->domain = $alias['domain'];
$aliasSite->path = '/';
$aliasSite->alias = $alias;
$aliasSite->parentDomain = $site->domain;
$aliasSite->parentPath = $site->path;
if ( $this->includeSite( $aliasSite, $filter ) ) {
$allSites[] = $aliasSite;
}
}
}
// If we have a search term, let's filter down these results.
if ( ! empty( $searchTerm ) ) {
foreach ( $allSites as $key => $site ) {
$keep = false;
if (
false !== stripos( $site->domain, $searchTerm ) ||
false !== stripos( $site->path, $searchTerm ) ||
false !== stripos( $site->parentDomain, $searchTerm ) ||
false !== stripos( $site->parentPath, $searchTerm )
) {
$keep = true;
}
if ( ! $keep ) {
unset( $allSites[ $key ] );
}
}
}
// Ordering the sites.
if ( ! empty( $orderBy ) ) {
usort( $allSites, function( $site1, $site2 ) use ( $orderBy, $orderDir ) {
if ( empty( $site1->{ $orderBy } ) ) {
return 0;
}
return 'ASC' === strtoupper( $orderDir )
? ( $site1->{ $orderBy } > $site2->{ $orderBy } ? 1 : 0 )
: ( $site1->{ $orderBy } < $site2->{ $orderBy } ? 1 : 0 );
} );
}
return [
'total' => count( $allSites ),
'limit' => $limit,
'sites' => 'all' === $limit ? $allSites : array_slice( $allSites, $offset, $limit )
];
}
/**
* Count the number of sites in the network. A clone of wp_count_sites. We use this because
* we don't yet support WordPress 5.3. Once we do, we can revert to wp_count_sites.
*
* @since 4.4.5
*
* @return array An array of aliases.
*/
private function countSites() {
$networkId = get_current_network_id();
$counts = [];
$args = [
'network_id' => $networkId,
'number' => 1,
'fields' => 'ids',
'no_found_rows' => false,
];
$q = new \WP_Site_Query( $args );
$counts['all'] = $q->found_sites;
$_args = $args;
$statuses = [ 'public', 'archived', 'mature', 'spam', 'deleted' ];
foreach ( $statuses as $status ) {
$_args = $args;
$_args[ $status ] = 1;
$q = new \WP_Site_Query( $_args );
$counts[ $status ] = $q->found_sites;
}
return $counts;
}
/**
* Filter sites based on a passed in filter. Options include 'all', 'activated' or 'deactivated'.
*
* @since 4.2.5
*
* @param Object $site The site object.
* @param string $filter The filter to use.
* @return bool The site if allowed or null if not.
*/
private function includeSite( $site, $filter ) {
if ( 'all' === $filter ) {
return true;
}
$siteIsActive = aioseo()->networkLicense->isSiteActive( $site );
if (
( 'deactivated' === $filter && ! $siteIsActive ) ||
( 'activated' === $filter && $siteIsActive )
) {
return true;
}
return false;
}
/**
* Get an array of aliases for a WP_Site.
*
* @since 4.2.5
*
* @param \WP_Site $site The Site.
* @return array An array of aliases.
*/
public function getSiteAliases( $site ) {
// We need to look up aliases for Mercator, this checks to see if it's even enabled.
if ( ! class_exists( '\Mercator\Mapping' ) ) {
return [];
}
$aliases = \Mercator\Mapping::get_by_site( $site->blog_id );
if ( empty( $aliases ) ) {
return [];
}
$aliasData = [];
foreach ( $aliases as $alias ) {
$aliasData[] = [
'alias_id' => $alias->get_id(),
'domain' => $alias->get_domain(),
'active' => $alias->is_active()
];
}
return $aliasData;
}
/**
* Wrapper for switch_to_blog especially for non-multisite setups.
*
* @since 4.2.5
*
* @param int $blogId The blog ID to switch to.
* @return bool Whether the blog was switched to or not.
*/
public function switchToBlog( $blogId ) {
if ( ! is_multisite() ) {
return false;
}
switch_to_blog( $blogId );
aioseo()->core->db->init();
return true;
}
/**
* Wrapper for restore_current_blog especially for non-multisite setups.
*
* @since 4.2.5
*
* @return bool Whether the blog was restored or not.
*/
public function restoreCurrentBlog() {
if ( ! is_multisite() ) {
return false;
}
restore_current_blog();
aioseo()->core->db->init();
return true;
}
/**
* Checks if the current plugin is network activated.
*
* @since 4.2.8
*
* @param string|null $plugin The plugin to check for network activation.
* @return bool True if network activated, false if not.
*/
public function isPluginNetworkActivated( $plugin = null ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
if ( ! is_multisite() ) {
return false;
}
$plugin = $plugin ? $plugin : plugin_basename( AIOSEO_FILE );
// If the plugin is not network activated, then no it's not network licensed.
if ( ! is_plugin_active_for_network( $plugin ) ) {
return false;
}
return true;
}
/**
* Returns the current site domain.
*
* @since 4.7.7
*
* @return string The site domain.
*/
public function getMultiSiteDomain() {
$site = aioseo()->helpers->getSite();
return $site->domain . $site->path;
}
} Helpers/DateTime.php 0000666 00000012035 15114625461 0010365 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains date/time specific helper methods.
*
* @since 4.1.2
*/
trait DateTime {
/**
* Formats a date in ISO8601 format.
*
* @since 4.1.2
*
* @param string $date The date.
* @return string The date formatted in ISO8601 format.
*/
public function dateToIso8601( $date ) {
return date( 'Y-m-d', strtotime( $date ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
}
/**
* Formats a date & time in ISO8601 format.
*
* @since 4.0.0
*
* @param string $dateTime The date.
* @return string The date formatted in ISO8601 format.
*/
public function dateTimeToIso8601( $dateTime ) {
return date( 'c', strtotime( $dateTime ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
}
/**
* Formats a date & time in RFC-822 format.
*
* @since 4.2.1
*
* @param string $dateTime The date.
* @return string The date formatted in RFC-822 format.
*/
public function dateTimeToRfc822( $dateTime ) {
return date( 'D, d M Y H:i:s O', strtotime( $dateTime ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
}
/**
* Retrieves the timezone offset in seconds.
*
* @since 4.0.0
* @version 4.7.2 Returns the actual timezone offset.
*
* @return int The timezone offset in seconds.
*/
public function getTimeZoneOffset() {
try {
$timezone = get_option( 'timezone_string' );
if ( $timezone ) {
$timezone_object = new \DateTimeZone( $timezone ); // phpcs:ignore Squiz.NamingConventions.ValidVariableName
return $timezone_object->getOffset( new \DateTime( 'now' ) ); // phpcs:ignore Squiz.NamingConventions.ValidVariableName
}
} catch ( \Exception $e ) {
// Do nothing.
}
return intval( get_option( 'gmt_offset', 0 ) ) * HOUR_IN_SECONDS;
}
/**
* Formats an amount of days, hours and minutes in ISO8601 duration format.
* This is used in our JSON schema to adhere to Google's standards.
*
* @since 4.2.5
*
* @param integer|string $days The days.
* @param integer|string $hours The hours.
* @param integer|string $minutes The minutes.
* @return string The days, hours and minutes formatted in ISO8601 duration format.
*/
public function timeToIso8601DurationFormat( $days, $hours, $minutes ) {
$duration = 'P';
if ( $days ) {
$duration .= $days . 'D';
}
$duration .= 'T';
if ( $hours ) {
$duration .= $hours . 'H';
}
if ( $minutes ) {
$duration .= $minutes . 'M';
}
return $duration;
}
/**
* Returns a MySQL formatted date.
*
* @since 4.1.5
*
* @param int|string $time Any format accepted by strtotime.
* @return false|string The MySQL formatted string.
*/
public function timeToMysql( $time ) {
$time = is_string( $time ) ? strtotime( $time ) : $time;
return date( 'Y-m-d H:i:s', $time ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
}
/**
* Formats a date in WordPress format.
*
* @since 4.8.2
*
* @param string $dateTime Same as you'd pass to `strtotime()`.
* @param string $dateTimeSeparator The separator between the date and time.
* @return string|null The date formatted in WordPress format. Null if the passed date is invalid.
*/
public function dateToWpFormat( $dateTime, $dateTimeSeparator = ', ' ) {
static $format = null;
if ( ! isset( $format ) ) {
$dateFormat = get_option( 'date_format', 'd M' );
$timeFormat = get_option( 'time_format', 'H:i' );
$format = $dateFormat . $dateTimeSeparator . $timeFormat;
}
$timestamp = strtotime( (string) $dateTime );
return $timestamp && 0 < $timestamp ? date_i18n( $format, $timestamp ) : null;
}
/**
* Checks if a given string is a valid date.
*
* @since 4.8.3
*
* @param string $date The date string to check.
* @param string $format The format of the date string.
* @return bool True if the string is a valid date, false otherwise.
*/
public function isValidDate( $date, $format = null ) {
if ( ! $date ) {
return false;
}
if ( $format ) {
$d = \DateTime::createFromFormat( $format, $date );
return $d && $d->format( $format ) === $date;
}
$timestamp = strtotime( $date );
return false !== $timestamp;
}
/**
* Generates a random (yet unique per identifier) time offset based on a site identifier.
*
* @since 4.7.9
*
* @param string $identifier Data such as the site URL, site ID, or a combination of both to serve as the seed for generating a random time offset.
* @param int $maxOffsetMinutes The range for the random offset in minutes.
* @return int The random (yet unique per identifier) time offset in minutes.
*/
public function generateRandomTimeOffset( $identifier, $maxOffsetMinutes ) {
$hash = md5( strval( $identifier ) );
// Convert part of the hash to an integer.
$hashInteger = hexdec( substr( $hash, 0, 8 ) );
return $hashInteger % $maxOffsetMinutes;
}
} Helpers/Buffer.php 0000666 00000000605 15114625461 0010102 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains buffer specific helper methods.
*
* @since 4.8.3
*/
trait Buffer {
/**
* Clears all output buffers.
*
* @since 4.8.3
*
* @return void
*/
public function clearBuffers() {
while ( ob_get_level() > 0 ) {
ob_end_clean();
}
}
} Helpers/WpUri.php 0000666 00000040107 15114625461 0007740 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use AIOSEO\Plugin\Common\Integrations\BuddyPress as BuddyPressIntegration;
/**
* Contains all WordPress related URL, URI, path, slug, etc. related helper methods.
*
* @since 4.1.4
*/
trait WpUri {
/**
* Returns the site domain.
*
* @since 4.0.0
*
* @param bool $unfiltered Whether to get the unfiltered value.
* @return string The site's domain.
*/
public function getSiteDomain( $unfiltered = false ) {
return wp_parse_url( $this->getHomeUrl( $unfiltered ), PHP_URL_HOST );
}
/**
* Returns the site URL.
* NOTE: For multisites inside a sub-directory, this returns the URL for the main site.
* This is intentional.
*
* @since 4.0.0
*
* @param bool $unfiltered Whether to get the unfiltered value.
* @return string The site's domain.
*/
public function getSiteUrl( $unfiltered = false ) {
$homeUrl = $this->getHomeUrl( $unfiltered );
return wp_parse_url( $homeUrl, PHP_URL_SCHEME ) . '://' . wp_parse_url( $homeUrl, PHP_URL_HOST );
}
/**
* Returns the current URL.
*
* @since 4.0.0
*
* @param boolean $canonical Whether or not to get the canonical URL.
* @return string The URL.
*/
public function getUrl( $canonical = false ) {
$url = '';
if ( is_singular() ) {
$objectId = aioseo()->helpers->getPostId();
if ( $canonical ) {
$url = aioseo()->helpers->wpGetCanonicalUrl( $objectId );
}
if ( ! $url ) {
// wp_get_canonical_url() returns false if the post isn't published.
// Therefore, we must to fall back to the permalink if the post isn't published, e.g. draft post or attachment (inherit).
$url = get_permalink( $objectId );
}
}
if ( $url ) {
return $url;
}
global $wp;
// Permalink url without the query string.
$url = user_trailingslashit( home_url( $wp->request ) );
// If permalinks are not being used we need to append the query string to the home url.
if ( ! $this->usingPermalinks() ) {
$url = home_url( ! empty( $wp->query_string ) ? '?' . $wp->query_string : '' );
}
return $url;
}
/**
* Gets the canonical URL for the current page/post.
*
* @since 4.0.0
*
* @return string $url The canonical URL.
*/
public function canonicalUrl() {
$queriedObject = get_queried_object(); // Don't use our getTerm helper here.
$hash = md5( wp_json_encode( $queriedObject ?? [] ) );
static $url = [];
if ( isset( $url[ $hash ] ) ) {
return $url[ $hash ];
}
if ( is_404() || is_search() ) {
$url[ $hash ] = apply_filters( 'aioseo_canonical_url', '' );
return $url[ $hash ];
}
$metaData = [];
$post = $this->getPost();
if ( $post ) {
$metaData = aioseo()->meta->metaData->getMetaData( $post );
}
if ( is_category() || is_tag() || is_tax() ) {
$metaData = aioseo()->meta->metaData->getMetaData( $queriedObject );
$url[ $hash ] = get_term_link( $queriedObject, $queriedObject->taxonomy ?? '' );
// If the term link is a WP_Error, set it to an empty string.
if ( ! is_string( $url[ $hash ] ) ) {
$url[ $hash ] = '';
}
// Add pagination to the URL. We need to do this here because get_term_link() doesn't handle pagination.
// We'll strip it further down if no pagination for canonical is enabled.
if ( $this->getPageNumber() > 1 ) {
$url[ $hash ] = user_trailingslashit( rtrim( $url[ $hash ], '/' ) . '/page/' . $this->getPageNumber() );
}
}
if ( $metaData && ! empty( $metaData->canonical_url ) ) {
$url[ $hash ] = apply_filters( 'aioseo_canonical_url', $this->makeUrlAbsolute( $metaData->canonical_url ) );
return $url[ $hash ];
}
if ( BuddyPressIntegration::isComponentPage() ) {
$url[ $hash ] = aioseo()->standalone->buddyPress->component->getMeta( 'canonical' );
}
if ( empty( $url[ $hash ] ) || is_wp_error( $url[ $hash ] ) ) {
$url[ $hash ] = $this->getUrl( true );
}
$pageNumber = $this->getPageNumber();
if (
in_array( 'noPaginationForCanonical', aioseo()->internalOptions->deprecatedOptions, true ) &&
aioseo()->options->deprecated->searchAppearance->advanced->noPaginationForCanonical
) {
if ( 1 < $pageNumber ) {
if ( $this->usingPermalinks() ) {
// Replace /page/3 and /page/3/.
$url[ $hash ] = preg_replace( "@(?<=/)page/$pageNumber(/|)$@", '', (string) $url[ $hash ] );
// Replace /3 and /3/.
$url[ $hash ] = preg_replace( "@(?<=/)$pageNumber(/|)$@", '', (string) $url[ $hash ] );
} else {
// Replace /?page_id=457&paged=1 and /?page_id=457&page=1.
$url[ $hash ] = aioseo()->helpers->urlRemoveQueryParameter( $url[ $hash ], [ 'page', 'paged' ] );
}
}
// Comment pages.
$url[ $hash ] = preg_replace( '/(?<=\/)comment-page-\d+\/*(#comments)*$/', '', (string) $url[ $hash ] );
}
$url[ $hash ] = $this->maybeRemoveTrailingSlash( $url[ $hash ] );
// Get rid of /amp at the end of the URL.
if (
aioseo()->helpers->isAmpPage() &&
! apply_filters( 'aioseo_disable_canonical_url_amp', false )
) {
$url[ $hash ] = preg_replace( '/\/amp$/', '', (string) $url[ $hash ] );
$url[ $hash ] = preg_replace( '/\/amp\/$/', '/', (string) $url[ $hash ] );
}
$url[ $hash ] = apply_filters( 'aioseo_canonical_url', $url[ $hash ] );
return $url[ $hash ];
}
/**
* Sanitizes a given domain.
*
* @since 4.0.0
*
* @param string $domain The domain to sanitize.
* @return mixed|string The sanitized domain.
*/
public function sanitizeDomain( $domain ) {
$domain = trim( $domain );
$domain = strtolower( $domain );
if ( 0 === strpos( $domain, 'http://' ) ) {
$domain = substr( $domain, 7 );
} elseif ( 0 === strpos( $domain, 'https://' ) ) {
$domain = substr( $domain, 8 );
}
$domain = untrailingslashit( $domain );
return $domain;
}
/**
* Remove trailing slashes if not set in the permalink structure.
*
* @since 4.0.0
*
* @param string $url The original URL.
* @return string The adjusted URL.
*/
public function maybeRemoveTrailingSlash( $url ) {
$permalinks = get_option( 'permalink_structure' );
if ( $permalinks && ( ! is_home() || ! is_front_page() ) ) {
$trailing = substr( $permalinks, -1 );
if ( '/' !== $trailing ) {
$url = untrailingslashit( $url );
}
}
// Don't slash urls with query args.
if ( false !== strpos( $url, '?' ) ) {
$url = untrailingslashit( $url );
}
return $url;
}
/**
* Removes image dimensions from the slug of a URL.
*
* @since 4.0.0
*
* @param string $url The image URL.
* @return string The formatted image URL.
*/
public function removeImageDimensions( $url ) {
return $this->isValidAttachment( $url ) ? preg_replace( '#(-[0-9]*x[0-9]*|-scaled)#', '', (string) $url ) : $url;
}
/**
* Returns the URL for the WP content folder.
*
* @since 4.0.5
*
* @return string The URL.
*/
public function getWpContentUrl() {
$info = wp_get_upload_dir();
return isset( $info['baseurl'] ) ? $info['baseurl'] : '';
}
/**
* Retrieves a post by its given path.
* Based on the built-in get_page_by_path() function, but only checks ancestry if the post type is actually hierarchical.
*
* @since 4.1.4
*
* @param string $path The path.
* @param string $output The output type. OBJECT, ARRAY_A, or ARRAY_N.
* @param string|array $postType The post type(s) to check against.
* @return object|false The post or false on failure.
*/
public function getPostByPath( $path, $output = OBJECT, $postType = 'page' ) {
$lastChanged = wp_cache_get_last_changed( 'aioseo_posts_by_path' );
$hash = md5( $path . serialize( $postType ) );
$cacheKey = "get_page_by_path:$hash:$lastChanged";
$cached = wp_cache_get( $cacheKey, 'aioseo_posts_by_path' );
if ( false !== $cached ) {
// Special case: '0' is a bad `$path`.
if ( '0' === $cached || 0 === $cached ) {
return false;
}
return get_post( $cached, $output );
}
$path = rawurlencode( urldecode( $path ) );
$path = str_replace( '%2F', '/', $path );
$path = str_replace( '%20', ' ', $path );
$parts = explode( '/', trim( $path, '/' ) );
$reversedParts = array_reverse( $parts );
$postNames = "'" . implode( "','", $parts ) . "'";
$postTypes = is_array( $postType ) ? $postType : [ $postType, 'attachment' ];
$postTypes = "'" . implode( "','", $postTypes ) . "'";
$posts = aioseo()->core->db->start( 'posts' )
->select( 'ID, post_name, post_parent, post_type' )
->whereRaw( "post_name in ( $postNames )" )
->whereRaw( "post_type in ( $postTypes )" )
->run()
->result();
$foundId = 0;
foreach ( $posts as $post ) {
if ( $post->post_name === $reversedParts[0] ) {
$count = 0;
$p = $post;
// Loop through the given path parts from right to left, ensuring each matches the post ancestry.
while ( 0 !== (int) $p->post_parent && isset( $posts[ $p->post_parent ] ) ) {
$count++;
$parent = $posts[ $p->post_parent ];
if ( ! isset( $reversedParts[ $count ] ) || $parent->post_name !== $reversedParts[ $count ] ) {
break;
}
$p = $parent;
}
if (
0 === (int) $p->post_parent &&
( ! is_post_type_hierarchical( $p->post_type ) || count( $reversedParts ) === $count + 1 ) &&
$p->post_name === $reversedParts[ $count ]
) {
$foundId = $post->ID;
if ( $post->post_type === $postType ) {
break;
}
}
}
}
// We cache misses as well as hits.
wp_cache_set( $cacheKey, $foundId, 'aioseo_posts_by_path' );
return $foundId ? get_post( $foundId, $output ) : false;
}
/**
* Validates a URL.
*
* @since 4.1.2
*
* @param string $url The url.
* @return bool Is it a valid/safe url.
*/
public function isUrl( $url ) {
return esc_url_raw( $url ) === $url;
}
/**
* Retrieves the parameters for a given URL.
*
* @since 4.1.5
*
* @param string $url The url.
* @return array The parameters.
*/
public function getParametersFromUrl( $url ) {
$parsedUrl = wp_parse_url( wp_unslash( $url ) );
$parameters = [];
if ( empty( $parsedUrl['query'] ) ) {
return [];
}
wp_parse_str( $parsedUrl['query'], $parameters );
return $parameters;
}
/**
* Adds a leading slash to an url.
*
* @since 4.1.8
*
* @param string $url The url.
* @return string The url with a leading slash.
*/
public function leadingSlashIt( $url ) {
return '/' . ltrim( $url, '/' );
}
/**
* Returns the path from a permalink.
* This function will help get the correct path from WP installations in subfolders.
*
* @since 4.1.8
*
* @param string $permalink A permalink from get_permalink().
* @return string The path without the home_url().
*/
public function getPermalinkPath( $permalink ) {
// We want to get this value straight from the DB to prevent plugins like WPML from filtering it.
// This will otherwise mess with things like license activation requests and redirects.
$homeUrl = $this->getHomeUrl( true );
return $this->leadingSlashIt( str_replace( $homeUrl, '', $permalink ) );
}
/**
* Changed if permalinks are different and the before wasn't
* the site url (we don't want to redirect the site URL).
*
* @since 4.2.3
*
* @param string $before The URL before the change.
* @param string $after The URL after the change.
* @return boolean True if the permalink has changed.
*/
public function hasPermalinkChanged( $before, $after ) {
// Check it's not redirecting from the root.
if ( $this->getHomePath() === $before || '/' === $before ) {
return false;
}
// Are the URLs the same?
return ( $before !== $after );
}
/**
* Retrieve the home path.
*
* @since 4.2.3
*
* @param bool $unfiltered Whether to get the unfiltered value.
* @return string The home path.
*/
public function getHomePath( $unfiltered = false ) {
$path = wp_parse_url( $this->getHomeUrl( $unfiltered ), PHP_URL_PATH );
return $path ? trailingslashit( $path ) : '/';
}
/**
* Returns the home URL.
*
* @since 4.7.3
*
* @param bool $unfiltered Whether to get the unfiltered value.
* @return string The home URL.
*/
private function getHomeUrl( $unfiltered = false ) {
$homeUrl = home_url();
if ( $unfiltered ) {
// We want to get this value straight from the DB to prevent plugins like WPML from filtering it.
// This will otherwise mess with things like license activation requests and redirects.
$homeUrl = get_option( 'home' );
}
return $homeUrl;
}
/**
* Checks if the given URL is an internal URL for the current site.
*
* @since 4.2.6
*
* @param string $urlToCheck The URL to check.
* @return bool Whether the given URL is an internal one.
*/
public function isInternalUrl( $urlToCheck ) {
$parsedHomeUrl = wp_parse_url( home_url() );
$parsedUrlToCheck = wp_parse_url( $urlToCheck );
return ! empty( $parsedHomeUrl['host'] ) && ! empty( $parsedUrlToCheck['host'] )
? $parsedHomeUrl['host'] === $parsedUrlToCheck['host']
: false;
}
/**
* Helper for the rest url.
*
* @since 4.4.9
*
* @return string
*/
public function getRestUrl() {
$restUrl = get_rest_url();
if ( aioseo()->helpers->isWpmlActive() ) {
global $sitepress;
// Replace the rest url 'all' language prefix so our rest calls don't fail.
if (
is_object( $sitepress ) &&
method_exists( $sitepress, 'get_current_language' ) &&
method_exists( $sitepress, 'get_default_language' ) &&
'all' === $sitepress->get_current_language()
) {
$restUrl = str_replace(
get_home_url( null, '/all/' ),
get_home_url( null, '/' . $sitepress->get_default_language() . '/' ),
$restUrl
);
}
}
return $restUrl;
}
/**
* Exclude the home path from a full path.
*
* @since 1.2.3 Moved from aioseo-redirects.
* @version 4.5.8
*
* @param string $path The original path.
* @return string The path without WP's home path.
*/
public function excludeHomePath( $path ) {
return preg_replace( '@^' . $this->getHomePath() . '@', '/', (string) $path );
}
/**
* Get the canonical URL for a post.
* This is a duplicate of wp_get_canonical_url() with a fix for issue #6372 where
* posts with paginated comment pages return the wrong canonical URL due to how WordPress sets the cpage var.
* We can remove this once trac ticket 60806 is resolved.
*
* @since 4.6.9
*
* @param \WP_Post|int|null $post The post object or ID.
* @return string|false The post's canonical URL, or false if the post is not published.
*/
public function wpGetCanonicalUrl( $post = null ) {
$post = get_post( $post );
if ( ! $post ) {
return false;
}
if ( 'publish' !== $post->post_status ) {
return false;
}
$canonical_url = get_permalink( $post ); // phpcs:ignore Squiz.NamingConventions.ValidVariableName
// If a canonical is being generated for the current page, make sure it has pagination if needed.
if ( get_queried_object_id() === $post->ID ) {
$page = get_query_var( 'page', 0 );
if ( $page >= 2 ) {
if ( ! get_option( 'permalink_structure' ) ) {
$canonical_url = add_query_arg( 'page', $page, $canonical_url ); // phpcs:ignore Squiz.NamingConventions.ValidVariableName
} else {
$canonical_url = trailingslashit( $canonical_url ) . user_trailingslashit( $page, 'single_paged' ); // phpcs:ignore Squiz.NamingConventions.ValidVariableName
}
}
$cpage = aioseo()->helpers->getCommentPageNumber(); // We're calling our own function here to get the correct cpage number.
if ( $cpage ) {
$canonical_url = get_comments_pagenum_link( $cpage ); // phpcs:ignore Squiz.NamingConventions.ValidVariableName
}
}
return apply_filters( 'get_canonical_url', $canonical_url, $post ); // phpcs:ignore Squiz.NamingConventions.ValidVariableName
}
/**
* Checks if permalinks are enabled.
*
* @since 4.8.3
*
* @return bool Whether permalinks are enabled.
*/
public function usingPermalinks() {
global $wp_rewrite; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
return $wp_rewrite->using_permalinks(); // phpcs:ignore Squiz.NamingConventions.ValidVariableName
}
} Helpers/Request.php 0000666 00000003440 15114625461 0010321 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Parse the current request.
*
* @since 4.2.1
*/
trait Request {
/**
* Get the server port.
*
* @since 4.2.1
*
* @return string The server port.
*/
private function getServerPort() {
if (
empty( $_SERVER['SERVER_PORT'] ) ||
80 === (int) $_SERVER['SERVER_PORT'] ||
443 === (int) $_SERVER['SERVER_PORT']
) {
return '';
}
return ':' . (int) $_SERVER['SERVER_PORT'];
}
/**
* Get the protocol.
*
* @since 4.2.1
*
* @return string The protocol.
*/
private function getProtocol() {
return is_ssl() ? 'https' : 'http';
}
/**
* Get the server name (from $_SERVER['SERVER_NAME]), or use the request name ($_SERVER['HTTP_HOST']) if not present.
*
* @since 4.2.1
*
* @return string The server name.
*/
private function getServerName() {
$host = $this->getRequestServerName();
if ( isset( $_SERVER['SERVER_NAME'] ) ) {
$host = sanitize_text_field( wp_unslash( $_SERVER['SERVER_NAME'] ) ); // phpcs:ignore HM.Security.ValidatedSanitizedInput.InputNotSanitized
}
return $host;
}
/**
* Get the request server name (from $_SERVER['HTTP_HOST]).
*
* @since 4.2.1
*
* @return string The request server name.
*/
private function getRequestServerName() {
$host = '';
if ( isset( $_SERVER['HTTP_HOST'] ) ) {
$host = sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) );
}
return $host;
}
/**
* Retrieve the request URL.
*
* @since 4.2.1
*
* @return string The request URL.
*/
public function getRequestUrl() {
$url = '';
if ( isset( $_SERVER['REQUEST_URI'] ) ) {
$url = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) );
}
return rawurldecode( $url );
}
} Helpers/Url.php 0000666 00000022265 15114625461 0007441 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\Traits\Helpers;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Contains URL helper methods.
*
* @since 4.2.5
*/
trait Url {
/**
* Removes a query string parameter from a URL.
*
* @since 4.2.5
*
* @param string $url The url.
* @param array $parameters The parameter keys to remove.
* @return string The url without the parameters removed.
*/
public function urlRemoveQueryParameter( $url, $parameters ) {
$url = wp_parse_url( $url );
if ( ! empty( $url['query'] ) ) {
// Take the query string apart.
parse_str( $url['query'], $queryStringArray );
// Remove parameters.
foreach ( $parameters as $parameter ) {
if ( isset( $queryStringArray[ $parameter ] ) ) {
unset( $queryStringArray[ $parameter ] );
}
}
// Rebuild the query string.
$url['query'] = build_query( $queryStringArray );
// Rebuild the URL from parse_url.
$url = $this->buildUrl( $url );
}
return $url;
}
/**
* Builds a URL from a parse_url array.
*
* @since 4.2.5
*
* @param array $params The params array.
* @param array $include The keys to include [scheme, user, pass, host, port, path, query, fragment].
* @param array $exclude The keys to exclude [scheme, user, pass, host, port, path, query, fragment].
* @return string The built url.
*/
public function buildUrl( $params, $include = [], $exclude = [] ) {
if ( ! is_array( $params ) ) {
return $params;
}
if ( ! empty( $include ) ) {
foreach ( array_keys( $params ) as $includeKey ) {
if ( ! in_array( $includeKey, $include, true ) ) {
unset( $params[ $includeKey ] );
}
}
}
if ( ! empty( $exclude ) ) {
foreach ( array_keys( $params ) as $excludeKey ) {
if ( in_array( $excludeKey, $exclude, true ) ) {
unset( $params[ $excludeKey ] );
}
}
}
$url = '';
if ( ! empty( $params['scheme'] ) ) {
$url .= $params['scheme'] . '://';
}
if ( ! empty( $params['user'] ) ) {
$url .= $params['user'];
if ( isset( $params['pass'] ) ) {
$url .= ':' . $params['pass'];
}
$url .= '@';
}
if ( ! empty( $params['host'] ) ) {
$url .= $params['host'];
}
if ( ! empty( $params['port'] ) ) {
$url .= ':' . $params['port'];
}
if ( ! empty( $params['path'] ) ) {
$url .= $params['path'];
}
if ( ! empty( $params['query'] ) ) {
$url .= '?' . $params['query'];
}
if ( ! empty( $params['fragment'] ) ) {
$url .= '#' . $params['fragment'];
}
return $url;
}
/**
* Checks if a URL is considered a local one.
*
* @since 4.5.9
*
* @param string $url The URL.
* @return bool Whether the URL is a local one or not.
*/
public function isLocalUrl( $url ) {
$domain = wp_parse_url( $url, PHP_URL_HOST );
if ( empty( $domain ) ) {
return false;
}
if (
false !== ip2long( $domain ) &&
! filter_var( $domain, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE )
) {
return true;
}
if ( 'localhost' === $domain ) {
return true;
}
if ( ! $this->isValidDomain( $domain ) ) {
return true;
}
$tldsToCheck = [
'.local',
'.test',
];
foreach ( $tldsToCheck as $tld ) {
if ( false !== strpos( $this->getTld( $domain ), $tld ) ) {
return true;
}
}
if ( substr_count( $domain, '.' ) > 1 ) {
$subdomainsToCheck = [
'dev',
'development',
'staging',
'stage',
'test',
'staging*',
'*staging',
'dev*',
'*dev',
'test*',
'*test'
];
foreach ( $subdomainsToCheck as $subdomain ) {
foreach ( $this->getSubdomains( $domain ) as $sd ) {
$subdomain = str_replace( '.', '(.)', $subdomain );
$subdomain = str_replace( [ '*', '(.)' ], '(.*)', $subdomain );
if ( preg_match( '/^(' . $subdomain . ')$/', (string) $sd ) ) {
return true;
}
}
}
}
return false;
}
/**
* Checks if a domain is valid.
*
* @since 4.5.9
*
* @param string $domain The domain.
* @return bool Whether the domain is valid or not.
*/
private function isValidDomain( $domain ) {
// In case there are unicode characters, convert it into
// IDNA ASCII URLs
if ( function_exists( 'idn_to_ascii' ) ) {
$domain = idn_to_ascii( $domain, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46 );
}
if ( ! $domain ) {
return false;
}
$domain = preg_replace( '/^\*\.+/', '', (string) $domain );
return preg_match( '/^(?!\-)(?:[a-z\d\-]{0,62}[a-z\d]\.){1,126}(?!\d+)[a-z\d]{1,63}$/i', (string) $domain );
}
/**
* Checks if a domain is valid and optionally contains paths at the end.
*
* @since 4.7.7
*
* @param string $domain The domain.
* @return bool Whether the domain is valid or not.
*/
private function isDomainWithPaths( $domain ) {
// In case there are unicode characters, convert it into IDNA ASCII URLs.
if ( function_exists( 'idn_to_ascii' ) ) {
$domain = idn_to_ascii( $domain, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46 );
}
if ( ! $domain ) {
return false;
}
$domain = preg_replace( '/^\*\.+/', '', $domain );
return preg_match( '/^(?!\-)(?:[a-z\d\-]{0,62}[a-z\d]\.){1,126}(?!\d+)[a-z\d]{1,63}(\/[a-z\d\-\/]*)?$/i', $domain );
}
/**
* Returns a single string of all subdomains associated with this domain.
* Example 1: www
* Example 2: ww2.www
*
* @since 4.5.9
*
* @return array The subdomains associated with this domain.
*/
public function getSubdomains( $domain ) {
// If we can't find a TLD, we won't be able to parse a subdomain.
if ( empty( $this->getTld( $domain ) ) ) {
return [];
}
// Return any subdomains as an array.
return array_filter( explode( '.', rtrim( strstr( $domain, $this->getTld( $domain ), true ), '.' ) ) );
}
/**
* Returns the TLD associated with the given domain.
*
* @since 4.5.9
*
* @param string $domain The domain.
* @return string The TLD.
*/
public function getTld( $domain ) {
if ( preg_match( '/(?P<tld>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', (string) $domain, $matches ) ) {
return $matches['tld'];
}
return $domain;
}
/**
* Returns a decoded URL string.
*
* @since 4.6.7
*
* @param string $url The URL string.
* @return string The decoded URL.
*/
public function decodeUrl( $url ) {
// Ensure input is a string to prevent errors.
if ( ! is_string( $url ) ) {
return $url;
}
// Set a reasonable iteration limit to prevent infinite loops.
$maxIterations = 10;
$iterations = 0;
$decodedUrl = rawurldecode( $url );
while ( $decodedUrl !== $url && $iterations < $maxIterations ) {
$url = $decodedUrl;
$decodedUrl = rawurldecode( $url );
$iterations++;
}
return $decodedUrl;
}
/**
* Redirects to a specific URL.
*
* @since 4.8.0
*
* @param string $url The URL to redirect to.
* @param int $status The status code to use.
* @param string $reason The reason for redirecting.
*
* @return void
*/
public function redirect( $url, $status = 301, $reason = '' ) {
$redirectBy = 'AIOSEO';
if ( ! empty( $reason ) ) {
$redirectBy .= ': ' . $reason;
}
wp_safe_redirect( $url, $status, $redirectBy );
exit;
}
/**
* Checks if the given URL is external.
*
* @since 4.8.3
*
* @param string $url The URL to check.
* @return bool Whether the URL is external or not.
*/
public function isExternalUrl( $url ) {
$parsedUrl = wp_parse_url( $url );
if ( ! $parsedUrl ) {
return false;
}
static $parsedSiteUrl = null;
if ( ! $parsedSiteUrl ) {
$parsedSiteUrl = wp_parse_url( site_url() );
}
return $parsedSiteUrl['host'] !== $parsedUrl['host'];
}
/**
* Checks if the given URL is relative.
*
* @since 4.8.3
*
* @param string $url The URL to check.
* @return bool Whether the URL is relative or not.
*/
public function isRelativeUrl( $url ) {
$parsedUrl = wp_parse_url( $url );
if ( ! $parsedUrl ) {
return false;
}
return empty( $parsedUrl['scheme'] ) && empty( $parsedUrl['host'] );
}
/**
* Makes the given URL relative.
*
* @since 4.8.3
*
* @param string $url The URL to make relative.
* @return string The relative URL.
*/
public function makeUrlRelative( $url ) {
$parsedUrl = wp_parse_url( $url );
if ( ! $parsedUrl ) {
return $url;
}
static $parsedSiteUrl = null;
if ( ! $parsedSiteUrl ) {
$parsedSiteUrl = wp_parse_url( site_url() );
}
if ( $parsedSiteUrl['host'] !== $parsedUrl['host'] ) {
return $url;
}
return ! empty( $parsedUrl['path'] ) ? $parsedUrl['path'] : $url;
}
/**
* Formats a given URL as an absolute URL if it is relative.
*
* @since 4.0.0
* @version 4.8.3 Moved from WpUri trait to Url trait.
*
* @param string $url The URL.
* @return string The absolute URL.
*/
public function makeUrlAbsolute( $url ) {
if ( 0 !== strpos( $url, 'http' ) && '/' !== $url ) {
$url = $this->sanitizeDomain( $url );
if ( $this->isDomainWithPaths( $url ) ) {
$scheme = wp_parse_url( site_url(), PHP_URL_SCHEME );
$url = $scheme . '://' . $url;
} elseif ( 0 === strpos( $url, '//' ) ) {
$scheme = wp_parse_url( site_url(), PHP_URL_SCHEME );
$url = $scheme . ':' . $url;
} else {
$url = site_url( $url );
}
}
return $url;
}
}