Modularización de GKACHELE SaaS
This commit is contained in:
309
wp-content/plugins/akismet/class-akismet-compatible-plugins.php
Normal file
309
wp-content/plugins/akismet/class-akismet-compatible-plugins.php
Normal file
@@ -0,0 +1,309 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles compatibility checks for Akismet with other plugins.
|
||||
*
|
||||
* @package Akismet
|
||||
* @since 5.4.0
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
// Following existing Akismet convention for file naming.
|
||||
// phpcs:ignore WordPress.Files.FileName.NotHyphenatedLowercase
|
||||
|
||||
/**
|
||||
* Class for managing compatibility checks for Akismet with other plugins.
|
||||
*
|
||||
* This class includes methods for determining whether specific plugins are
|
||||
* installed and active relative to the ability to work with Akismet.
|
||||
*/
|
||||
class Akismet_Compatible_Plugins {
|
||||
/**
|
||||
* The endpoint for the compatible plugins API.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected const COMPATIBLE_PLUGIN_ENDPOINT = 'https://rest.akismet.com/1.2/compatible-plugins';
|
||||
|
||||
/**
|
||||
* The error key for the compatible plugins API error.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected const COMPATIBLE_PLUGIN_API_ERROR = 'akismet_compatible_plugins_api_error';
|
||||
|
||||
/**
|
||||
* The valid fields for a compatible plugin object.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected const COMPATIBLE_PLUGIN_FIELDS = array(
|
||||
'slug',
|
||||
'name',
|
||||
'logo',
|
||||
'help_url',
|
||||
'path',
|
||||
);
|
||||
|
||||
/**
|
||||
* The cache key for the compatible plugins.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected const CACHE_KEY = 'akismet_compatible_plugin_list';
|
||||
|
||||
|
||||
/**
|
||||
* How many plugins should be visible by default?
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public const DEFAULT_VISIBLE_PLUGIN_COUNT = 2;
|
||||
|
||||
/**
|
||||
* Get the list of active, installed compatible plugins.
|
||||
*
|
||||
* @param bool $bypass_cache Whether to bypass the cache and fetch fresh data.
|
||||
* @return WP_Error|array {
|
||||
* Array of active, installed compatible plugins with their metadata.
|
||||
* @type string $name The display name of the plugin
|
||||
* @type string $help_url URL to the plugin's help documentation
|
||||
* @type string $logo URL or path to the plugin's logo
|
||||
* }
|
||||
*/
|
||||
public static function get_installed_compatible_plugins( bool $bypass_cache = false ) {
|
||||
// Retrieve and validate the full compatible plugins list.
|
||||
$compatible_plugins = static::get_compatible_plugins( $bypass_cache );
|
||||
|
||||
if ( empty( $compatible_plugins ) ) {
|
||||
return new WP_Error(
|
||||
self::COMPATIBLE_PLUGIN_API_ERROR,
|
||||
__( 'Error getting compatible plugins.', 'akismet' )
|
||||
);
|
||||
}
|
||||
|
||||
// Retrieve all installed plugins once.
|
||||
$all_plugins = get_plugins();
|
||||
|
||||
// Build list of compatible plugins that are both installed and active.
|
||||
$active_compatible_plugins = array();
|
||||
|
||||
foreach ( $compatible_plugins as $slug => $data ) {
|
||||
$path = $data['path'];
|
||||
// Skip if not installed.
|
||||
if ( ! isset( $all_plugins[ $path ] ) ) {
|
||||
continue;
|
||||
}
|
||||
// Check activation: per-site or network-wide (multisite).
|
||||
$site_active = is_plugin_active( $path );
|
||||
$network_active = is_multisite() && is_plugin_active_for_network( $path );
|
||||
if ( $site_active || $network_active ) {
|
||||
$active_compatible_plugins[ $slug ] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
return $active_compatible_plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes action hooks for the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(): void {
|
||||
add_action( 'activated_plugin', array( static::class, 'handle_plugin_change' ), true );
|
||||
add_action( 'deactivated_plugin', array( static::class, 'handle_plugin_change' ), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles plugin activation and deactivation events.
|
||||
*
|
||||
* @param string $plugin The path to the main plugin file from plugins directory.
|
||||
* @return void
|
||||
*/
|
||||
public static function handle_plugin_change( string $plugin ): void {
|
||||
$cached_plugins = static::get_cached_plugins();
|
||||
|
||||
/**
|
||||
* Terminate if nothing's cached.
|
||||
*/
|
||||
if ( false === $cached_plugins ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$plugin_change_should_invalidate_cache = in_array( $plugin, array_column( $cached_plugins, 'path' ) );
|
||||
|
||||
/**
|
||||
* Purge the cache if the plugin is activated or deactivated.
|
||||
*/
|
||||
if ( $plugin_change_should_invalidate_cache ) {
|
||||
static::purge_cache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets plugins that are compatible with Akismet from the Akismet API.
|
||||
*
|
||||
* @param bool $bypass_cache Whether to bypass the cache and fetch fresh data.
|
||||
* @return array
|
||||
*/
|
||||
private static function get_compatible_plugins( bool $bypass_cache = false ): array {
|
||||
// Return cached result if present (false => cache miss; empty array is valid).
|
||||
$cached_plugins = static::get_cached_plugins();
|
||||
|
||||
if ( false !== $cached_plugins && ! $bypass_cache ) {
|
||||
return $cached_plugins;
|
||||
}
|
||||
|
||||
$response = wp_remote_get(
|
||||
self::COMPATIBLE_PLUGIN_ENDPOINT
|
||||
);
|
||||
|
||||
$sanitized = static::validate_compatible_plugin_response( $response );
|
||||
|
||||
if ( false === $sanitized ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets local static associative array of plugin data keyed by plugin slug.
|
||||
*/
|
||||
$compatible_plugins = array();
|
||||
|
||||
foreach ( $sanitized as $plugin ) {
|
||||
$compatible_plugins[ $plugin['slug'] ] = $plugin;
|
||||
}
|
||||
|
||||
static::set_cached_plugins( $compatible_plugins );
|
||||
|
||||
return $compatible_plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a response object from the Compatible Plugins API.
|
||||
*
|
||||
* @param array|WP_Error $response
|
||||
* @return array|false
|
||||
*/
|
||||
private static function validate_compatible_plugin_response( $response ) {
|
||||
/**
|
||||
* Terminates the function if the response is a WP_Error object.
|
||||
*/
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The response returned is an array of header + body string data.
|
||||
* This pops off the body string for processing.
|
||||
*/
|
||||
$response_body = wp_remote_retrieve_body( $response );
|
||||
|
||||
if ( empty( $response_body ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$plugins = json_decode( $response_body, true );
|
||||
|
||||
if ( false === is_array( $plugins ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( $plugins as $plugin ) {
|
||||
if ( ! is_array( $plugin ) ) {
|
||||
/**
|
||||
* Skips to the next iteration if for some reason the plugin is not an array.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure that the plugin config read in from the API has all the required fields.
|
||||
$plugin_key_count = count(
|
||||
array_intersect_key( $plugin, array_flip( static::COMPATIBLE_PLUGIN_FIELDS ) )
|
||||
);
|
||||
|
||||
$does_not_have_all_required_fields = ! (
|
||||
$plugin_key_count === count( static::COMPATIBLE_PLUGIN_FIELDS )
|
||||
);
|
||||
|
||||
if ( $does_not_have_all_required_fields ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( false === static::has_valid_plugin_path( $plugin['path'] ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return static::sanitize_compatible_plugin_response( $plugins );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a plugin path format.
|
||||
*
|
||||
* The path should be in the format of 'plugin-name/plugin-name.php'.
|
||||
* Allows alphanumeric characters, dashes, underscores, and optional dots in folder names.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
private static function has_valid_plugin_path( string $path ): bool {
|
||||
return preg_match( '/^[a-zA-Z0-9._-]+\/[a-zA-Z0-9_-]+\.php$/', $path ) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes a response object from the Compatible Plugins API.
|
||||
*
|
||||
* @param array $plugins
|
||||
* @return array
|
||||
*/
|
||||
private static function sanitize_compatible_plugin_response( array $plugins = array() ): array {
|
||||
foreach ( $plugins as $key => $plugin ) {
|
||||
$plugins[ $key ] = array_map( 'sanitize_text_field', $plugin );
|
||||
$plugins[ $key ]['help_url'] = sanitize_url( $plugins[ $key ]['help_url'] );
|
||||
$plugins[ $key ]['logo'] = sanitize_url( $plugins[ $key ]['logo'] );
|
||||
}
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $plugins
|
||||
* @return bool
|
||||
*/
|
||||
private static function set_cached_plugins( array $plugins ): bool {
|
||||
$_blog_id = (int) get_current_blog_id();
|
||||
|
||||
return set_transient(
|
||||
static::CACHE_KEY . "_$_blog_id",
|
||||
$plugins,
|
||||
DAY_IN_SECONDS
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to get cached compatible plugins.
|
||||
*
|
||||
* @return mixed|false
|
||||
*/
|
||||
private static function get_cached_plugins() {
|
||||
$_blog_id = (int) get_current_blog_id();
|
||||
|
||||
return get_transient(
|
||||
static::CACHE_KEY . "_$_blog_id"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges the cache for the compatible plugins.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function purge_cache(): bool {
|
||||
$_blog_id = (int) get_current_blog_id();
|
||||
|
||||
return delete_transient(
|
||||
static::CACHE_KEY . "_$_blog_id"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user