Adds a new 'S3-Compatible Storage' provider that works with any
S3-API-compatible object storage service, including MinIO, Ceph,
Cloudflare R2, Backblaze B2, and others.
Changes:
- New provider class: classes/providers/storage/s3-compatible-provider.php
- Provider key: s3compatible
- Reads user-configured endpoint URL from settings
- Uses path-style URL access (required by most S3-compatible services)
- Supports credentials via AS3CF_S3COMPAT_ACCESS_KEY_ID /
AS3CF_S3COMPAT_SECRET_ACCESS_KEY wp-config.php constants
- Disables AWS-specific features (Block Public Access, Object Ownership)
- New provider SVG icons (s3compatible.svg, -link.svg, -round.svg)
- Registered provider in main plugin class with endpoint setting support
- Updated StorageProviderSubPage to show endpoint URL input for S3-compatible
- Built pro settings bundle with rollup (Svelte 4.2.19)
- Added package.json and updated rollup.config.mjs for pro-only builds
561 lines
17 KiB
PHP
561 lines
17 KiB
PHP
<?php
|
|
/**
|
|
* Licence Class
|
|
*
|
|
* @package deliciousbrains
|
|
* @subpackage api/licences
|
|
* @copyright Copyright (c) 2015, Delicious Brains
|
|
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
|
* @since 0.1
|
|
*/
|
|
|
|
// Exit if accessed directly
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Delicious_Brains_API_Licences Class
|
|
*
|
|
* This class handles the licencing calls with the Delicious Brains WooCommerce API
|
|
*
|
|
* @since 0.1
|
|
*/
|
|
abstract class Delicious_Brains_API_Licences extends Delicious_Brains_API_Base {
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
public $addons;
|
|
|
|
/**
|
|
* @var Delicious_Brains_API_Updates
|
|
*/
|
|
protected $updates;
|
|
|
|
/**
|
|
* @param Delicious_Brains_API_Plugin $plugin
|
|
*/
|
|
function __construct( Delicious_Brains_API_Plugin $plugin ) {
|
|
parent::__construct( $plugin );
|
|
|
|
$this->actions();
|
|
$this->set_addons();
|
|
|
|
// Fire up the plugin updates
|
|
$this->updates = new Delicious_Brains_API_Updates( $this );
|
|
}
|
|
|
|
/**
|
|
* Fire up the actions for the licenses
|
|
*/
|
|
public function actions() {
|
|
add_action( $this->plugin->load_hook, array( $this, 'remove_licence_when_constant_set' ) );
|
|
add_action( $this->plugin->load_hook, array( $this, 'http_disable_ssl' ) );
|
|
add_action( $this->plugin->load_hook, array( $this, 'http_refresh_licence' ) );
|
|
|
|
add_action( 'wp_ajax_' . $this->plugin->prefix . '_check_licence', array( $this, 'ajax_check_licence' ) );
|
|
add_action( 'wp_ajax_' . $this->plugin->prefix . '_reactivate_licence', array( $this, 'ajax_reactivate_licence' ) );
|
|
}
|
|
|
|
/**
|
|
* Define the addons available for the plugin
|
|
*
|
|
* @return array
|
|
*/
|
|
function set_addons() {
|
|
if ( is_null( $this->addons ) ) {
|
|
$meta_key = $this->plugin->get_global_meta_key();
|
|
|
|
$addons = array();
|
|
|
|
if ( isset( $GLOBALS[ $meta_key ][ $this->plugin->slug ]['supported_addon_versions'] ) ) {
|
|
$available_addons = get_site_transient( $this->plugin->prefix . '_addons_available' );
|
|
|
|
foreach ( $GLOBALS[ $meta_key ][ $this->plugin->slug ]['supported_addon_versions'] as $addon => $version ) {
|
|
$basename = $this->plugin->get_plugin_basename( $addon );
|
|
$name = $this->plugin->get_plugin_name( $addon );
|
|
$installed = file_exists( WP_PLUGIN_DIR . '/' . $basename );
|
|
|
|
$addons[ $basename ] = array(
|
|
'name' => $name,
|
|
'slug' => $addon,
|
|
'basename' => $basename,
|
|
'required_version' => $version,
|
|
'available' => ( false === $available_addons || isset( $available_addons[ $addon ] ) ),
|
|
'installed' => $installed,
|
|
);
|
|
}
|
|
}
|
|
|
|
$this->addons = $addons;
|
|
}
|
|
|
|
return $this->addons;
|
|
}
|
|
|
|
/**
|
|
* Get method for returning the license key from the database
|
|
* that must be defined by the extending class
|
|
*
|
|
* @return mixed
|
|
*/
|
|
abstract protected function get_plugin_licence_key();
|
|
|
|
/**
|
|
* Set method for saving the license key to the database
|
|
* that must be defined by the extending class
|
|
*
|
|
* @param string $key
|
|
*
|
|
* @return mixed
|
|
*/
|
|
abstract protected function set_plugin_licence_key( $key );
|
|
|
|
/**
|
|
* Get the name of the license constant
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function get_licence_constant_name() {
|
|
return strtoupper( $this->plugin->prefix ) . '_LICENCE';
|
|
}
|
|
|
|
/**
|
|
* Is the license key defined as a constant?
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function is_licence_constant() {
|
|
return defined( $this->get_licence_constant_name() );
|
|
}
|
|
|
|
/**
|
|
* Get the license key either from a constant or saved by the plugin
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_licence_key() {
|
|
if ( $this->is_licence_constant() ) {
|
|
$licence = constant( $this->get_licence_constant_name() );
|
|
} else {
|
|
$licence = $this->get_plugin_licence_key();
|
|
}
|
|
|
|
return $licence;
|
|
}
|
|
|
|
/**
|
|
* Sets the licence key
|
|
*
|
|
* @param string $key
|
|
*/
|
|
protected function set_licence_key( $key ) {
|
|
$this->set_plugin_licence_key( $key );
|
|
}
|
|
|
|
/**
|
|
* Checks whether the saved licence has expired or not.
|
|
*
|
|
* @param bool $skip_transient_check
|
|
* @param bool $skip_expired_check
|
|
* @param array $licence_response Optional pre-fetched licence response data.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function is_valid_licence( $skip_transient_check = false, $skip_expired_check = true, $licence_response = array() ) {
|
|
if ( ! empty( $licence_response ) && is_array( $licence_response ) ) {
|
|
$response = $licence_response;
|
|
} else {
|
|
$response = $this->is_licence_expired( $skip_transient_check );
|
|
}
|
|
|
|
if ( isset( $response['dbrains_api_down'] ) ) {
|
|
return true;
|
|
}
|
|
|
|
if ( $this->plugin->expired_licence_is_valid && isset( $response['errors']['subscription_expired'] ) && 1 === count( $response['errors'] ) ) {
|
|
// Maybe don't cripple the plugin's functionality if the user's licence is expired.
|
|
return $skip_expired_check;
|
|
}
|
|
|
|
return ! isset( $response['errors'] );
|
|
}
|
|
|
|
/**
|
|
* Check to see if the license has expired
|
|
*
|
|
* @param bool $skip_transient_check
|
|
*
|
|
* @return array|mixed
|
|
*/
|
|
public function is_licence_expired( $skip_transient_check = false ) {
|
|
$licence = $this->get_licence_key();
|
|
|
|
if ( empty( $licence ) ) {
|
|
$settings_link = sprintf( '<a href="%s" class="js-action-link as3cf-enter-licence">%s</a>', $this->admin_url( $this->plugin->settings_url_path ) . $this->plugin->settings_url_hash, __( 'enter your license key' ) );
|
|
$message = sprintf( __( 'To finish activating %1$s, %2$s. If you don\'t have a license key, you may <a href="%3$s">purchase one</a>.' ), $this->plugin->name, $settings_link, $this->plugin->purchase_url );
|
|
|
|
return array( 'errors' => array( 'no_licence' => $message ) );
|
|
}
|
|
|
|
return json_decode( $this->check_licence( $licence ), true );
|
|
}
|
|
|
|
/**
|
|
* Check the license validity and fetch a response from the Delicious Brains API
|
|
*
|
|
* @param string $licence_key
|
|
*
|
|
* @return bool|mixed
|
|
*/
|
|
public function check_licence( $licence_key ) {
|
|
if ( empty( $licence_key ) ) {
|
|
return false;
|
|
}
|
|
|
|
$response = [];
|
|
$response['addon_list'] = '';
|
|
$response['addons_available_list'] = '';
|
|
$response = json_encode($response);
|
|
$response = $this->store_licence_addon_data( $response, true );
|
|
|
|
$response = apply_filters( $this->plugin->prefix . '_check_licence_response', $response );
|
|
|
|
set_site_transient( $this->plugin->prefix . '_licence_response', $response, $this->transient_timeout );
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Process and store the Addon data after a license request
|
|
*
|
|
* @param string|array $response
|
|
* @param bool $encoded
|
|
*
|
|
* @return array|mixed|string|void
|
|
*/
|
|
protected function store_licence_addon_data( $response, $encoded = false ) {
|
|
$decoded_response = $encoded ? json_decode( $response, ARRAY_A ) : $response;
|
|
|
|
if ( isset( $decoded_response['addon_list'] ) ) {
|
|
// Save the addons list for use when installing
|
|
// Don't really need to expire it ever, but let's clean it up after 60 days
|
|
set_site_transient( $this->plugin->prefix . '_addons', $decoded_response['addon_list'], DAY_IN_SECONDS * 60 );
|
|
}
|
|
|
|
if ( isset( $decoded_response['addons_available_list'] ) ) {
|
|
// Save the available addons list for use
|
|
set_site_transient( $this->plugin->prefix . '_addons_available', $decoded_response['addons_available_list'], DAY_IN_SECONDS * 60 );
|
|
}
|
|
|
|
$response = $encoded ? json_encode( $decoded_response ) : $decoded_response;
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Remove the license from the settings if defined as a constant
|
|
*/
|
|
public function remove_licence_when_constant_set() {
|
|
$licence_key = $this->get_plugin_licence_key();
|
|
// Remove licence from the database if constant is set
|
|
if ( $this->is_licence_constant() && ! empty( $licence_key ) ) {
|
|
$this->set_licence_key( '' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a formatted message dependent on the status of the licence.
|
|
*
|
|
* @param bool|array $trans
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_licence_status_message( $trans = false ) {
|
|
$licence = $this->get_licence_key();
|
|
$api_response_provided = true;
|
|
|
|
if ( empty( $licence ) && ! $trans ) {
|
|
$message = sprintf( __( '<strong>Activate Your License</strong> — Please <a href="#" class="%s">enter your license key</a>.' ), 'js-action-link enter-licence' );
|
|
|
|
return $message;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return a license key formatted with HTML to mask all but the last part
|
|
*
|
|
* @param string $licence
|
|
*
|
|
* @return string
|
|
*/
|
|
public function mask_licence( $licence ) {
|
|
$licence_parts = explode( '-', $licence );
|
|
$i = count( $licence_parts ) - 1;
|
|
$masked_licence = '';
|
|
|
|
foreach ( $licence_parts as $licence_part ) {
|
|
if ( $i == 0 ) {
|
|
$masked_licence .= $licence_part;
|
|
continue;
|
|
}
|
|
|
|
$masked_licence .= '<span class="bull">';
|
|
$masked_licence .= str_repeat( '•', strlen( $licence_part ) ) . '</span>–';
|
|
--$i;
|
|
}
|
|
|
|
return $masked_licence;
|
|
}
|
|
|
|
/**
|
|
* Helper method to display markup of the masked license
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_formatted_masked_licence() {
|
|
return sprintf(
|
|
'<p class="masked-licence">%s <a href="%s">%s</a></p>',
|
|
$this->mask_licence( $this->get_plugin_licence_key() ),
|
|
$this->admin_url( $this->plugin->settings_url_path . '&nonce=' . wp_create_nonce( $this->plugin->prefix . '-remove-licence' ) . '&' . $this->plugin->prefix . '-remove-licence=1' . $this->plugin->settings_url_hash ),
|
|
_x( 'Remove', 'Delete license' )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check for {prefix}-disable-ssl and related nonce
|
|
* if found temporarily disable ssl via transient
|
|
*
|
|
* @return void
|
|
*/
|
|
function http_disable_ssl() {
|
|
if ( isset( $_GET[ $this->plugin->prefix . '-disable-ssl' ] ) && wp_verify_nonce( $_GET['nonce'], $this->plugin->prefix . '-disable-ssl' ) ) { // input var okay
|
|
set_site_transient( $this->plugin->prefix . '_temporarily_disable_ssl', '1', DAY_IN_SECONDS * 30 ); // 30 days
|
|
$hash = ( isset( $_GET['hash'] ) ) ? '#' . sanitize_title( $_GET['hash'] ) : ''; // input var okay
|
|
// delete the licence transient as we want to attempt to fetch the licence details again
|
|
delete_site_transient( $this->plugin->prefix . '_licence_response' );
|
|
// redirecting here because we don't want to keep the query string in the web browsers address bar
|
|
wp_redirect( $this->admin_url( $this->plugin->settings_url_path . $hash ) );
|
|
exit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove the license.
|
|
*/
|
|
public function remove() {
|
|
$this->set_licence_key( '' );
|
|
|
|
// Delete these transients as they contain information only valid for authenticated licence holders.
|
|
delete_site_transient( 'update_plugins' );
|
|
delete_site_transient( $this->plugin->prefix . '_upgrade_data' );
|
|
delete_site_transient( $this->plugin->prefix . '_licence_response' );
|
|
delete_site_transient( $this->plugin->prefix . '_addons_available' );
|
|
delete_site_transient( $this->plugin->prefix . '_licence_media_check' );
|
|
|
|
do_action( $this->plugin->prefix . '_http_remove_licence' );
|
|
}
|
|
|
|
/**
|
|
* Check for {prefix}-check-licence and related nonce
|
|
* if found refresh licence details
|
|
*
|
|
* @return void
|
|
*/
|
|
function http_refresh_licence() {
|
|
if ( isset( $_GET[ $this->plugin->prefix . '-check-licence' ] ) && wp_verify_nonce( $_GET['nonce'], $this->plugin->prefix . '-check-licence' ) ) { // input var okay
|
|
$hash = ( isset( $_GET['hash'] ) ) ? '#' . sanitize_title( $_GET['hash'] ) : ''; // input var okay
|
|
// delete the licence transient as we want to attempt to fetch the licence details again
|
|
delete_site_transient( $this->plugin->prefix . '_licence_response' );
|
|
do_action( $this->plugin->prefix . '_http_refresh_licence' );
|
|
|
|
$sendback = $this->admin_url( $this->plugin->settings_url_path . $hash );
|
|
if ( isset( $_GET['sendback'] ) ) {
|
|
$sendback = $_GET['sendback'];
|
|
}
|
|
|
|
// redirecting because we don't want to keep the query string in the web browsers address bar
|
|
wp_redirect( esc_url_raw( $sendback ) );
|
|
exit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Activate a licence.
|
|
*
|
|
* @param string $licence_key
|
|
*
|
|
* @return bool|WP_Error
|
|
*/
|
|
public function activate( $licence_key ) {
|
|
if ( empty( $licence_key ) ) {
|
|
return new WP_Error( 'missing-licence-key', __( 'Licence Key not supplied.' ) );
|
|
}
|
|
|
|
// Regardless of whether the license is valid or not, save it and let status/errors deal with fallout.
|
|
// This way license status is handled the same whether saved in db or set via define.
|
|
$this->set_licence_key( $licence_key );
|
|
|
|
$api_response_json = $this->activate_licence( $licence_key, $this->home_url );
|
|
set_site_transient( $this->plugin->prefix . '_licence_response', $api_response_json, $this->transient_timeout );
|
|
|
|
$api_response = json_decode( $api_response_json, true );
|
|
$api_down = ! empty( $api_response['dbrains_api_down'] );
|
|
$errors = isset( $api_response['errors'] ) ? $api_response['errors'] : array();
|
|
|
|
// Remove insignificant errors.
|
|
unset( $errors['activation_deactivated'], $errors['subscription_expired'] );
|
|
|
|
if ( empty( $errors ) && ! $api_down ) {
|
|
return true;
|
|
}
|
|
|
|
// For some reason we couldn't activate the licence, reason saved in transient.
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* AJAX handler for checking a licence.
|
|
*/
|
|
public function ajax_check_licence() {
|
|
$this->check_ajax_referer( 'check-licence' );
|
|
|
|
$decoded_response = $this->check( true );
|
|
$decoded_response = apply_filters( $this->plugin->prefix . '_ajax_check_licence_response', $decoded_response );
|
|
|
|
wp_send_json( $decoded_response );
|
|
}
|
|
|
|
/**
|
|
* Check a licence.
|
|
*
|
|
* @param bool $skip_transient_check
|
|
*
|
|
* @return array
|
|
*/
|
|
public function check( $skip_transient_check = false ) {
|
|
$decoded_response = $this->is_licence_expired( $skip_transient_check );
|
|
|
|
if ( ! empty( $decoded_response['dbrains_api_down'] ) ) {
|
|
$decoded_response['message'] = $this->get_default_help_message();
|
|
} elseif ( ! empty( $decoded_response['errors'] ) ) {
|
|
$decoded_response['htmlErrors'] = array( sprintf( '<div class="notification-message warning-notice inline-message invalid-licence">%s</div>', $this->get_licence_status_message() ) );
|
|
} elseif ( ! empty( $decoded_response['message'] ) && ! get_site_transient( $this->plugin->prefix . '_help_message' ) ) {
|
|
set_site_transient( $this->plugin->prefix . '_help_message', $decoded_response['message'], $this->transient_timeout );
|
|
}
|
|
|
|
return $decoded_response;
|
|
}
|
|
|
|
/**
|
|
* Get previously saved or fallback help message/form.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_default_help_message() {
|
|
$help_message = get_site_transient( $this->plugin->prefix . '_help_message' );
|
|
|
|
if ( ! $help_message ) {
|
|
ob_start();
|
|
?>
|
|
<p><?php _e( 'A problem occurred when trying to get the support request form.', 'amazon-s3-and-cloudfront' ); ?></p>
|
|
<p><?php _e( 'If you have an <strong>active license</strong>, you may send an email to the following address.' ); ?></p>
|
|
<p>
|
|
<strong><?php _e( 'Please download the below Diagnostic Info and attach it to your email.' ); ?></strong>
|
|
</p>
|
|
<p class="email">
|
|
<a class="button" href="mailto:<?php echo $this->plugin->get_email_address_name(); ?>@deliciousbrains.com">
|
|
<?php echo $this->plugin->get_email_address_name(); ?>@deliciousbrains.com
|
|
</a>
|
|
</p>
|
|
<?php
|
|
$help_message = ob_get_clean();
|
|
}
|
|
|
|
return $help_message;
|
|
}
|
|
|
|
/**
|
|
* AJAX handler for reactivating this instance for the activated license
|
|
*/
|
|
public function ajax_reactivate_licence() {
|
|
$this->check_ajax_referer( 'reactivate-licence' );
|
|
|
|
$return = array();
|
|
|
|
$response = $this->reactivate_licence( $this->get_licence_key(), $this->home_url );
|
|
|
|
$decoded_response = json_decode( $response, true );
|
|
|
|
if ( isset( $decoded_response['dbrains_api_down'] ) ) {
|
|
$return['dbrains_api_down'] = 1;
|
|
|
|
$return['body'] = $decoded_response['dbrains_api_down'];
|
|
$this->end_ajax( json_encode( $return ) );
|
|
}
|
|
|
|
if ( isset( $decoded_response['errors'] ) ) {
|
|
$return[ $this->plugin->prefix . '_error' ] = 1;
|
|
|
|
$return['body'] = reset( $decoded_response['errors'] );
|
|
$this->log_error( $return['body'], $decoded_response );
|
|
$this->end_ajax( json_encode( $return ) );
|
|
}
|
|
|
|
delete_site_transient( $this->plugin->prefix . '_upgrade_data' );
|
|
delete_site_transient( $this->plugin->prefix . '_licence_response' );
|
|
|
|
$this->end_ajax( json_encode( array() ) );
|
|
}
|
|
|
|
/**
|
|
* Helper to end an AJAX request
|
|
*
|
|
* @param bool|string $return
|
|
*/
|
|
protected function end_ajax( $return = false ) {
|
|
echo ( false === $return ) ? '' : $return;
|
|
wp_die();
|
|
}
|
|
|
|
/**
|
|
* Custom verification of the AJAX request to prevent processing requests
|
|
*
|
|
* @param string $action Action nonce name
|
|
*/
|
|
protected function check_ajax_referer( $action ) {
|
|
$result = check_ajax_referer( $action, 'nonce', false );
|
|
|
|
if ( false === $result ) {
|
|
$return = array(
|
|
$this->plugin->prefix . '_error' => 1,
|
|
'body' => sprintf( __( 'Invalid nonce for: %s' ), $action ),
|
|
);
|
|
$this->end_ajax( json_encode( $return ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the raw licence masked with bullets except for the last segment.
|
|
*
|
|
* @return bool|string false if no licence, masked string otherwise.
|
|
*/
|
|
public function get_masked_licence() {
|
|
$licence_key = $this->get_licence_key();
|
|
|
|
if ( ! $licence_key ) {
|
|
return false;
|
|
}
|
|
|
|
$licence_segments = explode( '-', $licence_key );
|
|
$visible_segment = array_pop( $licence_segments );
|
|
$masked_segments = array_map( function ( $segment ) {
|
|
return str_repeat( '•', strlen( $segment ) );
|
|
}, $licence_segments );
|
|
$masked_segments[] = $visible_segment;
|
|
|
|
return join( '-', $masked_segments );
|
|
}
|
|
}
|