Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/optimole-wp/inc/manager.php
+++ b/optimole-wp/inc/manager.php
@@ -453,10 +453,11 @@
if ( ! $this->page_profiler->exists_all( $profile_id ) ) {
$missing = $this->page_profiler->missing_devices( $profile_id );
$time = time();
- $hmac = wp_hash( $profile_id . $time . $this->get_current_url(), 'nonce' );
+ $url = esc_url( esc_js( $this->get_current_url() ) );
+ $hmac = wp_hash( $profile_id . $time . $url, 'nonce' );
$js_optimizer = str_replace(
[ Profile::PLACEHOLDER, Profile::PLACEHOLDER_MISSING, Profile::PLACEHOLDER_TIME, Profile::PLACEHOLDER_HMAC, Profile::PLACEHOLDER_URL ],
- [ $profile_id, implode( ',', $missing ), strval( $time ), $hmac, $this->get_current_url() ],
+ [ $profile_id, implode( ',', $missing ), strval( $time ), $hmac, $url ],
$js_optimizer
);
$html = str_replace( Optml_Admin::get_optimizer_script( true ), $js_optimizer, $html );
--- a/optimole-wp/optimole-wp.php
+++ b/optimole-wp/optimole-wp.php
@@ -2,7 +2,7 @@
/**
* Plugin Name: Image optimization service by Optimole
* Description: Complete handling of your website images.
- * Version: 4.2.3
+ * Version: 4.2.4
* Author: Optimole
* Author URI: https://optimole.com
* License: GPL-2.0+
@@ -87,7 +87,7 @@
}
define( 'OPTML_URL', plugin_dir_url( __FILE__ ) );
define( 'OPTML_PATH', plugin_dir_path( __FILE__ ) );
- define( 'OPTML_VERSION', '4.2.3' );
+ define( 'OPTML_VERSION', '4.2.4' );
define( 'OPTML_NAMESPACE', 'optml' );
define( 'OPTML_BASEFILE', __FILE__ );
define( 'OPTML_PRODUCT_SLUG', basename( OPTML_PATH ) );
--- a/optimole-wp/vendor/codeinwp/themeisle-sdk/load.php
+++ b/optimole-wp/vendor/codeinwp/themeisle-sdk/load.php
@@ -14,7 +14,7 @@
return;
}
// Current SDK version and path.
-$themeisle_sdk_version = '3.3.50';
+$themeisle_sdk_version = '3.3.51';
$themeisle_sdk_path = dirname( __FILE__ );
global $themeisle_sdk_max_version;
--- a/optimole-wp/vendor/codeinwp/themeisle-sdk/src/Loader.php
+++ b/optimole-wp/vendor/codeinwp/themeisle-sdk/src/Loader.php
@@ -64,6 +64,7 @@
'announcements',
'featured_plugins',
'float_widget',
+ 'migrator',
];
/**
* Holds the labels for the modules.
@@ -72,10 +73,11 @@
*/
public static $labels = [
'announcements' => [
- 'notice_link_label' => 'See the Offer',
- 'max_savings' => 'Our biggest sale of the year: <strong>%s OFF everything!</strong> Don't miss this limited-time offer.',
- 'black_friday' => 'Black Friday Sale',
- 'time_left' => '%s left',
+ 'notice_link_label' => 'See the deals',
+ 'max_savings' => 'Best WordPress Black Friday deals of %s — themes, plugins, hosting. Curated by the Themeisle team.',
+ 'black_friday' => 'Black Friday Sale',
+ 'time_left' => '%s left',
+ 'plugin_meta_message' => 'Black Friday Sale - 60% OFF',
],
'compatibilities' => [
'notice' => '%s requires a newer version of %s. Please %supdate%s %s %s to the latest version.',
@@ -108,10 +110,14 @@
'valid' => 'Valid',
'invalid' => 'Invalid',
'notice' => 'Enter your license from %s purchase history in order to get %s updates',
- 'expired' => 'Your %s's License Key has expired. In order to continue receiving support and software updates you must %srenew%s your license key.',
+ 'expired' => '%s license expired',
+ 'expired_date' => 'Expired on %s',
+ 'expired_notice' => 'Your current setup continues working, but premium features are disabled and you're no longer receive updates - including critical patches - or support.',
'inactive' => 'In order to benefit from updates and support for %s, please add your license code from your %spurchase history%s and validate it %shere%s.',
'no_activations' => 'No more activations left for %s. You need to upgrade your plan in order to use %s on more websites. If you need assistance, please get in touch with %s staff.',
+ 'renew_license' => 'Renew License',
+ 'learn_more' => 'Learn More',
],
'promotions' => [
'recommended' => 'Recommended by %s',
@@ -249,9 +255,9 @@
'cta' => 'Rollback to v%s',
],
'logger' => [
- 'notice' => 'Do you enjoy <b>{product}</b>? Become a contributor by opting in to our anonymous data tracking. We guarantee no sensitive data is collected.',
- 'cta_y' => 'Sure, I would love to help.',
- 'cta_n' => 'No, thanks.',
+ 'notice' => 'Help improve <b>{product}</b> by sharing anonymous usage data about your setup. No personal data collected.',
+ 'cta_y' => 'Count me in',
+ 'cta_n' => 'No thanks',
],
'about_us' => [
'title' => 'About Us',
--- a/optimole-wp/vendor/codeinwp/themeisle-sdk/src/Modules/About_us.php
+++ b/optimole-wp/vendor/codeinwp/themeisle-sdk/src/Modules/About_us.php
@@ -96,6 +96,9 @@
return;
}
+ // Refresh the about data to get the latest changes.
+ $this->about_data = apply_filters( $this->product->get_key() . '_about_us_metadata', array() );
+
add_submenu_page(
$this->about_data['location'],
Loader::$labels['about_us']['title'],
--- a/optimole-wp/vendor/codeinwp/themeisle-sdk/src/Modules/Abstract_Migration.php
+++ b/optimole-wp/vendor/codeinwp/themeisle-sdk/src/Modules/Abstract_Migration.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * The abstract migration class for ThemeIsle SDK.
+ *
+ * @package ThemeIsleSDK
+ * @subpackage Modules
+ * @copyright Copyright (c) 2024, Themeisle
+ * @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
+ * @since 3.3.50
+ */
+
+namespace ThemeisleSDKModules;
+
+// Exit if accessed directly.
+if ( ! defined( 'ABSPATH' ) ) {
+ exit;
+}
+
+/**
+ * Abstract base class for SDK migrations.
+ *
+ * Migration files should return an anonymous class instance extending this class:
+ *
+ * return new class extends ThemeisleSDKModulesAbstract_Migration {
+ * public function up() { ... }
+ * };
+ */
+abstract class Abstract_Migration {
+ /**
+ * WordPress database object.
+ *
+ * @var wpdb
+ */
+ protected $wpdb;
+
+ /**
+ * WordPress table prefix.
+ *
+ * @var string
+ */
+ protected $prefix;
+
+ /**
+ * WordPress charset and collation string.
+ *
+ * @var string
+ */
+ protected $charset_collate;
+
+ /**
+ * Constructor. Populates database helpers.
+ */
+ public function __construct() {
+ global $wpdb;
+ $this->wpdb = $wpdb;
+ $this->prefix = $wpdb->prefix;
+ $this->charset_collate = $wpdb->get_charset_collate();
+ }
+
+ /**
+ * Run the migration.
+ */
+ abstract public function up();
+
+ /**
+ * Reverse the migration.
+ *
+ * Override in concrete migrations to undo what up() did. Called by
+ * Migrator::rollback() — never invoked automatically.
+ *
+ * @return void
+ */
+ public function down() {
+ // No-op by default. Override to implement rollback logic.
+ }
+
+ /**
+ * Determine whether this migration should run.
+ *
+ * Override to add a custom idempotency check beyond name-based tracking.
+ * Return false to skip the migration without recording it.
+ *
+ * @return bool
+ */
+ public function should_run() {
+ return true;
+ }
+}
--- a/optimole-wp/vendor/codeinwp/themeisle-sdk/src/Modules/Announcements.php
+++ b/optimole-wp/vendor/codeinwp/themeisle-sdk/src/Modules/Announcements.php
@@ -22,8 +22,9 @@
* Announcement module for the ThemeIsle SDK.
*/
class Announcements extends Abstract_Module {
-
+
const SALE_DURATION_BLACK_FRIDAY = '+7 days'; // DateTime modifier. (Include Cyber Monday)
+ const MINIMUM_INSTALL_AGE = 3 * DAY_IN_SECONDS;
/**
* Mark if the notice was already loaded.
@@ -33,8 +34,15 @@
private static $notice_loaded = false;
/**
+ * Mark if the plugin meta link was already loaded.
+ *
+ * @var boolean
+ */
+ private static $meta_link_loaded = false;
+
+ /**
* The product to be used.
- *
+ *
* @var string
*/
private static $current_product = '';
@@ -63,15 +71,16 @@
*/
public function load( $product ) {
$this->product = $product;
-
+
add_filter(
'themeisle_sdk_is_black_friday_sale',
function( $is_black_friday ) {
return $this->is_black_friday_sale( $this->get_current_date() );
}
);
-
- add_action( 'admin_init', array( $this, 'load_announcements' ) );
+
+ add_action( 'admin_menu', array( $this, 'load_announcements' ), 9 );
+ add_action( 'wp_ajax_themeisle_sdk_dismiss_black_friday_notice', array( $this, 'disable_notification_ajax' ) );
}
/**
@@ -80,23 +89,29 @@
* @return void
*/
public function load_announcements() {
- if ( ! $this->is_black_friday_sale( $this->get_current_date() ) ) {
+ $current_date = $this->get_current_date();
+ if ( ! $this->is_black_friday_sale( $current_date ) ) {
return;
}
-
+
+ if ( self::MINIMUM_INSTALL_AGE > ( $current_date->getTimestamp() - $this->product->get_install_time() ) ) {
+ return;
+ }
+
add_action( 'admin_notices', array( $this, 'black_friday_notice_render' ) );
- add_action( 'wp_ajax_themeisle_sdk_dismiss_black_friday_notice', array( $this, 'disable_notification_ajax' ) );
+
add_action(
'themeisle_internal_page',
function( $plugin, $page_slug ) {
self::$current_product = $plugin;
},
10,
- 2
+ 2
);
- }
-
+ add_filter( 'plugin_row_meta', array( $this, 'add_plugin_meta_links' ), 10, 2 );
+ add_filter( $this->product->get_key() . '_about_us_metadata', array( $this, 'override_about_us_metadata' ), 100 );
+ }
/**
* Get the remaining time for the event in a human-readable format.
@@ -176,7 +191,7 @@
/**
* Get the notice data.
- *
+ *
* @return array The notice data.
*/
public function get_notice_data() {
@@ -187,10 +202,12 @@
if ( ! empty( $this->product ) ) {
$utm_location = $this->product->get_friendly_name();
}
-
- $sale_title = Loader::$labels['announcements']['black_friday'];
- $sale_url = tsdk_translate_link( tsdk_utmify( 'https://themeisle.com/blackfriday/', 'bfcm25', $utm_location ) );
- $sale_message = sprintf( Loader::$labels['announcements']['max_savings'], '50%' );
+
+ $sale_title = Loader::$labels['announcements']['black_friday'];
+ $sale_url = tsdk_translate_link( tsdk_utmify( 'https://themeisle.com/blackfriday/', 'bfcm26', $utm_location ) );
+
+ $current_year = $this->get_current_date()->format( 'Y' );
+ $sale_message = sprintf( Loader::$labels['announcements']['max_savings'], $current_year );
return array(
'title' => $sale_title,
@@ -212,45 +229,57 @@
return;
}
self::$notice_loaded = true;
-
- $all_configs = apply_filters( 'themeisle_sdk_blackfriday_data', array( 'default' => $this->get_notice_data() ) );
- if ( empty( $all_configs ) ) {
+ $current_user_id = get_current_user_id();
+
+ if ( ! $this->can_show_notice( $this->get_current_date(), $current_user_id ) ) {
return;
}
- $data = end( $all_configs );
-
- if ( ! empty( self::$current_product ) && isset( $all_configs[ self::$current_product ] ) ) {
- $data = $all_configs[ self::$current_product ];
- }
+ $all_configs = apply_filters( 'themeisle_sdk_blackfriday_data', array( 'default' => $this->get_notice_data() ) );
- if ( empty( $data ) || ! is_array( $data ) ) {
+ if ( empty( $all_configs ) || ! is_array( $all_configs ) ) {
return;
}
-
- $current_user_id = get_current_user_id();
- $can_dismiss = true;
- if ( ! empty( $data['dismiss'] ) ) {
- $can_dismiss = $data['dismiss'];
- } else {
- // Disable by default if we are on a product page.
- if ( 0 < did_action( 'themeisle_internal_page' ) ) {
- $can_dismiss = false;
+ $data = isset( $all_configs['default'] ) ? $all_configs['default'] : $this->get_notice_data();
+ $products = Loader::get_products();
+ $current_time = $this->get_current_date()->getTimestamp();
+ $can_show = false;
+
+ // Check if we have products that are eligible to show the notice with the default data. If the product provide its own config, use it.
+ foreach ( $products as $product ) {
+ $slug = $product->get_slug();
+
+ if ( self::MINIMUM_INSTALL_AGE < ( $current_time - $product->get_install_time() ) ) {
+ $can_show = true;
+
+ if ( isset( $all_configs[ $slug ] ) && ! empty( $all_configs[ $slug ] ) && is_array( $all_configs[ $slug ] ) ) {
+ $data = $all_configs[ $slug ];
+
+ if ( self::$current_product === $slug ) {
+ $data = $all_configs[ $slug ];
+ break;
+ }
+ }
}
}
- if ( $can_dismiss && ! $this->can_show_notice( $this->get_current_date(), $current_user_id ) ) {
+ if ( ! $can_show ) {
return;
}
+ $displayed_on_internal_page = 0 < did_action( 'themeisle_internal_page' );
+
+ $title = ! empty( $data['title'] ) ? $data['title'] : Loader::$labels['announcements']['black_friday'];
+ $time_left_label = ! empty( $data['time_left'] ) ? $data['time_left'] : '';
+ $message = ! empty( $data['message'] ) ? $data['message'] : '';
$logo_url = ! empty( $data['logo_url'] ) ? $data['logo_url'] : $this->get_sdk_uri() . 'assets/images/themeisle-logo.png';
$cta_label = ! empty( $data['cta_label'] ) ? $data['cta_label'] : Loader::$labels['announcements']['notice_link_label'];
$sale_url = ! empty( $data['sale_url'] ) ? $data['sale_url'] : '';
- $hide_other_notices = ! empty( $data['hide_other_notices'] ) ? $data['hide_other_notices'] : ! $can_dismiss;
- $dismiss_notice_url = wp_nonce_url(
- add_query_arg(
+ $hide_other_notices = ! empty( $data['hide_other_notices'] ) ? $data['hide_other_notices'] : $displayed_on_internal_page;
+ $dismiss_notice_url = wp_nonce_url(
+ add_query_arg(
array( 'action' => 'themeisle_sdk_dismiss_black_friday_notice' ),
admin_url( 'admin-ajax.php' )
),
@@ -260,7 +289,7 @@
if ( empty( $sale_url ) ) {
return;
}
-
+
if ( ! current_user_can( 'install_plugins' ) ) {
$sale_url = remove_query_arg( 'lkey', $sale_url );
}
@@ -347,13 +376,13 @@
</div>
<div class="themeisle-sale-content">
<h4 class="themeisle-sale-title">
- <?php echo esc_html( $data['title'] ); ?>
+ <?php echo esc_html( $title ); ?>
<span class="themeisle-sale-time-left">
- <?php echo esc_html( $data['time_left'] ); ?>
+ <?php echo esc_html( $time_left_label ); ?>
</span>
</h4>
<p>
- <?php echo wp_kses_post( $data['message'] ); ?>
+ <?php echo wp_kses_post( $message ); ?>
</p>
</div>
<div class="themeisle-sale-action">
@@ -365,11 +394,9 @@
<?php echo esc_html( $cta_label ); ?>
</a>
</div>
- <?php if ( $can_dismiss ) : ?>
<a href="<?php echo esc_url( $dismiss_notice_url ); ?>" class="themeisle-sale-dismiss">
<span class="dashicons dashicons-dismiss"></span>
</a>
- <?php endif; ?>
</div>
</div>
<script>
@@ -380,7 +407,7 @@
if ( ! bannerRoot || ! saleNotice ) {
return;
}
-
+
bannerRoot.appendChild(saleNotice);
};
@@ -405,8 +432,115 @@
if ( empty( $return_page_url ) ) {
$return_page_url = admin_url();
}
-
+
wp_safe_redirect( $return_page_url );
exit;
}
+
+ /**
+ * Add the plugin meta links.
+ *
+ * @param array<string, string> $links The plugin meta links.
+ * @param string $plugin_file The plugin file.
+ * @return array<string, string> The plugin meta links.
+ */
+ public function add_plugin_meta_links( $links, $plugin_file ) {
+ if ( self::$meta_link_loaded ) {
+ return $links;
+ }
+
+ if ( $plugin_file !== plugin_basename( $this->product->get_basefile() ) ) {
+ return $links;
+ }
+
+ $configs = apply_filters( 'themeisle_sdk_blackfriday_data', array( 'default' => $this->get_notice_data() ) );
+
+ if ( empty( $configs ) || ! is_array( $configs ) ) {
+ return $links;
+ }
+
+ $current_slug = $this->product->get_slug();
+ $data = isset( $configs[ $current_slug ] ) && ! empty( $configs[ $current_slug ] ) && is_array( $configs[ $current_slug ] ) ? $configs[ $current_slug ] : array();
+
+ $plugin_meta_message = '';
+ $plugin_meta_url = '';
+
+ if ( isset( $data['plugin_meta_targets'] ) && ! empty( $data['plugin_meta_targets'] ) && ! in_array( $current_slug, $data['plugin_meta_targets'] ) ) {
+ return $links; // The current configuration is for another plugins.
+ }
+
+ $plugin_meta_message = ! empty( $data['plugin_meta_message'] ) ? $data['plugin_meta_message'] : '';
+ $plugin_meta_url = ! empty( $data['sale_url'] ) ? $data['sale_url'] : '';
+
+ if ( empty( $plugin_meta_url ) || empty( $plugin_meta_message ) ) {
+
+ // Check if a configuration is in another plugin.
+ $products = Loader::get_products();
+ foreach ( $products as $product ) {
+ $slug = $product->get_slug();
+
+ if ( $slug === $current_slug || ! isset( $configs[ $slug ] ) || empty( $configs[ $slug ] ) || ! is_array( $configs[ $slug ] ) ) {
+ continue;
+ }
+
+ if ( ! empty( $configs[ $slug ]['plugin_meta_targets'] ) && in_array( $current_slug, $configs[ $slug ]['plugin_meta_targets'] ) ) {
+ $plugin_meta_message = ! empty( $configs[ $slug ]['plugin_meta_message'] ) ? $configs[ $slug ]['plugin_meta_message'] : '';
+ $plugin_meta_url = ! empty( $configs[ $slug ]['sale_url'] ) ? $configs[ $slug ]['sale_url'] : '';
+ break;
+ }
+ }
+ }
+
+ if ( empty( $plugin_meta_url ) || empty( $plugin_meta_message ) ) {
+ return $links;
+ }
+
+ $links[] = sprintf( '<a class="themeisle-sale-plugin-meta-link" style="color: red;" href="%s" target="_blank">%s</a>', esc_url( $plugin_meta_url ), esc_html( $plugin_meta_message ) );
+
+ self::$meta_link_loaded = true;
+
+ return $links;
+ }
+
+ /**
+ * Override the About Us upgrade menu during Black Friday.
+ *
+ * Registered dynamically during admin_menu when sale is active.
+ * Only applies if About_Us module is loaded for the product.
+ *
+ * @param array<string, mixed> $about_data About Us metadata.
+ *
+ * @return array<string, mixed>
+ */
+ public function override_about_us_metadata( $about_data ) {
+ if ( ! $this->is_black_friday_sale( $this->get_current_date() ) ) {
+ return $about_data;
+ }
+
+ if ( empty( $about_data ) || ! is_array( $about_data ) ) {
+ return $about_data;
+ }
+
+ if ( empty( $about_data['has_upgrade_menu'] ) || true !== $about_data['has_upgrade_menu'] ) {
+ return $about_data;
+ }
+
+ $configs = apply_filters( 'themeisle_sdk_blackfriday_data', array( 'default' => $this->get_notice_data() ) );
+
+ $current_slug = $this->product->get_slug();
+ if ( ! isset( $configs[ $current_slug ] ) || empty( $configs[ $current_slug ] ) || ! is_array( $configs[ $current_slug ] ) ) {
+ return $about_data;
+ }
+
+ $config = $configs[ $current_slug ];
+
+ if ( empty( $config['upgrade_menu_text'] ) || empty( $config['sale_url'] ) ) {
+ return $about_data;
+ }
+
+ $about_data['upgrade_text'] = $config['upgrade_menu_text'];
+ $about_data['upgrade_link'] = $config['sale_url'];
+
+ return $about_data;
+ }
}
--- a/optimole-wp/vendor/codeinwp/themeisle-sdk/src/Modules/Licenser.php
+++ b/optimole-wp/vendor/codeinwp/themeisle-sdk/src/Modules/Licenser.php
@@ -380,7 +380,7 @@
$status = $this->get_license_status( true );
$no_activations_string = apply_filters( $this->product->get_key() . '_lc_no_activations_string', Loader::$labels['licenser']['no_activations'] );
$no_valid_string = apply_filters( $this->product->get_key() . '_lc_no_valid_string', sprintf( Loader::$labels['licenser']['inactive'], '%s', '<a href="%s" target="_blank">', '</a>', '<a href="%s">', '</a>' ) );
- $expired_license_string = apply_filters( $this->product->get_key() . '_lc_expired_string', sprintf( Loader::$labels['licenser']['expired'], '%s', '<a href="%s" target="_blank">', '</a>' ) );
+ $expired_license_string = apply_filters( $this->product->get_key() . '_lc_expired_heading_string', Loader::$labels['licenser']['expired'] );
// No activations left for this license.
if ( 'valid' != $status && $this->check_activation() ) {
?>
@@ -403,12 +403,72 @@
// Invalid license key.
if ( 'active_expired' === $status ) {
+ // Check if the notice was dismissed.
+ $dismiss_option_key = $this->product->get_key() . '_expired_notice_dismissed';
+ if ( get_option( $dismiss_option_key, false ) ) {
+ return false;
+ }
+
+ $license_data = get_option( $this->product->get_key() . '_license_data', '' );
+ $expiration_date = '';
+ if ( is_object( $license_data ) && isset( $license_data->expires ) ) {
+ $timestamp = strtotime( (string) $license_data->expires );
+ if ( false !== $timestamp ) {
+ $expiration_date = gmdate( 'F j, Y', $timestamp );
+ }
+ }
+
+ $discount_config = apply_filters( $this->product->get_key() . '_lc_renew_discount', false );
+
+ if ( is_array( $discount_config ) && isset( $discount_config['url'] ) && isset( $discount_config['renew_button'] ) ) {
+ $renew_url = $discount_config['url'];
+ $renew_button = $discount_config['renew_button'];
+ } else {
+ $renew_url = apply_filters( $this->product->get_key() . '_lc_renew_url', $this->renew_url() );
+ $renew_button = apply_filters( $this->product->get_key() . '_lc_renew_button_string', Loader::$labels['licenser']['renew_license'] );
+ }
+
+ $learn_more_url = apply_filters( $this->product->get_key() . '_lc_learn_more_url', $this->get_api_url() );
+ $learn_more_button = apply_filters( $this->product->get_key() . '_lc_learn_more_button_string', Loader::$labels['licenser']['learn_more'] );
+ $notice_message = apply_filters( $this->product->get_key() . '_lc_expired_notice_message', Loader::$labels['licenser']['expired_notice'] );
+
+ $expired_date_string = apply_filters( $this->product->get_key() . '_lc_expired_date_string', sprintf( Loader::$labels['licenser']['expired_date'], esc_html( $expiration_date ) ) );
+ $heading = apply_filters( $this->product->get_key() . '_lc_expired_heading_string', sprintf( Loader::$labels['licenser']['expired'], $this->product->get_name() ) );
+ $notice_id = $this->product->get_key() . '_expired_notice';
?>
- <div class="error">
- <p>
- <strong><?php echo sprintf( wp_kses_data( $expired_license_string ), esc_attr( $this->product->get_name() . ' ' . $this->product->get_type() ), esc_url( $this->get_api_url() . '?license=' . $this->license_key ) ); ?> </strong>
+ <div class="notice notice-warning notice-alt is-dismissible themeisle-sdk-license-notice" id="<?php echo esc_attr( $notice_id ); ?>" data-notice-id="<?php echo esc_attr( $notice_id ); ?>" style="position: relative; border-left: 4px solid #d63638; padding: 12px; background-color: #fff;">
+ <p style="margin: 0.5em 0; font-size: 13px;">
+ <strong><?php echo wp_kses_post( $heading ); ?></strong>
+ · <?php echo esc_html( $expired_date_string ); ?>
+ </p>
+ <p style="margin: 0.5em 0 1em 0; font-size: 13px;">
+ <?php echo esc_html( $notice_message ); ?>
+ </p>
+ <p style="margin: 0.5em 0;">
+ <a href="<?php echo esc_url( $renew_url ); ?>" class="button button-primary" target="_blank" rel="noopener noreferrer">
+ <?php echo esc_html( $renew_button ); ?>
+ </a>
+ <a href="<?php echo esc_url( $learn_more_url ); ?>" class="button" target="_blank" rel="noopener noreferrer" style="border: none; background-color: transparent;">
+ <?php echo esc_html( $learn_more_button ); ?>
+ </a>
</p>
</div>
+ <script type="text/javascript">
+ jQuery(document).ready(function($) {
+ $('#<?php echo esc_js( $notice_id ); ?>').on('click', '.notice-dismiss', function(e) {
+ const noticeId = '<?php echo esc_js( $notice_id ); ?>';
+ $.ajax({
+ url: ajaxurl,
+ type: 'POST',
+ data: {
+ action: 'themeisle_sdk_dismiss_license_notice',
+ notice_id: noticeId,
+ nonce: '<?php echo esc_js( wp_create_nonce( 'themeisle_sdk_dismiss_license_notice' ) ); ?>'
+ }
+ });
+ });
+ });
+ </script>
<?php
return false;
@@ -1045,6 +1105,32 @@
add_action( 'admin_init', array( $this, 'product_valid' ), 99999999 );
add_action( 'admin_notices', array( $this, 'show_notice' ) );
add_filter( $this->product->get_key() . '_license_status', array( $this, 'get_license_status' ) );
+ add_action( 'wp_ajax_themeisle_sdk_dismiss_license_notice', array( $this, 'dismiss_license_notice' ) );
+ }
+
+ /**
+ * Handle AJAX request to dismiss the license notice.
+ */
+ public function dismiss_license_notice() {
+ if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'themeisle_sdk_dismiss_license_notice' ) ) {
+ wp_send_json_error( 'Invalid nonce' );
+ }
+
+ if ( ! current_user_can( 'manage_options' ) ) {
+ wp_send_json_error( 'Insufficient permissions' );
+ }
+
+ $notice_id = isset( $_POST['notice_id'] ) ? sanitize_text_field( $_POST['notice_id'] ) : '';
+
+ if ( empty( $notice_id ) ) {
+ wp_send_json_error( 'Missing notice ID' );
+ }
+
+ // Save the dismissal option.
+ $dismiss_option_key = $notice_id . '_dismissed';
+ update_option( $dismiss_option_key, true );
+
+ wp_send_json_success();
}
/**
--- a/optimole-wp/vendor/codeinwp/themeisle-sdk/src/Modules/Migrator.php
+++ b/optimole-wp/vendor/codeinwp/themeisle-sdk/src/Modules/Migrator.php
@@ -0,0 +1,177 @@
+<?php
+/**
+ * The migrator module for ThemeIsle SDK.
+ *
+ * @package ThemeIsleSDK
+ * @subpackage Modules
+ * @copyright Copyright (c) 2024, Themeisle
+ * @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
+ * @since 3.3.50
+ */
+
+namespace ThemeisleSDKModules;
+
+use ThemeisleSDKCommonAbstract_Module;
+use ThemeisleSDKProduct;
+
+// Exit if accessed directly.
+if ( ! defined( 'ABSPATH' ) ) {
+ exit;
+}
+
+/**
+ * Migrator module for ThemeIsle SDK.
+ *
+ * Allows products to ship PHP migration files that run automatically on
+ * admin page loads. Each product opts in by registering its migrations
+ * directory via the `{product_slug}_sdk_migrations_path` filter.
+ */
+class Migrator extends Abstract_Module {
+ /**
+ * Option key suffix used to store the list of ran migrations.
+ */
+ const OPTION_SUFFIX = '_ran_migrations';
+
+ /**
+ * Check if we should load the module for this product.
+ *
+ * Always returns true — the actual path check happens lazily at admin_init.
+ *
+ * @param Product $product Product to load the module for.
+ *
+ * @return bool
+ */
+ public function can_load( $product ) {
+ return apply_filters( $product->get_slug() . '_sdk_enable_migrator', true );
+ }
+
+ /**
+ * Load module logic.
+ *
+ * @param Product $product Product to load.
+ *
+ * @return Migrator
+ */
+ public function load( $product ) {
+ $this->product = $product;
+ add_action( 'admin_init', array( $this, 'run_pending' ) );
+ add_action( 'themeisle_sdk_rollback_migration_' . $product->get_slug(), array( $this, 'rollback' ) );
+ return $this;
+ }
+
+ /**
+ * Discover and run any pending migrations for the product.
+ *
+ * Only runs when a version upgrade was detected during this request, indicated
+ * by the themeisle_sdk_update_{slug} action having fired.
+ *
+ * @return void
+ */
+ public function run_pending() {
+ if ( ! did_action( 'themeisle_sdk_update_' . $this->product->get_slug() ) ) {
+ return;
+ }
+
+ $path = $this->get_migrations_path();
+
+ if ( empty( $path ) || ! is_dir( $path ) ) {
+ return;
+ }
+
+ $files = glob( trailingslashit( $path ) . '*.php' );
+
+ if ( empty( $files ) ) {
+ return;
+ }
+
+ sort( $files ); // Alphabetical order = chronological order given timestamp naming.
+
+ $option_key = $this->product->get_key() . self::OPTION_SUFFIX;
+ $ran = get_option( $option_key, array() );
+
+ foreach ( $files as $file ) {
+ $name = basename( $file, '.php' );
+
+ if ( in_array( $name, $ran, true ) ) {
+ continue;
+ }
+
+ try {
+ $migration = require $file; // Migration files return an anonymous class instance.
+
+ if ( ! ( $migration instanceof Abstract_Migration ) ) {
+ continue;
+ }
+
+ if ( ! $migration->should_run() ) {
+ continue;
+ }
+
+ $migration->up();
+ $ran[] = $name;
+ update_option( $option_key, $ran );
+ } catch ( Throwable $e ) {
+ // Log and stop — leave the migration unrecorded so it retries next load.
+ // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
+ error_log( 'ThemeIsle SDK Migrator: failed to run ' . $name . ': ' . $e->getMessage() );
+ break;
+ }
+ }
+ }
+
+ /**
+ * Roll back a single migration by name.
+ *
+ * Calls down() on the migration and removes it from the ran list so it will
+ * be picked up again on the next upgrade. This method is never called
+ * automatically — products invoke it explicitly when needed.
+ *
+ * @param string $migration_name Migration basename without .php extension.
+ *
+ * @return bool True if rolled back successfully, false if not found or not previously run.
+ */
+ public function rollback( $migration_name ) {
+ $option_key = $this->product->get_key() . self::OPTION_SUFFIX;
+ $ran = get_option( $option_key, array() );
+
+ if ( ! in_array( $migration_name, $ran, true ) ) {
+ return false;
+ }
+
+ $path = $this->get_migrations_path();
+ $file = trailingslashit( $path ) . $migration_name . '.php';
+
+ if ( ! is_file( $file ) ) {
+ return false;
+ }
+
+ try {
+ $migration = require $file;
+
+ if ( ! ( $migration instanceof Abstract_Migration ) ) {
+ return false;
+ }
+
+ $migration->down();
+ update_option( $option_key, array_values( array_diff( $ran, array( $migration_name ) ) ) );
+
+ return true;
+ } catch ( Throwable $e ) {
+ // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
+ error_log( 'ThemeIsle SDK Migrator: failed to roll back ' . $migration_name . ': ' . $e->getMessage() );
+
+ return false;
+ }
+ }
+
+ /**
+ * Get the migrations directory path for the current product.
+ *
+ * Products register their path via the `{slug}_sdk_migrations_path` filter.
+ *
+ * @return string Absolute path to the migrations directory, or empty string.
+ */
+ private function get_migrations_path() {
+ return (string) apply_filters( $this->product->get_slug() . '_sdk_migrations_path', '' );
+ }
+}
--- a/optimole-wp/vendor/codeinwp/themeisle-sdk/start.php
+++ b/optimole-wp/vendor/codeinwp/themeisle-sdk/start.php
@@ -41,6 +41,8 @@
$themeisle_library_path . '/src/Modules/Announcements.php',
$themeisle_library_path . '/src/Modules/Featured_plugins.php',
$themeisle_library_path . '/src/Modules/Float_widget.php',
+ $themeisle_library_path . '/src/Modules/Abstract_Migration.php',
+ $themeisle_library_path . '/src/Modules/Migrator.php',
];
$files_to_load = array_merge( $files_to_load, apply_filters( 'themeisle_sdk_required_files', [] ) );
--- a/optimole-wp/vendor/composer/installed.php
+++ b/optimole-wp/vendor/composer/installed.php
@@ -29,9 +29,9 @@
'dev_requirement' => false,
),
'codeinwp/themeisle-sdk' => array(
- 'pretty_version' => '3.3.50',
- 'version' => '3.3.50.0',
- 'reference' => '3c1f8dfc2390e667bbc086c5d660900a7985efa6',
+ 'pretty_version' => '3.3.51',
+ 'version' => '3.3.51.0',
+ 'reference' => 'bb2a8414b0418b18c68c9ff1df3d7fb10467928d',
'type' => 'library',
'install_path' => __DIR__ . '/../codeinwp/themeisle-sdk',
'aliases' => array(),